blob: 3da2dab3dbeb8030f06af7fc44a0e6fc2ee0b15c [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? statements? EOF
;
statements
: statement (sep statement)* sep?
;
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
)
;
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
// 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? nls
|
)
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 { require(false, "void is not allowed here", -4); }
)
|
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
;
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
locals[ String footprint = "" ]
: LBRACE nls (formalParameterList? nls ARROW nls)? blockStatementsOpt RBRACE
;
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
locals[ String footprint = "" ]
: SWITCH expressionInPar nls LBRACE nls switchBlockStatementGroup* nls RBRACE
;
loopStatement
locals[ String footprint = "" ]
: FOR LPAREN forControl rparen nls statement #forStmtAlt
| WHILE expressionInPar nls statement #whileStmtAlt
| DO nls statement nls WHILE expressionInPar #doWhileStmtAlt
;
continueStatement
locals[ boolean isInsideLoop ]
@init {
try {
$isInsideLoop = null != $loopStatement::footprint;
} catch(NullPointerException e) {
$isInsideLoop = false;
}
}
: CONTINUE
{ require($isInsideLoop, "the continue statement is only allowed inside loops", -8); }
identifier?
;
breakStatement
locals[ boolean isInsideLoop, boolean isInsideSwitch ]
@init {
try {
$isInsideLoop = null != $loopStatement::footprint;
} catch(NullPointerException e) {
$isInsideLoop = false;
}
try {
$isInsideSwitch = null != $switchStatement::footprint;
} catch(NullPointerException e) {
$isInsideSwitch = false;
}
}
: BREAK
{ require($isInsideLoop || $isInsideSwitch, "the break statement is only allowed inside loops or switches", -5); }
identifier?
;
tryCatchStatement
: TRY resources? nls block
(nls catchClause)*
(nls finallyBlock)?
;
assertStatement
locals[ String footprint = "" ]
: 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
// Import statement. Can be used in any scope. Has "import x as y" also.
| importDeclaration #importStmtAlt
| assertStatement #assertStmtAlt
| typeDeclaration #typeDeclarationStmtAlt
| 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 enhancedExpression 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
;
/**
* In order to resolve the syntactic ambiguities, e.g. (String)'abc' can be parsed as a cast expression or a parentheses-less method call(method name: (String), arguments: 'abc')
* try to match expression first.
* If it is not a normal expression, then try to match the command expression
*/
statementExpression
: expression #normalExprAlt
| commandExpression #commandExprAlt
;
postfixExpression
locals[ boolean isInsideAssert ]
@init {
try {
$isInsideAssert = null != $assertStatement::footprint;
} catch(NullPointerException e) {
$isInsideAssert = false;
}
}
: 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 expression #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
;
enhancedExpression
: expression
| standardLambdaExpression
;
commandExpression
: pathExpression
(
{ SemanticPredicates.isFollowingMethodName($pathExpression.t) }?
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: closure, 4: indexPropertyArgs, 5: namedPropertyArgs
*/
pathExpression returns [int t]
: primary (pathElement { $t = $pathElement.t; })*
;
pathElement returns [int t]
locals[ boolean isInsideClosure ]
@init {
try {
$isInsideClosure = null != $closure::footprint;
} catch(NullPointerException e) {
$isInsideClosure = false;
}
}
: 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; }
| arguments
{ $t = 2; }
// Can always append a block, as foo{bar}
| nls closure
{ $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
: LBRACK mapEntryList RBRACK
;
primary
: identifier #identifierPrmrAlt
| literal #literalPrmrAlt
| gstring #gstringPrmrAlt
| NEW nls creator #newPrmrAlt
| THIS #thisPrmrAlt
| SUPER #superPrmrAlt
| parExpression #parenPrmrAlt
| closure #closurePrmrAlt
| lambdaExpression #lambdaPrmrAlt
| list #listPrmrAlt
| map #mapPrmrAlt
| builtInType #typePrmrAlt
;
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
;
creator
: createdName
( nls arguments anonymousInnerClassDeclaration[0]?
| (annotationsOpt LBRACK expression RBRACK)+ dimsOpt
| 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
|
// if 'static' followed by DOT, we can treat them as identifiers, e.g. static.unused = { -> }
{ DOT == _input.LT(2).getType() }?
STATIC
;
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
| VOLATILE
| WHILE
| NullLiteral
| BooleanLiteral
| BuiltInPrimitiveType
| VOID
| PUBLIC
| PROTECTED
| PRIVATE
;
rparen
: RPAREN
|
// !!!Error Alternative, impact the performance of parsing
{ require(false, "Missing ')'"); }
;
nls
: NL*
;
sep : SEMI NL*
| NL+ (SEMI NL*)*
;