Hi Daniel, I don't think you should push this to master without further discussion. It would be better to develop experiments on branches, and only integrate once we got general agreement.
2017-09-27 16:25 GMT+02:00 <sun...@apache.org>: > Repository: groovy > Updated Branches: > refs/heads/master 4cc78440d -> ce1d251bf > > > Support safe chain operator > > > Project: http://git-wip-us.apache.org/repos/asf/groovy/repo > Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/ce1d251b > Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/ce1d251b > Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/ce1d251b > > Branch: refs/heads/master > Commit: ce1d251bfbd67c8a3129034179e9de034149d04b > Parents: 4cc7844 > Author: sunlan <sun...@apache.org> > Authored: Wed Sep 27 22:25:38 2017 +0800 > Committer: sunlan <sun...@apache.org> > Committed: Wed Sep 27 22:25:38 2017 +0800 > > ---------------------------------------------------------------------- > src/antlr/GroovyLexer.g4 | 1 + > src/antlr/GroovyParser.g4 | 1 + > .../apache/groovy/parser/antlr4/AstBuilder.java | 42 ++++++++++++-------- > .../parser/antlr4/GroovyParserTest.groovy | 4 ++ > .../resources/core/SafeChainOperator.groovy | 30 ++++++++++++++ > 5 files changed, 62 insertions(+), 16 deletions(-) > ---------------------------------------------------------------------- > > > http://git-wip-us.apache.org/repos/asf/groovy/blob/ce1d251b/src/antlr/ > GroovyLexer.g4 > ---------------------------------------------------------------------- > diff --git a/src/antlr/GroovyLexer.g4 b/src/antlr/GroovyLexer.g4 > index 05fb767..bcf6f24 100644 > --- a/src/antlr/GroovyLexer.g4 > +++ b/src/antlr/GroovyLexer.g4 > @@ -768,6 +768,7 @@ RANGE_INCLUSIVE : '..'; > RANGE_EXCLUSIVE : '..<'; > SPREAD_DOT : '*.'; > SAFE_DOT : '?.'; > +SAFE_CHAIN_DOT : '??.'; > ELVIS : '?:'; > METHOD_POINTER : '.&'; > METHOD_REFERENCE : '::'; > > http://git-wip-us.apache.org/repos/asf/groovy/blob/ce1d251b/src/antlr/ > GroovyParser.g4 > ---------------------------------------------------------------------- > diff --git a/src/antlr/GroovyParser.g4 b/src/antlr/GroovyParser.g4 > index 8f8aad3..cb2c3de 100644 > --- a/src/antlr/GroovyParser.g4 > +++ b/src/antlr/GroovyParser.g4 > @@ -982,6 +982,7 @@ locals[ boolean isInsideClosure ] > // AT: foo.@bar selects the field (or attribute), not property > ( SPREAD_DOT nls (AT | nonWildcardTypeArguments)? // Spread > operator: x*.y === x?.collect{it.y} > | SAFE_DOT nls (AT | nonWildcardTypeArguments)? // > Optional-null operator: x?.y === (x==null)?null:x.y > + | SAFE_CHAIN_DOT nls (AT | nonWildcardTypeArguments)? // > Optional-null chain operator: x??.y.z === x?.y?.z > | METHOD_POINTER nls // Method > pointer operator: foo.&y == foo.metaClass.getMethodPointer(foo, "y") > | METHOD_REFERENCE nls // Method > reference: System.out::println > | DOT nls (AT | nonWildcardTypeArguments)? // The > all-powerful dot. > > http://git-wip-us.apache.org/repos/asf/groovy/blob/ > ce1d251b/subprojects/parser-antlr4/src/main/java/org/ > apache/groovy/parser/antlr4/AstBuilder.java > ---------------------------------------------------------------------- > diff --git a/subprojects/parser-antlr4/src/main/java/org/apache/ > groovy/parser/antlr4/AstBuilder.java b/subprojects/parser-antlr4/ > src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java > index 5abc847..ad632d9 100644 > --- a/subprojects/parser-antlr4/src/main/java/org/apache/ > groovy/parser/antlr4/AstBuilder.java > +++ b/subprojects/parser-antlr4/src/main/java/org/apache/ > groovy/parser/antlr4/AstBuilder.java > @@ -1959,23 +1959,16 @@ public class AstBuilder extends > GroovyParserBaseVisitor<Object> implements Groov > > > if (asBoolean(ctx.DOT())) { > - if (asBoolean(ctx.AT())) { // e.g. obj.@a > - return configureAST(new AttributeExpression(baseExpr, > namePartExpr), ctx); > - } else { // e.g. obj.p > - PropertyExpression propertyExpression = new > PropertyExpression(baseExpr, namePartExpr); > - propertyExpression.putNodeMetaData(PATH_ > EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes); > + boolean isSafeChain = isTrue(baseExpr, > PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN); > > - return configureAST(propertyExpression, ctx); > - } > + return createDotExpression(ctx, baseExpr, namePartExpr, > genericsTypes, isSafeChain); > } else if (asBoolean(ctx.SAFE_DOT())) { > - if (asBoolean(ctx.AT())) { // e.g. obj?.@a > - return configureAST(new AttributeExpression(baseExpr, > namePartExpr, true), ctx); > - } else { // e.g. obj?.p > - PropertyExpression propertyExpression = new > PropertyExpression(baseExpr, namePartExpr, true); > - propertyExpression.putNodeMetaData(PATH_ > EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes); > + return createDotExpression(ctx, baseExpr, namePartExpr, > genericsTypes, true); > + } else if (asBoolean(ctx.SAFE_CHAIN_DOT())) { // e.g. > obj??.a OR obj??.@a > + Expression expression = createDotExpression(ctx, > baseExpr, namePartExpr, genericsTypes, true); > + > expression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN, > true); > > - return configureAST(propertyExpression, ctx); > - } > + return expression; > } else if (asBoolean(ctx.METHOD_POINTER())) { // e.g. obj.&m > return configureAST(new MethodPointerExpression(baseExpr, > namePartExpr), ctx); > } else if (asBoolean(ctx.METHOD_REFERENCE())) { // e.g. > obj::m > @@ -2210,6 +2203,17 @@ public class AstBuilder extends > GroovyParserBaseVisitor<Object> implements Groov > throw createParsingFailedException("Unsupported path element: " > + ctx.getText(), ctx); > } > > + private Expression createDotExpression(PathElementContext ctx, > Expression baseExpr, Expression namePartExpr, GenericsType[] genericsTypes, > boolean safe) { > + if (asBoolean(ctx.AT())) { // e.g. obj.@a OR obj?.@a > + return configureAST(new AttributeExpression(baseExpr, > namePartExpr, safe), ctx); > + } else { // e.g. obj.p OR obj?.p > + PropertyExpression propertyExpression = new > PropertyExpression(baseExpr, namePartExpr, safe); > + propertyExpression.putNodeMetaData(PATH_ > EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes); > + > + return configureAST(propertyExpression, ctx); > + } > + } > + > private MethodCallExpression createCallMethodCallExpression(Expression > baseExpr, Expression argumentsExpr) { > return createCallMethodCallExpression(baseExpr, argumentsExpr, > false); > } > @@ -3973,10 +3977,15 @@ public class AstBuilder extends > GroovyParserBaseVisitor<Object> implements Groov > .reduce(primaryExpr, > (r, e) -> { > PathElementContext pathElementContext = > (PathElementContext) e; > - > > pathElementContext.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR, > r); > + Expression expression = this.visitPathElement( > pathElementContext); > + > + boolean isSafeChain = isTrue((Expression) r, > PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN); > + if (isSafeChain) { > + expression.putNodeMetaData( > PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN, true); > + } > > - return this.visitPathElement( > pathElementContext); > + return expression; > } > ); > } > @@ -4415,6 +4424,7 @@ public class AstBuilder extends > GroovyParserBaseVisitor<Object> implements Groov > > private static final String PATH_EXPRESSION_BASE_EXPR = > "_PATH_EXPRESSION_BASE_EXPR"; > private static final String PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES > = "_PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES"; > + private static final String PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN = > "_PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN"; > private static final String CMD_EXPRESSION_BASE_EXPR = > "_CMD_EXPRESSION_BASE_EXPR"; > private static final String TYPE_DECLARATION_MODIFIERS = > "_TYPE_DECLARATION_MODIFIERS"; > private static final String CLASS_DECLARATION_CLASS_NODE = > "_CLASS_DECLARATION_CLASS_NODE"; > > http://git-wip-us.apache.org/repos/asf/groovy/blob/ > ce1d251b/subprojects/parser-antlr4/src/test/groovy/org/ > apache/groovy/parser/antlr4/GroovyParserTest.groovy > ---------------------------------------------------------------------- > diff --git a/subprojects/parser-antlr4/src/test/groovy/org/apache/ > groovy/parser/antlr4/GroovyParserTest.groovy b/subprojects/parser-antlr4/ > src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy > index 91cd61f..fd65344 100644 > --- a/subprojects/parser-antlr4/src/test/groovy/org/apache/ > groovy/parser/antlr4/GroovyParserTest.groovy > +++ b/subprojects/parser-antlr4/src/test/groovy/org/apache/ > groovy/parser/antlr4/GroovyParserTest.groovy > @@ -369,6 +369,10 @@ class GroovyParserTest extends GroovyTestCase { > doRunAndTest('core/Number_01x.groovy'); > } > > + void "test groovy core - SafeChainOperator"() { > + doRunAndTest('core/SafeChainOperator.groovy'); > + } > + > void "test groovy core - BUG"() { > doRunAndTest('bugs/BUG-GROOVY-4757.groovy'); > doRunAndTest('bugs/BUG-GROOVY-5652.groovy'); > > http://git-wip-us.apache.org/repos/asf/groovy/blob/ > ce1d251b/subprojects/parser-antlr4/src/test/resources/ > core/SafeChainOperator.groovy > ---------------------------------------------------------------------- > diff --git > a/subprojects/parser-antlr4/src/test/resources/core/SafeChainOperator.groovy > b/subprojects/parser-antlr4/src/test/resources/core/ > SafeChainOperator.groovy > new file mode 100644 > index 0000000..790dbfe > --- /dev/null > +++ b/subprojects/parser-antlr4/src/test/resources/core/ > SafeChainOperator.groovy > @@ -0,0 +1,30 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one > + * or more contributor license agreements. See the NOTICE file > + * distributed with this work for additional information > + * regarding copyright ownership. The ASF licenses this file > + * to you under the Apache License, Version 2.0 (the > + * "License"); you may not use this file except in compliance > + * with the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, > + * software distributed under the License is distributed on an > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > + * KIND, either express or implied. See the License for the > + * specific language governing permissions and limitations > + * under the License. > + */ > +assert 3 == 1??.plus(2) > +assert 6 == 1??.plus(2).plus(3) > +assert 6 == 1??.plus(2)?.plus(3) > +assert 6 == 1??.plus(2)??.plus(3) > +assert 10 == 1??.plus(2)?.plus(3).plus(4) > +assert 10 == 1?.plus(2)??.plus(3).plus(4) > +assert 10 == 1?.plus(2)?.plus(3)??.plus(4) > +assert 10 == 1.plus(2).plus(3)??.plus(4) > +assert null == null??.plus(2).plus(3) > +assert null == null??.plus(2).plus(3).plus(4) > +assert null == null??.plus(2)??.plus(3).plus(4) > +assert null == null??.plus(2)??.plus(3)?.plus(4) > >