You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/07/24 07:32:51 UTC

[groovy] branch master updated: GROOVY-9272: Tweak switch expression(basic support for JEP 406)

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

sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new a353850  GROOVY-9272: Tweak switch expression(basic support for JEP 406)
a353850 is described below

commit a353850eae5adbf69c2a339d6b468dc50817bc6d
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Jul 24 15:29:22 2021 +0800

    GROOVY-9272: Tweak switch expression(basic support for JEP 406)
---
 .../apache/groovy/parser/antlr4/AstBuilder.java    | 49 ++++++++++++++++++--
 .../core/SwitchExpression_27x.groovy               | 29 ++++++++++++
 .../core/SwitchExpression_28x.groovy               | 52 ++++++++++++++++++++++
 .../groovy/parser/antlr4/GroovyParserTest.groovy   |  2 +
 4 files changed, 128 insertions(+), 4 deletions(-)

diff --git a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 621bc0c..1b1b241 100644
--- a/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ b/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -353,6 +353,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.closureX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
@@ -364,6 +365,7 @@ import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
  * Builds the AST from the parse tree generated by Antlr4.
  */
 public class AstBuilder extends GroovyParserBaseVisitor<Object> {
+
     public AstBuilder(final SourceUnit sourceUnit, final boolean groovydocEnabled, final boolean runtimeGroovydocEnabled) {
         this.sourceUnit = sourceUnit;
         this.moduleNode = new ModuleNode(sourceUnit);
@@ -1142,9 +1144,13 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
         switchExpressionRuleContextStack.push(ctx);
         try {
             validateSwitchExpressionLabels(ctx);
+            final String variableName = "__$$sev" + switchExpressionVariableSeq++;
             List<Tuple3<List<Statement>, Boolean, Boolean>> statementInfoList =
                     ctx.switchBlockStatementExpressionGroup().stream()
-                            .map(e -> this.visitSwitchBlockStatementExpressionGroup(e))
+                            .map(e -> {
+                                e.putNodeMetaData(SWITCH_EXPRESSION_VARIABLE_NAME, variableName);
+                                return this.visitSwitchBlockStatementExpressionGroup(e);
+                            })
                             .collect(Collectors.toList());
 
             Boolean isArrow = statementInfoList.get(0).getV2();
@@ -1182,7 +1188,6 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
                 throw createParsingFailedException("default case should appear at last", defaultStatementList.get(0));
             }
 
-            String variableName = "__$$sev" + switchExpressionVariableSeq++;
             Statement declarationStatement = declS(localVarX(variableName), this.visitExpressionInPar(ctx.expressionInPar()));
             SwitchStatement switchStatement = configureAST(
                     new SwitchStatement(
@@ -1299,6 +1304,8 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
                         }
                     }
 
+                    VariableExpression switchExpressionVar = varX((String) ctx.getNodeMetaData(SWITCH_EXPRESSION_VARIABLE_NAME));
+
                     switch (tuple.getV1().getType()) {
                         case CASE: {
                             if (!asBoolean(statementList)) {
@@ -1307,6 +1314,18 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
 
                             for (int i = 0, n = tuple.getV2().size(); i < n; i++) {
                                 Expression expr = tuple.getV2().get(i);
+                                MethodCallExpression mce = expr.getNodeMetaData(SWITCH_EXPRESSION_DECONSTRUCTED);
+                                BlockStatement newCodeBlock = codeBlock;
+                                if (null != mce) {
+                                    List<Statement> declarationStatementList = ((TupleExpression) mce.getArguments()).getExpressions().stream().map(arg -> {
+                                        String argName = arg.getText();
+                                        return declS(localVarX(argName), propX(switchExpressionVar, argName));
+                                    }).collect(Collectors.toList());
+                                    newCodeBlock =
+                                            createBlockStatement(
+                                                    Stream.concat(declarationStatementList.stream(), Stream.of(codeBlock))
+                                                            .collect(Collectors.toList()));
+                                }
 
                                 statementList.add(
                                         configureAST(
@@ -1314,7 +1333,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
                                                         expr,
 
                                                         // check whether processing the last label. if yes, block statement should be attached.
-                                                        (isLast && i == n - 1) ? codeBlock
+                                                        (isLast && i == n - 1) ? newCodeBlock
                                                                 : EmptyStatement.INSTANCE
                                                 ),
                                                 firstLabelHolder.get(0)));
@@ -1355,7 +1374,27 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     public Tuple3<Token, List<Expression>, Integer> visitSwitchExpressionLabel(SwitchExpressionLabelContext ctx) {
         final Integer acType = ctx.ac.getType();
         if (asBoolean(ctx.CASE())) {
-            return tuple(ctx.CASE().getSymbol(), this.visitExpressionList(ctx.expressionList()), acType);
+            List<Expression> expressionList = this.visitExpressionList(ctx.expressionList());
+            expressionList = expressionList.stream().map(expr -> {
+                if (expr instanceof MethodCallExpression) {
+                    MethodCallExpression mce = (MethodCallExpression) expr;
+                    String methodName = mce.getMethodAsString();
+                    if (mce.isImplicitThis() && Character.isUpperCase(methodName.charAt(0))) {
+                        List<Expression> argList = ((TupleExpression) mce.getArguments()).getExpressions();
+                        boolean allVariableArgs = !argList.isEmpty() && argList.stream()
+                                .allMatch(arg -> arg instanceof VariableExpression
+                                        && Character.isLowerCase(((VariableExpression) arg).getName().charAt(0)));
+                        if (allVariableArgs) {
+                            VariableExpression variableExpression = new VariableExpression(methodName);
+                            variableExpression.putNodeMetaData(SWITCH_EXPRESSION_DECONSTRUCTED, expr);
+                            return variableExpression;
+                        }
+                    }
+                }
+
+                return expr;
+            }).collect(Collectors.toList());
+            return tuple(ctx.CASE().getSymbol(), expressionList, acType);
         } else if (asBoolean(ctx.DEFAULT())) {
             return tuple(ctx.DEFAULT().getSymbol(), Collections.singletonList(EmptyExpression.INSTANCE), acType);
         }
@@ -5043,4 +5082,6 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> {
     private static final String FLOATING_POINT_LITERAL_TEXT = "_FLOATING_POINT_LITERAL_TEXT";
     private static final String ENCLOSING_INSTANCE_EXPRESSION = "_ENCLOSING_INSTANCE_EXPRESSION";
     private static final String IS_YIELD_STATEMENT = "_IS_YIELD_STATEMENT";
+    private static final String SWITCH_EXPRESSION_DECONSTRUCTED = "_SWITCH_EXPRESSION_DECONSTRUCTED";
+    private static final String SWITCH_EXPRESSION_VARIABLE_NAME = "_SWITCH_EXPRESSION_VARIABLE_NAME";
 }
diff --git a/src/test-resources/core/SwitchExpression_27x.groovy b/src/test-resources/core/SwitchExpression_27x.groovy
new file mode 100644
index 0000000..a9a148a
--- /dev/null
+++ b/src/test-resources/core/SwitchExpression_27x.groovy
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+class Coord {
+    int x
+    int y
+}
+
+def s = new Coord(x: 2, y: 5)
+def result = switch (s) {
+    case Coord(x, y) -> x * 3 + y
+}
+assert 11 == result
diff --git a/src/test-resources/core/SwitchExpression_28x.groovy b/src/test-resources/core/SwitchExpression_28x.groovy
new file mode 100644
index 0000000..53c324f
--- /dev/null
+++ b/src/test-resources/core/SwitchExpression_28x.groovy
@@ -0,0 +1,52 @@
+/*
+ *  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.
+ */
+
+interface Expr {}
+class IntExpr implements Expr {
+    int i
+}
+class NegExpr implements Expr {
+    Expr n
+}
+class AddExpr implements Expr {
+    Expr left, right
+}
+class MulExpr implements Expr {
+    Expr left, right
+}
+
+int eval(Expr expr) {
+    int result = switch(expr) {
+        case IntExpr(i) -> i
+        case NegExpr(n) -> -eval(n)
+        case AddExpr(left, right) -> eval(left) + eval(right)
+        case MulExpr(left, right) -> eval(left) * eval(right)
+        default -> throw new IllegalStateException()
+    }
+    return result
+}
+
+// -2 * 5 + 4
+assert -6 == eval(new AddExpr(
+        left: new MulExpr(
+                left: new NegExpr(n: new IntExpr(i: 2)),
+                right: new IntExpr(i: 5)
+        ),
+        right: new IntExpr(i: 4)
+))
diff --git a/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy b/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
index 10c2af0..7a15398 100644
--- a/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
+++ b/src/test/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
@@ -450,6 +450,8 @@ final class GroovyParserTest extends GroovyTestCase {
         doRunAndTestAntlr4('core/SwitchExpression_24x.groovy')
         doRunAndTestAntlr4('core/SwitchExpression_25x.groovy')
         doRunAndTestAntlr4('core/SwitchExpression_26x.groovy')
+        doRunAndTestAntlr4('core/SwitchExpression_27x.groovy')
+        doRunAndTestAntlr4('core/SwitchExpression_28x.groovy')
     }
 
     void "test groovy core - BUG"() {