Machine-readable SX semantics reference with 35 evaluation rules
covering literals, lookup, special forms, definitions, higher-order
forms, scopes, continuations, and reactive primitives.
New sx_explain MCP tool: query by form name ("let", "map") or
category ("special-form", "higher-order") to get pattern, rule,
effects, and examples.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
274 lines
11 KiB
Plaintext
274 lines
11 KiB
Plaintext
;; Evaluation rules — machine-readable SX semantics reference.
|
|
;;
|
|
;; Each rule describes one dispatch case in the CEK evaluator.
|
|
;; Rules are data — queried by tools (sx_explain), validated against behavior.
|
|
;; Examples are strings to avoid evaluation: "expr → result"
|
|
|
|
(define eval-rules
|
|
(list
|
|
|
|
{:name "number" :category "literal"
|
|
:pattern "42"
|
|
:rule "Numbers evaluate to themselves."
|
|
:effects ()
|
|
:examples "42 → 42, -3.14 → -3.14"}
|
|
|
|
{:name "string" :category "literal"
|
|
:pattern "\"hello\""
|
|
:rule "Strings evaluate to themselves."
|
|
:effects ()
|
|
:examples "\"hello\" → \"hello\""}
|
|
|
|
{:name "boolean" :category "literal"
|
|
:pattern "true | false"
|
|
:rule "Booleans evaluate to themselves."
|
|
:effects ()
|
|
:examples "true → true, false → false"}
|
|
|
|
{:name "nil" :category "literal"
|
|
:pattern "nil"
|
|
:rule "Nil evaluates to itself. Nil is falsy."
|
|
:effects ()
|
|
:examples "nil → nil"}
|
|
|
|
{:name "keyword" :category "literal"
|
|
:pattern ":name"
|
|
:rule "Keywords evaluate to their string name."
|
|
:effects ()
|
|
:examples ":foo → \"foo\", :class → \"class\""}
|
|
|
|
{:name "dict" :category "literal"
|
|
:pattern "{:key1 val1 :key2 val2 ...}"
|
|
:rule "Create a dictionary. Keys are keywords (evaluated to strings). Values are evaluated."
|
|
:effects ()
|
|
:examples "{:x 1 :y 2} → {\"x\" 1 \"y\" 2}"}
|
|
|
|
{:name "symbol" :category "lookup"
|
|
:pattern "name"
|
|
:rule "Look up in: (1) environment chain, (2) primitives, (3) true/false/nil literals. Error if not found."
|
|
:effects ()
|
|
:examples "+ → <native:+>, undefined → ERROR"}
|
|
|
|
{:name "if" :category "special-form"
|
|
:pattern "(if test then else?)"
|
|
:rule "Evaluate test. If truthy (not false, not nil), evaluate then. Otherwise evaluate else (or nil if absent). Both branches are in tail position."
|
|
:effects ()
|
|
:examples "(if true 1 2) → 1, (if false 1 2) → 2, (if nil 1) → nil"}
|
|
|
|
{:name "when" :category "special-form"
|
|
:pattern "(when test body ...)"
|
|
:rule "Evaluate test. If truthy, evaluate body forms in sequence, return last. If falsy, return nil. Last body form is in tail position."
|
|
:effects ()
|
|
:examples "(when true 1 2 3) → 3, (when false 1) → nil"}
|
|
|
|
{:name "cond" :category "special-form"
|
|
:pattern "(cond test1 expr1 test2 expr2 ... :else default)"
|
|
:rule "Evaluate tests in order. First truthy test: evaluate and return its expr. :else always matches. If no match, return nil."
|
|
:effects ()
|
|
:examples "(cond false 1 true 2) → 2, (cond false 1 :else 3) → 3"}
|
|
|
|
{:name "case" :category "special-form"
|
|
:pattern "(case expr val1 result1 val2 result2 ... :else default)"
|
|
:rule "Evaluate expr once. Compare against each val (by equality). Return the matched result. :else is the fallback."
|
|
:effects ()
|
|
:examples "(case 2 1 \"one\" 2 \"two\") → \"two\""}
|
|
|
|
{:name "and" :category "special-form"
|
|
:pattern "(and expr ...)"
|
|
:rule "Evaluate left to right. Return first falsy value, or last value if all truthy. Short-circuits."
|
|
:effects ()
|
|
:examples "(and 1 2 3) → 3, (and 1 false 3) → false"}
|
|
|
|
{:name "or" :category "special-form"
|
|
:pattern "(or expr ...)"
|
|
:rule "Evaluate left to right. Return first truthy value, or last value if all falsy. Short-circuits."
|
|
:effects ()
|
|
:examples "(or false 2 3) → 2, (or false nil) → nil"}
|
|
|
|
{:name "let" :category "special-form"
|
|
:pattern "(let ((name val) ...) body ...)"
|
|
:rule "Create new scope. Evaluate each val sequentially, bind name. Then evaluate body forms, return last. Values see only earlier bindings. Last body form is in tail position."
|
|
:effects ()
|
|
:examples "(let ((x 1) (y 2)) (+ x y)) → 3"}
|
|
|
|
{:name "letrec" :category "special-form"
|
|
:pattern "(letrec ((name val) ...) body ...)"
|
|
:rule "Like let, but all bindings are visible to all vals (mutual recursion). Bindings exist before vals are evaluated."
|
|
:effects ()
|
|
:examples "(letrec ((f (fn (n) (if (= n 0) 1 (* n (f (- n 1))))))) (f 5)) → 120"}
|
|
|
|
{:name "lambda" :category "special-form"
|
|
:pattern "(fn (params ...) body ...) | (lambda (params ...) body ...)"
|
|
:rule "Create a closure capturing the current environment. Parameters support: positional, &key (keyword args), &rest (variadic), (:as type) annotations. Last body form is in tail position."
|
|
:effects ()
|
|
:examples "(fn (x) (+ x 1)) → <lambda>"}
|
|
|
|
{:name "define" :category "special-form"
|
|
:pattern "(define name value) | (define name :effects (e ...) value)"
|
|
:rule "Evaluate value, bind name in the current environment. Optional :effects annotation declares side effects. Returns the value."
|
|
:effects ()
|
|
:examples "(define x 42) → 42"}
|
|
|
|
{:name "set!" :category "special-form"
|
|
:pattern "(set! name value)"
|
|
:rule "Evaluate value, mutate existing binding of name. Walks the scope chain to find the binding. Error if name is not bound."
|
|
:effects "mutation"
|
|
:examples "(let ((x 1)) (set! x 2) x) → 2"}
|
|
|
|
{:name "begin" :category "special-form"
|
|
:pattern "(begin expr ...) | (do expr ...)"
|
|
:rule "Evaluate expressions in sequence, return last. Last form is in tail position."
|
|
:effects ()
|
|
:examples "(begin 1 2 3) → 3"}
|
|
|
|
{:name "quote" :category "special-form"
|
|
:pattern "(quote expr)"
|
|
:rule "Return expr unevaluated."
|
|
:effects ()
|
|
:examples "(quote (+ 1 2)) → (+ 1 2)"}
|
|
|
|
{:name "quasiquote" :category "special-form"
|
|
:pattern "`expr with ,x and ,@xs"
|
|
:rule "Like quote, but (unquote x) evaluates x, and (splice-unquote x) splices a list."
|
|
:effects ()
|
|
:examples "(let ((x 1)) `(a ,x b)) → (a 1 b)"}
|
|
|
|
{:name "thread-first" :category "special-form"
|
|
:pattern "(-> val (fn1 args...) (fn2 args...) ...)"
|
|
:rule "Thread val through forms. Each form receives the previous result as its first argument. (-> x (f a)) becomes (f x a)."
|
|
:effects ()
|
|
:examples "(-> 1 (+ 2) (* 3)) → 9"}
|
|
|
|
{:name "defcomp" :category "definition"
|
|
:pattern "(defcomp ~name (params ...) body ...)"
|
|
:rule "Define a component. Keyword args via &key, variadic via &rest. Body evaluated in merged env (closure + caller-env + params)."
|
|
:effects ()
|
|
:examples "(defcomp ~card (&key title) (div (h2 title)))"}
|
|
|
|
{:name "defisland" :category "definition"
|
|
:pattern "(defisland ~name (params ...) body ...)"
|
|
:rule "Define an island — a component that hydrates on the client. Server renders a placeholder; client evaluates the body with reactive capabilities."
|
|
:effects ()
|
|
:examples "(defisland ~counter () (let ((n (signal 0))) (button :on-click (fn (e) (swap! n inc)) (deref n))))"}
|
|
|
|
{:name "defmacro" :category "definition"
|
|
:pattern "(defmacro name (params ...) body ...)"
|
|
:rule "Define a macro. At call time, args are passed unevaluated. Body produces a new expression which is then evaluated."
|
|
:effects ()
|
|
:examples "(defmacro unless (test body) `(if (not ,test) ,body))"}
|
|
|
|
{:name "map" :category "higher-order"
|
|
:pattern "(map fn coll) | (map coll fn)"
|
|
:rule "Apply fn to each element of coll, return new list. Argument order is flexible."
|
|
:effects ()
|
|
:examples "(map (fn (x) (* x 2)) (list 1 2 3)) → (2 4 6)"}
|
|
|
|
{:name "filter" :category "higher-order"
|
|
:pattern "(filter fn coll) | (filter coll fn)"
|
|
:rule "Return elements of coll where fn returns truthy. Flexible argument order."
|
|
:effects ()
|
|
:examples "(filter (fn (x) (> x 2)) (list 1 2 3 4)) → (3 4)"}
|
|
|
|
{:name "reduce" :category "higher-order"
|
|
:pattern "(reduce fn init coll)"
|
|
:rule "Fold coll from left. Call (fn acc item) for each element, starting with init."
|
|
:effects ()
|
|
:examples "(reduce + 0 (list 1 2 3)) → 6"}
|
|
|
|
{:name "some" :category "higher-order"
|
|
:pattern "(some fn coll)"
|
|
:rule "Return first truthy result of (fn item), or false if none."
|
|
:effects ()
|
|
:examples "(some (fn (x) (> x 2)) (list 1 2 3)) → true"}
|
|
|
|
{:name "every?" :category "higher-order"
|
|
:pattern "(every? fn coll)"
|
|
:rule "Return true if (fn item) is truthy for all items."
|
|
:effects ()
|
|
:examples "(every? (fn (x) (> x 0)) (list 1 2 3)) → true"}
|
|
|
|
{:name "for-each" :category "higher-order"
|
|
:pattern "(for-each fn coll)"
|
|
:rule "Call (fn item) for each element. Returns nil. Used for side effects."
|
|
:effects "mutation"
|
|
:examples "(for-each print (list 1 2 3)) → nil (prints 1, 2, 3)"}
|
|
|
|
{:name "scope" :category "scope"
|
|
:pattern "(scope name body ...)"
|
|
:rule "Create a named dynamic scope. The unified primitive beneath provide, collect!, and spreads."
|
|
:effects "mutation"
|
|
:examples "(scope \"my-scope\" (emit! \"my-scope\" 42) (emitted \"my-scope\")) → (42)"}
|
|
|
|
{:name "provide" :category "scope"
|
|
:pattern "(provide name value body ...)"
|
|
:rule "Make value available to descendants via (context name)."
|
|
:effects "mutation"
|
|
:examples "(provide \"theme\" \"dark\" (context \"theme\")) → \"dark\""}
|
|
|
|
{:name "context" :category "scope"
|
|
:pattern "(context name)"
|
|
:rule "Retrieve the value from the nearest enclosing (provide name value ...)."
|
|
:effects ()
|
|
:examples "(provide \"x\" 42 (context \"x\")) → 42"}
|
|
|
|
{:name "emit!" :category "scope"
|
|
:pattern "(emit! name value)"
|
|
:rule "Emit a value upward into a named scope."
|
|
:effects "mutation"
|
|
:examples "see scope example"}
|
|
|
|
{:name "emitted" :category "scope"
|
|
:pattern "(emitted name)"
|
|
:rule "Collect all values emitted into the named scope."
|
|
:effects ()
|
|
:examples "see scope example"}
|
|
|
|
{:name "reset" :category "continuation"
|
|
:pattern "(reset body ...)"
|
|
:rule "Delimit a continuation. shift inside body captures up to this point."
|
|
:effects ()
|
|
:examples "(reset (+ 1 (shift k (k 10)))) → 11"}
|
|
|
|
{:name "shift" :category "continuation"
|
|
:pattern "(shift k body ...)"
|
|
:rule "Capture the continuation up to the nearest reset. k is a function that resumes the captured computation."
|
|
:effects ()
|
|
:examples "(reset (+ 1 (shift k (k (k 10))))) → 12"}
|
|
|
|
{:name "deref" :category "reactive"
|
|
:pattern "(deref signal)"
|
|
:rule "Read the current value of a reactive signal. In a reactive context, establishes a dependency."
|
|
:effects ()
|
|
:examples "(let ((s (signal 42))) (deref s)) → 42"}
|
|
|
|
{:name "function-call" :category "call"
|
|
:pattern "(f arg1 arg2 ...)"
|
|
:rule "Evaluate f and all args left to right. Then: native → apply, lambda → bind params + TCO, component → parse kwargs + bind params + TCO, macro → expand unevaluated args + re-eval."
|
|
:effects ()
|
|
:examples "(+ 1 2) → 3, (list 1 2 3) → (1 2 3)"}
|
|
))
|
|
|
|
;; Lookup helpers
|
|
|
|
(define find-rule
|
|
(fn (name)
|
|
(some (fn (rule)
|
|
(when (= (get rule "name") name) rule))
|
|
eval-rules)))
|
|
|
|
(define rules-by-category
|
|
(fn (category)
|
|
(filter (fn (rule) (= (get rule "category") category))
|
|
eval-rules)))
|
|
|
|
(define rule-categories
|
|
(fn ()
|
|
(let ((seen (dict)) (result (list)))
|
|
(for-each (fn (rule)
|
|
(let ((cat (get rule "category")))
|
|
(when (not (has-key? seen cat))
|
|
(dict-set! seen cat true)
|
|
(append! result cat))))
|
|
eval-rules)
|
|
result)))
|