The sx-get links were doing full page refreshes because click handlers never attached. Root causes: VM frame management bug, missing primitives, CEK/VM type dispatch mismatch, and silent error swallowing. Fixes: - VM frame exhaustion: frames <- [] now properly pops to rest_frames - length primitive: add alias for len in OCaml primitives - call_sx_fn: use sx_call directly instead of eval_expr (CEK checks for type "lambda" but VmClosure reports "function") - Boot error surfacing: Sx.init() now has try/catch + failure summary - Callback error surfacing: catch-all handler for non-Eval_error exceptions - Silent JIT failures: log before CEK fallback instead of swallowing - vm→env sync: loadModule now calls sync_vm_to_env() - sx_build_bytecode MCP tool added for bytecode compilation Tests: 50 new tests across test-vm.sx and test-vm-primitives.sx covering nested VM calls, frame integrity, CEK bridge, primitive availability, cross-module symbol resolution, and callback dispatch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
173 lines
5.0 KiB
Plaintext
173 lines
5.0 KiB
Plaintext
(define
|
|
vm-eval
|
|
(fn
|
|
(expr)
|
|
(let
|
|
((code (compile expr)))
|
|
(vm-execute-module (code-from-value code) {}))))
|
|
|
|
(define
|
|
vm-eval-with
|
|
(fn
|
|
(expr globals)
|
|
(let
|
|
((code (compile expr)))
|
|
(vm-execute-module (code-from-value code) globals))))
|
|
|
|
(define
|
|
try-eval
|
|
(fn
|
|
(expr)
|
|
(try-catch (fn () (vm-eval expr)) (fn (err) (str "Error: " err)))))
|
|
|
|
(defsuite
|
|
"vm-primitive-availability"
|
|
(deftest
|
|
"length is available"
|
|
(assert-equal 3 (vm-eval (quote (length (list 1 2 3))))))
|
|
(deftest
|
|
"len is available"
|
|
(assert-equal 3 (vm-eval (quote (len (list 1 2 3))))))
|
|
(deftest
|
|
"first is available"
|
|
(assert-equal 1 (vm-eval (quote (first (list 1 2 3))))))
|
|
(deftest
|
|
"rest is available"
|
|
(assert-equal (list 2 3) (vm-eval (quote (rest (list 1 2 3))))))
|
|
(deftest
|
|
"nth is available"
|
|
(assert-equal 2 (vm-eval (quote (nth (list 1 2 3) 1)))))
|
|
(deftest
|
|
"get is available"
|
|
(assert-equal "v" (vm-eval (quote (get {:k "v"} :k)))))
|
|
(deftest
|
|
"type-of is available"
|
|
(assert-equal "number" (vm-eval (quote (type-of 42)))))
|
|
(deftest
|
|
"str concatenation works"
|
|
(assert-equal "ab" (vm-eval (quote (str "a" "b")))))
|
|
(deftest
|
|
"empty? is available"
|
|
(assert-equal true (vm-eval (quote (empty? (list))))))
|
|
(deftest
|
|
"not is available"
|
|
(assert-equal true (vm-eval (quote (not false)))))
|
|
(deftest
|
|
"append is available"
|
|
(assert-equal
|
|
(list 1 2 3)
|
|
(vm-eval (quote (append (list 1 2) (list 3))))))
|
|
(deftest
|
|
"assoc is available"
|
|
(assert-equal "b" (vm-eval (quote (get (assoc {:a "a"} :a "b") :a)))))
|
|
(deftest
|
|
"keys is available"
|
|
(assert-equal 1 (vm-eval (quote (len (keys {:a 1}))))))
|
|
(deftest
|
|
"contains? is available"
|
|
(assert-equal true (vm-eval (quote (contains? "hello" "ell")))))
|
|
(deftest
|
|
"starts-with? is available"
|
|
(assert-equal true (vm-eval (quote (starts-with? "hello" "hel")))))
|
|
(deftest
|
|
"split is available"
|
|
(assert-equal 3 (vm-eval (quote (len (split "a,b,c" ","))))))
|
|
(deftest
|
|
"join is available"
|
|
(assert-equal "a,b" (vm-eval (quote (join "," (list "a" "b"))))))
|
|
(deftest
|
|
"trim is available"
|
|
(assert-equal "x" (vm-eval (quote (trim " x ")))))
|
|
(deftest
|
|
"upper is available"
|
|
(assert-equal "ABC" (vm-eval (quote (upper "abc"))))))
|
|
|
|
(defsuite
|
|
"vm-cross-module-symbols"
|
|
(deftest
|
|
"module B sees module A definition"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with (quote (define helper (fn (x) (+ x 100)))) g)
|
|
(vm-eval-with (quote (define user (fn (x) (helper x)))) g)
|
|
(assert-equal 142 (vm-eval-with (quote (user 42)) g))))
|
|
(deftest
|
|
"module C sees definitions from both A and B"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with (quote (define a (fn (x) (* x 2)))) g)
|
|
(vm-eval-with (quote (define b (fn (x) (+ (a x) 1)))) g)
|
|
(vm-eval-with (quote (define c (fn (x) (b (a x))))) g)
|
|
(assert-equal 21 (vm-eval-with (quote (c 5)) g))))
|
|
(deftest
|
|
"late-defined symbol visible to earlier closure"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with (quote (define caller (fn () (callee 10)))) g)
|
|
(vm-eval-with (quote (define callee (fn (x) (* x 3)))) g)
|
|
(assert-equal 30 (vm-eval-with (quote (caller)) g)))))
|
|
|
|
(defsuite
|
|
"vm-callback-dispatch"
|
|
(deftest
|
|
"simple closure callback"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with
|
|
(quote (define make-fn (fn (n) (fn (x) (+ n x)))))
|
|
g)
|
|
(let
|
|
((result (vm-eval-with (quote (let ((f (make-fn 10))) (f 5))) g)))
|
|
(assert-equal 15 result))))
|
|
(deftest
|
|
"closure capturing multiple vars"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with
|
|
(quote (define make-adder (fn (a b) (fn (x) (+ a b x)))))
|
|
g)
|
|
(assert-equal 60 (vm-eval-with (quote ((make-adder 10 20) 30)) g))))
|
|
(deftest
|
|
"nested closure callback"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with
|
|
(quote (define wrap (fn (f) (fn (x) (f (f x))))))
|
|
g)
|
|
(vm-eval-with (quote (define inc (fn (x) (+ x 1)))) g)
|
|
(assert-equal 12 (vm-eval-with (quote ((wrap inc) 10)) g)))))
|
|
|
|
(defsuite
|
|
"vm-error-surfacing"
|
|
(deftest
|
|
"undefined symbol in compiled fn raises error"
|
|
(let
|
|
((g (dict)) (ok false))
|
|
(vm-eval-with (quote (define good-fn (fn () 42))) g)
|
|
(set! ok (= 42 (vm-eval-with (quote (good-fn)) g)))
|
|
(assert-equal true ok)))
|
|
(deftest
|
|
"compiled fn calling another compiled fn works"
|
|
(let
|
|
((g (dict)))
|
|
(vm-eval-with (quote (define a (fn (x) (+ x 1)))) g)
|
|
(vm-eval-with (quote (define b (fn (x) (a x)))) g)
|
|
(assert-equal 6 (vm-eval-with (quote (b 5)) g))))
|
|
(deftest
|
|
"when block executes all body forms"
|
|
(assert-equal
|
|
30
|
|
(vm-eval
|
|
(quote
|
|
(let ((x 0)) (when true (set! x 10) (set! x (+ x 20))) x)))))
|
|
(deftest
|
|
"when block: function call then side effect"
|
|
(assert-equal
|
|
50
|
|
(vm-eval
|
|
(quote
|
|
(let
|
|
((x 0) (f (fn (n) (* n 10))))
|
|
(when true (f 1) (set! x (f 5)))
|
|
x))))))
|