/* An EBNF grammar for EBNF */
[1] ebnf ::= (declaration | rule)*
[2] declaration ::= '@terminals' | pass
# Use the LHS terminal to match the identifier, rule name and assignment due to
# confusion between the identifier and RANGE.
# Note, for grammars not using identifiers, it is still possible to confuse
# a rule ending with a range the next rule, as it may be interpreted as an identifier.
# In such case, best to enclose the rule in '()'.
[3] rule ::= LHS expression
[4] expression ::= alt
[5] alt ::= seq ('|' seq)*
[6] seq ::= diff+
[7] diff ::= postfix ('-' postfix)?
[8] postfix ::= primary POSTFIX?
[9] primary ::= HEX
| SYMBOL
| O_RANGE
| RANGE
| STRING1
| STRING2
| '(' expression ')'
[10] pass ::= '@pass' expression
@terminals
[11] LHS ::= ('[' SYMBOL ']' ' '+)? SYMBOL ' '* '::='
[12] SYMBOL ::= ([a-z] | [A-Z] | [0-9] | '_' | '.')+
[13] HEX ::= '#x' ([a-f] | [A-F] | [0-9])+
[14] RANGE ::= '[' ((R_CHAR '-' R_CHAR) | (HEX '-' HEX) | R_CHAR | HEX)+ '-'? ']' - LHS
[15] O_RANGE ::= '[^' ((R_CHAR '-' R_CHAR) | (HEX '-' HEX) | R_CHAR | HEX)+ '-'? ']'
# Strings are unescaped Unicode, excepting control characters and hash (#)
[16] STRING1 ::= '"' (CHAR - '"')* '"'
[17] STRING2 ::= "'" (CHAR - "'")* "'"
[18] CHAR ::= [#x9#xA#xD] | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
[19] R_CHAR ::= CHAR - (']' | '-' | HEX)
[20] POSTFIX ::= [?*+]
# Ignore all whitespace and comments between non-terminals
[21] PASS ::= [#x9#xA#xD#x20]
| ( ('#' - '#x') | '//' ) [^#xA#xD]*
| '/*' (( '*' [^/] )? | [^*] )* '*/'
| '(*' (( '*' [^)] )? | [^*] )* '*)'
@pass PASS