boot2

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

commit a0b1dde7214f0ea5574565c1c0fbcdd16fedef90
parent 608c2bdc5926f697bace3c4d9881226acd06f792
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 23 Apr 2026 08:07:10 -0700

m1m simplify spec

Diffstat:
Adocs/M1M-IMPL.md | 399+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdocs/M1M-P1-PORT.md | 58++++++++++++++++++++++++++++++++--------------------------
Mpost.md | 45++++++++++++++++-----------------------------
Msrc/m1macro.c | 134++-----------------------------------------------------------------------------
Mtests/m1m/full-parity.M1M | 22+---------------------
Mtests/m1m/full-parity.expected | 17-----------------
6 files changed, 451 insertions(+), 224 deletions(-)

diff --git a/docs/M1M-IMPL.md b/docs/M1M-IMPL.md @@ -0,0 +1,399 @@ +## M1M Implementation Sketch + +This note is the implementation-oriented companion to +`docs/M1M-P1-PORT.md`. It describes a practical structure for the P1 +macro expander. + +### Supported Features + +The target expander supports the features required by `p1/*.M1M`: + +- `%macro NAME(a, b)` / `%endm` +- `%NAME(x, y)` function-like expansion with recursive rescanning +- `##` token paste +- `%le32(expr)` / `%le64(expr)` +- `%select(cond, then, else)` +- Lisp-shaped integer expressions used by the builtins + +### Top-Down Shape + +The program should be structured as a small compiler pipeline: + +1. Runtime shell + Read `argv[1]` into `input_buf`, lex into `source_tokens`, process + tokens through the macro engine, write `output_buf` to `argv[2]`. + This part mostly exists. + +2. Lexer + Keep the current C-compatible tokenizer: + `WORD`, `STRING`, `NEWLINE`, `LPAREN`, `RPAREN`, `COMMA`, `PASTE`. + All token text lives in `text_buf`; tokens store pointers into that + arena. + +3. Definition pass during processing + Processing is single-pass, not a separate pre-scan. At line start, + `%macro` defines a macro and produces no output. Macro definitions + become available only after their definition, matching + `src/m1macro.c`. + +4. Stream-driven expansion + The main processor reads from the top stream. Source input is stream 0. + Macro expansions and `%select` selections push temporary token + streams onto a stack. When a stream is exhausted, it pops and + restores the expansion pool mark. + +5. Macro call expansion + `%NAME(...)` resolves only if `NAME` is already defined and the next + token is `(`. Expansion produces temporary tokens in `expand_pool`, + applies plain parameter substitution and paste, then pushes the + result as a new stream for recursive rescanning. + +6. Builtins + `%le32(expr)` and `%le64(expr)` evaluate integer expressions and emit + one generated token directly. + `%select(cond, then, else)` evaluates `cond` first, then chooses + exactly one of `then` or `else`, copies only that chosen token range + into `expand_pool`, and pushes it as a stream. The unchosen branch is + not expanded, validated, or expression-evaluated. + +7. Errors + Coarse fatal paths are sufficient: malformed macro header, wrong arg + count, bad paste, bad expression, overflow, unterminated macro/call. + Exact C error strings are not required. + +### Core Data Structures + +Use fixed BSS arenas and simple power-of-two-ish records. + +Token record, 32 bytes: + +```text ++0 kind u64 ++8 text_ptr u64 ++16 len u64 ++24 line u64 +``` + +Macro record, 320 bytes: + +```text ++0 name_ptr u64 ++8 name_len u64 ++16 def_line u64 ++24 param_count u64 ++32 body_ptr u64 pointer into macro_body_tokens ++40 body_count u64 ++48 param_ptr[16] 16 * u64 ++176 param_len[16] 16 * u64 ++304 reserved / padding +``` + +Stream record, 40 bytes: + +```text ++0 toks_start u64 ++8 toks_end u64 exclusive pointer, not count ++16 pos u64 current token pointer ++24 line_start u64 ++32 pool_mark u64 -1 for source-owned streams +``` + +Expression frame, around 48 bytes: + +```text ++0 op_code u64 ++8 op_token u64 ++16 argc u64 ++24 accum u64 ++32 line u64 ++40 reserved +``` + +Global arenas: + +```text +input_buf +output_buf +text_buf +source_tokens +macro_body_tokens +macros +expand_pool +streams +arg_starts[16] +arg_ends[16] +expr_frames +``` + +Token range boundaries should be stored as token pointers rather than +indices. That keeps stream and argument walking simple in P1: increment +by 32 bytes, compare pointers, no repeated `base + index << 5`. + +### Bottom-Up Helper Layers + +#### Layer 0: raw memory/text helpers + +```text +append_text(src_ptr, len) -> text_ptr +append_text_cstr(const_ptr, len) -> text_ptr +copy_bytes(dst, src, len) +``` + +#### Layer 1: token helpers + +```text +push_source_token(kind, text_ptr, len, line) +push_macro_body_token(token_ptr) +push_pool_token(token_ptr) +copy_token(dst_ptr, src_ptr) +tok_eq_const(tok, const_ptr, len) -> bool +span_eq_token(ptr, len, tok) -> bool +``` + +#### Layer 2: stream helpers + +```text +push_stream(toks_ptr, count, pool_mark) +pop_stream() +current_stream() -> stream_ptr +stream_peek(stream) -> token_ptr +stream_advance(stream) +``` + +#### Layer 3: macro table helpers + +```text +find_macro(call_tok) -> macro_ptr or 0 +find_param(macro_ptr, body_tok) -> param_index+1 or 0 +define_macro(stream_ptr) +``` + +No `find_prefixed_param` or local-rewrite helper is needed for this +feature set. + +#### Layer 4: argument parser + +```text +parse_args(stream_ptr, lparen_tok_ptr) +``` + +Outputs: + +```text +arg_starts[i] = first token ptr +arg_ends[i] = exclusive token ptr +arg_count +call_end_pos = token ptr after closing RPAREN +``` + +It tracks nested parentheses with a depth counter. Commas split only at +depth 1. + +#### Layer 5: macro body expander + +```text +expand_macro_at(stream_ptr, call_tok, macro_ptr) +``` + +Algorithm: + +1. Parse call args. +2. Validate arg count. +3. Save `mark = pool_used`. +4. Walk macro body tokens. +5. If body token is a param, copy arg tokens into the pool. +6. Otherwise copy the body token as-is. +7. Run paste compaction over `[mark, pool_used)`. +8. Push an expansion stream if non-empty; otherwise restore pool mark. + +#### Layer 6: paste pass + +```text +paste_range(start_ptr, end_ptr) -> new_count +``` + +This is an in-place compactor over `expand_pool`. + +Rules: + +```text +## cannot be first or last +left/right operands cannot be NEWLINE or PASTE +pasted result is TOK_WORD +if a substituted parameter participates in ##, its argument must be exactly one token +``` + +#### Layer 7: expression evaluator + +Do not implement expression evaluation as recursive P1 calls. Use an +explicit expression frame stack. That avoids fragile recursion and makes +macro-in-expression expansion controllable. + +Expression evaluator API: + +```text +eval_expr_range(start_tok_ptr, end_tok_ptr) -> r0 value +``` + +Internal state: + +```text +expr_pos +expr_end +expr_frame_top +expr_done +expr_result +``` + +Loop model: + +1. Skip expression newlines. +2. If token is `(`: + Read next token as operator. + Convert operator token to `op_code`. + Push an expression frame with `argc = 0`, `accum = 0`. + Advance past the operator. +3. If token is `)`: + Finalize the top frame based on `op_code` and `argc`. + Pop the frame. + Feed the produced value into the parent frame, or finish if there is + no parent. +4. If token is an atom: + If token is a macro call, expand it to the pool, then evaluate that + expansion as a nested expression range. + Otherwise parse the integer atom. + Feed the value into the parent frame, or finish if there is no + parent. + +Operators: + +```text ++ variadic, argc >= 1 +- unary neg or binary/variadic subtract, argc >= 1 +* variadic, argc >= 1 +/ binary, div-by-zero check +% binary, div-by-zero check +<< binary +>> binary arithmetic shift +& variadic, argc >= 1 +| variadic, argc >= 1 +^ variadic, argc >= 1 +~ unary += binary +== binary alias +!= binary +< binary +<= binary +> binary +>= binary +``` + +Keeping the full current operator set is cheap and avoids pointless +divergence from the C oracle. + +For macro-in-expression, the clean composition is: + +```text +eval atom sees %NAME followed by LPAREN +expand_macro_at into pool without pushing a stream +temporarily evaluate [mark, mark + expanded_count) +require exactly one expression result and no extra tokens +restore pool mark +advance outer expr_pos to call_end_pos +``` + +That gives the C behavior without mixing expression parsing with the +main output stream. + +#### Layer 8: builtins + +```text +expand_builtin_call(stream_ptr, builtin_tok) +``` + +`%le32` / `%le64`: + +```text +parse args +require one arg +value = eval_expr_range(arg_start, arg_end) +emit_hex_value(value, 4 or 8) +advance stream pos to call_end_pos +line_start = 0 +``` + +`%select`: + +```text +parse args +require three args +value = eval_expr_range(arg0_start, arg0_end) +chosen = arg1 if value != 0 else arg2 +copy chosen tokens to expand_pool +advance stream pos to call_end_pos +push chosen stream +line_start = 0 +``` + +Only `cond` is evaluated eagerly. The selected branch is rescanned as a +normal token stream; the unselected branch is ignored completely. + +#### Layer 9: main processor + +```text +process_tokens: + push_stream(source_tokens, source_count, -1) + + while stream_top > 0: + s = current_stream() + if s.pos == s.end: + pop_stream() + continue + + tok = *s.pos + + if s.line_start && tok == "%macro": + define_macro(s) + continue + + if tok.kind == NEWLINE: + emit_newline() + s.pos += 32 + s.line_start = 1 + continue + + if tok is builtin call: + expand_builtin_call(s, tok) + continue + + if tok is defined macro call: + expand_call(s, macro) + continue + + emit_token(tok) + s.pos += 32 + s.line_start = 0 +``` + +### Implementation Slices + +1. Replace structural `%macro` skipping with `define_macro` storage + only. Verify definition-only inputs still match the C oracle. +2. Add the stream stack and make pass-through processing read from + streams instead of `proc_pos`. +3. Add macro calls with plain parameter substitution. Test one-token and + multi-token args. +4. Add recursive rescanning by pushing expansion streams. Test macro + calling macro. +5. Add paste compaction. Test valid paste, misplaced paste, bad + operands, and pasted-parameter single-token validation. +6. Add integer atom parsing and explicit-stack expression evaluation. + Test arithmetic and comparison expressions without macros. +7. Add `%le32` and `%le64`. +8. Add macro expansion inside expressions. This is required for + `p1/aarch64.M1M`. +9. Add `%select`. +10. Add malformed-input smoke tests and coarse fatal labels. +11. Run C-oracle parity for `tests/m1m/full-parity.M1M`, then combined + `p1/aarch64.M1M + p1/P1.M1M`, then use the produced frontend on a + small P1 program. diff --git a/docs/M1M-P1-PORT.md b/docs/M1M-P1-PORT.md @@ -13,8 +13,9 @@ Contract: m1m input.M1 output.M1 ``` -Behavior should match `src/m1macro.c` byte-for-byte for valid inputs, except -where an implementation limit is explicitly documented. +Behavior should match `src/m1macro.c` byte-for-byte for valid inputs in the +current M1M feature set, except where an implementation limit is explicitly +documented. Architecture-specific code is not allowed in `src/m1m.M1`. The only architecture-specific layer is the generated P1 `DEFINE` file that `catm` @@ -24,17 +25,22 @@ regenerate the arch-specific define tables. ## Scope -Implement the full current macro language: +Implement the current M1M feature set needed by `p1/*.M1M` to define +instruction encodings: - `%macro NAME(a, b)` / `%endm` - `%NAME(x, y)` function-like expansion with recursive rescanning - `##` token paste -- `@local`, `:@local`, `&@local` per-expansion local rewriting -- `:param` / `&param` prefixed single-token parameter substitution - `%le32(expr)` / `%le64(expr)` - `%select(cond, then, else)` - Lisp-shaped integer expressions used by the builtins +Not supported: per-expansion locals (`@local`, `:@local`, `&@local`), +prefixed parameter substitution (`:param`/`&param`), duplicate macro +diagnostics, and byte-identical malformed-input diagnostics. Avoid duplicate +macro names; the feature set does not promise a particular +duplicate-definition behavior. + Preserve the C tokenizer model: whitespace is normalized, strings are single tokens, `#` and `;` comments are skipped, and output is emitted as tokens plus newlines rather than preserving original formatting. @@ -94,20 +100,18 @@ applicable. pass-through fixture output. - [x] Add `tests/m1m/full-parity.M1M` and its C-oracle expected output as the real expansion parity target. This fixture intentionally uses macro calls, - recursive rescanning, paste, prefixed params, locals, `%le32`, `%le64`, and - `%select`; it is expected to fail under the partial P1 implementation until - the remaining unchecked expansion tasks land. + recursive rescanning, paste, `%le32`, `%le64`, and `%select`; it is expected + to fail under the partial P1 implementation until the remaining unchecked + expansion tasks land. - [ ] Replace structural `%macro` skipping with real macro table storage: - parse headers, parameters, body tokens, duplicate-name checks, body limits, - and line-start `%endm` recognition. + parse headers, parameters, body tokens, body limits, and line-start `%endm` + recognition. - [ ] Add stream stack push/pop for recursive rescanning and expansion-pool lifetime management. - [ ] Port macro call argument parsing, including nested parentheses and argument-count validation. -- [ ] Port parameter substitution, single-token `:param`/`&param` prefixed - substitution, and precise empty/multi-token argument errors. -- [ ] Port per-expansion local rewriting for `@local`, `:@local`, and - `&@local`. +- [ ] Port plain parameter substitution, including the single-token argument + requirement when a parameter participates in `##`. - [ ] Port `##` token paste, including bad operand and misplaced paste failures. - [ ] Port integer atom parsing and S-expression evaluation for arithmetic, @@ -116,12 +120,13 @@ applicable. evaluation and token emission. - [ ] Implement `%select(cond, then, else)` on top of expression evaluation and stream pushback. -- [ ] Add malformed-input oracle tests: duplicate macro, unterminated macro, - wrong arg count, bad paste, bad expression, and bad builtin arity. +- [ ] Add malformed-input smoke tests: unterminated macro, wrong arg count, + bad paste, bad expression, and bad builtin arity. These only need non-zero + exit, not exact diagnostic text. - [ ] Use the P1 `m1m` binary to expand a representative M1M frontend and assemble a small program through the normal stage0 toolchain. - [ ] Revisit static limits and error strings so every documented arena limit - has a precise fatal path. + has a clear fatal path. - [ ] Re-run all acceptance tests and update this plan with any explicitly documented implementation limits. @@ -150,13 +155,13 @@ applicable. 5. **Macro definitions** - Port `define_macro`: parse header, params, body tokens, duplicate-name - checks, body limit checks, and line-start `%endm` recognition. + Port `define_macro`: parse header, params, body tokens, body limit checks, + and line-start `%endm` recognition. 6. **Macro call expansion** - Port `parse_args`, parameter substitution, prefixed substitution, local - rewriting, token paste, and expansion-stream pushback. + Port `parse_args`, plain parameter substitution, token paste, and + expansion-stream pushback. 7. **Expression evaluator** @@ -172,9 +177,10 @@ applicable. 9. **Cleanup and limits** - Replace generic “not implemented” errors with precise failures for buffer - overflow, malformed macro headers, arg-count mismatch, bad expressions, and - bad paste operands. + Replace generic “not implemented” errors with coarse but useful failures + for buffer overflow, malformed macro headers, arg-count mismatch, bad + expressions, and bad paste operands. Exact C diagnostic parity is not a + goal. ## Portability Rule @@ -227,8 +233,8 @@ Minimum checks: ``` src/m1macro.c oracle: p1/aarch64.M1M src/m1macro.c oracle: p1/P1.M1M - custom fixture: paste, locals, prefixed args, %le32/%le64, %select - malformed fixtures: duplicate macro, bad paste, wrong arg count + custom fixture: paste, recursive rescanning, %le32/%le64, %select + malformed fixtures: bad paste, wrong arg count, bad expression ``` 3. Require byte-identical output for valid fixtures. diff --git a/post.md b/post.md @@ -123,49 +123,36 @@ M0 to TCC (but no promises). Mostly it's just a cute base to work on. (defn link (hex2-src) (hex2 (catm ELF.hex2 hex2-src))) -;; Compile P1 source, no macros -(defn p1compile0 (src) (M0 (catm P1.M1 src))) - ;; Bootstrap the macro expander once without macros. -(define m1m (link (p1compile0 m1m.M1))) +(define m1m (link (M0 (catm P1.M1 m1m.M1)))) ;; Normal P1 build: expand macros, then assemble/link. -(defn p1compile (src) (M0 (catm P1.M1 (m1m (catm P1M.M1 src))))) +(defn p1compile (src) (M0 (m1m (catm P1M.M1 src)))) (defn p1exe (src) (link (p1compile src))) ``` -That's the first trick: define only the tiny slice of generated P1 `DEFINE`s -needed to write `m1m` in portable P1, then use `m1m` to describe the full P1 -surface in macros instead of by hand. +Now with P1, with macro support, we have a half-decent portable assembler. +We use that to build a Scheme with file IO and process support. ``` -;; Bootstrap P1 in two steps. -(define P1-min (catm P1-min.M1 m1m.M1)) -(define m1m (link (M0 P1-min))) -(define P1 (m1m (catm P1-backend.M1M P1.M1M))) - -;; Now P1 is the portable executable substrate. -(defn p1compile (src) (M0 (catm P1 (m1m src)))) -(defn p1exe (src) (link (p1compile src))) - ;; Build the language that will host the rest of the climb. (define scheme (p1exe scheme.M1)) +``` -;; Scheme is small, but not just an evaluator: it is also the minimal shell -;; language, with enough file I/O and spawn/wait to drive the build. -(define cc-p1 - (scheme cc.scm)) +And then Scheme is our shell and our C compiler. -(defn c->p1 (src) - (scheme cc.scm src)) +``` +(defn scc (src) (scheme cc.scm src)) +(defn ccexe (src) (p1exe (scc src))) +``` -(defn cexe (src) - (p1exe (c->p1 src))) +So, just like in the current bootstrap, a unified source TCC would then compile +as: -;; The Scheme driver compiles the pinned tcc-boot sources through cc-p1, -;; then invokes M1/hex2 for each generated P1 unit. -(define tcc-boot - (scheme build-tcc-boot.scm cc.scm tcc-boot-sources)) +``` +(define tcc0 (ccexe tcc.c)) ;; compiler: scheme +(define tcc1 (tcc0 tcc.c)) ;; compiler: scheme-compiled tcc +(define tcc (tcc1 tcc.c)) ;; compiler: tcc-compiled tcc ``` So, what does P1 code with the macro expansion base look like? diff --git a/src/m1macro.c b/src/m1macro.c @@ -13,9 +13,6 @@ * * %NAME(x, y) function-like macro call * ## token pasting inside macro bodies - * @loop local token inside a macro body - * :@loop / &@loop local label / reference shorthand - * :param / &param prefix a single-token parameter with ':' or '&' * %le32(expr) evaluate an integer S-expression, emit LE 32-bit hex * %le64(expr) evaluate an integer S-expression, emit LE 64-bit hex * %select(c,t,e) evaluate condition S-expression; expand t if nonzero else e @@ -32,6 +29,7 @@ * - There is no cycle detection. Recursive macros will loop until a limit. * - Only recognized %NAME(...) calls expand. Other text passes through. * - Output formatting is normalized to tokens plus '\n', not preserved. + * - Locals (@name) and prefixed params (:param/&param) are not supported. */ #define MAX_INPUT 262144 @@ -93,7 +91,6 @@ static int text_used; static int source_count; static int pool_used; static int macro_count; -static int local_id_next = 1; static int output_used; static int output_need_space; static int stream_top; @@ -318,69 +315,6 @@ static int find_param(const struct Macro *m, const struct Token *tok) return -1; } -static int find_prefixed_param(const struct Macro *m, const struct Token *tok, char *prefix) -{ - int i; - const char *s; - - if (tok->kind != TOK_WORD || tok->len < 2) { - return -1; - } - s = text_buf + tok->start; - if (s[0] != ':' && s[0] != '&') { - return -1; - } - for (i = 0; i < m->param_count; i++) { - if (tok->len - 1 == m->param_len[i] && - memcmp(s + 1, text_buf + m->param_start[i], (size_t)m->param_len[i]) == 0) { - *prefix = s[0]; - return i; - } - } - return -1; -} - -static int rewrite_local(const struct Token *in, int local_id, struct Token *out) -{ - const char *src = text_buf + in->start; - char tmp[256]; - int prefix_len = 0; - int name_start = 0; - int n; - int text_start; - - if (in->kind != TOK_WORD) { - *out = *in; - return 1; - } - if (in->len >= 2 && (src[0] == ':' || src[0] == '&') && src[1] == '@') { - prefix_len = 1; - name_start = 2; - } else if (in->len >= 1 && src[0] == '@') { - prefix_len = 0; - name_start = 1; - } else { - *out = *in; - return 1; - } - - n = snprintf(tmp, sizeof(tmp), "%.*s__mlocal_%d_%.*s", - prefix_len, src, local_id, in->len - name_start, src + name_start); - if (n < 0 || n >= (int)sizeof(tmp)) { - snprintf(error_buf, sizeof(error_buf), "local label too long at line %d", in->line); - return 0; - } - text_start = append_text_len(tmp, n); - if (text_start < 0) { - return 0; - } - out->kind = TOK_WORD; - out->start = text_start; - out->len = n; - out->line = in->line; - return 1; -} - static int push_pool_token(struct Token tok) { if (pool_used >= MAX_EXPAND) { @@ -452,7 +386,6 @@ static void pop_stream(void) static int define_macro(struct Stream *s) { struct Macro *m; - int i; int line_start; if (macro_count >= MAX_MACROS) { @@ -470,14 +403,6 @@ static int define_macro(struct Stream *s) } m->name_start = s->toks[s->pos].start; m->name_len = s->toks[s->pos].len; - for (i = 0; i < macro_count; i++) { - if (macros[i].name_len == m->name_len && - memcmp(text_buf + macros[i].name_start, - text_buf + m->name_start, (size_t)m->name_len) == 0) { - snprintf(error_buf, sizeof(error_buf), "duplicate macro at line %d", m->line); - return 0; - } - } s->pos++; if (s->pos >= s->count || s->toks[s->pos].kind != TOK_LPAREN) { @@ -607,7 +532,7 @@ static int append_arg(struct Stream *s, int start, int end, int single) return 0; } if (single && end - start != 1) { - snprintf(error_buf, sizeof(error_buf), "pasted or prefixed arg must be one token"); + snprintf(error_buf, sizeof(error_buf), "pasted arg must be one token"); return 0; } for (i = start; i < end; i++) { @@ -618,34 +543,6 @@ static int append_arg(struct Stream *s, int start, int end, int single) return 1; } -static int append_prefixed_arg(struct Stream *s, int start, int end, char prefix) -{ - char tmp[512]; - int n; - int text_start; - struct Token tok; - - if (end - start != 1) { - snprintf(error_buf, sizeof(error_buf), "prefixed arg must be one token"); - return 0; - } - n = snprintf(tmp, sizeof(tmp), "%c%.*s", - prefix, s->toks[start].len, text_buf + s->toks[start].start); - if (n < 0 || n >= (int)sizeof(tmp)) { - snprintf(error_buf, sizeof(error_buf), "prefixed arg too long"); - return 0; - } - text_start = append_text_len(tmp, n); - if (text_start < 0) { - return 0; - } - tok.kind = TOK_WORD; - tok.start = text_start; - tok.len = n; - tok.line = s->toks[start].line; - return push_pool_token(tok); -} - static int paste_range(int start, int *count) { int in = start; @@ -708,7 +605,6 @@ static int expand_macro_at(struct Stream *s, int call_pos, int macro_idx, int end_pos; int mark; int count; - int local_id; int i; if (call_pos + 1 >= s->count || s->toks[call_pos + 1].kind != TOK_LPAREN) { @@ -727,13 +623,10 @@ static int expand_macro_at(struct Stream *s, int call_pos, int macro_idx, } mark = pool_used; - local_id = local_id_next++; for (i = 0; i < m->body_count; i++) { int param_idx = find_param(m, &m->body[i]); int pasted = 0; - char prefix = 0; - struct Token tok; if (param_idx >= 0) { pasted = (i > 0 && m->body[i - 1].kind == TOK_PASTE) || @@ -745,28 +638,7 @@ static int expand_macro_at(struct Stream *s, int call_pos, int macro_idx, continue; } - param_idx = find_prefixed_param(m, &m->body[i], &prefix); - if (param_idx >= 0) { - if (!append_prefixed_arg(s, arg_starts[param_idx], arg_ends[param_idx], prefix)) { - pool_used = mark; - return 0; - } - continue; - } - - if (m->body[i].kind == TOK_PASTE) { - if (!push_pool_token(m->body[i])) { - pool_used = mark; - return 0; - } - continue; - } - - if (!rewrite_local(&m->body[i], local_id, &tok)) { - pool_used = mark; - return 0; - } - if (!push_pool_token(tok)) { + if (!push_pool_token(m->body[i])) { pool_used = mark; return 0; } diff --git a/tests/m1m/full-parity.M1M b/tests/m1m/full-parity.M1M @@ -1,25 +1,9 @@ -# Exercises actual macro expansion parity for src/m1m.M1. - -%macro LABEL(name) -:name -&name -%endm - -%macro PREFIXED(x) -:x -&x -%endm +# Exercises actual macro expansion parity for the P1-oriented m1m subset. %macro PASTE(a, b) a ## b %endm -%macro LOCAL_PAIR() -:@loop -&@loop -@tmp -%endm - %macro EMIT_WORD(x) x %endm @@ -32,11 +16,7 @@ x %select(flag, yes, no) %endm -%LABEL(entry) -%PREFIXED(target) %PASTE(HELLO, _WORLD) -%LOCAL_PAIR() -%LOCAL_PAIR() %CHAIN(recursed) %le32((+ 1 (<< 2 8))) %le64((| 0x1122 (<< 0x33 16))) diff --git a/tests/m1m/full-parity.expected b/tests/m1m/full-parity.expected @@ -4,25 +4,8 @@ - - - -:entry -&entry - -:target -&target - HELLO_WORLD -:__mlocal_4_loop -&__mlocal_4_loop -__mlocal_4_tmp - -:__mlocal_5_loop -&__mlocal_5_loop -__mlocal_5_tmp - recursed