boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs

commit de636ed71441d5dd25d5e73eb0bc1cb808acdc17
parent fa9eb5967ef6ac61d3711e3687f7b2ffdbaf8767
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 23 Apr 2026 14:45:59 -0700

m1pp: Phase 2 — store %macro definitions in arenas

Replace the structural %macro/%endm skip in m1pp.M1 with a real
define_macro that parses the header (name, comma-separated params,
trailing newline) and copies body tokens into macro_body_tokens[]
until a line-start %endm. Records live in a 32-slot macros[] arena
(296 B/record) with running tail pointers. Macros are still not
invoked; defs-only input continues to match the C oracle byte-for-byte.

Adds tests/m1pp/02-defs.{M1pp,expected} as the Phase 2 parity fixture.

Diffstat:
Mdocs/M1M-IMPL.md | 13++++++++-----
Mm1pp/m1pp.M1 | 453++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Atests/m1pp/02-defs.M1pp | 21+++++++++++++++++++++
Atests/m1pp/02-defs.expected | 8++++++++
4 files changed, 428 insertions(+), 67 deletions(-)

diff --git a/docs/M1M-IMPL.md b/docs/M1M-IMPL.md @@ -487,11 +487,14 @@ symbol name. plus `append_text_len`, `push_token`, `token_text_eq`, `span_eq_token`. -- [ ] **Phase 2 — Macro definition storage.** - Replace structural skipping with real storage: parse header, - params, body tokens, body limits, line-start `%endm`. Does not - yet call macros — adding defs-only input to an otherwise - pass-through run must still match the oracle. +- [x] **Phase 2 — Macro definition storage.** + Replaced structural skipping with real storage: `define_macro` + parses the header (name, params with comma splits, trailing + newline) and copies body tokens into `macro_body_tokens[]` until + a line-start `%endm`. Records land in a 32-slot `macros[]` arena + (296 B/record). Macros are not yet called — defs-only input + matches the oracle. `find_macro` / `find_param` deferred to the + phases that exercise them (Phase 5). Oracle: `define_macro`, `find_macro`, `find_param`. - [ ] **Phase 3 — Stream stack + expansion-pool lifetime.** diff --git a/m1pp/m1pp.M1 b/m1pp/m1pp.M1 @@ -2,8 +2,9 @@ ## ## Runtime shape: m1pp input.M1 output.M1 ## -## Phase 1: lexer + pass-through with structural %macro/%endm skip. -## Behavior mirrors m1pp/m1pp.c (the oracle) for definition-only inputs. +## Phase 2: lexer + pass-through + real %macro storage (header, params, +## body tokens, body limits). Macros are stored but not yet called, so +## definition-only inputs match the C oracle byte-for-byte. ## ## Pipeline: ## _start argv/argc from kernel SP; openat+read into input_buf @@ -12,9 +13,10 @@ ## -> openat+write output_buf to argv[2]; exit ## lex_source input_buf -> source_tokens[] (via append_text + push_source_token) ## process_tokens source_tokens[] -> output_buf (via emit_token / emit_newline, -## branching to skip_macro_def at +## branching to define_macro at ## line-start %macro) -## skip_macro_def structural consume through %endm; no tokens emitted +## define_macro parse %macro header+body; record in macros[] + macro_body_tokens[]; +## consume through the %endm line without emitting output ## ## P1v2 ABI: a0..a3 arg/return, t0..t2 caller-saved temps, s0..s3 callee-saved ## (unused here). Non-leaf functions use enter_0 / leave. _start has no frame; @@ -26,6 +28,14 @@ DEFINE M1PP_INPUT_CAP 0020000000000000 DEFINE M1PP_OUTPUT_CAP 0020000000000000 DEFINE M1PP_TEXT_CAP 0010000000000000 DEFINE M1PP_TOKENS_END 0018000000000000 +## Macro record is 296 bytes: name (16) + param_count (8) + params[16]*16 (256) +## + body_start (8) + body_end (8). 32 macros × 296 = 9472 bytes = MACROS_CAP. +## Body-token arena: 256 × 24 = 6144 bytes = MACRO_BODY_CAP. +DEFINE M1PP_MACRO_RECORD_SIZE 2801000000000000 +DEFINE M1PP_MACRO_BODY_START_OFF 1801000000000000 +DEFINE M1PP_MACRO_BODY_END_OFF 2001000000000000 +DEFINE M1PP_MACROS_CAP 0025000000000000 +DEFINE M1PP_MACRO_BODY_CAP 0018000000000000 DEFINE O_WRONLY_CREAT_TRUNC 4102000000000000 DEFINE MODE_0644 A401000000000000 DEFINE AT_FDCWD 9CFFFFFFFFFFFFFF @@ -58,6 +68,14 @@ DEFINE TOK_PASTE 0600000000000000 la_a1 &source_end st_a0,a1,0 + # macros_end = &macros; macro_body_end = &macro_body_tokens + la_a0 &macros + la_a1 &macros_end + st_a0,a1,0 + la_a0 &macro_body_tokens + la_a1 &macro_body_end + st_a0,a1,0 + # input_fd = openat(AT_FDCWD, argv[1], O_RDONLY, 0) li_a0 sys_openat li_a1 AT_FDCWD @@ -744,8 +762,8 @@ DEFINE TOK_PASTE 0600000000000000 la_br &process_not_macro beqz_a0 - # line-start %macro -> consume the definition - la_br &skip_macro_def + # line-start %macro -> record the definition + la_br &define_macro call la_br &process_loop b @@ -794,111 +812,326 @@ DEFINE TOK_PASTE 0600000000000000 leave ret -## --- Structural %macro skip (placeholder for Phase 2's define_macro) --------- -## Consumes the macro header, body, and %endm line without emitting anything -## or recording the definition. Phase 2 replaces this with real storage of -## the macro's name, params, and body tokens. - -## skip_macro_def(): proc_pos at %macro. Leaves proc_pos after %endm line. -:skip_macro_def +## --- %macro storage: parse header + body into macros[] / macro_body_tokens -- +## Called at proc_pos == line-start `%macro`. Leaves proc_pos past the %endm +## line with proc_line_start = 1. Uses BSS scratch (def_m_ptr, def_param_ptr, +## def_body_line_start) since P1v2 enter/leave does not save s* registers. +## +## Macro record layout (296 bytes, see M1PP_MACRO_RECORD_SIZE): +## +0 name.ptr (8) +## +8 name.len (8) +## +16 param_count (8) +## +24 params[16].ptr/.len (16 * 16 = 256) +## +280 body_start (8) -> *Token into macro_body_tokens[] +## +288 body_end (8) -> exclusive end + +## define_macro(): consume `%macro NAME(params...)\n ... %endm\n`. +:define_macro enter_0 - # consume the %macro token itself, then scan with our own line_start bit + # macros_end bounds check: if (macros_end == &macros + MACROS_CAP) fatal + la_a0 &macros_end + ld_t0,a0,0 + la_a1 &macros + li_a2 M1PP_MACROS_CAP + add_a1,a1,a2 + la_br &err_too_many_macros + beq_t0,a1 + + # def_m_ptr = macros_end (Macro *m = &macros[macro_count]) + la_a1 &def_m_ptr + st_t0,a1,0 + + # advance past the %macro token itself la_a0 &proc_pos ld_t0,a0,0 addi_t0,t0,24 st_t0,a0,0 - la_a0 &skip_line_start - li_a1 %0 %0 - st_a1,a0,0 -:skip_macro_loop - # tok = proc_pos; if (tok == source_end) fatal (unterminated) + # ---- header: name (WORD) ---- + la_a1 &source_end + ld_t1,a1,0 + la_br &err_bad_macro_header + beq_t0,t1 + ld_a1,t0,0 + li_a2 TOK_WORD + la_br &err_bad_macro_header + bne_a1,a2 + + # m->name.ptr = tok->text_ptr; m->name.len = tok->text_len + ld_a2,t0,8 + ld_a3,t0,16 + la_a0 &def_m_ptr + ld_t2,a0,0 + st_a2,t2,0 + st_a3,t2,8 + + # m->param_count = 0; def_param_ptr = m + 24 (first TextSpan slot) + li_a0 %0 %0 + st_a0,t2,16 + addi_t2,t2,24 + la_a0 &def_param_ptr + st_t2,a0,0 + + # advance past name + addi_t0,t0,24 la_a0 &proc_pos - ld_t0,a0,0 + st_t0,a0,0 + + # ---- header: LPAREN ---- la_a1 &source_end ld_t1,a1,0 - la_br &err_unterminated_macro + la_br &err_bad_macro_header beq_t0,t1 + ld_a1,t0,0 + li_a2 TOK_LPAREN + la_br &err_bad_macro_header + bne_a1,a2 - # if (!skip_line_start) just advance past tok - la_a0 &skip_line_start + # advance past '(' + addi_t0,t0,24 + la_a0 &proc_pos + st_t0,a0,0 + + # ---- header: optional param list ---- + # if at end -> fall through to RPAREN check (which will fail) + # if next is RPAREN -> skip the param loop + # else enter param loop + la_a1 &source_end + ld_t1,a1,0 + la_br &def_header_close + beq_t0,t1 + ld_a1,t0,0 + li_a2 TOK_RPAREN + la_br &def_header_close + beq_a1,a2 + +:def_param_loop + # reject > 16 params: if (15 < param_count) fail (param_count capped at 16) + la_a0 &def_m_ptr ld_t2,a0,0 - la_br &skip_check_advance - beqz_t2 + ld_a1,t2,16 + li_a2 %15 %0 + la_br &err_bad_macro_header + blt_a2,a1 - # if (tok->kind != TOK_WORD) just advance + # tok must be in range and WORD + la_a0 &proc_pos + ld_t0,a0,0 + la_a1 &source_end + ld_t1,a1,0 + la_br &err_bad_macro_header + beq_t0,t1 ld_a1,t0,0 li_a2 TOK_WORD - la_br &skip_check_advance + la_br &err_bad_macro_header bne_a1,a2 - # if (!tok_eq_const(tok, "%endm", 5)) just advance - mov_a0,t0 - la_a1 &const_endm - li_a2 %5 %0 - la_br &tok_eq_const - call - la_br &skip_check_advance - beqz_a0 + # *def_param_ptr = (tok.text_ptr, tok.text_len); def_param_ptr += 16 + ld_a2,t0,8 + ld_a3,t0,16 + la_a0 &def_param_ptr + ld_t1,a0,0 + st_a2,t1,0 + st_a3,t1,8 + addi_t1,t1,16 + st_t1,a0,0 - # matched %endm at line start -- fall through to skip_to_line_end + # m->param_count++ + la_a0 &def_m_ptr + ld_t2,a0,0 + ld_a1,t2,16 + addi_a1,a1,1 + st_a1,t2,16 -:skip_to_line_end - # walk to the first newline (inclusive) and stop + # advance past the param word + addi_t0,t0,24 + la_a0 &proc_pos + st_t0,a0,0 + + # if next is COMMA, consume and loop; else break + la_a1 &source_end + ld_t1,a1,0 + la_br &def_header_close + beq_t0,t1 + ld_a1,t0,0 + li_a2 TOK_COMMA + la_br &def_header_close + bne_a1,a2 + addi_t0,t0,24 + la_a0 &proc_pos + st_t0,a0,0 + la_br &def_param_loop + b + +:def_header_close + # ---- header: RPAREN ---- la_a0 &proc_pos ld_t0,a0,0 la_a1 &source_end ld_t1,a1,0 - la_br &skip_done_at_eof + la_br &err_bad_macro_header + beq_t0,t1 + ld_a1,t0,0 + li_a2 TOK_RPAREN + la_br &err_bad_macro_header + bne_a1,a2 + + addi_t0,t0,24 + la_a0 &proc_pos + st_t0,a0,0 + + # ---- header: terminating NEWLINE ---- + la_a1 &source_end + ld_t1,a1,0 + la_br &err_bad_macro_header beq_t0,t1 ld_a1,t0,0 li_a2 TOK_NEWLINE + la_br &err_bad_macro_header + bne_a1,a2 + addi_t0,t0,24 la_a0 &proc_pos st_t0,a0,0 - la_br &skip_done_after_newline - beq_a1,a2 - la_br &skip_to_line_end - b -:skip_done_after_newline -:skip_done_at_eof - # caller resumes at line start - la_a0 &proc_line_start + # ---- body: m->body_start = macro_body_end; body_line_start = 1 ---- + la_a1 &macro_body_end + ld_t2,a1,0 + la_a0 &def_m_ptr + ld_t1,a0,0 + li_a0 M1PP_MACRO_BODY_START_OFF + add_a0,t1,a0 + st_t2,a0,0 + la_a0 &def_body_line_start li_a1 %1 %0 st_a1,a0,0 - leave - ret -:skip_check_advance - # update skip_line_start based on tok->kind, then bump proc_pos +:def_body_loop + # if proc_pos == source_end: unterminated %macro la_a0 &proc_pos ld_t0,a0,0 + la_a1 &source_end + ld_t1,a1,0 + la_br &err_unterminated_macro + beq_t0,t1 + + # if (!body_line_start) copy token + la_a0 &def_body_line_start + ld_t2,a0,0 + la_br &def_body_copy + beqz_t2 + + # if (tok.kind != TOK_WORD) copy token ld_a1,t0,0 - li_a2 TOK_NEWLINE - la_br &skip_set_not_line_start + li_a2 TOK_WORD + la_br &def_body_copy bne_a1,a2 - # tok is TOK_NEWLINE: next iteration is at line start - la_a0 &skip_line_start + # if (!tok_eq_const(tok, "%endm", 5)) copy token + mov_a0,t0 + la_a1 &const_endm + li_a2 %5 %0 + la_br &tok_eq_const + call + la_br &def_body_copy + beqz_a0 + + # matched %endm at line start -> skip to end of the line, then finish + la_br &def_endm_skip_to_newline + b + +:def_body_copy + # bounds: if (macro_body_end - macro_body_tokens + 24 > MACRO_BODY_CAP) fail + la_a0 &macro_body_end + ld_t1,a0,0 + la_a2 &macro_body_tokens + sub_a3,t1,a2 + addi_a3,a3,24 + li_t2 M1PP_MACRO_BODY_CAP + la_br &err_macro_body_overflow + blt_t2,a3 + + # copy 24 bytes from *proc_pos to *macro_body_end + la_a0 &proc_pos + ld_t0,a0,0 + ld_a1,t0,0 + st_a1,t1,0 + ld_a1,t0,8 + st_a1,t1,8 + ld_a1,t0,16 + st_a1,t1,16 + + # macro_body_end += 24 + addi_t1,t1,24 + la_a0 &macro_body_end + st_t1,a0,0 + + # body_line_start = (tok.kind == TOK_NEWLINE) + ld_a1,t0,0 + li_a2 TOK_NEWLINE + la_br &def_body_clear_ls + bne_a1,a2 + la_a0 &def_body_line_start li_a1 %1 %0 st_a1,a0,0 - la_br &skip_inc_pos + la_br &def_body_advance b -:skip_set_not_line_start - la_a0 &skip_line_start +:def_body_clear_ls + la_a0 &def_body_line_start li_a1 %0 %0 st_a1,a0,0 -:skip_inc_pos - # proc_pos++ +:def_body_advance + # proc_pos += 24 la_a0 &proc_pos ld_t0,a0,0 addi_t0,t0,24 st_t0,a0,0 - la_br &skip_macro_loop + la_br &def_body_loop + b + +:def_endm_skip_to_newline + # consume tokens through the first NEWLINE (inclusive); tolerate EOF + la_a0 &proc_pos + ld_t0,a0,0 + la_a1 &source_end + ld_t1,a1,0 + la_br &def_finish + beq_t0,t1 + ld_a1,t0,0 + li_a2 TOK_NEWLINE + addi_t0,t0,24 + la_a0 &proc_pos + st_t0,a0,0 + la_br &def_finish + beq_a1,a2 + la_br &def_endm_skip_to_newline b +:def_finish + # m->body_end = macro_body_end + la_a1 &macro_body_end + ld_t2,a1,0 + la_a0 &def_m_ptr + ld_t1,a0,0 + li_a0 M1PP_MACRO_BODY_END_OFF + add_a0,t1,a0 + st_t2,a0,0 + + # macros_end += MACRO_RECORD_SIZE + la_a0 &macros_end + ld_t0,a0,0 + li_a1 M1PP_MACRO_RECORD_SIZE + add_t0,t0,a1 + st_t0,a0,0 + + # caller resumes at line start + la_a0 &proc_line_start + li_a1 %1 %0 + st_a1,a0,0 + leave + ret + ## --- Error paths ------------------------------------------------------------- ## Each err_* loads a (msg, len) pair for fatal; fatal writes "m1pp: <msg>\n" ## to stderr and exits 1. Error labels are branched to from range/overflow @@ -954,6 +1187,21 @@ DEFINE TOK_PASTE 0600000000000000 li_a1 %30 %0 la_br &fatal b +:err_bad_macro_header + la_a0 &msg_bad_macro_header + li_a1 %16 %0 + la_br &fatal + b +:err_too_many_macros + la_a0 &msg_too_many_macros + li_a1 %15 %0 + la_br &fatal + b +:err_macro_body_overflow + la_a0 &msg_macro_body_overflow + li_a1 %19 %0 + la_br &fatal + b ## fatal(a0=msg_ptr, a1=msg_len): writes "m1pp: <msg>\n" to stderr, exits 1. ## Saves args across the three syscalls since a0..a3 are caller-saved. @@ -1014,6 +1262,9 @@ DEFINE TOK_PASTE 0600000000000000 :msg_token_overflow "token buffer overflow" :msg_output_overflow "output buffer overflow" :msg_unterminated_macro "unterminated %macro definition" +:msg_bad_macro_header "bad macro header" +:msg_too_many_macros "too many macros" +:msg_macro_body_overflow "macro body overflow" ## --- BSS --------------------------------------------------------------------- ## Placed before :ELF_end so filesz/memsz (which this ELF header sets equal) @@ -1053,7 +1304,15 @@ ZERO32 ZERO32 :proc_line_start ZERO32 -:skip_line_start +:macros_end +ZERO32 +:macro_body_end +ZERO32 +:def_m_ptr +ZERO32 +:def_param_ptr +ZERO32 +:def_body_line_start ZERO32 :err_saved_msg ZERO32 @@ -1176,4 +1435,74 @@ ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +## macros: 32 records × 296 bytes = 9472 bytes (M1PP_MACROS_CAP). +## 37 lines × 256 bytes = 9472. Each line is 8 × ZERO32 = 256 bytes. +:macros +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 + +## macro_body_tokens: 256 slots × 24 bytes = 6 KB (M1PP_MACRO_BODY_CAP). +## 24 lines × 256 bytes = 6144. Source tokens are copied in 24 bytes at a +## time as macro bodies are recorded. +:macro_body_tokens +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 +ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 + :ELF_end diff --git a/tests/m1pp/02-defs.M1pp b/tests/m1pp/02-defs.M1pp @@ -0,0 +1,21 @@ +## Phase 2 parity fixture: %macro definitions are stored, not invoked. +## Defs produce no output; non-def tokens pass through as in Phase 1. +## Exercises: 0/1/many params, multi-token bodies, string body tokens, +## body-internal ## paste, %macro-looking words mid-line, empty body. +before +%macro A() + body one + body two +%endm +middle +%macro B(x) + x before ## x after + "string in body" +%endm +not %macro at line start +%macro C(p, q, r) + p q r +%endm +%macro EMPTY(x) +%endm +last diff --git a/tests/m1pp/02-defs.expected b/tests/m1pp/02-defs.expected @@ -0,0 +1,8 @@ +## Phase 2 parity fixture: %macro definitions are stored , not invoked. +## Defs produce no output +## Exercises: 0/1/many params , multi-token bodies , string body tokens , +## body-internal ## paste , %macro-looking words mid-line , empty body. +before +middle +not %macro at line start +last