boot2

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

commit b97e3d36e52f0da1c9260fb3f5c56dcc35e6ee4f
parent 1721f75a69bfd518d1f4c1c7327118b7353b1393
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 22 Apr 2026 09:16:36 -0700

lisp.M1: move 14 primitives to Scheme prelude for reviewability

Promoted all fixed-arity helpers that can be derived from the
remaining primitives:

  arithmetic:  <=, >=, zero?, negative?, positive?, abs
  list:        list?, length, reverse, assoc, member
  vector:      vector->list, list->vector
  structural:  equal? (+ removes equal_helper and eq_* sub-labels)

Also adds Scheme-side nice-to-haves that were never P1 primitives but
are commonly expected: not, caar/cadr/cdar/cddr/caddr, list-ref,
for-each.

Variadic primitives (list, append, min, max) stay in P1 because
env_extend doesn't yet support (lambda args body) or dotted-tail
params; revisit after that lands. Remaining primitive code ids are
renumbered contiguously 0..45.

lisp.M1 shrinks ~560 lines; src/prelude.scm grows ~150. Tests
28/28 on aarch64 and amd64 (riscv64 in progress at commit time).

Diffstat:
Msrc/lisp.M1 | 828+++++++++++++------------------------------------------------------------------
Msrc/prelude.scm | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 289 insertions(+), 698 deletions(-)

diff --git a/src/lisp.M1 b/src/lisp.M1 @@ -3865,159 +3865,117 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' li_br &prim_gt beq_r3,r0 li_r0 %8 - li_br &prim_le - beq_r3,r0 - li_r0 %9 - li_br &prim_ge - beq_r3,r0 - li_r0 %10 - li_br &prim_zerop - beq_r3,r0 - li_r0 %11 - li_br &prim_negativep - beq_r3,r0 - li_r0 %12 - li_br &prim_positivep - beq_r3,r0 - li_r0 %13 - li_br &prim_abs - beq_r3,r0 - li_r0 %14 li_br &prim_min beq_r3,r0 - li_r0 %15 + li_r0 %9 li_br &prim_max beq_r3,r0 - li_r0 %16 + li_r0 %10 li_br &prim_bitand beq_r3,r0 - li_r0 %17 + li_r0 %11 li_br &prim_bitor beq_r3,r0 - li_r0 %18 + li_r0 %12 li_br &prim_bitxor beq_r3,r0 - li_r0 %19 + li_r0 %13 li_br &prim_bitnot beq_r3,r0 - li_r0 %20 + li_r0 %14 li_br &prim_ashift beq_r3,r0 - li_r0 %21 + li_r0 %15 li_br &prim_numberp beq_r3,r0 - li_r0 %22 + li_r0 %16 li_br &prim_symbolp beq_r3,r0 - li_r0 %23 + li_r0 %17 li_br &prim_stringp beq_r3,r0 - li_r0 %24 + li_r0 %18 li_br &prim_vectorp beq_r3,r0 - li_r0 %25 + li_r0 %19 li_br &prim_procp beq_r3,r0 - li_r0 %26 + li_r0 %20 li_br &prim_eqp beq_r3,r0 - li_r0 %27 + li_r0 %21 li_br &prim_cons beq_r3,r0 - li_r0 %28 + li_r0 %22 li_br &prim_car beq_r3,r0 - li_r0 %29 + li_r0 %23 li_br &prim_cdr beq_r3,r0 - li_r0 %30 + li_r0 %24 li_br &prim_pairp beq_r3,r0 - li_r0 %31 + li_r0 %25 li_br &prim_nullp beq_r3,r0 - li_r0 %32 + li_r0 %26 li_br &prim_list beq_r3,r0 - li_r0 %33 - li_br &prim_length - beq_r3,r0 - li_r0 %34 - li_br &prim_listp - beq_r3,r0 - li_r0 %35 + li_r0 %27 li_br &prim_append beq_r3,r0 - li_r0 %36 - li_br &prim_reverse - beq_r3,r0 - li_r0 %37 - li_br &prim_assoc - beq_r3,r0 - li_r0 %38 - li_br &prim_member - beq_r3,r0 - li_r0 %39 + li_r0 %28 li_br &prim_string_length beq_r3,r0 - li_r0 %40 + li_r0 %29 li_br &prim_string_ref beq_r3,r0 - li_r0 %41 + li_r0 %30 li_br &prim_substring beq_r3,r0 - li_r0 %42 + li_r0 %31 li_br &prim_string_append beq_r3,r0 - li_r0 %43 + li_r0 %32 li_br &prim_string_to_symbol beq_r3,r0 - li_r0 %44 + li_r0 %33 li_br &prim_symbol_to_string beq_r3,r0 - li_r0 %45 + li_r0 %34 li_br &prim_make_vector beq_r3,r0 - li_r0 %46 + li_r0 %35 li_br &prim_vector_ref beq_r3,r0 - li_r0 %47 + li_r0 %36 li_br &prim_vector_set beq_r3,r0 - li_r0 %48 + li_r0 %37 li_br &prim_vector_length beq_r3,r0 - li_r0 %49 - li_br &prim_vector_to_list - beq_r3,r0 - li_r0 %50 - li_br &prim_list_to_vector - beq_r3,r0 - li_r0 %51 + li_r0 %38 li_br &prim_display beq_r3,r0 - li_r0 %52 + li_r0 %39 li_br &prim_write beq_r3,r0 - li_r0 %53 + li_r0 %40 li_br &prim_newline beq_r3,r0 - li_r0 %54 + li_r0 %41 li_br &prim_format beq_r3,r0 - li_r0 %55 + li_r0 %42 li_br &prim_error beq_r3,r0 - li_r0 %56 + li_r0 %43 li_br &prim_read_file beq_r3,r0 - li_r0 %57 + li_r0 %44 li_br &prim_write_file beq_r3,r0 - li_r0 %58 - li_br &prim_equal - beq_r3,r0 - li_r0 %59 + li_r0 %45 li_br &prim_apply beq_r3,r0 @@ -4172,74 +4130,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' ret -## (<= x y) ≡ !(y < x). Invert the BLT result. -:prim_le - ld_r3,r2,0 - ld_r0,r2,8 - li_br &prim_le_false - blt_r0,r3 - li_r0 TRUE ## #t - ret -:prim_le_false - li_r0 %23 - ret - - -## (>= x y) ≡ !(x < y). -:prim_ge - ld_r3,r2,0 - ld_r0,r2,8 - li_br &prim_ge_false - blt_r3,r0 - li_r0 %15 - ret -:prim_ge_false - li_r0 %23 - ret - - -## (zero? x). Tagged fixnum 0 = 0x01. -:prim_zerop - ld_r3,r2,0 - li_r0 %1 - li_br &prim_true - beq_r3,r0 - li_r0 %23 - ret - - -## (negative? x) — tagged-compare against 0x01. -:prim_negativep - ld_r3,r2,0 - li_r0 %1 - li_br &prim_true - blt_r3,r0 - li_r0 %23 - ret - - -## (positive? x). -:prim_positivep - ld_r3,r2,0 - li_r0 %1 - li_br &prim_true - blt_r0,r3 - li_r0 %23 - ret - - -## (abs x) — branch-on-sign; negate by 0 - v. -:prim_abs - ld_r3,r2,0 - sari_r3,r3,3 ## decode - li_r0 %0 - li_br &prim_abs_done - blt_r0,r3 ## 0 < v → already positive - sub_r3,r0,r3 ## v = -v -:prim_abs_done - shli_r0,r3,3 - ori_r0,r0,1 - ret +## (<=, >=, zero?, negative?, positive?, abs) live in the Scheme +## prelude now — see src/prelude.scm. ## (min ...) — variadic; first arg is seed, each later arg replaces @@ -4554,47 +4446,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' ret -## (length lst) — count pairs until nil. No proper-list check; an -## improper or non-list tail will dereference a bogus ptr (matching -## the seed's "no validation on bad input" stance). -:prim_length - ld_r1,r2,0 - li_r3 %0 -:prim_length_loop - li_r0 %7 - li_br &prim_length_done - beq_r1,r0 - addi_r0,r1,neg2 - ld_r1,r0,8 - addi_r3,r3,1 - li_br &prim_length_loop - b -:prim_length_done - shli_r0,r3,3 - ori_r0,r0,1 - ret - - -## (list? x) — walks to nil. Returns #t at nil, #f at any non-pair -## node. No cycle check (per LISP.md §equal? policy). -:prim_listp - ld_r1,r2,0 -:prim_listp_loop - li_r0 %7 - li_br &prim_true - beq_r1,r0 - mov_r0,r1 - andi_r0,r0,7 - li_r3 %2 - li_br &prim_listp_false - bne_r0,r3 - addi_r0,r1,neg2 - ld_r1,r0,8 - li_br &prim_listp_loop - b -:prim_listp_false - li_r0 %23 - ret +## (length, list?) live in the Scheme prelude now — see +## src/prelude.scm. ## append_one(r1=xs, r2=ys) -> r0 = xs ++ ys. Copies xs's spine; ys @@ -4663,83 +4516,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' ret -## (reverse lst) — single-pass cons-reverse onto nil accumulator. -:prim_reverse - prologue_n2 - ld_r0,r2,0 - mov_r3,sp - st_r0,sp,24 ## slot1 = current - li_r0 %7 - st_r0,sp,32 ## slot2 = accumulator (nil) -:prim_reverse_loop - mov_r3,sp - ld_r0,sp,24 - li_r1 %7 - li_br &prim_reverse_done - beq_r0,r1 - addi_r0,r0,neg2 - ld_r1,r0,0 ## r1 = car - ld_r2,r0,8 ## r2 = cdr (next current) - st_r2,sp,24 ## slot1 = next - ld_r2,sp,32 ## r2 = accumulator - li_br &cons - call - st_r0,sp,32 - li_br &prim_reverse_loop - b -:prim_reverse_done - ld_r0,sp,32 - epilogue_n2 - ret - - -## (assoc key alist) — eq?-keyed alist lookup. Returns the matched -## (key . val) cell or #f. -:prim_assoc - ld_r3,r2,0 ## r3 = key - ld_r0,r2,8 ## r0 = alist cursor -:prim_assoc_loop - li_r1 %7 - li_br &prim_assoc_miss - beq_r0,r1 - addi_r1,r0,neg2 ## raw alist cell - ld_r2,r1,0 ## r2 = inner pair (tagged) - addi_r2,r2,neg2 - ld_r2,r2,0 ## r2 = candidate key - li_br &prim_assoc_hit - beq_r2,r3 - ld_r0,r1,8 ## advance alist - li_br &prim_assoc_loop - b -:prim_assoc_hit - ld_r0,r1,0 ## return inner pair - ret -:prim_assoc_miss - li_r0 %23 - ret - - -## (member x lst) — eq?-keyed; returns the first sublist whose car -## equals x, else #f. -:prim_member - ld_r3,r2,0 ## r3 = needle - ld_r0,r2,8 ## r0 = list cursor -:prim_member_loop - li_r1 %7 - li_br &prim_member_miss - beq_r0,r1 - addi_r1,r0,neg2 - ld_r2,r1,0 ## r2 = car - li_br &prim_member_hit - beq_r2,r3 - ld_r0,r1,8 ## next sublist (tagged) - li_br &prim_member_loop - b -:prim_member_hit - ret ## r0 already = current sublist -:prim_member_miss - li_r0 %23 - ret +## (reverse, assoc, member) live in the Scheme prelude now — see +## src/prelude.scm. ## ---- (string-length s) — fixnum length out of header --------------- @@ -4957,100 +4735,8 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' ret -## ---- (vector->list v) — cons-snake from tail to head --------------- -## Walks slots end → base, pushing each onto an accumulator. Result -## order matches slot order. -:prim_vector_to_list - prologue_n4 - st_r6,sp,24 - st_r7,sp,32 - - ld_r0,r2,0 ## tagged vec - addi_r0,r0,neg3 ## raw - ld_r3,r0,0 - shli_r3,r3,32 - shri_r3,r3,32 ## len - addi_r0,r0,8 ## payload base - shli_r1,r3,3 - mov_r2,r0 ## r2 = payload base - add_r6,r1,r2 ## r6 = end cursor = base + len*8 - - mov_r3,sp - st_r0,r3,40 ## slot3 = base ptr - - li_r7 NIL ## acc -:pvtl_loop - mov_r3,sp - ld_r0,r3,40 - li_br &pvtl_done - beq_r6,r0 - - addi_r6,r6,neg8 - ld_r1,r6,0 ## element - mov_r2,r7 ## acc - li_br &cons - call - mov_r7,r0 - li_br &pvtl_loop - b -:pvtl_done - mov_r0,r7 - ld_r6,sp,24 - ld_r7,sp,32 - epilogue_n4 - ret - - -## ---- (list->vector lst) — count then fill --------------------------- -:prim_list_to_vector - prologue_n4 - st_r6,sp,24 - st_r7,sp,32 - - ld_r0,r2,0 ## tagged list - mov_r3,sp - st_r0,r3,40 ## slot3 = list head - - li_r6 %0 ## r6 = count - mov_r7,r0 ## r7 = cursor -:pltv_lp1 - li_r0 NIL - li_br &pltv_done1 - beq_r7,r0 - addi_r0,r7,neg2 ## raw pair - ld_r7,r0,8 ## next - addi_r6,r6,1 - li_br &pltv_lp1 - b -:pltv_done1 - mov_r1,r6 ## raw len - li_r2 NIL - li_br &make_vector - call ## r0 = tagged vector - mov_r3,sp - st_r0,r3,48 ## slot4 = result - - addi_r6,r0,neg3 - addi_r6,r6,8 ## r6 = payload cursor - ld_r7,r3,40 ## r7 = list cursor -:pltv_lp2 - li_r0 NIL - li_br &pltv_done2 - beq_r7,r0 - addi_r0,r7,neg2 - ld_r1,r0,0 ## car - ld_r7,r0,8 ## next - st_r1,r6,0 - addi_r6,r6,8 - li_br &pltv_lp2 - b -:pltv_done2 - mov_r3,sp - ld_r0,r3,48 - ld_r6,sp,24 - ld_r7,sp,32 - epilogue_n4 - ret +## (vector->list, list->vector) live in the Scheme prelude now — +## see src/prelude.scm. ## ---- (display x) — runtime printer (unspec result) ------------------ @@ -5310,159 +4996,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' ret -## ---- equal_helper(r1=a, r2=b) -> r0 = 0|1 -------------------------- -## Structural equality: eq?, then bytewise for strings, elementwise for -## vectors, recursive for pairs. No cycle detection. -## Always saves r6/r7 (slot1/slot2); slot3/slot4 store tagged a/b for -## paths that need them across recursive calls. -:equal_helper - prologue_n4 - st_r6,sp,24 - st_r7,sp,32 - - li_br &eq_true - beq_r1,r2 - - mov_r3,r1 - andi_r3,r3,7 - mov_r0,r2 - andi_r0,r0,7 - li_br &eq_false - bne_r3,r0 - - li_r0 TAG_PAIR - li_br &eq_pair - beq_r3,r0 - li_r0 TAG_STRING - li_br &eq_string - beq_r3,r0 - li_r0 TAG_VECTOR - li_br &eq_vector - beq_r3,r0 - -:eq_false - li_r0 %0 - ld_r6,sp,24 - ld_r7,sp,32 - epilogue_n4 - ret - -:eq_true - li_r0 %1 - ld_r6,sp,24 - ld_r7,sp,32 - epilogue_n4 - ret - -:eq_pair - st_r1,sp,40 ## slot3 = tagged a - st_r2,sp,48 ## slot4 = tagged b - - addi_r0,r1,neg2 - ld_r1,r0,0 ## a.car - addi_r0,r2,neg2 - ld_r2,r0,0 ## b.car - - li_br &equal_helper - call - li_br &eq_false - beqz_r0 - - mov_r3,sp - ld_r0,r3,40 - addi_r0,r0,neg2 - ld_r1,r0,8 ## a.cdr - ld_r0,r3,48 - addi_r0,r0,neg2 - ld_r2,r0,8 ## b.cdr - li_br &equal_helper - call - - ld_r6,sp,24 - ld_r7,sp,32 - epilogue_n4 - ret - -:eq_string - addi_r1,r1,neg4 - addi_r2,r2,neg4 - ld_r3,r1,0 - shli_r3,r3,32 - shri_r3,r3,32 ## len_a - ld_r0,r2,0 - shli_r0,r0,16 - shri_r0,r0,16 ## len_b - li_br &eq_false - bne_r3,r0 - - addi_r6,r1,8 ## a payload cursor - addi_r7,r2,8 ## b payload cursor -:eq_string_loop - li_br &eq_true - beqz_r3 - lb_r0,r6,0 - lb_r1,r7,0 - li_br &eq_false - bne_r0,r1 - addi_r6,r6,1 - addi_r7,r7,1 - addi_r3,r3,neg1 - li_br &eq_string_loop - b - -:eq_vector - addi_r1,r1,neg3 - addi_r2,r2,neg3 - ld_r3,r1,0 - shli_r3,r3,32 - shri_r3,r3,32 ## len_a - ld_r0,r2,0 - shli_r0,r0,16 - shri_r0,r0,16 ## len_b - li_br &eq_false - bne_r3,r0 - - addi_r6,r1,8 ## a payload cursor - addi_r7,r2,8 ## b payload cursor -:eq_vector_loop - li_br &eq_true - beqz_r3 - - ld_r1,r6,0 - ld_r2,r7,0 - st_r3,sp,40 ## save remaining count - - li_br &equal_helper - call - li_br &eq_false - beqz_r0 - - mov_r3,sp - ld_r3,r3,40 - addi_r6,r6,8 - addi_r7,r7,8 - addi_r3,r3,neg1 - li_br &eq_vector_loop - b - - -## ---- (equal? a b) — wraps equal_helper ---------------------------- -:prim_equal - prologue - ld_r1,r2,0 - ld_r2,r2,8 - li_br &equal_helper - call - - li_br &peq_true - bnez_r0 - li_r0 FALSE - epilogue - ret -:peq_true - li_r0 TRUE - epilogue - ret +## (equal?) lives in the Scheme prelude now — see src/prelude.scm. ## ---- (apply proc arg ... last-list) --------------------------------- @@ -6634,12 +6168,6 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' :str_prim_numeq "=" :str_prim_lt "<" :str_prim_gt ">" -:str_prim_le "<=" -:str_prim_ge ">=" -:str_prim_zerop "zero?" -:str_prim_negativep "negative?" -:str_prim_positivep "positive?" -:str_prim_abs "abs" :str_prim_min "min" :str_prim_max "max" :str_prim_bitand "bit-and" @@ -6661,12 +6189,7 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' :str_prim_pairp "pair?" :str_prim_nullp "null?" :str_prim_list "list" -:str_prim_length "length" -:str_prim_listp "list?" :str_prim_append "append" -:str_prim_reverse "reverse" -:str_prim_assoc "assoc" -:str_prim_member "member" ## Step-10e string / vector / I/O / equal? / apply names. :str_prim_string_length "string-length" @@ -6679,8 +6202,6 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' :str_prim_vector_ref "vector-ref" :str_prim_vector_set "vector-set!" :str_prim_vector_length "vector-length" -:str_prim_vector_to_list "vector->list" -:str_prim_list_to_vector "list->vector" :str_prim_display "display" :str_prim_write "write" :str_prim_newline "newline" @@ -6688,7 +6209,6 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' :str_prim_error "error" :str_prim_read_file "read-file" :str_prim_write_file "write-file" -:str_prim_equal "equal?" :str_prim_apply "apply" @@ -6696,364 +6216,284 @@ DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' ## type(8) + arity(8). End-sentinel = zero name pointer. _start iterates ## with ADDI +40. :prim_table -## (+ ...) variadic +## Primitives promoted to the Scheme prelude (src/prelude.scm) and no +## longer registered here: <=, >=, zero?, negative?, positive?, abs, +## length, list?, reverse, assoc, member, vector->list, list->vector, +## equal?. Code ids are contiguous 0..45 after that removal. +## (+ ...) variadic — code 0 &str_prim_plus %0 %1 %0 %0 %0 %6 %0 %0 %0 -## (- x ...) variadic (fixes unary-negate branch in body) +## (- x ...) variadic — code 1 (unary-negate branch in body) &str_prim_minus %0 %1 %0 %1 %0 %6 %0 %0 %0 -## (* ...) variadic +## (* ...) variadic — code 2 &str_prim_mul %0 %1 %0 %2 %0 %6 %0 %0 %0 -## (/ x y) fixed 2 +## (/ x y) fixed 2 — code 3 &str_prim_div %0 %1 %0 %3 %0 %5 %0 %2 %0 -## (% x y) fixed 2 +## (% x y) fixed 2 — code 4 &str_prim_mod %0 %1 %0 %4 %0 %5 %0 %2 %0 -## (= x y) fixed 2 +## (= x y) fixed 2 — code 5 &str_prim_numeq %0 %1 %0 %5 %0 %5 %0 %2 %0 -## (< x y) fixed 2 +## (< x y) fixed 2 — code 6 &str_prim_lt %0 %1 %0 %6 %0 %5 %0 %2 %0 -## (> x y) fixed 2 +## (> x y) fixed 2 — code 7 &str_prim_gt %0 %1 %0 %7 %0 %5 %0 %2 %0 -## (<= x y) fixed 2 -&str_prim_le %0 -%2 %0 -%8 %0 -%5 %0 -%2 %0 -## (>= x y) fixed 2 -&str_prim_ge %0 -%2 %0 -%9 %0 -%5 %0 -%2 %0 -## (zero? x) fixed 1 -&str_prim_zerop %0 -%5 %0 -%10 %0 -%5 %0 -%1 %0 -## (negative? x) fixed 1 -&str_prim_negativep %0 -%9 %0 -%11 %0 -%5 %0 -%1 %0 -## (positive? x) fixed 1 -&str_prim_positivep %0 -%9 %0 -%12 %0 -%5 %0 -%1 %0 -## (abs x) fixed 1 -&str_prim_abs %0 -%3 %0 -%13 %0 -%5 %0 -%1 %0 -## (min ...) variadic — arity 0 ignored, but ≥1 enforced by body load +## (min ...) variadic — code 8 (≥1 enforced by body load) &str_prim_min %0 %3 %0 -%14 %0 +%8 %0 %6 %0 %0 %0 -## (max ...) variadic +## (max ...) variadic — code 9 &str_prim_max %0 %3 %0 -%15 %0 +%9 %0 %6 %0 %0 %0 -## (bit-and ...) variadic +## (bit-and ...) variadic — code 10 &str_prim_bitand %0 %7 %0 -%16 %0 +%10 %0 %6 %0 %0 %0 -## (bit-or ...) variadic +## (bit-or ...) variadic — code 11 &str_prim_bitor %0 %6 %0 -%17 %0 +%11 %0 %6 %0 %0 %0 -## (bit-xor ...) variadic +## (bit-xor ...) variadic — code 12 &str_prim_bitxor %0 %7 %0 -%18 %0 +%12 %0 %6 %0 %0 %0 -## (bit-not x) fixed 1 +## (bit-not x) fixed 1 — code 13 &str_prim_bitnot %0 %7 %0 -%19 %0 +%13 %0 %5 %0 %1 %0 -## (arithmetic-shift n k) fixed 2 +## (arithmetic-shift n k) fixed 2 — code 14 &str_prim_ashift %0 %16 %0 -%20 %0 +%14 %0 %5 %0 %2 %0 -## (number? x) fixed 1 +## (number? x) fixed 1 — code 15 &str_prim_numberp %0 %7 %0 -%21 %0 +%15 %0 %5 %0 %1 %0 -## (symbol? x) fixed 1 +## (symbol? x) fixed 1 — code 16 &str_prim_symbolp %0 %7 %0 -%22 %0 +%16 %0 %5 %0 %1 %0 -## (string? x) fixed 1 +## (string? x) fixed 1 — code 17 &str_prim_stringp %0 %7 %0 -%23 %0 +%17 %0 %5 %0 %1 %0 -## (vector? x) fixed 1 +## (vector? x) fixed 1 — code 18 &str_prim_vectorp %0 %7 %0 -%24 %0 +%18 %0 %5 %0 %1 %0 -## (procedure? x) fixed 1 +## (procedure? x) fixed 1 — code 19 &str_prim_procp %0 %10 %0 -%25 %0 +%19 %0 %5 %0 %1 %0 -## (eq? x y) fixed 2 +## (eq? x y) fixed 2 — code 20 &str_prim_eqp %0 %3 %0 -%26 %0 +%20 %0 %5 %0 %2 %0 -## (cons a d) fixed 2 +## (cons a d) fixed 2 — code 21 &str_prim_cons %0 %4 %0 -%27 %0 +%21 %0 %5 %0 %2 %0 -## (car p) fixed 1 +## (car p) fixed 1 — code 22 &str_prim_car %0 %3 %0 -%28 %0 +%22 %0 %5 %0 %1 %0 -## (cdr p) fixed 1 +## (cdr p) fixed 1 — code 23 &str_prim_cdr %0 %3 %0 -%29 %0 +%23 %0 %5 %0 %1 %0 -## (pair? x) fixed 1 +## (pair? x) fixed 1 — code 24 &str_prim_pairp %0 %5 %0 -%30 %0 +%24 %0 %5 %0 %1 %0 -## (null? x) fixed 1 +## (null? x) fixed 1 — code 25 &str_prim_nullp %0 %5 %0 -%31 %0 +%25 %0 %5 %0 %1 %0 -## (list ...) variadic +## (list ...) variadic — code 26 &str_prim_list %0 %4 %0 -%32 %0 +%26 %0 %6 %0 %0 %0 -## (length lst) fixed 1 -&str_prim_length %0 -%6 %0 -%33 %0 -%5 %0 -%1 %0 -## (list? x) fixed 1 -&str_prim_listp %0 -%5 %0 -%34 %0 -%5 %0 -%1 %0 -## (append ...) variadic +## (append ...) variadic — code 27 &str_prim_append %0 %6 %0 -%35 %0 +%27 %0 %6 %0 %0 %0 -## (reverse lst) fixed 1 -&str_prim_reverse %0 -%7 %0 -%36 %0 -%5 %0 -%1 %0 -## (assoc key alist) fixed 2 -&str_prim_assoc %0 -%5 %0 -%37 %0 -%5 %0 -%2 %0 -## (member x lst) fixed 2 -&str_prim_member %0 -%6 %0 -%38 %0 -%5 %0 -%2 %0 -## (string-length s) fixed 1 +## (string-length s) fixed 1 — code 28 &str_prim_string_length %0 %13 %0 -%39 %0 +%28 %0 %5 %0 %1 %0 -## (string-ref s i) fixed 2 +## (string-ref s i) fixed 2 — code 29 &str_prim_string_ref %0 %10 %0 -%40 %0 +%29 %0 %5 %0 %2 %0 -## (substring s start end) fixed 3 +## (substring s start end) fixed 3 — code 30 &str_prim_substring %0 %9 %0 -%41 %0 +%30 %0 %5 %0 %3 %0 -## (string-append ...) variadic +## (string-append ...) variadic — code 31 &str_prim_string_append %0 %13 %0 -%42 %0 +%31 %0 %6 %0 %0 %0 -## (string->symbol s) fixed 1 +## (string->symbol s) fixed 1 — code 32 &str_prim_string_to_symbol %0 %14 %0 -%43 %0 +%32 %0 %5 %0 %1 %0 -## (symbol->string sym) fixed 1 +## (symbol->string sym) fixed 1 — code 33 &str_prim_symbol_to_string %0 %14 %0 -%44 %0 +%33 %0 %5 %0 %1 %0 -## (make-vector n init) fixed 2 +## (make-vector n init) fixed 2 — code 34 &str_prim_make_vector %0 %11 %0 -%45 %0 +%34 %0 %5 %0 %2 %0 -## (vector-ref v i) fixed 2 +## (vector-ref v i) fixed 2 — code 35 &str_prim_vector_ref %0 %10 %0 -%46 %0 +%35 %0 %5 %0 %2 %0 -## (vector-set! v i x) fixed 3 +## (vector-set! v i x) fixed 3 — code 36 &str_prim_vector_set %0 %11 %0 -%47 %0 +%36 %0 %5 %0 %3 %0 -## (vector-length v) fixed 1 +## (vector-length v) fixed 1 — code 37 &str_prim_vector_length %0 %13 %0 -%48 %0 -%5 %0 -%1 %0 -## (vector->list v) fixed 1 -&str_prim_vector_to_list %0 -%12 %0 -%49 %0 -%5 %0 -%1 %0 -## (list->vector lst) fixed 1 -&str_prim_list_to_vector %0 -%12 %0 -%50 %0 +%37 %0 %5 %0 %1 %0 -## (display x) fixed 1 +## (display x) fixed 1 — code 38 &str_prim_display %0 %7 %0 -%51 %0 +%38 %0 %5 %0 %1 %0 -## (write x) fixed 1 +## (write x) fixed 1 — code 39 &str_prim_write %0 %5 %0 -%52 %0 +%39 %0 %5 %0 %1 %0 -## (newline) fixed 0 +## (newline) fixed 0 — code 40 &str_prim_newline %0 %7 %0 -%53 %0 +%40 %0 %5 %0 %0 %0 -## (format fmt ...) variadic +## (format fmt ...) variadic — code 41 &str_prim_format %0 %6 %0 -%54 %0 +%41 %0 %6 %0 %0 %0 -## (error msg) fixed 1 +## (error msg) fixed 1 — code 42 &str_prim_error %0 %5 %0 -%55 %0 +%42 %0 %5 %0 %1 %0 -## (read-file path) fixed 1 +## (read-file path) fixed 1 — code 43 &str_prim_read_file %0 %9 %0 -%56 %0 +%43 %0 %5 %0 %1 %0 -## (write-file path data) fixed 2 +## (write-file path data) fixed 2 — code 44 &str_prim_write_file %0 %10 %0 -%57 %0 -%5 %0 -%2 %0 -## (equal? a b) fixed 2 -&str_prim_equal %0 -%6 %0 -%58 %0 +%44 %0 %5 %0 %2 %0 -## (apply proc arg ... last-list) variadic +## (apply proc arg ... last-list) variadic — code 45 &str_prim_apply %0 %5 %0 -%59 %0 +%45 %0 %6 %0 %0 %0 ## End sentinel: zero name pointer. diff --git a/src/prelude.scm b/src/prelude.scm @@ -1,7 +1,83 @@ -;; Prelude: map / filter / fold. Concatenated ahead of every user script -;; by the Makefile (cat src/prelude.scm $user.scm > combined.scm) so the -;; interpreter keeps its single-argv contract and lisp.M1 stays free of -;; embedded Scheme source. +;; Prelude — helpers promoted out of lisp.M1 into Scheme for +;; reviewability. Prepended to every user script by the Makefile +;; (cat src/prelude.scm $user.scm > combined.scm) so these names +;; are in scope before user code runs. +;; +;; Only fixed-arity helpers live here — variadic lambdas aren't yet +;; supported by env_extend, so list/append/min/max stay as P1 primitives. + +;; --- Boolean / logical ---------------------------------------------- +(define not + (lambda (x) (if x #f #t))) + +;; --- Arithmetic helpers (derivable from <, =, -) -------------------- +(define <= + (lambda (x y) (if (< y x) #f #t))) + +(define >= + (lambda (x y) (if (< x y) #f #t))) + +(define zero? + (lambda (x) (= x 0))) + +(define negative? + (lambda (x) (< x 0))) + +(define positive? + (lambda (x) (> x 0))) + +(define abs + (lambda (x) (if (< x 0) (- 0 x) x))) + +;; --- Common c*r compositions --------------------------------------- +(define caar (lambda (x) (car (car x)))) +(define cadr (lambda (x) (car (cdr x)))) +(define cdar (lambda (x) (cdr (car x)))) +(define cddr (lambda (x) (cdr (cdr x)))) +(define caddr (lambda (x) (car (cdr (cdr x))))) + +;; --- List helpers (derivable from cons/car/cdr/null?/pair?/eq?) ----- +(define list? + (lambda (x) + (if (null? x) + #t + (if (pair? x) (list? (cdr x)) #f)))) + +(define length-helper + (lambda (xs acc) + (if (null? xs) acc (length-helper (cdr xs) (+ acc 1))))) + +(define length + (lambda (xs) (length-helper xs 0))) + +(define reverse-helper + (lambda (xs acc) + (if (null? xs) acc (reverse-helper (cdr xs) (cons (car xs) acc))))) + +(define reverse + (lambda (xs) (reverse-helper xs (quote ())))) + +(define list-ref + (lambda (xs n) + (if (= n 0) (car xs) (list-ref (cdr xs) (- n 1))))) + +(define assoc + (lambda (key alist) + (if (null? alist) + #f + (if (eq? (car (car alist)) key) + (car alist) + (assoc key (cdr alist)))))) + +(define member + (lambda (x xs) + (if (null? xs) + #f + (if (eq? (car xs) x) + xs + (member x (cdr xs)))))) + +;; --- map / filter / fold / for-each --------------------------------- (define map (lambda (f xs) (if (null? xs) @@ -21,3 +97,78 @@ (if (null? xs) acc (fold f (f acc (car xs)) (cdr xs))))) + +(define for-each + (lambda (f xs) + (if (null? xs) + (quote ()) + (begin + (f (car xs)) + (for-each f (cdr xs)))))) + +;; --- Vector <-> list (derivable from make-vector/vector-ref/set) ---- +(define vector->list-helper + (lambda (v i acc) + (if (< i 0) + acc + (vector->list-helper v (- i 1) (cons (vector-ref v i) acc))))) + +(define vector->list + (lambda (v) + (vector->list-helper v (- (vector-length v) 1) (quote ())))) + +(define list->vector-helper + (lambda (v xs i) + (if (null? xs) + v + (begin + (vector-set! v i (car xs)) + (list->vector-helper v (cdr xs) (+ i 1)))))) + +(define list->vector + (lambda (xs) + (list->vector-helper (make-vector (length xs) 0) xs 0))) + +;; --- Structural equality -------------------------------------------- +;; equal? — eq? fast-path, then recurse into pairs, string contents, +;; and vector elements. No cycle detection (matches the pre-move P1 +;; version's policy). +(define equal?-string + (lambda (a b i n) + (if (= i n) + #t + (if (= (string-ref a i) (string-ref b i)) + (equal?-string a b (+ i 1) n) + #f)))) + +(define equal?-vector + (lambda (a b i n) + (if (= i n) + #t + (if (equal? (vector-ref a i) (vector-ref b i)) + (equal?-vector a b (+ i 1) n) + #f)))) + +(define equal? + (lambda (a b) + (if (eq? a b) + #t + (if (pair? a) + (if (pair? b) + (if (equal? (car a) (car b)) + (equal? (cdr a) (cdr b)) + #f) + #f) + (if (string? a) + (if (string? b) + (if (= (string-length a) (string-length b)) + (equal?-string a b 0 (string-length a)) + #f) + #f) + (if (vector? a) + (if (vector? b) + (if (= (vector-length a) (vector-length b)) + (equal?-vector a b 0 (vector-length a)) + #f) + #f) + #f))))))