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"() {