(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))))))