Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Dashboard
Groovy JSR
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<h1>Metadata</h1> <table class="confluenceTable"><tbody> <tr> <td class="confluenceTd"><p> Number: </p></td> <td class="confluenceTd"><p> GEP-2 </p></td> </tr> <tr> <td class="confluenceTd"><p> Title: </p></td> <td class="confluenceTd"><p> AST Builder Support </p></td> </tr> <tr> <td class="confluenceTd"><p> Version: </p></td> <td class="confluenceTd"><p> 7 <br class="atl-forced-newline" /> </p></td> </tr> <tr> <td class="confluenceTd"><p> Type: </p></td> <td class="confluenceTd"><p> Feature </p></td> </tr> <tr> <td class="confluenceTd"><p> Target: </p></td> <td class="confluenceTd"><p> 1.7 </p></td> </tr> <tr> <td class="confluenceTd"><p> Status: </p></td> <td class="confluenceTd"><p> Draft </p></td> </tr> <tr> <td class="confluenceTd"><p> Leader: </p></td> <td class="confluenceTd"><p> <a href="http://docs.codehaus.org/display/~hamletdrc">Hamlet D'Arcy</a><br class="atl-forced-newline" /> </p></td> </tr> <tr> <td class="confluenceTd"><p> Created: </p></td> <td class="confluenceTd"><p> 2009-04-01 </p></td> </tr> <tr> <td class="confluenceTd"><p> Last modification: </p></td> <td class="confluenceTd"><p> 2009-06-17 </p></td> </tr> </tbody></table> <h1>Abstract</h1> <p>Groovy 1.6 introduced the ability to perform local and global AST (Abstract Syntax Tree) transformations, allowing users to read and modify the AST of Groovy code as it is being compiled. Reading information in the AST is relatively easy in Groovy. The core library provides a strongly typed visitor called <a href="http://groovy.codehaus.org/gapi/org/codehaus/groovy/ast/GroovyCodeVisitor.html">GroovyCodeVisitor</a>. Nodes can be read and modified using the API provided through the subtypes of <a href="http://groovy.codehaus.org/gapi/org/codehaus/groovy/ast/ASTNode.html">ASTNode</a>. Writing new AST nodes is not as simple. The AST generated from source is not always obvious, and using constructor calls to generate trees of nodes can be verbose. This GEP proposes an ASTBuilder object that allows users to easily create AST.</p> <p>The ASTBuilder object allows AST to be created from Strings containing Groovy source code, from a closure containing Groovy source, and from a closure containing an AST creation DSL. All three approaches share the same API: a builder object is instantiated and a build* method is invoked.</p> <h1>Approach</h1> <h2>ASTNode from String</h2> <p>The simplest approach to implement is to provide an AST Builder with an API that takes a String and returns List<ASTNode>.</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> def builder = new AstBuilder() List<ASTNode> statements = builder.buildFromString( CompilePhase.CONVERSION, true, """ println "Hello World" """ ) </pre></td></tr></table> <ul> <li>phase parameter tells the builder from which phase to return AST. This parameter is optional, and the default CompilePhase is CLASS_GENERATION. This provides a cleaner API for the common case, and CLASS_GENERATION was chosen because more types are available in later phases.</li> <li>the "statementsOnly" boolean parameter is an optional parameter, and tells the builder to discard the generated top level Script ClassNode. Default is true.</li> <li>The last String parameter is the input</li> <li>The builder returns List<ASTNode></li> </ul> <p>The above example produces the following AST:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> BlockStatement -> ExpressionStatement -> MethodCallExpression -> VariableExpression -> ConstantExpression -> ArgumentListExpression -> ConstantExpression </pre></td></tr></table> <p> <strong>Alternatives</strong></p> <ul> <li>Some sort of AST Template was considered for this feature. Consider the following example:</li> </ul> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> def astTemplate = builder.buildAst ( "println $txt" ).head() def constant = builder.buildAst ( "To be, or not to be: that is the question" ).head() def methodCallExpression = astTemplate.apply(txt: constant) // method call expression not contains println "To be ... " </pre></td></tr></table> <p> This templating approach adds complexity that may not be used. It overloads the GString $ operator, in that it is used here only with objects of type ASTNode but is used normally in GStrings with any Object type at all. Also, the templating approach can create order of precedence confusion. Consider source = "$expr * y", and later $expr is bound to "x+a". The result is "x + a * y", which was probably unintentional. At this time, the AST builder does <strong>not</strong> include such a feature.</p> <h1>ASTNode from Code Block</h1> <p>A useful API would be creating AST from code blocks.</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> AstBuilder builder = new AstBuilder() def statementBlock = builder.buildFromCode (CompilePhase.CONVERSION, true) { println "Hello World" } </pre></td></tr></table> <ul> <li>Expressing Groovy source within Groovy source seems the most natural way to write it (as opposed to putting Groovy source into a String). </li> <li>Some IDE support is naturally available (highlighting, etc), but IDE warnings will be misleading for variable scoping rules</li> <li>Same issues and rules from "ASTNode from String" for phase and statementsOnly properties apply to this version</li> <li>Provides similar API as builder from String, except the code property accepts any block of code that is legal in the context of a Closure.</li> <li>Converting from a closure into AST is performed through a global compiler transformation. This requires that the AstBuilder reference be strongly typed so that the global annotation can be triggered.</li> </ul> <p>The above example produces the following AST:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> BlockStatement -> ExpressionStatement -> MethodCallExpression -> VariableExpression -> ConstantExpression -> ArgumentListExpression -> ConstantExpression </pre></td></tr></table> <p> <strong>Alternatives</strong></p> <ul> <li>If @ASTSource annotation is used, then it would be very easy to let users reuse that annotation outside of the builder. Consider the following example:</li> </ul> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> @AstSource(CompilePhase.CONVERSION) List<ASTNode> source = { println "compiled on: ${new Date()}" } </pre></td></tr></table> <p> This option seems helpful; however, annotations on local variables are not yet supported. This approach will not be implemented.</p> <h2>ASTNode from psuedo-specification</h2> <p>Building AST conditionally, such as inserting an if-statement or looping, is not easily accomplished in the String or code based builders. Consider this example:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> def builder = new AstBuilder() List<ASTNode> statements = builder.buildFromSpec { methodCall { variable "this" constant "println" argumentList { if (locale == "US") constant "Hello" if (locale == "FR") constant "Bonjour" else constant "Ni hao" } } } </pre></td></tr></table> <p>This library class is useful for several reasons:</p> <ul> <li>Using conditionals or looping within an AST Builder will probably be a common occurrence</li> <li>It is difficult to create a Field or Method references in any of the other approaches</li> <li>Simply using the @Newify annotation does not sufficiently improve the syntax</li> <li>This construct alleviates the need to distinguish between Statement and Expressions, since those words are dropped from the method names</li> <li>There is no need for a phase or statementsOnly property in this approach</li> <li>Many expressions take a type ClassNode, which wraps a Class. The syntax for ClassNode is to just pass a Class instance and the builder wraps it in a ClassNode automatically.</li> </ul> <p><strong>Issues</strong></p> <ul> <li>Constructor parameter lists can be lengthy on ASTNode subtypes, and this approach removes the possibility for an IDE to help. This is the price to pay for a builder, and the planned builder metadata feature in 1.7 may alleviate this.</li> <li>The class creating AST from the psuedo-specification should be implemented so that it does not create a mirror-image class heirarchy of the current AST types. This would force all changes to the AST types to be performed in two places: once in the ASTNode subclass and once in this builder. If this is not possible, then at least the AST heirarchy doesn't change frequently.</li> <li>Several ASTNode types have constructor signatures all of the same type: (Expression, Expression, Expression) most commonly. This means the parameters in the DSL are order dependent, and specifying arguments in the wrong order doesn't create an exception but causes drastically different results at runtime. This is fully documented on the <a href="http://www.nabble.com/Several-issues-with-GEP-2-AST-Builder-%22from-specification%22-td23460361.html">mailing list</a>.</li> <li>The syntax for specifying Parameter objects is documented on the <a href="http://www.nabble.com/Several-issues-with-GEP-2-AST-Builder-%22from-specification%22-td23460361.html">mailing list</a>.</li> <li>A few of the ASTNode types having naming conflicts with language keywords. For instance the ClassExpression type cannot be abbreviated to 'class' and IfStatement cannot be reduced to 'if'. This is fully documented on the <a href="http://www.nabble.com/Several-issues-with-GEP-2-AST-Builder-%22from-specification%22-td23460361.html">mailing list</a>.</li> <li>Parameters have default values and can be varargs. A suitable syntax needs to be proposed.</li> <li>Sometimes the order of the constructor parameters needed to be switched within the DSL. For instance, conside SwitchStatement(Expression expression, List<CaseStatement> caseStatements, Expression defaultStatement). The current syntax of the DSL imposes a sort of VarArgs rigidity on the arguments: lists are just implied by repeated elements. So having the middle parameter of SwitchStatement be a list is problematic because the natural way to convert the constructor is to have it become (Expression expression, CaseStatement... caseStatements, Expression default), which isn't possible. This is fully documented on the <a href="http://www.nabble.com/Several-issues-with-GEP-2-AST-Builder-%22from-specification%22-td23460361.html">mailing list</a>.</li> </ul> <h2>Alternatives</h2> <p>Template Haskell and Boo provide a special syntax for AST building statements. Quasi-quote (or Oxford quotes) can be used to trigger an AST building operation:</p> <table class="wysiwyg-macro" data-macro-name="code" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre> ConstantExpression exp = [| "Hello World" |] </pre></td></tr></table> <p> Those languages also supply a splice operator $(...) to turn AST back into code. This is not part of the AstBuilder work.</p> <h2>References</h2> <h2>Mailing-list discussions</h2> <ul> <li><a href="http://www.nabble.com/How-to-write-an-AST-Builder-td22502731.html">[groovy-user] How to write an AST Builder</a></li> <li><a class="confluence-link unresolved" data-content-title="Groovy AST Builder Discussion" data-linked-resource-default-alias="Groovy AST Builder Discussion" href="#">Groovy AST Builder Discussion</a></li> <li><a href="http://www.nabble.com/AST-Transformation-on-local-variables-td23108834.html">[groovy-user] Local Variables Declaration Discussion</a></li> <li>[groovy-dev] <a href="http://www.nabble.com/Several-issues-with-GEP-2-AST-Builder-%22from-specification%22-td23460361.html">Several issues with GEP-2 AST Builder "from specification"</a></li> </ul> <h2>JIRA issues</h2> <ul> <li>This feature is dependent on allowing annotations on local variables - <a href="http://jira.codehaus.org/browse/GROOVY-3481">http://jira.codehaus.org/browse/GROOVY-3481</a></li> </ul> <h2>Useful links</h2> <ul> <li><a href="http://blogs.codehaus.org/people/bamboo/archives/001593_boo_meta_methods.html">Boo Meta Methods</a></li> <li><a class="confluence-link unresolved" data-content-title="Template Haskell in Wikipedia" data-linked-resource-default-alias="Template Haskell in Wikipedia" href="#">Template Haskell in Wikipedia</a></li> <li><a class="confluence-link unresolved" data-content-title="Template Haskell Home Page" data-linked-resource-default-alias="Template Haskell Home Page" href="#">Template Haskell Home Page</a></li> <li><a class="confluence-link unresolved" data-content-title=""First Stab at Template Haskell" Blog Post" data-linked-resource-default-alias="&quot;First Stab at Template Haskell&quot; Blog Post" href="#">"First Stab at Template Haskell" Blog Post</a></li> <li><a href="http://wiki.cython.org/enhancements/metaprogramming">Cython Metaprogramming Proposal</a> by Martin C Martin - Contains nice write up of use cases.</li> </ul> <h1>Reference Implementation</h1> <p><a href="http://code.assembla.com/AstBuilderPrototype">http://code.assembla.com/AstBuilderPrototype</a> (very in-progress)</p> <p>Test Case for AstBuilder from Code: <a href="http://subversion.assembla.com/svn/AstBuilderPrototype/src/test/org/codehaus/groovy/ast/builder/AstFactoryFromCodeTest.groovy">http://subversion.assembla.com/svn/AstBuilderPrototype/src/test/org/codehaus/groovy/ast/builder/AstFactoryFromCodeTest.groovy</a></p> <p>Test Case For AstBuilder from String: <a href="http://subversion.assembla.com/svn/AstBuilderPrototype/src/test/org/codehaus/groovy/ast/builder/AstFactoryFromStringTest.groovy">http://subversion.assembla.com/svn/AstBuilderPrototype/src/test/org/codehaus/groovy/ast/builder/AstFactoryFromStringTest.groovy</a></p> <p>Test Case For AstBuilder from Specification: <a href="http://subversion.assembla.com/svn/AstBuilderPrototype/src/test/org/codehaus/groovy/ast/builder/AstBuilderFromSpecificationTest.groovy">http://subversion.assembla.com/svn/AstBuilderPrototype/src/test/org/codehaus/groovy/ast/builder/AstBuilderFromSpecificationTest.groovy</a><a href="http://subversion.assembla.com/svn/AstBuilderPrototype/tests/org/codehaus/groovy/ast/builder/AstBuilderFromSpecificationTest.groovy">http://subversion.assembla.com/svn/AstBuilderPrototype/tests/org/codehaus/groovy/ast/builder/AstBuilderFromSpecificationTest.groovy</a></p>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced