Files
rose-ash/lib/tests/vm-inline.sx
giles f3f70cc00b Move stdlib out of spec — clean spec/library boundary
spec/ now contains only the language definition (5 files):
  evaluator.sx, parser.sx, primitives.sx, render.sx, special-forms.sx

lib/ contains code written IN the language (8 files):
  stdlib.sx, types.sx, freeze.sx, content.sx,
  bytecode.sx, compiler.sx, vm.sx, callcc.sx

Test files follow source: spec/tests/ for core language tests,
lib/tests/ for library tests (continuations, freeze, types, vm).

Updated all consumers:
- JS/Python/OCaml bootstrappers: added lib/ to source search paths
- OCaml bridge: spec_dir for parser/render, lib_dir for compiler/freeze
- JS test runner: scans spec/tests/ (always) + lib/tests/ (--full)
- OCaml test runner: scans spec/tests/, lib tests via explicit request
- Docker dev mounts: added ./lib:/app/lib:ro

Tests: 1041 JS standard, 1322 JS full, 1101 OCaml — all pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:18:30 +00:00

118 lines
4.9 KiB
Plaintext

;; vm-inline.sx — Tests for inline VM opcodes (OP_ADD, OP_EQ, etc.)
;;
;; These verify that the JIT-compiled inline opcodes produce
;; identical results to the CALL_PRIM fallback.
;; --------------------------------------------------------------------------
;; Arithmetic
;; --------------------------------------------------------------------------
(test "inline + integers" (= (+ 3 4) 7))
(test "inline + floats" (= (+ 1.5 2.5) 4.0))
(test "inline + string concat" (= (+ "hello" " world") "hello world"))
(test "inline - integers" (= (- 10 3) 7))
(test "inline - negative" (= (- 3 10) -7))
(test "inline * integers" (= (* 6 7) 42))
(test "inline * float" (= (* 2.5 4.0) 10.0))
(test "inline / integers" (= (/ 10 2) 5))
(test "inline / float" (= (/ 7.0 2.0) 3.5))
(test "inline inc" (= (inc 5) 6))
(test "inline dec" (= (dec 5) 4))
(test "inline inc float" (= (inc 2.5) 3.5))
(test "inline dec zero" (= (dec 0) -1))
;; --------------------------------------------------------------------------
;; Comparison
;; --------------------------------------------------------------------------
(test "inline = numbers" (= 5 5))
(test "inline = strings" (= "hello" "hello"))
(test "inline = false" (not (= 5 6)))
(test "inline = nil" (= nil nil))
(test "inline = mixed false" (not (= 5 "5")))
(test "inline < numbers" (< 3 5))
(test "inline < false" (not (< 5 3)))
(test "inline < equal" (not (< 5 5)))
(test "inline < strings" (< "abc" "def"))
(test "inline > numbers" (> 5 3))
(test "inline > false" (not (> 3 5)))
(test "inline > equal" (not (> 5 5)))
(test "inline not true" (= (not true) false))
(test "inline not false" (= (not false) true))
(test "inline not nil" (= (not nil) true))
(test "inline not number" (= (not 0) true))
(test "inline not string" (= (not "") true))
(test "inline not nonempty" (= (not "x") false))
;; --------------------------------------------------------------------------
;; Collection ops
;; --------------------------------------------------------------------------
(test "inline len list" (= (len (list 1 2 3)) 3))
(test "inline len string" (= (len "hello") 5))
(test "inline len empty" (= (len (list)) 0))
(test "inline len nil" (= (len nil) 0))
(test "inline first" (= (first (list 10 20 30)) 10))
(test "inline first empty" (= (first (list)) nil))
(test "inline rest" (= (rest (list 1 2 3)) (list 2 3)))
(test "inline rest single" (= (rest (list 1)) (list)))
(test "inline nth" (= (nth (list 10 20 30) 1) 20))
(test "inline nth zero" (= (nth (list 10 20 30) 0) 10))
(test "inline nth out of bounds" (= (nth (list 1 2) 5) nil))
(test "inline cons" (= (cons 1 (list 2 3)) (list 1 2 3)))
(test "inline cons to empty" (= (cons 1 (list)) (list 1)))
(test "inline cons to nil" (= (cons 1 nil) (list 1)))
;; --------------------------------------------------------------------------
;; Composition — inline ops in expressions
;; --------------------------------------------------------------------------
(test "nested arithmetic" (= (+ (* 3 4) (- 10 5)) 17))
(test "comparison in if" (if (< 3 5) "yes" "no") (= "yes"))
(test "len in condition" (if (> (len (list 1 2 3)) 2) true false))
(test "inc in loop" (= (let ((x 0)) (for-each (fn (_) (set! x (inc x))) (list 1 2 3)) x) 3))
(test "first + rest roundtrip" (= (cons (first (list 1 2 3)) (rest (list 1 2 3))) (list 1 2 3)))
(test "nested comparison" (= (and (< 1 2) (> 3 0) (= 5 5)) true))
;; --------------------------------------------------------------------------
;; Edge cases
;; --------------------------------------------------------------------------
(test "+ with nil" (= (+ 5 nil) 5))
(test "len of dict" (= (len {:a 1 :b 2}) 2))
(test "= with booleans" (= (= true true) true))
(test "= with keywords" (= (= :foo :foo) true))
(test "not with list" (= (not (list 1)) false))
;; --------------------------------------------------------------------------
;; Recursive mutation — VM closure capture must preserve mutable state
;; --------------------------------------------------------------------------
;;
;; Regression: recursive functions that append! to a shared mutable list
;; lost mutations after the first call under JIT. The VM closure capture
;; was copying the list value instead of sharing the mutable reference.
(test "recursive append! to shared list"
(let ((walk (fn (items result)
(when (not (empty? items))
(append! result (first items))
(walk (rest items) result)))))
(let ((result (list)))
(walk (list "a" "b" "c") result)
(= (len result) 3))))
(test "recursive tree walk with append!"
(let ((walk (fn (expr result)
(cond
(not (list? expr))
(append! result "leaf")
(empty? expr) nil
:else
(do (append! result "open")
(for-each (fn (c) (walk c result)) (rest expr))
(append! result "close"))))))
(let ((tree (first (sx-parse "(div \"a\" (span \"b\") \"c\")")))
(result (list)))
(walk tree result)
(= (len result) 7))))