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 2019/08/17 20:16:29 UTC

[freemarker] branch 2.3-gae updated (f87eb85 -> c94b7ae)

This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a change to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git.


    from f87eb85  (Accidentally public class )
     new 6795cd7  Updated release date in Manual
     new f7a1013  Updated version number to 2.3.30 nightly
     new 0e0c2f9  Finished local lambda expression AST API (overlooked this TODO in 2.3.29)
     new c94b7ae  Bug fixed: In <#escape placeholder as escExpression>, the placeholder wasn't substituted inside lambda expressions inside escExpression. Fortunately it's very unlikely that anyone wanted to use lambdas there (given the few built-ins that accept lambdas).

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/main/java/freemarker/core/EscapeBlock.java     |  8 ++-
 .../IntermediateStreamOperationLikeBuiltIn.java    | 21 +++++--
 .../freemarker/core/LocalLambdaExpression.java     | 36 ++++++++---
 ...Exception.java => UncheckedParseException.java} | 16 ++---
 src/main/javacc/FTL.jj                             |  2 +-
 src/main/resources/freemarker/version.properties   |  8 +--
 src/manual/en_US/book.xml                          | 42 ++++++++++++-
 src/test/java/freemarker/core/ASTTest.java         |  4 ++
 ...bdaParsingTest.java => LamdaAndEscapeTest.java} | 15 ++++-
 src/test/resources/freemarker/core/ast-lambda.ast  | 72 ++++++++++++++++++++++
 .../{cache/test.ftl => core/ast-lambda.ftl}        |  6 +-
 11 files changed, 195 insertions(+), 35 deletions(-)
 copy src/main/java/freemarker/core/{TemplatePostProcessorException.java => UncheckedParseException.java} (65%)
 copy src/test/java/freemarker/core/{LambdaParsingTest.java => LamdaAndEscapeTest.java} (63%)
 create mode 100644 src/test/resources/freemarker/core/ast-lambda.ast
 copy src/test/resources/freemarker/{cache/test.ftl => core/ast-lambda.ftl} (82%)


[freemarker] 04/04: Bug fixed: In <#escape placeholder as escExpression>, the placeholder wasn't substituted inside lambda expressions inside escExpression. Fortunately it's very unlikely that anyone wanted to use lambdas there (given the few built-ins that accept lambdas).

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git

commit c94b7aeed2085d39e2039f9a964b1b06b93750e0
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Aug 17 20:02:11 2019 +0200

    Bug fixed: In <#escape placeholder as escExpression>, the placeholder wasn't substituted inside lambda expressions inside escExpression. Fortunately it's very unlikely that anyone wanted to use lambdas there (given the few built-ins that accept lambdas).
---
 src/main/java/freemarker/core/EscapeBlock.java     |  8 +++--
 .../IntermediateStreamOperationLikeBuiltIn.java    | 21 +++++++----
 .../freemarker/core/LocalLambdaExpression.java     | 13 +++++--
 .../freemarker/core/UncheckedParseException.java   | 37 +++++++++++++++++++
 src/main/javacc/FTL.jj                             |  2 +-
 src/manual/en_US/book.xml                          | 11 ++++++
 .../java/freemarker/core/LamdaAndEscapeTest.java   | 42 ++++++++++++++++++++++
 7 files changed, 123 insertions(+), 11 deletions(-)

diff --git a/src/main/java/freemarker/core/EscapeBlock.java b/src/main/java/freemarker/core/EscapeBlock.java
index f5d5921..0ff9aa0 100644
--- a/src/main/java/freemarker/core/EscapeBlock.java
+++ b/src/main/java/freemarker/core/EscapeBlock.java
@@ -51,8 +51,12 @@ class EscapeBlock extends TemplateElement {
         return getChildBuffer();
     }
 
-    Expression doEscape(Expression expression) {
-        return escapedExpr.deepCloneWithIdentifierReplaced(variable, expression, new ReplacemenetState());
+    Expression doEscape(Expression expression) throws ParseException {
+        try {
+            return escapedExpr.deepCloneWithIdentifierReplaced(variable, expression, new ReplacemenetState());
+        } catch (UncheckedParseException e) {
+            throw e.getParseException();
+        }
     }
 
     @Override
diff --git a/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java b/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java
index 0d1378b..d743d8d 100644
--- a/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java
+++ b/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java
@@ -48,9 +48,14 @@ abstract class IntermediateStreamOperationLikeBuiltIn extends BuiltInWithParseTi
         if (parameters.size() != 1) {
             throw newArgumentCountException("requires exactly 1", openParen, closeParen);
         }
-        this.elementTransformerExp = parameters.get(0);
-        if (elementTransformerExp instanceof LocalLambdaExpression) {
-            LocalLambdaExpression localLambdaExp = (LocalLambdaExpression) elementTransformerExp;
+        Expression elementTransformerExp = parameters.get(0);
+        setElementTransformerExp(elementTransformerExp);
+    }
+
+    private void setElementTransformerExp(Expression elementTransformerExp) throws ParseException {
+        this.elementTransformerExp = elementTransformerExp;
+        if (this.elementTransformerExp instanceof LocalLambdaExpression) {
+            LocalLambdaExpression localLambdaExp = (LocalLambdaExpression) this.elementTransformerExp;
             checkLocalLambdaParamCount(localLambdaExp, 1);
             // We can't do this with other kind of expressions, like a function or method reference, as they
             // need to be evaluated on runtime:
@@ -100,9 +105,13 @@ abstract class IntermediateStreamOperationLikeBuiltIn extends BuiltInWithParseTi
 
     protected void cloneArguments(
             Expression clone, String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) {
-        ((IntermediateStreamOperationLikeBuiltIn) clone).elementTransformerExp
-                = elementTransformerExp.deepCloneWithIdentifierReplaced(
-                        replacedIdentifier, replacement, replacementState);
+        try {
+            ((IntermediateStreamOperationLikeBuiltIn) clone).setElementTransformerExp(
+                    elementTransformerExp.deepCloneWithIdentifierReplaced(
+                            replacedIdentifier, replacement, replacementState));
+        } catch (ParseException e) {
+            throw new BugException("Deep-clone elementTransformerExp failed", e);
+        }
     }
 
     TemplateModel _eval(Environment env) throws TemplateException {
diff --git a/src/main/java/freemarker/core/LocalLambdaExpression.java b/src/main/java/freemarker/core/LocalLambdaExpression.java
index ece8d37..68b3712 100644
--- a/src/main/java/freemarker/core/LocalLambdaExpression.java
+++ b/src/main/java/freemarker/core/LocalLambdaExpression.java
@@ -75,8 +75,17 @@ final class LocalLambdaExpression extends Expression {
     @Override
     protected Expression deepCloneWithIdentifierReplaced_inner(
             String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) {
-        // TODO [lambda] replacement in lho is illegal; detect it
-    	return new LocalLambdaExpression(
+        for (Identifier parameter : lho.getParameters()) {
+            if (parameter.getName().equals(replacedIdentifier)) {
+                // As Expression.deepCloneWithIdentifierReplaced was exposed to users back then, now we can't add
+                // "throws ParseException" to this, therefore, we use UncheckedParseException as a workaround.
+                throw new UncheckedParseException(new ParseException(
+                        "Escape placeholder (" + replacedIdentifier + ") can't be used in the " +
+                        "parameter list of a lambda expressions.", this));
+            }
+        }
+
+        return new LocalLambdaExpression(
     	        lho,
     	        rho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState));
     }
diff --git a/src/main/java/freemarker/core/UncheckedParseException.java b/src/main/java/freemarker/core/UncheckedParseException.java
new file mode 100644
index 0000000..a7b3d86
--- /dev/null
+++ b/src/main/java/freemarker/core/UncheckedParseException.java
@@ -0,0 +1,37 @@
+/*
+ * 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 freemarker.core;
+
+/**
+ * Workaround for throwing {@link ParseException} in methods that wasn't declared to throw it, and we can't change that
+ * for backward compatibility.
+ */
+final class UncheckedParseException extends RuntimeException {
+
+    private final ParseException parseException;
+
+    public UncheckedParseException(ParseException parseException) {
+        this.parseException = parseException;
+    }
+
+    public ParseException getParseException() {
+        return parseException;
+    }
+}
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index 363e8bc..a12f4f1 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -477,7 +477,7 @@ public class FMParser {
         notNumberLiteral(exp, "boolean (true/false)");
     }
 
-    private Expression escapedExpression(Expression exp) {
+    private Expression escapedExpression(Expression exp) throws ParseException {
         if (!escapes.isEmpty()) {
             return ((EscapeBlock) escapes.getFirst()).doEscape(exp);
         } else {
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index afb00b2..d3a4ef1 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -28689,6 +28689,17 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
 
           <itemizedlist>
             <listitem>
+              <para>Bug fixed: In <literal>&lt;#escape
+              <replaceable>placeholder</replaceable> as
+              <replaceable>escExpression</replaceable>&gt;</literal>, the
+              <literal><replaceable>placeholder</replaceable></literal> wasn't
+              substituted inside lambda expressions inside
+              <literal><replaceable>escExpression</replaceable></literal>.
+              Fortunately it's very unlikely that anyone wanted to use lambdas
+              there (given the few built-ins that accept lambdas).</para>
+            </listitem>
+
+            <listitem>
               <para>Bug fixed: AST traversal API now can properly traverse the
               inside of lambda expressions (such as the parameter list)</para>
             </listitem>
diff --git a/src/test/java/freemarker/core/LamdaAndEscapeTest.java b/src/test/java/freemarker/core/LamdaAndEscapeTest.java
new file mode 100644
index 0000000..2c0ccc7
--- /dev/null
+++ b/src/test/java/freemarker/core/LamdaAndEscapeTest.java
@@ -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.
+ */
+
+package freemarker.core;
+
+import org.junit.Test;
+
+import freemarker.test.TemplateTest;
+
+public class LamdaAndEscapeTest extends TemplateTest {
+
+    @Test
+    public void testSubstitutionInLambdaLHO() throws Exception {
+        assertErrorContains("<#escape myPlaceholder as ['a', 'b', 'c']?map(myPlaceholder -> 'x')>${'X'}</#escape>",
+                "myPlaceholder", "lambda");
+    }
+
+    @Test
+    public void testSubstitutionInLambdaRHO() throws Exception {
+        assertOutput("<#escape x as ['a', 'b', 'c']?map(it -> it + x)?join(', ')>" +
+                "${'X'}" +
+                "</#escape>",
+                "aX, bX, cX");
+    }
+
+}


[freemarker] 02/04: Updated version number to 2.3.30 nightly

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git

commit f7a1013ecf9b5fa098eb8d47128c47ca8e9bf12d
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Aug 17 19:43:07 2019 +0200

    Updated version number to 2.3.30 nightly
---
 src/main/resources/freemarker/version.properties |  8 +++----
 src/manual/en_US/book.xml                        | 28 +++++++++++++++++++++++-
 2 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/src/main/resources/freemarker/version.properties b/src/main/resources/freemarker/version.properties
index 4f58488..121ac78 100644
--- a/src/main/resources/freemarker/version.properties
+++ b/src/main/resources/freemarker/version.properties
@@ -56,11 +56,11 @@
 #   continue working without modification or recompilation.
 # - When the major version number is increased, major backward
 #   compatibility violations are allowed, but still should be avoided.
-version=2.3.29
+version=2.3.30-nightly_@timestampInVersion@
 # This exists as for Maven we use "-SNAPSHOT" for nightly releases,
 # and no _nightly_@timestampInVersion@. For final releases it's the
 # same as "version".
-mavenVersion=2.3.29
+mavenVersion=2.3.30-SNAPSHOT
 
 # Version string that conforms to OSGi
 # ------------------------------------
@@ -73,7 +73,7 @@ mavenVersion=2.3.29
 #   2.4.0.rc01
 #   2.4.0.pre01
 #   2.4.0.nightly_@timestampInVersion@
-versionForOSGi=2.3.29.stable
+versionForOSGi=2.3.30.nightly
 
 # Version string that conforms to legacy MF
 # -----------------------------------------
@@ -92,7 +92,7 @@ versionForOSGi=2.3.29.stable
 # "97 denotes "nightly", 98 denotes "pre", 99 denotes "rc" build.
 # In general, for the nightly/preview/rc Y of version 2.X, the versionForMf is
 # 2.X-1.(99|98).Y. Note the X-1.
-versionForMf=2.3.29
+versionForMf=2.3.29.97
 
 # The date of the build.
 # This should be automatically filled by the building tool (Ant).
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index dea0f3f..a49e8ea 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -30,7 +30,7 @@
 
     <titleabbrev>Manual</titleabbrev>
 
-    <productname>Freemarker 2.3.29</productname>
+    <productname>Freemarker 2.3.30</productname>
   </info>
 
   <preface role="index.html" xml:id="preface">
@@ -28669,6 +28669,32 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
     <appendix xml:id="app_versions">
       <title>Version history</title>
 
+      <section xml:id="versions_2_3_30">
+        <title>2.3.30</title>
+
+        <para>Release date: [FIXME]</para>
+
+        <section>
+          <title>Changes on the FTL side</title>
+
+          <itemizedlist>
+            <listitem>
+              <para>[TODO]</para>
+            </listitem>
+          </itemizedlist>
+        </section>
+
+        <section>
+          <title>Changes on the Java side</title>
+
+          <itemizedlist>
+            <listitem>
+              <para>[TODO]</para>
+            </listitem>
+          </itemizedlist>
+        </section>
+      </section>
+
       <section xml:id="versions_2_3_29">
         <title>2.3.29</title>
 


[freemarker] 03/04: Finished local lambda expression AST API (overlooked this TODO in 2.3.29)

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git

commit 0e0c2f953b470a48845b7d08f61cb4e6588d8264
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Aug 17 19:45:24 2019 +0200

    Finished local lambda expression AST API (overlooked this TODO in 2.3.29)
---
 .../freemarker/core/LocalLambdaExpression.java     | 23 ++++---
 src/manual/en_US/book.xml                          |  3 +-
 src/test/java/freemarker/core/ASTTest.java         |  4 ++
 src/test/resources/freemarker/core/ast-lambda.ast  | 72 ++++++++++++++++++++++
 src/test/resources/freemarker/core/ast-lambda.ftl  | 23 +++++++
 5 files changed, 117 insertions(+), 8 deletions(-)

diff --git a/src/main/java/freemarker/core/LocalLambdaExpression.java b/src/main/java/freemarker/core/LocalLambdaExpression.java
index b7d1896..ece8d37 100644
--- a/src/main/java/freemarker/core/LocalLambdaExpression.java
+++ b/src/main/java/freemarker/core/LocalLambdaExpression.java
@@ -83,22 +83,31 @@ final class LocalLambdaExpression extends Expression {
 
     @Override
     int getParameterCount() {
-        return 2;
+        return lho.getParameters().size() + 1;
     }
 
     @Override
     Object getParameterValue(int idx) {
-        // TODO [lambda] should be similar to #function
-        switch (idx) {
-        case 0: return lho;
-        case 1: return rho;
-        default: throw new IndexOutOfBoundsException();
+        int paramCount = getParameterCount();
+        if (idx < paramCount - 1) {
+            return lho.getParameters().get(idx);
+        } else if (idx == paramCount - 1) {
+            return rho;
+        } else {
+            throw new IndexOutOfBoundsException();
         }
     }
 
     @Override
     ParameterRole getParameterRole(int idx) {
-        return ParameterRole.forBinaryOperatorOperand(idx);
+        int paramCount = getParameterCount();
+        if (idx < paramCount - 1) {
+            return ParameterRole.ARGUMENT_NAME;
+        } else if (idx == paramCount - 1) {
+            return ParameterRole.VALUE;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
     }
 
     LambdaParameterList getLambdaParameterList() {
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index a49e8ea..afb00b2 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -28689,7 +28689,8 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
 
           <itemizedlist>
             <listitem>
-              <para>[TODO]</para>
+              <para>Bug fixed: AST traversal API now can properly traverse the
+              inside of lambda expressions (such as the parameter list)</para>
             </listitem>
           </itemizedlist>
         </section>
diff --git a/src/test/java/freemarker/core/ASTTest.java b/src/test/java/freemarker/core/ASTTest.java
index 8a7dee1..b1b308e 100644
--- a/src/test/java/freemarker/core/ASTTest.java
+++ b/src/test/java/freemarker/core/ASTTest.java
@@ -69,6 +69,10 @@ public class ASTTest extends FileTestCase {
         testAST("ast-nestedignoredchildren");
     }
 
+    public void testLambda() throws Exception {
+        testAST("ast-lambda");
+    }
+
     public void testLocations() throws Exception {
         testASTWithLocations("ast-locations");
     }
diff --git a/src/test/resources/freemarker/core/ast-lambda.ast b/src/test/resources/freemarker/core/ast-lambda.ast
new file mode 100644
index 0000000..2b02f53
--- /dev/null
+++ b/src/test/resources/freemarker/core/ast-lambda.ast
@@ -0,0 +1,72 @@
+#mixed_content  // f.c.MixedContent
+    #text  // f.c.TextBlock
+        - content: "1 "  // String
+    ${...}  // f.c.DollarVariable
+        - content: ?filter(...)  // f.c.BuiltInsForSequences$filterBI
+            - left-hand operand: x  // f.c.Identifier
+            - right-hand operand: "filter"  // String
+            - argument value: ->  // f.c.LocalLambdaExpression
+                - argument name: x  // f.c.Identifier
+                - value: !  // f.c.NotExpression
+                    - right-hand operand: x  // f.c.Identifier
+    #text  // f.c.TextBlock
+        - content: "\n2 "  // String
+    ${...}  // f.c.DollarVariable
+        - content: ?filter(...)  // f.c.BuiltInsForSequences$filterBI
+            - left-hand operand: x  // f.c.Identifier
+            - right-hand operand: "filter"  // String
+            - argument value: ->  // f.c.LocalLambdaExpression
+                - argument name: x  // f.c.Identifier
+                - value: !  // f.c.NotExpression
+                    - right-hand operand: x  // f.c.Identifier
+    #text  // f.c.TextBlock
+        - content: "\n3 "  // String
+    ${...}  // f.c.DollarVariable
+        - content: ?filter(...)  // f.c.BuiltInsForSequences$filterBI
+            - left-hand operand: x  // f.c.Identifier
+            - right-hand operand: "filter"  // String
+            - argument value: ->  // f.c.LocalLambdaExpression
+                - argument name: x  // f.c.Identifier
+                - value: (...)  // f.c.ParentheticalExpression
+                    - enclosed operand: !  // f.c.NotExpression
+                        - right-hand operand: x  // f.c.Identifier
+    #text  // f.c.TextBlock
+        - content: "\n4 "  // String
+    ${...}  // f.c.DollarVariable
+        - content: ?filter(...)  // f.c.BuiltInsForSequences$filterBI
+            - left-hand operand: x  // f.c.Identifier
+            - right-hand operand: "filter"  // String
+            - argument value: ->  // f.c.LocalLambdaExpression
+                - argument name: x  // f.c.Identifier
+                - value: ||  // f.c.OrExpression
+                    - left-hand operand: ==  // f.c.ComparisonExpression
+                        - left-hand operand: +  // f.c.AddConcatExpression
+                            - left-hand operand: x  // f.c.Identifier
+                            - right-hand operand: 1  // f.c.NumberLiteral
+                        - right-hand operand: 2  // f.c.NumberLiteral
+                    - right-hand operand: ==  // f.c.ComparisonExpression
+                        - left-hand operand: /  // f.c.ArithmeticExpression
+                            - left-hand operand: x  // f.c.Identifier
+                            - right-hand operand: 2  // f.c.NumberLiteral
+                            - AST-node subtype: "2"  // Integer
+                        - right-hand operand: 4  // f.c.NumberLiteral
+    #text  // f.c.TextBlock
+        - content: "\n5 "  // String
+    ${...}  // f.c.DollarVariable
+        - content: ?filter(...)  // f.c.BuiltInsForSequences$filterBI
+            - left-hand operand: x  // f.c.Identifier
+            - right-hand operand: "filter"  // String
+            - argument value: ->  // f.c.LocalLambdaExpression
+                - argument name: x  // f.c.Identifier
+                - value: ?hasContent  // f.c.BuiltInsForExistenceHandling$has_contentBI
+                    - left-hand operand: ?filter(...)  // f.c.BuiltInsForSequences$filterBI
+                        - left-hand operand: x  // f.c.Identifier
+                        - right-hand operand: "filter"  // String
+                        - argument value: ->  // f.c.LocalLambdaExpression
+                            - argument name: y  // f.c.Identifier
+                            - value: ==  // f.c.ComparisonExpression
+                                - left-hand operand: y  // f.c.Identifier
+                                - right-hand operand: 1  // f.c.NumberLiteral
+                    - right-hand operand: "hasContent"  // String
+    #text  // f.c.TextBlock
+        - content: "\n"  // String
diff --git a/src/test/resources/freemarker/core/ast-lambda.ftl b/src/test/resources/freemarker/core/ast-lambda.ftl
new file mode 100644
index 0000000..974d3c0
--- /dev/null
+++ b/src/test/resources/freemarker/core/ast-lambda.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.
+-->
+1 ${x?filter(x -> !x)}
+2 ${x?filter((x) -> !x)}
+3 ${x?filter((x) -> (!x))}
+4 ${x?filter(x -> x + 1 == 2 || x / 2 == 4)}
+5 ${x?filter(x -> x?filter(y -> y == 1)?hasContent)}


[freemarker] 01/04: Updated release date in Manual

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch 2.3-gae
in repository https://gitbox.apache.org/repos/asf/freemarker.git

commit 6795cd7e39891b94ef91858a3902d0613aac9c56
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Aug 17 19:36:02 2019 +0200

    Updated release date in Manual
---
 src/manual/en_US/book.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 4a6e917..dea0f3f 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -28672,7 +28672,7 @@ TemplateModel x = env.getVariable("x");  // get variable x</programlisting>
       <section xml:id="versions_2_3_29">
         <title>2.3.29</title>
 
-        <para>Release date: 2019-08-09 + release process</para>
+        <para>Release date: 2019-08-17</para>
 
         <section>
           <title>Changes on the FTL side</title>