commit a0b1dde7214f0ea5574565c1c0fbcdd16fedef90
parent 608c2bdc5926f697bace3c4d9881226acd06f792
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 23 Apr 2026 08:07:10 -0700
m1m simplify spec
Diffstat:
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` / `¶m` 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`/`¶m`), 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`/`¶m` 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 / ¶m 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/¶m) 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