All Downloads are FREE. Search and download functionalities are using the official Maven repository.

4j-cypher-javacc-parser.5.25.1.source-code.cypher.jj Maven / Gradle / Ivy

There is a newer version: 5.23.0
Show newest version
/*
 * Note: this file has its license header first after PARSER_BEGIN(Cypher).
 * This allows the license header to be automatically added to the generated files.
 */

options {
  LOOKAHEAD = 1;
  CHOICE_AMBIGUITY_CHECK = 2;
  OTHER_AMBIGUITY_CHECK = 1;
  DEBUG_PARSER = false;
  DEBUG_LOOKAHEAD = false;
  DEBUG_TOKEN_MANAGER = false;
  ERROR_REPORTING = true;
  JAVA_UNICODE_ESCAPE = true;
  UNICODE_INPUT = false;
  IGNORE_CASE = false;
  USER_TOKEN_MANAGER = false;
  USER_CHAR_STREAM = true;
  BUILD_PARSER = true;
  BUILD_TOKEN_MANAGER = true;
  SANITY_CHECK = true;
  FORCE_LA_CHECK = false;
  TOKEN_EXTENDS = "WithOffset";
  COMMON_TOKEN_ACTION = true;
}

PARSER_BEGIN(Cypher)

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.neo4j.cypher.internal.parser.javacc;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import org.neo4j.cypher.internal.parser.common.ast.factory.AccessType;
import org.neo4j.cypher.internal.parser.common.ast.factory.ASTExceptionFactory;
import org.neo4j.cypher.internal.ast.factory.ASTFactory;
import org.neo4j.cypher.internal.ast.factory.ASTFactory.StringPos;
import org.neo4j.cypher.internal.parser.common.ast.factory.ActionType;
import org.neo4j.cypher.internal.parser.common.ast.factory.ConstraintType;
import org.neo4j.cypher.internal.parser.common.ast.factory.CallInTxsOnErrorBehaviourType;
import org.neo4j.cypher.internal.parser.common.ast.factory.ConstraintVersion;
import org.neo4j.cypher.internal.parser.common.ast.factory.CreateIndexTypes;
import org.neo4j.cypher.internal.parser.common.ast.factory.HintIndexType;
import org.neo4j.cypher.internal.parser.common.ast.factory.ParameterType;
import org.neo4j.cypher.internal.parser.common.ast.factory.ParserCypherTypeName;
import org.neo4j.cypher.internal.parser.common.ast.factory.ParserNormalForm;
import org.neo4j.cypher.internal.parser.common.ast.factory.ParserTrimSpecification;
import org.neo4j.cypher.internal.parser.common.ast.factory.ScopeType;
import org.neo4j.cypher.internal.parser.common.ast.factory.ShowCommandFilterTypes;
import org.neo4j.cypher.internal.parser.common.ast.factory.SimpleEither;
import org.neo4j.cypher.internal.parser.common.InvalidUnicodeLiteral;
import org.neo4j.cypher.internal.parser.common.deprecation.DeprecatedChars;

public class Cypher {

    ASTExceptionFactory exceptionFactory;
    ASTFactory astFactory;
    // to distinguish a bar that follows a label expression from the or-operator as part of a label expression, this stack keeps track of the current context
    Stack labelExpressionStack = new Stack<>();

    public Cypher(ASTFactory astFactory,
                  ASTExceptionFactory exceptionFactory,
                  CharStream stream) {
        this(stream);
        this.astFactory = astFactory;
        this.exceptionFactory = exceptionFactory;
    }

    private POS pos( Token t )
    {
        return t != null ? astFactory.inputPosition( t.beginOffset, t.beginLine, t.beginColumn ) : null;
    }

    private POS endPos( Token t )
        {
            return t != null ? astFactory.inputPosition( t.endOffset, t.endLine, t.endColumn ) : null;
        }

    private ENTITY_TYPE nodeType()
    {
        return astFactory.nodeType();
    }

    private ENTITY_TYPE relationshipType()
    {
        return astFactory.relationshipType();
    }

    private ENTITY_TYPE nodeOrRelationshipType()
    {
        return astFactory.nodeOrRelationshipType();
    }

    private void assertValidType( Token t, String expected, String actual ) throws Exception
    {
        if ( expected != null && !expected.equals(actual) )
        {
            throw exceptionFactory.syntaxException( new ParseException(
                String.format("Invalid input '%s': expected \"%s\"", t.image, expected ) ), t.beginOffset, t.beginLine, t.beginColumn );
        }
    }

    private void assertNotAlreadySet( Object object, Token token, String errorMessage ) throws Exception
    {
        if ( object != null )
        {
            throw exceptionFactory.syntaxException( new ParseException( errorMessage ), token.beginOffset, token.beginLine, token.beginColumn );
        }
    }
}

PARSER_END(Cypher)

TOKEN_MGR_DECLS :
{
    public void CommonTokenAction( Token t )
    {
        CypherCharStream ccStream = (CypherCharStream) input_stream;
        t.beginOffset = ccStream.getBeginOffset();
        t.endOffset = ccStream.getEndOffset();
    }
}

/*
 * Whitespaces are skipped, and function as a deliminiter.
 * This includes all unicode characters in the categories Zp, Zs, Zl and more selected unicodes as defined in CIP-88 and the GQL standard.
 */
SKIP :
{

    "\u0009"
    | "\n" //can't parse this in unicode
    | "\u000B"
    | "\u000C"
    | "\r" //can't parse this in unicode
    | "\u001C"
    | "\u001D"
    | "\u001E"
    | "\u001F"
    | "\u0020"
    | "\u00A0"
    | "\u1680"
    | "\u2000"
    | "\u2001"
    | "\u2002"
    | "\u2003"
    | "\u2004"
    | "\u2005"
    | "\u2006"
    | "\u2007"
    | "\u2008"
    | "\u2009"
    | "\u200A"
    | "\u2028"
    | "\u2029"
    | "\u202F"
    | "\u205F"
    | "\u3000"
}

/* COMMENTS */

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

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


SPECIAL_TOKEN :
{
    < FORMAL_COMMENT: "*/" > : DEFAULT
}


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


MORE :
{
    < ~[] >
}

/* LITERALS */

TOKEN :
{
    < DECIMAL_DOUBLE: ["0"-"9"] (  )* "." ["0"-"9"] (  )* (  )? (  )?
                      | "." ["0"-"9"] (  )* (  )? (  )?
                      | ["0"-"9"] (  )*  (  )? >
    | < UNSIGNED_DECIMAL_INTEGER: ( ( ["1"-"9"] (  )* (  )* ) | "0" ) > //0 with part letter -> Octal integer?
    | < #DECIMAL_EXPONENT: ["e","E"] ( ["+","-"] )? (  )+ (  )* >
    | < #INTEGER_PART: ( ("_")? ["0"-"9"] ) >
    | < UNSIGNED_HEX_INTEGER: "0" ["x","X"] (  )* >
    | < UNSIGNED_OCTAL_INTEGER: "0" ( "o" )? (  )* >
}

MORE : { < STRING1_OPEN: "'" > : STRING1 }
 MORE: { "\\\\" { image.delete( image.length() - 2, image.length() ); image.append( "\\" ); } }
 MORE: { "\\\'" { image.delete( image.length() - 2, image.length() ); image.append( "'" ); } }
 MORE: { "\\\"" { image.delete( image.length() - 2, image.length() ); image.append( "\"" ); } }
 MORE: { "\\b" { image.delete( image.length() - 2, image.length() ); image.append( "\b" ); } }
 MORE: { "\\f" { image.delete( image.length() - 2, image.length() ); image.append( "\f" ); } }
 MORE: { "\\n" { image.delete( image.length() - 2, image.length() ); image.append( "\n" ); } }
 MORE: { "\\r" { image.delete( image.length() - 2, image.length() ); image.append( "\r" ); } }
 MORE: { "\\t" { image.delete( image.length() - 2, image.length() ); image.append( "\t" ); } }
 MORE: { "\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]" }
 MORE: { < ~["'"] > }
 TOKEN: {  { matchedToken.image = image.substring( 1, image.length() - 1 ); } : DEFAULT }

MORE : { < STRING2_OPEN: "\"" > : STRING2 }
 MORE: { "\\\\" { image.delete( image.length() - 2, image.length() ); image.append( "\\" ); } }
 MORE: { "\\\'" { image.delete( image.length() - 2, image.length() ); image.append( "'" ); } }
 MORE: { "\\\"" { image.delete( image.length() - 2, image.length() ); image.append( "\"" ); } }
 MORE: { "\\b" { image.delete( image.length() - 2, image.length() ); image.append( "\b" ); } }
 MORE: { "\\f" { image.delete( image.length() - 2, image.length() ); image.append( "\f" ); } }
 MORE: { "\\n" { image.delete( image.length() - 2, image.length() ); image.append( "\n" ); } }
 MORE: { "\\r" { image.delete( image.length() - 2, image.length() ); image.append( "\r" ); } }
 MORE: { "\\t" { image.delete( image.length() - 2, image.length() ); image.append( "\t" ); } }
 MORE: { "\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]" }
 MORE: { < ~["\""] > }
 TOKEN: {  { matchedToken.image = image.substring( 1, image.length() - 1 ); } : DEFAULT }

MORE : { < ESCAPED_SYMBOLIC_NAME_OPEN: "`" > : ESC_SYMB_NAME }
 MORE: { < ~["`"] > }
 MORE: { "``" { image.delete( image.length() - 2, image.length() ); image.append( "`" ); } }
 TOKEN: {  { matchedToken.image = image.substring( 1, image.length() - 1 ); } : DEFAULT }

/**
* add new tokens to SymbolicNameString() if they should be accepted as an identifier as well
* add new tokens to IdentifierTokens.tokens for correct filtering of identifier tokens
*/
TOKEN [IGNORE_CASE] :
{
    < ACCESS: "ACCESS" >
    | < ACTIVE: "ACTIVE" >
    | < ADMIN: "ADMIN" >
    | < ADMINISTRATOR: "ADMINISTRATOR">
    | < ALIAS: "ALIAS" >
    | < ALIASES: "ALIASES" >
    | < ALL_SHORTEST_PATH: "allShortestPaths" >
    | < ALL: "ALL" >
    | < ALTER: "ALTER" >
    | < AND: "AND" >
    | < ANY: "ANY" >
    | < ARRAY: "ARRAY" >
    | < AS: "AS" >
    | < ASC: "ASC" >
    | < ASCENDING: "ASCENDING" >
    | < ASSERT: "ASSERT" >
    | < ASSIGN: "ASSIGN" >
    | < AT: "AT" >
    | < AUTH: "AUTH" >
    | < BAR: "|" >
    | < BINDINGS: "BINDINGS" >
    | < BOOL: "BOOL" >
    | < BOOLEAN: "BOOLEAN" >
    | < BOOSTED: "BOOSTED" >
    | < BOTH: "BOTH" >
    | < BREAK: "BREAK" >
    | < BRIEF: "BRIEF" >
    | < BTREE: "BTREE" >
    | < BUILT: "BUILT" >
    | < BY: "BY" >
    | < CALL: "CALL" >
    | < CASCADE: "CASCADE" >
    | < CASE: "CASE" >
    | < CHANGE: "CHANGE">
    | < CIDR: "CIDR">
    | < COLLECT: "COLLECT">
    | < COLON: ":" >
    | < COLONCOLON: "::" >
    | < COMMA: "," >
    | < COMMAND: "COMMAND">
    | < COMMANDS: "COMMANDS">
    | < COMMIT: "COMMIT" >
    | < COMPOSITE: "COMPOSITE" >
    | < CONCURRENT: "CONCURRENT" >
    | < CONSTRAINT: "CONSTRAINT" >
    | < CONSTRAINTS: "CONSTRAINTS" >
    | < CONTAINS: "CONTAINS" >
    | < COPY: "COPY" >
    | < CONTINUE: "CONTINUE" >
    | < COUNT: "COUNT" >
    | < CREATE: "CREATE" >
    | < CSV: "CSV" >
    | < CURRENT: "CURRENT" >
    | < DATA: "DATA">
    | < DATABASE: "DATABASE">
    | < DATABASES: "DATABASES">
    | < DATE: "DATE">
    | < DATETIME: "DATETIME">
    | < DBMS: "DBMS">
    | < DEALLOCATE: "DEALLOCATE" >
    | < DEFAULT_TOKEN: "DEFAULT">
    | < DEFINED: "DEFINED">
    | < DELETE: "DELETE" >
    | < DENY: "DENY" >
    | < DESC: "DESC" >
    | < DESCENDING: "DESCENDING" >
    | < DESTROY: "DESTROY">
    | < DETACH: "DETACH" >
    | < DOLLAR: "$">
    | < DOUBLEBAR: "||">
    | < DIFFERENT: "DIFFERENT" >
    | < DISTINCT: "DISTINCT" >
    | < DIVIDE: "/" >
    | < DOT: "." >
    | < DOTDOT: ".." >
    | < DRIVER: "DRIVER" >
    | < DROP: "DROP" >
    | < DRYRUN: "DRYRUN" >
    | < DUMP: "DUMP" >
    | < DURATION: "DURATION" >
    | < EACH: "EACH" >
    | < EDGE: "EDGE" >
    | < ENABLE: "ENABLE" >
    | < ELEMENT: "ELEMENT" >
    | < ELEMENTS: "ELEMENTS" >
    | < ELSE: "ELSE" >
    | < ENCRYPTED: "ENCRYPTED" >
    | < END: "END" >
    | < ENDS: "ENDS" >
    | < EQ: "=" >
    | < EXECUTABLE: "EXECUTABLE" >
    | < EXECUTE: "EXECUTE" >
    | < EXIST: "EXIST" >
    | < EXISTENCE: "EXISTENCE" >
    | < EXISTS: "EXISTS" >
    | < ERROR: "ERROR" >
    | < FAIL: "FAIL" >
    | < FALSE: "false" >
    | < FIELDTERMINATOR: "FIELDTERMINATOR" >
    | < FINISH: "FINISH" >
    | < FLOAT: "FLOAT" >
    | < FOR: "FOR" >
    | < FOREACH: "FOREACH" >
    | < FROM: "FROM" >
    | < FULLTEXT: "FULLTEXT" >
    | < FUNCTION: "FUNCTION" >
    | < FUNCTIONS: "FUNCTIONS" >
    | < GE: ">=" >
    | < GRANT: "GRANT">
    | < GRAPH: "GRAPH" >
    | < GRAPHS: "GRAPHS" >
    | < GROUP: "GROUP" >
    | < GROUPS: "GROUPS" >
    | < GT: ">" >
    | < HEADERS: "HEADERS" >
    | < HOME: "HOME">
    | < ID: "ID" >
    | < IF: "IF" >
    | < IMPERSONATE: "IMPERSONATE" >
    | < IMMUTABLE: "IMMUTABLE" >
    | < IN: "IN" >
    | < INDEX: "INDEX" >
    | < INDEXES: "INDEXES" >
    | < INF: "INF" >
    | < INFINITY: "INFINITY" >
    | < INSERT: "INSERT">
    | < INT: "INT" >
    | < INTEGER: "INTEGER" >
    | < IS: "IS" >
    | < JOIN: "JOIN" >
    | < KEY: "KEY" >
    | < LABEL: "LABEL" >
    | < LABELS: "LABELS" >
    | < AMPERSAND: "&" >
    | < EXCLAMATION_MARK: "!" >
    | < LBRACKET: "[" >
    | < LCURLY: "{" >
    | < LE: "<=" >
    | < LEADING: "LEADING" >
    | < LIMITROWS: "LIMIT" >
    | < LIST: "LIST" >
    | < LOAD: "LOAD" >
    | < LOCAL: "LOCAL" >
    | < LOOKUP: "LOOKUP" >
    | < LPAREN: "(" >
    | < LT: "<" >
    | < MANAGEMENT: "MANAGEMENT" >
    | < MAP: "MAP" >
    | < MATCH: "MATCH" >
    | < MERGE: "MERGE" >
    | < MINUS: "-" >
    | < PERCENT: "%" >
    | < NEQ: "!=" >
    | < NEQ2: "<>" >
    | < NAME: "NAME" >
    | < NAMES: "NAMES" >
    | < NAN: "NAN" >
    | < NFC: "NFC" >
    | < NFD: "NFD" >
    | < NFKC: "NFKC" >
    | < NFKD: "NFKD" >
    | < NEW: "NEW" >
    | < NODE: "NODE" >
    | < NODETACH: "NODETACH" >
    | < NODES: "NODES" >
    | < NONE: "NONE" >
    | < NORMALIZE: "NORMALIZE" >
    | < NORMALIZED: "NORMALIZED" >
    | < NOT: "NOT" >
    | < NOTHING: "NOTHING" >
    | < NOWAIT: "NOWAIT">
    | < NULL: "null">
    | < OF: "OF" >
    | < OFFSET: "OFFSET" >
    | < ON: "ON" >
    | < ONLY: "ONLY" >
    | < OPTIONAL: "OPTIONAL" >
    | < OPTIONS: "OPTIONS" >
    | < OPTION: "OPTION" >
    | < OR: "OR" >
    | < ORDER: "ORDER" >
    | < OUTPUT: "OUTPUT" >
    | < PASSWORD: "PASSWORD" >
    | < PASSWORDS: "PASSWORDS" >
    | < PATH: "PATH" >
    | < PATHS: "PATHS" >
    | < PERIODIC: "PERIODIC" >
    | < PLAINTEXT: "PLAINTEXT" >
    | < PLUS: "+" >
    | < PLUSEQUAL: "+=" >
    | < POINT: "POINT">
    | < POPULATED: "POPULATED">
    | < POW: "^" >
    | < REPEATABLE: "REPEATABLE" >
    | < PRIMARY: "PRIMARY" >
    | < PRIMARIES: "PRIMARIES" >
    | < PRIVILEGE: "PRIVILEGE" >
    | < PRIVILEGES: "PRIVILEGES" >
    | < PROCEDURE: "PROCEDURE" >
    | < PROCEDURES: "PROCEDURES" >
    | < PROPERTIES: "PROPERTIES" >
    | < PROPERTY: "PROPERTY" >
    | < PROVIDER: "PROVIDER" >
    | < PROVIDERS: "PROVIDERS" >
    | < QUESTION: "?" >
    | < RANGE: "RANGE" >
    | < RBRACKET: "]" >
    | < RCURLY: "}" >
    | < READ: "READ" >
    | < REALLOCATE: "REALLOCATE" >
    | < REDUCE: "REDUCE" >
    | < RENAME: "RENAME" >
    | < REGEQ: "=~" >
    | < REL: "REL" >
    | < RELATIONSHIP: "RELATIONSHIP" >
    | < RELATIONSHIPS: "RELATIONSHIPS" >
    | < REMOVE: "REMOVE" >
    | < REPLACE: "REPLACE" >
    | < REPORT: "REPORT" >
    | < REQUIRE: "REQUIRE" >
    | < REQUIRED: "REQUIRED" >
    | < RESTRICT: "RESTRICT" >
    | < RETURN: "RETURN" >
    | < REVOKE: "REVOKE">
    | < ROLE: "ROLE">
    | < ROLES: "ROLES">
    | < ROW: "ROW">
    | < ROWS: "ROWS">
    | < RPAREN: ")" >
    | < SCAN: "SCAN" >
    | < SEC: "SEC">
    | < SECOND: "SECOND">
    | < SECONDARY: "SECONDARY" >
    | < SECONDARIES: "SECONDARIES" >
    | < SECONDS: "SECONDS">
    | < SEEK: "SEEK" >
    | < SEMICOLON: ";" >
    | < SERVER: "SERVER" >
    | < SERVERS: "SERVERS" >
    | < SET: "SET" >
    | < SETTING: "SETTING" >
    | < SETTINGS: "SETTINGS" >
    | < SHORTEST_PATH: "shortestPath" >
    | < SHORTEST: "SHORTEST" >
    | < SHOW: "SHOW">
    | < SIGNED: "SIGNED" >
    | < SINGLE: "SINGLE" >
    | < SKIPROWS: "SKIP" >
    | < START: "START">
    | < STARTS: "STARTS" >
    | < STATUS: "STATUS" >
    | < STOP: "STOP">
    | < STRING: "STRING">
    | < SUPPORTED: "SUPPORTED">
    | < SUSPENDED: "SUSPENDED">
    | < TARGET: "TARGET" >
    | < TERMINATE: "TERMINATE">
    | < TEXT: "TEXT">
    | < THEN: "THEN" >
    | < TIME: "TIME" >
    | < TIMES: "*" >
    | < TIMESTAMP: "TIMESTAMP" >
    | < TIMEZONE: "TIMEZONE" >
    | < TO: "TO" >
    | < TOPOLOGY: "TOPOLOGY" >
    | < TRAILING: "TRAILING" >
    | < TRANSACTION: "TRANSACTION" >
    | < TRANSACTIONS: "TRANSACTIONS" >
    | < TRAVERSE: "TRAVERSE" >
    | < TRIM: "TRIM" >
    | < TRUE: "true">
    | < TYPE: "TYPE">
    | < TYPED: "TYPED">
    | < TYPES: "TYPES">
    | < UNION: "UNION" >
    | < UNIQUE: "UNIQUE" >
    | < UNIQUENESS: "UNIQUENESS" >
    | < UNWIND: "UNWIND" >
    | < URL: "URL" >
    | < USE: "USE" >
    | < USER: "USER">
    | < USERS: "USERS">
    | < USING: "USING" >
    | < VALUE: "VALUE" >
    | < VARCHAR: "VARCHAR" >
    | < VECTOR: "VECTOR" >
    | < VERBOSE: "VERBOSE" >
    | < VERTEX: "VERTEX" >
    | < WAIT: "WAIT">
    | < WHEN: "WHEN" >
    | < WHERE: "WHERE" >
    | < WITH: "WITH" >
    | < WITHOUT: "WITHOUT" >
    | < WRITE: "WRITE" >
    | < XOR: "XOR" >
    | < YIELD: "YIELD" >
    | < ZONE: "ZONE" >
    | < ZONED: "ZONED" >
}

/* IDENTIFIERS */

TOKEN :
{
    < IDENTIFIER:  ()* >
    | < #LETTER:
        [  // all chars for which Character.isJavaIdentifierStart(c) && Character.getType(c) != Character.CURRENCY_SYMBOL is true (in Java 17)
            "\u0041"-"\u005a",
            "\u005f",
            "\u0061"-"\u007a",
            "\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",
            "\u0560"-"\u0588",
            "\u05d0"-"\u05ea",
            "\u05ef"-"\u05f2",
            "\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"-"\u08c7",
            "\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"-"\u09f1",
            "\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",
            "\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",
            "\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",
            "\u0d04"-"\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",
            "\u0e40"-"\u0e46",
            "\u0e81"-"\u0e82",
            "\u0e84",
            "\u0e86"-"\u0e8a",
            "\u0e8c"-"\u0ea3",
            "\u0ea5",
            "\u0ea7"-"\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",
            "\u17dc",
            "\u1820"-"\u1878",
            "\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",
            "\u1c90"-"\u1cba",
            "\u1cbd"-"\u1cbf",
            "\u1ce9"-"\u1cec",
            "\u1cee"-"\u1cf3",
            "\u1cf5"-"\u1cf6",
            "\u1cfa",
            "\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",
            "\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"-"\u312f",
            "\u3131"-"\u318e",
            "\u31a0"-"\u31bf",
            "\u31f0"-"\u31ff",
            "\u3400"-"\u4dbf",
            "\u4e00"-"\u9ffc",
            "\ua000"-"\ua48c",
            "\ua4d0"-"\ua4fd",
            "\ua500"-"\ua60c",
            "\ua610"-"\ua61f",
            "\ua62a"-"\ua62b",
            "\ua640"-"\ua66e",
            "\ua67f"-"\ua69d",
            "\ua6a0"-"\ua6ef",
            "\ua717"-"\ua71f",
            "\ua722"-"\ua788",
            "\ua78b"-"\ua7bf",
            "\ua7c2"-"\ua7ca",
            "\ua7f5"-"\ua801",
            "\ua803"-"\ua805",
            "\ua807"-"\ua80a",
            "\ua80c"-"\ua822",
            "\ua840"-"\ua873",
            "\ua882"-"\ua8b3",
            "\ua8f2"-"\ua8f7",
            "\ua8fb",
            "\ua8fd"-"\ua8fe",
            "\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"-"\uab69",
            "\uab70"-"\uabe2",
            "\uac00"-"\ud7a3",
            "\ud7b0"-"\ud7c6",
            "\ud7cb"-"\ud7fb",
            "\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"-"\ufdfb",
            "\ufe33"-"\ufe34",
            "\ufe4d"-"\ufe4f",
            "\ufe70"-"\ufe74",
            "\ufe76"-"\ufefc",
            "\uff21"-"\uff3a",
            "\uff3f",
            "\uff41"-"\uff5a",
            "\uff66"-"\uffbe",
            "\uffc2"-"\uffc7",
            "\uffca"-"\uffcf",
            "\uffd2"-"\uffd7",
            "\uffda"-"\uffdc"
        ] >
    | < #PART_LETTER:
        [  // all chars for which Character.isIdentifierPart is true (in Java 17)
            "\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",
            "\u0560"-"\u0588",
            "\u058f",
            "\u0591"-"\u05bd",
            "\u05bf",
            "\u05c1"-"\u05c2",
            "\u05c4"-"\u05c5",
            "\u05c7",
            "\u05d0"-"\u05ea",
            "\u05ef"-"\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",
            "\u07fd"-"\u082d",
            "\u0840"-"\u085b",
            "\u0860"-"\u086a",
            "\u08a0"-"\u08b4",
            "\u08b6"-"\u08c7",
            "\u08d3"-"\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",
            "\u09fe",
            "\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",
            "\u0b55"-"\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"-"\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"-"\u0d0c",
            "\u0d0e"-"\u0d10",
            "\u0d12"-"\u0d44",
            "\u0d46"-"\u0d48",
            "\u0d4a"-"\u0d4e",
            "\u0d54"-"\u0d57",
            "\u0d5f"-"\u0d63",
            "\u0d66"-"\u0d6f",
            "\u0d7a"-"\u0d7f",
            "\u0d81"-"\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",
            "\u0e86"-"\u0e8a",
            "\u0e8c"-"\u0ea3",
            "\u0ea5",
            "\u0ea7"-"\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"-"\u1878",
            "\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",
            "\u1abf"-"\u1ac0",
            "\u1b00"-"\u1b4b",
            "\u1b50"-"\u1b59",
            "\u1b6b"-"\u1b73",
            "\u1b80"-"\u1bf3",
            "\u1c00"-"\u1c37",
            "\u1c40"-"\u1c49",
            "\u1c4d"-"\u1c7d",
            "\u1c80"-"\u1c88",
            "\u1c90"-"\u1cba",
            "\u1cbd"-"\u1cbf",
            "\u1cd0"-"\u1cd2",
            "\u1cd4"-"\u1cfa",
            "\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"-"\u312f",
            "\u3131"-"\u318e",
            "\u31a0"-"\u31bf",
            "\u31f0"-"\u31ff",
            "\u3400"-"\u4dbf",
            "\u4e00"-"\u9ffc",
            "\ua000"-"\ua48c",
            "\ua4d0"-"\ua4fd",
            "\ua500"-"\ua60c",
            "\ua610"-"\ua62b",
            "\ua640"-"\ua66f",
            "\ua674"-"\ua67d",
            "\ua67f"-"\ua6f1",
            "\ua717"-"\ua71f",
            "\ua722"-"\ua788",
            "\ua78b"-"\ua7bf",
            "\ua7c2"-"\ua7ca",
            "\ua7f5"-"\ua827",
            "\ua82c",
            "\ua838",
            "\ua840"-"\ua873",
            "\ua880"-"\ua8c5",
            "\ua8d0"-"\ua8d9",
            "\ua8e0"-"\ua8f7",
            "\ua8fb",
            "\ua8fd"-"\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"-"\uab69",
            "\uab70"-"\uabea",
            "\uabec"-"\uabed",
            "\uabf0"-"\uabf9",
            "\uac00"-"\ud7a3",
            "\ud7b0"-"\ud7c6",
            "\ud7cb"-"\ud7fb",
            "\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"
        ] >
}

TOKEN :
{
    < ARROW_LINE: ["\u002d", "\u00ad", "\u2010", "\u2011", "\u2012", "\u2013", "\u2014", "\u2015", "\ufe58", "\ufe63", "\uff0d"] >
    | < ARROW_LEFT_HEAD: ["\u27e8", "\u3008", "\ufe64", "\uff1c"] >
    | < ARROW_RIGHT_HEAD: ["\u27e9", "\u3009", "\ufe65", "\uff1e"] >
}

<*> TOKEN :
{
    < UNKNOWN: ~[] >
}

/** Root production. */
STATEMENTS Statements() throws Exception:
{
    STATEMENT x;
    List stmts = new ArrayList<>();
}
{
    try {
        x=Statement() { stmts.add( x ); }
        ( LOOKAHEAD(2)  x=Statement() { stmts.add( x ); } )* ( LOOKAHEAD(2)  )? 
        {
            return astFactory.statements(stmts);
        }
    } catch ( ParseException e ) {
        Token t = e.currentToken.next;
        if ( e.getMessage().contains( "Encountered \"\"" ) )
        {
            throw exceptionFactory.syntaxException( t.image, ParseExceptions.expected( e.expectedTokenSequences, e.tokenImage, e.currentToken.image ), e,
                                                   t.endOffset + 1, t.endLine, t.endColumn + 1 );
        }
        else
        {
            throw exceptionFactory.syntaxException( t.image, ParseExceptions.expected( e.expectedTokenSequences, e.tokenImage, e.currentToken.image ), e,
                                                   t.beginOffset, t.beginLine, t.beginColumn );
        }
    } catch ( InvalidUnicodeLiteral e ) {
        throw exceptionFactory.syntaxException( e, e.offset, e.line, e.column );
    }
}

STATEMENT Statement() throws Exception:
{
    STATEMENT statement;
    USE_CLAUSE useClause = null;
}
{
    [ PeriodicCommitQueryHintFailure() ]
    (
        useClause=UseClause() statement=SingleQueryOrCommandWithUseClause(useClause)
        | statement=SingleQueryOrCommand()
    )
    {
        return statement;
    }
}

STATEMENT SingleQueryOrCommand() throws Exception:
{
    STATEMENT statement = null;
    QUERY query = null;
}
{
    (
        LOOKAHEAD(2) statement=CreateCommand( null ) // CREATE admin thing vs CREATE graph thing
        | statement=Command( null )
        | query=SingleQuery() ( query=Union(query) )*
    )
    {
        if ( query != null )
        {
            return query;
        }
        return statement;
    }
}

STATEMENT SingleQueryOrCommandWithUseClause( USE_CLAUSE useClause ) throws Exception:
{
    STATEMENT statement = null;
    QUERY query = null;
}
{
    (
        LOOKAHEAD(2) statement=CreateCommand( useClause ) // CREATE admin thing vs CREATE graph thing
        | statement=Command( useClause )
        | query=SingleQueryWithUseClause( useClause ) ( query=Union(query) )*
    )
    {
        if ( query != null )
        {
            return query;
        }
        return statement;
    }
}

void PeriodicCommitQueryHintFailure() throws Exception:
{
    Token tPeriodicCommit;
}
{
     tPeriodicCommit=  (  )?
    {
        throw exceptionFactory.syntaxException(
            new ParseException( ASTExceptionFactory.periodicCommitNotSupported ),
            tPeriodicCommit.beginOffset, tPeriodicCommit.beginLine, tPeriodicCommit.beginColumn );
    }
}

QUERY RegularQuery() throws Exception:
{
    QUERY x;
}
{
    x=SingleQuery() ( x=Union(x) )*
    {
        return x;
    }
}

QUERY Union( QUERY lhs ) throws Exception :
{
    Token t;
    QUERY rhs;
    boolean all = false;
}
{
    t= (  { all = true; } |  )? rhs=SingleQuery()
    {
        return astFactory.newUnion( pos( t ), lhs, rhs, all );
    }
}

QUERY SingleQuery() throws Exception:
{
    CLAUSE x;
    List clauses = new ArrayList<>();
}
{
    ( x=Clause() { clauses.add( x ); } )+
    {
        return astFactory.newSingleQuery( clauses );
    }
}

QUERY SingleQueryWithUseClause( CLAUSE useClause ) throws Exception:
{
    CLAUSE x;
    List clauses = new ArrayList<>();
    if ( useClause != null )
    {
      clauses.add( useClause );
    }
}
{
    ( x=Clause() { clauses.add( x ); } )*
    {
        return astFactory.newSingleQuery( clauses );
    }
}

CLAUSE Clause() throws Exception:
{
    CLAUSE x = null;
}
{
    (
        x=UseClause()
        | x=FinishClause()
        | x=ReturnClause()
        | x=CreateClause() // because create index/constraint
        | x=InsertClause()
        | x=DeleteClause()
        | x=SetClause()
        | x=RemoveClause()
        | LOOKAHEAD(2) x=MatchClause() // because call/subqueries also use OPTIONAL
        | x=MergeClause()
        | x=WithClause()
        | x=UnwindClause()
        | LOOKAHEAD(3) x=CallClause() // because subqueries also start with CALL
        | x=SubqueryClause()
        | x=LoadCSVClause()
        | x=ForeachClause()
        | x=OrderBySkipLimitClause()
    )
    {
        return x;
    }
}

// USE

USE_CLAUSE UseClause() throws Exception:
{
    Token t;
    USE_CLAUSE useClause;
}
{
    t= (
        LOOKAHEAD( 2 )  (useClause=graphReference(t))
        | useClause=graphReference(t)
        )
    {
        return useClause;
    }
}

USE_CLAUSE graphReference(Token t) throws Exception:
{
    AliasName aliasName;
    FUNCTION_INVOCATION functionInvocation;
    USE_CLAUSE useClause;
}
{
     useClause=graphReference(t)  { return useClause; }
    | LOOKAHEAD(FunctionInvocation()) functionInvocation = FunctionInvocation(true)  { return astFactory.functionUseClause(pos(t), functionInvocation); }
    | aliasName = SymbolicAliasName()  { return astFactory.directUseClause(pos(t), aliasName.getLocalAliasName(astFactory));}
}

// FINISH

FINISH_CLAUSE FinishClause() throws Exception:
{
    Token t;
}
{
    t=
    {
        return astFactory.newFinishClause( pos( t ) );
    }
}

// RETURN

RETURN_CLAUSE ReturnClause() throws Exception:
{
    Token t;
    RETURN_CLAUSE clause = null;
}
{
    t=
    clause=ReturnBody( t )
    {
        return clause;
    }
}

RETURN_CLAUSE ReturnBody( Token t ) throws Exception:
{
    Token skipPosition = null;
    Token limitPosition = null;
    boolean distinct = false;
    List order = new ArrayList<>();
    Token orderPos = null;
    EXPRESSION skip = null;
    EXPRESSION limit = null;
    ORDER_ITEM o = null;
    RETURN_ITEM x;
    RETURN_ITEMS returnItems;
}
{
    ( LOOKAHEAD( 2 )  { distinct = true; } )?
    ( returnItems=ReturnItems() )
    ( orderPos=  o=OrderItem() { order.add( o ); } (  o=OrderItem() {order.add( o ); } )* )? ( { skipPosition=token.next; } skip=Skip() )? ( { limitPosition=token.next; } limit=Limit() )?
    {
        return astFactory.newReturnClause(  pos( t ), distinct, returnItems, order, pos( orderPos ), skip, pos( skipPosition ), limit, pos( limitPosition ) );
    }
}

RETURN_ITEM ReturnItem() throws Exception:
{
    EXPRESSION e;
    VARIABLE v = null;
    Token eStart;
    Token eEnd;
}
{
    { eStart = token; }
    e=Expression() { eEnd = token; } (  v=Variable() )?
    {
        if ( v != null )
        {
            return astFactory.newReturnItem( pos( eStart.next ), e, v );
        }
        else
        {
            return astFactory.newReturnItem( pos( eStart.next ), e, eStart.next.beginOffset, eEnd.endOffset );
        }
    }
}

RETURN_ITEMS ReturnItems() throws Exception:
{
    Token returnItemsPosition;
    RETURN_ITEM x;
    List returnItems = new ArrayList<>();
    boolean returnAll = false;
}
{
    { returnItemsPosition = token; }
    (
         { returnAll = true; } (  x=ReturnItem() { returnItems.add( x ); } )*
        | x=ReturnItem() { returnItems.add( x ); } (  x=ReturnItem() { returnItems.add( x ); } )*
    ) {
        return astFactory.newReturnItems( pos( returnItemsPosition.next ), returnAll, returnItems );
    }
}

ORDER_ITEM OrderItem() throws Exception:
{
    Token t;
    EXPRESSION e;
}
{
    {t = token;}
    e=Expression()
    (
        ( | ) { return astFactory.orderDesc( pos( t.next ), e ); }
        | (  |  )? { return astFactory.orderAsc( pos( t.next ), e ); }
    )
}

EXPRESSION Skip() throws Exception:
{
    EXPRESSION e;
}
{
    (  |  ) e=Expression()
    {
        return e;
    }
}

EXPRESSION Limit() throws Exception:
{
    EXPRESSION e; }
{
     e=Expression()
    {
        return e;
    }
}

// WHERE

WHERE WhereClause() throws Exception:
{
    Token t;
    EXPRESSION e;
}
{
    t= e=Expression()
    {
        return astFactory.whereClause( pos( t ), e );
    }
}

// WITH

CLAUSE WithClause() throws Exception:
{
    Token t;
    RETURN_CLAUSE returnClause;
    WHERE where = null;
}
{
    t= returnClause=ReturnBody( t ) ( where=WhereClause() )?
    {
        return astFactory.withClause( pos( t ), returnClause, where );
    }
}

// CREATE

CLAUSE CreateClause() throws Exception:
{
    Token t;
    List patterns;
}
{
    t= patterns=PatternList()
    {
        return astFactory.createClause( pos( t ), patterns );
    }
}

// INSERT

CLAUSE InsertClause() throws Exception:
{
    Token t;
    List patterns;
}
{
    t= patterns=InsertPatternList()
    {
        return astFactory.insertClause( pos( t ), patterns );
    }
}

// SET

SET_CLAUSE SetClause() throws Exception:
{
    Token t;
    SET_ITEM item;
    List items = new ArrayList<>();
}
{
    t= item=SetItem() { items.add( item ); } (  item=SetItem() { items.add( item ); } )*
    {
        return astFactory.setClause( pos( t ), items );
    }
}

SET_ITEM SetItem() throws Exception:
{
    EXPRESSION e;
    PROPERTY p;
    EXPRESSION d;
    VARIABLE v;
    SET_ITEM setItem = null;
}
{
    LOOKAHEAD( Expression1()  ) p=PropertyExpression()  e=Expression() { return astFactory.setProperty( p, e ); }
    | LOOKAHEAD( Expression1()  ) d=DynamicPropertyExpression()  e=Expression() { return astFactory.setDynamicProperty( d, e ); }
    | LOOKAHEAD( 2 ) v=Variable()  e=Expression() { return astFactory.setVariable( v, e ); }
    | LOOKAHEAD( 2 ) v = Variable()  e=Expression() { return astFactory.addAndSetVariable( v, e ); }
    | LOOKAHEAD( 2 ) v = Variable() setItem = SetNodeLabels(v) { return setItem; }
    | v = Variable() setItem = SetNodeLabelsIs(v) { return setItem; }
}

// REMOVE

CLAUSE RemoveClause() throws Exception:
{
    Token t;
    REMOVE_ITEM item;
    List items = new ArrayList<>();
}
{
    t= item=RemoveItem() { items.add( item ); } (  item=RemoveItem() { items.add( item ); })*
    {
        return astFactory.removeClause( pos( t ), items );
    }
}

REMOVE_ITEM RemoveItem() throws Exception:
{
    EXPRESSION e;
    PROPERTY p;
    VARIABLE v;
    REMOVE_ITEM removeItem = null;
}
{
    LOOKAHEAD( Expression1()  ) p=PropertyExpression() { return astFactory.removeProperty( p ); }
    | LOOKAHEAD( Expression1()  ) e=DynamicPropertyExpression() { return astFactory.removeDynamicProperty( e ); }
    | LOOKAHEAD( 2 ) v=Variable() removeItem=RemoveNodeLabelsColon(v) { return removeItem; }
    | v=Variable() removeItem=RemoveNodeLabelsIs(v) { return removeItem; }
}

// DELETE

CLAUSE DeleteClause() throws Exception:
{
    Token detachT = null;
    Token t;
    boolean detach = false;
    EXPRESSION e;
    List list = new ArrayList<>();
}
{
    ( detachT= { detach = true; } | detachT= )? t= e=Expression() {list.add( e );} (  e=Expression() { list.add( e ); } )*
    {
        return astFactory.deleteClause( pos( detachT != null ? detachT : t ), detach, list );
    }
}

// MATCH

CLAUSE MatchClause() throws Exception:
{
    Token optionalT = null;
    Token t;
    Token whereToken = null;
    boolean optional = false;
    MATCH_MODE matchMode = null;
    List patterns;
    List hints;
    WHERE where = null;
}
{
    ( optionalT= { optional = true; } )? t= (LOOKAHEAD(2) matchMode = MatchMode() )? patterns=PatternList() hints=Hints() ( where=WhereClause() )?
    {
        return astFactory.matchClause( pos( optionalT != null ? optionalT : t ), optional, matchMode, patterns, pos( t.next ), hints, where );
    }
}

MATCH_MODE MatchMode():
{
    Token t;
    MATCH_MODE matchMode;
}
{
    (
        t =  (LOOKAHEAD(2)   |  |   ) { matchMode = astFactory.repeatableElements(pos(t)); } |
        t =  (LOOKAHEAD(2)   |  |  ) { matchMode = astFactory.differentRelationships(pos(t)); }
    )
    {
        return matchMode;
    }
}

List Hints() :
{
    Token t;
    boolean seek;
    VARIABLE v;
    Token labelOrRelType;
    List joinVariables;
    HINT hint;
    List hints = null;
}
{
    ( t=
        (
               hint = IndexHintBody( HintIndexType.ANY, pos( t ) )
            |   hint = IndexHintBody( HintIndexType.BTREE, pos( t ) )
            |   hint = IndexHintBody( HintIndexType.TEXT, pos( t ) )
            |   hint = IndexHintBody( HintIndexType.RANGE, pos( t ) )
            |   hint = IndexHintBody( HintIndexType.POINT, pos( t ) )
            |   joinVariables=VariableList1() { hint = astFactory.usingJoin( pos( t ), joinVariables ); }
            |  v=Variable() labelOrRelType=LabelOrRelType() { hint = astFactory.usingScan( pos( t ), v, labelOrRelType.image ); }
        )
        {
            if ( hints == null )
            {
                hints = new ArrayList<>();
            }
            hints.add( hint );
        }
    )*
    {
        return hints;
    }
}

HINT IndexHintBody( HintIndexType indexType, POS p ) :
{
    boolean seek = false;
    VARIABLE v;
    Token labelOrRelType;
    List propNames;
}
{
    ( LOOKAHEAD( 2 )  { seek = true; } )? v=Variable() labelOrRelType=LabelOrRelType()  propNames=SymbolicNameList1() 
    {
        return astFactory.usingIndexHint( p, v, labelOrRelType.image, propNames, seek, indexType);
    }
}

// MERGE

CLAUSE MergeClause() throws Exception:
{
    Token t;
    Token onToken;
    PATTERN p;
    SET_CLAUSE c;
    ArrayList clauses = new ArrayList<>();
    ArrayList positions = new ArrayList<>();
    ArrayList actionTypes = new ArrayList<>();
}
{
    t= p=Pattern()
    (
        onToken =  (
             c=SetClause() { clauses.add( c ); actionTypes.add( ASTFactory.MergeActionType.OnMatch ); positions.add( pos( onToken ) ); }
            |  c=SetClause() { clauses.add( c ); actionTypes.add( ASTFactory.MergeActionType.OnCreate ); positions.add( pos( onToken ) ); }
        )
    )*
    {
        return astFactory.mergeClause( pos( t ), p, clauses, actionTypes, positions );
    }
}

// UNWIND

CLAUSE UnwindClause() throws Exception:
{
    Token t;
    EXPRESSION e;
    VARIABLE v;}
{
    t= e=Expression()  v=Variable()
    {
        return astFactory.unwindClause( pos( t ), e, v );
    }
}

// CALL

CLAUSE CallClause() throws Exception:
{
    Token optionalT = null;
    Token t;
    Token procedureNamePosition;
    Token procedureResultPosition = null;
    List namespace;
    String name;
    EXPRESSION e;
    List arguments = null;
    boolean yieldAll = false;
    CALL_RESULT_ITEM x;
    List items = null;
    WHERE where = null;
    boolean optional = false;
}
{
    ( optionalT= { optional=true; })? t= namespace=Namespace() {procedureNamePosition = token;} name=ProcedureName()
    (  { arguments = new ArrayList<>(); }
        ( e=Expression() { arguments.add( e ); } (  e=Expression() { arguments.add( e ); })* )?
        
    )?
    ( procedureResultPosition= (
         { yieldAll = true; }
        | { items = new ArrayList<>(); }
            x=ProcedureResultItem() { items.add( x ); }
            (  x=ProcedureResultItem() { items.add( x ); })*
            ( where=WhereClause() )?
        )
    )?
    {
        return astFactory.callClause( pos( optionalT != null ? optionalT : t ),
                                     pos( t.next ),
                                     pos( procedureNamePosition.next ),
                                     pos( procedureResultPosition ),
                                     namespace,
                                     name,
                                     arguments,
                                     yieldAll,
                                     items,
                                     where,
                                     optional);
    }
}

String ProcedureName() :
{
    Token t;
}
{
    t=SymbolicNameString()
    {
        return t.image;
    }
}

CALL_RESULT_ITEM ProcedureResultItem() :
{
    Token t;
    VARIABLE v = null;
}
{
    t=SymbolicNameString() (  v=Variable() )?
    {
        return astFactory.callResultItem( pos( t ), t.image,  v );
    }
}

// LOAD CSV

CLAUSE LoadCSVClause() throws Exception:
{
    Token t;
    boolean headers = false;
    EXPRESSION source;
    VARIABLE v;
    Token sep = null;
}
{
    t= 
    (   { headers = true; } )?
    
    try {
        source=Expression()
    } catch (Exception e) {
       throw new ParseException( exceptionFactory.failedToParseFile + " " + e.getMessage() );
    }
     v=Variable()
    (  ( sep=StringToken() ) )?
    {
        return astFactory.loadCsvClause( pos( t ), headers, source, v, sep == null ? null : sep.image );
    }
}

// FOREACH

CLAUSE ForeachClause() throws Exception:
{
    Token t;
    VARIABLE v = null;
    EXPRESSION list;
    CLAUSE c;
    List clauses = new ArrayList<>();
}
{
    t=  v=Variable()  list=Expression() 
    ( c=Clause() { clauses.add( c ); } )+
    
    {
        return astFactory.foreachClause( pos( t ), v, list, clauses );
    }
}

CLAUSE SubqueryClause() throws Exception :
{
    Token optionalT = null;
    Token t;
    QUERY q;
    SUBQUERY_IN_TRANSACTIONS_PARAMETERS inTransactionsParams = null;
    boolean isImportingAll = false;
    boolean hasScope = false;
    VARIABLE v;
    List variables = new ArrayList<>();
    boolean optional = false;
}
{
    ( optionalT= { optional=true; } )?
    t=
    (  { hasScope = true; }
    (  { isImportingAll = true; } |
    ( v=Variable() { variables.add(v); } (  v=Variable() { variables.add(v); } )* )? )
     )?
    
    q=RegularQuery()
    
    ( inTransactionsParams = SubqueryInTransactionsParameters() )?
    {
        return astFactory.subqueryClause(
            pos( optionalT != null ? optionalT : t ),
            q,
            inTransactionsParams,
            isImportingAll,
            hasScope,
            variables,
            optional
        );
    }
}

SUBQUERY_IN_TRANSACTIONS_PARAMETERS SubqueryInTransactionsParameters() throws Exception:
{
    Token t;
    SUBQUERY_IN_TRANSACTIONS_BATCH_PARAMETERS batchParams = null;
    SUBQUERY_IN_TRANSACTIONS_CONCURRENCY_PARAMETERS concurrencyParams = null;
    SUBQUERY_IN_TRANSACTIONS_ERROR_PARAMETERS errorParams = null;
    SUBQUERY_IN_TRANSACTIONS_REPORT_PARAMETERS reportParams = null;
    EXPRESSION c = null;
    String repeatedBatch = "Duplicated OF ROWS parameter";
    String repeatedOnError = "Duplicated ON ERROR parameter";
    String repeatedReport = "Duplicated REPORT STATUS parameter";
}
{
    t=
    (
        LOOKAHEAD(2) c=Expression()  { concurrencyParams = astFactory.subqueryInTransactionsConcurrencyParameters(pos( t ), c); }
      | LOOKAHEAD(1)                 { concurrencyParams = astFactory.subqueryInTransactionsConcurrencyParameters(pos( t ), null); }
      |                                          { concurrencyParams = null; }
    )
    t=
    // The 3 parameters for CALL IN TRANSACTIONS:
    // ON ERROR CONTINUE / BREAK / FAIL, OF ROWS, and REPORT STATUS, can appear
    // in any order and shouldn't be duplicated
    (
        (
            { assertNotAlreadySet( batchParams, t, repeatedBatch ); } batchParams=SubqueryInTransactionsBatchParameters() |
            { assertNotAlreadySet( errorParams, t, repeatedOnError ); } errorParams=SubqueryInTransactionsErrorParameters() |
            { assertNotAlreadySet( reportParams, t, repeatedReport ); } reportParams=SubqueryInTransactionsReportParameters()
        )
    )*
    {
        return astFactory.subqueryInTransactionsParams( pos( t ), batchParams, concurrencyParams, errorParams, reportParams);
    }
}

SUBQUERY_IN_TRANSACTIONS_BATCH_PARAMETERS SubqueryInTransactionsBatchParameters() throws Exception:
{
    Token t;
    EXPRESSION batchSize;
}
{
    t= batchSize=Expression() (|)
    {
        return astFactory.subqueryInTransactionsBatchParameters( pos( t ), batchSize);
    }
}

SUBQUERY_IN_TRANSACTIONS_ERROR_PARAMETERS SubqueryInTransactionsErrorParameters() :
{
    Token t;
    CallInTxsOnErrorBehaviourType onErrorBehaviour = CallInTxsOnErrorBehaviourType.ON_ERROR_FAIL;
}
{
    t=  ( { onErrorBehaviour = CallInTxsOnErrorBehaviourType.ON_ERROR_CONTINUE; } |  { onErrorBehaviour = CallInTxsOnErrorBehaviourType.ON_ERROR_BREAK; }  | )
    {
        return astFactory.subqueryInTransactionsErrorParameters( pos( t ), onErrorBehaviour );
    }
}

SUBQUERY_IN_TRANSACTIONS_REPORT_PARAMETERS SubqueryInTransactionsReportParameters() :
{
    Token t;
    VARIABLE v;
}
{
    t=   v=Variable()
    {
        return astFactory.subqueryInTransactionsReportParameters( pos( t ), v );
    }
}

// ORDER BY AND PAGE STATEMENT
CLAUSE OrderBySkipLimitClause() throws Exception:
{
    Token t = null;
    Token orderPos = null;
    Token skipPos = null;
    Token limitPos = null;
    EXPRESSION skip = null;
    EXPRESSION limit = null;
    List order = new ArrayList<>();
    ORDER_ITEM o = null;
}
{

    {t = token;}
    (
        ( ( orderPos=  o=OrderItem() { t=orderPos; order.add( o ); } (  o=OrderItem() {order.add( o ); } )* ) ( { skipPos=token.next; } skip=Skip() )? ( { limitPos=token.next; } limit=Limit() )? )
        | ( ( { skipPos=token.next; t=skipPos; } skip=Skip() ) ( { limitPos=token.next; } limit=Limit() )? )
        | ( { limitPos=token.next; t=limitPos;} limit=Limit() )
    )
    {
        return astFactory.orderBySkipLimitClause( pos( t ), order, pos( orderPos ), skip, pos( skipPos ), limit, pos( limitPos ) );
    }
}

// PATTERN

List PatternList() throws Exception:
{
    PATTERN p;
    List patterns = new ArrayList<>();
}
{
    p=Pattern() { patterns.add( p ); } (  p=Pattern() { patterns.add( p ); } )*
    {
        return patterns;
    }
}

List InsertPatternList() throws Exception:
{
    PATTERN p;
    List patterns = new ArrayList<>();
}
{
    p=InsertPattern() { patterns.add( p ); } (  p=InsertPattern() { patterns.add( p ); } )*
    {
        return patterns;
    }
}

PATTERN Pattern() throws Exception:
{
    VARIABLE v = null;
    PATTERN p;
    PATTERN_SELECTOR s = null;
}
{
     ( LOOKAHEAD(2)
       v=Variable()  [s=Selector()]
     | [s=Selector()]
     )
     p=AnonymousPattern()
    {
        if (v != null) p = astFactory.namedPattern( v, p );
        if (s != null) p = astFactory.patternWithSelector( s, p );
        return p;
    }
}

PATTERN InsertPattern() throws Exception:
{
    Token errorStart = null;
    List patternAtoms = new ArrayList<>();
}
{
     ( LOOKAHEAD(2) errorStart = SymbolicNameString() )?
     patternAtoms = InsertPathPatternAtoms()
    {
        if (errorStart != null)
        {
            throw exceptionFactory.syntaxException(
                new ParseException( ASTExceptionFactory.namedPatternInInsertNotSupported ),
                errorStart.beginOffset, errorStart.beginLine, errorStart.beginColumn );
        }
        return astFactory.insertPathPattern( patternAtoms );
    }
}

PATTERN_QUANTIFIER Quantifier() :
{
    Token range;
    Token l = null;
    Token u = null;
}
{
    LOOKAHEAD(3)
    range =  l=  {
        return astFactory.fixedPathQuantifier(pos(range), pos(l), l.image);
    }
    | range =  ( l= )?  ( u= )?  {
        return astFactory.intervalPathQuantifier(pos(range), pos(l), pos(u), l == null ? null : l.image, u == null ? null : u.image);
    }
    | range =  { return astFactory.plusPathQuantifier(pos(range)); }
    | range =  { return astFactory.starPathQuantifier(pos(range)); }
}

PATTERN AnonymousPattern() throws Exception:
{
    PATTERN p;
    PATTERN_ELEMENT pe;
}
{
    LOOKAHEAD( 3 ) p=ShortestPathPattern() { return p; }
    | pe=PatternElement() { return astFactory.pathPattern( pe ); }
}

PATTERN ShortestPathPattern() throws Exception:
{
    Token t;
    PATTERN_ELEMENT pe;
}
{
    t=  pe=PatternElement()  { return astFactory.shortestPathPattern( pos( t ), pe ); }
    | t=  pe=PatternElement()  { return astFactory.allShortestPathsPattern( pos( t ), pe ); }
}

PATTERN_ATOM MaybeQuantifiedRelationshipPattern() throws Exception:
{
  REL_PATTERN rel;
  PATTERN_QUANTIFIER q = null;
}
{
    rel=RelationshipPattern() [q=Quantifier()]
    {
        if ( q == null )
        {
            return rel;
        }
        else
        {
            return astFactory.quantifiedRelationship(rel, q);
        }
    }
}

PATTERN_ELEMENT PatternElement() throws Exception:
{
    List patternAtoms = new ArrayList<>();
}
{
    patternAtoms = PathPatternAtoms()
    {
    return astFactory.patternElement( patternAtoms );
    }
}

List PathPatternAtoms() throws Exception:
{
     NODE_PATTERN n;
     PATTERN_ATOM r;
     PATTERN_ATOM parenthesizedAtom;
     List patternAtoms = new ArrayList<>();
     labelExpressionStack.push(ExpectBar.DO_NOT_EXPECT_BAR);
}
{
     (
         (
             LOOKAHEAD(3)
             (
                 n=NodePattern() { patternAtoms.add( n ); }
                 (
                     LOOKAHEAD( 2 ) r=MaybeQuantifiedRelationshipPattern() { patternAtoms.add( r ); }
                     n=NodePattern() { patternAtoms.add( n ); }
                 )*
             ) |
             (
                 parenthesizedAtom = ParenthesizedPath() { patternAtoms.add( parenthesizedAtom ); }
             )
         )+
     )
     {
        labelExpressionStack.pop();
         return patternAtoms;
     }
}

List InsertPathPatternAtoms() throws Exception:
{
     NODE_PATTERN n;
     REL_PATTERN r;
     List patternAtoms = new ArrayList<>();
}
{
     (
         n=InsertNodePattern() { patternAtoms.add( n ); }
         (
            LOOKAHEAD( 2 ) r=InsertRelationshipPattern() { patternAtoms.add( r ); }
            n=InsertNodePattern() { patternAtoms.add( n ); }
         )*
     )
     {
         return patternAtoms;
     }
}

PATTERN_SELECTOR Selector() throws Exception:
{
    Token k = null;
    Token s;
    PATTERN_SELECTOR selector = null;
}
{
    ( LOOKAHEAD( 2 ) s =   (|)? { selector = astFactory.anyShortestPathSelector(null, null, pos(s)); }
    | LOOKAHEAD( 2 ) s =   (|)? { selector = astFactory.allShortestPathSelector(pos(s)); }
    | LOOKAHEAD( 2 ) s =  (k = )? (|)? { selector = astFactory.anyPathSelector(k != null ? k.image : null, pos(k), pos(s)); }
    | LOOKAHEAD( 2 ) s =  (|)? { selector = astFactory.allPathSelector(pos(s)); }
    | LOOKAHEAD( 4 ) s =  (k = )? (|)? (|) { selector = astFactory.shortestGroupsSelector(k != null ? k.image : null, pos(k), pos(s)); }
    | s =  k =  (|)? { selector = astFactory.anyShortestPathSelector(k.image, pos(k), pos(s)); }
    )
    {
        return selector;
    }
}

PATTERN PathPatternNonEmpty() throws Exception:
{
    NODE_PATTERN n;
    REL_PATTERN r;
    List patternAtoms = new ArrayList<>();
    labelExpressionStack.push(ExpectBar.DO_NOT_EXPECT_BAR);
}
{
      n=NodePattern() { patternAtoms.add( n ); }
      (
        LOOKAHEAD( 2 ) r=RelationshipPattern() {patternAtoms.add( r );}
        n=NodePattern() { patternAtoms.add( n ); }
      )+
      {
        labelExpressionStack.pop();
        return astFactory.pathPattern( astFactory.patternElement( patternAtoms ) );
      }
}

NODE_PATTERN NodePattern() throws Exception:
{
    Token t;
    NODE_PATTERN n;
}
{
    t=
    (
        // Shortcuts for node patterns not containing IS keyword or ambigious inlined WHERE
        LOOKAHEAD() n=NodePatternEmpty(t)
        | LOOKAHEAD(Variable() ) n=NodePatternOnlyVariable(t)
        | LOOKAHEAD() n=NodePatternColon(t)
        | LOOKAHEAD(Variable() ) n=NodePatternVariableColon(t)
        | LOOKAHEAD(|) n=NodePatternProperties(t)
        | n=NodePatternComplex(t)
    )
    { return n; }
}

NODE_PATTERN NodePatternEmpty(Token t) throws Exception:
{}
{
    
    {
        return astFactory.nodePattern( pos( t ), null, null, null, null );
    }
}

NODE_PATTERN NodePatternOnlyVariable(Token t) throws Exception:
{
    VARIABLE v = null;
}
{
    v = Variable()
    
    {
        return astFactory.nodePattern( pos( t ), v, null, null, null );
    }
}

NODE_PATTERN NodePatternColon(Token t) throws Exception:
{
    LABEL_EXPRESSION labelExpression = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this node pattern started with a colon,
    // so it must be of the form (labelExpression properties? WHERE?).
    labelExpression = LabelExpression(astFactory.nodeType())
    ( properties=Properties() )?
    (  predicate=Expression() )?
    
    {
        return astFactory.nodePattern( pos( t ), null, labelExpression, properties, predicate );
    }
}

NODE_PATTERN NodePatternVariableColon(Token t) throws Exception:
{
    VARIABLE v = null;
    LABEL_EXPRESSION labelExpression = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this node pattern started with variable followed by a colon,
    // so it must be of the form (variable labelExpression properties? WHERE?).
    v = Variable()
    labelExpression = LabelExpression(astFactory.nodeType())
    ( properties=Properties() )?
    (  predicate=Expression() )?
    
    {
        return astFactory.nodePattern( pos( t ), v, labelExpression, properties, predicate );
    }
}

NODE_PATTERN NodePatternProperties(Token t) throws Exception:
{
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this node pattern started with a {,
    // so it must be of the form (properties WHERE?).
    properties=Properties()
    (  predicate=Expression() )?
    
    {
        return astFactory.nodePattern( pos( t ), null, null, properties, predicate );
    }
}

NODE_PATTERN NodePatternComplex(Token t) throws Exception:
{
    VARIABLE v = null;
    LABEL_EXPRESSION labelExpression = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    (
       // The general node pattern is  (variable? labelExpression? properties? WHERE?),
       // but since 'IS' and 'WHERE' can be both node variables, labels and keywords,
       // we must check all of the 2^4 combinations that has not been covered earlier
       // in a certain order to avoid ambiguity.
       // The below is a trial and error solution to make all tests in IsWhereNodePatternParserTest work
       // while also have each combination in exactly one branch below.
       (
       LOOKAHEAD(LabelExpression(astFactory.nodeType())  Expression() )
            labelExpression = LabelExpression(astFactory.nodeType())
             predicate=Expression()
       | LOOKAHEAD(  ( | )? ) labelExpression = LabelExpression(astFactory.nodeType())
                                                                        ( properties=Properties() )?
                                                                        (  predicate=Expression() )?
       | LOOKAHEAD(3) v=Variable() properties=Properties() (  predicate=Expression() )?
       | LOOKAHEAD(Variable()  Expression() ) v=Variable()  predicate=Expression()
       | LOOKAHEAD( Expression() )  predicate=Expression()
       | LOOKAHEAD(3) v=Variable()
            labelExpression = LabelExpression(astFactory.nodeType())
            ( properties=Properties() )?
            (  predicate=Expression() )?
       | LOOKAHEAD(LabelExpression(astFactory.nodeType()) Properties())
         labelExpression = LabelExpression(astFactory.nodeType())
            properties=Properties()
            (  predicate=Expression() )?
       | labelExpression = LabelExpression(astFactory.nodeType())
       )
    )
    
    {
        return astFactory.nodePattern( pos( t ), v, labelExpression, properties, predicate );
    }
}

NODE_PATTERN InsertNodePattern() throws Exception:
{
    Token t;
    VARIABLE v = null;
    LABEL_EXPRESSION labelExpression = null;
    EXPRESSION properties = null;
}
{
    t=
    (
       LOOKAHEAD(3) v=Variable() labelExpression = InsertNodeLabelExpression() ( properties=MapLiteral() )?
       | LOOKAHEAD(2) v=Variable() properties=MapLiteral()
       | LOOKAHEAD(2) labelExpression = InsertNodeLabelExpression() ( properties=MapLiteral() )?
       | properties=MapLiteral()
       | ( v = Variable() )?
    )
    

    { return astFactory.nodePattern( pos( t ), v, labelExpression, properties, null); }
}

PATTERN_ATOM ParenthesizedPath() throws Exception:
{
    Token t;
    PATTERN p;
    VARIABLE v;
    EXPRESSION where = null;
    PATTERN_QUANTIFIER quantifier = null;
}
{
    t =  p = Pattern() (  where=Expression() )?  ( quantifier=Quantifier() )?
    {
        return astFactory.parenthesizedPathPattern( pos(t), p, where, quantifier );
    }
}

SET_ITEM SetNodeLabels(VARIABLE v) throws Exception:
{
    Token label;
    List> labels = new ArrayList<>();
    List dynamicLabels = new ArrayList<>();
    EXPRESSION e = null;
}
{
    (
        LOOKAHEAD( 2 ) label=LabelOrRelType() { labels.add( new StringPos<>( label.image, pos( label ) ) ); }
        |    e=Expression()  { dynamicLabels.add( e ); }
    )+
    {
        return astFactory.setLabels( v, labels, dynamicLabels, false );
    }
}

SET_ITEM SetNodeLabelsIs(VARIABLE v) throws Exception:
{
    Token label;
    List> labels = new ArrayList<>();
    List dynamicLabels = new ArrayList<>();
    EXPRESSION e = null;
}
{
     (LOOKAHEAD( 2 ) label=SymbolicNameString() { labels.add( new StringPos<>( label.image, pos( label ) ) ); }
        |   e=Expression()  { dynamicLabels.add( e ); })
    (
        LOOKAHEAD( 2 ) label=LabelOrRelType() { labels.add( new StringPos<>( label.image, pos( label ) ) ); }
        |    e=Expression()  { dynamicLabels.add( e ); }
    )*
    {
        return astFactory.setLabels( v, labels, dynamicLabels, true );
    }
}

REMOVE_ITEM RemoveNodeLabelsColon(VARIABLE v) throws Exception:
{
    Token label;
    List> labels = new ArrayList<>();
    List dynamicLabels = new ArrayList<>();
    EXPRESSION e = null;
}
{
    (
        LOOKAHEAD( 2 ) label=LabelOrRelType() { labels.add( new StringPos<>( label.image, pos( label ) ) ); }
        |    e=Expression()  { dynamicLabels.add( e ); }
    )+
    {
        return astFactory.removeLabels( v, labels, dynamicLabels, false );
    }
}

REMOVE_ITEM RemoveNodeLabelsIs(VARIABLE v) throws Exception:
{
    Token label;
    List> labels = new ArrayList<>();
    List dynamicLabels = new ArrayList<>();
    EXPRESSION e = null;
}
{
     (
              e=Expression()  { dynamicLabels.add( e ); }
            | label=SymbolicNameString() { labels.add( new StringPos<>( label.image, pos( label ) ) ); }
        )
    (
        LOOKAHEAD( 2 ) label=LabelOrRelType() { labels.add( new StringPos<>( label.image, pos( label ) ) ); }
        |    e=Expression()  { dynamicLabels.add( e ); }
    )*
    {
        return astFactory.removeLabels( v, labels, dynamicLabels, true );
    }
}

EXPRESSION LabelExpressionPredicate(EXPRESSION subject) throws Exception :
{
    LABEL_EXPRESSION exp = null;
}
{
    exp=LabelExpression(astFactory.nodeOrRelationshipType())
    {
        return astFactory.labelExpressionPredicate( subject, exp );
    }
}

Token LabelOrRelType() :
{
    Token t;
}
{
     t=SymbolicNameString()
    {
        return t;
    }
}

List> LabelOrRelTypes() :
{
    List> labels = new ArrayList<>();
    StringPos label;
}
{
     SymbolicNameString() { labels.add( new StringPos( token.image, pos(token) ) ); }
    (  SymbolicNameString() { labels.add( new StringPos( token.image, pos(token) ) ); } )*
    {
        return labels;
    }
}

EXPRESSION Properties() throws Exception:
{
    EXPRESSION e;
}
{
    (
        LOOKAHEAD( 3 ) e=MapLiteral()
        | e=Parameter( ParameterType.ANY )
    )
    {
        return e;
    }
}

REL_PATTERN RelationshipPattern() throws Exception:
{
    Token firstToken = token;
    boolean left = false;
    REL_PATTERN r;
}
{
    ( LeftArrow() { left = true; })? ArrowLine()
    (
        // Shortcuts for rel patterns not containing IS keyword or ambigious inlined WHERE
        LOOKAHEAD(ArrowLine()) r=RelationshipPatternDoubleDash(firstToken, left)
        | 
        (
            LOOKAHEAD() r=RelationshipPatternEmpty(firstToken, left)
            | LOOKAHEAD(Variable() ) r=RelationshipPatternOnlyVariable(firstToken, left)
            | LOOKAHEAD() r=RelationshipPatternColon(firstToken, left)
            | LOOKAHEAD(Variable() ) r=RelationshipPatternVariableColon(firstToken, left)
            | LOOKAHEAD(|) r=RelationshipPatternProperties(firstToken, left)
            | LOOKAHEAD(PathLength()) r=RelationshipPatternPathLength(firstToken, left)
            | LOOKAHEAD(Variable() PathLength() )
                r=RelationshipPatternOnlyVariablePathLength(firstToken, left)
            | r=RelationshipPatternComplex(firstToken, left)
        )
    )
    {
        return r;
    }
}

REL_PATTERN RelationshipPatternDoubleDash(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
}
{
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, null, null, null, null, null );
    }
}

REL_PATTERN RelationshipPatternEmpty(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
}
{
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, null, null, null, null, null );
    }
}

REL_PATTERN RelationshipPatternOnlyVariable(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    VARIABLE v = null;
}
{
    v=Variable()
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, v, null, null, null, null );
    }
}

REL_PATTERN RelationshipPatternColon(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    LABEL_EXPRESSION labelExpression = null;
    PATH_LENGTH pathLength = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this rel pattern started with a colon,
    // so it must be of the form [labelExpression pathLength? properties? WHERE?].
    labelExpression = LabelExpression(astFactory.relationshipType())
    ( pathLength=PathLength() )?
    ( properties=Properties() )?
    (  predicate=Expression() )?
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, null, labelExpression, pathLength, properties, predicate );
    }
}

REL_PATTERN RelationshipPatternVariableColon(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    VARIABLE v = null;
    LABEL_EXPRESSION labelExpression = null;
    PATH_LENGTH pathLength = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this rel pattern started with variable followed by a colon,
    // so it must be of the form [variable labelExpression pathLength? properties? WHERE?].
    v = Variable()
    labelExpression = LabelExpression(astFactory.relationshipType())
    ( pathLength=PathLength() )?
    ( properties=Properties() )?
    (  predicate=Expression() )?
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, v, labelExpression, pathLength, properties, predicate );
    }
}

REL_PATTERN RelationshipPatternProperties(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this rel pattern started with  a {,
    // so it must be of the form [properties WHERE?].
    properties=Properties()
    (  predicate=Expression() )?
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, null, null, null, properties, predicate );
    }
}

REL_PATTERN RelationshipPatternPathLength(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    PATH_LENGTH pathLength = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    // From the LOOKAHEAD outside, we know this rel pattern started with  a pathLength,
    // so it must be of the form [pathLength properties? WHERE?].
    pathLength=PathLength()
    ( properties=Properties() )?
    (  predicate=Expression() )?
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, null, null, pathLength, properties, predicate );
    }
}

REL_PATTERN RelationshipPatternOnlyVariablePathLength(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    VARIABLE v = null;
    PATH_LENGTH pathLength = null;
}
{
    // From the LOOKAHEAD outside, we know this rel pattern must be of the form [variable pathLength].
    v=Variable()
    pathLength=PathLength()
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, v, null, pathLength, null, null );
    }
}

REL_PATTERN RelationshipPatternComplex(Token firstToken, boolean left) throws Exception:
{
    boolean right = false;
    VARIABLE v = null;
    LABEL_EXPRESSION labelExpression = null;
    PATH_LENGTH pathLength = null;
    EXPRESSION properties = null;
    EXPRESSION predicate = null;
}
{
    (
       // The general relationship pattern is  [variable? labelExpression? pathLength? properties? WHERE?],
       // but since 'IS' and 'WHERE' can be both relationship variables, types and keywords,
       // we must check all of the 2^5 combinations that has not been covered earlier in a certain order to avoid ambiguity.
       // The below is a trial and error solution to make all tests in IsWhereRelationshipPatternParserTest work
       // while also have each combination in exactly one branch below.
       (
       LOOKAHEAD(LabelExpression(astFactory.relationshipType()) ( pathLength=PathLength() )?  Expression() )
            labelExpression = LabelExpression(astFactory.relationshipType())
            ( pathLength=PathLength() )?
             predicate=Expression()
       | LOOKAHEAD(  ( | )? ) labelExpression = LabelExpression(astFactory.relationshipType())
                                                                        ( pathLength=PathLength() )?
                                                                        ( properties=Properties() )?
                                                                        (  predicate=Expression() )?
       | LOOKAHEAD(v=Variable() ( PathLength() )? properties=Properties()) v=Variable()
            ( pathLength=PathLength() )?
            properties=Properties()
            (  predicate=Expression() )?
       | LOOKAHEAD(Variable() ( PathLength() )?  Expression() ) v=Variable() ( pathLength=PathLength() )?  predicate=Expression()
       | LOOKAHEAD( Expression() )  predicate=Expression()
       | LOOKAHEAD(3) v=Variable()
            labelExpression = LabelExpression(astFactory.relationshipType())
            ( pathLength=PathLength() )?
            ( properties=Properties() )?
            (  predicate=Expression() )?
       | LOOKAHEAD(LabelExpression(astFactory.relationshipType()) (PathLength())? Properties())
            labelExpression = LabelExpression(astFactory.relationshipType())
            ( pathLength=PathLength() )?
            properties=Properties()
            (  predicate=Expression() )?
       | labelExpression = LabelExpression(astFactory.relationshipType()) ( pathLength=PathLength() )?
       )
    )
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, v, labelExpression, pathLength, properties, predicate );
    }
}

REL_PATTERN InsertRelationshipPattern() throws Exception:
{
    Token firstToken = token;
    boolean left = false;
    boolean right = false;
    VARIABLE v = null;
    LABEL_EXPRESSION labelExpression = null;
    EXPRESSION properties = null;
}
{
    ( LeftArrow() { left = true; })? ArrowLine()
    
   (
   LOOKAHEAD(3) v=Variable() labelExpression = InsertRelationshipLabelExpression() ( properties=MapLiteral() )?
   | labelExpression = InsertRelationshipLabelExpression() ( properties=MapLiteral() )?
    )
    
    ArrowLine() ( RightArrow() { right = true; } )?
    {
        return astFactory.relationshipPattern( pos( firstToken.next ), left, right, v, labelExpression, null, properties, null );
    }
}

void LeftArrow() :
{}
{
   | 
}

void ArrowLine() :
{}
{
   | 
}

void RightArrow() :
{}
{
   | 
}

PATH_LENGTH PathLength() :
{
    Token t;
    PATH_LENGTH p = null;
}
{
    t= ( p=PathLengthLiteral( t ) )?
    {
        return p == null ? astFactory.pathLength( pos( t ), null, null, null, null ) : p;
    }
}

PATH_LENGTH PathLengthLiteral( Token t ) :
{
    Token v1 = null;
    Token v2 = null;
}
{
    LOOKAHEAD( 2 ) ( v1= )? t= ( v2= )?
        {
            return astFactory.pathLength( pos( t ), v1 == null ? null : pos ( v1 ), v2 == null ? null : pos ( v2 ),  v1 == null ? "" : v1.image, v2 == null ? "" : v2.image );
        }
    | v1=
        {
            String v = v1 == null ? "" : v1.image;
            return astFactory.pathLength( pos( t ), v1 == null ? null : pos ( v1 ), v1 == null ? null : pos ( v1 ), v, v);
        }
}

// LABEL EXPRESSIONS

LABEL_EXPRESSION LabelExpression( ENTITY_TYPE type ) throws Exception :
{
    LABEL_EXPRESSION e;
}
{
    (
        ( (
            LOOKAHEAD({ !labelExpressionStack.isEmpty() && labelExpressionStack.peek() == ExpectBar.EXPECT_BAR})
            e = LabelExpression4ExpectingBar(type)
            |
            LOOKAHEAD({ labelExpressionStack.isEmpty() || labelExpressionStack.peek() == ExpectBar.DO_NOT_EXPECT_BAR})
            e = LabelExpression4(type)
        ))
        |
        ( (
            LOOKAHEAD({ !labelExpressionStack.isEmpty() && labelExpressionStack.peek() == ExpectBar.EXPECT_BAR})
            e = LabelExpression4ExpectingBarIs(type)
            |
            LOOKAHEAD({ labelExpressionStack.isEmpty() || labelExpressionStack.peek() == ExpectBar.DO_NOT_EXPECT_BAR})
            e = LabelExpression4Is(type)
        ))
    )
    {
        return e;
    }
}

LABEL_EXPRESSION LabelExpression4( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=LabelExpression3(type) (
        LOOKAHEAD(2)
        t=
        (
             temp=LabelExpression3(type) { e = astFactory.labelColonDisjunction( pos( t ), e, temp, false ); }
        |
            temp=LabelExpression3(type) { e = astFactory.labelDisjunction( pos( t ), e, temp, false ); }
        )
    )*
    {
        return e;
    }
}


LABEL_EXPRESSION LabelExpression4Is( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=LabelExpression3Is(type) (
        LOOKAHEAD(2)
        t=
        (
             temp=LabelExpression3Is(type) { e = astFactory.labelColonDisjunction( pos( t ), e, temp, true ); }
        |
            temp=LabelExpression3Is(type) { e = astFactory.labelDisjunction( pos( t ), e, temp, true ); }
        )
    )*
    {
        return e;
    }
}
LABEL_EXPRESSION LabelExpression4ExpectingBar( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=LabelExpression3(type) (
        LOOKAHEAD(
             ()? LabelExpression3() 
        )
        t=
        (
             temp=LabelExpression3(type) { e = astFactory.labelColonDisjunction( pos( t ), e, temp, false ); }
        |
            temp=LabelExpression3(type) { e = astFactory.labelDisjunction( pos( t ), e, temp, false ); }
        )
    )*
    {
        return e;
    }
}

LABEL_EXPRESSION LabelExpression4ExpectingBarIs( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=LabelExpression3Is(type) (
        LOOKAHEAD(
             ()? LabelExpression3Is() 
        )
        t=
        (
             temp=LabelExpression3Is(type) { e = astFactory.labelColonDisjunction( pos( t ), e, temp, true ); }
        |
            temp=LabelExpression3Is(type) { e = astFactory.labelDisjunction( pos( t ), e, temp, true ); }
        )
    )*
    {
        return e;
    }
}

LABEL_EXPRESSION LabelExpression3( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=LabelExpression2(type)
    (
        LOOKAHEAD(2)
        (
            t= temp=LabelExpression2(type) { e = astFactory.labelConjunction( pos( t ), e, temp, false ); }
        |
            t= temp=LabelExpression2(type) { e = astFactory.labelColonConjunction( pos( t ), e, temp, false ); }
        )
    )*
    {
        return e;
    }
}


LABEL_EXPRESSION LabelExpression3Is( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=LabelExpression2Is(type)
    (
        LOOKAHEAD(2)
        (
            t= temp=LabelExpression2Is(type) { e = astFactory.labelConjunction( pos( t ), e, temp, true ); }
        |
            t= temp=LabelExpression2Is(type) { e = astFactory.labelColonConjunction( pos( t ), e, temp, true ); }
        )
    )*
    {
        return e;
    }
}

LABEL_EXPRESSION LabelExpression2( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
}
{
    (
        LOOKAHEAD( 3 ) t= e=LabelExpression2(type) { e = astFactory.labelNegation( pos( t ), e, false ); }
        | e=LabelExpression1(type)
    )
    {
        return e;
    }
}

LABEL_EXPRESSION LabelExpression2Is( ENTITY_TYPE type ) throws Exception :
{
    Token t;
    LABEL_EXPRESSION e;
}
{
    (
        LOOKAHEAD( 3 ) t= e=LabelExpression2Is(type) { e = astFactory.labelNegation( pos( t ), e, true ); }
        | e=LabelExpression1Is(type)
    )
    {
        return e;
    }
}

LABEL_EXPRESSION LabelExpression1( ENTITY_TYPE type ) throws Exception :
{
    LABEL_EXPRESSION e = null;
    String label = null;
    Token t;
    labelExpressionStack.push(ExpectBar.DO_NOT_EXPECT_BAR);
    EXPRESSION dynamicExpression;
    Boolean all = true;
}
{
    (
         e=LabelExpression4(type) 
        | t =  { e = astFactory.labelWildcard( pos ( t ), false ); }
        | t =  ( |  { all = false; })?  dynamicExpression = Expression()  { e = astFactory.dynamicLabelLeaf( pos ( t ), dynamicExpression, type, all, false ); }
        | t=SymbolicNameString() { e = astFactory.labelLeaf( pos ( t ), t.image, type, false ); }
    )
    {
        labelExpressionStack.pop();
        return e;
    }
}

LABEL_EXPRESSION LabelExpression1Is( ENTITY_TYPE type ) throws Exception :
{
    LABEL_EXPRESSION e = null;
    String label = null;
    Token t;
    labelExpressionStack.push(ExpectBar.DO_NOT_EXPECT_BAR);
    EXPRESSION dynamicExpression;
    Boolean all = true;
}
{
    (
         e=LabelExpression4Is(type) 
        | t =  { e = astFactory.labelWildcard( pos ( t ), true ); }
        | t =  ( |  { all = false; })?  dynamicExpression = Expression()  { e = astFactory.dynamicLabelLeaf( pos ( t ), dynamicExpression, type, all, true ); }
        | t=SymbolicLabelNameString() { e = astFactory.labelLeaf( pos ( t ), t.image, type, true ); }
    )
    {
        labelExpressionStack.pop();
        return e;
    }
}

LABEL_EXPRESSION InsertNodeLabelExpression() throws Exception:
{
    Token t;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
    boolean containsIs = false;
}
{
    (
         |   { containsIs = true; } ) e = InsertLabelConjunction(astFactory.nodeType(), containsIs
    )
    {
        return e;
    }
}

LABEL_EXPRESSION InsertRelationshipLabelExpression() throws Exception:
{
    LABEL_EXPRESSION e;
    boolean containsIs = false;
}
{
    (
         |   { containsIs = true; } ) e = InsertLabelLeaf(astFactory.relationshipType(), containsIs
    )
    {
        return e;
    }
}

LABEL_EXPRESSION InsertLabelConjunction( ENTITY_TYPE type, boolean containsIs) throws Exception:
{
    Token t;
    Token errorStart = null;
    LABEL_EXPRESSION e;
    LABEL_EXPRESSION temp;
}
{
    e=InsertLabelLeaf(type, containsIs)
    (
        LOOKAHEAD(2)
        (
            t= temp=InsertLabelLeaf(type, containsIs) { e = astFactory.labelConjunction( pos( t ), e, temp, containsIs ); }
        |
            errorStart=
        )
    )*
    {
        if (errorStart != null)
        {
            throw exceptionFactory.syntaxException(
                new ParseException( ASTExceptionFactory.colonConjunctionInInsertNotSupported ),
                errorStart.beginOffset, errorStart.beginLine, errorStart.beginColumn );
        }
        return e;
    }
}


LABEL_EXPRESSION InsertLabelLeaf( ENTITY_TYPE type, boolean containsIs ) :
{
    Token t;
}
{
    t=SymbolicNameString()
    {
        return astFactory.labelLeaf( pos ( t ), t.image, type, containsIs );
    }
}

// EXPRESSIONS

EXPRESSION Expression() throws Exception:
{
    EXPRESSION e;
}
{
    e=Expression12()
    {
        return e;
    }
}

EXPRESSION Expression12() throws Exception:
{
    Token t;
    EXPRESSION e;
    EXPRESSION temp;
}
{
    e=Expression11() ( t= temp=Expression11() { e = astFactory.or( pos( t ), e, temp ); } )*
    {
        return e;
    }
}

EXPRESSION Expression11() throws Exception:
{
    Token t;
    EXPRESSION e;
    EXPRESSION temp;
}
{
    e=Expression10() ( t= temp=Expression10() { e = astFactory.xor( pos( t ), e, temp ); } )*
    {
        return e;
    }
}

EXPRESSION Expression10() throws Exception:
{
    Token t;
    EXPRESSION e;
    EXPRESSION temp;
}
{
    e=Expression9() (t= temp=Expression9() { e = astFactory.and( pos( t ), e, temp ); } )*
    {
        return e;
    }
}

EXPRESSION Expression9() throws Exception:
{
    Token t;
    EXPRESSION e;
}
{
    (
        LOOKAHEAD( 3 ) t= e=Expression9() { e = astFactory.not( pos( t ), e ); }
        | e=Expression8()
    )
    {
        return e;
    }
}

/**
 * Implements comparison operation chaining. That is that expressions like `a < b < c` are interpreted as `a < b AND b < c`.
 * See https://neo4j.com/docs/cypher-manual/current/syntax/operators/#cypher-operations-chaining
 */
EXPRESSION Expression8() throws Exception:
{
    Token t;
    EXPRESSION e;
    EXPRESSION lhs;
    EXPRESSION rhs;
    List expressions = new ArrayList();
}
{
    e=Expression7() { lhs = e; } (
        LOOKAHEAD( 2 ) t=  rhs=Expression7() { expressions.add( astFactory.eq( pos( t ), lhs, rhs)); lhs = rhs; }
        | t= rhs=Expression7() { expressions.add( astFactory.neq( pos( t ), lhs, rhs ) ); lhs = rhs; }
        | t= rhs=Expression7() { expressions.add( astFactory.neq2( pos( t ), lhs, rhs ) ); lhs = rhs; }
        | t= rhs=Expression7() { expressions.add( astFactory.lte( pos( t ), lhs, rhs ) ); lhs = rhs; }
        | t= rhs=Expression7() { expressions.add( astFactory.gte( pos( t ), lhs, rhs ) ); lhs = rhs; }
        | t= rhs=Expression7() { expressions.add( astFactory.lt( pos( t ), lhs, rhs ) ); lhs = rhs; }
        | t= rhs=Expression7() { expressions.add( astFactory.gt( pos( t ), lhs, rhs ) ); lhs = rhs; }
    )*
    {
        if ( expressions.isEmpty() )
        {
            return e;
        }
        else if ( expressions.size() == 1 )
        {
            return expressions.get( 0 );
        }
        else
        {
            return astFactory.ands( expressions );
        }
    }
}

EXPRESSION Expression8ComparatorExpression( EXPRESSION lhs ) throws Exception:
{
    Token t;
    EXPRESSION e;
    EXPRESSION rhs;
}
{
        LOOKAHEAD( 2 ) t=  rhs=Expression7() { return astFactory.eq( pos( t ), lhs, rhs); }
        | t= rhs=Expression7() { return astFactory.neq( pos( t ), lhs, rhs ); }
        | t= rhs=Expression7() { return astFactory.neq2( pos( t ), lhs, rhs ); }
        | t= rhs=Expression7() { return astFactory.lte( pos( t ), lhs, rhs ); }
        | t= rhs=Expression7() { return astFactory.gte( pos( t ), lhs, rhs ); }
        | t= rhs=Expression7() { return astFactory.lt( pos( t ), lhs, rhs ); }
        | t= rhs=Expression7() { return astFactory.gt( pos( t ), lhs, rhs ); }
}

EXPRESSION Expression7() throws Exception:
{
    EXPRESSION e;
}
{
    e=Expression6() ( e=ComparisonExpression6( e ) )?
    {
        return e;
    }
}

EXPRESSION ComparisonExpression6( EXPRESSION lhs ) throws Exception:
{
    Token t;
    EXPRESSION rhs;
    ParserCypherTypeName typeName;
    ParserNormalForm normalForm;
}
{
    LOOKAHEAD( 2 ) t= rhs=Expression6() { return astFactory.regeq( pos( t ), lhs, rhs ); }
    | t=  rhs=Expression6() { return astFactory.startsWith( pos( t ), lhs, rhs ); }
    | t=  rhs=Expression6() { return astFactory.endsWith( pos( t ), lhs, rhs ); }
    | t= rhs=Expression6() { return astFactory.contains( pos( t ), lhs, rhs ); }
    | t= rhs=Expression6() { return astFactory.in( pos( t ), lhs, rhs ); }
    | t= (
         { return astFactory.isNull( pos( t ), lhs ); }
        |  (
             { return astFactory.isNotNull( pos( t ), lhs ); }
            | (( | ) typeName = cypherTypeName() { return astFactory.isNotTyped( pos( t ), lhs, typeName ); })
            | (normalForm = normalForm()  { return astFactory.isNotNormalized( pos( t ), lhs, normalForm ); })
        )
        | ( | ) typeName = cypherTypeName() { return astFactory.isTyped( pos( t ), lhs, typeName ); }
        |  normalForm = normalForm() { return astFactory.isNormalized( pos( t ), lhs, normalForm ); }

    )
    | t= typeName = cypherTypeName() { return astFactory.isTyped( pos( t ), lhs, typeName ); }
}

ParserNormalForm normalForm():
{
    ParserNormalForm normalForm = ParserNormalForm.NFC;
}
{
        (
          
          |  { normalForm = ParserNormalForm.NFD; }
          |  { normalForm = ParserNormalForm.NFKC; }
          |  { normalForm = ParserNormalForm.NFKD; }
        )?
        { return normalForm; }
}

EXPRESSION Expression6() throws Exception:
{
    Token t;
    EXPRESSION lhs;
    EXPRESSION rhs;
}
{
    lhs=Expression5() (
        t= rhs=Expression5() { lhs = astFactory.plus( pos( t ), lhs, rhs); }
        | t= rhs=Expression5() { lhs = astFactory.minus( pos( t ), lhs, rhs); }
        | t= rhs=Expression5() { lhs = astFactory.concatenate( pos( t ), lhs, rhs); }
    )*
    {
        return lhs;
    }
}

EXPRESSION Expression5() throws Exception:
{
    Token t;
    EXPRESSION lhs;
    EXPRESSION rhs;
}
{
    lhs=Expression4() (
        t= rhs=Expression4() { lhs = astFactory.multiply( pos( t ), lhs, rhs ); }
        | t= rhs=Expression4() { lhs = astFactory.divide( pos( t ), lhs, rhs ); }
        | t= rhs=Expression4() { lhs = astFactory.modulo( pos( t ), lhs, rhs ); }
    )*
    {
        return lhs;
    }
}

EXPRESSION Expression4() throws Exception:
{
    Token t;
    EXPRESSION lhs;
    EXPRESSION rhs;
}
{
    lhs=Expression3() ( t= rhs=Expression3() { lhs = astFactory.pow( pos( t ), lhs, rhs ); } )*
    {
        return lhs;
    }
}

EXPRESSION Expression3() throws Exception:
{
    Token t;
    EXPRESSION e;
}
{
    (
        LOOKAHEAD(3) e=Expression2()
        | t =  e=Expression2() { e = astFactory.unaryPlus( pos( t ), e ); }
        | t =  e=Expression2() { e = astFactory.unaryMinus( pos( t ), e ); }
    )
    {
        return e;
    }
}

EXPRESSION Expression2() throws Exception:
{
    EXPRESSION e;
}
{
    e=Expression1()
    ( LOOKAHEAD( |  |  |  ( |  |  | SymbolicLabelNameString() ))
        e=PostFix1( e ) )*
    {
        return e;
    }
}

EXPRESSION PostFix1( EXPRESSION subject ) throws Exception:
{
    Token t;
    EXPRESSION e1 = null;
    EXPRESSION e2 = null;
    EXPRESSION ret;
}
{
    (
        ret=Property( subject )
        | LOOKAHEAD() ret=LabelExpressionPredicate( subject )
        | LOOKAHEAD( ( |  |  | SymbolicLabelNameString())) ret=LabelExpressionPredicate( subject )
        | LOOKAHEAD( Expression() )  e1=Expression()  { ret=astFactory.listLookup( subject, e1 ); }
        | t= ( e1=Expression() )?  ( e2=Expression() )?  { ret=astFactory.listSlice( pos( t ), subject, e1, e2 ); }
    )
    {
        return ret;
    }
}

PROPERTY Property( EXPRESSION subject ) :
{
    EXPRESSION e;
    StringPos propKeyName;
}
{
     propKeyName=PropertyKeyName()
    {
        return astFactory.property( subject, propKeyName );
    }
}

EXPRESSION DynamicPropertyExpression() throws Exception:
{
    EXPRESSION subject;
    EXPRESSION e;
}
{
    subject=Expression1() e=DynamicProperty( subject )
    {
        return e; 
    }
}

PROPERTY PropertyExpression() throws Exception:
{
    EXPRESSION subject;
    PROPERTY p;
}
{
    subject=Expression1() ( p=Property( subject ) { subject = p; } )+
    {
        return p;
    }
}

EXPRESSION DynamicProperty( EXPRESSION subject ) throws Exception:
{
    EXPRESSION e;
}
{
     e=Expression()  
    {
        return astFactory.listLookup( subject, e ); 
    }
}

EXPRESSION Expression1() throws Exception:
{
    EXPRESSION e = null;
    Token t;
}
{
    (
        e=NumberLiteral()
        | e=StringLiteral()
        | e=Parameter( ParameterType.ANY )
        | t= { e = astFactory.newTrueLiteral( pos( t ) ); }
        | t= { e = astFactory.newFalseLiteral( pos( t ) ); }
        | ( t= | t= ) { e = astFactory.newInfinityLiteral( pos( t ) ); }
        | t= { e = astFactory.newNaNLiteral( pos( t ) ); }
        | LOOKAHEAD( 2 ) t= { e = astFactory.newNullLiteral( pos( t ) ); }
        | LOOKAHEAD( 3 ) e=CaseExpression()
        | LOOKAHEAD( 3 ) t=    { e = astFactory.newCountStar( pos( t ) ); }
        | LOOKAHEAD( 3 ) e=MapLiteral()
        | LOOKAHEAD( 3 ) e=ExistsExpression() // `exists { match` should not be a map-projection
        | LOOKAHEAD( 3 ) e=CountExpression()
        | LOOKAHEAD( 3 ) e=CollectExpression()
        | LOOKAHEAD( 2 ) e=MapProjection()
        | LOOKAHEAD(  v=Variable()  e=Expression() (  |  |  )) e=ListComprehension() // before literal because it takes precedence. Lookahead due to potential confusion with ListLiteral
        | LOOKAHEAD( PatternComprehensionPrefix() ) e=PatternComprehension() // Only after matching up to "WHERE" or "|" can we be sure it is a PatternComprehension and not a ListLiteral.
        | LOOKAHEAD( 3 ) e=ListLiteral()
        | LOOKAHEAD( 3 ) e=ReduceExpression()
        | LOOKAHEAD( 3 ) e=AllExpression()
        | LOOKAHEAD( 3 ) e=AnyExpression()
        | LOOKAHEAD( 3 ) e=NoneExpression()
        | LOOKAHEAD( 3 ) e=SingleExpression()
        | LOOKAHEAD( 2 ) e=NormalizeExpression()
        | LOOKAHEAD( 2 ) e=TrimFunction()
        | LOOKAHEAD( NodePattern() RelationshipPattern() ) e=PatternExpression()
        | LOOKAHEAD( 2 ) e=ShortestPathExpression()
        | 
          { labelExpressionStack.push(ExpectBar.DO_NOT_EXPECT_BAR); }
          e=Expression()
          { labelExpressionStack.pop(); }
          
        | LOOKAHEAD( (SymbolicNameString()  )* SymbolicNameString()  ) e=FunctionInvocation(false)
        | e=Variable()
    )
    {
        return e;
    }
}

EXPRESSION CaseExpression() throws Exception:
{
    EXPRESSION e = null;
}
{
    (
        LOOKAHEAD(  Expression()  ) e=SimpleCaseExpression()
        | e=GeneralCaseExpression()
    )
    {
        return e;
    }
}

EXPRESSION SimpleCaseExpression() throws Exception:
{
    Token t;
    EXPRESSION caseExpr;
    EXPRESSION e;
    List tempWhen = new ArrayList<>();
    List when = new ArrayList<>();
    List then = new ArrayList<>();
    EXPRESSION elseCase = null;
}
{
    t=  caseExpr=Expression()
     tempWhen=simpleCaseWhenOperandList(caseExpr) { when.addAll( tempWhen ); }
     e=Expression() { for (var i = 0; i < tempWhen.size(); i++) { then.add( e ); } }
    (
         tempWhen=simpleCaseWhenOperandList(caseExpr) { when.addAll( tempWhen ); }
         e=Expression() { for (var i = 0; i < tempWhen.size(); i++) { then.add( e ); } }
    )*
    (  elseCase=Expression() )?
    
    {
        return astFactory.caseExpression( pos( t ), caseExpr, when, then, elseCase);
    }
}

List simpleCaseWhenOperandList( EXPRESSION caseExpr ) throws Exception:
{
    EXPRESSION e;
    List list = new ArrayList<>();
}
{
    e=whenOperand( caseExpr ) { list.add( e ); }
    ( LOOKAHEAD(2)  e=whenOperand( caseExpr ) { list.add( e ); } )*
    {
        return list;
    }
}

EXPRESSION whenOperand( EXPRESSION lhs ) throws Exception:
{
    Token t;
    EXPRESSION e;
    EXPRESSION rhs;
    ParserCypherTypeName typeName;
    ParserNormalForm normalForm;
}
{
    { t = token; }
    (
         t= rhs=Expression6() { return astFactory.regeq( pos( t ), lhs, rhs ); }
         | t=  rhs=Expression6() { return astFactory.startsWith( pos( t ), lhs, rhs ); }
         | t=  rhs=Expression6() { return astFactory.endsWith( pos( t ), lhs, rhs ); }
         | LOOKAHEAD( 3 ) t=  { return astFactory.isNull( pos( t ), lhs ); }
         | LOOKAHEAD( 3 ) t=   { return astFactory.isNotNull( pos( t ), lhs ); }
         | LOOKAHEAD( 3 ) t= (normalForm = normalForm())  { return astFactory.isNormalized( pos( t ), lhs, normalForm ); }
         | LOOKAHEAD( 3 ) t=  (normalForm = normalForm())  { return astFactory.isNotNormalized( pos( t ), lhs, normalForm ); }
         | LOOKAHEAD( 3 ) t=  typeName = cypherTypeName() { return astFactory.isTyped( pos( t ), lhs, typeName ); }
         | LOOKAHEAD( 3 ) t=   typeName = cypherTypeName() { return astFactory.isNotTyped( pos( t ), lhs, typeName ); }
         | t= typeName = cypherTypeName() { return astFactory.isTyped( pos( t ), lhs, typeName ); }
         | e=Expression8ComparatorExpression( lhs )  { return e; }
         | e=Expression() { return astFactory.eq( pos( t ), lhs, e); }
    )
}

EXPRESSION GeneralCaseExpression() throws Exception:
{
    Token t;
    EXPRESSION caseExpr = null;
    EXPRESSION e;
    List when = new ArrayList<>();
    List then = new ArrayList<>();
    EXPRESSION elseCase = null;
}
{
    t=
     e=Expression() { when.add( e ); }
     e=Expression() { then.add( e ); }
    (
         e=Expression() { when.add( e ); }
         e=Expression() {then.add( e );}
    )*
    (  elseCase=Expression() )?
    
    {
        return astFactory.caseExpression( pos( t ), caseExpr, when, then, elseCase);
    }
}

EXPRESSION ListComprehension() throws Exception:
{
    Token t;
    VARIABLE v;
    EXPRESSION e;
    EXPRESSION where = null;
    EXPRESSION projection = null;
    labelExpressionStack.push(ExpectBar.EXPECT_BAR);
}
{
    t= v=Variable()  e=Expression() (  where=Expression() )? (  projection=Expression() )? 
    {
        labelExpressionStack.pop();
        return astFactory.listComprehension( pos( t ), v, e, where, projection );
    }
}

EXPRESSION PatternComprehension() throws Exception:
{
    Token t;
    Token relationshipPatternPosition;
    VARIABLE v = null;
    PATTERN p;
    EXPRESSION where = null;
    EXPRESSION projection = null;
}
{
    t= ( v=Variable()  )? {relationshipPatternPosition = token;} p=PathPatternNonEmpty()
    (  {
        labelExpressionStack.push(ExpectBar.EXPECT_BAR);
      }
      where=Expression()
      {
        labelExpressionStack.pop();
      }
    )?  projection=Expression() 
    {
        return astFactory.patternComprehension( pos( t ), pos( relationshipPatternPosition.next ), v, p, where, projection );
    }
}

void PatternComprehensionPrefix() throws Exception:
{}
{
     ( Variable()  )? PathPatternNonEmpty() (  |  )
}

EXPRESSION ReduceExpression() throws Exception:
{
    Token t;
    VARIABLE acc;
    EXPRESSION accExpr;
    VARIABLE v;
    EXPRESSION vExpr;
    EXPRESSION innerExpr;
}
{
    t=
    
        acc=Variable()  accExpr=Expression()
         v=Variable()  vExpr=Expression()
         innerExpr=Expression()
    
    {
        return astFactory.reduceExpression( pos( t ), acc, accExpr, v, vExpr, innerExpr );
    }
}

EXPRESSION AllExpression() throws Exception:
{
    Token t;
    VARIABLE v;
    EXPRESSION e;
    EXPRESSION where = null;
}
{
    t=  v=Variable()  e=Expression() (  where=Expression() )? 
    {
        return astFactory.allExpression( pos( t ), v, e, where );
    }
}

EXPRESSION AnyExpression() throws Exception:
{
    Token t;
    VARIABLE v;
    EXPRESSION e;
    EXPRESSION where = null;
}
{
    t=  v=Variable()  e=Expression() (  where=Expression() )? 
    {
        return astFactory.anyExpression( pos( t ), v, e, where );
    }
}

EXPRESSION NoneExpression() throws Exception:
{
    Token t;
    VARIABLE v;
    EXPRESSION e;
    EXPRESSION where = null;
}
{
    t=  v=Variable()  e=Expression() (  where=Expression() )? 
    {
        return astFactory.noneExpression( pos( t ), v, e, where );
    }
}

EXPRESSION SingleExpression() throws Exception:
{
    Token t;
    VARIABLE v;
    EXPRESSION e;
    EXPRESSION where = null;
}
{
    t=  v=Variable()  e=Expression() (  where=Expression() )? 
    {
        return astFactory.singleExpression( pos( t ), v, e, where );
    }
}

EXPRESSION NormalizeExpression() throws Exception:
{
    Token t;
    EXPRESSION e;
    ParserNormalForm normalForm = ParserNormalForm.NFC;
}
{
    t= 
        e=Expression()
        ( (
          
          |  { normalForm = ParserNormalForm.NFD; }
          |  { normalForm = ParserNormalForm.NFKC; }
          |  { normalForm = ParserNormalForm.NFKD; }
        ))?
        
    {
        return astFactory.normalizeExpression( pos( t ), e, normalForm );
    }
}


EXPRESSION TrimFunction() throws Exception:
{
    Token t;
    EXPRESSION trimCharacterString = null;
    EXPRESSION trimSource = null;
    EXPRESSION expression = null;
    ParserTrimSpecification trimSpec = ParserTrimSpecification.BOTH;
}
{
    t= 
    // TRIM(FROM )
    (LOOKAHEAD()  trimSource=Expression()  { return astFactory.trimFunction( pos( t ), trimSpec, trimCharacterString, trimSource ); }
    // TRIM(BOTH/LEADING/TRAILING FROM )
    | LOOKAHEAD(( |  | ) )
        (  |  { trimSpec = ParserTrimSpecification.LEADING; } |  { trimSpec = ParserTrimSpecification.TRAILING; })
             trimSource=Expression()  { return astFactory.trimFunction( pos( t ), trimSpec, trimCharacterString, trimSource ); }
    // TRIM(BOTH/LEADING/TRAILING  FROM )
    | LOOKAHEAD( |  | )
        (  |  { trimSpec = ParserTrimSpecification.LEADING; } |  { trimSpec = ParserTrimSpecification.TRAILING; })
        trimCharacterString = Expression()  trimSource=Expression()  { return astFactory.trimFunction( pos( t ), trimSpec, trimCharacterString, trimSource ); }
    // TRIM( FROM ) or TRIM()
    | expression = Expression()
        (LOOKAHEAD() ( { return astFactory.trimFunction( pos( t ), trimSpec, trimCharacterString, expression ); })
        |  trimSource=Expression()  { return astFactory.trimFunction( pos( t ), trimSpec, expression, trimSource ); }))
}


EXPRESSION PatternExpression() throws Exception:
{
    PATTERN p;
    Token t;
}
{
    { t = token; }
    p=PathPatternNonEmpty()
    {
        return astFactory.patternExpression( pos( t.next ), p );
    }
}

EXPRESSION ShortestPathExpression() throws Exception:
{
    PATTERN p;
    Token t;
}
{
    { t = token; }
    p=ShortestPathPattern()
    {
        return astFactory.patternExpression( pos( t.next ), p );
    }
}

EXPRESSION MapProjection() throws Exception:
{
    Token t;
    VARIABLE v;
    MAP_PROJECTION_ITEM x;
    List items = new ArrayList<>();
}
{
    v=Variable() t= ( x=MapProjectionItem() { items.add( x ); } (  x=MapProjectionItem() { items.add( x ); } )* )? 
    {
        return astFactory.mapProjection( pos( t ), v, items );
    }
}

MAP_PROJECTION_ITEM MapProjectionItem() throws Exception:
{
    Token t;
    StringPos p;
    EXPRESSION e;
    VARIABLE v;
}
{
    LOOKAHEAD( 2 ) p=PropertyKeyName()  e=Expression() { return astFactory.mapProjectionLiteralEntry( p, e ); }
    | LOOKAHEAD( 2 )  p=PropertyKeyName() { return astFactory.mapProjectionProperty( p ); }
    | v=Variable() { return astFactory.mapProjectionVariable( v ); }
    |  t= { return astFactory.mapProjectionAll( pos( t ) ); }
}

EXPRESSION ExistsExpression() throws Exception:
{
    Token t;
    List patterns = null;
    MATCH_MODE matchMode = null;
    QUERY q = null;
    WHERE where = null;
}
{
    t=
    
        (q=RegularQuery() | ( (matchMode = MatchMode() )? patterns=PatternList() ( where=WhereClause() )?))
    
    {
        return astFactory.existsExpression( pos( t ), matchMode, patterns, q, where );
    }
}

EXPRESSION CountExpression() throws Exception:
{
    Token t;
    List patterns = null;
    MATCH_MODE matchMode = null;
    QUERY q = null;
    WHERE where = null;
}
{
    t=
    
        (q=RegularQuery() | ((matchMode = MatchMode() )? patterns=PatternList() ( where=WhereClause() )?))
    
    {
        return astFactory.countExpression( pos( t ), matchMode, patterns, q, where );
    }
}

EXPRESSION CollectExpression() throws Exception:
{
    Token t;
    QUERY q;
}
{
    t=
    
        q=RegularQuery()
    
    {
        return astFactory.collectExpression( pos( t ), q );
    }
}

EXPRESSION StringLiteral() :
{
    Token t;
}
{
    t=StringToken() { return astFactory.newString( pos( t ), endPos( t ), token.image ); }
}

EXPRESSION NumberLiteral() :
{
    Token sign = null;
    Token t;
    boolean negated = false;
}
{
    ( sign= { negated = true; } )?
    (
        t= { return astFactory.newDouble(  pos( sign != null ? sign : t ) , sign != null ? sign.image + token.image : token.image ); }
        | t= { return astFactory.newDecimalInteger( pos( sign != null ? sign : t ), token.image, negated ); }
        | t= { return astFactory.newHexInteger( pos( sign != null ? sign : t ), token.image, negated ); }
        | t= { return astFactory.newOctalInteger( pos( sign != null ? sign : t ), token.image, negated ); }
    )
}

EXPRESSION SignedIntegerLiteral():
{
    Token sign = null;
    Token number;
    boolean negated = false;
}
{
    ( sign= { negated = true; })?
    number=
    {
        return astFactory.newDecimalInteger( pos( sign != null ? sign : number ), token.image, negated );
    }
}

EXPRESSION ListLiteral() throws Exception:
{
    Token t;
    EXPRESSION e;
    List list = new ArrayList<>();
}
{
    t= ( e=Expression() { list.add( e ); } (  e=Expression() { list.add( e ); } )* )? 
    {
        return astFactory.listLiteral( pos( t ), list );
    }
}

EXPRESSION StringListLiteral() throws Exception:
{
    Token t;
    EXPRESSION e;
    List list = new ArrayList<>();
}
{
    t= ( e=StringLiteral() { list.add( e ); } (  e=StringLiteral() { list.add( e ); } )* )? 
    {
        return astFactory.listLiteral( pos( t ), list );
    }
}

EXPRESSION MapLiteral() throws Exception:
{
    Token t;
    StringPos key;
    EXPRESSION value;
    List> keys = new ArrayList<>();
    List values = new ArrayList<>();
}
{
    t= ( key=PropertyKeyName()  value=Expression() { keys.add( key ); values.add( value ); }
           (  key=PropertyKeyName()  value=Expression() { keys.add( key ); values.add( value ); } )* )? 
    {
        return astFactory.mapLiteral( pos( t ), keys, values);
    }
}

StringPos PropertyKeyName() :
{
    Token t;
}
{
    t=SymbolicNameString()
    {
        return new StringPos( t.image,  pos( t ) );
    }
}

PARAMETER Parameter( ParameterType type ) :
{
    Token t;
    VARIABLE v;
}
{
    t= (
        v=Variable() { return astFactory.newParameter( pos( t ), v, type ); }
        |  { return astFactory.newParameter( pos( t ), token.image, type ); }
    )
}

FUNCTION_INVOCATION FunctionInvocation(boolean calledFromUseClause)  throws Exception:
{
    Token before = token;
    Token nameSpaceToken;
    Token nameToken;
    List namespace;
    boolean distinct = false;
    EXPRESSION e;
    List arguments = new ArrayList<>();
}
{
    namespace=Namespace() nameToken=SymbolicNameString()
    
        ( LOOKAHEAD( 2 )  { distinct = true; } |  )?
        ( e=Expression() { arguments.add( e ); } (  e=Expression() { arguments.add( e ); } )* )?
    
    {
        return astFactory.functionInvocation( pos( before.next ), pos( nameToken ), namespace, nameToken.image, distinct, arguments, calledFromUseClause );
    }
}

List Namespace() :
{
    Token t;
    List parts = new ArrayList<>();
}
{
    ( LOOKAHEAD( 2 ) t=SymbolicNameString() { parts.add( t.image ); }  )*
    {
        return parts;
    }
}

List VariableList1() :
{
    Token t;
    List list = new ArrayList<>();
}
{
    t=SymbolicNameString() { list.add( astFactory.newVariable( pos( t ), t.image ) ); }
    (  t=SymbolicNameString() { list.add( astFactory.newVariable( pos( t ), t.image) ); })*
    {
        return list;
    }
}

VARIABLE Variable() :
{
    Token t;
}
{
    t=SymbolicNameString()
    {
        return astFactory.newVariable( pos( t ), t.image );
    }
}

List SymbolicNameList1() :
{
    Token n;
    List list = new ArrayList<>();
}
{
    n=SymbolicNameString() { list.add(n.image); } (  n=SymbolicNameString() { list.add( n.image ); } )*
    {
        return list;
    }
}

// Command Section

STATEMENT_WITH_GRAPH CreateCommand( USE_CLAUSE useClause ) throws Exception:
{
    Token start;
    boolean replace = false;
    STATEMENT_WITH_GRAPH statement;
}
{
    start= (  { replace = true; })? (
        statement=CreateRole( start, replace )
        | statement=CreateUser( start, replace )
        | statement=CreateDatabase( start, replace )
        | statement=CreateConstraint( start, replace )
        | statement=CreateIndex( start, replace )
        | statement=CreateAlias( start, replace )
        | statement=CreateCompositeDatabase( start, replace )
    )
    {
        return astFactory.useGraph( statement, useClause );
    }
}

STATEMENT Command( USE_CLAUSE useClause ) throws Exception:
{
    STATEMENT statement = null;
}
{
    (

        statement=CommandWithUseGraph( useClause )
        | statement=ShowCommand( useClause )
        | statement=TerminateCommand( useClause )
    )
    {
        return statement;
    }
}

STATEMENT_WITH_GRAPH CommandWithUseGraph( USE_CLAUSE useClause ) throws Exception:
{
    STATEMENT_WITH_GRAPH s;
}
{
    (
        s=DropCommand()
        | s=AlterCommand()
        | s=RenameCommand()
        | s=DenyPrivilege()
        | s=RevokeCommand()
        | s=GrantCommand()
        | s=StartDatabase()
        | s=StopDatabase()
        | s=EnableServerCommand()
        | s=AllocationCommand()
    )
    {
        return astFactory.useGraph( s, useClause );
    }
}

STATEMENT_WITH_GRAPH DropCommand() throws Exception:
{
    Token start;
    STATEMENT_WITH_GRAPH s;
}
{
    start=
    (
        s=DropRole( start )
        | s=DropUser( start )
        | s=DropDatabase( start )
        | s=DropConstraint( start )
        | s=DropIndex( start )
        | s=DropAlias( start )
        | s=DropServer( start )
    )
    {
        return s;
    }
}

STATEMENT_WITH_GRAPH AlterCommand() throws Exception:
{
    Token start = null;
    STATEMENT_WITH_GRAPH s;
}
{
    start=
    (
        s=AlterDatabase( start )
        | s=AlterAlias( start )
        | s=AlterCurrentUser( start )
        | s=AlterUser( start )
        | s=AlterServer( start )
    )
    {
        return s;
    }
}

// SHOW commands

STATEMENT ShowCommand( USE_CLAUSE useClause ) throws Exception:
{
    Token start = null;
    Token showCommandType = null;
    STATEMENT statement = null;
    List clauses = null;
}
{
    start=
    (
        showCommandType= statement=ShowAllCommand( start, useClause )
        |  ( | ) statement=ShowRoles( start, useClause, false )
        | showCommandType= clauses=ShowIndexesAllowBrief( start, ShowCommandFilterTypes.BTREE )
        | showCommandType= clauses=ShowIndexesNoBrief( start, ShowCommandFilterTypes.RANGE )
        | showCommandType= clauses=ShowIndexesNoBrief( start, ShowCommandFilterTypes.FULLTEXT )
        | showCommandType= clauses=ShowIndexesNoBrief( start, ShowCommandFilterTypes.TEXT )
        | showCommandType= clauses=ShowIndexesNoBrief( start, ShowCommandFilterTypes.POINT )
        | showCommandType= clauses=ShowIndexesNoBrief( start, ShowCommandFilterTypes.VECTOR )
        | showCommandType= clauses=ShowIndexesNoBrief( start, ShowCommandFilterTypes.LOOKUP )
        | showCommandType= clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.UNIQUE )
        | showCommandType= clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.UNIQUE )
        | showCommandType= clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.KEY )
        | showCommandType= clauses=ShowNodeCommand( start )
        | showCommandType= clauses=ShowPropertyCommand( start, ShowCommandFilterTypes.EXIST )
        | showCommandType= clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.EXIST )
        | showCommandType= ShowConstraintsAllowBrief( start )
        | showCommandType= clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.OLD_EXIST )
        | showCommandType= clauses=ShowRelationshipCommand( start )
        | showCommandType= clauses=ShowRelCommand( start )
        | showCommandType=  clauses=ShowFunctions( start, ShowCommandFilterTypes.BUILT_IN )
        | clauses=ShowIndexesAllowBrief( start, ShowCommandFilterTypes.ALL )
        | statement=ShowDatabase( start, useClause )
        | statement=ShowCurrentUser( start, useClause )
        | clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.ALL )
        | clauses=ShowProcedures( start )
        | clauses=ShowSettings( start )
        | clauses=ShowFunctions( start, ShowCommandFilterTypes.ALL )
        | clauses=ShowTransactions( start )
        | statement=ShowAliases( start, useClause )
        | statement=ShowServers( start, useClause )
        | statement=ShowPrivileges( start, useClause )
        | statement=ShowSupportedPrivileges( start, useClause )
        | ( | )
            (
                // Need these lookaheads to distinguish e.g. 'SHOW ROLE[S] YIELD ...' from 'SHOW ROLE[S] yield[, ...] PRIVILEGE[S]'
                LOOKAHEAD((  |  |  ) (  |  |  )) statement=ShowRolePrivileges( start, useClause )
                | LOOKAHEAD( |  |  | ) statement=ShowRoles( start, useClause, true )
                | statement=ShowRolePrivileges( start, useClause )
            )
        // Need a lookahead to distinguish 'SHOW USER DEFINED FUNCTION[S]' from 'SHOW USER defined[, ...] PRIVILEGE[S]'
        | LOOKAHEAD(  (  |  )) showCommandType=  clauses=ShowFunctions( start, ShowCommandFilterTypes.USER_DEFINED )
        | ( | )
            (
                // Need these lookaheads to distinguish e.g. 'SHOW USER[S] YIELD ...' from 'SHOW USER[S] yield[, ...] PRIVILEGE[S]'
                LOOKAHEAD((  |  |  ) (  |  |  )) statement=ShowUserPrivileges( start, useClause )
                | LOOKAHEAD( |  |  | ) statement=ShowUsers( start, useClause )
                | statement=ShowUserPrivileges( start, useClause )
            )
    )
    {
        if ( clauses != null ) {
            if ( useClause != null )
            {
                clauses.add( 0, useClause );
            }
            statement = astFactory.newSingleQuery( pos( start ), clauses );
        }
        return statement;
    }
}

STATEMENT TerminateCommand( USE_CLAUSE useClause ) throws Exception:
{
    Token start = null;
    List clauses = null;
}
{
    start=
    (
        clauses=TerminateTransactions( start )
    )
    {
        if ( useClause != null )
        {
            clauses.add( 0, useClause );
        }
        return astFactory.newSingleQuery( pos( start ), clauses );
    }
}

STATEMENT ShowAllCommand( Token start, USE_CLAUSE useClause ) throws Exception:
{
    STATEMENT statement = null;
    List clauses = null;
}
{
    (
        ( | ) statement=ShowRoles( start, useClause, true )
        | clauses=ShowIndexesAllowBrief( start, ShowCommandFilterTypes.ALL )
        | clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.ALL )
        | clauses=ShowFunctions( start, ShowCommandFilterTypes.ALL )
        | statement=ShowPrivileges( start, useClause )
    )
    {
        if ( clauses != null ) {
            if ( useClause != null )
            {
                clauses.add( 0, useClause );
            }
            statement = astFactory.newSingleQuery( pos( start ), clauses );
        }
        return statement;
    }
}

List ShowNodeCommand( Token start ) throws Exception:
{
    List clauses = null;
}
{
    // Needs to add  to the type
    (
        ( | ) clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.NODE_UNIQUE )
        |  clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.NODE_KEY )
        |  clauses=ShowPropertyCommand( start, ShowCommandFilterTypes.NODE_EXIST )
        |  clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.NODE_EXIST )
        |  ShowConstraintsAllowBrief( start )
        |  clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.NODE_OLD_EXIST )
    )
    {
        return clauses;
    }
}

List ShowRelationshipCommand( Token start ) throws Exception:
{
    List clauses = null;
}
{
    (
        ( | ) clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_UNIQUE )
        |  clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_KEY )
        |  clauses=ShowPropertyCommand( start, ShowCommandFilterTypes.RELATIONSHIP_EXIST )
        |  clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_EXIST )
        |  ShowConstraintsAllowBrief( start )
        |  clauses=ShowConstraintsAllowBriefAndYield( start, ShowCommandFilterTypes.RELATIONSHIP_OLD_EXIST )
    )
    {
        return clauses;
    }
}

List ShowRelCommand( Token start ) throws Exception:
{
    List clauses = null;
}
{
    (
        ( | ) clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_UNIQUE )
        |  clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_KEY )
        |  clauses=ShowPropertyCommand( start, ShowCommandFilterTypes.RELATIONSHIP_EXIST )
        |  clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_EXIST )
        |  clauses=ShowConstraintsAllowYield( start, ShowCommandFilterTypes.RELATIONSHIP_EXIST )
    )
    {
        return clauses;
    }
}

List ShowPropertyCommand( Token start, ShowCommandFilterTypes existenceConstraintType ) throws Exception:
{
    List clauses = null;
    ShowCommandFilterTypes propertyTypeConstraintType;
    if (existenceConstraintType.description().startsWith("NODE")) {
        propertyTypeConstraintType = ShowCommandFilterTypes.NODE_PROP_TYPE;
    } else if (existenceConstraintType.description().startsWith("REL")) {
        propertyTypeConstraintType = ShowCommandFilterTypes.RELATIONSHIP_PROP_TYPE;
    } else {
        propertyTypeConstraintType = ShowCommandFilterTypes.PROP_TYPE;
    }
    ShowCommandFilterTypes constraintType;
}
{
    ( ( | ) { constraintType = existenceConstraintType; }
      |  { constraintType = propertyTypeConstraintType; }
    ) clauses=ShowConstraintsAllowYield( start, constraintType )
     { return clauses; }
}

RETURN_ITEM YieldItem() :
{
    VARIABLE e;
    VARIABLE v = null;
    Token eStart;
    Token eEnd;
}
{
    { eStart = token; }
    e=Variable() { eEnd = token; } (  v=Variable() )?
    {
        if ( v != null )
        {
            return astFactory.newReturnItem( pos( eStart.next ), e, v );
        }
        else
        {
            return astFactory.newReturnItem( pos( eStart.next ), e, eStart.next.beginOffset, eEnd.endOffset );
        }
    }
}

YIELD YieldClause() throws Exception:
{
    Token start;
    Token skipPosition = null;
    Token limitPosition = null;
    Token orderToken = null;
    boolean returnAll = false;
    RETURN_ITEM item;
    ORDER_ITEM o;
    List returnItems = new ArrayList<>();
    List orders = new ArrayList<>();
    EXPRESSION skip = null;
    EXPRESSION limit = null;
    WHERE where = null;
}
{
    start=
    (  { returnAll = true; }  |
    ( item=YieldItem() { returnItems.add( item ); } ( item=YieldItem() { returnItems.add( item ); } )*))
    ( orderToken=  o=OrderItem() { orders.add( o ); } (  o=OrderItem() {orders.add( o ); } )*)?
    ( ( skipPosition= skip=SignedIntegerLiteral() ) | ( skipPosition= skip=SignedIntegerLiteral() ) )?
    ( limitPosition= limit=SignedIntegerLiteral() )?
    ( where=WhereClause() ) ?
    {
        return astFactory.yieldClause( pos( start ), returnAll, returnItems, pos( start.next ), orders, pos( orderToken ), skip, pos( skipPosition ), limit, pos( limitPosition ), where );
    }
}

List ShowIndexesAllowBrief( Token start, ShowCommandFilterTypes indexType ) throws Exception:
{
    // all and btree indexes
    List clauses = new ArrayList<>();
    boolean brief = false;
    boolean verbose = false;
    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;
    List tailClauses = null;
}
{
    (  |  )
    (
        ( (  { brief = true; } |  { verbose = true; } ) (  )? )
        | ( yieldClause=YieldClause() ( returnClause=ReturnClause() )? )
        | where=WhereClause()
    )?
    ( tailClauses=composableCommandClauses() )?
    {
        if (brief || verbose) {
            throw exceptionFactory.syntaxException(
                    new ParseException(ASTExceptionFactory.invalidBriefVerbose("SHOW INDEXES")),
                    start.beginOffset, start.beginLine, start.beginColumn );
        }

        if (indexType == ShowCommandFilterTypes.BTREE ) {
            throw exceptionFactory.syntaxException(
                    new ParseException(ASTExceptionFactory.invalidShowBtreeIndexes),
                    start.beginOffset, start.beginLine, start.beginColumn );
        }

        if ( yieldClause != null )
        {
            clauses.add( astFactory.showIndexClause( pos( start ), indexType, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showIndexClause( pos( start ), indexType, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List ShowIndexesNoBrief(Token start, ShowCommandFilterTypes indexType) throws Exception:
{
  // remaining indexes
  List clauses = new ArrayList<>();
  WHERE where = null;
  YIELD yieldClause = null;
  RETURN_CLAUSE returnClause = null;
  List tailClauses = null;
}
{
    (  |  )
    ( (yieldClause=YieldClause() ( returnClause=ReturnClause() )?) | where=WhereClause() )?
    ( tailClauses=composableCommandClauses() )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.showIndexClause( pos( start ), indexType, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showIndexClause( pos( start ), indexType, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List ShowConstraintsAllowBriefAndYield( Token start, ShowCommandFilterTypes constraintType ) throws Exception:
{
    // constraints that previously allowed brief/verbose
    List clauses = new ArrayList<>();
    boolean brief = false;
    boolean verbose = false;
    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;
    List tailClauses = null;
}
{
    (  |  )
    (
        ( (  { brief = true; } |  { verbose = true; }) ()? )
        | ( yieldClause=YieldClause() ( returnClause=ReturnClause() )?)
        | where=WhereClause()
    )?
    ( tailClauses=composableCommandClauses() )?
    {
        if (brief || verbose) {
            throw exceptionFactory.syntaxException(
                    new ParseException(ASTExceptionFactory.invalidBriefVerbose("SHOW CONSTRAINTS")),
                    start.beginOffset, start.beginLine, start.beginColumn );
        }

        if ( yieldClause != null )
        {
            clauses.add( astFactory.showConstraintClause( pos( start ), constraintType, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showConstraintClause( pos( start ), constraintType, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

void ShowConstraintsAllowBrief( Token start ) throws Exception:
{
    // old removed existence constraints, parsing kept for nice error
}
{
    (  |  )
    ( (  | ) ()? )?
    ( composableCommandClauses() )?
    {
        throw exceptionFactory.syntaxException(
                new ParseException(ASTExceptionFactory.invalidExistsForShowConstraints),
                start.beginOffset, start.beginLine, start.beginColumn );
    }
}

List ShowConstraintsAllowYield( Token start, ShowCommandFilterTypes constraintType ) throws Exception:
{
    // constraints only allowing yield and not brief/verbose
    // this is the method that will be used for all constraints in 6.0
    List clauses = new ArrayList<>();
    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;
    List tailClauses = null;
}
{
    (  |  )
    (
        ( yieldClause=YieldClause() ( returnClause=ReturnClause() ) ? )
        | where=WhereClause()
    )?
    ( tailClauses=composableCommandClauses() )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.showConstraintClause( pos( start ), constraintType, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showConstraintClause( pos( start ), constraintType, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List ShowProcedures( Token start ) throws Exception:
{
    List clauses = new ArrayList<>();

    boolean currentUser = false;
    Token userToken = null;
    String user = null;

    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;

    List tailClauses = null;
}
{
    (  |  )
    (
         { currentUser = true; } ( 
        ( LOOKAHEAD( )   { currentUser = true; }
        | userToken=SymbolicNameString() { user = userToken.image; currentUser = false; } ) )?
    )?
    (
        ( yieldClause=YieldClause() ( returnClause=ReturnClause() ) ? )
        | where=WhereClause()
    )?
    ( tailClauses=composableCommandClauses() )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.showProcedureClause( pos( start ), currentUser, user, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showProcedureClause( pos( start ), currentUser, user, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List ShowFunctions( Token start, ShowCommandFilterTypes functionType ) throws Exception:
{
    List clauses = new ArrayList<>();

    boolean currentUser = false;
    Token userToken = null;
    String user = null;

    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;

    List tailClauses = null;
}
{
    (  |  )
    (
         { currentUser = true; } ( 
        ( LOOKAHEAD( )   { currentUser = true; }
        | userToken=SymbolicNameString() { user = userToken.image; currentUser = false; } ) )?
    )?
    (
        ( yieldClause=YieldClause() ( returnClause=ReturnClause() ) ? )
        | where=WhereClause()
    )?
    ( tailClauses=composableCommandClauses() )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.showFunctionClause( pos( start ), functionType, currentUser, user, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showFunctionClause( pos( start ), functionType, currentUser, user, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List ShowTransactions( Token start ) throws Exception:
{
    List clauses = new LinkedList<>();
    SimpleEither, EXPRESSION> idEither = SimpleEither.left(new ArrayList<>());

    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;

    List tailClauses = null;
}
{
    (  |  )
    (
        LOOKAHEAD(composableCommandClauses()) tailClauses=composableCommandClauses()
        | ( LOOKAHEAD(stringsOrExpression() (WhereClause() | YieldClause()))
              idEither=stringsOrExpression() (
                  ( yieldClause=YieldClause() ( returnClause=ReturnClause() ) ? )
                  | where=WhereClause()
              )
          | LOOKAHEAD(YieldClause()) yieldClause=YieldClause() ( returnClause=ReturnClause() ) ?
          | LOOKAHEAD(WhereClause()) where=WhereClause()
          | idEither=stringsOrExpression()
        ) ( tailClauses=composableCommandClauses() )?
    )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.showTransactionsClause( pos( start ), idEither,  where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showTransactionsClause( pos( start ), idEither, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List TerminateTransactions( Token start ) throws Exception:
{
    List clauses = new LinkedList<>();
    SimpleEither, EXPRESSION> idEither = SimpleEither.left(new ArrayList<>());

    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;

    List tailClauses = null;
}
{
    (  |  )
    (
        LOOKAHEAD(composableCommandClauses()) tailClauses=composableCommandClauses()
        | ( LOOKAHEAD(stringsOrExpression() (WhereClause() | YieldClause()))
              idEither=stringsOrExpression() (
                  ( yieldClause=YieldClause() ( returnClause=ReturnClause() ) ? )
                  | where=WhereClause()
              )
          | LOOKAHEAD(YieldClause()) yieldClause=YieldClause() ( returnClause=ReturnClause() ) ?
          | LOOKAHEAD(WhereClause()) where=WhereClause()
          | idEither=stringsOrExpression()
        ) ( tailClauses=composableCommandClauses() )?
    )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.terminateTransactionsClause( pos( start ), idEither, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.terminateTransactionsClause( pos( start ), idEither, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List ShowSettings( Token start ) throws Exception:
{
    List clauses = new ArrayList<>();
    SimpleEither, EXPRESSION> nameEither = SimpleEither.left(new ArrayList<>());

    WHERE where = null;
    YIELD yieldClause = null;
    RETURN_CLAUSE returnClause = null;

    List tailClauses = null;
}
{
    (  |  )
    (
        LOOKAHEAD(composableCommandClauses()) tailClauses=composableCommandClauses()
        | ( LOOKAHEAD(stringsOrExpression() (WhereClause() | YieldClause()))
              nameEither=stringsOrExpression() (
                  ( yieldClause=YieldClause() ( returnClause=ReturnClause() ) ? )
                  | where=WhereClause()
              )
          | LOOKAHEAD(YieldClause()) yieldClause=YieldClause() ( returnClause=ReturnClause() ) ?
          | LOOKAHEAD(WhereClause()) where=WhereClause()
          | nameEither=stringsOrExpression()
        ) ( tailClauses=composableCommandClauses() )?
    )?
    {
        if ( yieldClause != null )
        {
            clauses.add( astFactory.showSettingsClause( pos( start ), nameEither, where, yieldClause ) );
            clauses.add( astFactory.turnYieldToWith( yieldClause ) );
            if ( returnClause != null )
            {
                clauses.add( returnClause );
            }
        }
        else
        {
            clauses.add( astFactory.showSettingsClause( pos( start ), nameEither, where, null ) );
        }
        if ( tailClauses != null ) {
            clauses.addAll( tailClauses );
        }
        return clauses;
    }
}

List composableCommandClauses() throws Exception:
{
    Token start = null;
    List clauses = null;
}
{
    (
        (start= clauses=TerminateTransactions(start))
        | start= (
            // order: first the read tokens than lookaheads,
            // each group is alphabetical but longer words first
             (
                LOOKAHEAD(  |  ) clauses=ShowConstraintsAllowBriefAndYield(start, ShowCommandFilterTypes.ALL)
                | LOOKAHEAD(  |  ) clauses=ShowIndexesAllowBrief(start, ShowCommandFilterTypes.ALL)
                | clauses=ShowFunctions(start, ShowCommandFilterTypes.ALL)
            )
            |  clauses=ShowIndexesAllowBrief(start, ShowCommandFilterTypes.BTREE)
            |   clauses=ShowFunctions(start, ShowCommandFilterTypes.BUILT_IN)
            |  clauses=ShowConstraintsAllowYield(start, ShowCommandFilterTypes.EXIST)
            |  ShowConstraintsAllowBrief(start)
            |  clauses=ShowConstraintsAllowBriefAndYield(start, ShowCommandFilterTypes.OLD_EXIST)
            |  clauses=ShowIndexesNoBrief(start, ShowCommandFilterTypes.FULLTEXT)
            |  clauses=ShowConstraintsAllowYield(start, ShowCommandFilterTypes.KEY)
            |  clauses=ShowIndexesNoBrief(start, ShowCommandFilterTypes.LOOKUP)
            |  clauses=ShowNodeCommand( start )
            |  clauses=ShowIndexesNoBrief(start, ShowCommandFilterTypes.POINT)
            |  clauses=ShowPropertyCommand( start, ShowCommandFilterTypes.EXIST )
            |  clauses=ShowIndexesNoBrief(start, ShowCommandFilterTypes.RANGE)
            |  clauses=ShowRelationshipCommand( start )
            |  clauses=ShowRelCommand( start )
            |  clauses=ShowIndexesNoBrief(start, ShowCommandFilterTypes.TEXT)
            |  clauses=ShowConstraintsAllowYield(start, ShowCommandFilterTypes.UNIQUE)
            |  clauses=ShowConstraintsAllowBriefAndYield(start, ShowCommandFilterTypes.UNIQUE)
            |   clauses=ShowFunctions(start, ShowCommandFilterTypes.USER_DEFINED)
            |  clauses=ShowIndexesNoBrief(start, ShowCommandFilterTypes.VECTOR)
            | LOOKAHEAD(  |  ) clauses=ShowConstraintsAllowBriefAndYield(start, ShowCommandFilterTypes.ALL)
            | LOOKAHEAD(  |  ) clauses=ShowFunctions(start, ShowCommandFilterTypes.ALL)
            | LOOKAHEAD(  |  ) clauses=ShowIndexesAllowBrief(start, ShowCommandFilterTypes.ALL)
            | LOOKAHEAD(  |  ) clauses=ShowProcedures(start)
            | LOOKAHEAD(  |  )  clauses=ShowSettings(start)
            | clauses=ShowTransactions(start)
        )
    )
    {
        return clauses;
    }
}

SimpleEither, EXPRESSION> stringsOrExpression() throws Exception:
{
    List ids = new ArrayList<>();
    EXPRESSION expr = null;
    SimpleEither, EXPRESSION> idEither = SimpleEither.left(ids);
}
{
    ( LOOKAHEAD(StringImage() ) (ids=StringList() { idEither=SimpleEither.left(ids); })
    | (expr=Expression() { idEither=SimpleEither.right(expr); }) )
    {
        return idEither;
    }
}

// Schema commands
// Constraint commands

SCHEMA_COMMAND CreateConstraint( Token start, boolean replace ) throws Exception:
{
    SimpleEither, PARAMETER> name = null;
    boolean ifNotExists = false;
    Token label;
    VARIABLE variable = null;
    List properties = new ArrayList<>();
    ParserCypherTypeName propertyType = null;
    SimpleEither, PARAMETER> options = null;
    ConstraintType constraintType = ConstraintType.NODE_EXISTS;
    boolean isNode = false;
    Token errorStart;
    boolean containsOn = true;
    ConstraintVersion constraintVersion = ConstraintVersion.CONSTRAINT_VERSION_0;
}
{
    
    (
        //special construct for allowing lookahead since on could also be an identifier
        LOOKAHEAD ( )  
        | LOOKAHEAD ( )   { containsOn = false; }
        | LOOKAHEAD ( )    { ifNotExists = true; } ( |  { containsOn = false; }) 
        | ( LOOKAHEAD(2) name=SymbolicNameOrStringParameter() )? (    { ifNotExists = true; } )? ( |  { containsOn = false; }) 
    )
    (
        //(variable:LABEL)
        variable=Variable() label=LabelOrRelType()  { isNode = true; }
    |   //()-[variable:LABEL]-()
         ( LeftArrow() )? ArrowLine()
         variable=Variable() label=LabelOrRelType() 
        ArrowLine() ( RightArrow() )?  
    )
    (
         // CREATE CONSTRAINT ON (variable:LABEL) ASSERT EXISTS variable.prop
        LOOKAHEAD( )
         { constraintVersion = ConstraintVersion.CONSTRAINT_VERSION_0; }
        errorStart= { constraintType = isNode ? ConstraintType.NODE_EXISTS : ConstraintType.REL_EXISTS; }
        properties=PropertyList()
    |
        (
             { constraintVersion = ConstraintVersion.CONSTRAINT_VERSION_2; }
            |  { constraintVersion = ConstraintVersion.CONSTRAINT_VERSION_0; }
        )
        properties=PropertyList()
        (
             propertyType = cypherTypeName() {
                // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop :: 
                // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop :: 
                constraintType = isNode ? ConstraintType.NODE_IS_TYPED : ConstraintType.REL_IS_TYPED;
            }
            | errorStart=
            (
                 {
                        // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS UNIQUE
                        // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS UNIQUE
                        constraintType = isNode ? ConstraintType.NODE_UNIQUE : ConstraintType.REL_UNIQUE;
                     }
                |  {
                        // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS KEY
                        // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS KEY
                        constraintType = isNode ? ConstraintType.NODE_KEY : ConstraintType.REL_KEY;
                     }
                | 
                    (
                    {
                        // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS NODE KEY
                        constraintType = ConstraintType.NODE_KEY;
                        if ( !isNode )
                        {
                            throw exceptionFactory.syntaxException(
                                new ParseException( ASTExceptionFactory.relationshipPatternNotAllowed( constraintType ) ),
                                errorStart.beginOffset, errorStart.beginLine, errorStart.beginColumn );
                        }
                    }
                    | 
                    {
                        // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS NODE UNIQUE
                        constraintType = ConstraintType.NODE_UNIQUE;
                        if ( !isNode )
                        {
                            throw exceptionFactory.syntaxException(
                                new ParseException( ASTExceptionFactory.relationshipPatternNotAllowed( constraintType ) ),
                                errorStart.beginOffset, errorStart.beginLine, errorStart.beginColumn );
                        }
                    })
                | ( | )
                    (
                    {
                        // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS RELATIONSHIP KEY
                        constraintType = ConstraintType.REL_KEY;
                        if ( isNode )
                        {
                            throw exceptionFactory.syntaxException(
                                new ParseException( ASTExceptionFactory.nodePatternNotAllowed( constraintType ) ),
                                errorStart.beginOffset, errorStart.beginLine, errorStart.beginColumn );
                        }
                    }
                    | 
                    {
                        // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS RELATIONSHIP UNIQUE
                        constraintType = ConstraintType.REL_UNIQUE;
                        if ( isNode )
                        {
                            throw exceptionFactory.syntaxException(
                                new ParseException( ASTExceptionFactory.nodePatternNotAllowed( constraintType ) ),
                                errorStart.beginOffset, errorStart.beginLine, errorStart.beginColumn );
                        }
                    })
                |   {
                    // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS NOT NULL
                    // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS NOT NULL
                    constraintType = isNode ? ConstraintType.NODE_IS_NOT_NULL : ConstraintType.REL_IS_NOT_NULL;
                    if (constraintVersion == ConstraintVersion.CONSTRAINT_VERSION_0)
                    {
                        constraintVersion = ConstraintVersion.CONSTRAINT_VERSION_1;
                    }
                }
                | ( | ) propertyType = cypherTypeName() {
                    // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS :: 
                    // CREATE CONSTRAINT FOR (variable:LABEL) REQUIRE variable.prop IS TYPED 
                    // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS :: 
                    // CREATE CONSTRAINT FOR ()-[variable:LABEL]-() REQUIRE variable.prop IS TYPED 
                    constraintType = isNode ? ConstraintType.NODE_IS_TYPED : ConstraintType.REL_IS_TYPED;
                }
            )
        )
    )
    (  options=MapOrParameter() )?
    {
        String error = ASTExceptionFactory.checkForInvalidCreateConstraint(constraintType, constraintVersion, containsOn, properties.size() > 1);
        if (error != null) {
            throw exceptionFactory.syntaxException(
                new ParseException( error ),
                start.beginOffset, start.beginLine, start.beginColumn );
        } else {
            return astFactory.createConstraint( pos( start ), constraintType, replace, ifNotExists, name, variable,
            new StringPos( label.image, pos( label ) ), properties, propertyType, options );
        }
    }
}

ParserCypherTypeName cypherTypeName() throws Exception:
{
    Token t = null;
    List list = new ArrayList<>();
}
{
    { t = token; }
    list = cypherTypeNameList()
    {
        if ( list.size() ==  1)
        {
            return list.get(0);
        }
        else
        {
             t = t.next;
            return ParserCypherTypeName.closedDynamicUnionOf(list).withPos(t.beginOffset, t.beginLine, t.beginColumn);
        }
    }
}

List cypherTypeNameList() throws Exception:
{
    ParserCypherTypeName typeName;
    List list = new ArrayList<>();
}
{
    typeName=cypherTypeNamePart() { list.add( typeName ); }
    ( LOOKAHEAD(2)  typeName=cypherTypeNamePart() { list.add( typeName ); } )*
    {
        return list;
    }
}

ParserCypherTypeName cypherTypeNamePart() throws Exception:
{
    Token t = null;
    ParserCypherTypeName typeName = null;
    ParserCypherTypeName innerType = null;
    List unionTypeList = null;
}
{
    { t = token; }
    try {
        (
             { typeName = ParserCypherTypeName.NOTHING; }
            |  { typeName = ParserCypherTypeName.NULL; }
            | ( | ) { typeName = ParserCypherTypeName.BOOLEAN; }
            | ( | ) { typeName = ParserCypherTypeName.STRING; }
            | ( | ()? ) { typeName = ParserCypherTypeName.INTEGER; }
            |  { typeName = ParserCypherTypeName.FLOAT; }
            |  { typeName = ParserCypherTypeName.DATE; }
            |  (