boot2

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

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

m1pp: add strlen expression op

Diffstat:
Mm1pp/m1pp.c | 28+++++++++++++++++++++++++++-
Mtests/m1pp/04-expr-ops.M1pp | 7+++++++
Mtests/m1pp/04-expr-ops.expected | 7+++++++
Atests/m1pp/_04-strlen-badarg.M1pp | 7+++++++
4 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/m1pp/m1pp.c b/m1pp/m1pp.c @@ -102,6 +102,7 @@ enum ExprOp { EXPR_LE, EXPR_GT, EXPR_GE, + EXPR_STRLEN, EXPR_INVALID }; @@ -804,6 +805,9 @@ static enum ExprOp expr_op_code(const struct Token *tok) if (token_text_eq(tok, ">=")) { return EXPR_GE; } + if (token_text_eq(tok, "strlen")) { + return EXPR_STRLEN; + } return EXPR_INVALID; } @@ -932,6 +936,7 @@ static int apply_expr_op(enum ExprOp op, const long long *args, int argc, long l } *out = (args[0] >= args[1]); return 1; + case EXPR_STRLEN: case EXPR_INVALID: break; } @@ -1021,13 +1026,34 @@ static int eval_expr_range(struct TokenSpan span, long long *out) if (op == EXPR_INVALID) { return fail("bad expression"); } + pos++; + if (op == EXPR_STRLEN) { + /* strlen is degenerate: argument is a TOK_STRING atom, + * not a recursive expression. Handle inline and yield + * the string's raw byte count (span.len - 2). */ + skip_expr_newlines(&pos, span.end); + if (pos >= span.end || pos->kind != TOK_STRING) { + return fail("bad expression"); + } + if (pos->text.len < 2 || pos->text.ptr[0] != '"') { + return fail("bad expression"); + } + value = (long long)(pos->text.len - 2); + pos++; + skip_expr_newlines(&pos, span.end); + if (pos >= span.end || pos->kind != TOK_RPAREN) { + return fail("bad expression"); + } + pos++; + have_value = 1; + continue; + } if (frame_top >= MAX_EXPR_FRAMES) { return fail("expression overflow"); } frames[frame_top].op = op; frames[frame_top].argc = 0; frame_top++; - pos++; continue; } diff --git a/tests/m1pp/04-expr-ops.M1pp b/tests/m1pp/04-expr-ops.M1pp @@ -33,4 +33,11 @@ $((>= 5 6)) # nested expressions $((+ (* 2 3) (- 7 4) (/ 12 3))) + +# strlen: raw byte count between the quotes (matches what M1's "..." emits +# before appending NUL). Composes with arithmetic like any other op. +%((strlen "hello")) +%((+ (strlen "hello") 1)) +!((strlen "x")) +%((strlen "")) END diff --git a/tests/m1pp/04-expr-ops.expected b/tests/m1pp/04-expr-ops.expected @@ -33,4 +33,11 @@ '0D00000000000000' + + + +'05000000' +'06000000' +'01' +'00000000' END diff --git a/tests/m1pp/_04-strlen-badarg.M1pp b/tests/m1pp/_04-strlen-badarg.M1pp @@ -0,0 +1,7 @@ +# Malformed: strlen requires a double-quoted TOK_STRING argument. +# Single-quoted '...' hex literals are meaningless for strlen and must +# be rejected. Expected behavior: non-zero exit from the expander. +# (Underscore-prefixed filename so test.sh skips this fixture.) + +%((strlen 'deadbeef')) +END