Use match for value dispatch in evaluator and compiler
Convert large cond chains doing string equality dispatch to use the match special form: step-eval-list (42 arms), step-continue (31 arms), compile-list (30 arms), ho-setup-dispatch (7 arms), value-matches-type? (10 arms). Also fix test-canonical.sx to use defsuite/deftest format and load canonical.sx in both test runners. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,6 +82,18 @@ env["env-merge"] = function(a, b) { return Object.assign({}, a, b); };
|
|||||||
|
|
||||||
// Missing primitives referenced by tests
|
// Missing primitives referenced by tests
|
||||||
// primitive? is now in platform.py PRIMITIVES
|
// primitive? is now in platform.py PRIMITIVES
|
||||||
|
env["contains-char?"] = function(s, c) { return typeof s === "string" && typeof c === "string" && s.indexOf(c) >= 0; };
|
||||||
|
env["escape-string"] = function(s) { return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t"); };
|
||||||
|
env["trim-right"] = function(s) { return typeof s === "string" ? s.trimEnd() : s; };
|
||||||
|
env["sha3-256"] = function(s) {
|
||||||
|
// Simple hash stub for testing — not real SHA3
|
||||||
|
var h = 0;
|
||||||
|
for (var i = 0; i < s.length; i++) { h = ((h << 5) - h + s.charCodeAt(i)) | 0; }
|
||||||
|
h = Math.abs(h);
|
||||||
|
var hex = h.toString(16);
|
||||||
|
while (hex.length < 64) hex = "0" + hex;
|
||||||
|
return hex;
|
||||||
|
};
|
||||||
env["upcase"] = function(s) { return s.toUpperCase(); };
|
env["upcase"] = function(s) { return s.toUpperCase(); };
|
||||||
env["downcase"] = function(s) { return s.toLowerCase(); };
|
env["downcase"] = function(s) { return s.toLowerCase(); };
|
||||||
env["make-keyword"] = function(name) { return new Sx.Keyword(name); };
|
env["make-keyword"] = function(name) { return new Sx.Keyword(name); };
|
||||||
@@ -291,6 +303,18 @@ if (fs.existsSync(harnessPath)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load canonical.sx (content-addressing, serialization)
|
||||||
|
const canonicalPath = path.join(projectDir, "spec", "canonical.sx");
|
||||||
|
if (fs.existsSync(canonicalPath)) {
|
||||||
|
const canonicalSrc = fs.readFileSync(canonicalPath, "utf8");
|
||||||
|
const canonicalExprs = Sx.parse(canonicalSrc);
|
||||||
|
for (const expr of canonicalExprs) {
|
||||||
|
try { Sx.eval(expr, env); } catch (e) {
|
||||||
|
console.error(`Error loading canonical.sx: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load compiler + VM from lib/ when running full tests
|
// Load compiler + VM from lib/ when running full tests
|
||||||
if (fullBuild) {
|
if (fullBuild) {
|
||||||
const libDir = path.join(projectDir, "lib");
|
const libDir = path.join(projectDir, "lib");
|
||||||
|
|||||||
@@ -390,6 +390,22 @@ let make_test_env () =
|
|||||||
bind "defeffect" (fun _args -> Nil);
|
bind "defeffect" (fun _args -> Nil);
|
||||||
|
|
||||||
(* --- Primitives for canonical.sx / content tests --- *)
|
(* --- Primitives for canonical.sx / content tests --- *)
|
||||||
|
bind "symbol-name" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Symbol s] -> String s
|
||||||
|
| _ -> raise (Eval_error "symbol-name: expected symbol"));
|
||||||
|
bind "keyword-name" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [Keyword k] -> String k
|
||||||
|
| _ -> raise (Eval_error "keyword-name: expected keyword"));
|
||||||
|
bind "trim-right" (fun args ->
|
||||||
|
match args with
|
||||||
|
| [String s] ->
|
||||||
|
let len = String.length s in
|
||||||
|
let i = ref (len - 1) in
|
||||||
|
while !i >= 0 && (s.[!i] = ' ' || s.[!i] = '\t' || s.[!i] = '\n' || s.[!i] = '\r') do decr i done;
|
||||||
|
String (String.sub s 0 (!i + 1))
|
||||||
|
| _ -> raise (Eval_error "trim-right: expected string"));
|
||||||
bind "contains-char?" (fun args ->
|
bind "contains-char?" (fun args ->
|
||||||
match args with
|
match args with
|
||||||
| [String s; String c] when String.length c = 1 ->
|
| [String s; String c] when String.length c = 1 ->
|
||||||
@@ -809,6 +825,8 @@ let run_spec_tests env test_files =
|
|||||||
with e -> Printf.eprintf "Warning: %s: %s\n%!" name (Printexc.to_string e))
|
with e -> Printf.eprintf "Warning: %s: %s\n%!" name (Printexc.to_string e))
|
||||||
end
|
end
|
||||||
in
|
in
|
||||||
|
(* Content-addressing, serialization *)
|
||||||
|
load_module "canonical.sx" spec_dir;
|
||||||
(* Render adapter for test-render-html.sx *)
|
(* Render adapter for test-render-html.sx *)
|
||||||
load_module "render.sx" spec_dir;
|
load_module "render.sx" spec_dir;
|
||||||
load_module "adapter-html.sx" web_dir;
|
load_module "adapter-html.sx" web_dir;
|
||||||
|
|||||||
1260
lib/compiler.sx
1260
lib/compiler.sx
File diff suppressed because it is too large
Load Diff
1445
spec/evaluator.sx
1445
spec/evaluator.sx
File diff suppressed because it is too large
Load Diff
@@ -1,91 +1,97 @@
|
|||||||
(test-group
|
(defsuite
|
||||||
"canonical-serialize"
|
"canonical-serialize"
|
||||||
(test "nil" (assert= (canonical-serialize nil) "nil"))
|
(deftest "nil" (assert= (canonical-serialize nil) "nil"))
|
||||||
(test
|
(deftest
|
||||||
"booleans"
|
"booleans"
|
||||||
(assert= (canonical-serialize true) "true")
|
(assert= (canonical-serialize true) "true")
|
||||||
(assert= (canonical-serialize false) "false"))
|
(assert= (canonical-serialize false) "false"))
|
||||||
(test
|
(deftest
|
||||||
"integers"
|
"integers"
|
||||||
(assert= (canonical-serialize 0) "0")
|
(assert= (canonical-serialize 0) "0")
|
||||||
(assert= (canonical-serialize 42) "42")
|
(assert= (canonical-serialize 42) "42")
|
||||||
(assert= (canonical-serialize -7) "-7"))
|
(assert= (canonical-serialize -7) "-7"))
|
||||||
(test
|
(deftest
|
||||||
"strings"
|
"strings"
|
||||||
(assert= (canonical-serialize "hello") "\"hello\"")
|
(assert= (canonical-serialize "hello") "\"hello\"")
|
||||||
(assert= (canonical-serialize "") "\"\"")
|
(assert= (canonical-serialize "") "\"\"")
|
||||||
(assert= (canonical-serialize "a\"b") "\"a\\\"b\""))
|
(assert= (canonical-serialize "a\"b") "\"a\\\"b\""))
|
||||||
(test
|
(deftest
|
||||||
"symbols"
|
"symbols"
|
||||||
(assert= (canonical-serialize (quote deref)) "deref")
|
(assert= (canonical-serialize (quote deref)) "deref")
|
||||||
(assert= (canonical-serialize (quote swap!)) "swap!"))
|
(assert= (canonical-serialize (quote swap!)) "swap!"))
|
||||||
(test
|
(deftest
|
||||||
"keywords"
|
"keywords"
|
||||||
(assert= (canonical-serialize :class) ":class")
|
(assert= (canonical-serialize (make-keyword "class")) ":class")
|
||||||
(assert= (canonical-serialize :arity) ":arity"))
|
(assert= (canonical-serialize (make-keyword "arity")) ":arity"))
|
||||||
(test "empty list" (assert= (canonical-serialize (list)) "()"))
|
(deftest "empty list" (assert= (canonical-serialize (list)) "()"))
|
||||||
(test "flat list" (assert= (canonical-serialize (list 1 2 3)) "(1 2 3)"))
|
(deftest
|
||||||
(test
|
"flat list"
|
||||||
|
(assert= (canonical-serialize (list 1 2 3)) "(1 2 3)"))
|
||||||
|
(deftest
|
||||||
"nested list"
|
"nested list"
|
||||||
(assert=
|
(assert=
|
||||||
(canonical-serialize
|
(canonical-serialize
|
||||||
(list (quote div) :class "flex" (list (quote h2) "title")))
|
(list
|
||||||
|
(quote div)
|
||||||
|
(make-keyword "class")
|
||||||
|
"flex"
|
||||||
|
(list (quote h2) "title")))
|
||||||
"(div :class \"flex\" (h2 \"title\"))"))
|
"(div :class \"flex\" (h2 \"title\"))"))
|
||||||
(test
|
(deftest
|
||||||
"dict keys sorted"
|
"dict keys sorted"
|
||||||
(let
|
(let
|
||||||
((d (dict "zebra" 1 "alpha" 2 "middle" 3)))
|
((d {:zebra 1 :middle 3 :alpha 2}))
|
||||||
(assert= (canonical-serialize d) "{:alpha 2 :middle 3 :zebra 1}")))
|
(assert= (canonical-serialize d) "{:alpha 2 :middle 3 :zebra 1}")))
|
||||||
(test
|
(deftest
|
||||||
"dict with nested values"
|
"dict with nested values"
|
||||||
(let
|
(let
|
||||||
((d (dict "a" (list 1 2) "b" "hello")))
|
((d {:b "hello" :a (list 1 2)}))
|
||||||
(assert= (canonical-serialize d) "{:a (1 2) :b \"hello\"}"))))
|
(assert= (canonical-serialize d) "{:a (1 2) :b \"hello\"}"))))
|
||||||
|
|
||||||
(test-group
|
(defsuite
|
||||||
"content-id"
|
"content-id"
|
||||||
(test
|
(deftest
|
||||||
"same expression same CID"
|
"same expression same CID"
|
||||||
(assert= (content-id (list 1 2 3)) (content-id (list 1 2 3))))
|
(assert= (content-id (list 1 2 3)) (content-id (list 1 2 3))))
|
||||||
(test
|
(deftest
|
||||||
"different expression different CID"
|
"different expression different CID"
|
||||||
(assert
|
(assert
|
||||||
(not (= (content-id (list 1 2 3)) (content-id (list 1 2 4))))))
|
(not (= (content-id (list 1 2 3)) (content-id (list 1 2 4))))))
|
||||||
(test
|
(deftest
|
||||||
"CID is a hex string"
|
"CID is a hex string"
|
||||||
(let
|
(let
|
||||||
((cid (content-id 42)))
|
((cid (content-id 42)))
|
||||||
(assert (string? cid))
|
(assert (string? cid))
|
||||||
(assert= (len cid) 64)))
|
(assert= (len cid) 64)))
|
||||||
(test
|
(deftest
|
||||||
"short CID is 16 chars"
|
"short CID is 16 chars"
|
||||||
(let ((cid (content-id-short 42))) (assert= (len cid) 16)))
|
(let ((cid (content-id-short 42))) (assert= (len cid) 16)))
|
||||||
(test
|
(deftest
|
||||||
"short CID is prefix of full CID"
|
"short CID is prefix of full CID"
|
||||||
(let
|
(let
|
||||||
((full (content-id 42)) (short (content-id-short 42)))
|
((full (content-id 42)) (short (content-id-short 42)))
|
||||||
(assert= short (slice full 0 16)))))
|
(assert= short (slice full 0 16)))))
|
||||||
|
|
||||||
(test-group
|
(defsuite
|
||||||
"bytecode-module"
|
"bytecode-module"
|
||||||
(test
|
(deftest
|
||||||
"make and query"
|
"make and query"
|
||||||
(let
|
(let
|
||||||
((m (make-bytecode-module 1 "abc123" (list (quote code) :bytecode (list 1 2 3)))))
|
((m (make-bytecode-module 1 "abc123" (list 1 2 3))))
|
||||||
(assert (bytecode-module? m))
|
(assert (bytecode-module? m))
|
||||||
(assert= (bytecode-module-version m) 1)
|
(assert= (bytecode-module-version m) 1)
|
||||||
(assert= (bytecode-module-source-hash m) "abc123")))
|
(assert= (bytecode-module-source-hash m) "abc123")))
|
||||||
(test
|
(deftest
|
||||||
"non-module fails predicate"
|
"non-module fails predicate"
|
||||||
(assert (not (bytecode-module? (list 1 2 3))))
|
(assert (not (bytecode-module? (list 1 2 3))))
|
||||||
(assert (not (bytecode-module? "hello")))))
|
(assert (not (bytecode-module? "hello")))))
|
||||||
|
|
||||||
(test-group
|
(defsuite
|
||||||
"provenance"
|
"provenance"
|
||||||
(test
|
(deftest
|
||||||
"make provenance record"
|
"make provenance record"
|
||||||
(let
|
(let
|
||||||
((p (make-provenance "src-cid" "bc-cid" "compiler-cid" "2026-03-27T00:00:00Z")))
|
((p (make-provenance "src-cid" "bc-cid" "v1" "js")))
|
||||||
(assert= (first p) (quote provenance))
|
(assert= (first p) (quote provenance))
|
||||||
(assert= (nth p 2) "src-cid")
|
(assert= (nth p 2) "src-cid")
|
||||||
(assert= (nth p 4) "bc-cid"))))
|
(assert= (nth p 4) "bc-cid"))))
|
||||||
|
|||||||
Reference in New Issue
Block a user