aboutsummaryrefslogtreecommitdiffstats
path: root/spec/lib
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2022-02-05 10:11:13 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:24:49 +0000
commitc484cce5ab1f6acd14ecad30adb586a6d5fa0fd1 (patch)
treea45c702a83e150fea5b14b279e5f0cab9b65bc3e /spec/lib
parentexamples: more smoothing in love example :) (diff)
downloadalive-c484cce5ab1f6acd14ecad30adb586a6d5fa0fd1.tar.gz
alive-c484cce5ab1f6acd14ecad30adb586a6d5fa0fd1.zip
rearrange spec, fix for Lua 5.1
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/array_spec.moon148
-rw-r--r--spec/lib/builtins/cond_spec.moon92
-rw-r--r--spec/lib/builtins/do_spec.moon27
-rw-r--r--spec/lib/builtins/fn_spec.moon51
-rw-r--r--spec/lib/builtins/literal_spec.moon33
-rw-r--r--spec/lib/builtins/thread_spec.moon24
-rw-r--r--spec/lib/logic_spec.moon264
-rw-r--r--spec/lib/math_spec.moon267
-rw-r--r--spec/lib/struct_spec.moon51
-rw-r--r--spec/lib/testing_spec.moon80
10 files changed, 1037 insertions, 0 deletions
diff --git a/spec/lib/array_spec.moon b/spec/lib/array_spec.moon
new file mode 100644
index 0000000..b55cec6
--- /dev/null
+++ b/spec/lib/array_spec.moon
@@ -0,0 +1,148 @@
+import TestPilot from require 'spec.test_setup'
+import T, Array from require 'alv'
+
+describe "array", ->
+ test = TestPilot '', '(import* array-)\n'
+
+ svec3 = Array 3, T.str
+
+ it "can contain any type", ->
+ COPILOT\eval_once '(array 1 2 3)'
+ COPILOT\eval_once '(array true false)'
+ COPILOT\eval_once '(array "a")'
+ COPILOT\eval_once '(array (array 1 2) (array 3 4))'
+
+ it "cannot contain mixed types", ->
+ err = assert.has.error -> COPILOT\eval_once '(array 1 false)'
+ assert.matches "argument error: couldn't match arguments", err
+
+ describe "(set)", ->
+ it "can swap values", ->
+ rt = COPILOT\eval_once '(set (array "f" "b" "c") 0 "a")'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ it "checks value type", ->
+ err = assert.has.error -> COPILOT\eval_once '(set (array 1) 0 "a")'
+ assert.matches "expected value of type num, not str", err
+
+ it "checks index range", ->
+ err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) -1 0)'
+ assert.matches "index '%-1' out of range!", err
+
+ COPILOT\eval_once '(set (array 1 2) 0 0)'
+
+ COPILOT\eval_once '(set (array 1 2) 1 0)'
+
+ err = assert.has.error -> COPILOT\eval_once '(set (array 1 2) 2 0)'
+ assert.matches "index '2' out of range!", err
+
+ describe "(get)", ->
+ it "can get a value", ->
+ rt = COPILOT\eval_once '(get (array 1 2) 0)'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 1>', tostring rt.result
+
+ it "checks index range", ->
+ err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) -1)'
+ assert.matches "index '%-1' out of range!", err
+
+ COPILOT\eval_once '(get (array 1 2) 0)'
+
+ COPILOT\eval_once '(get (array 1 2) 1)'
+
+ err = assert.has.error -> COPILOT\eval_once '(get (array 1 2) 2)'
+ assert.matches "index '2' out of range!", err
+
+ describe '(head)', ->
+ it "can peek a value", ->
+ rt = COPILOT\eval_once '(head (array 1 2))'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 1>', tostring rt.result
+
+ describe '(tail)', ->
+ it "gets rest of an array", ->
+ rt = COPILOT\eval_once '(tail (array 1))'
+ assert.is.true rt\is_const!
+ assert.is.same (Array 0, T.num), rt.result.type
+ assert.is.same {}, rt.result!
+
+ rt = COPILOT\eval_once '(tail (array 1 2))'
+ assert.is.true rt\is_const!
+ assert.is.same (Array 1, T.num), rt.result.type
+ assert.is.same { 2 }, rt.result!
+
+ rt = COPILOT\eval_once '(tail (array 1 2 3 4))'
+ assert.is.true rt\is_const!
+ assert.is.same (Array 3, T.num), rt.result.type
+ assert.is.same { 2, 3, 4 }, rt.result!
+
+ describe '(prepend)', ->
+ it "prepends to array", ->
+ rt = COPILOT\eval_once '(prepend (array 2) 1)'
+ assert.is.true rt\is_const!
+ assert.is.same (Array 2, T.num), rt.result.type
+ assert.is.same { 1, 2 }, rt.result!
+
+ rt = COPILOT\eval_once '(prepend (array 2 3 4) 1)'
+ assert.is.true rt\is_const!
+ assert.is.same (Array 4, T.num), rt.result.type
+ assert.is.same { 1, 2, 3, 4 }, rt.result!
+
+ describe "(insert)", ->
+ it "can insert a value", ->
+ rt = COPILOT\eval_once '(insert (array "b" "c") 0 "a")'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ rt = COPILOT\eval_once '(insert (array "a" "c") 1 "b")'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ rt = COPILOT\eval_once '(insert (array "a" "b") 2 "c")'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ it "checks index range", ->
+ err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) -1 0)'
+ assert.matches "index '%-1' out of range!", err
+
+ COPILOT\eval_once '(insert (array 1 2) 0 0)'
+
+ COPILOT\eval_once '(insert (array 1 2) 1 0)'
+
+ COPILOT\eval_once '(insert (array 1 2) 2 0)'
+
+ err = assert.has.error -> COPILOT\eval_once '(insert (array 1 2) 3 0)'
+ assert.matches "index '3' out of range!", err
+
+ describe "(remove)", ->
+ it "can remove a value", ->
+ rt = COPILOT\eval_once '(remove (array "d" "a" "b" "c") 0)'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ rt = COPILOT\eval_once '(remove (array "a" "b" "c" "d") 3)'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ it "checks index range", ->
+ err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) -1)'
+ assert.matches "index '%-1' out of range!", err
+
+ err = assert.has.error -> COPILOT\eval_once '(remove (array 1 2 3) 3)'
+ assert.matches "index '3' out of range!", err
+
+ it "can be concatenated with (concat)", ->
+ rt = COPILOT\eval_once '(concat (array "a" "b") (array "c"))'
+ assert.is.true rt\is_const!
+ assert.is.equal svec3\mk_const({ 'a', 'b', 'c' }), rt.result
+
+ it "size can be read using (size)", ->
+ rt = COPILOT\eval_once '(size (array 1))'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 1>', tostring rt.result
+
+ rt = COPILOT\eval_once '(size (array 1 2 3))'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 3>', tostring rt.result
diff --git a/spec/lib/builtins/cond_spec.moon b/spec/lib/builtins/cond_spec.moon
new file mode 100644
index 0000000..89f7c7e
--- /dev/null
+++ b/spec/lib/builtins/cond_spec.moon
@@ -0,0 +1,92 @@
+import TestPilot from require 'spec.test_setup'
+import T, Struct, Array, Constant from require 'alv'
+
+describe "if", ->
+ COPILOT = TestPilot!
+
+ it "checks truthiness", ->
+ for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'}
+ with COPILOT\eval_once "(if #{truthy} 'yes' 'no')"
+ assert.is.true \is_const!
+ assert.is.equal 'yes', .result!
+
+ for falsy in *{'false', '0'}
+ with COPILOT\eval_once "(if #{falsy} 'yes' 'no')"
+ assert.is.true \is_const!
+ assert.is.equal 'no', .result!
+
+ it "can be used without else clause", ->
+ with COPILOT\eval_once '(if true "yes")'
+ assert.is.true \is_const!
+ assert.is.equal 'yes', .result!
+
+ with COPILOT\eval_once '(if false "yes")'
+ assert.is.true \is_const!
+ assert.is.equal nil, .result
+
+ it "doesn't evaluate the untaken branch", ->
+ with COPILOT\eval_once '(if true "yes" (error/doesnt-exist))'
+ assert.is.true \is_const!
+ assert.is.equal 'yes', .result!
+
+ with COPILOT\eval_once '(if false (error/doesnt-exist) "no")'
+ assert.is.true \is_const!
+ assert.is.equal 'no', .result!
+
+ it "errors on non-const choice", ->
+ err = assert.has.error ->
+ COPILOT\eval_once '(import* time) (if (ramp 1) "yes" "no")'
+ assert.matches "'if'%-expression needs to be constant", err
+
+ it "forwards any result", ->
+ with COPILOT\eval_once '
+ (import* time)
+ (if true (every 1 (array 1 2 3)))'
+ assert.is.false \is_const!
+ assert.is.equal '<num[3]! nil>', tostring .result
+
+describe "when", ->
+ COPILOT = TestPilot!
+
+ it "checks truthiness", ->
+ for truthy in *{'true', '1', '-1', '1234', '(array 1 2 3)', '"test"', '""'}
+ with COPILOT\eval_once "(when #{truthy} 'yes')"
+ assert.is.true \is_const!
+ assert.is.equal 'yes', .result!
+
+ for falsy in *{'false', '0'}
+ with COPILOT\eval_once "(when #{falsy} 'yes')"
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ it "doesn't evaluate if falsy", ->
+ with COPILOT\eval_once '(when false (error/doesnt-exist))'
+ assert.is.true \is_const!
+ assert.is.nil, .result
+
+ with COPILOT\eval_once '
+ (when false
+ (error/doesnt-exist)
+ (error/doesnt-exist)
+ (error/doesnt-exist))'
+ assert.is.true \is_const!
+ assert.is.nil, .result
+
+ it "errors on non-const choice", ->
+ err = assert.has.error ->
+ COPILOT\eval_once '(import* time) (when (ramp 1) 1 2 3)'
+ assert.matches "'when'%-expression needs to be constant", err
+
+ it "forwards any result", ->
+ with COPILOT\eval_once '
+ (import* time)
+ (when true
+ (every 1 (array 1 2 3))
+ 1 2 3)'
+ assert.is.false \is_const!
+ assert.is.equal '<num~ 3>', tostring .result
+
+ with COPILOT\eval_once '(when true (array 1 2 3))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[3]= [1 2 3]>', tostring .result
+
diff --git a/spec/lib/builtins/do_spec.moon b/spec/lib/builtins/do_spec.moon
new file mode 100644
index 0000000..fd2825b
--- /dev/null
+++ b/spec/lib/builtins/do_spec.moon
@@ -0,0 +1,27 @@
+import TestPilot from require 'spec.test_setup'
+
+describe "do", ->
+ COPILOT = TestPilot!
+
+ it "can be empty", ->
+ with COPILOT\eval_once '(do)'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ it "returns the last result, if any", ->
+ with COPILOT\eval_once '(do 1 2 3)'
+ assert.is.true \is_const!
+ assert.is.equal '<num= 3>', tostring .result
+
+ with COPILOT\eval_once '(do 1 2 (def _ 3))'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ it "passes through side-effects", ->
+ with COPILOT\eval_once '
+ (import* time)
+ (do
+ (every 0.5 "bang! side-effect")
+ 3)'
+ assert.is.false \is_const!
+ assert.is.equal '<num= 3>', tostring .result
diff --git a/spec/lib/builtins/fn_spec.moon b/spec/lib/builtins/fn_spec.moon
new file mode 100644
index 0000000..af27cf3
--- /dev/null
+++ b/spec/lib/builtins/fn_spec.moon
@@ -0,0 +1,51 @@
+import TestPilot from require 'spec.test_setup'
+import T, Struct, Array, Constant from require 'alv'
+
+describe "function", ->
+ COPILOT = TestPilot!
+
+ it "returns constant results when constant", ->
+ rt = COPILOT\eval_once '
+ (import* math)
+
+ (defn my-plus (a b)
+ (+ a b))
+
+ (my-plus 2 3)'
+ assert.is.true rt\is_const!
+ assert.is.equal (Constant.num 5), rt.result
+
+ it "checks argument arity when invoked", ->
+ err = assert.has.error -> COPILOT\eval_once '
+ ([1]import* math)
+
+ ([2]defn my-plus (a b)
+ ([4]+ a b))
+
+ ([3]my-plus 2)'
+ assert.matches "argument error: expected 2 arguments, found 1", err
+ assert.matches "while invoking function 'my%-plus' at %[3%]", err
+
+ err = assert.has.error -> COPILOT\eval_once '
+ ([1]import* math)
+
+ ([2]defn my-plus (a b)
+ ([4]+ a b))
+
+ ([3]my-plus 2 3 4)'
+ assert.matches "argument error: expected 2 arguments, found 3", err
+ assert.matches "while invoking function 'my%-plus' at %[3%]", err
+
+ it "can be anonymously invoked", ->
+ rt = COPILOT\eval_once '
+ ([1]
+ ([2]fn (a b) b)
+ 3 4)'
+ assert.is.equal (Constant.num 4), rt\const!
+
+ err = assert.has.error -> COPILOT\eval_once '
+ ([1]
+ ([2]fn (a b) b)
+ 3)'
+ assert.matches "argument error: expected 2 arguments, found 1", err
+ assert.matches "while invoking function %(unnamed%) at %[1%]", err
diff --git a/spec/lib/builtins/literal_spec.moon b/spec/lib/builtins/literal_spec.moon
new file mode 100644
index 0000000..3db010d
--- /dev/null
+++ b/spec/lib/builtins/literal_spec.moon
@@ -0,0 +1,33 @@
+import TestPilot from require 'spec.test_setup'
+import T, Struct, Array, Constant from require 'alv'
+
+describe "literal", ->
+ TestPilot '
+ (def str "hello"
+ num 2
+ bool true
+ curl ([5]struct "a" 2 "b" false)
+ sqre ([7]array 1 2 3 4))
+ (export*)'
+
+ assert.is.true COPILOT.active_module.root\is_const!
+ scope = (assert COPILOT.active_module.root.result)\unwrap T.scope
+
+ it "string is parsed and returned correctly", ->
+ assert.is.equal (Constant.str 'hello'), (scope\get 'str')\const!
+
+ it "number is parsed and returned correctly", ->
+ assert.is.equal (Constant.num 2), (scope\get 'num')\const!
+
+ it "boolean is parsed and returned correctly", ->
+ assert.is.equal (Constant.bool true), (scope\get 'bool')\const!
+
+ it "struct is parsed and returned correctly", ->
+ struct = (scope\get 'curl')\const!
+ assert.is.equal (Struct a: T.num, b: T.bool), struct.type
+ assert.is.same { a: 2, b: false }, struct!
+
+ it "array is parsed and returned correctly", ->
+ array = (scope\get 'sqre')\const!
+ assert.is.equal (Array 4, T.num), array.type
+ assert.is.same {1, 2, 3, 4}, array!
diff --git a/spec/lib/builtins/thread_spec.moon b/spec/lib/builtins/thread_spec.moon
new file mode 100644
index 0000000..4b81119
--- /dev/null
+++ b/spec/lib/builtins/thread_spec.moon
@@ -0,0 +1,24 @@
+import TestPilot from require 'spec.test_setup'
+
+describe "thread macros", ->
+ COPILOT = TestPilot!
+
+ it "thread forward (->)", ->
+ rt = COPILOT\eval_once '
+ (import* math)
+ #((/ (+ 10 2) 8) = 1.5)
+ (-> 10
+ (+ 2)
+ (/ 8))'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 1.5>', tostring rt.result
+
+ it "thread last forward (->>)", ->
+ rt = COPILOT\eval_once '
+ (import* math)
+ #((/ 10 (+ 2 2)) = 2.5)
+ (->> 2
+ (+ 2)
+ (/ 10))'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 2.5>', tostring rt.result
diff --git a/spec/lib/logic_spec.moon b/spec/lib/logic_spec.moon
new file mode 100644
index 0000000..f3d0610
--- /dev/null
+++ b/spec/lib/logic_spec.moon
@@ -0,0 +1,264 @@
+import TestPilot from require 'spec.test_setup'
+import T, Array, Constant from require 'alv'
+
+describe "logic", ->
+ test = TestPilot '', '(import* logic)\n'
+ TRUE = T.bool\mk_const true
+ FALSE = T.bool\mk_const false
+
+ describe "==", ->
+ it "can compare any type", ->
+ with COPILOT\eval_once '(== 1 1)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(== 1 2)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(== 1 "hello")'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(== "hello" "hello")'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 1))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(== (array 1 2 3) (array 1 2))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(== (array 1 2 3) (array 1 2 3 4))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(==
+ (struct "a" 1 "b" true "c" (array "test"))
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(==
+ (struct "a" 1 "b" false "c" (array "test"))
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(==
+ (struct "a" 1 "b" true "c" (array "test" "toast"))
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(==
+ (struct "a" 1 "b" true)
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(==
+ (struct "a" 1 "b" true)
+ (struct "a" 1))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(== print print)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(== print ==)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ it "is aliased as eq", ->
+ with COPILOT\eval_once '(== eq ==)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ describe "!=", ->
+ it "can compare any type", ->
+ with COPILOT\eval_once '(!= 1 1)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(!= 1 2)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!= 1 "hello")'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!= "hello" "hello")'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 1))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!= (array 1 2 3) (array 1 2 3 4))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!=
+ (struct "a" 1 "b" true "c" (array "test"))
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(!=
+ (struct "a" 1 "b" false "c" (array "test"))
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!=
+ (struct "a" 1 "b" true "c" (array "test" "toast"))
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!=
+ (struct "a" 1 "b" true)
+ (struct "a" 1 "b" true "c" (array "test")))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!=
+ (struct "a" 1 "b" true)
+ (struct "a" 1))'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(!= print print)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(!= print ==)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ it "is aliased as note-eq", ->
+ with COPILOT\eval_once '(== not-eq !=)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ describe "bool", ->
+ it "coerces numbers", ->
+ with COPILOT\eval_once '(bool 0)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(bool 1)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(bool -1)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(bool 1024)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ it "accepts booleans", ->
+ with COPILOT\eval_once '(bool false)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(bool true)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ describe "not", ->
+ it "accepts booleans", ->
+ with COPILOT\eval_once '(not false)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(not true)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ it "coerces numbers", ->
+ with COPILOT\eval_once '(not 0)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(not 1)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(not -1)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(not 1024)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+
+ describe "or", ->
+ it "accepts any number of mixed arguments", ->
+ with COPILOT\eval_once '(or false 0)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(or 1 0)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(or 0 false 0 0 0 0)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(or 0 0 0 true 0 0)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(or 0 true 0 1 0 0)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ describe "and", ->
+ it "accepts any number of mixed arguments", ->
+ with COPILOT\eval_once '(and false 1)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(and 1 true)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(and false 0)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(and 1 1 true 0)'
+ assert.is.true \is_const!
+ assert.is.equal FALSE, .result
+
+ with COPILOT\eval_once '(and 1 1 true true 1)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
+
+ with COPILOT\eval_once '(and 1 1 1)'
+ assert.is.true \is_const!
+ assert.is.equal TRUE, .result
diff --git a/spec/lib/math_spec.moon b/spec/lib/math_spec.moon
new file mode 100644
index 0000000..23a353b
--- /dev/null
+++ b/spec/lib/math_spec.moon
@@ -0,0 +1,267 @@
+import TestPilot from require 'spec.test_setup'
+import T, Array, Constant from require 'alv'
+
+describe_both = (fn) ->
+ describe "math", ->
+ test = TestPilot '', '(import* testing math)\n'
+ fn!
+
+ describe "math-simple", ->
+ test = TestPilot '', '(import* testing math-simple)\n'
+ fn!
+
+describe_both ->
+ describe "add, sub, mul, div, pow, mod", ->
+ it "are aliased as +-*/^%", ->
+ COPILOT\eval_once '
+ (expect= + add)
+ (expect= - sub)
+ (expect= * mul)
+ (expect= / div)
+ (expect= ^ pow)
+ (expect= % mod)
+ '
+
+ it "are sane", ->
+ COPILOT\eval_once '
+ (expect= 2 (+ 1 1))
+ (expect= 6 (+ 2 3 0 1))
+
+ (expect= -5 (- 2 7))
+ (expect= 0 (- 10 4 3 2 1 0))
+ (expect= -10 (- 10))
+
+ (expect= 1 (* 1 1))
+ (expect= -2 (* -1 2))
+ (expect= 14 (* 4 0.5 7))
+
+ (expect= 1 (/ 4 4))
+ (expect= -2 (/ -10 5))
+ (expect= 3 (/ -30 -10))
+
+ (expect= 1024 (^ 2 10))
+ (expect= 0.25 (^ 4 -1))
+ (expect= 1 (^ 999 0))
+
+ (expect= 4 (% 10 6))
+ (expect= 3 (% -2 5))
+ (expect= -2 (% -2 -5))
+ '
+
+ describe "trigonometric functions and constants", ->
+ COPILOT\eval_once '
+ (expect= tau (* pi 2))
+
+ #(darn you fp accuracy!
+ (expect= 0.5 (asin (sin 0.5)))
+ (expect= 0.5 (acos (cos 0.5)))
+ ...)
+ '
+
+ it "min, max, clamp, huge", ->
+ COPILOT\eval_once '
+ (expect= 0 (min 0 1 2))
+ (expect= 2 (max 0 1 2))
+
+ (expect= -2 (clamp -2 3.5 -4))
+ (expect= -1 (clamp -2 3.5 -1))
+ (expect= 0 (clamp -2 3.5 0))
+ (expect= 1 (clamp -2 3.5 1))
+ (expect= 3.5 (clamp -2 3.5 4))
+
+ (expect= (- huge) (min 0 1 -123456789 (- huge)))
+ (expect= huge (max 0 1 123456789 huge))
+ '
+
+ it "inc, dec", ->
+ COPILOT\eval_once '
+ (expect= 1 (inc 0))
+ (expect= -1 (dec 0))
+ (expect= 3 (inc (inc 1)))
+ (expect= -1 (dec (dec 1)))
+ (expect= 0 (inc (dec 0)) (dec (inc 0)))
+ '
+
+describe "math", ->
+ test = TestPilot '', '(import* testing math)\n'
+
+ describe "add, sub, mul, div, pow, mod", ->
+ it "handle scalar/vector", ->
+ COPILOT\eval_once '
+ (expect= (array 3 4 5)
+ (+ 1 (array 1 2 3) 1))
+
+ (expect= (array 0 1 2)
+ (- (array 1 2 3) 1))
+
+ (expect= (array 3 6 9)
+ (* 3 (array 1 2 3)))
+ (expect= (array 3 6 9)
+ (* (array 1 2 3) 3))
+
+ (expect= (array 12 9 4)
+ (/ 36 (array 3 4 9)))
+
+ (expect= (array 9 16 25)
+ (^ (array 3 4 5) 2))
+ (expect= (array 1 2 4 8)
+ (^ 2 (array 0 1 2 3)))
+
+ (expect= (array 3 0 1)
+ (% (array 3 4 5) 4))
+ '
+
+ it "handle vector/vector and matrix/matrix", ->
+ COPILOT\eval_once '
+ (expect= (array 5 7 9)
+ (+ (array 1 2 3)
+ (array 4 5 6)))
+
+ (expect= (array (array 11 12)
+ (array 13 14))
+ (+
+ (array (array 1 2)
+ (array 3 4))
+ 5
+ 5))
+
+ (expect= (array 2 0 -2)
+ (- (array 3 2 1)
+ (array 1 2 3)))
+
+ (expect= (array 1 -2 -3)
+ (- (array -1 2 3)))
+ '
+
+ err = assert.has.error ->
+ COPILOT\eval_once '
+ (+ (array 1 2 3)
+ (array 1 2))'
+
+ err = assert.has.error ->
+ COPILOT\eval_once '
+ (+ (array (array 1 2) (array 1 2))
+ (array 1 2))'
+
+ err = assert.has.error ->
+ COPILOT\eval_once '
+ (- (array 1 2 3)
+ (array 1 2))'
+
+ err = assert.has.error ->
+ COPILOT\eval_once '
+ (- (array (array 1 2) (array 1 2))
+ (array 1 2))'
+
+ describe "mul", ->
+ it "handles scalars and matrices", ->
+ with COPILOT\eval_once '
+ (* 3
+ (array
+ (array 1 2)
+ (array 4 5)))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[2][2]= [[3 6] [12 15]]>', tostring .result
+
+ with COPILOT\eval_once '
+ (* (array
+ (array 1 2)
+ (array 4 5))
+ 3)'
+ assert.is.true \is_const!
+ assert.is.equal '<num[2][2]= [[3 6] [12 15]]>', tostring .result
+
+ it "handles vectors and matrices", ->
+ with COPILOT\eval_once '
+ (*
+ (array (array 1 0 0)
+ (array 0 1 0)
+ (array 0 0 1))
+ (array 4 5 6))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[3]= [4 5 6]>', tostring .result
+
+ with COPILOT\eval_once '
+ (*
+ (array (array 1 0 0)
+ (array 0 1 0)
+ (array 3 2 1))
+ (array 4 5 1))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[3]= [4 5 23]>', tostring .result
+
+ it "handles matrices", ->
+ with COPILOT\eval_once '
+ (*
+ (array (array 1 2 3)
+ (array 4 5 6))
+ (array (array 10 11)
+ (array 20 21)
+ (array 30 31)))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[2][2]= [[140 146] [320 335]]>', tostring .result
+
+ it "handles everything mixed", ->
+ with COPILOT\eval_once '
+ (*
+ (array (array 1 2 3)
+ (array 4 5 6))
+ 2
+ (array (array 10 11)
+ (array 20 21)
+ (array 30 31)))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[2][2]= [[280 292] [640 670]]>', tostring .result
+
+ with COPILOT\eval_once '
+ (*
+ (array (array 1 2 3)
+ (array 4 5 6))
+ 2
+ (array (array 10 11)
+ (array 20 21)
+ (array 30 31))
+ (array 4 7))'
+ assert.is.true \is_const!
+ assert.is.equal '<num[2]= [3164 7250]>', tostring .result
+
+ it "errors with wrong sizes (matrix and vector)", ->
+ err = assert.has.error -> COPILOT\eval_once '
+ (*
+ (array (array 1 2 3)
+ (array 4 5 6))
+ (array 1 2))'
+ -- assert.matches "", err
+
+ err = assert.has.error -> COPILOT\eval_once '
+ (*
+ (array 1 2 3)
+ (array (array 1 2 3)
+ (array 4 5 6)))'
+ -- assert.matches "", err
+
+ it "errors with wrong sizes (matrix)", ->
+ err = assert.has.error -> COPILOT\eval_once '
+ (*
+ (array (array 1 2 3)
+ (array 4 5 6))
+ (array (array 1 2)
+ (array 4 5)))'
+ -- assert.matches "", err
+
+ it "min, max, clamp, huge", ->
+ COPILOT\eval_once '
+ (expect= (array 3 2 1)
+ (min (array 3 4 1) (array 5 2 huge)))
+ (expect= (array 5 huge 4)
+ (max (array 3 huge 4) (array 5 999 2)))
+
+ (expect= (array -2 -1 0 1 3.5)
+ (clamp -2 3.5 (array -4 -1 0 1 4)))
+
+ (expect= 1 (inc 0))
+ (expect= -1 (dec 0))
+ (expect= 3 (inc (inc 1)))
+ (expect= -1 (dec (dec 1)))
+ (expect= 0 (inc (dec 0)) (dec (inc 0)))
+ '
diff --git a/spec/lib/struct_spec.moon b/spec/lib/struct_spec.moon
new file mode 100644
index 0000000..9937f99
--- /dev/null
+++ b/spec/lib/struct_spec.moon
@@ -0,0 +1,51 @@
+import TestPilot from require 'spec.test_setup'
+import T, Struct from require 'alv'
+
+describe "struct", ->
+ test = TestPilot '', '(import* struct-)\n'
+
+ ab = Struct { a: T.num, b: T.bool }
+
+ describe "(set)", ->
+ it "can update values", ->
+ rt = COPILOT\eval_once '(set (struct "a" 1 "b" false) "a" 2)'
+ assert.is.true rt\is_const!
+ assert.is.equal ab\mk_const({ a: 2, b: false }), rt.result
+
+ it "cannot add members", ->
+ err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "b" 2)'
+ assert.matches "{a: num} has no 'b' key", err
+
+ it "checks value type", ->
+ err = assert.has.error -> COPILOT\eval_once '(set (struct "a" 1) "a" "str")'
+ assert.matches "expected value for key 'a' to be num, not str", err
+
+ describe "(get)", ->
+ it "can get values", ->
+ rt = COPILOT\eval_once '(get (struct "a" 1 "b" false) "a")'
+ assert.is.true rt\is_const!
+ assert.is.equal '<num= 1>', tostring rt.result
+
+ it "checks keys", ->
+ err = assert.has.error -> COPILOT\eval_once '(get (struct "a" 1) "b")'
+ assert.matches "has no 'b' key", err
+
+ describe "(insert)", ->
+ it "can add members", ->
+ rt = COPILOT\eval_once '(insert (struct "b" true) "a" 1)'
+ assert.is.true rt\is_const!
+ assert.is.equal ab\mk_const({ a: 1, b: true }), rt.result
+
+ it "doesn't clobber existing members", ->
+ err = assert.has.error -> COPILOT\eval_once '(insert (struct "a" 1) "a" 2)'
+ assert.matches "key 'a' already exists in value of type {a: num}", err
+
+ describe "(remove)", ->
+ it "can remove members", ->
+ rt = COPILOT\eval_once '(remove (struct "a" 1 "b" false "c" "abc") "c")'
+ assert.is.true rt\is_const!
+ assert.is.equal ab\mk_const({ a: 1, b: false }), rt.result
+
+ it "checks keys", ->
+ err = assert.has.error -> COPILOT\eval_once '(remove (struct "a" 1) "b")'
+ assert.matches "has no 'b' key", err
diff --git a/spec/lib/testing_spec.moon b/spec/lib/testing_spec.moon
new file mode 100644
index 0000000..5be7848
--- /dev/null
+++ b/spec/lib/testing_spec.moon
@@ -0,0 +1,80 @@
+import TestPilot from require 'spec.test_setup'
+import T, Array, Constant from require 'alv'
+
+describe "testing", ->
+ test = TestPilot '', '(import* testing logic)\n'
+
+ describe "assert", ->
+ it "ignores true", ->
+ with COPILOT\eval_once '(assert true)'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ with COPILOT\eval_once '(assert true "with message")'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ it "throws on false", ->
+ assert.has.error -> COPILOT\eval_once '(assert false)'
+
+ it "shows failing expression", ->
+ err = assert.has.error -> COPILOT\eval_once '(assert false)'
+ assert.matches "assertion failed: false", err
+
+ err = assert.has.error -> COPILOT\eval_once '(assert (== 1 2))'
+ assert.matches "assertion failed: %(== 1 2%)", err
+
+ it "supports custom error messages", ->
+ err = assert.has.error -> COPILOT\eval_once '(assert (== "green" "red") "duck isnt green")'
+ assert.matches "duck isnt green", err
+
+ describe "expect=", ->
+ it "passes equal params", ->
+ with COPILOT\eval_once '(expect= 1 1 1)'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ with COPILOT\eval_once '(expect= 2 2 2)'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ with COPILOT\eval_once '(expect= true true)'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ with COPILOT\eval_once '(expect= "hello" "hello")'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ with COPILOT\eval_once '(expect= (array 1 2) (array 1 2))'
+ assert.is.true \is_const!
+ assert.is.nil .result
+
+ it "fails different values", ->
+ assert.has.error -> COPILOT\eval_once '(expect= 1 2)'
+ assert.has.error -> COPILOT\eval_once '(expect= 1 1 2)'
+ assert.has.error -> COPILOT\eval_once '(expect= 2 1 1)'
+
+ assert.has.error -> COPILOT\eval_once '(expect= true false)'
+
+ assert.has.error -> COPILOT\eval_once '(expect= "asdf" "bsdf")'
+ assert.has.error -> COPILOT\eval_once '(expect= (array 1 2) (array 1 3))'
+
+ it "fails different types", ->
+ assert.has.error -> COPILOT\eval_once '(expect= true 2)'
+ assert.has.error -> COPILOT\eval_once '(expect= true true 1)'
+ assert.has.error -> COPILOT\eval_once '(expect= true false "str")'
+
+ it "reports first failing arguments", ->
+ err = assert.has.error -> COPILOT\eval_once '(expect= 1 2)'
+ assert.matches "assertion error: Expected 2 to equal <num= 1> %(got <num= 2>%)", err
+
+ err = assert.has.error -> COPILOT\eval_once '(expect= 1 1 2 4)'
+ assert.matches "assertion error: Expected 2 to equal <num= 1> %(got <num= 2>%)", err
+
+ err = assert.has.error -> COPILOT\eval_once '(expect= true 2 3)'
+ assert.matches "assertion error: Expected 2 to equal <bool= true> %(got <num= 2>%)", err
+
+ it "reports full expressions", ->
+ err = assert.has.error -> COPILOT\eval_once '(import* math) (expect= 1 (+ 1 1))'
+ assert.matches "assertion error: Expected %(%+ 1 1%) to equal <num= 1> %(got <num= 2>%)", err