desugar_test.c (3865B)
1 /* Runtime check that front-end desugaring produces correct recognizers: 2 * %def fragment inlining, `+` / `{n,m}` quantifier expansion, and ASCII 3 * shorthand classes. Drives the generated byte-mode lexer from test/desugar.ebnf. */ 4 #include "generated_desugar.h" 5 6 #include <stdio.h> 7 #include <string.h> 8 9 static int failures = 0; 10 11 static void ck(const char *what, int ok) { 12 printf("%s %s\n", ok ? "ok " : "FAIL", what); 13 if (!ok) failures++; 14 } 15 16 typedef struct { 17 KitGramTokenKind kind; 18 const char *lexeme; 19 } Want; 20 21 static void check_stream(const char *src, const Want *want, size_t nwant) { 22 KitGramLexInput in; 23 kit_gram_lex_input_init(&in, &(KitGramLexInputConfig){0}); 24 KitGramLexer lx; 25 KitGramLexConfig cfg = {0}; 26 desugar_lexer_init(&lx, &in, &cfg); 27 KitGramLexInputSpan sp = { .bytes = (const unsigned char *)src, .len = strlen(src) }; 28 kit_gram_lex_input_push(&in, &sp); 29 kit_gram_lex_input_finish(&in); 30 31 size_t i = 0; 32 char label[256]; 33 for (;;) { 34 KitGramToken t; 35 KitGramLexStatus st = kit_gram_lexer_next(&lx, &t); 36 if (st == KIT_GRAM_LEX_TOKEN) { 37 if (i >= nwant) { 38 ck("lexer emitted too many tokens", 0); 39 return; 40 } 41 int ok = t.kind == want[i].kind && t.len == strlen(want[i].lexeme) && 42 strncmp(t.lexeme, want[i].lexeme, t.len) == 0; 43 snprintf(label, sizeof label, "[%s] tok[%zu] kind=%u \"%.*s\" (want kind=%u \"%s\")", 44 src, i, (unsigned)t.kind, (int)t.len, t.lexeme, 45 (unsigned)want[i].kind, want[i].lexeme); 46 ck(label, ok); 47 i++; 48 continue; 49 } 50 if (st == KIT_GRAM_LEX_EOF) { 51 ck("stream fully consumed", i == nwant); 52 return; 53 } 54 ck("unexpected lexer status", 0); 55 return; 56 } 57 } 58 59 /* Expect the first token at the start of `src` to be a lex error (no match). */ 60 static void check_reject(const char *src) { 61 KitGramLexInput in; 62 kit_gram_lex_input_init(&in, &(KitGramLexInputConfig){0}); 63 KitGramLexer lx; 64 KitGramLexConfig cfg = {0}; 65 desugar_lexer_init(&lx, &in, &cfg); 66 KitGramLexInputSpan sp = { .bytes = (const unsigned char *)src, .len = strlen(src) }; 67 kit_gram_lex_input_push(&in, &sp); 68 kit_gram_lex_input_finish(&in); 69 KitGramToken t; 70 char label[128]; 71 snprintf(label, sizeof label, "rejects \"%s\"", src); 72 ck(label, kit_gram_lexer_next(&lx, &t) == KIT_GRAM_LEX_ERROR); 73 } 74 75 int main(void) { 76 printf("== desugaring: fragments, +, {n,m}, shorthands ==\n"); 77 78 { /* INT via fragment digit ("_"? digit)*, HEX via nested fragments */ 79 Want w[] = { { DESUGAR_TOK_INT, "12" }, { DESUGAR_TOK_INT, "3_456" }, 80 { DESUGAR_TOK_HEX, "0x1f" }, { DESUGAR_TOK_HEX, "0xAB_CD" } }; 81 check_stream("12 3_456 0x1f 0xAB_CD", w, 4); 82 } 83 { /* FLOAT exercises digit+ and the optional exponent group */ 84 Want w[] = { { DESUGAR_TOK_FLOAT, "3.14" }, { DESUGAR_TOK_FLOAT, "1.0e-9" }, { DESUGAR_TOK_FLOAT, "2.5E+3" } }; 85 check_stream("3.14 1.0e-9 2.5E+3", w, 3); 86 } 87 { /* IDENT = [A-Za-z_] \w* : \w shorthand standalone */ 88 Want w[] = { { DESUGAR_TOK_IDENT, "foo" }, { DESUGAR_TOK_IDENT, "_bar9" }, { DESUGAR_TOK_IDENT, "Baz_Qux" } }; 89 check_stream("foo _bar9 Baz_Qux", w, 3); 90 } 91 { /* PERCENT = "%" digit{2} (exact count); OP is a 1-2 char operator (range count) */ 92 Want w[] = { { DESUGAR_TOK_PERCENT, "%50" }, { DESUGAR_TOK_OP, "+" }, { DESUGAR_TOK_OP, "--" }, { DESUGAR_TOK_OP, "*/" } }; 93 check_stream("%50 + -- */", w, 4); 94 } 95 96 /* {2} is exact: one digit after % is not a PERCENT. */ 97 check_reject("%5"); 98 99 printf(failures ? "\nDESUGAR FAILURES=%d\n" : "\nDESUGAR ALL OK\n", failures); 100 return failures ? 1 : 0; 101 }