packages feed

rtk-0.11: test-grammars/java.pg

grammar 'Java';

# ============================================================================
# LALR conflict inventory (happy: 17 shift/reduce, 0 reduce/reduce)
# ============================================================================
# Every remaining conflict is a shift/reduce whose default resolution (shift)
# is the correct Java reading. They fall into seven families; the items
# identify the automaton state (happy -i state numbers move with every
# grammar change, the items do not):
#
# 1. Dangling else - 1 state, 2 conflicts: 'else' and its splice token
#    qq_OptElsePart, against reduce OptElsePart -> empty. Item:
#        IfStatement -> 'if' '(' Expression ')' Statement . OptElsePart
#    Shift binds the else part to the NEAREST 'if'; the reduce would close
#    this if and hand the 'else' to an enclosing one. Shifting is the JLS
#    14.5 rule. See IfStatement.
#
# 2. catch/finally attach to the nearest try - 1 state, 4 conflicts:
#    'catch', 'finally' and their splice tokens qq_CatchList, qq_OptFinally,
#    against reduce OptFinally -> empty. Items:
#        TryStatement -> 'try' Statement CatchList . OptFinally
#        CatchList -> CatchList . ('catch' '(' Parameter ')' Statement)
#    In 'try try { } catch (E e) { } finally { }' each clause could extend
#    the inner try (shift) or close it and attach to the outer one (reduce).
#    Java attaches both to the nearest try.
#
# 3. Member id vs empty TypeParameters - 2 states (a type body after
#    ModifierList, and the MemberDeclaration QQ entry), 1 conflict each on
#    id, against reduce TypeParameters -> empty. Items:
#        MemberDeclaration -> . id MemberAfterFirstId
#        TypeParameters -> .
#    In 'Foo bar(...)' the id either starts the non-generic alternative
#    (shift) or follows an empty TypeParameters inside the generic one
#    (reduce). No input is lost: id MemberAfterFirstId derives everything
#    the empty-TypeParameters generic alternative would.
#
# 4. Greedy CompoundName '.' - 1 state, 1 conflict on '.', against reduce
#    CompoundName -> id CompoundNameTail . Item:
#        CompoundNameTail -> CompoundNameTail . ('.' id)
#    'a.b.c' extends the unreduced name instead of reducing 'a' early and
#    parsing '.' as a field access; PostfixExpression '.' id still handles
#    non-name bases ('f().x', 'this.x'), so no input is lost. The state
#    also holds the class-literal/qualified-this/generic-invocation items
#    anchored on the same 'id CompoundNameTail' prefix; they ride the SAME
#    '.' shift, and the token after the '.' picks the branch (id extends
#    the name, 'class'/'this'/'<' commit to one of them) - see the NOTE at
#    PrimaryNoPostfix. Only expression positions carry those items: pure
#    type positions (package, throws, extends, after 'new', ...) parse the
#    name in a twin state without them and without the conflict.
#
# 5. Bracket-list shifts on '[' - 5 states, 1 conflict each:
#      - after CompoundName in an expression, at statement start, and in a
#        cast head (reduce: the call-parens option ('(' Arglist ')')? ->
#        empty, i.e. commit to the bare name as a complete primary). Items:
#            PrimaryNoPostfix -> CompoundName . '[' Expression ']'
#            Type             -> CompoundName . NonEmptyDims      (and the
#            CastExpression heads in the cast state)
#      - after DimExprs and after NonEmptyDims in array creation (reduce:
#        stop the dims list). Items:
#            DimExprs     -> DimExprs . ('[' Expression ']')
#            NonEmptyDims -> NonEmptyDims . ('[' ']')
#    Shifting '[' defers the type-vs-access decision by exactly one token
#    (']' means a type's empty pair, an expression start means array
#    access/sizing) or greedily extends the dims list; no input is lost.
#    See the NOTEs at Dims and PrimaryNoPostfix.
#
# 6. '<' commits to type arguments - 2 states, 1 conflict each on '<':
#    after CompoundName at statement start and in a cast head (reduce: bare-
#    name primary, as in family 5). Standard for LALR Java parsers; see the
#    NOTE at TypeArguments. Only the cast-head state loses real input
#    ('(a < b)' as a parenthesized primary, see KNOWN LIMITATION at
#    CastExpression); the other rejects only semantically invalid Java
#    ('a < b;' as a bare statement-expression). The family's third state -
#    pure type position, reduce Type -> CompoundName, whose lookahead held
#    '<' only through 'x instanceof T < y' - vanished when
#    RelationalExpression became non-associative: '<' no longer follows a
#    complete relational operand. The same restructure lets ShiftOp be
#    composed from single '>' tokens with NO conflict at all: RelationalOp
#    '>' and ShiftOp '>' '>' share the shift of the first '>', and the next
#    token splits them deterministically (another '>' extends the shift
#    operator, an operand start reduces RelationalOp). See the NOTEs at
#    RelationalExpression and ShiftOp.
#
# 7. QQ bootstrap dummy bracket - 1 state, 1 conflict. The quasi-quoter
#    wraps a quote body in a per-rule dummy-token pair, and the whole-file
#    entry 'Java -> DUMMY . Java DUMMY' lets a second opening DUMMY (shift)
#    compete with deriving an empty CompilationUnit ((Package)? -> empty)
#    right before the closing DUMMY (reduce). Only a completely empty
#    [java| |] quote is affected (it fails to parse); any non-empty quote
#    has a real token after the opening DUMMY.
#
# The cast-vs-paren decision one token AFTER ')' is conflict-free; see
# CastExpression.
# ============================================================================

Java = CompilationUnit ;

OptDocComment = (DocComment)? ;

TypeDeclaration =
 OptDocComment ModifierList TypeDeclRest ;

ImportList = (ImportStatement)*;
CompilationUnit  =
 (Package)?
 ImportList
 (TypeDeclaration)?  ;

Package  =
 'package' CompoundName  ';'  ;

ImportStatement  =
 'import'  ('static')?  ImportName  ';'  ;

# Import names have their own LEFT-recursive spelling instead of reusing
# CompoundName: the on-demand form would need CompoundName reduced on
# lookahead '.' (to then shift '.' '*'), but that reduce loses to the greedy
# name-extension shift (family 4), which made the star branch unreachable
# ('import a.b.*;' never parsed). Left recursion keeps both '.'
# continuations in one item set, so the token AFTER each '.' (id vs '*')
# picks the branch - no early commitment, no conflict.
ImportName =
 ImportHead
 | ImportHead '.' '*' ;

ImportHead =
 id
 | ImportHead '.' id ;

DocComment = doccomment;

# Annotations support
Annotation = '@' CompoundName ('(' AnnotationArguments? ')')? ;

AnnotationArguments = AnnotationElement (',' AnnotationElement)* ;

# Explicit alternatives (not (id '=')? Expression) so LALR keeps both items
# alive after shifting id; otherwise values starting with an identifier, e.g.
# @Retention(RetentionPolicy.RUNTIME), fail. ConditionalExpression is the JLS
# ElementValue level; full Expression would make @Foo(a = b) ambiguous.
AnnotationElement = id '=' ConditionalExpression | ConditionalExpression ;

AnnotationList = Annotation* ;

# Exactly ONE ModifierList is parsed per declaration, owned by the enclosing
# declaration rule (TypeDeclaration at the top level, FieldDeclaration inside
# a type body). The class/interface/enum/@interface rules must NOT start with
# their own nullable ModifierList: after the enclosing list, every token that
# can extend a ModifierList (the 10 modifier keywords, '@', and the splice
# tokens qq_Modifier/qq_Annotation/qq_ModifierList) used to conflict between
# extending the outer list and epsilon-starting the inner one - 14
# shift/reduce conflicts whose shift put all modifiers on the outer list and
# left the inner one always empty.
ModifierList = (Modifier | Annotation)* ;

# A supertype reference (JLS ClassOrInterfaceType): a possibly qualified
# name with optional type arguments - Comparable<A>, java.util.List<String>.
# The nullable TypeArguments directly after CompoundName is safe here: in
# extends/implements position no expression interpretation competes, so the
# epsilon-reduce of the empty TypeArguments (on ',', '{' or 'implements') is
# the state's only action on those tokens and '<' is a plain shift into
# NonEmptyTypeArguments. (Contrast with Type, which must spell out its
# NonEmptyTypeArguments alternative; see the NOTE there.)
ClassOrInterfaceType = CompoundName TypeArguments ;

ExtendsList = 'extends' ClassOrInterfaceType (',' ClassOrInterfaceType)* ;
ImplementsList = 'implements' ClassOrInterfaceType + ~',';
FieldDeclarationList = FieldDeclaration *;

ClassDeclaration  =
 'class' id TypeParameters
 ExtendsList?
 ImplementsList?
 '{'  FieldDeclarationList  '}'  ;

InterfaceDeclaration  =
 'interface' id TypeParameters
 ExtendsList?
 '{'  FieldDeclarationList  '}'  ;

# Annotation declaration (@interface). The leading '@' shifts from the same
# state where '@' could extend the enclosing ModifierList with an Annotation;
# the NEXT token disambiguates ('interface' here vs id for an annotation), so
# the two uses of '@' never conflict.
AnnotationDeclaration =
 '@' 'interface' id
 '{' AnnotationTypeElementList '}' ;

AnnotationTypeElementList = AnnotationTypeElement* ;

# Annotation-type elements reuse FieldDeclaration; the default clause is
# handled by MemberRest (a dedicated alternative here would race its nullable
# ModifierList against FieldDeclaration's nullable OptDocComment → r/r).
AnnotationTypeElement = FieldDeclaration ;

# Enum declaration
EnumConstant = AnnotationList id ('(' Arglist ')')? ('{' FieldDeclarationList '}')? ;

EnumConstantList = EnumConstant (',' EnumConstant)* (',')? ;

EnumDeclaration =
 'enum' id
 ImplementsList?
 '{' EnumConstantList (';' FieldDeclarationList)? '}' ;

# NOTE: MemberDeclaration unifies MethodDeclaration, VariableDeclaration, and ConstructorDeclaration
# to eliminate ambiguity. All three can start with an identifier:
# - Constructor: id '(' ...
# - Method: Type id '(' ...  (where Type can be CompoundName, which is also id)
# - Field: Type id ';' or '=' ...
# By structuring alternatives carefully, we can distinguish them.
#
# NOTE: DocComment comes BEFORE ModifierList to match standard Java style:
#   /** doc */ public int field;
# NOT: public /** doc */ int field;
# TypeDeclRest is everything of a type declaration AFTER its modifiers: the
# class/interface/enum/@interface keyword onwards. The ModifierList lives on
# the enclosing rule (TypeDeclaration at the top level, FieldDeclaration for
# nested types), see the NOTE at ModifierList.
TypeDeclRest =
 ClassDeclaration
 | InterfaceDeclaration
 | EnumDeclaration
 | AnnotationDeclaration ;

FieldDeclaration  =
 OptDocComment ModifierList (MemberDeclaration | TypeDeclRest | StaticInitializer)
 |  ';'  ;

# Array brackets (JLS-style split): types and declarators may only contain
# EMPTY bracket pairs; sizing expressions are only allowed in array creation
# (see CreationExpression). Keeping expressions out of type brackets is what
# lets one token after 'name [' decide between a type (']') and an array
# access (expression start), so 'a[0] = 1;' and 'Foo[] x;' both parse.
#
# NOTE: RTK expands X* into a LEFT-recursive list with an empty base case, so
# a nullable Dims placed directly after CompoundName would demand an
# epsilon-reduce on '[' - exactly the early commitment we must avoid. Where
# the brackets compete with an expression interpretation (Type, casts) we
# therefore use NonEmptyDims, whose first '[' is a plain shift.
Dims = ('[' ']')* ;
NonEmptyDims = ('[' ']')+ ;

# MemberDeclaration unifies constructors, methods, and fields
# Strategy: Disambiguate early by checking what comes first
# - Primitive type keyword → definitely method/field
# - TypeParameters → definitely method (generic)
# - id → could be constructor or reference type method/field
MemberDeclaration =
 PrimitiveTypeKeyword Dims id MemberRest                       # Primitive type method/field
 | TypeParameters id MoreTypeSpecifier id MemberRest           # Generic method with any type
 | id MemberAfterFirstId ;                                     # Constructor or reference type

# Primitive type keywords (from TypeSpecifier)
PrimitiveTypeKeyword =
 'boolean' | 'byte' | 'char' | 'short' | 'int' | 'float' | 'long' | 'double' | 'void' ;

# After first id (without TypeParameters or primitive type), branch on next token:
MemberAfterFirstId =
 '(' ParameterList? ')' ThrowsClause? StatementBlock            # Constructor: id '(' ...
 | MoreTypeSpecifier id MemberRest ;                            # Reference type: id ... id MemberRest

# MoreTypeSpecifier handles the rest of a type after the first id
# Could be: empty (simple type), '.' id (qualified), '<' ... '>' (generics), '[' ']' (array)
MoreTypeSpecifier =
 '.' id MoreTypeSpecifier                                       # Qualified name
 | TypeArguments Dims ;                                          # Generics + array brackets

# Throws clause for methods and constructors (JLS 8.4.6): 'throws' is a fresh
# keyword in follow position, so the optional clause never conflicts with the
# tokens that end a method header ('{', ';', 'default'). Exception types are
# CompoundNames: a generic class may not subclass Throwable, so type
# arguments cannot occur here.
ThrowsClause = 'throws' CompoundName (',' CompoundName)* ;

# MemberRest branches on what follows "Type id":
# - '(' indicates a method declaration
# - '[' or '=' or ',' or ';' indicates a variable declaration
# The optional 'default' clause before ';' is for annotation-type elements
# (int value() default 5;). Accepting it on ordinary methods too is
# parser-level tolerance; rejecting it there is a semantic check (javac's job).
MemberRest =
 '(' ParameterList? ')' Dims ThrowsClause? ( StatementBlock | ('default' Expression)? ';' )
 | Dims OptVariableInitializer MoreVariableDeclarators ';' ;

MoreVariableDeclarators = (',' VariableDeclarator)* ;

# Legacy: MethodDeclaration is now unified with VariableDeclaration in MemberDeclaration above
# MethodDeclaration  =
#  TypeParameters Type id
#  '('  ParameterList  ')'  SquareBracketsList
#  ( StatementBlock  |  ';'  )  ;

# Legacy: ConstructorDeclaration is now unified in MemberDeclaration above
# ConstructorDeclaration  =
#  id  '('  ParameterList  ')'
# StatementBlock  ;

StatementBlock  =  '{'  StatementList  '}'  ;

VariableDeclaratorList = VariableDeclarator (',' VariableDeclarator)* ;

# VariableDeclaration is used for:
# 1. Local variable declarations inside method bodies (via Statement)
# 2. For loops (via ForStatement)
# Note: Field-level variable declarations use MemberDeclaration above
#
# The modified alternative carries a NON-EMPTY modifier list - deliberately
# not Parameter's nullable ParamModifierList: at statement start the parser
# already weighs "Type vs Expression" after an identifier, and a nullable
# prefix would force an epsilon-reduce decision BEFORE that identifier is
# shifted, sending 'Foo x;' down the expression path. 'final' and '@' cannot
# start an expression, so the modified alternative is keyword-anchored and
# the unmodified one keeps today's exact automaton.
VariableDeclaration  =
 Type VariableDeclaratorList
   ';'
 | LocalModifierList1 Type VariableDeclaratorList ';' ;

# Local variable modifiers (JLS 14.4 VariableModifier), at least one.
LocalModifierList1 = ('final' | Annotation)+ ;

OptVariableInitializer = ('=' VariableInitializer)? ;

VariableDeclarator  =
 id  Dims  OptVariableInitializer  ;

VariableInitializerList = (VariableInitializer (',' VariableInitializer)* (',')?)? ;

VariableInitializer  =
Expression
 |  '{'  VariableInitializerList  '}'  ;

StaticInitializer  =
 StatementBlock  ;

ParameterList  =
Parameter (',' Parameter)* ;

# Parameter modifiers (JLS 8.4.1 VariableModifier: 'final' and annotations).
# The NULLABLE prefix is safe here because parameter positions (after '(' or
# ',' in a parameter list, and in 'catch (...)') have no competing expression
# interpretation: the epsilon-reduce of the empty list on a type-start token
# is the state's only action. Do NOT reuse this at statement level, where a
# nullable prefix would break 'Foo x;' - see the NOTE at VariableDeclaration.
ParamModifierList = ('final' | Annotation)* ;

# '...' (varargs, JLS 8.4.1) is one lexer token: Alex's maximal munch prefers
# it over '.', and no float literal can follow '..'. That only the LAST
# parameter may be variadic is a semantic check (javac's job): the parser
# accepts the ellipsis on any parameter, including a catch parameter.
Parameter  =
ParamModifierList Type ('...')? id Dims ;

StatementList = Statement *;

OptExpression = Expression? ;

OptId = id?;

# The 'super'/'this' call alternatives are explicit constructor invocations
# (JLS 8.8.7.1). They are allowed wherever a statement is: that they may
# only appear as the FIRST statement of a constructor body is a semantic
# check (javac's job). Both are conflict-free: 'this'/'super' directly
# followed by '(' had no parse at all before, and after shifting the
# keyword at statement start the next token decides ('(' starts the
# invocation; '.' and the operator/closer tokens reduce the keyword to the
# expression primary).
Statement =
VariableDeclaration
 |  'return'  OptExpression  ';'
 |  Expression  ';'
 |  StatementBlock
 |  IfStatement
 |  DoStatement
 |  WhileStatement
 |  ForStatement
 |  TryStatement
 |  SwitchStatement
 |  'synchronized'  '(' Expression  ')' Statement
 |  'throw' Expression ';'
 |  id  ':' Statement
 |  'break'  OptId  ';'
 |  'continue'  OptId  ';'
 |  'super' '(' Arglist ')' ';'
 |  'this' '(' Arglist ')' ';'
 |  ';' ;

OptElsePart = ('else' Statement)? ;

# The then-branch is a full Statement, so a nested if needs no braces:
# 'if (a) if (b) f(); else g();' is valid Java. This is the classic
# dangling-else shift/reduce conflict (family 1 of the inventory at the top
# of this file); happy resolves it by shifting, i.e. 'else' binds to the
# NEAREST 'if' - exactly the JLS 14.5 rule. (A StatementWithoutIf then-
# branch used to exclude nested ifs from the then position: that dodged
# nothing - the conflict already existed via loop bodies like
# 'if (a) while (b) if (c) f(); else g();' - while rejecting the valid
# direct form above.)
IfStatement =
 'if'  '(' Expression  ')' Statement
   OptElsePart ;

DoStatement =
 'do' Statement  'while'  '(' Expression  ')'  ';'  ;

WhileStatement =
 'while'  '(' Expression  ')' Statement  ;

ForStatement =
 'for'  '('  ( VariableDeclaration  |  ( Expression  ';'  )  |  ';'  )
 OptExpression  ';'
 OptExpression
 ')' Statement  ;

CatchList = (  'catch'  '(' Parameter  ')' Statement)* ;

OptFinally = ('finally' Statement)?;

TryStatement =
 'try' Statement
  CatchList
  OptFinally  ;

SwitchCaseList = ((  'case' Expression  ':'  )
 |  (  'default'  ':'  )
 | Statement )*;

SwitchStatement =
 'switch'  '(' Expression  ')'  '{'
 SwitchCaseList
 '}'  ;

# Expression Hierarchy
# Restructured to eliminate reduce/reduce conflicts by ensuring a single parse path
# through the operator precedence chain. Each expression level flows into the next
# without overlapping alternatives.
#
# UPDATED: All expression rules now use shared "Expression" type for QQ support
Expression : Expression = AssignmentExpression ;

Expression : AssignmentExpression =
 ConditionalExpression (AssignmentOp AssignmentExpression)? ;

# COMMENTED OUT - CAUSES LEFT RECURSION:
 # | Expression (/*NumericExpressionEnd
 # | TestingExpressionEnd
 # | LogicalExpressionEnd
 # | BitExpressionEnd
 # | */'(' Arglist ')'
 # | '[' Expression  ']'
 # | '.' Expression
 # | ',' Expression
 # | 'instanceof'  CompoundName )

AssignmentOp =
 '='
 | '+='
 | '-='
 | '*='
 | '/='
 | '|='
 | '&='
 | '^='
 | '%='
 | '<<='
 | '>>='
 | '>>>=' ;

Expression : ConditionalExpression = ConditionalOrExpression | ConditionalOrExpression '?' Expression ':' ConditionalExpression ;

Expression : ConditionalOrExpression = ConditionalAndExpression | ConditionalOrExpression '||' ConditionalAndExpression ;

Expression : ConditionalAndExpression = InclusiveOrEpression | ConditionalAndExpression '&&' InclusiveOrEpression ;

Expression : InclusiveOrEpression = ExclusiveOrExpression | InclusiveOrEpression '|' ExclusiveOrExpression;

Expression : ExclusiveOrExpression = AndExpression | ExclusiveOrExpression '^' AndExpression ;

Expression : AndExpression = EqualityExpression | AndExpression '&' EqualityExpression ;

EqualityOp = '==' | '!=' ;

Expression : EqualityExpression = RelationalExpression | EqualityExpression EqualityOp RelationalExpression ;

RelationalOp = '<' | '>' | '<=' | '>=' ;

# Non-associative on purpose: both operands are ShiftExpressions, not the
# JLS's left-recursive RelationalExpression. This is what lets ShiftOp below
# be composed from single '>' tokens: RelationalOp and ShiftOp are then
# expected from the SAME automaton state, so a lone '>' is shifted without
# committing and the NEXT token decides - a second '>' extends a shift
# operator, an operand start reduces RelationalOp '>'. With a left-recursive
# first operand that decision would instead be a reduce-vs-shift at the
# FIRST '>' (reduce to RelationalExpression to compare vs shift into
# ShiftOp), and happy's shift preference would make every plain 'a > b'
# unparseable. Cost: a chained relational ('a < b < c') is now a parse
# error instead of a semantic error - no loss for valid Java, where a
# relational result (boolean) admits no further relational operator.
# 'instanceof' keeps its left-recursive operand: the keyword follows a
# COMPLETE relational expression, so shifting it needs no early commitment.
Expression : RelationalExpression = ShiftExpression | ShiftExpression RelationalOp ShiftExpression | RelationalExpression 'instanceof' Type ;

# There are NO '>>'/'>>>' tokens: shift operators are composed in the parser
# from adjacent '>' tokens. RTK collects lexer literals from the rule text,
# so once no rule spells '>>'/'>>>', the input '>>' lexes as two '>' tokens:
# in 'Map<String, List<String>>' each '>' closes one TypeArguments level,
# while in 'a >> b' the parser assembles the operator from the two tokens
# (see the RelationalExpression NOTE above for why that is decidable).
# '>>=' and '>>>=' STAY single tokens in AssignmentOp: in valid Java those
# character sequences are always the assignment operator (a type-argument
# closer is never followed directly by '='). Trade-off: token adjacency is
# not checked, so a spaced 'a > > b' is (wrongly but harmlessly) accepted
# as 'a >> b'.
ShiftOp = '<<' | '>' '>' | '>' '>' '>' ;

Expression : ShiftExpression = AdditiveExpression | ShiftExpression ShiftOp AdditiveExpression ;

AdditiveOp = '+' | '-' ;

Expression : AdditiveExpression = MultiplicativeExpression | AdditiveExpression AdditiveOp MultiplicativeExpression ;

MultiplicativeOp = '*' | '/' | '%' ;

Expression : MultiplicativeExpression = UnaryExpression | MultiplicativeExpression MultiplicativeOp UnaryExpression ;

# JLS cast trick (JLS 19, CastExpression): no alternative needs a reduce
# before its first distinguishing token, so LALR(1) never has to decide
# "type or expression?" early:
#  - a primitive keyword after '(' can only be a cast;
#  - '(' CompoundName '<' commits to a generic cast (NonEmptyTypeArguments);
#  - '(' CompoundName '[' is decided one token later: ']' means an array
#    cast (NonEmptyDims), an expression start means an array access inside
#    a parenthesized expression;
#  - every other reference cast is parsed as '(' Expression ')' with the
#    type-as-expression, e.g. (Foo) x or (java.util.List) x. Whether
#    '(' Expression ')' is a cast or a parenthesized expression is decided
#    one token after ')': an operand start means cast. '(a) + b' stays an
#    addition precisely because '+'/'-' are excluded from
#    UnaryExpressionNotPlusMinus (which is why the JLS has that nonterminal).
#
# KNOWN LIMITATION: '(a < b)' as a primary expression mis-commits on '<'
# (the shift prefers a generic cast '(a<...>) ...'), so e.g. 'x = (a < b);'
# is rejected. This is the standard trade-off of LALR Java parsers. In
# conditions (if/while/for) and argument lists there is no cast context, so
# 'if (a < b)' and 'f(a < b)' are unaffected.
#
# The '[' and '<' shifts inside the cast head are families 5 and 6 of the
# conflict inventory at the top of this file. The cast-vs-paren decision
# after ')' is conflict-FREE: operand-start tokens can never follow a
# complete parenthesized expression, so the lookahead sets are disjoint.
Expression : CastExpression =
 '(' PrimitiveTypeKeyword Dims ')' UnaryExpression
 | '(' CompoundName NonEmptyTypeArguments Dims ')' UnaryExpressionNotPlusMinus
 | '(' CompoundName NonEmptyDims ')' UnaryExpressionNotPlusMinus
 | '(' Expression ')' UnaryExpressionNotPlusMinus ;

# '~' and '!' live in UnaryExpressionNotPlusMinus (JLS 15.15), not here:
# keeping them in both derives ~x two ways, and they must stay in
# UnaryExpressionNotPlusMinus so reference-type casts can be followed by ~/!.
PrefixOp = '++' | '--' | '+' | '-' ;

PostfixOp = '++' | '--' ;

Expression : UnaryExpression =
 PrefixOp UnaryExpression
 | UnaryExpressionNotPlusMinus ;

Expression : UnaryExpressionNotPlusMinus =
 PostfixExpression
 | '~' UnaryExpression
 | '!' UnaryExpression
 | CastExpression ;

# The NonEmptyTypeArguments alternative is the explicit-type-argument call
# on a non-name base: this.<T>m(), f().<T>m(). After the shifted '.' the
# tokens id and '<' are distinct, so it adds no conflict. A NAME base
# (Collections.<String>emptyList()) never reaches it - the greedy family-4
# shift keeps the name unreduced - and parses through the PrimaryNoPostfix
# alternative anchored on 'id CompoundNameTail' instead; see the NOTE
# there.
Expression : PostfixExpression =
 PrimaryNoPostfix
 | PostfixExpression PostfixOp
 | PostfixExpression '.' id
 | PostfixExpression '.' id '(' Arglist ')'
 | PostfixExpression '.' NonEmptyTypeArguments id '(' Arglist ')'
 | PostfixExpression '[' Expression ']' ;

# The 'CompoundName '[' Expression ']'' alternative anchors array access on
# the unreduced name (JLS ArrayAccess: Name [ Expression ]). It must sit here
# and not only on PostfixExpression: reaching PostfixExpression first would
# reduce CompoundName on lookahead '[', committing against the type
# interpretation ('Foo[] x;', '(Foo[]) x') one token too early. With the
# anchored alternative both interpretations shift '[' and the NEXT token
# decides. 'a[0]' is still derivable through PostfixExpression too; the
# resulting shift/reduce conflicts on '[' prefer the shift, i.e. exactly this
# anchored production (the PostfixExpression production still applies to
# non-name bases such as 'f()[0]' or 'this.x[0]').
#
# The class-literal, qualified-this and explicit-type-argument-call
# alternatives anchor on the UNREDUCED name spelling 'id CompoundNameTail',
# NOT on CompoundName: the greedy name-extension shift (family 4) means a
# complete CompoundName is never reduced on lookahead '.', so a
# 'CompoundName '.' X' alternative could never be reached - the import-star
# lesson (see ImportName), expression edition. Spelling the shared
# CompoundNameTail keeps every '.' continuation in ONE item set, and the
# token after the shifted '.' decides: id extends the name, 'class'/'this'/
# '<' commit to one of these alternatives. The primitive class literal
# (int.class, void.class) is its own alternative; after the keyword the '.'
# shift is distinct from every Dims/declarator continuation. Array class
# literals (String[].class) are not covered (rare).
Expression : PrimaryNoPostfix =
 Literal
 | 'this'
 | '(' Expression ')'
 | CreationExpression
 | CompoundName ('(' Arglist ')')?
 | CompoundName '[' Expression ']'
 | 'super' '.' id ('(' Arglist ')')?
 | id CompoundNameTail '.' 'class'
 | id CompoundNameTail '.' 'this'
 | id CompoundNameTail '.' NonEmptyTypeArguments id '(' Arglist ')'
 | PrimitiveTypeKeyword '.' 'class' ;

# Only array creation takes sizing expressions (DimExprs); trailing empty
# pairs allow 'new int[3][]'. Uses TypeSpecifier, not Type, so the dims are
# not consumed twice. 'new int[] {1,2}' (initializer syntax) is a separate
# feature (Phase 4g).
CreationExpression = 'new' TypeSpecifier ( '(' Arglist ')' | DimExprs (NonEmptyDims)? );

DimExprs = ('[' Expression ']')+ ;

Literal = integerLiteral | floatLiteral | 'true' | 'false' | char | string | 'null' ;

Arglist = (Expression (',' Expression)*)? ;

# Generics support
# NOTE: '<' is both the type-argument opener (List<String>) and the
# comparison operator (x < y). Wherever a CompoundName can be followed by
# either, the parser shifts '<' into NonEmptyTypeArguments (standard for
# Java parsers); see family 6 of the conflict inventory at the top of this
# file for the two states where this surfaces.
TypeArguments = NonEmptyTypeArguments? ;

# Non-optional variant: Type and CastExpression need the '<' to be a plain
# shift from CompoundName (an optional TypeArguments would interpose an
# epsilon-reduce and recreate the type-vs-expression conflict).
#
# The '<' '>' alternative is the diamond (JLS 15.9, new ArrayList<>()).
# After the shifted '<', the '>' and every type-argument start token are
# distinct, so it adds no conflict. "NonEmpty" still holds in the token
# sense - the rule always consumes the '<' '>' pair, it just may hold zero
# TypeArguments - which is exactly what keeps the '<' a plain shift. The
# diamond is thereby accepted wherever type arguments are (casts, extends,
# member types, ...); rejecting it outside 'new' is a semantic check
# (javac's job).
NonEmptyTypeArguments = '<' TypeArgument (',' TypeArgument)* '>' | '<' '>' ;

TypeArgument =
 Type
 | WildcardType ;

WildcardType =
 '?'
 | '?' 'extends' Type
 | '?' 'super' Type ;

TypeParameters = ('<' TypeParameter (',' TypeParameter)* '>')? ;

TypeParameter = id ('extends' Type ('&' Type)*)? ;

# Type is spelled out by alternatives (instead of 'TypeSpecifier Dims') so
# that in positions where both a type and an expression may start (statement
# start, for-init) every alternative either shifts its next token directly
# from CompoundName ('<' for generics, '[' for array types) or reduces bare
# CompoundName only on lookahead 'id' (the declared variable's name), which
# no expression interpretation shares. This is what removes the
# PrimaryNoPostfix-vs-TypeSpecifier reduce/reduce conflict: the parser no
# longer has to pick type-or-expression while staring at '['.
Type  =
 PrimitiveTypeKeyword Dims
 | CompoundName NonEmptyTypeArguments Dims
 | CompoundName NonEmptyDims
 | CompoundName ;

# Only used by CreationExpression nowadays (Type spells out its own
# alternatives, see above).
TypeSpecifier =
 'boolean'
 |  'byte'
 |  'char'
 |  'short'
 |  'int'
 |  'float'
 |  'long'
 |  'double'
 |  'void'
 | CompoundName TypeArguments ;

Modifier =
 'public'
 |  'private'
 |  'protected'
 |  'static'
 |  'final'
 |  'native'
 |  'synchronized'
 |  'abstract'
 |  'threadsafe'
 |  'transient'  ;

# The tail of a dotted name, hoisted into a NAMED rule so that CompoundName
# and the PrimaryNoPostfix alternatives anchored on 'id CompoundNameTail'
# (class literal, qualified this, explicit-type-argument call) share ONE
# list nonterminal. The sharing is what makes those alternatives reachable:
# RTK extracts every inline sub-pattern into a FRESH nonterminal, and two
# distinct nullable lists starting after the same id would epsilon-reduce
# against each other (reduce/reduce) before the first '.' ever arrives.
# With one shared tail, all '.' continuations sit in one item set and the
# token after the '.' picks the branch - the same cure as ImportName's
# left-recursive spelling, applied to expression position.
CompoundNameTail = ('.' id)* ;

CompoundName =
id CompoundNameTail ;

# Integer literals (JLS 3.10.1): decimal/octal, hex (0x/0X), binary (0b/0B),
# underscores between digits, and l/L suffix. The decimal branch also covers
# octal since they are lexically identical.
integerLiteral =
 (  (  [0-9]  ([0-9_]* [0-9])?  )
 |  (  '0'  [xX]  [0-9a-fA-F]  ([0-9a-fA-F_]* [0-9a-fA-F])?  )
 |  (  '0'  [bB]  [01]  ([01_]* [01])?  )  )
 [lL]?  ;

# Float literals (JLS 3.10.2): underscores between digits, e/E exponent,
# f/F/d/D suffix. A bare exponent (1e5) or suffix (10f) also makes a float.
floatLiteral  =
 [0-9] ([0-9_]* [0-9])? '.' ([0-9] ([0-9_]* [0-9])?)? exponentPart? floatTypeSuffix?
 |  '.' [0-9] ([0-9_]* [0-9])? exponentPart? floatTypeSuffix?
 |  [0-9] ([0-9_]* [0-9])? exponentPart floatTypeSuffix?
 |  [0-9] ([0-9_]* [0-9])? floatTypeSuffix ;

exponentPart  =
 [eE]  (  '+'  |  '-'  )? [0-9] ([0-9_]* [0-9])?  ;

floatTypeSuffix  =
 [fFdD]  ;

# Character literal: handles simple chars and escape sequences
# Simple char: 'a', single-char escapes: '\n', '\t', multi-digit octal
# escapes: '\12', '\101', '\377' (JLS 3.10.6), and Unicode: '\u0041'
char  = '\'' . '\''
      | '\'' backslash . '\''
      | '\'' backslash [0-7] [0-7] '\''
      | '\'' backslash [0-3] [0-7] [0-7] '\''
      | '\'' backslash 'u' [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] '\'' ;

# String literal: handles escape sequences like \", \\, \n, etc.
# Use negated class to match any char except quote, backslash, newline, CR
# Allow backslash followed by any char for escape sequences
string  = [\"] ([^\"\\\n\r] | backslash .)* [\"] ;

# The literal-backslash class, as a charset macro. The grammar lexer reads
# '\]' as an escape pair inside [...], so a class ENDING in a backslash
# (like this one) mis-lexes whenever another ']' follows it on the same
# line; the macro pins the class at a line end once instead of constraining
# every use site. (It used to be spelled [\\x5C] at each use, leaking
# Alex's hex escape through the then-unescaped bracket translation -
# issue #95.)
@symmacro
backslash = [\\] ;

id = [a-zA-Z$_][a-zA-Z$_0-9]* ;
# Fixed: Split [*] [^/] into explicit cases for better Alex DFA generation
# This fixes blank line + {@link Class#method()} parsing issues
doccomment = '/**' ([\n] | [^*\n] | [*] [^/\n] | [*] [\n])* '*/';

# Whitespace and comments (ignored)
Ignore: ws = [ \t\n\r]+ ;
Ignore: comment = '//' [^\n]* '\n' ;
# Block comment must not match /** (which is doccomment)
# Same fix applied as doccomment
# Note: [^*] doesn't match newline in Alex, so we use ([^*\n] | [\n])
Ignore: blockComment = '/*' ([^*\n] | [\n]) ([\n] | [^*\n] | [*] [^/\n] | [*] [\n])* '*/' ;