No significant changes. Adding a few more tests to the primitives test case and...
[boo.git] / examples / showcompilersteps.boo
blob0710209d763d3399af2a980740a680699e660864
1 #region license
2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #endregion
30 // By Doug Holton. More stuff added by David Piepgrass.
31 // For help, please see the help string below.
32 import System
33 import System.IO
34 import System.Xml.Serialization from System.Xml
35 import Boo.Lang.Compiler from Boo.Lang.Compiler
36 import Boo.Lang.Compiler.IO
37 import Boo.Lang.Compiler.Pipelines
38 import Boo.Lang.Compiler.Ast
39 import Boo.Lang.Compiler.Ast.Visitors
40 import Boo.Lang.Compiler.TypeSystem
41 import Boo.Lang.Compiler.Steps
42 import System.Reflection
44 [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute]
45 class Globals:
46 static help = """
47 This script visits the AST structure after each step in the compile
48 process
49 and converts it to XML or back to boo syntax. If there are visible
50 differences since the previous step, it saves the output to a file in a
52 folder named after the command-line arguments (name of source file plus
54 options).
56 How to use:
58 booi path/to/showcompilersteps.boo [-xml | (-ent | -exp | -nodes) [-short]
59 [-bind]]
60 path/to/your/script.boo
62 -xml: generate XML representation of AST
63 -ent: show entity type names that are associated with AST nodes
64 (node.Entity)
65 -exp: show entity type names and expression types (node.ExpressionType)
66 -nodes: show entity type names and expression types, and for each typed
68 expression, also show the AST type (node.GetType())
69 -bind: show binding information contained in entities
70 -short: abbreviate the output so that lines hopefully fit on your
71 screen.
73 You can also use the "-r:assembly.dll" flag to add assembly references.
75 ShowSteps will generate a folder in the current directory named after
76 the
77 input file and the options you specified. It generates copies of the
78 script
79 after each compiler step, and puts them in that folder.
81 If you use -exp -nodes -bind, the output can get pretty long and
82 confusing.
83 For example, a simple expression like "_x", that refers to a variable
84 _x in
85 the current class, eventually expands to a the following (all on one
86 line):
88 <InternalField MemberReferenceExpression=double
89 Bind=Field:Ns.MyClass._x>
90 <InternalMethod SelfLiteralExpression=Foo.B
91 Bind=Method:Ns.MyClass.MyFn>
92 self</InternalMethod>._x
93 </InternalField>
95 The -short command-line option will use an abbreviated syntax like
96 this:
98 <ItlField MemberRefrExpr=double @F:Ns.MyClass._x>
99 <ItlMethod SelfLiteralExpr=Foo.B @M:Ns.MyClass.MyFn>
100 self</IM>._x
101 </IF>
103 Here's how to understand it. First of all, of course, it's not really
104 XML,
105 it's just an XML-like notation. If you have a text editor that can do
107 syntax highlighting, use it. Second, notice that a reference to "self"
109 been added. Third, the outer tag (InternalField) describes the whole
110 expression, "self._x", whereas the inner tag (InternalMethod) describes
111 only the "self" part. The tags have the following syntax:
113 <E N=T Bind=S:P> where
115 N: Class of AST node. For example, "MemberReferenceExpression" refers
116 to the Boo.Lang.Compiler.Ast.MemberReferenceExpression class.
117 T: The value of node.ExpressionType.ToString(), e.g. int
118 E: Type of entity associated with the node, or "_" if the node has no
119 entity. For example, "InternalField" actually refers to the
120 Boo.Lang.Compiler.TypeSystem.InternalField class. It seems that an
121 entity's main purpose is to hold binding information.
122 S: The entity's EntityType (although I don't actually know what it's
123 for.)
124 If the EntityType is EntityType.Type, which is the most common
125 case,
126 then S is omitted.
127 P: Binding Path. For example, X.Y.Z might represent a variable or
128 method
129 "Z" in class "Y" in namespace "X".
131 A tag is not printed at all if there is no entity nor data type
132 associated with a node. That's why you don't see very many tags during
133 the first compiler steps. The "N=T" part is printed only if the node is
134 an Expression and it has a a known data type; the Bind=S:P part is only
136 printed if binding information is available.
138 static format = "boo" //or "xml" //format for output
139 static foldername = "compilersteps" //folder where files are saved
140 static showents = false //whether to print entity types
141 static showexp = false //show expression types as well
142 static shownodetypes = false
143 static shorten = false
144 static showbindings = false
145 //used internally:
146 static savefolder as string
147 static n = 0
148 static laststep as string
150 //basic boo printer visitor, but adds comments if a node is synthetic (generated
151 //by the compiler instead of the user).
152 class BooSyntheticPrinterVisitor(BooPrinterVisitor):
153 def constructor(writer as TextWriter):
154 super(writer)
155 override def Visit(node as Node) as bool:
156 if node is not null and node.IsSynthetic:
157 WriteIndented("// synthetic")
158 WriteLine()
159 WriteIndented("")
160 return super(node)
162 class BooTypePrinterVisitor(BooPrinterVisitor):
163 _showexp = false
164 _shownodetypes = false
165 _shorten = false
166 _showbindings = false
167 def constructor(writer as TextWriter, show_expressions as bool, shownodetypes as bool, shorten as bool, showbindings as bool):
168 super(writer)
169 _showexp = show_expressions
170 _shownodetypes = shownodetypes
171 _shorten = shorten
172 _showbindings = showbindings
174 override def Visit(node as Node) as bool:
175 return true if node is null
176 if node.IsSynthetic:
177 WriteIndented("// synthetic")
178 WriteLine()
180 WriteIndented() // Automatically indent iff starting a new line
181 tagname = ""
183 entity = TypeSystemServices.GetOptionalEntity(node) // aka node.Entity
184 if entity is not null:
185 tagname = ShortName(entity.GetType())
186 s = "<"
187 s += tagname
188 s += ExtraJunk(node)
189 s += ">"
190 Write(s)
191 if _shorten:
192 tagname = InitialsOf(tagname)
193 elif _showexp or _showbindings:
194 junk = ExtraJunk(node)
195 if junk.Length > 0:
196 tagname = "_"
197 s = "<_${junk}>"
198 Write(s)
200 result = super(node)
201 if tagname != "":
202 WriteIndented("</"+tagname+">")
203 return result
205 def ShortName(t as object):
206 t2 = t.ToString(). \
207 Replace("Boo.Lang.Compiler.TypeSystem.",""). \
208 Replace("Boo.Lang.Compiler.Ast.","")
209 return t2 unless _shorten
210 return t2. \
211 Replace("Expression", "Expr"). \
212 Replace("Reference", "Refr"). \
213 Replace("Internal", "Itl").Replace("External", "Xtl")
215 def InitialsOf(s as string):
216 s2 = System.Text.StringBuilder()
217 for ch in s:
218 if ch >= char('A') and ch <= char('Z'):
219 s2.Append(ch)
220 if s2.Length>0:
221 return s2.ToString()
222 else:
223 return s
225 def ExtraJunk(node as Node):
226 s = System.Text.StringBuilder()
227 if _showexp:
228 exp = node as Expression
229 if exp is not null and exp.ExpressionType is not null:
230 if _shownodetypes:
231 s.Append(" ")
232 s.Append(ShortName(node.GetType()))
233 s.Append("=")
234 elif _shorten:
235 s.Append(":")
236 else:
237 s.Append(" EType=")
238 s.Append(ShortName(exp.ExpressionType.ToString()))
240 if _showbindings:
241 entity = TypeSystemServices.GetOptionalEntity(node) // aka node.Entity
242 if entity is not null:
243 if _shorten:
244 s.Append(" @")
245 else:
246 s.Append(" Bind=")
247 if entity.EntityType != EntityType.Type:
248 if _shorten:
249 s.Append(InitialsOf(entity.EntityType.ToString()))
250 else:
251 s.Append(entity.EntityType.ToString())
252 s.Append(char(':'))
253 s.Append(entity.FullName)
254 return s.ToString()
256 def PrintAST([required]result as CompilerContext, [required]o as TextWriter):
257 astobject = result.CompileUnit
258 try:
259 s = XmlSerializer( astobject.GetType() )
260 s.Serialize( o, astobject )
261 except e:
262 print
263 print e.GetType(), ":", e.Message
265 def AfterStep(sender, e as CompilerStepEventArgs):
267 stepname = e.Step.ToString().Replace("Boo.Lang.Parser.","").Replace("Boo.Lang.Compiler.Steps.","")
269 tempfile = Path.GetTempFileName()
270 using temp = StreamWriter(tempfile):
271 if format == "xml":
272 PrintAST(e.Context, temp)
273 else:
274 try:
275 printer as BooPrinterVisitor
276 if showents:
277 printer = BooTypePrinterVisitor(temp, showexp, shownodetypes, shorten, showbindings)
278 else:
279 printer = BooSyntheticPrinterVisitor(temp)
280 printer.Print(e.Context.CompileUnit)
281 except e:
282 print e.Message + "\n" + e.StackTrace
284 using r = StreamReader(tempfile):
285 thisstep = r.ReadToEnd()
287 filename = string.Format("STEP{0:D2}-{1}.{2}", n, stepname, format)
289 if thisstep != laststep:
290 File.Move(tempfile, Path.Combine(savefolder, filename))
291 laststep = thisstep
292 print string.Format("STEP{0:D2}-{1}: SAVED TO {2} FILE.", n, stepname, format.ToUpper())
293 else:
294 File.Delete(tempfile)
295 print string.Format("STEP{0:D2}-{1}: NO CHANGE TO AST.", n, stepname)
297 def LoadAssembly(assemblyName as string) as Assembly:
298 reference as Assembly
299 if File.Exists(Path.GetFullPath(assemblyName)):
300 reference = Assembly.LoadFrom(Path.GetFullPath(assemblyName))
301 if reference is null:
302 reference = Assembly.LoadWithPartialName(assemblyName)
303 if reference is null:
304 raise ApplicationException(
305 ResourceManager.Format("BooC.UnableToLoadAssembly",
306 assemblyName))
307 return reference
309 ///////////////////////////////////////////////////
311 if len(argv) == 0:
312 print help
313 return
315 compiler = BooCompiler()
317 compiler.Parameters.Pipeline = Compile()
318 compiler.Parameters.Pipeline.AfterStep += AfterStep
320 foldername_base = foldername_extra = ""
322 for arg in argv:
324 if arg[0:3] == "-r:":
325 compiler.Parameters.References.Add(LoadAssembly(arg[3:]))
326 continue
327 elif arg == "-xml":
328 format = "xml"
329 elif arg == "-ent":
330 showents = true
331 elif arg == "-exp":
332 showents = true
333 showexp = true
334 elif arg == "-ducky":
335 compiler.Parameters.Ducky = true
336 elif arg == "-nodes":
337 showents = true
338 showexp = true
339 shownodetypes = true
340 elif arg == "-short":
341 shorten = true
342 elif arg == "-bind":
343 showbindings = true
344 else:
345 compiler.Parameters.Input.Add(FileInput(arg))
346 foldername_base += /^(.*?[\/\\])*([^\\\/]+?)(\.[^.\\\/]*)?$/.Match(arg).Groups[2]
347 continue
348 foldername_extra += " " + arg
350 foldername = foldername_base + foldername_extra
352 //delete old folder if running more than once:
353 if Directory.Exists(foldername):
354 Directory.Delete(foldername, true)
356 savedir = Directory.CreateDirectory(foldername)
357 if savedir is null or not Directory.Exists(foldername):
358 print "The directory '${foldername}' could not be created."
359 return
361 savefolder = savedir.FullName
363 try:
364 print
365 print "See boo/src/Boo.Lang.Compiler/Steps/ for the source code for these steps."
366 print
367 result = compiler.Run()
368 if len(result.Errors) > 0:
369 print "\nThere were ${len(result.Errors)} errors compiling the boo file(s)"
370 print result.Errors.ToString(true)
371 else:
372 print "\nSuccessful: See the files under: '${savefolder}'"
373 except e:
374 print e.GetType(), ":", e.Message