Wraps all core .sx files in R7RS define-library with explicit export lists, plus (import ...) at end for backward-compatible global re-export. Libraries registered: (sx bytecode) — 83 opcode constants (sx render) — 15 tag registries + render helpers (sx signals) — 23 reactive signal primitives (sx r7rs) — 21 R7RS aliases (sx compiler) — 42 compiler functions (sx vm) — 32 VM functions (sx freeze) — 9 freeze/thaw functions (sx content) — 6 content store functions (sx callcc) — 1 call/cc wrapper (sx highlight) — 13 syntax highlighting functions (sx stdlib) — 47 stdlib functions (sx swap) — 13 swap algebra functions (sx render-trace) — 8 render trace functions (sx harness) — 21 test harness functions (sx canonical) — 12 canonical serialization functions (web adapter-html) — 13 HTML renderer functions (web adapter-sx) — 13 SX wire format functions (web engine) — 33 hypermedia engine functions (web request-handler) — 4 request handling functions (web page-helpers) — 12 page helper functions (web router) — 36 routing functions (web deps) — 19 dependency analysis functions (web orchestration) — 59 page orchestration functions Key changes: - define-library now inherits parent env (env-extend env instead of env-extend make-env) so library bodies can access platform primitives - sx_server.ml: added resolve_library_path + load_library_file for import resolution (maps library specs to file paths) - cek_run_with_io: handles "import" locally instead of sending to Python bridge 2608/2608 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3196 lines
104 KiB
Plaintext
3196 lines
104 KiB
Plaintext
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 1: CEK State
|
|
;;
|
|
;; The CEK machine state is a 5-tuple: {control, env, kont, value, phase}.
|
|
;; In "eval" phase, control holds the expression to evaluate.
|
|
;; In "continue" phase, value holds the result and kont is unwound.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define make-cek-state (fn (control env kont) {:control control :env env :kont kont :value nil :phase "eval"}))
|
|
|
|
(define make-cek-value (fn (value env kont) {:control nil :env env :kont kont :value value :phase "continue"}))
|
|
|
|
(define make-cek-suspended (fn (request env kont) {:env env :kont kont :phase "io-suspended" :request request}))
|
|
|
|
(define
|
|
cek-terminal?
|
|
(fn
|
|
(state)
|
|
(and (= (get state "phase") "continue") (empty? (get state "kont")))))
|
|
|
|
(define cek-suspended? (fn (state) (= (get state "phase") "io-suspended")))
|
|
|
|
(define cek-control (fn (s) (get s "control")))
|
|
|
|
(define cek-env (fn (s) (get s "env")))
|
|
|
|
(define cek-kont (fn (s) (get s "kont")))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 2: Continuation Frames
|
|
;;
|
|
;; Each frame type represents a pending computation — what to do
|
|
;; when the current sub-expression finishes evaluating. The kont
|
|
;; (continuation) is a list of frames, forming a reified call stack.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define cek-phase (fn (s) (get s "phase")))
|
|
|
|
(define cek-io-request (fn (s) (get s "request")))
|
|
|
|
(define cek-value (fn (s) (get s "value")))
|
|
|
|
(define make-if-frame (fn (then-expr else-expr env) {:else else-expr :env env :type "if" :then then-expr}))
|
|
|
|
(define make-when-frame (fn (body-exprs env) {:body body-exprs :env env :type "when"}))
|
|
|
|
(define make-begin-frame (fn (remaining env) {:env env :type "begin" :remaining remaining}))
|
|
|
|
;; Function call frames: accumulate evaluated args, then dispatch
|
|
(define make-let-frame (fn (name remaining body local) {:body body :env local :type "let" :remaining remaining :name name}))
|
|
|
|
(define make-define-frame (fn (name env has-effects effect-list) {:env env :effect-list effect-list :has-effects has-effects :type "define" :name name}))
|
|
|
|
(define make-set-frame (fn (name env) {:env env :type "set" :name name}))
|
|
|
|
(define
|
|
make-arg-frame
|
|
(fn (f evaled remaining env raw-args head-name) {:env env :head-name (or head-name nil) :evaled evaled :type "arg" :f f :remaining remaining :raw-args raw-args}))
|
|
|
|
(define make-call-frame (fn (f args env) {:args args :env env :type "call" :f f}))
|
|
|
|
(define make-cond-frame (fn (remaining env scheme?) {:scheme scheme? :env env :type "cond" :remaining remaining}))
|
|
|
|
(define make-cond-arrow-frame (fn (test-value env) {:env env :match-val test-value :type "cond-arrow"}))
|
|
|
|
;; Higher-order iteration frames
|
|
(define make-case-frame (fn (match-val remaining env) {:match-val match-val :env env :type "case" :remaining remaining}))
|
|
|
|
(define make-thread-frame (fn (remaining env) {:env env :type "thread" :remaining remaining}))
|
|
|
|
(define
|
|
thread-insert-arg
|
|
(fn
|
|
(form value fenv)
|
|
(if
|
|
(= (type-of form) "list")
|
|
(eval-expr
|
|
(cons (first form) (cons (list (quote quote) value) (rest form)))
|
|
fenv)
|
|
(eval-expr (list form (list (quote quote) value)) fenv))))
|
|
|
|
(define make-map-frame (fn (f remaining results env) {:indexed false :env env :results results :type "map" :f f :remaining remaining}))
|
|
|
|
(define make-map-indexed-frame (fn (f remaining results env) {:indexed true :env env :results results :type "map" :f f :remaining remaining}))
|
|
|
|
(define make-multi-map-frame (fn (f remaining-lists results env) {:env env :results results :type "multi-map" :f f :remaining remaining-lists}))
|
|
|
|
(define
|
|
make-filter-frame
|
|
(fn (f remaining results current-item env) {:current-item current-item :env env :results results :type "filter" :f f :remaining remaining}))
|
|
|
|
(define make-reduce-frame (fn (f remaining env) {:env env :type "reduce" :f f :remaining remaining}))
|
|
|
|
;; Scope/provide/context — downward data passing without env threading
|
|
(define make-for-each-frame (fn (f remaining env) {:env env :type "for-each" :f f :remaining remaining}))
|
|
|
|
(define make-some-frame (fn (f remaining env) {:env env :type "some" :f f :remaining remaining}))
|
|
|
|
(define make-every-frame (fn (f remaining env) {:env env :type "every" :f f :remaining remaining}))
|
|
|
|
;; Delimited continuations (shift/reset)
|
|
(define make-scope-frame (fn (name remaining env) {:env env :type "scope" :remaining remaining :name name}))
|
|
|
|
(define make-provide-frame (fn (name value remaining env) {:env env :value value :type "provide" :remaining remaining :name name}))
|
|
|
|
(define make-scope-acc-frame (fn (name value remaining env) {:env env :value (or value nil) :type "scope-acc" :remaining remaining :emitted (list) :name name}))
|
|
|
|
(define make-reset-frame (fn (env) {:env env :type "reset"}))
|
|
|
|
;; Dynamic wind + reactive signals
|
|
(define make-dict-frame (fn (remaining results env) {:env env :results results :type "dict" :remaining remaining}))
|
|
|
|
(define make-and-frame (fn (remaining env) {:env env :type "and" :remaining remaining}))
|
|
|
|
;; Undelimited continuations (call/cc)
|
|
(define make-or-frame (fn (remaining env) {:env env :type "or" :remaining remaining}))
|
|
|
|
(define
|
|
make-dynamic-wind-frame
|
|
(fn (phase body-thunk after-thunk env) {:env env :phase phase :after-thunk after-thunk :type "dynamic-wind" :body-thunk body-thunk}))
|
|
|
|
;; HO setup: staged argument evaluation for map/filter/etc.
|
|
;; Evaluates args one at a time, then dispatches to the correct
|
|
;; HO frame (map, filter, reduce) once all args are ready.
|
|
(define
|
|
make-reactive-reset-frame
|
|
(fn (env update-fn first-render?) {:first-render first-render? :update-fn update-fn :env env :type "reactive-reset"}))
|
|
|
|
(define make-callcc-frame (fn (env) {:env env :type "callcc"}))
|
|
|
|
(define make-deref-frame (fn (env) {:env env :type "deref"}))
|
|
|
|
;; Condition system frames (handler-bind, restart-case, signal)
|
|
(define
|
|
make-ho-setup-frame
|
|
(fn (ho-type remaining-args evaled-args env) {:ho-type ho-type :env env :evaled evaled-args :type "ho-setup" :remaining remaining-args}))
|
|
|
|
(define make-comp-trace-frame (fn (name file) {:env file :type "comp-trace" :name name}))
|
|
|
|
(define
|
|
kont-collect-comp-trace
|
|
(fn
|
|
(kont)
|
|
(if
|
|
(empty? kont)
|
|
(list)
|
|
(let
|
|
((frame (first kont)))
|
|
(if
|
|
(= (frame-type frame) "comp-trace")
|
|
(cons {:file (get frame "file") :name (get frame "name")} (kont-collect-comp-trace (rest kont)))
|
|
(kont-collect-comp-trace (rest kont)))))))
|
|
|
|
;; R7RS exception frames (raise, guard)
|
|
(define make-handler-frame (fn (handlers remaining env) {:env env :type "handler" :f handlers :remaining remaining}))
|
|
|
|
(define make-restart-frame (fn (restarts remaining env) {:env env :type "restart" :f restarts :remaining remaining}))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 3: Continuation Stack Operations
|
|
;;
|
|
;; Searching and manipulating the kont list — finding handlers,
|
|
;; restarts, scope accumulators, and capturing delimited slices.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define make-signal-return-frame (fn (env saved-kont) {:env env :type "signal-return" :f saved-kont}))
|
|
|
|
(define make-raise-eval-frame (fn (env continuable?) {:scheme continuable? :env env :type "raise-eval"}))
|
|
|
|
(define make-raise-guard-frame (fn (env saved-kont) {:env env :type "raise-guard" :remaining saved-kont}))
|
|
|
|
(define make-perform-frame (fn (env) {:env env :type "perform"}))
|
|
|
|
;; Basic kont operations
|
|
(define make-vm-resume-frame (fn (resume-fn env) {:env env :type "vm-resume" :f resume-fn}))
|
|
|
|
(define make-import-frame (fn (import-set remaining-sets env) {:args import-set :env env :type "import" :remaining remaining-sets}))
|
|
|
|
(define
|
|
find-matching-handler
|
|
(fn
|
|
(handlers condition)
|
|
(if
|
|
(empty? handlers)
|
|
nil
|
|
(let
|
|
((pair (first handlers)))
|
|
(let
|
|
((pred (first pair)) (handler-fn (nth pair 1)))
|
|
(if
|
|
(cek-call pred (list condition))
|
|
handler-fn
|
|
(find-matching-handler (rest handlers) condition)))))))
|
|
|
|
(define
|
|
kont-find-handler
|
|
(fn
|
|
(kont condition)
|
|
(if
|
|
(empty? kont)
|
|
nil
|
|
(let
|
|
((frame (first kont)))
|
|
(if
|
|
(= (frame-type frame) "handler")
|
|
(let
|
|
((match (find-matching-handler (get frame "f") condition)))
|
|
(if
|
|
(nil? match)
|
|
(kont-find-handler (rest kont) condition)
|
|
match))
|
|
(kont-find-handler (rest kont) condition))))))
|
|
|
|
(define
|
|
find-named-restart
|
|
(fn
|
|
(restarts name)
|
|
(if
|
|
(empty? restarts)
|
|
nil
|
|
(let
|
|
((entry (first restarts)))
|
|
(if
|
|
(= (first entry) name)
|
|
entry
|
|
(find-named-restart (rest restarts) name))))))
|
|
|
|
;; Capture frames up to a reset boundary — used by shift
|
|
(define
|
|
kont-find-restart
|
|
(fn
|
|
(kont name)
|
|
(if
|
|
(empty? kont)
|
|
nil
|
|
(let
|
|
((frame (first kont)))
|
|
(if
|
|
(= (frame-type frame) "restart")
|
|
(let
|
|
((match (find-named-restart (get frame "f") name)))
|
|
(if
|
|
(nil? match)
|
|
(kont-find-restart (rest kont) name)
|
|
(list match frame (rest kont))))
|
|
(kont-find-restart (rest kont) name))))))
|
|
|
|
(define frame-type (fn (f) (get f "type")))
|
|
|
|
(define kont-push (fn (frame kont) (cons frame kont)))
|
|
|
|
(define kont-top (fn (kont) (first kont)))
|
|
|
|
(define kont-pop (fn (kont) (rest kont)))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 4: Extension Points & Mutable State
|
|
;;
|
|
;; Custom special forms registry, render hooks, strict mode.
|
|
;; Mutable globals use set! — the transpiler emits OCaml refs.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define kont-empty? (fn (kont) (empty? kont)))
|
|
|
|
(define
|
|
kont-capture-to-reset
|
|
(fn
|
|
(kont)
|
|
(define
|
|
scan
|
|
(fn
|
|
(k captured)
|
|
(if
|
|
(empty? k)
|
|
(error "shift without enclosing reset")
|
|
(let
|
|
((frame (first k)))
|
|
(if
|
|
(or
|
|
(= (frame-type frame) "reset")
|
|
(= (frame-type frame) "reactive-reset"))
|
|
(list captured (rest k))
|
|
(scan (rest k) (append captured (list frame))))))))
|
|
(scan kont (list))))
|
|
|
|
(define
|
|
kont-find-provide
|
|
(fn
|
|
(kont name)
|
|
(if
|
|
(empty? kont)
|
|
nil
|
|
(let
|
|
((frame (first kont)))
|
|
(if
|
|
(and
|
|
(= (frame-type frame) "provide")
|
|
(= (get frame "name") name))
|
|
frame
|
|
(kont-find-provide (rest kont) name))))))
|
|
|
|
(define
|
|
kont-find-scope-acc
|
|
(fn
|
|
(kont name)
|
|
(if
|
|
(empty? kont)
|
|
nil
|
|
(let
|
|
((frame (first kont)))
|
|
(if
|
|
(and
|
|
(= (frame-type frame) "scope-acc")
|
|
(= (get frame "name") name))
|
|
frame
|
|
(kont-find-scope-acc (rest kont) name))))))
|
|
|
|
(define
|
|
has-reactive-reset-frame?
|
|
(fn
|
|
(kont)
|
|
(if
|
|
(empty? kont)
|
|
false
|
|
(if
|
|
(= (frame-type (first kont)) "reactive-reset")
|
|
true
|
|
(has-reactive-reset-frame? (rest kont))))))
|
|
|
|
(define
|
|
kont-capture-to-reactive-reset
|
|
(fn
|
|
(kont)
|
|
(define
|
|
scan
|
|
(fn
|
|
(k captured)
|
|
(if
|
|
(empty? k)
|
|
(error "reactive deref without enclosing reactive-reset")
|
|
(let
|
|
((frame (first k)))
|
|
(if
|
|
(= (frame-type frame) "reactive-reset")
|
|
(list captured frame (rest k))
|
|
(scan (rest k) (append captured (list frame))))))))
|
|
(scan kont (list))))
|
|
|
|
(define *custom-special-forms* (dict))
|
|
|
|
(define
|
|
register-special-form!
|
|
(fn
|
|
((name :as string) handler)
|
|
(dict-set! *custom-special-forms* name handler)))
|
|
|
|
(define *render-check* nil)
|
|
|
|
(define *render-fn* nil)
|
|
|
|
(define *library-registry* (dict))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 5: Evaluation Utilities
|
|
;;
|
|
;; Forward-declared eval-expr, lambda/component calling, keyword
|
|
;; arg parsing, special form constructors (lambda, defcomp,
|
|
;; defmacro, quasiquote), and macro expansion.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Forward declaration — redefined at end of file as CEK entry point
|
|
(define
|
|
library-name-key
|
|
(fn
|
|
(spec)
|
|
(join
|
|
"."
|
|
(map (fn (s) (if (symbol? s) (symbol-name s) (str s))) spec))))
|
|
|
|
;; Shared param binding for lambda/component calls.
|
|
;; Handles &rest collection — used by both call-lambda and continue-with-call.
|
|
(define
|
|
library-loaded?
|
|
(fn (spec) (has-key? *library-registry* (library-name-key spec))))
|
|
|
|
(define
|
|
library-exports
|
|
(fn
|
|
(spec)
|
|
(get (get *library-registry* (library-name-key spec)) "exports")))
|
|
|
|
;; Component calls: parse keyword args, bind params, TCO thunk
|
|
(define
|
|
register-library
|
|
(fn
|
|
(spec exports)
|
|
(dict-set! *library-registry* (library-name-key spec) {:exports exports})))
|
|
|
|
(define *io-registry* (dict))
|
|
|
|
;; Cond/case helpers
|
|
(define io-register! (fn (name spec) (dict-set! *io-registry* name spec)))
|
|
|
|
(define io-registered? (fn (name) (has-key? *io-registry* name)))
|
|
|
|
;; Special form constructors — build state for CEK evaluation
|
|
(define io-lookup (fn (name) (get *io-registry* name)))
|
|
|
|
(define io-names (fn () (keys *io-registry*)))
|
|
|
|
(define
|
|
step-sf-io
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name (first args)) (io-args (rest args)))
|
|
(when
|
|
(not (io-registered? name))
|
|
(error
|
|
(str "io: unknown operation '" name "' — not in *io-registry*")))
|
|
(make-cek-state (cons (quote perform) (list {:args io-args :op name})) env kont))))
|
|
|
|
(define
|
|
trampoline
|
|
(fn
|
|
((val :as any))
|
|
(let
|
|
((result val))
|
|
(do
|
|
(if
|
|
(thunk? result)
|
|
(trampoline (eval-expr (thunk-expr result) (thunk-env result)))
|
|
result)))))
|
|
|
|
(define *strict* false)
|
|
|
|
(define set-strict! (fn (val) (set! *strict* val)))
|
|
|
|
(define *prim-param-types* nil)
|
|
|
|
(define set-prim-param-types! (fn (types) (set! *prim-param-types* types)))
|
|
|
|
;; Quasiquote expansion
|
|
(define
|
|
value-matches-type?
|
|
(fn
|
|
(val expected-type)
|
|
(match
|
|
expected-type
|
|
("any" true)
|
|
("number" (number? val))
|
|
("string" (string? val))
|
|
("boolean" (boolean? val))
|
|
("nil" (nil? val))
|
|
("list" (list? val))
|
|
("dict" (dict? val))
|
|
("lambda" (lambda? val))
|
|
("symbol" (= (type-of val) "symbol"))
|
|
("keyword" (= (type-of val) "keyword"))
|
|
(_
|
|
(if
|
|
(and (string? expected-type) (ends-with? expected-type "?"))
|
|
(or
|
|
(nil? val)
|
|
(value-matches-type?
|
|
val
|
|
(slice expected-type 0 (- (string-length expected-type) 1))))
|
|
true)))))
|
|
|
|
(define
|
|
strict-check-args
|
|
(fn
|
|
(name args)
|
|
(when
|
|
(and *strict* *prim-param-types*)
|
|
(let
|
|
((spec (get *prim-param-types* name)))
|
|
(when
|
|
spec
|
|
(let
|
|
((positional (get spec "positional"))
|
|
(rest-type (get spec "rest-type")))
|
|
(when
|
|
positional
|
|
(for-each
|
|
(fn
|
|
(pair)
|
|
(let
|
|
((idx (first pair))
|
|
(param (nth pair 1))
|
|
(p-name (first param))
|
|
(p-type (nth param 1)))
|
|
(when
|
|
(< idx (len args))
|
|
(let
|
|
((val (nth args idx)))
|
|
(when
|
|
(not (value-matches-type? val p-type))
|
|
(error
|
|
(str
|
|
"Type error: "
|
|
name
|
|
" expected "
|
|
p-type
|
|
" for param "
|
|
p-name
|
|
", got "
|
|
(type-of val)
|
|
" ("
|
|
(str val)
|
|
")")))))))
|
|
(map-indexed (fn (i p) (list i p)) positional)))
|
|
(when
|
|
(and rest-type (> (len args) (len (or positional (list)))))
|
|
(for-each
|
|
(fn
|
|
(pair)
|
|
(let
|
|
((idx (first pair)) (val (nth pair 1)))
|
|
(when
|
|
(not (value-matches-type? val rest-type))
|
|
(error
|
|
(str
|
|
"Type error: "
|
|
name
|
|
" expected "
|
|
rest-type
|
|
" for rest arg "
|
|
idx
|
|
", got "
|
|
(type-of val)
|
|
" ("
|
|
(str val)
|
|
")")))))
|
|
(map-indexed
|
|
(fn (i v) (list i v))
|
|
(slice args (len (or positional (list)))))))))))))
|
|
|
|
(define eval-expr (fn (expr (env :as dict)) nil))
|
|
|
|
(define
|
|
bind-lambda-params
|
|
(fn
|
|
(params args local)
|
|
(let
|
|
((rest-idx (index-of params "&rest")))
|
|
(if
|
|
(and (number? rest-idx) (< rest-idx (len params)))
|
|
(let
|
|
((positional (slice params 0 rest-idx))
|
|
(rest-name (nth params (+ rest-idx 1))))
|
|
(do
|
|
(for-each-indexed
|
|
(fn
|
|
(i p)
|
|
(env-bind! local p (if (< i (len args)) (nth args i) nil)))
|
|
positional)
|
|
(env-bind!
|
|
local
|
|
rest-name
|
|
(if (> (len args) rest-idx) (slice args rest-idx) (quote ())))
|
|
true))
|
|
false))))
|
|
|
|
(define
|
|
call-lambda
|
|
(fn
|
|
((f :as lambda) (args :as list) (caller-env :as dict))
|
|
(let
|
|
((params (lambda-params f))
|
|
(local (env-merge (lambda-closure f) caller-env)))
|
|
(when
|
|
(not (bind-lambda-params params args local))
|
|
(when
|
|
(> (len args) (len params))
|
|
(error
|
|
(str
|
|
(or (lambda-name f) "lambda")
|
|
" expects "
|
|
(len params)
|
|
" args, got "
|
|
(len args))))
|
|
(for-each
|
|
(fn (pair) (env-bind! local (first pair) (nth pair 1)))
|
|
(zip params args))
|
|
(for-each
|
|
(fn (p) (env-bind! local p nil))
|
|
(slice params (len args))))
|
|
(make-thunk (lambda-body f) local))))
|
|
|
|
(define
|
|
call-component
|
|
(fn
|
|
((comp :as component) (raw-args :as list) (env :as dict))
|
|
(let
|
|
((parsed (parse-keyword-args raw-args env))
|
|
(kwargs (first parsed))
|
|
(children (nth parsed 1))
|
|
(local (env-merge (component-closure comp) env)))
|
|
(for-each
|
|
(fn (p) (env-bind! local p (or (dict-get kwargs p) nil)))
|
|
(component-params comp))
|
|
(when
|
|
(component-has-children? comp)
|
|
(env-bind! local "children" children))
|
|
(make-thunk (component-body comp) local))))
|
|
|
|
;; Macro expansion — expand then re-evaluate the result
|
|
(define
|
|
parse-keyword-args
|
|
(fn
|
|
((raw-args :as list) (env :as dict))
|
|
(let
|
|
((kwargs (dict)) (children (list)) (i 0))
|
|
(reduce
|
|
(fn
|
|
(state arg)
|
|
(let
|
|
((idx (get state "i")) (skip (get state "skip")))
|
|
(if
|
|
skip
|
|
(assoc state "skip" false "i" (inc idx))
|
|
(if
|
|
(and
|
|
(= (type-of arg) "keyword")
|
|
(< (inc idx) (len raw-args)))
|
|
(do
|
|
(dict-set!
|
|
kwargs
|
|
(keyword-name arg)
|
|
(trampoline (eval-expr (nth raw-args (inc idx)) env)))
|
|
(assoc state "skip" true "i" (inc idx)))
|
|
(do
|
|
(append! children (trampoline (eval-expr arg env)))
|
|
(assoc state "i" (inc idx)))))))
|
|
(dict "i" 0 "skip" false)
|
|
raw-args)
|
|
(list kwargs children))))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 6: CEK Machine Core
|
|
;;
|
|
;; cek-run: trampoline loop — steps until terminal.
|
|
;; cek-step: single step — dispatches on phase (eval vs continue).
|
|
;; step-eval: evaluates control expression, pushes frames.
|
|
;; step-continue: pops a frame, processes result.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define
|
|
cond-scheme?
|
|
(fn
|
|
((clauses :as list))
|
|
(every?
|
|
(fn
|
|
(c)
|
|
(and
|
|
(= (type-of c) "list")
|
|
(or
|
|
(= (len c) 2)
|
|
(and
|
|
(= (len c) 3)
|
|
(= (type-of (nth c 1)) "symbol")
|
|
(= (symbol-name (nth c 1)) "=>")))))
|
|
clauses)))
|
|
|
|
(define
|
|
is-else-clause?
|
|
(fn
|
|
(test)
|
|
(or
|
|
(and (= (type-of test) "keyword") (= (keyword-name test) "else"))
|
|
(and
|
|
(= (type-of test) "symbol")
|
|
(or (= (symbol-name test) "else") (= (symbol-name test) ":else"))))))
|
|
|
|
(define
|
|
sf-named-let
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((loop-name (symbol-name (first args)))
|
|
(bindings (nth args 1))
|
|
(body (slice args 2))
|
|
(params (list))
|
|
(inits (list)))
|
|
(if
|
|
(and
|
|
(= (type-of (first bindings)) "list")
|
|
(= (len (first bindings)) 2))
|
|
(for-each
|
|
(fn
|
|
(binding)
|
|
(append!
|
|
params
|
|
(if
|
|
(= (type-of (first binding)) "symbol")
|
|
(symbol-name (first binding))
|
|
(first binding)))
|
|
(append! inits (nth binding 1)))
|
|
bindings)
|
|
(reduce
|
|
(fn
|
|
(acc pair-idx)
|
|
(do
|
|
(append!
|
|
params
|
|
(if
|
|
(= (type-of (nth bindings (* pair-idx 2))) "symbol")
|
|
(symbol-name (nth bindings (* pair-idx 2)))
|
|
(nth bindings (* pair-idx 2))))
|
|
(append! inits (nth bindings (inc (* pair-idx 2))))))
|
|
nil
|
|
(range 0 (/ (len bindings) 2))))
|
|
(let
|
|
((loop-body (if (= (len body) 1) (first body) (cons (make-symbol "begin") body)))
|
|
(loop-fn (make-lambda params loop-body env)))
|
|
(set-lambda-name! loop-fn loop-name)
|
|
(env-bind! (lambda-closure loop-fn) loop-name loop-fn)
|
|
(let
|
|
((init-vals (map (fn (e) (trampoline (eval-expr e env))) inits)))
|
|
(cek-call loop-fn init-vals))))))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 7: Special Form Step Functions
|
|
;;
|
|
;; Each step-sf-* handles one special form in the eval phase.
|
|
;; They push frames and return new CEK states — never recurse.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define
|
|
sf-lambda
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((params-expr (first args))
|
|
(body-exprs (rest args))
|
|
(body
|
|
(if
|
|
(= (len body-exprs) 1)
|
|
(first body-exprs)
|
|
(cons (make-symbol "begin") body-exprs)))
|
|
(param-names
|
|
(map
|
|
(fn
|
|
(p)
|
|
(cond
|
|
(= (type-of p) "symbol")
|
|
(symbol-name p)
|
|
(and
|
|
(= (type-of p) "list")
|
|
(= (len p) 3)
|
|
(= (type-of (nth p 1)) "keyword")
|
|
(= (keyword-name (nth p 1)) "as"))
|
|
(symbol-name (first p))
|
|
:else p))
|
|
params-expr)))
|
|
(make-lambda param-names body env))))
|
|
|
|
;; R7RS guard: desugars to call/cc + handler-bind with sentinel re-raise
|
|
(define
|
|
sf-defcomp
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((name-sym (first args))
|
|
(params-raw (nth args 1))
|
|
(body (last args))
|
|
(comp-name (strip-prefix (symbol-name name-sym) "~"))
|
|
(parsed (parse-comp-params params-raw))
|
|
(params (first parsed))
|
|
(has-children (nth parsed 1))
|
|
(param-types (nth parsed 2))
|
|
(affinity (defcomp-kwarg args "affinity" "auto")))
|
|
(let
|
|
((comp (make-component comp-name params has-children body env affinity))
|
|
(effects (defcomp-kwarg args "effects" nil)))
|
|
(when
|
|
(and (not (nil? param-types)) (not (empty? (keys param-types))))
|
|
(component-set-param-types! comp param-types))
|
|
(when
|
|
(not (nil? effects))
|
|
(let
|
|
((effect-list (if (= (type-of effects) "list") (map (fn (e) (if (= (type-of e) "symbol") (symbol-name e) (str e))) effects) (list (str effects))))
|
|
(effect-anns
|
|
(if
|
|
(env-has? env "*effect-annotations*")
|
|
(env-get env "*effect-annotations*")
|
|
(dict))))
|
|
(dict-set! effect-anns (symbol-name name-sym) effect-list)
|
|
(env-bind! env "*effect-annotations*" effect-anns)))
|
|
(when
|
|
(env-has? env "*current-file*")
|
|
(component-set-file! comp (env-get env "*current-file*")))
|
|
(env-bind! env (symbol-name name-sym) comp)
|
|
comp))))
|
|
|
|
;; List evaluation — dispatches on head: special forms, macros,
|
|
;; higher-order forms, or function calls. This is the main
|
|
;; expression dispatcher for the CEK machine.
|
|
(define
|
|
defcomp-kwarg
|
|
(fn
|
|
((args :as list) (key :as string) default)
|
|
(let
|
|
((end (- (len args) 1)) (result default))
|
|
(for-each
|
|
(fn
|
|
(i)
|
|
(when
|
|
(and
|
|
(= (type-of (nth args i)) "keyword")
|
|
(= (keyword-name (nth args i)) key)
|
|
(< (+ i 1) end))
|
|
(let
|
|
((val (nth args (+ i 1))))
|
|
(set!
|
|
result
|
|
(if (= (type-of val) "keyword") (keyword-name val) val)))))
|
|
(range 2 end 1))
|
|
result)))
|
|
|
|
;; call/cc: capture entire kont as undelimited escape continuation
|
|
(define
|
|
parse-comp-params
|
|
(fn
|
|
((params-expr :as list))
|
|
(let
|
|
((params (list))
|
|
(param-types (dict))
|
|
(has-children false)
|
|
(in-key false))
|
|
(for-each
|
|
(fn
|
|
(p)
|
|
(if
|
|
(and
|
|
(= (type-of p) "list")
|
|
(= (len p) 3)
|
|
(= (type-of (first p)) "symbol")
|
|
(= (type-of (nth p 1)) "keyword")
|
|
(= (keyword-name (nth p 1)) "as"))
|
|
(let
|
|
((name (symbol-name (first p))) (ptype (nth p 2)))
|
|
(let
|
|
((type-val (if (= (type-of ptype) "symbol") (symbol-name ptype) ptype)))
|
|
(when
|
|
(not has-children)
|
|
(append! params name)
|
|
(dict-set! param-types name type-val))))
|
|
(when
|
|
(= (type-of p) "symbol")
|
|
(let
|
|
((name (symbol-name p)))
|
|
(cond
|
|
(= name "&key")
|
|
(set! in-key true)
|
|
(= name "&rest")
|
|
(set! has-children true)
|
|
(= name "&children")
|
|
(set! has-children true)
|
|
has-children
|
|
nil
|
|
in-key
|
|
(append! params name)
|
|
:else (append! params name))))))
|
|
params-expr)
|
|
(list params has-children param-types))))
|
|
|
|
(define
|
|
sf-defisland
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((name-sym (first args))
|
|
(params-raw (nth args 1))
|
|
(body-exprs (slice args 2))
|
|
(body
|
|
(if
|
|
(= (len body-exprs) 1)
|
|
(first body-exprs)
|
|
(cons (make-symbol "begin") body-exprs)))
|
|
(comp-name (strip-prefix (symbol-name name-sym) "~"))
|
|
(parsed (parse-comp-params params-raw))
|
|
(params (first parsed))
|
|
(has-children (nth parsed 1)))
|
|
(let
|
|
((island (make-island comp-name params has-children body env)))
|
|
(when
|
|
(env-has? env "*current-file*")
|
|
(component-set-file! island (env-get env "*current-file*")))
|
|
(env-bind! env (symbol-name name-sym) island)
|
|
island))))
|
|
|
|
(define
|
|
defio-parse-kwargs!
|
|
(fn
|
|
(spec remaining)
|
|
(when
|
|
(and
|
|
(not (empty? remaining))
|
|
(>= (len remaining) 2)
|
|
(keyword? (first remaining)))
|
|
(dict-set! spec (keyword-name (first remaining)) (nth remaining 1))
|
|
(defio-parse-kwargs! spec (rest (rest remaining))))))
|
|
|
|
;; Pattern matching (match form)
|
|
(define
|
|
sf-defio
|
|
(fn
|
|
(args env)
|
|
(let
|
|
((name (first args)) (spec (dict)))
|
|
(dict-set! spec "name" name)
|
|
(defio-parse-kwargs! spec (rest args))
|
|
(io-register! name spec)
|
|
spec)))
|
|
|
|
;; Condition system special forms
|
|
(define
|
|
sf-defmacro
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((name-sym (first args))
|
|
(params-raw (nth args 1))
|
|
(body (nth args 2))
|
|
(parsed (parse-macro-params params-raw))
|
|
(params (first parsed))
|
|
(rest-param (nth parsed 1)))
|
|
(let
|
|
((mac (make-macro params rest-param body env (symbol-name name-sym))))
|
|
(env-bind! env (symbol-name name-sym) mac)
|
|
mac))))
|
|
|
|
(define
|
|
parse-macro-params
|
|
(fn
|
|
((params-expr :as list))
|
|
(let
|
|
((params (list)) (rest-param nil))
|
|
(reduce
|
|
(fn
|
|
(state p)
|
|
(if
|
|
(and (= (type-of p) "symbol") (= (symbol-name p) "&rest"))
|
|
(assoc state "in-rest" true)
|
|
(if
|
|
(get state "in-rest")
|
|
(do
|
|
(set!
|
|
rest-param
|
|
(if (= (type-of p) "symbol") (symbol-name p) p))
|
|
state)
|
|
(do
|
|
(append!
|
|
params
|
|
(if (= (type-of p) "symbol") (symbol-name p) p))
|
|
state))))
|
|
(dict "in-rest" false)
|
|
params-expr)
|
|
(list params rest-param))))
|
|
|
|
(define
|
|
qq-expand
|
|
(fn
|
|
(template (env :as dict))
|
|
(if
|
|
(not (= (type-of template) "list"))
|
|
template
|
|
(if
|
|
(empty? template)
|
|
(list)
|
|
(let
|
|
((head (first template)))
|
|
(if
|
|
(and
|
|
(= (type-of head) "symbol")
|
|
(= (symbol-name head) "unquote"))
|
|
(trampoline (eval-expr (nth template 1) env))
|
|
(reduce
|
|
(fn
|
|
(result item)
|
|
(if
|
|
(and
|
|
(= (type-of item) "list")
|
|
(= (len item) 2)
|
|
(= (type-of (first item)) "symbol")
|
|
(= (symbol-name (first item)) "splice-unquote"))
|
|
(let
|
|
((spliced (trampoline (eval-expr (nth item 1) env))))
|
|
(if
|
|
(= (type-of spliced) "list")
|
|
(concat result spliced)
|
|
(if
|
|
(nil? spliced)
|
|
result
|
|
(concat result (list spliced)))))
|
|
(concat result (list (qq-expand item env)))))
|
|
(list)
|
|
template)))))))
|
|
|
|
(define
|
|
sf-letrec
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((bindings (first args))
|
|
(body (rest args))
|
|
(local (env-extend env))
|
|
(names (list))
|
|
(val-exprs (list)))
|
|
(if
|
|
(and
|
|
(= (type-of (first bindings)) "list")
|
|
(= (len (first bindings)) 2))
|
|
(for-each
|
|
(fn
|
|
(binding)
|
|
(let
|
|
((vname (if (= (type-of (first binding)) "symbol") (symbol-name (first binding)) (first binding))))
|
|
(append! names vname)
|
|
(append! val-exprs (nth binding 1))
|
|
(env-bind! local vname nil)))
|
|
bindings)
|
|
(reduce
|
|
(fn
|
|
(acc pair-idx)
|
|
(let
|
|
((vname (if (= (type-of (nth bindings (* pair-idx 2))) "symbol") (symbol-name (nth bindings (* pair-idx 2))) (nth bindings (* pair-idx 2))))
|
|
(val-expr (nth bindings (inc (* pair-idx 2)))))
|
|
(append! names vname)
|
|
(append! val-exprs val-expr)
|
|
(env-bind! local vname nil)))
|
|
nil
|
|
(range 0 (/ (len bindings) 2))))
|
|
(let
|
|
((values (map (fn (e) (trampoline (eval-expr e local))) val-exprs)))
|
|
(for-each
|
|
(fn (pair) (env-bind! local (first pair) (nth pair 1)))
|
|
(zip names values))
|
|
(for-each
|
|
(fn
|
|
(val)
|
|
(when
|
|
(lambda? val)
|
|
(for-each
|
|
(fn
|
|
(n)
|
|
(env-bind! (lambda-closure val) n (env-get local n)))
|
|
names)))
|
|
values))
|
|
(for-each
|
|
(fn (e) (trampoline (eval-expr e local)))
|
|
(slice body 0 (dec (len body))))
|
|
(make-thunk (last body) local))))
|
|
|
|
(define
|
|
step-sf-letrec
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((thk (sf-letrec args env)))
|
|
(make-cek-state (thunk-expr thk) (thunk-env thk) kont))))
|
|
|
|
(define
|
|
sf-dynamic-wind
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((before (trampoline (eval-expr (first args) env)))
|
|
(body (trampoline (eval-expr (nth args 1) env)))
|
|
(after (trampoline (eval-expr (nth args 2) env))))
|
|
(dynamic-wind-call before body after env))))
|
|
|
|
(define
|
|
sf-scope
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(rest (slice args 1))
|
|
(val nil)
|
|
(body-exprs nil))
|
|
(if
|
|
(and
|
|
(>= (len rest) 2)
|
|
(= (type-of (first rest)) "keyword")
|
|
(= (keyword-name (first rest)) "value"))
|
|
(do
|
|
(set! val (trampoline (eval-expr (nth rest 1) env)))
|
|
(set! body-exprs (slice rest 2)))
|
|
(set! body-exprs rest))
|
|
(scope-push! name val)
|
|
(let
|
|
((result nil))
|
|
(for-each
|
|
(fn (e) (set! result (trampoline (eval-expr e env))))
|
|
body-exprs)
|
|
(scope-pop! name)
|
|
result))))
|
|
|
|
(define
|
|
sf-provide
|
|
(fn
|
|
((args :as list) (env :as dict))
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(val (trampoline (eval-expr (nth args 1) env)))
|
|
(body-exprs (slice args 2))
|
|
(result nil))
|
|
(scope-push! name val)
|
|
(for-each
|
|
(fn (e) (set! result (trampoline (eval-expr e env))))
|
|
body-exprs)
|
|
(scope-pop! name)
|
|
result)))
|
|
|
|
(define
|
|
expand-macro
|
|
(fn
|
|
((mac :as macro) (raw-args :as list) (env :as dict))
|
|
(let
|
|
((local (env-merge (macro-closure mac) env)))
|
|
(for-each
|
|
(fn
|
|
(pair)
|
|
(env-bind!
|
|
local
|
|
(first pair)
|
|
(if
|
|
(< (nth pair 1) (len raw-args))
|
|
(nth raw-args (nth pair 1))
|
|
nil)))
|
|
(map-indexed (fn (i p) (list p i)) (macro-params mac)))
|
|
(when
|
|
(macro-rest-param mac)
|
|
(env-bind!
|
|
local
|
|
(macro-rest-param mac)
|
|
(slice raw-args (len (macro-params mac)))))
|
|
(trampoline (eval-expr (macro-body mac) local)))))
|
|
|
|
(define
|
|
cek-step-loop
|
|
(fn
|
|
(state)
|
|
(if
|
|
(or (cek-terminal? state) (cek-suspended? state))
|
|
state
|
|
(cek-step-loop (cek-step state)))))
|
|
|
|
(define
|
|
cek-run
|
|
(fn
|
|
(state)
|
|
(let
|
|
((final (cek-step-loop state)))
|
|
(if
|
|
(cek-suspended? final)
|
|
(error "IO suspension in non-IO context")
|
|
(cek-value final)))))
|
|
|
|
(define
|
|
cek-resume
|
|
(fn
|
|
(suspended-state result)
|
|
(cek-step-loop
|
|
(make-cek-value
|
|
result
|
|
(cek-env suspended-state)
|
|
(cek-kont suspended-state)))))
|
|
|
|
(define
|
|
cek-step
|
|
(fn
|
|
(state)
|
|
(if
|
|
(= (cek-phase state) "eval")
|
|
(step-eval state)
|
|
(step-continue state))))
|
|
|
|
(define
|
|
step-eval
|
|
(fn
|
|
(state)
|
|
(let
|
|
((expr (cek-control state))
|
|
(env (cek-env state))
|
|
(kont (cek-kont state)))
|
|
(case
|
|
(type-of expr)
|
|
"number"
|
|
(make-cek-value expr env kont)
|
|
"string"
|
|
(make-cek-value expr env kont)
|
|
"boolean"
|
|
(make-cek-value expr env kont)
|
|
"nil"
|
|
(make-cek-value nil env kont)
|
|
"symbol"
|
|
(let
|
|
((name (symbol-name expr)))
|
|
(let
|
|
((val (cond (env-has? env name) (env-get env name) (primitive? name) (get-primitive name) (= name "true") true (= name "false") false (= name "nil") nil :else (error (str "Undefined symbol: " name)))))
|
|
(when
|
|
(and (nil? val) (starts-with? name "~"))
|
|
(debug-log "Component not found:" name))
|
|
(make-cek-value val env kont)))
|
|
"keyword"
|
|
(make-cek-value (keyword-name expr) env kont)
|
|
"dict"
|
|
(let
|
|
((ks (keys expr)))
|
|
(if
|
|
(empty? ks)
|
|
(make-cek-value (dict) env kont)
|
|
(let
|
|
((first-key (first ks)) (remaining-entries (list)))
|
|
(for-each
|
|
(fn (k) (append! remaining-entries (list k (get expr k))))
|
|
(rest ks))
|
|
(make-cek-state
|
|
(get expr first-key)
|
|
env
|
|
(kont-push
|
|
(make-dict-frame
|
|
remaining-entries
|
|
(list (list first-key))
|
|
env)
|
|
kont)))))
|
|
"list"
|
|
(if
|
|
(empty? expr)
|
|
(make-cek-value (list) env kont)
|
|
(step-eval-list expr env kont))
|
|
:else (make-cek-value expr env kont)))))
|
|
|
|
(define
|
|
step-sf-raise
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-raise-eval-frame env false) kont))))
|
|
|
|
(define
|
|
step-sf-guard
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((var-clauses (first args))
|
|
(body (rest args))
|
|
(var (first var-clauses))
|
|
(clauses (rest var-clauses))
|
|
(sentinel (make-symbol "__guard-reraise__")))
|
|
(step-eval-list
|
|
(list
|
|
(quote let)
|
|
(list
|
|
(list
|
|
(quote __guard-result)
|
|
(cons
|
|
(quote call/cc)
|
|
(list
|
|
(cons
|
|
(quote fn)
|
|
(cons
|
|
(quote (__guard-k))
|
|
(list
|
|
(cons
|
|
(quote handler-bind)
|
|
(cons
|
|
(list
|
|
(list
|
|
(cons
|
|
(quote fn)
|
|
(cons (quote (_)) (quote (true))))
|
|
(cons
|
|
(quote fn)
|
|
(cons
|
|
(list var)
|
|
(list
|
|
(list
|
|
(quote __guard-k)
|
|
(cons
|
|
(quote cond)
|
|
(append
|
|
clauses
|
|
(list
|
|
(list
|
|
(quote else)
|
|
(list
|
|
(quote list)
|
|
(list
|
|
(quote quote)
|
|
sentinel)
|
|
var)))))))))))
|
|
(list
|
|
(list
|
|
(quote __guard-k)
|
|
(cons (quote begin) body))))))))))))
|
|
(list
|
|
(quote if)
|
|
(list
|
|
(quote and)
|
|
(list (quote list?) (quote __guard-result))
|
|
(list (quote =) (list (quote len) (quote __guard-result)) 2)
|
|
(list
|
|
(quote =)
|
|
(list (quote first) (quote __guard-result))
|
|
(list (quote quote) sentinel)))
|
|
(list
|
|
(quote raise)
|
|
(list (quote nth) (quote __guard-result) 1))
|
|
(quote __guard-result)))
|
|
env
|
|
kont))))
|
|
|
|
;; Scope/provide/context — structured downward data passing
|
|
(define
|
|
step-eval-list
|
|
(fn
|
|
(expr env kont)
|
|
(let
|
|
((head (first expr)) (args (rest expr)))
|
|
(if
|
|
(not
|
|
(or
|
|
(= (type-of head) "symbol")
|
|
(= (type-of head) "lambda")
|
|
(= (type-of head) "list")))
|
|
(if
|
|
(empty? expr)
|
|
(make-cek-value (list) env kont)
|
|
(make-cek-state
|
|
(first expr)
|
|
env
|
|
(kont-push (make-map-frame nil (rest expr) (list) env) kont)))
|
|
(if
|
|
(= (type-of head) "symbol")
|
|
(let
|
|
((name (symbol-name head)))
|
|
(match
|
|
name
|
|
("if" (step-sf-if args env kont))
|
|
("when" (step-sf-when args env kont))
|
|
("cond" (step-sf-cond args env kont))
|
|
("case" (step-sf-case args env kont))
|
|
("and" (step-sf-and args env kont))
|
|
("or" (step-sf-or args env kont))
|
|
("let" (step-sf-let args env kont))
|
|
("let*" (step-sf-let args env kont))
|
|
("lambda" (step-sf-lambda args env kont))
|
|
("fn" (step-sf-lambda args env kont))
|
|
("define" (step-sf-define args env kont))
|
|
("defcomp" (make-cek-value (sf-defcomp args env) env kont))
|
|
("defisland" (make-cek-value (sf-defisland args env) env kont))
|
|
("defmacro" (make-cek-value (sf-defmacro args env) env kont))
|
|
("defio" (make-cek-value (sf-defio args env) env kont))
|
|
("io" (step-sf-io args env kont))
|
|
("begin" (step-sf-begin args env kont))
|
|
("do"
|
|
(if
|
|
(and
|
|
(not (empty? args))
|
|
(list? (first args))
|
|
(not (empty? (first args)))
|
|
(list? (first (first args))))
|
|
(let
|
|
((bindings (first args))
|
|
(test-clause (nth args 1))
|
|
(body (rest (rest args)))
|
|
(vars (map (fn (b) (first b)) bindings))
|
|
(inits (map (fn (b) (nth b 1)) bindings))
|
|
(steps
|
|
(map
|
|
(fn (b) (if (> (len b) 2) (nth b 2) (first b)))
|
|
bindings))
|
|
(test (first test-clause))
|
|
(result (rest test-clause)))
|
|
(step-eval-list
|
|
(cons
|
|
(quote let)
|
|
(cons
|
|
(quote __do-loop)
|
|
(cons
|
|
(map
|
|
(fn (b) (list (first b) (nth b 1)))
|
|
bindings)
|
|
(list
|
|
(cons
|
|
(quote if)
|
|
(cons
|
|
test
|
|
(cons
|
|
(if
|
|
(empty? result)
|
|
nil
|
|
(cons (quote begin) result))
|
|
(list
|
|
(cons
|
|
(quote begin)
|
|
(append
|
|
body
|
|
(list
|
|
(cons (quote __do-loop) steps))))))))))))
|
|
env
|
|
kont))
|
|
(step-sf-begin args env kont)))
|
|
("guard" (step-sf-guard args env kont))
|
|
("quote"
|
|
(make-cek-value
|
|
(if (empty? args) nil (first args))
|
|
env
|
|
kont))
|
|
("quasiquote"
|
|
(make-cek-value (qq-expand (first args) env) env kont))
|
|
("->" (step-sf-thread-first args env kont))
|
|
("set!" (step-sf-set! args env kont))
|
|
("letrec" (step-sf-letrec args env kont))
|
|
("reset" (step-sf-reset args env kont))
|
|
("shift" (step-sf-shift args env kont))
|
|
("deref" (step-sf-deref args env kont))
|
|
("scope" (step-sf-scope args env kont))
|
|
("provide" (step-sf-provide args env kont))
|
|
("context" (step-sf-context args env kont))
|
|
("emit!" (step-sf-emit args env kont))
|
|
("emitted" (step-sf-emitted args env kont))
|
|
("handler-bind" (step-sf-handler-bind args env kont))
|
|
("restart-case" (step-sf-restart-case args env kont))
|
|
("signal-condition" (step-sf-signal args env kont))
|
|
("invoke-restart" (step-sf-invoke-restart args env kont))
|
|
("match" (step-sf-match args env kont))
|
|
("dynamic-wind"
|
|
(make-cek-value (sf-dynamic-wind args env) env kont))
|
|
("map" (step-ho-map args env kont))
|
|
("map-indexed" (step-ho-map-indexed args env kont))
|
|
("filter" (step-ho-filter args env kont))
|
|
("reduce" (step-ho-reduce args env kont))
|
|
("some" (step-ho-some args env kont))
|
|
("every?" (step-ho-every args env kont))
|
|
("for-each" (step-ho-for-each args env kont))
|
|
("raise" (step-sf-raise args env kont))
|
|
("raise-continuable"
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-raise-eval-frame env true) kont)))
|
|
("call/cc" (step-sf-callcc args env kont))
|
|
("call-with-current-continuation"
|
|
(step-sf-callcc args env kont))
|
|
("perform" (step-sf-perform args env kont))
|
|
("define-library" (step-sf-define-library args env kont))
|
|
("import" (step-sf-import args env kont))
|
|
(_
|
|
(cond
|
|
(has-key? *custom-special-forms* name)
|
|
(make-cek-value
|
|
((get *custom-special-forms* name) args env)
|
|
env
|
|
kont)
|
|
(and (env-has? env name) (macro? (env-get env name)))
|
|
(let
|
|
((mac (env-get env name)))
|
|
(make-cek-state (expand-macro mac args env) env kont))
|
|
(and *render-check* (*render-check* expr env))
|
|
(make-cek-value (*render-fn* expr env) env kont)
|
|
:else (step-eval-call head args env kont)))))
|
|
(step-eval-call head args env kont))))))
|
|
|
|
(define
|
|
step-sf-define-library
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((lib-spec (first args)) (decls (rest args)))
|
|
(let
|
|
((lib-env (env-extend env)) (exports (list)) (body-forms (list)))
|
|
(for-each
|
|
(fn
|
|
(decl)
|
|
(when
|
|
(and
|
|
(list? decl)
|
|
(not (empty? decl))
|
|
(symbol? (first decl)))
|
|
(let
|
|
((kind (symbol-name (first decl))))
|
|
(cond
|
|
(= kind "export")
|
|
(set!
|
|
exports
|
|
(append
|
|
exports
|
|
(map
|
|
(fn (s) (if (symbol? s) (symbol-name s) (str s)))
|
|
(rest decl))))
|
|
(= kind "begin")
|
|
(set! body-forms (append body-forms (rest decl)))
|
|
:else nil))))
|
|
decls)
|
|
(for-each (fn (form) (eval-expr form lib-env)) body-forms)
|
|
(let
|
|
((export-dict (dict)))
|
|
(for-each
|
|
(fn
|
|
(name)
|
|
(when
|
|
(env-has? lib-env name)
|
|
(dict-set! export-dict name (env-get lib-env name))))
|
|
exports)
|
|
(register-library lib-spec export-dict)
|
|
(make-cek-value nil env kont))))))
|
|
|
|
(define
|
|
bind-import-set
|
|
(fn
|
|
(import-set env)
|
|
(let
|
|
((head (if (and (list? import-set) (not (empty? import-set)) (symbol? (first import-set))) (symbol-name (first import-set)) nil)))
|
|
(let
|
|
((lib-spec (if (or (= head "only") (= head "except") (= head "prefix") (= head "rename")) (nth import-set 1) import-set)))
|
|
(let
|
|
((exports (library-exports lib-spec)))
|
|
(cond
|
|
(= head "only")
|
|
(for-each
|
|
(fn
|
|
(s)
|
|
(let
|
|
((id (if (symbol? s) (symbol-name s) (str s))))
|
|
(when
|
|
(has-key? exports id)
|
|
(env-bind! env id (get exports id)))))
|
|
(rest (rest import-set)))
|
|
(= head "prefix")
|
|
(let
|
|
((pfx (str (nth import-set 2))))
|
|
(for-each
|
|
(fn (key) (env-bind! env (str pfx key) (get exports key)))
|
|
(keys exports)))
|
|
:else (for-each
|
|
(fn (key) (env-bind! env key (get exports key)))
|
|
(keys exports))))))))
|
|
|
|
(define
|
|
step-sf-import
|
|
(fn
|
|
(args env kont)
|
|
(if
|
|
(empty? args)
|
|
(make-cek-value nil env kont)
|
|
(let
|
|
((import-set (first args)) (rest-sets (rest args)))
|
|
(let
|
|
((lib-spec (let ((head (if (and (list? import-set) (not (empty? import-set)) (symbol? (first import-set))) (symbol-name (first import-set)) nil))) (if (or (= head "only") (= head "except") (= head "prefix") (= head "rename")) (nth import-set 1) import-set))))
|
|
(if
|
|
(library-loaded? lib-spec)
|
|
(do
|
|
(bind-import-set import-set env)
|
|
(if
|
|
(empty? rest-sets)
|
|
(make-cek-value nil env kont)
|
|
(step-sf-import rest-sets env kont)))
|
|
(make-cek-suspended
|
|
{:library lib-spec :op "import"}
|
|
env
|
|
(kont-push (make-import-frame import-set rest-sets env) kont))))))))
|
|
|
|
(define
|
|
step-sf-perform
|
|
(fn
|
|
(args env kont)
|
|
(if
|
|
(empty? args)
|
|
(error "perform requires an IO request argument")
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-perform-frame env) kont)))))
|
|
|
|
;; Delimited continuations
|
|
(define
|
|
step-sf-callcc
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-callcc-frame env) kont))))
|
|
|
|
(define
|
|
match-find-clause
|
|
(fn
|
|
(val clauses env)
|
|
(if
|
|
(empty? clauses)
|
|
nil
|
|
(let
|
|
((clause (first clauses))
|
|
(pattern (first clause))
|
|
(body (nth clause 1))
|
|
(local (env-extend env)))
|
|
(if
|
|
(match-pattern pattern val local)
|
|
(list local body)
|
|
(match-find-clause val (rest clauses) env))))))
|
|
|
|
;; Signal dereferencing with reactive dependency tracking
|
|
(define
|
|
match-pattern
|
|
(fn
|
|
(pattern value env)
|
|
(cond
|
|
(= pattern (quote _))
|
|
true
|
|
(and
|
|
(list? pattern)
|
|
(= (len pattern) 2)
|
|
(= (first pattern) (quote ?)))
|
|
(let
|
|
((pred (trampoline (eval-expr (nth pattern 1) env))))
|
|
(cek-call pred (list value)))
|
|
(and
|
|
(list? pattern)
|
|
(not (empty? pattern))
|
|
(= (first pattern) (quote quote)))
|
|
(= value (nth pattern 1))
|
|
(symbol? pattern)
|
|
(do (env-bind! env (symbol-name pattern) value) true)
|
|
(and (list? pattern) (list? value))
|
|
(if
|
|
(not (= (len pattern) (len value)))
|
|
false
|
|
(let
|
|
((pairs (zip pattern value)))
|
|
(every?
|
|
(fn (pair) (match-pattern (first pair) (nth pair 1) env))
|
|
pairs)))
|
|
:else (= pattern value))))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 8: Call Dispatch
|
|
;;
|
|
;; cek-call: invoke a function from native code (runs a nested
|
|
;; trampoline). step-eval-call: CEK-native call dispatch for
|
|
;; lambda, component, native fn, and continuations.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define
|
|
step-sf-match
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((val (trampoline (eval-expr (first args) env)))
|
|
(clauses (rest args)))
|
|
(let
|
|
((result (match-find-clause val clauses env)))
|
|
(if
|
|
(nil? result)
|
|
(error (str "match: no clause matched " (inspect val)))
|
|
(make-cek-state (nth result 1) (first result) kont))))))
|
|
|
|
;; Reactive signal tracking — captures dependency continuation for re-render
|
|
(define
|
|
step-sf-handler-bind
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((handler-specs (first args))
|
|
(body (rest args))
|
|
(handlers
|
|
(map
|
|
(fn
|
|
(spec)
|
|
(list
|
|
(trampoline (eval-expr (first spec) env))
|
|
(trampoline (eval-expr (nth spec 1) env))))
|
|
handler-specs)))
|
|
(if
|
|
(empty? body)
|
|
(make-cek-value nil env kont)
|
|
(make-cek-state
|
|
(first body)
|
|
env
|
|
(kont-push (make-handler-frame handlers (rest body) env) kont))))))
|
|
|
|
(define
|
|
step-sf-restart-case
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((body (first args))
|
|
(restart-specs (rest args))
|
|
(restarts
|
|
(map
|
|
(fn
|
|
(spec)
|
|
(list
|
|
(if
|
|
(symbol? (first spec))
|
|
(symbol-name (first spec))
|
|
(first spec))
|
|
(nth spec 1)
|
|
(nth spec 2)))
|
|
restart-specs)))
|
|
(make-cek-state
|
|
body
|
|
env
|
|
(kont-push (make-restart-frame restarts (list) env) kont)))))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 9: Higher-Order Form Machinery
|
|
;;
|
|
;; Data-first HO forms: (map coll fn) and (map fn coll) both work.
|
|
;; ho-swap-args auto-detects argument order. HoSetupFrame stages
|
|
;; argument evaluation, then dispatches to the appropriate step-ho-*.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define
|
|
step-sf-signal
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((condition (trampoline (eval-expr (first args) env)))
|
|
(handler-fn (kont-find-handler kont condition)))
|
|
(if
|
|
(nil? handler-fn)
|
|
(error (str "Unhandled condition: " (inspect condition)))
|
|
(continue-with-call
|
|
handler-fn
|
|
(list condition)
|
|
env
|
|
(list condition)
|
|
(kont-push (make-signal-return-frame env kont) kont))))))
|
|
|
|
(define
|
|
step-sf-invoke-restart
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((restart-name (let ((rn (if (symbol? (first args)) (symbol-name (first args)) (trampoline (eval-expr (first args) env))))) (if (symbol? rn) (symbol-name rn) rn)))
|
|
(restart-arg
|
|
(if
|
|
(>= (len args) 2)
|
|
(trampoline (eval-expr (nth args 1) env))
|
|
nil))
|
|
(found (kont-find-restart kont restart-name)))
|
|
(if
|
|
(nil? found)
|
|
(error (str "No restart named: " (inspect restart-name)))
|
|
(let
|
|
((entry (first found))
|
|
(restart-frame (nth found 1))
|
|
(rest-kont (nth found 2)))
|
|
(let
|
|
((params (nth entry 1))
|
|
(body (nth entry 2))
|
|
(restart-env (env-extend (get restart-frame "env"))))
|
|
(when
|
|
(not (empty? params))
|
|
(env-bind! restart-env (first params) restart-arg))
|
|
(make-cek-state body restart-env rest-kont)))))))
|
|
|
|
(define
|
|
step-sf-if
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push
|
|
(make-if-frame
|
|
(nth args 1)
|
|
(if (> (len args) 2) (nth args 2) nil)
|
|
env)
|
|
kont))))
|
|
|
|
(define
|
|
step-sf-when
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-when-frame (rest args) env) kont))))
|
|
|
|
(define
|
|
step-sf-begin
|
|
(fn
|
|
(args env kont)
|
|
(if
|
|
(empty? args)
|
|
(make-cek-value nil env kont)
|
|
(if
|
|
(= (len args) 1)
|
|
(make-cek-state (first args) env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-begin-frame (rest args) env) kont))))))
|
|
|
|
(define
|
|
step-sf-let
|
|
(fn
|
|
(args env kont)
|
|
(if
|
|
(= (type-of (first args)) "symbol")
|
|
(make-cek-value (sf-named-let args env) env kont)
|
|
(let
|
|
((bindings (first args))
|
|
(body (rest args))
|
|
(local (env-extend env)))
|
|
(if
|
|
(empty? bindings)
|
|
(step-sf-begin body local kont)
|
|
(let
|
|
((first-binding (if (and (= (type-of (first bindings)) "list") (= (len (first bindings)) 2)) (first bindings) (list (first bindings) (nth bindings 1))))
|
|
(rest-bindings
|
|
(if
|
|
(and
|
|
(= (type-of (first bindings)) "list")
|
|
(= (len (first bindings)) 2))
|
|
(rest bindings)
|
|
(let
|
|
((pairs (list)))
|
|
(reduce
|
|
(fn
|
|
(acc i)
|
|
(append!
|
|
pairs
|
|
(list
|
|
(nth bindings (* i 2))
|
|
(nth bindings (inc (* i 2))))))
|
|
nil
|
|
(range 1 (/ (len bindings) 2)))
|
|
pairs))))
|
|
(let
|
|
((vname (if (= (type-of (first first-binding)) "symbol") (symbol-name (first first-binding)) (first first-binding))))
|
|
(make-cek-state
|
|
(nth first-binding 1)
|
|
local
|
|
(kont-push
|
|
(make-let-frame vname rest-bindings body local)
|
|
kont)))))))))
|
|
|
|
(define
|
|
step-sf-define
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name-sym (first args))
|
|
(has-effects
|
|
(and
|
|
(>= (len args) 4)
|
|
(= (type-of (nth args 1)) "keyword")
|
|
(= (keyword-name (nth args 1)) "effects")))
|
|
(val-idx
|
|
(if
|
|
(and
|
|
(>= (len args) 4)
|
|
(= (type-of (nth args 1)) "keyword")
|
|
(= (keyword-name (nth args 1)) "effects"))
|
|
3
|
|
1))
|
|
(effect-list
|
|
(if
|
|
(and
|
|
(>= (len args) 4)
|
|
(= (type-of (nth args 1)) "keyword")
|
|
(= (keyword-name (nth args 1)) "effects"))
|
|
(nth args 2)
|
|
nil)))
|
|
(make-cek-state
|
|
(nth args val-idx)
|
|
env
|
|
(kont-push
|
|
(make-define-frame
|
|
(symbol-name name-sym)
|
|
env
|
|
has-effects
|
|
effect-list)
|
|
kont)))))
|
|
|
|
(define
|
|
step-sf-set!
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(nth args 1)
|
|
env
|
|
(kont-push (make-set-frame (symbol-name (first args)) env) kont))))
|
|
|
|
(define
|
|
step-sf-and
|
|
(fn
|
|
(args env kont)
|
|
(if
|
|
(empty? args)
|
|
(make-cek-value true env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-and-frame (rest args) env) kont)))))
|
|
|
|
(define
|
|
step-sf-or
|
|
(fn
|
|
(args env kont)
|
|
(if
|
|
(empty? args)
|
|
(make-cek-value false env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-or-frame (rest args) env) kont)))))
|
|
|
|
(define
|
|
step-sf-cond
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((scheme? (cond-scheme? args)))
|
|
(if
|
|
scheme?
|
|
(if
|
|
(empty? args)
|
|
(make-cek-value nil env kont)
|
|
(let
|
|
((clause (first args)) (test (first clause)))
|
|
(if
|
|
(is-else-clause? test)
|
|
(make-cek-state (nth clause 1) env kont)
|
|
(make-cek-state
|
|
test
|
|
env
|
|
(kont-push (make-cond-frame args env true) kont)))))
|
|
(if
|
|
(< (len args) 2)
|
|
(make-cek-value nil env kont)
|
|
(let
|
|
((test (first args)))
|
|
(if
|
|
(is-else-clause? test)
|
|
(make-cek-state (nth args 1) env kont)
|
|
(make-cek-state
|
|
test
|
|
env
|
|
(kont-push (make-cond-frame args env false) kont)))))))))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 10: Continue Phase — Frame Dispatch
|
|
;;
|
|
;; When phase="continue", pop the top frame and process the value.
|
|
;; Each frame type has its own handling: if frames check truthiness,
|
|
;; let frames bind the value, arg frames accumulate it, etc.
|
|
;; continue-with-call handles the final function/component dispatch.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define
|
|
step-sf-case
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-case-frame nil (rest args) env) kont))))
|
|
|
|
;; Final call dispatch from arg frame — all args evaluated, invoke function.
|
|
;; Handles: lambda (bind params + TCO), component (keyword args + TCO),
|
|
;; native fn (direct call), continuation (resume), callcc continuation (escape).
|
|
(define
|
|
step-sf-thread-first
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-thread-frame (rest args) env) kont))))
|
|
|
|
(define
|
|
step-sf-lambda
|
|
(fn (args env kont) (make-cek-value (sf-lambda args env) env kont)))
|
|
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
;; Part 11: Entry Points
|
|
;;
|
|
;; eval-expr-cek / trampoline-cek: CEK evaluation entry points.
|
|
;; eval-expr / trampoline: top-level bindings that override the
|
|
;; forward declarations from Part 5.
|
|
;; ═══════════════════════════════════════════════════════════════
|
|
(define
|
|
step-sf-scope
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(rest-args (slice args 1))
|
|
(val nil)
|
|
(body nil))
|
|
(if
|
|
(and
|
|
(>= (len rest-args) 2)
|
|
(= (type-of (first rest-args)) "keyword")
|
|
(= (keyword-name (first rest-args)) "value"))
|
|
(do
|
|
(set! val (trampoline (eval-expr (nth rest-args 1) env)))
|
|
(set! body (slice rest-args 2)))
|
|
(set! body rest-args))
|
|
(if
|
|
(empty? body)
|
|
(make-cek-value nil env kont)
|
|
(make-cek-state
|
|
(first body)
|
|
env
|
|
(kont-push (make-scope-acc-frame name val (rest body) env) kont))))))
|
|
|
|
(define
|
|
step-sf-provide
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(val (trampoline (eval-expr (nth args 1) env)))
|
|
(body (slice args 2)))
|
|
(if
|
|
(empty? body)
|
|
(make-cek-value nil env kont)
|
|
(make-cek-state
|
|
(first body)
|
|
env
|
|
(kont-push (make-provide-frame name val (rest body) env) kont))))))
|
|
|
|
(define
|
|
step-sf-context
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(default-val
|
|
(if
|
|
(>= (len args) 2)
|
|
(trampoline (eval-expr (nth args 1) env))
|
|
nil))
|
|
(frame (kont-find-provide kont name)))
|
|
(make-cek-value
|
|
(if
|
|
frame
|
|
(get frame "value")
|
|
(if
|
|
(env-has? env "context")
|
|
(apply (env-get env "context") (list name default-val))
|
|
default-val))
|
|
env
|
|
kont))))
|
|
|
|
(define
|
|
step-sf-emit
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(val (trampoline (eval-expr (nth args 1) env)))
|
|
(frame (kont-find-scope-acc kont name)))
|
|
(if
|
|
frame
|
|
(do
|
|
(dict-set!
|
|
frame
|
|
"emitted"
|
|
(append (get frame "emitted") (list val)))
|
|
(make-cek-value nil env kont))
|
|
(do
|
|
(when
|
|
(env-has? env "scope-emit!")
|
|
(apply (env-get env "scope-emit!") (list name val)))
|
|
(make-cek-value nil env kont))))))
|
|
|
|
(define
|
|
step-sf-emitted
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((name (trampoline (eval-expr (first args) env)))
|
|
(frame (kont-find-scope-acc kont name)))
|
|
(make-cek-value
|
|
(if
|
|
frame
|
|
(get frame "emitted")
|
|
(if
|
|
(env-has? env "emitted")
|
|
(apply (env-get env "emitted") (list name))
|
|
(list)))
|
|
env
|
|
kont))))
|
|
|
|
(define
|
|
step-sf-reset
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-reset-frame env) kont))))
|
|
|
|
(define
|
|
step-sf-shift
|
|
(fn
|
|
(args env kont)
|
|
(let
|
|
((k-name (symbol-name (first args)))
|
|
(body (nth args 1))
|
|
(captured-result (kont-capture-to-reset kont))
|
|
(captured (first captured-result))
|
|
(rest-kont (nth captured-result 1)))
|
|
(let
|
|
((k (make-cek-continuation captured rest-kont)))
|
|
(let
|
|
((shift-env (env-extend env)))
|
|
(env-bind! shift-env k-name k)
|
|
(make-cek-state body shift-env rest-kont))))))
|
|
|
|
(define
|
|
step-sf-deref
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-deref-frame env) kont))))
|
|
|
|
(define
|
|
cek-call
|
|
(fn
|
|
(f args)
|
|
(let
|
|
((a (if (nil? args) (list) args)))
|
|
(cond
|
|
(nil? f)
|
|
nil
|
|
(or (lambda? f) (callable? f))
|
|
(cek-run (continue-with-call f a (make-env) a (list)))
|
|
:else nil))))
|
|
|
|
(define
|
|
reactive-shift-deref
|
|
(fn
|
|
(sig env kont)
|
|
(let
|
|
((scan-result (kont-capture-to-reactive-reset kont))
|
|
(captured-frames (first scan-result))
|
|
(reset-frame (nth scan-result 1))
|
|
(remaining-kont (nth scan-result 2))
|
|
(update-fn (get reset-frame "update-fn")))
|
|
(let
|
|
((sub-disposers (list)))
|
|
(let
|
|
((subscriber (fn () (for-each (fn (d) (cek-call d nil)) sub-disposers) (set! sub-disposers (list)) (let ((new-reset (make-reactive-reset-frame env update-fn false)) (new-kont (concat captured-frames (list new-reset) remaining-kont))) (with-island-scope (fn (d) (append! sub-disposers d)) (fn () (cek-run (make-cek-value (signal-value sig) env new-kont))))))))
|
|
(signal-add-sub! sig subscriber)
|
|
(register-in-scope
|
|
(fn
|
|
()
|
|
(signal-remove-sub! sig subscriber)
|
|
(for-each (fn (d) (cek-call d nil)) sub-disposers)))
|
|
(let
|
|
((initial-kont (concat captured-frames (list reset-frame) remaining-kont)))
|
|
(make-cek-value (signal-value sig) env initial-kont)))))))
|
|
|
|
(define
|
|
step-eval-call
|
|
(fn
|
|
(head args env kont)
|
|
(let
|
|
((hname (if (= (type-of head) "symbol") (symbol-name head) nil)))
|
|
(make-cek-state
|
|
head
|
|
env
|
|
(kont-push (make-arg-frame nil (list) args env args hname) kont)))))
|
|
|
|
(define
|
|
ho-form-name?
|
|
(fn
|
|
(name)
|
|
(or
|
|
(= name "map")
|
|
(= name "map-indexed")
|
|
(= name "filter")
|
|
(= name "reduce")
|
|
(= name "some")
|
|
(= name "every?")
|
|
(= name "for-each"))))
|
|
|
|
(define ho-fn? (fn (v) (or (callable? v) (lambda? v))))
|
|
|
|
(define
|
|
ho-swap-args
|
|
(fn
|
|
(ho-type evaled)
|
|
(if
|
|
(= ho-type "reduce")
|
|
(let
|
|
((a (first evaled)) (b (nth evaled 1)))
|
|
(if
|
|
(and (not (ho-fn? a)) (ho-fn? b))
|
|
(list b (nth evaled 2) a)
|
|
evaled))
|
|
(let
|
|
((a (first evaled)) (b (nth evaled 1)))
|
|
(if (and (not (ho-fn? a)) (ho-fn? b)) (list b a) evaled)))))
|
|
|
|
(define
|
|
ho-setup-dispatch
|
|
(fn
|
|
(ho-type evaled env kont)
|
|
(let
|
|
((ordered (ho-swap-args ho-type evaled)))
|
|
(let
|
|
((f (first ordered)))
|
|
(match
|
|
ho-type
|
|
("map"
|
|
(if
|
|
(> (len ordered) 2)
|
|
(let
|
|
((colls (rest ordered)))
|
|
(if
|
|
(some (fn (c) (empty? c)) colls)
|
|
(make-cek-value (list) env kont)
|
|
(let
|
|
((heads (map (fn (c) (first c)) colls))
|
|
(tails (map (fn (c) (rest c)) colls)))
|
|
(continue-with-call
|
|
f
|
|
heads
|
|
env
|
|
(list)
|
|
(kont-push
|
|
(make-multi-map-frame f tails (list) env)
|
|
kont)))))
|
|
(let
|
|
((coll (nth ordered 1)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value (list) env kont)
|
|
(continue-with-call
|
|
f
|
|
(list (first coll))
|
|
env
|
|
(list)
|
|
(kont-push
|
|
(make-map-frame f (rest coll) (list) env)
|
|
kont))))))
|
|
("map-indexed"
|
|
(let
|
|
((coll (nth ordered 1)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value (list) env kont)
|
|
(continue-with-call
|
|
f
|
|
(list 0 (first coll))
|
|
env
|
|
(list)
|
|
(kont-push
|
|
(make-map-indexed-frame f (rest coll) (list) env)
|
|
kont)))))
|
|
("filter"
|
|
(let
|
|
((coll (nth ordered 1)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value (list) env kont)
|
|
(continue-with-call
|
|
f
|
|
(list (first coll))
|
|
env
|
|
(list)
|
|
(kont-push
|
|
(make-filter-frame
|
|
f
|
|
(rest coll)
|
|
(list)
|
|
(first coll)
|
|
env)
|
|
kont)))))
|
|
("reduce"
|
|
(let
|
|
((init (nth ordered 1)) (coll (nth ordered 2)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value init env kont)
|
|
(continue-with-call
|
|
f
|
|
(list init (first coll))
|
|
env
|
|
(list)
|
|
(kont-push (make-reduce-frame f (rest coll) env) kont)))))
|
|
("some"
|
|
(let
|
|
((coll (nth ordered 1)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value false env kont)
|
|
(continue-with-call
|
|
f
|
|
(list (first coll))
|
|
env
|
|
(list)
|
|
(kont-push (make-some-frame f (rest coll) env) kont)))))
|
|
("every"
|
|
(let
|
|
((coll (nth ordered 1)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value true env kont)
|
|
(continue-with-call
|
|
f
|
|
(list (first coll))
|
|
env
|
|
(list)
|
|
(kont-push (make-every-frame f (rest coll) env) kont)))))
|
|
("for-each"
|
|
(let
|
|
((coll (nth ordered 1)))
|
|
(if
|
|
(empty? coll)
|
|
(make-cek-value nil env kont)
|
|
(continue-with-call
|
|
f
|
|
(list (first coll))
|
|
env
|
|
(list)
|
|
(kont-push (make-for-each-frame f (rest coll) env) kont)))))
|
|
(_ (error (str "Unknown HO type: " ho-type))))))))
|
|
|
|
(define
|
|
step-ho-map
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-ho-setup-frame "map" (rest args) (list) env) kont))))
|
|
|
|
(define
|
|
step-ho-map-indexed
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push
|
|
(make-ho-setup-frame "map-indexed" (rest args) (list) env)
|
|
kont))))
|
|
|
|
(define
|
|
step-ho-filter
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-ho-setup-frame "filter" (rest args) (list) env) kont))))
|
|
|
|
(define
|
|
step-ho-reduce
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-ho-setup-frame "reduce" (rest args) (list) env) kont))))
|
|
|
|
(define
|
|
step-ho-some
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-ho-setup-frame "some" (rest args) (list) env) kont))))
|
|
|
|
(define
|
|
step-ho-every
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push (make-ho-setup-frame "every" (rest args) (list) env) kont))))
|
|
|
|
(define
|
|
step-ho-for-each
|
|
(fn
|
|
(args env kont)
|
|
(make-cek-state
|
|
(first args)
|
|
env
|
|
(kont-push
|
|
(make-ho-setup-frame "for-each" (rest args) (list) env)
|
|
kont))))
|
|
|
|
(define
|
|
step-continue
|
|
(fn
|
|
(state)
|
|
(let
|
|
((value (cek-value state))
|
|
(env (cek-env state))
|
|
(kont (cek-kont state)))
|
|
(if
|
|
(kont-empty? kont)
|
|
state
|
|
(let
|
|
((frame (kont-top kont))
|
|
(rest-k (kont-pop kont))
|
|
(ft (frame-type frame)))
|
|
(match
|
|
ft
|
|
("if"
|
|
(if
|
|
(and value (not (nil? value)))
|
|
(make-cek-state (get frame "then") (get frame "env") rest-k)
|
|
(if
|
|
(nil? (get frame "else"))
|
|
(make-cek-value nil env rest-k)
|
|
(make-cek-state
|
|
(get frame "else")
|
|
(get frame "env")
|
|
rest-k))))
|
|
("when"
|
|
(if
|
|
(and value (not (nil? value)))
|
|
(let
|
|
((body (get frame "body")) (fenv (get frame "env")))
|
|
(if
|
|
(empty? body)
|
|
(make-cek-value nil fenv rest-k)
|
|
(if
|
|
(= (len body) 1)
|
|
(make-cek-state (first body) fenv rest-k)
|
|
(make-cek-state
|
|
(first body)
|
|
fenv
|
|
(kont-push
|
|
(make-begin-frame (rest body) fenv)
|
|
rest-k)))))
|
|
(make-cek-value nil env rest-k)))
|
|
("begin"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value fenv rest-k)
|
|
(if
|
|
(= (len remaining) 1)
|
|
(make-cek-state (first remaining) fenv rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-begin-frame (rest remaining) fenv)
|
|
rest-k))))))
|
|
("let"
|
|
(let
|
|
((name (get frame "name"))
|
|
(remaining (get frame "remaining"))
|
|
(body (get frame "body"))
|
|
(local (get frame "env")))
|
|
(env-bind! local name value)
|
|
(if
|
|
(empty? remaining)
|
|
(step-sf-begin body local rest-k)
|
|
(let
|
|
((next-binding (first remaining))
|
|
(vname
|
|
(if
|
|
(= (type-of (first next-binding)) "symbol")
|
|
(symbol-name (first next-binding))
|
|
(first next-binding))))
|
|
(make-cek-state
|
|
(nth next-binding 1)
|
|
local
|
|
(kont-push
|
|
(make-let-frame vname (rest remaining) body local)
|
|
rest-k))))))
|
|
("define"
|
|
(let
|
|
((name (get frame "name"))
|
|
(fenv (get frame "env"))
|
|
(has-effects (get frame "has-effects"))
|
|
(effect-list (get frame "effect-list")))
|
|
(when
|
|
(and (lambda? value) (nil? (lambda-name value)))
|
|
(set-lambda-name! value name))
|
|
(env-bind! fenv name value)
|
|
(when
|
|
has-effects
|
|
(let
|
|
((effect-names (map (fn (e) (if (= (type-of e) "symbol") (symbol-name e) e)) effect-list))
|
|
(effect-anns
|
|
(if
|
|
(env-has? fenv "*effect-annotations*")
|
|
(env-get fenv "*effect-annotations*")
|
|
(dict))))
|
|
(dict-set! effect-anns name effect-names)
|
|
(env-bind! fenv "*effect-annotations*" effect-anns)))
|
|
(make-cek-value value fenv rest-k)))
|
|
("set"
|
|
(let
|
|
((name (get frame "name")) (fenv (get frame "env")))
|
|
(env-set! fenv name value)
|
|
(make-cek-value value env rest-k)))
|
|
("and"
|
|
(if
|
|
(not value)
|
|
(make-cek-value value env rest-k)
|
|
(let
|
|
((remaining (get frame "remaining")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value env rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
(get frame "env")
|
|
(if
|
|
(= (len remaining) 1)
|
|
rest-k
|
|
(kont-push
|
|
(make-and-frame (rest remaining) (get frame "env"))
|
|
rest-k)))))))
|
|
("or"
|
|
(if
|
|
value
|
|
(make-cek-value value env rest-k)
|
|
(let
|
|
((remaining (get frame "remaining")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value false env rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
(get frame "env")
|
|
(if
|
|
(= (len remaining) 1)
|
|
rest-k
|
|
(kont-push
|
|
(make-or-frame (rest remaining) (get frame "env"))
|
|
rest-k)))))))
|
|
("cond"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(fenv (get frame "env"))
|
|
(scheme? (get frame "scheme")))
|
|
(if
|
|
scheme?
|
|
(if
|
|
value
|
|
(let
|
|
((clause (first remaining)))
|
|
(if
|
|
(and
|
|
(> (len clause) 2)
|
|
(= (type-of (nth clause 1)) "symbol")
|
|
(= (symbol-name (nth clause 1)) "=>"))
|
|
(make-cek-state
|
|
(nth clause 2)
|
|
fenv
|
|
(kont-push
|
|
(make-cond-arrow-frame value fenv)
|
|
rest-k))
|
|
(make-cek-state (nth clause 1) fenv rest-k)))
|
|
(let
|
|
((next-clauses (rest remaining)))
|
|
(if
|
|
(empty? next-clauses)
|
|
(make-cek-value nil fenv rest-k)
|
|
(let
|
|
((next-clause (first next-clauses))
|
|
(next-test (first next-clause)))
|
|
(if
|
|
(is-else-clause? next-test)
|
|
(make-cek-state (nth next-clause 1) fenv rest-k)
|
|
(make-cek-state
|
|
next-test
|
|
fenv
|
|
(kont-push
|
|
(make-cond-frame next-clauses fenv true)
|
|
rest-k)))))))
|
|
(if
|
|
value
|
|
(make-cek-state (nth remaining 1) fenv rest-k)
|
|
(let
|
|
((next (slice remaining 2 (len remaining))))
|
|
(if
|
|
(< (len next) 2)
|
|
(make-cek-value nil fenv rest-k)
|
|
(let
|
|
((next-test (first next)))
|
|
(if
|
|
(is-else-clause? next-test)
|
|
(make-cek-state (nth next 1) fenv rest-k)
|
|
(make-cek-state
|
|
next-test
|
|
fenv
|
|
(kont-push
|
|
(make-cond-frame next fenv false)
|
|
rest-k))))))))))
|
|
("case"
|
|
(let
|
|
((match-val (get frame "match-val"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(nil? match-val)
|
|
(sf-case-step-loop value remaining fenv rest-k)
|
|
(sf-case-step-loop match-val remaining fenv rest-k))))
|
|
("thread"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value fenv rest-k)
|
|
(let
|
|
((form (first remaining))
|
|
(rest-forms (rest remaining))
|
|
(new-kont
|
|
(if
|
|
(empty? (rest remaining))
|
|
rest-k
|
|
(kont-push
|
|
(make-thread-frame (rest remaining) fenv)
|
|
rest-k))))
|
|
(if
|
|
(and
|
|
(= (type-of form) "list")
|
|
(not (empty? form))
|
|
(= (type-of (first form)) "symbol")
|
|
(ho-form-name? (symbol-name (first form))))
|
|
(make-cek-state
|
|
(cons
|
|
(first form)
|
|
(cons (list (quote quote) value) (rest form)))
|
|
fenv
|
|
new-kont)
|
|
(let
|
|
((result (thread-insert-arg form value fenv)))
|
|
(if
|
|
(empty? rest-forms)
|
|
(make-cek-value result fenv rest-k)
|
|
(make-cek-value
|
|
result
|
|
fenv
|
|
(kont-push
|
|
(make-thread-frame rest-forms fenv)
|
|
rest-k)))))))))
|
|
("arg"
|
|
(let
|
|
((f (get frame "f"))
|
|
(evaled (get frame "evaled"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env"))
|
|
(raw-args (get frame "raw-args"))
|
|
(hname (get frame "head-name")))
|
|
(if
|
|
(nil? f)
|
|
(do
|
|
(when
|
|
(and *strict* hname)
|
|
(strict-check-args hname (list)))
|
|
(if
|
|
(empty? remaining)
|
|
(continue-with-call value (list) fenv raw-args rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-arg-frame
|
|
value
|
|
(list)
|
|
(rest remaining)
|
|
fenv
|
|
raw-args
|
|
hname)
|
|
rest-k))))
|
|
(let
|
|
((new-evaled (append evaled (list value))))
|
|
(if
|
|
(empty? remaining)
|
|
(do
|
|
(when
|
|
(and *strict* hname)
|
|
(strict-check-args hname new-evaled))
|
|
(continue-with-call f new-evaled fenv raw-args rest-k))
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-arg-frame
|
|
f
|
|
new-evaled
|
|
(rest remaining)
|
|
fenv
|
|
raw-args
|
|
hname)
|
|
rest-k)))))))
|
|
("dict"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(results (get frame "results"))
|
|
(fenv (get frame "env")))
|
|
(let
|
|
((last-result (last results))
|
|
(completed
|
|
(append
|
|
(slice results 0 (dec (len results)))
|
|
(list (list (first last-result) value)))))
|
|
(if
|
|
(empty? remaining)
|
|
(let
|
|
((d (dict)))
|
|
(for-each
|
|
(fn (pair) (dict-set! d (first pair) (nth pair 1)))
|
|
completed)
|
|
(make-cek-value d fenv rest-k))
|
|
(let
|
|
((next-entry (first remaining)))
|
|
(make-cek-state
|
|
(nth next-entry 1)
|
|
fenv
|
|
(kont-push
|
|
(make-dict-frame
|
|
(rest remaining)
|
|
(append
|
|
completed
|
|
(list (list (first next-entry))))
|
|
fenv)
|
|
rest-k)))))))
|
|
("ho-setup"
|
|
(let
|
|
((ho-type (get frame "ho-type"))
|
|
(remaining (get frame "remaining"))
|
|
(evaled (append (get frame "evaled") (list value)))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(ho-setup-dispatch ho-type evaled fenv rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-ho-setup-frame
|
|
ho-type
|
|
(rest remaining)
|
|
evaled
|
|
fenv)
|
|
rest-k)))))
|
|
("reset" (make-cek-value value env rest-k))
|
|
("deref"
|
|
(let
|
|
((val value) (fenv (get frame "env")))
|
|
(if
|
|
(not (signal? val))
|
|
(make-cek-value val fenv rest-k)
|
|
(if
|
|
(has-reactive-reset-frame? rest-k)
|
|
(reactive-shift-deref val fenv rest-k)
|
|
(do
|
|
(let
|
|
((ctx (context "sx-reactive" nil)))
|
|
(when
|
|
ctx
|
|
(let
|
|
((dep-list (get ctx "deps"))
|
|
(notify-fn (get ctx "notify")))
|
|
(when
|
|
(not (contains? dep-list val))
|
|
(append! dep-list val)
|
|
(signal-add-sub! val notify-fn)))))
|
|
(make-cek-value (signal-value val) fenv rest-k))))))
|
|
("reactive-reset"
|
|
(let
|
|
((update-fn (get frame "update-fn"))
|
|
(first? (get frame "first-render")))
|
|
(when
|
|
(and update-fn (not first?))
|
|
(cek-call update-fn (list value)))
|
|
(make-cek-value value env rest-k)))
|
|
("scope"
|
|
(let
|
|
((name (get frame "name"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(do (scope-pop! name) (make-cek-value value fenv rest-k))
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-scope-frame name (rest remaining) fenv)
|
|
rest-k)))))
|
|
("provide"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value fenv rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-provide-frame
|
|
(get frame "name")
|
|
(get frame "value")
|
|
(rest remaining)
|
|
fenv)
|
|
rest-k)))))
|
|
("scope-acc"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value fenv rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(let
|
|
((new-frame (make-scope-acc-frame (get frame "name") (get frame "value") (rest remaining) fenv)))
|
|
(dict-set! new-frame "emitted" (get frame "emitted"))
|
|
new-frame)
|
|
rest-k)))))
|
|
("map"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(results (get frame "results"))
|
|
(indexed (get frame "indexed"))
|
|
(fenv (get frame "env")))
|
|
(let
|
|
((new-results (append results (list value))))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value new-results fenv rest-k)
|
|
(let
|
|
((call-args (if indexed (list (len new-results) (first remaining)) (list (first remaining))))
|
|
(next-frame
|
|
(if
|
|
indexed
|
|
(make-map-indexed-frame
|
|
f
|
|
(rest remaining)
|
|
new-results
|
|
fenv)
|
|
(make-map-frame
|
|
f
|
|
(rest remaining)
|
|
new-results
|
|
fenv))))
|
|
(continue-with-call
|
|
f
|
|
call-args
|
|
fenv
|
|
(list)
|
|
(kont-push next-frame rest-k)))))))
|
|
("filter"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(results (get frame "results"))
|
|
(current-item (get frame "current-item"))
|
|
(fenv (get frame "env")))
|
|
(let
|
|
((new-results (if value (append results (list current-item)) results)))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value new-results fenv rest-k)
|
|
(continue-with-call
|
|
f
|
|
(list (first remaining))
|
|
fenv
|
|
(list)
|
|
(kont-push
|
|
(make-filter-frame
|
|
f
|
|
(rest remaining)
|
|
new-results
|
|
(first remaining)
|
|
fenv)
|
|
rest-k))))))
|
|
("reduce"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value fenv rest-k)
|
|
(continue-with-call
|
|
f
|
|
(list value (first remaining))
|
|
fenv
|
|
(list)
|
|
(kont-push
|
|
(make-reduce-frame f (rest remaining) fenv)
|
|
rest-k)))))
|
|
("for-each"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value nil fenv rest-k)
|
|
(continue-with-call
|
|
f
|
|
(list (first remaining))
|
|
fenv
|
|
(list)
|
|
(kont-push
|
|
(make-for-each-frame f (rest remaining) fenv)
|
|
rest-k)))))
|
|
("some"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
value
|
|
(make-cek-value value fenv rest-k)
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value false fenv rest-k)
|
|
(continue-with-call
|
|
f
|
|
(list (first remaining))
|
|
fenv
|
|
(list)
|
|
(kont-push
|
|
(make-some-frame f (rest remaining) fenv)
|
|
rest-k))))))
|
|
("every"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(not value)
|
|
(make-cek-value false fenv rest-k)
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value true fenv rest-k)
|
|
(continue-with-call
|
|
f
|
|
(list (first remaining))
|
|
fenv
|
|
(list)
|
|
(kont-push
|
|
(make-every-frame f (rest remaining) fenv)
|
|
rest-k))))))
|
|
("handler"
|
|
(let
|
|
((remaining (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(empty? remaining)
|
|
(make-cek-value value fenv rest-k)
|
|
(make-cek-state
|
|
(first remaining)
|
|
fenv
|
|
(kont-push
|
|
(make-handler-frame
|
|
(get frame "f")
|
|
(rest remaining)
|
|
fenv)
|
|
rest-k)))))
|
|
("restart" (make-cek-value value env rest-k))
|
|
("signal-return"
|
|
(let
|
|
((saved-kont (get frame "saved-kont")))
|
|
(make-cek-value value (get frame "env") saved-kont)))
|
|
("comp-trace" (make-cek-value value env rest-k))
|
|
("cond-arrow"
|
|
(let
|
|
((test-value (get frame "match-val"))
|
|
(fenv (get frame "env")))
|
|
(continue-with-call
|
|
value
|
|
(list test-value)
|
|
fenv
|
|
(list test-value)
|
|
rest-k)))
|
|
("raise-eval"
|
|
(let
|
|
((condition value)
|
|
(fenv (get frame "env"))
|
|
(continuable? (get frame "scheme"))
|
|
(handler-fn (kont-find-handler rest-k condition)))
|
|
(if
|
|
(nil? handler-fn)
|
|
(do
|
|
(set! *last-error-kont* rest-k)
|
|
(host-error
|
|
(str "Unhandled exception: " (inspect condition))))
|
|
(continue-with-call
|
|
handler-fn
|
|
(list condition)
|
|
fenv
|
|
(list condition)
|
|
(if
|
|
continuable?
|
|
(kont-push
|
|
(make-signal-return-frame fenv rest-k)
|
|
rest-k)
|
|
(kont-push (make-raise-guard-frame fenv rest-k) rest-k))))))
|
|
("raise-guard"
|
|
(do
|
|
(set! *last-error-kont* rest-k)
|
|
(host-error
|
|
"exception handler returned from non-continuable raise")))
|
|
("multi-map"
|
|
(let
|
|
((f (get frame "f"))
|
|
(remaining (get frame "remaining"))
|
|
(new-results (append (get frame "results") (list value)))
|
|
(fenv (get frame "env")))
|
|
(if
|
|
(some (fn (c) (empty? c)) remaining)
|
|
(make-cek-value new-results fenv rest-k)
|
|
(let
|
|
((heads (map (fn (c) (first c)) remaining))
|
|
(tails (map (fn (c) (rest c)) remaining)))
|
|
(continue-with-call
|
|
f
|
|
heads
|
|
fenv
|
|
(list)
|
|
(kont-push
|
|
(make-multi-map-frame f tails new-results fenv)
|
|
rest-k))))))
|
|
("callcc"
|
|
(let
|
|
((k (make-callcc-continuation rest-k)))
|
|
(continue-with-call
|
|
value
|
|
(list k)
|
|
(get frame "env")
|
|
(list k)
|
|
rest-k)))
|
|
("vm-resume"
|
|
(let
|
|
((resume-fn (get frame "f")))
|
|
(let
|
|
((result (apply resume-fn (list value))))
|
|
(if
|
|
(and (dict? result) (get result "__vm_suspended"))
|
|
(make-cek-suspended
|
|
(get result "request")
|
|
(get frame "env")
|
|
(kont-push
|
|
(make-vm-resume-frame
|
|
(get result "resume")
|
|
(get frame "env"))
|
|
rest-k))
|
|
(make-cek-value result (get frame "env") rest-k)))))
|
|
("perform" (make-cek-suspended value (get frame "env") rest-k))
|
|
("import"
|
|
(let
|
|
((import-set (get frame "args"))
|
|
(remaining-sets (get frame "remaining"))
|
|
(fenv (get frame "env")))
|
|
(do
|
|
(bind-import-set import-set fenv)
|
|
(if
|
|
(empty? remaining-sets)
|
|
(make-cek-value nil fenv rest-k)
|
|
(step-sf-import remaining-sets fenv rest-k)))))
|
|
(_
|
|
(do
|
|
(set! *last-error-kont* rest-k)
|
|
(error (str "Unknown frame type: " ft))))))))))
|
|
|
|
(define
|
|
continue-with-call
|
|
(fn
|
|
(f args env raw-args kont)
|
|
(cond
|
|
(callcc-continuation? f)
|
|
(let
|
|
((arg (if (empty? args) nil (first args)))
|
|
(captured (callcc-continuation-data f)))
|
|
(make-cek-value arg env captured))
|
|
(continuation? f)
|
|
(let
|
|
((arg (if (empty? args) nil (first args)))
|
|
(cont-data (continuation-data f)))
|
|
(let
|
|
((captured (get cont-data "captured")))
|
|
(let
|
|
((result (cek-run (make-cek-value arg env captured))))
|
|
(make-cek-value result env kont))))
|
|
(and
|
|
(callable? f)
|
|
(not (lambda? f))
|
|
(not (component? f))
|
|
(not (island? f)))
|
|
(make-cek-value (apply f args) env kont)
|
|
(lambda? f)
|
|
(let
|
|
((params (lambda-params f))
|
|
(local (env-merge (lambda-closure f) env)))
|
|
(when
|
|
(not (bind-lambda-params params args local))
|
|
(when
|
|
(> (len args) (len params))
|
|
(error
|
|
(str
|
|
(or (lambda-name f) "lambda")
|
|
" expects "
|
|
(len params)
|
|
" args, got "
|
|
(len args))))
|
|
(for-each
|
|
(fn (pair) (env-bind! local (first pair) (nth pair 1)))
|
|
(zip params args))
|
|
(for-each
|
|
(fn (p) (env-bind! local p nil))
|
|
(slice params (len args))))
|
|
(let
|
|
((jit-result (jit-try-call f args)))
|
|
(cond
|
|
(nil? jit-result)
|
|
(make-cek-state (lambda-body f) local kont)
|
|
(and (dict? jit-result) (get jit-result "__vm_suspended"))
|
|
(make-cek-suspended
|
|
(get jit-result "request")
|
|
env
|
|
(kont-push
|
|
(make-vm-resume-frame (get jit-result "resume") env)
|
|
kont))
|
|
:else (make-cek-value jit-result local kont))))
|
|
(or (component? f) (island? f))
|
|
(let
|
|
((parsed (parse-keyword-args raw-args env))
|
|
(kwargs (first parsed))
|
|
(children (nth parsed 1))
|
|
(local (env-merge (component-closure f) env)))
|
|
(for-each
|
|
(fn (p) (env-bind! local p (or (dict-get kwargs p) nil)))
|
|
(component-params f))
|
|
(when
|
|
(component-has-children? f)
|
|
(env-bind! local "children" children))
|
|
(make-cek-state
|
|
(component-body f)
|
|
local
|
|
(kont-push
|
|
(make-comp-trace-frame (component-name f) (component-file f))
|
|
kont)))
|
|
:else (error (str "Not callable: " (inspect f))))))
|
|
|
|
(define
|
|
sf-case-step-loop
|
|
(fn
|
|
(match-val clauses env kont)
|
|
(if
|
|
(< (len clauses) 2)
|
|
(make-cek-value nil env kont)
|
|
(let
|
|
((test (first clauses)) (body (nth clauses 1)))
|
|
(if
|
|
(is-else-clause? test)
|
|
(make-cek-state body env kont)
|
|
(let
|
|
((test-val (trampoline (eval-expr test env))))
|
|
(if
|
|
(= match-val test-val)
|
|
(make-cek-state body env kont)
|
|
(sf-case-step-loop match-val (slice clauses 2) env kont))))))))
|
|
|
|
(define
|
|
eval-expr-cek
|
|
(fn (expr env) (cek-run (make-cek-state expr env (list)))))
|
|
|
|
(define
|
|
trampoline-cek
|
|
(fn
|
|
(val)
|
|
(if (thunk? val) (eval-expr-cek (thunk-expr val) (thunk-env val)) val)))
|
|
|
|
(define
|
|
eval-expr
|
|
(fn (expr (env :as dict)) (cek-run (make-cek-state expr env (list)))))
|
|
|
|
(define
|
|
trampoline
|
|
(fn
|
|
(val)
|
|
(if (thunk? val) (eval-expr (thunk-expr val) (thunk-env val)) val)))
|