Phase 8: Tree-walking interpreter for in-memory REPL eval (~0.02ms vs 1133ms compiled). 66 eval tests.

This commit is contained in:
2026-05-08 20:16:42 +03:00
parent feb9cee287
commit 1f9d662ed9
7 changed files with 1540 additions and 10 deletions
+372
View File
@@ -0,0 +1,372 @@
import unittest
import ../src/types
import ../src/reader
import ../src/eval
suite "Eval - Basic Values":
setup:
let env = newTopLevelEnv()
test "eval integer":
let r = eval("42", env)
check r.ok
check r.value.kind == ckInt
check r.value.intVal == 42
test "eval string":
let r = eval("\"hello\"", env)
check r.ok
check r.value.kind == ckString
check r.value.strVal == "hello"
test "eval bool true":
let r = eval("true", env)
check r.ok
check r.value.kind == ckBool
check r.value.boolVal == true
test "eval nil":
let r = eval("nil", env)
check r.ok
check r.value.kind == ckNil
test "eval keyword":
let r = eval(":foo", env)
check r.ok
check r.value.kind == ckKeyword
check r.value.kwName == "foo"
suite "Eval - Arithmetic":
setup:
let env = newTopLevelEnv()
test "addition":
let r = eval("(+ 1 2)", env)
check r.ok
check r.value.intVal == 3
test "subtraction":
let r = eval("(- 10 3)", env)
check r.ok
check r.value.intVal == 7
test "multiplication":
let r = eval("(* 3 4)", env)
check r.ok
check r.value.intVal == 12
test "division":
let r = eval("(/ 10 2)", env)
check r.ok
check r.value.intVal == 5
test "nested arithmetic":
let r = eval("(+ 1 (* 2 3))", env)
check r.ok
check r.value.intVal == 7
test "multi-arg addition":
let r = eval("(+ 1 2 3 4)", env)
check r.ok
check r.value.intVal == 10
suite "Eval - Comparison":
setup:
let env = newTopLevelEnv()
test "greater than":
let r = eval("(> 5 3)", env)
check r.ok
check r.value.boolVal == true
test "less than":
let r = eval("(< 3 5)", env)
check r.ok
check r.value.boolVal == true
test "equality":
let r = eval("(= 42 42)", env)
check r.ok
check r.value.boolVal == true
test "not equal":
let r = eval("(not= 1 2)", env)
check r.ok
check r.value.boolVal == true
suite "Eval - Special Forms":
setup:
let env = newTopLevelEnv()
test "def":
let r = eval("(def x 42)", env)
check r.ok
check r.isDef
check r.defName == "x"
test "defn and call":
discard eval("(defn square [x] (* x x))", env)
let r = eval("(square 5)", env)
check r.ok
check r.value.intVal == 25
test "let":
let r = eval("(let [a 10 b 20] (+ a b))", env)
check r.ok
check r.value.intVal == 30
test "if true":
let r = eval("(if true 1 2)", env)
check r.ok
check r.value.intVal == 1
test "if false":
let r = eval("(if false 1 2)", env)
check r.ok
check r.value.intVal == 2
test "if nil is falsy":
let r = eval("(if nil 1 2)", env)
check r.ok
check r.value.intVal == 2
test "when":
let r = eval("(when true 42)", env)
check r.ok
check r.value.intVal == 42
test "when false":
let r = eval("(when false 42)", env)
check r.ok
check r.value.kind == ckNil
test "do":
let r = eval("(do 1 2 3)", env)
check r.ok
check r.value.intVal == 3
test "fn and call":
let r = eval("((fn [x] (+ x 1)) 5)", env)
check r.ok
check r.value.intVal == 6
test "quote":
let r = eval("(quote (1 2 3))", env)
check r.ok
check r.value.kind == ckList
check r.value.items.len == 3
suite "Eval - Collections":
setup:
let env = newTopLevelEnv()
test "vector literal":
let r = eval("[1 2 3]", env)
check r.ok
check r.value.kind == ckVector
check r.value.items.len == 3
test "map literal":
let r = eval("{:a 1 :b 2}", env)
check r.ok
check r.value.kind == ckMap
check r.value.mapKeys.len == 2
test "count":
let r = eval("(count [1 2 3])", env)
check r.ok
check r.value.intVal == 3
test "first":
let r = eval("(first [10 20 30])", env)
check r.ok
check r.value.intVal == 10
test "rest":
let r = eval("(rest [1 2 3])", env)
check r.ok
check r.value.items.len == 2
test "conj vector":
let r = eval("(conj [1 2] 3)", env)
check r.ok
check r.value.kind == ckVector
check r.value.items.len == 3
test "cons":
let r = eval("(cons 0 [1 2])", env)
check r.ok
check r.value.items.len == 3
check r.value.items[0].intVal == 0
test "concat":
let r = eval("(concat [1 2] [3 4])", env)
check r.ok
check r.value.items.len == 4
suite "Eval - Higher-order":
setup:
let env = newTopLevelEnv()
test "map inc":
let r = eval("(map inc [1 2 3])", env)
check r.ok
check r.value.items.len == 3
check r.value.items[0].intVal == 2
check r.value.items[2].intVal == 4
test "filter":
let r = eval("(filter (fn [x] (> x 2)) [1 2 3 4 5])", env)
check r.ok
check r.value.items.len == 3
test "reduce":
let r = eval("(reduce + 0 [1 2 3 4 5])", env)
check r.ok
check r.value.intVal == 15
test "apply":
let r = eval("(apply + [1 2 3])", env)
check r.ok
check r.value.intVal == 6
suite "Eval - String/Primitives":
setup:
let env = newTopLevelEnv()
test "str concatenation":
let r = eval("(str \"hello\" \" \" \"world\")", env)
check r.ok
check r.value.strVal == "hello world"
test "println":
let r = eval("(println \"test\")", env)
check r.ok
check r.value.kind == ckNil
test "inc":
let r = eval("(inc 41)", env)
check r.ok
check r.value.intVal == 42
test "dec":
let r = eval("(dec 43)", env)
check r.ok
check r.value.intVal == 42
test "zero?":
let r = eval("(zero? 0)", env)
check r.ok
check r.value.boolVal == true
test "nil?":
let r = eval("(nil? nil)", env)
check r.ok
check r.value.boolVal == true
test "not":
let r = eval("(not false)", env)
check r.ok
check r.value.boolVal == true
test "type":
let r = eval("(type 42)", env)
check r.ok
check r.value.kind == ckKeyword
check r.value.kwName == "integer"
suite "Eval - Map Operations":
setup:
let env = newTopLevelEnv()
test "get from map":
let r = eval("(get {:a 1 :b 2} :a)", env)
check r.ok
check r.value.intVal == 1
test "get with default":
let r = eval("(get {:a 1} :z 99)", env)
check r.ok
check r.value.intVal == 99
test "assoc":
let r = eval("(assoc {:a 1} :b 2)", env)
check r.ok
check r.value.mapKeys.len == 2
test "dissoc":
let r = eval("(dissoc {:a 1 :b 2} :a)", env)
check r.ok
check r.value.mapKeys.len == 1
test "keys":
let r = eval("(keys {:a 1 :b 2})", env)
check r.ok
check r.value.items.len == 2
test "vals":
let r = eval("(vals {:a 1 :b 2})", env)
check r.ok
check r.value.items.len == 2
test "contains?":
let r = eval("(contains? {:a 1} :a)", env)
check r.ok
check r.value.boolVal == true
suite "Eval - Range/Take/Drop":
setup:
let env = newTopLevelEnv()
test "range 5":
let r = eval("(range 5)", env)
check r.ok
check r.value.items.len == 5
check r.value.items[0].intVal == 0
check r.value.items[4].intVal == 4
test "range 1 to 5":
let r = eval("(range 1 5)", env)
check r.ok
check r.value.items.len == 4
check r.value.items[0].intVal == 1
test "take":
let r = eval("(take 3 [1 2 3 4 5])", env)
check r.ok
check r.value.items.len == 3
test "drop":
let r = eval("(drop 2 [1 2 3 4 5])", env)
check r.ok
check r.value.items.len == 3
check r.value.items[0].intVal == 3
suite "Eval - Boolean Logic":
setup:
let env = newTopLevelEnv()
test "and true":
let r = eval("(and true true)", env)
check r.ok
check r.value.boolVal == true
test "and false":
let r = eval("(and true false)", env)
check r.ok
check r.value.boolVal == false
test "or true":
let r = eval("(or false true)", env)
check r.ok
check r.value.boolVal == true
test "or false":
let r = eval("(or false false)", env)
check r.ok
check r.value.kind == ckNil
test "cond":
let r = eval("(cond false 1 true 2 false 3)", env)
check r.ok
check r.value.intVal == 2