commit d0c704cfef090603c28aae04be29a82233d07761
parent ce22557fc8cb164acd92288a244669cc4651ec03
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 23 Apr 2026 17:20:31 -0700
m1pp.M1: port strlen expression op
Diffstat:
| M | m1pp/m1pp.M1 | | | 92 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
1 file changed, 89 insertions(+), 3 deletions(-)
diff --git a/m1pp/m1pp.M1 b/m1pp/m1pp.M1
@@ -105,7 +105,8 @@ DEFINE EXPR_LT 0D00000000000000
DEFINE EXPR_LE 0E00000000000000
DEFINE EXPR_GT 0F00000000000000
DEFINE EXPR_GE 1000000000000000
-DEFINE EXPR_INVALID 1100000000000000
+DEFINE EXPR_STRLEN 1100000000000000
+DEFINE EXPR_INVALID 1200000000000000
## --- Runtime shell: argv, read input, call pipeline, write output, exit ------
@@ -2547,9 +2548,9 @@ DEFINE EXPR_INVALID 1100000000000000
:pit_done
ret
-## expr_op_code(a0=tok) -> a0 = EXPR_ADD..EXPR_GE, or EXPR_INVALID.
+## expr_op_code(a0=tok) -> a0 = EXPR_ADD..EXPR_STRLEN, or EXPR_INVALID.
## Accepts operator tokens: + - * / % << >> & | ^ ~ = !=
-## < <= > >=. Non-WORD tok or unknown operator -> EXPR_INVALID.
+## < <= > >= strlen. Non-WORD tok or unknown operator -> EXPR_INVALID.
##
## tok_eq_const is a leaf but clobbers a0..a3,t0..t2; spill tok to eoc_tok
## once, reload before each compare. Needs an enter_0 frame because it
@@ -2734,6 +2735,16 @@ DEFINE EXPR_INVALID 1100000000000000
la_br &eoc_gt
bnez_a0
+ # "strlen" -> EXPR_STRLEN
+ la_a0 &eoc_tok
+ ld_a0,a0,0
+ la_a1 &op_strlen
+ li_a2 %6 %0
+ la_br &tok_eq_const
+ call
+ la_br &eoc_strlen
+ bnez_a0
+
:eoc_invalid
li_a0 EXPR_INVALID
leave
@@ -2806,6 +2817,10 @@ DEFINE EXPR_INVALID 1100000000000000
li_a0 EXPR_GE
leave
ret
+:eoc_strlen
+ li_a0 EXPR_STRLEN
+ leave
+ ret
## apply_expr_op(a0=op_code, a1=args_ptr, a2=argc) -> a0 = i64 result
## Reduce args[0..argc) per op:
@@ -3585,6 +3600,11 @@ DEFINE EXPR_INVALID 1100000000000000
li_t0 EXPR_INVALID
la_br &err_bad_macro_header
beq_a0,t0
+ # if (op == EXPR_STRLEN) handle inline — strlen's argument is a
+ # TOK_STRING atom, not a recursive expression. Yield text.len - 2.
+ li_t0 EXPR_STRLEN
+ la_br &eer_strlen
+ beq_a0,t0
# frame stack overflow check: if (expr_frame_top >= 16) fatal
# (the global expr_frames[] array has 16 slots, shared across recursive
# eval_expr_range calls)
@@ -3653,6 +3673,71 @@ DEFINE EXPR_INVALID 1100000000000000
la_br &eer_loop
b
+:eer_strlen
+ # (strlen "literal") — degenerate unary op whose argument is a
+ # TOK_STRING atom, not a recursive expression.
+ # pos++ past the "strlen" operator word.
+ ld_t0,sp,16
+ addi_t0,t0,24
+ st_t0,sp,16
+ # skip_expr_newlines(pos, end)
+ ld_a0,sp,16
+ ld_a1,sp,24
+ la_br &skip_expr_newlines
+ call
+ st_a0,sp,16
+ # if (pos >= end) fatal
+ ld_t0,sp,16
+ ld_t1,sp,24
+ la_br &err_bad_macro_header
+ beq_t0,t1
+ # if (pos->kind != TOK_STRING) fatal
+ ld_t2,t0,0
+ li_a3 TOK_STRING
+ la_br &err_bad_macro_header
+ bne_t2,a3
+ # if (pos->text.len < 2) fatal
+ ld_a1,t0,16
+ li_a2 %2 %0
+ la_br &err_bad_macro_header
+ blt_a1,a2
+ # if (pos->text.ptr[0] != '"') fatal — rejects single-quoted '..' hex
+ ld_a2,t0,8
+ lb_a3,a2,0
+ li_a0 %34 %0
+ la_br &err_bad_macro_header
+ bne_a3,a0
+ # value = pos->text.len - 2
+ addi_a1,a1,neg2
+ st_a1,sp,32
+ # pos++
+ addi_t0,t0,24
+ st_t0,sp,16
+ # skip_expr_newlines(pos, end)
+ ld_a0,sp,16
+ ld_a1,sp,24
+ la_br &skip_expr_newlines
+ call
+ st_a0,sp,16
+ # if (pos >= end) fatal
+ ld_t0,sp,16
+ ld_t1,sp,24
+ la_br &err_bad_macro_header
+ beq_t0,t1
+ # if (pos->kind != TOK_RPAREN) fatal
+ ld_t2,t0,0
+ li_a3 TOK_RPAREN
+ la_br &err_bad_macro_header
+ bne_t2,a3
+ # pos++
+ addi_t0,t0,24
+ st_t0,sp,16
+ # have_value = 1
+ li_t0 %1 %0
+ st_t0,sp,48
+ la_br &eer_loop
+ b
+
:eer_loop_done
# frame_top must equal entry_frame_top
la_a0 &expr_frame_top
@@ -4253,6 +4338,7 @@ DEFINE EXPR_INVALID 1100000000000000
:op_le "<="
:op_gt ">"
:op_ge ">="
+:op_strlen "strlen"
## Nibble-to-hex lookup table for emit_hex_value.
:hex_chars "0123456789ABCDEF"