generator.c (212221B)
1 #include "meta_tables.h" 2 #include "internal.h" 3 4 /* Meta-parser value-channel carriers. The actions (cb_lift/cb_reduce) build the 5 * real Ast directly; the only `Node` values left on the value stack are token 6 * leaves (N_LEAF, carrying lexeme bytes + source Loc) and generic lists 7 * (N_LIST, holding child pointers of whatever Ast type the rule collects). */ 8 typedef enum { 9 N_LEAF, 10 N_LIST, 11 } NodeKind; 12 13 typedef struct Node { 14 GramgenContext* ctx; 15 NodeKind kind; 16 Loc loc; /* source location of a token leaf */ 17 char* text; 18 size_t len; 19 char* scalar_text; 20 size_t scalar_len; 21 struct Node** items; /* N_LIST: collected child pointers (cast per rule) */ 22 size_t nitems, cap; 23 } Node; 24 25 #define META_MAX_EXPECTED 32 26 27 typedef struct { 28 int err_seen; 29 KitGramTokenKind err_tok; 30 KitGramRuleId err_rule; 31 KitGramTokenKind expected[META_MAX_EXPECTED]; 32 size_t nexpected; 33 int expected_truncated; 34 uint32_t err_line, err_col; 35 char err_lexeme[80]; 36 size_t err_lexeme_len; 37 } Meta; 38 39 /* ---- bump arena ---- 40 * Each allocation is [16-byte header | user data], the header holding the 41 * rounded data size. Allocation bumps `used`; the most-recent allocation can be 42 * freed (rewind) or grown in place, which covers the common LIFO temporaries. 43 * Other frees are no-ops; all space is reclaimed by mem_release. */ 44 #define ARENA_HDR 16u 45 #define ARENA_ALIGN 16u 46 #define ARENA_BLOCK_MIN (64u * 1024u) 47 48 /* Adapter from the upstream realloc-for-everything allocator onto the kit heap 49 * (KitContext.heap): a NULL pointer means allocate, a zero new size means free, 50 * otherwise resize. Replaces the per-compile gramgen_allocator. */ 51 static void* gmem_realloc(KitHeap* h, void* p, size_t old, size_t n, 52 size_t align) { 53 if (n == 0) { 54 if (p) h->free(h, p, old); 55 return NULL; 56 } 57 if (!p) return h->alloc(h, n, align); 58 return h->realloc(h, p, old, n, align); 59 } 60 61 static size_t arena_round(size_t n) { 62 return (n + (ARENA_ALIGN - 1u)) & ~(size_t)(ARENA_ALIGN - 1u); 63 } 64 65 static char* arena_base(ArenaBlock* b) { 66 uintptr_t p = (uintptr_t)(b + 1); 67 p = (p + (ARENA_ALIGN - 1u)) & ~(uintptr_t)(ARENA_ALIGN - 1u); 68 return (char*)p; 69 } 70 71 static ArenaBlock* arena_new_block(GramgenMem* m, size_t need) { 72 size_t cap = need > ARENA_BLOCK_MIN ? need : ARENA_BLOCK_MIN; 73 /* +ARENA_ALIGN covers the alignment slack consumed by arena_base. */ 74 ArenaBlock* b = 75 gmem_realloc(m->heap, NULL, 0, sizeof *b + cap + ARENA_ALIGN, 76 _Alignof(max_align_t)); 77 if (!b) return NULL; 78 b->next = NULL; 79 b->cap = cap; 80 b->used = 0; 81 return b; 82 } 83 84 void diag_vset(GramgenContext* ctx, Loc loc, const char* fmt, va_list ap) { 85 if (!ctx || !ctx->diag) return; 86 GramDiag* d = ctx->diag; 87 d->path = loc.path; 88 d->line = loc.line; 89 d->col = loc.col; 90 vsnprintf(d->message, sizeof d->message, fmt, ap); 91 d->message[sizeof d->message - 1] = '\0'; 92 } 93 94 void diag_set(GramgenContext* ctx, Loc loc, const char* fmt, ...) { 95 va_list ap; 96 va_start(ap, fmt); 97 diag_vset(ctx, loc, fmt, ap); 98 va_end(ap); 99 } 100 101 void die_oom(GramgenContext* ctx) { 102 Loc loc = {0}; 103 diag_set(ctx, loc, "out of memory"); 104 if (ctx && ctx->can_jump) longjmp(ctx->jmp, 1); 105 for (;;) { 106 } 107 } 108 109 /* libkit operational-diagnostic bridge (src/core/diag.c). Forward-declared 110 * rather than including "core/diag.h" so this TU's `Buf`/`Str`/`Loc` cannot 111 * collide with kit's internal core types. */ 112 void kit_ctx_diagf(const KitContext* ctx, const char* fmt, ...); 113 114 /* Flush the pending diagnostic (set by diag_set / parse errors) to the caller's 115 * KitContext.diag sink, then clear it. Called on every error return from the 116 * public API entry points; a no-op when no message was captured. */ 117 static void gram_flush_diag(GramgenContext* ctx) { 118 GramDiag* d = ctx ? ctx->diag : NULL; 119 if (!d || d->message[0] == '\0') return; 120 if (d->path) 121 kit_ctx_diagf(ctx->kit, "%s:%u:%u: %s", d->path, d->line, d->col, 122 d->message); 123 else 124 kit_ctx_diagf(ctx->kit, "%s", d->message); 125 d->message[0] = '\0'; 126 } 127 128 static GramgenMem* active_mem(GramgenContext* ctx) { 129 return ctx->active ? ctx->active : &ctx->mem; 130 } 131 132 /* Freeing (list) allocator: one tracked block per allocation, immediate free. 133 */ 134 static void* list_alloc(GramgenContext* ctx, GramgenMem* m, size_t size) { 135 void* p = gmem_realloc(m->heap, NULL, 0, size, _Alignof(max_align_t)); 136 if (!p) die_oom(ctx); 137 MemBlock* blk = gmem_realloc(m->heap, NULL, 0, sizeof *blk, 138 _Alignof(max_align_t)); 139 if (!blk) { 140 gmem_realloc(m->heap, p, size, 0, _Alignof(max_align_t)); 141 die_oom(ctx); 142 } 143 *blk = (MemBlock){.ptr = p, .size = size, .next = m->blocks}; 144 m->blocks = blk; 145 return p; 146 } 147 148 void* xmalloc(GramgenContext* ctx, size_t n) { 149 if (!ctx || !ctx->mem.heap) die_oom(ctx); 150 GramgenMem* m = active_mem(ctx); 151 if (m->kind == MEM_LIST) return list_alloc(ctx, m, n ? n : 1); 152 size_t rn = arena_round(n ? n : 1); 153 size_t need = ARENA_HDR + rn; 154 ArenaBlock* b = m->cur; 155 if (!b || b->cap - b->used < need) { 156 ArenaBlock* nb = arena_new_block(m, need); 157 if (!nb) die_oom(ctx); 158 if (m->cur) 159 m->cur->next = nb; 160 else 161 m->head = nb; 162 m->cur = nb; 163 b = nb; 164 } 165 char* hdr = arena_base(b) + b->used; 166 *(size_t*)hdr = rn; 167 b->used += need; 168 return hdr + ARENA_HDR; 169 } 170 171 /* True iff p is the most-recent allocation in the current block (so it ends 172 * exactly at the bump pointer), with *rn_out set to its rounded size. */ 173 static int arena_is_top(GramgenMem* m, void* p, size_t* rn_out) { 174 ArenaBlock* b = m->cur; 175 if (!b || !p) return 0; 176 char* base = arena_base(b); 177 char* up = (char*)p; 178 if (up < base + ARENA_HDR || up > base + b->used) return 0; 179 size_t rn = *(size_t*)(up - ARENA_HDR); 180 if (up + rn != base + b->used) return 0; 181 *rn_out = rn; 182 return 1; 183 } 184 185 /* Find a tracked block by user pointer (MEM_LIST only); returns its predecessor 186 * (or NULL if head) via *prev, and the block itself via the return value. */ 187 static MemBlock* list_find(GramgenMem* m, void* p, MemBlock** prev) { 188 MemBlock* pv = NULL; 189 for (MemBlock* b = m->blocks; b; b = b->next) { 190 if (b->ptr == p) { 191 *prev = pv; 192 return b; 193 } 194 pv = b; 195 } 196 *prev = NULL; 197 return NULL; 198 } 199 200 void* xrealloc(GramgenContext* ctx, void* p, size_t n) { 201 if (!ctx || !ctx->mem.heap) die_oom(ctx); 202 if (!p) return xmalloc(ctx, n); 203 GramgenMem* m = active_mem(ctx); 204 if (m->kind == MEM_LIST) { 205 MemBlock *prev, *blk = list_find(m, p, &prev); 206 if (!blk) die_oom(ctx); 207 void* q = gmem_realloc(m->heap, p, blk->size, n ? n : 1, 208 _Alignof(max_align_t)); 209 if (!q) die_oom(ctx); 210 blk->ptr = q; 211 blk->size = n ? n : 1; 212 return q; 213 } 214 size_t rn_new = arena_round(n ? n : 1); 215 size_t rn_old; 216 if (arena_is_top(m, p, &rn_old)) { 217 /* Grow/shrink in place when the bump block has room. */ 218 char* base = arena_base(m->cur); 219 size_t off = (size_t)((char*)p - ARENA_HDR - base); 220 if (off + ARENA_HDR + rn_new <= m->cur->cap) { 221 m->cur->used = off + ARENA_HDR + rn_new; 222 *(size_t*)((char*)p - ARENA_HDR) = rn_new; 223 return p; 224 } 225 } else { 226 rn_old = *(size_t*)((char*)p - ARENA_HDR); 227 } 228 void* q = xmalloc(ctx, n); 229 memcpy(q, p, rn_old < rn_new ? rn_old : rn_new); 230 return q; 231 } 232 233 void* xcalloc(GramgenContext* ctx, size_t n, size_t size) { 234 if (size && n > (SIZE_MAX / size)) die_oom(ctx); 235 void* p = xmalloc(ctx, n * size); 236 memset(p, 0, n * size); 237 return p; 238 } 239 240 void xfree(GramgenContext* ctx, void* p) { 241 if (!p || !ctx) return; 242 GramgenMem* m = active_mem(ctx); 243 if (m->kind == MEM_LIST) { 244 MemBlock *prev, *blk = list_find(m, p, &prev); 245 if (!blk) die_oom(ctx); 246 if (prev) 247 prev->next = blk->next; 248 else 249 m->blocks = blk->next; 250 gmem_realloc(m->heap, blk->ptr, blk->size, 0, 251 _Alignof(max_align_t)); 252 gmem_realloc(m->heap, blk, sizeof *blk, 0, _Alignof(max_align_t)); 253 return; 254 } 255 size_t rn; 256 if (arena_is_top(m, p, &rn)) /* LIFO free: rewind the bump pointer */ 257 m->cur->used = (size_t)((char*)p - ARENA_HDR - arena_base(m->cur)); 258 /* else: reclaimed wholesale at mem_release */ 259 } 260 261 void mem_release(GramgenMem* m) { 262 if (m->kind == MEM_LIST) { 263 MemBlock* blk = m->blocks; 264 while (blk) { 265 MemBlock* next = blk->next; 266 gmem_realloc(m->heap, blk->ptr, blk->size, 0, 267 _Alignof(max_align_t)); 268 gmem_realloc(m->heap, blk, sizeof *blk, 0, _Alignof(max_align_t)); 269 blk = next; 270 } 271 m->blocks = NULL; 272 return; 273 } 274 ArenaBlock* b = m->head; 275 while (b) { 276 ArenaBlock* next = b->next; 277 gmem_realloc(m->heap, b, sizeof *b + b->cap + ARENA_ALIGN, 0, 278 _Alignof(max_align_t)); 279 b = next; 280 } 281 m->head = m->cur = NULL; 282 } 283 284 /* Scratch region: x* allocations between scratch_enter and scratch_leave land 285 * in a separate arena that scratch_release frees wholesale. Used to bound peak 286 * for a self-contained phase (lexer construction) whose intermediates would 287 * otherwise linger in the permanent arena until the compiled grammar is freed. 288 * Anything that must survive the phase is copied back to the perm arena before 289 * release. Not nestable. */ 290 void scratch_enter(GramgenContext* ctx) { 291 ctx->scratch.heap = ctx->mem.heap; 292 ctx->scratch.kind = MEM_LIST; /* incremental frees keep mid-build peak low */ 293 ctx->active = &ctx->scratch; 294 } 295 void scratch_leave(GramgenContext* ctx) { 296 ctx->active = NULL; /* back to the permanent arena */ 297 } 298 void scratch_release(GramgenContext* ctx) { mem_release(&ctx->scratch); } 299 300 char* xstrndup(GramgenContext* ctx, const char* s, size_t n) { 301 char* out = xmalloc(ctx, n + 1); 302 memcpy(out, s, n); 303 out[n] = '\0'; 304 return out; 305 } 306 307 char* xstrdup(GramgenContext* ctx, const char* s) { 308 return xstrndup(ctx, s, strlen(s)); 309 } 310 311 static uint64_t hash_bytes(const char* s, size_t len) { 312 uint64_t h = KIT_GRAM_HASH_OFFSET_BASIS; 313 for (size_t i = 0; i < len; i++) { 314 h ^= (unsigned char)s[i]; 315 h *= KIT_GRAM_HASH_PRIME; 316 } 317 h ^= len; 318 h *= KIT_GRAM_HASH_PRIME; 319 return h; 320 } 321 322 static void intern_rehash(GramgenContext* ctx) { 323 size_t old_n = ctx->intern_nbuckets; 324 InternEntry** old = ctx->intern_buckets; 325 size_t new_n = old_n ? old_n * 2 : 1024; 326 InternEntry** buckets = xcalloc(ctx, new_n, sizeof *buckets); 327 for (size_t i = 0; i < old_n; i++) { 328 InternEntry* entry = old[i]; 329 while (entry) { 330 InternEntry* next = entry->next; 331 size_t bucket = (size_t)(entry->hash & (uint64_t)(new_n - 1)); 332 entry->next = buckets[bucket]; 333 buckets[bucket] = entry; 334 entry = next; 335 } 336 } 337 ctx->intern_buckets = buckets; 338 ctx->intern_nbuckets = new_n; 339 } 340 341 char* intern_len(GramgenContext* ctx, const char* s, size_t len) { 342 if (!ctx || !s) die_oom(ctx); 343 if (!ctx->intern_nbuckets || 344 (ctx->intern_count + 1) * 2 > ctx->intern_nbuckets) 345 intern_rehash(ctx); 346 uint64_t hash = hash_bytes(s, len); 347 size_t bucket = (size_t)(hash & (uint64_t)(ctx->intern_nbuckets - 1)); 348 for (InternEntry* entry = ctx->intern_buckets[bucket]; entry; 349 entry = entry->next) { 350 if (entry->hash == hash && entry->len == len && 351 memcmp(entry->s, s, len) == 0) 352 return entry->s; 353 } 354 InternEntry* entry = xmalloc(ctx, sizeof *entry); 355 entry->s = xstrndup(ctx, s, len); 356 entry->len = len; 357 entry->hash = hash; 358 entry->next = ctx->intern_buckets[bucket]; 359 ctx->intern_buckets[bucket] = entry; 360 ctx->intern_count++; 361 return entry->s; 362 } 363 364 char* intern_c(GramgenContext* ctx, const char* s) { 365 return intern_len(ctx, s, strlen(s)); 366 } 367 368 static uint64_t hash_ptr_key(const char* key) { 369 uintptr_t x = (uintptr_t)key; 370 x >>= 3; 371 x ^= x >> 33; 372 x *= (uintptr_t)0xff51afd7ed558ccdull; 373 x ^= x >> 33; 374 return (uint64_t)x; 375 } 376 377 static void name_index_rehash(GramgenContext* ctx, NameIndexMap* map) { 378 size_t old_cap = map->cap; 379 const char** old_keys = map->keys; 380 int* old_values = map->values; 381 size_t new_cap = old_cap ? old_cap * 2 : 64; 382 map->keys = xcalloc(ctx, new_cap, sizeof *map->keys); 383 map->values = xmalloc(ctx, new_cap * sizeof *map->values); 384 map->cap = new_cap; 385 map->n = 0; 386 for (size_t i = 0; i < old_cap; i++) { 387 if (!old_keys[i]) continue; 388 name_index_put(ctx, map, old_keys[i], old_values[i]); 389 } 390 } 391 392 int name_index_find(const NameIndexMap* map, const char* key, int* out) { 393 if (!map->cap || !key) return 0; 394 size_t mask = map->cap - 1; 395 size_t pos = (size_t)(hash_ptr_key(key) & (uint64_t)mask); 396 for (;;) { 397 const char* cur = map->keys[pos]; 398 if (!cur) return 0; 399 if (cur == key) { 400 if (out) *out = map->values[pos]; 401 return 1; 402 } 403 pos = (pos + 1) & mask; 404 } 405 } 406 407 void name_index_put(GramgenContext* ctx, NameIndexMap* map, const char* key, 408 int value) { 409 if (!key) die_oom(ctx); 410 if (!map->cap || (map->n + 1) * 2 > map->cap) name_index_rehash(ctx, map); 411 size_t mask = map->cap - 1; 412 size_t pos = (size_t)(hash_ptr_key(key) & (uint64_t)mask); 413 for (;;) { 414 if (!map->keys[pos]) { 415 map->keys[pos] = key; 416 map->values[pos] = value; 417 map->n++; 418 return; 419 } 420 if (map->keys[pos] == key) { 421 map->values[pos] = value; 422 return; 423 } 424 pos = (pos + 1) & mask; 425 } 426 } 427 428 char* xasprintf(GramgenContext* ctx, const char* fmt, ...) { 429 va_list ap, aq; 430 va_start(ap, fmt); 431 va_copy(aq, ap); 432 int n = vsnprintf(NULL, 0, fmt, ap); 433 va_end(ap); 434 if (n < 0) die_oom(ctx); 435 char* out = xmalloc(ctx, (size_t)n + 1); 436 vsnprintf(out, (size_t)n + 1, fmt, aq); 437 va_end(aq); 438 return out; 439 } 440 441 static void die_write_failed(Buf* b) { 442 Loc loc = {.path = b->write_path, .line = 1, .col = 1}; 443 diag_set(b->ctx, loc, "codegen write failed"); 444 if (b->ctx && b->ctx->can_jump) longjmp(b->ctx->jmp, 1); 445 for (;;) { 446 } 447 } 448 449 void buf_init(GramgenContext* ctx, Buf* b) { 450 b->ctx = ctx; 451 b->cap = 64; 452 b->len = 0; 453 b->s = xmalloc(ctx, b->cap); 454 b->s[0] = '\0'; 455 b->writer = NULL; 456 b->write_path = NULL; 457 } 458 459 void buf_init_writer(GramgenContext* ctx, Buf* b, KitWriter* writer, 460 const char* path) { 461 b->ctx = ctx; 462 b->s = NULL; 463 b->len = 0; 464 b->cap = 0; 465 b->writer = writer; 466 b->write_path = path; 467 } 468 469 void buf_reserve(Buf* b, size_t add) { 470 if (b->writer) return; 471 size_t need = b->len + add + 1; 472 if (need <= b->cap) return; 473 while (b->cap < need) b->cap *= 2; 474 b->s = xrealloc(b->ctx, b->s, b->cap); 475 } 476 477 void buf_appendn(Buf* b, const char* s, size_t n) { 478 if (!n) return; 479 if (b->writer) { 480 if (kit_writer_write(b->writer, s, n) != KIT_OK) die_write_failed(b); 481 b->len += n; 482 return; 483 } 484 buf_reserve(b, n); 485 memcpy(b->s + b->len, s, n); 486 b->len += n; 487 b->s[b->len] = '\0'; 488 } 489 490 void buf_append(Buf* b, const char* s) { buf_appendn(b, s, strlen(s)); } 491 492 /* Append an unsigned integer as decimal without going through printf. The 493 * dense lexer table emits tens of thousands of small integers; a hand-rolled 494 * formatter avoids a malloc + vsnprintf per cell. */ 495 void buf_append_uint(Buf* b, unsigned v) { 496 char tmp[10]; 497 size_t n = sizeof tmp; 498 tmp[--n] = (char)('0' + v % 10u); 499 while ((v /= 10u)) tmp[--n] = (char)('0' + v % 10u); 500 buf_appendn(b, tmp + n, sizeof tmp - n); 501 } 502 503 char* buf_take(Buf* b) { return b->s; } 504 505 static Node* node_new(GramgenContext* ctx, NodeKind kind) { 506 Node* n = xcalloc(ctx, 1, sizeof *n); 507 if (!n) die_oom(ctx); 508 n->ctx = ctx; 509 n->kind = kind; 510 return n; 511 } 512 513 static void list_append(Node* list, Node* item) { 514 if (list->nitems == list->cap) { 515 list->cap = list->cap ? list->cap * 2 : 4; 516 list->items = 517 xrealloc(list->ctx, list->items, list->cap * sizeof *list->items); 518 } 519 list->items[list->nitems++] = item; 520 } 521 522 static char* quote_len(GramgenContext* ctx, const char* s, size_t n, 523 bool use_octal) { 524 Buf b; 525 buf_init(ctx, &b); 526 buf_append(&b, "\""); 527 for (size_t i = 0; i < n; i++) { 528 unsigned char ch = (unsigned char)s[i]; 529 switch (ch) { 530 case '\\': 531 buf_append(&b, "\\\\"); 532 break; 533 case '"': 534 buf_append(&b, "\\\""); 535 break; 536 case '\n': 537 buf_append(&b, "\\n"); 538 break; 539 case '\r': 540 buf_append(&b, "\\r"); 541 break; 542 case '\t': 543 buf_append(&b, "\\t"); 544 break; 545 default: 546 if (ch >= 32 && ch <= 126) { 547 char c = (char)ch; 548 buf_appendn(&b, &c, 1); 549 } else { 550 char tmp[5]; 551 if (use_octal) 552 snprintf(tmp, sizeof tmp, "\\%03o", ch); 553 else 554 snprintf(tmp, sizeof tmp, "\\x%02x", ch); 555 buf_append(&b, tmp); 556 } 557 break; 558 } 559 } 560 buf_append(&b, "\""); 561 return buf_take(&b); 562 } 563 564 /* ---- AST -> s-expression renderer (the `--dump-sexpr` debug surface) -------- 565 * This is the ONLY consumer of the s-expression form; the compile path uses the 566 * Ast directly. Output is held byte-identical with gen/gramgen.py's renderer. 567 */ 568 static void quote_str_into(Buf* b, Str s) { 569 char* q = quote_len(b->ctx, s.s, s.len, false); 570 buf_append(b, q); 571 xfree(b->ctx, q); 572 } 573 574 static void ast_alt_sexpr(Buf* b, const AstAlt* alt); 575 576 static void ast_node_sexpr(Buf* b, const AstNode* node) { 577 switch (node->kind) { 578 case AST_NAME: 579 buf_append(b, "(name "); 580 buf_appendn(b, node->value.s, node->value.len); 581 buf_append(b, ")"); 582 break; 583 case AST_LITERAL: 584 buf_append(b, "(literal "); 585 quote_str_into(b, node->value); 586 buf_append(b, ")"); 587 break; 588 case AST_GROUP: 589 buf_append(b, "(group "); 590 ast_alt_sexpr(b, node->alts); 591 buf_append(b, ")"); 592 break; 593 case AST_OPT: 594 buf_append(b, "(opt "); 595 ast_alt_sexpr(b, node->alts); 596 buf_append(b, ")"); 597 break; 598 case AST_REP: 599 buf_append(b, "(rep "); 600 ast_alt_sexpr(b, node->alts); 601 buf_append(b, ")"); 602 break; 603 } 604 } 605 606 static void ast_seq_sexpr(Buf* b, const AstSeq* seq) { 607 buf_append(b, "(seq"); 608 for (size_t i = 0; i < seq->nitems; i++) { 609 buf_append(b, " "); 610 ast_node_sexpr(b, seq->items[i]); 611 } 612 buf_append(b, ")"); 613 } 614 615 static void ast_alt_sexpr(Buf* b, const AstAlt* alt) { 616 buf_append(b, "(alt"); 617 for (size_t i = 0; i < alt->nseqs; i++) { 618 buf_append(b, " "); 619 ast_seq_sexpr(b, alt->seqs[i]); 620 } 621 buf_append(b, ")"); 622 } 623 624 static void ast_pratt_sexpr(Buf* b, const AstPrattSpec* spec) { 625 buf_append(b, "(pratt"); 626 for (size_t i = 0; i < spec->nlines; i++) { 627 const AstPrattLine* line = spec->lines[i]; 628 buf_append(b, " ("); 629 buf_append(b, line->kind); 630 for (size_t j = 0; j < line->natoms; j++) { 631 const AstPrattAtom* atom = line->atoms[j]; 632 if (atom->kind == PRATT_NAME) { 633 buf_append(b, " (name "); 634 buf_appendn(b, atom->value.s, atom->value.len); 635 buf_append(b, ")"); 636 } else { 637 buf_append(b, " (literal "); 638 quote_str_into(b, atom->value); 639 buf_append(b, ")"); 640 } 641 } 642 buf_append(b, ")"); 643 } 644 buf_append(b, ")"); 645 } 646 647 static void ast_lex_alt_sexpr(Buf* b, const AstLexAlt* alt); 648 649 static void ast_lex_node_sexpr(Buf* b, const AstLexNode* node) { 650 switch (node->kind) { 651 case LEX_LITERAL: 652 buf_append(b, "(literal "); 653 quote_str_into(b, node->value); 654 buf_append(b, " "); 655 quote_str_into(b, 656 node->scalar_value.s ? node->scalar_value : node->value); 657 buf_append(b, ")"); 658 break; 659 case LEX_CLASS: 660 buf_append(b, "(class "); 661 quote_str_into(b, node->value); 662 buf_append(b, ")"); 663 break; 664 case LEX_PROP: 665 buf_append(b, "(prop "); 666 quote_str_into(b, node->value); 667 buf_append(b, ")"); 668 break; 669 case LEX_ANY: 670 buf_append(b, "(any)"); 671 break; 672 case LEX_ANCHOR: 673 buf_append(b, "(anchor "); 674 buf_appendn(b, node->value.s, node->value.len); 675 buf_append(b, ")"); 676 break; 677 case LEX_NAME: 678 buf_append(b, "(name "); 679 buf_appendn(b, node->value.s, node->value.len); 680 buf_append(b, ")"); 681 break; 682 case LEX_GROUP: 683 buf_append(b, "(group "); 684 ast_lex_alt_sexpr(b, node->alts); 685 buf_append(b, ")"); 686 break; 687 case LEX_OPT: 688 buf_append(b, "(opt "); 689 ast_lex_alt_sexpr(b, node->alts); 690 buf_append(b, ")"); 691 break; 692 case LEX_REP: 693 buf_append(b, "(rep "); 694 ast_lex_alt_sexpr(b, node->alts); 695 buf_append(b, ")"); 696 break; 697 case LEX_REPEAT: 698 buf_append(b, "(repeat "); 699 quote_str_into(b, node->value); 700 buf_append(b, " "); 701 ast_lex_alt_sexpr(b, node->alts); 702 buf_append(b, ")"); 703 break; 704 } 705 } 706 707 static void ast_lex_seq_sexpr(Buf* b, const AstLexSeq* seq) { 708 buf_append(b, "(seq"); 709 for (size_t i = 0; i < seq->nitems; i++) { 710 buf_append(b, " "); 711 ast_lex_node_sexpr(b, seq->items[i]); 712 } 713 buf_append(b, ")"); 714 } 715 716 static void ast_lex_alt_sexpr(Buf* b, const AstLexAlt* alt) { 717 buf_append(b, "(alt"); 718 for (size_t i = 0; i < alt->nseqs; i++) { 719 buf_append(b, " "); 720 ast_lex_seq_sexpr(b, alt->seqs[i]); 721 } 722 buf_append(b, ")"); 723 } 724 725 static void ast_lex_line_sexpr(Buf* b, const AstLexLine* line) { 726 if (line->kind == LEX_LINE_KEYWORDS) { 727 buf_append(b, "(keywords "); 728 buf_append(b, line->name); 729 for (size_t i = 0; i < line->nkw_entries; i++) { 730 const AstKwEntry* e = &line->kw_entries[i]; 731 if (e->name && e->has_literal) { 732 buf_append(b, " (kwfull "); 733 buf_append(b, e->name); 734 buf_append(b, " "); 735 quote_str_into(b, e->literal); 736 buf_append(b, ")"); 737 } else if (e->name) { 738 buf_append(b, " (kwname "); 739 buf_append(b, e->name); 740 buf_append(b, ")"); 741 } else { 742 buf_append(b, " (kwlit "); 743 quote_str_into(b, e->literal); 744 buf_append(b, ")"); 745 } 746 } 747 buf_append(b, ")"); 748 return; 749 } 750 const char* head = line->kind == LEX_LINE_TOKEN ? "token" 751 : line->kind == LEX_LINE_SKIP ? "skip" 752 : "def"; 753 buf_append(b, "("); 754 buf_append(b, head); 755 buf_append(b, " "); 756 buf_append(b, line->name); 757 buf_append(b, " "); 758 ast_lex_alt_sexpr(b, line->alts); 759 buf_append(b, ")"); 760 } 761 762 static void ast_lex_block_sexpr(Buf* b, const AstLexBlock* block) { 763 buf_append(b, "(lex "); 764 buf_append(b, block->name); 765 buf_append(b, " "); 766 buf_append(b, block->mode == KIT_GRAM_LEX_INPUT_UTF8 ? "utf8" 767 : block->mode == KIT_GRAM_LEX_INPUT_TOKENS ? "tokens" 768 : "bytes"); 769 for (size_t i = 0; i < block->nlines; i++) { 770 buf_append(b, " "); 771 ast_lex_line_sexpr(b, block->lines[i]); 772 } 773 buf_append(b, ")"); 774 } 775 776 static void ast_rule_sexpr(Buf* b, const AstRule* rule) { 777 buf_append(b, "(rule "); 778 buf_append(b, rule->name); 779 buf_append(b, " "); 780 if (rule->pratt) 781 ast_pratt_sexpr(b, rule->pratt); 782 else 783 ast_alt_sexpr(b, rule->alts); 784 buf_append(b, ")"); 785 } 786 787 typedef struct { 788 GramgenContext* ctx; 789 const char* path; /* source path, stamped into every token Loc */ 790 ParsedGrammar* pg; /* accumulated as top-level items reduce */ 791 Meta meta; 792 } ParseUd; 793 794 static void pratt_line_append(AstPrattLine* line, AstPrattAtom* atom); 795 static void pratt_spec_append(AstPrattSpec* spec, AstPrattLine* line); 796 static void lex_block_append(AstLexBlock* block, AstLexLine* line); 797 static void parsed_add_rule(ParsedGrammar* g, AstRule* rule); 798 static void parsed_add_token_decl(ParsedGrammar* g, TokenDecl* decl); 799 static void parsed_add_lex_block(ParsedGrammar* g, AstLexBlock* block); 800 801 static KitGramSem cb_lift(void* ud, KitGramToken t) { 802 ParseUd* pu = ud; 803 GramgenContext* ctx = pu->ctx; 804 Node* n = node_new(ctx, N_LEAF); 805 n->loc = (Loc){.path = pu->path, .line = t.line, .col = t.col}; 806 /* Token bytes are interned to a single canonical, NUL-terminated copy. That 807 * dedups the many repeated names a grammar contains (every rule reference) 808 * and gives them the pointer identity the builder's name indexes key on — so 809 * cb_reduce can use the bytes directly with no further copy. (Literals are 810 * value-compared, but interning them is harmless and dedups duplicates.) 811 * String literals arrive from the lexer hook as "<bytelen>:<value><scalar>"; 812 * the value and scalar halves are interned separately. */ 813 if (t.kind == KIT_GRAM_META_TOK_STRING && t.lexeme && t.len) { 814 size_t prefix = 0; 815 while (prefix < t.len && t.lexeme[prefix] >= '0' && t.lexeme[prefix] <= '9') 816 prefix++; 817 if (prefix < t.len && t.lexeme[prefix] == ':') { 818 size_t byte_len = 0; 819 for (size_t i = 0; i < prefix; i++) 820 byte_len = byte_len * 10 + (size_t)(t.lexeme[i] - '0'); 821 size_t start = prefix + 1; 822 if (start + byte_len <= t.len) { 823 n->text = intern_len(ctx, t.lexeme + start, byte_len); 824 n->len = byte_len; 825 n->scalar_len = t.len - start - byte_len; 826 n->scalar_text = 827 intern_len(ctx, t.lexeme + start + byte_len, n->scalar_len); 828 return n; 829 } 830 } 831 } 832 n->text = intern_len(ctx, t.lexeme ? t.lexeme : "", t.len); 833 n->len = t.len; 834 return n; 835 } 836 837 static KitGramSem cb_list_empty(void* ud) { 838 ParseUd* pu = ud; 839 return node_new(pu->ctx, N_LIST); 840 } 841 842 static KitGramSem cb_list_push(void* ud, KitGramSem list, KitGramSem item) { 843 (void)ud; 844 list_append((Node*)list, (Node*)item); 845 return list; 846 } 847 848 static KitGramSem cb_opt_none(void* ud) { 849 (void)ud; 850 return NULL; 851 } 852 853 static KitGramSem cb_opt_some(void* ud, KitGramSem item) { 854 (void)ud; 855 return item; 856 } 857 858 /* A leaf's bytes — already interned to a canonical, NUL-terminated pointer by 859 * cb_lift, so this is both the value form (Str) and, via n->text, the name form 860 * (char *) the builder's pointer-keyed name indexes use. */ 861 static Str leaf_str(const Node* n) { 862 return (Str){.s = n->text, .len = n->len}; 863 } 864 865 /* Build a single-symbol AstAlt `(alt (seq node))`, used to wrap a quantified 866 * primary into an opt/rep/repeat node body. */ 867 static AstAlt* single_node_alt(GramgenContext* ctx, AstNode* node) { 868 AstSeq* seq = ast_seq_new(ctx, node->loc); 869 ast_seq_append(seq, node); 870 AstAlt* alt = ast_alt_new(ctx, node->loc); 871 ast_alt_append(alt, seq); 872 return alt; 873 } 874 static AstLexAlt* single_lex_node_alt(GramgenContext* ctx, AstLexNode* node) { 875 AstLexSeq* seq = ast_lex_seq_new(ctx, node->loc); 876 ast_lex_seq_append(seq, node); 877 AstLexAlt* alt = ast_lex_alt_new(ctx, node->loc); 878 ast_lex_alt_append(alt, seq); 879 return alt; 880 } 881 882 static KitGramSem cb_reduce(void* ud, KitGramRuleId r, int prod, KitGramSem* k, 883 size_t n) { 884 ParseUd* pu = ud; 885 GramgenContext* ctx = pu->ctx; 886 (void)n; 887 switch (r) { 888 case KIT_GRAM_META_R_grammar: 889 case KIT_GRAM_META_R_item: 890 case KIT_GRAM_META_R_decl: 891 return NULL; /* top-level items self-append to pu->pg */ 892 893 case KIT_GRAM_META_R_token_decl: { /* "%token" NAME "=" STRING ";" */ 894 Node *kw = k[0], *name = k[1], *lit = k[3]; 895 TokenDecl* d = xcalloc(ctx, 1, sizeof *d); 896 if (!d) die_oom(ctx); 897 d->name = name->text; 898 d->literal = leaf_str(lit); 899 d->loc = kw->loc; 900 parsed_add_token_decl(pu->pg, d); 901 return NULL; 902 } 903 case KIT_GRAM_META_R_pratt_rule: { /* "%pratt" NAME "{" pratt_line* "}" */ 904 Node *name = k[1], *lines = k[3]; 905 AstPrattSpec* spec = xcalloc(ctx, 1, sizeof *spec); 906 if (!spec) die_oom(ctx); 907 spec->ctx = ctx; 908 for (size_t i = 0; i < lines->nitems; i++) 909 pratt_spec_append(spec, (AstPrattLine*)lines->items[i]); 910 AstRule* rule = xcalloc(ctx, 1, sizeof *rule); 911 if (!rule) die_oom(ctx); 912 rule->name = name->text; 913 rule->loc = name->loc; 914 rule->pratt = spec; 915 parsed_add_rule(pu->pg, rule); 916 return NULL; 917 } 918 case KIT_GRAM_META_R_pratt_line: { /* NAME pratt_atom pratt_atom* */ 919 Node *kind = k[0], *rest = k[2]; 920 AstPrattLine* line = xcalloc(ctx, 1, sizeof *line); 921 if (!line) die_oom(ctx); 922 line->ctx = ctx; 923 line->kind = kind->text; 924 line->loc = kind->loc; 925 pratt_line_append(line, (AstPrattAtom*)k[1]); 926 for (size_t i = 0; i < rest->nitems; i++) 927 pratt_line_append(line, (AstPrattAtom*)rest->items[i]); 928 return line; 929 } 930 case KIT_GRAM_META_R_pratt_atom: { /* NAME | STRING */ 931 Node* tok = k[0]; 932 AstPrattAtom* atom = xcalloc(ctx, 1, sizeof *atom); 933 if (!atom) die_oom(ctx); 934 atom->kind = prod == 0 ? PRATT_NAME : PRATT_LITERAL; 935 atom->loc = tok->loc; 936 atom->value = leaf_str(tok); 937 return atom; 938 } 939 940 case KIT_GRAM_META_R_lex_block: { /* "%lex" lex_name lex_mode "{" lex_line* "}" 941 */ 942 Node *kw = k[0], *name = k[1], *mode = k[2], *lines = k[4]; 943 AstLexBlock* block = xcalloc(ctx, 1, sizeof *block); 944 if (!block) die_oom(ctx); 945 block->ctx = ctx; 946 block->name = name ? name->text : intern_c(ctx, "main"); 947 block->loc = kw->loc; 948 block->mode = KIT_GRAM_LEX_INPUT_BYTES; 949 if (mode) { 950 if (strcmp(mode->text, "utf8") == 0) 951 block->mode = KIT_GRAM_LEX_INPUT_UTF8; 952 else 953 kit_gram_error(ctx, mode->loc, "unknown %%lex mode '%s'", mode->text); 954 } 955 for (size_t i = 0; i < lines->nitems; i++) 956 lex_block_append(block, (AstLexLine*)lines->items[i]); 957 parsed_add_lex_block(pu->pg, block); 958 return NULL; 959 } 960 case KIT_GRAM_META_R_machine_block: { /* "%machine" NAME "{" lex_line* "}" */ 961 Node *kw = k[0], *name = k[1], *lines = k[3]; 962 AstLexBlock* block = xcalloc(ctx, 1, sizeof *block); 963 if (!block) die_oom(ctx); 964 block->ctx = ctx; 965 block->name = name->text; 966 block->loc = kw->loc; 967 block->mode = KIT_GRAM_LEX_INPUT_TOKENS; 968 for (size_t i = 0; i < lines->nitems; i++) 969 lex_block_append(block, (AstLexLine*)lines->items[i]); 970 parsed_add_lex_block(pu->pg, block); 971 return NULL; 972 } 973 case KIT_GRAM_META_R_lex_name: /* NAME | %empty */ 974 return prod == 0 ? k[0] : NULL; 975 case KIT_GRAM_META_R_lex_mode: /* ":" NAME | %empty */ 976 return prod == 0 ? k[1] : NULL; 977 case KIT_GRAM_META_R_lex_line: 978 return k[0]; 979 case KIT_GRAM_META_R_lex_rule: /* NAME "=" lex_alt ";" */ 980 case KIT_GRAM_META_R_skip_rule: /* "%skip" NAME "=" lex_alt ";" */ 981 case KIT_GRAM_META_R_def_rule: { /* "%def" NAME "=" lex_alt ";" */ 982 AstLexLine* line = xcalloc(ctx, 1, sizeof *line); 983 if (!line) die_oom(ctx); 984 if (r == KIT_GRAM_META_R_lex_rule) { 985 Node* name = k[0]; 986 line->kind = LEX_LINE_TOKEN; 987 line->name = name->text; 988 line->loc = name->loc; 989 line->alts = (AstLexAlt*)k[2]; 990 } else { 991 Node *kw = k[0], *name = k[1]; 992 line->kind = r == KIT_GRAM_META_R_skip_rule ? LEX_LINE_SKIP : LEX_LINE_DEF; 993 line->name = name->text; 994 line->loc = kw->loc; 995 line->alts = (AstLexAlt*)k[3]; 996 } 997 return line; 998 } 999 case KIT_GRAM_META_R_keywords_block: { /* "%keywords" NAME "{" kw_entry* "}" */ 1000 Node *kw = k[0], *host = k[1], *entries = k[3]; 1001 AstLexLine* line = xcalloc(ctx, 1, sizeof *line); 1002 if (!line) die_oom(ctx); 1003 line->kind = LEX_LINE_KEYWORDS; 1004 line->name = host->text; 1005 line->loc = kw->loc; 1006 line->nkw_entries = entries->nitems; 1007 if (entries->nitems) { 1008 line->kw_entries = 1009 xmalloc(ctx, entries->nitems * sizeof *line->kw_entries); 1010 for (size_t i = 0; i < entries->nitems; i++) 1011 line->kw_entries[i] = *(AstKwEntry*)entries->items[i]; 1012 } 1013 return line; 1014 } 1015 case KIT_GRAM_META_R_kw_entry: { /* NAME kw_value? ";" | STRING ";" */ 1016 AstKwEntry* e = xcalloc(ctx, 1, sizeof *e); 1017 if (!e) die_oom(ctx); 1018 if (prod == 0) { 1019 Node *name = k[0], *value = k[1]; /* value: STRING leaf or NULL */ 1020 e->name = name->text; 1021 e->loc = name->loc; 1022 if (value) { 1023 e->literal = leaf_str(value); 1024 e->has_literal = 1; 1025 } 1026 } else { 1027 Node* lit = k[0]; 1028 e->literal = leaf_str(lit); 1029 e->has_literal = 1; 1030 e->loc = lit->loc; 1031 } 1032 return e; 1033 } 1034 case KIT_GRAM_META_R_kw_value: 1035 return k[1]; /* the STRING leaf */ 1036 1037 case KIT_GRAM_META_R_lex_alt: { /* lex_seq lex_alt_tail* */ 1038 AstLexSeq* seq = k[0]; 1039 Node* tails = k[1]; 1040 AstLexAlt* alt = ast_lex_alt_new(ctx, seq->loc); 1041 ast_lex_alt_append(alt, seq); 1042 for (size_t i = 0; i < tails->nitems; i++) 1043 ast_lex_alt_append(alt, (AstLexSeq*)tails->items[i]); 1044 return alt; 1045 } 1046 case KIT_GRAM_META_R_lex_alt_tail: 1047 return k[1]; 1048 case KIT_GRAM_META_R_lex_seq: { /* lex_factor lex_factor* */ 1049 AstLexNode* first = k[0]; 1050 Node* rest = k[1]; 1051 AstLexSeq* seq = ast_lex_seq_new(ctx, first->loc); 1052 ast_lex_seq_append(seq, first); 1053 for (size_t i = 0; i < rest->nitems; i++) 1054 ast_lex_seq_append(seq, (AstLexNode*)rest->items[i]); 1055 return seq; 1056 } 1057 case KIT_GRAM_META_R_lex_factor: { /* lex_primary lex_quant? */ 1058 AstLexNode* prim = k[0]; 1059 Node* quant = k[1]; 1060 if (!quant) return prim; 1061 AstLexNodeKind kind; 1062 Str count = {0}; 1063 if (quant->text[0] == '?') 1064 kind = LEX_OPT; 1065 else if (quant->text[0] == '*') 1066 kind = LEX_REP; 1067 else if (quant->text[0] == '+') { 1068 kind = LEX_REPEAT; 1069 count = (Str){.s = (char*)"1,", .len = 2}; 1070 } else { 1071 kind = LEX_REPEAT; 1072 count = (Str){.s = quant->text + 1, 1073 .len = quant->len >= 2 ? quant->len - 2 : 0}; 1074 } 1075 AstLexNode* out = ast_lex_node_new(ctx, kind, quant->loc); 1076 if (kind == LEX_REPEAT) out->value = count; 1077 out->alts = single_lex_node_alt(ctx, prim); 1078 return out; 1079 } 1080 case KIT_GRAM_META_R_lex_quant: 1081 /* "?" | "*" | "+" | REPEAT — pass the quantifier token to lex_factor. */ 1082 return k[0]; 1083 case KIT_GRAM_META_R_lex_primary: { 1084 Node* tok = k[0]; 1085 AstLexNode* node; 1086 switch (prod) { 1087 case 0: /* STRING */ 1088 node = ast_lex_node_new(ctx, LEX_LITERAL, tok->loc); 1089 node->value = leaf_str(tok); 1090 node->scalar_value = tok->scalar_text ? (Str){.s = tok->scalar_text, 1091 .len = tok->scalar_len} 1092 : leaf_str(tok); 1093 return node; 1094 case 1: /* CHAR_CLASS */ 1095 node = ast_lex_node_new(ctx, LEX_CLASS, tok->loc); 1096 node->value = leaf_str(tok); 1097 return node; 1098 case 2: /* UNICODE_PROP */ 1099 node = ast_lex_node_new(ctx, LEX_PROP, tok->loc); 1100 node->value = leaf_str(tok); 1101 return node; 1102 case 3: { /* SHORTHAND "\d" -> class "[\d]" */ 1103 node = ast_lex_node_new(ctx, LEX_CLASS, tok->loc); 1104 char* raw = xasprintf(ctx, "[%s]", tok->text); 1105 node->value = (Str){.s = raw, .len = strlen(raw)}; 1106 return node; 1107 } 1108 case 4: /* "." */ 1109 return ast_lex_node_new(ctx, LEX_ANY, tok->loc); 1110 case 5: /* NAME */ 1111 node = ast_lex_node_new(ctx, LEX_NAME, tok->loc); 1112 node->value = leaf_str(tok); 1113 return node; 1114 case 6: /* "(" lex_alt ")" */ 1115 node = ast_lex_node_new(ctx, LEX_GROUP, ((AstLexAlt*)k[1])->loc); 1116 node->alts = (AstLexAlt*)k[1]; 1117 return node; 1118 default: { /* ANCHOR: tag by the last byte (start A/^, end z/$) */ 1119 node = ast_lex_node_new(ctx, LEX_ANCHOR, tok->loc); 1120 node->value = 1121 (Str){.s = tok->len ? tok->text + tok->len - 1 : tok->text, 1122 .len = tok->len ? 1 : 0}; 1123 return node; 1124 } 1125 } 1126 } 1127 1128 case KIT_GRAM_META_R_rule: { /* NAME "=" alt ";" */ 1129 Node* name = k[0]; 1130 AstRule* rule = xcalloc(ctx, 1, sizeof *rule); 1131 if (!rule) die_oom(ctx); 1132 rule->name = name->text; 1133 rule->loc = name->loc; 1134 rule->alts = (AstAlt*)k[2]; 1135 parsed_add_rule(pu->pg, rule); 1136 return NULL; 1137 } 1138 case KIT_GRAM_META_R_alt: { /* seq alt_tail* */ 1139 AstSeq* seq = k[0]; 1140 Node* tails = k[1]; 1141 AstAlt* alt = ast_alt_new(ctx, seq->loc); 1142 ast_alt_append(alt, seq); 1143 for (size_t i = 0; i < tails->nitems; i++) 1144 ast_alt_append(alt, (AstSeq*)tails->items[i]); 1145 return alt; 1146 } 1147 case KIT_GRAM_META_R_alt_tail: 1148 return k[1]; 1149 case KIT_GRAM_META_R_seq: { /* "%empty" | factor* */ 1150 if (prod == 0) { 1151 Node* empty = k[0]; 1152 return ast_seq_new(ctx, empty->loc); 1153 } 1154 Node* factors = k[0]; 1155 Loc loc = factors->nitems ? ((AstNode*)factors->items[0])->loc : (Loc){0}; 1156 AstSeq* seq = ast_seq_new(ctx, loc); 1157 for (size_t i = 0; i < factors->nitems; i++) 1158 ast_seq_append(seq, (AstNode*)factors->items[i]); 1159 return seq; 1160 } 1161 case KIT_GRAM_META_R_factor: { /* primary quant? */ 1162 AstNode* prim = k[0]; 1163 Node* quant = k[1]; 1164 if (!quant) return prim; 1165 AstNode* out = ast_node_new( 1166 ctx, quant->text[0] == '?' ? AST_OPT : AST_REP, quant->loc); 1167 out->alts = single_node_alt(ctx, prim); 1168 return out; 1169 } 1170 case KIT_GRAM_META_R_primary: { 1171 Node* tok = k[0]; 1172 AstNode* node; 1173 switch (prod) { 1174 case 0: /* NAME */ 1175 node = ast_node_new(ctx, AST_NAME, tok->loc); 1176 node->value = leaf_str(tok); 1177 return node; 1178 case 1: /* STRING */ 1179 node = ast_node_new(ctx, AST_LITERAL, tok->loc); 1180 node->value = leaf_str(tok); 1181 return node; 1182 default: /* "(" alt ")" */ 1183 node = ast_node_new(ctx, AST_GROUP, ((AstAlt*)k[1])->loc); 1184 node->alts = (AstAlt*)k[1]; 1185 return node; 1186 } 1187 } 1188 case KIT_GRAM_META_R_quant: 1189 /* "?" | "*" — pass the quantifier token to factor. */ 1190 return k[0]; 1191 } 1192 return NULL; 1193 } 1194 1195 static KitGramErrorAction cb_error(void* ud, const KitGramError* e) { 1196 ParseUd* pu = ud; 1197 Meta* m = &pu->meta; 1198 m->err_seen = 1; 1199 m->err_tok = e->found.kind; 1200 m->err_rule = e->in_rule; 1201 m->err_line = e->found.line; 1202 m->err_col = e->found.col; 1203 m->nexpected = 1204 e->nexpected < META_MAX_EXPECTED ? e->nexpected : META_MAX_EXPECTED; 1205 m->expected_truncated = e->nexpected > META_MAX_EXPECTED; 1206 for (size_t i = 0; i < m->nexpected; i++) m->expected[i] = e->expected[i]; 1207 m->err_lexeme_len = 0; 1208 if (e->found.lexeme && e->found.len) { 1209 m->err_lexeme_len = e->found.len < sizeof m->err_lexeme - 1 1210 ? e->found.len 1211 : sizeof m->err_lexeme - 1; 1212 memcpy(m->err_lexeme, e->found.lexeme, m->err_lexeme_len); 1213 } 1214 m->err_lexeme[m->err_lexeme_len] = '\0'; 1215 return KIT_GRAM_ABORT; 1216 } 1217 1218 static void append_str(char* buf, size_t cap, size_t* off, const char* s) { 1219 if (!buf || !cap || !s) return; 1220 while (*s && *off + 1 < cap) buf[(*off)++] = *s++; 1221 buf[*off < cap ? *off : cap - 1] = '\0'; 1222 } 1223 1224 static void append_fmt(char* buf, size_t cap, size_t* off, const char* fmt, 1225 ...) { 1226 if (!buf || !cap || *off >= cap) return; 1227 va_list ap; 1228 va_start(ap, fmt); 1229 int n = vsnprintf(buf + *off, cap - *off, fmt, ap); 1230 va_end(ap); 1231 if (n < 0) return; 1232 size_t wrote = (size_t)n; 1233 if (wrote >= cap - *off) 1234 *off = cap - 1; 1235 else 1236 *off += wrote; 1237 } 1238 1239 static void append_quoted(char* buf, size_t cap, size_t* off, const char* s, 1240 size_t n) { 1241 append_str(buf, cap, off, "\""); 1242 for (size_t i = 0; i < n; i++) { 1243 unsigned char ch = (unsigned char)s[i]; 1244 switch (ch) { 1245 case '\\': 1246 append_str(buf, cap, off, "\\\\"); 1247 break; 1248 case '"': 1249 append_str(buf, cap, off, "\\\""); 1250 break; 1251 case '\n': 1252 append_str(buf, cap, off, "\\n"); 1253 break; 1254 case '\r': 1255 append_str(buf, cap, off, "\\r"); 1256 break; 1257 case '\t': 1258 append_str(buf, cap, off, "\\t"); 1259 break; 1260 default: 1261 if (ch >= 32 && ch <= 126) { 1262 char c[2] = {(char)ch, 0}; 1263 append_str(buf, cap, off, c); 1264 } else { 1265 append_fmt(buf, cap, off, "\\x%02x", ch); 1266 } 1267 break; 1268 } 1269 } 1270 append_str(buf, cap, off, "\""); 1271 } 1272 1273 static void append_token_desc(char* buf, size_t cap, size_t* off, 1274 const KitGramParser* ps, KitGramTokenKind kind, 1275 const char* lexeme, size_t len) { 1276 if (kind == KIT_GRAM_META_TOK_EOF) { 1277 append_str(buf, cap, off, "end of input"); 1278 return; 1279 } 1280 const char* name = kit_gram_parser_tok_name(ps, kind); 1281 if (strcmp(name, "?") == 0) 1282 append_fmt(buf, cap, off, "invalid token %u", (unsigned)kind); 1283 else 1284 append_str(buf, cap, off, name); 1285 if (lexeme && len) { 1286 append_str(buf, cap, off, " "); 1287 append_quoted(buf, cap, off, lexeme, len); 1288 } 1289 } 1290 1291 static void format_parse_error(char* buf, size_t cap, const KitGramParser* ps, 1292 const Meta* m) { 1293 size_t off = 0; 1294 append_str(buf, cap, &off, "parse error"); 1295 const char* rule = kit_gram_parser_rule_name(ps, m->err_rule); 1296 if (strcmp(rule, "?") != 0) { 1297 append_str(buf, cap, &off, " in "); 1298 append_str(buf, cap, &off, rule); 1299 } 1300 append_str(buf, cap, &off, ": unexpected "); 1301 append_token_desc(buf, cap, &off, ps, m->err_tok, m->err_lexeme, 1302 m->err_lexeme_len); 1303 if (m->nexpected) { 1304 append_str(buf, cap, &off, ", expected "); 1305 for (size_t i = 0; i < m->nexpected; i++) { 1306 if (i) append_str(buf, cap, &off, ", "); 1307 append_token_desc(buf, cap, &off, ps, m->expected[i], NULL, 0); 1308 } 1309 if (m->expected_truncated) append_str(buf, cap, &off, ", ..."); 1310 } 1311 } 1312 1313 typedef struct { 1314 GramgenContext* ctx; 1315 const char* s; 1316 size_t len, i; 1317 uint32_t line, col; 1318 char err_msg[120]; 1319 uint32_t err_line, err_col; 1320 } StringDecoder; 1321 1322 static int sd_peek(const StringDecoder* sd, size_t n) { 1323 size_t j = sd->i + n; 1324 return j < sd->len ? (unsigned char)sd->s[j] : 0; 1325 } 1326 1327 static int sd_advance(StringDecoder* sd) { 1328 int c = sd_peek(sd, 0); 1329 if (!c) return 0; 1330 sd->i++; 1331 if (c == '\n') { 1332 sd->line++; 1333 sd->col = 1; 1334 } else { 1335 sd->col++; 1336 } 1337 return c; 1338 } 1339 1340 static void sd_error(StringDecoder* sd, uint32_t line, uint32_t col, 1341 const char* msg) { 1342 sd->err_line = line; 1343 sd->err_col = col; 1344 snprintf(sd->err_msg, sizeof sd->err_msg, "%s", msg); 1345 } 1346 1347 static void decoded_string_append(GramgenContext* ctx, char** buf, size_t* cap, 1348 size_t* len, unsigned char byte) { 1349 if (*len + 1 >= *cap) { 1350 *cap *= 2; 1351 *buf = xrealloc(ctx, *buf, *cap); 1352 } 1353 (*buf)[(*len)++] = (char)byte; 1354 } 1355 1356 static int sd_read_hex(StringDecoder* sd, int n, uint32_t line, uint32_t col, 1357 const char* what, uint32_t* out) { 1358 uint32_t value = 0; 1359 for (int i = 0; i < n; i++) { 1360 int c = sd_peek(sd, 0); 1361 if (!c) { 1362 char msg[64]; 1363 snprintf(msg, sizeof msg, "short %s escape", what); 1364 sd_error(sd, line, col, msg); 1365 return 0; 1366 } 1367 int digit = hex_val(c); 1368 if (digit < 0) { 1369 char msg[64]; 1370 snprintf(msg, sizeof msg, "bad %s escape", what); 1371 sd_error(sd, sd->line, sd->col, msg); 1372 return 0; 1373 } 1374 sd_advance(sd); 1375 value = (value << 4) | (uint32_t)digit; 1376 } 1377 *out = value; 1378 return 1; 1379 } 1380 1381 static int sd_read_braced_hex(StringDecoder* sd, uint32_t line, uint32_t col, 1382 const char* what, uint32_t* out) { 1383 if (sd_peek(sd, 0) != '{') { 1384 char msg[64]; 1385 snprintf(msg, sizeof msg, "bad %s escape", what); 1386 sd_error(sd, line, col, msg); 1387 return 0; 1388 } 1389 sd_advance(sd); 1390 uint32_t value = 0; 1391 size_t ndigits = 0; 1392 while (1) { 1393 int c = sd_peek(sd, 0); 1394 if (!c) { 1395 char msg[80]; 1396 snprintf(msg, sizeof msg, "unterminated %s escape", what); 1397 sd_error(sd, line, col, msg); 1398 return 0; 1399 } 1400 if (c == '\n') { 1401 char msg[80]; 1402 snprintf(msg, sizeof msg, "newline in %s escape", what); 1403 sd_error(sd, sd->line, sd->col, msg); 1404 return 0; 1405 } 1406 if (c == '}') { 1407 sd_advance(sd); 1408 if (!ndigits) { 1409 char msg[80]; 1410 snprintf(msg, sizeof msg, "empty %s escape", what); 1411 sd_error(sd, line, col, msg); 1412 return 0; 1413 } 1414 *out = value; 1415 return 1; 1416 } 1417 int digit = hex_val(c); 1418 if (digit < 0) { 1419 char msg[64]; 1420 snprintf(msg, sizeof msg, "bad %s escape", what); 1421 sd_error(sd, sd->line, sd->col, msg); 1422 return 0; 1423 } 1424 sd_advance(sd); 1425 ndigits++; 1426 value = (value << 4) | (uint32_t)digit; 1427 if (value > 0x10FFFFu) { 1428 char msg[80]; 1429 snprintf(msg, sizeof msg, "invalid Unicode scalar U+%04X", 1430 (unsigned)value); 1431 sd_error(sd, line, col, msg); 1432 return 0; 1433 } 1434 } 1435 } 1436 1437 static int decoded_string_append_utf8(StringDecoder* sd, char** buf, 1438 size_t* cap, size_t* len, uint32_t cp, 1439 uint32_t line, uint32_t col) { 1440 if (cp > 0x10FFFFu || (cp >= 0xD800u && cp <= 0xDFFFu)) { 1441 char msg[80]; 1442 snprintf(msg, sizeof msg, "invalid Unicode scalar U+%04X", (unsigned)cp); 1443 sd_error(sd, line, col, msg); 1444 return 0; 1445 } 1446 if (cp <= 0x7Fu) { 1447 decoded_string_append(sd->ctx, buf, cap, len, (unsigned char)cp); 1448 } else if (cp <= 0x7FFu) { 1449 decoded_string_append(sd->ctx, buf, cap, len, 1450 (unsigned char)(0xC0u | (cp >> 6))); 1451 decoded_string_append(sd->ctx, buf, cap, len, 1452 (unsigned char)(0x80u | (cp & 0x3Fu))); 1453 } else if (cp <= 0xFFFFu) { 1454 decoded_string_append(sd->ctx, buf, cap, len, 1455 (unsigned char)(0xE0u | (cp >> 12))); 1456 decoded_string_append(sd->ctx, buf, cap, len, 1457 (unsigned char)(0x80u | ((cp >> 6) & 0x3Fu))); 1458 decoded_string_append(sd->ctx, buf, cap, len, 1459 (unsigned char)(0x80u | (cp & 0x3Fu))); 1460 } else { 1461 decoded_string_append(sd->ctx, buf, cap, len, 1462 (unsigned char)(0xF0u | (cp >> 18))); 1463 decoded_string_append(sd->ctx, buf, cap, len, 1464 (unsigned char)(0x80u | ((cp >> 12) & 0x3Fu))); 1465 decoded_string_append(sd->ctx, buf, cap, len, 1466 (unsigned char)(0x80u | ((cp >> 6) & 0x3Fu))); 1467 decoded_string_append(sd->ctx, buf, cap, len, 1468 (unsigned char)(0x80u | (cp & 0x3Fu))); 1469 } 1470 return 1; 1471 } 1472 1473 static int decode_string_token(GramgenContext* ctx, KitGramToken* tok, 1474 char** owned, char* err_msg, size_t err_cap, 1475 uint32_t* err_line, uint32_t* err_col) { 1476 StringDecoder sd = { 1477 .ctx = ctx, 1478 .s = tok->lexeme, 1479 .len = tok->len, 1480 .line = tok->line, 1481 .col = tok->col, 1482 }; 1483 uint32_t line = sd.line, col = sd.col; 1484 if (sd_peek(&sd, 0) != '"') { 1485 snprintf(err_msg, err_cap, "malformed string literal"); 1486 *err_line = line; 1487 *err_col = col; 1488 return 0; 1489 } 1490 sd_advance(&sd); 1491 1492 size_t cap = 32, len = 0; 1493 size_t scalar_cap = 32, scalar_len = 0; 1494 char* buf = xmalloc(ctx, cap); 1495 char* scalar = xmalloc(ctx, scalar_cap); 1496 while (1) { 1497 int c = sd_peek(&sd, 0); 1498 if (!c) { 1499 xfree(ctx, buf); 1500 xfree(ctx, scalar); 1501 sd_error(&sd, line, col, "unterminated string literal"); 1502 break; 1503 } 1504 if (c == '\n') { 1505 xfree(ctx, buf); 1506 xfree(ctx, scalar); 1507 sd_error(&sd, sd.line, sd.col, "newline in string literal"); 1508 break; 1509 } 1510 if (c == '"') { 1511 sd_advance(&sd); 1512 char prefix[32]; 1513 int pn = snprintf(prefix, sizeof prefix, "%zu:", len); 1514 if (pn < 0 || (size_t)pn >= sizeof prefix) die_oom(ctx); 1515 char* payload = xmalloc(ctx, (size_t)pn + len + scalar_len); 1516 memcpy(payload, prefix, (size_t)pn); 1517 memcpy(payload + pn, buf, len); 1518 memcpy(payload + pn + len, scalar, scalar_len); 1519 xfree(ctx, buf); 1520 xfree(ctx, scalar); 1521 tok->lexeme = payload; 1522 tok->len = (size_t)pn + len + scalar_len; 1523 *owned = payload; 1524 return 1; 1525 } 1526 if (c == '\\') { 1527 sd_advance(&sd); 1528 uint32_t esc_line = sd.line, esc_col = sd.col; 1529 int e = sd_peek(&sd, 0); 1530 if (!e) { 1531 xfree(ctx, buf); 1532 xfree(ctx, scalar); 1533 sd_error(&sd, line, col, "unterminated string literal"); 1534 break; 1535 } 1536 if (e == '\n') { 1537 xfree(ctx, buf); 1538 xfree(ctx, scalar); 1539 sd_error(&sd, sd.line, sd.col, "newline in string literal"); 1540 break; 1541 } 1542 sd_advance(&sd); 1543 switch (e) { 1544 case 'n': 1545 decoded_string_append(ctx, &buf, &cap, &len, '\n'); 1546 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, '\n'); 1547 break; 1548 case 'r': 1549 decoded_string_append(ctx, &buf, &cap, &len, '\r'); 1550 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, '\r'); 1551 break; 1552 case 't': 1553 decoded_string_append(ctx, &buf, &cap, &len, '\t'); 1554 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, '\t'); 1555 break; 1556 case '0': 1557 decoded_string_append(ctx, &buf, &cap, &len, '\0'); 1558 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, '\0'); 1559 break; 1560 case '"': 1561 decoded_string_append(ctx, &buf, &cap, &len, '"'); 1562 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, '"'); 1563 break; 1564 case '\\': 1565 decoded_string_append(ctx, &buf, &cap, &len, '\\'); 1566 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, '\\'); 1567 break; 1568 case 'x': { 1569 uint32_t value = 0; 1570 if (!sd_read_hex(&sd, 2, esc_line, esc_col, "hex", &value)) { 1571 xfree(ctx, buf); 1572 xfree(ctx, scalar); 1573 break; 1574 } 1575 decoded_string_append(ctx, &buf, &cap, &len, (unsigned char)value); 1576 if (!decoded_string_append_utf8(&sd, &scalar, &scalar_cap, 1577 &scalar_len, value, esc_line, 1578 esc_col)) { 1579 xfree(ctx, buf); 1580 xfree(ctx, scalar); 1581 break; 1582 } 1583 break; 1584 } 1585 case 'u': { 1586 uint32_t value = 0; 1587 int ok = 1588 sd_peek(&sd, 0) == '{' 1589 ? sd_read_braced_hex(&sd, esc_line, esc_col, "Unicode", 1590 &value) 1591 : sd_read_hex(&sd, 4, esc_line, esc_col, "Unicode", &value); 1592 if (!ok || !decoded_string_append_utf8(&sd, &buf, &cap, &len, value, 1593 esc_line, esc_col)) { 1594 xfree(ctx, buf); 1595 xfree(ctx, scalar); 1596 break; 1597 } 1598 if (!decoded_string_append_utf8(&sd, &scalar, &scalar_cap, 1599 &scalar_len, value, esc_line, 1600 esc_col)) { 1601 xfree(ctx, buf); 1602 xfree(ctx, scalar); 1603 break; 1604 } 1605 break; 1606 } 1607 case 'U': { 1608 uint32_t value = 0; 1609 if (!sd_read_hex(&sd, 8, esc_line, esc_col, "Unicode", &value) || 1610 !decoded_string_append_utf8(&sd, &buf, &cap, &len, value, 1611 esc_line, esc_col)) { 1612 xfree(ctx, buf); 1613 xfree(ctx, scalar); 1614 break; 1615 } 1616 if (!decoded_string_append_utf8(&sd, &scalar, &scalar_cap, 1617 &scalar_len, value, esc_line, 1618 esc_col)) { 1619 xfree(ctx, buf); 1620 xfree(ctx, scalar); 1621 break; 1622 } 1623 break; 1624 } 1625 default: 1626 decoded_string_append(ctx, &buf, &cap, &len, (unsigned char)e); 1627 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, 1628 (unsigned char)e); 1629 break; 1630 } 1631 } else { 1632 unsigned char b = (unsigned char)sd_advance(&sd); 1633 decoded_string_append(ctx, &buf, &cap, &len, b); 1634 decoded_string_append(ctx, &scalar, &scalar_cap, &scalar_len, b); 1635 } 1636 if (sd.err_msg[0]) break; 1637 } 1638 1639 snprintf(err_msg, err_cap, "%s", 1640 sd.err_msg[0] ? sd.err_msg : "malformed string literal"); 1641 *err_line = sd.err_line ? sd.err_line : line; 1642 *err_col = sd.err_col ? sd.err_col : col; 1643 return 0; 1644 } 1645 1646 typedef struct { 1647 GramgenContext* ctx; 1648 char* owned; 1649 char err_msg[120]; 1650 } MetaLexHook; 1651 1652 static KitGramLexHookResult meta_token_hook(KitGramLexer* lx, void* ud, 1653 KitGramToken* tok, 1654 KitGramLexError* err) { 1655 (void)lx; 1656 MetaLexHook* hook = ud; 1657 if (tok->kind != KIT_GRAM_META_TOK_STRING) return KIT_GRAM_LEX_HOOK_KEEP; 1658 1659 char* owned = NULL; 1660 uint32_t err_line = tok->line, err_col = tok->col; 1661 if (!decode_string_token(hook->ctx, tok, &owned, hook->err_msg, 1662 sizeof hook->err_msg, &err_line, &err_col)) { 1663 if (err) { 1664 err->line = err_line; 1665 err->col = err_col; 1666 err->byte = 0; 1667 err->message = hook->err_msg; 1668 } 1669 return KIT_GRAM_LEX_HOOK_ERROR; 1670 } 1671 1672 hook->owned = owned; 1673 return KIT_GRAM_LEX_HOOK_KEEP; 1674 } 1675 1676 static size_t source_offset_for_loc(const char* text, size_t len, uint32_t line, 1677 uint32_t col) { 1678 uint32_t cur_line = 1, cur_col = 1; 1679 for (size_t i = 0; i < len; i++) { 1680 if (cur_line == line && cur_col == col) return i; 1681 if (text[i] == '\n') { 1682 cur_line++; 1683 cur_col = 1; 1684 } else { 1685 cur_col++; 1686 } 1687 } 1688 return len; 1689 } 1690 1691 static void source_advance(const char* text, size_t len, size_t* i, 1692 uint32_t* line, uint32_t* col) { 1693 if (*i >= len) return; 1694 unsigned char c = (unsigned char)text[*i]; 1695 (*i)++; 1696 if (c == '\n') { 1697 (*line)++; 1698 *col = 1; 1699 } else { 1700 (*col)++; 1701 } 1702 } 1703 1704 static int diagnose_string_lex_error(const char* text, size_t len, size_t off, 1705 uint32_t line, uint32_t col, char* msg, 1706 size_t msg_cap, uint32_t* err_line, 1707 uint32_t* err_col) { 1708 if (off >= len || text[off] != '"') return 0; 1709 size_t i = off; 1710 uint32_t cur_line = line, cur_col = col; 1711 source_advance(text, len, &i, &cur_line, &cur_col); 1712 while (i < len) { 1713 unsigned char c = (unsigned char)text[i]; 1714 if (c == '\n') { 1715 snprintf(msg, msg_cap, "newline in string literal"); 1716 *err_line = cur_line; 1717 *err_col = cur_col; 1718 return 1; 1719 } 1720 if (c == '"') return 0; 1721 if (c == '\\') { 1722 source_advance(text, len, &i, &cur_line, &cur_col); 1723 if (i >= len) break; 1724 if (text[i] == '\n') { 1725 snprintf(msg, msg_cap, "newline in string literal"); 1726 *err_line = cur_line; 1727 *err_col = cur_col; 1728 return 1; 1729 } 1730 } 1731 source_advance(text, len, &i, &cur_line, &cur_col); 1732 } 1733 snprintf(msg, msg_cap, "unterminated string literal"); 1734 *err_line = line; 1735 *err_col = col; 1736 return 1; 1737 } 1738 1739 static int diagnose_char_class_lex_error(const char* text, size_t len, 1740 size_t off, uint32_t line, 1741 uint32_t col, char* msg, 1742 size_t msg_cap, uint32_t* err_line, 1743 uint32_t* err_col) { 1744 if (off >= len || text[off] != '[') return 0; 1745 size_t start = off; 1746 size_t i = off; 1747 uint32_t cur_line = line, cur_col = col; 1748 source_advance(text, len, &i, &cur_line, &cur_col); 1749 while (i < len) { 1750 unsigned char c = (unsigned char)text[i]; 1751 if (c == '\n') { 1752 snprintf(msg, msg_cap, "newline in character class"); 1753 *err_line = cur_line; 1754 *err_col = cur_col; 1755 return 1; 1756 } 1757 if (c == '\\') { 1758 source_advance(text, len, &i, &cur_line, &cur_col); 1759 if (i >= len) break; 1760 if (text[i] == '\n') { 1761 snprintf(msg, msg_cap, "newline in character class"); 1762 *err_line = cur_line; 1763 *err_col = cur_col; 1764 return 1; 1765 } 1766 source_advance(text, len, &i, &cur_line, &cur_col); 1767 continue; 1768 } 1769 if (c == ']') { 1770 size_t content = start + 1; 1771 if (content < i && text[content] == '^') content++; 1772 if (content >= i) { 1773 snprintf(msg, msg_cap, "empty character class"); 1774 *err_line = line; 1775 *err_col = col; 1776 return 1; 1777 } 1778 return 0; 1779 } 1780 source_advance(text, len, &i, &cur_line, &cur_col); 1781 } 1782 snprintf(msg, msg_cap, "unterminated character class"); 1783 *err_line = line; 1784 *err_col = col; 1785 return 1; 1786 } 1787 1788 static int diagnose_unicode_prop_lex_error(const char* text, size_t len, 1789 size_t off, uint32_t line, 1790 uint32_t col, char* msg, 1791 size_t msg_cap, uint32_t* err_line, 1792 uint32_t* err_col) { 1793 if (off + 2 >= len || text[off] != '\\' || 1794 (text[off + 1] != 'p' && text[off + 1] != 'P') || text[off + 2] != '{') 1795 return 0; 1796 1797 size_t i = off; 1798 uint32_t cur_line = line, cur_col = col; 1799 source_advance(text, len, &i, &cur_line, &cur_col); 1800 source_advance(text, len, &i, &cur_line, &cur_col); 1801 source_advance(text, len, &i, &cur_line, &cur_col); 1802 size_t n = 0; 1803 while (i < len) { 1804 unsigned char c = (unsigned char)text[i]; 1805 if (c == '\n') { 1806 snprintf(msg, msg_cap, "newline in Unicode property escape"); 1807 *err_line = cur_line; 1808 *err_col = cur_col; 1809 return 1; 1810 } 1811 if (c == '}') { 1812 if (!n) { 1813 snprintf(msg, msg_cap, "empty Unicode property escape"); 1814 *err_line = line; 1815 *err_col = col; 1816 return 1; 1817 } 1818 return 0; 1819 } 1820 source_advance(text, len, &i, &cur_line, &cur_col); 1821 n++; 1822 } 1823 snprintf(msg, msg_cap, "unterminated Unicode property escape"); 1824 *err_line = line; 1825 *err_col = col; 1826 return 1; 1827 } 1828 1829 static int set_unknown_decl_diag(const char* text, size_t len, size_t off, 1830 char* msg, size_t msg_cap) { 1831 if (off >= len || text[off] != '%') return 0; 1832 size_t i = off + 1; 1833 if (i >= len || !(isalpha((unsigned char)text[i]) || text[i] == '_')) { 1834 snprintf(msg, msg_cap, "expected declaration name after '%%'"); 1835 return 1; 1836 } 1837 while (i < len && (isalnum((unsigned char)text[i]) || text[i] == '_')) i++; 1838 snprintf(msg, msg_cap, "unknown declaration %.*s", (int)(i - off), 1839 text + off); 1840 return 1; 1841 } 1842 1843 static int token_decl_has_ident_suffix(const char* text, size_t len, 1844 const KitGramToken* tok, char* msg, 1845 size_t msg_cap, uint32_t* line, 1846 uint32_t* col) { 1847 if (tok->kind != KIT_GRAM_META_TOK_TOKEN_DECL && 1848 tok->kind != KIT_GRAM_META_TOK_EMPTY_DECL && 1849 tok->kind != KIT_GRAM_META_TOK_PRATT_DECL && 1850 tok->kind != KIT_GRAM_META_TOK_LEX_DECL && 1851 tok->kind != KIT_GRAM_META_TOK_SKIP_DECL) 1852 return 0; 1853 size_t off = source_offset_for_loc(text, len, tok->line, tok->col); 1854 if (off >= len || off + tok->len >= len) return 0; 1855 unsigned char next = (unsigned char)text[off + tok->len]; 1856 if (!(isalnum(next) || next == '_')) return 0; 1857 if (!set_unknown_decl_diag(text, len, off, msg, msg_cap)) return 0; 1858 *line = tok->line; 1859 *col = tok->col; 1860 return 1; 1861 } 1862 1863 static void set_generated_lex_diag(GramDiag* diag, const char* path, 1864 const char* text, size_t len, 1865 const KitGramLexError* err) { 1866 if (!diag) return; 1867 diag->path = path; 1868 diag->line = err->line; 1869 diag->col = err->col; 1870 1871 char msg[120]; 1872 uint32_t msg_line = err->line, msg_col = err->col; 1873 size_t off = source_offset_for_loc(text, len, err->line, err->col); 1874 if ((diagnose_string_lex_error(text, len, off, err->line, err->col, msg, 1875 sizeof msg, &msg_line, &msg_col)) || 1876 (diagnose_char_class_lex_error(text, len, off, err->line, err->col, msg, 1877 sizeof msg, &msg_line, &msg_col)) || 1878 (diagnose_unicode_prop_lex_error(text, len, off, err->line, err->col, msg, 1879 sizeof msg, &msg_line, &msg_col)) || 1880 (set_unknown_decl_diag(text, len, off, msg, sizeof msg))) { 1881 diag->line = msg_line; 1882 diag->col = msg_col; 1883 snprintf(diag->message, sizeof diag->message, "%s", msg); 1884 return; 1885 } 1886 1887 if (strcmp(err->message, "invalid token") == 0 && off < len) { 1888 unsigned char c = (unsigned char)text[off]; 1889 if (isprint(c)) 1890 snprintf(diag->message, sizeof diag->message, "unexpected character '%c'", 1891 c); 1892 else 1893 snprintf(diag->message, sizeof diag->message, "unexpected byte 0x%02x", 1894 c); 1895 return; 1896 } 1897 snprintf(diag->message, sizeof diag->message, "%s", err->message); 1898 } 1899 1900 static void source_end_loc(const char* text, size_t len, uint32_t* line, 1901 uint32_t* col) { 1902 uint32_t out_line = 1, out_col = 1; 1903 for (size_t i = 0; i < len; i++) { 1904 if (text[i] == '\n') { 1905 out_line++; 1906 out_col = 1; 1907 } else { 1908 out_col++; 1909 } 1910 } 1911 *line = out_line; 1912 *col = out_col; 1913 } 1914 1915 /* Meta-parser stack sizing. Real grammars touch only a few dozen control slots; 1916 * nesting (groups, quantifiers, pratt blocks) grows the stacks ~9 ctl per 1917 * level. Rather than reserve a fixed worst case (live for the whole compile), 1918 * we start small and double on overflow. META_STACK_MAX is a backstop against 1919 * pathological input (a file that is almost entirely nested punctuation). */ 1920 enum { META_STACK_START = 256, META_STACK_MAX = 1u << 20 }; 1921 1922 /* One meta lexer+parser pass with the given stack cap. Returns 1 on accept 1923 * (*result set). On failure returns 0: if *overflowed is set the stacks were 1924 * too small for this grammar's nesting (the caller may retry with a larger 1925 * cap), otherwise diag holds a genuine lex/parse error. */ 1926 static int parse_text_attempt(GramgenContext* ctx, const char* path, 1927 const char* text, size_t len, size_t cap, 1928 ParsedGrammar** result, GramDiag* diag, 1929 bool* overflowed) { 1930 *overflowed = false; 1931 ParseUd pu; 1932 memset(&pu, 0, sizeof pu); 1933 pu.ctx = ctx; 1934 pu.path = path; 1935 pu.pg = xcalloc(ctx, 1, sizeof *pu.pg); 1936 if (!pu.pg) die_oom(ctx); 1937 pu.pg->ctx = ctx; 1938 KitGramActions acts = { 1939 .reduce = cb_reduce, 1940 .lift_token = cb_lift, 1941 .list_empty = cb_list_empty, 1942 .list_push = cb_list_push, 1943 .opt_none = cb_opt_none, 1944 .opt_some = cb_opt_some, 1945 .on_error = cb_error, 1946 }; 1947 KitGramParser ps; 1948 /* Stacks live in the generator arena (reclaimed by mem_release on the normal 1949 * and longjmp-error paths), not on the C stack: at 96 B/slot even a modest 1950 * cap would be a large stack frame. `cap` is grown by the caller on overflow. 1951 */ 1952 KitGramSlot* ctl = xmalloc(ctx, cap * sizeof *ctl); 1953 KitGramSem* vals = xmalloc(ctx, cap * sizeof *vals); 1954 KitGramConfig cfg = {.actions = &acts, 1955 .ud = &pu, 1956 .recover = false, 1957 .ctl_stack = ctl, 1958 .ctl_cap = cap, 1959 .val_stack = vals, 1960 .val_cap = cap}; 1961 kit_gram_meta_parser_init(&ps, &cfg); 1962 1963 KitGramLexInput in; 1964 kit_gram_lex_input_init(&in, &(KitGramLexInputConfig){0}); 1965 KitGramLexInputSpan span = { 1966 .bytes = (const unsigned char*)text, 1967 .len = len, 1968 }; 1969 KitGramLexer lx; 1970 MetaLexHook hook = {.ctx = ctx}; 1971 KitGramLexConfig lex_cfg = { 1972 .token_hook = meta_token_hook, 1973 .hook_ud = &hook, 1974 }; 1975 kit_gram_meta_lexer_init(&lx, &in, &lex_cfg); 1976 kit_gram_lex_input_push(&in, &span); 1977 kit_gram_lex_input_finish(&in); 1978 1979 uint32_t eof_line = 1, eof_col = 1; 1980 source_end_loc(text, len, &eof_line, &eof_col); 1981 1982 KitGramToken t; 1983 for (;;) { 1984 KitGramLexStatus lst = kit_gram_lexer_next(&lx, &t); 1985 if (lst == KIT_GRAM_LEX_EOF) break; 1986 if (lst == KIT_GRAM_LEX_NEED_MORE) { 1987 if (diag) { 1988 diag->path = path; 1989 diag->line = eof_line; 1990 diag->col = eof_col; 1991 snprintf(diag->message, sizeof diag->message, 1992 "lexer needs more input after end of input"); 1993 } 1994 return 0; 1995 } 1996 if (lst == KIT_GRAM_LEX_ERROR) { 1997 set_generated_lex_diag(diag, path, text, len, kit_gram_lex_input_error(&in)); 1998 return 0; 1999 } 2000 2001 char* owned = hook.owned; 2002 hook.owned = NULL; 2003 if (t.kind != KIT_GRAM_META_TOK_STRING) { 2004 char msg[120]; 2005 uint32_t err_line = t.line, err_col = t.col; 2006 if (token_decl_has_ident_suffix(text, len, &t, msg, sizeof msg, &err_line, 2007 &err_col)) { 2008 if (diag) { 2009 diag->path = path; 2010 diag->line = err_line; 2011 diag->col = err_col; 2012 snprintf(diag->message, sizeof diag->message, "%s", msg); 2013 } 2014 xfree(ctx, owned); 2015 return 0; 2016 } 2017 } 2018 2019 KitGramStatus st = kit_gram_parser_push(&ps, t); 2020 xfree(ctx, owned); 2021 if (st == KIT_GRAM_PARSE_ERROR) { 2022 if (kit_gram_parser_overflowed(&ps)) { 2023 *overflowed = true; 2024 return 0; 2025 } 2026 if (diag) { 2027 diag->path = path; 2028 diag->line = pu.meta.err_line ? pu.meta.err_line : t.line; 2029 diag->col = pu.meta.err_col ? pu.meta.err_col : t.col; 2030 if (pu.meta.err_seen) 2031 format_parse_error(diag->message, sizeof diag->message, &ps, 2032 &pu.meta); 2033 else 2034 snprintf(diag->message, sizeof diag->message, "parse error"); 2035 } 2036 return 0; 2037 } 2038 } 2039 if (kit_gram_parser_finish(&ps) != KIT_GRAM_PARSE_ACCEPT) { 2040 if (kit_gram_parser_overflowed(&ps)) { 2041 *overflowed = true; 2042 return 0; 2043 } 2044 if (diag) { 2045 diag->path = path; 2046 diag->line = pu.meta.err_line ? pu.meta.err_line : eof_line; 2047 diag->col = pu.meta.err_col ? pu.meta.err_col : eof_col; 2048 if (pu.meta.err_seen) 2049 format_parse_error(diag->message, sizeof diag->message, &ps, &pu.meta); 2050 else 2051 snprintf(diag->message, sizeof diag->message, 2052 "parse error at end of input"); 2053 } 2054 return 0; 2055 } 2056 (void)kit_gram_parser_result( 2057 &ps); /* items self-appended to pu.pg as they reduced */ 2058 *result = pu.pg; 2059 return 1; 2060 } 2061 2062 /* Parse `text`, growing the meta-parser stacks on overflow. A failed attempt's 2063 * arena allocations are abandoned (reclaimed with everything else at 2064 * mem_release); the common case fits the initial cap and runs exactly one 2065 * attempt. */ 2066 static int parse_text(GramgenContext* ctx, const char* path, const char* text, 2067 size_t len, ParsedGrammar** result, 2068 GramDiag* diag) { 2069 for (size_t cap = META_STACK_START;; cap *= 2) { 2070 bool overflowed = false; 2071 if (parse_text_attempt(ctx, path, text, len, cap, result, diag, 2072 &overflowed)) 2073 return 1; 2074 if (!overflowed) return 0; 2075 if (cap >= META_STACK_MAX) { 2076 if (diag) { 2077 uint32_t line = 1, col = 1; 2078 source_end_loc(text, len, &line, &col); 2079 diag->path = path; 2080 diag->line = line; 2081 diag->col = col; 2082 snprintf( 2083 diag->message, sizeof diag->message, 2084 "grammar too deeply nested (meta-parser stack exceeded %u slots)", 2085 (unsigned)META_STACK_MAX); 2086 } 2087 return 0; 2088 } 2089 } 2090 } 2091 2092 /* Render a compiled ParsedGrammar to the `(grammar ...)` s-expression: token 2093 * decls, then lex blocks, then rules — matching gen/gramgen.py's 2094 * dump_ast_sexpr. 2095 */ 2096 static char* dump_grammar_text(GramgenContext* ctx, const ParsedGrammar* pg, 2097 size_t* len_out) { 2098 Buf out; 2099 buf_init(ctx, &out); 2100 buf_append(&out, "(grammar\n"); 2101 for (size_t i = 0; i < pg->ntoken_decls; i++) { 2102 const TokenDecl* d = pg->token_decls[i]; 2103 buf_append(&out, " (token "); 2104 buf_append(&out, d->name); 2105 buf_append(&out, " "); 2106 quote_str_into(&out, d->literal); 2107 buf_append(&out, ")\n"); 2108 } 2109 for (size_t i = 0; i < pg->nlex_blocks; i++) { 2110 buf_append(&out, " "); 2111 ast_lex_block_sexpr(&out, pg->lex_blocks[i]); 2112 buf_append(&out, "\n"); 2113 } 2114 for (size_t i = 0; i < pg->nrules; i++) { 2115 buf_append(&out, " "); 2116 ast_rule_sexpr(&out, pg->rules[i]); 2117 buf_append(&out, "\n"); 2118 } 2119 buf_append(&out, ")"); 2120 char* exact = xstrndup(ctx, out.s, out.len); 2121 if (len_out) *len_out = out.len; 2122 xfree(ctx, out.s); 2123 return exact; 2124 } 2125 2126 KitStatus kit_gram_dump_sexpr(const KitContext* kit, KitSlice text, 2127 KitSlice path, const KitGramOptions* opts, 2128 KitWriter* out) { 2129 if (!kit || !kit->heap || !opts || !out) return KIT_INVALID; 2130 if (!text.s && text.len) return KIT_INVALID; 2131 2132 GramgenContext ctx; 2133 memset(&ctx, 0, sizeof ctx); 2134 ctx.kit = kit; 2135 ctx.mem.heap = kit->heap; 2136 ctx.diag = &ctx.diag_storage; 2137 ctx.can_jump = 1; 2138 2139 if (setjmp(ctx.jmp)) { 2140 gram_flush_diag(&ctx); /* before release: diag->path may point into arena */ 2141 mem_release(&ctx.mem); 2142 return KIT_ERR; 2143 } 2144 2145 const char* use_path = 2146 path.len ? xstrndup(&ctx, path.s, path.len) : "<memory>"; 2147 ParsedGrammar* pg = NULL; 2148 if (!parse_text(&ctx, use_path, text.len ? text.s : "", text.len, &pg, 2149 ctx.diag)) { 2150 gram_flush_diag(&ctx); 2151 mem_release(&ctx.mem); 2152 return KIT_ERR; 2153 } 2154 2155 size_t out_len = 0; 2156 char* data = dump_grammar_text(&ctx, pg, &out_len); 2157 KitStatus wst = kit_writer_write(out, data, out_len); 2158 if (wst != KIT_OK) 2159 kit_ctx_diagf(kit, "%s: writing s-expr dump failed", use_path); 2160 mem_release(&ctx.mem); 2161 return wst == KIT_OK ? KIT_OK : KIT_ERR; 2162 } 2163 2164 Str str_from_owned(char* s, size_t len) { 2165 Str out = {.s = s, .len = len}; 2166 return out; 2167 } 2168 2169 Str str_dup_len(GramgenContext* ctx, const char* s, size_t len) { 2170 return str_from_owned(xstrndup(ctx, s, len), len); 2171 } 2172 2173 Str str_dup_c(GramgenContext* ctx, const char* s) { 2174 return str_dup_len(ctx, s, strlen(s)); 2175 } 2176 2177 int str_eq(Str a, Str b) { 2178 return a.len == b.len && memcmp(a.s, b.s, a.len) == 0; 2179 } 2180 2181 int str_eq_c(Str a, const char* b) { 2182 size_t n = strlen(b); 2183 return a.len == n && memcmp(a.s, b, n) == 0; 2184 } 2185 2186 char* str_to_c(GramgenContext* ctx, Str s) { return xstrndup(ctx, s.s, s.len); } 2187 2188 AstSeq* ast_seq_new(GramgenContext* ctx, Loc loc) { 2189 AstSeq* seq = xcalloc(ctx, 1, sizeof *seq); 2190 if (!seq) die_oom(ctx); 2191 seq->ctx = ctx; 2192 seq->loc = loc; 2193 return seq; 2194 } 2195 2196 AstAlt* ast_alt_new(GramgenContext* ctx, Loc loc) { 2197 AstAlt* alt = xcalloc(ctx, 1, sizeof *alt); 2198 if (!alt) die_oom(ctx); 2199 alt->ctx = ctx; 2200 alt->loc = loc; 2201 return alt; 2202 } 2203 2204 AstNode* ast_node_new(GramgenContext* ctx, AstNodeKind kind, Loc loc) { 2205 AstNode* node = xcalloc(ctx, 1, sizeof *node); 2206 if (!node) die_oom(ctx); 2207 node->kind = kind; 2208 node->loc = loc; 2209 return node; 2210 } 2211 2212 AstLexSeq* ast_lex_seq_new(GramgenContext* ctx, Loc loc) { 2213 AstLexSeq* seq = xcalloc(ctx, 1, sizeof *seq); 2214 if (!seq) die_oom(ctx); 2215 seq->ctx = ctx; 2216 seq->loc = loc; 2217 return seq; 2218 } 2219 2220 AstLexAlt* ast_lex_alt_new(GramgenContext* ctx, Loc loc) { 2221 AstLexAlt* alt = xcalloc(ctx, 1, sizeof *alt); 2222 if (!alt) die_oom(ctx); 2223 alt->ctx = ctx; 2224 alt->loc = loc; 2225 return alt; 2226 } 2227 2228 AstLexNode* ast_lex_node_new(GramgenContext* ctx, AstLexNodeKind kind, 2229 Loc loc) { 2230 AstLexNode* node = xcalloc(ctx, 1, sizeof *node); 2231 if (!node) die_oom(ctx); 2232 node->kind = kind; 2233 node->loc = loc; 2234 return node; 2235 } 2236 2237 void ast_seq_append(AstSeq* seq, AstNode* node) { 2238 if (seq->nitems == seq->cap) { 2239 seq->cap = seq->cap ? seq->cap * 2 : 4; 2240 seq->items = xrealloc(seq->ctx, seq->items, seq->cap * sizeof *seq->items); 2241 } 2242 seq->items[seq->nitems++] = node; 2243 } 2244 2245 void ast_alt_append(AstAlt* alt, AstSeq* seq) { 2246 if (alt->nseqs == alt->cap) { 2247 alt->cap = alt->cap ? alt->cap * 2 : 4; 2248 alt->seqs = xrealloc(alt->ctx, alt->seqs, alt->cap * sizeof *alt->seqs); 2249 } 2250 alt->seqs[alt->nseqs++] = seq; 2251 } 2252 2253 void ast_lex_seq_append(AstLexSeq* seq, AstLexNode* node) { 2254 if (seq->nitems == seq->cap) { 2255 seq->cap = seq->cap ? seq->cap * 2 : 4; 2256 seq->items = xrealloc(seq->ctx, seq->items, seq->cap * sizeof *seq->items); 2257 } 2258 seq->items[seq->nitems++] = node; 2259 } 2260 2261 void ast_lex_alt_append(AstLexAlt* alt, AstLexSeq* seq) { 2262 if (alt->nseqs == alt->cap) { 2263 alt->cap = alt->cap ? alt->cap * 2 : 4; 2264 alt->seqs = xrealloc(alt->ctx, alt->seqs, alt->cap * sizeof *alt->seqs); 2265 } 2266 alt->seqs[alt->nseqs++] = seq; 2267 } 2268 2269 static void parsed_add_rule(ParsedGrammar* g, AstRule* rule) { 2270 if (g->nrules == g->cap_rules) { 2271 g->cap_rules = g->cap_rules ? g->cap_rules * 2 : 8; 2272 g->rules = xrealloc(g->ctx, g->rules, g->cap_rules * sizeof *g->rules); 2273 } 2274 g->rules[g->nrules++] = rule; 2275 } 2276 2277 static void parsed_add_token_decl(ParsedGrammar* g, TokenDecl* decl) { 2278 if (g->ntoken_decls == g->cap_token_decls) { 2279 g->cap_token_decls = g->cap_token_decls ? g->cap_token_decls * 2 : 4; 2280 g->token_decls = xrealloc(g->ctx, g->token_decls, 2281 g->cap_token_decls * sizeof *g->token_decls); 2282 } 2283 g->token_decls[g->ntoken_decls++] = decl; 2284 } 2285 2286 static void parsed_add_lex_block(ParsedGrammar* g, AstLexBlock* block) { 2287 if (g->nlex_blocks == g->cap_lex_blocks) { 2288 g->cap_lex_blocks = g->cap_lex_blocks ? g->cap_lex_blocks * 2 : 2; 2289 g->lex_blocks = xrealloc(g->ctx, g->lex_blocks, 2290 g->cap_lex_blocks * sizeof *g->lex_blocks); 2291 } 2292 g->lex_blocks[g->nlex_blocks++] = block; 2293 } 2294 2295 static void pratt_line_append(AstPrattLine* line, AstPrattAtom* atom) { 2296 if (line->natoms == line->cap) { 2297 line->cap = line->cap ? line->cap * 2 : 4; 2298 line->atoms = 2299 xrealloc(line->ctx, line->atoms, line->cap * sizeof *line->atoms); 2300 } 2301 line->atoms[line->natoms++] = atom; 2302 } 2303 2304 static void pratt_spec_append(AstPrattSpec* spec, AstPrattLine* line) { 2305 if (spec->nlines == spec->cap) { 2306 spec->cap = spec->cap ? spec->cap * 2 : 4; 2307 spec->lines = 2308 xrealloc(spec->ctx, spec->lines, spec->cap * sizeof *spec->lines); 2309 } 2310 spec->lines[spec->nlines++] = line; 2311 } 2312 2313 static void lex_block_append(AstLexBlock* block, AstLexLine* line) { 2314 if (block->nlines == block->cap) { 2315 block->cap = block->cap ? block->cap * 2 : 4; 2316 block->lines = 2317 xrealloc(block->ctx, block->lines, block->cap * sizeof *block->lines); 2318 } 2319 block->lines[block->nlines++] = line; 2320 } 2321 2322 int hex_val(int c) { 2323 if (c >= '0' && c <= '9') return c - '0'; 2324 if (c >= 'a' && c <= 'f') return c - 'a' + 10; 2325 if (c >= 'A' && c <= 'F') return c - 'A' + 10; 2326 return -1; 2327 } 2328 2329 void kit_gram_error(GramgenContext* ctx, Loc loc, const char* fmt, ...) { 2330 va_list ap; 2331 va_start(ap, fmt); 2332 if (ctx && ctx->can_jump) { 2333 diag_vset(ctx, loc, fmt, ap); 2334 va_end(ap); 2335 longjmp(ctx->jmp, 1); 2336 } 2337 va_end(ap); 2338 for (;;) { 2339 } 2340 } 2341 2342 int is_c_ident_str(Str s) { 2343 if (!s.len) return 0; 2344 unsigned char c = (unsigned char)s.s[0]; 2345 if (!(isalpha(c) || c == '_')) return 0; 2346 for (size_t i = 1; i < s.len; i++) { 2347 c = (unsigned char)s.s[i]; 2348 if (!(isalnum(c) || c == '_')) return 0; 2349 } 2350 return 1; 2351 } 2352 2353 int is_rule_name(const char* name) { 2354 unsigned char c = (unsigned char)name[0]; 2355 if (!(c >= 'a' && c <= 'z')) return 0; 2356 for (size_t i = 1; name[i]; i++) { 2357 c = (unsigned char)name[i]; 2358 if (!(isalnum(c) || c == '_')) return 0; 2359 } 2360 return 1; 2361 } 2362 2363 int is_token_name(const char* name) { 2364 unsigned char c = (unsigned char)name[0]; 2365 if (!(c >= 'A' && c <= 'Z')) return 0; 2366 for (size_t i = 1; name[i]; i++) { 2367 c = (unsigned char)name[i]; 2368 if (!((c >= 'A' && c <= 'Z') || isdigit(c) || c == '_')) return 0; 2369 } 2370 return 1; 2371 } 2372 2373 static int int_cmp(const void* a, const void* b) { 2374 int x = *(const int*)a; 2375 int y = *(const int*)b; 2376 return (x > y) - (x < y); 2377 } 2378 2379 /* IntSet keeps its elements sorted ascending, so membership is a binary search 2380 * and union is a linear merge. The FIRST/FOLLOW fixpoint unions sets 2381 * repeatedly; the old unsorted array made each union O(|dst|*|src|). */ 2382 static size_t intset_lower_bound(const IntSet* s, int v) { 2383 size_t lo = 0, hi = s->n; 2384 while (lo < hi) { 2385 size_t mid = lo + (hi - lo) / 2; 2386 if (s->v[mid] < v) 2387 lo = mid + 1; 2388 else 2389 hi = mid; 2390 } 2391 return lo; 2392 } 2393 2394 int intset_contains(const IntSet* s, int v) { 2395 size_t i = intset_lower_bound(s, v); 2396 return i < s->n && s->v[i] == v; 2397 } 2398 2399 int intset_add(GramgenContext* ctx, IntSet* s, int v) { 2400 size_t i = intset_lower_bound(s, v); 2401 if (i < s->n && s->v[i] == v) return 0; 2402 if (s->n == s->cap) { 2403 s->cap = s->cap ? s->cap * 2 : 4; 2404 s->v = xrealloc(ctx, s->v, s->cap * sizeof *s->v); 2405 } 2406 memmove(&s->v[i + 1], &s->v[i], (s->n - i) * sizeof *s->v); 2407 s->v[i] = v; 2408 s->n++; 2409 return 1; 2410 } 2411 2412 int intset_union(GramgenContext* ctx, IntSet* dst, const IntSet* src) { 2413 if (src->n == 0) return 0; 2414 /* Merge the two sorted runs into a reused scratch buffer, then copy back into 2415 * dst. Reusing the buffer avoids an allocation per union (the FIRST/FOLLOW 2416 * fixpoint does many), which matters under the bump arena where the old 2417 * per-union buffer could not be reclaimed until the whole arena is freed. */ 2418 size_t need = dst->n + src->n; 2419 if (need > ctx->union_tmp_cap) { 2420 ctx->union_tmp = 2421 xrealloc(ctx, ctx->union_tmp, need * sizeof *ctx->union_tmp); 2422 ctx->union_tmp_cap = need; 2423 } 2424 int* out = ctx->union_tmp; 2425 size_t i = 0, j = 0, k = 0; 2426 int changed = 0; 2427 while (i < dst->n && j < src->n) { 2428 int a = dst->v[i], b = src->v[j]; 2429 if (a < b) { 2430 out[k++] = a; 2431 i++; 2432 } else if (a > b) { 2433 out[k++] = b; 2434 j++; 2435 changed = 1; 2436 } else { 2437 out[k++] = a; 2438 i++; 2439 j++; 2440 } 2441 } 2442 while (i < dst->n) out[k++] = dst->v[i++]; 2443 while (j < src->n) { 2444 out[k++] = src->v[j++]; 2445 changed = 1; 2446 } 2447 if (!changed) return 0; /* src was a subset; dst unchanged */ 2448 if (k > dst->cap) { /* grow dst in place (capacity reused) */ 2449 dst->v = xrealloc(ctx, dst->v, k * sizeof *dst->v); 2450 dst->cap = k; 2451 } 2452 memcpy(dst->v, out, k * sizeof *dst->v); 2453 dst->n = k; 2454 return 1; 2455 } 2456 2457 IntSet intset_copy(GramgenContext* ctx, const IntSet* src) { 2458 IntSet out = {0}; 2459 if (src->n) { 2460 out.v = xmalloc(ctx, src->n * sizeof *out.v); 2461 memcpy(out.v, src->v, src->n * sizeof *out.v); 2462 out.n = out.cap = src->n; 2463 } 2464 return out; 2465 } 2466 2467 int* intset_sorted(GramgenContext* ctx, const IntSet* s) { 2468 int* out = NULL; 2469 if (s->n) { 2470 out = xmalloc(ctx, s->n * sizeof *out); 2471 memcpy(out, s->v, s->n * sizeof *out); 2472 qsort(out, s->n, sizeof *out, int_cmp); 2473 } 2474 return out; 2475 } 2476 2477 int intset_equal(const IntSet* a, const IntSet* b) { 2478 if (a->n != b->n) return 0; 2479 for (size_t i = 0; i < a->n; i++) 2480 if (!intset_contains(b, a->v[i])) return 0; 2481 return 1; 2482 } 2483 2484 int intset_intersection_min(const IntSet* a, const IntSet* b) { 2485 int found = -1; 2486 for (size_t i = 0; i < a->n; i++) { 2487 int v = a->v[i]; 2488 if (intset_contains(b, v) && (found < 0 || v < found)) found = v; 2489 } 2490 return found; 2491 } 2492 2493 char* c_string_str(GramgenContext* ctx, Str s) { 2494 return quote_len(ctx, s.s, s.len, true); 2495 } 2496 2497 static const char* base_name(const char* path) { 2498 const char* slash = strrchr(path, '/'); 2499 return slash ? slash + 1 : path; 2500 } 2501 2502 static char* path_stem(GramgenContext* ctx, const char* path) { 2503 const char* base = base_name(path); 2504 const char* dot = strrchr(base, '.'); 2505 size_t n = dot && dot > base ? (size_t)(dot - base) : strlen(base); 2506 return xstrndup(ctx, base, n); 2507 } 2508 2509 static char* header_guard_name(GramgenContext* ctx, const char* path) { 2510 const char* base = base_name(path); 2511 Buf b; 2512 buf_init(ctx, &b); 2513 for (size_t i = 0; base[i]; i++) { 2514 unsigned char c = (unsigned char)base[i]; 2515 if (isalnum(c)) { 2516 char up = (char)toupper(c); 2517 buf_appendn(&b, &up, 1); 2518 } else { 2519 buf_append(&b, "_"); 2520 } 2521 } 2522 while (b.len && b.s[b.len - 1] == '_') b.s[--b.len] = '\0'; 2523 if (!b.len) buf_append(&b, "KIT_GRAM_OUTPUT"); 2524 if (isdigit((unsigned char)b.s[0])) { 2525 Buf out; 2526 buf_init(ctx, &out); 2527 buf_append(&out, "_"); 2528 buf_append(&out, b.s); 2529 xfree(ctx, b.s); 2530 b = out; 2531 } 2532 buf_append(&b, "_"); 2533 return buf_take(&b); 2534 } 2535 2536 static void validate_prefix_arg(GramgenContext* ctx, const char* prefix, 2537 const char* path) { 2538 char* g = xasprintf(ctx, "%sgrammar", prefix); 2539 char* p = xasprintf(ctx, "%sparser_init", prefix); 2540 if (!is_c_ident_str(str_dup_c(ctx, g)) || 2541 !is_c_ident_str(str_dup_c(ctx, p))) { 2542 Loc loc = {.path = path, .line = 1, .col = 1}; 2543 kit_gram_error(ctx, loc, "invalid identifier prefix '%s'", prefix); 2544 } 2545 } 2546 2547 static void emit_line(Buf* b, const char* fmt, ...) { 2548 char stack[512]; 2549 va_list ap, aq; 2550 va_start(ap, fmt); 2551 va_copy(aq, ap); 2552 int n = vsnprintf(stack, sizeof stack, fmt, ap); 2553 va_end(ap); 2554 if (n < 0) die_oom(b->ctx); 2555 if ((size_t)n < sizeof stack) { 2556 stack[n] = '\n'; 2557 buf_appendn(b, stack, (size_t)n + 1u); 2558 va_end(aq); 2559 return; 2560 } 2561 char* tmp = xmalloc(b->ctx, (size_t)n + 2u); 2562 vsnprintf(tmp, (size_t)n + 1u, fmt, aq); 2563 va_end(aq); 2564 tmp[n] = '\n'; 2565 tmp[(size_t)n + 1u] = '\0'; 2566 buf_appendn(b, tmp, (size_t)n + 1u); 2567 xfree(b->ctx, tmp); 2568 } 2569 2570 static char* make_upper_prefix(GramgenContext* ctx, const char* prefix) { 2571 size_t n = strlen(prefix); 2572 char* up = xmalloc(ctx, n + 1); 2573 for (size_t i = 0; i < n; i++) 2574 up[i] = (char)toupper((unsigned char)prefix[i]); 2575 up[n] = '\0'; 2576 return up; 2577 } 2578 2579 static char* token_ref(GramgenContext* ctx, Builder* b, const char* up, 2580 int tok) { 2581 return xasprintf(ctx, "%sTOK_%s", up, b->tokens[tok].name); 2582 } 2583 2584 static char* rule_ref(GramgenContext* ctx, Builder* b, const char* up, 2585 int idx) { 2586 if ((size_t)idx < b->public_count) 2587 return xasprintf(ctx, "%sR_%s", up, b->rules[idx].name); 2588 return xasprintf(ctx, "%sR__SYN_%d", up, idx - (int)b->public_count); 2589 } 2590 2591 static const char* pratt_role_ref(Builder* b, const char* role) { 2592 if (role == b->role.prefix) return "KIT_GRAM_PO_PREFIX"; 2593 if (role == b->role.postfix) return "KIT_GRAM_PO_POSTFIX"; 2594 if (role == b->role.ternary) return "KIT_GRAM_PO_TERNARY"; 2595 if (role == b->role.circumfix) return "KIT_GRAM_PO_CIRCUMFIX"; 2596 return "KIT_GRAM_PO_INFIX"; 2597 } 2598 2599 static int lex_name_is_main_c(const char* name) { 2600 return strcmp(name, "main") == 0; 2601 } 2602 2603 static char* lex_grammar_symbol(GramgenContext* ctx, const char* prefix, 2604 const char* name) { 2605 if (lex_name_is_main_c(name)) return xasprintf(ctx, "%slex_grammar", prefix); 2606 return xasprintf(ctx, "%s%s_lex_grammar", prefix, name); 2607 } 2608 2609 static char* lex_constructor_symbol(GramgenContext* ctx, const char* prefix, 2610 const char* name) { 2611 if (lex_name_is_main_c(name)) return xasprintf(ctx, "%slexer_init", prefix); 2612 return xasprintf(ctx, "%slexer_%s_init", prefix, name); 2613 } 2614 2615 static char* lex_scan_symbol(GramgenContext* ctx, const char* prefix, 2616 const char* name) { 2617 if (lex_name_is_main_c(name)) return xasprintf(ctx, "%sscan", prefix); 2618 return xasprintf(ctx, "%sscan_%s", prefix, name); 2619 } 2620 2621 /* Standalone (--lexer-standalone) tokenizer entry base: <prefix>lex for the 2622 * main mode and <prefix><name>_lex for each additional %lex mode. The _init / 2623 * _next suffixes hang off this; all modes share the one <prefix>lex state 2624 * struct. */ 2625 static char* std_lex_base(GramgenContext* ctx, const char* prefix, 2626 const char* name) { 2627 if (lex_name_is_main_c(name)) return xasprintf(ctx, "%slex", prefix); 2628 return xasprintf(ctx, "%s%s_lex", prefix, name); 2629 } 2630 2631 /* Standalone match-API base: <prefix>match for the main lexer and 2632 * <prefix><name>_match for each named %lex mode. The _anchored/_full/_find / 2633 * _iter_init/_iter_next suffixes and the <base>_iter struct hang off this, so 2634 * every lexer (including sub-lexers) drives its own match/iterator API. */ 2635 static char* std_match_base(GramgenContext* ctx, const char* prefix, 2636 const char* name) { 2637 if (lex_name_is_main_c(name)) return xasprintf(ctx, "%smatch", prefix); 2638 return xasprintf(ctx, "%s%s_match", prefix, name); 2639 } 2640 2641 /* The pull-RD parser covers the full supported grammar set: LL(1) rules, 2642 * postfix 2643 * `?`/`*`, %empty, and every Pratt role (prefix/postfix/infixl/infixr and the 2644 * mixfix ternary/circumfix). Kept as a hook for any future unsupported form. */ 2645 static int parser_codegen_ok(Builder* b) { 2646 (void)b; 2647 return 1; 2648 } 2649 2650 /* Fusion is available when the standalone lexer and parser codegen are both on 2651 * and there is a main lexer, so the parser can pull tokens by running the 2652 * standalone scanner inline. The standalone scanner handles every mode itself 2653 * (byte/UTF-8, start and end anchors), so there is no DFA-shape restriction. */ 2654 static int parser_fused_ok(Builder* b) { 2655 if (!b->lexer_standalone || !b->parser_codegen || !parser_codegen_ok(b)) 2656 return 0; 2657 for (size_t i = 0; i < b->nlex_dfas; i++) 2658 if (lex_name_is_main_c(b->lex_dfas[i].name)) return 1; 2659 return 0; 2660 } 2661 2662 static char* sym_init(GramgenContext* ctx, Builder* b, const char* up, 2663 Sym* sym) { 2664 if (sym->kind == SYM_TERM) { 2665 char* tok = token_ref(ctx, b, up, sym->value); 2666 char* out = xasprintf(ctx, "KIT_GRAM_TERM(%s)", tok); 2667 xfree(ctx, tok); 2668 return out; 2669 } 2670 if (sym->kind == SYM_RULE) { 2671 char* rule = rule_ref(ctx, b, up, sym->value); 2672 char* out = xasprintf(ctx, "KIT_GRAM_RULE(%s)", rule); 2673 xfree(ctx, rule); 2674 return out; 2675 } 2676 const char* kind = sym->kind == SYM_REP ? "KIT_GRAM_S_REP" : "KIT_GRAM_S_OPT"; 2677 return xasprintf(ctx, "{ .kind = %s, .first = %d, .sub = sub_%d, .nsub = 1 }", 2678 kind, sym->set_index, sym->sub_array_id); 2679 } 2680 2681 static char* join_syms(GramgenContext* ctx, Builder* b, const char* up, 2682 Sym* syms, size_t nsyms) { 2683 Buf out; 2684 buf_init(ctx, &out); 2685 for (size_t i = 0; i < nsyms; i++) { 2686 if (i) buf_append(&out, ", "); 2687 char* s = sym_init(ctx, b, up, &syms[i]); 2688 buf_append(&out, s); 2689 xfree(ctx, s); 2690 } 2691 return buf_take(&out); 2692 } 2693 2694 static char* tok_list(GramgenContext* ctx, Builder* b, const char* up, 2695 const IntSet* set) { 2696 int* sorted = intset_sorted(ctx, set); 2697 Buf out; 2698 buf_init(ctx, &out); 2699 for (size_t i = 0; i < set->n; i++) { 2700 if (i) buf_append(&out, ", "); 2701 char* tok = token_ref(ctx, b, up, sorted[i]); 2702 buf_append(&out, tok); 2703 xfree(ctx, tok); 2704 } 2705 xfree(ctx, sorted); 2706 return buf_take(&out); 2707 } 2708 2709 static void emit_machine_decls(Builder* b, Buf* out, const char* prefix); 2710 static void emit_machines(Builder* b, Buf* out, const char* prefix); 2711 2712 static void emit_header_text(Buf* out, Builder* b, const char* grammar_path, 2713 const char* header_path, const char* prefix) { 2714 GramgenContext* ctx = out->ctx; 2715 char* guard = header_guard_name(ctx, header_path); 2716 emit_line(out, "/* Generated by gramgen from %s. */", 2717 base_name(grammar_path)); 2718 emit_line(out, "#ifndef %s", guard); 2719 emit_line(out, "#define %s", guard); 2720 emit_line(out, ""); 2721 /* A machine-only file emits no parser grammar: just the codegen-only token 2722 * machines (steppable verifier + sampler), self-contained. */ 2723 if (b->public_count == 0 && b->nmachines) { 2724 emit_line(out, "#include <kit/gram_lex.h>"); 2725 emit_line(out, ""); 2726 emit_machine_decls(b, out, prefix); 2727 emit_line(out, "#endif /* %s */", guard); 2728 return; 2729 } 2730 emit_line(out, "#include <kit/gram_parse.h>"); 2731 if (b->nlex_dfas || b->nmachines) emit_line(out, "#include <kit/gram_lex.h>"); 2732 emit_line(out, ""); 2733 char* up = make_upper_prefix(ctx, prefix); 2734 emit_line(out, "enum {"); 2735 for (size_t i = 0; i < b->ntokens; i++) { 2736 if (i == 0) 2737 emit_line(out, " %sTOK_EOF = 0,", up); 2738 else 2739 emit_line(out, " %sTOK_%s,", up, b->tokens[i].name); 2740 } 2741 emit_line(out, " %sTOK__COUNT", up); 2742 emit_line(out, "};"); 2743 emit_line(out, ""); 2744 emit_line(out, "enum {"); 2745 for (size_t i = 0; i < b->public_count; i++) { 2746 if (i == 0) 2747 emit_line(out, " %sR_%s = 0,", up, b->rules[i].name); 2748 else 2749 emit_line(out, " %sR_%s,", up, b->rules[i].name); 2750 } 2751 emit_line(out, " %sR__COUNT", up); 2752 emit_line(out, "};"); 2753 emit_line(out, ""); 2754 for (size_t i = 0; i < b->public_count; i++) { 2755 Rule* rule = &b->rules[i]; 2756 if (!rule->pratt) continue; 2757 emit_line(out, "enum {"); 2758 char* primary = 2759 kit_gram_pratt_prod_enum_name(ctx, b, rule->name, b->role.primary, 0); 2760 emit_line(out, " %s%s = 0,", up, primary); 2761 xfree(ctx, primary); 2762 for (size_t j = 0; j < rule->pratt->nops; j++) 2763 emit_line(out, " %s%s,", up, rule->pratt->ops[j].enum_name); 2764 emit_line(out, "};"); 2765 emit_line(out, ""); 2766 } 2767 xfree(ctx, up); 2768 emit_line(out, 2769 "void %sstack_bounds(size_t max_depth, size_t* ctl_cap, size_t* " 2770 "val_cap);", 2771 prefix); 2772 emit_line(out, "size_t %sgenerate_scratch_count(void);", prefix); 2773 emit_line(out, 2774 "void %sparser_init(KitGramParser *mem, const KitGramConfig *cfg);", 2775 prefix); 2776 if (b->parser_codegen && parser_codegen_ok(b)) { 2777 emit_line(out, 2778 "/* Experimental pull-based recursive-descent parser over a " 2779 "buffered token"); 2780 emit_line(out, 2781 " array (no EOF token; end = ntok). Runs the same KitGramActions " 2782 "value channel"); 2783 emit_line( 2784 out, 2785 " as the table parser; returns the start rule's value, sets *ok."); 2786 emit_line(out, 2787 " On a syntax error it fills *err (if non-NULL) with the " 2788 "offending token,"); 2789 emit_line(out, 2790 " its location, the expected set, and the enclosing rule, and " 2791 "also calls"); 2792 emit_line(out, " KitGramActions.on_error if bound; *ok is set to 0. */"); 2793 emit_line(out, "KitGramSem %sparse_rd(const KitGramToken *toks, size_t ntok,", 2794 prefix); 2795 emit_line(out, 2796 " const KitGramActions *act, void *ud, KitGramError " 2797 "*err, int *ok);"); 2798 if (parser_fused_ok(b)) { 2799 emit_line(out, 2800 "/* Fused lex+parse: pulls tokens by running the generated " 2801 "scanner inline"); 2802 emit_line(out, 2803 " over the raw byte buffer (no token materialization). */"); 2804 emit_line(out, 2805 "KitGramSem %sparse_fused(const unsigned char *buf, size_t len,", 2806 prefix); 2807 emit_line(out, 2808 " const KitGramActions *act, void *ud, " 2809 "KitGramError *err, int *ok);"); 2810 } 2811 } 2812 if (b->nlex_dfas && b->lexer_standalone) { 2813 /* Self-contained resident tokenizer + match API for the main lexer; no 2814 * KitGramLexGrammar (the table is gone). KitGramToken / KitGramLexStatus / 2815 * KitGramMatch come from the included gramlex.h (header-only types, nothing 2816 * linked). */ 2817 emit_line(out, ""); 2818 emit_line(out, 2819 "/* Standalone tokenizer (--lexer-standalone): resident " 2820 "(buf,len) input, table-free. */"); 2821 emit_line(out, "typedef struct {"); 2822 emit_line(out, " const unsigned char *cur, *end;"); 2823 emit_line(out, " uint32_t line, col;"); 2824 emit_line(out, 2825 " bool after_cr; /* UTF-8 CRLF coalescing state */"); 2826 emit_line(out, "} %slex;", prefix); 2827 /* One resident tokenizer per %lex mode: the main lexer plus any named 2828 * sub-lexers. Re-lex a token's bytes by pointing a sub-lexer at its 2829 * lexeme/len (e.g. a NUMBER or STRING payload). All share the struct above. 2830 */ 2831 for (size_t i = 0; i < b->nlex_dfas; i++) { 2832 char* base = std_lex_base(ctx, prefix, b->lex_dfas[i].name); 2833 emit_line(out, 2834 "void %s_init(%slex *lx, const unsigned char *buf, " 2835 "size_t len);", 2836 base, prefix); 2837 emit_line(out, 2838 "KitGramLexStatus %s_next(%slex *lx, KitGramToken *out); /* " 2839 "TOKEN/EOF/ERROR */", 2840 base, prefix); 2841 xfree(ctx, base); 2842 } 2843 /* One match API per %lex mode (resident (buf,len)); each mirrors 2844 * kit_gram_match_anchored/full/find + KitGramMatchIter over its own scanner. */ 2845 emit_line(out, 2846 "/* Match API per lexer (resident (buf,len)); mirrors " 2847 "kit_gram_match_anchored/full/find + KitGramMatchIter. */"); 2848 for (size_t i = 0; i < b->nlex_dfas; i++) { 2849 char* mb = std_match_base(ctx, prefix, b->lex_dfas[i].name); 2850 emit_line(out, 2851 "bool %s_anchored(const unsigned char *buf, size_t len, " 2852 "const KitGramMatchOpts *opts, KitGramMatch *out);", 2853 mb); 2854 emit_line(out, 2855 "bool %s_full (const unsigned char *buf, size_t len, " 2856 "const KitGramMatchOpts *opts, KitGramMatch *out);", 2857 mb); 2858 emit_line(out, 2859 "bool %s_find (const unsigned char *buf, size_t len, " 2860 "const KitGramMatchOpts *opts, KitGramMatch *out);", 2861 mb); 2862 emit_line(out, 2863 "typedef struct { const unsigned char *buf; size_t len, from; " 2864 "int multiline; } %s_iter;", 2865 mb); 2866 emit_line(out, 2867 "void %s_iter_init(%s_iter *it, const unsigned char *buf, " 2868 "size_t len, const KitGramMatchOpts *opts);", 2869 mb, mb); 2870 emit_line(out, "bool %s_iter_next(%s_iter *it, KitGramMatch *out);", mb, 2871 mb); 2872 xfree(ctx, mb); 2873 } 2874 } else if (b->nlex_dfas) { 2875 emit_line(out, ""); 2876 for (size_t i = 0; i < b->nlex_dfas; i++) { 2877 LexDFA* dfa = &b->lex_dfas[i]; 2878 char* grammar = lex_grammar_symbol(ctx, prefix, dfa->name); 2879 char* ctor = lex_constructor_symbol(ctx, prefix, dfa->name); 2880 char* minit = 2881 lex_name_is_main_c(dfa->name) 2882 ? xasprintf(ctx, "%smatcher_init", prefix) 2883 : xasprintf(ctx, "%s%s_matcher_init", prefix, dfa->name); 2884 emit_line(out, "void %s(KitGramMatcher *m);", minit); 2885 emit_line(out, 2886 "void %s(KitGramLexer *mem, KitGramLexInput *in, const " 2887 "KitGramLexConfig *cfg);", 2888 ctor); 2889 xfree(ctx, grammar); 2890 xfree(ctx, ctor); 2891 xfree(ctx, minit); 2892 } 2893 } 2894 if (b->nmachines) { 2895 emit_line(out, ""); 2896 emit_machine_decls(b, out, prefix); 2897 } 2898 emit_line(out, ""); 2899 emit_line(out, "#endif /* %s */", guard); 2900 } 2901 2902 static void emit_sets(Builder* b, Buf* out, const char* up) { 2903 GramgenContext* ctx = out->ctx; 2904 if (!b->nsets) return; 2905 int* flat = NULL; 2906 size_t nflat = 0, cap_flat = 0; 2907 int* offs = xmalloc(ctx, (b->nsets + 1) * sizeof *offs); 2908 offs[0] = 0; 2909 for (size_t i = 0; i < b->nsets; i++) { 2910 int* sorted = intset_sorted(ctx, &b->sets[i]); 2911 for (size_t j = 0; j < b->sets[i].n; j++) { 2912 if (nflat == cap_flat) { 2913 cap_flat = cap_flat ? cap_flat * 2 : 16; 2914 flat = xrealloc(ctx, flat, cap_flat * sizeof *flat); 2915 } 2916 flat[nflat++] = sorted[j]; 2917 } 2918 offs[i + 1] = (int)nflat; 2919 xfree(ctx, sorted); 2920 } 2921 emit_line(out, "static const KitGramTokenKind g_sets[] = {"); 2922 if (nflat) { 2923 Buf joined; 2924 buf_init(ctx, &joined); 2925 for (size_t i = 0; i < nflat; i++) { 2926 if (i) buf_append(&joined, ", "); 2927 char* tok = token_ref(ctx, b, up, flat[i]); 2928 buf_append(&joined, tok); 2929 xfree(ctx, tok); 2930 } 2931 emit_line(out, " %s", joined.s); 2932 } 2933 emit_line(out, "};"); 2934 Buf off; 2935 buf_init(ctx, &off); 2936 for (size_t i = 0; i < b->nsets + 1; i++) { 2937 if (i) buf_append(&off, ", "); 2938 char tmp[32]; 2939 snprintf(tmp, sizeof tmp, "%d", offs[i]); 2940 buf_append(&off, tmp); 2941 } 2942 emit_line(out, "static const uint16_t g_set_off[] = { %s };", off.s); 2943 emit_line(out, ""); 2944 } 2945 2946 static void emit_sub_arrays(Builder* b, Buf* out, const char* up) { 2947 GramgenContext* ctx = out->ctx; 2948 for (size_t i = 0; i < b->nwrapper_syms; i++) { 2949 char* s = sym_init(ctx, b, up, b->wrapper_syms[i]->sub); 2950 emit_line(out, "static const KitGramSym sub_%zu[] = { %s };", i, s); 2951 xfree(ctx, s); 2952 } 2953 if (b->nwrapper_syms) emit_line(out, ""); 2954 } 2955 2956 static void emit_rule_arrays(Builder* b, Buf* out, const char* up) { 2957 GramgenContext* ctx = out->ctx; 2958 for (size_t ri = 0; ri < b->nrules; ri++) { 2959 Rule* rule = &b->rules[ri]; 2960 if (rule->pratt) continue; 2961 for (size_t pi = 0; pi < rule->nprods; pi++) { 2962 Prod* prod = &rule->prods[pi]; 2963 if (!prod->nsyms) continue; 2964 char* body = join_syms(ctx, b, up, prod->syms, prod->nsyms); 2965 emit_line(out, "static const KitGramSym p_r%zu_%zu[] = { %s };", ri, pi, 2966 body); 2967 xfree(ctx, body); 2968 } 2969 } 2970 emit_line(out, ""); 2971 emit_line(out, 2972 "#define PROD(arr) { .syms = (arr), .nsyms = " 2973 "(uint16_t)(sizeof(arr) / sizeof((arr)[0])) }"); 2974 emit_line(out, "#define PROD_EMPTY { .syms = NULL, .nsyms = 0 }"); 2975 emit_line(out, ""); 2976 for (size_t ri = 0; ri < b->nrules; ri++) { 2977 Rule* rule = &b->rules[ri]; 2978 if (rule->pratt) continue; 2979 Buf items; 2980 buf_init(ctx, &items); 2981 for (size_t pi = 0; pi < rule->nprods; pi++) { 2982 if (pi) buf_append(&items, ", "); 2983 if (rule->prods[pi].nsyms) { 2984 char* tmp = xasprintf(ctx, "PROD(p_r%zu_%zu)", ri, pi); 2985 buf_append(&items, tmp); 2986 xfree(ctx, tmp); 2987 } else { 2988 buf_append(&items, "PROD_EMPTY"); 2989 } 2990 } 2991 emit_line(out, "static const KitGramProd prods_r%zu[] = { %s };", ri, 2992 items.s); 2993 } 2994 emit_line(out, ""); 2995 } 2996 2997 typedef struct { 2998 int tok, prod; 2999 } PredictEntry; 3000 3001 static int predict_entry_cmp(const void* a, const void* b) { 3002 const PredictEntry* x = a; 3003 const PredictEntry* y = b; 3004 if (x->tok != y->tok) return x->tok < y->tok ? -1 : 1; 3005 return (x->prod > y->prod) - (x->prod < y->prod); 3006 } 3007 3008 static void emit_first_follow(Builder* b, Buf* out, const char* up) { 3009 GramgenContext* ctx = out->ctx; 3010 for (size_t ri = 0; ri < b->nrules; ri++) { 3011 Rule* rule = &b->rules[ri]; 3012 if (rule->first.n) { 3013 char* first = tok_list(ctx, b, up, &rule->first); 3014 emit_line(out, "static const KitGramTokenKind fst_r%zu[] = { %s };", ri, 3015 first); 3016 xfree(ctx, first); 3017 if (!rule->pratt) { 3018 PredictEntry* entries = NULL; 3019 size_t nentries = 0, cap_entries = 0; 3020 for (size_t pi = 0; pi < rule->nprods; pi++) { 3021 Prod* prod = &rule->prods[pi]; 3022 for (size_t ti = 0; ti < prod->first.n; ti++) { 3023 if (nentries == cap_entries) { 3024 cap_entries = cap_entries ? cap_entries * 2 : 8; 3025 entries = xrealloc(ctx, entries, cap_entries * sizeof *entries); 3026 } 3027 entries[nentries++] = (PredictEntry){prod->first.v[ti], (int)pi}; 3028 } 3029 } 3030 if (nentries > 1) 3031 qsort(entries, nentries, sizeof *entries, predict_entry_cmp); 3032 Buf pp; 3033 buf_init(ctx, &pp); 3034 for (size_t i = 0; i < nentries; i++) { 3035 if (i) buf_append(&pp, ", "); 3036 char tmp[32]; 3037 snprintf(tmp, sizeof tmp, "%d", entries[i].prod); 3038 buf_append(&pp, tmp); 3039 } 3040 emit_line(out, "static const uint8_t pp_r%zu[] = { %s };", ri, pp.s); 3041 } 3042 } 3043 if (rule->follow.n) { 3044 char* follow = tok_list(ctx, b, up, &rule->follow); 3045 emit_line(out, "static const KitGramTokenKind flw_r%zu[] = { %s };", ri, 3046 follow); 3047 xfree(ctx, follow); 3048 } 3049 } 3050 emit_line(out, ""); 3051 } 3052 3053 static void emit_pratt_tables(Builder* b, Buf* out, const char* up) { 3054 GramgenContext* ctx = out->ctx; 3055 for (size_t ri = 0; ri < b->nrules; ri++) { 3056 Rule* rule = &b->rules[ri]; 3057 if (!rule->pratt) continue; 3058 const char* ops_name = "NULL"; 3059 char nops_buf[64] = "0"; 3060 if (rule->pratt->nops) { 3061 emit_line(out, "static const KitGramPrattOp pratt_ops_r%zu[] = {", ri); 3062 for (size_t i = 0; i < rule->pratt->nops; i++) { 3063 PrattOp* op = &rule->pratt->ops[i]; 3064 char* tok = token_ref(ctx, b, up, op->tok); 3065 char* prod_name = xasprintf(ctx, "%s%s", up, op->enum_name); 3066 if (op->role == b->role.ternary) { 3067 char* tok2 = token_ref(ctx, b, up, op->tok2); 3068 emit_line(out, 3069 " { .role = %s, .tok = %s, .tok2 = %s, .prod = %s, .lbp " 3070 "= %d, .rbp = %d },", 3071 pratt_role_ref(b, op->role), tok, tok2, prod_name, op->lbp, 3072 op->rbp); 3073 xfree(ctx, tok2); 3074 } else if (op->role == b->role.circumfix) { 3075 char* tok2 = token_ref(ctx, b, up, op->tok2); 3076 char* inner = rule_ref(ctx, b, up, op->inner_rule); 3077 emit_line(out, 3078 " { .role = %s, .tok = %s, .tok2 = %s, .prod = %s, .lbp " 3079 "= %d, .rbp = %d, .inner = KIT_GRAM_RULE(%s) },", 3080 pratt_role_ref(b, op->role), tok, tok2, prod_name, op->lbp, 3081 op->rbp, inner); 3082 xfree(ctx, tok2); 3083 xfree(ctx, inner); 3084 } else { 3085 emit_line(out, 3086 " { .role = %s, .tok = %s, .prod = %s, .lbp = %d, .rbp " 3087 "= %d },", 3088 pratt_role_ref(b, op->role), tok, prod_name, op->lbp, 3089 op->rbp); 3090 } 3091 xfree(ctx, tok); 3092 xfree(ctx, prod_name); 3093 } 3094 emit_line(out, "};"); 3095 ops_name = "pratt_ops_r"; 3096 snprintf(nops_buf, sizeof nops_buf, "N(pratt_ops_r%zu)", ri); 3097 } 3098 char* primary_ref = rule_ref(ctx, b, up, rule->pratt->primary_rule); 3099 char* primary_name_raw = 3100 kit_gram_pratt_prod_enum_name(ctx, b, rule->name, b->role.primary, 0); 3101 char* primary_name = xasprintf(ctx, "%s%s", up, primary_name_raw); 3102 xfree(ctx, primary_name_raw); 3103 emit_line(out, "static const KitGramPratt pratt_r%zu = {", ri); 3104 emit_line(out, " .primary = %s,", primary_ref); 3105 emit_line(out, " .primary_prod = %s,", primary_name); 3106 if (rule->pratt->nops) 3107 emit_line(out, " .ops = pratt_ops_r%zu, .nops = %s,", ri, nops_buf); 3108 else 3109 emit_line(out, " .ops = %s, .nops = %s,", ops_name, nops_buf); 3110 emit_line(out, "};"); 3111 emit_line(out, ""); 3112 xfree(ctx, primary_ref); 3113 xfree(ctx, primary_name); 3114 } 3115 } 3116 3117 static void arr_ptr(GramgenContext* ctx, const char* prefix, size_t ri, 3118 const IntSet* set, char** ptr, char** nptr) { 3119 if (!set->n) { 3120 *ptr = xstrdup(ctx, "NULL"); 3121 *nptr = xstrdup(ctx, "0"); 3122 } else { 3123 *ptr = xasprintf(ctx, "%s_r%zu", prefix, ri); 3124 *nptr = xasprintf(ctx, "N(%s_r%zu)", prefix, ri); 3125 } 3126 } 3127 3128 static void predict_ptr(GramgenContext* ctx, Rule* rule, size_t ri, char** tok, 3129 char** prod, char** n) { 3130 if (rule->pratt || !rule->first.n) { 3131 *tok = xstrdup(ctx, "NULL"); 3132 *prod = xstrdup(ctx, "NULL"); 3133 *n = xstrdup(ctx, "0"); 3134 } else { 3135 *tok = xasprintf(ctx, "fst_r%zu", ri); 3136 *prod = xasprintf(ctx, "pp_r%zu", ri); 3137 *n = xasprintf(ctx, "N(fst_r%zu)", ri); 3138 } 3139 } 3140 3141 static void emit_rules(Builder* b, Buf* out, const char* up) { 3142 GramgenContext* ctx = out->ctx; 3143 emit_line(out, "static const KitGramRule g_rules[] = {"); 3144 for (size_t ri = 0; ri < b->nrules; ri++) { 3145 Rule* rule = &b->rules[ri]; 3146 char *pred_tok, *pred_prod, *npred; 3147 char *first, *nfirst, *follow, *nfollow; 3148 predict_ptr(ctx, rule, ri, &pred_tok, &pred_prod, &npred); 3149 arr_ptr(ctx, "fst", ri, &rule->first, &first, &nfirst); 3150 arr_ptr(ctx, "flw", ri, &rule->follow, &follow, &nfollow); 3151 char* rref = rule_ref(ctx, b, up, (int)ri); 3152 char* name = quote_len(ctx, rule->name, strlen(rule->name), false); 3153 const char* hidden = rule->public_rule ? "false" : "true"; 3154 const char* is_pratt = rule->pratt ? "true" : "false"; 3155 char* pratt = 3156 rule->pratt ? xasprintf(ctx, "&pratt_r%zu", ri) : xstrdup(ctx, "NULL"); 3157 char* prods = 3158 rule->pratt ? xstrdup(ctx, "NULL") : xasprintf(ctx, "prods_r%zu", ri); 3159 char* nprods = 3160 rule->pratt ? xstrdup(ctx, "0") : xasprintf(ctx, "N(prods_r%zu)", ri); 3161 emit_line(out, 3162 " [%s] = { .name = %s, .hidden = %s, .is_pratt = %s, .pratt = " 3163 "%s, .prods = %s, .nprods = %s, .predict_tok = %s, .predict_prod " 3164 "= %s, .npredict = %s, .nullable = %s, .empty_prod = %d, .first " 3165 "= %s, .nfirst = %s, .follow = %s, .nfollow = %s },", 3166 rref, name, hidden, is_pratt, pratt, prods, nprods, pred_tok, 3167 pred_prod, npred, rule->nullable ? "true" : "false", 3168 rule->empty_prod, first, nfirst, follow, nfollow); 3169 xfree(ctx, pred_tok); 3170 xfree(ctx, pred_prod); 3171 xfree(ctx, npred); 3172 xfree(ctx, first); 3173 xfree(ctx, nfirst); 3174 xfree(ctx, follow); 3175 xfree(ctx, nfollow); 3176 xfree(ctx, rref); 3177 xfree(ctx, name); 3178 xfree(ctx, pratt); 3179 xfree(ctx, prods); 3180 xfree(ctx, nprods); 3181 } 3182 emit_line(out, "};"); 3183 emit_line(out, ""); 3184 } 3185 3186 /* One per-state accept table (accept / accept_text / accept_line). `suffix` is 3187 * "", "_text", or "_line". Each cell is LXA (none) or an A_LEX_di_n accept id. 3188 */ 3189 static void emit_lex_accept_array(Buf* out, size_t di, const char* suffix, 3190 const uint16_t* arr, size_t n) { 3191 emit_line(out, "static const uint16_t lex_%zu_accept%s[] = {", di, suffix); 3192 for (size_t i = 0; i < n; i += 12) { 3193 size_t end = i + 12 < n ? i + 12 : n; 3194 buf_append(out, " "); 3195 for (size_t j = i; j < end; j++) { 3196 if (j > i) buf_append(out, ", "); 3197 uint16_t acc = arr[j]; 3198 if (acc == UINT16_MAX) 3199 buf_append(out, "LXA"); 3200 else { 3201 buf_append(out, "A_LEX_"); 3202 buf_append_uint(out, (unsigned)di); 3203 buf_append(out, "_"); 3204 buf_append_uint(out, acc); 3205 } 3206 } 3207 buf_append(out, ",\n"); 3208 } 3209 emit_line(out, "};"); 3210 emit_line(out, ""); 3211 } 3212 3213 static void emit_lexer_tables(Builder* b, Buf* out, const char* grammar_path, 3214 const char* prefix) { 3215 GramgenContext* ctx = out->ctx; 3216 if (!b->nlex_dfas) return; 3217 char* stem = path_stem(ctx, grammar_path); 3218 char* up = make_upper_prefix(ctx, prefix); 3219 3220 for (size_t di = 0; di < b->nlex_dfas; di++) { 3221 LexDFA* dfa = &b->lex_dfas[di]; 3222 emit_line(out, "enum {"); 3223 for (size_t i = 0; i < dfa->nrecognizers; i++) 3224 emit_line(out, " A_LEX_%zu_%zu,", di, i); 3225 emit_line(out, " A_LEX_%zu_COUNT", di); 3226 emit_line(out, "};"); 3227 emit_line(out, ""); 3228 emit_line(out, "static const KitGramLexAccept lex_%zu_accepts[] = {", di); 3229 for (size_t i = 0; i < dfa->nrecognizers; i++) { 3230 LexRecognizer* rec = &dfa->recognizers[i]; 3231 char* base; 3232 if (rec->skip) { 3233 base = xstrdup(ctx, ".skip = true"); 3234 } else { 3235 char* tok = token_ref(ctx, b, up, rec->tok); 3236 base = xasprintf(ctx, ".tok = %s", tok); 3237 xfree(ctx, tok); 3238 } 3239 emit_line(out, " [A_LEX_%zu_%zu] = { %s },", di, i, base); 3240 xfree(ctx, base); 3241 } 3242 emit_line(out, "};"); 3243 emit_line(out, ""); 3244 emit_line(out, "#define LXA KIT_GRAM_LEX_ACCEPT_NONE"); 3245 emit_line(out, "#define LXD KIT_GRAM_LEX_DEAD"); 3246 emit_line(out, ""); 3247 emit_lex_accept_array(out, di, "", dfa->accept, dfa->nstates); 3248 /* End-of-match context accept tables, only when the grammar uses that 3249 * end anchor (NULL otherwise keeps anchor-free output byte-identical). */ 3250 if (dfa->accept_text) 3251 emit_lex_accept_array(out, di, "_text", dfa->accept_text, dfa->nstates); 3252 if (dfa->accept_line) 3253 emit_lex_accept_array(out, di, "_line", dfa->accept_line, dfa->nstates); 3254 uint16_t stride = dfa->nclasses; 3255 emit_line(out, "static const uint16_t lex_%zu_trans[] = {", di); 3256 { 3257 /* Dense by-state table: cell (s, c) lives at s * class_stride + c, 3258 * indexed directly by state. Generated tables use nclasses as the 3259 * stride; class_stride exists so older and experimental tables can 3260 * still be consumed. */ 3261 size_t ncells = (size_t)dfa->nstates * stride; 3262 for (size_t i = 0; i < ncells; i += 12) { 3263 size_t end = i + 12 < ncells ? i + 12 : ncells; 3264 buf_append(out, " "); 3265 for (size_t j = i; j < end; j++) { 3266 if (j > i) buf_append(out, ", "); 3267 uint16_t dst = dfa->trans[j]; 3268 if (dst == UINT16_MAX) 3269 buf_append(out, "LXD"); 3270 else 3271 buf_append_uint(out, dst); 3272 } 3273 buf_append(out, ",\n"); 3274 } 3275 } 3276 emit_line(out, "};"); 3277 emit_line(out, ""); 3278 emit_line(out, "#undef LXA"); 3279 emit_line(out, "#undef LXD"); 3280 emit_line(out, ""); 3281 3282 /* Extracted-keyword tables: per host, CHD bucket seeds + the minimal 3283 * perfect hash slot array. */ 3284 for (size_t ki = 0; ki < dfa->nkeyword_tables; ki++) { 3285 LexKeywordTable* kt = &dfa->keyword_tables[ki]; 3286 emit_line(out, "static const uint32_t lex_%zu_kw%zu_seeds[] = {", di, ki); 3287 for (size_t i = 0; i < kt->nseeds; i += 12) { 3288 size_t end = i + 12 < kt->nseeds ? i + 12 : kt->nseeds; 3289 buf_append(out, " "); 3290 for (size_t j = i; j < end; j++) { 3291 if (j > i) buf_append(out, ", "); 3292 buf_append_uint(out, (unsigned)kt->seeds[j]); 3293 } 3294 buf_append(out, ",\n"); 3295 } 3296 emit_line(out, "};"); 3297 emit_line(out, ""); 3298 emit_line(out, "static const KitGramLexKeyword lex_%zu_kw%zu[] = {", di, 3299 ki); 3300 for (size_t wi = 0; wi < kt->nslots; wi++) { 3301 LexKeyword* kw = &kt->slots[wi]; 3302 if (!kw->literal.s) { 3303 emit_line(out, " { 0 },"); 3304 continue; 3305 } 3306 char* q = quote_len(ctx, kw->literal.s, kw->literal.len, false); 3307 char* tr = token_ref(ctx, b, up, kw->tok); 3308 emit_line(out, " { %s, %u, %s },", q, (unsigned)kw->literal.len, tr); 3309 xfree(ctx, q); 3310 xfree(ctx, tr); 3311 } 3312 emit_line(out, "};"); 3313 emit_line(out, ""); 3314 } 3315 if (dfa->nkeyword_tables) { 3316 emit_line( 3317 out, 3318 "static const KitGramLexKeywordTable lex_%zu_keyword_tables[] = {", 3319 di); 3320 for (size_t ki = 0; ki < dfa->nkeyword_tables; ki++) { 3321 LexKeywordTable* kt = &dfa->keyword_tables[ki]; 3322 char* hr = token_ref(ctx, b, up, kt->host); 3323 emit_line( 3324 out, 3325 " { .host = %s, .seeds = lex_%zu_kw%zu_seeds, .nseeds = %zu," 3326 " .keywords = lex_%zu_kw%zu, .nkeywords = %zu," 3327 " .min_len = %zu, .max_len = %zu },", 3328 hr, di, ki, kt->nseeds, di, ki, kt->nslots, kt->min_len, 3329 kt->max_len); 3330 xfree(ctx, hr); 3331 } 3332 emit_line(out, "};"); 3333 emit_line(out, ""); 3334 } 3335 3336 char* runtime_name; 3337 if (lex_name_is_main_c(dfa->name)) 3338 runtime_name = xstrdup(ctx, stem); 3339 else 3340 runtime_name = xasprintf(ctx, "%s:%s", stem, dfa->name); 3341 char* runtime_name_q = 3342 quote_len(ctx, runtime_name, strlen(runtime_name), false); 3343 char* grammar = lex_grammar_symbol(ctx, prefix, dfa->name); 3344 char* ctor = lex_constructor_symbol(ctx, prefix, dfa->name); 3345 emit_line(out, "const KitGramLexGrammar %s = {", grammar); 3346 emit_line(out, " .name = %s,", runtime_name_q); 3347 emit_line(out, " .input = %s,", 3348 dfa->input == KIT_GRAM_LEX_INPUT_UTF8 ? "KIT_GRAM_LEX_INPUT_UTF8" 3349 : "KIT_GRAM_LEX_INPUT_BYTES"); 3350 emit_line(out, " .class_of = {"); 3351 for (int i = 0; i < 256; i += 16) { 3352 buf_append(out, " "); 3353 for (int j = 0; j < 16; j++) { 3354 if (j) buf_append(out, ", "); 3355 buf_append_uint(out, (unsigned)dfa->class_of[i + j]); 3356 } 3357 buf_append(out, ",\n"); 3358 } 3359 emit_line(out, " },"); 3360 emit_line(out, " .nclasses = %u,", (unsigned)dfa->nclasses); 3361 emit_line(out, " .class_stride = %u,", (unsigned)stride); 3362 emit_line(out, " .trans = lex_%zu_trans,", di); 3363 emit_line(out, " .nstates = %u,", (unsigned)dfa->nstates); 3364 emit_line(out, " .accept = lex_%zu_accept,", di); 3365 if (dfa->accept_text) 3366 emit_line(out, " .accept_text = lex_%zu_accept_text,", di); 3367 if (dfa->accept_line) 3368 emit_line(out, " .accept_line = lex_%zu_accept_line,", di); 3369 emit_line(out, " .accepts = lex_%zu_accepts,", di); 3370 emit_line(out, " .naccepts = A_LEX_%zu_COUNT,", di); 3371 if (dfa->start_text) 3372 emit_line(out, " .start_text = %u,", (unsigned)dfa->start_text); 3373 if (dfa->start_line) 3374 emit_line(out, " .start_line = %u,", (unsigned)dfa->start_line); 3375 if (b->multiline) emit_line(out, " .multiline = true,"); 3376 if (dfa->nkeyword_tables) { 3377 emit_line(out, " .keyword_tables = lex_%zu_keyword_tables,", di); 3378 emit_line(out, " .nkeyword_tables = %zu,", dfa->nkeyword_tables); 3379 } 3380 emit_line(out, "};"); 3381 emit_line(out, ""); 3382 char* minit = lex_name_is_main_c(dfa->name) 3383 ? xasprintf(ctx, "%smatcher_init", prefix) 3384 : xasprintf(ctx, "%s%s_matcher_init", prefix, dfa->name); 3385 emit_line(out, "void %s(KitGramMatcher *m) {", minit); 3386 emit_line(out, " kit_gram_matcher_bind(m, &%s);", grammar); 3387 emit_line(out, "}"); 3388 emit_line(out, ""); 3389 emit_line(out, 3390 "void %s(KitGramLexer *mem, KitGramLexInput *in, const " 3391 "KitGramLexConfig *cfg) {", 3392 ctor); 3393 emit_line(out, " kit_gram_lexer_init(mem, &%s, in, cfg);", grammar); 3394 emit_line(out, "}"); 3395 if (di + 1 < b->nlex_dfas) emit_line(out, ""); 3396 xfree(ctx, runtime_name); 3397 xfree(ctx, runtime_name_q); 3398 xfree(ctx, grammar); 3399 xfree(ctx, ctor); 3400 xfree(ctx, minit); 3401 } 3402 xfree(ctx, up); 3403 xfree(ctx, stem); 3404 } 3405 3406 /* ---- standalone (table-free) lexer emission (--lexer-standalone) ---------- 3407 * A self-contained tokenizer + match API: the DFA table is dropped and the 3408 * generated `.c` links nothing from the lexer runtime. The scanner is a 3409 * re2c-style state machine — one label per state, byte transitions as inline 3410 * range comparisons — with the accepted token kind and skip-ness baked in 3411 * directly (no accepts[] lookup) and the edge-anchor end context decided inline 3412 * per accepting state. %keywords default to a minimal perfect hash (an inline 3413 * lookup over the same seeds/slots the table path emits); --fold-keywords folds 3414 * them into the DFA instead. One resident-buffer driver per %lex mode returns 3415 * KitGramToken streams identical to kit_gram_lexer_next; the main lexer's match 3416 * drivers mirror kit_gram_match_anchored/full/find + KitGramMatchIter. 3417 */ 3418 3419 /* Min of two accept ids; KIT_GRAM_LEX_ACCEPT_NONE (UINT16_MAX) is the additive max, 3420 * so plain min naturally drops the "no accept" sentinel and keeps the lowest 3421 * source priority (smallest id) among the eligible accepts. */ 3422 static uint16_t std_min2(uint16_t a, uint16_t b) { return a < b ? a : b; } 3423 3424 /* Emit the longest-match backup for accept id `id` (an index into recognizers), 3425 * baking the resolved token kind / skip-ness; nothing when id is NONE. */ 3426 static void emit_std_record(Buf* out, Builder* b, LexDFA* dfa, uint16_t id, 3427 const char* ind, const char* up) { 3428 if (id == UINT16_MAX) return; 3429 GramgenContext* ctx = out->ctx; 3430 LexRecognizer* r = &dfa->recognizers[id]; 3431 /* position: lazy drops the line-state snapshot from every accept. */ 3432 const char* pos = 3433 b->position_lazy ? "" : " best_nlines = nlines; best_last_nl = last_nl;"; 3434 if (r->skip) { 3435 emit_line(out, "%sbest_skip = 1; best_kind = 0; mark = p; found = 1;%s", 3436 ind, pos); 3437 } else { 3438 char* tok = token_ref(ctx, b, up, r->tok); 3439 emit_line(out, "%sbest_skip = 0; best_kind = %s; mark = p; found = 1;%s", 3440 ind, tok, pos); 3441 xfree(ctx, tok); 3442 } 3443 } 3444 3445 static void emit_lexer_standalone(Builder* b, Buf* out, const char* prefix) { 3446 GramgenContext* ctx = out->ctx; 3447 if (!b->nlex_dfas) return; 3448 int lazy = b->position_lazy; /* omit per-token line/col tracking */ 3449 char* up = make_upper_prefix(ctx, prefix); 3450 /* Every %lex mode gets its own resident tokenizer and its own match API, so a 3451 * caller can drive each lexer independently and re-lex a token's bytes (a 3452 * NUMBER or STRING payload, say) with a named sub-mode. */ 3453 3454 /* The UTF-8 position/line-break helpers are prefix-scoped (shared by every 3455 * mode), so emit each once if any mode needs it (union over all modes). */ 3456 int any_utf8 = 0, any_break_at = 0, any_break_before = 0; 3457 for (size_t i = 0; i < b->nlex_dfas; i++) { 3458 LexDFA* d = &b->lex_dfas[i]; 3459 if (d->input != KIT_GRAM_LEX_INPUT_UTF8) continue; 3460 any_utf8 = 1; 3461 if (d->accept_line) any_break_at = 1; 3462 if (d->start_line) any_break_before = 1; 3463 } 3464 const char* ML = b->multiline ? "1" : "0"; 3465 3466 emit_line(out, ""); 3467 emit_line( 3468 out, 3469 "/* ---- standalone tokenizer + match API (--lexer-standalone) ---- */"); 3470 emit_line( 3471 out, 3472 "typedef struct { KitGramTokenKind kind; int skip; int found; size_t len;"); 3473 emit_line(out, " size_t nlines, last_nl; } %ssres;", prefix); 3474 emit_line(out, ""); 3475 3476 /* UTF-8 scalar position folding (mirrors scan_pos_advance + the CRLF-aware 3477 * kit_gram_unicode_pos_advance) — only the byte arithmetic is needed in byte 3478 * mode. 3479 */ 3480 if (any_utf8 && !lazy) { 3481 /* Pointer-taking so both the tokenizer and the fused parser fold position 3482 * into their own line/col/after_cr state. */ 3483 emit_line(out, 3484 "static void %scp_advance(uint32_t *line, uint32_t *col, bool " 3485 "*after_cr, uint32_t cp) {", 3486 prefix); 3487 emit_line( 3488 out, 3489 " if (cp == 0x0Au && *after_cr) { *after_cr = false; return; }"); 3490 emit_line(out, 3491 " if (cp == 0x0Du) { (*line)++; *col = 1; *after_cr = true; " 3492 "return; }"); 3493 emit_line(out, " *after_cr = false;"); 3494 emit_line( 3495 out, 3496 " if (cp == 0x0Au || cp == 0x0Bu || cp == 0x0Cu || cp == 0x0Du ||"); 3497 emit_line(out, 3498 " cp == 0x85u || cp == 0x2028u || cp == 0x2029u) { " 3499 "(*line)++; *col = 1; }"); 3500 emit_line(out, " else (*col)++;"); 3501 emit_line(out, "}"); 3502 emit_line( 3503 out, 3504 "static void %sfold_pos(uint32_t *line, uint32_t *col, bool *after_cr,", 3505 prefix); 3506 emit_line(out, 3507 " const unsigned char *s, size_t len) {"); 3508 emit_line(out, " uint32_t cp = 0; unsigned need = 0;"); 3509 emit_line(out, " for (size_t i = 0; i < len; i++) {"); 3510 emit_line(out, " unsigned char bb = s[i];"); 3511 emit_line(out, " if (!need) {"); 3512 emit_line( 3513 out, 3514 " if (bb < 0x80u) %scp_advance(line, col, after_cr, bb);", 3515 prefix); 3516 emit_line(out, 3517 " else if (bb >= 0xC2u && bb <= 0xDFu) { cp = " 3518 "(uint32_t)(bb & 0x1Fu); need = 1; }"); 3519 emit_line(out, 3520 " else if (bb >= 0xE0u && bb <= 0xEFu) { cp = " 3521 "(uint32_t)(bb & 0x0Fu); need = 2; }"); 3522 emit_line(out, 3523 " else if (bb >= 0xF0u && bb <= 0xF4u) { cp = " 3524 "(uint32_t)(bb & 0x07u); need = 3; }"); 3525 emit_line(out, " } else if ((bb & 0xC0u) != 0x80u) { need = 0; }"); 3526 emit_line(out, 3527 " else { cp = (cp << 6) | (uint32_t)(bb & 0x3Fu); if " 3528 "(--need == 0) %scp_advance(line, col, after_cr, cp); }", 3529 prefix); 3530 emit_line(out, " }"); 3531 emit_line(out, "}"); 3532 emit_line(out, ""); 3533 } 3534 3535 /* UTF-8 line-break peek helpers, emitted only when an end/start line anchor 3536 * needs them (a byte-mode peek is just `*p == '\\n'`, no helper). */ 3537 if (any_break_at || any_break_before) { 3538 emit_line(out, 3539 "static size_t %sdecode(const unsigned char *p, size_t avail, " 3540 "uint32_t *cp) {", 3541 prefix); 3542 emit_line(out, " if (!avail) return 0;"); 3543 emit_line(out, " unsigned char b0 = p[0];"); 3544 emit_line(out, " if (b0 < 0x80u) { *cp = b0; return 1; }"); 3545 emit_line(out, " if (b0 >= 0xC2u && b0 <= 0xDFu) {"); 3546 emit_line(out, 3547 " if (avail < 2 || (p[1] & 0xC0u) != 0x80u) return 0;"); 3548 emit_line(out, 3549 " *cp = ((uint32_t)(b0 & 0x1Fu) << 6) | (uint32_t)(p[1] & " 3550 "0x3Fu); return 2;"); 3551 emit_line(out, " }"); 3552 emit_line(out, " if (b0 >= 0xE0u && b0 <= 0xEFu) {"); 3553 emit_line(out, 3554 " if (avail < 3 || (p[1] & 0xC0u) != 0x80u || (p[2] & " 3555 "0xC0u) != 0x80u) return 0;"); 3556 emit_line(out, 3557 " *cp = ((uint32_t)(b0 & 0x0Fu) << 12) | ((uint32_t)(p[1] " 3558 "& 0x3Fu) << 6) | (uint32_t)(p[2] & 0x3Fu);"); 3559 emit_line(out, " return 3;"); 3560 emit_line(out, " }"); 3561 emit_line(out, " if (b0 >= 0xF0u && b0 <= 0xF4u) {"); 3562 emit_line(out, 3563 " if (avail < 4 || (p[1] & 0xC0u) != 0x80u || (p[2] & " 3564 "0xC0u) != 0x80u || (p[3] & 0xC0u) != 0x80u) return 0;"); 3565 emit_line(out, 3566 " *cp = ((uint32_t)(b0 & 0x07u) << 18) | ((uint32_t)(p[1] " 3567 "& 0x3Fu) << 12) |"); 3568 emit_line(out, 3569 " ((uint32_t)(p[2] & 0x3Fu) << 6) | (uint32_t)(p[3] " 3570 "& 0x3Fu); return 4;"); 3571 emit_line(out, " }"); 3572 emit_line(out, " return 0;"); 3573 emit_line(out, "}"); 3574 } 3575 if (any_break_at) { 3576 emit_line(out, 3577 "static int %sbreak_at(const unsigned char *p, const unsigned " 3578 "char *end) {", 3579 prefix); 3580 emit_line(out, " uint32_t cp;"); 3581 emit_line(out, " if (!%sdecode(p, (size_t)(end - p), &cp)) return 0;", 3582 prefix); 3583 emit_line(out, 3584 " return cp == 0x0Au || cp == 0x0Bu || cp == 0x0Cu || cp == " 3585 "0x0Du ||"); 3586 emit_line(out, " cp == 0x85u || cp == 0x2028u || cp == 0x2029u;"); 3587 emit_line(out, "}"); 3588 } 3589 if (any_break_before) { 3590 emit_line(out, 3591 "static int %sbreak_before(const unsigned char *buf, size_t len, " 3592 "size_t pos) {", 3593 prefix); 3594 emit_line(out, " if (pos == 0) return 0;"); 3595 emit_line(out, " size_t start = pos - 1;"); 3596 emit_line(out, " while (start > 0) {"); 3597 emit_line(out, " unsigned char bb = buf[start];"); 3598 emit_line(out, " if ((bb & 0xC0u) != 0x80u) break;"); 3599 emit_line(out, " if (pos - start >= 4) return 0;"); 3600 emit_line(out, " start--;"); 3601 emit_line(out, " }"); 3602 emit_line( 3603 out, 3604 " uint32_t cp; size_t n = %sdecode(buf + start, len - start, &cp);", 3605 prefix); 3606 emit_line(out, 3607 " return n && start + n == pos && (cp == 0x0Au || cp == 0x0Bu " 3608 "|| cp == 0x0Cu ||"); 3609 emit_line(out, 3610 " cp == 0x0Du || cp == 0x85u || cp == 0x2028u || cp == " 3611 "0x2029u);"); 3612 emit_line(out, "}"); 3613 } 3614 if (any_break_at || any_break_before) emit_line(out, ""); 3615 3616 /* Shared keyword-compare for the MPH lookup (default; --fold-keywords skips 3617 * this and bakes keywords into the DFA). One byte loop keeps the standalone 3618 * lexer free of <string.h>. */ 3619 int any_kw = 0; 3620 for (size_t i = 0; i < b->nlex_dfas; i++) 3621 if (b->lex_dfas[i].nkeyword_tables) { 3622 any_kw = 1; 3623 break; 3624 } 3625 if (any_kw) { 3626 emit_line( 3627 out, 3628 "static int %skw_eq(const char *a, const unsigned char *b, size_t " 3629 "n) {", 3630 prefix); 3631 emit_line(out, 3632 " for (size_t i = 0; i < n; i++) if ((unsigned char)a[i] != " 3633 "b[i]) return 0;"); 3634 emit_line(out, " return 1;"); 3635 emit_line(out, "}"); 3636 emit_line(out, ""); 3637 } 3638 3639 /* One scanner + resident tokenizer per %lex mode (declaration order). Each 3640 * mode also gets its own match API below. */ 3641 for (size_t mi = 0; mi < b->nlex_dfas; mi++) { 3642 LexDFA* dfa = &b->lex_dfas[mi]; 3643 int utf8 = dfa->input == KIT_GRAM_LEX_INPUT_UTF8; 3644 int has_end = dfa->accept_text || dfa->accept_line; 3645 int has_start = dfa->start_text || dfa->start_line; 3646 size_t nclasses = dfa->nclasses; 3647 char* scan = lex_scan_symbol(ctx, prefix, dfa->name); 3648 char* base = std_lex_base(ctx, prefix, dfa->name); 3649 char* brk_at = utf8 ? xasprintf(ctx, "%sbreak_at(p, end)", prefix) 3650 : xstrdup(ctx, "*p == 10"); 3651 3652 /* Extracted-keyword MPH tables + inline lookup (the default; 3653 * --fold-keywords folds keywords into the DFA above and leaves 3654 * nkeyword_tables == 0). Each host gets the same CHD seeds + slot array the 3655 * table runtime would, and the rewrite mirrors kw_lookup_hashed using the 3656 * header-only kit_gram_lex_kw_hash64, so it is table-free yet agrees with 3657 * construction byte-for-byte. */ 3658 int has_kw = dfa->nkeyword_tables > 0; 3659 if (has_kw) { 3660 for (size_t ki = 0; ki < dfa->nkeyword_tables; ki++) { 3661 LexKeywordTable* kt = &dfa->keyword_tables[ki]; 3662 emit_line(out, "static const uint32_t %skw%zu_%zu_seeds[] = {", prefix, 3663 mi, ki); 3664 for (size_t j = 0; j < kt->nseeds; j += 12) { 3665 size_t e = j + 12 < kt->nseeds ? j + 12 : kt->nseeds; 3666 buf_append(out, " "); 3667 for (size_t k = j; k < e; k++) { 3668 if (k > j) buf_append(out, ", "); 3669 buf_append_uint(out, (unsigned)kt->seeds[k]); 3670 } 3671 buf_append(out, ",\n"); 3672 } 3673 emit_line(out, "};"); 3674 emit_line(out, "static const KitGramLexKeyword %skw%zu_%zu[] = {", 3675 prefix, mi, ki); 3676 for (size_t wi = 0; wi < kt->nslots; wi++) { 3677 LexKeyword* kw = &kt->slots[wi]; 3678 if (!kw->literal.s) { 3679 emit_line(out, " { 0 },"); 3680 continue; 3681 } 3682 char* q = quote_len(ctx, kw->literal.s, kw->literal.len, false); 3683 char* tr = token_ref(ctx, b, up, kw->tok); 3684 emit_line(out, " { %s, %u, %s },", q, (unsigned)kw->literal.len, 3685 tr); 3686 xfree(ctx, q); 3687 xfree(ctx, tr); 3688 } 3689 emit_line(out, "};"); 3690 } 3691 emit_line(out, 3692 "static KitGramTokenKind %skw_rw%zu(KitGramTokenKind host, const " 3693 "unsigned char *s, size_t len) {", 3694 prefix, mi); 3695 emit_line(out, " switch (host) {"); 3696 for (size_t ki = 0; ki < dfa->nkeyword_tables; ki++) { 3697 LexKeywordTable* kt = &dfa->keyword_tables[ki]; 3698 char* hr = token_ref(ctx, b, up, kt->host); 3699 emit_line(out, " case %s: {", hr); 3700 emit_line(out, " if (len < %zu || len > %zu) return host;", 3701 (size_t)kt->min_len, (size_t)kt->max_len); 3702 emit_line(out, 3703 " uint32_t bk = (uint32_t)(kit_gram_lex_kw_hash64(0, s, " 3704 "len) & %zuu);", 3705 (size_t)kt->nseeds - 1); 3706 emit_line(out, 3707 " uint32_t sl = (uint32_t)(kit_gram_lex_kw_hash64(" 3708 "%skw%zu_%zu_seeds[bk], s, len) & %zuu);", 3709 prefix, mi, ki, (size_t)kt->nslots - 1); 3710 emit_line(out, " const KitGramLexKeyword *kw = &%skw%zu_%zu[sl];", 3711 prefix, mi, ki); 3712 emit_line(out, 3713 " if (kw->lexeme && kw->len == len && %skw_eq(kw->" 3714 "lexeme, s, len)) return kw->kind;", 3715 prefix); 3716 emit_line(out, " return host;"); 3717 emit_line(out, " }"); 3718 xfree(ctx, hr); 3719 } 3720 emit_line(out, " default: return host;"); 3721 emit_line(out, " }"); 3722 emit_line(out, "}"); 3723 } 3724 3725 /* The scanner. Parameterized on `ml` (the tokenizer passes the baked 3726 * multiline constant; the match API passes opts->multiline at runtime). */ 3727 emit_line(out, 3728 "static %ssres %s(uint16_t start_state, const unsigned char *p,", 3729 prefix, scan); 3730 emit_line( 3731 out, 3732 " const unsigned char *end, int ml) {"); 3733 emit_line(out, " const unsigned char *const s0 = p;"); 3734 emit_line(out, " const unsigned char *mark = p;"); 3735 emit_line(out, " KitGramTokenKind best_kind = 0;"); 3736 emit_line(out, " int best_skip = 0, found = 0;"); 3737 if (!lazy) 3738 emit_line(out, 3739 " size_t nlines = 0, last_nl = 0, best_nlines = 0, " 3740 "best_last_nl = 0;"); 3741 emit_line(out, " unsigned char yych;"); 3742 emit_line(out, " (void)ml;"); 3743 if (has_start) { 3744 emit_line(out, " switch (start_state) {"); 3745 if (dfa->start_text) 3746 emit_line(out, " case %u: goto st%u;", (unsigned)dfa->start_text, 3747 (unsigned)dfa->start_text); 3748 if (dfa->start_line && dfa->start_line != dfa->start_text) 3749 emit_line(out, " case %u: goto st%u;", (unsigned)dfa->start_line, 3750 (unsigned)dfa->start_line); 3751 emit_line(out, " default: goto st0;"); 3752 emit_line(out, " }"); 3753 } else { 3754 emit_line(out, " switch (start_state) { default: goto st0; }"); 3755 } 3756 for (size_t s = 0; s < dfa->nstates; s++) { 3757 const uint16_t* row = &dfa->trans[s * nclasses]; 3758 emit_line(out, " st%zu:", s); 3759 int has_out = 0; 3760 for (int by = 0; by < 256; by++) 3761 if (row[dfa->class_of[by]] != UINT16_MAX) { 3762 has_out = 1; 3763 break; 3764 } 3765 if (!has_end) { 3766 emit_std_record(out, b, dfa, dfa->accept[s], " ", up); 3767 if (!has_out) { 3768 emit_line(out, " goto done;"); 3769 continue; 3770 } 3771 emit_line(out, " if (p == end) goto done;"); 3772 emit_line(out, " yych = *p;"); 3773 } else { 3774 uint16_t plain = dfa->accept[s]; 3775 uint16_t text = dfa->accept_text ? dfa->accept_text[s] : UINT16_MAX; 3776 uint16_t line = dfa->accept_line ? dfa->accept_line[s] : UINT16_MAX; 3777 uint16_t win_textend = std_min2(std_min2(plain, text), line); 3778 uint16_t win_break = std_min2(plain, line); 3779 emit_line(out, " if (p == end) {"); 3780 emit_std_record(out, b, dfa, win_textend, " ", up); 3781 emit_line(out, " goto done;"); 3782 emit_line(out, " }"); 3783 if (line != UINT16_MAX) { 3784 emit_line(out, " if (ml && %s) {", brk_at); 3785 emit_std_record(out, b, dfa, win_break, " ", up); 3786 emit_line(out, " } else {"); 3787 emit_std_record(out, b, dfa, plain, " ", up); 3788 emit_line(out, " }"); 3789 } else { 3790 emit_std_record(out, b, dfa, plain, " ", up); 3791 } 3792 if (!has_out) { 3793 emit_line(out, " goto done;"); 3794 continue; 3795 } 3796 emit_line(out, " yych = *p;"); 3797 } 3798 int by = 0, wildcard = 0; 3799 while (by < 256) { 3800 uint16_t tgt = row[dfa->class_of[by]]; 3801 int lo = by, hi = by; 3802 while (hi + 1 < 256 && row[dfa->class_of[hi + 1]] == tgt) hi++; 3803 by = hi + 1; 3804 if (tgt == UINT16_MAX) continue; 3805 int has_nl = (lo <= 10 && 10 <= hi); 3806 const char* nl = (has_nl && !lazy) 3807 ? "if (yych == 10) { nlines++; last_nl = " 3808 "(size_t)(p - s0) + 1; } " 3809 : ""; 3810 if (lo == 0 && hi == 255) { 3811 emit_line(out, " %sp++; goto st%u;", nl, (unsigned)tgt); 3812 wildcard = 1; 3813 break; 3814 } else if (lo == hi) { 3815 emit_line(out, " if (yych == %d) { %sp++; goto st%u; }", lo, nl, 3816 (unsigned)tgt); 3817 } else if (lo == 0) { 3818 emit_line(out, " if (yych <= %d) { %sp++; goto st%u; }", hi, nl, 3819 (unsigned)tgt); 3820 } else if (hi == 255) { 3821 emit_line(out, " if (yych >= %d) { %sp++; goto st%u; }", lo, nl, 3822 (unsigned)tgt); 3823 } else { 3824 emit_line(out, 3825 " if (yych >= %d && yych <= %d) { %sp++; goto st%u; }", 3826 lo, hi, nl, (unsigned)tgt); 3827 } 3828 } 3829 if (!wildcard) emit_line(out, " goto done;"); 3830 } 3831 emit_line(out, " done:"); 3832 /* Rewrite a host token's kind by MPH lookup over its matched lexeme 3833 * [s0, mark) before returning, so the tokenizer, match API, and fused 3834 * parser all see the keyword kind without a separate pass. */ 3835 if (has_kw) 3836 emit_line(out, 3837 " if (found) best_kind = %skw_rw%zu(best_kind, s0, " 3838 "(size_t)(mark - s0));", 3839 prefix, mi); 3840 emit_line( 3841 out, 3842 " return (%ssres){ .kind = best_kind, .skip = best_skip, .found " 3843 "= found,", 3844 prefix); 3845 if (lazy) 3846 emit_line(out, " .len = (size_t)(mark - s0) };"); 3847 else 3848 emit_line(out, 3849 " .len = (size_t)(mark - s0), .nlines = best_nlines, " 3850 ".last_nl = best_last_nl };"); 3851 emit_line(out, "}"); 3852 3853 /* The tokenizer (one per mode; the struct is shared, the entry base is 3854 * not). */ 3855 emit_line(out, ""); 3856 emit_line(out, 3857 "void %s_init(%slex *lx, const unsigned char *buf, size_t len) {", 3858 base, prefix); 3859 emit_line(out, 3860 " lx->cur = buf; lx->end = buf + len; lx->line = 1; lx->col = " 3861 "1; lx->after_cr = false;"); 3862 emit_line(out, "}"); 3863 emit_line(out, ""); 3864 emit_line(out, "KitGramLexStatus %s_next(%slex *lx, KitGramToken *out) {", 3865 base, prefix); 3866 emit_line(out, " for (;;) {"); 3867 emit_line(out, " if (lx->cur == lx->end) {"); 3868 emit_line( 3869 out, 3870 " if (out) *out = (KitGramToken){ .kind = %sTOK_EOF, .lexeme " 3871 "= (const char *)lx->cur,", 3872 up); 3873 if (lazy) 3874 emit_line(out, 3875 " .line = 0, .col = " 3876 "0 };"); 3877 else 3878 emit_line( 3879 out, 3880 " .line = lx->line, .col " 3881 "= lx->col };"); 3882 emit_line(out, " return KIT_GRAM_LEX_EOF;"); 3883 emit_line(out, " }"); 3884 emit_line(out, " uint16_t start = 0;"); 3885 if (has_start) { 3886 emit_line(out, " if (lx->line == 1u && lx->col == 1u) start = %u;", 3887 (unsigned)dfa->start_text); 3888 if (dfa->start_line) 3889 emit_line(out, " else if (%s && lx->col == 1u) start = %u;", ML, 3890 (unsigned)dfa->start_line); 3891 } 3892 emit_line(out, " %ssres r = %s(start, lx->cur, lx->end, %s);", 3893 prefix, scan, ML); 3894 emit_line(out, " if (!r.found) {"); 3895 emit_line( 3896 out, 3897 " if (out) *out = (KitGramToken){ .kind = %sTOK_EOF, .lexeme " 3898 "= (const char *)lx->cur,", 3899 up); 3900 if (lazy) 3901 emit_line(out, 3902 " .len = 1, .line = " 3903 "0, .col = 0 };"); 3904 else 3905 emit_line(out, 3906 " .len = 1, .line = " 3907 "lx->line, .col = lx->col };"); 3908 emit_line(out, " return KIT_GRAM_LEX_ERROR;"); 3909 emit_line(out, " }"); 3910 emit_line(out, " const unsigned char *lex = lx->cur;"); 3911 if (!lazy) { 3912 emit_line(out, " uint32_t tline = lx->line, tcol = lx->col;"); 3913 if (utf8) { 3914 emit_line(out, 3915 " %sfold_pos(&lx->line, &lx->col, &lx->after_cr, lex, " 3916 "r.len);", 3917 prefix); 3918 } else { 3919 emit_line(out, " lx->line += (uint32_t)r.nlines;"); 3920 emit_line(out, 3921 " lx->col = r.last_nl ? (uint32_t)(1u + (r.len - " 3922 "r.last_nl)) : lx->col + (uint32_t)r.len;"); 3923 } 3924 } 3925 emit_line(out, " lx->cur += r.len;"); 3926 emit_line(out, " if (r.skip) continue;"); 3927 emit_line(out, 3928 " if (out) *out = (KitGramToken){ .kind = r.kind, .lexeme = " 3929 "(const char *)lex, .len = r.len,"); 3930 if (lazy) 3931 emit_line(out, 3932 " .line = 0, .col = 0 };"); 3933 else 3934 emit_line( 3935 out, 3936 " .line = tline, .col = tcol };"); 3937 emit_line(out, " return KIT_GRAM_LEX_TOKEN;"); 3938 emit_line(out, " }"); 3939 emit_line(out, "}"); 3940 3941 xfree(ctx, scan); 3942 xfree(ctx, base); 3943 xfree(ctx, brk_at); 3944 } /* for each %lex mode */ 3945 3946 /* One match API per %lex mode: each scans with its own generated scanner, 3947 * start anchors, and line-break test. */ 3948 for (size_t i = 0; i < b->nlex_dfas; i++) { 3949 LexDFA* dfa = &b->lex_dfas[i]; 3950 int has_start = dfa->start_text || dfa->start_line; 3951 char* scan = lex_scan_symbol(ctx, prefix, dfa->name); 3952 char* mb = std_match_base(ctx, prefix, dfa->name); 3953 char* brk_before = 3954 (dfa->input == KIT_GRAM_LEX_INPUT_UTF8) 3955 ? xasprintf(ctx, "%sbreak_before(buf, len, p)", prefix) 3956 : xstrdup(ctx, "buf[p - 1] == 10"); 3957 emit_line(out, ""); 3958 emit_line( 3959 out, "static int %s_from(const unsigned char *buf, size_t len, int ml,", 3960 mb); 3961 emit_line(out, " size_t from, KitGramMatch *out) {"); 3962 emit_line(out, " for (size_t p = from; p < len; p++) {"); 3963 emit_line(out, " uint16_t start = 0;"); 3964 if (has_start) { 3965 emit_line(out, " if (p == 0) start = %u;", 3966 (unsigned)dfa->start_text); 3967 if (dfa->start_line) 3968 emit_line(out, " else if (ml && %s) start = %u;", brk_before, 3969 (unsigned)dfa->start_line); 3970 } 3971 emit_line(out, " %ssres r = %s(start, buf + p, buf + len, ml);", 3972 prefix, scan); 3973 emit_line(out, 3974 " if (r.found) { out->start = p; out->end = p + r.len; " 3975 "out->kind = r.kind; return 1; }"); 3976 emit_line(out, " }"); 3977 emit_line(out, " return 0;"); 3978 emit_line(out, "}"); 3979 emit_line(out, ""); 3980 emit_line(out, "bool %s_anchored(const unsigned char *buf, size_t len,", 3981 mb); 3982 emit_line(out, 3983 " const KitGramMatchOpts *opts, KitGramMatch " 3984 "*out) {"); 3985 emit_line(out, " int ml = opts && opts->multiline;"); 3986 emit_line(out, " %ssres r = %s(%u, buf, buf + len, ml);", prefix, scan, 3987 (unsigned)dfa->start_text); 3988 emit_line(out, " if (!r.found) return false;"); 3989 emit_line(out, " out->start = 0; out->end = r.len; out->kind = r.kind;"); 3990 emit_line(out, " return true;"); 3991 emit_line(out, "}"); 3992 emit_line(out, "bool %s_full(const unsigned char *buf, size_t len,", mb); 3993 emit_line( 3994 out, 3995 " const KitGramMatchOpts *opts, KitGramMatch *out) {"); 3996 emit_line(out, " KitGramMatch m;"); 3997 emit_line(out, " if (!%s_anchored(buf, len, opts, &m)) return false;", 3998 mb); 3999 emit_line(out, " if (m.end != len) return false;"); 4000 emit_line(out, " *out = m;"); 4001 emit_line(out, " return true;"); 4002 emit_line(out, "}"); 4003 emit_line(out, "bool %s_find(const unsigned char *buf, size_t len,", mb); 4004 emit_line( 4005 out, 4006 " const KitGramMatchOpts *opts, KitGramMatch *out) {"); 4007 emit_line(out, " int ml = opts && opts->multiline;"); 4008 emit_line(out, " return %s_from(buf, len, ml, 0, out) != 0;", mb); 4009 emit_line(out, "}"); 4010 emit_line(out, "void %s_iter_init(%s_iter *it, const unsigned char *buf,", 4011 mb, mb); 4012 emit_line( 4013 out, 4014 " size_t len, const KitGramMatchOpts *opts) {"); 4015 emit_line(out, 4016 " it->buf = buf; it->len = len; it->from = 0; it->multiline = " 4017 "opts && opts->multiline;"); 4018 emit_line(out, "}"); 4019 emit_line(out, "bool %s_iter_next(%s_iter *it, KitGramMatch *out) {", mb, mb); 4020 emit_line(out, 4021 " if (!%s_from(it->buf, it->len, it->multiline, it->from, " 4022 "out)) return false;", 4023 mb); 4024 emit_line(out, 4025 " it->from = out->end > it->from ? out->end : it->from + 1;"); 4026 emit_line(out, " return true;"); 4027 emit_line(out, "}"); 4028 4029 xfree(ctx, scan); 4030 xfree(ctx, mb); 4031 xfree(ctx, brk_before); 4032 } 4033 xfree(ctx, up); 4034 } 4035 4036 /* ---- experimental pull recursive-descent parser emission (--parser-codegen) 4037 * ---- One static C function per rule: the prediction `switch (lookahead)` 4038 * replaces the table search, rule references become calls (the hardware call 4039 * stack replaces the control stack), `*`/`?` become a loop/if guarded by the 4040 * body's FIRST set, and a %pratt rule becomes a precedence-climbing pair. The 4041 * same KitGramActions value channel (lift_token, reduce, the list and opt 4042 * builders) fires, so the value semantics match the table parser. Errors unwind 4043 * with setjmp/longjmp (fail-fast). Buffered token array (end of buffer == EOF); 4044 * enter/exit/on_token listeners and streaming suspension are intentionally out 4045 * of scope for the prototype. */ 4046 4047 /* A disjunction `la == A || la == B ...` with no outer parens (callers wrap as 4048 * needed) so a single-token guard stays `la == A` rather than `(la == A)`, 4049 * which -Wparentheses-equality flags when it lands directly inside an `if`. */ 4050 static char* rd_guard_str(GramgenContext* ctx, Builder* b, const char* up, 4051 const IntSet* set) { 4052 int* s = intset_sorted(ctx, set); 4053 Buf t; 4054 buf_init(ctx, &t); 4055 if (set->n == 0) buf_append(&t, "0"); 4056 for (size_t i = 0; i < set->n; i++) { 4057 if (i) buf_append(&t, " || "); 4058 char* tk = token_ref(ctx, b, up, s[i]); 4059 buf_append(&t, "la == "); 4060 buf_append(&t, tk); 4061 xfree(ctx, tk); 4062 } 4063 xfree(ctx, s); 4064 return buf_take(&t); 4065 } 4066 4067 static void rd_emit_cases(Buf* out, Builder* b, const char* up, 4068 const IntSet* set) { 4069 int* s = intset_sorted(out->ctx, set); 4070 for (size_t i = 0; i < set->n; i++) { 4071 char* tk = token_ref(out->ctx, b, up, s[i]); 4072 emit_line(out, " case %s:", tk); 4073 xfree(out->ctx, tk); 4074 } 4075 xfree(out->ctx, s); 4076 } 4077 4078 /* Parse one TERM or RULE symbol, assigning its value to lvalue `dst`. `R` is 4079 * the enclosing rule's id, threaded into rd_match for the in_rule of an error. 4080 */ 4081 static void rd_emit_one(Buf* out, Builder* b, const char* up, const Sym* sym, 4082 const char* dst, const char* ind, const char* R) { 4083 if (sym->kind == SYM_TERM) { 4084 char* tk = token_ref(out->ctx, b, up, sym->value); 4085 emit_line(out, "%s%s = rd_match(p, %s, %s);", ind, dst, tk, R); 4086 xfree(out->ctx, tk); 4087 } else { /* SYM_RULE */ 4088 emit_line(out, "%s%s = rd_r%d(p);", ind, dst, sym->value); 4089 } 4090 } 4091 4092 /* Parse one top-level factor of a production into k[idx]. */ 4093 static void rd_emit_factor(Buf* out, Builder* b, const char* up, const Sym* sym, 4094 size_t idx, const char* R) { 4095 GramgenContext* ctx = out->ctx; 4096 char* dst = xasprintf(ctx, "k[%zu]", idx); 4097 if (sym->kind == SYM_TERM || sym->kind == SYM_RULE) { 4098 rd_emit_one(out, b, up, sym, dst, " ", R); 4099 xfree(ctx, dst); 4100 return; 4101 } 4102 const IntSet* guard = &b->sets[sym->set_index]; 4103 char* g = rd_guard_str(ctx, b, up, guard); 4104 if (sym->kind == SYM_REP) { 4105 emit_line(out, " {"); 4106 emit_line(out, 4107 " KitGramSem _l = p->act->list_empty ? " 4108 "p->act->list_empty(p->ud) : (KitGramSem)0;"); 4109 emit_line(out, " for (;;) {"); 4110 emit_line(out, " KitGramTokenKind la = rd_look(p);"); 4111 emit_line(out, " if (!(%s)) break;", g); 4112 emit_line(out, " KitGramSem _it;"); 4113 rd_emit_one(out, b, up, sym->sub, "_it", " ", R); 4114 emit_line(out, 4115 " _l = p->act->list_push ? " 4116 "p->act->list_push(p->ud, _l, _it) : _it;"); 4117 emit_line(out, " }"); 4118 emit_line(out, " %s = _l;", dst); 4119 emit_line(out, " }"); 4120 } else { /* SYM_OPT */ 4121 emit_line(out, " {"); 4122 emit_line(out, " KitGramTokenKind la = rd_look(p);"); 4123 emit_line(out, " if (%s) {", g); 4124 emit_line(out, " KitGramSem _it;"); 4125 rd_emit_one(out, b, up, sym->sub, "_it", " ", R); 4126 emit_line(out, 4127 " %s = p->act->opt_some ? p->act->opt_some(p->ud, " 4128 "_it) : _it;", 4129 dst); 4130 emit_line(out, " } else {"); 4131 emit_line(out, 4132 " %s = p->act->opt_none ? p->act->opt_none(p->ud) " 4133 ": (KitGramSem)0;", 4134 dst); 4135 emit_line(out, " }"); 4136 emit_line(out, " }"); 4137 } 4138 xfree(ctx, g); 4139 xfree(ctx, dst); 4140 } 4141 4142 static void rd_emit_pratt(Buf* out, Builder* b, int ri, const char* prefix, 4143 int recover) { 4144 GramgenContext* ctx = out->ctx; 4145 Rule* rule = &b->rules[ri]; 4146 PrattInfo* pr = rule->pratt; 4147 char* up = make_upper_prefix(ctx, prefix); 4148 char* R = rule_ref(ctx, b, up, ri); 4149 /* The episode entry checks the lookahead against the rule's FIRST set the way 4150 * the interpreter does at the LL(1)->Pratt boundary, so an expression that 4151 * cannot start is attributed to this rule (not the primary delegate). */ 4152 int has_exp = rule->first.n > 0; 4153 if (has_exp) { 4154 char* toks = tok_list(ctx, b, up, &rule->first); 4155 emit_line(out, "static const KitGramTokenKind rd_exp_%d[] = { %s };", ri, 4156 toks); 4157 xfree(ctx, toks); 4158 } 4159 emit_line(out, "static KitGramSem rd_r%d_climb(RD *p, int min_bp);", ri); 4160 emit_line(out, "static KitGramSem rd_r%d(RD *p) {", ri); 4161 if (has_exp) { 4162 char* g = rd_guard_str(ctx, b, up, &rule->first); 4163 if (recover) { 4164 emit_line(out, " retry:"); /* re-predict after recovery */ 4165 emit_line(out, " {"); 4166 emit_line(out, " KitGramTokenKind la = rd_look(p);"); 4167 emit_line( 4168 out, 4169 " if (!(%s)) { if (rd_recover_predict(p, rd_exp_%d, %zu, %s, " 4170 "&%sgrammar.rules[%d])) goto retry; return (KitGramSem)0; }", 4171 g, ri, rule->first.n, R, prefix, ri); 4172 emit_line(out, " }"); 4173 } else { 4174 emit_line(out, " KitGramTokenKind la = rd_look(p);"); 4175 emit_line(out, " if (!(%s)) rd_fail(p, rd_exp_%d, %zu, %s);", g, ri, 4176 rule->first.n, R); 4177 } 4178 xfree(ctx, g); 4179 } 4180 /* enter/exit bracket the whole Pratt episode once (with primary_prod), 4181 * matching the interpreter; the per-operator reduces in the climb are 4182 * value-channel only. */ 4183 emit_line(out, " if (p->act->enter) p->act->enter(p->ud, %s, %d);", R, 4184 pr->primary_prod); 4185 emit_line(out, " KitGramSem r = rd_r%d_climb(p, 0);", ri); 4186 emit_line(out, " if (p->act->exit) p->act->exit(p->ud, %s, %d);", R, 4187 pr->primary_prod); 4188 emit_line(out, " return r;"); 4189 emit_line(out, "}"); 4190 emit_line(out, "static KitGramSem rd_r%d_climb(RD *p, int min_bp) {", ri); 4191 emit_line(out, " KitGramSem lhs;"); 4192 emit_line(out, 4193 " switch (rd_look(p)) { /* nud: prefix op " 4194 "or primary */"); 4195 for (size_t oi = 0; oi < pr->nops; oi++) { 4196 PrattOp* op = &pr->ops[oi]; 4197 if (op->role != b->role.prefix) continue; 4198 char* tk = token_ref(ctx, b, up, op->tok); 4199 emit_line(out, " case %s: {", tk); 4200 emit_line(out, " KitGramSem op = rd_match(p, %s, %s);", tk, R); 4201 emit_line(out, " KitGramSem rhs = rd_r%d_climb(p, %d);", ri, op->rbp); 4202 emit_line(out, " KitGramSem k[2] = { op, rhs };"); 4203 emit_line(out, " lhs = rd_reduce(p, %s, %d, k, 2);", R, op->prod); 4204 emit_line(out, " break;"); 4205 emit_line(out, " }"); 4206 xfree(ctx, tk); 4207 } 4208 emit_line(out, " default: {"); 4209 emit_line(out, " KitGramSem pv = rd_r%d(p);", pr->primary_rule); 4210 emit_line(out, " KitGramSem k[1] = { pv };"); 4211 emit_line(out, " lhs = rd_reduce(p, %s, %d, k, 1);", R, 4212 pr->primary_prod); 4213 emit_line(out, " break;"); 4214 emit_line(out, " }"); 4215 emit_line(out, " }"); 4216 emit_line(out, 4217 " for (;;) { /* led: operator " 4218 "loop */"); 4219 emit_line(out, " switch (rd_look(p)) {"); 4220 for (size_t oi = 0; oi < pr->nops; oi++) { 4221 PrattOp* op = &pr->ops[oi]; 4222 if (op->role == b->role.prefix) continue; /* nud only */ 4223 char* tk = 4224 token_ref(ctx, b, up, 4225 op->tok); /* infix/postfix op, ternary op1, circumfix open */ 4226 emit_line(out, " case %s: {", tk); 4227 emit_line(out, " if (%d <= min_bp) goto done;", op->lbp); 4228 if (op->role == b->role.postfix) { 4229 emit_line(out, " KitGramSem op = rd_match(p, %s, %s);", tk, R); 4230 emit_line(out, " KitGramSem k[2] = { lhs, op };"); 4231 emit_line(out, " lhs = rd_reduce(p, %s, %d, k, 2);", R, 4232 op->prod); 4233 } else if (op->role == b->role.ternary) { 4234 char* t2 = token_ref(ctx, b, up, op->tok2); 4235 emit_line(out, " KitGramSem op1 = rd_match(p, %s, %s);", tk, R); 4236 emit_line(out, " KitGramSem mid = rd_r%d_climb(p, 0);", ri); 4237 emit_line(out, " KitGramSem op2 = rd_match(p, %s, %s);", t2, R); 4238 emit_line(out, " KitGramSem els = rd_r%d_climb(p, %d);", ri, 4239 op->rbp); 4240 emit_line(out, 4241 " KitGramSem k[5] = { lhs, op1, mid, op2, els };"); 4242 emit_line(out, " lhs = rd_reduce(p, %s, %d, k, 5);", R, 4243 op->prod); 4244 xfree(ctx, t2); 4245 } else if (op->role == b->role.circumfix) { 4246 char* t2 = token_ref(ctx, b, up, op->tok2); 4247 emit_line(out, " KitGramSem open = rd_match(p, %s, %s);", tk, R); 4248 emit_line(out, " KitGramSem inner = rd_r%d(p);", op->inner_rule); 4249 emit_line(out, " KitGramSem close = rd_match(p, %s, %s);", t2, 4250 R); 4251 emit_line(out, 4252 " KitGramSem k[4] = { lhs, open, inner, close };"); 4253 emit_line(out, " lhs = rd_reduce(p, %s, %d, k, 4);", R, 4254 op->prod); 4255 xfree(ctx, t2); 4256 } else { /* infixl / infixr / infix */ 4257 emit_line(out, " KitGramSem op = rd_match(p, %s, %s);", tk, R); 4258 emit_line(out, " KitGramSem rhs = rd_r%d_climb(p, %d);", ri, 4259 op->rbp); 4260 emit_line(out, " KitGramSem k[3] = { lhs, op, rhs };"); 4261 emit_line(out, " lhs = rd_reduce(p, %s, %d, k, 3);", R, 4262 op->prod); 4263 } 4264 emit_line(out, " break;"); 4265 emit_line(out, " }"); 4266 xfree(ctx, tk); 4267 } 4268 emit_line(out, " default: goto done;"); 4269 emit_line(out, " }"); 4270 emit_line(out, " }"); 4271 emit_line(out, "done:"); 4272 emit_line(out, " return lhs;"); 4273 emit_line(out, "}"); 4274 xfree(ctx, up); 4275 xfree(ctx, R); 4276 } 4277 4278 static void rd_emit_rule(Buf* out, Builder* b, int ri, const char* prefix, 4279 int recover) { 4280 GramgenContext* ctx = out->ctx; 4281 Rule* rule = &b->rules[ri]; 4282 if (rule->pratt) { 4283 rd_emit_pratt(out, b, ri, prefix, recover); 4284 return; 4285 } 4286 char* up = make_upper_prefix(ctx, prefix); 4287 int hidden = !rule->public_rule; 4288 char* R = rule_ref(ctx, b, up, ri); 4289 /* On a prediction miss the expected set is the rule's FIRST set — the same 4290 * tokens the interpreter reports in KitGramError.expected. Emit it as static 4291 * data the error path points at (so it outlives the parse for the out-param). 4292 */ 4293 int has_exp = rule->first.n > 0; 4294 if (has_exp) { 4295 char* toks = tok_list(ctx, b, up, &rule->first); 4296 emit_line(out, "static const KitGramTokenKind rd_exp_%d[] = { %s };", ri, 4297 toks); 4298 xfree(ctx, toks); 4299 } 4300 emit_line(out, "static KitGramSem rd_r%d(RD *p) {", ri); 4301 if (recover) emit_line(out, " retry:"); /* re-predict after recovery */ 4302 emit_line(out, " switch (rd_look(p)) {"); 4303 for (size_t pi = 0; pi < rule->nprods; pi++) { 4304 Prod* prod = &rule->prods[pi]; 4305 rd_emit_cases(out, b, up, &prod->first); 4306 /* A nullable alternative (e.g. %empty) is also predicted by the rule's 4307 * FOLLOW set; FIRST and FOLLOW are disjoint here (LL(1)), so no dup case. 4308 */ 4309 if (prod->nullable) rd_emit_cases(out, b, up, &rule->follow); 4310 emit_line(out, " {"); 4311 if (hidden) { 4312 /* hidden synthetic rule: pass the single kid through, no 4313 * enter/exit/reduce */ 4314 emit_line(out, " KitGramSem v;"); 4315 rd_emit_one(out, b, up, &prod->syms[0], "v", " ", R); 4316 emit_line(out, " return v;"); 4317 } else if (prod->nsyms == 0) { 4318 emit_line(out, 4319 " if (p->act->enter) p->act->enter(p->ud, %s, %zu);", R, 4320 pi); 4321 emit_line( 4322 out, " KitGramSem _r = rd_reduce(p, %s, %zu, (KitGramSem *)0, 0);", 4323 R, pi); 4324 emit_line(out, " if (p->act->exit) p->act->exit(p->ud, %s, %zu);", 4325 R, pi); 4326 emit_line(out, " return _r;"); 4327 } else { 4328 emit_line(out, 4329 " if (p->act->enter) p->act->enter(p->ud, %s, %zu);", R, 4330 pi); 4331 emit_line(out, " KitGramSem k[%zu];", prod->nsyms); 4332 for (size_t si = 0; si < prod->nsyms; si++) 4333 rd_emit_factor(out, b, up, &prod->syms[si], si, R); 4334 emit_line(out, " KitGramSem _r = rd_reduce(p, %s, %zu, k, %zu);", R, 4335 pi, prod->nsyms); 4336 emit_line(out, " if (p->act->exit) p->act->exit(p->ud, %s, %zu);", 4337 R, pi); 4338 emit_line(out, " return _r;"); 4339 } 4340 emit_line(out, " }"); 4341 } 4342 /* Prediction miss. Fail-fast: rd_fail (noreturn) — the hot switch keeps its 4343 * single-return shape. Recover: skip to a sync point (cold) and abandon the 4344 * rule with a NULL value; an enclosing `*`/`?` loop re-checks FIRST and 4345 * retries. */ 4346 const char* exp = has_exp ? NULL : "(const KitGramTokenKind *)0"; 4347 if (recover) { 4348 if (has_exp) 4349 emit_line(out, 4350 " default: if (rd_recover_predict(p, rd_exp_%d, %zu, %s, " 4351 "&%sgrammar.rules[%d])) goto retry; return (KitGramSem)0;", 4352 ri, rule->first.n, R, prefix, ri); 4353 else 4354 emit_line(out, 4355 " default: if (rd_recover_predict(p, %s, 0, %s, " 4356 "&%sgrammar.rules[%d])) goto retry; return (KitGramSem)0;", 4357 exp, R, prefix, ri); 4358 } else { 4359 if (has_exp) 4360 emit_line(out, " default: rd_fail(p, rd_exp_%d, %zu, %s);", ri, 4361 rule->first.n, R); 4362 else 4363 emit_line(out, " default: rd_fail(p, %s, 0, %s);", exp, R); 4364 } 4365 emit_line(out, " }"); 4366 emit_line(out, " return (KitGramSem)0;"); 4367 emit_line(out, "}"); 4368 xfree(ctx, up); 4369 xfree(ctx, R); 4370 } 4371 4372 /* ---- token machine (%machine) emission ------------------------------------ 4373 * A %machine compiles, codegen-only, to a steppable DFA over an abstract symbol 4374 * alphabet plus an AST-directed sampler. Per machine we emit the 4375 * <prefix><name>_sym / _kind enums, the static DFA tables (class_of/trans/ 4376 * accept/live), the sym_name[] array, the fsm_* surface + whole-trace accepts, 4377 * and the gen_* sampler. It links nothing from libgram (cf. 4378 * emit_lexer_standalone). The per-machine identifier base is "<prefix><name>_" 4379 * (lowercase) with an uppercased twin for enum values. See doc/DESIGN.md "Token 4380 * machines". */ 4381 4382 static char* machine_base(GramgenContext* ctx, const char* prefix, 4383 const char* name) { 4384 return xasprintf(ctx, "%s%s_", prefix, name); 4385 } 4386 static char* machine_base_upper(GramgenContext* ctx, const char* base) { 4387 char* u = xstrdup(ctx, base); 4388 for (char* p = u; *p; p++) *p = (char)toupper((unsigned char)*p); 4389 return u; 4390 } 4391 4392 /* Emit a uint16 table body, 12 per line; UINT16_MAX -> "0xffff". */ 4393 static void emit_machine_u16(Buf* out, const uint16_t* arr, size_t n) { 4394 for (size_t i = 0; i < n; i += 12) { 4395 size_t end = i + 12 < n ? i + 12 : n; 4396 buf_append(out, " "); 4397 for (size_t j = i; j < end; j++) { 4398 if (j > i) buf_append(out, ", "); 4399 if (arr[j] == UINT16_MAX) 4400 buf_append(out, "0xffff"); 4401 else 4402 buf_append_uint(out, arr[j]); 4403 } 4404 buf_append(out, ",\n"); 4405 } 4406 } 4407 4408 static void emit_machine_decls(Builder* b, Buf* out, const char* prefix) { 4409 GramgenContext* ctx = out->ctx; 4410 for (size_t mi = 0; mi < b->nmachines; mi++) { 4411 LexDFA* m = &b->machines[mi]; 4412 char* lb = machine_base(ctx, prefix, m->name); 4413 char* ub = machine_base_upper(ctx, lb); 4414 emit_line( 4415 out, 4416 "/* token machine \"%s\": regular language over a symbol alphabet. */", 4417 m->name); 4418 emit_line(out, "typedef enum {"); 4419 for (uint16_t i = 0; i < m->nsym; i++) 4420 emit_line(out, " %s%s,", ub, m->sym_names[i]); 4421 emit_line(out, " %sSYM__COUNT", ub); 4422 emit_line(out, "} %ssym;", lb); 4423 emit_line(out, "typedef enum {"); 4424 for (size_t i = 0; i < m->nrecognizers; i++) 4425 emit_line(out, " %s%s,", ub, m->recognizers[i].name); 4426 emit_line(out, " %sKIND__COUNT", ub); 4427 emit_line(out, "} %skind;", lb); 4428 emit_line(out, "extern const char *const %ssym_name[%sSYM__COUNT];", lb, 4429 ub); 4430 emit_line(out, "typedef struct { uint16_t state; } %sfsm;", lb); 4431 emit_line(out, "void %sfsm_init(%sfsm *m);", lb, lb); 4432 emit_line(out, "KitGramFsmStatus %sfsm_step(%sfsm *m, %ssym s);", lb, lb, 4433 lb); 4434 emit_line(out, 4435 "bool %sfsm_accepting(const %sfsm *m, %skind *which);", 4436 lb, lb, lb); 4437 emit_line(out, "bool %sfsm_live(const %sfsm *m);", lb, lb); 4438 emit_line( 4439 out, 4440 "size_t %sfsm_enabled(const %sfsm *m, %ssym *out, size_t cap);", 4441 lb, lb, lb); 4442 emit_line(out, 4443 "bool %sfsm_accepts(const %ssym *trace, size_t n, " 4444 "%skind *which);", 4445 lb, lb, lb); 4446 /* AST-directed sampler: deterministic by seed; stop_prob governs each opt 4447 * and rep loop, max_repeat caps a loop, max_tokens caps the trace (-> LIMIT 4448 * on overflow). `which` selects the rule to sample (the first is the spec). 4449 */ 4450 emit_line(out, 4451 "typedef struct { uint64_t seed; double stop_prob; size_t " 4452 "max_repeat; size_t max_tokens; } %sgen_config;", 4453 lb); 4454 emit_line(out, 4455 "KitGramGenStatus %ssample(%skind which, %sgen_config *cfg, %ssym " 4456 "*out, size_t cap, size_t *n);", 4457 lb, lb, lb, lb); 4458 emit_line(out, ""); 4459 xfree(ctx, lb); 4460 xfree(ctx, ub); 4461 } 4462 } 4463 4464 /* ---- AST-directed sampler emission --------------------------------------- 4465 * The sampler walks the *desugared* recognizer AST (%def inlined, +/{n,m} 4466 * lowered, so only seq/alt/group/opt/rep + symbol/class/. leaves remain) and 4467 * emits structure-as-code: a symbol leaf emits that symbol, an alt picks a 4468 * branch, an opt/rep flips a stop coin (continue with prob 1-stop_prob, capped 4469 * by max_repeat). A trace is valid by construction. */ 4470 4471 static void emit_gen_indent(Buf* out, int ind) { 4472 for (int i = 0; i < ind; i++) buf_append(out, " "); 4473 } 4474 4475 static void emit_gen_alt(Buf* out, const char* lb, const char* ub, 4476 const LexDFA* m, const AstLexAlt* alt, int ind, 4477 int* ctr); 4478 4479 static void emit_gen_node(Buf* out, const char* lb, const char* ub, 4480 const LexDFA* m, const AstLexNode* node, int ind, 4481 int* ctr) { 4482 GramgenContext* ctx = out->ctx; 4483 switch (node->kind) { 4484 case LEX_NAME: 4485 emit_gen_indent(out, ind); 4486 emit_line(out, "%sgen_emit(c, %s%s);", lb, ub, node->value.s); 4487 break; 4488 case LEX_ANY: 4489 emit_gen_indent(out, ind); 4490 emit_line(out, 4491 "%sgen_emit(c, (%ssym)(%srng_next(&c->rng) %% %sSYM__COUNT));", 4492 lb, lb, lb, ub); 4493 break; 4494 case LEX_CLASS: { 4495 size_t nm = 0; 4496 int comp = 0; 4497 char* const* names = 4498 kit_gram_machine_class_names(ctx, node->value, node->loc, &nm, &comp); 4499 int id = (*ctr)++; 4500 /* Resolve to a concrete member list (the complement is the alphabet minus 4501 * the listed names); pick one uniformly. */ 4502 emit_gen_indent(out, ind); 4503 emit_line(out, "{"); 4504 emit_gen_indent(out, ind + 4); 4505 buf_append(out, "static const "); 4506 buf_append(out, lb); 4507 buf_append(out, "sym _cls"); 4508 buf_append_uint(out, (unsigned)id); 4509 buf_append(out, "[] = { "); 4510 size_t k = 0; 4511 if (!comp) { 4512 for (size_t j = 0; j < nm; j++) { 4513 if (k) buf_append(out, ", "); 4514 buf_append(out, ub); 4515 buf_append(out, names[j]); 4516 k++; 4517 } 4518 } else { 4519 for (uint16_t s = 0; s < m->nsym; s++) { 4520 int in = 0; 4521 for (size_t j = 0; j < nm; j++) 4522 if (strcmp(m->sym_names[s], names[j]) == 0) { 4523 in = 1; 4524 break; 4525 } 4526 if (in) continue; 4527 if (k) buf_append(out, ", "); 4528 buf_append(out, ub); 4529 buf_append(out, m->sym_names[s]); 4530 k++; 4531 } 4532 } 4533 buf_append(out, " };\n"); 4534 emit_gen_indent(out, ind + 4); 4535 emit_line(out, "%sgen_emit(c, _cls%d[%srng_next(&c->rng) %% %zuu]);", lb, 4536 id, lb, k); 4537 emit_gen_indent(out, ind); 4538 emit_line(out, "}"); 4539 xfree(ctx, (void*)names); 4540 break; 4541 } 4542 case LEX_GROUP: 4543 emit_gen_alt(out, lb, ub, m, node->alts, ind, ctr); 4544 break; 4545 case LEX_OPT: 4546 emit_gen_indent(out, ind); 4547 emit_line(out, "if (%sgen_coin(c)) {", lb); 4548 emit_gen_alt(out, lb, ub, m, node->alts, ind + 4, ctr); 4549 emit_gen_indent(out, ind); 4550 emit_line(out, "}"); 4551 break; 4552 case LEX_REP: { 4553 int id = (*ctr)++; 4554 emit_gen_indent(out, ind); 4555 emit_line(out, 4556 "for (size_t _r%d = 0; _r%d < c->max_repeat && %sgen_coin(c); " 4557 "_r%d++) {", 4558 id, id, lb, id); 4559 emit_gen_alt(out, lb, ub, m, node->alts, ind + 4, ctr); 4560 emit_gen_indent(out, ind); 4561 emit_line(out, "}"); 4562 break; 4563 } 4564 default: /* LITERAL/PROP/ANCHOR/REPEAT cannot occur post-desugar in token 4565 mode */ 4566 break; 4567 } 4568 } 4569 4570 static void emit_gen_seq(Buf* out, const char* lb, const char* ub, 4571 const LexDFA* m, const AstLexSeq* seq, int ind, 4572 int* ctr) { 4573 for (size_t i = 0; i < seq->nitems; i++) 4574 emit_gen_node(out, lb, ub, m, seq->items[i], ind, ctr); 4575 } 4576 4577 static void emit_gen_alt(Buf* out, const char* lb, const char* ub, 4578 const LexDFA* m, const AstLexAlt* alt, int ind, 4579 int* ctr) { 4580 if (alt->nseqs == 1) { 4581 emit_gen_seq(out, lb, ub, m, alt->seqs[0], ind, ctr); 4582 return; 4583 } 4584 emit_gen_indent(out, ind); 4585 emit_line(out, "switch (%srng_next(&c->rng) %% %zuu) {", lb, alt->nseqs); 4586 for (size_t i = 0; i < alt->nseqs; i++) { 4587 emit_gen_indent(out, ind); 4588 emit_line(out, "case %zu: {", i); 4589 emit_gen_seq(out, lb, ub, m, alt->seqs[i], ind + 4, ctr); 4590 emit_gen_indent(out, ind + 4); 4591 emit_line(out, "break;"); 4592 emit_gen_indent(out, ind); 4593 emit_line(out, "}"); 4594 } 4595 emit_gen_indent(out, ind); 4596 emit_line(out, "default: break;"); 4597 emit_gen_indent(out, ind); 4598 emit_line(out, "}"); 4599 } 4600 4601 static void emit_machine_sampler(Buf* out, const char* lb, const char* ub, 4602 const LexDFA* m) { 4603 /* Deterministic PRNG + the emit/coin helpers (static inline so an 4604 * alt/opt/rep-free machine does not trip -Wunused-function). */ 4605 emit_line(out, "typedef struct {"); 4606 emit_line(out, " %ssym *out; size_t cap, n, max_tokens, max_repeat;", lb); 4607 emit_line(out, " uint64_t rng; double stop_prob; int limit;"); 4608 emit_line(out, "} %sgen_ctx;", lb); 4609 emit_line(out, "static inline uint64_t %srng_next(uint64_t *s) {", lb); 4610 emit_line(out, 4611 " *s = *s * 6364136223846793005ull + 1442695040888963407ull;"); 4612 emit_line(out, 4613 " uint64_t x = *s; x ^= x >> 33; x *= 0xff51afd7ed558ccdull; x " 4614 "^= x >> 33; return x;"); 4615 emit_line(out, "}"); 4616 emit_line(out, "static inline void %sgen_emit(%sgen_ctx *c, %ssym s) {", lb, 4617 lb, lb); 4618 emit_line(out, " if (c->n >= c->max_tokens) { c->limit = 1; return; }"); 4619 emit_line(out, " if (c->n < c->cap) c->out[c->n] = s;"); 4620 emit_line(out, " c->n++;"); 4621 emit_line(out, "}"); 4622 emit_line(out, 4623 "static inline int %sgen_coin(%sgen_ctx *c) { /* continue with " 4624 "prob 1 - stop_prob */", 4625 lb, lb); 4626 emit_line(out, " uint64_t r = %srng_next(&c->rng);", lb); 4627 emit_line(out, 4628 " double u = (double)(r >> 11) * (1.0 / 9007199254740992.0);"); 4629 emit_line(out, " return u >= c->stop_prob;"); 4630 emit_line(out, "}"); 4631 for (size_t r = 0; r < m->nrecognizers; r++) { 4632 int ctr = 0; 4633 emit_line(out, "static void %sgen_%s(%sgen_ctx *c) {", lb, 4634 m->recognizers[r].name, lb); 4635 emit_gen_alt(out, lb, ub, m, m->recognizers[r].alts, 4, &ctr); 4636 emit_line(out, "}"); 4637 } 4638 emit_line(out, 4639 "KitGramGenStatus %ssample(%skind which, %sgen_config *cfg, %ssym " 4640 "*out, size_t cap, size_t *n) {", 4641 lb, lb, lb, lb); 4642 /* Reference the helpers so a machine with no alt/opt/rep leaf (which would 4643 * not call coin/rng) does not trip -Wunused-function. */ 4644 emit_line(out, " (void)%srng_next; (void)%sgen_emit; (void)%sgen_coin;", 4645 lb, lb, lb); 4646 emit_line(out, 4647 " %sgen_ctx c = { .out = out, .cap = cap, .n = 0, .max_tokens = " 4648 "cfg->max_tokens,", 4649 lb); 4650 emit_line(out, 4651 " .max_repeat = cfg->max_repeat, .rng = " 4652 "cfg->seed, .stop_prob = cfg->stop_prob, .limit = 0 };"); 4653 emit_line(out, " switch (which) {"); 4654 for (size_t r = 0; r < m->nrecognizers; r++) 4655 emit_line(out, " case %s%s: %sgen_%s(&c); break;", ub, 4656 m->recognizers[r].name, lb, m->recognizers[r].name); 4657 emit_line(out, " default: break;"); 4658 emit_line(out, " }"); 4659 emit_line(out, " cfg->seed = c.rng;"); 4660 emit_line(out, " if (n) *n = c.n;"); 4661 emit_line(out, " return c.limit ? KIT_GRAM_GEN_LIMIT : KIT_GRAM_GEN_DONE;"); 4662 emit_line(out, "}"); 4663 } 4664 4665 static void emit_machines(Builder* b, Buf* out, const char* prefix) { 4666 GramgenContext* ctx = out->ctx; 4667 for (size_t mi = 0; mi < b->nmachines; mi++) { 4668 LexDFA* m = &b->machines[mi]; 4669 char* lb = machine_base(ctx, prefix, m->name); 4670 char* ub = machine_base_upper(ctx, lb); 4671 size_t nc = m->nclasses; 4672 emit_line(out, "/* ---- token machine \"%s\" (%%machine) ---- */", m->name); 4673 4674 emit_line(out, "const char *const %ssym_name[%sSYM__COUNT] = {", lb, ub); 4675 for (uint16_t i = 0; i < m->nsym; i++) { 4676 char* q = quote_len(ctx, m->sym_names[i], strlen(m->sym_names[i]), false); 4677 emit_line(out, " %s,", q); 4678 xfree(ctx, q); 4679 } 4680 emit_line(out, "};"); 4681 4682 emit_line(out, "static const uint16_t %sclass_of[%sSYM__COUNT] = {", lb, 4683 ub); 4684 emit_machine_u16(out, m->sym_class_of, m->nsym); 4685 emit_line(out, "};"); 4686 4687 emit_line(out, "static const uint16_t %strans[] = {", lb); 4688 emit_machine_u16(out, m->trans, (size_t)m->nstates * nc); 4689 emit_line(out, "};"); 4690 4691 emit_line(out, "static const uint16_t %saccept[] = {", lb); 4692 emit_machine_u16(out, m->accept, m->nstates); 4693 emit_line(out, "};"); 4694 4695 emit_line(out, "static const uint8_t %slive[] = {", lb); 4696 for (size_t i = 0; i < m->nstates; i += 24) { 4697 size_t end = i + 24 < m->nstates ? i + 24 : m->nstates; 4698 buf_append(out, " "); 4699 for (size_t j = i; j < end; j++) { 4700 if (j > i) buf_append(out, ", "); 4701 buf_append_uint(out, m->live[j] ? 1u : 0u); 4702 } 4703 buf_append(out, ",\n"); 4704 } 4705 emit_line(out, "};"); 4706 emit_line(out, ""); 4707 4708 emit_line(out, "void %sfsm_init(%sfsm *m) { m->state = 0; }", lb, lb); 4709 emit_line(out, "KitGramFsmStatus %sfsm_step(%sfsm *m, %ssym s) {", lb, lb, 4710 lb); 4711 emit_line( 4712 out, 4713 " uint16_t nx = %strans[(size_t)m->state * %zuu + %sclass_of[s]];", 4714 lb, nc, lb); 4715 emit_line(out, " if (nx == 0xffffu) return KIT_GRAM_FSM_DEAD;"); 4716 emit_line(out, " m->state = nx; return KIT_GRAM_FSM_OK;"); 4717 emit_line(out, "}"); 4718 emit_line(out, "bool %sfsm_accepting(const %sfsm *m, %skind *which) {", lb, 4719 lb, lb); 4720 emit_line(out, " uint16_t a = %saccept[m->state];", lb); 4721 emit_line(out, " if (a == 0xffffu) return false;"); 4722 emit_line(out, " if (which) *which = (%skind)a;", lb); 4723 emit_line(out, " return true;"); 4724 emit_line(out, "}"); 4725 emit_line( 4726 out, 4727 "bool %sfsm_live(const %sfsm *m) { return %slive[m->state] != 0; }", lb, 4728 lb, lb); 4729 emit_line(out, 4730 "size_t %sfsm_enabled(const %sfsm *m, %ssym *out, size_t cap) {", 4731 lb, lb, lb); 4732 emit_line(out, " size_t n = 0;"); 4733 emit_line(out, " for (uint16_t s = 0; s < %sSYM__COUNT; s++)", ub); 4734 emit_line(out, 4735 " if (%strans[(size_t)m->state * %zuu + %sclass_of[s]] != " 4736 "0xffffu) {", 4737 lb, nc, lb); 4738 emit_line(out, " if (n < cap) out[n] = (%ssym)s;", lb); 4739 emit_line(out, " n++;"); 4740 emit_line(out, " }"); 4741 emit_line(out, " return n;"); 4742 emit_line(out, "}"); 4743 emit_line( 4744 out, 4745 "bool %sfsm_accepts(const %ssym *trace, size_t n, %skind *which) {", lb, 4746 lb, lb); 4747 emit_line(out, " %sfsm m; %sfsm_init(&m);", lb, lb); 4748 emit_line(out, " for (size_t i = 0; i < n; i++)"); 4749 emit_line( 4750 out, 4751 " if (%sfsm_step(&m, trace[i]) == KIT_GRAM_FSM_DEAD) return false;", 4752 lb); 4753 emit_line(out, " return %sfsm_accepting(&m, which);", lb); 4754 emit_line(out, "}"); 4755 emit_line(out, ""); 4756 emit_machine_sampler(out, lb, ub, m); 4757 emit_line(out, ""); 4758 xfree(ctx, lb); 4759 xfree(ctx, ub); 4760 } 4761 } 4762 4763 static void emit_parser_codegen(Builder* b, Buf* out, const char* prefix) { 4764 if (!parser_codegen_ok(b)) return; 4765 GramgenContext* ctx = out->ctx; 4766 char* up = make_upper_prefix(ctx, prefix); 4767 /* Fusion target: when the main lexer also has a generated scanner, the parser 4768 * pulls its lookahead by running that scanner inline (no KitGramToken vector, 4769 * no kit_gram_lexer_next/kit_gram_parser_push boundary). Both the buffered and fused 4770 * entries share the rule functions; only the `refill` hook that loads the 4771 * lookahead differs. */ 4772 LexDFA* mainlex = NULL; 4773 for (size_t i = 0; i < b->nlex_dfas; i++) 4774 if (lex_name_is_main_c(b->lex_dfas[i].name)) { 4775 mainlex = &b->lex_dfas[i]; 4776 break; 4777 } 4778 /* Fusion drives the standalone table-free scanner inline — no KitGramToken 4779 * vector, no kit_gram_lexer_next/kit_gram_parser_push boundary, no KitGramLexGrammar. 4780 * It requires the standalone lexer; --parser-codegen on its own emits only 4781 * the buffered parse_rd. */ 4782 int standalone = b->lexer_standalone && mainlex; 4783 int fused = standalone; 4784 int recover = b->parser_recover; 4785 int lex_utf8 = mainlex && mainlex->input == KIT_GRAM_LEX_INPUT_UTF8; 4786 int lex_has_start = mainlex && (mainlex->start_text || mainlex->start_line); 4787 const char* ML = b->multiline ? "1" : "0"; 4788 char* scan_sym = fused ? lex_scan_symbol(ctx, prefix, "main") : NULL; 4789 4790 emit_line(out, ""); 4791 emit_line(out, 4792 "/* ---- pull recursive-descent parser (--parser-codegen) ---- */"); 4793 emit_line(out, "#include <setjmp.h>"); 4794 emit_line(out, ""); 4795 emit_line(out, "typedef struct RD RD;"); 4796 emit_line(out, "struct RD {"); 4797 emit_line( 4798 out, 4799 " KitGramToken look; /* current lookahead token */"); 4800 emit_line(out, " const KitGramActions *act;"); 4801 emit_line(out, " void *ud;"); 4802 emit_line(out, " jmp_buf err;"); 4803 emit_line(out, 4804 " KitGramError e; /* error detail, filled on " 4805 "failure */"); 4806 emit_line(out, 4807 " size_t nerrors; /* errors reported (incl. " 4808 "recovered) */"); 4809 emit_line( 4810 out, 4811 " void (*refill)(RD *); /* load the next token into look */"); 4812 emit_line(out, 4813 " const KitGramToken *toks; /* buffered source " 4814 " */"); 4815 emit_line(out, " size_t ntok, pos;"); 4816 emit_line( 4817 out, 4818 " const unsigned char *cur, *end; /* fused (byte) source */"); 4819 emit_line(out, " uint32_t line, col;"); 4820 if (standalone && lex_utf8) 4821 emit_line( 4822 out, 4823 " bool after_cr; /* UTF-8 CRLF coalescing state */"); 4824 emit_line(out, "};"); 4825 emit_line(out, ""); 4826 /* rd_self[k] == k, so &rd_self[k] is a one-element expected set {k} backed by 4827 * static storage (safe to hand back through the error out-param). */ 4828 emit_line(out, "static const KitGramTokenKind rd_self[] = {"); 4829 for (size_t i = 0; i < b->ntokens; i += 16) { 4830 buf_append(out, " "); 4831 for (size_t j = i; j < i + 16 && j < b->ntokens; j++) { 4832 if (j > i) buf_append(out, ", "); 4833 buf_append_uint(out, (unsigned)j); 4834 } 4835 buf_append(out, ",\n"); 4836 } 4837 emit_line(out, "};"); 4838 emit_line(out, 4839 "static KitGramTokenKind rd_look(RD *p) { return p->look.kind; }"); 4840 /* Always-abort failure (lex errors, and every parse error in fail-fast mode). 4841 * In recovery mode parse errors go through rd_error instead, so rd_fail is 4842 * needed only for the fused/standalone refill's lex-error path. */ 4843 if (!recover || fused) { 4844 emit_line(out, 4845 "static _Noreturn void rd_fail(RD *p, const KitGramTokenKind *exp, " 4846 "size_t nexp, KitGramRuleId in_rule) {"); 4847 emit_line(out, 4848 " p->e.found = p->look; p->e.expected = exp; p->e.nexpected = " 4849 "nexp; p->e.in_rule = in_rule;"); 4850 emit_line(out, " p->nerrors++;"); 4851 emit_line(out, " if (p->act->on_error) p->act->on_error(p->ud, &p->e);"); 4852 emit_line(out, " longjmp(p->err, 1);"); 4853 emit_line(out, "}"); 4854 } 4855 if (recover) { 4856 /* Recovery (opt-in). Report the error and act on the on_error return: ABORT 4857 * (the default with no on_error bound) longjmps; SKIP discards the 4858 * lookahead and retries the same expectation; RESYNC skips to a sync set. 4859 * No stack unwinding. Driven by the lookahead only — no hidden state. */ 4860 emit_line(out, 4861 "static int rd_in(const KitGramTokenKind *s, uint16_t n, " 4862 "KitGramTokenKind k) {"); 4863 emit_line(out, 4864 " for (uint16_t i = 0; i < n; i++) if (s[i] == k) return 1;"); 4865 emit_line(out, " return 0;"); 4866 emit_line(out, "}"); 4867 emit_line( 4868 out, 4869 "static KitGramErrorAction rd_error(RD *p, const KitGramTokenKind *exp, " 4870 "size_t nexp, KitGramRuleId in_rule) {"); 4871 emit_line(out, 4872 " p->e.found = p->look; p->e.expected = exp; p->e.nexpected = " 4873 "nexp; p->e.in_rule = in_rule;"); 4874 emit_line(out, " p->nerrors++;"); 4875 emit_line(out, 4876 " KitGramErrorAction a = p->act->on_error ? " 4877 "p->act->on_error(p->ud, &p->e) : KIT_GRAM_ABORT;"); 4878 emit_line(out, " if (a == KIT_GRAM_ABORT) longjmp(p->err, 1);"); 4879 emit_line(out, " return a;"); 4880 emit_line(out, "}"); 4881 /* Recover a terminal mismatch: SKIP discards and retries; RESYNC skips to k 4882 * or to a token that can follow the rule, then matches k or returns NULL. 4883 */ 4884 emit_line(out, 4885 "static KitGramSem rd_match(RD *p, KitGramTokenKind k, KitGramRuleId " 4886 "in_rule) {"); 4887 emit_line(out, " while (p->look.kind != k) {"); 4888 emit_line( 4889 out, 4890 " if (rd_error(p, &rd_self[k], 1, in_rule) == KIT_GRAM_SKIP) { " 4891 "p->refill(p); continue; }"); 4892 emit_line(out, " const KitGramRule *_r = &%sgrammar.rules[in_rule];", 4893 prefix); 4894 emit_line( 4895 out, " while (p->look.kind != k && p->look.kind != %sTOK_EOF &&", 4896 up); 4897 emit_line(out, 4898 " !rd_in(_r->follow, _r->nfollow, p->look.kind)) " 4899 "p->refill(p);"); 4900 emit_line(out, 4901 " if (p->look.kind != k) return (KitGramSem)0; /* k " 4902 "missing; resume the rule */"); 4903 emit_line(out, " break;"); 4904 emit_line(out, " }"); 4905 emit_line(out, " KitGramToken t = p->look;"); 4906 emit_line(out, " p->refill(p);"); 4907 emit_line(out, 4908 " KitGramSem v = p->act->lift_token ? p->act->lift_token(p->ud, " 4909 "t) : (KitGramSem)0;"); 4910 emit_line(out, " if (p->act->on_token) p->act->on_token(p->ud, t);"); 4911 emit_line(out, " return v;"); 4912 emit_line(out, "}"); 4913 /* Recover a prediction miss: returns 1 to re-predict (SKIP, or RESYNC 4914 * landed on a token that can start the rule) or 0 to abandon the rule with 4915 * NULL. */ 4916 emit_line(out, 4917 "static int rd_recover_predict(RD *p, const KitGramTokenKind *exp, " 4918 "size_t nexp, KitGramRuleId in_rule, const KitGramRule *_r) {"); 4919 emit_line(out, 4920 " if (rd_error(p, exp, nexp, in_rule) == KIT_GRAM_SKIP) { " 4921 "p->refill(p); return 1; }"); 4922 emit_line(out, 4923 " while (p->look.kind != %sTOK_EOF && !rd_in(_r->first, " 4924 "_r->nfirst, p->look.kind) &&", 4925 up); 4926 emit_line(out, 4927 " !rd_in(_r->follow, _r->nfollow, p->look.kind)) " 4928 "p->refill(p);"); 4929 emit_line(out, " return rd_in(_r->first, _r->nfirst, p->look.kind);"); 4930 emit_line(out, "}"); 4931 } else { 4932 /* Fail-fast: a mismatch never returns (rd_fail longjmps), so the compiler 4933 * optimizes the hot path with a single return — no recovery overhead. */ 4934 emit_line(out, 4935 "static KitGramSem rd_match(RD *p, KitGramTokenKind k, KitGramRuleId " 4936 "in_rule) {"); 4937 emit_line( 4938 out, " if (p->look.kind != k) rd_fail(p, &rd_self[k], 1, in_rule);"); 4939 emit_line(out, " KitGramToken t = p->look;"); 4940 emit_line(out, " p->refill(p);"); 4941 emit_line(out, 4942 " KitGramSem v = p->act->lift_token ? p->act->lift_token(p->ud, " 4943 "t) : (KitGramSem)0;"); 4944 emit_line(out, " if (p->act->on_token) p->act->on_token(p->ud, t);"); 4945 emit_line(out, " return v;"); 4946 emit_line(out, "}"); 4947 } 4948 emit_line( 4949 out, 4950 "static KitGramSem rd_reduce(RD *p, KitGramRuleId r, int prod, KitGramSem " 4951 "*kids, size_t n) {"); 4952 emit_line(out, 4953 " return p->act->reduce ? p->act->reduce(p->ud, r, prod, kids, " 4954 "n) : (KitGramSem)0;"); 4955 emit_line(out, "}"); 4956 emit_line(out, "static void rd_refill_buf(RD *p) {"); 4957 emit_line(out, " if (p->pos < p->ntok) p->look = p->toks[p->pos++];"); 4958 emit_line(out, " else p->look = (KitGramToken){ .kind = %sTOK_EOF };", up); 4959 emit_line(out, "}"); 4960 if (standalone) { 4961 /* Self-contained refill: drives the table-free scanner (kind+skip and the 4962 * %keywords rewrite baked in, end-context decided inline), so there is no 4963 * KitGramLexGrammar and no runtime-lexer dependency. */ 4964 emit_line(out, "static void rd_refill_lex(RD *p) {"); 4965 emit_line(out, " for (;;) {"); 4966 emit_line(out, " if (p->cur == p->end) {"); 4967 emit_line( 4968 out, 4969 " p->look = (KitGramToken){ .kind = %sTOK_EOF, .lexeme = " 4970 "(const char *)p->cur,", 4971 up); 4972 emit_line( 4973 out, 4974 " .line = p->line, .col = p->col };"); 4975 emit_line(out, " return;"); 4976 emit_line(out, " }"); 4977 emit_line(out, " uint16_t start = 0;"); 4978 if (lex_has_start) { 4979 emit_line(out, " if (p->line == 1u && p->col == 1u) start = %u;", 4980 (unsigned)mainlex->start_text); 4981 if (mainlex->start_line) 4982 emit_line(out, " else if (%s && p->col == 1u) start = %u;", ML, 4983 (unsigned)mainlex->start_line); 4984 } 4985 emit_line(out, " %ssres r = %s(start, p->cur, p->end, %s);", prefix, 4986 scan_sym, ML); 4987 emit_line(out, 4988 " if (!r.found) { /* lex error at " 4989 "the cursor */"); 4990 emit_line( 4991 out, 4992 " p->look = (KitGramToken){ .kind = %sTOK_EOF, .lexeme = " 4993 "(const char *)p->cur,", 4994 up); 4995 emit_line(out, 4996 " .len = (size_t)(p->cur < " 4997 "p->end), .line = p->line, .col = p->col };"); 4998 emit_line(out, " rd_fail(p, (const KitGramTokenKind *)0, 0, 0);"); 4999 emit_line(out, " }"); 5000 emit_line(out, " const unsigned char *lex = p->cur;"); 5001 emit_line(out, " uint32_t tline = p->line, tcol = p->col;"); 5002 if (lex_utf8) { 5003 emit_line( 5004 out, 5005 " %sfold_pos(&p->line, &p->col, &p->after_cr, lex, r.len);", 5006 prefix); 5007 } else { 5008 emit_line(out, " p->line += (uint32_t)r.nlines;"); 5009 emit_line(out, 5010 " p->col = r.last_nl ? (uint32_t)(1u + (r.len - " 5011 "r.last_nl)) : p->col + (uint32_t)r.len;"); 5012 } 5013 emit_line(out, " p->cur += r.len;"); 5014 emit_line(out, " if (r.skip) continue;"); 5015 emit_line( 5016 out, 5017 " p->look = (KitGramToken){ .kind = r.kind, .lexeme = (const " 5018 "char *)lex, .len = r.len,"); 5019 emit_line(out, 5020 " .line = tline, .col = tcol };"); 5021 emit_line(out, " return;"); 5022 emit_line(out, " }"); 5023 emit_line(out, "}"); 5024 } 5025 emit_line(out, ""); 5026 for (size_t ri = 0; ri < b->nrules; ri++) 5027 emit_line(out, "static KitGramSem rd_r%zu(RD *p);", ri); 5028 emit_line(out, ""); 5029 for (size_t ri = 0; ri < b->nrules; ri++) { 5030 rd_emit_rule(out, b, (int)ri, prefix, recover); 5031 emit_line(out, ""); 5032 } 5033 emit_line(out, 5034 "KitGramSem %sparse_rd(const KitGramToken *toks, size_t ntok, const " 5035 "KitGramActions *act, void *ud, KitGramError *err, int *ok) {", 5036 prefix); 5037 emit_line(out, 5038 " RD p = { .act = act, .ud = ud, .refill = rd_refill_buf, .toks " 5039 "= toks, .ntok = ntok };"); 5040 emit_line(out, 5041 " if (setjmp(p.err)) { if (err) *err = p.e; if (ok) *ok = 0; " 5042 "return (KitGramSem)0; }"); 5043 emit_line(out, " p.refill(&p);"); 5044 emit_line(out, " KitGramSem r = rd_r0(&p);"); 5045 if (recover) { 5046 emit_line(out, 5047 " if (p.look.kind != %sTOK_EOF) { /* trailing " 5048 "tokens */", 5049 up); 5050 emit_line(out, 5051 " (void)rd_error(&p, &rd_self[%sTOK_EOF], 1, 0); /* " 5052 "aborts unless recovering */", 5053 up); 5054 emit_line(out, " while (p.look.kind != %sTOK_EOF) p.refill(&p);", 5055 up); 5056 emit_line(out, " }"); 5057 } else { 5058 emit_line( 5059 out, 5060 " if (p.look.kind != %sTOK_EOF) rd_fail(&p, &rd_self[%sTOK_EOF], " 5061 "1, 0); /* trailing tokens */", 5062 up, up); 5063 } 5064 emit_line(out, " if (ok) *ok = 1;"); 5065 emit_line(out, " return r;"); 5066 emit_line(out, "}"); 5067 if (fused) { 5068 emit_line(out, ""); 5069 emit_line(out, 5070 "KitGramSem %sparse_fused(const unsigned char *buf, size_t len, " 5071 "const KitGramActions *act, void *ud, KitGramError *err, int *ok) {", 5072 prefix); 5073 emit_line(out, 5074 " RD p = { .act = act, .ud = ud, .refill = rd_refill_lex,"); 5075 emit_line( 5076 out, 5077 " .cur = buf, .end = buf + len, .line = 1, .col = 1 };"); 5078 emit_line(out, 5079 " if (setjmp(p.err)) { if (err) *err = p.e; if (ok) *ok = 0; " 5080 "return (KitGramSem)0; }"); 5081 emit_line(out, " p.refill(&p);"); 5082 emit_line(out, " KitGramSem r = rd_r0(&p);"); 5083 if (recover) { 5084 emit_line(out, " if (p.look.kind != %sTOK_EOF) {", up); 5085 emit_line(out, " (void)rd_error(&p, &rd_self[%sTOK_EOF], 1, 0);", 5086 up); 5087 emit_line(out, " while (p.look.kind != %sTOK_EOF) p.refill(&p);", 5088 up); 5089 emit_line(out, " }"); 5090 } else { 5091 emit_line(out, 5092 " if (p.look.kind != %sTOK_EOF) rd_fail(&p, " 5093 "&rd_self[%sTOK_EOF], 1, 0);", 5094 up, up); 5095 } 5096 emit_line(out, " if (ok) *ok = 1;"); 5097 emit_line(out, " return r;"); 5098 emit_line(out, "}"); 5099 } 5100 if (scan_sym) xfree(ctx, scan_sym); 5101 xfree(ctx, up); 5102 } 5103 5104 static void emit_c_text(Buf* out, Builder* b, const char* grammar_path, 5105 const char* header_path, const char* prefix) { 5106 GramgenContext* ctx = out->ctx; 5107 size_t hidden_count = b->nrules - b->public_count; 5108 emit_line(out, "/* Generated by gramgen from %s. */", 5109 base_name(grammar_path)); 5110 emit_line(out, "#include \"%s\"", base_name(header_path)); 5111 /* Machine-only file: emit just the codegen-only token machines, table-free 5112 * (no parser grammar, no gramparse_tables.h / gramlex_tables.h contract). */ 5113 if (b->public_count == 0 && b->nmachines) { 5114 emit_line(out, ""); 5115 emit_machines(b, out, prefix); 5116 return; 5117 } 5118 emit_line(out, "#include <kit/support/gram_parse_tables.h>"); 5119 /* Standalone mode emits no lexer table, so it normally needs none of the 5120 * table contract (KitGramLexGrammar / KitGramLexAccept). The exception is a 5121 * standalone lexer that keeps %keywords in a minimal perfect hash (the 5122 * default): it reuses the header-only KitGramLexKeyword record and 5123 * kit_gram_lex_kw_hash64 so its inline lookup matches the construction 5124 * byte-for-byte, with no runtime link dependency. */ 5125 int sa_kw_tables = 0; 5126 if (b->lexer_standalone) 5127 for (size_t i = 0; i < b->nlex_dfas; i++) 5128 if (b->lex_dfas[i].nkeyword_tables) { 5129 sa_kw_tables = 1; 5130 break; 5131 } 5132 if (b->nlex_dfas && (!b->lexer_standalone || sa_kw_tables)) 5133 emit_line(out, "#include <kit/support/gram_lex_tables.h>"); 5134 emit_line(out, ""); 5135 char* up = make_upper_prefix(ctx, prefix); 5136 if (hidden_count) { 5137 emit_line(out, "enum {"); 5138 for (size_t i = 0; i < hidden_count; i++) { 5139 if (i == 0) 5140 emit_line(out, " %sR__SYN_0 = %sR__COUNT,", up, up); 5141 else 5142 emit_line(out, " %sR__SYN_%zu,", up, i); 5143 } 5144 emit_line(out, " %sR__ALL_COUNT", up); 5145 emit_line(out, "};"); 5146 emit_line(out, ""); 5147 } 5148 emit_sets(b, out, up); 5149 emit_sub_arrays(b, out, up); 5150 emit_rule_arrays(b, out, up); 5151 emit_first_follow(b, out, up); 5152 emit_line(out, "#define N(arr) (uint16_t)(sizeof(arr) / sizeof((arr)[0]))"); 5153 emit_line(out, ""); 5154 emit_pratt_tables(b, out, up); 5155 emit_rules(b, out, up); 5156 emit_line(out, "static const char *const g_tok_names[] = {"); 5157 Buf names; 5158 buf_init(ctx, &names); 5159 for (size_t i = 0; i < b->ntokens; i++) { 5160 if (i) buf_append(&names, ", "); 5161 char* q = c_string_str(ctx, b->tokens[i].display); 5162 buf_append(&names, q); 5163 xfree(ctx, q); 5164 } 5165 emit_line(out, " %s", names.s); 5166 emit_line(out, "};"); 5167 emit_line(out, ""); 5168 char* stem = path_stem(ctx, grammar_path); 5169 char* stem_q = quote_len(ctx, stem, strlen(stem), false); 5170 const char* nrules = hidden_count ? xasprintf(ctx, "%sR__ALL_COUNT", up) 5171 : xasprintf(ctx, "%sR__COUNT", up); 5172 const char* sets_ptr = b->nsets ? "g_sets" : "NULL"; 5173 const char* off_ptr = b->nsets ? "g_set_off" : "NULL"; 5174 char* start = rule_ref(ctx, b, up, 0); 5175 emit_line(out, "const KitGramGrammar %sgrammar = {", prefix); 5176 emit_line(out, " .name = %s,", stem_q); 5177 emit_line(out, " .rules = g_rules, .nrules = %s,", nrules); 5178 emit_line(out, " .start = %s, .eof = %sTOK_EOF,", start, up); 5179 emit_line(out, " .sets = %s, .set_off = %s, .nsets = %zu,", sets_ptr, 5180 off_ptr, b->nsets); 5181 emit_line(out, " .tok_names = g_tok_names, .ntoks = %sTOK__COUNT,", up); 5182 emit_line(out, "};"); 5183 emit_line(out, ""); 5184 emit_line(out, 5185 "void %sstack_bounds(size_t max_depth, size_t* ctl_cap, size_t* " 5186 "val_cap) {", 5187 prefix); 5188 emit_line(out, 5189 " kit_gram_stack_bounds(&%sgrammar, max_depth, ctl_cap, val_cap);", 5190 prefix); 5191 emit_line(out, "}"); 5192 emit_line(out, "size_t %sgenerate_scratch_count(void) {", prefix); 5193 emit_line(out, " return kit_gram_parser_generate_scratch_count(&%sgrammar);", 5194 prefix); 5195 emit_line(out, "}"); 5196 emit_line(out, ""); 5197 emit_line(out, 5198 "void %sparser_init(KitGramParser *mem, const KitGramConfig *cfg) {", 5199 prefix); 5200 emit_line(out, " kit_gram_parser_init(mem, &%sgrammar, cfg);", prefix); 5201 emit_line(out, "}"); 5202 if (b->nlex_dfas) emit_line(out, ""); 5203 if (b->lexer_standalone) { 5204 emit_lexer_standalone(b, out, prefix); 5205 } else { 5206 emit_lexer_tables(b, out, grammar_path, prefix); 5207 } 5208 if (b->parser_codegen) emit_parser_codegen(b, out, prefix); 5209 if (b->nmachines) { 5210 emit_line(out, ""); 5211 emit_machines(b, out, prefix); 5212 } 5213 xfree(ctx, up); 5214 xfree(ctx, stem); 5215 xfree(ctx, stem_q); 5216 xfree(ctx, start); 5217 } 5218 5219 /* One materialized %lex block: its KitGramLexGrammar plus the table arrays the 5220 * grammar points into (all arena-allocated, reclaimed wholesale at 5221 * mem_release). The main block and every named sub-lexer get one of these. */ 5222 typedef struct { 5223 const char* name; /* "main" for the main block, else the %lex <name> */ 5224 int is_main; 5225 KitGramLexGrammar grammar; 5226 KitGramLexAccept* accepts; 5227 uint16_t* accept; 5228 uint16_t* accept_text; 5229 uint16_t* accept_line; 5230 uint16_t* trans; 5231 KitGramLexKeywordTable* keyword_tables; 5232 KitGramLexKeyword* keywords; 5233 uint32_t* kw_seeds; 5234 } LexMat; 5235 5236 struct KitGramCompiled { 5237 GramgenMem mem; 5238 const KitContext* kit; /* caller context, for kit_gram_emit_c diagnostics */ 5239 Builder* b; 5240 char* path; 5241 char* name; 5242 5243 KitGramGrammar grammar; 5244 KitGramRule* rules; 5245 KitGramSym** prod_syms; 5246 size_t nprod_syms; 5247 KitGramProd** rule_prods; 5248 KitGramSym* wrapper_subs; 5249 KitGramPratt* pratts; 5250 KitGramPrattOp** pratt_ops; 5251 KitGramTokenKind** first_arrays; 5252 KitGramTokenKind** follow_arrays; 5253 uint8_t** predict_arrays; 5254 KitGramTokenKind* sets_flat; 5255 uint16_t* set_off; 5256 const char** tok_names; 5257 5258 int has_lex; 5259 /* One materialized lexer per %lex block (main + named sub-lexers), in source 5260 * order with the main block first. Each owns its table arrays in the arena. 5261 */ 5262 LexMat* lexers; 5263 size_t nlexers; 5264 size_t main_lexer; /* index of the main block in lexers[] */ 5265 }; 5266 5267 static KitGramTokenKind* copy_intset_toks(GramgenContext* ctx, const IntSet* set) { 5268 if (!set->n) return NULL; 5269 int* sorted = intset_sorted(ctx, set); 5270 KitGramTokenKind* out = xmalloc(ctx, set->n * sizeof *out); 5271 for (size_t i = 0; i < set->n; i++) out[i] = (KitGramTokenKind)sorted[i]; 5272 xfree(ctx, sorted); 5273 return out; 5274 } 5275 5276 static KitGramSym runtime_sym(KitGramCompiled* c, const Sym* sym) { 5277 switch (sym->kind) { 5278 case SYM_TERM: 5279 return (KitGramSym){.kind = KIT_GRAM_S_TERM, .tok = (KitGramTokenKind)sym->value}; 5280 case SYM_RULE: 5281 return (KitGramSym){.kind = KIT_GRAM_S_RULE, .rule = (KitGramRuleId)sym->value}; 5282 case SYM_REP: 5283 case SYM_OPT: 5284 return (KitGramSym){ 5285 .kind = sym->kind == SYM_REP ? KIT_GRAM_S_REP : KIT_GRAM_S_OPT, 5286 .first = (uint16_t)sym->set_index, 5287 .sub = &c->wrapper_subs[sym->sub_array_id], 5288 .nsub = 1, 5289 }; 5290 } 5291 return (KitGramSym){0}; 5292 } 5293 5294 static uint8_t* build_predict_array(GramgenContext* ctx, Rule* rule, 5295 size_t ntokens) { 5296 if (rule->pratt || !rule->first.n) return NULL; 5297 int* prod_for_tok = xmalloc(ctx, ntokens * sizeof *prod_for_tok); 5298 for (size_t i = 0; i < ntokens; i++) prod_for_tok[i] = -1; 5299 for (size_t pi = 0; pi < rule->nprods; pi++) { 5300 Prod* prod = &rule->prods[pi]; 5301 for (size_t ti = 0; ti < prod->first.n; ti++) { 5302 int tok = prod->first.v[ti]; 5303 if (tok >= 0 && (size_t)tok < ntokens) prod_for_tok[tok] = (int)pi; 5304 } 5305 } 5306 int* sorted = intset_sorted(ctx, &rule->first); 5307 uint8_t* out = xmalloc(ctx, rule->first.n * sizeof *out); 5308 for (size_t ti = 0; ti < rule->first.n; ti++) { 5309 int tok = sorted[ti]; 5310 int prod_idx = (tok >= 0 && (size_t)tok < ntokens) ? prod_for_tok[tok] : 0; 5311 if (prod_idx < 0) prod_idx = 0; 5312 out[ti] = (uint8_t)prod_idx; 5313 } 5314 xfree(ctx, prod_for_tok); 5315 xfree(ctx, sorted); 5316 return out; 5317 } 5318 5319 static void materialize_sets(GramgenContext* ctx, KitGramCompiled* c) { 5320 Builder* b = c->b; 5321 if (!b->nsets) return; 5322 size_t nflat = 0; 5323 for (size_t i = 0; i < b->nsets; i++) nflat += b->sets[i].n; 5324 c->sets_flat = nflat ? xmalloc(ctx, nflat * sizeof *c->sets_flat) : NULL; 5325 c->set_off = xmalloc(ctx, (b->nsets + 1) * sizeof *c->set_off); 5326 size_t off = 0; 5327 c->set_off[0] = 0; 5328 for (size_t i = 0; i < b->nsets; i++) { 5329 int* sorted = intset_sorted(ctx, &b->sets[i]); 5330 for (size_t j = 0; j < b->sets[i].n; j++) 5331 c->sets_flat[off++] = (KitGramTokenKind)sorted[j]; 5332 c->set_off[i + 1] = (uint16_t)off; 5333 xfree(ctx, sorted); 5334 } 5335 } 5336 5337 static void materialize_parser(GramgenContext* ctx, KitGramCompiled* c) { 5338 Builder* b = c->b; 5339 materialize_sets(ctx, c); 5340 5341 if (b->nwrapper_syms) { 5342 c->wrapper_subs = xmalloc(ctx, b->nwrapper_syms * sizeof *c->wrapper_subs); 5343 for (size_t i = 0; i < b->nwrapper_syms; i++) 5344 c->wrapper_subs[i] = runtime_sym(c, b->wrapper_syms[i]->sub); 5345 } 5346 5347 c->rules = xcalloc(ctx, b->nrules, sizeof *c->rules); 5348 c->rule_prods = xcalloc(ctx, b->nrules, sizeof *c->rule_prods); 5349 c->first_arrays = xcalloc(ctx, b->nrules, sizeof *c->first_arrays); 5350 c->follow_arrays = xcalloc(ctx, b->nrules, sizeof *c->follow_arrays); 5351 c->predict_arrays = xcalloc(ctx, b->nrules, sizeof *c->predict_arrays); 5352 c->pratts = xcalloc(ctx, b->nrules, sizeof *c->pratts); 5353 c->pratt_ops = xcalloc(ctx, b->nrules, sizeof *c->pratt_ops); 5354 c->tok_names = xmalloc(ctx, b->ntokens * sizeof *c->tok_names); 5355 for (size_t i = 0; i < b->ntokens; i++) 5356 c->tok_names[i] = b->tokens[i].display.s; 5357 5358 size_t total_prods = 0; 5359 for (size_t ri = 0; ri < b->nrules; ri++) total_prods += b->rules[ri].nprods; 5360 c->prod_syms = 5361 total_prods ? xcalloc(ctx, total_prods, sizeof *c->prod_syms) : NULL; 5362 5363 size_t prod_array_idx = 0; 5364 for (size_t ri = 0; ri < b->nrules; ri++) { 5365 Rule* rule = &b->rules[ri]; 5366 if (!rule->pratt && rule->nprods) { 5367 c->rule_prods[ri] = xcalloc(ctx, rule->nprods, sizeof *c->rule_prods[ri]); 5368 for (size_t pi = 0; pi < rule->nprods; pi++) { 5369 Prod* prod = &rule->prods[pi]; 5370 if (prod->nsyms) { 5371 KitGramSym* syms = xmalloc(ctx, prod->nsyms * sizeof *syms); 5372 for (size_t si = 0; si < prod->nsyms; si++) 5373 syms[si] = runtime_sym(c, &prod->syms[si]); 5374 c->prod_syms[prod_array_idx++] = syms; 5375 c->rule_prods[ri][pi] = 5376 (KitGramProd){.syms = syms, .nsyms = (uint16_t)prod->nsyms}; 5377 } else { 5378 c->rule_prods[ri][pi] = (KitGramProd){0}; 5379 } 5380 } 5381 } 5382 5383 c->first_arrays[ri] = copy_intset_toks(ctx, &rule->first); 5384 c->follow_arrays[ri] = copy_intset_toks(ctx, &rule->follow); 5385 c->predict_arrays[ri] = build_predict_array(ctx, rule, b->ntokens); 5386 5387 const KitGramPratt* pratt_ptr = NULL; 5388 if (rule->pratt) { 5389 if (rule->pratt->nops) { 5390 c->pratt_ops[ri] = 5391 xmalloc(ctx, rule->pratt->nops * sizeof *c->pratt_ops[ri]); 5392 for (size_t oi = 0; oi < rule->pratt->nops; oi++) { 5393 PrattOp* op = &rule->pratt->ops[oi]; 5394 KitGramPrattOpRole role = KIT_GRAM_PO_INFIX; 5395 if (op->role == b->role.prefix) 5396 role = KIT_GRAM_PO_PREFIX; 5397 else if (op->role == b->role.postfix) 5398 role = KIT_GRAM_PO_POSTFIX; 5399 else if (op->role == b->role.ternary) 5400 role = KIT_GRAM_PO_TERNARY; 5401 else if (op->role == b->role.circumfix) 5402 role = KIT_GRAM_PO_CIRCUMFIX; 5403 KitGramSym inner = {0}; 5404 if (role == KIT_GRAM_PO_CIRCUMFIX) 5405 inner = (KitGramSym){.kind = KIT_GRAM_S_RULE, 5406 .rule = (KitGramRuleId)op->inner_rule}; 5407 c->pratt_ops[ri][oi] = (KitGramPrattOp){ 5408 .role = role, 5409 .tok = (KitGramTokenKind)op->tok, 5410 .tok2 = (KitGramTokenKind)op->tok2, 5411 .prod = (uint16_t)op->prod, 5412 .lbp = (uint16_t)op->lbp, 5413 .rbp = (uint16_t)op->rbp, 5414 .inner = inner, 5415 }; 5416 } 5417 } 5418 c->pratts[ri] = (KitGramPratt){ 5419 .primary = (KitGramRuleId)rule->pratt->primary_rule, 5420 .primary_prod = (uint16_t)rule->pratt->primary_prod, 5421 .ops = c->pratt_ops[ri], 5422 .nops = (uint16_t)rule->pratt->nops, 5423 }; 5424 pratt_ptr = &c->pratts[ri]; 5425 } 5426 5427 c->rules[ri] = (KitGramRule){ 5428 .name = rule->name, 5429 .hidden = !rule->public_rule, 5430 .is_pratt = rule->pratt != NULL, 5431 .pratt = pratt_ptr, 5432 .prods = c->rule_prods[ri], 5433 .nprods = (uint16_t)rule->nprods, 5434 .predict_tok = c->first_arrays[ri], 5435 .predict_prod = c->predict_arrays[ri], 5436 .npredict = rule->pratt ? 0 : (uint16_t)rule->first.n, 5437 .nullable = rule->nullable != 0, 5438 .empty_prod = (uint8_t)rule->empty_prod, 5439 .first = c->first_arrays[ri], 5440 .nfirst = (uint16_t)rule->first.n, 5441 .follow = c->follow_arrays[ri], 5442 .nfollow = (uint16_t)rule->follow.n, 5443 }; 5444 } 5445 5446 c->grammar = (KitGramGrammar){ 5447 .name = c->name, 5448 .rules = c->rules, 5449 .nrules = (uint16_t)b->nrules, 5450 .start = 0, 5451 .eof = 0, 5452 .sets = c->sets_flat, 5453 .set_off = c->set_off, 5454 .nsets = (uint16_t)b->nsets, 5455 .tok_names = c->tok_names, 5456 .ntoks = (uint16_t)b->ntokens, 5457 }; 5458 } 5459 5460 /* Materialize one %lex block's DFA into an owned KitGramLexGrammar. The same 5461 * table layout the codegen path emits as static arrays, built in the arena 5462 * instead. The grammar name mirrors codegen: the stem for the main block, 5463 * "stem:name" for a named sub-lexer. */ 5464 static void materialize_one_lexer(GramgenContext* ctx, KitGramCompiled* c, 5465 LexDFA* dfa, LexMat* m) { 5466 m->name = dfa->name; 5467 m->is_main = lex_name_is_main_c(dfa->name); 5468 m->accepts = xcalloc(ctx, dfa->nrecognizers, sizeof *m->accepts); 5469 for (size_t i = 0; i < dfa->nrecognizers; i++) { 5470 LexRecognizer* rec = &dfa->recognizers[i]; 5471 m->accepts[i] = (KitGramLexAccept){ 5472 .tok = (KitGramTokenKind)rec->tok, 5473 .skip = rec->skip != 0, 5474 }; 5475 } 5476 m->accept = xmalloc(ctx, dfa->nstates * sizeof *m->accept); 5477 memcpy(m->accept, dfa->accept, dfa->nstates * sizeof *m->accept); 5478 if (dfa->accept_text) { 5479 m->accept_text = xmalloc(ctx, dfa->nstates * sizeof *m->accept_text); 5480 memcpy(m->accept_text, dfa->accept_text, 5481 dfa->nstates * sizeof *m->accept_text); 5482 } 5483 if (dfa->accept_line) { 5484 m->accept_line = xmalloc(ctx, dfa->nstates * sizeof *m->accept_line); 5485 memcpy(m->accept_line, dfa->accept_line, 5486 dfa->nstates * sizeof *m->accept_line); 5487 } 5488 5489 /* Dense by-state transition table: nstates * nclasses uint16. */ 5490 uint16_t stride = dfa->nclasses; 5491 size_t trans_cells = (size_t)dfa->nstates * stride; 5492 m->trans = xmalloc(ctx, trans_cells * sizeof *m->trans); 5493 memcpy(m->trans, dfa->trans, trans_cells * sizeof *m->trans); 5494 5495 /* Extracted-keyword tables, mirroring the emitted static arrays. The lexeme 5496 * bytes point straight into the permanent lex AST owned by this compiled 5497 * object, so no copy is needed. */ 5498 if (dfa->nkeyword_tables) { 5499 size_t total_kw = 0, total_seeds = 0; 5500 for (size_t i = 0; i < dfa->nkeyword_tables; i++) { 5501 total_kw += dfa->keyword_tables[i].nslots; 5502 total_seeds += dfa->keyword_tables[i].nseeds; 5503 } 5504 m->keywords = xmalloc(ctx, (total_kw ? total_kw : 1) * sizeof *m->keywords); 5505 m->kw_seeds = 5506 xmalloc(ctx, (total_seeds ? total_seeds : 1) * sizeof *m->kw_seeds); 5507 m->keyword_tables = 5508 xcalloc(ctx, dfa->nkeyword_tables, sizeof *m->keyword_tables); 5509 size_t koff = 0, soff = 0; 5510 for (size_t i = 0; i < dfa->nkeyword_tables; i++) { 5511 LexKeywordTable* kt = &dfa->keyword_tables[i]; 5512 KitGramLexKeyword* kbase = m->keywords + koff; 5513 for (size_t j = 0; j < kt->nslots; j++) { 5514 if (kt->slots[j].literal.s) { 5515 kbase[j].lexeme = kt->slots[j].literal.s; 5516 kbase[j].len = (uint16_t)kt->slots[j].literal.len; 5517 kbase[j].kind = (KitGramTokenKind)kt->slots[j].tok; 5518 } else { 5519 kbase[j] = (KitGramLexKeyword){0}; 5520 } 5521 } 5522 uint32_t* sbase = m->kw_seeds + soff; 5523 for (size_t j = 0; j < kt->nseeds; j++) sbase[j] = kt->seeds[j]; 5524 m->keyword_tables[i] = (KitGramLexKeywordTable){ 5525 .host = (KitGramTokenKind)kt->host, 5526 .seeds = sbase, 5527 .nseeds = (uint16_t)kt->nseeds, 5528 .keywords = kbase, 5529 .nkeywords = (uint16_t)kt->nslots, 5530 .min_len = (uint16_t)kt->min_len, 5531 .max_len = (uint16_t)kt->max_len, 5532 }; 5533 koff += kt->nslots; 5534 soff += kt->nseeds; 5535 } 5536 } 5537 5538 const char* gname = 5539 m->is_main ? c->name : xasprintf(ctx, "%s:%s", c->name, dfa->name); 5540 m->grammar = (KitGramLexGrammar){ 5541 .name = gname, 5542 .input = dfa->input, 5543 .nclasses = dfa->nclasses, 5544 .class_stride = stride, 5545 .trans = m->trans, 5546 .nstates = dfa->nstates, 5547 .accept = m->accept, 5548 .accept_text = m->accept_text, 5549 .accept_line = m->accept_line, 5550 .accepts = m->accepts, 5551 .naccepts = (uint16_t)dfa->nrecognizers, 5552 .start_text = dfa->start_text, 5553 .start_line = dfa->start_line, 5554 .multiline = c->b->multiline != 0, 5555 .keyword_tables = m->keyword_tables, 5556 .nkeyword_tables = (uint16_t)dfa->nkeyword_tables, 5557 }; 5558 memcpy(m->grammar.class_of, dfa->class_of, sizeof m->grammar.class_of); 5559 } 5560 5561 /* Materialize every %lex block (the main lexer plus each named sub-lexer) into 5562 * its own KitGramLexGrammar, all reachable through the kit_gram_lexer_* 5563 * introspection API. lexers[main_lexer] is the main block (also returned by the 5564 * kit_gram_lexer_grammar shorthand). Machines live in a separate array and never 5565 * materialize an KitGramLexGrammar, so they are not represented here. */ 5566 static void materialize_lexer(GramgenContext* ctx, KitGramCompiled* c) { 5567 Builder* b = c->b; 5568 if (!b->nlex_dfas || !b->lex_dfa) return; 5569 c->has_lex = 1; 5570 c->nlexers = b->nlex_dfas; 5571 c->lexers = xcalloc(ctx, c->nlexers, sizeof *c->lexers); 5572 c->main_lexer = 0; 5573 for (size_t i = 0; i < b->nlex_dfas; i++) { 5574 materialize_one_lexer(ctx, c, &b->lex_dfas[i], &c->lexers[i]); 5575 if (c->lexers[i].is_main) c->main_lexer = i; 5576 } 5577 } 5578 5579 static void materialize_compiled(GramgenContext* ctx, KitGramCompiled* c) { 5580 materialize_parser(ctx, c); 5581 materialize_lexer(ctx, c); 5582 } 5583 5584 KitStatus kit_gram_compile_text(const KitContext* kit, KitSlice text, 5585 KitSlice path, const KitGramOptions* opts, 5586 KitGramCompiled** out) { 5587 if (out) *out = NULL; 5588 if (!kit || !kit->heap || !opts || !out) return KIT_INVALID; 5589 if (!text.s && text.len) return KIT_INVALID; 5590 5591 GramgenContext ctx; 5592 memset(&ctx, 0, sizeof ctx); 5593 ctx.kit = kit; 5594 ctx.mem.heap = kit->heap; 5595 ctx.diag = &ctx.diag_storage; 5596 ctx.can_jump = 1; 5597 5598 if (setjmp(ctx.jmp)) { 5599 gram_flush_diag(&ctx); /* before release: diag->path may point into arena */ 5600 mem_release(&ctx.scratch); 5601 mem_release(&ctx.mem); 5602 return KIT_ERR; 5603 } 5604 5605 const char* use_path = 5606 path.len ? xstrndup(&ctx, path.s, path.len) : "<memory>"; 5607 ParsedGrammar* pg = NULL; 5608 if (!parse_text(&ctx, use_path, text.len ? text.s : "", text.len, &pg, 5609 ctx.diag)) { 5610 gram_flush_diag(&ctx); 5611 mem_release(&ctx.mem); 5612 return KIT_ERR; 5613 } 5614 Builder* b = kit_gram_builder_new(&ctx, pg); 5615 b->multiline = opts->multiline ? 1 : 0; 5616 b->lexer_standalone = opts->lexer_standalone ? 1 : 0; 5617 b->fold_keywords = opts->fold_keywords ? 1 : 0; 5618 b->position_lazy = opts->position_lazy ? 1 : 0; 5619 b->parser_codegen = opts->parser_codegen ? 1 : 0; 5620 b->parser_recover = opts->parser_recover ? 1 : 0; 5621 b = kit_gram_builder_build(b); 5622 if (b->fold_keywords && !b->lexer_standalone) { 5623 Loc loc = {0}; 5624 kit_gram_error(&ctx, loc, "fold-keywords requires --lexer-standalone"); 5625 } 5626 if (b->position_lazy) { 5627 Loc loc = {0}; 5628 if (!b->lexer_standalone) 5629 kit_gram_error(&ctx, loc, "position: lazy requires --lexer-standalone"); 5630 if (b->parser_codegen) 5631 kit_gram_error(&ctx, loc, 5632 "position: lazy is not supported with --parser-codegen"); 5633 for (size_t i = 0; i < b->nlex_dfas; i++) 5634 if (b->lex_dfas[i].start_text || b->lex_dfas[i].start_line) 5635 kit_gram_error(&ctx, loc, 5636 "position: lazy is incompatible with ^ / \\A start " 5637 "anchors (they need column tracking)"); 5638 } 5639 KitGramCompiled* c = xcalloc(&ctx, 1, sizeof *c); 5640 c->b = b; 5641 c->kit = kit; 5642 c->path = xstrdup(&ctx, use_path); 5643 if (opts->name) 5644 c->name = xstrdup(&ctx, opts->name); 5645 else 5646 c->name = path_stem(&ctx, use_path); 5647 materialize_compiled(&ctx, c); 5648 b->ctx = NULL; 5649 5650 c->mem = ctx.mem; 5651 ctx.mem.head = ctx.mem.cur = NULL; 5652 *out = c; 5653 return KIT_OK; 5654 } 5655 5656 void kit_gram_free(KitGramCompiled* c) { 5657 if (!c) return; 5658 GramgenMem mem = c->mem; 5659 mem_release(&mem); 5660 } 5661 5662 const KitGramGrammar* kit_gram_parser_grammar(const KitGramCompiled* c) { 5663 return c ? &c->grammar : NULL; 5664 } 5665 5666 const KitGramLexGrammar* kit_gram_lexer_grammar(const KitGramCompiled* c) { 5667 return (c && c->has_lex) ? &c->lexers[c->main_lexer].grammar : NULL; 5668 } 5669 5670 size_t kit_gram_lexer_count(const KitGramCompiled* c) { 5671 return c ? c->nlexers : 0; 5672 } 5673 5674 const char* kit_gram_lexer_name(const KitGramCompiled* c, size_t i) { 5675 return (c && i < c->nlexers) ? c->lexers[i].name : NULL; 5676 } 5677 5678 const KitGramLexGrammar* kit_gram_lexer_grammar_at(const KitGramCompiled* c, 5679 size_t i) { 5680 return (c && i < c->nlexers) ? &c->lexers[i].grammar : NULL; 5681 } 5682 5683 bool kit_gram_find_lexer(const KitGramCompiled* c, const char* name, 5684 size_t* out) { 5685 if (!c || !name) return false; 5686 for (size_t i = 0; i < c->nlexers; i++) { 5687 if (strcmp(c->lexers[i].name, name) == 0) { 5688 if (out) *out = i; 5689 return true; 5690 } 5691 } 5692 return false; 5693 } 5694 5695 /* Single-pattern convenience: wrap the pattern into a one-rule grammar and 5696 * compile it with the existing pipeline, so the lexer DFA, anchors, longest 5697 * match, and priority logic are all reused with no special casing. */ 5698 #define GRAMREGEX_TOKEN "GRAMRE" 5699 KitStatus kit_gram_regex_compile(const KitContext* kit, KitSlice pattern, 5700 const KitGramOptions* opts, 5701 KitGramCompiled** out) { 5702 if (out) *out = NULL; 5703 if (!kit || !kit->heap || !opts || !out) return KIT_INVALID; 5704 if (!pattern.s && pattern.len) return KIT_INVALID; 5705 const char* head = "%lex {\n" GRAMREGEX_TOKEN " = "; 5706 const char* tail = ";\n}\nllre = " GRAMREGEX_TOKEN ";\n"; 5707 size_t hlen = strlen(head), plen = pattern.len, tlen = strlen(tail); 5708 size_t len = hlen + plen + tlen; 5709 KitHeap* h = kit->heap; 5710 char* text = h->alloc(h, len + 1, 1); 5711 if (!text) { 5712 kit_ctx_diagf(kit, "out of memory"); 5713 return KIT_NOMEM; 5714 } 5715 memcpy(text, head, hlen); 5716 if (plen) memcpy(text + hlen, pattern.s, plen); 5717 memcpy(text + hlen + plen, tail, tlen); 5718 text[len] = '\0'; 5719 KitStatus st = 5720 kit_gram_compile_text(kit, (KitSlice){.s = text, .len = len}, 5721 KIT_SLICE_LIT("<regex>"), opts, out); 5722 h->free(h, text, len + 1); 5723 return st; 5724 } 5725 5726 const KitGramLexGrammar* kit_gram_regex_grammar(const KitGramCompiled* re) { 5727 return kit_gram_lexer_grammar(re); 5728 } 5729 5730 KitGramTokenKind kit_gram_regex_kind(const KitGramCompiled* re) { 5731 KitGramTokenKind kind = 0; 5732 if (re) kit_gram_find_token(re, GRAMREGEX_TOKEN, &kind); 5733 return kind; 5734 } 5735 5736 size_t kit_gram_token_count(const KitGramCompiled* c) { 5737 return c ? c->b->ntokens : 0; 5738 } 5739 5740 const char* kit_gram_token_name(const KitGramCompiled* c, KitGramTokenKind tok) { 5741 return (c && tok < c->b->ntokens) ? c->b->tokens[tok].name : NULL; 5742 } 5743 5744 const char* kit_gram_token_display(const KitGramCompiled* c, 5745 KitGramTokenKind tok) { 5746 return (c && tok < c->b->ntokens) ? c->b->tokens[tok].display.s : NULL; 5747 } 5748 5749 bool kit_gram_find_token(const KitGramCompiled* c, const char* name, 5750 KitGramTokenKind* out) { 5751 if (!c || !name) return false; 5752 for (size_t i = 0; i < c->b->ntokens; i++) { 5753 if (strcmp(c->b->tokens[i].name, name) == 0) { 5754 if (out) *out = (KitGramTokenKind)i; 5755 return true; 5756 } 5757 } 5758 return false; 5759 } 5760 5761 size_t kit_gram_rule_count(const KitGramCompiled* c) { 5762 return c ? c->b->public_count : 0; 5763 } 5764 5765 const char* kit_gram_rule_name(const KitGramCompiled* c, KitGramRuleId rule) { 5766 return (c && rule < c->b->public_count) ? c->b->rules[rule].name : NULL; 5767 } 5768 5769 bool kit_gram_find_rule(const KitGramCompiled* c, const char* name, 5770 KitGramRuleId* out) { 5771 if (!c || !name) return false; 5772 for (size_t i = 0; i < c->b->public_count; i++) { 5773 if (strcmp(c->b->rules[i].name, name) == 0) { 5774 if (out) *out = (KitGramRuleId)i; 5775 return true; 5776 } 5777 } 5778 return false; 5779 } 5780 5781 /* ---- token machine (%machine) introspection + sampling -------------------- 5782 */ 5783 5784 size_t kit_gram_machine_count(const KitGramCompiled* c) { 5785 return c ? c->b->nmachines : 0; 5786 } 5787 5788 const char* kit_gram_machine_name(const KitGramCompiled* c, size_t mi) { 5789 return (c && mi < c->b->nmachines) ? c->b->machines[mi].name : NULL; 5790 } 5791 5792 bool kit_gram_find_machine(const KitGramCompiled* c, const char* name, 5793 size_t* out) { 5794 if (!c || !name) return false; 5795 for (size_t i = 0; i < c->b->nmachines; i++) { 5796 if (strcmp(c->b->machines[i].name, name) == 0) { 5797 if (out) *out = i; 5798 return true; 5799 } 5800 } 5801 return false; 5802 } 5803 5804 size_t kit_gram_machine_rule_count(const KitGramCompiled* c, size_t mi) { 5805 return (c && mi < c->b->nmachines) ? c->b->machines[mi].nrecognizers : 0; 5806 } 5807 5808 const char* kit_gram_machine_rule_name(const KitGramCompiled* c, size_t mi, 5809 size_t rule) { 5810 if (!c || mi >= c->b->nmachines) return NULL; 5811 const LexDFA* m = &c->b->machines[mi]; 5812 return rule < m->nrecognizers ? m->recognizers[rule].name : NULL; 5813 } 5814 5815 KitGramGenStatus kit_gram_machine_generate(const KitGramCompiled* c, size_t mi, 5816 size_t rule, uint64_t* seed, 5817 double stop_prob, size_t max_repeat, 5818 size_t max_tokens, const char** out, 5819 size_t cap, size_t* n) { 5820 if (!c || mi >= c->b->nmachines) { 5821 if (n) *n = 0; /* header documents *n as the produced length on every return */ 5822 return KIT_GRAM_GEN_ERROR; 5823 } 5824 /* The compiled object detached its context; sampling allocates only a small 5825 * complement scratch, so borrow a temporary list-arena context. */ 5826 GramgenContext ctx; 5827 memset(&ctx, 0, sizeof ctx); 5828 ctx.kit = c->kit; 5829 ctx.mem.heap = c->mem.heap; 5830 ctx.can_jump = 0; 5831 KitGramGenStatus st = 5832 kit_gram_machine_sample(&ctx, &c->b->machines[mi], rule, seed, stop_prob, 5833 max_repeat, max_tokens, out, cap, n); 5834 mem_release(&ctx.mem); 5835 return st; 5836 } 5837 5838 KitStatus kit_gram_emit_c(const KitGramCompiled* c, 5839 const KitGramEmitOptions* opts, KitWriter* header, 5840 KitWriter* source) { 5841 if (!c || !opts || !opts->header_path || !opts->source_path || 5842 !opts->prefix || !header || !source) 5843 return KIT_INVALID; 5844 5845 GramgenContext ctx; 5846 memset(&ctx, 0, sizeof ctx); 5847 ctx.kit = c->kit; 5848 ctx.mem.heap = ((KitGramCompiled*)c)->mem.heap; 5849 ctx.diag = &ctx.diag_storage; 5850 ctx.can_jump = 1; 5851 5852 if (setjmp(ctx.jmp)) { 5853 gram_flush_diag(&ctx); 5854 mem_release(&ctx.mem); 5855 return KIT_ERR; 5856 } 5857 5858 validate_prefix_arg(&ctx, opts->prefix, c->path); 5859 Buf h; 5860 Buf src; 5861 buf_init_writer(&ctx, &h, header, opts->header_path); 5862 buf_init_writer(&ctx, &src, source, opts->source_path); 5863 emit_header_text(&h, c->b, c->path, opts->header_path, opts->prefix); 5864 emit_c_text(&src, c->b, c->path, opts->header_path, opts->prefix); 5865 5866 mem_release(&ctx.mem); 5867 return KIT_OK; 5868 }