Floor Reference
Every form the floor understands, in one place. Entries marked
staging are covered properly in Part II; they are listed here so the
reference is complete. Special forms are recognized by head symbol
and arity — a special-form name applied at the wrong arity falls
through to ordinary application and, since none of these names is a
bound variable, fails as unbound (let is the one exception, with its
own arity error). Following the reference implementation, operator
names are not values: car can head a form but cannot be passed to a
function.
Literals and atoms
Numbers — 42, -7
64-bit signed integers, self-evaluating. Also the boolean encoding:
predicates answer 1/0 and if branches on 0/nonzero.
#t, #f
Read-time aliases for 1 and 0. Not distinct values.
nil
The empty list. Self-evaluating; terminates proper lists; not a valid
if condition; tested with null?.
Symbols — x, two-words?
A bare symbol is a variable reference, resolved at parse time to an
environment position — unbound names are parse errors. A quoted symbol
is data. Symbol characters: alphanumerics and
+ - * / ? ! < > = _ # ..
Comments — ; …
Semicolon to end of line, discarded by the reader.
Binding and control
(lambda f x body)
The unary, self-naming function. f names the closure in its own body
(write _ when unused); x names the argument. Application extends
the closure environment with the closure itself, then the argument —
recursion without a fixpoint operator. Evaluates to #<closure>.
(f a), (f a b …)
Application. Strictly unary; (f a b) is sugar for ((f a) b),
associating left. Applying a non-closure is a type error; applying
code residualizes (staging).
(let x e body)
Evaluate e, bind the result to x, evaluate body. One binder;
nest for more. Also the sequencing idiom: (let _ effect body).
(if c t e)
Branch on a number: 0 false, anything else true. Both branches
required; nil and other non-numbers are type errors. A code condition
reifies both branches and residualizes the if (staging).
Data
(cons a b) / (car p) / (cdr p)
Build a pair; take its first / second component. car/cdr of a
non-pair is a type error. Lists are nil-terminated cons chains,
printed (1 2 3), with (1 . 2) for improper tails.
(quote d), 'd
The datum d as a value, unevaluated. Symbols inside are data, not
variables.
(quasiquote d), `d, (unquote e), ,e
A quote with holes: ,e inside a template splices the value of e.
,@ is read as plain , — no splicing. In loaded files, quasiquote
is expanded at read time into cons/quote source.
(cadr p) / (caddr p) / (cadddr p)
Second / third / fourth element. Surface sugar, rewritten to
car/cdr chains over the whole source tree — including quoted data,
so Pink source never contains them.
Predicates
(number? e) / (symbol? e) / (null? e) / (pair? e)
Kind tests, answering 1 or 0. Never an error.
(eq? a b)
Equality, answering 1 or 0. Structural on numbers, symbols, nil,
pairs, and closures; by identity on cells. Different kinds are
unequal. Code operands residualize the comparison (staging); a code
operand against a non-scalar raises a stage error.
(code? d e) — staging
1 if e’s value is staged code. d is a stage dispatch: when it is
code, the test itself residualizes.
Arithmetic
(+ a b) / (- a b) / (* a b)
Addition, subtraction, multiplication on integers. Binary only; no division. Code operands residualize the operation (staging).
Staging
(lift e) — staging
Reify the value of e as next-stage code: numbers and symbols become
literals, pairs of code become cons nodes, closures η-expand into
staged lambdas (memoized, so a recursive function lifts to one
residual definition). Lifting a cell is an error.
(run b e) — staging
Stage dispatch on b: non-code b compiles e — evaluates it in a
fresh scope, expecting code — and immediately executes the result;
code b residualizes the run.
(lift-ref name v) — staging
Cross-stage persistence: embed the live value of v in residual code
as a reference, not a syntactic copy. With a code first operand,
residualizes.
(evalms env-list e) — staging
Evaluate a code value under an explicit environment given as a list of
values. The reference base.scm’s evalms, exposed as a primitive;
back half of the trans/evalms pipeline the Purple boot uses to
animate source-as-data.
(trans e env) — staging
Translate an s-expression value into a code value, resolving names
positionally against env — a list of symbols, or (name . code)
pairs that splice code in place of a variable. base.scm’s trans as
a primitive.
Effects
(print e)
Write the value; return nil. (log is the value-returning variant.)
(read prompt)
Print the prompt, read one input line, parse it as a datum (a value — no evaluation). End of input and blank lines read as nil.
(read-file 'path)
Parse a source file into a list of forms-as-data, one element per
top-level form, with read-time transforms (cadr sugar, quasiquote)
applied. The floor primitive underneath load and the tower boot.
(log b v) — stage-aware
With non-code b: print v tagged [log], return it. With code b:
residualize the log, persisting a non-code v as a cross-stage
reference.
Errors
(throw v)
Raise the value of v as an error with a first-class payload. Unwinds
to the nearest catch; uncaught, aborts the top-level form.
(catch e)
Evaluate e to a tagged list: (ok value) on success, (error payload) on error. A thrown payload arrives structurally; any other
error arrives as a single symbol holding its report. Written for the
Purple REPL loop; deliberately minimal.
Cells
(cell-new v) / (cell-read c) / (cell-set! c v)
Allocate a mutable cell / read it / overwrite it (returning the new
value). Identity semantics: copies alias, eq? compares identity,
printed #<cell N>. Cells never fold under staging: lift of a cell
errors, staged cell operations residualize.
No surface syntax
One expression form exists only inside generated code, with no way to
write it in source: the cross-stage persisted value (the reference’s
proc), produced when staged log or lift-ref embeds a live value
in residual code. Residual programs are otherwise ordinary source —
which is what lets run execute them and Part III print them.