/* An EBNF grammar for EBNF */
[1] ebnf        ::= (declaration | rule)*

[2] declaration ::= '@terminals' | pass

[3] rule        ::= LHS expression

# Use a terminal to match the identifier, rule name and assignment due to
# confusion between the identifier RANGE

[4] expression  ::= alt

[5] alt         ::= seq ('|' seq)*

[6] seq         ::= diff+

[7] diff        ::= postfix ('-' postfix)?

[8] postfix     ::= primary POSTFIX?

[9] primary     ::= HEX
                |   SYMBOL
                |   ENUM
                |   O_ENUM
                |   RANGE
                |   O_RANGE
                |   STRING1
                |   STRING2
                |   '(' expression ')'

[10] pass       ::= '@pass' expression

@terminals

[11] LHS        ::= ('[' SYMBOL+ ']')? SYMBOL "::="

[12] SYMBOL     ::= ([a-z] | [A-Z] | [0-9] | "_" | ".")+

[13] HEX        ::= '#x' ([0-9]|[a-f]|[A-F])+

[14] ENUM       ::= '[' ((R_BEGIN (HEX | R_CHAR)) | (HEX | R_CHAR)) '-' ((R_BEGIN (HEX | R_CHAR)) | (HEX | R_CHAR)) ']'

[15] O_ENUM     ::= '[^' ((R_BEGIN (HEX | R_CHAR)) | (HEX | R_CHAR)) '-' ((R_BEGIN (HEX | R_CHAR)) | (HEX | R_CHAR)) ']'

# Range is any combination of R_CHAR '-' R_CHAR or R_CHAR+
[16] RANGE      ::= '[' ((R_BEGIN (HEX | R_CHAR)) | (HEX | R_CHAR))+ ']'

# Range is any combination of R_CHAR '-' R_CHAR or R_CHAR+ preceded by ^
[17] O_RANGE    ::= '[^' ((R_BEGIN (HEX | R_CHAR)) | (HEX | R_CHAR))+ ']'

# Strings are unescaped Unicode, excepting control characters and hash (#)
[18] STRING1    ::= '"' (CHAR - '"')* '"'

[19] STRING2    ::= "'" (CHAR - "'")* "'"

[20] CHAR       ::= HEX
                  | [#x20#x21#x22]
                  | [#x24-#x00FFFFFF]

[21] R_CHAR     ::= CHAR - ']'

[22] R_BEGIN    ::= (HEX | R_CHAR) "-"

# Should be able to do this inline, but not until terminal regular expressions are created automatically
[23] POSTFIX    ::= [?*+]

[24] PASS       ::= ( [#x00-#x20]
                    | ( '#' | '//' ) [^#x0A#x0D]*
                    | '/*' (( '*' [^/] )? | [^*] )* '*/'
                    | '(*' (( '*' [^)] )? | [^*] )* '*)'
                    )+

# Should be able to do this inline, but not until terminal regular expressions are created automatically
@pass           PASS