(*
 * Copyright (c) 2015-2021 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.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.
 * 
 * Attribution Notice under the terms of the Apache License 2.0
 * 
 * This work was created by the collective efforts of the openCypher community.
 * Without limiting the terms of Section 6, any Derivative Work that is not
 * approved by the public consensus process of the openCypher Implementers Group
 * should not be described as “Cypher” (and Cypher® is a registered trademark of
 * Neo4j Inc.) or as "openCypher". Extensions by implementers or prototypes or
 * proposals for change that have been documented or implemented should only be
 * described as "implementation extensions to Cypher" or as "proposed changes to
 * Cypher that are not yet approved by the openCypher community".
 *)
Cypher = [SP], Statement, [[SP], ';'], [SP], EOI ;

Statement = Query ;

Query = RegularQuery
      | StandaloneCall
      ;

RegularQuery = SingleQuery, { [SP], Union } ;

Union = ((U,N,I,O,N), SP, (A,L,L), [SP], SingleQuery)
      | ((U,N,I,O,N), [SP], SingleQuery)
      ;

SingleQuery = SinglePartQuery
            | MultiPartQuery
            ;

SinglePartQuery = ({ ReadingClause, [SP] }, Return)
                | ({ ReadingClause, [SP] }, UpdatingClause, { [SP], UpdatingClause }, [[SP], Return])
                ;

MultiPartQuery = { { ReadingClause, [SP] }, { UpdatingClause, [SP] }, With, [SP] }-, SinglePartQuery ;

UpdatingClause = Create
               | Merge
               | Delete
               | Set
               | Remove
               ;

ReadingClause = Match
              | Unwind
              | InQueryCall
              ;

Match = [(O,P,T,I,O,N,A,L), SP], (M,A,T,C,H), [SP], Pattern, [[SP], Where] ;

Unwind = (U,N,W,I,N,D), [SP], Expression, SP, (A,S), SP, Variable ;

Merge = (M,E,R,G,E), [SP], PatternPart, { SP, MergeAction } ;

MergeAction = ((O,N), SP, (M,A,T,C,H), SP, Set)
            | ((O,N), SP, (C,R,E,A,T,E), SP, Set)
            ;

Create = (C,R,E,A,T,E), [SP], Pattern ;

Set = (S,E,T), [SP], SetItem, { [SP], ',', [SP], SetItem } ;

SetItem = (PropertyExpression, [SP], '=', [SP], Expression)
        | (Variable, [SP], '=', [SP], Expression)
        | (Variable, [SP], '+=', [SP], Expression)
        | (Variable, [SP], NodeLabels)
        ;

Delete = [(D,E,T,A,C,H), SP], (D,E,L,E,T,E), [SP], Expression, { [SP], ',', [SP], Expression } ;

Remove = (R,E,M,O,V,E), SP, RemoveItem, { [SP], ',', [SP], RemoveItem } ;

RemoveItem = (Variable, NodeLabels)
           | PropertyExpression
           ;

InQueryCall = (C,A,L,L), SP, ExplicitProcedureInvocation, [[SP], (Y,I,E,L,D), SP, YieldItems] ;

StandaloneCall = (C,A,L,L), SP, (ExplicitProcedureInvocation | ImplicitProcedureInvocation), [[SP], (Y,I,E,L,D), SP, ('*' | YieldItems)] ;

YieldItems = YieldItem, { [SP], ',', [SP], YieldItem }, [[SP], Where] ;

YieldItem = [ProcedureResultField, SP, (A,S), SP], Variable ;

With = (W,I,T,H), ProjectionBody, [[SP], Where] ;

Return = (R,E,T,U,R,N), ProjectionBody ;

ProjectionBody = [[SP], (D,I,S,T,I,N,C,T)], SP, ProjectionItems, [SP, Order], [SP, Skip], [SP, Limit] ;

ProjectionItems = ('*', { [SP], ',', [SP], ProjectionItem })
                | (ProjectionItem, { [SP], ',', [SP], ProjectionItem })
                ;

ProjectionItem = (Expression, SP, (A,S), SP, Variable)
               | Expression
               ;

Order = (O,R,D,E,R), SP, (B,Y), SP, SortItem, { ',', [SP], SortItem } ;

Skip = (S,K,I,P), SP, Expression ;

Limit = (L,I,M,I,T), SP, Expression ;

SortItem = Expression, [[SP], ((A,S,C,E,N,D,I,N,G) | (A,S,C) | (D,E,S,C,E,N,D,I,N,G) | (D,E,S,C))] ;

Where = (W,H,E,R,E), SP, Expression ;

Pattern = PatternPart, { [SP], ',', [SP], PatternPart } ;

PatternPart = (Variable, [SP], '=', [SP], AnonymousPatternPart)
            | AnonymousPatternPart
            ;

AnonymousPatternPart = PatternElement ;

PatternElement = (NodePattern, { [SP], PatternElementChain })
               | ('(', PatternElement, ')')
               ;

NodePattern = '(', [SP], [Variable, [SP]], [NodeLabels, [SP]], [Properties, [SP]], ')' ;

PatternElementChain = RelationshipPattern, [SP], NodePattern ;

RelationshipPattern = (LeftArrowHead, [SP], Dash, [SP], [RelationshipDetail], [SP], Dash, [SP], RightArrowHead)
                    | (LeftArrowHead, [SP], Dash, [SP], [RelationshipDetail], [SP], Dash)
                    | (Dash, [SP], [RelationshipDetail], [SP], Dash, [SP], RightArrowHead)
                    | (Dash, [SP], [RelationshipDetail], [SP], Dash)
                    ;

RelationshipDetail = '[', [SP], [Variable, [SP]], [RelationshipTypes, [SP]], [RangeLiteral], [Properties, [SP]], ']' ;

Properties = MapLiteral
           | Parameter
           ;

RelationshipTypes = ':', [SP], RelTypeName, { [SP], '|', [':'], [SP], RelTypeName } ;

NodeLabels = NodeLabel, { [SP], NodeLabel } ;

NodeLabel = ':', [SP], LabelName ;

RangeLiteral = '*', [SP], [IntegerLiteral, [SP]], ['..', [SP], [IntegerLiteral, [SP]]] ;

LabelName = SchemaName ;

RelTypeName = SchemaName ;

Expression = OrExpression ;

OrExpression = XorExpression, { SP, (O,R), SP, XorExpression } ;

XorExpression = AndExpression, { SP, (X,O,R), SP, AndExpression } ;

AndExpression = NotExpression, { SP, (A,N,D), SP, NotExpression } ;

NotExpression = { (N,O,T), [SP] }, ComparisonExpression ;

ComparisonExpression = AddOrSubtractExpression, { [SP], PartialComparisonExpression } ;

AddOrSubtractExpression = MultiplyDivideModuloExpression, { ([SP], '+', [SP], MultiplyDivideModuloExpression) | ([SP], '-', [SP], MultiplyDivideModuloExpression) } ;

MultiplyDivideModuloExpression = PowerOfExpression, { ([SP], '*', [SP], PowerOfExpression) | ([SP], '/', [SP], PowerOfExpression) | ([SP], '%', [SP], PowerOfExpression) } ;

PowerOfExpression = UnaryAddOrSubtractExpression, { [SP], '^', [SP], UnaryAddOrSubtractExpression } ;

UnaryAddOrSubtractExpression = { ('+' | '-'), [SP] }, StringListNullOperatorExpression ;

StringListNullOperatorExpression = PropertyOrLabelsExpression, { StringOperatorExpression | ListOperatorExpression | NullOperatorExpression } ;

ListOperatorExpression = (SP, (I,N), [SP], PropertyOrLabelsExpression)
                       | ([SP], '[', Expression, ']')
                       | ([SP], '[', [Expression], '..', [Expression], ']')
                       ;

StringOperatorExpression = ((SP, (S,T,A,R,T,S), SP, (W,I,T,H)) | (SP, (E,N,D,S), SP, (W,I,T,H)) | (SP, (C,O,N,T,A,I,N,S))), [SP], PropertyOrLabelsExpression ;

NullOperatorExpression = (SP, (I,S), SP, (N,U,L,L))
                       | (SP, (I,S), SP, (N,O,T), SP, (N,U,L,L))
                       ;

PropertyOrLabelsExpression = Atom, { [SP], PropertyLookup }, [[SP], NodeLabels] ;

Atom = Literal
     | Parameter
     | CaseExpression
     | ((C,O,U,N,T), [SP], '(', [SP], '*', [SP], ')')
     | ListComprehension
     | PatternComprehension
     | ((A,L,L), [SP], '(', [SP], FilterExpression, [SP], ')')
     | ((A,N,Y), [SP], '(', [SP], FilterExpression, [SP], ')')
     | ((N,O,N,E), [SP], '(', [SP], FilterExpression, [SP], ')')
     | ((S,I,N,G,L,E), [SP], '(', [SP], FilterExpression, [SP], ')')
     | RelationshipsPattern
     | ParenthesizedExpression
     | FunctionInvocation
     | ExistentialSubquery
     | Variable
     ;

Literal = NumberLiteral
        | StringLiteral
        | BooleanLiteral
        | (N,U,L,L)
        | MapLiteral
        | ListLiteral
        ;

BooleanLiteral = (T,R,U,E)
               | (F,A,L,S,E)
               ;

ListLiteral = '[', [SP], [Expression, [SP], { ',', [SP], Expression, [SP] }], ']' ;

PartialComparisonExpression = ('=', [SP], AddOrSubtractExpression)
                            | ('<>', [SP], AddOrSubtractExpression)
                            | ('<', [SP], AddOrSubtractExpression)
                            | ('>', [SP], AddOrSubtractExpression)
                            | ('<=', [SP], AddOrSubtractExpression)
                            | ('>=', [SP], AddOrSubtractExpression)
                            ;

ParenthesizedExpression = '(', [SP], Expression, [SP], ')' ;

RelationshipsPattern = NodePattern, { [SP], PatternElementChain }- ;

FilterExpression = IdInColl, [[SP], Where] ;

IdInColl = Variable, SP, (I,N), SP, Expression ;

FunctionInvocation = FunctionName, [SP], '(', [SP], [(D,I,S,T,I,N,C,T), [SP]], [Expression, [SP], { ',', [SP], Expression, [SP] }], ')' ;

FunctionName = Namespace, SymbolicName ;

ExistentialSubquery = (E,X,I,S,T,S), [SP], '{', [SP], (RegularQuery | (Pattern, [[SP], Where])), [SP], '}' ;

ExplicitProcedureInvocation = ProcedureName, [SP], '(', [SP], [Expression, [SP], { ',', [SP], Expression, [SP] }], ')' ;

ImplicitProcedureInvocation = ProcedureName ;

ProcedureResultField = SymbolicName ;

ProcedureName = Namespace, SymbolicName ;

Namespace = { SymbolicName, '.' } ;

ListComprehension = '[', [SP], FilterExpression, [[SP], '|', [SP], Expression], [SP], ']' ;

PatternComprehension = '[', [SP], [Variable, [SP], '=', [SP]], RelationshipsPattern, [SP], [Where, [SP]], '|', [SP], Expression, [SP], ']' ;

PropertyLookup = '.', [SP], (PropertyKeyName) ;

CaseExpression = (((C,A,S,E), { [SP], CaseAlternative }-) | ((C,A,S,E), [SP], Expression, { [SP], CaseAlternative }-)), [[SP], (E,L,S,E), [SP], Expression], [SP], (E,N,D) ;

CaseAlternative = (W,H,E,N), [SP], Expression, [SP], (T,H,E,N), [SP], Expression ;

Variable = SymbolicName ;

StringLiteral = ('"', { ANY - ('"' | '\') | EscapedChar }, '"')
              | ("'", { ANY - ("'" | '\') | EscapedChar }, "'")
              ;

EscapedChar = '\', ('\' | "'" | '"' | (B) | (F) | (N) | (R) | (T) | ((U), 4 * HexDigit) | ((U), 8 * HexDigit)) ;

NumberLiteral = DoubleLiteral
              | IntegerLiteral
              ;

MapLiteral = '{', [SP], [PropertyKeyName, [SP], ':', [SP], Expression, [SP], { ',', [SP], PropertyKeyName, [SP], ':', [SP], Expression, [SP] }], '}' ;

Parameter = '$', (SymbolicName | DecimalInteger) ;

PropertyExpression = Atom, { [SP], PropertyLookup }- ;

PropertyKeyName = SchemaName ;

IntegerLiteral = HexInteger
               | OctalInteger
               | DecimalInteger
               ;

HexInteger = '0x', { HexDigit }- ;

DecimalInteger = ZeroDigit
               | (NonZeroDigit, { Digit })
               ;

OctalInteger = ZeroDigit, { OctDigit }- ;

HexLetter = (A)
          | (B)
          | (C)
          | (D)
          | (E)
          | (F)
          ;

HexDigit = Digit
         | HexLetter
         ;

Digit = ZeroDigit
      | NonZeroDigit
      ;

NonZeroDigit = NonZeroOctDigit
             | '8'
             | '9'
             ;

NonZeroOctDigit = '1'
                | '2'
                | '3'
                | '4'
                | '5'
                | '6'
                | '7'
                ;

OctDigit = ZeroDigit
         | NonZeroOctDigit
         ;

ZeroDigit = '0' ;

DoubleLiteral = ExponentDecimalReal
              | RegularDecimalReal
              ;

ExponentDecimalReal = ({ Digit }- | ({ Digit }-, '.', { Digit }-) | ('.', { Digit }-)), (E), ['-'], { Digit }- ;

RegularDecimalReal = { Digit }, '.', { Digit }- ;

SchemaName = SymbolicName
           | ReservedWord
           ;

ReservedWord = (A,L,L)
             | (A,S,C)
             | (A,S,C,E,N,D,I,N,G)
             | (B,Y)
             | (C,R,E,A,T,E)
             | (D,E,L,E,T,E)
             | (D,E,S,C)
             | (D,E,S,C,E,N,D,I,N,G)
             | (D,E,T,A,C,H)
             | (E,X,I,S,T,S)
             | (L,I,M,I,T)
             | (M,A,T,C,H)
             | (M,E,R,G,E)
             | (O,N)
             | (O,P,T,I,O,N,A,L)
             | (O,R,D,E,R)
             | (R,E,M,O,V,E)
             | (R,E,T,U,R,N)
             | (S,E,T)
             | (S,K,I,P)
             | (W,H,E,R,E)
             | (W,I,T,H)
             | (U,N,I,O,N)
             | (U,N,W,I,N,D)
             | (A,N,D)
             | (A,S)
             | (C,O,N,T,A,I,N,S)
             | (D,I,S,T,I,N,C,T)
             | (E,N,D,S)
             | (I,N)
             | (I,S)
             | (N,O,T)
             | (O,R)
             | (S,T,A,R,T,S)
             | (X,O,R)
             | (F,A,L,S,E)
             | (T,R,U,E)
             | (N,U,L,L)
             | (C,O,N,S,T,R,A,I,N,T)
             | (D,O)
             | (F,O,R)
             | (R,E,Q,U,I,R,E)
             | (U,N,I,Q,U,E)
             | (C,A,S,E)
             | (W,H,E,N)
             | (T,H,E,N)
             | (E,L,S,E)
             | (E,N,D)
             | (M,A,N,D,A,T,O,R,Y)
             | (S,C,A,L,A,R)
             | (O,F)
             | (A,D,D)
             | (D,R,O,P)
             ;

SymbolicName = UnescapedSymbolicName
             | EscapedSymbolicName
             | HexLetter
             | (C,O,U,N,T)
             | (F,I,L,T,E,R)
             | (E,X,T,R,A,C,T)
             | (A,N,Y)
             | (N,O,N,E)
             | (S,I,N,G,L,E)
             ;

UnescapedSymbolicName = IdentifierStart, { IdentifierPart } ;

(* Based on the unicode identifier and pattern syntax
 *   (http://www.unicode.org/reports/tr31/)
 * And extended with a few characters.
 *)IdentifierStart = ID_Start
                | Pc
                ;

(* Based on the unicode identifier and pattern syntax
 *   (http://www.unicode.org/reports/tr31/)
 * And extended with a few characters.
 *)IdentifierPart = ID_Continue
               | Sc
               ;

(* Any character except "`", enclosed within `backticks`. Backticks are escaped with double backticks.
 *)EscapedSymbolicName = { '`', { ANY - ('`') }, '`' }- ;

SP = { whitespace }- ;

whitespace = SPACE
           | TAB
           | LF
           | VT
           | FF
           | CR
           | FS
           | GS
           | RS
           | US
           | ' '
           | '᠎'
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | ' '
           | '　'
           | ' '
           | ' '
           | ' '
           | Comment
           ;

Comment = ('/*', { ANY - ('*') | ('*', ANY - ('/')) }, '*/')
        | ('//', { ANY - (LF | CR) }, [CR], (LF | EOI))
        ;

LeftArrowHead = '<'
              | '⟨'
              | '〈'
              | '﹤'
              | '＜'
              ;

RightArrowHead = '>'
               | '⟩'
               | '〉'
               | '﹥'
               | '＞'
               ;

Dash = '-'
     | '­'
     | '‐'
     | '‑'
     | '‒'
     | '–'
     | '—'
     | '―'
     | '−'
     | '﹘'
     | '﹣'
     | '－'
     ;

A = 'A' | 'a' ;

B = 'B' | 'b' ;

C = 'C' | 'c' ;

D = 'D' | 'd' ;

E = 'E' | 'e' ;

F = 'F' | 'f' ;

G = 'G' | 'g' ;

H = 'H' | 'h' ;

I = 'I' | 'i' ;

K = 'K' | 'k' ;

L = 'L' | 'l' ;

M = 'M' | 'm' ;

N = 'N' | 'n' ;

O = 'O' | 'o' ;

P = 'P' | 'p' ;

Q = 'Q' | 'q' ;

R = 'R' | 'r' ;

S = 'S' | 's' ;

T = 'T' | 't' ;

U = 'U' | 'u' ;

V = 'V' | 'v' ;

W = 'W' | 'w' ;

X = 'X' | 'x' ;

Y = 'Y' | 'y' ;

