Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 274778428 -> 9a13687cc


Forward ported from 2.3-gae: [=exp] interpolation syntax


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/9a13687c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/9a13687c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/9a13687c

Branch: refs/heads/3
Commit: 9a13687cc003d02b7147d5ebbdd555e2b9c994e0
Parents: 2747784
Author: ddekany <ddek...@apache.org>
Authored: Sun Mar 18 17:32:07 2018 +0100
Committer: ddekany <ddek...@apache.org>
Committed: Sun Mar 18 18:41:29 2018 +0100

----------------------------------------------------------------------
 .../freemarker/core/ConfigurationTest.java      |  21 ++-
 .../core/InterpolationSyntaxTest.java           |  82 ++++++++++
 .../core/TemplateConfigurationTest.java         |  11 +-
 .../org/apache/freemarker/core/ast-1.ast        |  16 +-
 .../org/apache/freemarker/core/ast-builtins.ast |  10 +-
 .../apache/freemarker/core/ast-locations.ast    |   8 +-
 .../core/ast-mixedcontentsimplifications.ast    |   2 +-
 .../org/apache/freemarker/core/ast-range.ast    |   2 +-
 .../freemarker/core/ast-strlitinterpolation.ast |  32 ++--
 .../freemarker/core/ast-whitespacestripping.ast |   2 +-
 .../freemarker/core/ASTDollarInterpolation.java | 151 -------------------
 .../freemarker/core/ASTExpStringLiteral.java    |  11 +-
 .../freemarker/core/ASTInterpolation.java       | 138 +++++++++++++++--
 .../apache/freemarker/core/Configuration.java   |  18 ++-
 .../freemarker/core/InterpolationSyntax.java    |  28 ++++
 ...utableParsingAndProcessingConfiguration.java |  59 +++++++-
 .../freemarker/core/ParsingConfiguration.java   |   8 +
 .../core/ParsingConfigurationWithFallback.java  |  10 ++
 .../org/apache/freemarker/core/TagSyntax.java   |   4 +
 .../org/apache/freemarker/core/Template.java    |  14 ++
 .../freemarker/core/TemplateConfiguration.java  |  25 ++-
 .../core/model/impl/OverloadedNumberUtils.java  |   2 +-
 freemarker-core/src/main/javacc/FTL.jj          | 105 ++++++++-----
 23 files changed, 514 insertions(+), 245 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
index a66c65b..993b810 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ConfigurationTest.java
@@ -45,6 +45,7 @@ import java.util.Set;
 import java.util.TimeZone;
 import java.util.TreeSet;
 
+import org.apache.freemarker.core.Configuration.Builder;
 import org.apache.freemarker.core.model.TemplateStringModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
@@ -433,6 +434,24 @@ public class ConfigurationTest {
     }
 
     @Test
+    public void testInterpolationSyntaxSetting() throws TemplateException {
+        Builder cfgB = new Builder(VERSION_3_0_0);
+
+        // Default is "dollar":
+        assertEquals(InterpolationSyntax.DOLLAR, 
cfgB.getInterpolationSyntax());
+        
+        cfgB.setSetting("interpolationSyntax", "squareBracket");
+        assertEquals(InterpolationSyntax.SQUARE_BRACKET, 
cfgB.getInterpolationSyntax());
+        
+        try {
+            cfgB.setSetting("interpolationSyntax", "noSuchSyntax");
+            fail();
+        } catch (ConfigurationException e) {
+            assertThat(e.getMessage(), containsString("noSuchSyntax"));
+        }
+    }
+    
+    @Test
     public void testObjectWrapperSetSetting() throws Exception {
         Builder cfgB = new Builder(VERSION_3_0_0);
         {
@@ -617,7 +636,7 @@ public class ConfigurationTest {
         Builder cfgB = new Builder(VERSION_3_0_0);
         assertEquals(DEFAULT_INCOMPATIBLE_IMPROVEMENTS, 
cfgB.getIncompatibleImprovements());
         // This is the only valid value ATM:
-        cfgB.setSetting(INCOMPATIBLE_IMPROVEMENTS_KEY, "3.0.0");
+        
cfgB.setSetting(MutableParsingAndProcessingConfiguration.INCOMPATIBLE_IMPROVEMENTS_KEY,
 "3.0.0");
         assertEquals(VERSION_3_0_0, cfgB.getIncompatibleImprovements());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
index b352298..888b5a8 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
@@ -19,9 +19,13 @@
 
 package org.apache.freemarker.core;
 
+import static org.junit.Assert.*;
+
 import java.io.IOException;
+import java.io.StringWriter;
 
 import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.junit.Test;
 
 public class InterpolationSyntaxTest extends TemplateTest {
@@ -32,4 +36,82 @@ public class InterpolationSyntaxTest extends TemplateTest {
         assertOutput("#{1} ${1} ${'#{2} ${2}'}", "#{1} 1 #{2} 2");
     }
 
+    @Test
+    public void dollarInterpolationSyntaxTest() throws Exception {
+        assertOutput("${1} #{1} [=1]", "1 #{1} [=1]");
+        assertOutput(
+                "${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
+                "1 #{{'x': 1}['x']} [={'x': 1}['x']]");
+    }
+
+    @Test
+    public void squareBracketInterpolationSyntaxTest() throws Exception {
+        setConfiguration(new TestConfigurationBuilder()
+                .interpolationSyntax(InterpolationSyntax.SQUARE_BRACKET)
+                .build());
+        
+        assertOutput("${1} #{1} [=1]", "${1} #{1} 1");
+        assertOutput(
+                "${{'x': 1}['x']} #{{'x': 1}['x']} [={'x': 1}['x']]",
+                "${{'x': 1}['x']} #{{'x': 1}['x']} 1");
+
+        assertOutput("[=1]][=2]]", "1]2]");
+        assertOutput("[= 1 ][= <#-- c --> 2 <#-- c --> ]", "12");
+        assertOutput("[ =1]", "[ =1]");
+        
+        assertErrorContains("<#if [true][0]]></#if>", "\"]\"", "nothing open");
+        assertOutput("[#ftl][#if [true][0]]>[/#if]", ">");
+        
+        assertOutput("[='a[=1]b']", "a1b");
+        
+        StringWriter sw = new StringWriter();
+        new Template(null, "[= 1 + '[= 2 ]' ]", getConfiguration()).dump(sw);
+        assertEquals("[=1 + \"[=2]\"]", sw.toString());
+    }
+
+    @Test
+    public void squareBracketTagSyntaxStillWorks() throws Exception {
+        for (InterpolationSyntax intepolationSyntax : new 
InterpolationSyntax[] {
+                InterpolationSyntax.DOLLAR, InterpolationSyntax.SQUARE_BRACKET 
}) {
+            setConfiguration(new TestConfigurationBuilder()
+                    .tagSyntax(TagSyntax.SQUARE_BRACKET)
+                    .interpolationSyntax(intepolationSyntax)
+                    .build());
+            
+            assertOutput("[#if [true][0]]t[#else]f[/#if]", "t");
+        }
+    }
+    
+    @Test
+    public void legacyTagSyntaxGlitchStillWorksTest() throws Exception {
+        String ftl = "<#if [true][0]]t<#else]f</#if]";
+        
+        setConfiguration(new TestConfigurationBuilder()
+                .interpolationSyntax(InterpolationSyntax.DOLLAR)
+                .build());
+        assertOutput(ftl, "t");
+        
+        // Glitch is not emulated with this:
+        setConfiguration(new TestConfigurationBuilder()
+                .interpolationSyntax(InterpolationSyntax.SQUARE_BRACKET)
+                .build());
+        assertErrorContains(ftl, "\"]\"");
+    }
+
+    @Test
+    public void errorMessagesAreSquareBracketInterpolationSyntaxAwareTest() 
throws Exception {
+        assertErrorContains("<#if ${x}></#if>", "${...}", "${myExpression}");
+        assertErrorContains("<#if [=x]></#if>", "[=...]", "[=myExpression]");
+    }
+
+    @Test
+    public void unclosedSyntaxErrorTest() throws Exception {
+        assertErrorContains("${1", "unclosed \"{\"");
+        
+        setConfiguration(new TestConfigurationBuilder()
+                .interpolationSyntax(InterpolationSyntax.SQUARE_BRACKET)
+                .build());
+        assertErrorContains("[=1", "unclosed \"[\"");
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
index 2c85fc4..5f16c0d 100644
--- 
a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
+++ 
b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateConfigurationTest.java
@@ -18,7 +18,7 @@
  */
 package org.apache.freemarker.core;
 
-import static 
org.apache.freemarker.core.ProcessingConfiguration.MISSING_VALUE_MARKER;
+import static org.apache.freemarker.core.ProcessingConfiguration.*;
 import static org.junit.Assert.*;
 
 import java.beans.BeanInfo;
@@ -192,6 +192,7 @@ public class TemplateConfigurationTest {
         // Parser-only settings:
         SETTING_ASSIGNMENTS.put("templateLanguage", 
TemplateLanguage.STATIC_TEXT);
         SETTING_ASSIGNMENTS.put("tagSyntax", TagSyntax.SQUARE_BRACKET);
+        SETTING_ASSIGNMENTS.put("interpolationSyntax", 
InterpolationSyntax.SQUARE_BRACKET);
         SETTING_ASSIGNMENTS.put("whitespaceStripping", false);
         SETTING_ASSIGNMENTS.put("strictSyntaxMode", false);
         SETTING_ASSIGNMENTS.put("autoEscapingPolicy", 
AutoEscapingPolicy.DISABLE);
@@ -549,6 +550,14 @@ public class TemplateConfigurationTest {
             assertOutputWithoutAndWithTC(tc, "[#if true]y[/#if]", "[#if 
true]y[/#if]", "y");
             testedProps.add(Configuration.ExtendableBuilder.TAG_SYNTAX_KEY);
         }
+
+        {
+            TemplateConfiguration.Builder tcb = new 
TemplateConfiguration.Builder();
+            tcb.setInterpolationSyntax(InterpolationSyntax.SQUARE_BRACKET);
+            TemplateConfiguration tc = tcb.build();
+            assertOutputWithoutAndWithTC(tc, "${1}[=2]", "1[=2]", "${1}2");
+            
testedProps.add(Configuration.ExtendableBuilder.INTERPOLATION_SYNTAX_KEY);
+        }
         
         {
             TemplateConfiguration.Builder tcb = new 
TemplateConfiguration.Builder();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/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
index 81f83dc..e5f7035 100644
--- 
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
@@ -73,19 +73,19 @@
             - AST-node subtype: "0"  // Integer
             #text  // o.a.f.c.ASTStaticText
                 - content: "foo"  // String
-            ${...}  // o.a.f.c.ASTDollarInterpolation
+            ${...}  // o.a.f.c.ASTInterpolation
                 - 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
+            ${...}  // o.a.f.c.ASTInterpolation
                 - content: "static"  // o.a.f.c.ASTExpStringLiteral
-            ${...}  // o.a.f.c.ASTDollarInterpolation
+            ${...}  // o.a.f.c.ASTInterpolation
                 - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
                     - value part: "x"  // String
-                    - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+                    - value part: ${...}  // o.a.f.c.ASTInterpolation
                         - content: *  // o.a.f.c.ASTExpArithmetic
                             - left-hand operand: baaz  // 
o.a.f.c.ASTExpVariable
                             - right-hand operand: 10  // 
o.a.f.c.ASTExpNumberLiteral
@@ -151,7 +151,7 @@
                 - content: "["  // String
             #items  // o.a.f.c.ASTDirItems
                 - nested content parameter: "x"  // String
-                ${...}  // o.a.f.c.ASTDollarInterpolation
+                ${...}  // o.a.f.c.ASTInterpolation
                     - content: x  // o.a.f.c.ASTExpVariable
                 #sep  // o.a.f.c.ASTDirSep
                     #text  // o.a.f.c.ASTStaticText
@@ -170,10 +170,10 @@
     #outputFormat  // o.a.f.c.ASTDirOutputFormat
         - value: "XML"  // o.a.f.c.ASTExpStringLiteral
         #noAutoEsc  // o.a.f.c.ASTDirNoAutoEsc
-            ${...}  // o.a.f.c.ASTDollarInterpolation
+            ${...}  // o.a.f.c.ASTInterpolation
                 - content: a  // o.a.f.c.ASTExpVariable
             #autoEsc  // o.a.f.c.ASTDirAutoEsc
-                ${...}  // o.a.f.c.ASTDollarInterpolation
+                ${...}  // o.a.f.c.ASTInterpolation
                     - content: b  // o.a.f.c.ASTExpVariable
-            ${...}  // o.a.f.c.ASTDollarInterpolation
+            ${...}  // o.a.f.c.ASTInterpolation
                 - content: c  // o.a.f.c.ASTExpVariable

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/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
index 0b0a8f8..b1d073e 100644
--- 
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
@@ -17,13 +17,13 @@
  * under the License.
  */
 #mixedContent  // o.a.f.c.ASTImplicitParent
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - 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
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: ...(...)  // o.a.f.c.ASTExpFunctionCall
             - callee: ?leftPad  // o.a.f.c.BuiltInsForStringsBasic$padBI
                 - left-hand operand: x  // o.a.f.c.ASTExpVariable
@@ -31,7 +31,7 @@
             - argument value: 5  // o.a.f.c.ASTExpNumberLiteral
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: ...(...)  // o.a.f.c.ASTExpFunctionCall
             - callee: ?leftPad  // o.a.f.c.BuiltInsForStringsBasic$padBI
                 - left-hand operand: x  // o.a.f.c.ASTExpVariable
@@ -40,7 +40,7 @@
             - argument value: "-"  // o.a.f.c.ASTExpStringLiteral
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: ?then(...)  // 
o.a.f.c.BuiltInsWithParseTimeParameters$then_BI
             - left-hand operand: x  // o.a.f.c.ASTExpVariable
             - right-hand operand: "then"  // String
@@ -48,7 +48,7 @@
             - argument value: "n"  // o.a.f.c.ASTExpStringLiteral
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: ?switch(...)  // 
o.a.f.c.BuiltInsWithParseTimeParameters$switch_BI
             - left-hand operand: x  // o.a.f.c.ASTExpVariable
             - right-hand operand: "switch"  // String

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/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
index 5bdaded..ba3cb97 100644
--- 
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
@@ -30,7 +30,7 @@
     #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
+        ${...}  // o.a.f.c.ASTInterpolation; 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
@@ -60,14 +60,14 @@
         #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
+            ${...}  // o.a.f.c.ASTInterpolation; 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
+            ${...}  // o.a.f.c.ASTInterpolation; 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
@@ -149,7 +149,7 @@
             - 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
+    ${...}  // o.a.f.c.ASTInterpolation; 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/9a13687c/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
index 41fc90b..de05086 100644
--- 
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
@@ -32,7 +32,7 @@
     #if  // o.a.f.c.ASTDirIfOrElseOrElseIf
         - condition: true  // o.a.f.c.ASTExpBooleanLiteral
         - AST-node subtype: "0"  // Integer
-        ${...}  // o.a.f.c.ASTDollarInterpolation
+        ${...}  // o.a.f.c.ASTInterpolation
             - 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/9a13687c/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
index 3d66e58..3d65e3f 100644
--- 
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
@@ -235,7 +235,7 @@
         - namespace: null  // Null
     #text  // o.a.f.c.ASTStaticText
         - content: "\n"  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: ...(...)  // o.a.f.c.ASTExpFunctionCall
             - callee: f  // o.a.f.c.ASTExpVariable
             - argument value: ..  // o.a.f.c.ASTExpRange

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
index a87144e..3adebaf 100644
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-strlitinterpolation.ast
@@ -23,58 +23,58 @@
         - callee: m  // o.a.f.c.ASTExpVariable
         - argument name: "x"  // String
         - argument value: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: e1  // o.a.f.c.ASTExpVariable
         - argument name: "y"  // String
         - argument value: r"$\{e2}"  // o.a.f.c.ASTExpStringLiteral
     #text  // o.a.f.c.ASTStaticText
         - content: "\n2. "  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
             - value part: "a"  // String
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
             - value part: "b"  // String
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
             - value part: "c"  // String
     #text  // o.a.f.c.ASTStaticText
         - content: "\n3. "  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
             - value part: "b"  // String
     #text  // o.a.f.c.ASTStaticText
         - content: "\n4. "  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
             - value part: "a"  // String
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
     #text  // o.a.f.c.ASTStaticText
         - content: "\n5. "  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: y  // o.a.f.c.ASTExpVariable
     #text  // o.a.f.c.ASTStaticText
         - content: "\n6. "  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
             - value part: "a b "  // String
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
             - value part: " c d"  // String
     #text  // o.a.f.c.ASTStaticText
         - content: "\n7. "  // String
-    ${...}  // o.a.f.c.ASTDollarInterpolation
+    ${...}  // o.a.f.c.ASTInterpolation
         - content: dynamic "..."  // o.a.f.c.ASTExpStringLiteral
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: x  // o.a.f.c.ASTExpVariable
             - value part: " a b "  // String
-            - value part: ${...}  // o.a.f.c.ASTDollarInterpolation
+            - value part: ${...}  // o.a.f.c.ASTInterpolation
                 - content: y  // o.a.f.c.ASTExpVariable
             - value part: " c$d"  // String

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
----------------------------------------------------------------------
diff --git 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
index ba69aa5..5d281aa 100644
--- 
a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
+++ 
b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/ast-whitespacestripping.ast
@@ -30,7 +30,7 @@
         - nested content parameter: "xs"  // String
         #text  // o.a.f.c.ASTStaticText
             - content: "    "  // String
-        ${...}  // o.a.f.c.ASTDollarInterpolation
+        ${...}  // o.a.f.c.ASTInterpolation
             - 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/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
deleted file mode 100644
index 5dc66c1..0000000
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDollarInterpolation.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.IOException;
-import java.io.Writer;
-
-import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
-import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
-import org.apache.freemarker.core.outputformat.OutputFormat;
-import org.apache.freemarker.core.util.TemplateLanguageUtils;
-
-/**
- * AST interpolation node: <tt>${exp}</tt>
- */
-final class ASTDollarInterpolation extends ASTInterpolation {
-
-    private final ASTExpression expression;
-    
-    /** For {@code #escape x as ...} (legacy auto-escaping) */
-    private final ASTExpression escapedExpression;
-    
-    /** For OutputFormat-based auto-escaping */
-    private final OutputFormat outputFormat;
-    private final MarkupOutputFormat markupOutputFormat;
-    private final boolean autoEscape;
-
-    ASTDollarInterpolation(
-            ASTExpression expression, ASTExpression escapedExpression,
-            OutputFormat outputFormat, boolean autoEscape) {
-        this.expression = expression;
-        this.escapedExpression = escapedExpression;
-        this.outputFormat = outputFormat;
-        markupOutputFormat
-                = (MarkupOutputFormat) (outputFormat instanceof 
MarkupOutputFormat ? outputFormat : null);
-        this.autoEscape = autoEscape;
-    }
-
-    /**
-     * Outputs the string value of the enclosed expression.
-     */
-    @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException 
{
-        final Object moOrStr = calculateInterpolatedStringOrMarkup(env);
-        final Writer out = env.getOut();
-        if (moOrStr instanceof String) {
-            final String s = (String) moOrStr;
-            if (autoEscape) {
-                markupOutputFormat.output(s, out);
-            } else {
-                out.write(s);
-            }
-        } else {
-            final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) 
moOrStr;
-            final MarkupOutputFormat moOF = mo.getOutputFormat();
-            // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
-            if (moOF != outputFormat && 
!outputFormat.isOutputFormatMixingAllowed()) {
-                final String srcPlainText;
-                // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
-                srcPlainText = moOF.getSourcePlainText(mo);
-                if (srcPlainText == null) {
-                    throw new TemplateException(escapedExpression,
-                            "The value to print is in ", new 
_DelayedToString(moOF),
-                            " format, which differs from the current output 
format, ",
-                            new _DelayedToString(outputFormat), ". Format 
conversion wasn't possible.");
-                }
-                if (outputFormat instanceof MarkupOutputFormat) {
-                    ((MarkupOutputFormat) outputFormat).output(srcPlainText, 
out);
-                } else {
-                    out.write(srcPlainText);
-                }
-            } else {
-                moOF.output(mo, out);
-            }
-        }
-        return null;
-    }
-
-    @Override
-    protected Object calculateInterpolatedStringOrMarkup(Environment env) 
throws TemplateException {
-        return 
_EvalUtils.coerceModelToPlainTextOrMarkup(escapedExpression.eval(env), 
escapedExpression, null, env);
-    }
-
-    @Override
-    protected String dump(boolean canonical, boolean inStringLiteral) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("${");
-        final String exprCF = expression.getCanonicalForm();
-        sb.append(inStringLiteral ? 
TemplateLanguageUtils.escapeStringLiteralPart(exprCF, '"') : exprCF);
-        sb.append("}");
-        if (!canonical && expression != escapedExpression) {
-            sb.append(" auto-escaped");            
-        }
-        return sb.toString();
-    }
-    
-    @Override
-    String getASTNodeDescriptor() {
-        return "${...}";
-    }
-
-    @Override
-    boolean heedsOpeningWhitespace() {
-        return true;
-    }
-
-    @Override
-    boolean heedsTrailingWhitespace() {
-        return true;
-    }
-
-    @Override
-    int getParameterCount() {
-        return 1;
-    }
-
-    @Override
-    Object getParameterValue(int idx) {
-        if (idx != 0) throw new IndexOutOfBoundsException();
-        return expression;
-    }
-
-    @Override
-    ParameterRole getParameterRole(int idx) {
-        if (idx != 0) throw new IndexOutOfBoundsException();
-        return ParameterRole.CONTENT;
-    }
-
-    @Override
-    boolean isNestedBlockRepeater() {
-        return false;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
index c93483c..a86c4d5 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTExpStringLiteral.java
@@ -49,12 +49,15 @@ final class ASTExpStringLiteral extends ASTExpression 
implements TemplateStringM
      *            inherit tokenizer-level auto-detected settings.
      */
     void parseValue(FMParserTokenManager parentTkMan, OutputFormat 
outputFormat) throws ParseException {
+        // TODO [FM3] (Find related: [interpolation prefixes])
         // The way this works is incorrect (the literal should be parsed 
without un-escaping),
         // but we can't fix this backward compatibly.
-        if (value.length() > 3 && value.indexOf("${") >= 0) { // Find related: 
[interpolation prefixes]
-            Template parentTemplate = getTemplate();
-            ParsingConfiguration pCfg = 
parentTemplate.getParsingConfiguration();
-
+        Template parentTemplate = getTemplate();
+        ParsingConfiguration pCfg = parentTemplate.getParsingConfiguration();
+        InterpolationSyntax intSyn = pCfg.getInterpolationSyntax();
+        if (value.length() > 3 && (
+                intSyn == InterpolationSyntax.DOLLAR && value.indexOf("${") != 
-1
+                || intSyn == InterpolationSyntax.SQUARE_BRACKET && 
value.indexOf("[=") != -1)) {        
             try {
                 SimpleCharStream simpleCharacterStream = new SimpleCharStream(
                         new StringReader(value),

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java
index 028acc2..66c2563 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTInterpolation.java
@@ -16,24 +16,81 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 package org.apache.freemarker.core;
 
+import java.io.IOException;
+import java.io.Writer;
+
 import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
+import org.apache.freemarker.core.outputformat.OutputFormat;
+import org.apache.freemarker.core.util.TemplateLanguageUtils;
 
 /**
- * AST interpolation node superclass.
+ * AST interpolation node: <tt>${exp}</tt>
  */
-abstract class ASTInterpolation extends ASTElement {
+final class ASTInterpolation extends ASTElement {
 
-    protected abstract String dump(boolean canonical, boolean inStringLiteral);
+    private final ASTExpression expression;
+    
+    /** For {@code #escape x as ...} (legacy auto-escaping) */
+    private final ASTExpression escapedExpression;
+    
+    /** For OutputFormat-based auto-escaping */
+    private final OutputFormat outputFormat;
+    private final MarkupOutputFormat markupOutputFormat;
+    private final boolean autoEscape;
 
-    @Override
-    protected final String dump(boolean canonical) {
-        return dump(canonical, false);
+    ASTInterpolation(
+            ASTExpression expression, ASTExpression escapedExpression,
+            OutputFormat outputFormat, boolean autoEscape) {
+        this.expression = expression;
+        this.escapedExpression = escapedExpression;
+        this.outputFormat = outputFormat;
+        markupOutputFormat
+                = (MarkupOutputFormat) (outputFormat instanceof 
MarkupOutputFormat ? outputFormat : null);
+        this.autoEscape = autoEscape;
     }
-    
-    final String getCanonicalFormInStringLiteral() {
-        return dump(true, true);
+
+    /**
+     * Outputs the string value of the enclosed expression.
+     */
+    @Override
+    ASTElement[] accept(Environment env) throws TemplateException, IOException 
{
+        final Object moOrStr = calculateInterpolatedStringOrMarkup(env);
+        final Writer out = env.getOut();
+        if (moOrStr instanceof String) {
+            final String s = (String) moOrStr;
+            if (autoEscape) {
+                markupOutputFormat.output(s, out);
+            } else {
+                out.write(s);
+            }
+        } else {
+            final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) 
moOrStr;
+            final MarkupOutputFormat moOF = mo.getOutputFormat();
+            // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
+            if (moOF != outputFormat && 
!outputFormat.isOutputFormatMixingAllowed()) {
+                final String srcPlainText;
+                // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic!
+                srcPlainText = moOF.getSourcePlainText(mo);
+                if (srcPlainText == null) {
+                    throw new TemplateException(escapedExpression,
+                            "The value to print is in ", new 
_DelayedToString(moOF),
+                            " format, which differs from the current output 
format, ",
+                            new _DelayedToString(outputFormat), ". Format 
conversion wasn't possible.");
+                }
+                if (outputFormat instanceof MarkupOutputFormat) {
+                    ((MarkupOutputFormat) outputFormat).output(srcPlainText, 
out);
+                } else {
+                    out.write(srcPlainText);
+                }
+            } else {
+                moOF.output(mo, out);
+            }
+        }
+        return null;
     }
 
     /**
@@ -41,11 +98,72 @@ abstract class ASTInterpolation extends ASTElement {
      * 
      * @return A {@link String} or {@link TemplateMarkupOutputModel}. Not 
{@code null}.
      */
-    protected abstract Object calculateInterpolatedStringOrMarkup(Environment 
env) throws TemplateException;
+    Object calculateInterpolatedStringOrMarkup(Environment env) throws 
TemplateException {
+        return 
_EvalUtils.coerceModelToPlainTextOrMarkup(escapedExpression.eval(env), 
escapedExpression, null, env);
+    }
 
     @Override
+    protected final String dump(boolean canonical) {
+        return dump(canonical, false);
+    }
+    
+    final String getCanonicalFormInStringLiteral() {
+        return dump(true, true);
+    }
+
+    private String dump(boolean canonical, boolean inStringLiteral) {
+        StringBuilder sb = new StringBuilder();
+        InterpolationSyntax syntax = getTemplate().getInterpolationSyntax();
+        sb.append(syntax != InterpolationSyntax.SQUARE_BRACKET ? "${" : "[=");
+        final String exprCF = expression.getCanonicalForm();
+        sb.append(inStringLiteral ? 
TemplateLanguageUtils.escapeStringLiteralPart(exprCF, '"') : exprCF);
+        sb.append(syntax != InterpolationSyntax.SQUARE_BRACKET ? "}" : "]");
+        if (!canonical && expression != escapedExpression) {
+            sb.append(" auto-escaped");            
+        }
+        return sb.toString();
+    }
+    
+    @Override
     boolean isShownInStackTrace() {
         return true;
     }
     
+    @Override
+    String getASTNodeDescriptor() {
+        return "${...}";
+    }
+
+    @Override
+    boolean heedsOpeningWhitespace() {
+        return true;
+    }
+
+    @Override
+    boolean heedsTrailingWhitespace() {
+        return true;
+    }
+
+    @Override
+    int getParameterCount() {
+        return 1;
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        if (idx != 0) throw new IndexOutOfBoundsException();
+        return expression;
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        if (idx != 0) throw new IndexOutOfBoundsException();
+        return ParameterRole.CONTENT;
+    }
+
+    @Override
+    boolean isNestedBlockRepeater() {
+        return false;
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
index b0043c6..fb113d9 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -184,6 +184,7 @@ public final class Configuration implements 
TopLevelConfiguration, CustomStateSc
 
     private final TemplateLanguage templateLanguage;
     private final TagSyntax tagSyntax;
+    private final InterpolationSyntax interpolationSyntax;
     private final boolean whitespaceStripping;
     private final AutoEscapingPolicy autoEscapingPolicy;
     private final OutputFormat outputFormat;
@@ -349,6 +350,7 @@ public final class Configuration implements 
TopLevelConfiguration, CustomStateSc
 
         templateLanguage = builder.getTemplateLanguage();
         tagSyntax = builder.getTagSyntax();
+        interpolationSyntax = builder.getInterpolationSyntax();
         whitespaceStripping = builder.getWhitespaceStripping();
         autoEscapingPolicy = builder.getAutoEscapingPolicy();
         outputFormat = builder.getOutputFormat();
@@ -815,13 +817,22 @@ public final class Configuration implements 
TopLevelConfiguration, CustomStateSc
      * Always {@code true} in {@link Configuration}-s; even if this setting 
wasn't set in the builder, it gets a default
      * value in the {@link Configuration}.
      */
+    @Override
+    public boolean isTagSyntaxSet() {
+        return true;
+    }
 
+    @Override
+    public InterpolationSyntax getInterpolationSyntax() {
+        return interpolationSyntax;
+    }
+    
     /**
      * Always {@code true} in {@link Configuration}-s; even if this setting 
wasn't set in the builder, it gets a default
      * value in the {@link Configuration}.
      */
     @Override
-    public boolean isTagSyntaxSet() {
+    public boolean isInterpolationSyntaxSet() {
         return true;
     }
 
@@ -2332,6 +2343,11 @@ public final class Configuration implements 
TopLevelConfiguration, CustomStateSc
         }
 
         @Override
+        protected InterpolationSyntax getDefaultInterpolationSyntax() {
+            return InterpolationSyntax.DOLLAR;
+        }
+        
+        @Override
         protected TemplateLanguage getDefaultTemplateLanguage() {
             return TemplateLanguage.FTL;
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/InterpolationSyntax.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/InterpolationSyntax.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/InterpolationSyntax.java
new file mode 100644
index 0000000..dc4ce88
--- /dev/null
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/InterpolationSyntax.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Used as the value of the {@link 
ParsingConfiguration#getInterpolationSyntax()}  interpolationSyntax} setting.
+ */
+public enum InterpolationSyntax {
+    DOLLAR,
+    SQUARE_BRACKET;
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
index 0c845b7..a486547 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/MutableParsingAndProcessingConfiguration.java
@@ -43,6 +43,7 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
     public static final String RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY = 
"recognizeStandardFileExtensions";
     public static final String TEMPLATE_LANGUAGE_KEY = "templateLanguage";
     public static final String TAG_SYNTAX_KEY = "tagSyntax";
+    public static final String INTERPOLATION_SYNTAX_KEY = 
"interpolationSyntax";
     public static final String TAB_SIZE_KEY = "tabSize";
     public static final String INCOMPATIBLE_IMPROVEMENTS_KEY = 
"incompatibleImprovements";
 
@@ -52,6 +53,7 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
                 // Must be sorted alphabetically!
                 AUTO_ESCAPING_POLICY_KEY,
                 INCOMPATIBLE_IMPROVEMENTS_KEY,
+                INTERPOLATION_SYNTAX_KEY,
                 OUTPUT_FORMAT_KEY,
                 RECOGNIZE_STANDARD_FILE_EXTENSIONS_KEY,
                 SOURCE_ENCODING_KEY,
@@ -63,6 +65,7 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
 
     private TemplateLanguage templateLanguage;
     private TagSyntax tagSyntax;
+    private InterpolationSyntax interpolationSyntax;
     private Boolean whitespaceStripping;
     private AutoEscapingPolicy autoEscapingPolicy;
     private Boolean recognizeStandardFileExtensions;
@@ -134,6 +137,17 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
                             "square_bracket".equals(value) ? "The correct 
value is: squareBracket" :
                             "No such predefined tag syntax name");
                 }
+            } else if (INTERPOLATION_SYNTAX_KEY.equals(name)) {
+                if ("dollar".equals(value)) {
+                    setInterpolationSyntax(InterpolationSyntax.DOLLAR);
+                } else if ("squareBracket".equals(value)) {
+                    setInterpolationSyntax(InterpolationSyntax.SQUARE_BRACKET);
+                } else {
+                    throw new InvalidSettingValueException(name, value,
+                            "legacy".equals(value) ? "The supported 
alternative is: dollar" :
+                            "square_bracket".equals(value) ? "The correct 
value is: squareBracket" :
+                            "No such predefined interpolation syntax name");
+                }
             } else if (TAB_SIZE_KEY.equals(name)) {
                 setTabSize(Integer.parseInt(value));
             } else {
@@ -192,7 +206,7 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
     public TagSyntax getTagSyntax() {
         return isTagSyntaxSet() ? tagSyntax : getDefaultTagSyntax();
     }
-
+    
     /**
      * Returns the value the getter method returns when the setting is not 
set, possibly by inheriting the setting value
      * from another {@link ParsingConfiguration}, or throws {@link 
CoreSettingValueNotSetException}.
@@ -203,6 +217,49 @@ public abstract class 
MutableParsingAndProcessingConfiguration<
     public boolean isTagSyntaxSet() {
         return tagSyntax != null;
     }
+    
+    /**
+     * Setter pair of {@link #getInterpolationSyntax()}.
+     *
+     * @param interpolationSyntax
+     *         Can't be {@code null}
+     */
+    public void setInterpolationSyntax(InterpolationSyntax 
interpolationSyntax) {
+        _NullArgumentException.check("interpolationSyntax", 
interpolationSyntax);
+        this.interpolationSyntax = interpolationSyntax;
+    }
+
+    /**
+     * Fluent API equivalent of {@link 
#interpolationSyntax(InterpolationSyntax)}
+     */
+    public SelfT interpolationSyntax(InterpolationSyntax interpolationSyntax) {
+        setInterpolationSyntax(interpolationSyntax);
+        return self();
+    }
+
+    /**
+     * Resets the setting value as if it was never set (but it doesn't affect 
the value inherited from another
+     * {@link ParsingConfiguration}).
+     */
+    public void unsetInterpolationSyntax() {
+        this.interpolationSyntax = null;
+    }
+
+    @Override
+    public InterpolationSyntax getInterpolationSyntax() {
+        return isInterpolationSyntaxSet() ? interpolationSyntax : 
getDefaultInterpolationSyntax();
+    }
+    
+    /**
+     * Returns the value the getter method returns when the setting is not 
set, possibly by inheriting the setting value
+     * from another {@link ParsingConfiguration}, or throws {@link 
CoreSettingValueNotSetException}.
+     */
+    protected abstract InterpolationSyntax getDefaultInterpolationSyntax();
+
+    @Override
+    public boolean isInterpolationSyntaxSet() {
+        return interpolationSyntax != null;
+    }
 
     @Override
     public TemplateLanguage getTemplateLanguage() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
index 96d00f9..abfdf9e 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfiguration.java
@@ -76,7 +76,15 @@ public interface ParsingConfiguration {
      * configuration or throws a {@link CoreSettingValueNotSetException}.
      */
     boolean isTagSyntaxSet();
+    
+    
+    /**
+     * Determines the interpolation syntax (<code>${x}</code> VS 
<code>[=x]</code>) of the template files.
+     */
+    InterpolationSyntax getInterpolationSyntax();
 
+    boolean isInterpolationSyntaxSet();
+    
     /**
      * Whether the template parser will try to remove superfluous white-space 
around certain tags.
      */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfigurationWithFallback.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfigurationWithFallback.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfigurationWithFallback.java
index 64f43fc..8deeb47 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfigurationWithFallback.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ParsingConfigurationWithFallback.java
@@ -58,6 +58,16 @@ final class ParsingConfigurationWithFallback implements 
ParsingConfiguration {
     }
 
     @Override
+    public InterpolationSyntax getInterpolationSyntax() {
+        return tCfg.isInterpolationSyntaxSet() ? tCfg.getInterpolationSyntax() 
: cfg.getInterpolationSyntax();
+    }
+
+    @Override
+    public boolean isInterpolationSyntaxSet() {
+        return true;
+    }
+    
+    @Override
     public boolean getWhitespaceStripping() {
         return tCfg.isWhitespaceStrippingSet() ? tCfg.getWhitespaceStripping() 
: cfg.getWhitespaceStripping();
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/TagSyntax.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TagSyntax.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TagSyntax.java
index 423ab9c..52a6aec 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/TagSyntax.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TagSyntax.java
@@ -23,7 +23,11 @@ package org.apache.freemarker.core;
  * Used as the value of the {@link ParsingConfiguration#getTagSyntax()}  
tagSyntax} setting.
  */
 public enum TagSyntax {
+    /** The parser decides between {@link #ANGLE_BRACKET} and {@link 
#SQUARE_BRACKET} based on the first tag it mets. */
+    // TODO [FM3] Get rid of this, as it's too hard for tooling.
     AUTO_DETECT,
+    /** For example {@code <#if x><@foo /></#if>} */
     ANGLE_BRACKET,
+    /** For example {@code [#if x][@foo /][/#if]} */
     SQUARE_BRACKET
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
index 1354694..4036b3d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Template.java
@@ -123,6 +123,7 @@ public class Template implements ProcessingConfiguration, 
CustomStateScope {
     // Values from template content that are detected automatically:
     private Charset actualSourceEncoding;
     private TagSyntax actualTagSyntax;
+    private InterpolationSyntax interpolationSyntax;
 
     // Custom state:
     private final Object customStateMapLock = new Object();
@@ -319,6 +320,7 @@ public class Template implements ProcessingConfiguration, 
CustomStateScope {
                     rootElement = null;
                 }
                 actualTagSyntax = parser._getLastTagSyntax();
+                interpolationSyntax = 
parsingConfiguration.getInterpolationSyntax();
             } catch (TokenMgrError exc) {
                 // TokenMgrError VS ParseException is not an interesting 
difference for the user, so we just convert it
                 // to ParseException
@@ -681,12 +683,24 @@ public class Template implements ProcessingConfiguration, 
CustomStateScope {
      * couldn't be determined (like because there was no tags in the template, 
or it was a plain text template), this
      * returns whatever the default is in the current configuration, so it's 
maybe
      * {@link TagSyntax#AUTO_DETECT}.
+     * 
+     * @see ParsingConfiguration#getTagSyntax()
      */
     public TagSyntax getActualTagSyntax() {
         return actualTagSyntax;
     }
     
     /**
+     * Returns the interpolation syntax the parser has used for this template. 
Because the interpolation syntax is
+     * never auto-detected, it's not called "getActualInterpolationSyntax" 
(unlike {@link #getActualTagSyntax()}).
+     * 
+     * @see ParsingConfiguration#getInterpolationSyntax()
+     */
+    public InterpolationSyntax getInterpolationSyntax() {
+        return interpolationSyntax;
+    }
+    
+    /**
      * Returns the output format (see {@link Configuration#getOutputFormat()}) 
used for this template.
      * The output format of a template can come from various places, in order 
of increasing priority:
      * {@link Configuration#getOutputFormat()}, {@link 
ParsingConfiguration#getOutputFormat()} (which is usually

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
index 901c4f4..34b9d0a 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplateConfiguration.java
@@ -83,6 +83,7 @@ public final class TemplateConfiguration implements 
ParsingAndProcessingConfigur
     
     private final TemplateLanguage templateLanguage;
     private final TagSyntax tagSyntax;
+    private final InterpolationSyntax interpolationSyntax;
     private final Boolean whitespaceStripping;
     private final AutoEscapingPolicy autoEscapingPolicy;
     private final Boolean recognizeStandardFileExtensions;
@@ -124,6 +125,7 @@ public final class TemplateConfiguration implements 
ParsingAndProcessingConfigur
 
         templateLanguage = builder.isTemplateLanguageSet() ? 
builder.getTemplateLanguage() : null;
         tagSyntax = builder.isTagSyntaxSet() ? builder.getTagSyntax() : null;
+        interpolationSyntax = builder.isInterpolationSyntaxSet() ? 
builder.getInterpolationSyntax() : null;
         whitespaceStripping = builder.isWhitespaceStrippingSet() ? 
builder.getWhitespaceStripping() : null;
         autoEscapingPolicy = builder.isAutoEscapingPolicySet() ? 
builder.getAutoEscapingPolicy() : null;
         recognizeStandardFileExtensions = 
builder.isRecognizeStandardFileExtensionsSet() ? 
builder.getRecognizeStandardFileExtensions() : null;
@@ -139,13 +141,26 @@ public final class TemplateConfiguration implements 
ParsingAndProcessingConfigur
         }
         return tagSyntax;
     }
-
+    
     @Override
     public boolean isTagSyntaxSet() {
         return tagSyntax != null;
     }
 
     @Override
+    public InterpolationSyntax getInterpolationSyntax() {
+        if (!isInterpolationSyntaxSet()) {
+            throw new CoreSettingValueNotSetException("interpolationSyntax");
+        }
+        return interpolationSyntax;
+    }
+
+    @Override
+    public boolean isInterpolationSyntaxSet() {
+        return interpolationSyntax != null;
+    }
+    
+    @Override
     public TemplateLanguage getTemplateLanguage() {
         if (!isTemplateLanguageSet()) {
             throw new CoreSettingValueNotSetException("templateLanguage");
@@ -825,6 +840,9 @@ public final class TemplateConfiguration implements 
ParsingAndProcessingConfigur
             if (tc.isTagSyntaxSet()) {
                 setTagSyntax(tc.getTagSyntax());
             }
+            if (tc.isInterpolationSyntaxSet()) {
+                setInterpolationSyntax(tc.getInterpolationSyntax());
+            }
             if (tc.isTemplateLanguageSet()) {
                 setTemplateLanguage(tc.getTemplateLanguage());
             }
@@ -890,6 +908,11 @@ public final class TemplateConfiguration implements 
ParsingAndProcessingConfigur
         protected TagSyntax getDefaultTagSyntax() {
             throw new CoreSettingValueNotSetException("tagSyntax");
         }
+        
+        @Override
+        protected InterpolationSyntax getDefaultInterpolationSyntax() {
+            throw new CoreSettingValueNotSetException("interpolationSyntax");
+        }
 
         @Override
         protected TemplateLanguage getDefaultTemplateLanguage() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
----------------------------------------------------------------------
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
index 6ffe937..bf79849 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/OverloadedNumberUtils.java
@@ -89,7 +89,7 @@ class OverloadedNumberUtils {
      * @param num the number to coerce
      * @param typeFlags the type flags of the target parameter position; see 
{@link TypeFlags}
      * 
-     * @returns The original number or a {@link NumberWithFallbackType}, 
depending on the actual value and the types
+     * @return The original number or a {@link NumberWithFallbackType}, 
depending on the actual value and the types
      *     indicated in the {@code targetNumTypes} parameter.
      */
     static Number addFallbackType(final Number num, final int typeFlags) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/9a13687c/freemarker-core/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/javacc/FTL.jj 
b/freemarker-core/src/main/javacc/FTL.jj
index 33a86da..46782d6 100644
--- a/freemarker-core/src/main/javacc/FTL.jj
+++ b/freemarker-core/src/main/javacc/FTL.jj
@@ -172,6 +172,8 @@ public class FMParser {
             throw new BugException("Unsupported tagSyntax: " + tagSyntax);
         }
 
+        token_source.interpolationSyntax = pCfg.getInterpolationSyntax();
+
         this.stripWhitespace = pCfg.getWhitespaceStripping();
 
         // If this is a Template under construction, we do the below.
@@ -519,12 +521,12 @@ TOKEN_MGR_DECLS:
      */
     String noParseTag;
 
+    private FMParser parser;
+    private int postInterpolationLexState = -1;
     /**
      * Keeps track of how deeply nested we have the hash literals. This is 
necessary since we need to be able to
      * distinguish the } used to close a hash literal and the one used to 
close a ${
      */
-    private FMParser parser;
-    private int postInterpolationLexState = -1;
     private int hashLiteralNesting;
     private int parenthesisNesting;
     private int bracketNesting;
@@ -533,6 +535,7 @@ TOKEN_MGR_DECLS:
             autodetectTagSyntax,
             directiveSyntaxEstablished,
             inNamedParameterExpression;
+    InterpolationSyntax interpolationSyntax;
     int incompatibleImprovements;
 
     void setParser(FMParser parser) {
@@ -605,25 +608,22 @@ TOKEN_MGR_DECLS:
         }
     }
 
-    private void closeBracket(Token tok) {
-        if (bracketNesting > 0) {
-            --bracketNesting;
-        } else {
-            tok.kind = DIRECTIVE_END;
-            if (inFTLHeader) {
-                eatNewline();
-                inFTLHeader = false;
-            }
-            SwitchTo(DEFAULT);
-        }
-    }
-    
     private void startInterpolation(Token tok) {
+        if ( 
+                interpolationSyntax == InterpolationSyntax.DOLLAR
+                    && tok.kind != DOLLAR_INTERPOLATION_OPENING
+                || interpolationSyntax == InterpolationSyntax.SQUARE_BRACKET
+                    && tok.kind != SQUARE_BRACKET_INTERPOLATION_OPENING) {
+            tok.kind = STATIC_TEXT_NON_WS;
+            return;
+        }
+        
         if (postInterpolationLexState != -1) {
             char c = tok.image.charAt(0);
             throw new TokenMgrError(
-                    "You can't start an interpolation (" + c + "{...}) here "
-                    + "as you are inside another interpolation.)",
+                    "You can't start an interpolation (" + tok.image + "..."
+                    + (interpolationSyntax == 
InterpolationSyntax.SQUARE_BRACKET ? "]" : "}")
+                    + ") here as you are inside another interpolation.)",
                     TokenMgrError.LEXICAL_ERROR,
                     tok.beginLine, tok.beginColumn,
                     tok.endLine, tok.endColumn);
@@ -952,6 +952,8 @@ TOKEN:
     |
     // Find related: [interpolation prefixes]
     <DOLLAR_INTERPOLATION_OPENING : "${"> { startInterpolation(matchedToken); }
+    |
+    <SQUARE_BRACKET_INTERPOLATION_OPENING : "[="> { 
startInterpolation(matchedToken); }
 }
 
 <FM_EXPRESSION, IN_PAREN> SKIP :
@@ -1086,7 +1088,21 @@ TOKEN:
     |
     <CLOSE_BRACKET : "]">
     {
-        closeBracket(matchedToken);
+        if (bracketNesting > 0) {
+            --bracketNesting;
+        } else if (interpolationSyntax == InterpolationSyntax.SQUARE_BRACKET
+                && (postInterpolationLexState != -1 || !squBracTagSyntax)) {
+            endInterpolation(matchedToken);
+        } else {
+            // TODO [FM3] Don't mimic this glitch anymore.
+            // Glitch where you can close any tag with `]`, like `<#if x]`. We 
keep it for backward compatibility.
+            matchedToken.kind = DIRECTIVE_END;
+            if (inFTLHeader) {
+                eatNewline();
+                inFTLHeader = false;
+            }
+            SwitchTo(DEFAULT);
+        }
     }
     |
     <OPEN_PAREN : "(">
@@ -1137,15 +1153,16 @@ TOKEN:
         }
     }
     |
-    <OPEN_MISPLACED_INTERPOLATION : "${"> // Find related: [interpolation 
prefixes]
+    <OPEN_MISPLACED_INTERPOLATION : "${" | "[="> // Find related: 
[interpolation prefixes]
     {
         if ("".length() == 0) {  // prevents unreachabe "break" compilation 
error in generated Java
-            char c = matchedToken.image.charAt(0);
+            char closerC = matchedToken.image.charAt(0) != '[' ? '}' : ']';
             throw new TokenMgrError(
-                    "You can't use \"" + c + "{\" here as you are already in 
FreeMarker-expression-mode. Thus, instead "
-                    + "of " + c + "{myExpression}, just write myExpression. "
-                    + "(" + c + "{...} is only needed where otherwise static 
text is expected, i.e, outside " 
-                    + "FreeMarker tags and ${...}-s.)",
+                    "You can't use " + matchedToken.image + "..." + closerC + 
" (an interpolation) here as you are "
+                    + "already in FreeMarker-expression-mode. Thus, instead of 
" + matchedToken.image + "myExpression"
+                    + closerC + ", just write myExpression. (" + 
matchedToken.image + "..." + closerC + " is only "
+                    + "used where otherwise static text is expected, i.e., 
outside FreeMarker tags and "
+                    + "interpolations, or inside string literals.)",
                     TokenMgrError.LEXICAL_ERROR,
                     matchedToken.beginLine, matchedToken.beginColumn,
                     matchedToken.endLine, matchedToken.endColumn);
@@ -2192,8 +2209,12 @@ ASTExpStringLiteral ASTExpStringLiteral(boolean 
interpolate) :
         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 $\{...}. 
-            if (t.image.indexOf("${") >= 0) result.parseValue(token_source, 
outputFormat);
+            // TODO [FM3]: This logic is broken. It can't handle literals that 
contains both ${...} and $\{...}. 
+            InterpolationSyntax intSyn = pCfg.getInterpolationSyntax();
+            if (intSyn == InterpolationSyntax.DOLLAR && t.image.indexOf("${") 
!= -1
+                    || intSyn == InterpolationSyntax.SQUARE_BRACKET && 
t.image.indexOf("[=") != -1) {
+                result.parseValue(token_source, outputFormat);
+            }
         }
         return result;
     }
@@ -2216,7 +2237,6 @@ ASTExpression ASTExpBooleanLiteral() :
     }
 }
 
-
 ASTExpHashLiteral ASTExpHashLiteral() :
 {
     Token begin, end;
@@ -2258,21 +2278,30 @@ ASTExpHashLiteral ASTExpHashLiteral() :
 /**
  * ${exp}
  */
-ASTDollarInterpolation ASTDollarInterpolation() :
+ASTInterpolation ASTInterpolation() :
 {
     ASTExpression exp;
     Token begin, end;
 }
 {
-    begin = <DOLLAR_INTERPOLATION_OPENING>
-    exp = ASTExpression()
+    (
+        (
+            begin = <DOLLAR_INTERPOLATION_OPENING>
+            exp = ASTExpression()
+            end = <CLOSING_CURLY_BRACKET>
+        )
+        |
+        (
+            begin = <SQUARE_BRACKET_INTERPOLATION_OPENING>
+            exp = ASTExpression()
+            end = <CLOSE_BRACKET>
+        )
+    )
     {
         notHashLiteral(exp, MessageUtils.STRING_COERCABLE_TYPES_DESC);
         notListLiteral(exp, MessageUtils.STRING_COERCABLE_TYPES_DESC);
-    }
-    end = <CLOSING_CURLY_BRACKET>
-    {
-        ASTDollarInterpolation result = new ASTDollarInterpolation(
+        
+        ASTInterpolation result = new ASTInterpolation(
                 exp, escapedExpression(exp),
                 outputFormat,
                 autoEscaping);
@@ -3983,7 +4012,7 @@ TemplateElements MixedContentElements() :
         (
             elem = PCData()
             |
-            elem = ASTDollarInterpolation()
+            elem = ASTInterpolation()
             |
             elem = FreemarkerDirective()
         )
@@ -4027,7 +4056,7 @@ ASTImplicitParent ASTImplicitParent() :
         (
             elem = PCData()
             |
-            elem = ASTDollarInterpolation()
+            elem = ASTInterpolation()
             |
             elem = FreemarkerDirective()
         )
@@ -4081,7 +4110,7 @@ ASTElement FreeMarkerText() :
         (
             elem = PCData()
             |
-            elem = ASTDollarInterpolation()  // Find related: [interpolation 
prefixes]
+            elem = ASTInterpolation()  // Find related: [interpolation 
prefixes]
         )
         {
             if (begin == null) {
@@ -4387,7 +4416,7 @@ List<Object> StaticTextAndInterpolations() :
            }
            |
            (
-            interpolation = ASTDollarInterpolation()
+            interpolation = ASTInterpolation()
            )
            {
             if (staticTextCollector != null) {

Reply via email to