import unittest import strutils import ../src/types import ../src/reader import ../src/emitter suite "Emitter - Basic Values": test "emit nil": check emitExpr(cljNil()) == "cljNil()" test "emit bool true": check emitExpr(cljBool(true)) == "cljBool(true)" test "emit bool false": check emitExpr(cljBool(false)) == "cljBool(false)" test "emit int": check emitExpr(cljInt(42)) == "cljInt(42)" test "emit float": check "cljFloat(3.14)" in emitExpr(cljFloat(3.14)) test "emit string": check emitExpr(cljString("hello")) == "cljString(\"hello\")" test "emit keyword": check "cljKeyword" in emitExpr(cljKeyword("key")) test "emit symbol": check emitExpr(cljSymbol("foo")) == "foo" test "emit mangled symbol": check mangleName("my-fn?") == "my_fn_Q" test "emit vector": let v = cljVector(@[cljInt(1), cljInt(2), cljInt(3)]) check "cljVector" in emitExpr(v) check "cljInt(1)" in emitExpr(v) test "emit empty list": check emitExpr(cljList(@[])) == "cljList(@[])" suite "Emitter - Special Forms": test "emit println": let v = read("(println \"hello\")") let code = emitExpr(v) check "cljPrintln" in code check "discard" in code test "emit def": let v = read("(def x 42)") let code = emitExpr(v) check "let x = cljInt(42)" in code test "emit defn": let v = read("(defn square [x] (* x x))") let code = emitExpr(v) check "proc square" in code check "CljVal" in code test "emit if": let v = read("(if true 1 2)") let code = emitExpr(v) check "cljIsTruthy" in code check "else:" in code test "emit when": let v = read("(when true (println \"yes\"))") let code = emitExpr(v) check "cljIsTruthy" in code test "emit let": let v = read("(let [x 1] x)") let code = emitExpr(v) check "block:" in code check "let x = cljInt(1)" in code test "emit cond": let v = read("(cond true 1 :else 2)") let code = emitExpr(v) check "cljIsTruthy" in code check "else:" in code test "emit fn": let v = read("(fn [x] x)") let code = emitExpr(v) check "proc" in code check "CljVal" in code test "emit do": let v = read("(do 1 2)") let code = emitExpr(v) check "cljInt" in code suite "Emitter - Operators": test "emit addition": let v = read("(+ 1 2)") let code = emitExpr(v) check "cljAdd" in code test "emit subtraction": let v = read("(- 5 3)") let code = emitExpr(v) check "cljSub" in code test "emit multiplication": let v = read("(* 2 3)") let code = emitExpr(v) check "cljMul" in code test "emit equality": let v = read("(= 1 1)") let code = emitExpr(v) check "cljNumEq" in code test "emit not=": let v = read("(not= 1 2)") let code = emitExpr(v) check "cljNotEq" in code test "emit not": let v = read("(not true)") let code = emitExpr(v) check "cljNot" in code suite "Emitter - Program": test "emitProgram with defs and main": let forms = readAll("(def x 10)\n(println x)") let prog = emitProgram(forms) check "let x = cljInt(10)" in prog check "when isMainModule:" in prog check "cljPrintln" in prog test "emitProgram multiple defs": let forms = readAll("(defn add [a b] (+ a b))\n(println (add 1 2))") let prog = emitProgram(forms) check "proc add" in prog check "add(cljInt(1), cljInt(2))" in prog suite "Emitter - Map Literals": test "emit empty map": let v = cljMap(@[], @[]) let code = emitExpr(v) check "cljMap" in code test "emit map with entries": let v = cljMapFromPairs(@[ (cljKeyword("a"), cljInt(1)), (cljKeyword("b"), cljInt(2)) ]) let code = emitExpr(v) check "cljMap" in code suite "Emitter - Higher-order": test "emit map with symbol fn": let v = read("(map inc [1 2 3])") let code = emitExpr(v) check "cljMap" in code check "cljFn" in code test "emit filter with inline fn": let v = read("(filter (fn [x] (> x 2)) [1 2 3])") let code = emitExpr(v) check "cljFilter" in code check "cljFn" in code test "emit reduce": let v = read("(reduce + 0 [1 2 3])") let code = emitExpr(v) check "cljReduce" in code