blob: 7d802fa13a57ee18c67270710f1faaa1f70bac66 [file] [log] [blame]
/*
* This file is adapted from the Antlr4 Java grammar which has the following license
*
* Copyright (c) 2013 Terence Parr, Sam Harwell
* All rights reserved.
* [The "BSD licence"]
*
* http://www.opensource.org/licenses/bsd-license.php
*
* Subsequent modifications by the Groovy community have been done under the Apache License v2:
*
* 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.
*/
/**
* The Groovy grammar is based on the official grammar for Java:
* https://github.com/antlr/grammars-v4/blob/master/java/Java.g4
*/
parser grammar GroovyParser;
options {
tokenVocab = GroovyLexer;
contextSuperClass = GroovyParserRuleContext;
superClass = AbstractParser;
}
@header {
import java.util.Map;
import org.codehaus.groovy.ast.NodeMetaDataHandler;
import org.apache.groovy.parser.antlr4.SemanticPredicates;
}
@members {
public static class GroovyParserRuleContext extends ParserRuleContext implements NodeMetaDataHandler {
private Map metaDataMap = null;
public GroovyParserRuleContext() {}
public GroovyParserRuleContext(ParserRuleContext parent, int invokingStateNumber) {
super(parent, invokingStateNumber);
}
@Override
public Map<?, ?> getMetaDataMap() {
return this.metaDataMap;
}
@Override
public void setMetaDataMap(Map<?, ?> metaDataMap) {
this.metaDataMap = metaDataMap;
}
}
@Override
public int getSyntaxErrorSource() {
return GroovySyntaxError.PARSER;
}
@Override
public int getErrorLine() {
Token token = _input.LT(-1);
if (null == token) {
return -1;
}
return token.getLine();
}
@Override
public int getErrorColumn() {
Token token = _input.LT(-1);
if (null == token) {
return -1;
}
return token.getCharPositionInLine() + 1 + token.getText().length();
}
}
// starting point for parsing a groovy file
compilationUnit
: nls
packageDeclaration? sep? scriptStatements? EOF
;
scriptStatements
: scriptStatement (sep scriptStatement)* sep?
;
scriptStatement
: importDeclaration // Import statement. Can be used in any scope. Has "import x as y" also.
| typeDeclaration
| statement
;
packageDeclaration
: annotationsOpt PACKAGE qualifiedName
;
importDeclaration
: annotationsOpt IMPORT STATIC? qualifiedName (DOT MUL | AS alias=identifier)?
;
typeDeclaration
: classOrInterfaceModifiersOpt classDeclaration
;
modifier
: classOrInterfaceModifier
| m=( NATIVE
| SYNCHRONIZED
| TRANSIENT
| VOLATILE
| DEF
| VAR
)
;
modifiersOpt
: modifiers?
;
modifiers
: (modifier nls)+
;
classOrInterfaceModifiersOpt
: classOrInterfaceModifiers?
;
classOrInterfaceModifiers
: (classOrInterfaceModifier nls)+
;
classOrInterfaceModifier
: annotation // class or interface
| m=( PUBLIC // class or interface
| PROTECTED // class or interface
| PRIVATE // class or interface
| STATIC // class or interface
| ABSTRACT // class or interface
| FINAL // class only -- does not apply to interfaces
| STRICTFP // class or interface
| DEFAULT // interface only -- does not apply to classes
)
;
variableModifier
: annotation
| m=( FINAL
| DEF
| VAR
// Groovy supports declaring local variables as instance/class fields,
// e.g. import groovy.transform.*; @Field static List awe = [1, 2, 3]
// e.g. import groovy.transform.*; def a = { @Field public List awe = [1, 2, 3] }
// Notice: Groovy 2.4.7 just allows to declare local variables with the following modifiers when using annotations(e.g. @Field)
// TODO check whether the following modifiers accompany annotations or not. Because the legacy codes(e.g. benchmark/bench/heapsort.groovy) allow to declare the special instance/class fields without annotations, we leave it as it is for the time being
| PUBLIC
| PROTECTED
| PRIVATE
| STATIC
| ABSTRACT
| STRICTFP
)
;
variableModifiersOpt
: variableModifiers?
;
variableModifiers
: (variableModifier nls)+
;
typeParameters
: LT nls typeParameter (COMMA nls typeParameter)* nls GT
;
typeParameter
: className (EXTENDS nls typeBound)?
;
typeBound
: type (BITAND nls type)*
;
typeList
: type (COMMA nls type)*
;
/**
* t 0: class; 1: interface; 2: enum; 3: annotation; 4: trait
*/
classDeclaration
locals[ int t ]
: ( CLASS { $t = 0; }
| INTERFACE { $t = 1; }
| ENUM { $t = 2; }
| AT INTERFACE { $t = 3; }
| TRAIT { $t = 4; }
)
identifier nls
(
{ 3 != $t }?
typeParameters? nls
(
{ 2 != $t }?
(EXTENDS nls
(
// Only interface can extend more than one super class
{1 == $t}? scs=typeList
|
sc=type
)
nls)?
|
/* enum should not have type parameters and extends */
)
(
{1 != $t}?
(IMPLEMENTS nls is=typeList nls)?
|
/* interface should not implement other interfaces */
)
|
/* annotation should not have implements and extends*/
)
classBody[$t]
;
// t see the comment of classDeclaration
classBody[int t]
: LBRACE nls
(
/* Only enum can have enum constants */
{ 2 == $t }?
enumConstants? sep?
|
)
classBodyDeclaration[$t]? (sep classBodyDeclaration[$t])* sep? RBRACE
;
enumConstants
: enumConstant (nls COMMA nls enumConstant)* (nls COMMA)?
;
enumConstant
: annotationsOpt identifier arguments? anonymousInnerClassDeclaration[1]?
;
classBodyDeclaration[int t]
: SEMI
| (STATIC nls)? block
| memberDeclaration[$t]
;
memberDeclaration[int t]
: methodDeclaration[0, $t]
| fieldDeclaration
| modifiersOpt classDeclaration
;
/**
* t 0: *class member* all kinds of method declaration AND constructor declaration,
* 1: normal method declaration, 2: abstract method declaration
* 3: normal method declaration OR abstract method declaration
* ct 9: script, other see the comment of classDeclaration
*/
methodDeclaration[int t, int ct]
: { 3 == $ct }?
returnType[$ct] methodName LPAREN rparen (DEFAULT nls elementValue)?
|
modifiersOpt typeParameters? returnType[$ct]?
methodName formalParameters (nls THROWS nls qualifiedClassNameList)?
nls methodBody?
;
methodName
: identifier
| stringLiteral
;
returnType[int ct]
:
standardType
|
// annotation method can not have void return type
{ 3 != $ct }? VOID
;
fieldDeclaration
: variableDeclaration[1]
;
variableDeclarators
: variableDeclarator (COMMA nls variableDeclarator)*
;
variableDeclarator
: variableDeclaratorId (nls ASSIGN nls variableInitializer)?
;
variableDeclaratorId
: identifier
;
variableInitializer
: enhancedStatementExpression
;
variableInitializers
: variableInitializer nls (COMMA nls variableInitializer nls)* nls COMMA?
;
dims
: (annotationsOpt LBRACK RBRACK)+
;
dimsOpt
: dims?
;
standardType
options { baseContext = type; }
: annotationsOpt
(
primitiveType
|
standardClassOrInterfaceType
)
dimsOpt
;
type
: annotationsOpt
(
(
primitiveType
|
// !!! Error Alternative !!!
VOID
)
|
generalClassOrInterfaceType
)
dimsOpt
;
classOrInterfaceType
: ( qualifiedClassName
| qualifiedStandardClassName
) typeArguments?
;
generalClassOrInterfaceType
options { baseContext = classOrInterfaceType; }
: qualifiedClassName typeArguments?
;
standardClassOrInterfaceType
options { baseContext = classOrInterfaceType; }
: qualifiedStandardClassName typeArguments?
;
primitiveType
: BuiltInPrimitiveType
;
typeArguments
: LT nls typeArgument (COMMA nls typeArgument)* nls GT
;
typeArgument
: type
| annotationsOpt QUESTION ((EXTENDS | SUPER) nls type)?
;
annotatedQualifiedClassName
: annotationsOpt qualifiedClassName
;
qualifiedClassNameList
: annotatedQualifiedClassName (COMMA nls annotatedQualifiedClassName)*
;
formalParameters
: LPAREN formalParameterList? rparen
;
formalParameterList
: (formalParameter | thisFormalParameter) (COMMA nls formalParameter)*
;
thisFormalParameter
: type THIS
;
formalParameter
: variableModifiersOpt type? ELLIPSIS? variableDeclaratorId (nls ASSIGN nls expression)?
;
methodBody
: block
;
qualifiedName
: qualifiedNameElement (DOT qualifiedNameElement)*
;
/**
* Java doesn't have the keywords 'as', 'in', 'def', 'trait' so we make some allowances
* for them in package names for better integration with existing Java packages
*/
qualifiedNameElement
: identifier
| DEF
| IN
| AS
| TRAIT
;
qualifiedNameElements
: (qualifiedNameElement DOT)*
;
qualifiedClassName
: qualifiedNameElements identifier
;
qualifiedStandardClassName
: qualifiedNameElements className (DOT className)*
;
literal
: IntegerLiteral #integerLiteralAlt
| FloatingPointLiteral #floatingPointLiteralAlt
| stringLiteral #stringLiteralAlt
| BooleanLiteral #booleanLiteralAlt
| NullLiteral #nullLiteralAlt
;
// GSTRING
gstring
: GStringBegin gstringValue (GStringPart gstringValue)* GStringEnd
;
gstringValue
: gstringPath
| LBRACE statementExpression? RBRACE
| closure
;
gstringPath
: identifier GStringPathPart*
;
// LAMBDA EXPRESSION
lambdaExpression
options { baseContext = standardLambdaExpression; }
: lambdaParameters nls ARROW nls lambdaBody
;
// JAVA STANDARD LAMBDA EXPRESSION
standardLambdaExpression
: standardLambdaParameters nls ARROW nls lambdaBody
;
lambdaParameters
options { baseContext = standardLambdaParameters; }
: formalParameters
// { a -> a * 2 } can be parsed as a lambda expression in a block, but we expect a closure.
// So it is better to put parameters in the parentheses and the following single parameter without parentheses is limited
// | variableDeclaratorId
;
standardLambdaParameters
: formalParameters
| variableDeclaratorId
;
lambdaBody
: block
| statementExpression
;
// CLOSURE
closure
: LBRACE nls (formalParameterList? nls ARROW nls)? blockStatementsOpt RBRACE
;
// GROOVY-8991: Difference in behaviour with closure and lambda
closureOrLambdaExpression
: closure
| lambdaExpression
;
blockStatementsOpt
: blockStatements?
;
blockStatements
: blockStatement (sep blockStatement)* sep?
;
// ANNOTATIONS
annotationsOpt
: (annotation nls)*
;
annotation
: AT annotationName ( LPAREN elementValues? rparen )?
;
elementValues
: elementValuePairs
| elementValue
;
annotationName : qualifiedClassName ;
elementValuePairs
: elementValuePair (COMMA elementValuePair)*
;
elementValuePair
: elementValuePairName nls ASSIGN nls elementValue
;
elementValuePairName
: identifier
| keywords
;
// TODO verify the potential performance issue because rule expression contains sub-rule assignments(https://github.com/antlr/grammars-v4/issues/215)
elementValue
: elementValueArrayInitializer
| annotation
| expression
;
elementValueArrayInitializer
: LBRACK (elementValue (COMMA elementValue)*)? (COMMA)? RBRACK
;
// STATEMENTS / BLOCKS
block
: LBRACE (nls | sep*) blockStatementsOpt RBRACE
;
blockStatement
: localVariableDeclaration
| statement
;
localVariableDeclaration
: { !SemanticPredicates.isInvalidLocalVariableDeclaration(_input) }?
variableDeclaration[0]
;
classifiedModifiers[int t]
: { 0 == $t }? variableModifiers
| { 1 == $t }? modifiers
;
/**
* t 0: local variable declaration; 1: field declaration
*/
variableDeclaration[int t]
@leftfactor { classifiedModifiers }
: classifiedModifiers[$t]
( type? variableDeclarators
| typeNamePairs nls ASSIGN nls variableInitializer
)
|
classifiedModifiers[$t]?
type variableDeclarators
;
typeNamePairs
: LPAREN typeNamePair (COMMA typeNamePair)* rparen
;
typeNamePair
: type? variableDeclaratorId
;
variableNames
: LPAREN variableDeclaratorId (COMMA variableDeclaratorId)+ rparen
;
conditionalStatement
: ifElseStatement
| switchStatement
;
ifElseStatement
: IF expressionInPar nls tb=statement ((nls | sep) ELSE nls fb=statement)?
;
switchStatement
: SWITCH expressionInPar nls LBRACE nls switchBlockStatementGroup* nls RBRACE
;
loopStatement
: FOR LPAREN forControl rparen nls statement #forStmtAlt
| WHILE expressionInPar nls statement #whileStmtAlt
| DO nls statement nls WHILE expressionInPar #doWhileStmtAlt
;
continueStatement
: CONTINUE
identifier?
;
breakStatement
: BREAK
identifier?
;
tryCatchStatement
: TRY resources? nls block
(nls catchClause)*
(nls finallyBlock)?
;
assertStatement
: ASSERT ce=expression (nls (COLON | COMMA) nls me=expression)?
;
statement
: block #blockStmtAlt
| conditionalStatement #conditionalStmtAlt
| loopStatement #loopStmtAlt
| tryCatchStatement #tryCatchStmtAlt
| SYNCHRONIZED expressionInPar nls block #synchronizedStmtAlt
| RETURN expression? #returnStmtAlt
| THROW expression #throwStmtAlt
| breakStatement #breakStmtAlt
| continueStatement #continueStmtAlt
| identifier COLON nls statement #labeledStmtAlt
| assertStatement #assertStmtAlt
| localVariableDeclaration #localVariableDeclarationStmtAlt
// validate the method in the AstBuilder#visitMethodDeclaration, e.g. method without method body is not allowed
| { !SemanticPredicates.isInvalidMethodDeclaration(_input) }?
methodDeclaration[3, 9] #methodDeclarationStmtAlt
| statementExpression #expressionStmtAlt
| SEMI #emptyStmtAlt
;
catchClause
: CATCH LPAREN variableModifiersOpt catchType? identifier rparen nls block
;
catchType
: qualifiedClassName (BITOR qualifiedClassName)*
;
finallyBlock
: FINALLY nls block
;
resources
: LPAREN nls resourceList sep? rparen
;
resourceList
: resource (sep resource)*
;
resource
: localVariableDeclaration
| expression
;
/** Matches cases then statements, both of which are mandatory.
* To handle empty cases at the end, we add switchLabel* to statement.
*/
switchBlockStatementGroup
: (switchLabel nls)+ blockStatements
;
switchLabel
: CASE expression COLON
| DEFAULT COLON
;
forControl
: enhancedForControl
| classicalForControl
;
enhancedForControl
: variableModifiersOpt type? variableDeclaratorId (COLON | IN) expression
;
classicalForControl
: forInit? SEMI expression? SEMI forUpdate?
;
forInit
: localVariableDeclaration
| expressionList[false]
;
forUpdate
: expressionList[false]
;
// EXPRESSIONS
castParExpression
: LPAREN type rparen
;
parExpression
: expressionInPar
;
expressionInPar
: LPAREN enhancedStatementExpression rparen
;
expressionList[boolean canSpread]
: expressionListElement[$canSpread] (COMMA expressionListElement[$canSpread])*
;
expressionListElement[boolean canSpread]
: ( MUL { require($canSpread, "spread operator is not allowed here", -1); }
|
) expression
;
enhancedStatementExpression
: statementExpression
| standardLambdaExpression
;
statementExpression
: commandExpression #commandExprAlt
;
postfixExpression
: pathExpression op=(INC | DEC)?
;
expression
// qualified names, array expressions, method invocation, post inc/dec, type casting (level 1)
// The cast expression must be put before pathExpression to resovle the ambiguities between type casting and call on parentheses expression, e.g. (int)(1 / 2)
: castParExpression castOperandExpression #castExprAlt
| postfixExpression #postfixExprAlt
// ~(BNOT)/!(LNOT) (level 1)
| (BITNOT | NOT) nls expression #unaryNotExprAlt
// math power operator (**) (level 2)
| left=expression op=POWER nls right=expression #powerExprAlt
// ++(prefix)/--(prefix)/+(unary)/-(unary) (level 3)
| op=(INC | DEC | ADD | SUB) expression #unaryAddExprAlt
// multiplication/division/modulo (level 4)
| left=expression nls op=(MUL | DIV | MOD) nls right=expression #multiplicativeExprAlt
// binary addition/subtraction (level 5)
| left=expression op=(ADD | SUB) nls right=expression #additiveExprAlt
// bit shift expressions (level 6)
| left=expression nls
( ( dlOp=LT LT
| tgOp=GT GT GT
| dgOp=GT GT
)
| rangeOp=( RANGE_INCLUSIVE
| RANGE_EXCLUSIVE
)
) nls
right=expression #shiftExprAlt
// boolean relational expressions (level 7)
| left=expression nls op=(AS | INSTANCEOF | NOT_INSTANCEOF) nls type #relationalExprAlt
| left=expression nls op=(LE | GE | GT | LT | IN | NOT_IN) nls right=expression #relationalExprAlt
// equality/inequality (==/!=) (level 8)
| left=expression nls
op=( IDENTICAL
| NOT_IDENTICAL
| EQUAL
| NOTEQUAL
| SPACESHIP
) nls
right=expression #equalityExprAlt
// regex find and match (=~ and ==~) (level 8.5)
// jez: moved =~ closer to precedence of == etc, as...
// 'if (foo =~ "a.c")' is very close in intent to 'if (foo == "abc")'
| left=expression nls op=(REGEX_FIND | REGEX_MATCH) nls right=expression #regexExprAlt
// bitwise or non-short-circuiting and (&) (level 9)
| left=expression nls op=BITAND nls right=expression #andExprAlt
// exclusive or (^) (level 10)
| left=expression nls op=XOR nls right=expression #exclusiveOrExprAlt
// bitwise or non-short-circuiting or (|) (level 11)
| left=expression nls op=BITOR nls right=expression #inclusiveOrExprAlt
// logical and (&&) (level 12)
| left=expression nls op=AND nls right=expression #logicalAndExprAlt
// logical or (||) (level 13)
| left=expression nls op=OR nls right=expression #logicalOrExprAlt
// conditional test (level 14)
| <assoc=right> con=expression nls
( QUESTION nls tb=expression nls COLON nls
| ELVIS nls
)
fb=expression #conditionalExprAlt
// assignment expression (level 15)
// "(a) = [1]" is a special case of multipleAssignmentExprAlt, it will be handle by assignmentExprAlt
| <assoc=right> left=variableNames nls op=ASSIGN nls right=statementExpression #multipleAssignmentExprAlt
| <assoc=right> left=expression nls
op=( ASSIGN
| ADD_ASSIGN
| SUB_ASSIGN
| MUL_ASSIGN
| DIV_ASSIGN
| AND_ASSIGN
| OR_ASSIGN
| XOR_ASSIGN
| RSHIFT_ASSIGN
| URSHIFT_ASSIGN
| LSHIFT_ASSIGN
| MOD_ASSIGN
| POWER_ASSIGN
| ELVIS_ASSIGN
) nls
enhancedStatementExpression #assignmentExprAlt
;
castOperandExpression
options { baseContext = expression; }
: castParExpression castOperandExpression #castExprAlt
| postfixExpression #postfixExprAlt
// ~(BNOT)/!(LNOT) (level 1)
| (BITNOT | NOT) nls castOperandExpression #unaryNotExprAlt
// ++(prefix)/--(prefix)/+(unary)/-(unary) (level 3)
| op=(INC | DEC | ADD | SUB) castOperandExpression #unaryAddExprAlt
;
/*
enhancedExpression
: expression
| standardLambdaExpression
;
*/
commandExpression
: expression
(
{ !SemanticPredicates.isFollowingArgumentsOrClosure($expression.ctx) }?
argumentList
|
/* if pathExpression is a method call, no need to have any more arguments */
)
commandArgument*
;
commandArgument
: primary
// what follows is either a normal argument, parens,
// an appended block, an index operation, or nothing
// parens (a b already processed):
// a b c() d e -> a(b).c().d(e)
// a b c()() d e -> a(b).c().call().d(e)
// index (a b already processed):
// a b c[x] d e -> a(b).c[x].d(e)
// a b c[x][y] d e -> a(b).c[x][y].d(e)
// block (a b already processed):
// a b c {x} d e -> a(b).c({x}).d(e)
//
// parens/block completes method call
// index makes method call to property get with index
//
( pathElement+
| argumentList
)?
;
/**
* A "path expression" is a name or other primary, possibly qualified by various
* forms of dot, and/or followed by various kinds of brackets.
* It can be used for value or assigned to, or else further qualified, indexed, or called.
* It is called a "path" because it looks like a linear path through a data structure.
* Examples: x.y, x?.y, x*.y, x.@y; x[], x[y], x[y,z]; x(), x(y), x(y,z); x{s}; a.b[n].c(x).d{s}
* (Compare to a C lvalue, or LeftHandSide in the JLS section 15.26.)
* General expressions are built up from path expressions, using operators like '+' and '='.
*
* t 0: primary, 1: namePart, 2: arguments, 3: closureOrLambdaExpression, 4: indexPropertyArgs, 5: namedPropertyArgs,
* 6: non-static inner class creator
*/
pathExpression returns [int t]
: primary (pathElement { $t = $pathElement.t; })*
;
pathElement returns [int t]
: nls
// AT: foo.@bar selects the field (or attribute), not property
(
( DOT // The all-powerful dot.
| SPREAD_DOT // Spread operator: x*.y === x?.collect{it.y}
| SAFE_DOT // Optional-null operator: x?.y === (x==null)?null:x.y
| SAFE_CHAIN_DOT // Optional-null chain operator: x??.y.z === x?.y?.z
) nls (AT | nonWildcardTypeArguments)?
|
METHOD_POINTER nls // Method pointer operator: foo.&y == foo.metaClass.getMethodPointer(foo, "y")
|
METHOD_REFERENCE nls // Method reference: System.out::println
)
namePart
{ $t = 1; }
|
nls DOT nls NEW creator[1]
{ $t = 6; }
| arguments
{ $t = 2; }
// Can always append a block, as foo{bar}
| nls closureOrLambdaExpression
{ $t = 3; }
// Element selection is always an option, too.
// In Groovy, the stuff between brackets is a general argument list,
// since the bracket operator is transformed into a method call.
| indexPropertyArgs
{ $t = 4; }
| namedPropertyArgs
{ $t = 5; }
;
/**
* This is the grammar for what can follow a dot: x.a, x.@a, x.&a, x.'a', etc.
*/
namePart
:
( identifier
// foo.'bar' is in all ways same as foo.bar, except that bar can have an arbitrary spelling
| stringLiteral
| dynamicMemberName
/* just a PROPOSAL, which has not been implemented yet!
// PROPOSAL, DECIDE: Is this inline form of the 'with' statement useful?
// Definition: a.{foo} === {with(a) {foo}}
// May cover some path expression use-cases previously handled by dynamic scoping (closure delegates).
| block
*/
// let's allow common keywords as property names
| keywords
)
;
/**
* If a dot is followed by a parenthesized or quoted expression, the member is computed dynamically,
* and the member selection is done only at runtime. This forces a statically unchecked member access.
*/
dynamicMemberName
: parExpression
| gstring
;
/** An expression may be followed by [...].
* Unlike Java, these brackets may contain a general argument list,
* which is passed to the array element operator, which can make of it what it wants.
* The brackets may also be empty, as in T[]. This is how Groovy names array types.
*/
indexPropertyArgs
: QUESTION? LBRACK expressionList[true]? RBRACK
;
namedPropertyArgs
: QUESTION? LBRACK (mapEntryList | COLON) RBRACK
;
primary
:
// Append `typeArguments?` to `identifier` to support constructor reference with generics, e.g. HashMap<String, Integer>::new
// Though this is not a graceful solution, it is much faster than replacing `builtInType` with `type`
identifier typeArguments? #identifierPrmrAlt
| literal #literalPrmrAlt
| gstring #gstringPrmrAlt
| NEW nls creator[0] #newPrmrAlt
| THIS #thisPrmrAlt
| SUPER #superPrmrAlt
| parExpression #parenPrmrAlt
| closureOrLambdaExpression #closureOrLambdaExpressionPrmrAlt
| list #listPrmrAlt
| map #mapPrmrAlt
| builtInType #builtInTypePrmrAlt
;
list
: LBRACK expressionList[true]? COMMA? RBRACK
;
map
: LBRACK
( mapEntryList COMMA?
| COLON
)
RBRACK
;
mapEntryList
: mapEntry (COMMA mapEntry)*
;
mapEntry
: mapEntryLabel COLON nls expression
| MUL COLON nls expression
;
mapEntryLabel
: keywords
| primary
;
/**
* t 0: general creation; 1: non-static inner class creation
*/
creator[int t]
: createdName
( {0 == $t || 1 == $t}? nls arguments anonymousInnerClassDeclaration[0]?
| {0 == $t}? (annotationsOpt LBRACK expression RBRACK)+ dimsOpt
| {0 == $t}? dims nls arrayInitializer
)
;
arrayInitializer
: LBRACE nls variableInitializers? nls RBRACE
;
/**
* t 0: anonymous inner class; 1: anonymous enum
*/
anonymousInnerClassDeclaration[int t]
: classBody[0]
;
createdName
: annotationsOpt
( primitiveType
| qualifiedClassName typeArgumentsOrDiamond?
)
;
nonWildcardTypeArguments
: LT nls typeList nls GT
;
typeArgumentsOrDiamond
: LT GT
| typeArguments
;
arguments
: LPAREN enhancedArgumentList? COMMA? rparen
;
argumentList
options { baseContext = enhancedArgumentList; }
: argumentListElement
( COMMA nls
argumentListElement
)*
;
enhancedArgumentList
: enhancedArgumentListElement
( COMMA nls
enhancedArgumentListElement
)*
;
argumentListElement
options { baseContext = enhancedArgumentListElement; }
: expressionListElement[true]
| mapEntry
;
enhancedArgumentListElement
: expressionListElement[true]
| standardLambdaExpression
| mapEntry
;
stringLiteral
: StringLiteral
;
className
: CapitalizedIdentifier
;
identifier
: Identifier
| CapitalizedIdentifier
| VAR
|
// if 'static' followed by DOT, we can treat them as identifiers, e.g. static.unused = { -> }
{ DOT == _input.LT(2).getType() }?
STATIC
| IN
// | DEF
| TRAIT
| AS
;
builtInType
: BuiltInPrimitiveType
| VOID
;
keywords
: ABSTRACT
| AS
| ASSERT
| BREAK
| CASE
| CATCH
| CLASS
| CONST
| CONTINUE
| DEF
| DEFAULT
| DO
| ELSE
| ENUM
| EXTENDS
| FINAL
| FINALLY
| FOR
| GOTO
| IF
| IMPLEMENTS
| IMPORT
| IN
| INSTANCEOF
| INTERFACE
| NATIVE
| NEW
| PACKAGE
| RETURN
| STATIC
| STRICTFP
| SUPER
| SWITCH
| SYNCHRONIZED
| THIS
| THROW
| THROWS
| TRANSIENT
| TRAIT
| THREADSAFE
| TRY
| VAR
| VOLATILE
| WHILE
| NullLiteral
| BooleanLiteral
| BuiltInPrimitiveType
| VOID
| PUBLIC
| PROTECTED
| PRIVATE
;
rparen
: RPAREN
|
// !!!Error Alternative, impact the performance of parsing
{ require(false, "Missing ')'"); }
;
nls
: NL*
;
sep : (NL | SEMI)+
;