boot2

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

scheme1

Minimal Scheme subset implemented by scheme1/scheme1.P1pp. It implements the micro+boot2 profile defined in R7RS-micro.md. The interpreter reads s-expressions from argv[1], evaluates them top-to-bottom in a single global env, and exits.

tests/boot-run-scheme1.sh invokes scheme1 with prelude.scm catted in front of the user file. The prelude (scheme1/prelude.scm) defines the portable micro helpers first, followed by a visibly labelled boot2 compiler/process/file-I/O layer.

Lexical syntax

Types

The runtime knows exactly:

Type Notes
boolean #t, #f
integer tagged exact integer; 61 value bits on P1-64, 29 on P1-32
character byte-repertoire character; disjoint from integer
symbol globally interned; eq?-comparable
string mutable, explicit-length character sequence (HDR.STRING)
bytevector mutable u8 sequence (HDR.BV), disjoint from string
pair cons cell
empty list '(), disjoint from pair
procedure closure or primitive
record via define-record-type
hash table private boot2 mutable map; outside the micro layer
eof-object singleton; bound at top level as eof; also returned on EOF reads
unspecified singleton; result of set!, define, (if #f x), etc.

Multiple-values packs flow through values / call-with-values / let-values / let*-values; they are not intended to be observed directly.

Special forms

Top-level binding:

Procedures and binding:

Conditionals and sequencing:

Quote, records, matching:

Primitives

The runtime built-ins — registered at startup from prim_table in scheme1.P1pp. The prelude builds the wider R7RS surface on top of these.

Equality / predicates eq?, equal?, not, null?, pair?, boolean?, integer?, char?, symbol?, string?, bytevector?, bytes?, procedure?, zero?, eof?. bytes? is the explicit boot2 string/bytevector bridge.

Pairs cons, car, cdr, set-car!, set-cdr!, length, list-ref, assq, assoc, reverse. assq compares alist keys by eq?; assoc compares keys by equal?; both return the matching alist pair or #f. reverse returns a fresh reversed list.

Integers (tagged exact range; overflow and divide-by-zero fail explicitly) + - *, quotient, remainder, =, <, >, bit-and, bit-or, bit-xor, bit-not, arithmetic-shift. Arities: + * bit-and bit-or bit-xor accept 0+ args (identities 0 1 -1 0 0); - accepts 1+ ((- x) is unary negate); = < > accept 2+ and chain pairwise. quotient / remainder / arithmetic-shift are binary; bit-not is unary. quotient truncates toward zero; remainder has the sign of the dividend.

Bytevectors / strings make-bytevector, bytevector-length, bytevector-u8-ref, bytevector-u8-set!, bytevector-copy (3-arg src start end → fresh bv), bytevector-copy! (dst dst-start src src-start src-end), bytevector-append (variadic), bytevector=?, bytes=?, make-string, string-length, string-ref, string-set!. The bytevector operations named by R7RS-micro's boot2 bridge accept strings as byte sources/destinations but still produce bytevectors where applicable. Copies have memmove semantics for overlapping ranges.

Private compiler hash tables %make-hash-table, %hash-ref, %hash-set!, %hash-delete!, %hash-size. These GC-traced open-addressed tables compare string/bytevector keys by bytes and every other key by eq?. They are a micro+boot2 facility used for compiler-wide maps; see R7RS-micro.md for the key-mutation and missing-value contracts.

Symbols / numbers as text string->symbol, symbol->string, number->string (decimal by default; lowercase hex when the optional radix arg is 16, with a leading - for negatives; other radices are rejected), string->number (decimal by default; hex when radix is 16, accepting upper- or lowercase digits and an optional leading +/-; returns #f on parse failure).

I/O and error display, write, format, error. write emits reader-compatible characters, strings, and bytevectors; string escaping preserves embedded nulls. format understands ~a (display), ~s (write), ~d (decimal fixnum), ~x (lowercase hex fixnum, signed: leading - for negatives), ~% (newline), ~~ (literal tilde); unknown directives pass through verbatim. error writes scheme1: error: <msg> <irritants…> to stderr and exits with status 1.

EOF eof (the singleton, bound at startup), eof?.

Multiple values values, call-with-values. (values x) is identical to x in single-value context; 0 or 2+ args produce an MV-pack consumable by call-with-values / let-values / let*-values.

Apply apply. Tail calls are guaranteed proper.

Syscalls (Linux). Each returns (#t . val) on success or (#f . errno) on failure. sys-read fd buf offset count, sys-write fd buf offset count, sys-close fd, sys-openat dirfd path-bv flags mode, sys-clone (fork-style, no args), sys-execve path-bv argv-list, sys-waitid idtype id infop options, sys-argv (no args; returns the process's argv as a list of bvs), sys-exit code (does not return).

Garbage collection heap-usage, collect-garbage, target-word-bytes, target-word-bits. heap-usage reports currently allocated managed bytes, including the 16-byte header on each allocation. collect-garbage performs a synchronous collection and returns unspecified. target-word-bytes and target-word-bits return 4/32 on P1-32 and 8/64 on P1-64 so Scheme-hosted tools can select their target data model without inspecting the host platform. Collection also runs automatically when an allocation cannot be satisfied without reclaiming garbage. See SCHEME1-GC.md for the heap and exact-rooting design.

Error semantics

error is the only structured error path. Everything else — (car '()), out-of-range bytevector-u8-ref, (quotient 1 0), mutating immutable state, integer overflow, or unknown-form pmatch fallthrough — is primitive failure: the runtime aborts with a short message on stderr. It never silently wraps an exact Scheme result.

There is no raise / guard / handlers, no call/cc, no exceptions. Wrap-and-return through (ok . val) pairs (the syscall convention) when failure needs to be observable.

Prelude surface

scheme1/prelude.scm is bundled in front of every user program by tests/boot-run-scheme1.sh. It adds: