meta.ebnf (6343B)
1 // gramgen EBNF source language. 2 // 3 // A file is a stream of declarations and rules. Whitespace and line comments 4 // `// ...` are ignored by the gramgen lexer. Block comments are not supported. 5 // Terminals are token kinds. They may be produced by a generated lexer or 6 // supplied externally by the embedder. Uppercase NAME values refer to named 7 // token kinds, and STRING literals refer to literal token kinds. Rule names 8 // must start with a lowercase letter. 9 10 // Literal declarations choose emitted token enum names. When a generated lexer 11 // is emitted, each literal also contributes an exact-match recognizer. 12 %token TOKEN_DECL = "%token"; 13 %token EMPTY_DECL = "%empty"; 14 %token PRATT_DECL = "%pratt"; 15 %token LEX_DECL = "%lex"; 16 %token SKIP_DECL = "%skip"; 17 %token DEF_DECL = "%def"; 18 %token KEYWORDS_DECL = "%keywords"; 19 %token COLON = ":"; 20 // `%machine NAME { ... }` declares a regular language over an abstract token 21 // alphabet (codegen-only verifier + sampler). 22 %token MACHINE_DECL = "%machine"; 23 24 %lex { 25 %skip WS = [ \t\r\n] [ \t\r\n]*; 26 %skip LINE_COMMENT = "//" [^\n]*; 27 28 NAME = [A-Za-z_] [A-Za-z0-9_]*; 29 STRING = "\"" ( "\\" [^\n] | [^"\\\n] )* "\""; 30 UNICODE_PROP = "\\" [pP] "{" [^}\n] [^}\n]* "}"; 31 CHAR_CLASS = "[" "^"? ( "\\" [^\n] | [^\]\\\n] ) ( "\\" [^\n] | [^\]\\\n] )* "]"; 32 // Tier-1 edge anchors: \A/\z (text) and ^/$ (line). Recorded as 33 // per-recognizer flags during lowering; mid-pattern uses are rejected. 34 ANCHOR = "\\A" | "\\z" | "^" | "$"; 35 // ASCII shorthand classes \d \w \s and their complements \D \W \S, usable 36 // standalone and inside `[...]`. Expanded to character classes during lowering. 37 SHORTHAND = "\\" [dDwWsS]; 38 // Counted-repetition suffix `{n}` `{n,}` `{n,m}` `{,m}`; the bounds are 39 // hand-parsed and validated (cap, ordering) during lowering. 40 REPEAT = "{" [0-9,] [0-9,]* "}"; 41 } 42 43 grammar = item*; 44 item = decl | rule | pratt_rule | lex_block | machine_block; 45 46 decl = token_decl; 47 token_decl = "%token" NAME "=" STRING ";"; 48 49 rule = NAME "=" alt ";"; 50 51 // Rules are LL(1): alternatives are separated by `|`, and each alternative is 52 // a sequence of factors. `%empty` is the explicit empty alternative. 53 alt = seq alt_tail*; 54 alt_tail = "|" seq; 55 seq = "%empty" 56 | factor*; 57 58 // A factor is a primary item plus optional quantifier sugar. `?` is 59 // zero-or-one and `*` is zero-or-more. 60 factor = primary quant?; 61 primary = NAME 62 | STRING 63 | "(" alt ")"; 64 65 quant = "?" | "*"; 66 67 // Pratt declarations are optional expression parsers. `%pratt NAME { ... }` 68 // declares a named parser rule whose body is an operator table instead of LL(1) 69 // alternatives. A `primary RULE;` line names the LL(1) leaf parser, and each 70 // operator line names a role followed by one or more token names or literals. 71 pratt_rule = "%pratt" NAME "{" pratt_line* "}"; 72 pratt_line = NAME pratt_atom pratt_atom* ";"; 73 pratt_atom = NAME | STRING; 74 75 // Lexer declarations are optional. `%lex { ... }` groups generated DFA 76 // recognizers in one block. `TOKEN = pattern;` binds an uppercase token. 77 // Tokens without a rule may still be supplied externally and pushed directly to 78 // the parser. `%skip NAME = pattern;` recognizes input that is consumed but not 79 // emitted to the parser. 80 // 81 // `%lex :utf8 { ... }` keeps generated DFA tables byte-oriented, but marks the 82 // runtime input model as UTF-8 for scalar-position tracking and encoding 83 // diagnostics. 84 // 85 // Patterns are regex-like but not slash-delimited. They support alternation, 86 // concatenation, grouping, string literals, character classes, and `.` as a 87 // wildcard. Postfix quantifiers are `?`, `*`, `+`, and counted `{n}` `{n,}` 88 // `{n,m}` `{,m}` (capped; lazy forms like `*?` are an error). The ASCII 89 // shorthands `\d \w \s` and complements `\D \W \S` work standalone and inside 90 // `[...]`, in both byte and utf8 mode. `%def name = pattern;` declares an 91 // inlined, acyclic fragment (lowercase name) scoped to its `%lex`/`%machine` 92 // block and referenced by name in other patterns in the same block; fragments 93 // are never tokens. All of these lower to the primitive pattern forms during a 94 // desugaring pass before NFA construction. 95 // 96 // Character classes are lexical primitives, not parsed by this grammar: 97 // `[A-Z_]`, `[^"\n]`, or `[A-Za-z0-9_]`. `^` negates only immediately after 98 // `[`. In byte mode ranges are written as `a-z`; a literal `-`, `]`, `^`, or 99 // `\` must be escaped inside the class. In UTF-8 mode the generator parses 100 // Unicode scalar escapes, `\p{...}`/`\P{...}`, and class algebra inside the 101 // bracketed primitive. Empty classes are invalid. 102 lex_block = "%lex" lex_name lex_mode "{" lex_line* "}"; 103 // `%machine NAME { ... }` is a token-alphabet lexer block: the same lex_line 104 // productions, compiled over an abstract symbol alphabet (UPPERCASE leaves are 105 // symbols, `[ A B ]` is a symbol set). Codegen-only verifier + sampler. 106 machine_block = "%machine" NAME "{" lex_line* "}"; 107 lex_name = NAME | %empty; 108 lex_mode = ":" NAME | %empty; 109 lex_line = lex_rule | skip_rule | def_rule | keywords_block; 110 lex_rule = NAME "=" lex_alt ";"; 111 skip_rule = "%skip" NAME "=" lex_alt ";"; 112 // `%def name = pattern;` declares an inlined pattern fragment (lowercase name). 113 // Fragments are scoped to their `%lex`/`%machine` block, acyclic, and never 114 // become tokens (a reference to another block's %def is an "unknown fragment"). 115 // In a %machine a %def whose body is a set (`[ A B ]`, a union `a | b`, or a 116 // class with set algebra) doubles as a named symbol set referenced inside 117 // `[ … ]`. 118 def_rule = "%def" NAME "=" lex_alt ";"; 119 // `%keywords HOST { ... }` lists string-literal tokens that the more general 120 // recognizer HOST shadows; they are kept out of the DFA and recovered by a 121 // lexeme lookup. Each entry names a token, a literal string, or both. 122 keywords_block = "%keywords" NAME "{" kw_entry* "}"; 123 kw_entry = NAME kw_value? ";" | STRING ";"; 124 kw_value = "=" STRING; 125 lex_alt = lex_seq lex_alt_tail*; 126 lex_alt_tail = "|" lex_seq; 127 lex_seq = lex_factor lex_factor*; 128 // Lexer quantifiers extend the parser's `? *` with `+` and counted `{...}`. 129 lex_factor = lex_primary lex_quant?; 130 lex_quant = "?" | "*" | "+" | REPEAT; 131 lex_primary = STRING 132 | CHAR_CLASS 133 | UNICODE_PROP 134 | SHORTHAND 135 | "." 136 | NAME 137 | "(" lex_alt ")" 138 | ANCHOR;