Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.


Here is a script that spits out the AST after each step in the compile process, in xml or boo format. It doesn't show everything going on, because only the visible changes to the AST structure itself are shown. See the source code for each step and for the boo compiler for more details.

*To run this script, you need to apply the patch at the bottom, then do a nant rebuild to re-generate your AST files.

No Format
// By DougHolton. /*
More stuff added by David Piepgrass.
// For help, please see the help string below.
import System
import System.IO
import System.Xml.Serialization from System.Xml
import Boo.Lang.Compiler from Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Ast.Visitors
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.Steps
import System.Reflection

class Globals:
	static help = """
This script visits the AST structure after each step in the compile
and converts it to XML or back to boo syntax. If there are visible
differences since the previous step, it saves the output to a file in a

folder named "compilersteps"after the command-line arguments (name of source file plus


How to runuse:

/path/to/booi.exe path/to/ [-xml | (-ent | -exp | -nodes) [-short]

-xml: generate XML representation of AST
-ent: show entity type names that are associated with AST nodes
-exp: show entity type names and expression types (node.ExpressionType)
-nodes: show entity type names and expression types, and for each typed

        expression, also show the AST type (node.GetType())
-bind: show binding information contained in entities
-short: abbreviate the output so that lines hopefully fit on your

You can also use the "-r:assembly.dll" flag to add assembly references.

ItShowSteps will generate a folder in the current directory named "compilersteps",
input file and put the options you specified. It generates copies of the
as it looks after each compiler step, and puts them in that folder.
If importyou Systemuse import System.IO
import System.Xml.Serialization from System.Xml
import Boo.Lang.Compiler from Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import -exp -nodes -bind, the output can get pretty long and
For example, a simple expression like "_x", that refers to a variable
_x in
the current class, eventually expands to a the following (all on one

  <InternalField MemberReferenceExpression=double
    <InternalMethod SelfLiteralExpression=Foo.B

The -short command-line option will use an abbreviated syntax like

  <ItlField MemberRefrExpr=double @F:Ns.MyClass._x>
    <ItlMethod SelfLiteralExpr=Foo.B @M:Ns.MyClass.MyFn>

Here's how to understand it. First of all, of course, it's not really
it's just an XML-like notation. If you have a text editor that can do
syntax highlighting, use it. Second, notice that a reference to "self"
been added. Third, the outer tag (InternalField) describes the whole
expression, "self._x", whereas the inner tag (InternalMethod) describes
only the "self" part. The tags have the following syntax:

  <E N=T Bind=S:P> where

  N: Class of AST node. For example, "MemberReferenceExpression" refers
     to the Boo.Lang.Compiler.Ast.MemberReferenceExpression import Boo.Lang.Compiler.Ast.Visitors
import class.
  T: The value of node.ExpressionType.ToString(), e.g. int
  E: Type of entity associated with the node, or "_" if the node has no
     entity. For example, "InternalField" actually refers to the
     Boo.Lang.Compiler.TypeSystem.InternalField import Boo.Lang.Compiler.Steps
import System.Reflection

class Globals:
	class. It seems that an
     entity's main purpose is to hold binding information.
  S: The entity's EntityType (although I don't actually know what it's
     If the EntityType is EntityType.Type, which is the most common
     then S is omitted.
  P: Binding Path. For example, X.Y.Z might represent a variable or
     "Z" in class "Y" in namespace "X".

A tag is not printed at all if there is no entity nor data type
associated with a node. That's why you don't see very many tags during
the first compiler steps. The "N=T" part is printed only if the node is
an Expression and it has a a known data type; the Bind=S:P part is only

printed if binding information is available.
	static format = "boo" //or "xml" //format for output
	static foldername = "compilersteps" //folder where files are saved
	static showtypesshowents = false  //whether to print typesystementity informationtypes
	static showexp = false //show expression types as well
	static shownodetypes = false
	static shorten = false
	static showbindings = false
	//used internally:
	static savefolder as string
	static n = 0
	static laststep as string

//basic boo printer visitor, but adds comments if a node is synthetic (generated
//by the compiler instead of the user).
class BooSyntheticPrinterVisitor(BooPrinterVisitor):
	def constructor(writer as TextWriter):
	override def Visit(node as Node) as bool:
		if node is not null and node.IsSynthetic:
			WriteIndented("// synthetic")
		return super(node)
class BooTypePrinterVisitor(BooPrinterVisitor):
	_showexp = false
	_shownodetypes = false
	_shorten = false
	_showbindings = false
	def constructor(writer as TextWriter, show_expressions as bool, shownodetypes as bool, shorten as bool, showbindings as bool):
		_showexp = show_expressions
		_shownodetypes = shownodetypes
		_shorten = shorten
		_showbindings = showbindings

	override def Visit(node as Node) as bool:
		tagnamereturn =true ""
if node is null
		if node is not null:
			WriteIndented("// synthetic")
		WriteIndented() // Automatically indent iff starting a new line
		tagname = ""
		entity = TypeSystemServices.GetOptionalEntity(node) // aka node.Entity
			if entity is not null:
				tagname = ShortName(entity.GetType())
			s = "<"
			s += tagname
			s += ExtraJunk(node)
			s += ">"
			if _shorten:
				tagname = InitialsOf(tagname)
		elif _showexp or _showbindings:
			junk = ExtraJunk(node)
			if junk.Length > 0:
				tagname = "_"
				s = "<_${junk}>"
		result = super(node)
		if tagname != "":
		return result

	def ShortName(t as object):
		t2 = t.ToString(). \
			Replace("Boo.Lang.Compiler.TypeSystem.",""). \
		return t2 unless _shorten
		sreturn = "<"
				s += tagname
				if t2. \
			Replace("Expression", "Expr"). \
			Replace("Reference", "Refr"). \
			Replace("Internal", "Itl").Replace("External", "Xtl")

	def InitialsOf(s as string):
		s2 = System.Text.StringBuilder()
		for ch in s:
			if ch >= char('A') and ch <= char('Z'):
		if s2.Length>0:
			return s2.ToString()
			return s

	def ExtraJunk(node as Node):
		s = System.Text.StringBuilder()
		if _showexp:
					exp = node as Expression
					if exp is not null and exp.ExpressionType is not null:
				if _shownodetypes:
					s += " ExpType="+exp.ExpressionType.ToString().Append(" ")
				elif _shorten:
					s += ">".Append(":")
					s.Append(" EType=")
		result = supers.Append(ShortName(exp.ExpressionType.ToString()))

		if _showbindings:
			entity = TypeSystemServices.GetOptionalEntity(node) // aka node.Entity
			if tagnameentity != ""is not null:
				if _shorten:
					s.Append(" @")
			Write		s.Append("</"+tagname+">" Bind=")
		return result
		if entity.EntityType != EntityType.Type:
					if _shorten:
		return s.ToString()

def PrintAST([required]result as CompilerContext, [required]o as TextWriter):
	astobject = result.CompileUnit
		s = XmlSerializer( astobject.GetType() )
	except e:
		print e.Message
		s.Serialize( o, astobject )
	except e:
"\n		print e.GetType(), ":", e.ToString()Message

def AfterStep(sender, e as CompilerStepEventArgs):
	stepname = e.Step.ToString().Replace("Boo.Lang.Parser.","").Replace("Boo.Lang.Compiler.Steps.","")
	tempfile = Path.GetTempFileName()
	using temp = StreamWriter(tempfile):
		if format == "xml":
			PrintAST(e.Context, temp)
				printer as BooPrinterVisitor
				if showtypesshowents:
					printer = BooTypePrinterVisitor(temp, showexp, shownodetypes, shorten, showbindings)
					printer = BooPrinterVisitorBooSyntheticPrinterVisitor(temp)
			except e:
				print e.Message + "\n" + e.StackTrace

	using r = StreamReader(tempfile):
		thisstep = r.ReadToEnd()
	filename = string.Format("STEP{0:D2}-{1}.{2}", n, stepname, format)
	if thisstep != laststep:
		File.Move(tempfile, Path.Combine(savefolder, filename))
		laststep = thisstep
		print string.Format("STEP{0:D2}-{1}: SAVED TO {2} FILE.", n, stepname, format.ToUpper())
		print string.Format("STEP{0:D2}-{1}: NO CHANGE TO AST.", n, stepname)
def LoadAssembly(assemblyName as string) as Assembly:
	reference as Assembly
	if File.Exists(Path.GetFullPath(assemblyName)):
		reference = Assembly.LoadFrom(Path.GetFullPath(assemblyName))
	if reference is null:
		reference = Assembly.LoadWithPartialName(assemblyName)
		if reference is null:
			raise ApplicationException(
	return reference


if len(argv) == 0:
	print "Please specify at least one boo file as a parameter"

compiler = BooCompiler()

//delete old folder if running more than once:
if Directory.Exists(foldername):
	Directory.Delete(foldername, true)
savedir = Directory.CreateDirectory(foldername)
if savedir is null or not Directory.Exists(foldername):
	print "The directory '${foldername}' could not be created."

savefolder = savedir.FullName

compiler.Parameters.Pipeline = Compile()
compiler.Parameters.Pipeline.AfterStep += AfterStep

foldername_base = foldername_extra = ""

for arg in argv:

	if arg[0:3] == "-r:":
	elif arg == "-xml":
		format = "xml"
	elif arg == "-typesent":
		showtypesshowents = true
	elif arg == "-exp":
		showtypesshowents = true
		showexp = true
	elif arg == "-ducky":
		compiler.Parameters.Ducky = true
	elif arg == "-nodes":
		showents = true
		showexp = true
		shownodetypes = true
	elif arg == "-short":
		shorten = true
	elif arg == "-bind":
		showbindings = true
		foldername_base += /^(.*?[\/\\])*([^\\\/]+?)(\.[^.\\\/]*)?$/.Match(arg).Groups[2]
	foldername_extra += " " + arg

foldername = foldername_base + foldername_extra

//delete old folder if running more than once:
if Directory.Exists(foldername):
	Directory.Delete(foldername, true)

savedir = Directory.CreateDirectory(foldername)
if savedir is null or not Directory.Exists(foldername):
	print "The directory '${foldername}' could not be created."

savefolder = savedir.FullName

	print "See boo/src/Boo.Lang.Compiler/Steps/ for the source code for these steps."
	result = compiler.Run()
	if len(result.Errors) > 0:
		print "\nThere were ${len(result.Errors)} errors compiling the boo file(s)"
		print result.Errors.ToString(true)
		print "\nSuccessful: See the files under: '${savefolder}'"
except e:
	print e.GetType(), ":", e.Message

patch required to run the above:

No Format

Index: scripts/Templates/NodeImpl.cs
--- scripts/Templates/NodeImpl.cs	(revision 2368)
+++ scripts/Templates/NodeImpl.cs	(working copy)
@@ -140,6 +140,7 @@
 			${node.Name} clone = (${node.Name})FormatterServices.GetUninitializedObject(typeof(${node.Name}));
 			clone._lexicalInfo = _lexicalInfo;
+			clone._isSynthetic = _isSynthetic;
 			clone._endSourceLocation = _endSourceLocation;
 			clone._documentation = _documentation;
 			clone._annotations = (Hashtable)_annotations.Clone();
Index: src/Boo.Lang.Compiler/Ast/Node.cs
--- src/Boo.Lang.Compiler/Ast/Node.cs	(revision 2368)
+++ src/Boo.Lang.Compiler/Ast/Node.cs	(working copy)
@@ -67,6 +67,7 @@
 		protected void InitializeFrom(Node other)
 			_lexicalInfo = other.LexicalInfo;
+			_isSynthetic = other.IsSynthetic;
 		public Node CloneNode()
Index: src/Boo.Lang.Compiler/Ast/Visitors/BooPrinterVisitor.cs
--- src/Boo.Lang.Compiler/Ast/Visitors/BooPrinterVisitor.cs	(revision 2368)
+++ src/Boo.Lang.Compiler/Ast/Visitors/BooPrinterVisitor.cs	(working copy)
@@ -366,10 +366,10 @@
 		override public void OnMethod(Method m)
-            if (m.IsRuntime)
-            {
-                WriteImplementationComment("runtime");
-            }			
+			if (m.IsRuntime)
+			{
+				WriteImplementationComment("runtime");
+			}
 			WriteCallableDefinitionHeader("def ", m);
@@ -1492,7 +1492,7 @@
-		void WriteTypeDefinition(string keyword, TypeDefinition td)
+		virtual protected void WriteTypeDefinition(string keyword, TypeDefinition td)
 			WriteAttributes(td.Attributes, true);
Index: src/Boo.Lang.Compiler/Steps/IntroduceModuleClasses.cs
--- src/Boo.Lang.Compiler/Steps/IntroduceModuleClasses.cs	(revision 2368)
+++ src/Boo.Lang.Compiler/Steps/IntroduceModuleClasses.cs	(working copy)
@@ -77,6 +77,7 @@
 			if (null == moduleClass)
 				moduleClass = new ClassDefinition();
+				moduleClass.IsSynthetic = true;
 				hasModuleClass = false;
@@ -104,6 +105,7 @@
 			if (node.Globals.Statements.Count > 0)
 				Method method = new Method(node.Globals.LexicalInfo);
+				method.IsSynthetic = true;
 				method.Parameters.Add(new ParameterDeclaration("argv", new ArrayTypeReference(new SimpleTypeReference("string"))));
 				method.ReturnType = CodeBuilder.CreateTypeReference(TypeSystemServices.VoidType);
 				method.Body = node.Globals;
Index: src/Boo.Lang.Compiler/TypeSystem/BooClassBuilder.cs
--- src/Boo.Lang.Compiler/TypeSystem/BooClassBuilder.cs	(revision 2368)
+++ src/Boo.Lang.Compiler/TypeSystem/BooClassBuilder.cs	(working copy)
@@ -27,11 +27,11 @@
 namespace Boo.Lang.Compiler.TypeSystem
-	using System;
-	using Boo.Lang.Compiler.Ast;
-	using Attribute = Boo.Lang.Compiler.Ast.Attribute;
+	using System;
+	using Boo.Lang.Compiler.Ast;
+	using Attribute = Boo.Lang.Compiler.Ast.Attribute;
 	public class BooClassBuilder
 		BooCodeBuilder _codeBuilder;
@@ -52,6 +52,7 @@
 			_cd = new ClassDefinition();
 			_cd.Name = name;
 			_cd.Entity = new InternalClass(_codeBuilder.TypeSystemServices, _cd);
+			_cd.IsSynthetic = true;
 		public BooCodeBuilder CodeBuilder
@@ -117,6 +118,7 @@
 		public BooMethodBuilder AddConstructor()
 			Constructor constructor = new Constructor();
+			constructor.IsSynthetic = true;
 			constructor.Modifiers = TypeMemberModifiers.Public;
 			constructor.Entity = new InternalConstructor(_codeBuilder.TypeSystemServices, constructor);
Index: src/Boo.Lang.Compiler/TypeSystem/BooCodeBuilder.cs
--- src/Boo.Lang.Compiler/TypeSystem/BooCodeBuilder.cs	(revision 2368)
+++ src/Boo.Lang.Compiler/TypeSystem/BooCodeBuilder.cs	(working copy)
@@ -400,7 +400,7 @@
 			MethodInvocationExpression mie = new MethodInvocationExpression(target.LexicalInfo);
 			mie.Target = CreateMemberReference(target, tag);			
-			mie.ExpressionType = tag.ReturnType;			
+			mie.ExpressionType = tag.ReturnType;
 			return mie;			
@@ -420,6 +420,7 @@
 			ReferenceExpression reference = new ReferenceExpression(type.FullName);
 			reference.Entity = type;
+			reference.IsSynthetic = true;
 			return reference;
@@ -611,6 +612,7 @@
 			Constructor constructor = new Constructor();
 			constructor.Modifiers = modifiers;
 			constructor.Entity = new InternalConstructor(_tss, constructor);
+			constructor.IsSynthetic = true;
 			return constructor;
@@ -705,6 +707,7 @@
 			method.Modifiers = modifiers;
 			method.ReturnType = returnType;
 			method.Entity = new InternalMethod(_tss, method);
+			method.IsSynthetic = true;
 			return method;
@@ -714,6 +717,7 @@
 			property.Modifiers = TypeMemberModifiers.Public;
 			property.Type = CreateTypeReference(type);
 			property.Entity = new InternalProperty(_tss, property);
+			property.IsSynthetic = true;
 			return property;
@@ -724,6 +728,7 @@
 			field.Name = name;
 			field.Type = CreateTypeReference(type);
 			field.Entity = new InternalField(field);
+			field.IsSynthetic = true;
 			return field;
@@ -770,6 +775,7 @@
 			Method method = new Method(lexicalInfo);
 			method.Name = baseMethod.Name;
 			method.Modifiers = newModifiers;
+			method.IsSynthetic = true;
 			IParameter[] parameters = baseMethod.GetParameters();
 			for (int i=0; i<parameters.Length; ++i)
Index: src/Boo.Lang.Compiler/TypeSystem/TypeSystemServices.cs
--- src/Boo.Lang.Compiler/TypeSystem/TypeSystemServices.cs	(revision 2368)
+++ src/Boo.Lang.Compiler/TypeSystem/TypeSystemServices.cs	(working copy)
@@ -566,6 +566,7 @@
 		public ClassDefinition CreateCallableDefinition(string name)
 			ClassDefinition cd = new ClassDefinition();
+			cd.IsSynthetic = true;
 			cd.Name = name;
@@ -579,6 +580,7 @@
 		Method CreateCallMethod()
 			Method method = new Method("Call");
+			method.IsSynthetic = true;
 			method.Modifiers = TypeMemberModifiers.Public|TypeMemberModifiers.Virtual;
 			method.Parameters.Add(CodeBuilder.CreateParameterDeclaration(1, "args", ObjectArrayType));
 			method.ReturnType = CodeBuilder.CreateTypeReference(ObjectType);
@@ -589,6 +591,7 @@
 		Constructor CreateCallableConstructor()
 			Constructor constructor = new Constructor();
+			constructor.IsSynthetic = true;
 			constructor.Modifiers = TypeMemberModifiers.Public;
 			constructor.ImplementationFlags = MethodImplementationFlags.Runtime;