commit 63959a59c2526161431457084565f13ffa939916
parent b00065b8511ec5507c85584bdeddc5a02e1bb059
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 25 Apr 2026 14:35:55 -0700
scheme1: string<->sym, bytevector-append, type preds, number<->str, quot/rem, bit-xor/not, set-car/cdr!
Diffstat:
2 files changed, 413 insertions(+), 142 deletions(-)
diff --git a/docs/scheme-shell-todo.md b/docs/scheme-shell-todo.md
@@ -5,8 +5,12 @@ Checklist for getting `lisp/shell.scm` running under scheme1.
**Workflow:** every item is red-green TDD. Add a failing
`tests/scheme1/NN-*.scm` (with `.expected-exit` and/or `.expected`) first,
run the suite to confirm it fails for the expected reason, then
-implement until green. Multi-arch suite (`make test SUITE=scheme1`)
-must stay clean before moving on.
+implement until green. Multi-arch suite must stay clean before moving on.
+
+```
+# Run 1+ specific tests
+scripts/run-tests.sh --suite scheme1 --arch aarch64 70-type-predicates
+```
# Audit: deviations and known issues
@@ -31,55 +35,60 @@ Per LISP.md and LISP-C.md, but not implemented:
- [ ] **Special forms missing**: `set!`, `pmatch`, `cond`'s
`=>` arrow form.
-- [ ] **Primitives missing** (LISP.md lists them as required):
- - Predicates: `boolean?`, `integer?`, `string?`, `procedure?`,
- `record?`, `record-type?`
- - Numeric: `quotient`, `remainder`, `modulo`,
- `abs`, `min`, `max`, `bit-xor`,
- `bit-not`, `number->string`, `string->number`
- - Pair / list: `set-car!`, `set-cdr!`, `length`,
- - Bytevector: `bytevector-append`, `bytevector=?`, `string->symbol`,
- `symbol->string`
-- [ ] **`+ - * = < >` are 2-arg only.** R7RS allows any arity.
-- [ ] **Type names are not bound by `define-record-type`.** The TD is
- reachable only via the parameterized prims that close over it; no
- `record-type-of`, no way to inspect a TD from user code. Spec is
- ambiguous on this; LISP-C.md's example uses a generated `<point-td>`
- binding.
-- [ ] **`scheme1/prelude.scm` carries both the language prelude and
- the shell.scm library.**
- Missing: `vector->list` / `list->vector` (need `make-vector` /
- `vector-ref` / `vector-set!` / `vector-length`),
- Each re-enables when its underlying primitives land.
-
-## Prelude scope (deliberately wider than `lisp/prelude.scm`)
-
-scheme1 keeps the shell.scm library inside `prelude.scm` rather than
-mirroring the `lisp/` two-file split. The boot script catm's a single
-file in front of the user script, so process / file-I/O / port
-wrappers are always in scope.
-
-What lives in `prelude.scm` today, grouped:
-
-- **Language helpers** (subset of `lisp/prelude.scm`): see the bullet
- in "Spec features still missing" above for the active set and what
- remains commented out pending primitives.
-- **Process management**: `sys-wait` (Scheme adapter over `sys-waitid`
- returning a wait4-style raw wstatus), `decode-wait-status`, `wait`,
- `exit`, `argv`, `spawn`, `run`.
-- **File-I/O constants**: `BUFSIZE`, `AT_FDCWD`, `O_RDONLY`, `O_WRONLY`,
- `O_CREAT`, `O_TRUNC`, `O_APPEND`, `MODE_644`, `NL-BYTE`, `NL-BV`.
-- **Port record + handles**: `port` (via `define-record-type`) plus
- `stdin` / `stdout` / `stderr`.
-- **Buffered I/O**: `open-input` / `open-output` / `open-append` /
- `close`, `refill!`, `read-bytes`, `read-line`, `read-all`,
- `bv-concat-reverse`, `write-bytes`, `write-string`, `write-line`.
-
-Items to add as the underlying primitives land:
-
-- [ ] Re-enable `vector->list` / `list->vector` once
- `make-vector` / `vector-ref` / `vector-set!` / `vector-length`
- are primitives.
+
+### Records: target R7RS-small semantics
+
+Decision: track R7RS-small `define-record-type` exactly. The form
+introduces a disjoint type and binds **only** these identifiers at
+runtime:
+
+1. The constructor name (e.g. `make-point`) — a procedure.
+2. The predicate name (e.g. `point?`) — a procedure.
+3. One accessor per field (e.g. `point-x`) — a procedure.
+4. One mutator per `(field acc mut)` clause (e.g. `set-point-y!`) — a
+ procedure.
+
+Explicitly **not** bound: the type name itself (`point` after
+`(define-record-type point …)`). It is purely syntactic; the type
+descriptor is not a user-visible value. There is no `record-type-of`,
+no way to grab the TD from Scheme code, no `<point-td>`-style
+generated binding. R7RS does not provide reflective access; we don't
+add one.
+
+Also **not** in the user-facing API:
+
+- `record?`, `record-is-a?`, `record-type?` (LISP-C.md currently lists
+ these — drop from the spec).
+- The six `%record-*` primitives (`%make-record-td`, `%make-record`,
+ `%record-ref`, `%record-set!`, `%record-is-a?`, `%record-td`).
+ Internal only; they must not appear in `prim_table`.
+
+Concrete cleanup tasks:
+
+- [ ] Remove `%make-record-td` / `%make-record` / `%record-ref` /
+ `%record-set!` / `%record-is-a?` / `%record-td` rows from
+ `prim_table` in `scheme1/scheme1.P1pp`. The parameterized record
+ PRIMs (`prim_ctor_entry`, `prim_pred_entry`, `prim_field_ref_entry`,
+ `prim_field_set_entry`) reach the entry labels directly via the
+ closure data slot, so removing the public bindings is local.
+ Tests `38-record-internal-prims.scm` (rewritten to assert unbound)
+ and `39-record-typename-unbound.scm` are the red-green guards.
+- [ ] Update `docs/LISP-C.md`:
+ - In the `define-record-type` desugaring example (currently lines
+ 643+), drop the generated `(define <point-td> …)` line. Replace
+ the `%record-is-a?` / `%record-ref` / `%make-record` references
+ in the desugaring body with the parameterized-PRIM mechanism, or
+ flag them as illustrative pseudocode. The user-visible result is
+ just the four kinds of bindings listed above.
+ - Drop `record?`, `record-is-a?`, `record-type?` from the
+ Predicates / equality public-primitive list (line 660–662). Adjust
+ the "13" predicate count to 10.
+ - Move the `%record-*` count from "55 user-facing primitives plus 6
+ internal record ops" to keep the 6 internal but ensure no
+ duplication or claim of public exposure.
+
+Note: `equal?` on records (test 59) keeps working — it walks the
+internal layout via the runtime, not via user-callable primitives.
## Hacks and fragile invariants
@@ -95,29 +104,10 @@ These work today but are easy to break.
Capacity is never reset by `bytevector-copy!` or any other op, so
the invariant only protects fresh / never-overwritten bytevectors.
-- [ ] **`bytevector-grow!`** is a public primitive (`bv_grow`) that's
- effectively only there to make the doubling path testable
- (test 34). Not in R7RS, not in LISP.md. Either expose it as part of
- a documented mutable-bytevector API or delete and demote `bv_grow`
- to internal.
-
-- [ ] **`%record-*` primitives are exposed publicly** in `prim_table`
- alongside the parameterized record entries. LISP-C.md says "internal,
- not part of the user-facing primitive list".
-
-- [ ] **PRIM size grew from 16 to 24 bytes uniformly** to fit the
- parameterized data slot used by record ctors / preds / accessors /
- mutators. Plain primitives (sys-exit, cons, +, …) waste those 8
- bytes per instance.
-
-- [ ] **`apply` modification: prim ptr is now passed in `a1`** alongside
- args in `a0`. All existing primitives ignore `a1`, but any future
- primitive that uses `a1` for anything else will silently break.
-
-- [ ] **Symbol-table linear scan.** `intern` walks the table from idx 0
- on every call. LISP-C.md describes a 16384-slot open-addressing
- hash; we have a 1024-slot linear scan that exits with code 5 on
- overflow.
+- [ ] **`%record-*` primitives are exposed publicly** — see the
+ Records: target R7RS-small semantics section above for the full
+ fix. Tracked there to keep desugaring + spec + tests + table
+ changes together.
## Test suite caveats
@@ -187,10 +177,17 @@ before the suite can be considered authoritative.
spawn bug by redefining `spawn` at user level. The prelude's
`spawn` / `run` are therefore covered by zero passing tests.
-- [ ] **`tests/scheme1/38-record-internal-prims.scm`** — the
- `%record-*` primitives are tested via the Scheme surface; the only
- way to invoke them is through the public binding, which conflicts
- with LISP-C.md's "internal" classification.
+- [ ] **`tests/scheme1/38-record-internal-prims.scm`** — rewritten to
+ assert that `%make-record-td` is **unbound** at user level, matching
+ the R7RS target (see the records spec section above). Currently red:
+ the prim is still in `prim_table`. Goes green once the table cleanup
+ lands.
+
+- [ ] **`tests/scheme1/39-record-typename-unbound.scm`** — asserts
+ `(define-record-type point …)` does not bind `point` to anything at
+ runtime; bare reference is unbound. Currently green (matches
+ existing impl) and protects against regressions that would add a
+ hidden TD binding.
- [ ] **No test verifies `(set-car! …)` / `(set-cdr! …)`** — the
primitives don't exist; spec requires them.
@@ -215,17 +212,3 @@ before the suite can be considered authoritative.
verify that a `(cond)` with no matching clause and no `else` returns
UNSPEC (or whatever the policy is — currently it does, but
unspecified by spec).
-
-## Suggested next steps before shipping
-
-In rough priority order:
-
-1. Track down and fix the prelude `spawn`-via-`run` bug; remove the
- workaround in test 45.
-2. Fill in the spec-required primitives (`eqv?`, `set-car!`,
- `set-cdr!`, the comparison family, the bytevector family, the
- number/string converters) — many of these unblock the
- commented-out helpers in `prelude.scm` (`vector->list` / `list->vector`).
-3. `set!`, `pmatch`.
-4. Replace the 1024-slot linear-scan symtab with an open-addressing
- hash per LISP-C.md.
diff --git a/scheme1/scheme1.P1pp b/scheme1/scheme1.P1pp
@@ -882,9 +882,13 @@
%tail(&intern)
::is_int
- %ld(a0, sp, 0)
- %ld(a1, sp, 8)
- %tail(&parse_int)
+ %ld(t0, sp, 0) ; start_off
+ %ld(t1, sp, 8) ; end_off
+ %la(a0, &readbuf_buf_ptr)
+ %ld(a0, a0, 0)
+ %add(a0, a0, t0) ; ptr = base + start_off
+ %sub(a1, t1, t0) ; len = end_off - start_off
+ %tail(&parse_dec)
})
# parse_string() -> tagged bytevector in a0. Cursor sits past the
@@ -1126,39 +1130,58 @@
%die(msg_bad_char)
})
-# parse_int(start_off=a0, end_off=a1) -> tagged fixnum in a0. Leaf.
-:parse_int
-%scope parse_int
- %la(t0, &readbuf_buf_ptr)
- %ld(t0, t0, 0)
- %add(t1, t0, a1) ; t1 = end pointer = base + end_off
- %add(t0, t0, a0) ; t0 = start pointer = base + start_off
+# parse_dec(data_ptr=a0, len=a1) -> (tagged fixnum=a0, ok=a1). Leaf.
+# Accepts an optional leading '-' followed by one or more decimal digits.
+# ok=1 on a fully-consumed valid input; ok=0 on empty input, lone "-",
+# or any non-digit byte. On failure a0 still holds the best-effort
+# accumulator (sign-applied, retagged) so reader-style callers that
+# don't check ok get the same garbage they got before this routine
+# validated. UB on integer overflow.
+:parse_dec
+%scope parse_dec
+ %li(t4, 0) ; sign flag (0 = positive)
+ %li(t0, 0) ; accumulator (raw)
+ %beqz(a1, &::fail)
- %li(a2, 0) ; a2 = "is negative" flag (0 = positive)
- %lb(a3, t0, 0)
- %addi(a3, a3, -45)
- %bnez(a3, &::loop)
- %li(a2, 1)
- %addi(t0, t0, 1)
+ %lb(t2, a0, 0)
+ %li(t3, 45) ; '-'
+ %bne(t2, t3, &::loop)
+ %li(t4, 1)
+ %addi(a0, a0, 1)
+ %addi(a1, a1, -1)
+ %beqz(a1, &::fail)
::loop
- %li(a0, 0)
- %li(t2, 10)
- ::step
- %beq(t0, t1, &::done)
- %lb(a3, t0, 0)
- %addi(a3, a3, -48)
- %mul(a0, a0, t2)
- %add(a0, a0, a3)
- %addi(t0, t0, 1)
- %b(&::step)
+ %beqz(a1, &::done)
+ %lb(t2, a0, 0)
+ %addi(t2, t2, -48)
+ %bltz(t2, &::fail)
+ %li(t3, 10)
+ %bltu(t2, t3, &::digit_ok)
+ %b(&::fail)
+ ::digit_ok
+ %mul(t0, t0, t3)
+ %add(t0, t0, t2)
+ %addi(a0, a0, 1)
+ %addi(a1, a1, -1)
+ %b(&::loop)
::done
- %beqz(a2, &::tag)
- %li(t2, 0)
- %sub(a0, t2, a0)
+ %beqz(t4, &::tag)
+ %li(t1, 0)
+ %sub(t0, t1, t0)
::tag
- %mkfix(a0, a0)
+ %mkfix(a0, t0)
+ %li(a1, 1)
+ %ret
+
+ ::fail
+ %beqz(t4, &::fail_tag)
+ %li(t1, 0)
+ %sub(t0, t1, t0)
+ ::fail_tag
+ %mkfix(a0, t0)
+ %li(a1, 0)
%ret
%endscope
@@ -1356,9 +1379,16 @@
%die(msg_not_proc)
::prim
- # Primitives are called with args list in a0 and the prim ptr itself
- # in a1, so parameterized prims (eg record accessors) can read their
- # data slot via a1+13. Plain primitives ignore a1.
+ # Primitive calling convention (interface, not coincidence):
+ # a0 = args list (proper list of evaluated args)
+ # a1 = the PRIM object itself (HEAP-tagged)
+ # Parameterized PRIMs (e.g. the per-field record accessors built
+ # by define-record-type) read their closed-over datum from
+ # a1+13. Plain PRIMs ignore a1. A primitive that needs a1 as a
+ # working register must save it first; this convention is shared
+ # across every entry in prim_table and is not negotiable per
+ # primitive. prim_apply_entry maintains the same contract when it
+ # tail-calls back into apply.
%mov(a1, a0)
%ld(t0, a0, 5)
%ld(a0, sp, 0)
@@ -2249,6 +2279,213 @@
%ret
%endscope
+# (bytevector-append bv ...) -- variadic concatenation. Two passes:
+# the first sums the bv lengths so we can size the result up front; the
+# second walks the args again and memcpy's each src into the result.
+# The args list head is saved at +0 because pass 1 walks a separate
+# cursor (t1) and pass 2 needs to re-read the head. memcpy clobbers
+# t-regs, so the running write offset and remaining-args cursor live
+# in the frame across each call.
+#
+# Frame: 32 bytes
+# +0 args list head (re-read for pass 2; cursor during pass 2)
+# +8 total length (raw)
+# +16 result bv
+# +24 write offset (raw, into result.data)
+%fn(prim_bv_append_entry, 32, {
+ %st(a0, sp, 0)
+
+ %li(t0, 0)
+ %mov(t1, a0)
+ ::sum_loop
+ %if_nil(t2, t1, &::sum_done)
+ %car(t2, t1)
+ %ld(a0, t2, -3)
+ %shri(a0, a0, 8)
+ %add(t0, t0, a0)
+ %cdr(t1, t1)
+ %b(&::sum_loop)
+ ::sum_done
+ %st(t0, sp, 8)
+
+ %mov(a0, t0)
+ %call(&bv_alloc)
+ %st(a0, sp, 16)
+
+ %li(t0, 0)
+ %st(t0, sp, 24)
+
+ ::copy_loop
+ %ld(t0, sp, 0)
+ %if_nil(t1, t0, &::copy_done)
+ %car(t1, t0) ; src bv
+ %ld(t2, t1, -3)
+ %shri(t2, t2, 8) ; src length
+
+ %ld(a0, sp, 16)
+ %ld(a0, a0, 5) ; result.data
+ %ld(a3, sp, 24)
+ %add(a0, a0, a3) ; dst = result.data + offset
+ %ld(a1, t1, 5) ; src.data
+ %mov(a2, t2) ; count
+
+ %add(a3, a3, t2)
+ %st(a3, sp, 24)
+ %cdr(t0, t0)
+ %st(t0, sp, 0)
+
+ %call(&memcpy)
+ %b(&::copy_loop)
+
+ ::copy_done
+ %ld(a0, sp, 16)
+})
+
+# (string->symbol bv) -- intern the bytes and return the SYM-tagged
+# value. intern copies the name into stable heap storage if it has to
+# append, so the bv's data buffer is safe to relocate afterwards.
+:prim_string_to_symbol_entry
+ %car(t0, a0)
+ %ld(a0, t0, 5) ; data ptr
+ %ld(a1, t0, -3)
+ %shri(a1, a1, 8) ; length
+ %b(&intern)
+
+# (symbol->string sym) -- fresh bv copy of the symtab name. sym_name
+# returns (ptr, len); bv_alloc gives us a clean wrapper; memcpy fills
+# the data. Frame holds the (ptr, len) pair across bv_alloc and the
+# resulting bv across memcpy.
+%fn(prim_symbol_to_string_entry, 24, {
+ %car(a0, a0)
+ %sari(a0, a0, 3) ; raw sym idx
+ %call(&sym_name) ; -> ptr (a0), len (a1)
+ %st(a0, sp, 0)
+ %st(a1, sp, 8)
+ %mov(a0, a1)
+ %call(&bv_alloc) ; tagged bv in a0
+ %st(a0, sp, 16)
+ %ld(a1, sp, 0) ; src ptr
+ %ld(a2, sp, 8) ; len
+ %ld(t0, a0, 5) ; dst = bv.data
+ %mov(a0, t0)
+ %call(&memcpy)
+ %ld(a0, sp, 16)
+})
+
+# (number->string n) -- decimal repr in a fresh bv. bv_putint takes the
+# raw value, so untag first; bv_alloc(0) gives an empty wrapper that
+# bv_putint grows in place. +0 holds the raw value across bv_alloc.
+%fn(prim_number_to_string_entry, 16, {
+ %car(t0, a0)
+ %sari(t0, t0, 3) ; raw value
+ %st(t0, sp, 0)
+ %li(a0, 0)
+ %call(&bv_alloc)
+ %ld(a1, sp, 0)
+ %tail(&bv_putint)
+})
+
+# (string->number bv) -- delegate parsing to parse_dec. Returns #f on
+# non-bytevector input, empty string, lone "-", or any non-digit byte.
+%fn(prim_string_to_number_entry, 0, {
+ %car(a0, a0)
+ %tagof(t0, a0)
+ %li(t1, %TAG.HEAP)
+ %bne(t0, t1, &::fail)
+ %hdr_type(t0, a0)
+ %li(t1, %HDR.BV)
+ %bne(t0, t1, &::fail)
+
+ %ld(t0, a0, 5) ; data ptr
+ %ld(t1, a0, -3)
+ %shri(t1, t1, 8) ; length
+ %mov(a0, t0)
+ %mov(a1, t1)
+ %call(&parse_dec) ; -> (a0=value, a1=ok)
+ %bnez(a1, &::end)
+ ::fail
+ %li(a0, %imm_val(%IMM.FALSE))
+ ::end
+})
+
+# (boolean? x) -- #t iff x is the IMM.FALSE or IMM.TRUE singleton.
+:prim_booleanq_entry
+%scope prim_booleanq
+ %car(t0, a0)
+ %li(a0, %imm_val(%IMM.TRUE))
+ %li(t1, %imm_val(%IMM.FALSE))
+ %beq(t0, t1, &::end)
+ %li(t1, %imm_val(%IMM.TRUE))
+ %beq(t0, t1, &::end)
+ %li(a0, %imm_val(%IMM.FALSE))
+ ::end
+ %ret
+%endscope
+
+# (integer? x) -- #t iff x is a fixnum (low 3 tag bits == TAG.FIXNUM == 0).
+:prim_integerq_entry
+%scope prim_integerq
+ %car(t0, a0)
+ %tagof(t1, t0)
+ %li(a0, %imm_val(%IMM.FALSE))
+ %bnez(t1, &::end)
+ %li(a0, %imm_val(%IMM.TRUE))
+ ::end
+ %ret
+%endscope
+
+# (procedure? x) -- #t iff x is HEAP-tagged with header HDR.CLOSURE or HDR.PRIM.
+:prim_procedureq_entry
+%scope prim_procedureq
+ %car(t0, a0)
+ %tagof(t1, t0)
+ %li(t2, %TAG.HEAP)
+ %li(a0, %imm_val(%IMM.FALSE))
+ %bne(t1, t2, &::end)
+ %hdr_type(t1, t0)
+ %li(t2, %HDR.CLOSURE)
+ %beq(t1, t2, &::yes)
+ %li(t2, %HDR.PRIM)
+ %beq(t1, t2, &::yes)
+ %b(&::end)
+ ::yes
+ %li(a0, %imm_val(%IMM.TRUE))
+ ::end
+ %ret
+%endscope
+
+# (record? x) -- #t iff x is HEAP-tagged with HDR.REC.
+:prim_recordq_entry
+%scope prim_recordq
+ %car(t0, a0)
+ %tagof(t1, t0)
+ %li(t2, %TAG.HEAP)
+ %li(a0, %imm_val(%IMM.FALSE))
+ %bne(t1, t2, &::end)
+ %hdr_type(t1, t0)
+ %li(t2, %HDR.REC)
+ %bne(t1, t2, &::end)
+ %li(a0, %imm_val(%IMM.TRUE))
+ ::end
+ %ret
+%endscope
+
+# (record-type? x) -- #t iff x is HEAP-tagged with HDR.TD.
+:prim_record_typeq_entry
+%scope prim_record_typeq
+ %car(t0, a0)
+ %tagof(t1, t0)
+ %li(t2, %TAG.HEAP)
+ %li(a0, %imm_val(%IMM.FALSE))
+ %bne(t1, t2, &::end)
+ %hdr_type(t1, t0)
+ %li(t2, %HDR.TD)
+ %bne(t1, t2, &::end)
+ %li(a0, %imm_val(%IMM.TRUE))
+ ::end
+ %ret
+%endscope
+
:prim_zeroq_entry
%scope prim_zeroq
%car(t0, a0)
@@ -2342,6 +2579,22 @@
%ret
%endscope
+# (quotient x y) -- truncating integer division. Both fixnums are tagged
+# (real << 3); div(tagged, tagged) yields the raw quotient (the shifts
+# cancel), which mkfix retags. UB on y == 0.
+:prim_quotient_entry
+ %args2(t0, t1, a0)
+ %div(a0, t0, t1)
+ %mkfix(a0, a0)
+ %ret
+
+# (remainder x y) -- truncating remainder, sign of dividend. rem(tagged,
+# tagged) = 8 * (real_x rem real_y), already in tagged form.
+:prim_remainder_entry
+ %args2(t0, t1, a0)
+ %rem(a0, t0, t1)
+ %ret
+
:prim_bit_and_entry
%car(t0, a0)
%cdr(t1, a0)
@@ -2356,6 +2609,23 @@
%or(a0, t0, t1)
%ret
+# (bit-xor x y) -- both operands have low 3 tag bits == 0 (fixnums), so
+# bitwise XOR preserves zero tag bits in the result.
+:prim_bit_xor_entry
+ %args2(t0, t1, a0)
+ %xor(a0, t0, t1)
+ %ret
+
+# (bit-not n) -- bitwise complement. Untag, XOR with -1 (= ~n), retag.
+# Can't XOR the tagged value directly: that would flip the low 3 tag bits.
+:prim_bit_not_entry
+ %car(t0, a0)
+ %untag_fix(t0, t0)
+ %li(t1, -1)
+ %xor(t0, t0, t1)
+ %mkfix(a0, t0)
+ %ret
+
# (arithmetic-shift n k): k > 0 means left shift; k < 0 means arith right.
# Untag both, branch on sign of k, retag.
:prim_arith_shift_entry
@@ -2645,21 +2915,6 @@
%die(msg_bv_oob)
})
-# (bytevector-grow! bv min-cap) -> bv. Forwards to bv_grow; min-cap is
-# untagged at the boundary. Useful when a builder pattern wants to
-# pre-extend capacity, but also makes the doubling path testable.
-:prim_bv_grow_entry
-%scope prim_bv_grow
- %args2(t0, t1, a0)
- %sari(t1, t1, 3)
- %bltz(t1, &::bad)
- %mov(a0, t0)
- %mov(a1, t1)
- %b(&bv_grow)
- ::bad
- %die(msg_bv_oob)
-%endscope
-
# (bytevector-copy! dst dst-start src src-start src-end). Bounds:
# 0 <= src-start <= src-end <= src.length and
# 0 <= dst-start && dst-start + (src-end-src-start) <= dst.length.
@@ -2937,6 +3192,13 @@
# (apply fn rest...) -- the trailing element of `rest` is a list; any
# leading elements get prepended to it. apply_build_args walks `rest` and
# returns the assembled args list; prim_apply_entry then tail-calls apply.
+#
+# `apply` is itself a primitive, so on entry here a0 holds (fn . rest)
+# and a1 holds the apply PRIM ptr (per the convention documented at
+# `apply::prim`). a1 is dead from this primitive's point of view; we
+# clobber it freely while assembling args, then tail-call apply, which
+# re-derives a1 from the callee fn it dispatches on. Outer convention
+# stays intact end-to-end.
%fn(prim_apply_entry, 16, {
%st(a0, sp, 0)
%cdr(a0, a0)
@@ -4209,6 +4471,16 @@
:name_set_cdr "set-cdr!"
:name_length "length"
:name_list_ref "list-ref"
+:name_str_to_sym "string->symbol"
+:name_sym_to_str "symbol->string"
+:name_num_to_str "number->string"
+:name_str_to_num "string->number"
+:name_bv_append "bytevector-append"
+:name_booleanq "boolean?"
+:name_integerq "integer?"
+:name_procedureq "procedure?"
+:name_recordq "record?"
+:name_record_typeq "record-type?"
:name_zeroq "zero?"
:name_not "not"
:name_eqq "eq?"
@@ -4219,8 +4491,12 @@
:name_eq "="
:name_lt "<"
:name_gt ">"
+:name_quotient "quotient"
+:name_remainder "remainder"
:name_bit_and "bit-and"
:name_bit_or "bit-or"
+:name_bit_xor "bit-xor"
+:name_bit_not "bit-not"
:name_arith_shift "arithmetic-shift"
:name_apply "apply"
:name_make_bv "make-bytevector"
@@ -4229,7 +4505,6 @@
:name_bv_u8_set "bytevector-u8-set!"
:name_bv_copy "bytevector-copy"
:name_bv_copy_b "bytevector-copy!"
-:name_bv_grow "bytevector-grow!"
:name_bv_eq "bytevector=?"
:name_make_rt "%make-record-td"
:name_make_rec "%make-record"
@@ -4283,6 +4558,16 @@
&name_set_cdr %(0) $(8) &prim_set_cdr_entry %(0)
&name_length %(0) $(6) &prim_length_entry %(0)
&name_list_ref %(0) $(8) &prim_list_ref_entry %(0)
+&name_str_to_sym %(0) $(14) &prim_string_to_symbol_entry %(0)
+&name_sym_to_str %(0) $(14) &prim_symbol_to_string_entry %(0)
+&name_num_to_str %(0) $(14) &prim_number_to_string_entry %(0)
+&name_str_to_num %(0) $(14) &prim_string_to_number_entry %(0)
+&name_bv_append %(0) $(17) &prim_bv_append_entry %(0)
+&name_booleanq %(0) $(8) &prim_booleanq_entry %(0)
+&name_integerq %(0) $(8) &prim_integerq_entry %(0)
+&name_procedureq %(0) $(10) &prim_procedureq_entry %(0)
+&name_recordq %(0) $(7) &prim_recordq_entry %(0)
+&name_record_typeq %(0) $(12) &prim_record_typeq_entry %(0)
&name_zeroq %(0) $(5) &prim_zeroq_entry %(0)
&name_not %(0) $(3) &prim_not_entry %(0)
&name_eqq %(0) $(3) &prim_eqq_entry %(0)
@@ -4293,8 +4578,12 @@
&name_eq %(0) $(1) &prim_eq_entry %(0)
&name_lt %(0) $(1) &prim_lt_entry %(0)
&name_gt %(0) $(1) &prim_gt_entry %(0)
+&name_quotient %(0) $(8) &prim_quotient_entry %(0)
+&name_remainder %(0) $(9) &prim_remainder_entry %(0)
&name_bit_and %(0) $(7) &prim_bit_and_entry %(0)
&name_bit_or %(0) $(6) &prim_bit_or_entry %(0)
+&name_bit_xor %(0) $(7) &prim_bit_xor_entry %(0)
+&name_bit_not %(0) $(7) &prim_bit_not_entry %(0)
&name_arith_shift %(0) $(16) &prim_arith_shift_entry %(0)
&name_apply %(0) $(5) &prim_apply_entry %(0)
&name_make_bv %(0) $(15) &prim_make_bytevector_entry %(0)
@@ -4303,7 +4592,6 @@
&name_bv_u8_set %(0) $(18) &prim_bv_u8_set_entry %(0)
&name_bv_copy %(0) $(15) &prim_bv_copy_entry %(0)
&name_bv_copy_b %(0) $(16) &prim_bv_copy_bang_entry %(0)
-&name_bv_grow %(0) $(16) &prim_bv_grow_entry %(0)
&name_bv_eq %(0) $(12) &prim_bytevector_eq_entry %(0)
&name_make_rt %(0) $(15) &prim_make_record_td_entry %(0)
&name_make_rec %(0) $(12) &prim_make_record_entry %(0)