📄 groovy.g
字号:
)?
RCURLY!
;
*OBS*/
/*OBS* // Use [...] for initializing all sorts of sequences, including arrays.
// The two "things" that can initialize an array element are an expression
// and another (nested) array initializer.
initializer
: expression
| arrayInitializer
;
*OBS*/
/*OBS???
// This is the header of a method. It includes the name and parameters
// for the method.
// This also watches for a list of exception classes in a "throws" clause.
ctorHead
: IDENT // the name of the method
// parse the formal parameter declarations.
LPAREN! parameterDeclarationList RPAREN!
// get the list of exceptions that this method is declared to throw
(throwsClause)?
;
*OBS*/
// This is a list of exception classes that the method is declared to throw
throwsClause
: nls! "throws"^ nls! identifier ( COMMA! nls! identifier )*
;
/** A list of zero or more formal parameters.
* If a parameter is variable length (e.g. String... myArg) it should be
* to the right of any other parameters of the same kind.
* General form: (req, ..., opt, ..., [rest], key, ..., [restKeys], [block]
* This must be sorted out after parsing, since the various declaration forms
* are impossible to tell apart without backtracking.
*/
parameterDeclarationList {Token first = LT(1);}
:
(
parameterDeclaration
( COMMA! nls!
parameterDeclaration
)*
)?
{#parameterDeclarationList = #(create(PARAMETERS,"PARAMETERS",first,LT(1)),
#parameterDeclarationList);}
;
/** A formal parameter for a method or closure. */
parameterDeclaration!
{ Token first = LT(1);boolean spreadParam = false; }
:
pm:parameterModifiersOpt
( options {greedy=true;} :
t:typeSpec[false]
)?
// TODO: What do formal parameters for keyword arguments look like?
// TODO: Should this be SPREAD_ARG instead?
( TRIPLE_DOT! { spreadParam = true; } )?
id:IDENT
// allow an optional default value expression
(exp:varInitializer)?
/*OBS*pd:declaratorBrackets[#t]*/
{
if (spreadParam) {
#parameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)),
pm, #(create(TYPE,"TYPE",first,LT(1)),t), id, exp);
} else {
#parameterDeclaration = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)),
pm, #(create(TYPE,"TYPE",first,LT(1)),t), id, exp);
}
}
;
/*OBS*
variableLengthParameterDeclaration! {Token first = LT(1);}
: pm:parameterModifier t:typeSpec[false] TRIPLE_DOT! id:IDENT
/*OBS* pd:declaratorBrackets[#t]* /
{#variableLengthParameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)),
pm, #(create(TYPE,"TYPE",first,LT(1)),t), id);}
;
*OBS*/
parameterModifiersOpt
{ Token first = LT(1);int seenDef = 0; }
//final and/or def can appear amongst annotations in any order
: ( {seenDef++ == 0}? // do not allow multiple "def" tokens
"def"! nls! // redundant, but allowed for symmetry
| "final" nls!
| annotation nls!
)*
{#parameterModifiersOpt = #(create(MODIFIERS,"MODIFIERS",first,LT(1)), #parameterModifiersOpt);}
;
/** Closure parameters are exactly like method parameters,
* except that they are not enclosed in parentheses, but rather
* are prepended to the front of a block, just after the brace.
* They are separated from the closure body by a CLOSABLE_BLOCK_OP token '->'.
*/
// With '|' there would be restrictions on bitwise-or expressions.
closableBlockParamsOpt[boolean addImplicit]
: (parameterDeclarationList nls CLOSABLE_BLOCK_OP)=>
parameterDeclarationList nls! CLOSABLE_BLOCK_OP! nls!
| {addImplicit}?
implicitParameters
|
/* else do not parse any parameters at all */
;
/** Lookahead to check whether a block begins with explicit closure arguments. */
closableBlockParamsStart!
:
parameterDeclarationList nls CLOSABLE_BLOCK_OP
;
/** Simple names, as in {x|...}, are completely equivalent to {(def x)|...}. Build the right AST. */
closableBlockParam! {Token first = LT(1);}
: id:IDENT!
{#closableBlockParam = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)),
#(create(MODIFIERS,"MODIFIERS",first,LT(1))), #(create(TYPE,"TYPE",first,LT(1))),
id);}
;
// Compound statement. This is used in many contexts:
// Inside a class definition prefixed with "static":
// it is a class initializer
// Inside a class definition without "static":
// it is an instance initializer
// As the body of a method
// As a completely indepdent braced block of code inside a method
// it starts a new scope for variable definitions
// In Groovy, this is called an "open block". It cannot have closure arguments.
compoundStatement
: openBlock
;
/** An open block is not allowed to have closure arguments. */
openBlock
: lc:LCURLY^ nls! {#lc.setType(SLIST);}
// AST type of SLIST means "never gonna be a closure"
blockBody[EOF]
RCURLY!
;
/** A block body is a parade of zero or more statements or expressions. */
blockBody[int prevToken]
:
(statement[prevToken])? (sep! (statement[sepToken])?)*
;
/** A block which is known to be a closure, even if it has no apparent arguments.
* A block inside an expression or after a method call is always assumed to be a closure.
* Only labeled, unparameterized blocks which occur directly as substatements are kept open.
*/
closableBlock
: lc:LCURLY^ nls! {#lc.setType(CLOSABLE_BLOCK);}
closableBlockParamsOpt[true]
blockBody[EOF]
RCURLY!
;
/** A block known to be a closure, but which omits its arguments, is given this placeholder.
* A subsequent pass is responsible for deciding if there is an implicit 'it' parameter,
* or if the parameter list should be empty.
*/
implicitParameters {Token first = LT(1);}
: { #implicitParameters = #(create(IMPLICIT_PARAMETERS,"IMPLICIT_PARAMETERS",first,LT(1))); }
;
/** A sub-block of a block can be either open or closable.
* It is closable if and only if there are explicit closure arguments.
* Compare this to a block which is appended to a method call,
* which is given closure arguments, even if they are not explicit in the code.
*/
openOrClosableBlock
: lc:LCURLY^ nls!
cp:closableBlockParamsOpt[false]
{ if (#cp == null) #lc.setType(SLIST);
else #lc.setType(CLOSABLE_BLOCK);
}
blockBody[EOF]
RCURLY!
;
/** A statement is an element of a block.
* Typical statements are declarations (which are scoped to the block)
* and expressions.
*/
statement[int prevToken]
// prevToken is NLS if previous statement is separated only by a newline
// declarations are ambiguous with "ID DOT" relative to expression
// statements. Must backtrack to be sure. Could use a semantic
// predicate to test symbol table to see what the type was coming
// up, but that's pretty hard without a symbol table ;)
: (declarationStart)=>
declaration
// Attach a label to the front of a statement
// This block is executed for effect, unless it has an explicit closure argument.
|
(IDENT COLON)=>
pfx:statementLabelPrefix!
{#statement = #pfx;} // nest it all under the label prefix
( (LCURLY) => openOrClosableBlock
| statement[COLON]
)
// An expression statement. This could be a method call,
// assignment statement, or any other expression evaluated for
// side-effects.
// The prevToken is used to check for dumb expressions like +1.
| expressionStatement[prevToken]
// class definition
| m:modifiersOpt! typeDefinitionInternal[#m]
// If-else statement
| "if"^ LPAREN! assignmentLessExpression RPAREN! nlsWarn! compatibleBodyStatement
(
// CONFLICT: the old "dangling-else" problem...
// ANTLR generates proper code matching
// as soon as possible. Hush warning.
options {
warnWhenFollowAmbig = false;
}
: // lookahead to check if we're entering an 'else' clause
( (sep!)? "else"! )=>
(sep!)? // allow SEMI here for compatibility with Java
"else"! nlsWarn! compatibleBodyStatement
)?
// For statement
| forStatement
// While statement
| "while"^ LPAREN! strictContextExpression RPAREN! nlsWarn! compatibleBodyStatement
/*OBS* no do-while statement in Groovy (too ambiguous)
// do-while statement
| "do"^ statement "while"! LPAREN! strictContextExpression RPAREN! SEMI!
*OBS*/
// With statement
// (This is the Groovy scope-shift mechanism, used for builders.)
| "with"^ LPAREN! strictContextExpression RPAREN! nlsWarn! compoundStatement
// Splice statement, meaningful only inside a "with" expression.
// PROPOSED, DECIDE. Prevents the namespace pollution of a "text" method or some such.
| sp:STAR^ nls! {#sp.setType(SPREAD_ARG);}
expressionStatement[EOF]
// Example: with(htmlbuilder) { head{} body{ *"some text" } }
// Equivalent to: { htmlbuilder.head{} htmlbuilder.body{ (htmlbuilder as Collection).add("some text") } }
// Import statement. Can be used in any scope. Has "import x as y" also.
| importStatement
// switch/case statement
| "switch"^ LPAREN! strictContextExpression RPAREN! nlsWarn! LCURLY! nls!
( casesGroup )*
RCURLY!
// exception try-catch block
| tryBlock
// synchronize a statement
| "synchronized"^ LPAREN! strictContextExpression RPAREN! nlsWarn! compoundStatement
/*OBS*
// empty statement
| s:SEMI {#s.setType(EMPTY_STAT);}
*OBS*/
| branchStatement
;
forStatement
: f:"for"^
LPAREN!
( (forInit SEMI)=>traditionalForClause
// *OBS*
// There's no need at all for squeezing in the new Java 5 "for"
// syntax, since Groovy's is a suitable alternative.
// | (parameterDeclaration COLON)=> forEachClause
// *OBS*
| // the coast is clear; it's a modern Groovy for statement
forInClause
)
RPAREN! nlsWarn!
compatibleBodyStatement // statement to loop over
;
traditionalForClause
:
forInit SEMI! // initializer
forCond SEMI! // condition test
forIter // updater
;
/*OBS*
forEachClause {Token first = LT(1);}
:
p:parameterDeclaration COLON! expression
{#forEachClause = #(create(FOR_EACH_CLAUSE,"FOR_EACH_CLAUSE",first,LT(1)), #forEachClause);}
;
*OBS*/
forInClause
: ( (declarationStart)=>
decl:singleDeclarationNoInit
| IDENT
)
(
i:"in"^ {#i.setType(FOR_IN_ITERABLE);}
shiftExpression[0]
|
{ addWa
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -