/*
 * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
 * Copyright (C) 2011, 2013-2024 The JavaParser Team.
 *
 * This file is part of JavaParser.
 *
 * JavaParser can be used either under the terms of
 * a) the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * b) the terms of the Apache License
 *
 * You should have received a copy of both licenses in LICENCE.LGPL and
 * LICENCE.APACHE. Please refer to those files for details.
 *
 * JavaParser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 */

/*
 * -- FILE STRUCTURE --
 * https://javacc.github.io/javacc/documentation/grammar.html#file-structure
 *
 * <pre>{@code
 * javacc_input ::= javacc_options
 *                  "PARSER_BEGIN" "(" <IDENTIFIER> ")"
 *                  java_compilation_unit
 *                  "PARSER_END" "(" <IDENTIFIER> ")"
 *                  ( production )*
 *                  <EOF>
 * }</pre>
 */


/*
* Available options are spread across:
* - https://javacc.github.io/javacc/documentation/cli.html
* - https://javacc.github.io/javacc/documentation/grammar.html#options
*/
options {
    COMMON_TOKEN_ACTION = true;
    ERROR_REPORTING = true;
    JAVA_TEMPLATE_TYPE = "modern"; // FIXME: Warning: Bad option name "JAVA_TEMPLATE_TYPE".  Option setting will be ignored.
    JDK_VERSION = "1.8";
    STATIC = false;
    TOKEN_EXTENDS = "TokenBase";
}


/********************************************************
 * This is the template for the generated file.         *
 * GeneratedJavaParser.java                             *
 ********************************************************/
PARSER_BEGIN(GeneratedJavaParser)
/*
 * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
 * Copyright (C) 2011, 2013-2024 The JavaParser Team.
 *
 * This file is part of JavaParser.
 *
 * JavaParser can be used either under the terms of
 * a) the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * b) the terms of the Apache License
 *
 * You should have received a copy of both licenses in LICENCE.LGPL and
 * LICENCE.APACHE. Please refer to those files for details.
 *
 * JavaParser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 */
package com.github.javaparser;

import java.io.*;
import java.util.*;
import com.github.javaparser.ast.*;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.comments.*;
import com.github.javaparser.ast.modules.*;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.type.*;
import com.github.javaparser.utils.*;
import static com.github.javaparser.JavaToken.INVALID;
import static com.github.javaparser.ast.Node.Parsedness.UNPARSABLE;
import static com.github.javaparser.utils.Utils.*;
import static com.github.javaparser.ast.NodeList.*;
import static com.github.javaparser.GeneratedJavaParser.*;
import static com.github.javaparser.Range.*;
import static com.github.javaparser.Position.*;
import static com.github.javaparser.ast.type.ArrayType.*;
import static com.github.javaparser.GeneratedJavaParserTokenManagerBase.*;
import static com.github.javaparser.ast.stmt.SwitchEntry.Type.*;

@Generated("JavaCC")
final class GeneratedJavaParser extends GeneratedJavaParserBase {
    /* Returns the JavaParser specific token type of the last matched token */
    JavaToken token() {
        return token.javaToken;
    }

    /* Returns the last matched token */
    Token getCurrentToken() {
        return token;
    }

    /* Changes the amount by which the horizontal position is increased when a tab character is encountered.
        One by default.*/
    void setTabSize(int size) {
        jj_input_stream.setTabSize(size);
    }

    @Override
    GeneratedJavaParserTokenManager getTokenSource() {
        return token_source;
    }
}
PARSER_END(GeneratedJavaParser)


/*
 * https://javacc.github.io/javacc/documentation/grammar.html#production
 *
 * <pre>{@code
 * production ::= javacode_production
 *              | regular_expr_production
 *              | bnf_production
 *              | token_manager_decls

 * }</pre>
 */


/***************************************************************************
 * TOKEN DEFINITIONS START HERE                                            *
 * https://javacc.github.io/javacc/documentation/grammar.html#regexpr_kind *
 *                                                                         *
 * <pre>{@code                                                             *
 * regexpr_kind ::= "TOKEN"                                                *
 *                | "SPECIAL_TOKEN"                                        *
 *                | "SKIP"                                                 *
 *                | "MORE"                                                 *
 * }</pre>                                                                 *
 ***************************************************************************/

/* WHITE SPACE */

SPECIAL_TOKEN :
{
  <SPACE: [ // TODO: Rename (or document) this as `WHITESPACE`?
     " ",      // regular space character
     "\t",     // tab character
     "\f",     // formfeed character
     "\u0085", // 'NEXT LINE (NEL)' (U+0085)
     "\u00A0", // 'NO-BREAK SPACE' (U+00A0)
     "\u1680", // 'OGHAM SPACE MARK' (U+1680)
     "\u180e", // 'MONGOLIAN VOWEL SEPARATOR' (U+180E)
     "\u2000", // 'EN QUAD' (U+2000)
     "\u2001", // 'EM QUAD' (U+2001)
     "\u2002", // 'EN SPACE' (U+2002)
     "\u2003", // 'EM SPACE' (U+2003)
     "\u2004", // 'THREE-PER-EM SPACE' (U+2004)
     "\u2005", // 'FOUR-PER-EM SPACE' (U+2005)
     "\u2006", // 'SIX-PER-EM SPACE' (U+2006)
     "\u2007", // 'FIGURE SPACE' (U+2007)
     "\u2008", // 'PUNCTUATION SPACE' (U+2008)
     "\u2009", // 'THIN SPACE' (U+2009)
     "\u200a", // 'HAIR SPACE' (U+200A)
     "\u200b", // 'ZERO WIDTH SPACE' (U+200B)
     "\u200c", // 'ZERO WIDTH NON-JOINER' (U+200C)
     "\u200d", // 'ZERO WIDTH JOINER' (U+200D)
     "\u2028", // 'LINE SEPARATOR' (U+2028) // TODO: Investigate this being EOL?
     "\u2029", // 'PARAGRAPH SEPARATOR' (U+2029) // TODO: Investigate this being EOL?
     "\u202f", // 'NARROW NO-BREAK SPACE' (U+202F)
     "\u205f", // 'MEDIUM MATHEMATICAL SPACE' (U+205F)
     "\u2060", // 'WORD JOINER' (U+2060)
     "\u3000", // 'IDEOGRAPHIC SPACE' (U+3000)
     "\ufeff"  // 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
  ]>
| <WINDOWS_EOL : "\r\n">
| <UNIX_EOL: "\n">
| <OLD_MAC_EOL: "\r">
}


/*
 * GeneratedJavaParserTokenManager.java is generated,
 * based on the template within this TOKEN_MGR_DECLS block.
 */
TOKEN_MGR_DECLS :
{
    private List<JavaToken> tokens = new ArrayList<JavaToken>();
    private CommentsCollection commentsCollection = new CommentsCollection();
    private JavaToken homeToken;
    private Stack<Token> tokenWorkStack = new Stack<Token>();
    private boolean storeTokens;
    private boolean yieldSupported = false;

    void reset() {
        tokens = new ArrayList<JavaToken>();
        commentsCollection = new CommentsCollection();
        homeToken = null;
    }

    List<JavaToken> getTokens() {
        if(storeTokens) {
            return tokens;
        }
        return null;
    }

    CommentsCollection getCommentsCollection() {
        return commentsCollection;
    }

    /* Get the very first token in the file */
    JavaToken getHomeToken() {
        return homeToken;
    }

    /* Makes the parser keep a list of tokens */
    public void setStoreTokens(boolean storeTokens) {
        this.storeTokens = storeTokens;
    }

    public void setYieldSupported() {
        yieldSupported = true;
    }

    private void CommonTokenAction(Token token) {
        // Use an intermediary stack to avoid recursion, see issue 1003
        do {
            tokenWorkStack.push(token);
            token = token.specialToken;
        } while (token != null);

        // The stack is now filled with tokens in left-to-right order. Process them.
        while(!tokenWorkStack.empty()) {
            token = tokenWorkStack.pop();
            token.javaToken = new JavaToken(token, tokens);

            if(storeTokens) {
                tokens.add(token.javaToken);
            }

            if (homeToken == null) {
                homeToken = token.javaToken;
            }

            if(TokenTypes.isComment(token.kind)) {
                Comment comment = createCommentFromToken(token);
                commentsCollection.addComment(comment);
            }
        }
    }
}


/* COMMENTS */

SPECIAL_TOKEN :
{
  <SINGLE_LINE_COMMENT: "//" (~["\n","\r"])*>
}

MORE :
{
  <ENTER_JAVADOC_COMMENT: "/**" ~["/"]> { input_stream.backup(1); } : IN_JAVADOC_COMMENT
|
  <ENTER_MULTILINE_COMMENT: "/*"> : IN_MULTI_LINE_COMMENT
}

<IN_JAVADOC_COMMENT>
SPECIAL_TOKEN :
{
  <JAVADOC_COMMENT: "*/" > : DEFAULT
}

<IN_MULTI_LINE_COMMENT>
SPECIAL_TOKEN :
{
  <MULTI_LINE_COMMENT: "*/" > : DEFAULT
}

<IN_JAVADOC_COMMENT, IN_MULTI_LINE_COMMENT>
MORE :
{
  <COMMENT_CONTENT: ~[] >
}


/* RESERVED WORDS AND LITERALS */

TOKEN :
{
  < ABSTRACT: "abstract" >
| < ASSERT: "assert" >
| < BOOLEAN: "boolean" >
| < BREAK: "break" >
| < BYTE: "byte" >
| < CASE: "case" >
| < CATCH: "catch" >
| < CHAR: "char" >
| < CLASS: "class" >
| < CONST: "const" >
| < CONTINUE: "continue" >
| < _DEFAULT: "default" >
| < DO: "do" >
| < DOUBLE: "double" >
| < ELSE: "else" >
| < ENUM: "enum" >
| < EXTENDS: "extends" >
| < FALSE: "false" >
| < FINAL: "final" >
| < FINALLY: "finally" >
| < FLOAT: "float" >
| < FOR: "for" >
| < GOTO: "goto" >
| < IF: "if" >
| < IMPLEMENTS: "implements" >
| < IMPORT: "import" >
| < INSTANCEOF: "instanceof" >
| < INT: "int" >
| < INTERFACE: "interface" >
| < LONG: "long" >
| < NATIVE: "native" >
| < NEW: "new" >
| < NON_SEALED: "non-sealed" >
| < NULL: "null" >
| < PACKAGE: "package">
| < PERMITS: "permits">
| < PRIVATE: "private" >
| < PROTECTED: "protected" >
| < PUBLIC: "public" >
| < RECORD: "record" >
| < RETURN: "return" >
| < SEALED: "sealed" >
| < SHORT: "short" >
| < STATIC: "static" >
| < STRICTFP: "strictfp" >
| < SUPER: "super" >
| < SWITCH: "switch" >
| < SYNCHRONIZED: "synchronized" >
| < THIS: "this" >
| < THROW: "throw" >
| < THROWS: "throws" >
| < TRANSIENT: "transient" >
| < TRUE: "true" >
| < TRY: "try" >
| < VOID: "void" >
| < VOLATILE: "volatile" >
| < WHILE: "while" >
| < YIELD: "yield" > {if(!yieldSupported)matchedToken.kind=IDENTIFIER;}
| < REQUIRES: "requires" >
| < TO: "to" >
| < WITH: "with" >
| < OPEN: "open" >
| < OPENS: "opens" >
| < USES: "uses" >
| < MODULE: "module" >
| < EXPORTS: "exports" >
| < PROVIDES: "provides" >
| < TRANSITIVE: "transitive" >
| < WHEN: "when" >
}

/* LITERALS */

TOKEN :
{
 /**
  * Note that the JLS treats longs as an int followed by the letter {@code L} (upper or lowercase)
  * Note that Javaparser differs, as it handles longs separately (as a {@code LONG_LITERAL})
  */
  < LONG_LITERAL:
        <DECIMAL_LITERAL> (["l","L"])
      | <HEX_LITERAL> (["l","L"])
      | <OCTAL_LITERAL> (["l","L"])
      | <BINARY_LITERAL> (["l","L"])
  >
|
 /**
  * Note that the JLS treats longs as an int followed by the letter {@code L} (upper or lowercase)
  * Note that Javaparser differs, as it handles longs separately (as a {@code LONG_LITERAL})
  * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.1
  * <pre>{@code
  *     IntegerLiteral:
  *         DecimalIntegerLiteral
  *         HexIntegerLiteral
  *         OctalIntegerLiteral
  *         BinaryIntegerLiteral
  *     DecimalIntegerLiteral:
  *         DecimalNumeral [IntegerTypeSuffix]
  *     HexIntegerLiteral:
  *         HexNumeral [IntegerTypeSuffix]
  *     OctalIntegerLiteral:
  *         OctalNumeral [IntegerTypeSuffix]
  *     BinaryIntegerLiteral:
  *         BinaryNumeral [IntegerTypeSuffix]
  *     IntegerTypeSuffix:
  *         (one of)
  *         l L
  * }</pre>
  * For convenience:
  * <pre>{@code
  *     DecimalNumeral:
  *         0
  *         NonZeroDigit [Digits]
  *         NonZeroDigit Underscores Digits
  *     NonZeroDigit:
  *         (one of)
  *         1 2 3 4 5 6 7 8 9
  *     Digits:
  *         Digit
  *         Digit [DigitsAndUnderscores] Digit
  *     Digit:
  *         0
  *         NonZeroDigit
  *     DigitsAndUnderscores:
  *         DigitOrUnderscore {DigitOrUnderscore}
  *     DigitOrUnderscore:
  *         Digit
  *         _
  *     Underscores:
  *         _ {_}
  * }</pre>
  * For convenience:
  * <pre>{@code
  *     HexNumeral:
  *         0 x HexDigits
  *         0 X HexDigits
  *     HexDigits:
  *         HexDigit
  *         HexDigit [HexDigitsAndUnderscores] HexDigit
  *     HexDigit:
  *         (one of)
  *         0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
  *     HexDigitsAndUnderscores:
  *         HexDigitOrUnderscore {HexDigitOrUnderscore}
  *     HexDigitOrUnderscore:
  *         HexDigit
  *         _
  * }</pre>
  * For convenience:
  * <pre>{@code
  *     OctalNumeral:
  *         0 OctalDigits
  *         0 Underscores OctalDigits
  *     OctalDigits:
  *         OctalDigit
  *         OctalDigit [OctalDigitsAndUnderscores] OctalDigit
  *     OctalDigit:
  *         (one of)
  *         0 1 2 3 4 5 6 7
  *     OctalDigitsAndUnderscores:
  *         OctalDigitOrUnderscore {OctalDigitOrUnderscore}
  *     OctalDigitOrUnderscore:
  *         OctalDigit
  *         _
  * }</pre>
  * For convenience:
  * <pre>{@code
  *     BinaryNumeral:
  *         0 b BinaryDigits
  *         0 B BinaryDigits
  *     BinaryDigits:
  *         BinaryDigit
  *         BinaryDigit [BinaryDigitsAndUnderscores] BinaryDigit
  *     BinaryDigit:
  *         (one of)
  *         0 1
  *     BinaryDigitsAndUnderscores:
  *         BinaryDigitOrUnderscore {BinaryDigitOrUnderscore}
  *     BinaryDigitOrUnderscore:
  *         BinaryDigit
  *         _
  * }</pre>
  */
  < INTEGER_LITERAL:
        <DECIMAL_LITERAL>
      | <HEX_LITERAL>
      | <OCTAL_LITERAL>
      | <BINARY_LITERAL>
  >
|
  < #DECIMAL_LITERAL: ["0"-"9"]((["0"-"9","_"])*["0"-"9"])? >
|
  < #HEX_LITERAL: "0" ["x","X"] <HEX_DIGITS> >
|
  < #OCTAL_LITERAL: "0" ["0"-"7"]((["0"-"7","_"])*["0"-"7"])? >
|
  < #BINARY_LITERAL: "0" ["b","B"] ["0","1"]((["0","1","_"])*["0","1"])? >
|

 /**
  * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.2
  * <pre>{@code
  *     FloatingPointLiteral:
  *         DecimalFloatingPointLiteral
  *         HexadecimalFloatingPointLiteral
  *     DecimalFloatingPointLiteral:
  *         Digits . [Digits] [ExponentPart] [FloatTypeSuffix]
  *         . Digits [ExponentPart] [FloatTypeSuffix]
  *         Digits ExponentPart [FloatTypeSuffix]
  *         Digits [ExponentPart] FloatTypeSuffix
  *     ExponentPart:
  *         ExponentIndicator SignedInteger
  *     ExponentIndicator:
  *         (one of)
  *         e E
  *     SignedInteger:
  *         [Sign] Digits
  *     Sign:
  *         (one of)
  *         + -
  *     FloatTypeSuffix:
  *         (one of)
  *         f F d D
  * }</pre>
  * <pre>{@code
  *     HexadecimalFloatingPointLiteral:
  *         HexSignificand BinaryExponent [FloatTypeSuffix]
  *     HexSignificand:
  *         HexNumeral [.]
  *         0 x [HexDigits] . HexDigits
  *         0 X [HexDigits] . HexDigits
  *     BinaryExponent:
  *         BinaryExponentIndicator SignedInteger
  *     BinaryExponentIndicator:
  *         (one of)
  *         p P
  * }</pre>
  */
  < FLOATING_POINT_LITERAL:
        <DECIMAL_FLOATING_POINT_LITERAL>
      | <HEXADECIMAL_FLOATING_POINT_LITERAL>
  >
|
  < #DECIMAL_FLOATING_POINT_LITERAL:
        <DECIMAL_LITERAL> "." (<DECIMAL_LITERAL>)? (<DECIMAL_EXPONENT>)? (["f","F","d","D"])?
      | "." <DECIMAL_LITERAL> (<DECIMAL_EXPONENT>)? (["f","F","d","D"])?
      | <DECIMAL_LITERAL> <DECIMAL_EXPONENT> (["f","F","d","D"])?
      | <DECIMAL_LITERAL> (<DECIMAL_EXPONENT>)? ["f","F","d","D"]
  >
|
  < #DECIMAL_EXPONENT: ["e","E"] (["+","-"])? (<DECIMAL_LITERAL>)+ >
|
  < #HEXADECIMAL_FLOATING_POINT_LITERAL:
        <HEX_LITERAL> (".")? <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?
      | "0" ["x","X"] (<HEX_DIGITS>)? "." <HEX_DIGITS> <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?
  >
|
  < #HEXADECIMAL_EXPONENT: ["p","P"] (["+","-"])? <DECIMAL_LITERAL> >
|
  < #HEX_DIGITS: ["0"-"9","a"-"f","A"-"F"]((["0"-"9","a"-"f","A"-"F","_"])*["0"-"9","a"-"f","A"-"F"])? >
|
  /**
   * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.3
   * <pre>{@code
   *     UnicodeInputCharacter:
   *         UnicodeEscape
   *         RawInputCharacter
   *     UnicodeEscape:
   *         \ UnicodeMarker HexDigit HexDigit HexDigit HexDigit
   *     UnicodeMarker:
   *         u {u}
   *     HexDigit:
   *         (one of)
   *         0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
   *     RawInputCharacter:
   *         any Unicode character
   * }</pre>
   */
  < #UNICODE_ESCAPE:
    "\\u"
    ["0"-"9","A"-"F","a"-"f"]
    ["0"-"9","A"-"F","a"-"f"]
    ["0"-"9","A"-"F","a"-"f"]
    ["0"-"9","A"-"F","a"-"f"] >
|
  /**
   * TODO: Verify this.
   * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.4
   * <pre>{@code
   *     CharacterLiteral:
   *         ' SingleCharacter '
   *         ' EscapeSequence '
   *     SingleCharacter:
   *         InputCharacter but not ' or \
   * }</pre>
   * <pre>{@code
   *     LineTerminator:
   *         the ASCII LF character, also known as "newline"
   *         the ASCII CR character, also known as "return"
   *         the ASCII CR character followed by the ASCII LF character
   *     InputCharacter:
   *         UnicodeInputCharacter but not CR or LF
   * }</pre>
   * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.7
   * <pre>{@code
   *     EscapeSequence:
   *         \ b (backspace BS, Unicode \u0008)
   *         \ s (space SP, Unicode \u0020)
   *         \ t (horizontal tab HT, Unicode \u0009)
   *         \ n (linefeed LF, Unicode \u000a)
   *         \ f (form feed FF, Unicode \u000c)
   *         \ r (carriage return CR, Unicode \u000d)
   *         \ LineTerminator (line continuation, no Unicode representation)
   *         \ " (double quote ", Unicode \u0022)
   *         \ ' (single quote ', Unicode \u0027)
   *         \ \ (backslash \, Unicode \u005c)
   *         OctalEscape (octal value, Unicode \u0000 to \u00ff)
   *     OctalEscape:
   *         \ OctalDigit
   *         \ OctalDigit OctalDigit
   *         \ ZeroToThree OctalDigit OctalDigit
   *     OctalDigit:
   *         (one of)
   *         0 1 2 3 4 5 6 7
   *     ZeroToThree:
   *         (one of)
   *         0 1 2 3
   * }</pre>
   */
  < CHARACTER_LITERAL:
      "'"
      // TODO: Could (and the duplicate code in STRING_LITERAL) this be extracted out?
      (
          (~["'","\\","\n","\r"])
       |
          // starts off with unicode backslash
          ("\\u005" ["c", "C"]
              // regular escape sequences
              (["s","n","t","b","r","f","\\","'","\""]
                  // escapes another unicode backslash
                  | ("\\u005" ["c", "C"])))
       |
          ( // TODO: Could these escape sequences be extracted out?
              "\\"
              (
                  ["s","n","t","b","r","f","\\","'","\""]
               |
                  ["0"-"7"] ( ["0"-"7"] )?
               |
                  ["0"-"3"] ["0"-"7"] ["0"-"7"]
              )
          )
       |
          <UNICODE_ESCAPE>
      )
      "'"
  >
|
  /**
   * TODO: Verify this.
   * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.5
   * <pre>{@code
   *     StringLiteral:
   *         " {StringCharacter} "
   *     StringCharacter:
   *         InputCharacter but not " or \
   *         EscapeSequence
   * }</pre>
   * <pre>{@code
   *     LineTerminator:
   *         the ASCII LF character, also known as "newline"
   *         the ASCII CR character, also known as "return"
   *         the ASCII CR character followed by the ASCII LF character
   *     InputCharacter:
   *         UnicodeInputCharacter but not CR or LF
   * }</pre>
   * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.7
   * <pre>{@code
   *     EscapeSequence:
   *         \ b (backspace BS, Unicode \u0008)
   *         \ s (space SP, Unicode \u0020)
   *         \ t (horizontal tab HT, Unicode \u0009)
   *         \ n (linefeed LF, Unicode \u000a)
   *         \ f (form feed FF, Unicode \u000c)
   *         \ r (carriage return CR, Unicode \u000d)
   *         \ LineTerminator (line continuation, no Unicode representation)
   *         \ " (double quote ", Unicode \u0022)
   *         \ ' (single quote ', Unicode \u0027)
   *         \ \ (backslash \, Unicode \u005c)
   *         OctalEscape (octal value, Unicode \u0000 to \u00ff)
   *     OctalEscape:
   *         \ OctalDigit
   *         \ OctalDigit OctalDigit
   *         \ ZeroToThree OctalDigit OctalDigit
   *     OctalDigit:
   *         (one of)
   *         0 1 2 3 4 5 6 7
   *     ZeroToThree:
   *         (one of)
   *         0 1 2 3
   * }</pre>
   */
  < STRING_LITERAL:
      "\""
      // TODO: Could (and the duplicate code in CHARACTER_LITERAL) this be extracted out?
      (
          (~["\"","\\","\n","\r"])
       |
          ( // TODO: Could these escape sequences be extracted out?
              "\\"
              (
                  ["s","n","t","b","r","f","\\","'","\""]
               |
                  ["0"-"7"] ( ["0"-"7"] )?
               |
                  ["0"-"3"] ["0"-"7"] ["0"-"7"]
              )
          )
       |
          (
              "\\u"
              ["0"-"9","A"-"F","a"-"f"]
              ["0"-"9","A"-"F","a"-"f"]
              ["0"-"9","A"-"F","a"-"f"]
              ["0"-"9","A"-"F","a"-"f"]
          )
      )*
      "\""
  >
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.6
 * <pre>{@code
 *     TextBlock:
 *         " " " {TextBlockWhiteSpace} LineTerminator {TextBlockCharacter} " " "
 *     TextBlockWhiteSpace:
 *         WhiteSpace but not LineTerminator
 *     TextBlockCharacter:
 *         InputCharacter but not \
 *         EscapeSequence
 *         LineTerminator
 * }</pre>
 * <pre>{@code
 *     WhiteSpace:
 *         the ASCII SP character, also known as "space"
 *         the ASCII HT character, also known as "horizontal tab"
 *         the ASCII FF character, also known as "form feed"
 *         LineTerminator
 *     LineTerminator:
 *         the ASCII LF character, also known as "newline"
 *         the ASCII CR character, also known as "return"
 *         the ASCII CR character followed by the ASCII LF character
 *     InputCharacter:
 *         UnicodeInputCharacter but not CR or LF
 *         UnicodeInputCharacter:
 *         UnicodeEscape
 *         RawInputCharacter
 *     UnicodeEscape:
 *         \ UnicodeMarker HexDigit HexDigit HexDigit HexDigit
 *     RawInputCharacter:
 *         any Unicode character
 * }</pre>
 */
/* TEXT BLOCK LITERAL */
MORE: { <ENTER_TEXT_BLOCK: "\"\"\"" >: IN_TEXT_BLOCK }
<IN_TEXT_BLOCK> TOKEN : { <TEXT_BLOCK_LITERAL: "\"\"\"" >: DEFAULT }
// Need to recognize an escaped " to prevent it from parsing as "backslash doublequote" -
// which might match that doublequote with following doublequotes.
<IN_TEXT_BLOCK> MORE :{ <TEXT_BLOCK_CONTENT:  ( "\\" "\"" | ~[] ) > }


/* IDENTIFIERS */

TOKEN :
{
  < IDENTIFIER: <LETTER> (<PART_LETTER>)* >
|
  < #LETTER: [
         "\u0024",  "\u0041"-"\u005a",  "\u005f",  "\u0061"-"\u007a",  "\u00a2"-"\u00a5",  "\u00aa",  "\u00b5",
         "\u00ba",  "\u00c0"-"\u00d6",  "\u00d8"-"\u00f6",  "\u00f8"-"\u02c1",  "\u02c6"-"\u02d1",  "\u02e0"-"\u02e4",
         "\u02ec",  "\u02ee",  "\u0370"-"\u0374",  "\u0376"-"\u0377",  "\u037a"-"\u037d",  "\u037f",  "\u0386",
         "\u0388"-"\u038a",  "\u038c",  "\u038e"-"\u03a1",  "\u03a3"-"\u03f5",  "\u03f7"-"\u0481",  "\u048a"-"\u052f",
         "\u0531"-"\u0556",  "\u0559",  "\u0561"-"\u0587",  "\u058f",  "\u05d0"-"\u05ea",  "\u05f0"-"\u05f2",
         "\u060b",  "\u0620"-"\u064a",  "\u066e"-"\u066f",  "\u0671"-"\u06d3",  "\u06d5",  "\u06e5"-"\u06e6",
         "\u06ee"-"\u06ef",  "\u06fa"-"\u06fc",  "\u06ff",  "\u0710",  "\u0712"-"\u072f",  "\u074d"-"\u07a5",
         "\u07b1",  "\u07ca"-"\u07ea",  "\u07f4"-"\u07f5",  "\u07fa",  "\u0800"-"\u0815",  "\u081a",  "\u0824",
         "\u0828",  "\u0840"-"\u0858",  "\u0860"-"\u086a",  "\u08a0"-"\u08b4",  "\u08b6"-"\u08bd",  "\u0904"-"\u0939",
         "\u093d",  "\u0950",  "\u0958"-"\u0961",  "\u0971"-"\u0980",  "\u0985"-"\u098c",  "\u098f"-"\u0990",
         "\u0993"-"\u09a8",  "\u09aa"-"\u09b0",  "\u09b2",  "\u09b6"-"\u09b9",  "\u09bd",  "\u09ce",  "\u09dc"-"\u09dd",
         "\u09df"-"\u09e1",  "\u09f0"-"\u09f3",  "\u09fb"-"\u09fc",  "\u0a05"-"\u0a0a",  "\u0a0f"-"\u0a10",
         "\u0a13"-"\u0a28",  "\u0a2a"-"\u0a30",  "\u0a32"-"\u0a33",  "\u0a35"-"\u0a36",  "\u0a38"-"\u0a39",
         "\u0a59"-"\u0a5c",  "\u0a5e",  "\u0a72"-"\u0a74",  "\u0a85"-"\u0a8d",  "\u0a8f"-"\u0a91",  "\u0a93"-"\u0aa8",
         "\u0aaa"-"\u0ab0",  "\u0ab2"-"\u0ab3",  "\u0ab5"-"\u0ab9",  "\u0abd",  "\u0ad0",  "\u0ae0"-"\u0ae1",
         "\u0af1",  "\u0af9",  "\u0b05"-"\u0b0c",  "\u0b0f"-"\u0b10",  "\u0b13"-"\u0b28",  "\u0b2a"-"\u0b30",
         "\u0b32"-"\u0b33",  "\u0b35"-"\u0b39",  "\u0b3d",  "\u0b5c"-"\u0b5d",  "\u0b5f"-"\u0b61",  "\u0b71",
         "\u0b83",  "\u0b85"-"\u0b8a",  "\u0b8e"-"\u0b90",  "\u0b92"-"\u0b95",  "\u0b99"-"\u0b9a",  "\u0b9c",
         "\u0b9e"-"\u0b9f",  "\u0ba3"-"\u0ba4",  "\u0ba8"-"\u0baa",  "\u0bae"-"\u0bb9",  "\u0bd0",  "\u0bf9",
         "\u0c05"-"\u0c0c",  "\u0c0e"-"\u0c10",  "\u0c12"-"\u0c28",  "\u0c2a"-"\u0c39",  "\u0c3d",  "\u0c58"-"\u0c5a",
         "\u0c60"-"\u0c61",  "\u0c80",  "\u0c85"-"\u0c8c",  "\u0c8e"-"\u0c90",  "\u0c92"-"\u0ca8",  "\u0caa"-"\u0cb3",
         "\u0cb5"-"\u0cb9",  "\u0cbd",  "\u0cde",  "\u0ce0"-"\u0ce1",  "\u0cf1"-"\u0cf2",  "\u0d05"-"\u0d0c",
         "\u0d0e"-"\u0d10",  "\u0d12"-"\u0d3a",  "\u0d3d",  "\u0d4e",  "\u0d54"-"\u0d56",  "\u0d5f"-"\u0d61",
         "\u0d7a"-"\u0d7f",  "\u0d85"-"\u0d96",  "\u0d9a"-"\u0db1",  "\u0db3"-"\u0dbb",  "\u0dbd",  "\u0dc0"-"\u0dc6",
         "\u0e01"-"\u0e30",  "\u0e32"-"\u0e33",  "\u0e3f"-"\u0e46",  "\u0e81"-"\u0e82",  "\u0e84",  "\u0e87"-"\u0e88",
         "\u0e8a",  "\u0e8d",  "\u0e94"-"\u0e97",  "\u0e99"-"\u0e9f",  "\u0ea1"-"\u0ea3",  "\u0ea5",  "\u0ea7",
         "\u0eaa"-"\u0eab",  "\u0ead"-"\u0eb0",  "\u0eb2"-"\u0eb3",  "\u0ebd",  "\u0ec0"-"\u0ec4",  "\u0ec6",
         "\u0edc"-"\u0edf",  "\u0f00",  "\u0f40"-"\u0f47",  "\u0f49"-"\u0f6c",  "\u0f88"-"\u0f8c",  "\u1000"-"\u102a",
         "\u103f",  "\u1050"-"\u1055",  "\u105a"-"\u105d",  "\u1061",  "\u1065"-"\u1066",  "\u106e"-"\u1070",
         "\u1075"-"\u1081",  "\u108e",  "\u10a0"-"\u10c5",  "\u10c7",  "\u10cd",  "\u10d0"-"\u10fa",  "\u10fc"-"\u1248",
         "\u124a"-"\u124d",  "\u1250"-"\u1256",  "\u1258",  "\u125a"-"\u125d",  "\u1260"-"\u1288",  "\u128a"-"\u128d",
         "\u1290"-"\u12b0",  "\u12b2"-"\u12b5",  "\u12b8"-"\u12be",  "\u12c0",  "\u12c2"-"\u12c5",  "\u12c8"-"\u12d6",
         "\u12d8"-"\u1310",  "\u1312"-"\u1315",  "\u1318"-"\u135a",  "\u1380"-"\u138f",  "\u13a0"-"\u13f5",
         "\u13f8"-"\u13fd",  "\u1401"-"\u166c",  "\u166f"-"\u167f",  "\u1681"-"\u169a",  "\u16a0"-"\u16ea",
         "\u16ee"-"\u16f8",  "\u1700"-"\u170c",  "\u170e"-"\u1711",  "\u1720"-"\u1731",  "\u1740"-"\u1751",
         "\u1760"-"\u176c",  "\u176e"-"\u1770",  "\u1780"-"\u17b3",  "\u17d7",  "\u17db"-"\u17dc",  "\u1820"-"\u1877",
         "\u1880"-"\u1884",  "\u1887"-"\u18a8",  "\u18aa",  "\u18b0"-"\u18f5",  "\u1900"-"\u191e",  "\u1950"-"\u196d",
         "\u1970"-"\u1974",  "\u1980"-"\u19ab",  "\u19b0"-"\u19c9",  "\u1a00"-"\u1a16",  "\u1a20"-"\u1a54",
         "\u1aa7",  "\u1b05"-"\u1b33",  "\u1b45"-"\u1b4b",  "\u1b83"-"\u1ba0",  "\u1bae"-"\u1baf",  "\u1bba"-"\u1be5",
         "\u1c00"-"\u1c23",  "\u1c4d"-"\u1c4f",  "\u1c5a"-"\u1c7d",  "\u1c80"-"\u1c88",  "\u1ce9"-"\u1cec",
         "\u1cee"-"\u1cf1",  "\u1cf5"-"\u1cf6",  "\u1d00"-"\u1dbf",  "\u1e00"-"\u1f15",  "\u1f18"-"\u1f1d",
         "\u1f20"-"\u1f45",  "\u1f48"-"\u1f4d",  "\u1f50"-"\u1f57",  "\u1f59",  "\u1f5b",  "\u1f5d",  "\u1f5f"-"\u1f7d",
         "\u1f80"-"\u1fb4",  "\u1fb6"-"\u1fbc",  "\u1fbe",  "\u1fc2"-"\u1fc4",  "\u1fc6"-"\u1fcc",  "\u1fd0"-"\u1fd3",
         "\u1fd6"-"\u1fdb",  "\u1fe0"-"\u1fec",  "\u1ff2"-"\u1ff4",  "\u1ff6"-"\u1ffc",  "\u203f"-"\u2040",
         "\u2054",  "\u2071",  "\u207f",  "\u2090"-"\u209c",  "\u20a0"-"\u20bf",  "\u2102",  "\u2107",  "\u210a"-"\u2113",
         "\u2115",  "\u2119"-"\u211d",  "\u2124",  "\u2126",  "\u2128",  "\u212a"-"\u212d",  "\u212f"-"\u2139",
         "\u213c"-"\u213f",  "\u2145"-"\u2149",  "\u214e",  "\u2160"-"\u2188",  "\u2c00"-"\u2c2e",  "\u2c30"-"\u2c5e",
         "\u2c60"-"\u2ce4",  "\u2ceb"-"\u2cee",  "\u2cf2"-"\u2cf3",  "\u2d00"-"\u2d25",  "\u2d27",  "\u2d2d",
         "\u2d30"-"\u2d67",  "\u2d6f",  "\u2d80"-"\u2d96",  "\u2da0"-"\u2da6",  "\u2da8"-"\u2dae",  "\u2db0"-"\u2db6",
         "\u2db8"-"\u2dbe",  "\u2dc0"-"\u2dc6",  "\u2dc8"-"\u2dce",  "\u2dd0"-"\u2dd6",  "\u2dd8"-"\u2dde",
         "\u2e2f",  "\u3005"-"\u3007",  "\u3021"-"\u3029",  "\u3031"-"\u3035",  "\u3038"-"\u303c",  "\u3041"-"\u3096",
         "\u309d"-"\u309f",  "\u30a1"-"\u30fa",  "\u30fc"-"\u30ff",  "\u3105"-"\u312e",  "\u3131"-"\u318e",
         "\u31a0"-"\u31ba",  "\u31f0"-"\u31ff",  "\u3400"-"\u4db5",  "\u4e00"-"\u9fea",  "\ua000"-"\ua48c",
         "\ua4d0"-"\ua4fd",  "\ua500"-"\ua60c",  "\ua610"-"\ua61f",  "\ua62a"-"\ua62b",  "\ua640"-"\ua66e",
         "\ua67f"-"\ua69d",  "\ua6a0"-"\ua6ef",  "\ua717"-"\ua71f",  "\ua722"-"\ua788",  "\ua78b"-"\ua7ae",
         "\ua7b0"-"\ua7b7",  "\ua7f7"-"\ua801",  "\ua803"-"\ua805",  "\ua807"-"\ua80a",  "\ua80c"-"\ua822",
         "\ua838",  "\ua840"-"\ua873",  "\ua882"-"\ua8b3",  "\ua8f2"-"\ua8f7",  "\ua8fb",  "\ua8fd",  "\ua90a"-"\ua925",
         "\ua930"-"\ua946",  "\ua960"-"\ua97c",  "\ua984"-"\ua9b2",  "\ua9cf",  "\ua9e0"-"\ua9e4",  "\ua9e6"-"\ua9ef",
         "\ua9fa"-"\ua9fe",  "\uaa00"-"\uaa28",  "\uaa40"-"\uaa42",  "\uaa44"-"\uaa4b",  "\uaa60"-"\uaa76",
         "\uaa7a",  "\uaa7e"-"\uaaaf",  "\uaab1",  "\uaab5"-"\uaab6",  "\uaab9"-"\uaabd",  "\uaac0",  "\uaac2",
         "\uaadb"-"\uaadd",  "\uaae0"-"\uaaea",  "\uaaf2"-"\uaaf4",  "\uab01"-"\uab06",  "\uab09"-"\uab0e",
         "\uab11"-"\uab16",  "\uab20"-"\uab26",  "\uab28"-"\uab2e",  "\uab30"-"\uab5a",  "\uab5c"-"\uab65",
         "\uab70"-"\uabe2",  "\uac00"-"\ud7a3",  "\ud7b0"-"\ud7c6",  "\ud7cb"-"\ud7fb",  "\ud800"-"\udfff",
         "\uf900"-"\ufa6d",  "\ufa70"-"\ufad9",  "\ufb00"-"\ufb06",  "\ufb13"-"\ufb17",  "\ufb1d",  "\ufb1f"-"\ufb28",
         "\ufb2a"-"\ufb36",  "\ufb38"-"\ufb3c",  "\ufb3e",  "\ufb40"-"\ufb41",  "\ufb43"-"\ufb44",  "\ufb46"-"\ufbb1",
         "\ufbd3"-"\ufd3d",  "\ufd50"-"\ufd8f",  "\ufd92"-"\ufdc7",  "\ufdf0"-"\ufdfc",  "\ufe33"-"\ufe34",
         "\ufe4d"-"\ufe4f",  "\ufe69",  "\ufe70"-"\ufe74",  "\ufe76"-"\ufefc",  "\uff04",  "\uff21"-"\uff3a",
         "\uff3f",  "\uff41"-"\uff5a",  "\uff66"-"\uffbe",  "\uffc2"-"\uffc7",  "\uffca"-"\uffcf",  "\uffd2"-"\uffd7",
         "\uffda"-"\uffdc",  "\uffe0"-"\uffe1",  "\uffe5"-"\uffe6"  ]
        | <UNICODE_ESCAPE>
  >
|
  < #PART_LETTER: [
         "\u0000"-"\u0008",  "\u000e"-"\u001b",  "\u0024",  "\u0030"-"\u0039",  "\u0041"-"\u005a",  "\u005f",
         "\u0061"-"\u007a",  "\u007f"-"\u009f",  "\u00a2"-"\u00a5",  "\u00aa",  "\u00ad",  "\u00b5",  "\u00ba",
         "\u00c0"-"\u00d6",  "\u00d8"-"\u00f6",  "\u00f8"-"\u02c1",  "\u02c6"-"\u02d1",  "\u02e0"-"\u02e4",
         "\u02ec",  "\u02ee",  "\u0300"-"\u0374",  "\u0376"-"\u0377",  "\u037a"-"\u037d",  "\u037f",  "\u0386",
         "\u0388"-"\u038a",  "\u038c",  "\u038e"-"\u03a1",  "\u03a3"-"\u03f5",  "\u03f7"-"\u0481",  "\u0483"-"\u0487",
         "\u048a"-"\u052f",  "\u0531"-"\u0556",  "\u0559",  "\u0561"-"\u0587",  "\u058f",  "\u0591"-"\u05bd",
         "\u05bf",  "\u05c1"-"\u05c2",  "\u05c4"-"\u05c5",  "\u05c7",  "\u05d0"-"\u05ea",  "\u05f0"-"\u05f2",
         "\u0600"-"\u0605",  "\u060b",  "\u0610"-"\u061a",  "\u061c",  "\u0620"-"\u0669",  "\u066e"-"\u06d3",
         "\u06d5"-"\u06dd",  "\u06df"-"\u06e8",  "\u06ea"-"\u06fc",  "\u06ff",  "\u070f"-"\u074a",  "\u074d"-"\u07b1",
         "\u07c0"-"\u07f5",  "\u07fa",  "\u0800"-"\u082d",  "\u0840"-"\u085b",  "\u0860"-"\u086a",  "\u08a0"-"\u08b4",
         "\u08b6"-"\u08bd",  "\u08d4"-"\u0963",  "\u0966"-"\u096f",  "\u0971"-"\u0983",  "\u0985"-"\u098c",
         "\u098f"-"\u0990",  "\u0993"-"\u09a8",  "\u09aa"-"\u09b0",  "\u09b2",  "\u09b6"-"\u09b9",  "\u09bc"-"\u09c4",
         "\u09c7"-"\u09c8",  "\u09cb"-"\u09ce",  "\u09d7",  "\u09dc"-"\u09dd",  "\u09df"-"\u09e3",  "\u09e6"-"\u09f3",
         "\u09fb"-"\u09fc",  "\u0a01"-"\u0a03",  "\u0a05"-"\u0a0a",  "\u0a0f"-"\u0a10",  "\u0a13"-"\u0a28",
         "\u0a2a"-"\u0a30",  "\u0a32"-"\u0a33",  "\u0a35"-"\u0a36",  "\u0a38"-"\u0a39",  "\u0a3c",  "\u0a3e"-"\u0a42",
         "\u0a47"-"\u0a48",  "\u0a4b"-"\u0a4d",  "\u0a51",  "\u0a59"-"\u0a5c",  "\u0a5e",  "\u0a66"-"\u0a75",
         "\u0a81"-"\u0a83",  "\u0a85"-"\u0a8d",  "\u0a8f"-"\u0a91",  "\u0a93"-"\u0aa8",  "\u0aaa"-"\u0ab0",
         "\u0ab2"-"\u0ab3",  "\u0ab5"-"\u0ab9",  "\u0abc"-"\u0ac5",  "\u0ac7"-"\u0ac9",  "\u0acb"-"\u0acd",
         "\u0ad0",  "\u0ae0"-"\u0ae3",  "\u0ae6"-"\u0aef",  "\u0af1",  "\u0af9"-"\u0aff",  "\u0b01"-"\u0b03",
         "\u0b05"-"\u0b0c",  "\u0b0f"-"\u0b10",  "\u0b13"-"\u0b28",  "\u0b2a"-"\u0b30",  "\u0b32"-"\u0b33",
         "\u0b35"-"\u0b39",  "\u0b3c"-"\u0b44",  "\u0b47"-"\u0b48",  "\u0b4b"-"\u0b4d",  "\u0b56"-"\u0b57",
         "\u0b5c"-"\u0b5d",  "\u0b5f"-"\u0b63",  "\u0b66"-"\u0b6f",  "\u0b71",  "\u0b82"-"\u0b83",  "\u0b85"-"\u0b8a",
         "\u0b8e"-"\u0b90",  "\u0b92"-"\u0b95",  "\u0b99"-"\u0b9a",  "\u0b9c",  "\u0b9e"-"\u0b9f",  "\u0ba3"-"\u0ba4",
         "\u0ba8"-"\u0baa",  "\u0bae"-"\u0bb9",  "\u0bbe"-"\u0bc2",  "\u0bc6"-"\u0bc8",  "\u0bca"-"\u0bcd",
         "\u0bd0",  "\u0bd7",  "\u0be6"-"\u0bef",  "\u0bf9",  "\u0c00"-"\u0c03",  "\u0c05"-"\u0c0c",  "\u0c0e"-"\u0c10",
         "\u0c12"-"\u0c28",  "\u0c2a"-"\u0c39",  "\u0c3d"-"\u0c44",  "\u0c46"-"\u0c48",  "\u0c4a"-"\u0c4d",
         "\u0c55"-"\u0c56",  "\u0c58"-"\u0c5a",  "\u0c60"-"\u0c63",  "\u0c66"-"\u0c6f",  "\u0c80"-"\u0c83",
         "\u0c85"-"\u0c8c",  "\u0c8e"-"\u0c90",  "\u0c92"-"\u0ca8",  "\u0caa"-"\u0cb3",  "\u0cb5"-"\u0cb9",
         "\u0cbc"-"\u0cc4",  "\u0cc6"-"\u0cc8",  "\u0cca"-"\u0ccd",  "\u0cd5"-"\u0cd6",  "\u0cde",  "\u0ce0"-"\u0ce3",
         "\u0ce6"-"\u0cef",  "\u0cf1"-"\u0cf2",  "\u0d00"-"\u0d03",  "\u0d05"-"\u0d0c",  "\u0d0e"-"\u0d10",
         "\u0d12"-"\u0d44",  "\u0d46"-"\u0d48",  "\u0d4a"-"\u0d4e",  "\u0d54"-"\u0d57",  "\u0d5f"-"\u0d63",
         "\u0d66"-"\u0d6f",  "\u0d7a"-"\u0d7f",  "\u0d82"-"\u0d83",  "\u0d85"-"\u0d96",  "\u0d9a"-"\u0db1",
         "\u0db3"-"\u0dbb",  "\u0dbd",  "\u0dc0"-"\u0dc6",  "\u0dca",  "\u0dcf"-"\u0dd4",  "\u0dd6",  "\u0dd8"-"\u0ddf",
         "\u0de6"-"\u0def",  "\u0df2"-"\u0df3",  "\u0e01"-"\u0e3a",  "\u0e3f"-"\u0e4e",  "\u0e50"-"\u0e59",
         "\u0e81"-"\u0e82",  "\u0e84",  "\u0e87"-"\u0e88",  "\u0e8a",  "\u0e8d",  "\u0e94"-"\u0e97",  "\u0e99"-"\u0e9f",
         "\u0ea1"-"\u0ea3",  "\u0ea5",  "\u0ea7",  "\u0eaa"-"\u0eab",  "\u0ead"-"\u0eb9",  "\u0ebb"-"\u0ebd",
         "\u0ec0"-"\u0ec4",  "\u0ec6",  "\u0ec8"-"\u0ecd",  "\u0ed0"-"\u0ed9",  "\u0edc"-"\u0edf",  "\u0f00",
         "\u0f18"-"\u0f19",  "\u0f20"-"\u0f29",  "\u0f35",  "\u0f37",  "\u0f39",  "\u0f3e"-"\u0f47",  "\u0f49"-"\u0f6c",
         "\u0f71"-"\u0f84",  "\u0f86"-"\u0f97",  "\u0f99"-"\u0fbc",  "\u0fc6",  "\u1000"-"\u1049",  "\u1050"-"\u109d",
         "\u10a0"-"\u10c5",  "\u10c7",  "\u10cd",  "\u10d0"-"\u10fa",  "\u10fc"-"\u1248",  "\u124a"-"\u124d",
         "\u1250"-"\u1256",  "\u1258",  "\u125a"-"\u125d",  "\u1260"-"\u1288",  "\u128a"-"\u128d",  "\u1290"-"\u12b0",
         "\u12b2"-"\u12b5",  "\u12b8"-"\u12be",  "\u12c0",  "\u12c2"-"\u12c5",  "\u12c8"-"\u12d6",  "\u12d8"-"\u1310",
         "\u1312"-"\u1315",  "\u1318"-"\u135a",  "\u135d"-"\u135f",  "\u1380"-"\u138f",  "\u13a0"-"\u13f5",
         "\u13f8"-"\u13fd",  "\u1401"-"\u166c",  "\u166f"-"\u167f",  "\u1681"-"\u169a",  "\u16a0"-"\u16ea",
         "\u16ee"-"\u16f8",  "\u1700"-"\u170c",  "\u170e"-"\u1714",  "\u1720"-"\u1734",  "\u1740"-"\u1753",
         "\u1760"-"\u176c",  "\u176e"-"\u1770",  "\u1772"-"\u1773",  "\u1780"-"\u17d3",  "\u17d7",  "\u17db"-"\u17dd",
         "\u17e0"-"\u17e9",  "\u180b"-"\u180e",  "\u1810"-"\u1819",  "\u1820"-"\u1877",  "\u1880"-"\u18aa",
         "\u18b0"-"\u18f5",  "\u1900"-"\u191e",  "\u1920"-"\u192b",  "\u1930"-"\u193b",  "\u1946"-"\u196d",
         "\u1970"-"\u1974",  "\u1980"-"\u19ab",  "\u19b0"-"\u19c9",  "\u19d0"-"\u19d9",  "\u1a00"-"\u1a1b",
         "\u1a20"-"\u1a5e",  "\u1a60"-"\u1a7c",  "\u1a7f"-"\u1a89",  "\u1a90"-"\u1a99",  "\u1aa7",  "\u1ab0"-"\u1abd",
         "\u1b00"-"\u1b4b",  "\u1b50"-"\u1b59",  "\u1b6b"-"\u1b73",  "\u1b80"-"\u1bf3",  "\u1c00"-"\u1c37",
         "\u1c40"-"\u1c49",  "\u1c4d"-"\u1c7d",  "\u1c80"-"\u1c88",  "\u1cd0"-"\u1cd2",  "\u1cd4"-"\u1cf9",
         "\u1d00"-"\u1df9",  "\u1dfb"-"\u1f15",  "\u1f18"-"\u1f1d",  "\u1f20"-"\u1f45",  "\u1f48"-"\u1f4d",
         "\u1f50"-"\u1f57",  "\u1f59",  "\u1f5b",  "\u1f5d",  "\u1f5f"-"\u1f7d",  "\u1f80"-"\u1fb4",  "\u1fb6"-"\u1fbc",
         "\u1fbe",  "\u1fc2"-"\u1fc4",  "\u1fc6"-"\u1fcc",  "\u1fd0"-"\u1fd3",  "\u1fd6"-"\u1fdb",  "\u1fe0"-"\u1fec",
         "\u1ff2"-"\u1ff4",  "\u1ff6"-"\u1ffc",  "\u200b"-"\u200f",  "\u202a"-"\u202e",  "\u203f"-"\u2040",
         "\u2054",  "\u2060"-"\u2064",  "\u2066"-"\u206f",  "\u2071",  "\u207f",  "\u2090"-"\u209c",  "\u20a0"-"\u20bf",
         "\u20d0"-"\u20dc",  "\u20e1",  "\u20e5"-"\u20f0",  "\u2102",  "\u2107",  "\u210a"-"\u2113",  "\u2115",
         "\u2119"-"\u211d",  "\u2124",  "\u2126",  "\u2128",  "\u212a"-"\u212d",  "\u212f"-"\u2139",  "\u213c"-"\u213f",
         "\u2145"-"\u2149",  "\u214e",  "\u2160"-"\u2188",  "\u2c00"-"\u2c2e",  "\u2c30"-"\u2c5e",  "\u2c60"-"\u2ce4",
         "\u2ceb"-"\u2cf3",  "\u2d00"-"\u2d25",  "\u2d27",  "\u2d2d",  "\u2d30"-"\u2d67",  "\u2d6f",  "\u2d7f"-"\u2d96",
         "\u2da0"-"\u2da6",  "\u2da8"-"\u2dae",  "\u2db0"-"\u2db6",  "\u2db8"-"\u2dbe",  "\u2dc0"-"\u2dc6",
         "\u2dc8"-"\u2dce",  "\u2dd0"-"\u2dd6",  "\u2dd8"-"\u2dde",  "\u2de0"-"\u2dff",  "\u2e2f",  "\u3005"-"\u3007",
         "\u3021"-"\u302f",  "\u3031"-"\u3035",  "\u3038"-"\u303c",  "\u3041"-"\u3096",  "\u3099"-"\u309a",
         "\u309d"-"\u309f",  "\u30a1"-"\u30fa",  "\u30fc"-"\u30ff",  "\u3105"-"\u312e",  "\u3131"-"\u318e",
         "\u31a0"-"\u31ba",  "\u31f0"-"\u31ff",  "\u3400"-"\u4db5",  "\u4e00"-"\u9fea",  "\ua000"-"\ua48c",
         "\ua4d0"-"\ua4fd",  "\ua500"-"\ua60c",  "\ua610"-"\ua62b",  "\ua640"-"\ua66f",  "\ua674"-"\ua67d",
         "\ua67f"-"\ua6f1",  "\ua717"-"\ua71f",  "\ua722"-"\ua788",  "\ua78b"-"\ua7ae",  "\ua7b0"-"\ua7b7",
         "\ua7f7"-"\ua827",  "\ua838",  "\ua840"-"\ua873",  "\ua880"-"\ua8c5",  "\ua8d0"-"\ua8d9",  "\ua8e0"-"\ua8f7",
         "\ua8fb",  "\ua8fd",  "\ua900"-"\ua92d",  "\ua930"-"\ua953",  "\ua960"-"\ua97c",  "\ua980"-"\ua9c0",
         "\ua9cf"-"\ua9d9",  "\ua9e0"-"\ua9fe",  "\uaa00"-"\uaa36",  "\uaa40"-"\uaa4d",  "\uaa50"-"\uaa59",
         "\uaa60"-"\uaa76",  "\uaa7a"-"\uaac2",  "\uaadb"-"\uaadd",  "\uaae0"-"\uaaef",  "\uaaf2"-"\uaaf6",
         "\uab01"-"\uab06",  "\uab09"-"\uab0e",  "\uab11"-"\uab16",  "\uab20"-"\uab26",  "\uab28"-"\uab2e",
         "\uab30"-"\uab5a",  "\uab5c"-"\uab65",  "\uab70"-"\uabea",  "\uabec"-"\uabed",  "\uabf0"-"\uabf9",
         "\uac00"-"\ud7a3",  "\ud7b0"-"\ud7c6",  "\ud7cb"-"\ud7fb",  "\ud800"-"\udfff",  "\uf900"-"\ufa6d",
         "\ufa70"-"\ufad9",  "\ufb00"-"\ufb06",  "\ufb13"-"\ufb17",  "\ufb1d"-"\ufb28",  "\ufb2a"-"\ufb36",
         "\ufb38"-"\ufb3c",  "\ufb3e",  "\ufb40"-"\ufb41",  "\ufb43"-"\ufb44",  "\ufb46"-"\ufbb1",  "\ufbd3"-"\ufd3d",
         "\ufd50"-"\ufd8f",  "\ufd92"-"\ufdc7",  "\ufdf0"-"\ufdfc",  "\ufe00"-"\ufe0f",  "\ufe20"-"\ufe2f",
         "\ufe33"-"\ufe34",  "\ufe4d"-"\ufe4f",  "\ufe69",  "\ufe70"-"\ufe74",  "\ufe76"-"\ufefc",  "\ufeff",
         "\uff04",  "\uff10"-"\uff19",  "\uff21"-"\uff3a",  "\uff3f",  "\uff41"-"\uff5a",  "\uff66"-"\uffbe",
         "\uffc2"-"\uffc7",  "\uffca"-"\uffcf",  "\uffd2"-"\uffd7",  "\uffda"-"\uffdc",  "\uffe0"-"\uffe1",
         "\uffe5"-"\uffe6",  "\ufff9"-"\ufffb" ]
        | <UNICODE_ESCAPE>
  >
}


/* SEPARATORS */
/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.11
 * Twelve tokens, formed from ASCII characters, are the separators (punctuators).
 * <pre>{@code
 *     Separator:
 *     (one of)
 *     (   )   {   }   [   ]   ;   ,   .   ...   @   ::
 * }</pre
 */
TOKEN :
{
  < LPAREN: "(" >
| < RPAREN: ")" >
| < LBRACE: "{" >
| < RBRACE: "}" >
| < LBRACKET: "[" >
| < RBRACKET: "]" >
| < SEMICOLON: ";" >
| < COMMA: "," >
| < DOT: "." >
| < ELLIPSIS: "..." >
| < AT: "@" >
| < DOUBLECOLON: "::" >
}


/* OPERATORS */
/**
 * Note that {@code >}'s need special attention due to generics syntax, thus are listed separately.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.12
 * 38 tokens, formed from ASCII characters, are the operators.
 * <pre>{@code
 *     Separator:
 *     (one of)
 *     =   >   <   !   ~   ?   :   ->
 *     ==  >=  <=  !=  &&  ||  ++  --
 *     +   -   *   /   &   |   ^   %   <<   >>   >>>
 *     +=  -=  *=  /=  &=  |=  ^=  %=  <<=  >>=  >>>=
 * }</pre
 */
TOKEN :
{
  < ASSIGN: "=" >
// GT - see below
| < LT: "<" >
| < BANG: "!" >
| < TILDE: "~" >
| < HOOK: "?" >
| < COLON: ":" >
| < ARROW: "->" >

| < EQ: "==" >
| < GE: ">=" >
| < LE: "<=" >
| < NE: "!=" >
| < SC_AND: "&&" >
| < SC_OR: "||" >
| < INCR: "++" >
| < DECR: "--" >

| < PLUS: "+" >
| < MINUS: "-" >
| < STAR: "*" >
| < SLASH: "/" >
| < BIT_AND: "&" >
| < BIT_OR: "|" >
| < XOR: "^" >
| < REM: "%" >
| < LSHIFT: "<<" >
// RSIGNEDSHIFT - see below
// RUNSIGNEDSHIFT - see below

| < PLUSASSIGN: "+=" >
| < MINUSASSIGN: "-=" >
| < STARASSIGN: "*=" >
| < SLASHASSIGN: "/=" >
| < ANDASSIGN: "&=" >
| < ORASSIGN: "|=" >
| < XORASSIGN: "^=" >
| < REMASSIGN: "%=" >
| < LSHIFTASSIGN: "<<=" >
| < RSIGNEDSHIFTASSIGN: ">>=" >
| < RUNSIGNEDSHIFTASSIGN: ">>>=" >

}


/*
 * Note that {@code >}'s need special attention due to generics syntax, thus are listed separately.
 * TODO: Investigate (and comment) if the ordering matters, or if it's just that there's a code block associated with the token.
.*/
TOKEN :
{
  < RUNSIGNEDSHIFT: ">>>" >
  {
     matchedToken.kind = GT;
     matchedToken.realKind = RUNSIGNEDSHIFT;
     input_stream.backup(2);
  }
| < RSIGNEDSHIFT: ">>" >
  {
     matchedToken.kind = GT;
     matchedToken.realKind = RSIGNEDSHIFT;
     input_stream.backup(1);
  }
| < GT: ">" >
}

TOKEN: { <CTRL_Z: "\u001A" /** ctrl+z char **/> }


/*************************************************************************************************************
 * THE JAVA LANGUAGE GRAMMAR STARTS HERE                                                                     *
 *                                                                                                           *
 * https://javacc.github.io/javacc/documentation/grammar.html#bnf-production                                 *
 * <pre>{@code                                                                                               *
 * bnf_production ::= java_access_modifier java_return_type java_identifier "(" java_parameter_list ")" ":"  *
 *                    java_block                                                                             *
 *                    "{" expansion_choices "}"                                                              *
 * }</pre>                                                                                                   *
 *************************************************************************************************************/

/*
 * Program structuring syntax follows.
 */

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-7.html#jls-7.3
 * <pre>{@code
 *     CompilationUnit:
 *         OrdinaryCompilationUnit
 *         ModularCompilationUnit
 *     OrdinaryCompilationUnit:
 *         [PackageDeclaration] {ImportDeclaration} {TypeDeclaration}
 *     ModularCompilationUnit:
 *         {ImportDeclaration} ModuleDeclaration
 * }</pre>
 * Note that {@code TypeDeclaration} is defined in the JLS as a class or interface.
 * <ul>
 *   <li>A class is defined as a normal class or enum declaration.</li>
 *   <li>An interface declaration is defined as a normal interface or annotation declaration.</li>
 * </ul>
 * For that reason, within this grammar, we combine them.
 *
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-7.html#jls-TypeDeclaration
 * <pre>{@code
 *     TypeDeclaration:
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-ClassDeclaration
 * <pre>{@code
 *     TypeDeclaration:
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-InterfaceDeclaration
 * <pre>{@code
 *     InterfaceDeclaration:
 *         NormalInterfaceDeclaration
 *         AnnotationTypeDeclaration
 * }</pre>
 */
public CompilationUnit CompilationUnit():
{
    PackageDeclaration packageDeclaration = null;
    NodeList<ImportDeclaration> imports = emptyNodeList();
    ImportDeclaration in = null;
    NodeList<TypeDeclaration<?>> types = emptyNodeList();
    ModifierHolder modifier;
    TypeDeclaration<?> typeDeclaration = null;
    ModuleDeclaration module = null;
}
{
    try {
        ( LOOKAHEAD(2)";" )*
        [ LOOKAHEAD(PackageDeclaration()) packageDeclaration = PackageDeclaration() ]
        (
            in = ImportDeclaration() { imports = add(imports, in); }
         |
            (
                modifier = Modifiers()
                (
                    typeDeclaration = ClassOrInterfaceDeclaration(modifier) { types = add(types, typeDeclaration); }
                 |
                    typeDeclaration = RecordDeclaration(modifier)  { types = add(types, typeDeclaration); }
                 |
                    typeDeclaration = EnumDeclaration(modifier) { types = add(types, typeDeclaration); }
                 |
                    typeDeclaration = AnnotationTypeDeclaration(modifier) { types = add(types, typeDeclaration); }
                 |
                    module = ModuleDeclaration(modifier)
                 |
                    ";"
                )
            )
        )*
        (<EOF> | <CTRL_Z>)
        { return new CompilationUnit(range(token_source.getHomeToken(), token()), packageDeclaration, imports, types, module); }
    } catch (ParseException e) {
        recover(EOF, e);
        final CompilationUnit compilationUnit = new CompilationUnit(range(token_source.getHomeToken(), token()), null, new NodeList<ImportDeclaration>(), new NodeList<TypeDeclaration<?>>(), null);
        compilationUnit.setParsed(UNPARSABLE);
        return compilationUnit;
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-7.html#jls-7.4.1
 * <pre>{@code
 *     PackageDeclaration:
 *         {PackageModifier} package Identifier {. Identifier} ;
 *     PackageModifier:
 *         Annotation
 * }</pre>
 */
PackageDeclaration PackageDeclaration():
{
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
    Name name;
    JavaToken begin = INVALID;
	AnnotationExpr ann;
}
{
	( ann = Annotation() { annotations = add(annotations, ann); begin = orIfInvalid(begin, ann); } )*
    "package" {begin = orIfInvalid(begin, token());}  name = Name() ";"
    { return new PackageDeclaration(range(begin, token()), annotations, name); }
}


/**
 * Import declaration types are not differentiated in the class hierarchy.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-7.html#jls-7.5
 * <pre>{@code
 *     ImportDeclaration:
 *         SingleTypeImportDeclaration
 *         TypeImportOnDemandDeclaration
 *         SingleStaticImportDeclaration
 *         StaticImportOnDemandDeclaration
 * }</pre>
 * Note that the JLS terminology "On Demand" and JavaParser terminology "Asterisk" are equivalent
 * <pre>{@code
 *     SingleTypeImportDeclaration:
 *         import TypeName ;
 *     TypeImportOnDemandDeclaration:
 *         import PackageOrTypeName . * ;
 *     SingleStaticImportDeclaration:
 *         import static TypeName . Identifier ;
 *     StaticImportOnDemandDeclaration:
 *         import static TypeName . * ;
 * }</pre>
 */
ImportDeclaration ImportDeclaration():
{
    Name name;
    boolean isStatic = false;
    boolean isAsterisk = false;
    JavaToken begin;
}
{
    "import" {begin = token();}
    [ "static" { isStatic = true; } ]
    name = Name()
    [ "." "*" { isAsterisk = true; } ] ";"
    { return new ImportDeclaration(range(begin, token()), name, isStatic, isAsterisk); }
}

/*
 * Modifiers. We match all modifiers in a single rule to reduce the chances of
 * syntax errors for simple modifier mistakes. It will also enable us to give
 * better error messages.
 * Class Modifiers: https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.1
 * <pre>{@code
 *     ClassModifier:
 *         (one of)
 *         Annotation public protected private
 *         abstract static final strictfp
 * }</pre>
 * Field Modifiers: https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.3.1
 * <pre>{@code
 *     FieldModifier:
 *         (one of)
 *         Annotation public protected private
 *         static final transient volatile
 * }</pre>
 * Method Modifiers: https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4.3
 * <pre>{@code
 *     MethodModifier:
 *         (one of)
 *         Annotation public protected private
 *         abstract static final synchronized native strictfp
 * }</pre>
 * Constructor Modifiers: https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.8.3
 * <pre>{@code
 *     ConstructorModifier:
 *         (one of)
 *         Annotation public protected private
 * }</pre>
 */
ModifierHolder Modifiers():
{
    JavaToken begin = INVALID;
    NodeList<Modifier> modifiers = new NodeList<Modifier>();
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
    AnnotationExpr ann;
}
{
    (
        LOOKAHEAD(2)
        (
            "public" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.PUBLIC)); begin = orIfInvalid(begin, token()); }
         |
            "static" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.STATIC)); begin = orIfInvalid(begin, token()); }
         |
            "protected" {  add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.PROTECTED)); begin = orIfInvalid(begin, token()); }
         |
            "private" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.PRIVATE)); begin = orIfInvalid(begin, token()); }
         |
            "final" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.FINAL)); begin = orIfInvalid(begin, token()); }
         |
            "abstract" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.ABSTRACT)); begin = orIfInvalid(begin, token()); }
         |
            "synchronized" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.SYNCHRONIZED)); begin = orIfInvalid(begin, token()); }
         |
            "native" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.NATIVE)); begin = orIfInvalid(begin, token()); }
         |
            "transient" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.TRANSIENT)); begin = orIfInvalid(begin, token()); }
         |
            "volatile" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.VOLATILE)); begin = orIfInvalid(begin, token()); }
         |
            "strictfp" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.STRICTFP));  begin = orIfInvalid(begin, token()); }
         |
            "transitive" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.TRANSITIVE));  begin = orIfInvalid(begin, token()); }
         |
            "default" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.DEFAULT));  begin = orIfInvalid(begin, token()); }
         |
            "sealed" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.SEALED));  begin = orIfInvalid(begin, token()); }
         |
            "non-sealed" { add(modifiers, new Modifier(tokenRange(), Modifier.Keyword.NON_SEALED));  begin = orIfInvalid(begin, token()); }
         |
             ann = Annotation() { annotations = add(annotations, ann); begin = orIfInvalid(begin, ann); }
        )
    )*
    {
        return new ModifierHolder(begin, modifiers, annotations);
    }
}

/*
 * Declaration syntax follows.
 */

/**
 * <strong>
 *   Note that this grammar is deliberately permissive. As a result, this grammar allows any combinations of
 *   class/interface and extends/implements (e.g. an interface implementing an interface).
 * </strong>
 * Validations of these combinations are handled at the same time as validating language features used.
 * Also note that JavaParser handles the hierarchy/specialisations of classes, interfaces, and enums differently to the JLS.
 * <ul>
 *   <li>This {@code ClassOrInterfaceDeclaration} refers to the {@code NormalClassDeclaration} and {@code NormalInterfaceDeclaration},</li>
 *   <li>while {@code AnnotationTypeDeclaration}, {@code EnumDeclaration}, and {@code RecordDeclaration} are handled separately within this grammar.</li>
 * </ul>
 * https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.1
 * <pre>{@code
 *     ClassDeclaration:
 *         NormalClassDeclaration
 *         EnumDeclaration
 *         RecordDeclaration
 *     NormalClassDeclaration:
 *         {ClassModifier} class TypeIdentifier [TypeParameters] [Superclass] [Superinterfaces] ClassBody
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.1
 * <pre>{@code
 *     InterfaceDeclaration:
 *         NormalInterfaceDeclaration
 *         AnnotationTypeDeclaration
 *     NormalInterfaceDeclaration:
 *         {InterfaceModifier} interface TypeIdentifier [TypeParameters] [ExtendsInterfaces] InterfaceBody
 * }</pre>
 */
ClassOrInterfaceDeclaration ClassOrInterfaceDeclaration(ModifierHolder modifier):
{
    boolean isInterface;
    SimpleName name;
    RangedList<TypeParameter> typePar = new RangedList<TypeParameter>(emptyNodeList());
    NodeList<ClassOrInterfaceType> extList = emptyNodeList();
    NodeList<ClassOrInterfaceType> impList = emptyNodeList();
    NodeList<ClassOrInterfaceType> permitsList = emptyNodeList();
    NodeList<BodyDeclaration<?>> members = emptyNodeList();
    JavaToken begin = modifier.begin;
}
{
    (
        "class" { isInterface = false; }
     |
        "interface" { isInterface = true; }
    ) { begin = orIfInvalid(begin, token()); }

    name = SimpleName()
    [ typePar = TypeParameters() ]
    [ extList = ExtendsList() ]
    [ impList = ImplementsList() ]
    [ permitsList = PermitsList() ]

    members = ClassOrInterfaceBody()

    { return new ClassOrInterfaceDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, isInterface, name, typePar.list, extList, impList, permitsList, members); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10
 * <pre>{@code
 *     RecordDeclaration:
 *        {ClassModifier} `record` TypeIdentifier [TypeParameters]
 *          RecordHeader [ClassImplements] RecordBody
 *
 *      RecordHeader:
 *       `(` [RecordComponentList] `)`
 *
 *      RecordComponentList:
 *       RecordComponent { `,` RecordComponent}
 *
 *      RecordComponent:
 *       {RecordComponentModifier} UnannType Identifier
 *       VariableArityRecordComponent
 *
 *      VariableArityRecordComponent:
 *       {RecordComponentModifier} UnannType {Annotation} `...` Identifier
 *
 *      RecordComponentModifier:
 *       Annotation
 *
 *      RecordBody:
 *        `{` {RecordBodyDeclaration} `}`
 *
 *      RecordBodyDeclaration:
 *        ClassBodyDeclaration
 *        CompactConstructorDeclaration
 *
 *      CompactConstructorDeclaration:
 *        {ConstructorModifier} SimpleTypeName ConstructorBody
 * }</pre>
 */

RecordDeclaration RecordDeclaration(ModifierHolder modifier):
{
    SimpleName name;
    Pair<NodeList<Parameter>, ReceiverParameter> parameters;
    RangedList<TypeParameter> typePar = new RangedList<TypeParameter>(emptyNodeList());
    NodeList<ClassOrInterfaceType> impList = emptyNodeList();
    NodeList<BodyDeclaration<?>> members = emptyNodeList();
    JavaToken begin = modifier.begin;
}
{
    // Note that BlockStatement and ClassOrInterfaceBodyDeclaration have extended lookaheads that should be maintained
    // Note that `record` is a "restricted identifier", not a keyword - JLS 3.9 has more detail.
    "record" { begin = orIfInvalid(begin, token()); }
    name = SimpleName()
    [ typePar = TypeParameters() ]
    parameters = Parameters()
    [ impList = ImplementsList() ]
    members = RecordBody()

    { return new RecordDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, name, parameters.a, typePar.list, impList, members, parameters.b); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.4
 * <pre>{@code
 *     Superclass:
 *         extends ClassType
 * }</pre>
 * For convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.3
 * <pre>{@code
 *     ClassOrInterfaceType:
 *         ClassType
 *         InterfaceType
 *     ClassType:
 *         {Annotation} TypeIdentifier [TypeArguments]
 *         PackageName . {Annotation} TypeIdentifier [TypeArguments]
 *         ClassOrInterfaceType . {Annotation} TypeIdentifier [TypeArguments]
 *     InterfaceType:
 *         ClassType
 * }</pre>
 */
NodeList<ClassOrInterfaceType> ExtendsList():
{
    boolean extendsMoreThanOne = false;
    NodeList<ClassOrInterfaceType> ret = new NodeList<ClassOrInterfaceType>();
    ClassOrInterfaceType cit;
}
{
    "extends" cit = AnnotatedClassOrInterfaceType() { ret.add(cit); }
    ( "," cit = AnnotatedClassOrInterfaceType() { ret.add(cit); extendsMoreThanOne = true; } )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.5
 * <pre>{@code
 *     Superinterfaces:
 *         implements InterfaceTypeList
 *     InterfaceTypeList:
 *         InterfaceType {, InterfaceType}
 * }</pre>
 * For convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.3
 * <pre>{@code
 *     ClassOrInterfaceType:
 *         ClassType
 *         InterfaceType
 *     ClassType:
 *         {Annotation} TypeIdentifier [TypeArguments]
 *         PackageName . {Annotation} TypeIdentifier [TypeArguments]
 *         ClassOrInterfaceType . {Annotation} TypeIdentifier [TypeArguments]
 *     InterfaceType:
 *         ClassType
 * }</pre>
 */
NodeList<ClassOrInterfaceType> ImplementsList():
{
    NodeList<ClassOrInterfaceType> ret = new NodeList<ClassOrInterfaceType>();
    ClassOrInterfaceType cit;
}
{
    "implements" cit = AnnotatedClassOrInterfaceType() { ret.add(cit); }
    ( "," cit = AnnotatedClassOrInterfaceType() { ret.add(cit); } )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.5
 * <pre>{@code
 *     ClassPermits:
 *         permits TypeName {, TypeName}
 * }</pre>
 * For convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.3
 * <pre>{@code
 *     ClassOrInterfaceType:
 *         ClassType
 *         InterfaceType
 *     ClassType:
 *         {Annotation} TypeIdentifier [TypeArguments]
 *         PackageName . {Annotation} TypeIdentifier [TypeArguments]
 *         ClassOrInterfaceType . {Annotation} TypeIdentifier [TypeArguments]
 *     InterfaceType:
 *         ClassType
 * }</pre>
 */
NodeList<ClassOrInterfaceType> PermitsList():
{
    NodeList<ClassOrInterfaceType> ret = new NodeList<ClassOrInterfaceType>();
    ClassOrInterfaceType cit;
}
{
    "permits" cit = AnnotatedClassOrInterfaceType() { ret.add(cit); }
    ( "," cit = AnnotatedClassOrInterfaceType() { ret.add(cit); } )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.9
 * <pre>{@code
 *     EnumDeclaration:
 *         {ClassModifier} enum TypeIdentifier [Superinterfaces] EnumBody
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.9.1
 * <pre>{@code
 *     EnumBody:
 *         { [EnumConstantList] [,] [EnumBodyDeclarations] }
 *     EnumConstantList:
 *         EnumConstant {, EnumConstant}
 *     EnumConstant:
 *         {EnumConstantModifier} Identifier [( [ArgumentList] )] [ClassBody]
 *     EnumConstantModifier:
 *         Annotation
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.9.2
 * <pre>{@code
 *     EnumBodyDeclarations:
 *         ; {ClassBodyDeclaration}
 * }</pre>
 */
EnumDeclaration EnumDeclaration(ModifierHolder modifier):
{
    SimpleName name;
    NodeList<ClassOrInterfaceType> impList = emptyNodeList();
    EnumConstantDeclaration entry;
    NodeList<EnumConstantDeclaration> entries = emptyNodeList();
    BodyDeclaration<?> member;
    NodeList<BodyDeclaration<?>> members = emptyNodeList();
    JavaToken begin = modifier.begin;
}
{
    "enum" { begin = orIfInvalid(begin, token()); }
    name = SimpleName()
    [ impList = ImplementsList() ]
    "{"
    [
        entry = EnumConstantDeclaration() { entries.add(entry); }
        ( LOOKAHEAD(2) "," entry = EnumConstantDeclaration() { entries.add(entry); } )*
    ]
    [ "," ]
    [
        ";"
        (
            member = ClassOrInterfaceBodyDeclaration() { members = add(members, member); }
         |
            ";"
        )*
    ]
    "}"

    { return new EnumDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, name, impList, entries, members); }
}


/**
 * Note that the {@code EnumConstantModifier} is defined as an {@code Annotation}.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.9.1
 * <pre>{@code
 *     EnumConstant:
 *         {EnumConstantModifier} Identifier [( [ArgumentList] )] [ClassBody]
 *     EnumConstantModifier:
 *         Annotation
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.9.2
 * <pre>{@code
 *     EnumBodyDeclarations:
 *         ; {ClassBodyDeclaration}
 * }</pre>
 */
EnumConstantDeclaration EnumConstantDeclaration():
{
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
    AnnotationExpr ann;
    SimpleName name;
    NodeList<Expression> args = emptyNodeList();
    NodeList<BodyDeclaration<?>> classBody = emptyNodeList();
    JavaToken begin = INVALID;
}
{
    ( ann = Annotation() { annotations = add(annotations, ann); begin = orIfInvalid(begin, ann); } )*
    name = SimpleName() { begin = orIfInvalid(begin, token()); }
    [ args = Arguments() ]
    [ classBody = ClassOrInterfaceBody() ]

    {
        return new EnumConstantDeclaration(range(begin, token()), annotations, name, args, classBody);
    }
}

/**
 * If the list inside the returned RangedList is null, there are no brackets.
 * If it is empty, there are brackets, but nothing is in them <>.
 * The normal case is that it contains TypeParameters, like <A, B, C>.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.2
 * <pre>{@code
 *     TypeParameters:
 *         < TypeParameterList >
 *     TypeParameterList:
 *         TypeParameter {, TypeParameter}
 * }</pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.4
 * <pre>{@code
 *     TypeParameter:
 *         {TypeParameterModifier} TypeIdentifier [TypeBound]
 *     TypeParameterModifier:
 *         Annotation
 *     TypeBound:
 *         extends TypeVariable
 *         extends ClassOrInterfaceType {AdditionalBound}
 *     AdditionalBound:
 *         & InterfaceType
 * }</pre>
 */
RangedList<TypeParameter> TypeParameters():
{
    RangedList<TypeParameter> ret = new RangedList<TypeParameter>(new NodeList<TypeParameter>());
    TypeParameter tp;
    NodeList<AnnotationExpr> annotations;
}
{
    "<" { ret.beginAt(token()); }
    annotations = Annotations()
    tp = TypeParameter(annotations) { ret.add(tp); annotations = null; }
    (
        ","
        annotations = Annotations()
        tp = TypeParameter(annotations) { ret.add(tp); annotations = null; }
    )*
    ">" { ret.endAt(token()); }
    { return ret; }
}

/**
 * Note that the {@code TypeParameterModifier} is defined as an {@code Annotation}.
 * Note that the annotations are passed as a parameter to this grammar entry.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.4
 * <pre>{@code
 *     TypeParameter:
 *         {TypeParameterModifier} TypeIdentifier [TypeBound]
 *     TypeParameterModifier:
 *         Annotation
 *     TypeBound:
 *         extends TypeVariable
 *         extends ClassOrInterfaceType {AdditionalBound}
 *     AdditionalBound:
 *         & InterfaceType
 * }</pre>
 */
TypeParameter TypeParameter(NodeList<AnnotationExpr> annotations):
{
    SimpleName name;
    NodeList<ClassOrInterfaceType> typeBound  = emptyNodeList();
    JavaToken begin;
}
{
    // Annotations are passed as a parameter to this grammar entry.
    name = SimpleName() { begin=token(); }
    [ typeBound = TypeBound() ]
    { return new TypeParameter(range(begin, token()), name, typeBound, annotations); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.4
 * <pre>{@code
 *     TypeBound:
 *         extends TypeVariable
 *         extends ClassOrInterfaceType {AdditionalBound}
 *     AdditionalBound:
 *         & InterfaceType
 * }</pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.3
 * <pre>{@code
 *     ClassOrInterfaceType:
 *         ClassType
 *         InterfaceType
 *     ClassType:
 *         {Annotation} TypeIdentifier [TypeArguments]
 *         PackageName . {Annotation} TypeIdentifier [TypeArguments]
 *         ClassOrInterfaceType . {Annotation} TypeIdentifier [TypeArguments]
 *     InterfaceType:
 *         ClassType
 *     TypeVariable:
 *         {Annotation} TypeIdentifier
 * }</pre>
 */
NodeList<ClassOrInterfaceType> TypeBound():
{
    NodeList<ClassOrInterfaceType> ret = emptyNodeList();
    ClassOrInterfaceType cit;
}
{
    "extends" cit = AnnotatedClassOrInterfaceType() { ret.add(cit); }
    (
        "&"
        cit = AnnotatedClassOrInterfaceType() { ret.add(cit); }
    )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.6
 * <pre>{@code
 *     ClassBody:
 *         { {ClassBodyDeclaration} }
 *     ClassBodyDeclaration:
 *         ClassMemberDeclaration
 *         InstanceInitializer
 *         StaticInitializer
 *         ConstructorDeclaration
 *     ClassMemberDeclaration:
 *         FieldDeclaration
 *         MethodDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.1.4
 * <pre>{@code
 *     InterfaceBody:
 *         { {InterfaceMemberDeclaration} }
 *     InterfaceMemberDeclaration:
 *         ConstantDeclaration
 *         InterfaceMethodDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 */
NodeList<BodyDeclaration<?>> ClassOrInterfaceBody():
{
    NodeList<BodyDeclaration<?>> ret = emptyNodeList();
    BodyDeclaration member;
}
{
    "{"
    (
        member = ClassOrInterfaceBodyDeclaration() { ret.add(member); }
     |
        ";"
    )*
    "}"
    { return ret; }
}

/**
 * Everything that can go into a "regular" class or interface declaration,
 * can also (usually) be included within a record.
 * Exceptions to this are handled within the validators.
 *
 * Note: This is HEAVILY based on ClassOrInterfaceBody() and ClassOrInterfaceBodyDeclaration(),
 * and there may be some scope/potential to optimise this and merge some parts together.
 *
 * https://docs.oracle.com/javase/specs/jls/se16/html/jls-8.html#jls-8.10.2
 */
NodeList<BodyDeclaration<?>> RecordBody():
{
    NodeList<BodyDeclaration<?>> ret = emptyNodeList();
    BodyDeclaration member;
}
{
    "{"
    (
        member = RecordBodyDeclaration() { ret.add(member); }
     |
        ";"
    )*
    "}"
    { return ret; }
}


/**
 * Almost identical to ClassOrInterfaceBodyDeclaration, but:
 * - with a CompactConstructorDeclaration
 * As a result, there may be some scope/potential to optimize this and merge some parts together.
 *
 * https://docs.oracle.com/javase/specs/jls/se16/html/jls-8.html#jls-8.10.2
 * <pre>{@code
 *     RecordBody:
 *         { {RecordBodyDeclaration} }
 *     RecordBodyDeclaration:
 *         ClassBodyDeclaration
 *         CompactConstructorDeclaration
 * }</pre>
 *
 * For convenience:
 * https://docs.oracle.com/javase/specs/jls/se16/html/jls-8.html#jls-8.10.2
 * <pre>{@code
 *     ClassBodyDeclaration:
 *         ClassMemberDeclaration
 *         InstanceInitializer
 *         StaticInitializer
 *         ConstructorDeclaration
 *     ClassMemberDeclaration:
 *         FieldDeclaration
 *         MethodDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 */
BodyDeclaration<?> RecordBodyDeclaration():
{
    ModifierHolder modifiers;
    BodyDeclaration<?> ret;
}
{
    (
        LOOKAHEAD(2)
        ret = InitializerDeclaration()
     |
        // Just get all the modifiers out of the way. If you want to do
        // more checks, pass the modifiers down to the member
        modifiers = Modifiers()
        (
            ret = ClassOrInterfaceDeclaration(modifiers)
         |
            LOOKAHEAD("enum")
            ret = EnumDeclaration(modifiers)
         |
            LOOKAHEAD("record")
            ret = RecordDeclaration(modifiers)
         |
            LOOKAHEAD("@" "interface")
            ret = AnnotationTypeDeclaration(modifiers)
         |
            // Compact Record Constructors
            LOOKAHEAD( [ TypeParameters() ] Identifier() "{" )
            ret = CompactConstructorDeclaration(modifiers)
         |
            // "Normal" Record Constructors
            LOOKAHEAD( [ TypeParameters() ] Identifier() "(" )
            ret = ConstructorDeclaration(modifiers)
         |
            LOOKAHEAD( Type() Identifier() ( ArrayBracketPair() )* ( "," | "=" | ";" ) )
            ret = FieldDeclaration(modifiers)
         |
            ret = MethodDeclaration(modifiers)
        )
    )
    { return ret; }
}


/**
 * https://openjdk.java.net/jeps/359#Grammar
 * <pre>{@code
 *     CompactConstructorDeclaration:
 *       {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName
 *         [Throws] ConstructorBody
 * }</pre>
 */
CompactConstructorDeclaration CompactConstructorDeclaration(ModifierHolder modifier):
{
    RangedList<TypeParameter> typeParameters = new RangedList<TypeParameter>(emptyNodeList());
    SimpleName name;
    Pair<NodeList<Parameter>, ReceiverParameter> parameters = new Pair<NodeList<Parameter>, ReceiverParameter>(emptyNodeList(), null);
    NodeList<ReferenceType> throws_ = emptyNodeList();
    ExplicitConstructorInvocationStmt exConsInv = null;
    NodeList<Statement> stmts = emptyNodeList();
    JavaToken begin = modifier.begin;
    JavaToken blockBegin = INVALID;
    ReferenceType throwType;
}
{
    [ typeParameters = TypeParameters() { begin = orIfInvalid(begin, typeParameters.range.getBegin()); } ]
    // Modifiers matched in the caller
    name = SimpleName() { begin = orIfInvalid(begin, typeParameters.range.getBegin()); begin = orIfInvalid(begin, token()); }
    //parameters = Parameters()
    [
        "throws"
        throwType = AnnotatedReferenceType() { throws_ = add(throws_, throwType); }
        ("," throwType = AnnotatedReferenceType() { throws_ = add(throws_, throwType); })*
    ]
    "{" { blockBegin=token(); }
    [
        LOOKAHEAD(ExplicitConstructorInvocation())
        exConsInv = ExplicitConstructorInvocation()
    ]
    stmts = Statements()
    "}"

    {
    if (exConsInv != null) {
        stmts = prepend(stmts, exConsInv);
    }
    return new CompactConstructorDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, typeParameters.list, name, throws_, new BlockStmt(range(blockBegin, token()), stmts));
}
}

/**
 * Note that {@code ClassDeclaration} and {@code InterfaceDeclaration} (in the JLS) include enum and annotation declarations.
 * Note that {@code InstanceInitializer} and {@code StaticInitializer} are treated as an {@code Initializer} with an
 * optional {@code static} modifier in JavaParser.
 * TODO: Investigate the {@code ClassBodyDeclaration} parts existing within an interface - is permitting this a potential problem?
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1.6
 * <pre>{@code
 *     ClassBody:
 *         { {ClassBodyDeclaration} }
 *     ClassBodyDeclaration:
 *         ClassMemberDeclaration
 *         InstanceInitializer
 *         StaticInitializer
 *         ConstructorDeclaration
 *     ClassMemberDeclaration:
 *         FieldDeclaration
 *         MethodDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.1.4
 * <pre>{@code
 *     InterfaceBody:
 *         { {InterfaceMemberDeclaration} }
 *     InterfaceMemberDeclaration:
 *         ConstantDeclaration
 *         InterfaceMethodDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }</pre>
 */
BodyDeclaration<?> ClassOrInterfaceBodyDeclaration():
{
    ModifierHolder modifiers;
    BodyDeclaration<?> ret;
}
{
    (
        LOOKAHEAD(2)
        ret = InitializerDeclaration()
     |
        // Just get all the modifiers out of the way. If you want to do
        // more checks, pass the modifiers down to the member
        modifiers = Modifiers()
        (
            ret = ClassOrInterfaceDeclaration(modifiers)
         |
            LOOKAHEAD("record" SimpleName() [ TypeParameters() ] Parameters() )
            ret = RecordDeclaration(modifiers)
         |
            LOOKAHEAD("enum")
            ret = EnumDeclaration(modifiers)
         |
            LOOKAHEAD("@" "interface")
            ret = AnnotationTypeDeclaration(modifiers)
         |
            LOOKAHEAD( [ TypeParameters() ] Identifier() "(" )
            ret = ConstructorDeclaration(modifiers)
         |
            LOOKAHEAD( Type() Identifier() ( ArrayBracketPair() )* ( "," | "=" | ";" ) )
            ret = FieldDeclaration(modifiers)
         |
            ret = MethodDeclaration(modifiers)
        )
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.3
 * <pre>{@code
 *     FieldDeclaration:
 *         {FieldModifier} UnannType VariableDeclaratorList ;
 *     VariableDeclaratorList:
 *         VariableDeclarator {, VariableDeclarator}
 *     VariableDeclarator:
 *         VariableDeclaratorId [= VariableInitializer]
 *     VariableDeclaratorId:
 *         Identifier [Dims]
 *     VariableInitializer:
 *         Expression
 *         ArrayInitializer
 * }</pre>
 */
FieldDeclaration FieldDeclaration(ModifierHolder modifier):
{
    Type partialType;
    NodeList<VariableDeclarator> variables = new NodeList<VariableDeclarator>();
    VariableDeclarator val;
}
{
    // Modifiers are already matched in the caller
    partialType = Type(emptyNodeList())
    val = VariableDeclarator(partialType) { variables.add(val); }
    (
        ","
        val = VariableDeclarator(partialType) { variables.add(val); }
    )*
    ";"

    {
        JavaToken begin = orIfInvalid(modifier.begin, partialType);
        return new FieldDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, variables);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.3
 * <pre>{@code
 *     VariableDeclarator:
 *         VariableDeclaratorId [= VariableInitializer]
 *     VariableDeclaratorId:
 *         Identifier [Dims]
 *     VariableInitializer:
 *         Expression
 *         ArrayInitializer
 * }</pre>
 */
VariableDeclarator VariableDeclarator(Type partialType):
{
    Pair<SimpleName, List<ArrayBracketPair>> id;
    Expression init = null;
}
{
    id = VariableDeclaratorId()
    [ "=" init = VariableInitializer() ]
    { return new VariableDeclarator(range(id.a, token()), juggleArrayType(partialType, id.b), id.a, init); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.3
 * <pre>{@code
 *     VariableDeclaratorId:
 *         Identifier [Dims]
 * }</pre>
 * For convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-Identifier
 * <pre>{@code
 *     Identifier:
 *         IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
 *     IdentifierChars:
 *         JavaLetter {JavaLetterOrDigit}
 *     JavaLetter:
 *         any Unicode character that is a "Java letter"
 *     JavaLetterOrDigit:
 *         any Unicode character that is a "Java letter-or-digit"
 * }</pre>
 * For convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-Dims
 * <pre>{@code
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }</pre>
 */
Pair<SimpleName, List<ArrayBracketPair>> VariableDeclaratorId():
{
    SimpleName name;
    JavaToken begin;
    ArrayBracketPair arrayBracketPair;
    List<ArrayBracketPair> arrayBracketPairs = new ArrayList<ArrayBracketPair>(0);
}
{
    name = SimpleName() { begin=token();}
    (
        arrayBracketPair = ArrayBracketPair(Origin.NAME) { arrayBracketPairs=add(arrayBracketPairs, arrayBracketPair); }
    )*

    {
        if(storeTokens) {
            name.setTokenRange(name.getTokenRange().get().withEnd(token()));
        }
        return new Pair(name, arrayBracketPairs);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.3
 * <pre>{@code
 *     VariableInitializer:
 *         Expression
 *         ArrayInitializer
 * }</pre>
 */
Expression VariableInitializer():
{
    Expression ret;
}
{
    (
        ret = ArrayInitializer()
     |
        ret = Expression()
    )
    { return ret; }
}

/**
 * An array initializer is written as a comma-separated list of expressions, enclosed by braces { and }.
 * A trailing comma may appear after the last expression in an array initializer and is ignored.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-10.html#jls-10.6
 * <pre>{@code
 *     ArrayInitializer:
 *         { [VariableInitializerList] [,] }
 *     VariableInitializerList:
 *         VariableInitializer {, VariableInitializer}
 * }</pre>
 * <pre>{@code
 *     VariableInitializer:
 *         Expression
 *         ArrayInitializer
 * }</pre>
 */
ArrayInitializerExpr ArrayInitializer():
{
    NodeList<Expression> values = emptyNodeList();
    Expression val;
    JavaToken begin;
}
{
    "{" { begin=token(); }
    [
        val = VariableInitializer() { values = add(values, val); }
        ( LOOKAHEAD(2) "," val = VariableInitializer() { values = add(values, val); } )*
    ]
    [ "," ]
    "}"

    {
        return new ArrayInitializerExpr(range(begin, token()), values);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4
 * <pre>{@code
 *     MethodDeclaration:
 *         {MethodModifier} MethodHeader MethodBody
 *     MethodHeader:
 *         Result MethodDeclarator [Throws]
 *         TypeParameters {Annotation} Result MethodDeclarator [Throws]
 *     MethodDeclarator:
 *         Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]
 *     ReceiverParameter:
 *         {Annotation} UnannType [Identifier .] this
 * }</pre>
 */
MethodDeclaration MethodDeclaration(ModifierHolder modifier):
{
    RangedList<TypeParameter> typeParameters = new RangedList<TypeParameter>(emptyNodeList());
    Type type;
    SimpleName name;
    Pair<NodeList<Parameter>, ReceiverParameter> parameters = new Pair<NodeList<Parameter>, ReceiverParameter>(emptyNodeList(), null);
    ArrayBracketPair arrayBracketPair;
    List<ArrayBracketPair> arrayBracketPairs = new ArrayList<ArrayBracketPair>(0);
    NodeList<ReferenceType> throws_ = emptyNodeList();
    BlockStmt body = null;
    NodeList<AnnotationExpr> annotations;
    JavaToken begin = modifier.begin;
    ReferenceType throwType;
}
{
    // Modifiers already matched in the caller!
    [ typeParameters = TypeParameters() { begin = orIfInvalid(begin, typeParameters.range.getBegin()); } ]
    annotations = Annotations() { modifier.annotations.addAll(annotations); begin = orIfInvalid(begin, nodeListBegin(annotations)); }
    type = ResultType(emptyNodeList()) { begin = orIfInvalid(begin, type); }
    name = SimpleName()
    parameters = Parameters()
    ( arrayBracketPair = ArrayBracketPair(Origin.NAME) { arrayBracketPairs=add(arrayBracketPairs, arrayBracketPair); } )*
    [
        "throws"
        throwType = AnnotatedReferenceType() { throws_ = add(throws_, throwType); }
        ("," throwType = AnnotatedReferenceType() { throws_ = add(throws_, throwType); } )*
    ]
    (
        body = Block()
     |
        ";"
    )

    {
        type = juggleArrayType(type, arrayBracketPairs);
        return new MethodDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, typeParameters.list, type, name, parameters.a, throws_, body, parameters.b);
    }
}

// TODO: JLS Has type and unannotated type, while JavaParser has type and annotated type.
ReferenceType AnnotatedReferenceType():
{
    NodeList<AnnotationExpr> annotations;
    ReferenceType type;
}
{
    annotations = Annotations()
    type = ReferenceType(annotations)
    { return type; }
}

// TODO: JLS Has type and unannotated type, while JavaParser has type and annotated type.
Type AnnotatedType():
{
    NodeList<AnnotationExpr> annotations;
    Type type;
}
{
    annotations = Annotations()
    type = Type(annotations)
    { return type; }
}

/**
 * The JLS refers to parameters as {@code FormalParameter}.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4.1
 * <pre>{@code
 *     FormalParameterList:
 *         FormalParameter {, FormalParameter}
 *     FormalParameter:
 *         {VariableModifier} UnannType VariableDeclaratorId
 *         VariableArityParameter
 *     VariableArityParameter:
 *         {VariableModifier} UnannType {Annotation} ... Identifier
 *     VariableModifier:
 *         Annotation
 *         final
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-ReceiverParameter
 * <pre>{@code
 *     MethodDeclarator:
 *         Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]
 *     ReceiverParameter:
 *         {Annotation} UnannType [Identifier .] this
 * }</pre>
 */
Pair<NodeList<Parameter>, ReceiverParameter> Parameters():
{
    NodeList<Parameter> ret = emptyNodeList();
    Parameter par;
    ReceiverParameter rp = null;
}
{
    "("
    [
        (
            LOOKAHEAD(ReceiverParameter())
            rp = ReceiverParameter()
         |
            par = Parameter() { ret = add(ret, par); }
        )
        ( "," par = Parameter() { ret = add(ret, par); } )*
    ]
    ")"
    { return new Pair(ret, rp); }
}

/**
 * Note: Similar, but not identical to, {@code FormalParameter}.
 * Note: Distinction between {@code LambdaParameters} and {@code InferredLambdaParameters} is presence of types.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.27.1
 * <pre>{@code
 *     LambdaParameters:
 *         ( [LambdaParameterList] )
 *         Identifier
 *     LambdaParameterList:
 *         LambdaParameter {, LambdaParameter}
 *         Identifier {, Identifier}
 *     LambdaParameter:
 *         {VariableModifier} LambdaParameterType VariableDeclaratorId
 *         VariableArityParameter
 *     LambdaParameterType:
 *         UnannType
 *         var
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     VariableArityParameter:
 *          {VariableModifier} UnannType {Annotation} ... Identifier
 *     VariableModifier:
 *         Annotation
 *         final
 *     VariableDeclaratorId:
 *         Identifier [Dims]
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }</pre>
 */
NodeList<Parameter> LambdaParameters():
{
    NodeList<Parameter> ret = null;
    Parameter par;
}
{
    par = Parameter() { ret = add(ret, par); }
    ( "," par = Parameter() { ret = add(ret, par); } )*
    { return ret;  }
}


/**
 * Note: Similar, but not identical to, {@code FormalParameter}.
 * Note: Distinction between {@code LambdaParameters} and {@code InferredLambdaParameters} is presence of types.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.27.1
 * <pre>{@code
 *     LambdaParameters:
 *         ( [LambdaParameterList] )
 *         Identifier
 *     LambdaParameterList:
 *         LambdaParameter {, LambdaParameter}
 *         Identifier {, Identifier}
 *     LambdaParameter:
 *         {VariableModifier} LambdaParameterType VariableDeclaratorId
 *         VariableArityParameter
 *     LambdaParameterType:
 *         UnannType
 *         var
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     VariableArityParameter:
 *          {VariableModifier} UnannType {Annotation} ... Identifier
 *     VariableModifier:
 *         Annotation
 *         final
 *     VariableDeclaratorId:
 *         Identifier [Dims]
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }</pre>
 */
NodeList<Parameter> InferredLambdaParameters():
{
    NodeList<Parameter> ret = null;
    Pair<SimpleName, List<ArrayBracketPair>> id;
}
{
    id = VariableDeclaratorId() { ret = add(ret, new Parameter(range(id.a, id.a), new NodeList<Modifier>(), emptyNodeList(), new UnknownType(), false, emptyNodeList(), id.a)); }
    (
        "," id = VariableDeclaratorId() { ret = add(ret, new Parameter(range(id.a, id.a), new NodeList<Modifier>(), emptyNodeList(), new UnknownType(), false, emptyNodeList(), id.a)); }
    )*
    { return ret;  }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4.1
 * <pre>{@code
 *     FormalParameter:
 *         {VariableModifier} UnannType VariableDeclaratorId
 *         VariableArityParameter
 *     VariableArityParameter:
 *         {VariableModifier} UnannType {Annotation} ... Identifier
 *     VariableModifier:
 *         Annotation
 *         final
 * }</pre>
 */
Parameter Parameter():
{
    ModifierHolder modifier;
    Type partialType;
    boolean isVarArg = false;
    Pair<SimpleName, List<ArrayBracketPair>> id;
    NodeList<AnnotationExpr> varArgAnnotations = emptyNodeList();
}
{
    modifier = Modifiers()
    partialType = Type(emptyNodeList())
    [
        varArgAnnotations = Annotations()
        "..." { isVarArg = true;}
    ]
    id = VariableDeclaratorId()

    {
        JavaToken begin = orIfInvalid(modifier.begin, partialType);
        return new Parameter(range(begin, token()), modifier.modifiers, modifier.annotations, juggleArrayType(partialType, id.b), isVarArg, varArgAnnotations, id.a);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-ReceiverParameter
 * <pre>{@code
 *     MethodDeclarator:
 *         Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]
 *     ReceiverParameter:
 *         {Annotation} UnannType [Identifier .] this
 * }</pre>
 */
ReceiverParameter ReceiverParameter():
{
    Type partialType;
    Name id;
    NodeList<AnnotationExpr> annotations = emptyNodeList();
}
{
    annotations = Annotations()
    partialType = Type(emptyNodeList())
    id = ReceiverParameterId()
    {
        return new ReceiverParameter(range(partialType, token()), annotations, partialType, id);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-ReceiverParameter
 * <pre>{@code
 *     MethodDeclarator:
 *         Identifier ( [ReceiverParameter ,] [FormalParameterList] ) [Dims]
 *     ReceiverParameter:
 *         {Annotation} UnannType [Identifier .] this
 * }</pre>
 */
Name ReceiverParameterId():
{
    Name ret = null;
}
{
    [ LOOKAHEAD(Name()) ret = Name() "."] "this"
    { return new Name(tokenRange(), ret, token.image); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.8
 * <pre>{@code
 *     ConstructorDeclaration:
 *         {ConstructorModifier} ConstructorDeclarator [Throws] ConstructorBody
 *     ConstructorDeclarator:
 *         [TypeParameters] SimpleTypeName ( [ReceiverParameter ,] [FormalParameterList] )
 *     SimpleTypeName:
 *         TypeIdentifier
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.8.7
 * <pre>{@code
 *     ConstructorBody:
 *         { [ExplicitConstructorInvocation] [BlockStatements] }
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.8.7.1
 * <pre>{@code
 *     ExplicitConstructorInvocation:
 *         [TypeArguments] this ( [ArgumentList] ) ;
 *         [TypeArguments] super ( [ArgumentList] ) ;
 *         ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
 *         Primary . [TypeArguments] super ( [ArgumentList] ) ;
 * }</pre>
 */
ConstructorDeclaration ConstructorDeclaration(ModifierHolder modifier):
{
    RangedList<TypeParameter> typeParameters = new RangedList<TypeParameter>(emptyNodeList());
    SimpleName name;
    Pair<NodeList<Parameter>, ReceiverParameter> parameters = new Pair<NodeList<Parameter>, ReceiverParameter>(emptyNodeList(), null);
    NodeList<ReferenceType> throws_ = emptyNodeList();
    ExplicitConstructorInvocationStmt exConsInv = null;
    NodeList<Statement> stmts = emptyNodeList();
    JavaToken begin = modifier.begin;
    JavaToken blockBegin = INVALID;
    ReferenceType throwType;
}
{
    [ typeParameters = TypeParameters() { begin = orIfInvalid(begin, typeParameters.range.getBegin()); } ]
    // Modifiers matched in the caller
    name = SimpleName() { begin = orIfInvalid(begin, typeParameters.range.getBegin()); begin = orIfInvalid(begin, token()); }
    parameters = Parameters()
    [
        "throws"
        throwType = AnnotatedReferenceType() { throws_ = add(throws_, throwType); }
        ("," throwType = AnnotatedReferenceType() { throws_ = add(throws_, throwType); })*
    ]
    "{" { blockBegin=token(); }
    [
        LOOKAHEAD(ExplicitConstructorInvocation())
        exConsInv = ExplicitConstructorInvocation()
    ]
    stmts = Statements()
    "}"

    {
        if (exConsInv != null) {
            stmts = prepend(stmts, exConsInv);
        }
        return new ConstructorDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, typeParameters.list, name, parameters.a, throws_, new BlockStmt(range(blockBegin, token()), stmts), parameters.b);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.8.7.1
 * <pre>{@code
 *     ExplicitConstructorInvocation:
 *         [TypeArguments] this ( [ArgumentList] ) ;
 *         [TypeArguments] super ( [ArgumentList] ) ;
 *         ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
 *         Primary . [TypeArguments] super ( [ArgumentList] ) ;
 * }</pre>
 * The same, but aligned on {@code super}, to emphasise how this grammar relates:
 * <pre>{@code
 *     ExplicitConstructorInvocation:
 *         [TypeArguments] this ( [ArgumentList] ) ;
 *                          [TypeArguments] super ( [ArgumentList] ) ;
 *         ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
 *                Primary . [TypeArguments] super ( [ArgumentList] ) ;
 * }</pre>
 */
ExplicitConstructorInvocationStmt ExplicitConstructorInvocation():
{
    boolean isThis = false;
    NodeList<Expression> args;
    Expression expr = null;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
    JavaToken begin = INVALID;
}
{
    (
        LOOKAHEAD([ TypeArguments() ] <THIS> "(")
        [ typeArgs = TypeArguments() { begin=typeArgs.range.getBegin(); } ]
        <THIS> { begin = orIfInvalid(begin, token()); isThis = true; }
        args = Arguments()
        ";"
     |
        [
            LOOKAHEAD( PrimaryExpressionWithoutSuperSuffix() "." )
            expr = PrimaryExpressionWithoutSuperSuffix() "." { begin = orIfInvalid(begin, expr); }
        ]
        [
            typeArgs = TypeArguments() { begin = orIfInvalid(begin, typeArgs.range.getBegin()); }
        ]
        <SUPER> { begin = orIfInvalid(begin, token()); }
        args = Arguments()
        ";"
    )
    { return new ExplicitConstructorInvocationStmt(range(begin, token()),typeArgs.list, isThis, expr, args); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.5
 * <pre>{@code
 *     Statement:
 *         StatementWithoutTrailingSubstatement
 *         LabeledStatement
 *         IfThenStatement
 *         IfThenElseStatement
 *         WhileStatement
 *         ForStatement
 *     StatementNoShortIf:
 *         StatementWithoutTrailingSubstatement
 *         LabeledStatementNoShortIf
 *         IfThenElseStatementNoShortIf
 *         WhileStatementNoShortIf
 *         ForStatementNoShortIf
 *     StatementWithoutTrailingSubstatement:
 *         Block
 *         EmptyStatement
 *         ExpressionStatement
 *         AssertStatement
 *         SwitchStatement
 *         DoStatement
 *         BreakStatement
 *         ContinueStatement
 *         ReturnStatement
 *         SynchronizedStatement
 *         ThrowStatement
 *         TryStatement
 *         YieldStatement
 * }</pre>
 */
NodeList<Statement> Statements():
{
    NodeList<Statement> ret = emptyNodeList();
    Statement stmt;
}
{
    ( LOOKAHEAD(2) stmt = BlockStatement() { ret = add(ret, stmt); } )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.6
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.
 * <pre>{@code
 *     InstanceInitializer:
 *         Block
 * }</pre>
 * <pre>{@code
 *     StaticInitializer:
 *         static Block
 * }</pre>
 */
InitializerDeclaration InitializerDeclaration():
{
    BlockStmt body;
    JavaToken begin = INVALID;
    boolean isStatic = false;
}
{
    [ "static" { isStatic = true; begin=token();} ]
    body = Block() {begin = orIfInvalid(begin, body);}
    { return new InitializerDeclaration(range(begin, token()), isStatic, body); }
}


/*
 * Type, name and expression syntax follows.
 */


/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.1
 * <pre>{@code
 *     Type:
 *         PrimitiveType
 *         ReferenceType
 * }<pre>
 */
Type Type(NodeList<AnnotationExpr> annotations):
{
    Type ret;
}
{
    (
        LOOKAHEAD(PrimitiveType(annotations) Annotations() "[" | ClassOrInterfaceType(annotations))
        ret = ReferenceType(annotations)
     |
        ret = PrimitiveType(annotations)
    )
    { return ret; }
}

/**
 * // TODO: JLS Has type and unannotated type, while JavaParser has type and annotated type.
 * Note that an array of primitives is considered a reference type.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.1
 * <pre>{@code
 *     ReferenceType:
 *         ClassOrInterfaceType
 *         TypeVariable
 *         ArrayType
 *     ClassOrInterfaceType:
 *         ClassType
 *         InterfaceType
 *     ClassType:
 *         {Annotation} TypeIdentifier [TypeArguments]
 *         PackageName . {Annotation} TypeIdentifier [TypeArguments]
 *         ClassOrInterfaceType . {Annotation} TypeIdentifier [TypeArguments]
 *     InterfaceType:
 *         ClassType
 *     TypeVariable:
 *         {Annotation} TypeIdentifier
 *     ArrayType:
 *         PrimitiveType Dims
 *         ClassOrInterfaceType Dims
 *         TypeVariable Dims
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }<pre>
 */
ReferenceType ReferenceType(NodeList<AnnotationExpr> annotations):
{
    Type type;
    ArrayBracketPair arrayBracketPair;
    List<ArrayBracketPair> arrayBracketPairs = new ArrayList<ArrayBracketPair>(0);
}
{
    (
        // Note that an array of primitives is considered a reference type.
        type = PrimitiveType(annotations)
        (
            LOOKAHEAD(Annotations() "[")
            arrayBracketPair = ArrayBracketPair(Origin.TYPE) { arrayBracketPairs=add(arrayBracketPairs, arrayBracketPair); }
        )+ // MUST have one or more array bracket pairs to be considered a reference type.
     |
        type = ClassOrInterfaceType(annotations)
        (
            LOOKAHEAD(Annotations() "[")
            arrayBracketPair = ArrayBracketPair(Origin.TYPE) { arrayBracketPairs=add(arrayBracketPairs, arrayBracketPair); }
        )* // Optionally MAY have zero or more array bracket pairs.
    )
    { return (ReferenceType)wrapInArrayTypes(type, arrayBracketPairs); }
}

/**
 * Array bracket pairs is referred to as {@code Dims} within the JLS.
 * <pre>{@code
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }</pre>
 *
 */
ArrayBracketPair ArrayBracketPair(Origin origin):
{
    NodeList<AnnotationExpr> annotations;
    JavaToken begin = INVALID;
}
{
    annotations = Annotations()
    "[" { begin = orIfInvalid(begin, token()); }
    "]"
    { return new ArrayBracketPair(range(begin, token()), origin, annotations); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.9
 * <blockquote>
 * An intersection type takes the form T1 & ... & Tn (n > 0), where Ti (1 ≤ i ≤ n) are types.
 * </blockquote>
 */
IntersectionType IntersectionType(NodeList<AnnotationExpr> annotations):
{
    JavaToken begin = INVALID;
    ReferenceType elementType;
    NodeList<ReferenceType> elements = emptyNodeList();
}
{
    elementType = ReferenceType(annotations) { begin = orIfInvalid(begin, elementType); elements = add(elements, elementType); }
    "&"
    (elementType = AnnotatedReferenceType() { elements = add(elements, elementType); } )+
    { return new IntersectionType(range(begin, token()), elements); }
}

// TODO: JLS Has type and unannotated type, while JavaParser has type and annotated type.
ClassOrInterfaceType AnnotatedClassOrInterfaceType():
{
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
    ClassOrInterfaceType cit;
}
{
    annotations = Annotations()
    cit = ClassOrInterfaceType(annotations)
    { return cit; }
}

// TODO: JLS Has type and unannotated type, while JavaParser has type and annotated type.
ClassOrInterfaceType ClassOrInterfaceType(NodeList<AnnotationExpr> firstAnnotations):
{
    ClassOrInterfaceType ret;
    SimpleName name;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
    JavaToken begin;
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
}
{
    name = SimpleName() { begin=token(); }
    [ LOOKAHEAD(2) typeArgs = TypeArguments() ]
    {
        ret = new ClassOrInterfaceType(range(begin, token()), null, name, typeArgs.list, firstAnnotations);
        typeArgs = new RangedList<Type>(null);
    }
    (
        LOOKAHEAD(2) "." annotations = Annotations()  name = SimpleName()
        [ LOOKAHEAD(2) typeArgs = TypeArguments() ]
        {
            ret = new ClassOrInterfaceType(range(begin, token()), ret, name, typeArgs.list, annotations);
            typeArgs = new RangedList<Type>(null);
        }
    )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.5.1
 * <pre>{@code
 *     TypeArguments:
 *         < TypeArgumentList >
 *     TypeArgumentList:
 *         TypeArgument {, TypeArgument}
 *     TypeArgument:
 *         ReferenceType
 *         Wildcard
 * }<pre>
 */
RangedList<Type> TypeArguments():
{
    RangedList<Type> ret = new RangedList<Type>(new NodeList<Type>());
    Type type;
}
{
    "<" { ret.beginAt(token()); }
    [
        type = TypeArgument() { ret.add(type); }
        ( "," type = TypeArgument() { ret.add(type); } )*
    ]
    ">" { ret.endAt(token()); }
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.5.1
 * <pre>{@code
 *     TypeArguments:
 *         < TypeArgumentList >
 *     TypeArgumentList:
 *         TypeArgument {, TypeArgument}
 *     TypeArgument:
 *         ReferenceType
 *         Wildcard
 * }<pre>
 */
Type TypeArgument():
{
    Type ret;
    NodeList<AnnotationExpr> annotations;
}
{
    annotations = Annotations()
    (
        ret = Type(annotations)
     |
        ret = Wildcard(annotations)
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.5.1
 * <pre>{@code
 *     Wildcard:
 *         {Annotation} ? [WildcardBounds]
 *     WildcardBounds:
 *         extends ReferenceType
 *         super ReferenceType
 * }<pre>
 */
WildcardType Wildcard(NodeList<AnnotationExpr> firstAnnotations):
{
    ReferenceType ext = null;
    ReferenceType sup = null;
    JavaToken begin;
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
}
{
    "?" { begin=token(); }
    [
        "extends"
        annotations = Annotations()
        ext = ReferenceType(annotations)
     |
        "super"
        annotations = Annotations()
        sup = ReferenceType(annotations)
    ]
    { return new WildcardType(range(begin, token()), ext, sup, firstAnnotations);}
}

/**
 * Note that {@code char} and {@code byte} are treated as an {@code IntegralType}, which is a subtype of {@code NumericType}.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-4.html#jls-4.2
 * <pre>{@code
 *     PrimitiveType:
 *         {Annotation} NumericType
 *         {Annotation} boolean
 *     NumericType:
 *         IntegralType
 *         FloatingPointType
 *     IntegralType:
 *         (one of)
 *         byte short int long char
 *     FloatingPointType:
 *         (one of)
 *         float double
 * }<pre>
 */
PrimitiveType PrimitiveType(NodeList<AnnotationExpr> annotations):
{
    PrimitiveType ret;
}
{
    (
        "boolean" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.BOOLEAN, annotations); }
     |
        "char" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.CHAR, annotations); }
     |
        "byte" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.BYTE, annotations); }
     |
        "short" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.SHORT, annotations); }
     |
        "int" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.INT, annotations); }
     |
        "long" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.LONG, annotations); }
     |
        "float" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.FLOAT, annotations); }
     |
        "double" { ret = new PrimitiveType(tokenRange(), PrimitiveType.Primitive.DOUBLE, annotations); }
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.4.5
 * <pre>{@code
 *     Result:
 *         UnannType
 *         void
 * }<pre>
 */
Type ResultType(NodeList<AnnotationExpr> annotations):
{
    Type ret;
}
{
    (
        "void" { ret = new VoidType(tokenRange()); }
     |
        ret = Type(annotations)
    )
    { return ret; }
}

/**
 * A {@code SimpleName} is just an identifier.
 * A {@code Name} can be a {@code SimpleName} or a {@code SimpleName} with qualifiers (e.g. a class or object reference).
 * Note that no disambiguation is made as to exactly what type of name it is - e.g. whether it is a package, subclass, or field. That happens within JavaParser, rather than the grammar.
 */
Name Name():
/*
 * A lookahead is required below since "Name" can be followed
 * by a ".*" when used in the context of an "ImportDeclaration".
 */
{
    Name ret;
}
{
    Identifier() { ret = new Name(tokenRange(), null, token.image); }
    (
        LOOKAHEAD(2)
        "."
        Identifier()
        { ret = new Name(range(ret, token()), ret, token.image); }
    )*
    { return ret; }
}

/**
 * A {@code SimpleName} is just an identifier.
 * A {@code Name} can be a {@code SimpleName} or a {@code SimpleName} with qualifiers (e.g. a class or object reference).
 * Note that no disambiguation is made as to exactly what type of name it is - e.g. whether it is a package, subclass, or field. That happens within JavaParser, rather than the grammar.
 */
SimpleName SimpleName():
{
    SimpleName ret;
}
{
    Identifier() { ret = new SimpleName(tokenRange(), token.image); }
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.8
 * <pre>{@code
 *     Identifier:
 *         IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
 *     IdentifierChars:
 *         JavaLetter {JavaLetterOrDigit}
 *     JavaLetter:
 *         any Unicode character that is a "Java letter"
 * }</pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.9
 * <blockquote>
 * 51 character sequences, formed from ASCII letters, are reserved for use as keywords and cannot be used as identifiers (§3.8).
 * </blockquote>
 * <pre>{@code
 *     Keyword:
 *         (one of)
 *
 *         abstract   continue   for          new         switch
 *         assert     default    if           package     synchronized
 *         boolean    do         goto         private     this
 *         break      double     implements   protected   throw
 *         byte       else       import       public      throws
 *         case       enum       instanceof   return      transient
 *         catch      extends    int          short       try
 *         char       final      interface    static      void
 *         class      finally    long         strictfp    volatile
 *         const      float      native       super       while
 *         _ (underscore)
 * }</pre>
 */
String Identifier():
{
    String ret;
}
{
    (
        // TODO: Explain why these keywords are explicitly flagged up as "included", and why they're not already covered by {@code <IDENTIFIER>}.
        // TODO: Explain how / where boolean and null literals are excluded.
        // Make sure the module info keywords don't interfere with normal Java parsing by matching them as normal identifiers.
        <MODULE> | <REQUIRES> | <TO> | <WITH> | <OPEN> | <OPENS> | <USES> | <EXPORTS> | <PROVIDES> | <TRANSITIVE> |
        // Make sure older Java versions parse
        <ENUM> | <STRICTFP> | <YIELD> | <RECORD> | <PERMITS> | <SEALED> | <WHEN> |
        // An actual plain old identifier
        <IDENTIFIER>
    ) { ret = token.image; setTokenKind(IDENTIFIER);}
    { return ret; }
}


/*
 * Expression syntax follows.
 */


Expression Expression():
/*
 * This expansion has been written this way instead of:
 *   Assignment() | ConditionalExpression()
 * for performance reasons.
 * However, it is a weakening of the grammar for it allows the LHS of
 * assignments to be any conditional expression whereas it can only be
 * a primary expression. This is caught by a validation after parsing.
 */
{
    Expression ret;
    AssignExpr.Operator op;
    Expression value;
    Statement lambdaBody = null;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
}
{
    ret = ConditionalExpression()
    [
        (
            LOOKAHEAD(2)
            op = AssignmentOperator()
            value = Expression() { ret = new AssignExpr(range(ret, token()), ret, value, op); }
         |
            "->"
            lambdaBody = LambdaBody()
            {
                ret = generateLambda(ret, lambdaBody);
            }
         |
            "::"
            [typeArgs = TypeArguments()]
            (Identifier() | "new") { ret = new MethodReferenceExpr(range(ret, token()), ret, typeArgs.list, token.image); }
        )
    ]

    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.26
 * <pre>{@code
 *     AssignmentExpression:
 *         ConditionalExpression
 *         Assignment
 *     Assignment:
 *         LeftHandSide AssignmentOperator Expression
 *     LeftHandSide:
 *         ExpressionName
 *         FieldAccess
 *         ArrayAccess
 *     AssignmentOperator:
 *         (one of)
 *         =  *=  /=  %=  +=  -=  <<=  >>=  >>>=  &=  ^=  |=
 * }<pre>
 */
AssignExpr.Operator AssignmentOperator():
{
    AssignExpr.Operator ret;
}
{
    (
        "="    { ret = AssignExpr.Operator.ASSIGN; }
      | "*="   { ret = AssignExpr.Operator.MULTIPLY; }
      | "/="   { ret = AssignExpr.Operator.DIVIDE; }
      | "%="   { ret = AssignExpr.Operator.REMAINDER; }
      | "+="   { ret = AssignExpr.Operator.PLUS; }
      | "-="   { ret = AssignExpr.Operator.MINUS; }
      | "<<="  { ret = AssignExpr.Operator.LEFT_SHIFT; }
      | ">>="  { ret = AssignExpr.Operator.SIGNED_RIGHT_SHIFT; }
      | ">>>=" { ret = AssignExpr.Operator.UNSIGNED_RIGHT_SHIFT; }
      | "&="   { ret = AssignExpr.Operator.BINARY_AND; }
      | "^="   { ret = AssignExpr.Operator.XOR; }
      | "|="   { ret = AssignExpr.Operator.BINARY_OR; }
    )
    { return ret; }
}

/**
 * // TODO: Examine further re: missing(?) {@code LambdaExpression} or whether it is permissive enough to include it.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.25
 * <pre>{@code
 *     ConditionalExpression:
 *         ConditionalOrExpression
 *         ConditionalOrExpression ? Expression : ConditionalExpression
 *         ConditionalOrExpression ? Expression : LambdaExpression
 * }<pre>
 */
Expression ConditionalExpression():
{
    Expression ret;
    Expression left;
    Expression right;
}
{
    ret = ConditionalOrExpression()
    [
        "?"
        left = Expression()
        ":"
        right = Expression()
        // TODO: Examine further re: missing(?) {@code LambdaExpression} or whether it is permissive enough to include it.
        {
            ret = new ConditionalExpr(range(ret, token()), ret, left, right);
        }
    ]
    { return ret; }
}

/**
 * <strong>
 * Note subtle distinctions between inclusive and exclusive expressions.
 * </strong>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.24
 * <pre>{@code
 *     ConditionalOrExpression:
 *         ConditionalAndExpression
 *         ConditionalOrExpression || ConditionalAndExpression
 * }<pre>
 */
Expression ConditionalOrExpression():
{
    Expression ret;
    Expression right;
}
{
    // First consider a higher-precedence operator, before considering the token as the left-hand part of this expression.
    ret = ConditionalAndExpression()
    (
        "||"
        right = ConditionalAndExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, BinaryExpr.Operator.OR); }
    )*
    { return ret; }
}

/**
 * <strong>
 * Note subtle distinctions between inclusive and exclusive expressions.
 * </strong>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.23
 * <pre>{@code
 *     ConditionalAndExpression:
 *         InclusiveOrExpression
 *         ConditionalAndExpression && InclusiveOrExpression
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     AndExpression:
 *         EqualityExpression
 *         AndExpression & EqualityExpression
 *     ExclusiveOrExpression:
 *         AndExpression
 *         ExclusiveOrExpression ^ AndExpression
 *     InclusiveOrExpression:
 *         ExclusiveOrExpression
 *         InclusiveOrExpression | ExclusiveOrExpression
 * }<pre>
 */
Expression ConditionalAndExpression():
{
    Expression ret;
    Expression right;
}
{
    // First consider a higher-precedence operator, before considering the token as the left-hand part of this expression.
    ret = InclusiveOrExpression()
    (
        "&&"
        right = InclusiveOrExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, BinaryExpr.Operator.AND); }
    )*
    { return ret; }
}

/**
 * <strong>
 * Note subtle distinctions between inclusive and exclusive expressions.
 * </strong>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.22
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-InclusiveOrExpression
 * <pre>{@code
 *     AndExpression:
 *         EqualityExpression
 *         AndExpression & EqualityExpression
 *     ExclusiveOrExpression:
 *         AndExpression
 *         ExclusiveOrExpression ^ AndExpression
 *     InclusiveOrExpression:
 *         ExclusiveOrExpression
 *         InclusiveOrExpression | ExclusiveOrExpression
 * }<pre>
 */
Expression InclusiveOrExpression():
{
    Expression ret;
    Expression right;
}
{
    // First consider a higher-precedence operator, before considering the token as the left-hand part of this expression.
    ret = ExclusiveOrExpression()
    (
        "|"
        right = ExclusiveOrExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, BinaryExpr.Operator.BINARY_OR); }
    )*
    { return ret; }
}

/**
 * <strong>
 * Note subtle distinctions between inclusive and exclusive expressions.
 * </strong>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.22
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-ExclusiveOrExpression
 * <pre>{@code
 *     AndExpression:
 *         EqualityExpression
 *         AndExpression & EqualityExpression
 *     ExclusiveOrExpression:
 *         AndExpression
 *         ExclusiveOrExpression ^ AndExpression
 *     InclusiveOrExpression:
 *         ExclusiveOrExpression
 *         InclusiveOrExpression | ExclusiveOrExpression
 * }<pre>
 */
Expression ExclusiveOrExpression():
{
    Expression ret;
    Expression right;
}
{
    // First consider a higher-precedence operator, before considering the token as the left-hand part of this expression.
    ret = AndExpression() (
        "^"
        right = AndExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, BinaryExpr.Operator.XOR); }
    )*
    { return ret; }
}

/**
 * <strong>
 * Note subtle distinctions between inclusive and exclusive expressions.
 * </strong>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.22
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-AndExpression
 * <pre>{@code
 *     AndExpression:
 *         EqualityExpression
 *         AndExpression & EqualityExpression
 *     ExclusiveOrExpression:
 *         AndExpression
 *         ExclusiveOrExpression ^ AndExpression
 *     InclusiveOrExpression:
 *         ExclusiveOrExpression
 *         InclusiveOrExpression | ExclusiveOrExpression
 * }<pre>
 */
Expression AndExpression():
{
    Expression ret;
    Expression right;
}
{
    // First consider a higher-precedence operator, before considering the token as the left-hand part of this expression.
    ret = EqualityExpression() (
        "&"
        right = EqualityExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, BinaryExpr.Operator.BINARY_AND); }
    )*
    { return ret; }
}

/**
 * // Note that instanceof is a {@code RelationalExpression} within the JLS, which differs from JavaParser
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.21
 * <pre>{@code
 *     EqualityExpression:
 *         RelationalExpression
 *         EqualityExpression == RelationalExpression
 *         EqualityExpression != RelationalExpression
 * }<pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.20
 * <pre>{@code
 *     RelationalExpression:
 *         ShiftExpression
 *         RelationalExpression < ShiftExpression
 *         RelationalExpression > ShiftExpression
 *         RelationalExpression <= ShiftExpression
 *         RelationalExpression >= ShiftExpression
 *         RelationalExpression instanceof ReferenceType
 * }<pre>
 */
Expression EqualityExpression():
{
    Expression ret;
    Expression right;
    BinaryExpr.Operator op;
}
{
    // First consider a higher-precedence operator, before considering the token as the left-hand part of this expression.
    // Note that instanceof is a {@code RelationalExpression} within the JLS, which differs from JavaParser
    ret = InstanceOfExpression()
    (
        (
            "==" { op = BinaryExpr.Operator.EQUALS; }
         |
            "!=" { op = BinaryExpr.Operator.NOT_EQUALS; }
        )
        right = InstanceOfExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, op); }
    )*
    { return ret; }
}

PatternExpr PatternExpression():
{
    PatternExpr ret;
}
{
	(
		LOOKAHEAD(TypePatternExpression())
		ret = TypePatternExpression()
	  |
	    ret = RecordPatternExpression()
	)
    { return ret; }
}

/**
 * https://openjdk.java.net/jeps/375
 * The instanceof grammar is extended accordingly:
 * <pre>{@code
 *     Pattern:
 *         ReferenceType Identifier
 * }<pre>
 */
TypePatternExpr TypePatternExpression():
{
    ModifierHolder modifier;
    Type type;
    SimpleName name;
}
{
    modifier = Modifiers()
    type = Type(modifier.annotations)
    name = SimpleName()
    { return new TypePatternExpr(range(type, token()), modifier.modifiers, type, name); }
}

/**
 * https://openjdk.org/jeps/440
 * <pre><code>
 * RecordPattern:
 *     ReferenceType ( [PatternList] )
 * PatternList:
 *     Pattern {, Pattern }
 * </code></pre>
 */
RecordPatternExpr RecordPatternExpression():
{
	ModifierHolder modifier;
	ReferenceType type;
	NodeList<PatternExpr> patternList;
}
{
	modifier = Modifiers()
	type = ReferenceType(modifier.annotations)
	patternList = PatternList()
	{ return new RecordPatternExpr(range(type, token()), modifier.modifiers, type, patternList); }
}

/**
 * https://openjdk.org/jeps/440
 * <pre><code>
 * PatternList:
 *     Pattern {, Pattern }
 * </code></pre>
 */
NodeList<PatternExpr> PatternList():
{
    PatternExpr pattern;
	NodeList<PatternExpr> ret = new NodeList<>();
}
{
	"("
    pattern = PatternExpression() { ret.add(pattern); }
    (
        ","
        pattern = PatternExpression() { ret.add(pattern); }
    )*
	")"
    { return ret; }
}


/**
 * // Note that instanceof is a {@code RelationalExpression} within the JLS, which differs from JavaParser
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.20
 * <pre>{@code
 *     RelationalExpression:
 *         ShiftExpression
 *         RelationalExpression < ShiftExpression
 *         RelationalExpression > ShiftExpression
 *         RelationalExpression <= ShiftExpression
 *         RelationalExpression >= ShiftExpression
 *         RelationalExpression instanceof ReferenceType
 * }<pre>
 */
Expression InstanceOfExpression():
{
    Expression ret;
    ReferenceType type;
    NodeList<AnnotationExpr> annotations;
    PatternExpr pattern;
}
{
    ret = RelationalExpression()
    [
        "instanceof"
        (
            LOOKAHEAD(PatternExpression())
            pattern = PatternExpression()
            // From the JLS https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-Pattern, the type of
            // a top-level pattern must be a reference type. This means that converting the pattern type to a
            // reference type here is always safe if the code being parsed compiles.
            { ret = new InstanceOfExpr(range(ret, token()), ret, pattern.getType().asReferenceType(), pattern); }
         |
            type = AnnotatedReferenceType()
            { ret = new InstanceOfExpr(range(ret, token()), ret, type, null); }
        )
    ]
    { return ret; }
}

/**
 * // Note that instanceof is a {@code RelationalExpression} within the JLS, which differs from JavaParser
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.20
 * <pre>{@code
 *     RelationalExpression:
 *         ShiftExpression
 *         RelationalExpression < ShiftExpression
 *         RelationalExpression > ShiftExpression
 *         RelationalExpression <= ShiftExpression
 *         RelationalExpression >= ShiftExpression
 *         RelationalExpression instanceof ReferenceType
 * }<pre>
 * // Note that JDK 13/14 introduce pattern Matching for instanceof
 * https://openjdk.java.net/jeps/375
 * The instanceof grammar is extended accordingly:
 * <pre>{@code
 *     RelationalExpression:
 *         ...
 *         RelationalExpression instanceof ReferenceType
 *         RelationalExpression instanceof Pattern
 *
 *     Pattern:
 *         ReferenceType Identifier
 * }<pre>
 */
Expression RelationalExpression():
{
    Expression ret;
    Expression right;
    BinaryExpr.Operator op;
}
{
    ret = ShiftExpression()
    (
        (
            "<"  { op = BinaryExpr.Operator.LESS; }
         |
            ">"  { op = BinaryExpr.Operator.GREATER; }
         |
            "<=" { op = BinaryExpr.Operator.LESS_EQUALS; }
         |
            ">=" { op = BinaryExpr.Operator.GREATER_EQUALS; }
        )
        right = ShiftExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, op); }
    )*

    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.19
 * <pre>{@code
 *     ShiftExpression:
 *         AdditiveExpression
 *         ShiftExpression << AdditiveExpression
 *         ShiftExpression >> AdditiveExpression
 *         ShiftExpression >>> AdditiveExpression
 * }<pre>
 */
Expression ShiftExpression():
{
    Expression ret;
    Expression right;
    BinaryExpr.Operator op;
}
{
    ret = AdditiveExpression()
    (
        (
            "<<"             { op = BinaryExpr.Operator.LEFT_SHIFT; }
         |
            RSIGNEDSHIFT()   { op = BinaryExpr.Operator.SIGNED_RIGHT_SHIFT; }
         |
            RUNSIGNEDSHIFT() { op = BinaryExpr.Operator.UNSIGNED_RIGHT_SHIFT; }
        )
        right = AdditiveExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, op); }
    )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.18
 * <pre>{@code
 *     AdditiveExpression:
 *         MultiplicativeExpression
 *         AdditiveExpression + MultiplicativeExpression
 *         AdditiveExpression - MultiplicativeExpression
 * }<pre>
 */
Expression AdditiveExpression():
{
    Expression ret;
    Expression right;
    BinaryExpr.Operator op;
}
{
    ret = MultiplicativeExpression()
    (
        (
            "+" { op = BinaryExpr.Operator.PLUS; }
         |
            "-" { op = BinaryExpr.Operator.MINUS; }
        )
        right = MultiplicativeExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, op); }
    )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.17
 * <pre>{@code
 *     MultiplicativeExpression:
 *         UnaryExpression
 *         MultiplicativeExpression * UnaryExpression
 *         MultiplicativeExpression / UnaryExpression
 *         MultiplicativeExpression % UnaryExpression
 * }<pre>
 */
Expression MultiplicativeExpression():
{
    Expression ret;
    Expression right;
    BinaryExpr.Operator op;
}
{
    ret = UnaryExpression()
    (
        (
            "*" { op = BinaryExpr.Operator.MULTIPLY; }
         |
            "/" { op = BinaryExpr.Operator.DIVIDE; }
         |
            "%" { op = BinaryExpr.Operator.REMAINDER; }
        )
        right = UnaryExpression()
        { ret = new BinaryExpr(range(ret, token()), ret, right, op); }
    )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.15
 * <pre>{@code
 *     UnaryExpression:
 *         PreIncrementExpression
 *         PreDecrementExpression
 *         + UnaryExpression
 *         - UnaryExpression
 *         UnaryExpressionNotPlusMinus
 *     PreIncrementExpression:
 *         ++ UnaryExpression
 *     PreDecrementExpression:
 *         -- UnaryExpression
 *     UnaryExpressionNotPlusMinus:
 *         PostfixExpression
 *         ~ UnaryExpression
 *         ! UnaryExpression
 *         CastExpression
 *         SwitchExpression
 * }<pre>
 */
Expression UnaryExpression():
{
    Expression ret;
    UnaryExpr.Operator op;
    JavaToken begin = INVALID;
}
{
    (
        ret = PreIncrementExpression()
     |
        ret = PreDecrementExpression()
     |
        (
            "+" { op = UnaryExpr.Operator.PLUS; begin=token();}
         |
            "-" { op = UnaryExpr.Operator.MINUS; begin=token();}
        )
        ret = UnaryExpression()
        { ret = new UnaryExpr(range(begin, token()), ret, op); }
     |
        ret = UnaryExpressionNotPlusMinus()
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.15
 * <pre>{@code
 *     PreIncrementExpression:
 *         ++ UnaryExpression
 * }<pre>
 */
Expression PreIncrementExpression():
{
    Expression ret;
    JavaToken begin = INVALID;
}
{
    "++" { begin=token(); }
    ret = UnaryExpression() { ret = new UnaryExpr(range(begin, token()), ret, UnaryExpr.Operator.PREFIX_INCREMENT); }
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.15
 * <pre>{@code
 *     PreDecrementExpression:
 *         -- UnaryExpression
 * }<pre>
 */
Expression PreDecrementExpression():
{
    Expression ret;
    JavaToken begin;
}
{
    "--" { begin=token(); }
    ret = UnaryExpression() { ret = new UnaryExpr(range(begin, token()), ret, UnaryExpr.Operator.PREFIX_DECREMENT); }
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.15
 * <pre>{@code
 *     UnaryExpressionNotPlusMinus:
 *         PostfixExpression
 *         ~ UnaryExpression
 *         ! UnaryExpression
 *         CastExpression
 *         SwitchExpression
 * }<pre>
 */
Expression UnaryExpressionNotPlusMinus():
{
    Expression ret;
    UnaryExpr.Operator op;
    JavaToken begin = INVALID;
}
{
    (
        (
            "~" { op = UnaryExpr.Operator.BITWISE_COMPLEMENT; begin=token(); }
         |
            "!" { op = UnaryExpr.Operator.LOGICAL_COMPLEMENT; begin=token(); }
        )
        ret = UnaryExpression() { ret = new UnaryExpr(range(begin, token()), ret, op); }
     |
        LOOKAHEAD( CastExpression() )
        ret = CastExpression()
     |
        ret = PostfixExpression()
     |
        ret = SwitchExpression()
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.15
 * <pre>{@code
 *     PostfixExpression:
 *         Primary
 *         ExpressionName
 *         PostIncrementExpression
 *         PostDecrementExpression
 * }<pre>
 */
Expression PostfixExpression():
{
    Expression ret;
    UnaryExpr.Operator op;
}
{
    ret = PrimaryExpression()
    [
        LOOKAHEAD(2)
        (
            "++" { op = UnaryExpr.Operator.POSTFIX_INCREMENT; }
         |
            "--" { op = UnaryExpr.Operator.POSTFIX_DECREMENT; }
        )
        { ret = new UnaryExpr(range(ret, token()), ret, op); }
    ]
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.16
 * <pre>{@code
 *     CastExpression:
 *         ( PrimitiveType ) UnaryExpression
 *         ( ReferenceType {AdditionalBound} ) UnaryExpressionNotPlusMinus
 *         ( ReferenceType {AdditionalBound} ) LambdaExpression
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     AdditionalBound:
 *         & InterfaceType
 * }<pre>
 */
Expression CastExpression():
{
    Expression ret;
    ReferenceType referenceType;
    PrimitiveType primitiveType;
    JavaToken begin = INVALID;
    NodeList<AnnotationExpr> annotations;
    NodeList<ReferenceType> typesOfMultiCast = emptyNodeList();
}
{
    "(" { begin=token(); }
    annotations = Annotations()
    (
        // ( PrimitiveType ) UnaryExpression
        LOOKAHEAD(2)
        primitiveType = PrimitiveType(annotations)
        ")"
        ret = UnaryExpression()
        { ret = new CastExpr(range(begin, token()), primitiveType, ret); }
     |
        // ( ReferenceType {AdditionalBound} ) UnaryExpressionNotPlusMinus
        // ( ReferenceType {AdditionalBound} ) LambdaExpression
        referenceType = ReferenceType(annotations) { typesOfMultiCast = add(typesOfMultiCast, referenceType); }
        (
            "&"
            referenceType = AnnotatedReferenceType() { typesOfMultiCast = add(typesOfMultiCast, referenceType); }
        )*
        ")"
        ret = UnaryExpressionNotPlusMinus()
        // TODO/FIXME: Handle LambdaExpression? Or is it handled elsewhere?
        {
            if (typesOfMultiCast.size() > 1) {
                ret = new CastExpr(range(begin, token()), new IntersectionType(range(typesOfMultiCast.get(0), typesOfMultiCast.get(typesOfMultiCast.size() -1)), typesOfMultiCast), ret);
            } else {
                ret = new CastExpr(range(begin, token()), referenceType, ret);
            }
        }
    )
    { return ret; }
}


/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.8
 * <pre>{@code
 *     Primary:
 *         PrimaryNoNewArray
 *         ArrayCreationExpression
 *     PrimaryNoNewArray:
 *         Literal
 *         ClassLiteral
 *         this
 *         TypeName . this
 *         ( Expression )
 *         ClassInstanceCreationExpression
 *         FieldAccess
 *         ArrayAccess
 *         MethodInvocation
 *         MethodReference
 * }<pre>
 */
Expression PrimaryExpression():
{
    Expression ret;
}
{
    ret = PrimaryPrefix()
    ( LOOKAHEAD(2) ret = PrimarySuffix(ret) )*
    { return ret; }
}

/**
 * // TODO: Unclear exactly which part of the JLS this relates to - a JavaParser-specific thing with no 1:1 mapping, perhaps?
 */
Expression PrimaryExpressionWithoutSuperSuffix():
{
    Expression ret;
}
{
    ret = PrimaryPrefix()
    (
        LOOKAHEAD( PrimarySuffixWithoutSuper(null) )
        ret = PrimarySuffixWithoutSuper(ret)
    )*
    { return ret; }
}

/**
 * // TODO: Unclear exactly which part of the JLS this relates to - a JavaParser-specific thing with no 1:1 mapping, perhaps?
 */
Expression PrimaryPrefix():
{
    Expression ret = null;
    SimpleName name;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
    NodeList<Expression> args = emptyNodeList();
    NodeList<Parameter> params = emptyNodeList();
    boolean hasArgs = false;
    boolean isLambda = false;
    Type type;
    JavaToken begin;
    Parameter p = null;
    SimpleName id = null;
}
{
    (
        ret = Literal()
     |
        <THIS> { ret = new ThisExpr(tokenRange(), null); }
     |
        <SUPER> { ret = new SuperExpr(tokenRange(), null); }
        (
            "."
            [ typeArgs = TypeArguments() ]
            name = SimpleName()
            [ args = Arguments() { hasArgs=true; } ]
            {
                if (hasArgs) {
                    ret = new MethodCallExpr(range(ret, token()), ret, typeArgs.list, name, args);
                } else {
                    ret = new FieldAccessExpr(range(ret, token()), ret, emptyNodeList(), name);
                }
            }
         |
            "::"
            [ typeArgs = TypeArguments() ]
            (Identifier() | "new")
            {
                ret = new MethodReferenceExpr(range(ret, token()), ret, typeArgs.list, token.image);
            }
        )
    |
        "(" { begin=token(); }
        (
            ")"
            { ret = new LambdaExpr(range(begin, token()), params, new BlockStmt(), true); }
         |
            LOOKAHEAD(Parameter())
            params = LambdaParameters()
            ")"
            { ret = new LambdaExpr(range(begin, token()), params, new BlockStmt(), true); }
         |
            LOOKAHEAD(VariableDeclaratorId() ",")
            params = InferredLambdaParameters()
            ")"
            { ret = new LambdaExpr(range(begin, token()), params, new BlockStmt(), true); }
         |
            // This could still be a lambda expression, but this is handled after matching -> elsewhere
            ret = Expression()
            ")"
            { ret = new EnclosedExpr(range(begin, token()), ret); }
        )
     |
        ret = AllocationExpression(null)
     |
        LOOKAHEAD( ResultType() "." "class" )
        type = ResultType(emptyNodeList())
        "."
        "class"
        { ret = new ClassExpr(range(type, token()), type); }
     |
        LOOKAHEAD (AnnotatedType() "::" )
        type = AnnotatedType()
        "::"
        [typeArgs = TypeArguments() ]
        (Identifier() | "new")
        {
            ret = new TypeExpr(range(type, type), type);
            ret = new MethodReferenceExpr(range(ret, token()), ret, typeArgs.list, token.image);
        }
     |
        name = SimpleName() { begin=token(); }
        [ args = Arguments() { hasArgs=true; } ]
        {
            if (hasArgs) {
                ret = new MethodCallExpr(range(begin, token()), null, null, name, args);
            } else {
                ret = new NameExpr(name);
            }
        }
    )
    { return ret; }
}

/**
 * // TODO: Unclear exactly which part of the JLS this relates to - a JavaParser-specific thing with no 1:1 mapping, perhaps?
 */
Expression PrimarySuffix(Expression scope):
{
    Expression ret;
}
{
    (
        LOOKAHEAD(2)
        ret = PrimarySuffixWithoutSuper(scope)
     |
        "."
        "super"
        { ret = new SuperExpr(range(scope, token()), scopeToName(scope)); }
    )
    { return ret; }
}

/**
 * // TODO: Unclear exactly which part of the JLS this relates to - a JavaParser-specific thing with no 1:1 mapping, perhaps?
 */
Expression PrimarySuffixWithoutSuper(Expression scope):
{
    Expression ret;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
    NodeList<Expression> args = emptyNodeList();
    boolean hasArgs = false;
    SimpleName name;
}
{
    (
        "."
        (
            "this"
            { ret = new ThisExpr(range(scope, token()), scopeToName(scope)); }
         |
            ret = AllocationExpression(scope)
         |
            LOOKAHEAD( [ TypeArguments() ] Identifier() )
            [ typeArgs = TypeArguments() ]
            name = SimpleName()
            [ args = Arguments() {hasArgs=true;} ]
            {
                if (hasArgs) {
                    ret = new MethodCallExpr(range(scope, token()), scope, typeArgs.list, name, args);
                } else {
                    ret =  new FieldAccessExpr(range(scope, token()), scope, typeArgs.list, name);
                }
            }
        )
     |
        "["
        ret = Expression()
        "]"
        { ret = new ArrayAccessExpr(range(scope, token()), scope, ret); }
    )
    { return ret; }
}

/**
 * Note that a Long Literal is defined as an {@code IntegerLiteral}, suffixed by "L"
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.1
 * <pre>{@code
 *     Literal:
 *         IntegerLiteral
 *         FloatingPointLiteral
 *         BooleanLiteral
 *         CharacterLiteral
 *         StringLiteral
 *         TextBlock
 *         NullLiteral
 * }</pre>
 */
Expression Literal():
{
    Expression ret;
}
{
    (
        <INTEGER_LITERAL> { ret = new IntegerLiteralExpr(tokenRange(), token.image); }
     |
        <LONG_LITERAL> { ret = new LongLiteralExpr(tokenRange(), token.image); }
     |
        <FLOATING_POINT_LITERAL> { ret = new DoubleLiteralExpr(tokenRange(), token.image); }
     |
        <CHARACTER_LITERAL> { ret = new CharLiteralExpr(tokenRange(), unquote(token.image)); }
     |
        <STRING_LITERAL> { ret = new StringLiteralExpr(tokenRange(), unquote(token.image)); }
     |
        <TEXT_BLOCK_LITERAL> { ret = new TextBlockLiteralExpr(tokenRange(), unTripleQuote(token.image)); }
     |
        ret = BooleanLiteral()
     |
        ret = NullLiteral()
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.3
 * <pre>{@code
 *     BooleanLiteral:
 *         (one of)
 *         true false
 * }</pre>
 */
Expression BooleanLiteral():
{
    Expression ret;
}
{
    (
        "true" { ret = new BooleanLiteralExpr(tokenRange(), true); }
     |
        "false" { ret = new BooleanLiteralExpr(tokenRange(), false); }
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10.8
 * <pre>{@code
 *     NullLiteral:
 *         null
 * }</pre>
 */
Expression NullLiteral():
{}
{
    "null"
    { return new NullLiteralExpr(tokenRange()); }
}


/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.12
 * <pre>{@code
 *     MethodInvocation:
 *         MethodName ( [ArgumentList] )
 *         TypeName . [TypeArguments] Identifier ( [ArgumentList] )
 *         ExpressionName . [TypeArguments] Identifier ( [ArgumentList] )
 *         Primary . [TypeArguments] Identifier ( [ArgumentList] )
 *         super . [TypeArguments] Identifier ( [ArgumentList] )
 *         TypeName . super . [TypeArguments] Identifier ( [ArgumentList] )
 *     ArgumentList:
 *         Expression {, Expression}
 * }</pre>
 */
NodeList<Expression> Arguments():
{
    NodeList<Expression> ret = emptyNodeList();
}
{
    "("
    [ ret = ArgumentList() ]
    ")"
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.12
 * <pre>{@code
 *     ArgumentList:
 *         Expression {, Expression}
 * }</pre>
 */
NodeList<Expression> ArgumentList():
{
    NodeList<Expression> ret = emptyNodeList();
    Expression expr;
}
{
    expr = Expression() { ret.add(expr); }
    (
        ","
        expr = Expression() { ret.add(expr); }
    )*
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.9
 * <pre>{@code
 *     ClassInstanceCreationExpression:
 *         UnqualifiedClassInstanceCreationExpression
 *         ExpressionName . UnqualifiedClassInstanceCreationExpression
 *         Primary . UnqualifiedClassInstanceCreationExpression
 *     UnqualifiedClassInstanceCreationExpression:
 *         new [TypeArguments] ClassOrInterfaceTypeToInstantiate ( [ArgumentList] ) [ClassBody]
 *     ClassOrInterfaceTypeToInstantiate:
 *         {Annotation} Identifier {. {Annotation} Identifier} [TypeArgumentsOrDiamond]
 *     TypeArgumentsOrDiamond:
 *         TypeArguments
 *         <>
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     ArgumentList:
 *         Expression {, Expression}
 * }</pre>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.10
 * <pre>{@code
 *     ArrayCreationExpression:
 *         new PrimitiveType DimExprs [Dims]
 *         new ClassOrInterfaceType DimExprs [Dims]
 *         new PrimitiveType Dims ArrayInitializer
 *         new ClassOrInterfaceType Dims ArrayInitializer
 *     DimExprs:
 *         DimExpr {DimExpr}
 *     DimExpr:
 *         {Annotation} [ Expression ]
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }</pre>
 */
Expression AllocationExpression(Expression scope):
{
    Expression ret;
    Type type;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
    NodeList<BodyDeclaration<?>> anonymousBody = null;
    NodeList<Expression> args;
    JavaToken begin = INVALID;
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
}
{
    "new" { if(scope==null) { begin=token(); } else { begin = orIfInvalid(begin, scope); } }

    [ typeArgs = TypeArguments() ]
    annotations = Annotations()
    (
        // new array of primitives
        type = PrimitiveType(annotations)
        ret = ArrayCreation(begin, type)
     |
        // new reference type e.g. class (or an array of those)
        type = ClassOrInterfaceType(annotations)
        (
            // new Integer[6] -- the array doesn't have parameters.
            ret = ArrayCreation(begin, type)
         |
            // new Integer(6) -- not an array if parameters being passed.
            args = Arguments()
            [
                LOOKAHEAD(2)
                anonymousBody = ClassOrInterfaceBody()
            ]
            { ret = new ObjectCreationExpr(range(begin, token()), scope, (ClassOrInterfaceType) type, typeArgs.list, args, anonymousBody); }
        )
//     |
        // TODO: new RecordType
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.10
 * <pre>{@code
 *     ArrayCreationExpression:
 *         new PrimitiveType DimExprs [Dims]
 *         new ClassOrInterfaceType DimExprs [Dims]
 *         new PrimitiveType Dims ArrayInitializer
 *         new ClassOrInterfaceType Dims ArrayInitializer
 *     DimExprs:
 *         DimExpr {DimExpr}
 *     DimExpr:
 *         {Annotation} [ Expression ]
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }</pre>
 */
ArrayCreationExpr ArrayCreation(JavaToken begin, Type type):
{
    Expression expr = null;
    ArrayInitializerExpr arrayInitializerExpr = null;
    NodeList<Expression> inits = emptyNodeList();
    List<NodeList<AnnotationExpr>> accum = new ArrayList<NodeList<AnnotationExpr>>();
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
    JavaToken arrayCreationLevelStart = INVALID;
    List<TokenRange> levelRanges = new ArrayList<TokenRange>();
}
{
    (
        LOOKAHEAD(2)
        annotations = Annotations()
        "[" { arrayCreationLevelStart = annotations.isEmpty() ? token() : orIfInvalid(arrayCreationLevelStart, annotations.get(0)); }
        [expr = Expression()] { accum = add(accum, annotations); inits = add(inits, expr); annotations=null; expr=null; }
        "]" { levelRanges.add(range(arrayCreationLevelStart, token())); }
    )+
    [arrayInitializerExpr = ArrayInitializer()]
    {
        return juggleArrayCreation(range(begin, token()), levelRanges, type, inits, accum, arrayInitializerExpr);
    }
}

/*
 * Statement syntax follows.
 */

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.5
 * <pre>{@code
 *     Statement:
 *         StatementWithoutTrailingSubstatement
 *         LabeledStatement
 *         IfThenStatement
 *         IfThenElseStatement
 *         WhileStatement
 *         ForStatement
 *     StatementNoShortIf:
 *         StatementWithoutTrailingSubstatement
 *         LabeledStatementNoShortIf
 *         IfThenElseStatementNoShortIf
 *         WhileStatementNoShortIf
 *         ForStatementNoShortIf
 *     StatementWithoutTrailingSubstatement:
 *         Block
 *         EmptyStatement
 *         ExpressionStatement
 *         AssertStatement
 *         SwitchStatement
 *         DoStatement
 *         BreakStatement
 *         ContinueStatement
 *         ReturnStatement
 *         SynchronizedStatement
 *         ThrowStatement
 *         TryStatement
 *         YieldStatement
 * }</pre>
 */
Statement Statement():
{
    Statement ret;
}
{
    try {
        (
            LOOKAHEAD(2) ret = LabeledStatement()
            | ret = AssertStatement()
            | LOOKAHEAD(3) ret = YieldStatement()
            | ret = Block()
            | ret = EmptyStatement()
            | ret = StatementExpression()
            | ret = SwitchStatement()
            | ret = IfStatement()
            | ret = WhileStatement()
            | ret = DoStatement()
            | ret = ForStatement()
            | ret = BreakStatement()
            | ret = ContinueStatement()
            | ret = ReturnStatement()
            | ret = ThrowStatement()
            | ret = SynchronizedStatement()
            | ret = TryStatement()
        )
        { return ret; }
    } catch (ParseException e) {
        TokenRange errorRange = recoverStatement(SEMICOLON, LBRACE, RBRACE, e);
        return new UnparsableStmt(errorRange);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.10
 * <pre>{@code
 *     AssertStatement:
 *         assert Expression ;
 *         assert Expression : Expression ;
 * }</pre>
 */
AssertStmt AssertStatement():
{
    Expression check;
    Expression msg = null;
    JavaToken begin;
}
{
    "assert" { begin=token(); }
    check = Expression()
    [ ":" msg = Expression() ]
    ";"
    { return new AssertStmt(range(begin, token()), check, msg);  }
}

/**
 * // TODO: Examine more closely.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.7
 * <pre>{@code
 *     LabeledStatement:
 *         Identifier : Statement
 *     LabeledStatementNoShortIf:
 *         Identifier : StatementNoShortIf
 * }</pre>
 */
LabeledStmt LabeledStatement():
{
    SimpleName label;
    Statement stmt;
    JavaToken begin;
}
{
    label = SimpleName() { begin=token(); }
    ":"
    stmt = Statement()
    { return new LabeledStmt(range(begin, token()), label, stmt); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.2
 * <pre>{@code
 *     Block:
 *         { [BlockStatements] }
 *     BlockStatements:
 *         BlockStatement {BlockStatement}
 *     BlockStatement:
 *         LocalVariableDeclarationStatement
 *         ClassDeclaration
 *         Statement
 * }</pre>
 */
BlockStmt Block():
{
    NodeList<Statement> stmts = emptyNodeList();
    JavaToken begin;
}
{
    "{" { begin=token(); }
    try {
        stmts = Statements()
        "}"
        { return new BlockStmt(range(begin, token()), stmts); }
    } catch (ParseException e) {
        recover(RBRACE, e);
        BlockStmt block = new BlockStmt(range(begin, token()), new NodeList<Statement>());
        block.setParsed(UNPARSABLE);
        return block;
    }
}

/**
 * Classes inside body statements can only be abstract or final. The semantic checks must check it.
 * TODO/FIXME: Note that the JLS specifies {@code ClassDeclaration} (thus {@code NormalClassDeclaration} and {@code EnumDeclaration}, but not interface.
 * TODO/FIXME: Is this a bug in the grammar?
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-Statement
 * <pre>{@code
 *     Block:
 *         { [BlockStatements] }
 *     BlockStatements:
 *         BlockStatement {BlockStatement}
 *     BlockStatement:
 *         LocalVariableDeclarationStatement
 *         ClassDeclaration
 *         Statement
 * }</pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.1
 * <pre>{@code
 *     ClassDeclaration:
 *         NormalClassDeclaration
 *         EnumDeclaration
 *     NormalClassDeclaration:
 *         {ClassModifier} class TypeIdentifier [TypeParameters] [Superclass] [Superinterfaces] ClassBody
 * }</pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-8.html#jls-8.9
 * <pre>{@code
 *     EnumDeclaration:
 *         {ClassModifier} enum TypeIdentifier [Superinterfaces] EnumBody
 * }</pre>
 *
 */
Statement BlockStatement():
{
    Statement ret;
    Expression expr;
    ClassOrInterfaceDeclaration typeDecl;
    RecordDeclaration recordDeclaration;
    ModifierHolder modifier;
}
{
    try {
        (
            LOOKAHEAD( Modifiers() ("class" | "interface") ) // JLS specifies local class. Since java 16 local enum or interface are allowed.
            modifier = Modifiers()
            typeDecl = ClassOrInterfaceDeclaration(modifier) { ret = new LocalClassDeclarationStmt(range(typeDecl, token()), typeDecl); }
         |
            // Extended lookahead required to allow for e.g. `record.toString()` where looking past "record" is required for disambiguation
            LOOKAHEAD( Modifiers() "record" SimpleName() [ TypeParameters() ] Parameters() )
            modifier = Modifiers()
            recordDeclaration = RecordDeclaration(modifier) { ret = new LocalRecordDeclarationStmt(range(recordDeclaration, token()), recordDeclaration); }
         |
            // try yield statement separate from more general Statement() because yield is not a keyword but
            // just a restricted identifier and a yield statement can be confused with VariableDeclarationExpression sometimes
            LOOKAHEAD( YieldStatement() )
            ret = YieldStatement()
         |
            LOOKAHEAD( VariableDeclarationExpression() )
            expr = VariableDeclarationExpression()
            ";"
            { ret = new ExpressionStmt(range(expr, token()), expr); }
         |
            ret = Statement()
        )
        { return ret; }
    } catch (ParseException e) {
        TokenRange errorRange = recoverStatement(SEMICOLON, LBRACE, RBRACE, e);
        return new UnparsableStmt(errorRange);
    }
}

VariableDeclarationExpr VariableDeclarationExpression():
{
    ModifierHolder modifier;
    Type partialType;
    NodeList<VariableDeclarator> variables = new NodeList<VariableDeclarator>();
    VariableDeclarator var;
}
{
    modifier = Modifiers()
    partialType = Type(emptyNodeList())
    var = VariableDeclarator(partialType) { variables.add(var); }
    (
        ","
        var = VariableDeclarator(partialType) { variables.add(var); }
    )*

    {
        JavaToken begin=orIfInvalid(modifier.begin, partialType);
        return new VariableDeclarationExpr(range(begin, token()), modifier.modifiers, modifier.annotations, variables);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.6
 * <pre>{@code
 *     EmptyStatement:
 *         ;
 * }</pre>
 */
EmptyStmt EmptyStatement():
{}
{
    ";"
    { return new EmptyStmt(tokenRange()); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.27.2
 * <pre>{@code
 *     LambdaBody:
 *         Expression
 *         Block
 * }</pre>
 */
Statement LambdaBody():
{
    Expression expr;
    Statement n = null;
}
{
    (
        expr = Expression() { n = new ExpressionStmt(range(expr, token()), expr); }
     |
        n = Block()
    )
    { return n; }
}

ExpressionStmt StatementExpression():
/*
 * The last expansion of this production accepts more than the legal
 * Java expansions for StatementExpression.  This expansion does not
 * use PostfixExpression for performance reasons.
 */
{
    Expression expr;
    AssignExpr.Operator op;
    Expression value;
    RangedList<Type> typeArgs = new RangedList<Type>(null);
    Statement lambdaBody;
}
{
    (
        LOOKAHEAD(2)
        expr = PreIncrementExpression()
     |
        expr = PreDecrementExpression()
     |
        expr = PrimaryExpression()
        [
            "++" { expr = new UnaryExpr(range(expr, token()), expr, UnaryExpr.Operator.POSTFIX_INCREMENT);  }
         |
            "--" { expr = new UnaryExpr(range(expr, token()), expr, UnaryExpr.Operator.POSTFIX_DECREMENT);  }
         |
            op = AssignmentOperator()
            value = Expression() { expr = new AssignExpr(range(expr, token()), expr, value, op); }
        ]
    )
    ";"
    { return new ExpressionStmt(range(expr, token()), expr); }
}


/**
 * Note that the {@code SwitchExpression} and {@code SwitchStatemnt} are similar but different. // TODO: Why/How?
 * // TODO: Examine more closely.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.11
 * <pre>{@code
 *     SwitchStatement:
 *         switch ( Expression ) SwitchBlock
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     SwitchBlock:
 *         { SwitchRule {SwitchRule} }
 *         { {SwitchBlockStatementGroup} {SwitchLabel :} }
 *     SwitchRule:
 *         SwitchLabel -> Expression ;
 *         SwitchLabel -> Block
 *         SwitchLabel -> ThrowStatement
 *     SwitchBlockStatementGroup:
 *         SwitchLabel : {SwitchLabel :} BlockStatements
 *     SwitchLabel:
 *         case CaseConstant {, CaseConstant}
 *         default
 *     CaseConstant:
 *         ConditionalExpression
 * }</pre>
 */
SwitchStmt SwitchStatement():
{
    Expression selector;
    SwitchEntry entry;
    NodeList<SwitchEntry> entries = emptyNodeList() ;
    JavaToken begin;
}
{
    "switch" { begin=token(); }
    "("
    selector = Expression()
    ")"
    "{"
    (
        entry = SwitchEntry() { entries = add(entries, entry); }
    )*
    "}"

    { return new SwitchStmt(range(begin, token()), selector, entries); }
}

/**
 * Note that the {@code SwitchExpression} and {@code SwitchStatemnt} are similar but different. // TODO: Why/How?
 * // TODO: Examine more closely.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.28
 * <pre>{@code
 *     SwitchExpression:
 *         switch ( Expression ) SwitchBlock
 * }</pre>
 * For Convenience:
 * <pre>{@code
 *     SwitchBlock:
 *         { SwitchRule {SwitchRule} }
 *         { {SwitchBlockStatementGroup} {SwitchLabel :} }
 *     SwitchRule:
 *         SwitchLabel -> Expression ;
 *         SwitchLabel -> Block
 *         SwitchLabel -> ThrowStatement
 *     SwitchBlockStatementGroup:
 *         SwitchLabel : {SwitchLabel :} BlockStatements
 *     SwitchLabel:
 *         case CaseConstant {, CaseConstant}
 *         default
 *     CaseConstant:
 *         ConditionalExpression
 * }</pre>
 */
SwitchExpr SwitchExpression():
{
    Expression selector;
    SwitchEntry entry;
    NodeList<SwitchEntry> entries = emptyNodeList();
    JavaToken begin;
}
{
    "switch" { begin=token(); }
    "("
    selector = Expression()
    ")"
    "{"
    (
        entry = SwitchEntry() { entries = add(entries, entry); }
    )*
    "}"

    { return new SwitchExpr(range(begin, token()), selector, entries); }
}

/**
 * Note that the {@code SwitchBlock} (JLS) and {@code SwitchEntry} (JavaParser) are equivlent.
 * // TODO: Examine more closely.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.11.1
 * <pre>{@code
 *     SwitchBlock:
 *         { SwitchRule {SwitchRule} }
 *         { {SwitchBlockStatementGroup} {SwitchLabel :} }
 *     SwitchRule:
 *         SwitchLabel -> Expression ;
 *         SwitchLabel -> Block
 *         SwitchLabel -> ThrowStatement
 *     SwitchBlockStatementGroup:
 *         SwitchLabel : {SwitchLabel :} BlockStatements
 *     SwitchLabel:
 *         case CaseConstant {, CaseConstant}
 *         case null [, default]
 *         case CasePattern [Guard]
 *         default
 *     CaseConstant:
 *         ConditionalExpression
 *     CasePattern:
 *         Pattern
 *     Guard:
 *         when Expression
 * }</pre>
 */
SwitchEntry SwitchEntry():
{
    Expression label = null;
    NodeList<Expression> labels = emptyNodeList();
    NodeList<Statement> stmts = emptyNodeList();
    JavaToken begin;
    SwitchEntry ret;
    Statement stmt = null;
    Expression expr = null;
    boolean isDefault = false;
    Expression guard = null;
}
{
    (
        "case" { begin=token(); }
        (
            /**
             * For the sake of keeping the lookaheads simple and fast, the cases here aren't checked
             * in the order in which they appear in the JLS. The order checked here corresponds to
             *
             * SwitchLabel:
             *   case null [, default]
             *   case CasePattern [Guard]
             *   case CaseConstant {, CaseConstant}
             *
             * This LOOKAHEAD(3) checks specifically for the "null , default" token sequence.
             */
            LOOKAHEAD(3)
            label = NullLiteral() { labels = add(labels, label); }
            ","
            "default" { isDefault = true; }
          |
            /**
            * This lookahead is necessary to avoid ambiguity with the ConditionalExpression branch
            * below.
            */
            LOOKAHEAD(PatternExpression())
            label = PatternExpression() { labels = add(labels, label); }
            [
              "when"
              guard = ConditionalExpression()
            ]
          |
            label = ConditionalExpression() { labels = add(labels, label); }
            (
                ","
                label = ConditionalExpression()  { labels = add(labels, label); }
            )*
        )
     |
        "default" { begin=token(); isDefault = true; }
    )
    (
        ":"
        stmts = Statements()
        { ret = new SwitchEntry(range(begin, token()), labels, STATEMENT_GROUP, stmts, isDefault, guard); }
     |
        "->"
        (
            stmt = SwitchEntryExpression()
            {
                TokenRange r=range(begin, token());
                stmts.add(stmt);
                ret = new SwitchEntry(r, labels, EXPRESSION, stmts, isDefault, guard);
            }
         |
            stmt = Block()
            {
                TokenRange r=range(begin, token());
                stmts.add(stmt);
                ret = new SwitchEntry(r, labels, BLOCK, stmts, isDefault, guard);
            }
         |
            stmt = ThrowStatement()
            {
                TokenRange r=range(begin, token());
                stmts.add(stmt);
                ret = new SwitchEntry(r, labels, THROWS_STATEMENT, stmts, isDefault, guard);
            }
        )
    )
    { return ret; }
}

/**
* This method provides special handling for the case
*
*<pre>{@code
*     SwitchRule:
*         SwitchLabel -> Expression ;
* }</pre}
*
* for switch entries in the JLS. The expression in the entry body is
* represented as an ExpressionStmt in JavaParser, but the regular
* StatementExpression() production cannot be used since that only
* supports a subset of expressions while any expression is supported
* in the entry body.
*/
ExpressionStmt SwitchEntryExpression():
{
    Expression expr;
}
{
    expr = Expression()
    ";"
    {
        TokenRange r = expr.getTokenRange().orElse(null);
        if (r != null) {
            r = r.withEnd(token());
        }
        return new ExpressionStmt(r, expr);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.9
 * <pre>{@code
 *     IfThenStatement:
 *         if ( Expression ) Statement
 *     IfThenElseStatement:
 *         if ( Expression ) StatementNoShortIf else Statement
 *     IfThenElseStatementNoShortIf:
 *         if ( Expression ) StatementNoShortIf else StatementNoShortIf
 * }</pre>
 */
IfStmt IfStatement():
/*
 * The disambiguating algorithm of JavaCC automatically binds dangling
 * else's to the innermost if statement.  The LOOKAHEAD specification
 * is to tell JavaCC that we know what we are doing.
 */
{
    Expression condition;
    Statement thenStmt;
    Statement elseStmt = null;
    JavaToken begin;
}
{
    "if" { begin=token(); }
    "("
    condition = Expression()
    ")"
    thenStmt = Statement()
    [
        LOOKAHEAD(1)
        "else"
        elseStmt = Statement()
    ]
    {
        return new IfStmt(range(begin, token()), condition, thenStmt, elseStmt);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.12
 * <pre>{@code
 *     WhileStatement:
 *         while ( Expression ) Statement
 *     WhileStatementNoShortIf:
 *         while ( Expression ) StatementNoShortIf
 * }</pre>
 */
WhileStmt WhileStatement():
{
    Expression condition;
    Statement body;
    JavaToken begin;
}
{
    "while" { begin=token(); }
    "("
    condition = Expression()
    ")"
    body = Statement()
    { return new WhileStmt(range(begin, token()),condition, body); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.12
 * <pre>{@code
 *     DoStatement:
 *         do Statement while ( Expression ) ;
 * }</pre>
 */
DoStmt DoStatement():
{
    Expression condition;
    Statement body;
    JavaToken begin;
}
{
    "do" { begin=token(); }
    body = Statement()
    "while"
    "("
    condition = Expression()
    ")"
    ";"
    { return new DoStmt(range(begin, token()),body, condition); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.14
 * <pre>{@code
 *     ForStatement:
 *         BasicForStatement
 *         EnhancedForStatement
 *     ForStatementNoShortIf:
 *         BasicForStatementNoShortIf
 *         EnhancedForStatementNoShortIf
 * }</pre>
 */
Statement ForStatement():
{
    VariableDeclarationExpr varExpr = null;
    Expression expr = null;
    NodeList<Expression> init = emptyNodeList();
    NodeList<Expression> update = emptyNodeList();
    Statement body;
    JavaToken begin;
}
{
    "for" { begin=token(); }
    "("

    (
        // Enhanced for statement
        LOOKAHEAD(VariableDeclarationExpression() ":")
        varExpr = VariableDeclarationExpression()
        ":"
        expr = Expression()
     |
        // Basic for statement
        [ init = ForInit() ]
        ";"
        [ expr = Expression() ]
        ";"
        [ update = ForUpdate() ]
    )

    ")"
    body = Statement()

    {
        if (varExpr != null) {
            return new ForEachStmt(range(begin, token()),varExpr, expr, body);
        }
        return new ForStmt(range(begin, token()),init, expr, update, body);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.14
 * <pre>{@code
 *     BasicForStatement:
 *         for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
 *     BasicForStatementNoShortIf:
 *         for ( [ForInit] ; [Expression] ; [ForUpdate] ) StatementNoShortIf
 *     ForInit:
 *         StatementExpressionList
 *         LocalVariableDeclaration
 *     ForUpdate:
 *         StatementExpressionList
 *     StatementExpressionList:
 *         StatementExpression {, StatementExpression}
 * }</pre>
 */
NodeList<Expression> ForInit():
{
    NodeList<Expression> ret;
    Expression expr;
}
{
    (
        LOOKAHEAD( Modifiers() Type() Identifier() )
        expr = VariableDeclarationExpression() { ret = new NodeList<Expression>(); ret.add(expr); }
     |
        ret = ExpressionList()
    )
    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.14
 * <pre>{@code
 *     BasicForStatement:
 *         for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
 *     BasicForStatementNoShortIf:
 *         for ( [ForInit] ; [Expression] ; [ForUpdate] ) StatementNoShortIf
 *     ForInit:
 *         StatementExpressionList
 *         LocalVariableDeclaration
 *     ForUpdate:
 *         StatementExpressionList
 *     StatementExpressionList:
 *         StatementExpression {, StatementExpression}
 * }</pre>
 */
NodeList<Expression> ExpressionList():
{
    NodeList<Expression> ret = new NodeList<Expression>();
    Expression expr;
}
{
    expr = Expression() { ret.add(expr); }
    (
        ","
        expr = Expression()  { ret.add(expr); }
    )*

    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.14
 * <pre>{@code
 *     BasicForStatement:
 *         for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
 *     BasicForStatementNoShortIf:
 *         for ( [ForInit] ; [Expression] ; [ForUpdate] ) StatementNoShortIf
 *     ForInit:
 *         StatementExpressionList
 *         LocalVariableDeclaration
 *     ForUpdate:
 *         StatementExpressionList
 *     StatementExpressionList:
 *         StatementExpression {, StatementExpression}
 * }</pre>
 */
NodeList<Expression> ForUpdate():
{
    NodeList<Expression> ret;
}
{
    ret = ExpressionList()

    { return ret; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.15
 * <pre>{@code
 *     BreakStatement:
 *         break [Identifier] ;
 * }</pre>
 */
BreakStmt BreakStatement():
{
    SimpleName label = null;
    JavaToken begin;
}
{
    "break" { begin=token(); }
    [ label = SimpleName() ]
    ";"
    { return new BreakStmt(range(begin, token()), label); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.21
 * <pre>{@code
 *     YieldStatement:
 *         yield Expression ;
 * }</pre>
 */
YieldStmt YieldStatement():
{
    Expression value;
    JavaToken begin;
}
{
    "yield" { begin=token(); }
    value = Expression()
    ";"
    { return new YieldStmt(range(begin, token()), value); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.16
 * <pre>{@code
 *     ContinueStatement:
 *         continue [Identifier] ;
 * }</pre>
 */
ContinueStmt ContinueStatement():
{
    SimpleName label = null;
    JavaToken begin;
}
{
    "continue" { begin=token(); }
    [ label = SimpleName() ]
    ";"
    { return new ContinueStmt(range(begin, token()), label); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.17
 * <pre>{@code
 *     ReturnStatement:
 *         return [Expression] ;
 * }</pre>
 */
ReturnStmt ReturnStatement():
{
    Expression expr = null;
    JavaToken begin;
}
{
    "return" { begin=token(); }
    [ expr = Expression() ]
    ";"
    { return new ReturnStmt(range(begin, token()),expr); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.18
 * <pre>{@code
 *     ThrowStatement:
 *         throw Expression ;
 * }</pre>
 */
ThrowStmt ThrowStatement():
{
    Expression expr;
    JavaToken begin;
}
{
    "throw" { begin=token(); }
    expr = Expression()
    ";"
    { return new ThrowStmt(range(begin, token()),expr); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.19
 * <pre>{@code
 *     SynchronizedStatement:
 *         synchronized ( Expression ) Block
 * }</pre>
 */
SynchronizedStmt SynchronizedStatement():
{
    Expression expr;
    BlockStmt body;
    JavaToken begin;
}
{
    "synchronized" { begin=token(); }
    "("
    expr = Expression()
    ")"
    body = Block()
    { return new SynchronizedStmt(range(begin, token()),expr, body); }
}

/**
 * // TODO: Examine more closely.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.20
 * <pre>{@code
 *     TryStatement:
 *         try Block Catches
 *         try Block [Catches] Finally
 *         TryWithResourcesStatement
 *     Catches:
 *         CatchClause {CatchClause}
 *     CatchClause:
 *         catch ( CatchFormalParameter ) Block
 *     CatchFormalParameter:
 *        {VariableModifier} CatchType VariableDeclaratorId
 *     CatchType:
 *         UnannClassType {| ClassType}
 *     Finally:
 *         finally Block
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     VariableModifier:
 *         Annotation
 *         final
 *     VariableDeclaratorId:
 *         Identifier [Dims]
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }<pre>
 */
TryStmt TryStatement():
{
    NodeList<Expression> resources = emptyNodeList();
    BlockStmt tryBlock;
    BlockStmt finallyBlock = null;
    NodeList<CatchClause> catchs = emptyNodeList();
    BlockStmt catchBlock;
    ModifierHolder exceptModifier;
    ReferenceType exceptionType;
    NodeList<ReferenceType> exceptionTypes = emptyNodeList();
    Pair<SimpleName, List<ArrayBracketPair>> exceptId;
    JavaToken begin;
    JavaToken catchBegin;
    JavaToken typesBegin;
    JavaToken paramEnd;
    Type type;
}
{
    "try" { begin=token(); }
    [ resources = ResourceSpecification() ]
    tryBlock = Block()
    (
        LOOKAHEAD(2)
        (
            "catch" {catchBegin=token();}
            "("
            exceptModifier = Modifiers() { typesBegin = exceptModifier.begin; }
            exceptionType = ReferenceType(emptyNodeList()) { exceptionTypes.add(exceptionType); typesBegin = orIfInvalid(typesBegin, token()); }
            (
                "|"
                exceptionType = AnnotatedReferenceType() { exceptionTypes.add(exceptionType); }
            )*
            exceptId = VariableDeclaratorId() { paramEnd = token(); }
            ")"

            catchBlock = Block()
            {
                if (exceptionTypes.size() > 1) {
                    type = new UnionType(range(exceptionTypes.get(0), exceptionTypes.get(exceptionTypes.size() - 1)), exceptionTypes);
                } else {
                    type = (Type)exceptionTypes.get(0);
                }
                Parameter catchType = new Parameter(range(typesBegin, paramEnd), exceptModifier.modifiers, exceptModifier.annotations, type, false, emptyNodeList(), exceptId.a);
                catchs = add(catchs, new CatchClause(range(catchBegin, token()), catchType, catchBlock));
                exceptionTypes = emptyNodeList();
            }
        )*
        [
            "finally"
            finallyBlock = Block()
        ]
     |
        "finally"
        finallyBlock = Block()
    )
    { return new TryStmt(range(begin, token()), resources, tryBlock, catchs, finallyBlock); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.20.3
 * <pre>{@code
 *     TryWithResourcesStatement:
 *         try ResourceSpecification Block [Catches] [Finally]
 *     ResourceSpecification:
 *         ( ResourceList [;] )
 *     ResourceList:
 *         Resource {; Resource}
 *     Resource:
 *         {VariableModifier} LocalVariableType Identifier = Expression
 *         VariableAccess
 *     VariableAccess:
 *         ExpressionName
 *         FieldAccess
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     VariableModifier:
 *         Annotation
 *         final
 *     LocalVariableType:
 *         UnannType
 *         var
 * }<pre>
 */
NodeList<Expression> ResourceSpecification():
{
    NodeList<Expression> variables;
}
{
    "("
    variables = Resources()
    (LOOKAHEAD(2) ";")?
    ")"
    { return variables; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.20.3
 * <pre>{@code
 *     ResourceList:
 *         Resource {; Resource}
 *     Resource:
 *         {VariableModifier} LocalVariableType Identifier = Expression
 *         VariableAccess
 *     VariableAccess:
 *         ExpressionName
 *         FieldAccess
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     VariableModifier:
 *         Annotation
 *         final
 *     LocalVariableType:
 *         UnannType
 *         var
 * }<pre>
 */
NodeList<Expression> Resources():
{
    NodeList<Expression> expressions = new NodeList<Expression>();
    Expression expr;
}
{
    expr = Resource() {expressions.add(expr);}
    (LOOKAHEAD(2)
        ";"
        expr = Resource() {expressions.add(expr);}
    )*
    { return expressions; }
}

/**
 * // TODO: Examine more closely.
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-14.html#jls-14.20.3
 * <pre>{@code
 *     Resource:
 *         {VariableModifier} LocalVariableType Identifier = Expression
 *         VariableAccess
 *     VariableAccess:
 *         ExpressionName
 *         FieldAccess
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     VariableModifier:
 *         Annotation
 *         final
 *     LocalVariableType:
 *         UnannType
 *         var
 * }<pre>
 */
Expression Resource():
{
    Expression expr;
}
{
    (
        LOOKAHEAD(
            Modifiers()
            partialType = Type()
            VariableDeclarator(partialType)
        )
        /*this is a bit more lenient than we need to be, e.g. allowing access modifiers like private*/
        expr = VariableDeclarationExpression()
     |
        expr = PrimaryExpression()
    )
    { return expr; }
}


/* We use productions to match >>>, >> and > so that we can keep the
 * type declaration syntax with generics clean
 */

void RUNSIGNEDSHIFT():
{}
{
    LOOKAHEAD({ getToken(1).kind == GT && getToken(1).realKind == RUNSIGNEDSHIFT} )
    ">"
    ">"
    ">"
}

void RSIGNEDSHIFT():
{}
{
    LOOKAHEAD({ getToken(1).kind == GT && getToken(1).realKind == RSIGNEDSHIFT} )
    ">"
    ">"
}


/*  ANNOTATION SYNTAX - Annotation syntax follows. */

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7
 * <pre>{@code
 *     Annotation:
 *         NormalAnnotation
 *         MarkerAnnotation
 *         SingleElementAnnotation
 * }<pre>
 */
NodeList<AnnotationExpr> Annotations():
{
    NodeList<AnnotationExpr> annotations = new NodeList<AnnotationExpr>();
    AnnotationExpr annotation;
}
{
    (
        LOOKAHEAD("@")
        annotation = Annotation() { annotations = add(annotations, annotation); }
    )*
    { return annotations; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7
 * <pre>{@code
 *     Annotation:
 *         NormalAnnotation
 *         MarkerAnnotation
 *         SingleElementAnnotation
 * }<pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.1
 * <pre>{@code
 *     NormalAnnotation:
 *         @ TypeName ( [ElementValuePairList] )
 *     ElementValuePairList:
 *         ElementValuePair {, ElementValuePair}
 *     ElementValuePair:
 *         Identifier = ElementValue
 *     ElementValue:
 *         ConditionalExpression
 *         ElementValueArrayInitializer
 *         Annotation
 *     ElementValueArrayInitializer:
 *         { [ElementValueList] [,] }
 *     ElementValueList:
 *         ElementValue {, ElementValue}
 * }<pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.2
 * <blockquote>
 * It [The marker annotation] is shorthand for the normal annotation:
 * {@code @TypeName()}
 * </blockquote>
 * <pre>{@code
 *     MarkerAnnotation:
 *         @ TypeName
 * }<pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.3
 * <blockquote>
 * It [The single element annotation] is shorthand for the normal annotation:
 * {@code @TypeName(value = ElementValue)}
 * </blockquote>
 * <pre>{@code
 *     SingleElementAnnotation:
 *         @ TypeName ( ElementValue )
 * }<pre>
 */
AnnotationExpr Annotation():
{
    AnnotationExpr ret;
    Name name;
    NodeList<MemberValuePair> pairs = emptyNodeList();
    JavaToken begin;
    Expression memberVal;
}
{
    "@" { begin=token(); }
    name = Name()
    (
        LOOKAHEAD("(" ( Identifier() "=" | ")" ))
        "("
        [ pairs = MemberValuePairs() ]
        ")"
        { ret = new NormalAnnotationExpr(range(begin, token()), name, pairs); }
     |
        LOOKAHEAD( "(" )
        "("
        memberVal = MemberValue()
        ")"
        { ret = new SingleMemberAnnotationExpr(range(begin, token()), name, memberVal); }
     |
        { ret = new MarkerAnnotationExpr(range(begin, token()), name); }
    )
    { return ret; }
}

/**
 * Note that {@code MemberValuePairs} (JavaParser) is synonymous with {@code ElementValuePairList} (JLS)
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.1
 * <pre>{@code
 *     NormalAnnotation:
 *         @ TypeName ( [ElementValuePairList] )
 *     ElementValuePairList:
 *         ElementValuePair {, ElementValuePair}
 *     ElementValuePair:
 *         Identifier = ElementValue
 * }<pre>
 */
NodeList<MemberValuePair> MemberValuePairs():
{
    NodeList<MemberValuePair> ret = new NodeList<MemberValuePair>();
    MemberValuePair pair;
}
{
    pair = MemberValuePair() { ret.add(pair); }
    (
        ","
        pair = MemberValuePair() { ret.add(pair); }
    )*
    { return ret; }
}

/**
 * Note that {@code MemberValuePair} (JavaParser) is synonymous with {@code ElementValuePair} (JLS)
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.1
 * <pre>{@code
 *     ElementValuePair:
 *         Identifier = ElementValue
 *     ElementValue:
 *         ConditionalExpression
 *         ElementValueArrayInitializer
 *         Annotation
 *     ElementValueArrayInitializer:
 *         { [ElementValueList] [,] }
 *     ElementValueList:
 *         ElementValue {, ElementValue}
 * }<pre>
 */
MemberValuePair MemberValuePair():
{
    SimpleName name;
    Expression value;
    JavaToken begin;
}
{
    name = SimpleName() { begin=token();}
    "="
    value = MemberValue()
    { return new MemberValuePair(range(begin, token()),name, value); }
}

/**
 * Note that {@code MemberValue} (JavaParser) is synonymous with {@code ElementValue} (JLS)
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.1
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-ElementValue
 * <pre>{@code
 *     ElementValue:
 *         ConditionalExpression
 *         ElementValueArrayInitializer
 *         Annotation
 *     ElementValueArrayInitializer:
 *         { [ElementValueList] [,] }
 *     ElementValueList:
 *         ElementValue {, ElementValue}
 * }<pre>
 */
Expression MemberValue():
{
    Expression ret;
}
{
    (
        LOOKAHEAD("@")
        ret = Annotation()
     |
        ret = MemberValueArrayInitializer()
     |
        ret = ConditionalExpression()
    )
    { return ret; }
}

/**
 * Note that {@code MemberValueArrayInitializer} (JavaParser) is synonymous with {@code ElementValueArrayInitializer} (JLS)
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.1
 * <pre>{@code
 *     ElementValueArrayInitializer:
 *         { [ElementValueList] [,] }
 *     ElementValueList:
 *         ElementValue {, ElementValue}
 * }<pre>
 */
Expression  MemberValueArrayInitializer():
{
    NodeList<Expression> ret = emptyNodeList();
    Expression member;
    JavaToken begin;
}
{
    "{" { begin=token(); }
    [
        member = MemberValue() { ret.add(member); }
        (
            LOOKAHEAD(2)
            ","
            member = MemberValue() { ret.add(member); }
        )*
    ]
    [ "," ]
    "}"
    { return new ArrayInitializerExpr(range(begin, token()),ret); }
}


/* ANNOTATION TYPES */

/**
 * <blockquote>
 * An annotation type declaration specifies a new annotation type, a special kind of interface type.
 * To distinguish an annotation type declaration from a normal interface declaration, the keyword interface is preceded by an at-sign (@).
 * </blockquote>
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.6
 * <pre>{@code
 *     AnnotationTypeDeclaration:
 *         {InterfaceModifier} @ interface TypeIdentifier AnnotationTypeBody
 * }<pre>
 */
AnnotationDeclaration AnnotationTypeDeclaration(ModifierHolder modifier):
{
    SimpleName name;
    NodeList<BodyDeclaration<?>> members = emptyNodeList();
    JavaToken begin = modifier.begin;
}
{
    "@" { begin=orIfInvalid(begin, token()); }
    "interface"
    name = SimpleName()
    members = AnnotationTypeBody()
    { return new AnnotationDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, name, members); }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.6.1
 * <pre>{@code
 *     AnnotationTypeBody:
 *         { {AnnotationTypeMemberDeclaration} }
 *     AnnotationTypeMemberDeclaration:
 *         AnnotationTypeElementDeclaration
 *         ConstantDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 * }<pre>
 */
NodeList<BodyDeclaration<?>> AnnotationTypeBody():
{
    NodeList<BodyDeclaration<?>> ret = emptyNodeList();
    BodyDeclaration member;
}
{
    "{"
    (
        member = AnnotationBodyDeclaration() { ret = addWhenNotNull(ret, member); }
     |
        ";"
    )*
    "}"
    { return ret; }
}

/**
 * Note that {@code AnnotationTypeMemberDeclaration} (JLS) and {@code AnnotationBodyDeclaration} (JavaParser) are synonymous.
 https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.6.1
 * <pre>{@code
 *     AnnotationTypeBody:
 *         { {AnnotationTypeMemberDeclaration} }
 *     AnnotationTypeMemberDeclaration:
 *         AnnotationTypeElementDeclaration
 *         ConstantDeclaration
 *         ClassDeclaration
 *         InterfaceDeclaration
 *         ;
 *     AnnotationTypeElementDeclaration:
 *         {AnnotationTypeElementModifier} UnannType Identifier ( ) [Dims] [DefaultValue] ;
 *     AnnotationTypeElementModifier:
 *         (one of)
 *         Annotation public
 *         abstract
 *     ClassDeclaration:
 *         NormalClassDeclaration
 *         EnumDeclaration
 *         RecordDeclaration
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }<pre>
 */
BodyDeclaration<?> AnnotationBodyDeclaration():
{
    ModifierHolder modifier;
    BodyDeclaration ret;
}
{
    modifier = Modifiers()
    (
        // This case must be checked before AnnotationTypeMemberDeclaration since the prefixes are the same
        LOOKAHEAD(RecordDeclaration())
        ret = RecordDeclaration(modifier)
     |
        LOOKAHEAD(Type() Identifier() "(")
        ret = AnnotationTypeMemberDeclaration(modifier)
     |
        ret = ClassOrInterfaceDeclaration(modifier)
     |
        LOOKAHEAD("enum")
        ret = EnumDeclaration(modifier)
     |
        ret = AnnotationTypeDeclaration(modifier)
     |
        ret = FieldDeclaration(modifier)
    )
    { return ret; }
}

/**
 * Note that {@code AnnotationTypeElementDeclaration} (JLS) and {@code AnnotationTypeMemberDeclaration} (JavaParser) are synonymous.
 * // TODO/FIXME: Consider missing `[Dims] (present in the JLS, but not the JavaParser grammar)
 * // TODO/FIXME: {AnnotationTypeElementModifier} UnannType Identifier ( ) [Dims] [DefaultValue] ;
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.6.1
 * <pre>{@code
 *     AnnotationTypeElementDeclaration:
 *         {AnnotationTypeElementModifier} UnannType Identifier ( ) [Dims] [DefaultValue] ;
 *     AnnotationTypeElementModifier:
 *         (one of)
 *         Annotation public
 *         abstract
 * }<pre>
 * For Convenience:
 * <pre>{@code
 *     Dims:
 *         {Annotation} [ ] {{Annotation} [ ]}
 * }<pre>
 */
AnnotationMemberDeclaration AnnotationTypeMemberDeclaration(ModifierHolder modifier):
{
    Type type;
    SimpleName name;
    Expression defaultVal = null;
}
{
    // TODO/FIXME: Consider missing `[Dims] (present in the JLS, but not the JavaParser grammar)
    // TODO/FIXME: {AnnotationTypeElementModifier} UnannType Identifier ( ) [Dims] [DefaultValue] ;
    type = Type(emptyNodeList())
    name = SimpleName()
    "(" ")"
    [ defaultVal = DefaultValue() ]
    ";"

    {
        JavaToken begin = orIfInvalid(modifier.begin, type);
        return new AnnotationMemberDeclaration(range(begin, token()), modifier.modifiers, modifier.annotations, type, name, defaultVal);
    }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.6.2
 * <pre>{@code
 *     DefaultValue:
 *     default ElementValue
 * }<pre>
 * For Convenience:
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-9.7.1
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-9.html#jls-ElementValue
 * <pre>{@code
 *     ElementValue:
 *         ConditionalExpression
 *         ElementValueArrayInitializer
 *         Annotation
 *     ElementValueArrayInitializer:
 *         { [ElementValueList] [,] }
 *     ElementValueList:
 *         ElementValue {, ElementValue}
 * }<pre>
 */
Expression DefaultValue():
{
    Expression ret;
}
{
    "default"
    ret = MemberValue()
    { return ret; }
}


/* MODULES - Module syntax follows */

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-7.html#jls-ModuleDirective
 * <pre>{@code
 *     ModuleDirective:
 *         requires {RequiresModifier} ModuleName ;
 *         exports PackageName [to ModuleName {, ModuleName}] ;
 *         opens PackageName [to ModuleName {, ModuleName}] ;
 *         uses TypeName ;
 *         provides TypeName with TypeName {, TypeName} ;
 *     RequiresModifier:
 *         (one of)
 *         transitive static
 * }<pre>
 */
ModuleDirective ModuleDirective():
{
    ModifierHolder modifiers;
    Name name;
    Name tmpName;
    NodeList<Name> names=emptyNodeList();
    JavaToken begin;
    ModuleDirective directive;
    JavaToken transitiveExceptionalToken;
}
{
    (
        // {@code requires {RequiresModifier} ModuleName ;}
        // This is a hack for the edge case "requires transitive;" which is supposed to mean "require the module named 'transitive'"
        LOOKAHEAD(<REQUIRES> <TRANSITIVE> ";")
        <REQUIRES> { begin=token(); }
        <TRANSITIVE> {transitiveExceptionalToken=token(); setTokenKind(IDENTIFIER);}
        ";"
        { directive=new ModuleRequiresDirective(range(begin, token()), new NodeList<Modifier>(), new Name(range(transitiveExceptionalToken, transitiveExceptionalToken), null, transitiveExceptionalToken.getText())); }
    |
        // {@code requires {RequiresModifier} ModuleName ;}
        <REQUIRES> { begin=token(); }
        modifiers=Modifiers() name=Name()
        ";"
        { directive=new ModuleRequiresDirective(range(begin, token()), modifiers.modifiers, name); }
    |
        // {@code exports PackageName [to ModuleName {, ModuleName}] ;}
        <EXPORTS> { begin=token(); }
        name=Name()
        [
            <TO>
            tmpName=Name() { names.add(tmpName); }
            (
                ","
                tmpName=Name(){ names.add(tmpName); }
            )*
        ]
        ";"
        { directive=new ModuleExportsDirective(range(begin, token()), name, names); }
    |
        // {@code opens PackageName [to ModuleName {, ModuleName}] ;}
        <OPENS> { begin=token(); }
        name=Name()
        [
            <TO>
            tmpName=Name() { names.add(tmpName); }
            (
                ","
                tmpName=Name() { names.add(tmpName); }
            )*
        ]
        ";"
        { directive=new ModuleOpensDirective(range(begin, token()), name, names); }
    |
        // {@code uses TypeName ;}
        <USES> { begin=token(); }
        name=Name()
        ";"
        { directive=new ModuleUsesDirective(range(begin, token()), name); }
    |
        // {@code provides TypeName with TypeName {, TypeName} ;}
        <PROVIDES> { begin=token(); }
        name=Name()
        <WITH>
        tmpName=Name() { names.add(tmpName); }
        (
            ","
            tmpName=Name() { names.add(tmpName); }
        )*
        ";"
        { directive=new ModuleProvidesDirective(range(begin, token()), name, names);}
    )
    { return directive; }
}

/**
 * https://docs.oracle.com/javase/specs/jls/se15/html/jls-7.html#jls-7.7
 * <pre>{@code
 *     ModuleDeclaration:
 *         {Annotation} [open] module Identifier {. Identifier} { {ModuleDirective} }
 * }<pre>
 */
ModuleDeclaration ModuleDeclaration(ModifierHolder modifier):
{
    NodeList<ModuleDirective> directives = new NodeList<ModuleDirective>();
    boolean open=false;
    ModuleDirective directive;
    Name name;
    JavaToken begin = modifier.begin;
}
{
    [ <OPEN> { open = true; begin = orIfInvalid(begin, token()); } ]
    <MODULE> { begin = orIfInvalid(begin, token()); }
    name = Name()
    "{"
    (
        directive = ModuleDirective() { directives = add(directives, directive); }
    )*
    "}"
    { return new ModuleDeclaration(range(begin, token()), modifier.annotations, name, open, directives); }
}


/* Rules for matching partial inputs.
 * These rules are needed to properly terminate them -
 * if we simply use the usual rules, they will ignore everything in the provider
 * after they matched their desired input, which will lead to unexpected behaviour
 */


BlockStmt BlockParseStart():
{ BlockStmt ret; }
{ ret = Block() <EOF> { return ret; } }

Statement BlockStatementParseStart():
{ Statement ret; }
{ (LOOKAHEAD(3) ret = BlockStatement()|ret = ExplicitConstructorInvocation()) <EOF> { return ret; } }

ImportDeclaration ImportDeclarationParseStart():
{ ImportDeclaration ret; }
{ ret = ImportDeclaration() <EOF> { return ret; } }

Expression ExpressionParseStart():
{ Expression ret; }
{ ret = Expression() <EOF> { return ret; } }

AnnotationExpr AnnotationParseStart():
{ AnnotationExpr ret; }
{ ret = Annotation() <EOF> { return ret; } }

BodyDeclaration<?> AnnotationBodyDeclarationParseStart():
{ BodyDeclaration<?> ret; }
{ ret = AnnotationBodyDeclaration() <EOF> { return ret; } }

BodyDeclaration<?> ClassOrInterfaceBodyDeclarationParseStart():
{ BodyDeclaration<?> ret; }
{ ret = ClassOrInterfaceBodyDeclaration() <EOF> { return ret; } }

ClassOrInterfaceType ClassOrInterfaceTypeParseStart():
{ ClassOrInterfaceType ret; }
{ ret = AnnotatedClassOrInterfaceType() <EOF> { return ret; } }

Type ResultTypeParseStart():
{ NodeList<AnnotationExpr> annotations; Type ret; }
{ annotations = Annotations() ret = ResultType(annotations) <EOF> { return ret; } }

VariableDeclarationExpr VariableDeclarationExpressionParseStart():
{ VariableDeclarationExpr ret; }
{ ret = VariableDeclarationExpression() <EOF> { return ret; } }

ExplicitConstructorInvocationStmt ExplicitConstructorInvocationParseStart():
{ ExplicitConstructorInvocationStmt ret; }
{ ret = ExplicitConstructorInvocation() <EOF> { return ret; } }

Name NameParseStart():
{ Name ret; }
{ ret = Name() <EOF> { return ret; } }

SimpleName SimpleNameParseStart():
{ SimpleName ret; }
{ ret = SimpleName() <EOF> { return ret; } }

Parameter ParameterParseStart():
{ Parameter ret; }
{ ret = Parameter() <EOF> { return ret; } }

PackageDeclaration PackageDeclarationParseStart():
{ PackageDeclaration ret; }
{ ret = PackageDeclaration() <EOF> { return ret; } }

TypeDeclaration<?> TypeDeclarationParseStart():
{
    TypeDeclaration<?> ret;
    ModifierHolder modifier;
}
{
    modifier = Modifiers()
    (ret = ClassOrInterfaceDeclaration(modifier)
    |ret = EnumDeclaration(modifier)
    |ret = AnnotationTypeDeclaration(modifier)
    )
    <EOF>
    { return ret; }
}

ModuleDeclaration ModuleDeclarationParseStart():
{
    ModuleDeclaration ret;
    ModifierHolder modifiers;
}
{
    modifiers = Modifiers()
    ret = ModuleDeclaration(modifiers)
    <EOF>
    { return ret; }
}

ModuleDirective ModuleDirectiveParseStart():
{ ModuleDirective ret; }
{ ret = ModuleDirective() <EOF> { return ret; } }

TypeParameter TypeParameterParseStart():
{
    TypeParameter ret;
    NodeList<AnnotationExpr> annotations;
}
{
    annotations = Annotations() ret = TypeParameter(annotations) <EOF>
    { return ret; }
}

MethodDeclaration MethodDeclarationParseStart():
{
    MethodDeclaration ret;
    ModifierHolder modifier;
}
{
    modifier = Modifiers()
    ret = MethodDeclaration(modifier)
    <EOF>
    { return ret; }
}
