Step 3: IO registry — spec-level defio + io contract dispatch
Promotes defio from native OCaml special form to spec-level CEK evaluator feature. The IO registry is now the contract layer between evaluator and platform. Evaluator additions (spec/evaluator.sx): - *io-registry* mutable dict global (like *library-registry*) - io-register!, io-registered?, io-lookup, io-names accessors - defio-parse-kwargs! recursive keyword parser - sf-defio processes (defio "name" :category :data :params (...) ...) - "defio" dispatch in step-eval-list - step-sf-io: the contract function — validates against registry, then delegates to perform for IO suspension - "io" dispatch in step-eval-list Native OCaml defio handlers removed from: - sx_server.ml (~20 lines) - sx_browser.ml (~20 lines) - run_tests.ml (~18 lines) All replaced with __io-registry alias to spec's *io-registry*. IO accessor functions bound in run_tests.ml env so tests can call io-registered?, io-lookup, io-names. 10 new tests (spec/tests/test-io-registry.sx): - defio populates registry - io-lookup returns spec with name/category/returns/doc - io-registered?/io-names work correctly - kwargs parsing (batchable, cacheable, params) - io contract rejects unregistered ops - io contract passes validation for registered ops 2608/2608 tests passing (+10 new). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -610,24 +610,13 @@ let make_test_env () =
|
|||||||
island
|
island
|
||||||
| _ -> Nil)));
|
| _ -> Nil)));
|
||||||
|
|
||||||
(* defio — IO registry for platform suspension points *)
|
(* IO registry — spec-level defio populates *io-registry* in evaluator.
|
||||||
let io_registry = Hashtbl.create 64 in
|
Bind accessor functions + __io-registry alias for backward compat. *)
|
||||||
ignore (Sx_types.env_bind env "__io-registry" (Dict io_registry));
|
ignore (Sx_types.env_bind env "__io-registry" Sx_ref._io_registry_);
|
||||||
ignore (Sx_ref.register_special_form (String "defio") (NativeFn ("defio", fun sf_args ->
|
bind "io-registered?" (fun args -> match args with [String n] -> Sx_ref.io_registered_p (String n) | _ -> Bool false);
|
||||||
let raw_args = match sf_args with
|
bind "io-lookup" (fun args -> match args with [String n] -> Sx_ref.io_lookup (String n) | _ -> Nil);
|
||||||
| [List a; Env _] | [ListRef { contents = a }; Env _] -> a
|
bind "io-names" (fun _args -> Sx_ref.io_names ());
|
||||||
| _ -> [] in
|
bind "io-register!" (fun args -> match args with [String n; spec] -> Sx_ref.io_register_b (String n) spec | _ -> Nil);
|
||||||
match raw_args with
|
|
||||||
| String name :: rest ->
|
|
||||||
let entry = Hashtbl.create 8 in
|
|
||||||
let rec parse = function
|
|
||||||
| Keyword k :: v :: rest -> Hashtbl.replace entry k v; parse rest
|
|
||||||
| _ -> () in
|
|
||||||
parse rest;
|
|
||||||
Hashtbl.replace entry "name" (String name);
|
|
||||||
Hashtbl.replace io_registry name (Dict entry);
|
|
||||||
Dict entry
|
|
||||||
| _ -> Nil)));
|
|
||||||
|
|
||||||
(* --- Primitives for canonical.sx / content tests --- *)
|
(* --- Primitives for canonical.sx / content tests --- *)
|
||||||
bind "contains-char?" (fun args ->
|
bind "contains-char?" (fun args ->
|
||||||
|
|||||||
@@ -2007,26 +2007,9 @@ let http_setup_declarative_stubs env =
|
|||||||
noop "defaction";
|
noop "defaction";
|
||||||
noop "defrelation";
|
noop "defrelation";
|
||||||
noop "defstyle";
|
noop "defstyle";
|
||||||
(* IO registry — starts empty, platforms extend via defio.
|
(* IO registry — spec-level defio populates *io-registry* in evaluator.
|
||||||
defio is a special form that populates __io-registry with metadata
|
Alias as __io-registry for backward compat. *)
|
||||||
about suspension points (IO ops that require platform resolution). *)
|
ignore (env_bind env "__io-registry" Sx_ref._io_registry_)
|
||||||
let io_registry = Hashtbl.create 64 in
|
|
||||||
ignore (env_bind env "__io-registry" (Dict io_registry));
|
|
||||||
ignore (Sx_ref.register_special_form (String "defio") (NativeFn ("defio", fun sf_args ->
|
|
||||||
let raw_args = match sf_args with
|
|
||||||
| [List a; Env _] | [ListRef { contents = a }; Env _] -> a
|
|
||||||
| _ -> [] in
|
|
||||||
match raw_args with
|
|
||||||
| String name :: rest ->
|
|
||||||
let entry = Hashtbl.create 8 in
|
|
||||||
let rec parse = function
|
|
||||||
| Keyword k :: v :: rest -> Hashtbl.replace entry k v; parse rest
|
|
||||||
| _ -> () in
|
|
||||||
parse rest;
|
|
||||||
Hashtbl.replace entry "name" (String name);
|
|
||||||
Hashtbl.replace io_registry name (Dict entry);
|
|
||||||
Dict entry
|
|
||||||
| _ -> Nil)))
|
|
||||||
|
|
||||||
let http_setup_platform_constructors env =
|
let http_setup_platform_constructors env =
|
||||||
(* Platform constructor functions expected by evaluator.sx.
|
(* Platform constructor functions expected by evaluator.sx.
|
||||||
|
|||||||
@@ -670,26 +670,9 @@ let () =
|
|||||||
|
|
||||||
bind "define-page-helper" (fun _ -> Nil);
|
bind "define-page-helper" (fun _ -> Nil);
|
||||||
|
|
||||||
(* IO registry — starts empty in browser. Platforms extend via defio.
|
(* IO registry — spec-level defio populates *io-registry* in evaluator.
|
||||||
Browser has zero suspension points initially; future browser IO
|
Alias as __io-registry for backward compat. *)
|
||||||
(lazy module loads, fetch-request) will add entries here. *)
|
ignore (env_bind global_env "__io-registry" Sx_ref._io_registry_);
|
||||||
let io_registry = Hashtbl.create 16 in
|
|
||||||
ignore (env_bind global_env "__io-registry" (Dict io_registry));
|
|
||||||
ignore (Sx_ref.register_special_form (String "defio") (NativeFn ("defio", fun sf_args ->
|
|
||||||
let raw_args = match sf_args with
|
|
||||||
| [List a; Env _] | [ListRef { contents = a }; Env _] -> a
|
|
||||||
| _ -> [] in
|
|
||||||
match raw_args with
|
|
||||||
| String name :: rest ->
|
|
||||||
let entry = Hashtbl.create 8 in
|
|
||||||
let rec parse = function
|
|
||||||
| Keyword k :: v :: rest -> Hashtbl.replace entry k v; parse rest
|
|
||||||
| _ -> () in
|
|
||||||
parse rest;
|
|
||||||
Hashtbl.replace entry "name" (String name);
|
|
||||||
Hashtbl.replace io_registry name (Dict entry);
|
|
||||||
Dict entry
|
|
||||||
| _ -> Nil)));
|
|
||||||
|
|
||||||
(* --- Render --- *)
|
(* --- Render --- *)
|
||||||
Sx_render.setup_render_env global_env;
|
Sx_render.setup_render_env global_env;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -159,6 +159,7 @@ let take a b = _prim "take" [a; b]
|
|||||||
let drop a b = _prim "drop" [a; b]
|
let drop a b = _prim "drop" [a; b]
|
||||||
|
|
||||||
(* Predicates *)
|
(* Predicates *)
|
||||||
|
let keyword_p a = _prim "keyword?" [a]
|
||||||
let empty_p a = _prim "empty?" [a]
|
let empty_p a = _prim "empty?" [a]
|
||||||
let number_p a = _prim "number?" [a]
|
let number_p a = _prim "number?" [a]
|
||||||
let string_p a = _prim "string?" [a]
|
let string_p a = _prim "string?" [a]
|
||||||
|
|||||||
@@ -391,6 +391,30 @@
|
|||||||
(spec exports)
|
(spec exports)
|
||||||
(dict-set! *library-registry* (library-name-key spec) {:exports 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
|
(define
|
||||||
trampoline
|
trampoline
|
||||||
(fn
|
(fn
|
||||||
@@ -403,16 +427,15 @@
|
|||||||
(trampoline (eval-expr (thunk-expr result) (thunk-env result)))
|
(trampoline (eval-expr (thunk-expr result) (thunk-env result)))
|
||||||
result)))))
|
result)))))
|
||||||
|
|
||||||
;; Cond/case helpers
|
|
||||||
(define *strict* false)
|
(define *strict* false)
|
||||||
|
|
||||||
(define set-strict! (fn (val) (set! *strict* val)))
|
(define set-strict! (fn (val) (set! *strict* val)))
|
||||||
|
|
||||||
;; Special form constructors — build state for CEK evaluation
|
|
||||||
(define *prim-param-types* nil)
|
(define *prim-param-types* nil)
|
||||||
|
|
||||||
(define set-prim-param-types! (fn (types) (set! *prim-param-types* types)))
|
(define set-prim-param-types! (fn (types) (set! *prim-param-types* types)))
|
||||||
|
|
||||||
|
;; Quasiquote expansion
|
||||||
(define
|
(define
|
||||||
value-matches-type?
|
value-matches-type?
|
||||||
(fn
|
(fn
|
||||||
@@ -577,7 +600,7 @@
|
|||||||
(env-bind! local "children" children))
|
(env-bind! local "children" children))
|
||||||
(make-thunk (component-body comp) local))))
|
(make-thunk (component-body comp) local))))
|
||||||
|
|
||||||
;; Quasiquote expansion
|
;; Macro expansion — expand then re-evaluate the result
|
||||||
(define
|
(define
|
||||||
parse-keyword-args
|
parse-keyword-args
|
||||||
(fn
|
(fn
|
||||||
@@ -609,6 +632,14 @@
|
|||||||
raw-args)
|
raw-args)
|
||||||
(list kwargs children))))
|
(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
|
(define
|
||||||
cond-scheme?
|
cond-scheme?
|
||||||
(fn
|
(fn
|
||||||
@@ -683,6 +714,12 @@
|
|||||||
((init-vals (map (fn (e) (trampoline (eval-expr e env))) inits)))
|
((init-vals (map (fn (e) (trampoline (eval-expr e env))) inits)))
|
||||||
(cek-call loop-fn init-vals))))))
|
(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
|
(define
|
||||||
sf-lambda
|
sf-lambda
|
||||||
(fn
|
(fn
|
||||||
@@ -712,6 +749,7 @@
|
|||||||
params-expr)))
|
params-expr)))
|
||||||
(make-lambda param-names body env))))
|
(make-lambda param-names body env))))
|
||||||
|
|
||||||
|
;; R7RS guard: desugars to call/cc + handler-bind with sentinel re-raise
|
||||||
(define
|
(define
|
||||||
sf-defcomp
|
sf-defcomp
|
||||||
(fn
|
(fn
|
||||||
@@ -749,7 +787,9 @@
|
|||||||
(env-bind! env (symbol-name name-sym) comp)
|
(env-bind! env (symbol-name name-sym) comp)
|
||||||
comp))))
|
comp))))
|
||||||
|
|
||||||
;; Macro expansion — expand then re-evaluate the result
|
;; 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
|
(define
|
||||||
defcomp-kwarg
|
defcomp-kwarg
|
||||||
(fn
|
(fn
|
||||||
@@ -772,14 +812,7 @@
|
|||||||
(range 2 end 1))
|
(range 2 end 1))
|
||||||
result)))
|
result)))
|
||||||
|
|
||||||
;; ═══════════════════════════════════════════════════════════════
|
;; call/cc: capture entire kont as undelimited escape continuation
|
||||||
;; 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
|
(define
|
||||||
parse-comp-params
|
parse-comp-params
|
||||||
(fn
|
(fn
|
||||||
@@ -851,6 +884,31 @@
|
|||||||
(env-bind! env (symbol-name name-sym) island)
|
(env-bind! env (symbol-name name-sym) island)
|
||||||
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
|
(define
|
||||||
sf-defmacro
|
sf-defmacro
|
||||||
(fn
|
(fn
|
||||||
@@ -867,12 +925,6 @@
|
|||||||
(env-bind! env (symbol-name name-sym) mac)
|
(env-bind! env (symbol-name name-sym) mac)
|
||||||
mac))))
|
mac))))
|
||||||
|
|
||||||
;; ═══════════════════════════════════════════════════════════════
|
|
||||||
;; 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
|
(define
|
||||||
parse-macro-params
|
parse-macro-params
|
||||||
(fn
|
(fn
|
||||||
@@ -901,7 +953,6 @@
|
|||||||
params-expr)
|
params-expr)
|
||||||
(list params rest-param))))
|
(list params rest-param))))
|
||||||
|
|
||||||
;; R7RS guard: desugars to call/cc + handler-bind with sentinel re-raise
|
|
||||||
(define
|
(define
|
||||||
qq-expand
|
qq-expand
|
||||||
(fn
|
(fn
|
||||||
@@ -941,9 +992,6 @@
|
|||||||
(list)
|
(list)
|
||||||
template)))))))
|
template)))))))
|
||||||
|
|
||||||
;; 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
|
(define
|
||||||
sf-letrec
|
sf-letrec
|
||||||
(fn
|
(fn
|
||||||
@@ -999,7 +1047,6 @@
|
|||||||
(slice body 0 (dec (len body))))
|
(slice body 0 (dec (len body))))
|
||||||
(make-thunk (last body) local))))
|
(make-thunk (last body) local))))
|
||||||
|
|
||||||
;; call/cc: capture entire kont as undelimited escape continuation
|
|
||||||
(define
|
(define
|
||||||
step-sf-letrec
|
step-sf-letrec
|
||||||
(fn
|
(fn
|
||||||
@@ -1045,7 +1092,6 @@
|
|||||||
(scope-pop! name)
|
(scope-pop! name)
|
||||||
result))))
|
result))))
|
||||||
|
|
||||||
;; Pattern matching (match form)
|
|
||||||
(define
|
(define
|
||||||
sf-provide
|
sf-provide
|
||||||
(fn
|
(fn
|
||||||
@@ -1062,7 +1108,6 @@
|
|||||||
(scope-pop! name)
|
(scope-pop! name)
|
||||||
result)))
|
result)))
|
||||||
|
|
||||||
;; Condition system special forms
|
|
||||||
(define
|
(define
|
||||||
expand-macro
|
expand-macro
|
||||||
(fn
|
(fn
|
||||||
@@ -1265,6 +1310,7 @@
|
|||||||
env
|
env
|
||||||
kont))))
|
kont))))
|
||||||
|
|
||||||
|
;; Scope/provide/context — structured downward data passing
|
||||||
(define
|
(define
|
||||||
step-eval-list
|
step-eval-list
|
||||||
(fn
|
(fn
|
||||||
@@ -1304,6 +1350,8 @@
|
|||||||
("defcomp" (make-cek-value (sf-defcomp args env) env kont))
|
("defcomp" (make-cek-value (sf-defcomp args env) env kont))
|
||||||
("defisland" (make-cek-value (sf-defisland args env) env kont))
|
("defisland" (make-cek-value (sf-defisland args env) env kont))
|
||||||
("defmacro" (make-cek-value (sf-defmacro 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))
|
("begin" (step-sf-begin args env kont))
|
||||||
("do"
|
("do"
|
||||||
(if
|
(if
|
||||||
@@ -1527,6 +1575,7 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-perform-frame env) kont)))))
|
(kont-push (make-perform-frame env) kont)))))
|
||||||
|
|
||||||
|
;; Delimited continuations
|
||||||
(define
|
(define
|
||||||
step-sf-callcc
|
step-sf-callcc
|
||||||
(fn
|
(fn
|
||||||
@@ -1553,6 +1602,7 @@
|
|||||||
(list local body)
|
(list local body)
|
||||||
(match-find-clause val (rest clauses) env))))))
|
(match-find-clause val (rest clauses) env))))))
|
||||||
|
|
||||||
|
;; Signal dereferencing with reactive dependency tracking
|
||||||
(define
|
(define
|
||||||
match-pattern
|
match-pattern
|
||||||
(fn
|
(fn
|
||||||
@@ -1585,7 +1635,13 @@
|
|||||||
pairs)))
|
pairs)))
|
||||||
:else (= pattern value))))
|
:else (= pattern value))))
|
||||||
|
|
||||||
;; Scope/provide/context — structured downward data passing
|
;; ═══════════════════════════════════════════════════════════════
|
||||||
|
;; 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
|
(define
|
||||||
step-sf-match
|
step-sf-match
|
||||||
(fn
|
(fn
|
||||||
@@ -1600,6 +1656,7 @@
|
|||||||
(error (str "match: no clause matched " (inspect val)))
|
(error (str "match: no clause matched " (inspect val)))
|
||||||
(make-cek-state (nth result 1) (first result) kont))))))
|
(make-cek-state (nth result 1) (first result) kont))))))
|
||||||
|
|
||||||
|
;; Reactive signal tracking — captures dependency continuation for re-render
|
||||||
(define
|
(define
|
||||||
step-sf-handler-bind
|
step-sf-handler-bind
|
||||||
(fn
|
(fn
|
||||||
@@ -1647,6 +1704,13 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-restart-frame restarts (list) env) kont)))))
|
(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
|
(define
|
||||||
step-sf-signal
|
step-sf-signal
|
||||||
(fn
|
(fn
|
||||||
@@ -1692,7 +1756,6 @@
|
|||||||
(env-bind! restart-env (first params) restart-arg))
|
(env-bind! restart-env (first params) restart-arg))
|
||||||
(make-cek-state body restart-env rest-kont)))))))
|
(make-cek-state body restart-env rest-kont)))))))
|
||||||
|
|
||||||
;; Delimited continuations
|
|
||||||
(define
|
(define
|
||||||
step-sf-if
|
step-sf-if
|
||||||
(fn
|
(fn
|
||||||
@@ -1716,7 +1779,6 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-when-frame (rest args) env) kont))))
|
(kont-push (make-when-frame (rest args) env) kont))))
|
||||||
|
|
||||||
;; Signal dereferencing with reactive dependency tracking
|
|
||||||
(define
|
(define
|
||||||
step-sf-begin
|
step-sf-begin
|
||||||
(fn
|
(fn
|
||||||
@@ -1732,13 +1794,6 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-begin-frame (rest args) env) kont))))))
|
(kont-push (make-begin-frame (rest args) env) kont))))))
|
||||||
|
|
||||||
;; ═══════════════════════════════════════════════════════════════
|
|
||||||
;; 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
|
(define
|
||||||
step-sf-let
|
step-sf-let
|
||||||
(fn
|
(fn
|
||||||
@@ -1783,7 +1838,6 @@
|
|||||||
(make-let-frame vname rest-bindings body local)
|
(make-let-frame vname rest-bindings body local)
|
||||||
kont)))))))))
|
kont)))))))))
|
||||||
|
|
||||||
;; Reactive signal tracking — captures dependency continuation for re-render
|
|
||||||
(define
|
(define
|
||||||
step-sf-define
|
step-sf-define
|
||||||
(fn
|
(fn
|
||||||
@@ -1831,13 +1885,6 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-set-frame (symbol-name (first args)) env) kont))))
|
(kont-push (make-set-frame (symbol-name (first args)) 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
|
(define
|
||||||
step-sf-and
|
step-sf-and
|
||||||
(fn
|
(fn
|
||||||
@@ -1895,6 +1942,14 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-cond-frame args env false) kont)))))))))
|
(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
|
(define
|
||||||
step-sf-case
|
step-sf-case
|
||||||
(fn
|
(fn
|
||||||
@@ -1904,6 +1959,9 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-case-frame nil (rest args) env) kont))))
|
(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
|
(define
|
||||||
step-sf-thread-first
|
step-sf-thread-first
|
||||||
(fn
|
(fn
|
||||||
@@ -1917,6 +1975,13 @@
|
|||||||
step-sf-lambda
|
step-sf-lambda
|
||||||
(fn (args env kont) (make-cek-value (sf-lambda args env) env kont)))
|
(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
|
(define
|
||||||
step-sf-scope
|
step-sf-scope
|
||||||
(fn
|
(fn
|
||||||
@@ -2022,14 +2087,6 @@
|
|||||||
env
|
env
|
||||||
kont))))
|
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
|
(define
|
||||||
step-sf-reset
|
step-sf-reset
|
||||||
(fn
|
(fn
|
||||||
@@ -2039,9 +2096,6 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-reset-frame env) kont))))
|
(kont-push (make-reset-frame 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
|
(define
|
||||||
step-sf-shift
|
step-sf-shift
|
||||||
(fn
|
(fn
|
||||||
@@ -2068,13 +2122,6 @@
|
|||||||
env
|
env
|
||||||
(kont-push (make-deref-frame env) kont))))
|
(kont-push (make-deref-frame 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
|
(define
|
||||||
cek-call
|
cek-call
|
||||||
(fn
|
(fn
|
||||||
|
|||||||
101
spec/tests/test-io-registry.sx
Normal file
101
spec/tests/test-io-registry.sx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
;; IO registry tests — defio, *io-registry*, accessor functions, io contract
|
||||||
|
(defsuite
|
||||||
|
"io-registry-basic"
|
||||||
|
(deftest
|
||||||
|
"defio registers an IO operation"
|
||||||
|
(defio
|
||||||
|
"test-io-basic"
|
||||||
|
:category :data
|
||||||
|
:params ()
|
||||||
|
:returns "string"
|
||||||
|
:doc "Basic test op.")
|
||||||
|
(assert (io-registered? "test-io-basic")))
|
||||||
|
(deftest
|
||||||
|
"io-lookup returns spec dict"
|
||||||
|
(defio
|
||||||
|
"test-io-lookup"
|
||||||
|
:category :effect
|
||||||
|
:params (x)
|
||||||
|
:returns "nil"
|
||||||
|
:doc "Test effect.")
|
||||||
|
(let
|
||||||
|
((spec (io-lookup "test-io-lookup")))
|
||||||
|
(assert= (get spec "name") "test-io-lookup")
|
||||||
|
(assert= (keyword-name (get spec "category")) "effect")
|
||||||
|
(assert= (get spec "returns") "nil")
|
||||||
|
(assert= (get spec "doc") "Test effect.")))
|
||||||
|
(deftest
|
||||||
|
"io-registered? returns false for unknown"
|
||||||
|
(assert (not (io-registered? "nonexistent-io-op"))))
|
||||||
|
(deftest
|
||||||
|
"io-names includes registered ops"
|
||||||
|
(defio
|
||||||
|
"test-io-names"
|
||||||
|
:category :data
|
||||||
|
:params ()
|
||||||
|
:returns "any"
|
||||||
|
:doc "Names test.")
|
||||||
|
(assert (contains? (io-names) "test-io-names")))
|
||||||
|
(deftest
|
||||||
|
"defio returns the spec dict"
|
||||||
|
(let
|
||||||
|
((result (defio "test-io-ret" :category :code :params (a b) :returns "string" :doc "Return test.")))
|
||||||
|
(assert= (get result "name") "test-io-ret")
|
||||||
|
(assert= (keyword-name (get result "category")) "code"))))
|
||||||
|
|
||||||
|
(defsuite
|
||||||
|
"io-registry-kwargs"
|
||||||
|
(deftest
|
||||||
|
"defio parses batchable flag"
|
||||||
|
(defio
|
||||||
|
"test-io-batch"
|
||||||
|
:category :code
|
||||||
|
:params (code lang)
|
||||||
|
:returns "string"
|
||||||
|
:batchable true
|
||||||
|
:doc "Batchable op.")
|
||||||
|
(assert= (get (io-lookup "test-io-batch") "batchable") true))
|
||||||
|
(deftest
|
||||||
|
"defio parses cacheable flag"
|
||||||
|
(defio
|
||||||
|
"test-io-cache"
|
||||||
|
:category :data
|
||||||
|
:params ()
|
||||||
|
:returns "list"
|
||||||
|
:cacheable true
|
||||||
|
:doc "Cacheable op.")
|
||||||
|
(assert= (get (io-lookup "test-io-cache") "cacheable") true))
|
||||||
|
(deftest
|
||||||
|
"defio parses params list"
|
||||||
|
(defio
|
||||||
|
"test-io-params"
|
||||||
|
:category :data
|
||||||
|
:params (a b c)
|
||||||
|
:returns "list"
|
||||||
|
:doc "Multi param.")
|
||||||
|
(assert= (len (get (io-lookup "test-io-params") "params")) 3)))
|
||||||
|
|
||||||
|
(defsuite
|
||||||
|
"io-contract"
|
||||||
|
(deftest
|
||||||
|
"io rejects unregistered operations"
|
||||||
|
(let
|
||||||
|
((caught false))
|
||||||
|
(try-catch
|
||||||
|
(fn () (io "totally-unknown-op-xyz"))
|
||||||
|
(fn (err) (set! caught true)))
|
||||||
|
(assert caught)))
|
||||||
|
(deftest
|
||||||
|
"io suspends for registered operations"
|
||||||
|
(defio
|
||||||
|
"test-io-contract"
|
||||||
|
:category :data
|
||||||
|
:params ()
|
||||||
|
:returns "string"
|
||||||
|
:doc "Contract test.")
|
||||||
|
(let
|
||||||
|
((caught-msg ""))
|
||||||
|
(try-catch
|
||||||
|
(fn () (io "test-io-contract"))
|
||||||
|
(fn (err) (set! caught-msg err)))
|
||||||
|
(assert (not (string-contains? caught-msg "unknown operation"))))))
|
||||||
Reference in New Issue
Block a user