freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [13/54] [partial] incubator-freemarker git commit: Unifying the o.a.f.core and o.a.f.core.ast
Date Thu, 23 Feb 2017 21:35:40 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 659d7bf..5dd65d9 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -27,7 +27,7 @@ options
 
 PARSER_BEGIN(FMParser)
 
-package org.apache.freemarker.core.ast;
+package org.apache.freemarker.core;
 
 import org.apache.freemarker.core.*;
 import org.apache.freemarker.core.model.*;
@@ -274,8 +274,8 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a String Literal
      */
-    private void notStringLiteral(Expression exp, String expected) throws ParseException {
-        if (exp instanceof StringLiteral) {
+    private void notStringLiteral(ASTExpression exp, String expected) throws ParseException {
+        if (exp instanceof ASTExpStringLiteral) {
             throw new ParseException(
                     "Found string literal: " + exp + ". Expecting: " + expected,
                     exp);
@@ -285,8 +285,8 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a Number Literal
      */
-    private void notNumberLiteral(Expression exp, String expected) throws ParseException {
-        if (exp instanceof NumberLiteral) {
+    private void notNumberLiteral(ASTExpression exp, String expected) throws ParseException {
+        if (exp instanceof ASTExpNumberLiteral) {
             throw new ParseException(
                     "Found number literal: " + exp.getCanonicalForm() + ". Expecting " + expected,
                     exp);
@@ -296,8 +296,8 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a boolean Literal
      */
-    private void notBooleanLiteral(Expression exp, String expected) throws ParseException {
-        if (exp instanceof BooleanLiteral) {
+    private void notBooleanLiteral(ASTExpression exp, String expected) throws ParseException {
+        if (exp instanceof ASTExpBooleanLiteral) {
             throw new ParseException("Found: " + exp.getCanonicalForm() + ". Expecting " + expected, exp);
         }
     }
@@ -305,8 +305,8 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a Hash Literal
      */
-    private void notHashLiteral(Expression exp, String expected) throws ParseException {
-        if (exp instanceof HashLiteral) {
+    private void notHashLiteral(ASTExpression exp, String expected) throws ParseException {
+        if (exp instanceof ASTExpHashLiteral) {
             throw new ParseException(
                     "Found hash literal: " + exp.getCanonicalForm() + ". Expecting " + expected,
                     exp);
@@ -316,10 +316,10 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a List Literal
      */
-    private void notListLiteral(Expression exp, String expected)
+    private void notListLiteral(ASTExpression exp, String expected)
             throws ParseException
     {
-        if (exp instanceof ListLiteral) {
+        if (exp instanceof ASTExpListLiteral) {
             throw new ParseException(
                     "Found list literal: " + exp.getCanonicalForm() + ". Expecting " + expected,
                     exp);
@@ -329,7 +329,7 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a literal other than of the numerical type
      */
-    private void numberLiteralOnly(Expression exp) throws ParseException {
+    private void numberLiteralOnly(ASTExpression exp) throws ParseException {
         notStringLiteral(exp, "number");
         notListLiteral(exp, "number");
         notHashLiteral(exp, "number");
@@ -339,7 +339,7 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is not a string.
      */
-    private void stringLiteralOnly(Expression exp) throws ParseException {
+    private void stringLiteralOnly(ASTExpression exp) throws ParseException {
         notNumberLiteral(exp, "string");
         notListLiteral(exp, "string");
         notHashLiteral(exp, "string");
@@ -349,22 +349,22 @@ public class FMParser {
     /**
      * Throw an exception if the expression passed in is a literal other than of the boolean type
      */
-    private void booleanLiteralOnly(Expression exp) throws ParseException {
+    private void booleanLiteralOnly(ASTExpression exp) throws ParseException {
         notStringLiteral(exp, "boolean (true/false)");
         notListLiteral(exp, "boolean (true/false)");
         notHashLiteral(exp, "boolean (true/false)");
         notNumberLiteral(exp, "boolean (true/false)");
     }
 
-    private Expression escapedExpression(Expression exp) {
+    private ASTExpression escapedExpression(ASTExpression exp) {
         if (!escapes.isEmpty()) {
-            return ((EscapeBlock) escapes.getFirst()).doEscape(exp);
+            return ((ASTDirEscape) escapes.getFirst()).doEscape(exp);
         } else {
             return exp;
         }
     }
 
-    private boolean getBoolean(Expression exp, boolean legacyCompat) throws ParseException {
+    private boolean getBoolean(ASTExpression exp, boolean legacyCompat) throws ParseException {
         TemplateModel tm = null;
         try {
             tm = exp.eval(null);
@@ -418,7 +418,7 @@ public class FMParser {
         return size != 0 ? (ParserIteratorBlockContext) iteratorBlockContexts.get(size - 1) : null; 
     }
     
-    private void checkLoopVariableBuiltInLHO(String loopVarName, Expression lhoExp, Token biName)
+    private void checkLoopVariableBuiltInLHO(String loopVarName, ASTExpression lhoExp, Token biName)
             throws ParseException {
         int size = iteratorBlockContexts != null ? iteratorBlockContexts.size() : 0;
         for (int i = size - 1; i >= 0; i--) {
@@ -1423,16 +1423,16 @@ TOKEN:
 // expression syntax.
 
 /**
- * This is the same as OrExpression, since
- * the OR is the operator with the lowest
+ * This is the same as ASTExpOr, since
+ * OR is the operator with the lowest
  * precedence.
  */
-Expression Expression() :
+ASTExpression ASTExpression() :
 {
-    Expression exp;
+    ASTExpression exp;
 }
 {
-    exp = OrExpression()
+    exp = ASTExpOr()
     {
         return exp;
     }
@@ -1443,27 +1443,27 @@ Expression Expression() :
  * or a possibly more complex expression bounded
  * by parentheses.
  */
-Expression PrimaryExpression() :
+ASTExpression PrimaryExpression() :
 {
-    Expression exp;
+    ASTExpression exp;
 }
 {
     (
-        exp = NumberLiteral()
+        exp = ASTExpNumberLiteral()
         |   
-        exp = HashLiteral()
+        exp = ASTExpHashLiteral()
         |   
-        exp = StringLiteral(true)
+        exp = ASTExpStringLiteral(true)
         |   
-        exp = BooleanLiteral()
+        exp = ASTExpBooleanLiteral()
         |   
-        exp = ListLiteral()
+        exp = ASTExpListLiteral()
         |   
-        exp = Identifier()
+        exp = ASTExpVariable()
         |   
         exp = Parenthesis()
         |   
-        exp = BuiltinVariable()
+        exp = ASTExpBuiltInVariable()
     )
     (
         LOOKAHEAD(<DOT> | <OPEN_BRACKET> |<OPEN_PAREN> | <BUILT_IN> | <EXCLAM> | <TERMINATING_EXCLAM> | <EXISTS>)
@@ -1474,17 +1474,17 @@ Expression PrimaryExpression() :
     }
 }
 
-Expression Parenthesis() :
+ASTExpression Parenthesis() :
 {
-    Expression exp, result;
+    ASTExpression exp, result;
     Token start, end;
 }
 {
     start = <OPEN_PAREN>
-    exp = Expression()
+    exp = ASTExpression()
     end = <CLOSE_PAREN>
     {
-        result = new ParentheticalExpression(exp);
+        result = new ASTExpParenthesis(exp);
         result.setLocation(template, start, end);
         return result;
     }
@@ -1495,17 +1495,17 @@ Expression Parenthesis() :
  * more unary operators. (The only unary operator we
  * currently have is the NOT.)
  */
-Expression UnaryExpression() :
+ASTExpression UnaryExpression() :
 {
-    Expression exp, result;
+    ASTExpression exp, result;
     boolean haveNot = false;
     Token t = null, start = null;
 }
 {
     (
-        result = UnaryPlusMinusExpression()
+        result = ASTExpNegateOrPlus()
         |
-        result = NotExpression()
+        result = ASTExpNot()
         |
         result = PrimaryExpression()
     )
@@ -1514,10 +1514,10 @@ Expression UnaryExpression() :
     }
 }
 
-Expression NotExpression() : 
+ASTExpression ASTExpNot() : 
 {
     Token t;
-    Expression exp, result = null;
+    ASTExpression exp, result = null;
     ArrayList nots = new ArrayList();
 }
 {
@@ -1527,7 +1527,7 @@ Expression NotExpression() :
     exp = PrimaryExpression()
     {
         for (int i = 0; i < nots.size(); i++) {
-            result = new NotExpression(exp);
+            result = new ASTExpNot(exp);
             Token tok = (Token) nots.get(nots.size() -i -1);
             result.setLocation(template, tok, exp);
             exp = result;
@@ -1536,9 +1536,9 @@ Expression NotExpression() :
     }
 }
 
-Expression UnaryPlusMinusExpression() :
+ASTExpression ASTExpNegateOrPlus() :
 {
-    Expression exp, result;
+    ASTExpression exp, result;
     boolean isMinus = false;
     Token t;
 }
@@ -1550,15 +1550,15 @@ Expression UnaryPlusMinusExpression() :
     )
     exp = PrimaryExpression()
     {
-        result = new UnaryPlusMinusExpression(exp, isMinus);  
+        result = new ASTExpNegateOrPlus(exp, isMinus);  
         result.setLocation(template, t, exp);
         return result;
     }
 }
 
-Expression AdditiveExpression() :
+ASTExpression AdditiveExpression() :
 {
-    Expression lhs, rhs, result;
+    ASTExpression lhs, rhs, result;
     boolean plus;
 }
 {
@@ -1577,7 +1577,7 @@ Expression AdditiveExpression() :
             if (plus) {
 	            // plus is treated separately, since it is also
 	            // used for concatenation.
-                result = new AddConcatExpression(lhs, rhs);
+                result = new ASTExpAddOrConcat(lhs, rhs);
             } else {
                 numberLiteralOnly(lhs);
                 numberLiteralOnly(rhs);
@@ -1596,9 +1596,9 @@ Expression AdditiveExpression() :
  * A unary expression followed by zero or more
  * unary expressions with operators in between.
  */
-Expression MultiplicativeExpression() :
+ASTExpression MultiplicativeExpression() :
 {
-    Expression lhs, rhs, result;
+    ASTExpression lhs, rhs, result;
     int operation = ArithmeticExpression.TYPE_MULTIPLICATION;
 }
 {
@@ -1629,9 +1629,9 @@ Expression MultiplicativeExpression() :
 }
 
 
-Expression EqualityExpression() :
+ASTExpression EqualityExpression() :
 {
-    Expression lhs, rhs, result;
+    ASTExpression lhs, rhs, result;
     Token t;
 }
 {
@@ -1651,7 +1651,7 @@ Expression EqualityExpression() :
 	        notHashLiteral(rhs, "scalar");
 	        notListLiteral(lhs, "scalar");
 	        notListLiteral(rhs, "scalar");
-	        result = new ComparisonExpression(lhs, rhs, t.image);
+	        result = new ASTExpComparison(lhs, rhs, t.image);
 	        result.setLocation(template, lhs, rhs);
         }
     ]
@@ -1660,9 +1660,9 @@ Expression EqualityExpression() :
     }
 }
 
-Expression RelationalExpression() :
+ASTExpression RelationalExpression() :
 {
-    Expression lhs, rhs, result;
+    ASTExpression lhs, rhs, result;
     Token t;
 }
 {
@@ -1690,7 +1690,7 @@ Expression RelationalExpression() :
             notListLiteral(rhs, "scalar");
             notStringLiteral(lhs, "number");
             notStringLiteral(rhs, "number");
-            result = new ComparisonExpression(lhs, rhs, t.image);
+            result = new ASTExpComparison(lhs, rhs, t.image);
             result.setLocation(template, lhs, rhs);
         }
     ]
@@ -1699,9 +1699,9 @@ Expression RelationalExpression() :
     }
 }
 
-Expression RangeExpression() :
+ASTExpression RangeExpression() :
 {
-    Expression lhs, rhs = null, result;
+    ASTExpression lhs, rhs = null, result;
     int endType;
     Token dotDot = null;
 }
@@ -1712,20 +1712,20 @@ Expression RangeExpression() :
         (
             (
                 (
-                    <DOT_DOT_LESS> { endType = Range.END_EXCLUSIVE; }
+                    <DOT_DOT_LESS> { endType = ASTExpRange.END_EXCLUSIVE; }
                     |
-                    <DOT_DOT_ASTERISK> { endType = Range.END_SIZE_LIMITED; }
+                    <DOT_DOT_ASTERISK> { endType = ASTExpRange.END_SIZE_LIMITED; }
                 )
                 rhs = AdditiveExpression()
             )
             | 
             (
-                dotDot = <DOT_DOT> { endType = Range.END_UNBOUND; }
+                dotDot = <DOT_DOT> { endType = ASTExpRange.END_UNBOUND; }
                 [
                     LOOKAHEAD(AdditiveExpression())
                     rhs = AdditiveExpression()
                     {
-                        endType = Range.END_INCLUSIVE;
+                        endType = ASTExpRange.END_INCLUSIVE;
                     }
                 ]
             )
@@ -1736,7 +1736,7 @@ Expression RangeExpression() :
                 numberLiteralOnly(rhs);
             }
            
-            Range range = new Range(lhs, rhs, endType);
+            ASTExpRange range = new ASTExpRange(lhs, rhs, endType);
             if (rhs != null) {
                 range.setLocation(template, lhs, rhs);
             } else {
@@ -1753,9 +1753,9 @@ Expression RangeExpression() :
 
 
 
-Expression AndExpression() :
+ASTExpression ASTExpAnd() :
 {
-    Expression lhs, rhs, result;
+    ASTExpression lhs, rhs, result;
 }
 {
     lhs = EqualityExpression() { result = lhs; }
@@ -1766,7 +1766,7 @@ Expression AndExpression() :
         {
             booleanLiteralOnly(lhs);
             booleanLiteralOnly(rhs);
-            result = new AndExpression(lhs, rhs);
+            result = new ASTExpAnd(lhs, rhs);
             result.setLocation(template, lhs, rhs);
             lhs = result;
         }
@@ -1776,20 +1776,20 @@ Expression AndExpression() :
     }
 }
 
-Expression OrExpression() :
+ASTExpression ASTExpOr() :
 {
-    Expression lhs, rhs, result;
+    ASTExpression lhs, rhs, result;
 }
 {
-    lhs = AndExpression() { result = lhs; }
+    lhs = ASTExpAnd() { result = lhs; }
     (
         LOOKAHEAD(<OR>)
         <OR>
-        rhs = AndExpression()
+        rhs = ASTExpAnd()
         {
             booleanLiteralOnly(lhs);
             booleanLiteralOnly(rhs);
-            result = new OrExpression(lhs, rhs);
+            result = new ASTExpOr(lhs, rhs);
             result.setLocation(template, lhs, rhs);
             lhs = result;
         }
@@ -1799,7 +1799,7 @@ Expression OrExpression() :
     }
 }
 
-ListLiteral ListLiteral() :
+ASTExpListLiteral ASTExpListLiteral() :
 {
     ArrayList values = new ArrayList();
     Token begin, end;
@@ -1809,13 +1809,13 @@ ListLiteral ListLiteral() :
     values = PositionalArgs()
     end = <CLOSE_BRACKET>
     {
-        ListLiteral result = new ListLiteral(values);
+        ASTExpListLiteral result = new ASTExpListLiteral(values);
         result.setLocation(template, begin, end);
         return result;
     }
 }
 
-Expression NumberLiteral() :
+ASTExpression ASTExpNumberLiteral() :
 {
     Token op = null, t;
 }
@@ -1827,42 +1827,42 @@ Expression NumberLiteral() :
     )
     {
         String s = t.image;
-        Expression result = new NumberLiteral(pCfg.getArithmeticEngine().toNumber(s));
+        ASTExpression result = new ASTExpNumberLiteral(pCfg.getArithmeticEngine().toNumber(s));
         Token startToken = (op != null) ? op : t;
         result.setLocation(template, startToken, t);
         return result;
     }
 }
 
-Identifier Identifier() :
+ASTExpVariable ASTExpVariable() :
 {
     Token t;
 }
 {
     t = <ID>
     {
-        Identifier id = new Identifier(t.image);
+        ASTExpVariable id = new ASTExpVariable(t.image);
         id.setLocation(template, t, t);
         return id;
     }
 }
 
-Expression IdentifierOrStringLiteral() :
+ASTExpression IdentifierOrStringLiteral() :
 {
-    Expression exp;
+    ASTExpression exp;
 }
 {
     (
-        exp = Identifier()
+        exp = ASTExpVariable()
         |
-        exp = StringLiteral(false)
+        exp = ASTExpStringLiteral(false)
     )
     {
         return exp;
     }   
 }
 
-BuiltinVariable BuiltinVariable() :
+ASTExpBuiltInVariable ASTExpBuiltInVariable() :
 {
     Token dot, name;
 }
@@ -1870,18 +1870,20 @@ BuiltinVariable BuiltinVariable() :
     dot = <DOT>
     name = <ID>
     {
-        BuiltinVariable result = null;
+        ASTExpBuiltInVariable result = null;
         token_source.checkNamingConvention(name);
 
         TemplateModel parseTimeValue;
         String nameStr = name.image;
-        if (nameStr.equals(BuiltinVariable.OUTPUT_FORMAT) || nameStr.equals(BuiltinVariable.OUTPUT_FORMAT_CC)) {
+        if (nameStr.equals(ASTExpBuiltInVariable.OUTPUT_FORMAT) || nameStr.equals(ASTExpBuiltInVariable.OUTPUT_FORMAT_CC)) {
             parseTimeValue = new SimpleScalar(outputFormat.getName());
-        } else if (nameStr.equals(BuiltinVariable.AUTO_ESC) || nameStr.equals(BuiltinVariable.AUTO_ESC_CC)) {
+        } else if (nameStr.equals(ASTExpBuiltInVariable.AUTO_ESC) || nameStr.equals(ASTExpBuiltInVariable.AUTO_ESC_CC)) {
             parseTimeValue = autoEscaping ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
-        } else {            parseTimeValue = null;
-        }        
-        result = new BuiltinVariable(name, token_source, parseTimeValue);
+        } else {
+            parseTimeValue = null;
+        }
+        
+        result = new ASTExpBuiltInVariable(name, token_source, parseTimeValue);
         
         result.setLocation(template, dot, name);
         return result;
@@ -1893,9 +1895,9 @@ BuiltinVariable BuiltinVariable() :
  * using the dot or dynamic key name
  * or the args list if this is a method invocation.
  */
-Expression AddSubExpression(Expression exp) :
+ASTExpression AddSubExpression(ASTExpression exp) :
 {
-    Expression result = null;
+    ASTExpression result = null;
 }
 {
     (
@@ -1905,7 +1907,7 @@ Expression AddSubExpression(Expression exp) :
         |
         result = MethodArgs(exp)
         |
-        result = BuiltIn(exp)
+        result = ASTExpBuiltIn(exp)
         |
         result = DefaultTo(exp)
         |
@@ -1916,9 +1918,9 @@ Expression AddSubExpression(Expression exp) :
     }
 }
 
-Expression DefaultTo(Expression exp) :
+ASTExpression DefaultTo(ASTExpression exp) :
 {
-    Expression rhs = null;
+    ASTExpression rhs = null;
     Token t;
 }
 {
@@ -1928,13 +1930,13 @@ Expression DefaultTo(Expression exp) :
         (
             t = <EXCLAM>
             [
-                LOOKAHEAD(Expression())
-                rhs = Expression()
+                LOOKAHEAD(ASTExpression())
+                rhs = ASTExpression()
             ]
         )
     )
     {
-        DefaultToExpression result = new DefaultToExpression(exp, rhs);
+        ASTExpDefault result = new ASTExpDefault(exp, rhs);
         if (rhs == null) {
             result.setLocation(template, exp, t);
         } else {
@@ -1944,24 +1946,24 @@ Expression DefaultTo(Expression exp) :
     }
 }
 
-Expression Exists(Expression exp) :
+ASTExpression Exists(ASTExpression exp) :
 {
     Token t;
 }
 {
     t = <EXISTS>
     {
-        ExistsExpression result = new ExistsExpression(exp);
+        ASTExpExists result = new ASTExpExists(exp);
         result.setLocation(template, exp, t);
         return result;
     }
 }
 
-Expression BuiltIn(Expression lhoExp) :
+ASTExpression ASTExpBuiltIn(ASTExpression lhoExp) :
 {
     Token t = null;
-    BuiltIn result;
-    ArrayList/*<Expression>*/ args = null;
+    ASTExpBuiltIn result;
+    ArrayList/*<ASTExpression>*/ args = null;
     Token openParen;
     Token closeParen;
 }
@@ -1970,7 +1972,7 @@ Expression BuiltIn(Expression lhoExp) :
     t = <ID>
     {
         token_source.checkNamingConvention(t);
-        result = BuiltIn.newBuiltIn(incompatibleImprovements, lhoExp, t, token_source);
+        result = ASTExpBuiltIn.newBuiltIn(incompatibleImprovements, lhoExp, t, token_source);
         result.setLocation(template, lhoExp, t);
         
         if (!(result instanceof SpecialBuiltIn)) {
@@ -1978,12 +1980,12 @@ Expression BuiltIn(Expression lhoExp) :
         }
 
         if (result instanceof BuiltInForLoopVariable) {
-            if (!(lhoExp instanceof Identifier)) {
+            if (!(lhoExp instanceof ASTExpVariable)) {
                 throw new ParseException(
                         "Expression used as the left hand operand of ?" + t.image
                         + " must be a simple loop variable name.", lhoExp);
             }
-            String loopVarName = ((Identifier) lhoExp).getName();
+            String loopVarName = ((ASTExpVariable) lhoExp).getName();
             checkLoopVariableBuiltInLHO(loopVarName, lhoExp, t);
             ((BuiltInForLoopVariable) result).bindToLoopVariable(loopVarName);
             
@@ -2039,7 +2041,7 @@ Expression BuiltIn(Expression lhoExp) :
 /**
  * production for when a key is specified by <DOT> + keyname
  */
-Expression DotVariable(Expression exp) :
+ASTExpression DotVariable(ASTExpression exp) :
 {
     Token t;
 }
@@ -2077,7 +2079,7 @@ Expression DotVariable(Expression exp) :
             notListLiteral(exp, "hash");
             notStringLiteral(exp, "hash");
             notBooleanLiteral(exp, "hash");
-            Dot dot = new Dot(exp, t.image);
+            ASTExpDot dot = new ASTExpDot(exp, t.image);
             dot.setLocation(template, exp, t);
             return dot;
         }
@@ -2087,19 +2089,19 @@ Expression DotVariable(Expression exp) :
  * production for when the key is specified
  * in brackets.
  */
-Expression DynamicKey(Expression exp) :
+ASTExpression DynamicKey(ASTExpression exp) :
 {
-    Expression arg;
+    ASTExpression arg;
     Token t;
 }
 {
     <OPEN_BRACKET>
-    arg = Expression()
+    arg = ASTExpression()
     t = <CLOSE_BRACKET>
     {
         notBooleanLiteral(exp, "list or hash");
         notNumberLiteral(exp, "list or hash");
-        DynamicKeyName dkn = new DynamicKeyName(exp, arg);
+        ASTExpDynamicKeyName dkn = new ASTExpDynamicKeyName(exp, arg);
         dkn.setLocation(template, exp, t);
         return dkn;
     }
@@ -2108,7 +2110,7 @@ Expression DynamicKey(Expression exp) :
 /**
  * production for an arglist part of a method invocation.
  */
-MethodCall MethodArgs(Expression exp) :
+ASTExpMethodCall MethodArgs(ASTExpression exp) :
 {
         ArrayList args = new ArrayList();
         Token end;
@@ -2119,13 +2121,13 @@ MethodCall MethodArgs(Expression exp) :
         end = <CLOSE_PAREN>
         {
             args.trimToSize();
-            MethodCall result = new MethodCall(exp, args);
+            ASTExpMethodCall result = new ASTExpMethodCall(exp, args);
             result.setLocation(template, exp, end);
             return result;
         }
 }
 
-StringLiteral StringLiteral(boolean interpolate) :
+ASTExpStringLiteral ASTExpStringLiteral(boolean interpolate) :
 {
     Token t;
     boolean raw = false;
@@ -2148,7 +2150,7 @@ StringLiteral StringLiteral(boolean interpolate) :
                 throw new ParseException(e.getMessage(), template, t);
             }
         }
-        StringLiteral result = new StringLiteral(s);
+        ASTExpStringLiteral result = new ASTExpStringLiteral(s);
         result.setLocation(template, t, t);
         if (interpolate && !raw) {
             // TODO: This logic is broken. It can't handle literals that contains both ${...} and $\{...}. 
@@ -2158,16 +2160,16 @@ StringLiteral StringLiteral(boolean interpolate) :
     }
 }
 
-Expression BooleanLiteral() :
+ASTExpression ASTExpBooleanLiteral() :
 {
     Token t;
-    Expression result;
+    ASTExpression result;
 }
 {
     (
-        t = <FALSE> { result = new BooleanLiteral(false); }
+        t = <FALSE> { result = new ASTExpBooleanLiteral(false); }
         |
-        t = <TRUE> { result = new BooleanLiteral(true); }
+        t = <TRUE> { result = new ASTExpBooleanLiteral(true); }
     )
     {
         result.setLocation(template, t, t);
@@ -2176,19 +2178,19 @@ Expression BooleanLiteral() :
 }
 
 
-HashLiteral HashLiteral() :
+ASTExpHashLiteral ASTExpHashLiteral() :
 {
     Token begin, end;
-    Expression key, value;
+    ASTExpression key, value;
     ArrayList keys = new ArrayList();
     ArrayList values = new ArrayList();
 }
 {
     begin = <OPENING_CURLY_BRACKET>
     [
-        key = Expression()
+        key = ASTExpression()
         (<COMMA>|<COLON>)
-        value = Expression()
+        value = ASTExpression()
         {
             stringLiteralOnly(key);
             keys.add(key);
@@ -2196,9 +2198,9 @@ HashLiteral HashLiteral() :
         }
         (
             <COMMA>
-            key = Expression()
+            key = ASTExpression()
             (<COMMA>|<COLON>)
-            value = Expression()
+            value = ASTExpression()
             {
                 stringLiteralOnly(key);
                 keys.add(key);
@@ -2208,7 +2210,7 @@ HashLiteral HashLiteral() :
     ]
     end = <CLOSING_CURLY_BRACKET>
     {
-        HashLiteral result = new HashLiteral(keys, values);
+        ASTExpHashLiteral result = new ASTExpHashLiteral(keys, values);
         result.setLocation(template, begin, end);
         return result;
     }
@@ -2218,21 +2220,21 @@ HashLiteral HashLiteral() :
  * A production representing the ${...}
  * that outputs a variable.
  */
-DollarVariable StringOutput() :
+ASTDollarInterpolation StringOutput() :
 {
-    Expression exp;
+    ASTExpression exp;
     Token begin, end;
 }
 {
     begin = <DOLLAR_INTERPOLATION_OPENING>
-    exp = Expression()
+    exp = ASTExpression()
     {
         notHashLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
         notListLiteral(exp, NonStringException.STRING_COERCABLE_TYPES_DESC);
     }
     end = <CLOSING_CURLY_BRACKET>
     {
-        DollarVariable result = new DollarVariable(
+        ASTDollarInterpolation result = new ASTDollarInterpolation(
                 exp, escapedExpression(exp),
                 outputFormat,
                 autoEscaping);
@@ -2241,14 +2243,14 @@ DollarVariable StringOutput() :
     }
 }
 
-NumericalOutput NumericalOutput() :
+ASTHashInterpolation ASTHashInterpolation() :
 {
-    Expression exp;
+    ASTExpression exp;
     Token fmt = null, begin, end;
 }
 {
     begin = <HASH_INTERPOLATION_OPENING>
-    exp = Expression() { numberLiteralOnly(exp); }
+    exp = ASTExpression() { numberLiteralOnly(exp); }
     [
         <SEMICOLON>
         fmt = <ID>
@@ -2258,7 +2260,7 @@ NumericalOutput NumericalOutput() :
         MarkupOutputFormat<?> autoEscOF = autoEscaping && outputFormat instanceof MarkupOutputFormat
                 ? (MarkupOutputFormat<?>) outputFormat : null;
     
-        NumericalOutput result;
+        ASTHashInterpolation result;
         if (fmt != null) {
             int minFrac = -1;  // -1 indicates that the value has not been set
             int maxFrac = -1;
@@ -2312,40 +2314,40 @@ NumericalOutput NumericalOutput() :
             if (minFrac > 50 || maxFrac > 50) {// sanity check
                 throw new ParseException("Cannot specify more than 50 fraction digits", template, fmt);
             }
-            result = new NumericalOutput(exp, minFrac, maxFrac, autoEscOF);
+            result = new ASTHashInterpolation(exp, minFrac, maxFrac, autoEscOF);
         } else {  // if format != null
-            result = new NumericalOutput(exp, autoEscOF);
+            result = new ASTHashInterpolation(exp, autoEscOF);
         }
         result.setLocation(template, begin, end);
         return result;
     }
 }
 
-TemplateElement If() :
+_ASTElement If() :
 {
     Token start, end, t;
-    Expression condition;
+    ASTExpression condition;
     TemplateElements children;
-    IfBlock ifBlock;
-    ConditionalBlock cblock;
+    ASTDirIfElseIfElseContainer ifBlock;
+    ASTDirIfOrElseOrElseIf cblock;
 }
 {
     start = <IF>
-    condition = Expression()
+    condition = ASTExpression()
     end = <DIRECTIVE_END>
     children = MixedContentElements()
     {
-        cblock = new ConditionalBlock(condition, children, ConditionalBlock.TYPE_IF);
+        cblock = new ASTDirIfOrElseOrElseIf(condition, children, ASTDirIfOrElseOrElseIf.TYPE_IF);
         cblock.setLocation(template, start, end, children);
-        ifBlock = new IfBlock(cblock);
+        ifBlock = new ASTDirIfElseIfElseContainer(cblock);
     }
     (
         t = <ELSE_IF>
-        condition = Expression()
+        condition = ASTExpression()
         end = LooseDirectiveEnd()
         children = MixedContentElements()
         {
-            cblock = new ConditionalBlock(condition, children, ConditionalBlock.TYPE_ELSE_IF);
+            cblock = new ASTDirIfOrElseOrElseIf(condition, children, ASTDirIfOrElseOrElseIf.TYPE_ELSE_IF);
             cblock.setLocation(template, t, end, children);
             ifBlock.addBlock(cblock);
         }
@@ -2354,7 +2356,7 @@ TemplateElement If() :
             t = <ELSE>
             children = MixedContentElements()
             {
-                cblock = new ConditionalBlock(null, children, ConditionalBlock.TYPE_ELSE);
+                cblock = new ASTDirIfOrElseOrElseIf(null, children, ASTDirIfOrElseOrElseIf.TYPE_ELSE);
                 cblock.setLocation(template, t, t, children);
                 ifBlock.addBlock(cblock);
             }
@@ -2366,11 +2368,11 @@ TemplateElement If() :
     }
 }
 
-AttemptBlock Attempt() :
+ASTDirAttemptRecoverContainer Attempt() :
 {
     Token start, end;
     TemplateElements children;
-    RecoveryBlock recoveryBlock;
+    ASTDirRecover recoveryBlock;
 }
 {
     start = <ATTEMPT>
@@ -2382,13 +2384,13 @@ AttemptBlock Attempt() :
         end = <END_ATTEMPT>
     )
     {
-        AttemptBlock result = new AttemptBlock(children, recoveryBlock);
+        ASTDirAttemptRecoverContainer result = new ASTDirAttemptRecoverContainer(children, recoveryBlock);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-RecoveryBlock Recover() : 
+ASTDirRecover Recover() : 
 {
     Token start;
     TemplateElements children;
@@ -2397,23 +2399,23 @@ RecoveryBlock Recover() :
     start = <RECOVER>
     children = MixedContentElements()
     {
-        RecoveryBlock result = new RecoveryBlock(children);
+        ASTDirRecover result = new ASTDirRecover(children);
         result.setLocation(template, start, start, children);
         return result;
     }
 }
 
-TemplateElement List() :
+_ASTElement List() :
 {
-    Expression exp;
+    ASTExpression exp;
     Token loopVar = null, loopVar2 = null, start, end;
     TemplateElements childrendBeforeElse;
-    ElseOfList elseOfList = null;
+    ASTDirElseOfList elseOfList = null;
     ParserIteratorBlockContext iterCtx;
 }
 {
     start = <LIST>
-    exp = Expression()
+    exp = ASTExpression()
     [
         <AS>
         loopVar = <ID>
@@ -2453,30 +2455,30 @@ TemplateElement List() :
     }
     
     [
-        elseOfList = ElseOfList()
+        elseOfList = ASTDirElseOfList()
     ]
     
     end = <END_LIST>
     {
-        IteratorBlock list = new IteratorBlock(
+        ASTDirList list = new ASTDirList(
                 exp,
                 loopVar != null ? loopVar.image : null,  // null when we have a nested #items
                 loopVar2 != null ? loopVar2.image : null,
                 childrendBeforeElse, iterCtx.hashListing, false);
         list.setLocation(template, start, end);
 
-        TemplateElement result;
+        _ASTElement result;
         if (elseOfList == null) {
             result = list;
         } else {
-            result = new ListElseContainer(list, elseOfList);
+            result = new ASTDirListElseContainer(list, elseOfList);
             result.setLocation(template, start, end);
         }
         return result;
     }
 }
 
-ElseOfList ElseOfList() :
+ASTDirElseOfList ASTDirElseOfList() :
 {
     Token start;
     TemplateElements children;
@@ -2485,15 +2487,15 @@ ElseOfList ElseOfList() :
         start = <ELSE>
         children = MixedContentElements()
         {
-            ElseOfList result = new ElseOfList(children);
+            ASTDirElseOfList result = new ASTDirElseOfList(children);
 	        result.setLocation(template, start, start, children);
 	        return result;
         }
 }
 
-IteratorBlock ForEach() :
+ASTDirList ForEach() :
 {
-    Expression exp;
+    ASTExpression exp;
     Token loopVar, start, end;
     TemplateElements children;
 }
@@ -2501,7 +2503,7 @@ IteratorBlock ForEach() :
     start = <FOREACH>
     loopVar = <ID>
     <IN>
-    exp = Expression()
+    exp = ASTExpression()
     <DIRECTIVE_END>
     {
         ParserIteratorBlockContext iterCtx = pushIteratorBlockContext();
@@ -2517,13 +2519,13 @@ IteratorBlock ForEach() :
         breakableDirectiveNesting--;
         popIteratorBlockContext();
                 
-        IteratorBlock result = new IteratorBlock(exp, loopVar.image, null, children, false, true);
+        ASTDirList result = new ASTDirList(exp, loopVar.image, null, children, false, true);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-Items Items() :
+ASTDirItems ASTDirItems() :
 {
     Token loopVar, loopVar2 = null, start, end;
     TemplateElements children;
@@ -2576,13 +2578,13 @@ Items Items() :
         iterCtx.loopVarName = null;
         iterCtx.loopVar2Name = null;
         
-        Items result = new Items(loopVar.image, loopVar2 != null ? loopVar2.image : null, children);
+        ASTDirItems result = new ASTDirItems(loopVar.image, loopVar2 != null ? loopVar2.image : null, children);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-Sep Sep() :
+ASTDirSep ASTDirSep() :
 {
     Token loopVar, start, end = null;
     TemplateElements children;
@@ -2602,7 +2604,7 @@ Sep Sep() :
         end = <END_SEP>
     ]
     {
-        Sep result = new Sep(children);
+        ASTDirSep result = new ASTDirSep(children);
         if (end != null) {
             result.setLocation(template, start, end);
         } else {
@@ -2612,30 +2614,30 @@ Sep Sep() :
     }
 }
 
-VisitNode Visit() :
+ASTDirVisit Visit() :
 {
     Token start, end;
-    Expression targetNode, namespaces = null;
+    ASTExpression targetNode, namespaces = null;
 }
 {
     start = <VISIT>
-    targetNode = Expression()
+    targetNode = ASTExpression()
     [
         <USING>
-        namespaces = Expression()
+        namespaces = ASTExpression()
     ]
     end = LooseDirectiveEnd()
     {
-        VisitNode result = new VisitNode(targetNode, namespaces);
+        ASTDirVisit result = new ASTDirVisit(targetNode, namespaces);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-RecurseNode Recurse() :
+ASTDirRecurse Recurse() :
 {
     Token start, end = null;
-    Expression node = null, namespaces = null;
+    ASTExpression node = null, namespaces = null;
 }
 {
     (
@@ -2644,24 +2646,24 @@ RecurseNode Recurse() :
         (
             start = <RECURSE>
             [
-                node = Expression()
+                node = ASTExpression()
             ]
             [
                 <USING>
-                namespaces = Expression()
+                namespaces = ASTExpression()
             ]
             end = LooseDirectiveEnd()
         )
     )
     {
         if (end == null) end = start;
-        RecurseNode result = new RecurseNode(node, namespaces);
+        ASTDirRecurse result = new ASTDirRecurse(node, namespaces);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-FallbackInstruction FallBack() :
+ASTDirFallback FallBack() :
 {
     Token tok;
 }
@@ -2671,7 +2673,7 @@ FallbackInstruction FallBack() :
         if (!inMacro) {
             throw new ParseException("Cannot fall back outside a macro.", template, tok);
         }
-        FallbackInstruction result = new FallbackInstruction();
+        ASTDirFallback result = new ASTDirFallback();
         result.setLocation(template, tok, tok);
         return result;
     }
@@ -2680,7 +2682,7 @@ FallbackInstruction FallBack() :
 /**
  * Production used to break out of a loop or a switch block.
  */
-BreakInstruction Break() :
+ASTDirBreak Break() :
 {
     Token start;
 }
@@ -2692,7 +2694,7 @@ BreakInstruction Break() :
                     + " #list with \"as\", #items, #switch (or the deprecated " + forEachDirectiveSymbol() + ")",
                     template, start);
         }
-        BreakInstruction result = new BreakInstruction();
+        ASTDirBreak result = new ASTDirBreak();
         result.setLocation(template, start, start);
         return result;
     }
@@ -2702,16 +2704,16 @@ BreakInstruction Break() :
  * Production used to jump out of a macro.
  * The stop instruction terminates the rendering of the template.
  */
-ReturnInstruction Return() :
+ASTDirReturn Return() :
 {
     Token start, end = null;
-    Expression exp = null;
+    ASTExpression exp = null;
 }
 {
     (
         start = <SIMPLE_RETURN> { end = start; }
         |
-        start = <RETURN> exp = Expression() end = LooseDirectiveEnd()
+        start = <RETURN> exp = ASTExpression() end = LooseDirectiveEnd()
     )
     {
         if (inMacro) {
@@ -2728,42 +2730,42 @@ ReturnInstruction Return() :
             			"A return instruction can only occur inside a macro or function", template, start);
             }
         }
-        ReturnInstruction result = new ReturnInstruction(exp);
+        ASTDirReturn result = new ASTDirReturn(exp);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-StopInstruction Stop() :
+ASTDirStop Stop() :
 {
     Token start = null;
-    Expression exp = null;
+    ASTExpression exp = null;
 }
 {
     (
         start = <HALT>
         |
-        start = <STOP> exp = Expression() LooseDirectiveEnd()
+        start = <STOP> exp = ASTExpression() LooseDirectiveEnd()
     )
     {
-        StopInstruction result = new StopInstruction(exp);
+        ASTDirStop result = new ASTDirStop(exp);
         result.setLocation(template, start, start);
         return result;
     }
 }
 
-TemplateElement Nested() :
+_ASTElement Nested() :
 {
     Token t, end;
     ArrayList bodyParameters;
-    BodyInstruction result = null;
+    ASTDirNested result = null;
 }
 {
     (
         (
             t = <SIMPLE_NESTED>
             {
-                result = new BodyInstruction(null);
+                result = new ASTDirNested(null);
                 result.setLocation(template, t, t);
             }
         )
@@ -2773,7 +2775,7 @@ TemplateElement Nested() :
             bodyParameters = PositionalArgs()
             end = LooseDirectiveEnd()
             {
-                result = new BodyInstruction(bodyParameters);
+                result = new ASTDirNested(bodyParameters);
                 result.setLocation(template, t, end);
             }
         )
@@ -2786,33 +2788,33 @@ TemplateElement Nested() :
     }
 }
 
-TemplateElement Flush() :
+_ASTElement Flush() :
 {
     Token t;
 }
 {
     t = <FLUSH>
     {
-        FlushInstruction result = new FlushInstruction();
+        ASTDirFlush result = new ASTDirFlush();
         result.setLocation(template, t, t);
         return result;
     }
 }
 
-TemplateElement Trim() :
+_ASTElement Trim() :
 {
     Token t;
-    TrimInstruction result = null;
+    ASTDirTOrTrOrTl result = null;
 }
 {
     (
-        t = <TRIM> { result = new TrimInstruction(true, true); }
+        t = <TRIM> { result = new ASTDirTOrTrOrTl(true, true); }
         |
-        t = <LTRIM> { result = new TrimInstruction(true, false); }
+        t = <LTRIM> { result = new ASTDirTOrTrOrTl(true, false); }
         |
-        t = <RTRIM> { result = new TrimInstruction(false, true); }
+        t = <RTRIM> { result = new ASTDirTOrTrOrTl(false, true); }
         |
-        t = <NOTRIM> { result = new TrimInstruction(false, false); }
+        t = <NOTRIM> { result = new ASTDirTOrTrOrTl(false, false); }
     )
     {
         result.setLocation(template, t, t);
@@ -2821,27 +2823,27 @@ TemplateElement Trim() :
 }
 
 
-TemplateElement Assign() :
+_ASTElement Assign() :
 {
     Token start, end;
     int scope;
     Token id = null;
     Token equalsOp;
-    Expression nameExp, exp, nsExp = null;
+    ASTExpression nameExp, exp, nsExp = null;
     String varName;
     ArrayList assignments = new ArrayList();
-    Assignment ass;
+    ASTDirAssignment ass;
     TemplateElements children;
 }
 {
     (
-        start = <ASSIGN> { scope = Assignment.NAMESPACE; }
+        start = <ASSIGN> { scope = ASTDirAssignment.NAMESPACE; }
         |
-        start = <GLOBALASSIGN> { scope = Assignment.GLOBAL; }
+        start = <GLOBALASSIGN> { scope = ASTDirAssignment.GLOBAL; }
         |
-        start = <LOCALASSIGN> { scope = Assignment.LOCAL; }
+        start = <LOCALASSIGN> { scope = ASTDirAssignment.LOCAL; }
         {
-            scope = Assignment.LOCAL;
+            scope = ASTDirAssignment.LOCAL;
             if (!inMacro && !inFunction) {
                 throw new ParseException("Local variable assigned outside a macro.", template, start);
             }
@@ -2849,9 +2851,9 @@ TemplateElement Assign() :
     )
     nameExp = IdentifierOrStringLiteral()
     {
-        varName = (nameExp instanceof StringLiteral)
-                ? ((StringLiteral) nameExp).getAsString()
-                : ((Identifier) nameExp).getName();
+        varName = (nameExp instanceof ASTExpStringLiteral)
+                ? ((ASTExpStringLiteral) nameExp).getAsString()
+                : ((ASTExpVariable) nameExp).getName();
     }
     (
     	(
@@ -2861,7 +2863,7 @@ TemplateElement Assign() :
 			        {
 			           equalsOp = token;
 			        }
-			        exp = Expression()
+			        exp = ASTExpression()
 		        )
 		        |
 		        (
@@ -2873,7 +2875,7 @@ TemplateElement Assign() :
 		        )
 	        )
 	        {
-	            ass = new Assignment(varName, equalsOp.kind, exp, scope);
+	            ass = new ASTDirAssignment(varName, equalsOp.kind, exp, scope);
                 if (exp != null) {
                    ass.setLocation(template, nameExp, exp);
                 } else {
@@ -2891,9 +2893,9 @@ TemplateElement Assign() :
 	            [<COMMA>]
 	            nameExp = IdentifierOrStringLiteral()
 	            {
-	                varName = (nameExp instanceof StringLiteral)
-	                		? ((StringLiteral) nameExp).getAsString()
-	                		: ((Identifier) nameExp).getName();
+	                varName = (nameExp instanceof ASTExpStringLiteral)
+	                		? ((ASTExpStringLiteral) nameExp).getAsString()
+	                		: ((ASTExpVariable) nameExp).getName();
 	            }
 	            (
 	                (
@@ -2901,7 +2903,7 @@ TemplateElement Assign() :
 	                    {
 	                       equalsOp = token;
 	                    }
-	                    exp = Expression()
+	                    exp = ASTExpression()
 	                )
 	                |
 	                (
@@ -2913,7 +2915,7 @@ TemplateElement Assign() :
 	                )
 	            )
 	            {
-	                ass = new Assignment(varName, equalsOp.kind, exp, scope);
+	                ass = new ASTDirAssignment(varName, equalsOp.kind, exp, scope);
 	                if (exp != null) {
 	                   ass.setLocation(template, nameExp, exp);
 	                } else {
@@ -2924,9 +2926,9 @@ TemplateElement Assign() :
 	        )*
 	        [
 	            id = <IN>
-	            nsExp = Expression()
+	            nsExp = ASTExpression()
 	            {
-	                if (scope != Assignment.NAMESPACE) {
+	                if (scope != ASTDirAssignment.NAMESPACE) {
 	                	throw new ParseException("Cannot assign to namespace here.", template, id);
                 	}
 	            }
@@ -2934,14 +2936,14 @@ TemplateElement Assign() :
 	        end = LooseDirectiveEnd()
 	        {
                 if (assignments.size() == 1) {
-                    Assignment a = (Assignment) assignments.get(0);
+                    ASTDirAssignment a = (ASTDirAssignment) assignments.get(0);
                     a.setNamespaceExp(nsExp);
                     a.setLocation(template, start, end);
                     return a;
                 } else {
-		            AssignmentInstruction ai = new AssignmentInstruction(scope);
+		            ASTDirAssignmentsContainer ai = new ASTDirAssignmentsContainer(scope);
 		            for (int i = 0; i< assignments.size(); i++) {
-		                ai.addAssignment((Assignment) assignments.get(i));
+		                ai.addAssignment((ASTDirAssignment) assignments.get(i));
 		            }
 		            ai.setNamespaceExp(nsExp);
 		            ai.setLocation(template, start, end);
@@ -2953,9 +2955,9 @@ TemplateElement Assign() :
 	    (
 	        [
 	            id = <IN>
-	            nsExp = Expression()
+	            nsExp = ASTExpression()
 	            {
-	                if (scope != Assignment.NAMESPACE) {
+	                if (scope != ASTDirAssignment.NAMESPACE) {
 	                	throw new ParseException("Cannot assign to namespace here.", template, id);
 	            	}
 	            }
@@ -2965,26 +2967,26 @@ TemplateElement Assign() :
 	        (
 	            end = <END_LOCAL>
 	            {
-	            	if (scope != Assignment.LOCAL) {
+	            	if (scope != ASTDirAssignment.LOCAL) {
 	            		throw new ParseException("Mismatched assignment tags.", template, end);
 	        		}
 	        	}
 	            |
 	            end = <END_ASSIGN>
 	            {
-	            	if (scope != Assignment.NAMESPACE) {
+	            	if (scope != ASTDirAssignment.NAMESPACE) {
 	            		throw new ParseException("Mismatched assignment tags.", template, end);
 	        		}
 	        	}
 	            |
 	            end = <END_GLOBAL>
 	            {
-	            	if (scope != Assignment.GLOBAL) throw new ParseException(
+	            	if (scope != ASTDirAssignment.GLOBAL) throw new ParseException(
 	            			"Mismatched assignment tags", template, end);
             	}
 	        )
 	        {
-	            BlockAssignment ba = new BlockAssignment(
+	            ASTDirCapturingAssignment ba = new ASTDirCapturingAssignment(
 	                   children, varName, scope, nsExp,
 	                   getMarkupOutputFormat());
 	            ba.setLocation(template, start, end);
@@ -2994,20 +2996,20 @@ TemplateElement Assign() :
     )
 }
 
-Include Include() :
+ASTDirInclude ASTDirInclude() :
 {
-    Expression nameExp;
+    ASTExpression nameExp;
     Token att, start, end;
-    Expression exp, parseExp = null, encodingExp = null, ignoreMissingExp = null;
+    ASTExpression exp, parseExp = null, encodingExp = null, ignoreMissingExp = null;
 }
 {
     start = <_INCLUDE>
-    nameExp = Expression()
+    nameExp = ASTExpression()
     [<SEMICOLON>]
     (
         att = <ID>
         <EQUALS>
-        exp = Expression()
+        exp = ASTExpression()
         {
             String attString = att.image;
             if (attString.equalsIgnoreCase("parse")) {
@@ -3033,40 +3035,40 @@ Include Include() :
     )*
     end = LooseDirectiveEnd()
     {
-        Include result = new Include(template, nameExp, encodingExp, parseExp, ignoreMissingExp);
+        ASTDirInclude result = new ASTDirInclude(template, nameExp, encodingExp, parseExp, ignoreMissingExp);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-LibraryLoad Import() :
+ASTDirImport Import() :
 {
     Token start, end, ns;
-    Expression nameExp;
+    ASTExpression nameExp;
 }
 {
     start = <IMPORT>
-    nameExp = Expression()
+    nameExp = ASTExpression()
     <AS>
     ns = <ID>
     end = LooseDirectiveEnd()
     {
-        LibraryLoad result = new LibraryLoad(template, nameExp, ns.image);
+        ASTDirImport result = new ASTDirImport(template, nameExp, ns.image);
         result.setLocation(template, start, end);
         template.addImport(result);
         return result;
     }
 }
 
-Macro Macro() :
+ASTDirMacro ASTDirMacro() :
 {
     Token arg, start, end;
-    Expression nameExp;
+    ASTExpression nameExp;
     String name;
     ArrayList argNames = new ArrayList();
     HashMap args = new HashMap();
     ArrayList defNames = new ArrayList();
-    Expression defValue = null;
+    ASTExpression defValue = null;
     List lastIteratorBlockContexts;
     int lastBreakableDirectiveNesting;
     TemplateElements children;
@@ -3088,9 +3090,9 @@ Macro Macro() :
     }
     nameExp = IdentifierOrStringLiteral()
     {
-        name = (nameExp instanceof StringLiteral)
-                ? ((StringLiteral) nameExp).getAsString()
-                : ((Identifier) nameExp).getName();
+        name = (nameExp instanceof ASTExpStringLiteral)
+                ? ((ASTExpStringLiteral) nameExp).getAsString()
+                : ((ASTExpVariable) nameExp).getName();
     }
     [<OPEN_PAREN>]
     (
@@ -3100,7 +3102,7 @@ Macro Macro() :
         ]
         [
             <EQUALS>
-            defValue = Expression()
+            defValue = ASTExpression()
             {
                 defNames.add(arg.image);
                 hasDefaults = true;
@@ -3158,14 +3160,14 @@ Macro Macro() :
         breakableDirectiveNesting = lastBreakableDirectiveNesting;
         
         inMacro = inFunction = false;
-        Macro result = new Macro(name, argNames, args, catchAll, isFunction, children);
+        ASTDirMacro result = new ASTDirMacro(name, argNames, args, catchAll, isFunction, children);
         result.setLocation(template, start, end);
         template.addMacro(result);
         return result;
     }
 }
 
-CompressedBlock Compress() :
+ASTDirCompress Compress() :
 {
     TemplateElements children;
     Token start, end;
@@ -3175,27 +3177,27 @@ CompressedBlock Compress() :
     children = MixedContentElements()
     end = <END_COMPRESS>
     {
-        CompressedBlock cb = new CompressedBlock(children);
+        ASTDirCompress cb = new ASTDirCompress(children);
         cb.setLocation(template, start, end);
         return cb;
     }
 }
 
-TemplateElement UnifiedMacroTransform() :
+_ASTElement UnifiedMacroTransform() :
 {
     Token start = null, end, t;
     HashMap namedArgs = null;
     ArrayList positionalArgs = null, bodyParameters = null;
-    Expression startTagNameExp;
+    ASTExpression startTagNameExp;
     TemplateElements children;
-    Expression exp;
+    ASTExpression exp;
     int pushedCtxCount = 0;
 }
 {
     start = <UNIFIED_CALL>
-    exp = Expression()
+    exp = ASTExpression()
     {
-        if (exp instanceof Identifier || (exp instanceof Dot && ((Dot) exp).onlyHasIdentifiers())) {
+        if (exp instanceof ASTExpVariable || (exp instanceof ASTExpDot && ((ASTExpDot) exp).onlyHasIdentifiers())) {
             startTagNameExp = exp;
         } else {
             startTagNameExp = null;
@@ -3268,15 +3270,15 @@ TemplateElement UnifiedMacroTransform() :
         )
     )
     {
-        TemplateElement result = (positionalArgs != null)
-        		? new UnifiedCall(exp, positionalArgs, children, bodyParameters)
-	            : new UnifiedCall(exp, namedArgs, children, bodyParameters);
+        _ASTElement result = (positionalArgs != null)
+        		? new ASTDirUserDefined(exp, positionalArgs, children, bodyParameters)
+	            : new ASTDirUserDefined(exp, namedArgs, children, bodyParameters);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-TemplateElement Call() :
+_ASTElement Call() :
 {
     Token start, end, id;
     HashMap namedArgs = null;
@@ -3301,11 +3303,11 @@ TemplateElement Call() :
     )
     end = LooseDirectiveEnd()
     {
-        UnifiedCall result = null;
+        ASTDirUserDefined result = null;
         if (positionalArgs != null) {
-            result = new UnifiedCall(new Identifier(macroName), positionalArgs, TemplateElements.EMPTY, null);
+            result = new ASTDirUserDefined(new ASTExpVariable(macroName), positionalArgs, TemplateElements.EMPTY, null);
         } else {
-            result = new UnifiedCall(new Identifier(macroName), namedArgs, TemplateElements.EMPTY, null);
+            result = new ASTDirUserDefined(new ASTExpVariable(macroName), namedArgs, TemplateElements.EMPTY, null);
         }
         result.legacySyntax = true;
         result.setLocation(template, start, end);
@@ -3317,7 +3319,7 @@ HashMap NamedArgs() :
 {
     HashMap result = new HashMap();
     Token t;
-    Expression exp;
+    ASTExpression exp;
 }
 {
     (
@@ -3327,7 +3329,7 @@ HashMap NamedArgs() :
             token_source.SwitchTo(token_source.NAMED_PARAMETER_EXPRESSION);
             token_source.inInvocation = true;
         }             
-        exp = Expression()
+        exp = ASTExpression()
         {
             result.put(t.image, exp);
         }
@@ -3341,14 +3343,14 @@ HashMap NamedArgs() :
 ArrayList PositionalArgs() :
 {
     ArrayList result = new ArrayList();
-    Expression arg;
+    ASTExpression arg;
 }
 {
     [
-        arg = Expression() { result.add(arg); }
+        arg = ASTExpression() { result.add(arg); }
         (
             [<COMMA>]
-            arg = Expression() { result.add(arg); }
+            arg = ASTExpression() { result.add(arg); }
         )*
     ]
     {
@@ -3357,7 +3359,7 @@ ArrayList PositionalArgs() :
 }
 
 
-Comment Comment() :
+ASTComment ASTComment() :
 {
     Token start, end;
     StringBuilder buf = new StringBuilder();
@@ -3370,13 +3372,13 @@ Comment Comment() :
     )
     end = UnparsedContent(start, buf)
     {
-        Comment result = new Comment(buf.toString());
+        ASTComment result = new ASTComment(buf.toString());
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-TextBlock NoParse() :
+ASTStaticText NoParse() :
 {
     Token start, end;
     StringBuilder buf = new StringBuilder();
@@ -3385,27 +3387,27 @@ TextBlock NoParse() :
     start = <NOPARSE>
     end = UnparsedContent(start, buf)
     {
-        TextBlock result = new TextBlock(buf.toString(), true);
+        ASTStaticText result = new ASTStaticText(buf.toString(), true);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-TransformBlock Transform() :
+ASTDirTransform Transform() :
 {
     Token start, end, argName;
-    Expression exp, argExp;
+    ASTExpression exp, argExp;
     TemplateElements children = null;
     HashMap args = null;
 }
 {
     start = <TRANSFORM>
-    exp = Expression()
+    exp = ASTExpression()
     [<SEMICOLON>]
     (
         argName = <ID>
         <EQUALS>
-        argExp = Expression()
+        argExp = ASTExpression()
         {
             if (args == null) args = new HashMap();
             args.put(argName.image, argExp);
@@ -3421,31 +3423,31 @@ TransformBlock Transform() :
         )
     )
     {
-        TransformBlock result = new TransformBlock(exp, args, children);
+        ASTDirTransform result = new ASTDirTransform(exp, args, children);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-SwitchBlock Switch() :
+ASTDirSwitch Switch() :
 {
-    SwitchBlock switchBlock;
-    Case caseIns;
-    Expression switchExp;
+    ASTDirSwitch switchBlock;
+    ASTDirCase caseIns;
+    ASTExpression switchExp;
     Token start, end;
     boolean defaultFound = false;
 }
 {
     start = <SWITCH>
-    switchExp = Expression()
+    switchExp = ASTExpression()
     <DIRECTIVE_END>
     {
         breakableDirectiveNesting++;
-        switchBlock = new SwitchBlock(switchExp);
+        switchBlock = new ASTDirSwitch(switchExp);
     }
     (
         LOOKAHEAD(2)
-        caseIns = Case()
+        caseIns = ASTDirCase()
         {
             if (caseIns.condition == null) {
                 if (defaultFound) {
@@ -3466,31 +3468,31 @@ SwitchBlock Switch() :
     }
 }
 
-Case Case() :
+ASTDirCase ASTDirCase() :
 {
-    Expression exp;
+    ASTExpression exp;
     TemplateElements children;
     Token start;
 }
 {
     [<STATIC_TEXT_WS>]
     (
-        start = <CASE> exp = Expression() <DIRECTIVE_END>
+        start = <CASE> exp = ASTExpression() <DIRECTIVE_END>
         |
         start = <DEFAUL> { exp = null; }
     )
     children = MixedContentElements()
     {
-        Case result = new Case(exp, children);
+        ASTDirCase result = new ASTDirCase(exp, children);
         result.setLocation(template, start, start, children);
         return result;
     }
 }
 
-EscapeBlock Escape() :
+ASTDirEscape Escape() :
 {
     Token variable, start, end;
-    Expression escapeExpr;
+    ASTExpression escapeExpr;
     TemplateElements children;
 }
 {
@@ -3506,10 +3508,10 @@ EscapeBlock Escape() :
     }
     variable = <ID>
     <AS>
-    escapeExpr = Expression()
+    escapeExpr = ASTExpression()
     <DIRECTIVE_END>
     {
-        EscapeBlock result = new EscapeBlock(variable.image, escapeExpr, escapedExpression(escapeExpr));
+        ASTDirEscape result = new ASTDirEscape(variable.image, escapeExpr, escapedExpression(escapeExpr));
         escapes.addFirst(result);
     }
     children = MixedContentElements()
@@ -3524,7 +3526,7 @@ EscapeBlock Escape() :
     }
 }
 
-NoEscapeBlock NoEscape() :
+ASTDirNoEscape NoEscape() :
 {
     Token start, end;
     TemplateElements children;
@@ -3541,22 +3543,22 @@ NoEscapeBlock NoEscape() :
     end = <END_NOESCAPE>
     {
         escapes.addFirst(escape);
-        NoEscapeBlock result = new NoEscapeBlock(children);
+        ASTDirNoEscape result = new ASTDirNoEscape(children);
         result.setLocation(template, start, end);
         return result;
     }
 }
 
-OutputFormatBlock OutputFormat() :
+ASTDirOutputFormat OutputFormat() :
 {
     Token start, end;
-    Expression paramExp;
+    ASTExpression paramExp;
     TemplateElements children;
     OutputFormat lastOutputFormat;
 }
 {
     start = <OUTPUTFORMAT>
-    paramExp = Expression()
+    paramExp = ASTExpression()
     <DIRECTIVE_END>
     {
         if (!paramExp.isLiteral()) {
@@ -3627,7 +3629,7 @@ OutputFormatBlock OutputFormat() :
     children = MixedContentElements()
     end = <END_OUTPUTFORMAT>
     {
-        OutputFormatBlock result = new OutputFormatBlock(children, paramExp);
+        ASTDirOutputFormat result = new ASTDirOutputFormat(children, paramExp);
         result.setLocation(template, start, end);
         
         outputFormat = lastOutputFormat;
@@ -3636,7 +3638,7 @@ OutputFormatBlock OutputFormat() :
     }
 }
 
-AutoEscBlock AutoEsc() :
+ASTDirAutoEsc AutoEsc() :
 {
     Token start, end;
     TemplateElements children;
@@ -3653,7 +3655,7 @@ AutoEscBlock AutoEsc() :
     children = MixedContentElements()
     end = <END_AUTOESC>
     {
-        AutoEscBlock result = new AutoEscBlock(children);
+        ASTDirAutoEsc result = new ASTDirAutoEsc(children);
         result.setLocation(template, start, end);
         
         autoEscapingPolicy = lastAutoEscapingPolicy; 
@@ -3662,7 +3664,7 @@ AutoEscBlock AutoEsc() :
     }
 }
 
-NoAutoEscBlock NoAutoEsc() :
+ASTDirNoAutoEsc NoAutoEsc() :
 {
     Token start, end;
     TemplateElements children;
@@ -3678,7 +3680,7 @@ NoAutoEscBlock NoAutoEsc() :
     children = MixedContentElements()
     end = <END_NOAUTOESC>
     {
-        NoAutoEscBlock result = new NoAutoEscBlock(children);
+        ASTDirNoAutoEsc result = new ASTDirNoAutoEsc(children);
         result.setLocation(template, start, end);
         
         autoEscapingPolicy = lastAutoEscapingPolicy;
@@ -3705,20 +3707,20 @@ Token LooseDirectiveEnd() :
     }
 }
 
-PropertySetting Setting() :
+ASTDirSetting Setting() :
 {
     Token start, end, key;
-    Expression value;
+    ASTExpression value;
 }
 {
     start = <SETTING>
     key = <ID>
     <EQUALS>
-    value = Expression()
+    value = ASTExpression()
     end = LooseDirectiveEnd()
     {
         token_source.checkNamingConvention(key);
-        PropertySetting result = new PropertySetting(key, token_source, value, template.getConfiguration());
+        ASTDirSetting result = new ASTDirSetting(key, token_source, value, template.getConfiguration());
         result.setLocation(template, start, end);
         return result;
     }
@@ -3727,9 +3729,9 @@ PropertySetting Setting() :
 /**
  * A production for FreeMarker directives.
  */
-TemplateElement FreemarkerDirective() :
+_ASTElement FreemarkerDirective() :
 {
-    TemplateElement tp;
+    _ASTElement tp;
 }
 {
     // Note that this doesn't include elements like "else", "recover", etc., because those indicate the end
@@ -3743,23 +3745,23 @@ TemplateElement FreemarkerDirective() :
         |
         tp = Assign()
         |
-        tp = Include()
+        tp = ASTDirInclude()
         |
         tp = Import()
         |
-        tp = Macro()
+        tp = ASTDirMacro()
         |
         tp = Compress()
         |
         tp = UnifiedMacroTransform()
         |
-        tp = Items()
+        tp = ASTDirItems()
         |
-        tp = Sep()
+        tp = ASTDirSep()
         |
         tp = Call()
         |
-        tp = Comment()
+        tp = ASTComment()
         |
         tp = NoParse()
         |
@@ -3809,7 +3811,7 @@ TemplateElement FreemarkerDirective() :
  * i.e. text that contains no
  * FreeMarker directives.
  */
-TextBlock PCData() :
+ASTStaticText PCData() :
 {
     StringBuilder buf = new StringBuilder();
     Token t = null, start = null, prevToken = null;
@@ -3833,7 +3835,7 @@ TextBlock PCData() :
     {
         if (stripText && mixedContentNesting == 1) return null;
 
-        TextBlock result = new TextBlock(buf.toString(), false);
+        ASTStaticText result = new ASTStaticText(buf.toString(), false);
         result.setLocation(template, start, t);
         return result;
     }
@@ -3867,9 +3869,9 @@ Token UnparsedContent(Token start, StringBuilder buf) :
 
 TemplateElements MixedContentElements() :
 {
-    TemplateElement[] childBuffer = null;
+    _ASTElement[] childBuffer = null;
     int childCount = 0;
-    TemplateElement elem;
+    _ASTElement elem;
     mixedContentNesting++;
 }
 {
@@ -3880,7 +3882,7 @@ TemplateElements MixedContentElements() :
             |
             elem = StringOutput()
             |
-            elem = NumericalOutput()
+            elem = ASTHashInterpolation()
             |
             elem = FreemarkerDirective()
         )
@@ -3889,9 +3891,9 @@ TemplateElements MixedContentElements() :
             if (elem != null) {
 	            childCount++;
 	            if (childBuffer == null) {
-	                childBuffer = new TemplateElement[16]; 
+	                childBuffer = new _ASTElement[16]; 
 	            } else if (childBuffer.length < childCount) {
-	                TemplateElement[] newChildBuffer = new TemplateElement[childCount * 2];
+	                _ASTElement[] newChildBuffer = new _ASTElement[childCount * 2];
 	                for (int i = 0; i < childBuffer.length; i++) {
 	                    newChildBuffer[i] = childBuffer[i];
 	                }
@@ -3912,10 +3914,10 @@ TemplateElements MixedContentElements() :
  *
  * @deprecated Use {@link #MixedContentElements} instead.
  */
-MixedContent MixedContent() :
+ASTImplicitParent ASTImplicitParent() :
 {
-    MixedContent mixedContent = new MixedContent();
-    TemplateElement elem, begin = null;
+    ASTImplicitParent mixedContent = new ASTImplicitParent();
+    _ASTElement elem, begin = null;
     mixedContentNesting++;
 }
 {
@@ -3926,7 +3928,7 @@ MixedContent MixedContent() :
             |
             elem = StringOutput()
             |
-            elem = NumericalOutput()
+            elem = ASTHashInterpolation()
             |
             elem = FreemarkerDirective()
         )
@@ -3953,17 +3955,17 @@ MixedContent MixedContent() :
  *
  * @deprecated Use {@link #MixedContentElements} instead.
  */
-TemplateElement OptionalBlock() :
+_ASTElement OptionalBlock() :
 {
-    TemplateElement tp = null;
+    _ASTElement tp = null;
 }
 {
     [
         LOOKAHEAD(1) // has no effect but to get rid of a spurious warning.
-        tp = MixedContent()
+        tp = ASTImplicitParent()
     ]
     {
-        return tp != null ? tp : new TextBlock(_CollectionUtil.EMPTY_CHAR_ARRAY, false);
+        return tp != null ? tp : new ASTStaticText(_CollectionUtil.EMPTY_CHAR_ARRAY, false);
     }
 }
 
@@ -3971,10 +3973,10 @@ TemplateElement OptionalBlock() :
  * A production freemarker text that may contain
  * ${...} and #{...} but no directives.
  */
-TemplateElement FreeMarkerText() :
+_ASTElement FreeMarkerText() :
 {
-    MixedContent nodes = new MixedContent();
-    TemplateElement elem, begin = null;
+    ASTImplicitParent nodes = new ASTImplicitParent();
+    _ASTElement elem, begin = null;
 }
 {
     (
@@ -3983,7 +3985,7 @@ TemplateElement FreeMarkerText() :
             |
             elem = StringOutput()
             |
-            elem = NumericalOutput()
+            elem = ASTHashInterpolation()
         )
         {
             if (begin == null) {
@@ -4001,7 +4003,7 @@ TemplateElement FreeMarkerText() :
 void HeaderElement() :
 {
     Token key;
-    Expression exp = null;
+    ASTExpression exp = null;
     Token autoEscRequester = null;
 }
 {
@@ -4014,7 +4016,7 @@ void HeaderElement() :
             (
                 key = <ID>
                 <EQUALS>
-                exp = Expression()
+                exp = ASTExpression()
                 {
                     token_source.checkNamingConvention(key);
                 
@@ -4140,15 +4142,15 @@ void HeaderElement() :
 
 Map ParamList() :
 {
-    Identifier id;
-    Expression exp;
+    ASTExpVariable id;
+    ASTExpression exp;
     Map result = new HashMap();
 }
 {
     (
-        id = Identifier()
+        id = ASTExpVariable()
         <EQUALS>
-        exp = Expression() { result.put(id.toString(), exp); }
+        exp = ASTExpression() { result.put(id.toString(), exp); }
         [<COMMA>]
     )+
     {
@@ -4159,12 +4161,12 @@ Map ParamList() :
 /**
  * Parses the already un-escaped content of a string literal (input must not include the quotation marks).
  *
- * @return A {@link List} of {@link String}-s and {@link Interpolation}-s. 
+ * @return A {@link List} of {@link String}-s and {@link ASTInterpolation}-s. 
  */
 List<Object> StaticTextAndInterpolations() :
 {
     Token t;
-    Interpolation interpolation;
+    ASTInterpolation interpolation;
     StringBuilder staticTextCollector = null;
     ArrayList<Object> parts = new ArrayList<Object>();
 }
@@ -4196,7 +4198,7 @@ List<Object> StaticTextAndInterpolations() :
 		    |
             LOOKAHEAD(<HASH_INTERPOLATION_OPENING>)
 		    (
-                interpolation = NumericalOutput()
+                interpolation = ASTHashInterpolation()
 		    )
 	    )
 	    {
@@ -4220,7 +4222,7 @@ List<Object> StaticTextAndInterpolations() :
  * Root production to be used when parsing
  * an entire file.
  */
-TemplateElement Root() :
+_ASTElement Root() :
 {
     TemplateElements children;
 }
@@ -4239,7 +4241,7 @@ TemplateElement Root() :
     children = MixedContentElements()
     <EOF>
     {
-        TemplateElement root = children.asSingleElement(); 
+        _ASTElement root = children.asSingleElement(); 
         root.setFieldsForRootElement();
         root = root.postParseCleanup(stripWhitespace);
         // The cleanup result is possibly an element from deeper:

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/ASTBasedErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ASTBasedErrorMessagesTest.java b/src/test/java/org/apache/freemarker/core/ASTBasedErrorMessagesTest.java
new file mode 100644
index 0000000..78ce1f3
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/ASTBasedErrorMessagesTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.util.Map;
+
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Test;
+
+public class ASTBasedErrorMessagesTest extends TemplateTest {
+    
+    @Test
+    public void testInvalidRefBasic() {
+        assertErrorContains("${foo}", "foo", "specify a default");
+        assertErrorContains("${map[foo]}", "foo", "\\!map[", "specify a default");
+    }
+    
+    @Test
+    public void testInvalidRefDollar() {
+        assertErrorContains("${$x}", "$x", "must not start with \"$\"", "specify a default");
+        assertErrorContains("${map.$x}", "map.$x", "must not start with \"$\"", "specify a default");
+    }
+
+    @Test
+    public void testInvalidRefAfterDot() {
+        assertErrorContains("${map.foo.bar}", "map.foo", "\\!foo.bar", "after the last dot", "specify a default");
+    }
+
+    @Test
+    public void testInvalidRefInSquareBrackets() {
+        assertErrorContains("${map['foo']}", "map", "final [] step", "specify a default");
+    }
+
+    @Test
+    public void testInvalidRefSize() {
+        assertErrorContains("${map.size()}", "map.size", "?size", "specify a default");
+        assertErrorContains("${map.length()}", "map.length", "?length", "specify a default");
+    }
+
+    @Override
+    protected Object createDataModel() {
+        Map<String, Object> dataModel = createCommonTestValuesDataModel();
+        dataModel.put("overloads", new Overloads());
+        return dataModel;
+    }
+    
+    public static class Overloads {
+        
+        @SuppressWarnings("unused")
+        public void m(String s) {}
+        
+        @SuppressWarnings("unused")
+        public void m(int i) {}
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/freemarker/core/ASTPrinter.java b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
new file mode 100644
index 0000000..c615734
--- /dev/null
+++ b/src/test/java/org/apache/freemarker/core/ASTPrinter.java
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CodingErrorAction;
+import java.util.Enumeration;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.ASTExpression;
+import org.apache.freemarker.core.ASTImplicitParent;
+import org.apache.freemarker.core.ParameterRole;
+import org.apache.freemarker.core.ParseException;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core._ASTElement;
+import org.apache.freemarker.core.ASTNode;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.FTLUtil;
+import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * Static methods and command-line tool for printing the AST of a template. 
+ */
+public class ASTPrinter {
+
+    private final Configuration cfg;
+    private int successfulCounter;
+    private int failedCounter;
+    
+    static public void main(String[] args) throws IOException {
+        if (args.length == 0) {
+            usage();
+            System.exit(-1);
+        }
+        
+        ASTPrinter astp = new ASTPrinter(); 
+        if (args[0].equalsIgnoreCase("-r")) {
+            astp.mainRecursive(args);
+        } else {
+            astp.mainSingleTemplate(args);
+        }
+    }
+    
+    private ASTPrinter() {
+        cfg = new Configuration(Configuration.VERSION_3_0_0);
+    }
+    
+    private void mainSingleTemplate(String[] args) throws IOException, FileNotFoundException {
+        final String templateFileName;
+        final String templateContent;
+        if (args[0].startsWith("ftl:")) {
+            templateFileName = null;
+            templateContent = args[0];
+        } else {
+            templateFileName = args[0];
+            templateContent = null;
+        }
+        
+        Template t = new Template(
+                templateFileName,
+                templateFileName == null ? new StringReader(templateContent) : new FileReader(templateFileName),
+                cfg);
+        
+        p(getASTAsString(t));
+    }
+
+    private void mainRecursive(String[] args) throws IOException {
+        if (args.length != 4) {
+            p("Number of arguments must be 4, but was: " + args.length);
+            usage();
+            System.exit(-1);
+        }
+        
+        final String srcDirPath = args[1].trim();
+        File srcDir = new File(srcDirPath);
+        if (!srcDir.isDirectory()) {
+            p("This should be an existing directory: " + srcDirPath);
+            System.exit(-1);
+        }
+        
+        Pattern fnPattern;
+        try {
+            fnPattern = Pattern.compile(args[2]);
+        } catch (PatternSyntaxException e) {
+            p(_StringUtil.jQuote(args[2]) + " is not a valid regular expression");
+            System.exit(-1);
+            return;
+        }
+        
+        final String dstDirPath = args[3].trim();
+        File dstDir = new File(dstDirPath);
+        if (!dstDir.isDirectory()) {
+            p("This should be an existing directory: " + dstDirPath);
+            System.exit(-1);
+        }
+        
+        long startTime = System.currentTimeMillis();
+        recurse(srcDir, fnPattern, dstDir);
+        long endTime = System.currentTimeMillis();
+        
+        p("Templates successfully processed " + successfulCounter + ", failed " + failedCounter
+                + ". Time taken: " + (endTime - startTime) / 1000.0 + " s");
+    }
+    
+    private void recurse(File srcDir, Pattern fnPattern, File dstDir) throws IOException {
+        File[] files = srcDir.listFiles();
+        if (files == null) {
+            throw new IOException("Failed to kust directory: " + srcDir);
+        }
+        for (File file : files) {
+            if (file.isDirectory()) {
+                recurse(file, fnPattern, new File(dstDir, file.getName()));
+            } else {
+                if (fnPattern.matcher(file.getName()).matches()) {
+                    File dstFile = new File(dstDir, file.getName());
+                    String res;
+                    try {
+                        Template t = new Template(file.getPath().replace('\\', '/'), loadIntoString(file), cfg);
+                        res = getASTAsString(t);
+                        successfulCounter++;
+                    } catch (ParseException e) {
+                        res = "<<<FAILED>>>\n" + e.getMessage();
+                        failedCounter++;
+                        p("");
+                        p("-------------------------failed-------------------------");
+                        p("Error message was saved into: " + dstFile.getAbsolutePath());
+                        p("");
+                        p(e.getMessage());
+                    }
+                    save(res, dstFile);
+                }
+            }
+        }
+    }
+
+    private String loadIntoString(File file) throws IOException {
+        long ln = file.length();
+        if (ln < 0) {
+            throw new IOException("Failed to get the length of " + file);
+        }
+        byte[] buffer = new byte[(int) ln];
+        InputStream in = new FileInputStream(file);
+        try {
+            int offset = 0;
+            int bytesRead;
+            while (offset < buffer.length) {
+                bytesRead = in.read(buffer, offset, buffer.length - offset);
+                if (bytesRead == -1) {
+                    throw new IOException("Unexpected end of file: " + file);
+                }
+                offset += bytesRead;
+            }
+        } finally {
+            in.close();
+        }
+        
+        try {
+            return decode(buffer, Charset.forName("UTF-8"));
+        } catch (CharacterCodingException e) {
+            return decode(buffer, Charset.forName("ISO-8859-1"));
+        }
+    }
+
+    private String decode(byte[] buffer, Charset charset) throws CharacterCodingException {
+        return charset.newDecoder()
+                .onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT)
+                .decode(ByteBuffer.wrap(buffer)).toString();
+    }
+
+    private void save(String astStr, File file) throws IOException {
+        File parentDir = file.getParentFile();
+        if (!parentDir.isDirectory() && !parentDir.mkdirs()) {
+            throw new IOException("Failed to create parent directory: " + parentDir);
+        }
+        
+        Writer w = new BufferedWriter(new FileWriter(file));
+        try {
+            w.write(astStr);
+        } finally {
+            w.close();
+        }
+    }
+
+    private static void usage() {
+        p("Prints template Abstract Syntax Tree (AST) as plain text.");
+        p("Usage:");
+        p("    java org.apache.freemarker.core.PrintAST <templateFile>");
+        p("    java org.apache.freemarker.core.PrintAST ftl:<templateSource>");
+        p("    java org.apache.freemarker.core.PrintAST -r <src-directory> <regexp> <dst-directory>");
+    }
+
+    private static final String INDENTATION = "    ";
+
+    public static String getASTAsString(String ftl) throws IOException {
+        return getASTAsString(ftl, (Options) null);
+    }
+    
+    public static String getASTAsString(String ftl, Options opts) throws IOException {
+        return getASTAsString(null, ftl, opts);
+    }
+
+    public static String getASTAsString(String templateName, String ftl) throws IOException {
+        return getASTAsString(templateName, ftl, null);
+    }
+    
+    public static String getASTAsString(String templateName, String ftl, Options opts) throws IOException {
+        Configuration cfg = new Configuration();
+        Template t = new Template(templateName, ftl, cfg);
+        return getASTAsString(t, opts);
+    }
+
+    public static String getASTAsString(Template t) throws IOException {
+        return getASTAsString(t, null);
+    }
+
+    public static String getASTAsString(Template t, Options opts) throws IOException {
+        validateAST(t);
+        
+        StringWriter out = new StringWriter();
+        printNode(t.getRootTreeNode(), "", null, opts != null ? opts : Options.DEFAULT_INSTANCE, out);
+        return out.toString();
+    }
+    
+    public static void validateAST(Template t) throws InvalidASTException {
+        final _ASTElement node = t.getRootTreeNode();
+        if (node.getParentElement() != null) {
+            throw new InvalidASTException("Root node parent must be null."
+                    + "\nRoot node: " + node.dump(false)
+                    + "\nParent"
+                    + ": " + node.getParentElement().getClass() + ", " + node.getParentElement().dump(false));
+        }
+        validateAST(node);
+    }
+
+    private static void validateAST(_ASTElement te) {
+        int childCount = te.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            _ASTElement child = te.getChild(i);
+            _ASTElement parentElement = child.getParentElement();
+            // As ASTImplicitParent.accept does nothing but returns its children, it's optimized out in the final
+            // AST tree. While it will be present as a child, the parent element also will have children
+            // that contains the children of the ASTImplicitParent directly. 
+            if (parentElement instanceof ASTImplicitParent && parentElement.getParentElement() != null) {
+                parentElement = parentElement.getParentElement();
+            }
+            if (parentElement != te) {
+                throw new InvalidASTException("Wrong parent node."
+                        + "\nNode: " + child.dump(false)
+                        + "\nExpected parent: " + te.dump(false)
+                        + "\nActual parent: " + parentElement.dump(false));
+            }
+            if (child.getIndex() != i) {
+                throw new InvalidASTException("Wrong node index."
+                        + "\nNode: " + child.dump(false)
+                        + "\nExpected index: " + i
+                        + "\nActual index: " + child.getIndex());
+            }
+        }
+        if (te instanceof ASTImplicitParent && te.getChildCount() < 2) {
+            throw new InvalidASTException("Mixed content with child count less than 2 should removed by optimizatoin, "
+                    + "but found one with " + te.getChildCount() + " child(ren).");
+        }
+        _ASTElement[] children = te.getChildBuffer();
+        if (children != null) {
+            if (childCount == 0) {
+                throw new InvalidASTException(
+                        "Children must be null when childCount is 0."
+                        + "\nNode: " + te.dump(false));
+            }
+            for (int i = 0; i < te.getChildCount(); i++) {
+                if (children[i] == null) {
+                    throw new InvalidASTException(
+                            "Child can't be null at index " + i
+                            + "\nNode: " + te.dump(false));
+                }
+            }
+            for (int i = te.getChildCount(); i < children.length; i++) {
+                if (children[i] != null) {
+                    throw new InvalidASTException(
+                            "Children can't be non-null at index " + i
+                            + "\nNode: " + te.dump(false));
+                }
+            }
+        } else {
+            if (childCount != 0) {
+                throw new InvalidASTException(
+                        "Children mustn't be null when child count isn't 0."
+                        + "\nNode: " + te.dump(false));
+            }
+        }
+    }
+
+    private static void printNode(Object node, String ind, ParameterRole paramRole, Options opts, Writer out) throws IOException {
+        if (node instanceof ASTNode) {
+            ASTNode tObj = (ASTNode) node;
+
+            printNodeLineStart(paramRole, ind, out);
+            out.write(tObj.getNodeTypeSymbol());
+            printNodeLineEnd(node, out, opts);
+            
+            if (opts.getShowConstantValue() && node instanceof ASTExpression) {
+                TemplateModel tm = ((ASTExpression) node).constantValue;
+                if (tm != null) {
+                    out.write(INDENTATION);
+                    out.write(ind);
+                    out.write("= const ");
+                    out.write(FTLUtil.getTypeDescription(tm));
+                    out.write(' ');
+                    out.write(tm.toString());
+                    out.write('\n');
+                }
+            }
+            
+            int paramCnt = tObj.getParameterCount();
+            for (int i = 0; i < paramCnt; i++) {
+                ParameterRole role = tObj.getParameterRole(i);
+                if (role == null) throw new NullPointerException("parameter role");
+                Object value = tObj.getParameterValue(i);
+                printNode(value, ind + INDENTATION, role, opts, out);
+            }
+            if (tObj instanceof _ASTElement) {
+                Enumeration enu = ((_ASTElement) tObj).children();
+                while (enu.hasMoreElements()) {
+                    printNode(enu.nextElement(), INDENTATION + ind, null, opts, out);
+                }
+            }
+        } else {
+            printNodeLineStart(paramRole, ind, out);
+            out.write(_StringUtil.jQuote(node));
+            printNodeLineEnd(node, out, opts);
+        }
+    }
+
+    protected static void printNodeLineEnd(Object node, Writer out, Options opts) throws IOException {
+        boolean commentStared = false;
+        if (opts.getShowJavaClass()) {
+            out.write("  // ");
+            commentStared = true;
+            out.write(_ClassUtil.getShortClassNameOfObject(node, true));
+        }
+        if (opts.getShowLocation() && node instanceof ASTNode) {
+            if (!commentStared) {
+                out.write("  // ");
+                commentStared = true;
+            } else {
+                out.write("; ");
+            }
+            ASTNode tObj = (ASTNode) node;
+            out.write("Location " + tObj.beginLine + ":" + tObj.beginColumn + "-" + tObj.endLine + ":" + tObj.endColumn);
+        }
+        out.write('\n');
+    }
+
+    private static void printNodeLineStart(ParameterRole paramRole, String ind, Writer out) throws IOException {
+        out.write(ind);
+        if (paramRole != null) {
+            out.write("- ");
+            out.write(paramRole.toString());
+            out.write(": ");
+        }
+    }
+    
+    public static class Options {
+        
+        private final static Options DEFAULT_INSTANCE = new Options(); 
+        
+        private boolean showJavaClass = true;
+        private boolean showConstantValue = false;
+        private boolean showLocation = false;
+        
+        public boolean getShowJavaClass() {
+            return showJavaClass;
+        }
+        
+        public void setShowJavaClass(boolean showJavaClass) {
+            this.showJavaClass = showJavaClass;
+        }
+        
+        public boolean getShowConstantValue() {
+            return showConstantValue;
+        }
+        
+        public void setShowConstantValue(boolean showConstantValue) {
+            this.showConstantValue = showConstantValue;
+        }
+
+        public boolean getShowLocation() {
+            return showLocation;
+        }
+
+        public void setShowLocation(boolean showLocation) {
+            this.showLocation = showLocation;
+        }
+        
+    }
+    
+    private static void p(Object obj) {
+        System.out.println(obj);
+    }
+
+    public static class InvalidASTException extends RuntimeException {
+
+        public InvalidASTException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        public InvalidASTException(String message) {
+            super(message);
+        }
+        
+    }
+}



Mime
View raw message