clike.ebnf (2337B)
1 // C-like kitchen sink: declarations, functions, statements, and a Pratt 2 // expression grammar with the usual C precedence ladder. 3 // 4 // Two natural forms had to be left-factored by hand to satisfy LL(1); both are 5 // preserved untouched as red fixtures under test/realistic/red/ so the friction 6 // stays visible: 7 // * dangling-else -> red/dangling_else.ebnf (LL(1) optional conflict) 8 // * IDENT vs call -> red/ident_call.ebnf (FIRST/FIRST conflict) 9 // Here `if` requires an `else` (sidesteps dangling-else) and a name/call is 10 // left-factored through `call_tail`. 11 // 12 // Keywords go through a %keywords block (host = IDENT). Declaring them with bare 13 // %token *before* IDENT also works; writing the %lex block first and the keyword 14 // %tokens after is now a hard error (the keyword would be shadowed by IDENT) — 15 // see red/keyword_shadow.ebnf. 16 %lex { 17 %skip WS = [ \t\r\n]+; 18 %skip LINE = "//" [^\n]*; 19 %skip BLOCK = "/*" ( [^*] | "*" [^/] )* "*/"; 20 21 IDENT = [A-Za-z_] [A-Za-z0-9_]*; 22 INT = "0" [xX] [0-9A-Fa-f]+ | [0-9]+; 23 FLOAT = [0-9]+ "." [0-9]* ( [eE] [+\-]? [0-9]+ )?; 24 CHAR = "'" ( [^'\\] | "\\" . ) "'"; 25 STRING = "\"" ( [^"\\] | "\\" . )* "\""; 26 27 %keywords IDENT { 28 IF="if"; ELSE="else"; WHILE="while"; RETURN="return"; 29 INT_T="int"; VOID="void"; 30 } 31 } 32 33 program = decl*; 34 decl = type IDENT decl_tail; 35 decl_tail = "(" params? ")" block // function definition 36 | ";" // global declaration 37 | "=" expr ";"; // global initializer 38 type = "int" | "void"; 39 params = param param_tail*; 40 param_tail = "," param; 41 param = type IDENT; 42 43 block = "{" stmt* "}"; 44 stmt = block 45 | "if" "(" expr ")" stmt else_part 46 | "while" "(" expr ")" stmt 47 | "return" expr? ";" 48 | type IDENT var_init? ";" 49 | expr ";"; 50 else_part = "else" stmt; 51 var_init = "=" expr; 52 53 %pratt expr { 54 primary primary; 55 infixr "="; // assignment: loosest, right-associative 56 infixl "||"; 57 infixl "&&"; 58 infixl "==" "!="; 59 infixl "<" ">" "<=" ">="; 60 infixl "+" "-"; 61 infixl "*" "/" "%"; 62 prefix "-" "!"; 63 } 64 primary = INT | FLOAT | STRING | CHAR | "(" expr ")" | IDENT call_tail; 65 call_tail = "(" args? ")" | %empty; 66 args = expr arg_tail*; 67 arg_tail = "," expr;