M1pp.c (63137B)
1 /* 2 * Tiny single-pass M1pp macro expander. Output is consumed directly by 3 * hex2pp -- there is no intermediate M0/hex2 stage. All emission is in 4 * the byte/label/directive vocabulary hex2pp accepts. 5 * 6 * Syntax: 7 * %macro NAME(a, b) 8 * ... body ... 9 * %endm 10 * 11 * %struct NAME { f1 f2 ... } fixed-layout 8-byte-field aggregate 12 * %enum NAME { l1 l2 ... } incrementing integer constants 13 * %reset-output BASE discard output so far and seed the 14 * macro-hygiene expansion id 15 * 16 * %NAME(x, y) function-like macro call 17 * ## token pasting inside macro bodies 18 * !(expr) evaluate an integer S-expression, emit LE 8-bit hex 19 * @(expr) evaluate an integer S-expression, emit LE 16-bit hex 20 * %(expr) evaluate an integer S-expression, emit LE 32-bit hex 21 * $(expr) evaluate an integer S-expression, emit LE 64-bit hex 22 * %select(c,t,e) evaluate condition S-expression; expand t if nonzero else e 23 * %str(IDENT) stringify a single WORD token into a "..."-quoted literal 24 * %bytes(STR) emit the raw bytes of STR as contiguous hex digits 25 * 26 * %frame NAME / %endframe set/clear a single-slot "current frame" 27 * %local(NAME) expand to the body of <frame>_FRAME.<NAME> 28 * 29 * Lexical scoping for control-flow labels is delegated to hex2pp's 30 * `.scope` / `.endscope` (which nest). M1pp itself only handles 31 * per-expansion macro hygiene labels (`:@name` / `&@name`). 32 * 33 * Expression syntax is intentionally Lisp-shaped: 34 * atoms: decimal or 0x-prefixed integer literals 35 * calls: (+ a b), (- a b), (* a b), (/ a b), (% a b), (<< a b), (>> a b) 36 * (& a b), (| a b), (^ a b), (~ a), (= a b), (!= a b), 37 * (< a b), (<= a b), (> a b), (>= a b) 38 * 39 * Flow: 40 * 1. lex_source(): scan input_buf into source_tokens[]. Tokens are words, 41 * strings, newlines, parens, commas, and ## paste markers. Whitespace 42 * (excluding newlines) is dropped; # and ; comments are dropped. 43 * 44 * 2. process_tokens(): main loop driven by a stream stack (streams[]). 45 * The source token array is pushed as the initial stream. Each iteration 46 * pops a token from the top stream: 47 * 48 * %macro NAME(p,...) / %endm 49 * -> define_macro(): consume header + body tokens into macros[] and 50 * macro_body_tokens[]; register name and param list. Header is 51 * whitespace-insensitive (newlines inside (...) are skipped); 52 * %endm is recognized anywhere and must be followed by NEWLINE. 53 * A directive that started at line_start consumes its trailing 54 * newline; mid-line directives leave it for the main loop. 55 * 56 * !(e) / @(e) / %(e) / $(e) / %select(c,t,e) 57 * -> expand_builtin_call(): parse arg spans, eval S-expression(s) via 58 * eval_expr_range(), emit LE hex or push the chosen token span. 59 * Only fuses when ( is tight against the name (no whitespace). 60 * 61 * %NAME(...) matching a defined macro 62 * -> expand_call() -> expand_macro_tokens(): substitute arguments, 63 * apply ## paste via paste_pool_range(), write result into 64 * expand_pool[], then push that slice as a new stream (rescan). 65 * Tight ( required for paren-form; otherwise treated as 0-arg. 66 * 67 * Anything else 68 * -> emit_token() / emit_newline() directly into output_buf. 69 * 70 * When a stream is exhausted it is popped; pool_used is rewound to the 71 * stream's pool_mark, reclaiming the expand_pool space it used. 72 * 73 * 3. Write output_buf to the output file. 74 * 75 * Notes: 76 * - Macros are define-before-use. There is no prescan. 77 * - Expansion rescans by pushing expanded tokens back through the same loop. 78 * - There is no cycle detection. Recursive macros will loop until a limit. 79 * - Only recognized %NAME(...) calls expand. Other text passes through. 80 * - Output formatting is normalized to tokens plus '\n', not preserved. 81 */ 82 83 #include <errno.h> 84 #include <stdio.h> 85 #include <stdlib.h> 86 #include <string.h> 87 88 /* Caps chosen to mirror the M1pp.P1 BSS layout, sized so the cc.scm 89 * emission of tcc.flat.c (~6.5 MB of macro-rich .P1pp) lexes cleanly. 90 * The native binary is host-side, so static globals at these sizes 91 * just live in .bss / anonymous mmap without any of the ELF-segment 92 * sizing dance the bootstrap m1pp has to do. */ 93 #define MAX_INPUT 16777216 /* 16 MiB */ 94 #define MAX_OUTPUT 134217728 /* 128 MiB */ 95 #define MAX_TEXT 67108864 /* 64 MiB: 96 * paste tokens, hex literals from 97 * %(EXPR) evaluation, and per-call 98 * @local label rewrites all live 99 * here for the run's lifetime. cc.scm 100 * triggers hundreds of thousands of 101 * each across the tcc.c expansion. */ 102 #define MAX_TOKENS 8388608 /* 8 M slots × 32 B = 256 MiB */ 103 #define MAX_MACROS 1024 104 #define MAX_PARAMS 16 105 #define MAX_MACRO_BODY_TOKENS MAX_TOKENS 106 #define MAX_EXPAND 524288 /* 512 K × 32 B = 16 MiB: 107 * cc.scm wraps each C function in 108 * %fn(... { body }), and m1pp's 109 * expand_macro_tokens copies the 110 * argument tokens into the pool — 111 * so the entire body of a long 112 * function is resident in the pool 113 * while its outer %fn is active. 114 * tcc.c's next_nomacro1 (~5900 115 * lines × ~13 m1pp tokens/line ≈ 116 * 77 K tokens, ~2.5 MiB) plus 117 * inner expansions sit comfortably 118 * under 16 MiB. */ 119 #define MAX_STACK 64 120 #define MAX_EXPR_FRAMES 256 121 122 enum { 123 TOK_WORD, 124 TOK_STRING, 125 TOK_NEWLINE, 126 TOK_LPAREN, 127 TOK_RPAREN, 128 TOK_COMMA, 129 TOK_PASTE, 130 TOK_LBRACE, 131 TOK_RBRACE 132 }; 133 134 enum ExprOp { 135 EXPR_ADD, 136 EXPR_SUB, 137 EXPR_MUL, 138 EXPR_DIV, 139 EXPR_MOD, 140 EXPR_SHL, 141 EXPR_SHR, 142 EXPR_AND, 143 EXPR_OR, 144 EXPR_XOR, 145 EXPR_NOT, 146 EXPR_EQ, 147 EXPR_NE, 148 EXPR_LT, 149 EXPR_LE, 150 EXPR_GT, 151 EXPR_GE, 152 EXPR_STRLEN, 153 EXPR_INVALID 154 }; 155 156 struct TextSpan { 157 const char *ptr; 158 int len; 159 }; 160 161 struct Token { 162 int kind; 163 int tight; 164 int line; 165 struct TextSpan text; 166 }; 167 168 struct TokenSpan { 169 struct Token *start; 170 struct Token *end; 171 }; 172 173 struct Macro { 174 struct TextSpan name; 175 int param_count; 176 int has_paste; 177 struct TextSpan params[MAX_PARAMS]; 178 struct Token *body_start; 179 struct Token *body_end; 180 }; 181 182 struct Stream { 183 struct Token *start; 184 struct Token *end; 185 struct Token *pos; 186 int line_start; 187 int pool_mark; 188 }; 189 190 struct ExprFrame { 191 enum ExprOp op; 192 long long args[MAX_PARAMS]; 193 int argc; 194 }; 195 196 static char input_buf[MAX_INPUT + 1]; 197 static char output_buf[MAX_OUTPUT + 1]; 198 static char text_buf[MAX_TEXT]; 199 200 static struct Token source_tokens[MAX_TOKENS]; 201 static struct Token macro_body_tokens[MAX_MACRO_BODY_TOKENS]; 202 /* Per-body-token classification cached at %macro definition time, so 203 * expand_macro_tokens never re-runs find_param / is_local_label_token in 204 * its hot loop. param_idx: 0 = not a param, k = params[k-1]. */ 205 static unsigned char macro_body_param_idx[MAX_MACRO_BODY_TOKENS]; 206 static unsigned char macro_body_is_local_label[MAX_MACRO_BODY_TOKENS]; 207 static struct Token expand_pool[MAX_EXPAND]; 208 static struct Macro macros[MAX_MACROS]; 209 static struct Stream streams[MAX_STACK]; 210 static struct TextSpan current_frame; 211 static int frame_active; 212 213 static int text_used; 214 static int source_count; 215 static int macro_count; 216 static int macro_body_used; 217 static int pool_used; 218 static int output_used; 219 static int output_need_space; 220 static int stream_top; 221 static int next_expansion_id; 222 static int current_line; 223 static int error_line; 224 static const char *input_path; 225 226 static struct Token *arg_starts[MAX_PARAMS]; 227 static struct Token *arg_ends[MAX_PARAMS]; 228 static int arg_count; 229 static struct Token *call_end_pos; 230 static int args_have_paste; 231 232 static const char *error_msg; 233 234 static int fail(const char *msg) 235 { 236 error_msg = msg; 237 error_line = current_line; 238 return 0; 239 } 240 241 static int is_space_no_nl(int c) 242 { 243 return c == ' ' || c == '\t' || c == '\r' || c == '\f' || c == '\v'; 244 } 245 246 static char *append_text_len(const char *s, int len) 247 { 248 int start; 249 250 if (text_used + len + 1 > MAX_TEXT) { 251 fail("text overflow"); 252 return NULL; 253 } 254 start = text_used; 255 memcpy(text_buf + text_used, s, (size_t)len); 256 text_used += len; 257 text_buf[text_used++] = '\0'; 258 return text_buf + start; 259 } 260 261 static int push_token(struct Token *buf, int *count, int max_count, 262 int kind, int tight, int line, struct TextSpan text) 263 { 264 if (*count >= max_count) { 265 return fail("token overflow"); 266 } 267 buf[*count].kind = kind; 268 buf[*count].tight = tight; 269 buf[*count].line = line; 270 buf[*count].text = text; 271 *count += 1; 272 return 1; 273 } 274 275 static int push_pool_token(struct Token tok) 276 { 277 if (pool_used >= MAX_EXPAND) { 278 return fail("expansion overflow"); 279 } 280 expand_pool[pool_used++] = tok; 281 return 1; 282 } 283 284 static int token_text_eq(const struct Token *tok, const char *s) 285 { 286 int len = (int)strlen(s); 287 288 return tok->text.len == len && 289 memcmp(tok->text.ptr, s, (size_t)len) == 0; 290 } 291 292 static int span_eq_token(struct TextSpan span, const struct Token *tok) 293 { 294 return span.len == tok->text.len && 295 memcmp(span.ptr, tok->text.ptr, (size_t)span.len) == 0; 296 } 297 298 static int lex_source(const char *src) 299 { 300 /* Track whether whitespace (space, tab, comment, OR newline) precedes 301 * the next token. tight=1 means "no whitespace before me"; only 302 * LPAREN's tight bit is consulted, to decide whether %FOO(...) / 303 * !(...) etc. are paren-call forms. */ 304 int i = 0; 305 int line = 1; 306 int saw_separator = 1; 307 308 while (src[i] != '\0') { 309 int start; 310 int len; 311 int tight; 312 313 current_line = line; 314 315 if (is_space_no_nl((unsigned char)src[i])) { 316 saw_separator = 1; 317 i++; 318 continue; 319 } 320 if (src[i] == '\n') { 321 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 322 TOK_NEWLINE, 0, line, (struct TextSpan){src + i, 1})) { 323 return 0; 324 } 325 line++; 326 saw_separator = 1; 327 i++; 328 continue; 329 } 330 if (src[i] == '"' || src[i] == '\'') { 331 int quote = src[i]; 332 333 tight = !saw_separator; 334 start = i; 335 i++; 336 while (src[i] != '\0' && src[i] != quote) { 337 if (src[i] == '\\' && src[i + 1] != '\0') { 338 /* Skip backslash + next char as a unit so the 339 * close-quote test doesn't fire on `\"`, and so 340 * `\\` doesn't leave the trailing `\` to start a 341 * spurious escape. The escape's *meaning* is 342 * decoded later (e.g. by %bytes); the lexer only 343 * cares about token boundaries. */ 344 if (src[i + 1] == '\n') { 345 line++; 346 } 347 i += 2; 348 continue; 349 } 350 if (src[i] == '\n') { 351 line++; 352 } 353 i++; 354 } 355 if (src[i] == quote) { 356 i++; 357 } 358 len = i - start; 359 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 360 TOK_STRING, tight, current_line, (struct TextSpan){src + start, len})) { 361 return 0; 362 } 363 saw_separator = 0; 364 continue; 365 } 366 if (src[i] == '#' && src[i + 1] == '#') { 367 tight = !saw_separator; 368 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 369 TOK_PASTE, tight, line, (struct TextSpan){src + i, 2})) { 370 return 0; 371 } 372 i += 2; 373 saw_separator = 0; 374 continue; 375 } 376 if (src[i] == '#' || src[i] == ';') { 377 saw_separator = 1; 378 while (src[i] != '\0' && src[i] != '\n') { 379 i++; 380 } 381 continue; 382 } 383 if (src[i] == '(') { 384 tight = !saw_separator; 385 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 386 TOK_LPAREN, tight, line, (struct TextSpan){src + i, 1})) { 387 return 0; 388 } 389 i++; 390 saw_separator = 0; 391 continue; 392 } 393 if (src[i] == ')') { 394 tight = !saw_separator; 395 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 396 TOK_RPAREN, tight, line, (struct TextSpan){src + i, 1})) { 397 return 0; 398 } 399 i++; 400 saw_separator = 0; 401 continue; 402 } 403 if (src[i] == ',') { 404 tight = !saw_separator; 405 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 406 TOK_COMMA, tight, line, (struct TextSpan){src + i, 1})) { 407 return 0; 408 } 409 i++; 410 saw_separator = 0; 411 continue; 412 } 413 if (src[i] == '{') { 414 tight = !saw_separator; 415 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 416 TOK_LBRACE, tight, line, (struct TextSpan){src + i, 1})) { 417 return 0; 418 } 419 i++; 420 saw_separator = 0; 421 continue; 422 } 423 if (src[i] == '}') { 424 tight = !saw_separator; 425 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 426 TOK_RBRACE, tight, line, (struct TextSpan){src + i, 1})) { 427 return 0; 428 } 429 i++; 430 saw_separator = 0; 431 continue; 432 } 433 434 tight = !saw_separator; 435 start = i; 436 while (src[i] != '\0' && 437 !is_space_no_nl((unsigned char)src[i]) && 438 src[i] != '\n' && 439 src[i] != '#' && 440 src[i] != ';' && 441 src[i] != '(' && 442 src[i] != ')' && 443 src[i] != ',' && 444 src[i] != '{' && 445 src[i] != '}' && 446 !(src[i] == '#' && src[i + 1] == '#')) { 447 i++; 448 } 449 len = i - start; 450 if (!push_token(source_tokens, &source_count, MAX_TOKENS, 451 TOK_WORD, tight, line, (struct TextSpan){src + start, len})) { 452 return 0; 453 } 454 saw_separator = 0; 455 } 456 457 return 1; 458 } 459 460 static const struct Macro *find_macro(const struct Token *tok) 461 { 462 int i; 463 464 if (tok->kind != TOK_WORD || tok->text.len < 2) { 465 return NULL; 466 } 467 if (tok->text.ptr[0] != '%') { 468 return NULL; 469 } 470 for (i = 0; i < macro_count; i++) { 471 if (macros[i].name.len == tok->text.len - 1 && 472 memcmp(tok->text.ptr + 1, 473 macros[i].name.ptr, 474 (size_t)macros[i].name.len) == 0) { 475 return ¯os[i]; 476 } 477 } 478 return NULL; 479 } 480 481 static int find_param(const struct Macro *m, const struct Token *tok) 482 { 483 int i; 484 485 if (tok->kind != TOK_WORD) { 486 return 0; 487 } 488 for (i = 0; i < m->param_count; i++) { 489 if (span_eq_token(m->params[i], tok)) { 490 return i + 1; 491 } 492 } 493 return 0; 494 } 495 496 static int emit_newline(void) 497 { 498 if (output_used + 1 >= MAX_OUTPUT) { 499 return fail("output overflow"); 500 } 501 output_buf[output_used++] = '\n'; 502 output_need_space = 0; 503 return 1; 504 } 505 506 static int emit_string_as_bytes(const struct Token *tok); 507 static int emit_hex_value(unsigned long long value, int bytes); 508 static int is_local_label_token(const struct Token *tok); 509 510 static int emit_token(const struct Token *tok) 511 { 512 if (tok->kind == TOK_LBRACE || tok->kind == TOK_RBRACE) { 513 return 1; 514 } 515 if (tok->kind == TOK_STRING) { 516 return emit_string_as_bytes(tok); 517 } 518 if (output_need_space) { 519 if (output_used + 1 >= MAX_OUTPUT) { 520 return fail("output overflow"); 521 } 522 output_buf[output_used++] = ' '; 523 } 524 if (output_used + tok->text.len >= MAX_OUTPUT) { 525 return fail("output overflow"); 526 } 527 memcpy(output_buf + output_used, tok->text.ptr, 528 (size_t)tok->text.len); 529 output_used += tok->text.len; 530 output_need_space = 1; 531 return 1; 532 } 533 534 /* Decode a "..." or '...' literal and emit one TOK_WORD per byte 535 * (each token's text is the two hex digits for that byte). Recognised 536 * escapes inside the literal: \n \t \r \0 \\ \" \xNN. No NUL is 537 * appended; user code writes one explicitly if needed. */ 538 static int emit_string_as_bytes(const struct Token *tok) 539 { 540 const char *src; 541 int src_len; 542 int src_i; 543 544 if (tok->text.len < 2) { 545 return fail("bad string"); 546 } 547 src = tok->text.ptr + 1; 548 src_len = tok->text.len - 2; 549 src_i = 0; 550 while (src_i < src_len) { 551 unsigned int b; 552 char c = src[src_i++]; 553 if (c == '\\') { 554 char e; 555 if (src_i >= src_len) { 556 return fail("bad escape"); 557 } 558 e = src[src_i++]; 559 if (e == 'n') b = 0x0A; 560 else if (e == 't') b = 0x09; 561 else if (e == 'r') b = 0x0D; 562 else if (e == '0') b = 0x00; 563 else if (e == '\\') b = 0x5C; 564 else if (e == '"') b = 0x22; 565 else if (e == 'x') { 566 int hi, lo, hv, lv; 567 if (src_i + 2 > src_len) { 568 return fail("bad escape"); 569 } 570 hi = (unsigned char)src[src_i++]; 571 lo = (unsigned char)src[src_i++]; 572 hv = (hi >= '0' && hi <= '9') ? hi - '0' : 573 (hi >= 'a' && hi <= 'f') ? hi - 'a' + 10 : 574 (hi >= 'A' && hi <= 'F') ? hi - 'A' + 10 : -1; 575 lv = (lo >= '0' && lo <= '9') ? lo - '0' : 576 (lo >= 'a' && lo <= 'f') ? lo - 'a' + 10 : 577 (lo >= 'A' && lo <= 'F') ? lo - 'A' + 10 : -1; 578 if (hv < 0 || lv < 0) { 579 return fail("bad escape"); 580 } 581 b = (unsigned int)((hv << 4) | lv); 582 } else { 583 return fail("bad escape"); 584 } 585 } else { 586 b = (unsigned char)c; 587 } 588 if (!emit_hex_value((unsigned long long)b, 1)) { 589 return 0; 590 } 591 } 592 return 1; 593 } 594 595 static int push_stream_span(struct TokenSpan span, int pool_mark) 596 { 597 struct Stream *s; 598 599 if (stream_top >= MAX_STACK) { 600 return fail("stream overflow"); 601 } 602 s = &streams[stream_top++]; 603 s->start = span.start; 604 s->end = span.end; 605 s->pos = span.start; 606 s->line_start = 1; 607 s->pool_mark = pool_mark; 608 return 1; 609 } 610 611 static struct Stream *current_stream(void) 612 { 613 if (stream_top <= 0) { 614 return NULL; 615 } 616 return &streams[stream_top - 1]; 617 } 618 619 static void pop_stream(void) 620 { 621 if (stream_top <= 0) { 622 return; 623 } 624 stream_top--; 625 if (streams[stream_top].pool_mark >= 0) { 626 pool_used = streams[stream_top].pool_mark; 627 } 628 } 629 630 static int copy_span_to_pool(struct TokenSpan span) 631 { 632 struct Token *tok; 633 634 for (tok = span.start; tok < span.end; tok++) { 635 if (!push_pool_token(*tok)) { 636 return 0; 637 } 638 } 639 return 1; 640 } 641 642 static int push_pool_stream_from_mark(int mark) 643 { 644 if (pool_used == mark) { 645 pool_used = mark; 646 return 1; 647 } 648 return push_stream_span((struct TokenSpan){expand_pool + mark, expand_pool + pool_used}, 649 mark); 650 } 651 652 static void skip_newlines(struct Token **pos, struct Token *end) 653 { 654 while (*pos < end && (*pos)->kind == TOK_NEWLINE) { 655 *pos += 1; 656 } 657 } 658 659 static int emit_decimal_text(long long value, struct TextSpan *out) 660 { 661 /* Render a non-negative integer as decimal into text_buf and 662 * return the span. No snprintf; plain reverse-fill. */ 663 char digits[24]; 664 int digit_count = 0; 665 long long v = value; 666 int start; 667 int i; 668 669 if (v < 0) { 670 return fail("bad directive"); 671 } 672 if (v == 0) { 673 digits[digit_count++] = '0'; 674 } else { 675 while (v > 0) { 676 digits[digit_count++] = (char)('0' + (v % 10)); 677 v /= 10; 678 } 679 } 680 681 if (text_used + digit_count + 1 > MAX_TEXT) { 682 return fail("text overflow"); 683 } 684 start = text_used; 685 for (i = digit_count - 1; i >= 0; i--) { 686 text_buf[text_used++] = digits[i]; 687 } 688 text_buf[text_used++] = '\0'; 689 out->ptr = text_buf + start; 690 out->len = digit_count; 691 return 1; 692 } 693 694 static int emit_dotted_name(struct TextSpan base, const char *suffix, 695 int suffix_len, struct TextSpan *out) 696 { 697 int total = base.len + 1 + suffix_len; 698 int start; 699 700 if (text_used + total + 1 > MAX_TEXT) { 701 return fail("text overflow"); 702 } 703 start = text_used; 704 memcpy(text_buf + text_used, base.ptr, (size_t)base.len); 705 text_used += base.len; 706 text_buf[text_used++] = '.'; 707 memcpy(text_buf + text_used, suffix, (size_t)suffix_len); 708 text_used += suffix_len; 709 text_buf[text_used++] = '\0'; 710 out->ptr = text_buf + start; 711 out->len = total; 712 return 1; 713 } 714 715 static int define_fielded_macro(struct TextSpan base, const char *suffix, 716 int suffix_len, long long value) 717 { 718 struct Macro *m; 719 struct Token body_tok; 720 721 if (macro_count >= MAX_MACROS) { 722 return fail("too many macros"); 723 } 724 if (macro_body_used >= MAX_MACRO_BODY_TOKENS) { 725 return fail("macro body overflow"); 726 } 727 m = ¯os[macro_count]; 728 memset(m, 0, sizeof(*m)); 729 if (!emit_dotted_name(base, suffix, suffix_len, &m->name)) { 730 return 0; 731 } 732 m->param_count = 0; 733 body_tok.kind = TOK_WORD; 734 body_tok.tight = 0; 735 body_tok.line = current_line; 736 if (!emit_decimal_text(value, &body_tok.text)) { 737 return 0; 738 } 739 m->body_start = macro_body_tokens + macro_body_used; 740 macro_body_param_idx[macro_body_used] = 0; 741 macro_body_is_local_label[macro_body_used] = 0; 742 macro_body_tokens[macro_body_used++] = body_tok; 743 m->body_end = macro_body_tokens + macro_body_used; 744 macro_count++; 745 return 1; 746 } 747 748 static int define_fielded(struct Stream *s, long long stride, 749 const char *total_name, int total_name_len) 750 { 751 /* Parses `%struct NAME { f1 f2 ... }` or `%enum NAME { ... }` and 752 * synthesizes N+1 zero-parameter macros: 753 * NAME.field_k -> k * stride 754 * NAME.<total> -> N * stride (SIZE for struct, COUNT for enum) 755 * The closing } must be immediately followed by TOK_NEWLINE. The 756 * newline is consumed iff the directive started at line_start. */ 757 struct TextSpan base; 758 long long index = 0; 759 int started_at_line_start = s->line_start; 760 761 s->pos++; 762 skip_newlines(&s->pos, s->end); 763 if (s->pos >= s->end || s->pos->kind != TOK_WORD) { 764 return fail("bad directive"); 765 } 766 base = s->pos->text; 767 s->pos++; 768 769 skip_newlines(&s->pos, s->end); 770 if (s->pos >= s->end || s->pos->kind != TOK_LBRACE) { 771 return fail("bad directive"); 772 } 773 s->pos++; 774 775 for (;;) { 776 while (s->pos < s->end && 777 (s->pos->kind == TOK_COMMA || s->pos->kind == TOK_NEWLINE)) { 778 s->pos++; 779 } 780 if (s->pos >= s->end) { 781 return fail("unterminated directive"); 782 } 783 if (s->pos->kind == TOK_RBRACE) { 784 s->pos++; 785 break; 786 } 787 if (s->pos->kind != TOK_WORD) { 788 return fail("bad directive"); 789 } 790 if (!define_fielded_macro(base, s->pos->text.ptr, s->pos->text.len, 791 index * stride)) { 792 return 0; 793 } 794 s->pos++; 795 index++; 796 } 797 798 if (!define_fielded_macro(base, total_name, total_name_len, index * stride)) { 799 return 0; 800 } 801 802 if (s->pos >= s->end || s->pos->kind != TOK_NEWLINE) { 803 return fail("expected newline after struct/enum"); 804 } 805 if (started_at_line_start) { 806 s->pos++; 807 s->line_start = 1; 808 } 809 return 1; 810 } 811 812 static int define_macro(struct Stream *s) 813 { 814 /* Header is whitespace-insensitive: newlines inside (...) and around 815 * the keywords are skipped. Body collection skips newlines that fall 816 * between `)` and the first body token (so `%macro N()\nbody\n%endm` 817 * has body=[WORD body, NEWLINE], same as the old required-newline form). 818 * %endm is recognized anywhere in the body; the next token must be 819 * TOK_NEWLINE. The newline is consumed only when the directive started 820 * at s->line_start — that way mid-line directives leave the user's 821 * trailing newline in the stream for the main loop to emit. */ 822 struct Macro *m; 823 int started_at_line_start = s->line_start; 824 825 if (macro_count >= MAX_MACROS) { 826 return fail("too many macros"); 827 } 828 if (macro_body_used >= MAX_MACRO_BODY_TOKENS) { 829 return fail("macro body overflow"); 830 } 831 832 m = ¯os[macro_count]; 833 memset(m, 0, sizeof(*m)); 834 s->pos++; 835 836 skip_newlines(&s->pos, s->end); 837 if (s->pos >= s->end || s->pos->kind != TOK_WORD) { 838 return fail("bad macro header"); 839 } 840 m->name = s->pos->text; 841 s->pos++; 842 843 skip_newlines(&s->pos, s->end); 844 if (s->pos >= s->end || s->pos->kind != TOK_LPAREN) { 845 return fail("bad macro header"); 846 } 847 s->pos++; 848 849 skip_newlines(&s->pos, s->end); 850 if (s->pos < s->end && s->pos->kind != TOK_RPAREN) { 851 while (1) { 852 if (m->param_count >= MAX_PARAMS) { 853 return fail("bad macro header"); 854 } 855 if (s->pos >= s->end || s->pos->kind != TOK_WORD) { 856 return fail("bad macro header"); 857 } 858 m->params[m->param_count] = s->pos->text; 859 m->param_count++; 860 s->pos++; 861 skip_newlines(&s->pos, s->end); 862 if (s->pos < s->end && s->pos->kind == TOK_COMMA) { 863 s->pos++; 864 skip_newlines(&s->pos, s->end); 865 continue; 866 } 867 break; 868 } 869 } 870 871 if (s->pos >= s->end || s->pos->kind != TOK_RPAREN) { 872 return fail("bad macro header"); 873 } 874 s->pos++; 875 skip_newlines(&s->pos, s->end); 876 877 m->body_start = macro_body_tokens + macro_body_used; 878 while (s->pos < s->end) { 879 int idx; 880 881 if (s->pos->kind == TOK_WORD && token_text_eq(s->pos, "%endm")) { 882 s->pos++; 883 if (s->pos >= s->end || s->pos->kind != TOK_NEWLINE) { 884 return fail("expected newline after %endm"); 885 } 886 if (started_at_line_start) { 887 s->pos++; 888 s->line_start = 1; 889 } 890 m->body_end = macro_body_tokens + macro_body_used; 891 macro_count++; 892 return 1; 893 } 894 if (macro_body_used >= MAX_MACRO_BODY_TOKENS) { 895 return fail("macro body overflow"); 896 } 897 idx = macro_body_used; 898 macro_body_tokens[idx] = *s->pos; 899 macro_body_param_idx[idx] = (unsigned char)find_param(m, s->pos); 900 macro_body_is_local_label[idx] = 901 is_local_label_token(s->pos) ? 1 : 0; 902 if (s->pos->kind == TOK_PASTE) { 903 m->has_paste = 1; 904 } 905 macro_body_used++; 906 s->pos++; 907 } 908 909 return fail("unterminated macro"); 910 } 911 912 static int parse_args(struct Token *lparen, struct Token *limit) 913 { 914 struct Token *tok = lparen + 1; 915 struct Token *arg_start = tok; 916 int depth = 1; 917 int brace_depth = 0; 918 int arg_index = 0; 919 920 args_have_paste = 0; 921 922 while (tok < limit) { 923 if (tok->kind == TOK_PASTE) { 924 args_have_paste = 1; 925 } 926 if (tok->kind == TOK_LPAREN) { 927 depth++; 928 tok++; 929 continue; 930 } 931 if (tok->kind == TOK_RPAREN) { 932 depth--; 933 if (depth == 0) { 934 if (brace_depth != 0) { 935 return fail("unbalanced braces"); 936 } 937 if (arg_start == tok && arg_index == 0) { 938 arg_count = 0; 939 } else { 940 if (arg_index >= MAX_PARAMS) { 941 return fail("too many args"); 942 } 943 arg_starts[arg_index] = arg_start; 944 arg_ends[arg_index] = tok; 945 arg_count = arg_index + 1; 946 } 947 call_end_pos = tok + 1; 948 return 1; 949 } 950 tok++; 951 continue; 952 } 953 if (tok->kind == TOK_LBRACE) { 954 brace_depth++; 955 tok++; 956 continue; 957 } 958 if (tok->kind == TOK_RBRACE) { 959 if (brace_depth <= 0) { 960 return fail("unbalanced braces"); 961 } 962 brace_depth--; 963 tok++; 964 continue; 965 } 966 if (tok->kind == TOK_COMMA && depth == 1 && brace_depth == 0) { 967 if (arg_index >= MAX_PARAMS) { 968 return fail("too many args"); 969 } 970 arg_starts[arg_index] = arg_start; 971 arg_ends[arg_index] = tok; 972 arg_index++; 973 arg_start = tok + 1; 974 tok++; 975 continue; 976 } 977 tok++; 978 } 979 980 return fail("unterminated macro call"); 981 } 982 983 static int arg_is_braced(struct TokenSpan span) 984 { 985 struct Token *tok; 986 int depth; 987 988 if (span.end - span.start < 2) { 989 return 0; 990 } 991 if (span.start->kind != TOK_LBRACE || 992 (span.end - 1)->kind != TOK_RBRACE) { 993 return 0; 994 } 995 depth = 0; 996 for (tok = span.start; tok < span.end; tok++) { 997 if (tok->kind == TOK_LBRACE) { 998 depth++; 999 } else if (tok->kind == TOK_RBRACE) { 1000 depth--; 1001 if (depth == 0 && tok != span.end - 1) { 1002 return 0; 1003 } 1004 } 1005 } 1006 return depth == 0; 1007 } 1008 1009 static int copy_arg_tokens_to_pool(struct TokenSpan span) 1010 { 1011 if (span.start == span.end) { 1012 return fail("bad macro argument"); 1013 } 1014 if (arg_is_braced(span)) { 1015 struct TokenSpan inner; 1016 inner.start = span.start + 1; 1017 inner.end = span.end - 1; 1018 if (inner.start == inner.end) { 1019 return 1; 1020 } 1021 return copy_span_to_pool(inner); 1022 } 1023 return copy_span_to_pool(span); 1024 } 1025 1026 static int copy_paste_arg_to_pool(struct TokenSpan span) 1027 { 1028 if (arg_is_braced(span)) { 1029 return fail("bad macro argument"); 1030 } 1031 if (span.end - span.start != 1) { 1032 return fail("bad macro argument"); 1033 } 1034 return copy_span_to_pool(span); 1035 } 1036 1037 static int append_pasted_token(struct Token *dst, 1038 const struct Token *left, 1039 const struct Token *right) 1040 { 1041 char tmp[512]; 1042 char *text_ptr; 1043 int n; 1044 1045 n = snprintf(tmp, sizeof(tmp), "%.*s%.*s", 1046 left->text.len, left->text.ptr, 1047 right->text.len, right->text.ptr); 1048 if (n < 0 || n >= (int)sizeof(tmp)) { 1049 return fail("bad paste"); 1050 } 1051 text_ptr = append_text_len(tmp, n); 1052 if (text_ptr == NULL) { 1053 return 0; 1054 } 1055 dst->kind = TOK_WORD; 1056 dst->tight = 0; 1057 dst->text.ptr = text_ptr; 1058 dst->text.len = n; 1059 return 1; 1060 } 1061 1062 static int paste_pool_range(int mark) 1063 { 1064 /* Skip newlines on both sides of TOK_PASTE: a body like `foo ##\n bar` 1065 * pastes to `foobar`, discarding the intervening newline. The left 1066 * operand is the rightmost non-newline already copied to `out`; the 1067 * right operand is the next non-newline past PASTE in `in`. */ 1068 struct Token *start = expand_pool + mark; 1069 struct Token *in = start; 1070 struct Token *out = start; 1071 struct Token *end = expand_pool + pool_used; 1072 1073 while (in < end) { 1074 if (in->kind == TOK_PASTE) { 1075 struct Token *left = out; 1076 struct Token *right = in + 1; 1077 1078 while (left > start && (left - 1)->kind == TOK_NEWLINE) { 1079 left--; 1080 } 1081 if (left == start) { 1082 pool_used = mark; 1083 return fail("bad paste"); 1084 } 1085 left--; 1086 if (left->kind == TOK_PASTE) { 1087 pool_used = mark; 1088 return fail("bad paste"); 1089 } 1090 while (right < end && right->kind == TOK_NEWLINE) { 1091 right++; 1092 } 1093 if (right >= end || right->kind == TOK_PASTE) { 1094 pool_used = mark; 1095 return fail("bad paste"); 1096 } 1097 if (!append_pasted_token(left, left, right)) { 1098 pool_used = mark; 1099 return 0; 1100 } 1101 out = left + 1; 1102 in = right + 1; 1103 continue; 1104 } 1105 if (out != in) { 1106 *out = *in; 1107 } 1108 out++; 1109 in++; 1110 } 1111 1112 pool_used = (int)(out - expand_pool); 1113 return 1; 1114 } 1115 1116 static int is_local_label_token(const struct Token *tok) 1117 { 1118 if (tok->kind != TOK_WORD || tok->text.len < 3) { 1119 return 0; 1120 } 1121 if (tok->text.ptr[0] != ':' && tok->text.ptr[0] != '&') { 1122 return 0; 1123 } 1124 if (tok->text.ptr[1] != '@') { 1125 return 0; 1126 } 1127 return 1; 1128 } 1129 1130 static int push_local_label_token(const struct Token *tok, int expansion_id) 1131 { 1132 /* Rewrite ":@name" -> ":name__NN", "&@name" -> "&name__NN". 1133 * Build the text directly in text_buf so the resulting span is stable. */ 1134 char digits[16]; 1135 int digit_count = 0; 1136 int unsigned_id; 1137 int start; 1138 int total; 1139 int i; 1140 struct Token out; 1141 1142 unsigned_id = expansion_id; 1143 if (unsigned_id == 0) { 1144 digits[digit_count++] = '0'; 1145 } else { 1146 while (unsigned_id > 0) { 1147 digits[digit_count++] = (char)('0' + (unsigned_id % 10)); 1148 unsigned_id /= 10; 1149 } 1150 } 1151 1152 /* Reserve: sigil(1) + tail(len-2) + "__"(2) + digits + NUL. */ 1153 total = 1 + (tok->text.len - 2) + 2 + digit_count; 1154 if (text_used + total + 1 > MAX_TEXT) { 1155 return fail("text overflow"); 1156 } 1157 start = text_used; 1158 text_buf[text_used++] = tok->text.ptr[0]; 1159 memcpy(text_buf + text_used, tok->text.ptr + 2, (size_t)(tok->text.len - 2)); 1160 text_used += tok->text.len - 2; 1161 text_buf[text_used++] = '_'; 1162 text_buf[text_used++] = '_'; 1163 for (i = digit_count - 1; i >= 0; i--) { 1164 text_buf[text_used++] = digits[i]; 1165 } 1166 text_buf[text_used++] = '\0'; 1167 1168 out.kind = TOK_WORD; 1169 out.tight = 0; 1170 out.line = current_line; 1171 out.text.ptr = text_buf + start; 1172 out.text.len = total; 1173 return push_pool_token(out); 1174 } 1175 1176 static int expand_macro_tokens(struct Token *call_tok, struct Token *limit, 1177 const struct Macro *m, struct Token **after_out, 1178 int *mark_out) 1179 { 1180 struct Token *body_tok; 1181 struct Token *end_pos; 1182 int mark; 1183 int expansion_id; 1184 int saw_arg_paste = 0; 1185 1186 if (call_tok + 1 < limit && (call_tok + 1)->kind == TOK_LPAREN && 1187 (call_tok + 1)->tight) { 1188 if (!parse_args(call_tok + 1, limit)) { 1189 return 0; 1190 } 1191 if (arg_count != m->param_count) { 1192 return fail("wrong arg count"); 1193 } 1194 end_pos = call_end_pos; 1195 saw_arg_paste = args_have_paste; 1196 } else if (m->param_count == 0) { 1197 arg_count = 0; 1198 end_pos = call_tok + 1; 1199 } else { 1200 return fail("bad macro call"); 1201 } 1202 1203 expansion_id = ++next_expansion_id; 1204 mark = pool_used; 1205 for (body_tok = m->body_start; body_tok < m->body_end; body_tok++) { 1206 int idx = (int)(body_tok - macro_body_tokens); 1207 int param_idx = macro_body_param_idx[idx]; 1208 int pasted = 0; 1209 int ok; 1210 1211 if (param_idx != 0) { 1212 struct TokenSpan arg = {arg_starts[param_idx - 1], arg_ends[param_idx - 1]}; 1213 pasted = (body_tok > m->body_start && (body_tok - 1)->kind == TOK_PASTE) || 1214 (body_tok + 1 < m->body_end && (body_tok + 1)->kind == TOK_PASTE); 1215 ok = pasted ? copy_paste_arg_to_pool(arg) : copy_arg_tokens_to_pool(arg); 1216 if (!ok) { 1217 pool_used = mark; 1218 return 0; 1219 } 1220 continue; 1221 } 1222 if (macro_body_is_local_label[idx]) { 1223 if (!push_local_label_token(body_tok, expansion_id)) { 1224 pool_used = mark; 1225 return 0; 1226 } 1227 continue; 1228 } 1229 if (!push_pool_token(*body_tok)) { 1230 pool_used = mark; 1231 return 0; 1232 } 1233 } 1234 1235 if ((m->has_paste || saw_arg_paste) && !paste_pool_range(mark)) { 1236 return 0; 1237 } 1238 *after_out = end_pos; 1239 *mark_out = mark; 1240 return 1; 1241 } 1242 1243 static int parse_int_token(const struct Token *tok, long long *out) 1244 { 1245 char tmp[128]; 1246 char *end; 1247 unsigned long long uv; 1248 long long sv; 1249 1250 if (tok->kind != TOK_WORD || tok->text.len <= 0 || tok->text.len >= (int)sizeof(tmp)) { 1251 return fail("bad integer"); 1252 } 1253 memcpy(tmp, tok->text.ptr, (size_t)tok->text.len); 1254 tmp[tok->text.len] = '\0'; 1255 1256 errno = 0; 1257 if (tmp[0] == '-') { 1258 sv = strtoll(tmp, &end, 0); 1259 if (errno != 0 || *end != '\0') { 1260 return fail("bad integer"); 1261 } 1262 *out = sv; 1263 return 1; 1264 } 1265 1266 uv = strtoull(tmp, &end, 0); 1267 if (errno != 0 || *end != '\0') { 1268 return fail("bad integer"); 1269 } 1270 *out = (long long)uv; 1271 return 1; 1272 } 1273 1274 static enum ExprOp expr_op_code(const struct Token *tok) 1275 { 1276 if (tok->kind != TOK_WORD) { 1277 return EXPR_INVALID; 1278 } 1279 if (token_text_eq(tok, "+")) { 1280 return EXPR_ADD; 1281 } 1282 if (token_text_eq(tok, "-")) { 1283 return EXPR_SUB; 1284 } 1285 if (token_text_eq(tok, "*")) { 1286 return EXPR_MUL; 1287 } 1288 if (token_text_eq(tok, "/")) { 1289 return EXPR_DIV; 1290 } 1291 if (token_text_eq(tok, "%")) { 1292 return EXPR_MOD; 1293 } 1294 if (token_text_eq(tok, "<<")) { 1295 return EXPR_SHL; 1296 } 1297 if (token_text_eq(tok, ">>")) { 1298 return EXPR_SHR; 1299 } 1300 if (token_text_eq(tok, "&")) { 1301 return EXPR_AND; 1302 } 1303 if (token_text_eq(tok, "|")) { 1304 return EXPR_OR; 1305 } 1306 if (token_text_eq(tok, "^")) { 1307 return EXPR_XOR; 1308 } 1309 if (token_text_eq(tok, "~")) { 1310 return EXPR_NOT; 1311 } 1312 if (token_text_eq(tok, "=")) { 1313 return EXPR_EQ; 1314 } 1315 if (token_text_eq(tok, "!=")) { 1316 return EXPR_NE; 1317 } 1318 if (token_text_eq(tok, "<")) { 1319 return EXPR_LT; 1320 } 1321 if (token_text_eq(tok, "<=")) { 1322 return EXPR_LE; 1323 } 1324 if (token_text_eq(tok, ">")) { 1325 return EXPR_GT; 1326 } 1327 if (token_text_eq(tok, ">=")) { 1328 return EXPR_GE; 1329 } 1330 if (token_text_eq(tok, "strlen")) { 1331 return EXPR_STRLEN; 1332 } 1333 return EXPR_INVALID; 1334 } 1335 1336 static int apply_expr_op(enum ExprOp op, const long long *args, int argc, long long *out) 1337 { 1338 int i; 1339 1340 switch (op) { 1341 case EXPR_ADD: 1342 if (argc < 1) { 1343 return fail("bad expression"); 1344 } 1345 *out = args[0]; 1346 for (i = 1; i < argc; i++) { 1347 *out += args[i]; 1348 } 1349 return 1; 1350 case EXPR_SUB: 1351 if (argc < 1) { 1352 return fail("bad expression"); 1353 } 1354 *out = (argc == 1) ? -args[0] : args[0]; 1355 for (i = 1; i < argc; i++) { 1356 *out -= args[i]; 1357 } 1358 return 1; 1359 case EXPR_MUL: 1360 if (argc < 1) { 1361 return fail("bad expression"); 1362 } 1363 *out = args[0]; 1364 for (i = 1; i < argc; i++) { 1365 *out *= args[i]; 1366 } 1367 return 1; 1368 case EXPR_DIV: 1369 if (argc != 2 || args[1] == 0) { 1370 return fail("bad expression"); 1371 } 1372 *out = args[0] / args[1]; 1373 return 1; 1374 case EXPR_MOD: 1375 if (argc != 2 || args[1] == 0) { 1376 return fail("bad expression"); 1377 } 1378 *out = args[0] % args[1]; 1379 return 1; 1380 case EXPR_SHL: 1381 if (argc != 2) { 1382 return fail("bad expression"); 1383 } 1384 *out = (long long)((unsigned long long)args[0] << args[1]); 1385 return 1; 1386 case EXPR_SHR: 1387 if (argc != 2) { 1388 return fail("bad expression"); 1389 } 1390 *out = args[0] >> args[1]; 1391 return 1; 1392 case EXPR_AND: 1393 if (argc < 1) { 1394 return fail("bad expression"); 1395 } 1396 *out = args[0]; 1397 for (i = 1; i < argc; i++) { 1398 *out &= args[i]; 1399 } 1400 return 1; 1401 case EXPR_OR: 1402 if (argc < 1) { 1403 return fail("bad expression"); 1404 } 1405 *out = args[0]; 1406 for (i = 1; i < argc; i++) { 1407 *out |= args[i]; 1408 } 1409 return 1; 1410 case EXPR_XOR: 1411 if (argc < 1) { 1412 return fail("bad expression"); 1413 } 1414 *out = args[0]; 1415 for (i = 1; i < argc; i++) { 1416 *out ^= args[i]; 1417 } 1418 return 1; 1419 case EXPR_NOT: 1420 if (argc != 1) { 1421 return fail("bad expression"); 1422 } 1423 *out = ~args[0]; 1424 return 1; 1425 case EXPR_EQ: 1426 if (argc != 2) { 1427 return fail("bad expression"); 1428 } 1429 *out = (args[0] == args[1]); 1430 return 1; 1431 case EXPR_NE: 1432 if (argc != 2) { 1433 return fail("bad expression"); 1434 } 1435 *out = (args[0] != args[1]); 1436 return 1; 1437 case EXPR_LT: 1438 if (argc != 2) { 1439 return fail("bad expression"); 1440 } 1441 *out = (args[0] < args[1]); 1442 return 1; 1443 case EXPR_LE: 1444 if (argc != 2) { 1445 return fail("bad expression"); 1446 } 1447 *out = (args[0] <= args[1]); 1448 return 1; 1449 case EXPR_GT: 1450 if (argc != 2) { 1451 return fail("bad expression"); 1452 } 1453 *out = (args[0] > args[1]); 1454 return 1; 1455 case EXPR_GE: 1456 if (argc != 2) { 1457 return fail("bad expression"); 1458 } 1459 *out = (args[0] >= args[1]); 1460 return 1; 1461 case EXPR_STRLEN: 1462 case EXPR_INVALID: 1463 break; 1464 } 1465 1466 return fail("bad expression"); 1467 } 1468 1469 static int eval_expr_range(struct TokenSpan span, long long *out); 1470 1471 static int expand_local_into_pool(struct Token *call_tok, struct Token *limit, 1472 struct Token **after_out, int *mark_out) 1473 { 1474 /* Resolve %local(NAME) against the current frame: build the lookup 1475 * key "<frame>_FRAME.<NAME>" and copy the matching macro's body 1476 * into the pool. NAME must be exactly one WORD token. The pool 1477 * mark and the position past the call's `)` are returned so the 1478 * caller can either push the body as a stream (process_tokens) or 1479 * recursively eval it as an expression (eval_expr_atom). */ 1480 char name[256]; 1481 int frame_len; 1482 int arg_len; 1483 int name_len; 1484 int i; 1485 const struct Macro *m = NULL; 1486 struct Token *arg_tok; 1487 int mark = pool_used; 1488 1489 if (call_tok + 1 >= limit || (call_tok + 1)->kind != TOK_LPAREN || 1490 !(call_tok + 1)->tight) { 1491 return fail("bad builtin"); 1492 } 1493 if (!parse_args(call_tok + 1, limit)) { 1494 return 0; 1495 } 1496 if (arg_count != 1) { 1497 return fail("bad builtin"); 1498 } 1499 if (arg_ends[0] - arg_starts[0] != 1) { 1500 return fail("bad builtin"); 1501 } 1502 arg_tok = arg_starts[0]; 1503 if (arg_tok->kind != TOK_WORD) { 1504 return fail("bad builtin"); 1505 } 1506 if (!frame_active) { 1507 return fail("local outside frame"); 1508 } 1509 1510 frame_len = current_frame.len; 1511 arg_len = arg_tok->text.len; 1512 name_len = frame_len + 7 /* _FRAME. */ + arg_len; 1513 if (name_len >= (int)sizeof(name)) { 1514 return fail("local name too long"); 1515 } 1516 memcpy(name, current_frame.ptr, (size_t)frame_len); 1517 memcpy(name + frame_len, "_FRAME.", 7); 1518 memcpy(name + frame_len + 7, arg_tok->text.ptr, (size_t)arg_len); 1519 1520 for (i = 0; i < macro_count; i++) { 1521 if (macros[i].name.len == name_len && 1522 memcmp(macros[i].name.ptr, name, (size_t)name_len) == 0) { 1523 m = ¯os[i]; 1524 break; 1525 } 1526 } 1527 if (m == NULL) { 1528 return fail("unknown local"); 1529 } 1530 1531 if (!copy_span_to_pool((struct TokenSpan){m->body_start, m->body_end})) { 1532 pool_used = mark; 1533 return 0; 1534 } 1535 *after_out = call_end_pos; 1536 *mark_out = mark; 1537 return 1; 1538 } 1539 1540 static int eval_expr_atom(struct Token *tok, struct Token *limit, 1541 struct Token **after_out, long long *out) 1542 { 1543 const struct Macro *macro; 1544 struct Token *after; 1545 int mark; 1546 1547 if (tok->kind == TOK_WORD && token_text_eq(tok, "%local")) { 1548 if (!expand_local_into_pool(tok, limit, &after, &mark)) { 1549 return 0; 1550 } 1551 if (pool_used == mark) { 1552 pool_used = mark; 1553 return fail("bad expression"); 1554 } 1555 if (!eval_expr_range((struct TokenSpan){expand_pool + mark, expand_pool + pool_used}, out)) { 1556 pool_used = mark; 1557 return 0; 1558 } 1559 pool_used = mark; 1560 *after_out = after; 1561 return 1; 1562 } 1563 1564 macro = find_macro(tok); 1565 if (macro != NULL && 1566 ((tok + 1 < limit && (tok + 1)->kind == TOK_LPAREN && 1567 (tok + 1)->tight) || 1568 macro->param_count == 0)) { 1569 if (!expand_macro_tokens(tok, limit, macro, &after, &mark)) { 1570 return 0; 1571 } 1572 if (pool_used == mark) { 1573 pool_used = mark; 1574 return fail("bad expression"); 1575 } 1576 if (!eval_expr_range((struct TokenSpan){expand_pool + mark, expand_pool + pool_used}, out)) { 1577 pool_used = mark; 1578 return 0; 1579 } 1580 pool_used = mark; 1581 *after_out = after; 1582 return 1; 1583 } 1584 1585 if (!parse_int_token(tok, out)) { 1586 return 0; 1587 } 1588 *after_out = tok + 1; 1589 return 1; 1590 } 1591 1592 static int eval_expr_range(struct TokenSpan span, long long *out) 1593 { 1594 struct ExprFrame frames[MAX_EXPR_FRAMES]; 1595 int frame_top = 0; 1596 struct Token *pos = span.start; 1597 long long value = 0; 1598 long long result = 0; 1599 int have_value = 0; 1600 int have_result = 0; 1601 1602 for (;;) { 1603 if (have_value) { 1604 if (frame_top > 0) { 1605 struct ExprFrame *frame = &frames[frame_top - 1]; 1606 1607 if (frame->argc >= MAX_PARAMS) { 1608 return fail("bad expression"); 1609 } 1610 frame->args[frame->argc++] = value; 1611 have_value = 0; 1612 continue; 1613 } 1614 if (have_result) { 1615 return fail("bad expression"); 1616 } 1617 result = value; 1618 have_result = 1; 1619 have_value = 0; 1620 continue; 1621 } 1622 1623 skip_newlines(&pos, span.end); 1624 if (pos >= span.end) { 1625 break; 1626 } 1627 if (pos->line > 0) { 1628 current_line = pos->line; 1629 } 1630 1631 if (pos->kind == TOK_LPAREN) { 1632 enum ExprOp op; 1633 1634 pos++; 1635 skip_newlines(&pos, span.end); 1636 if (pos >= span.end) { 1637 return fail("bad expression"); 1638 } 1639 op = expr_op_code(pos); 1640 if (op == EXPR_INVALID) { 1641 return fail("bad expression"); 1642 } 1643 pos++; 1644 if (op == EXPR_STRLEN) { 1645 /* strlen is degenerate: argument is a TOK_STRING atom, 1646 * not a recursive expression. Handle inline and yield 1647 * the string's raw byte count (span.len - 2). */ 1648 skip_newlines(&pos, span.end); 1649 if (pos >= span.end || pos->kind != TOK_STRING) { 1650 return fail("bad expression"); 1651 } 1652 if (pos->text.len < 2 || pos->text.ptr[0] != '"') { 1653 return fail("bad expression"); 1654 } 1655 value = (long long)(pos->text.len - 2); 1656 pos++; 1657 skip_newlines(&pos, span.end); 1658 if (pos >= span.end || pos->kind != TOK_RPAREN) { 1659 return fail("bad expression"); 1660 } 1661 pos++; 1662 have_value = 1; 1663 continue; 1664 } 1665 if (frame_top >= MAX_EXPR_FRAMES) { 1666 return fail("expression overflow"); 1667 } 1668 frames[frame_top].op = op; 1669 frames[frame_top].argc = 0; 1670 frame_top++; 1671 continue; 1672 } 1673 1674 if (pos->kind == TOK_RPAREN) { 1675 if (frame_top <= 0) { 1676 return fail("bad expression"); 1677 } 1678 if (!apply_expr_op(frames[frame_top - 1].op, 1679 frames[frame_top - 1].args, 1680 frames[frame_top - 1].argc, 1681 &value)) { 1682 return 0; 1683 } 1684 frame_top--; 1685 pos++; 1686 have_value = 1; 1687 continue; 1688 } 1689 1690 if (!eval_expr_atom(pos, span.end, &pos, &value)) { 1691 return 0; 1692 } 1693 have_value = 1; 1694 } 1695 1696 if (frame_top != 0 || !have_result) { 1697 return fail("bad expression"); 1698 } 1699 if (pos != span.end) { 1700 return fail("bad expression"); 1701 } 1702 1703 *out = result; 1704 return 1; 1705 } 1706 1707 static int emit_hex_value(unsigned long long value, int bytes) 1708 { 1709 /* Emit the bytes as bare little-endian hex digits. hex2pp's byte- 1710 * stream parser groups every two hex digits into one byte; no 1711 * quoting or separators are needed. */ 1712 char tmp[17]; 1713 static const char hex[] = "0123456789ABCDEF"; 1714 struct Token tok; 1715 int i; 1716 char *text_ptr; 1717 int total_len = 2 * bytes; 1718 1719 for (i = 0; i < bytes; i++) { 1720 unsigned int b = (unsigned int)((value >> (8 * i)) & 0xFF); 1721 tmp[2 * i] = hex[b >> 4]; 1722 tmp[2 * i + 1] = hex[b & 0x0F]; 1723 } 1724 tmp[total_len] = '\0'; 1725 1726 text_ptr = append_text_len(tmp, total_len); 1727 if (text_ptr == NULL) { 1728 return 0; 1729 } 1730 tok.kind = TOK_WORD; 1731 tok.tight = 0; 1732 tok.line = current_line; 1733 tok.text.ptr = text_ptr; 1734 tok.text.len = total_len; 1735 return emit_token(&tok); 1736 } 1737 1738 static int expand_builtin_call(struct Stream *s, const struct Token *tok) 1739 { 1740 long long value; 1741 1742 if (tok + 1 >= s->end || (tok + 1)->kind != TOK_LPAREN) { 1743 return fail("bad builtin"); 1744 } 1745 if (!parse_args((struct Token *)tok + 1, s->end)) { 1746 return 0; 1747 } 1748 1749 if (token_text_eq(tok, "!") || token_text_eq(tok, "@") || 1750 token_text_eq(tok, "%") || token_text_eq(tok, "$")) { 1751 struct TokenSpan arg; 1752 struct Token *end_pos; 1753 int bytes; 1754 1755 if (arg_count != 1) { 1756 return fail("bad builtin"); 1757 } 1758 arg.start = arg_starts[0]; 1759 arg.end = arg_ends[0]; 1760 end_pos = call_end_pos; 1761 if (!eval_expr_range(arg, &value)) { 1762 return 0; 1763 } 1764 s->pos = end_pos; 1765 s->line_start = 0; 1766 bytes = token_text_eq(tok, "!") ? 1 : 1767 token_text_eq(tok, "@") ? 2 : 1768 token_text_eq(tok, "%") ? 4 : 8; 1769 return emit_hex_value((unsigned long long)value, bytes); 1770 } 1771 1772 if (token_text_eq(tok, "%select")) { 1773 struct TokenSpan cond_arg, then_arg, else_arg, chosen; 1774 struct Token *end_pos; 1775 int mark; 1776 1777 if (arg_count != 3) { 1778 return fail("bad builtin"); 1779 } 1780 cond_arg.start = arg_starts[0]; cond_arg.end = arg_ends[0]; 1781 then_arg.start = arg_starts[1]; then_arg.end = arg_ends[1]; 1782 else_arg.start = arg_starts[2]; else_arg.end = arg_ends[2]; 1783 end_pos = call_end_pos; 1784 if (!eval_expr_range(cond_arg, &value)) { 1785 return 0; 1786 } 1787 chosen = (value != 0) ? then_arg : else_arg; 1788 s->pos = end_pos; 1789 s->line_start = 0; 1790 if (chosen.start == chosen.end) { 1791 return 1; 1792 } 1793 mark = pool_used; 1794 if (!copy_span_to_pool(chosen)) { 1795 pool_used = mark; 1796 return 0; 1797 } 1798 return push_pool_stream_from_mark(mark); 1799 } 1800 1801 if (token_text_eq(tok, "%local")) { 1802 struct Token *after; 1803 int mark; 1804 1805 if (!expand_local_into_pool((struct Token *)tok, s->end, &after, &mark)) { 1806 return 0; 1807 } 1808 s->pos = after; 1809 s->line_start = 0; 1810 return push_pool_stream_from_mark(mark); 1811 } 1812 1813 if (token_text_eq(tok, "%str")) { 1814 struct Token *arg_tok; 1815 struct Token *end_pos; 1816 struct Token out_tok; 1817 char *text_ptr; 1818 int orig_len; 1819 int out_len; 1820 1821 if (arg_count != 1) { 1822 return fail("bad builtin"); 1823 } 1824 if (arg_ends[0] - arg_starts[0] != 1) { 1825 return fail("bad builtin"); 1826 } 1827 arg_tok = arg_starts[0]; 1828 if (arg_tok->kind != TOK_WORD) { 1829 return fail("bad builtin"); 1830 } 1831 end_pos = call_end_pos; 1832 1833 orig_len = arg_tok->text.len; 1834 out_len = orig_len + 2; 1835 if (text_used + out_len + 1 > MAX_TEXT) { 1836 return fail("text overflow"); 1837 } 1838 text_ptr = text_buf + text_used; 1839 text_buf[text_used++] = '"'; 1840 memcpy(text_buf + text_used, arg_tok->text.ptr, (size_t)orig_len); 1841 text_used += orig_len; 1842 text_buf[text_used++] = '"'; 1843 text_buf[text_used++] = '\0'; 1844 1845 out_tok.kind = TOK_STRING; 1846 out_tok.tight = 0; 1847 out_tok.line = current_line; 1848 out_tok.text.ptr = text_ptr; 1849 out_tok.text.len = out_len; 1850 s->pos = end_pos; 1851 s->line_start = 0; 1852 return emit_token(&out_tok); 1853 } 1854 1855 return fail("bad builtin"); 1856 } 1857 1858 static int expand_call(struct Stream *s, const struct Macro *macro) 1859 { 1860 struct Token *after; 1861 int mark; 1862 1863 if (!expand_macro_tokens(s->pos, s->end, macro, &after, &mark)) { 1864 return 0; 1865 } 1866 s->pos = after; 1867 s->line_start = 0; 1868 return push_pool_stream_from_mark(mark); 1869 } 1870 1871 static int push_frame(struct Stream *s) 1872 { 1873 /* %frame NAME sets the single-slot current frame, used by %local 1874 * lookup. Frames do not nest: a second %frame before %endframe is 1875 * an error. The header behaves like %scope (newlines after the 1876 * name are absorbed when the directive appeared at line_start). */ 1877 int started_at_line_start = s->line_start; 1878 1879 s->pos++; 1880 skip_newlines(&s->pos, s->end); 1881 if (s->pos >= s->end || s->pos->kind != TOK_WORD) { 1882 return fail("bad frame header"); 1883 } 1884 if (frame_active) { 1885 return fail("frame already active"); 1886 } 1887 current_frame = s->pos->text; 1888 frame_active = 1; 1889 s->pos++; 1890 if (started_at_line_start) { 1891 skip_newlines(&s->pos, s->end); 1892 s->line_start = 1; 1893 } 1894 return 1; 1895 } 1896 1897 static int pop_frame(struct Stream *s) 1898 { 1899 /* %endframe must be immediately followed by TOK_NEWLINE; the newline 1900 * is consumed iff %endframe itself appeared at line_start. */ 1901 int started_at_line_start = s->line_start; 1902 1903 s->pos++; 1904 if (!frame_active) { 1905 return fail("frame underflow"); 1906 } 1907 frame_active = 0; 1908 if (s->pos >= s->end || s->pos->kind != TOK_NEWLINE) { 1909 return fail("expected newline after %endframe"); 1910 } 1911 if (started_at_line_start) { 1912 s->pos++; 1913 s->line_start = 1; 1914 } 1915 return 1; 1916 } 1917 1918 static int reset_output(struct Stream *s) 1919 { 1920 long long base; 1921 1922 if (!s->line_start) { 1923 return fail("bad reset-output directive"); 1924 } 1925 s->pos++; 1926 if (s->pos >= s->end || s->pos->kind != TOK_WORD || 1927 !parse_int_token(s->pos, &base) || base < 0 || base > 2147483646LL) { 1928 return fail("bad reset-output directive"); 1929 } 1930 s->pos++; 1931 if (s->pos >= s->end || s->pos->kind != TOK_NEWLINE) { 1932 return fail("bad reset-output directive"); 1933 } 1934 s->pos++; 1935 s->line_start = 1; 1936 output_used = 0; 1937 output_need_space = 0; 1938 next_expansion_id = (int)base; 1939 return 1; 1940 } 1941 1942 static int process_tokens(void) 1943 { 1944 if (!push_stream_span((struct TokenSpan){source_tokens, source_tokens + source_count}, -1)) { 1945 return 0; 1946 } 1947 1948 /* Per-token dispatch is gated on the first byte of WORD tokens. 1949 * Plain pass-through tokens (e.g. hex literals, bare identifiers) 1950 * fail the c0=='%' / c0 in {!,@,$} test in one byte compare and go 1951 * straight to emit_token. Within the c0=='%' branch we dispatch on 1952 * the second byte to pick the matching directive/builtin without 1953 * walking ~9 token_text_eq probes. */ 1954 for (;;) { 1955 struct Stream *s; 1956 struct Token *tok; 1957 1958 s = current_stream(); 1959 if (s == NULL) { 1960 break; 1961 } 1962 if (s->pos >= s->end) { 1963 pop_stream(); 1964 continue; 1965 } 1966 1967 tok = s->pos; 1968 if (tok->line > 0) { 1969 current_line = tok->line; 1970 } 1971 1972 if (tok->kind == TOK_NEWLINE) { 1973 s->pos++; 1974 s->line_start = 1; 1975 if (!emit_newline()) { 1976 return 0; 1977 } 1978 continue; 1979 } 1980 1981 if (tok->kind == TOK_WORD && tok->text.len >= 1) { 1982 const char *p = tok->text.ptr; 1983 int len = tok->text.len; 1984 char c0 = p[0]; 1985 int has_paren = (tok + 1 < s->end && 1986 (tok + 1)->kind == TOK_LPAREN && 1987 (tok + 1)->tight); 1988 1989 if (c0 == '%' && len >= 2) { 1990 char c1 = p[1]; 1991 const struct Macro *macro; 1992 int handled = 0; 1993 1994 switch (c1) { 1995 case 'm': 1996 if (len == 6 && memcmp(p + 2, "acro", 4) == 0) { 1997 if (!define_macro(s)) return 0; 1998 handled = 1; 1999 } 2000 break; 2001 case 's': 2002 if (len == 7 && memcmp(p + 2, "truct", 5) == 0) { 2003 if (!define_fielded(s, 8, "SIZE", 4)) return 0; 2004 handled = 1; 2005 } else if (has_paren && len == 7 && 2006 memcmp(p + 2, "elect", 5) == 0) { 2007 if (!expand_builtin_call(s, tok)) return 0; 2008 handled = 1; 2009 } else if (has_paren && len == 4 && 2010 memcmp(p + 2, "tr", 2) == 0) { 2011 if (!expand_builtin_call(s, tok)) return 0; 2012 handled = 1; 2013 } 2014 break; 2015 case 'e': 2016 if (len == 5 && memcmp(p + 2, "num", 3) == 0) { 2017 if (!define_fielded(s, 1, "COUNT", 5)) return 0; 2018 handled = 1; 2019 } else if (len == 9 && 2020 memcmp(p + 2, "ndframe", 7) == 0) { 2021 if (!pop_frame(s)) return 0; 2022 handled = 1; 2023 } 2024 break; 2025 case 'f': 2026 if (len == 6 && memcmp(p + 2, "rame", 4) == 0) { 2027 if (!push_frame(s)) return 0; 2028 handled = 1; 2029 } 2030 break; 2031 case 'b': 2032 if (has_paren && len == 6 && 2033 memcmp(p + 2, "ytes", 4) == 0) { 2034 if (!expand_builtin_call(s, tok)) return 0; 2035 handled = 1; 2036 } 2037 break; 2038 case 'l': 2039 if (has_paren && len == 6 && 2040 memcmp(p + 2, "ocal", 4) == 0) { 2041 if (!expand_builtin_call(s, tok)) return 0; 2042 handled = 1; 2043 } 2044 break; 2045 case 'r': 2046 if (len == 13 && 2047 memcmp(p + 2, "eset-output", 11) == 0) { 2048 if (!reset_output(s)) return 0; 2049 handled = 1; 2050 } 2051 break; 2052 } 2053 2054 if (handled) { 2055 continue; 2056 } 2057 2058 macro = find_macro(tok); 2059 if (macro != NULL && 2060 (has_paren || macro->param_count == 0)) { 2061 if (!expand_call(s, macro)) return 0; 2062 continue; 2063 } 2064 } else if (len == 1 && 2065 (c0 == '!' || c0 == '@' || 2066 c0 == '$' || c0 == '%')) { 2067 if (has_paren) { 2068 if (!expand_builtin_call(s, tok)) return 0; 2069 continue; 2070 } 2071 } 2072 } 2073 2074 s->pos++; 2075 s->line_start = 0; 2076 if (!emit_token(tok)) { 2077 return 0; 2078 } 2079 } 2080 2081 if (frame_active) { 2082 return fail("frame not closed"); 2083 } 2084 2085 if (output_used >= MAX_OUTPUT) { 2086 return fail("output overflow"); 2087 } 2088 output_buf[output_used] = '\0'; 2089 return 1; 2090 } 2091 2092 int main(int argc, char **argv) 2093 { 2094 FILE *in; 2095 FILE *out; 2096 size_t nread; 2097 2098 if (argc != 3) { 2099 fprintf(stderr, "usage: %s input.M1 output.M1\n", argv[0]); 2100 return 1; 2101 } 2102 2103 input_path = argv[1]; 2104 in = fopen(argv[1], "rb"); 2105 if (in == NULL) { 2106 perror(argv[1]); 2107 return 1; 2108 } 2109 nread = fread(input_buf, 1, MAX_INPUT, in); 2110 if (ferror(in)) { 2111 perror(argv[1]); 2112 fclose(in); 2113 return 1; 2114 } 2115 fclose(in); 2116 if (nread >= MAX_INPUT) { 2117 fprintf(stderr, "input too large\n"); 2118 return 1; 2119 } 2120 input_buf[nread] = '\0'; 2121 2122 if (!lex_source(input_buf) || !process_tokens()) { 2123 fprintf(stderr, "%s:%d: m1macro: %s\n", 2124 input_path != NULL ? input_path : "?", 2125 error_line, 2126 error_msg != NULL ? error_msg : "failed"); 2127 return 1; 2128 } 2129 2130 out = fopen(argv[2], "wb"); 2131 if (out == NULL) { 2132 perror(argv[2]); 2133 return 1; 2134 } 2135 if (fwrite(output_buf, 1, (size_t)output_used, out) != (size_t)output_used) { 2136 perror(argv[2]); 2137 fclose(out); 2138 return 1; 2139 } 2140 fclose(out); 2141 fprintf(stderr, "text_used=%d output_used=%d\n", text_used, output_used); 2142 return 0; 2143 }