boot2

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

commit ba9c6b8cadfd7b802403b6a2df129cad572cab58
parent 21cbd8312842f60c4ab99f36cedd0002d85fdac7
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 28 Apr 2026 09:41:38 -0700

scheme1: drop unused special forms (unless, define-values, case-lambda, letrec/letrec*, quasiquote)

cc.scm + prelude.scm don't use any of these. Removed handlers
(qq_walk, eval_quasiquote, eval_define_values, eval_letrec,
eval_unless, eval_case_lambda, apply_case_lambda), the CASELAMBDA
struct + HDR enum entry and its branches in apply / procedure? /
write_to_bv, the reader's `::quasiquote` branch and the `,@` peek
inside `::comma`, and the matching dispatch / intern / sym / name /
msg rows. Also dropped the define-values check in eval_body.

`unquote` (sym + name + intern + reader translation) stays — pmatch
recognizes `,ident` binder patterns by comparing against sym_unquote.

-454 lines from scheme1.P1pp.

Diffstat:
Mscheme1/scheme1.P1pp | 560++-----------------------------------------------------------------------------
Dtests/scheme1/020-letrec.expected-exit | 1-
Dtests/scheme1/020-letrec.scm | 25-------------------------
Dtests/scheme1/021-letrec-recursion.expected-exit | 1-
Dtests/scheme1/021-letrec-recursion.scm | 3---
Mtests/scheme1/085-internal-define-error.expected | 2+-
Mtests/scheme1/085-internal-define-error.scm | 6+++---
Dtests/scheme1/095-unless.expected-exit | 1-
Dtests/scheme1/095-unless.scm | 24------------------------
Dtests/scheme1/106-case-lambda.expected-exit | 1-
Dtests/scheme1/106-case-lambda.scm | 47-----------------------------------------------
Dtests/scheme1/107-quasiquote.expected-exit | 1-
Dtests/scheme1/107-quasiquote.scm | 56--------------------------------------------------------
Dtests/scheme1/114-define-values.expected-exit | 1-
Dtests/scheme1/114-define-values.scm | 23-----------------------
15 files changed, 13 insertions(+), 739 deletions(-)

diff --git a/scheme1/scheme1.P1pp b/scheme1/scheme1.P1pp @@ -13,7 +13,7 @@ %enum TAG { FIXNUM PAIR SYM HEAP IMM } %enum IMM { FALSE TRUE NIL UNSPEC UNBOUND EOF } -%enum HDR { BV CLOSURE PRIM TD REC CASELAMBDA MV } +%enum HDR { BV CLOSURE PRIM TD REC MV } # imm_val(idx) -> integer-expression for the tagged immediate at IMM index # `idx`. Used both at %li sites (loaded into a register) and at $() emission @@ -25,7 +25,6 @@ %struct SYMENT { name_ptr name_len global_val pad } # .SIZE = 32 %struct PRIM { hdr entry_w data } # .SIZE = 24 %struct CLOSURE { hdr params body env } # .SIZE = 32 -%struct CASELAMBDA { hdr clauses env pad } # .SIZE = 32 %struct TD { hdr name nfields fields } # .SIZE = 32 %struct BV { hdr data } # .SIZE = 16 %struct REC { hdr td } # .SIZE = 16 (header) @@ -311,8 +310,6 @@ %beqz(a1, &::quote) %addi(a1, a0, -44) ; ',' %beqz(a1, &::comma) - %addi(a1, a0, -96) ; '`' - %beqz(a1, &::quasiquote) %addi(a1, a0, -34) ; '"' %beqz(a1, &::string) @@ -417,33 +414,14 @@ %tail(&cons) ::comma - # Consume the leading ','. If the next byte is '@', this is `,@` - # (unquote-splicing); otherwise it's `,` (unquote). Recurse into - # parse_one for the inner datum and build the appropriate pair. - # The comma sugar predates quasiquote -- it was added so pmatch - # patterns could be written as `,ident`. Outside a quasiquote - # template (and outside pmatch) `(unquote x)` still reaches eval as - # an application of the unbound `unquote` and dies through the - # standard unbound-variable path. + # Consume the leading ','; recurse into parse_one for the datum; + # build (unquote <datum>). The comma sugar exists so pmatch + # patterns can be written as `,ident`. Outside pmatch + # `(unquote x)` reaches eval as an application of the unbound + # `unquote` and dies through the standard unbound-variable path. %lda_global(t0, t2, &readbuf_pos) %addi(t0, t0, 1) %st(t0, t2, 0) - %ld_global(t1, &readbuf_len) - %beq(t0, t1, &::comma_atom) - %readbuf_byte(a0, t0) - %addi(a1, a0, -64) ; '@' - %bnez(a1, &::comma_atom) - %addi(t0, t0, 1) - %st(t0, t2, 0) - %call(&parse_one) - %li(a1, %imm_val(%IMM.NIL)) - %call(&cons) - %ld_global(t0, &sym_unquote_splicing) - %mov(a1, a0) - %mov(a0, t0) - %tail(&cons) - - ::comma_atom %call(&parse_one) %li(a1, %imm_val(%IMM.NIL)) %call(&cons) @@ -452,23 +430,6 @@ %mov(a0, t0) %tail(&cons) - ::quasiquote - # Consume the leading '`'; recurse into parse_one for the datum; - # build (quasiquote <datum>). The evaluator implements one-level - # template walking with `,` and `,@` substitution; nested - # quasiquote forms are preserved literally rather than recursed - # into. - %lda_global(t0, t2, &readbuf_pos) - %addi(t0, t0, 1) - %st(t0, t2, 0) - %call(&parse_one) - %li(a1, %imm_val(%IMM.NIL)) - %call(&cons) - %ld_global(t0, &sym_quasiquote) - %mov(a1, a0) - %mov(a0, t0) - %tail(&cons) - ::char_lit # Cursor is already past '#\\'; parse_char scans the body and returns # a tagged fixnum (the u8 char value). @@ -1193,7 +1154,6 @@ %ldl(t0, expr) %car(t0, t0) ; t0 = head %dispatch_form(&sym_quote, &::do_quote) - %dispatch_form(&sym_quasiquote, &::do_quasiquote) %dispatch_form(&sym_if, &::do_if) %dispatch_form(&sym_lambda, &::do_lambda) %dispatch_form(&sym_define, &::do_define) @@ -1201,21 +1161,16 @@ %dispatch_form(&sym_cond, &::do_cond) %dispatch_form(&sym_let, &::do_let) %dispatch_form(&sym_letstar, &::do_letstar) - %dispatch_form(&sym_letrec, &::do_letrec) - %dispatch_form(&sym_letrecstar, &::do_letrec) %dispatch_form(&sym_let_values, &::do_let_values) %dispatch_form(&sym_letstar_values, &::do_letstar_values) - %dispatch_form(&sym_define_values, &::do_define_values) %dispatch_form(&sym_and, &::do_and) %dispatch_form(&sym_or, &::do_or) %dispatch_form(&sym_when, &::do_when) - %dispatch_form(&sym_unless, &::do_unless) %dispatch_form(&sym_case, &::do_case) %dispatch_form(&sym_setbang, &::do_setbang) %dispatch_form(&sym_define_record_type, &::do_define_record_type) %dispatch_form(&sym_pmatch, &::do_pmatch) %dispatch_form(&sym_do, &::do_do) - %dispatch_form(&sym_case_lambda, &::do_case_lambda) # head = eval(car(expr), env) %ldl(a0, expr) @@ -1242,9 +1197,6 @@ %car(a0, a0) %eret - ::do_quasiquote - %tail_to_handler(&eval_quasiquote) - ::do_if %tail_to_handler(&eval_if) ::do_lambda @@ -1259,22 +1211,16 @@ %tail_to_handler(&eval_let) ::do_letstar %tail_to_handler(&eval_letstar) - ::do_letrec - %tail_to_handler(&eval_letrec) ::do_let_values %tail_to_handler(&eval_let_values) ::do_letstar_values %tail_to_handler(&eval_letstar_values) - ::do_define_values - %tail_to_handler(&eval_define_values) ::do_and %tail_to_handler(&eval_and) ::do_or %tail_to_handler(&eval_or) ::do_when %tail_to_handler(&eval_when) - ::do_unless - %tail_to_handler(&eval_unless) ::do_case %tail_to_handler(&eval_case) ::do_setbang @@ -1285,8 +1231,6 @@ %tail_to_handler(&eval_pmatch) ::do_do %tail_to_handler(&eval_do) - ::do_case_lambda - %tail_to_handler(&eval_case_lambda) }) # eval_args(args=a0, env=a1) -> evaluated args list (cons-built). @@ -1339,149 +1283,6 @@ %ldl(a0, head) }) -# qq_walk(t=a0, env=a1) -> result (a0). One-level quasiquote walker -# (R7RS §4.2.8 minus the nested-template recursion). Atoms and any -# `(quasiquote ...)` form are returned literally; an `(unquote x)` head -# evaluates x; otherwise we walk the spine, splicing `(unquote-splicing -# xs)` elements and recursing on every other element. Improper tails -# are qq'd and stitched back onto the result. -# -# Locals: -# t the template -# env evaluation environment for `,` and `,@` -# head accumulator head (NIL until first cell appended) -# tail most recently appended cell -# walk spine cursor (advances) -# spliced inner cursor used during `,@` expansion -%fn2(qq_walk, {t env head tail walk spliced}, { - %stl(a0, t) - %stl(a1, env) - %li(t0, %imm_val(%IMM.NIL)) - %stl(t0, head) - %stl(t0, tail) - - # Atom -> return as-is. - %tagof(t0, a0) - %li(t1, %TAG.PAIR) - %bne(t0, t1, &::atom_ret) - - # (unquote x) at the top -> tail eval(cadr t, env). - %ldl(t0, t) - %car(t1, t0) - %ld_global(t2, &sym_unquote) - %beq(t1, t2, &::do_unquote) - # Nested (quasiquote ...) -> literal; do not recurse. - %ld_global(t2, &sym_quasiquote) - %beq(t1, t2, &::atom_ret) - - # Walk the spine. - %ldl(t0, t) - %stl(t0, walk) - - ::loop - %ldl(t0, walk) - %if_nil(t1, t0, &::done) - %tagof(t1, t0) - %li(t2, %TAG.PAIR) - %bne(t1, t2, &::improper) - - # elem = car(walk). Detect (unquote-splicing ...) shape. - %car(a0, t0) - %tagof(t1, a0) - %li(t2, %TAG.PAIR) - %bne(t1, t2, &::recurse_elem) - %car(t1, a0) - %ld_global(t2, &sym_unquote_splicing) - %bne(t1, t2, &::recurse_elem) - - # spliced = eval(cadr elem, env) - %cdr(a0, a0) - %car(a0, a0) - %ldl(a1, env) - %call(&eval) - %stl(a0, spliced) - - ::splice_loop - %ldl(t0, spliced) - %if_nil(t1, t0, &::splice_advance_walk) - %tagof(t1, t0) - %li(t2, %TAG.PAIR) - %bne(t1, t2, &::splice_advance_walk) - %car(a0, t0) - %li(a1, %imm_val(%IMM.NIL)) - %call(&cons) - %ldl(t0, head) - %if_nil(t1, t0, &::splice_first) - %ldl(t0, tail) - %set_cdr(a0, t0) - %stl(a0, tail) - %b(&::splice_step) - ::splice_first - %stl(a0, head) - %stl(a0, tail) - ::splice_step - %ldl(t0, spliced) - %cdr(t0, t0) - %stl(t0, spliced) - %b(&::splice_loop) - - ::splice_advance_walk - %ldl(t0, walk) - %cdr(t0, t0) - %stl(t0, walk) - %b(&::loop) - - ::recurse_elem - # new_elem = qq_walk(elem, env); elem already in a0. - %ldl(a1, env) - %call(&qq_walk) - %li(a1, %imm_val(%IMM.NIL)) - %call(&cons) - %ldl(t0, head) - %if_nil(t1, t0, &::elem_first) - %ldl(t0, tail) - %set_cdr(a0, t0) - %stl(a0, tail) - %b(&::elem_step) - ::elem_first - %stl(a0, head) - %stl(a0, tail) - ::elem_step - %ldl(t0, walk) - %cdr(t0, t0) - %stl(t0, walk) - %b(&::loop) - - ::improper - # walk holds a non-pair non-NIL cdr; qq it and stitch onto tail. - %ldl(a0, walk) - %ldl(a1, env) - %call(&qq_walk) - %ldl(t0, tail) - %set_cdr(a0, t0) - - ::done - %ldl(a0, head) - %eret - - ::atom_ret - %ldl(a0, t) - %eret - - ::do_unquote - %ldl(t0, t) - %cdr(t0, t0) - %car(a0, t0) - %ldl(a1, env) - %tail(&eval) -}) - -# eval_quasiquote(rest=a0, env=a1) -> result (a0). rest = (template . _); -# tail-walks the template through qq_walk. -%fn(eval_quasiquote, 0, { - %car(a0, a0) - %tail(&qq_walk) -}) # apply(fn=a0, args=a1) -> result (a0) # @@ -1501,8 +1302,6 @@ %beq(t0, t1, &::prim) %li(t1, %HDR.CLOSURE) %beq(t0, t1, &::closure) - %li(t1, %HDR.CASELAMBDA) - %beq(t0, t1, &::case_lambda) ::not_proc %die(msg_not_proc) @@ -1541,12 +1340,6 @@ %ldl(a0, body) %tail(&eval_body) - ::case_lambda - # apply_case_lambda(args, obj). args already in `args` slot; the obj - # is still in a0. - %mov(a1, a0) - %ldl(a0, args) - %tail(&apply_case_lambda) }) # ========================================================================= @@ -1561,8 +1354,6 @@ %fn(intern_special_forms, 0, { %intern_form(&name_quote, 5, &sym_quote) - %intern_form(&name_quasiquote, 10, &sym_quasiquote) - %intern_form(&name_unquote_splicing, 16, &sym_unquote_splicing) %intern_form(&name_if, 2, &sym_if) %intern_form(&name_lambda, 6, &sym_lambda) %intern_form(&name_define, 6, &sym_define) @@ -1572,21 +1363,16 @@ %intern_form(&name_arrow, 2, &sym_arrow) %intern_form(&name_let, 3, &sym_let) %intern_form(&name_letstar, 4, &sym_letstar) - %intern_form(&name_letrec, 6, &sym_letrec) - %intern_form(&name_letrecstar, 7, &sym_letrecstar) %intern_form(&name_let_values, 10, &sym_let_values) %intern_form(&name_letstar_values, 11, &sym_letstar_values) - %intern_form(&name_define_values, 13, &sym_define_values) %intern_form(&name_and, 3, &sym_and) %intern_form(&name_or, 2, &sym_or) %intern_form(&name_when, 4, &sym_when) - %intern_form(&name_unless, 6, &sym_unless) %intern_form(&name_case, 4, &sym_case) %intern_form(&name_setbang, 4, &sym_setbang) %intern_form(&name_define_record_type, 18, &sym_define_record_type) %intern_form(&name_pmatch, 6, &sym_pmatch) %intern_form(&name_do, 2, &sym_do) - %intern_form(&name_case_lambda, 11, &sym_case_lambda) %intern_form(&name_unquote, 7, &sym_unquote) %intern_form(&name_guard, 5, &sym_guard) %intern_form(&name_underscore, 1, &sym_underscore) @@ -1723,70 +1509,6 @@ %li(a0, %imm_val(%IMM.UNSPEC)) }) -# eval_define_values(rest=a0, env=a1) -> UNSPEC (a0). -# Top-level only. rest = (formals value-expr). Evaluates value-expr, -# normalizes its result via mv_to_list, then walks formals and the value -# list in lockstep, binding each name in the global symbol table. A SYM -# tail (or bare-SYM formals) binds the remaining values list as one name. -# Internal define-values is rejected by eval_body alongside `define`. -# -# Locals: -# rest -# env -# vals (remaining values list; advances each iteration) -# formals (remaining formals; advances each iteration) -%fn2(eval_define_values, {rest env vals formals}, { - %stl(a0, rest) - %stl(a1, env) - - # value = eval(cadr(rest), env) - %cdr(t0, a0) - %car(a0, t0) - %ldl(a1, env) - %call(&eval) - - # vals = mv_to_list(value) - %call(&mv_to_list) - %stl(a0, vals) - - %ldl(t0, rest) - %car(t0, t0) ; formals (initial) - %stl(t0, formals) - - ::loop - %ldl(t0, formals) - %tagof(t1, t0) - %li(t2, %TAG.PAIR) - %beq(t1, t2, &::pair_form) - %li(t2, %TAG.SYM) - %beq(t1, t2, &::rest_bind) - %b(&::done) - - ::pair_form - %ldl(t0, formals) - %car(t0, t0) ; name - %ldl(a0, vals) - %car(a0, a0) ; value - %bind_global_from_t0() - - %ldl(t0, formals) - %cdr(t0, t0) - %stl(t0, formals) - %ldl(t0, vals) - %cdr(t0, t0) - %stl(t0, vals) - %b(&::loop) - - ::rest_bind - # Bare-SYM (or dotted-tail) formal: bind remaining values list as one. - %ldl(t0, formals) - %ldl(a0, vals) - %bind_global_from_t0() - - ::done - %li(a0, %imm_val(%IMM.UNSPEC)) -}) - # eval_setbang(rest=a0, env=a1) -> UNSPEC (a0). # rest = (sym value-expr). Evaluates value-expr in env, then walks the # env alist looking for a binding cell whose car is the target sym; @@ -2037,85 +1759,6 @@ %tail(&eval_body) }) -# eval_letrec(rest=a0, env=a1) -> value (a0). -# Two-phase. Phase 1: pre-bind every name to UNSPEC in the new env so -# inits can reference each other. Phase 2: evaluate each init in the new -# env and patch the matching binding's cdr (linear-scan lookup is fine -# here -- bindings are typically ≤ a handful). -# -# Locals: -# rest -# env_orig -# walk -# new_env -%fn2(eval_letrec, {rest env_orig walk new_env}, { - %stl(a0, rest) - %stl(a1, env_orig) - - %ldl(t0, rest) - %car(t0, t0) - %stl(t0, walk) - %ldl(t0, env_orig) - %stl(t0, new_env) - - ::phase1 - %ldl(t0, walk) - %if_nil(t1, t0, &::p1_done) - - %car(t1, t0) - %car(t2, t1) ; name - %mov(a0, t2) - %li(a1, %imm_val(%IMM.UNSPEC)) - %call(&cons) - %ldl(a1, new_env) - %call(&cons) - %stl(a0, new_env) - - %advance_walk(16) - %b(&::phase1) - - ::p1_done - %ldl(t0, rest) - %car(t0, t0) - %stl(t0, walk) ; reset walk - - ::phase2 - %ldl(t0, walk) - %if_nil(t1, t0, &::p2_done) - - %car(t1, t0) - %cdr(t2, t1) - %car(t2, t2) ; init - %mov(a0, t2) - %ldl(a1, new_env) - %call(&eval) ; val in a0 - - %ldl(t0, walk) - %car(t1, t0) - %car(t2, t1) ; name (in t2) - - %ldl(t1, new_env) - ::scan - %car(a1, t1) - %car(a2, a1) - %beq(a2, t2, &::found) - %cdr(t1, t1) - %b(&::scan) - - ::found - %car(a1, t1) - %set_cdr(a0, a1) ; set-cdr! binding val - - %advance_walk(16) - %b(&::phase2) - - ::p2_done - %ldl(a0, rest) - %cdr(a0, a0) - %ldl(a1, new_env) - %tail(&eval_body) -}) - # eval_let_values(rest=a0, env=a1) -> value (a0). # rest = (((formals init) ...) body...). Each init is evaluated in the # OUTER env; mv_to_list normalizes its result so bind_params can drive @@ -2337,31 +1980,6 @@ %li(a0, %imm_val(%IMM.UNSPEC)) }) -# eval_unless(rest=a0, env=a1) -> value (a0). -# Mirror of eval_when: tail-eval body iff test is #f, else UNSPEC. -# -# Locals: -# rest -# env -%fn2(eval_unless, {rest env}, { - %stl(a0, rest) - %stl(a1, env) - - %car(a0, a0) ; test - %call(&eval) - - %li(t0, %imm_val(%IMM.FALSE)) - %bne(a0, t0, &::skip) - - %ldl(a0, rest) - %cdr(a0, a0) ; body - %ldl(a1, env) - %tail(&eval_body) - - ::skip - %li(a0, %imm_val(%IMM.UNSPEC)) -}) - # eval_case(rest=a0, env=a1) -> value (a0). # rest is (key-expr clause...). The key is evaluated once; clauses are # tried in order. Clause shape: @@ -2770,144 +2388,6 @@ %b(&::update_loop) }) -# eval_case_lambda(rest=a0, env=a1) -> case-lambda object (a0). -# rest = ((formals body...) ...). Allocates a 32-byte HDR.CASELAMBDA -# heap object holding the clauses list and the captured env. apply() -# dispatches by walking clauses and matching the first whose formals -# admit the argument count. -# -# Locals: -# rest -# env -# obj -%fn2(eval_case_lambda, {rest env obj}, { - %stl(a0, rest) - %stl(a1, env) - - %li(a0, 32) - %li(a1, %HDR.CASELAMBDA) - %call(&alloc_hdr) - %stl(a0, obj) - - # obj.clauses = rest. - %ldl(t1, rest) - %ldl(t0, obj) - %heap_st(t1, t0, %CASELAMBDA.clauses) - - # obj.env = env. - %ldl(t1, env) - %heap_st(t1, t0, %CASELAMBDA.env) - - %ldl(a0, obj) -}) - -# apply_case_lambda(args=a0, obj=a1) -> result. -# Walks obj's clauses; the first whose formals shape admits the given -# argument count is selected. Match rules: -# formals = NIL -> arglen == 0 -# formals = SYM (rest) -> always matches -# formals = (a b ...) -> arglen == count(formals) -# formals = (a b ... . rest) -> arglen >= count(proper-prefix) -# On match: bind_params(formals, args, captured_env) -> new_env; -# tail-eval body in new_env. No-match dies with msg_case_lambda_no_match. -# -# Locals: -# args -# obj -# arglen -# clauses (advances) -# env_capt (captured env from obj) -# formals (current clause's formals; saved across bind_params) -# body (current clause's body; saved across bind_params) -%fn2(apply_case_lambda, {args obj arglen clauses env_capt formals body}, { - %stl(a0, args) - %stl(a1, obj) - - %ldl(a0, args) - %call(&list_length) - %stl(a0, arglen) - - %ldl(t0, obj) - %heap_ld(t1, t0, %CASELAMBDA.clauses) - %stl(t1, clauses) - %heap_ld(t1, t0, %CASELAMBDA.env) - %stl(t1, env_capt) - - ::loop - %ldl(t0, clauses) - %if_nil(t1, t0, &::no_match) - - %car(t1, t0) ; clause - %car(t2, t1) ; formals - %stl(t2, formals) - %cdr(t2, t1) ; body - %stl(t2, body) - - %ldl(t0, formals) - %tagof(t1, t0) - - # SYM formals: unconditional rest, always matches. - %li(t2, %TAG.SYM) - %beq(t1, t2, &::matched) - - # PAIR formals: count proper prefix, inspect terminator. - %li(t2, %TAG.PAIR) - %beq(t1, t2, &::pair_walk) - - # Otherwise (NIL or other immediate): match iff arglen == 0. - %ldl(t0, arglen) - %beqz(t0, &::matched) - %b(&::next) - - ::pair_walk - %li(t2, 0) ; required count - %ldl(t0, formals) - ::pcount - %tagof(t1, t0) - %li(a3, %TAG.PAIR) - %bne(t1, a3, &::pcount_done) - %addi(t2, t2, 1) - %cdr(t0, t0) - %b(&::pcount) - - ::pcount_done - # cursor (t0) is NIL (proper) or SYM (improper). - %tagof(t1, t0) - %li(a3, %TAG.SYM) - %beq(t1, a3, &::improper) - - # Proper formals: arglen must equal required. - %ldl(a3, arglen) - %beq(a3, t2, &::matched) - %b(&::next) - - ::improper - # Improper formals: arglen must be >= required. - # `arglen >= required` is `!(arglen < required)`. - %ldl(a3, arglen) - %blt(a3, t2, &::next) - %b(&::matched) - - ::next - %ldl(t0, clauses) - %cdr(t0, t0) - %stl(t0, clauses) - %b(&::loop) - - ::matched - %ldl(a0, formals) - %ldl(a1, args) - %ldl(a2, env_capt) - %call(&bind_params) - - %mov(a1, a0) - %ldl(a0, body) - %tail(&eval_body) - - ::no_match - %die(msg_case_lambda_no_match) -}) - # pmatch_match(pat=a0, subj=a1, env=a2) -> (env=a0, ok=a1) # # Walks pat and subj structurally. On success returns the (possibly @@ -3342,9 +2822,8 @@ %stl(a0, body) %stl(a1, env) - # Reject internal `define` / `define-values`. Detect (define ...) or - # (define-values ...) at the head of any form before dispatching to - # eval; both are top-level-only. + # Reject internal `define`. Detect (define ...) at the head of any + # form before dispatching to eval; top-level-only. %car(t0, a0) ; form %tagof(t1, t0) %li(t2, %TAG.PAIR) @@ -3352,8 +2831,6 @@ %car(t1, t0) ; head sym %ld_global(t2, &sym_define) %beq(t1, t2, &::internal_define) - %ld_global(t2, &sym_define_values) - %beq(t1, t2, &::internal_define) ::not_define # If cdr(body) is NIL, body's car is the last form. @@ -4065,8 +3542,6 @@ %beq(t1, t2, &::yes) %li(t2, %HDR.PRIM) %beq(t1, t2, &::yes) - %li(t2, %HDR.CASELAMBDA) - %beq(t1, t2, &::yes) %b(&::end) ::yes %li(a0, %imm_val(%IMM.TRUE)) @@ -5475,8 +4950,6 @@ %beq(t0, t1, &::heap_bv) %li(t1, %HDR.CLOSURE) %beq(t0, t1, &::heap_closure) - %li(t1, %HDR.CASELAMBDA) - %beq(t0, t1, &::heap_closure) %li(t1, %HDR.PRIM) %beq(t0, t1, &::heap_prim) %li(t1, %HDR.TD) @@ -6353,24 +5826,17 @@ :name_arrow "=>" '0000000000' :name_let "let" '00000000' :name_letstar "let*" '000000' -:name_letrec "letrec" '00' -:name_letrecstar "letrec*" :name_let_values "let-values" '0000000000' :name_letstar_values "let*-values" '00000000' -:name_define_values "define-values" '0000' :name_and "and" '00000000' :name_or "or" '0000000000' :name_when "when" '000000' -:name_unless "unless" '00' :name_case "case" '000000' :name_setbang "set!" '000000' :name_define_record_type "define-record-type" '0000000000' :name_pmatch "pmatch" '00' :name_do "do" '0000000000' -:name_case_lambda "case-lambda" '00000000' :name_unquote "unquote" -:name_quasiquote "quasiquote" '0000000000' -:name_unquote_splicing "unquote-splicing" '00000000000000' :name_guard "guard" '0000' :name_underscore "_" '000000000000' :name_dollar "$" '000000000000' @@ -6558,10 +6024,9 @@ :msg_bad_char "scheme1: bad #\\ character literal" '0a' '00000000' ;; 34+1+1+4=40 :msg_bad_number "scheme1: bad number literal" '0a' '000000' ;; 27+1+1+3=32 :msg_bad_ident "scheme1: bad identifier" '0a' '00000000000000' ;; 23+1+1+7=32 -:msg_internal_define "scheme1: internal define is not supported (use letrec)" '0a' ;; 54+1+1+0=56 +:msg_internal_define "scheme1: internal define is not supported" '0a' '0000000000' ;; 41+1+1+5=48 :msg_pmatch_no_match "scheme1: pmatch: no clause matched" '0a' '00000000' ;; 34+1+1+4=40 :msg_bad_unquote_pattern "scheme1: pmatch: malformed ,-pattern" '0a' '0000' ;; 36+1+1+2=40 -:msg_case_lambda_no_match "scheme1: case-lambda: no clause matched arity" '0a' '00' ;; 45+1+1+1=48 :name_ch_tab "tab" '00000000' ;; 3+1+4=8 :name_ch_null "null" '000000' ;; 4+1+3=8 @@ -6666,24 +6131,17 @@ :sym_arrow $(0) :sym_let $(0) :sym_letstar $(0) -:sym_letrec $(0) -:sym_letrecstar $(0) :sym_let_values $(0) :sym_letstar_values $(0) -:sym_define_values $(0) :sym_and $(0) :sym_or $(0) :sym_when $(0) -:sym_unless $(0) :sym_case $(0) :sym_setbang $(0) :sym_define_record_type $(0) :sym_pmatch $(0) :sym_do $(0) -:sym_case_lambda $(0) :sym_unquote $(0) -:sym_quasiquote $(0) -:sym_unquote_splicing $(0) :sym_guard $(0) :sym_underscore $(0) :sym_dollar $(0) diff --git a/tests/scheme1/020-letrec.expected-exit b/tests/scheme1/020-letrec.expected-exit @@ -1 +0,0 @@ -4 diff --git a/tests/scheme1/020-letrec.scm b/tests/scheme1/020-letrec.scm @@ -1,25 +0,0 @@ -; letrec / letrec*: local recursive bindings. letrec* is an alias of -; letrec in scheme1 (eval_letrec already evaluates inits left-to-right); -; the R7RS guarantee letrec* adds — later inits may reference earlier -; ones — is exercised below. - -;; --- letrec: classic recursive factorial --------------------------- -(if (= 120 (letrec ((fact (lambda (n) - (if (= n 0) 1 (* n (fact (- n 1))))))) - (fact 5))) - 0 (sys-exit 1)) - -;; --- letrec* accepts the same shape and value --------------------- -(if (= 120 (letrec* ((fact (lambda (n) - (if (= n 0) 1 (* n (fact (- n 1))))))) - (fact 5))) - 0 (sys-exit 2)) - -;; --- letrec*: later inits reference earlier ones ------------------- -;; y depends on x; z depends on both. + is 2-arg in scheme1, so each -;; init uses pairwise sums. -(if (= 6 (letrec* ((x 1) (y (+ x 2)) (z (+ y x))) - (+ z 2))) - 0 (sys-exit 3)) - -(sys-exit 4) diff --git a/tests/scheme1/021-letrec-recursion.expected-exit b/tests/scheme1/021-letrec-recursion.expected-exit @@ -1 +0,0 @@ -7 diff --git a/tests/scheme1/021-letrec-recursion.scm b/tests/scheme1/021-letrec-recursion.scm @@ -1,3 +0,0 @@ -; letrec actually exercises recursion: f(2) recurses to f(#f), which -; takes the else branch and exits 7. -(sys-exit (letrec ((f (lambda (n) (if n (f #f) 7)))) (f 2))) diff --git a/tests/scheme1/085-internal-define-error.expected b/tests/scheme1/085-internal-define-error.expected @@ -1 +1 @@ -scheme1: internal define is not supported (use letrec) +scheme1: internal define is not supported diff --git a/tests/scheme1/085-internal-define-error.scm b/tests/scheme1/085-internal-define-error.scm @@ -1,6 +1,6 @@ ; Internal `define` -- inside any non-empty lexical env (lambda body, -; let body, letrec body) -- is forbidden in this Scheme. eval_define -; rejects with a clear message and exits 1 via runtime_error. Use -; letrec for local recursive bindings. +; let body) -- is forbidden in this Scheme. eval_define rejects with +; a clear message and exits 1 via runtime_error. Use a top-level +; define or named-let for local recursion. ((lambda () (define x 1) x)) (sys-exit 0) diff --git a/tests/scheme1/095-unless.expected-exit b/tests/scheme1/095-unless.expected-exit @@ -1 +0,0 @@ -17 diff --git a/tests/scheme1/095-unless.scm b/tests/scheme1/095-unless.scm @@ -1,24 +0,0 @@ -; (unless test body...) -- mirror of `when`. Body runs (and last value -; is returned) when test is #f; otherwise UNSPEC. - -;; --- Falsy test: body runs, last form is the value ------------------- -(if (= 5 (unless #f 1 2 3 4 5)) 0 (sys-exit 1)) - -;; --- Truthy test: body skipped; result is UNSPEC --------------------- -(if (eq? (unless #t (sys-exit 99)) (unless 0 42)) 0 (sys-exit 2)) -(if (not (eq? (unless #t 1) #t)) 0 (sys-exit 3)) -(if (not (eq? (unless #t 1) #f)) 0 (sys-exit 4)) -(if (not (eq? (unless #t 1) '())) 0 (sys-exit 5)) - -;; --- Body side-effects fire only when test is falsy ------------------ -(define counter 0) -(unless #t (set! counter 100)) -(if (= counter 0) 0 (sys-exit 6)) -(unless #f (set! counter (+ counter 1)) (set! counter (+ counter 2))) -(if (= counter 3) 0 (sys-exit 7)) - -;; --- Any non-#f counts as true (so body is skipped) ------------------ -(if (eq? (unless 0 1) (unless #t 1)) 0 (sys-exit 8)) -(if (eq? (unless '() 1) (unless #t 1)) 0 (sys-exit 9)) - -(sys-exit 17) diff --git a/tests/scheme1/106-case-lambda.expected-exit b/tests/scheme1/106-case-lambda.expected-exit @@ -1 +0,0 @@ -42 diff --git a/tests/scheme1/106-case-lambda.scm b/tests/scheme1/106-case-lambda.scm @@ -1,47 +0,0 @@ -; (case-lambda (formals body...) ...) -- pick the first clause whose -; formals match the call's arity. Formals follow the same shape as -; lambda: (a b), (), (a b . rest), or rest-symbol. - -;; --- Plain arity dispatch ----------------------------------------------- -(define f (case-lambda - (() 100) - ((x) (+ x 1)) - ((x y) (* x y)))) -(if (= 100 (f)) 0 (sys-exit 1)) -(if (= 4 (f 3)) 0 (sys-exit 2)) -(if (= 12 (f 3 4)) 0 (sys-exit 3)) - -;; --- First matching clause wins ------------------------------------------ -(define g (case-lambda - ((x) 'first) - ((x) 'second))) -(if (eq? 'first (g 7)) 0 (sys-exit 4)) - -;; --- Rest argument clause: variadic match -------------------------------- -(define h (case-lambda - (() 'none) - ((x) (list 'one x)) - (xs (cons 'many xs)))) -(if (eq? 'none (h)) 0 (sys-exit 5)) -(if (equal? (h 9) (list 'one 9)) 0 (sys-exit 6)) -(if (equal? (h 1 2 3) (cons 'many (list 1 2 3))) 0 (sys-exit 7)) - -;; --- Improper formals: (a . rest) ---------------------------------------- -(define k (case-lambda - ((a) 'lone) - ((a . rest) (cons a rest)))) -(if (eq? 'lone (k 5)) 0 (sys-exit 8)) -(if (equal? (k 1 2 3) (list 1 2 3)) 0 (sys-exit 9)) - -;; --- Closes over the enclosing environment ------------------------------- -(define base 10) -(define add (case-lambda - ((x) (+ base x)) - ((x y) (+ (+ base x) y)))) -(if (= 11 (add 1)) 0 (sys-exit 10)) -(if (= 13 (add 1 2)) 0 (sys-exit 11)) - -;; --- Higher-order: case-lambda result is a procedure -------------------- -(if (procedure? f) 0 (sys-exit 12)) - -(sys-exit 42) diff --git a/tests/scheme1/107-quasiquote.expected-exit b/tests/scheme1/107-quasiquote.expected-exit @@ -1 +0,0 @@ -0 diff --git a/tests/scheme1/107-quasiquote.scm b/tests/scheme1/107-quasiquote.scm @@ -1,56 +0,0 @@ -; One-level quasiquote: backtick reads as (quasiquote ...); inside that -; template, (unquote x) splices a single evaluated value, and -; (unquote-splicing xs) splices the elements of xs in place. Atoms in -; the template are returned literally; nested quasiquote forms are not -; supported -- we don't recurse into them, so a `(quasiquote ...) form -; sitting inside the template is preserved as-is. - -;; --- Atom template: returns the atom ------------------------------------- -(if (= 7 `7) 0 (sys-exit 1)) -(if (eq? 'foo `foo) 0 (sys-exit 2)) -(if (equal? '() `()) 0 (sys-exit 3)) - -;; --- All-literal list template ------------------------------------------ -(if (equal? (list 1 2 3) `(1 2 3)) 0 (sys-exit 4)) -(if (equal? '(a b c) `(a b c)) 0 (sys-exit 5)) - -;; --- ,x evaluates one element in place ---------------------------------- -(define x 10) -(if (equal? (list 1 10 3) `(1 ,x 3)) 0 (sys-exit 6)) -(if (equal? (list 'a 11 'b) `(a ,(+ x 1) b)) 0 (sys-exit 7)) - -;; --- ,x at head and tail positions -------------------------------------- -(if (equal? (list 10 'b) `(,x b)) 0 (sys-exit 8)) -(if (equal? (list 'a 10) `(a ,x)) 0 (sys-exit 9)) - -;; --- ,@xs splices a list in place --------------------------------------- -(define xs (list 2 3 4)) -(if (equal? (list 1 2 3 4 5) `(1 ,@xs 5)) 0 (sys-exit 10)) - -;; --- ,@ at the head of the list ----------------------------------------- -(if (equal? (list 2 3 4 'tail) `(,@xs tail)) 0 (sys-exit 11)) - -;; --- ,@ at the tail of the list ----------------------------------------- -(if (equal? (list 'head 2 3 4) `(head ,@xs)) 0 (sys-exit 12)) - -;; --- ,@'() splices nothing ---------------------------------------------- -(if (equal? (list 'a 'b) `(a ,@'() b)) 0 (sys-exit 13)) - -;; --- Mixed: literals, ,, ,@ in one template ----------------------------- -(if (equal? (list 'h 10 2 3 4 'mid 11 't) - `(h ,x ,@xs mid ,(+ x 1) t)) - 0 (sys-exit 14)) - -;; --- Nested-list template (no ,) is preserved literally ----------------- -(if (equal? '((a b) (c d)) `((a b) (c d))) 0 (sys-exit 15)) - -;; --- , inside a nested list still evaluates at this depth --------------- -(if (equal? (list (list 1 10) 'k) `((1 ,x) k)) 0 (sys-exit 16)) - -;; --- ,@ inside a nested list splices into that nested list -------------- -(if (equal? (list (list 1 2 3 4) 'k) `((1 ,@xs) k)) 0 (sys-exit 17)) - -;; --- (quasiquote x) prefix form parses identically to `x ---------------- -(if (equal? (list 1 10 3) (quasiquote (1 (unquote x) 3))) 0 (sys-exit 18)) - -(sys-exit 0) diff --git a/tests/scheme1/114-define-values.expected-exit b/tests/scheme1/114-define-values.expected-exit @@ -1 +0,0 @@ -0 diff --git a/tests/scheme1/114-define-values.scm b/tests/scheme1/114-define-values.scm @@ -1,23 +0,0 @@ -; define-values: bind multiple top-level names from a values producer. - -(define-values (a b c) (values 1 2 3)) -(if (= 6 (+ a b c)) 0 (sys-exit 1)) - -; Single-name formals work with non-values value-expr. -(define-values (x) 42) -(if (= 42 x) 0 (sys-exit 2)) - -; Variadic formal collects all yielded values. -(define-values rest (values 10 20 30)) -(if (equal? (list 10 20 30) rest) 0 (sys-exit 3)) - -; Dotted formal: head + tail. -(define-values (h . t) (values 1 2 3)) -(if (= 1 h) 0 (sys-exit 4)) -(if (equal? (list 2 3) t) 0 (sys-exit 5)) - -; Subsequent define-values reassigns globally. -(define-values (a b c) (values 100 200 300)) -(if (= 600 (+ a b c)) 0 (sys-exit 6)) - -(sys-exit 0)