kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

test_sharp_edges.c (4056B)


      1 /* Regression guard for the two sharp edges the realistic suite surfaced and that
      2  * were subsequently fixed in the generator:
      3  *
      4  *   1. Diagnostic source locations. The C generator used to round-trip the parse
      5  *      through a location-free s-expression and re-derive positions from a node
      6  *      counter, so every post-parse diagnostic pointed at a bogus line (often
      7  *      past EOF) with column 1. The generator now builds the AST directly with
      8  *      real token locations; this asserts the exact line:col for a range of
      9  *      error classes (matching the Python reference, which was always correct).
     10  *
     11  *   2. Silent keyword shadowing. Declaring a broad IDENT rule before a keyword
     12  *      used to silently make the keyword lex as IDENT with no diagnostic. The
     13  *      generator now rejects any string-literal token a broader recognizer
     14  *      shadows, suggesting %keywords.
     15  *
     16  * Drives the in-process compiler API (include/gramgen.h) directly — the exact code
     17  * path build/kit_gram_c uses.
     18  */
     19 #include <kit/gram.h>
     20 
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 
     25 static void *xrealloc(void *ud, void *ptr, size_t o, size_t n, size_t align) {
     26     (void)ud; (void)o; (void)align;
     27     if (!n) { free(ptr); return NULL; }
     28     return realloc(ptr, n);
     29 }
     30 
     31 static int failures = 0;
     32 static void check(int ok, const char *id, const char *detail) {
     33     printf("%s  %-24s %s\n", ok ? "ok  " : "FAIL", id, detail);
     34     if (!ok) failures++;
     35 }
     36 
     37 static KitGramCompiled *compile(const char *text, KitGramDiagnostic *diag) {
     38     static KitGramAllocator alloc = { .realloc = xrealloc };
     39     KitGramOptions opts = { .allocator = &alloc };
     40     memset(diag, 0, sizeof *diag);
     41     return kit_gram_compile_text(text, strlen(text), "edge", &opts, diag);
     42 }
     43 
     44 /* ---- Finding 1: exact diagnostic locations ------------------------------- */
     45 typedef struct { const char *id, *text; uint32_t line, col; } LocCase;
     46 
     47 static void check_location(const LocCase *c) {
     48     KitGramDiagnostic diag;
     49     KitGramCompiled *cg = compile(c->text, &diag);
     50     if (cg) { check(0, c->id, "grammar unexpectedly compiled (fixture is wrong)"); kit_gram_free(cg); return; }
     51     char detail[160];
     52     snprintf(detail, sizeof detail, "reported %u:%u (want %u:%u): %s",
     53              diag.line, diag.col, c->line, c->col, diag.message);
     54     check(diag.line == c->line && diag.col == c->col, c->id, detail);
     55 }
     56 
     57 /* ---- Finding 2: keyword shadowing is now a located error ----------------- */
     58 static void check_keyword_shadow(void) {
     59     static const char text[] =
     60         "%lex {\n"
     61         "  %skip WS = [ \\t\\r\\n]+;\n"
     62         "  IDENT = [A-Za-z_] [A-Za-z0-9_]*;\n"
     63         "}\n"
     64         "%token IF = \"if\";\n"
     65         "stmt = \"if\" IDENT | IDENT;\n";
     66     KitGramDiagnostic diag;
     67     KitGramCompiled *cg = compile(text, &diag);
     68     if (cg) { check(0, "keyword_shadow", "shadowed keyword compiled silently"); kit_gram_free(cg); return; }
     69     int ok = strstr(diag.message, "shadowed") && strstr(diag.message, "%keywords") && diag.line == 5;
     70     char detail[200];
     71     snprintf(detail, sizeof detail, "%u:%u: %s", diag.line, diag.col, diag.message);
     72     check(ok, "keyword_shadow", detail);
     73 }
     74 
     75 int main(void) {
     76     printf("== realistic-suite regression: diagnostic locations + keyword shadowing ==\n");
     77 
     78     static const LocCase cases[] = {
     79         { "loc_ll1_conflict", "r = \"a\" \"b\" | \"a\" \"c\" ;\n", 1, 15 },
     80         { "loc_group_body",   "r = a (\"x\" \"y\")? ;\na = \"z\" ;\n", 1, 8 },
     81         { "loc_charclass_dash","%lex {\n  X = [+-] ;\n}\nr = X ;\n", 2, 7 },
     82         { "loc_dangling_else",
     83           "%lex {\n  %skip WS = [ \\t]+;\n  IDENT = [A-Za-z_]+;\n"
     84           "  %keywords IDENT { IF=\"if\"; ELSE=\"else\"; }\n}\n"
     85           "stmt = \"if\" IDENT stmt else_part? | IDENT ;\n"
     86           "else_part = \"else\" stmt ;\n", 6, 33 },
     87     };
     88     for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++)
     89         check_location(&cases[i]);
     90 
     91     check_keyword_shadow();
     92 
     93     printf("\n%s\n", failures ? "FAILURES" : "all fixes hold");
     94     return failures ? 1 : 0;
     95 }