boot2

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

commit 48971a6c16a856b6b0fd6beafa72d2a170cab648
parent dca693c3073799b8d46cc9f6479839ea4c8d913d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 23 Apr 2026 17:04:31 -0700

m1pp: add local labels (:@name, &@name)

Diffstat:
Mm1pp/m1pp.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atests/m1pp/11-local-labels.M1pp | 37+++++++++++++++++++++++++++++++++++++
Atests/m1pp/11-local-labels.expected | 31+++++++++++++++++++++++++++++++
3 files changed, 136 insertions(+), 0 deletions(-)

diff --git a/m1pp/m1pp.c b/m1pp/m1pp.c @@ -160,6 +160,7 @@ static int pool_used; static int output_used; static int output_need_space; static int stream_top; +static int next_expansion_id; static struct Token *arg_starts[MAX_PARAMS]; static struct Token *arg_ends[MAX_PARAMS]; @@ -667,6 +668,64 @@ static int paste_pool_range(int mark) return 1; } +static int is_local_label_token(const struct Token *tok) +{ + if (tok->kind != TOK_WORD || tok->text.len < 3) { + return 0; + } + if (tok->text.ptr[0] != ':' && tok->text.ptr[0] != '&') { + return 0; + } + if (tok->text.ptr[1] != '@') { + return 0; + } + return 1; +} + +static int push_local_label_token(const struct Token *tok, int expansion_id) +{ + /* Rewrite ":@name" -> ":name__NN", "&@name" -> "&name__NN". + * Build the text directly in text_buf so the resulting span is stable. */ + char digits[16]; + int digit_count = 0; + int unsigned_id; + int start; + int total; + int i; + struct Token out; + + unsigned_id = expansion_id; + if (unsigned_id == 0) { + digits[digit_count++] = '0'; + } else { + while (unsigned_id > 0) { + digits[digit_count++] = (char)('0' + (unsigned_id % 10)); + unsigned_id /= 10; + } + } + + /* Reserve: sigil(1) + tail(len-2) + "__"(2) + digits + NUL. */ + total = 1 + (tok->text.len - 2) + 2 + digit_count; + if (text_used + total + 1 > MAX_TEXT) { + return fail("text overflow"); + } + start = text_used; + text_buf[text_used++] = tok->text.ptr[0]; + memcpy(text_buf + text_used, tok->text.ptr + 2, (size_t)(tok->text.len - 2)); + text_used += tok->text.len - 2; + text_buf[text_used++] = '_'; + text_buf[text_used++] = '_'; + for (i = digit_count - 1; i >= 0; i--) { + text_buf[text_used++] = digits[i]; + } + text_buf[text_used++] = '\0'; + + out.kind = TOK_WORD; + out.text.ptr = text_buf + start; + out.text.len = total; + return push_pool_token(out); +} + static int expand_macro_tokens(struct Token *call_tok, struct Token *limit, const struct Macro *m, struct Token **after_out, int *mark_out) @@ -674,6 +733,7 @@ static int expand_macro_tokens(struct Token *call_tok, struct Token *limit, struct Token *body_tok; struct Token *end_pos; int mark; + int expansion_id; if (call_tok + 1 >= limit || (call_tok + 1)->kind != TOK_LPAREN) { return fail("bad macro call"); @@ -686,6 +746,7 @@ static int expand_macro_tokens(struct Token *call_tok, struct Token *limit, } end_pos = call_end_pos; + expansion_id = ++next_expansion_id; mark = pool_used; for (body_tok = m->body_start; body_tok < m->body_end; body_tok++) { int param_idx = find_param(m, body_tok); @@ -703,6 +764,13 @@ static int expand_macro_tokens(struct Token *call_tok, struct Token *limit, } continue; } + if (is_local_label_token(body_tok)) { + if (!push_local_label_token(body_tok, expansion_id)) { + pool_used = mark; + return 0; + } + continue; + } if (!push_pool_token(*body_tok)) { pool_used = mark; return 0; diff --git a/tests/m1pp/11-local-labels.M1pp b/tests/m1pp/11-local-labels.M1pp @@ -0,0 +1,37 @@ +# Local labels (ยง1): `:@name` / `&@name` inside macro bodies rewrite to +# `:name__NN` / `&name__NN` where NN is a fresh monotonic id per expansion. +# Scoping: body-native only; param-substituted tokens pass through untouched. +# +# Scenarios: +# 1) a single macro using `:@end` called twice -> end__1, end__2 distinct +# 2) nested macros each using `:@done` -> outer/inner get separate ids +# 3) `&@label` address form rewrites the same way +# 4) a `:@name` literal passed as an argument is NOT rewritten + +%macro ONCE() + jne &@end + :@end +%endm + +%macro OUTER() + :@done + %INNER() + jmp &@done +%endm + +%macro INNER() + :@done + body +%endm + +%macro ARGPASS(lbl) + lbl +%endm + +%ONCE() +%ONCE() + +%OUTER() + +%ARGPASS(:@kept) +END diff --git a/tests/m1pp/11-local-labels.expected b/tests/m1pp/11-local-labels.expected @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + +jne &end__1 +:end__1 + +jne &end__2 +:end__2 + + +:done__3 +:done__4 +body + +jmp &done__3 + + +:@kept + +END