You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/05/15 21:23:48 UTC

[32/51] [abbrv] [partial] incubator-freemarker git commit: Restructured project so that freemarker-test-utils depends on freemarker-core (and hence can provide common classes for testing templates, and can use utility classes defined in the core). As a c

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
new file mode 100644
index 0000000..4602b67
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformHashWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.test.templatesuite.models;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.core.model.impl.SimpleHash;
+import org.apache.freemarker.core.util.HtmlEscape;
+import org.apache.freemarker.core.util.StandardCompress;
+
+/**
+ * Part of the TestTransform testcase suite.
+ */
+public class TransformHashWrapper implements TemplateHashModel,
+        TemplateScalarModel {
+
+    private ObjectWrapper ow = new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build();
+    private SimpleHash m_cHashModel = new SimpleHash(ow);
+
+    /** Creates new TransformHashWrapper */
+    public TransformHashWrapper() {
+        m_cHashModel.put( "htmlEscape", new HtmlEscape() );
+        m_cHashModel.put( "compress", new StandardCompress() );
+        m_cHashModel.put( "escape", new TransformMethodWrapper1() );
+        m_cHashModel.put( "special", new TransformMethodWrapper2() );
+    }
+
+    /**
+     * Gets a <tt>TemplateModel</tt> from the hash.
+     *
+     * @param key the name by which the <tt>TemplateModel</tt>
+     * is identified in the template.
+     * @return the <tt>TemplateModel</tt> referred to by the key,
+     * or null if not found.
+     */
+    @Override
+    public TemplateModel get(String key) throws TemplateModelException {
+        return m_cHashModel.get( key );
+    }
+
+    /**
+     * @return true if this object is empty.
+     */
+    @Override
+    public boolean isEmpty() {
+        return false;
+    }
+
+    /**
+     * Returns the scalar's value as a String.
+     * @return the String value of this scalar.
+     */
+    @Override
+    public String getAsString() {
+        return "Utility transformations";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper1.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper1.java b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper1.java
new file mode 100644
index 0000000..8311134
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper1.java
@@ -0,0 +1,49 @@
+/*
+ * 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.test.templatesuite.models;
+
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateMethodModel;
+import org.apache.freemarker.core.util.HtmlEscape;
+import org.apache.freemarker.core.util.XmlEscape;
+
+/**
+ * Simple test of the interaction between MethodModels and TransformModels.
+ */
+public class TransformMethodWrapper1 implements TemplateMethodModel {
+
+    /**
+     * Executes a method call.
+     *
+     * @param arguments a <tt>List</tt> of <tt>String</tt> objects containing
+     * the values of the arguments passed to the method.
+     * @return the <tt>TemplateModel</tt> produced by the method, or null.
+     */
+    @Override
+    public Object exec(List arguments) {
+
+        if (( arguments.size() > 0 ) && ( arguments.get( 0 ).toString().equals( "xml" ))) {
+            return new XmlEscape();
+        } else {
+            return new HtmlEscape();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper2.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper2.java b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper2.java
new file mode 100644
index 0000000..96eb1a5
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformMethodWrapper2.java
@@ -0,0 +1,64 @@
+/*
+ * 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.test.templatesuite.models;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateMethodModel;
+
+/**
+ * Another test of the interaction between MethodModels and TransformModels.
+ */
+public class TransformMethodWrapper2 implements TemplateMethodModel {
+
+    /**
+     * Executes a method call.
+     *
+     * @param arguments a <tt>List</tt> of <tt>String</tt> objects containing
+     * the values of the arguments passed to the method.
+     * @return the <tt>TemplateModel</tt> produced by the method, or null.
+     */
+    @Override
+    public Object exec(List arguments) {
+        TransformModel1 cTransformer = new TransformModel1();
+        Iterator    iArgument = arguments.iterator();
+
+        // Sets up properties of the Transform model based on the arguments
+        // passed into this method
+
+        while ( iArgument.hasNext() ) {
+            String  aArgument = (String) iArgument.next();
+
+            if ( aArgument.equals( "quote" )) {
+                cTransformer.setQuotes( true );
+            } else if ( aArgument.equals( "tag" )) {
+                cTransformer.setTags( true );
+            } else if ( aArgument.equals( "ampersand" )) {
+                cTransformer.setAmpersands( true );
+            } else {
+                cTransformer.setComment( aArgument );
+            }
+        }
+
+        // Now return the transform class.
+        return cTransformer;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformModel1.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformModel1.java b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformModel1.java
new file mode 100644
index 0000000..0875679
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/TransformModel1.java
@@ -0,0 +1,175 @@
+/*
+ * 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.test.templatesuite.models;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Map;
+
+import org.apache.freemarker.core.model.TemplateTransformModel;
+
+/**
+ * A TemplateTransformModel that includes properties. These properties can be
+ * set at model construction time, or, for the purposes of this demonstration,
+ * can be passed in from a wrapper TemplateMethodModel.
+ */
+public class TransformModel1 implements TemplateTransformModel {
+
+    private boolean m_bAmpersands = false;
+    private boolean m_bQuotes = false;
+    private boolean m_bTags = false;
+    private String  m_aComment = "";
+
+    private static final int READER_BUFFER_SIZE = 4096;
+
+    @Override
+    public Writer getWriter(final Writer out,
+                            final Map args) {
+        final StringBuilder buf = new StringBuilder();
+        return new Writer(out) {
+            @Override
+            public void write(char cbuf[], int off, int len) {
+                buf.append(cbuf, off, len);
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() throws IOException {
+                StringReader sr = new StringReader(buf.toString());
+                StringWriter sw = new StringWriter();
+                transform(sr, sw);
+                out.write(sw.toString());
+            }
+        };
+    }
+
+
+    /**
+     * Indicates whether we escape ampersands. This property can be set either
+     * while the model is being constructed, or via a property passed in through
+     * a <code>TemplateMethodModel</code>.
+     */
+    public void setAmpersands( boolean bAmpersands ) {
+        m_bAmpersands = bAmpersands;
+    }
+
+    /**
+     * Indicates whether we escape quotes. This property can be set either
+     * while the model is being constructed, or via a property passed in through
+     * a <code>TemplateMethodModel</code>.
+     */
+    public void setQuotes( boolean bQuotes ) {
+        m_bQuotes = bQuotes;
+    }
+
+    /**
+     * Indicates whether we escape tags. This property can be set either
+     * while the model is being constructed, or via a property passed in through
+     * a <code>TemplateMethodModel</code>.
+     */
+    public void setTags( boolean bTags ) {
+        m_bTags = bTags;
+    }
+
+    /**
+     * Sets a comment for this transformation. This property can be set either
+     * while the model is being constructed, or via a property passed in through
+     * a <code>TemplateMethodModel</code>.
+     */
+    public void setComment( String aComment ) {
+        m_aComment = aComment;
+    }
+
+    /**
+     * Performs a transformation/filter on FreeMarker output.
+     *
+     * @param source the input to be transformed
+     * @param output the destination of the transformation
+     */
+    public void transform(Reader source, Writer output)
+    throws IOException {
+        // Output the source, converting unsafe certain characters to their
+        // equivalent entities.
+        int n = 0;
+        boolean bCommentSent = false;
+        char[]  aBuffer = new char[ READER_BUFFER_SIZE ];
+        int i = source.read( aBuffer );
+        while (i >= 0) {
+            for ( int j = 0; j < i; j++ ) {
+                char c = aBuffer[j];
+                switch (c) {
+                    case '&':
+                        if ( m_bAmpersands ) {
+                            output.write("&amp;");
+                        } else {
+                            output.write( c );
+                        }
+                        break;
+                    case '<':
+                        if ( m_bTags ) {
+                            output.write("&lt;");
+                        } else {
+                            output.write( c );
+                        }
+                        break;
+                    case '>':
+                        if ( m_bTags ) {
+                            output.write("&gt;");
+                        } else {
+                            output.write( c );
+                        }
+                        break;
+                    case '"':
+                        if ( m_bQuotes ) {
+                            output.write("&quot;");
+                        } else {
+                            output.write( c );
+                        }
+                        break;
+                    case '\'':
+                        if ( m_bQuotes ) {
+                            output.write("&apos;");
+                        } else {
+                            output.write( c );
+                        }
+                        break;
+                    case '*':
+                        if ( ! bCommentSent ) {
+                            output.write( m_aComment );
+                            bCommentSent = true;
+                        } else {
+                            output.write( c );
+                        }
+                        break;
+                    default:
+                        output.write(c);
+                }
+                n++;
+            }
+            i = source.read( aBuffer );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/VarArgTestModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/VarArgTestModel.java b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/VarArgTestModel.java
new file mode 100644
index 0000000..940cb17
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/models/VarArgTestModel.java
@@ -0,0 +1,63 @@
+/*
+ * 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.test.templatesuite.models;
+
+import java.util.Date;
+
+public class VarArgTestModel {
+
+    public int bar(Integer... xs) {
+        int sum = 0;
+        for (Integer x : xs) {
+            if (x != null) {
+                sum *= 100;
+                sum += x;
+            }
+        }
+        return sum;
+    }
+
+    public int bar2(int first, int... xs) {
+        int sum = 0;
+        for (int x : xs) {
+            sum *= 100;
+            sum += x;
+        }
+        return -(sum * 100 + first);
+    }
+    
+    public int overloaded(int x, int y) {
+        return x * 100 + y;
+    }
+
+    public int overloaded(int... xs) {
+        int sum = 0;
+        for (int x : xs) {
+            sum *= 100;
+            sum += x;
+        }
+        return -sum;
+    }
+    
+    public String noVarArgs(String s, boolean b, int i, Date d) {
+        return s + ", " + b + ", " + i + ", " + d.getTime();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/package.html
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/package.html b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/package.html
new file mode 100644
index 0000000..cabffb0
--- /dev/null
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/test/templatesuite/package.html
@@ -0,0 +1,42 @@
+<!--
+  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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<p>JUnit test-suite that processes FreeMarker templates and compare their
+output to reference files.</p>
+
+<p>To add a test-case, go to
+<tt>src/test/resources/freemarker/test/templatesuite/</tt> and inside that
+directory:</p>
+<ol>
+  <li>Add a template to under <tt>templates/</tt> with whatever meaningful
+      file name</li>
+  <li>Add the expected output to <tt>references/</tt> with exactly the same
+      file name</li>
+  <li>Add a new <tt>testcase</tt> elemen to <tt>testcases.xml</tt></li>
+  <li>If you want to add items to the data-model or do something else special,
+      modify the <tt>setUp()</tt> method in
+      <tt>src/test/java/freemarker/test/templatesuite/TemplateTestCase.java</tt>
+      </li>
+</ol>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
new file mode 100644
index 0000000..1cdd496
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ast
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+#mixed_content  // o.a.f.c.ASTImplicitParent
+    #text  // o.a.f.c.ASTStaticText
+        - content: "1 "  // String
+    @  // o.a.f.c.ASTDirUserDefined
+        - callee: foo  // o.a.f.c.ASTExpVariable
+        - argument name: "x"  // String
+        - argument value: 1  // o.a.f.c.ASTExpNumberLiteral
+        - argument name: "y"  // String
+        - argument value: 2  // o.a.f.c.ASTExpNumberLiteral
+        - target loop variable: "b1"  // String
+        - target loop variable: "b2"  // String
+        #text  // o.a.f.c.ASTStaticText
+            - content: "x"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n2 "  // String
+    @  // o.a.f.c.ASTDirUserDefined
+        - callee: .  // o.a.f.c.ASTExpDot
+            - left-hand operand: ns  // o.a.f.c.ASTExpVariable
+            - right-hand operand: "bar"  // String
+        - argument value: 1  // o.a.f.c.ASTExpNumberLiteral
+        - argument value: 2  // o.a.f.c.ASTExpNumberLiteral
+        - target loop variable: "b1"  // String
+        - target loop variable: "b2"  // String
+        #text  // o.a.f.c.ASTStaticText
+            - content: "y"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n3 "  // String
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: 123  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: 123  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: ns  // o.a.f.c.ASTExpVariable
+    #global  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: 123  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "3"  // Integer
+        - namespace: null  // Null
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n4 "  // String
+    #if-#elseif-#else-container  // o.a.f.c.ASTDirIfElseIfElseContainer
+        #if  // o.a.f.c.ASTDirIfOrElseOrElseIf
+            - condition: ==  // o.a.f.c.ASTExpComparison
+                - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                    - left-hand operand: x  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: 0  // o.a.f.c.ASTExpNumberLiteral
+            - AST-node subtype: "0"  // Integer
+            #text  // o.a.f.c.ASTStaticText
+                - content: "foo"  // String
+            ${...}  // o.a.f.c.ASTDollarInterpolation
+                - content: y  // o.a.f.c.ASTExpVariable
+            #text  // o.a.f.c.ASTStaticText
+                - content: "bar"  // String
+        #else  // o.a.f.c.ASTDirIfOrElseOrElseIf
+            - condition: null  // Null
+            - AST-node subtype: "1"  // Integer
+            ${...}  // o.a.f.c.ASTDollarInterpolation
+                - content: "static"  // o.a.f.c.ASTExpStringLiteral
+            ${...}  // o.a.f.c.ASTDollarInterpolation
+                - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
+                    - value part: "x"  // String
+                    - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+                        - content: *  // o.a.f.c.ArithmeticExpression
+                            - left-hand operand: baaz  // o.a.f.c.ASTExpVariable
+                            - right-hand operand: 10  // o.a.f.c.ASTExpNumberLiteral
+                            - AST-node subtype: "1"  // Integer
+                    - value part: "y"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n5 "  // String
+    #switch  // o.a.f.c.ASTDirSwitch
+        - value: x  // o.a.f.c.ASTExpVariable
+        #case  // o.a.f.c.ASTDirCase
+            - condition: 1  // o.a.f.c.ASTExpNumberLiteral
+            - AST-node subtype: "0"  // Integer
+            #text  // o.a.f.c.ASTStaticText
+                - content: "one"  // String
+        #case  // o.a.f.c.ASTDirCase
+            - condition: 2  // o.a.f.c.ASTExpNumberLiteral
+            - AST-node subtype: "0"  // Integer
+            #text  // o.a.f.c.ASTStaticText
+                - content: "two"  // String
+        #default  // o.a.f.c.ASTDirCase
+            - condition: null  // Null
+            - AST-node subtype: "1"  // Integer
+            #text  // o.a.f.c.ASTStaticText
+                - content: "more"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n6 "  // String
+    #macro  // o.a.f.c.ASTDirMacro
+        - assignment target: "foo"  // String
+        - parameter name: "x"  // String
+        - parameter default: null  // Null
+        - parameter name: "y"  // String
+        - parameter default: 2  // o.a.f.c.ASTExpNumberLiteral
+        - parameter name: "z"  // String
+        - parameter default: +  // o.a.f.c.ASTExpAddOrConcat
+            - left-hand operand: y  // o.a.f.c.ASTExpVariable
+            - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+        - catch-all parameter name: "q"  // String
+        - AST-node subtype: "0"  // Integer
+        #nested  // o.a.f.c.ASTDirNested
+            - passed value: x  // o.a.f.c.ASTExpVariable
+            - passed value: y  // o.a.f.c.ASTExpVariable
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n7 "  // String
+    #function  // o.a.f.c.ASTDirMacro
+        - assignment target: "foo"  // String
+        - parameter name: "x"  // String
+        - parameter default: null  // Null
+        - parameter name: "y"  // String
+        - parameter default: null  // Null
+        - catch-all parameter name: null  // Null
+        - AST-node subtype: "1"  // Integer
+        #local  // o.a.f.c.ASTDirAssignment
+            - assignment target: "x"  // String
+            - assignment operator: "="  // String
+            - assignment source: 123  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "2"  // Integer
+            - namespace: null  // Null
+        #return  // o.a.f.c.ASTDirReturn
+            - value: 1  // o.a.f.c.ASTExpNumberLiteral
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n8 "  // String
+    #list  // o.a.f.c.ASTDirList
+        - list source: xs  // o.a.f.c.ASTExpVariable
+        - target loop variable: "x"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n9 "  // String
+    #list-#else-container  // o.a.f.c.ASTDirListElseContainer
+        #list  // o.a.f.c.ASTDirList
+            - list source: xs  // o.a.f.c.ASTExpVariable
+            #text  // o.a.f.c.ASTStaticText
+                - content: "["  // String
+            #items  // o.a.f.c.ASTDirItems
+                - target loop variable: "x"  // String
+                ${...}  // o.a.f.c.ASTDollarInterpolation
+                    - content: x  // o.a.f.c.ASTExpVariable
+                #sep  // o.a.f.c.ASTDirSep
+                    #text  // o.a.f.c.ASTStaticText
+                        - content: ", "  // String
+            #text  // o.a.f.c.ASTStaticText
+                - content: "]"  // String
+        #else  // o.a.f.c.ASTDirElseOfList
+            #text  // o.a.f.c.ASTStaticText
+                - content: "None"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n10 "  // String
+    #--...--  // o.a.f.c.ASTComment
+        - content: " A comment "  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n11 "  // String
+    #outputformat  // o.a.f.c.ASTDirOutputFormat
+        - value: "XML"  // o.a.f.c.ASTExpStringLiteral
+        #noautoesc  // o.a.f.c.ASTDirNoAutoEsc
+            ${...}  // o.a.f.c.ASTDollarInterpolation
+                - content: a  // o.a.f.c.ASTExpVariable
+            #autoesc  // o.a.f.c.ASTDirAutoEsc
+                ${...}  // o.a.f.c.ASTDollarInterpolation
+                    - content: b  // o.a.f.c.ASTExpVariable
+            ${...}  // o.a.f.c.ASTDollarInterpolation
+                - content: c  // o.a.f.c.ASTExpVariable

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ftl
new file mode 100644
index 0000000..8c8953a
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-1.ftl
@@ -0,0 +1,29 @@
+<#--
+  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.
+-->
+1 <@foo x=1 y=2; b1, b2>x</...@foo>
+2 <@ns.bar 1 2; b1, b2>y</@>
+3 <#assign x = 123><#assign x = 123 in ns><#global x = 123>
+4 <#if x + 1 == 0>foo${y}bar<#else>${"static"}${'x${baaz * 10}y'}</#if>
+5 <#switch x><#case 1>one<#case 2>two<#default>more</#switch>
+6 <#macro foo x y=2 z=y+1 q...><#nested x y></#macro>
+7 <#function foo x y><#local x = 123><#return 1></#function>
+8 <#list xs as x></#list>
+9 <#list xs>[<#items as x>${x}<#sep>, </#items>]<#else>None</#list>
+10 <#-- A comment -->
+11 <#outputFormat "XML"><#noAutoEsc>${a}<#autoEsc>${b}</#autoEsc>${c}</#noAutoEsc></#outputFormat>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ast
new file mode 100644
index 0000000..4839892
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ast
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+#mixed_content  // o.a.f.c.ASTImplicitParent
+    #text  // o.a.f.c.ASTStaticText
+        - content: "1 "  // String
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n2 "  // String
+    #assign  // o.a.f.c.ASTDirAssignmentsContainer
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "x"  // String
+            - assignment operator: "="  // String
+            - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "y"  // String
+            - assignment operator: "="  // String
+            - assignment source: 2  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n3 "  // String
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: ns  // o.a.f.c.ASTExpVariable
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n4 "  // String
+    #assign  // o.a.f.c.ASTDirAssignmentsContainer
+        - variable scope: "1"  // Integer
+        - namespace: ns  // o.a.f.c.ASTExpVariable
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "x"  // String
+            - assignment operator: "="  // String
+            - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: ns  // o.a.f.c.ASTExpVariable
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "y"  // String
+            - assignment operator: "="  // String
+            - assignment source: 2  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: ns  // o.a.f.c.ASTExpVariable
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n5 "  // String
+    #global  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "3"  // Integer
+        - namespace: null  // Null
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n6 "  // String
+    #global  // o.a.f.c.ASTDirAssignmentsContainer
+        - variable scope: "3"  // Integer
+        - namespace: null  // Null
+        #global  // o.a.f.c.ASTDirAssignment
+            - assignment target: "x"  // String
+            - assignment operator: "="  // String
+            - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "3"  // Integer
+            - namespace: null  // Null
+        #global  // o.a.f.c.ASTDirAssignment
+            - assignment target: "y"  // String
+            - assignment operator: "="  // String
+            - assignment source: 2  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "3"  // Integer
+            - namespace: null  // Null
+    #macro  // o.a.f.c.ASTDirMacro
+        - assignment target: "m"  // String
+        - catch-all parameter name: null  // Null
+        - AST-node subtype: "0"  // Integer
+        #text  // o.a.f.c.ASTStaticText
+            - content: "  7 "  // String
+        #local  // o.a.f.c.ASTDirAssignment
+            - assignment target: "x"  // String
+            - assignment operator: "="  // String
+            - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "2"  // Integer
+            - namespace: null  // Null
+        #text  // o.a.f.c.ASTStaticText
+            - content: "\n  8 "  // String
+        #local  // o.a.f.c.ASTDirAssignmentsContainer
+            - variable scope: "2"  // Integer
+            - namespace: null  // Null
+            #local  // o.a.f.c.ASTDirAssignment
+                - assignment target: "x"  // String
+                - assignment operator: "="  // String
+                - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+                - variable scope: "2"  // Integer
+                - namespace: null  // Null
+            #local  // o.a.f.c.ASTDirAssignment
+                - assignment target: "y"  // String
+                - assignment operator: "="  // String
+                - assignment source: 2  // o.a.f.c.ASTExpNumberLiteral
+                - variable scope: "2"  // Integer
+                - namespace: null  // Null
+        #text  // o.a.f.c.ASTStaticText
+            - content: "\n"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "9 "  // String
+    #assign  // o.a.f.c.ASTDirAssignmentsContainer
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "a"  // String
+            - assignment operator: "+="  // String
+            - assignment source: 1  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "b"  // String
+            - assignment operator: "-="  // String
+            - assignment source: 2  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "c"  // String
+            - assignment operator: "*="  // String
+            - assignment source: 3  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "d"  // String
+            - assignment operator: "/="  // String
+            - assignment source: 4  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "e"  // String
+            - assignment operator: "%="  // String
+            - assignment source: 5  // o.a.f.c.ASTExpNumberLiteral
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "f"  // String
+            - assignment operator: "++"  // String
+            - assignment source: null  // Null
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null
+        #assign  // o.a.f.c.ASTDirAssignment
+            - assignment target: "g"  // String
+            - assignment operator: "--"  // String
+            - assignment source: null  // Null
+            - variable scope: "1"  // Integer
+            - namespace: null  // Null

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ftl
new file mode 100644
index 0000000..c070635
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-assignments.ftl
@@ -0,0 +1,29 @@
+<#--
+  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.
+-->
+1 <#assign x = 1>
+2 <#assign x = 1, y = 2>
+3 <#assign x = 1 in ns>
+4 <#assign x = 1, y = 2 in ns>
+5 <#global x = 1>
+6 <#global x = 1, y = 2>
+<#macro m>
+  7 <#local x = 1>
+  8 <#local x = 1, y = 2>
+</#macro>
+9 <#assign a += 1, b -= 2, c *= 3, d /= 4, e %= 5, f++, g-->

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
new file mode 100644
index 0000000..b8b696f
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ast
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+#mixed_content  // o.a.f.c.ASTImplicitParent
+    ${...}  // o.a.f.c.ASTDollarInterpolation
+        - content: ?trim  // o.a.f.c.BuiltInsForStringsBasic$trimBI
+            - left-hand operand: x  // o.a.f.c.ASTExpVariable
+            - right-hand operand: "trim"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n"  // String
+    ${...}  // o.a.f.c.ASTDollarInterpolation
+        - content: ...(...)  // o.a.f.c.ASTExpMethodCall
+            - callee: ?left_pad  // o.a.f.c.BuiltInsForStringsBasic$padBI
+                - left-hand operand: x  // o.a.f.c.ASTExpVariable
+                - right-hand operand: "left_pad"  // String
+            - argument value: 5  // o.a.f.c.ASTExpNumberLiteral
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n"  // String
+    ${...}  // o.a.f.c.ASTDollarInterpolation
+        - content: ...(...)  // o.a.f.c.ASTExpMethodCall
+            - callee: ?left_pad  // o.a.f.c.BuiltInsForStringsBasic$padBI
+                - left-hand operand: x  // o.a.f.c.ASTExpVariable
+                - right-hand operand: "left_pad"  // String
+            - argument value: 5  // o.a.f.c.ASTExpNumberLiteral
+            - argument value: "-"  // o.a.f.c.ASTExpStringLiteral
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n"  // String
+    ${...}  // o.a.f.c.ASTDollarInterpolation
+        - content: ?then(...)  // o.a.f.c.BuiltInsWithParseTimeParameters$then_BI
+            - left-hand operand: x  // o.a.f.c.ASTExpVariable
+            - right-hand operand: "then"  // String
+            - argument value: "y"  // o.a.f.c.ASTExpStringLiteral
+            - argument value: "n"  // o.a.f.c.ASTExpStringLiteral
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n"  // String
+    ${...}  // o.a.f.c.ASTDollarInterpolation
+        - content: ?switch(...)  // o.a.f.c.BuiltInsWithParseTimeParameters$switch_BI
+            - left-hand operand: x  // o.a.f.c.ASTExpVariable
+            - right-hand operand: "switch"  // String
+            - argument value: 1  // o.a.f.c.ASTExpNumberLiteral
+            - argument value: 11  // o.a.f.c.ASTExpNumberLiteral
+            - argument value: 2  // o.a.f.c.ASTExpNumberLiteral
+            - argument value: 22  // o.a.f.c.ASTExpNumberLiteral
+            - argument value: 33  // o.a.f.c.ASTExpNumberLiteral

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ftl
new file mode 100644
index 0000000..74aee52
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-builtins.ftl
@@ -0,0 +1,23 @@
+<#--
+  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.
+-->
+${x?trim}
+${x?left_pad(5)}
+${x?left_pad(5, '-')}
+${x?then('y', 'n')}
+${x?switch(1, 11, 2, 22, 33)}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
new file mode 100644
index 0000000..7770d72
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ast
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+#mixed_content  // o.a.f.c.ASTImplicitParent; Location 1:1-18:8
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 1:1-1:15
+        - condition: exp  // o.a.f.c.ASTExpVariable; Location 1:6-1:8
+        - AST-node subtype: "0"  // Integer
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 2:1-2:16
+        - condition: exp  // o.a.f.c.ASTExpVariable; Location 2:6-2:8
+        - AST-node subtype: "0"  // Integer
+        #text  // o.a.f.c.ASTStaticText; Location 2:10-2:10
+            - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 2:17-2:17
+        - content: "\n"  // String
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 3:1-3:20
+        - condition: exp  // o.a.f.c.ASTExpVariable; Location 3:6-3:8
+        - AST-node subtype: "0"  // Integer
+        ${...}  // o.a.f.c.ASTDollarInterpolation; Location 3:10-3:13
+            - content: 1  // o.a.f.c.ASTExpNumberLiteral; Location 3:12-3:12
+        #text  // o.a.f.c.ASTStaticText; Location 3:14-3:14
+            - content: "2"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 3:21-3:21
+        - content: "\n"  // String
+    #if-#elseif-#else-container  // o.a.f.c.ASTDirIfElseIfElseContainer; Location 4:1-4:22
+        #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 4:1-4:9
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 4:6-4:8
+            - AST-node subtype: "0"  // Integer
+        #else  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 4:10-4:16
+            - condition: null  // Null
+            - AST-node subtype: "1"  // Integer
+    #if-#elseif-#else-container  // o.a.f.c.ASTDirIfElseIfElseContainer; Location 5:1-5:24
+        #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 5:1-5:10
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 5:6-5:8
+            - AST-node subtype: "0"  // Integer
+            #text  // o.a.f.c.ASTStaticText; Location 5:10-5:10
+                - content: "1"  // String
+        #else  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 5:11-5:18
+            - condition: null  // Null
+            - AST-node subtype: "1"  // Integer
+            #text  // o.a.f.c.ASTStaticText; Location 5:18-5:18
+                - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 5:25-5:25
+        - content: "\n"  // String
+    #if-#elseif-#else-container  // o.a.f.c.ASTDirIfElseIfElseContainer; Location 6:1-6:32
+        #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 6:1-6:14
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 6:6-6:8
+            - AST-node subtype: "0"  // Integer
+            ${...}  // o.a.f.c.ASTDollarInterpolation; Location 6:10-6:13
+                - content: 1  // o.a.f.c.ASTExpNumberLiteral; Location 6:12-6:12
+            #text  // o.a.f.c.ASTStaticText; Location 6:14-6:14
+                - content: "2"  // String
+        #else  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 6:15-6:26
+            - condition: null  // Null
+            - AST-node subtype: "1"  // Integer
+            ${...}  // o.a.f.c.ASTDollarInterpolation; Location 6:22-6:25
+                - content: 1  // o.a.f.c.ASTExpNumberLiteral; Location 6:24-6:24
+            #text  // o.a.f.c.ASTStaticText; Location 6:26-6:26
+                - content: "2"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 6:33-6:33
+        - content: "\n"  // String
+    #if-#elseif-#else-container  // o.a.f.c.ASTDirIfElseIfElseContainer; Location 7:1-7:28
+        #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 7:1-7:9
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 7:6-7:8
+            - AST-node subtype: "0"  // Integer
+        #elseif  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 7:10-7:22
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 7:19-7:21
+            - AST-node subtype: "2"  // Integer
+    #if-#elseif-#else-container  // o.a.f.c.ASTDirIfElseIfElseContainer; Location 8:1-8:29
+        #if  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 8:1-8:9
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 8:6-8:8
+            - AST-node subtype: "0"  // Integer
+        #elseif  // o.a.f.c.ASTDirIfOrElseOrElseIf; Location 8:10-8:23
+            - condition: exp  // o.a.f.c.ASTExpVariable; Location 8:19-8:21
+            - AST-node subtype: "2"  // Integer
+            #text  // o.a.f.c.ASTStaticText; Location 8:23-8:23
+                - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 8:30-8:30
+        - content: "\n"  // String
+    #attempt  // o.a.f.c.ASTDirAttemptRecoverContainer; Location 9:1-9:31
+        - error handler: #recover  // o.a.f.c.ASTDirRecover; Location 9:11-9:20
+        #recover  // o.a.f.c.ASTDirRecover; Location 9:11-9:20
+    #attempt  // o.a.f.c.ASTDirAttemptRecoverContainer; Location 10:1-10:33
+        - error handler: #recover  // o.a.f.c.ASTDirRecover; Location 10:12-10:22
+            #text  // o.a.f.c.ASTStaticText; Location 10:22-10:22
+                - content: "1"  // String
+        #text  // o.a.f.c.ASTStaticText; Location 10:11-10:11
+            - content: "1"  // String
+        #recover  // o.a.f.c.ASTDirRecover; Location 10:12-10:22
+            #text  // o.a.f.c.ASTStaticText; Location 10:22-10:22
+                - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 10:34-10:34
+        - content: "\n"  // String
+    #list  // o.a.f.c.ASTDirList; Location 11:1-11:22
+        - list source: s  // o.a.f.c.ASTExpVariable; Location 11:8-11:8
+        - target loop variable: "i"  // String
+    #list  // o.a.f.c.ASTDirList; Location 12:1-12:23
+        - list source: s  // o.a.f.c.ASTExpVariable; Location 12:8-12:8
+        - target loop variable: "i"  // String
+        #text  // o.a.f.c.ASTStaticText; Location 12:15-12:15
+            - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 12:24-12:24
+        - content: "\n"  // String
+    #list  // o.a.f.c.ASTDirList; Location 13:1-13:28
+        - list source: s  // o.a.f.c.ASTExpVariable; Location 13:8-13:8
+        - target loop variable: "i"  // String
+        #sep  // o.a.f.c.ASTDirSep; Location 13:15-13:20
+    #list  // o.a.f.c.ASTDirList; Location 14:1-14:30
+        - list source: s  // o.a.f.c.ASTExpVariable; Location 14:8-14:8
+        - target loop variable: "i"  // String
+        #text  // o.a.f.c.ASTStaticText; Location 14:15-14:15
+            - content: "1"  // String
+        #sep  // o.a.f.c.ASTDirSep; Location 14:16-14:22
+            #text  // o.a.f.c.ASTStaticText; Location 14:22-14:22
+                - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 14:31-14:31
+        - content: "\n"  // String
+    #list  // o.a.f.c.ASTDirList; Location 15:1-15:45
+        - list source: s  // o.a.f.c.ASTExpVariable; Location 15:8-15:8
+        #items  // o.a.f.c.ASTDirItems; Location 15:10-15:37
+            - target loop variable: "i"  // String
+            #sep  // o.a.f.c.ASTDirSep; Location 15:23-15:28
+    #list  // o.a.f.c.ASTDirList; Location 16:1-16:49
+        - list source: s  // o.a.f.c.ASTExpVariable; Location 16:8-16:8
+        #text  // o.a.f.c.ASTStaticText; Location 16:10-16:10
+            - content: "1"  // String
+        #items  // o.a.f.c.ASTDirItems; Location 16:11-16:40
+            - target loop variable: "i"  // String
+            #text  // o.a.f.c.ASTStaticText; Location 16:24-16:24
+                - content: "1"  // String
+            #sep  // o.a.f.c.ASTDirSep; Location 16:25-16:31
+                #text  // o.a.f.c.ASTStaticText; Location 16:31-16:31
+                    - content: "1"  // String
+        #text  // o.a.f.c.ASTStaticText; Location 16:41-16:41
+            - content: "1"  // String
+    #text  // o.a.f.c.ASTStaticText; Location 16:50-17:2
+        - content: "\n1\n"  // String
+    ${...}  // o.a.f.c.ASTDollarInterpolation; Location 18:1-18:8
+        - content: +  // o.a.f.c.ASTExpAddOrConcat; Location 18:3-18:7
+            - left-hand operand: x  // o.a.f.c.ASTExpVariable; Location 18:3-18:3
+            - right-hand operand: y  // o.a.f.c.ASTExpVariable; Location 18:7-18:7

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ftl
new file mode 100644
index 0000000..99fa244
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-locations.ftl
@@ -0,0 +1,36 @@
+<#--
+  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.
+-->
+<#if exp></#if>
+<#if exp>1</#if>
+<#if exp>${1}2</#if>
+<#if exp><#else></#if>
+<#if exp>1<#else>1</#if>
+<#if exp>${1}2<#else>${1}2</#if>
+<#if exp><#elseif exp></#if>
+<#if exp><#elseif exp>1</#if>
+<#attempt><#recover></#attempt>
+<#attempt>1<#recover>1</#attempt>
+<#list s as i></#list>
+<#list s as i>1</#list>
+<#list s as i><#sep></#list>
+<#list s as i>1<#sep>1</#list>
+<#list s><#items as i><#sep></#items></#list>
+<#list s>1<#items as i>1<#sep>1</#items>1</#list>
+1
+${x + y}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ast
new file mode 100644
index 0000000..41fc90b
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ast
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#if  // o.a.f.c.ASTDirIfOrElseOrElseIf
+    - condition: true  // o.a.f.c.ASTExpBooleanLiteral
+    - AST-node subtype: "0"  // Integer
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf
+        - condition: true  // o.a.f.c.ASTExpBooleanLiteral
+        - AST-node subtype: "0"  // Integer
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf
+        - condition: true  // o.a.f.c.ASTExpBooleanLiteral
+        - AST-node subtype: "0"  // Integer
+        #text  // o.a.f.c.ASTStaticText
+            - content: "        text\n"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "    "  // String
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf
+        - condition: true  // o.a.f.c.ASTExpBooleanLiteral
+        - AST-node subtype: "0"  // Integer
+        ${...}  // o.a.f.c.ASTDollarInterpolation
+            - content: x  // o.a.f.c.ASTExpVariable
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n"  // String

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ftl
new file mode 100644
index 0000000..53716e4
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-mixedcontentsimplifications.ftl
@@ -0,0 +1,26 @@
+<#--
+  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.
+-->
+<#if true>
+    <#if true>
+    </#if>
+    <#if true>
+        text
+    </#if>
+    <#if true>${x}</#if>
+</#if>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ast
new file mode 100644
index 0000000..7675522
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ast
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+#mixed_content  // o.a.f.c.ASTImplicitParent
+    #text  // o.a.f.c.ASTStaticText
+        - content: "a\n"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "b\n"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "c\n"  // String
+    #text  // o.a.f.c.ASTStaticText
+        - content: "d\n"  // String
+    #if  // o.a.f.c.ASTDirIfOrElseOrElseIf
+        - condition: true  // o.a.f.c.ASTExpBooleanLiteral
+        - AST-node subtype: "0"  // Integer

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ftl
new file mode 100644
index 0000000..501802a
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-multipleignoredchildren.ftl
@@ -0,0 +1,33 @@
+<#--
+  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.
+-->
+a
+<#compress></#compress>
+b
+<#compress></#compress>
+<#compress></#compress>
+c
+<#compress></#compress>
+<#compress></#compress>
+<#compress></#compress>
+d
+<#if true>
+  <#compress></#compress>
+  <#compress></#compress>
+  <#compress></#compress>
+</#if>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ast
new file mode 100644
index 0000000..eefa5b1
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ast
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+#outputformat  // o.a.f.c.ASTDirOutputFormat
+    - value: "HTML"  // o.a.f.c.ASTExpStringLiteral

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ftl
new file mode 100644
index 0000000..fbdb342
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-nestedignoredchildren.ftl
@@ -0,0 +1,19 @@
+<#--
+  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.
+-->
+<#outputFormat 'HTML'><#outputFormat 'HTML'><#outputFormat 'HTML'></#outputFormat></#outputFormat></#outputFormat>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
new file mode 100644
index 0000000..831c7dc
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ast
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+#mixed_content  // o.a.f.c.ASTImplicitParent
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: 0  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: null  // Null
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: 0  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..<  // o.a.f.c.ASTExpRange
+            - left-hand operand: 0  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..<  // o.a.f.c.ASTExpRange
+            - left-hand operand: 0  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..<  // o.a.f.c.ASTExpRange
+            - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..<  // o.a.f.c.ASTExpRange
+            - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: *  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - AST-node subtype: "1"  // Integer
+            - right-hand operand: *  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                - AST-node subtype: "1"  // Integer
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: ?abs  // o.a.f.c.BuiltInsForNumbers$absBI
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: "abs"  // String
+            - right-hand operand: ?abs  // o.a.f.c.BuiltInsForNumbers$absBI
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: "abs"  // String
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: ...(...)  // o.a.f.c.ASTExpMethodCall
+                - callee: ?index_of  // o.a.f.c.BuiltInsForStringsBasic$index_ofBI
+                    - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: "index_of"  // String
+                - argument value: "x"  // o.a.f.c.ASTExpStringLiteral
+            - right-hand operand: ...(...)  // o.a.f.c.ASTExpMethodCall
+                - callee: ?index_of  // o.a.f.c.BuiltInsForStringsBasic$index_ofBI
+                    - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: "index_of"  // String
+                - argument value: "y"  // o.a.f.c.ASTExpStringLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ==  // o.a.f.c.ASTExpComparison
+            - left-hand operand: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: m  // o.a.f.c.ASTExpVariable
+            - right-hand operand: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: o  // o.a.f.c.ASTExpVariable
+                - right-hand operand: p  // o.a.f.c.ASTExpVariable
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ==  // o.a.f.c.ASTExpComparison
+            - left-hand operand: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                    - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                        - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                        - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                    - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: -  // o.a.f.c.ArithmeticExpression
+                    - left-hand operand: -  // o.a.f.c.ArithmeticExpression
+                        - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                        - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                        - AST-node subtype: "0"  // Integer
+                    - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                    - AST-node subtype: "0"  // Integer
+            - right-hand operand: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                    - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                        - left-hand operand: o  // o.a.f.c.ASTExpVariable
+                        - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                    - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: -  // o.a.f.c.ArithmeticExpression
+                    - left-hand operand: -  // o.a.f.c.ArithmeticExpression
+                        - left-hand operand: p  // o.a.f.c.ASTExpVariable
+                        - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                        - AST-node subtype: "0"  // Integer
+                    - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                    - AST-node subtype: "0"  // Integer
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: a  // o.a.f.c.ASTExpVariable
+            - right-hand operand: +...  // o.a.f.c.ASTExpNegateOrPlus
+                - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                - AST-node subtype: "1"  // Integer
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: -  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: a  // o.a.f.c.ASTExpVariable
+                - AST-node subtype: "0"  // Integer
+            - right-hand operand: -...  // o.a.f.c.ASTExpNegateOrPlus
+                - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+                - AST-node subtype: "0"  // Integer
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ..*  // o.a.f.c.ASTExpRange
+            - left-hand operand: *  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: a  // o.a.f.c.ASTExpVariable
+                - AST-node subtype: "1"  // Integer
+            - right-hand operand: 2  // o.a.f.c.ASTExpNumberLiteral
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ||  // o.a.f.c.ASTExpOr
+            - left-hand operand: &&  // o.a.f.c.ASTExpAnd
+                - left-hand operand: a  // o.a.f.c.ASTExpVariable
+                - right-hand operand: ..  // o.a.f.c.ASTExpRange
+                    - left-hand operand: b  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: c  // o.a.f.c.ASTExpVariable
+            - right-hand operand: d  // o.a.f.c.ASTExpVariable
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #assign  // o.a.f.c.ASTDirAssignment
+        - assignment target: "x"  // String
+        - assignment operator: "="  // String
+        - assignment source: ||  // o.a.f.c.ASTExpOr
+            - left-hand operand: &&  // o.a.f.c.ASTExpAnd
+                - left-hand operand: ..  // o.a.f.c.ASTExpRange
+                    - left-hand operand: a  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: null  // Null
+                - right-hand operand: ..  // o.a.f.c.ASTExpRange
+                    - left-hand operand: b  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: null  // Null
+            - right-hand operand: d  // o.a.f.c.ASTExpVariable
+        - variable scope: "1"  // Integer
+        - namespace: null  // Null
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n"  // String
+    ${...}  // o.a.f.c.ASTDollarInterpolation
+        - content: ...(...)  // o.a.f.c.ASTExpMethodCall
+            - callee: f  // o.a.f.c.ASTExpVariable
+            - argument value: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: null  // Null
+            - argument value: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: -  // o.a.f.c.ArithmeticExpression
+                    - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                    - AST-node subtype: "0"  // Integer
+                - right-hand operand: null  // Null
+            - argument value: ..  // o.a.f.c.ASTExpRange
+                - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                    - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: -  // o.a.f.c.ArithmeticExpression
+                    - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                    - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                    - AST-node subtype: "0"  // Integer
+    #text  // o.a.f.c.ASTStaticText
+        - content: "\n\n"  // String
+    @  // o.a.f.c.ASTDirUserDefined
+        - callee: m  // o.a.f.c.ASTExpVariable
+        - argument value: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: *  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - right-hand operand: m  // o.a.f.c.ASTExpVariable
+                - AST-node subtype: "1"  // Integer
+            - right-hand operand: -  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - AST-node subtype: "0"  // Integer
+        - argument value: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: +  // o.a.f.c.ASTExpAddOrConcat
+                - left-hand operand: m  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+            - right-hand operand: -  // o.a.f.c.ArithmeticExpression
+                - left-hand operand: n  // o.a.f.c.ASTExpVariable
+                - right-hand operand: 1  // o.a.f.c.ASTExpNumberLiteral
+                - AST-node subtype: "0"  // Integer
+        - argument value: ..  // o.a.f.c.ASTExpRange
+            - left-hand operand: m  // o.a.f.c.ASTExpVariable
+            - right-hand operand: null  // Null

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/28a276c8/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ftl
new file mode 100644
index 0000000..cb55c8a
--- /dev/null
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-range.ftl
@@ -0,0 +1,47 @@
+<#--
+  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.
+-->
+<#assign x = 0..>
+
+<#assign x = 0..1>
+<#assign x = 0..<1>
+<#assign x = 0..!1>
+
+<#assign x = n + 1 .. m + 2>
+<#assign x = n + 1 ..< m + 2>
+<#assign x = n + 1 ..! m + 2>
+
+<#assign x = n * 1 .. m * 2>
+
+<#assign x = n?abs .. m?abs>
+
+<#assign x = n?index_of('x') .. m?index_of('y')>
+
+<#assign x = n..m == o..p>
+
+<#assign x = n+1+2..m-1-2 == o+1+2..p-1-2>
+
+<#assign x = 1+a..+2>
+<#assign x = 1-a..-2>
+<#assign x = 1*a..*2> 
+<#assign x = a && b..c || d> 
+<#assign x = a.. && b.. || d> 
+
+${f(m.., m-1.., m+1..n-1)}
+
+<@m 1 * m .. m - 1 m + 1 .. n - 1 m .. />