1package eval_test
2
3import (
4	"math"
5	"math/big"
6	"testing"
7	"unsafe"
8
9	. "src.elv.sh/pkg/eval"
10	"src.elv.sh/pkg/eval/errs"
11
12	. "src.elv.sh/pkg/eval/evaltest"
13	"src.elv.sh/pkg/eval/vals"
14)
15
16func TestNsCmd(t *testing.T) {
17	Test(t,
18		That("put (ns [&name=value])[name]").Puts("value"),
19		That("n: = (ns [&name=value]); put $n:name").Puts("value"),
20		That("ns [&[]=[]]").Throws(errs.BadValue{
21			What:  `key of argument of "ns"`,
22			Valid: "string", Actual: "list"}),
23	)
24}
25
26func TestMakeMap(t *testing.T) {
27	Test(t,
28		That("make-map []").Puts(vals.EmptyMap),
29		That("make-map [[k v]]").Puts(vals.MakeMap("k", "v")),
30		That("make-map [[k v] [k v2]]").Puts(vals.MakeMap("k", "v2")),
31		That("make-map [[k1 v1] [k2 v2]]").
32			Puts(vals.MakeMap("k1", "v1", "k2", "v2")),
33		That("make-map [kv]").Puts(vals.MakeMap("k", "v")),
34		That("make-map [{ } [k v]]").
35			Throws(
36				errs.BadValue{
37					What: "input to make-map", Valid: "iterable", Actual: "fn"},
38				"make-map [{ } [k v]]"),
39		That("make-map [[k v] [k]]").
40			Throws(
41				errs.BadValue{
42					What: "input to make-map", Valid: "iterable with 2 elements",
43					Actual: "list with 1 elements"},
44				"make-map [[k v] [k]]"),
45	)
46}
47
48var (
49	maxInt = 1<<((unsafe.Sizeof(0)*8)-1) - 1
50	minInt = -maxInt - 1
51
52	maxDenseIntInFloat = float64(1 << 53)
53)
54
55func TestRange(t *testing.T) {
56	Test(t,
57		// Basic argument sanity checks.
58		That("range").Throws(ErrorWithType(errs.ArityMismatch{})),
59		That("range 0 1 2").Throws(ErrorWithType(errs.ArityMismatch{})),
60
61		// Int count up.
62		That("range 3").Puts(0, 1, 2),
63		That("range 1 3").Puts(1, 2),
64		// Int count down.
65		That("range -1 10 &step=3").Puts(-1, 2, 5, 8),
66		That("range 3 -3").Puts(3, 2, 1, 0, -1, -2),
67		// Near maxInt or minInt.
68		That("range "+args(maxInt-2, maxInt)).Puts(maxInt-2, maxInt-1),
69		That("range "+args(maxInt, maxInt-2)).Puts(maxInt, maxInt-1),
70		That("range "+args(minInt, minInt+2)).Puts(minInt, minInt+1),
71		That("range "+args(minInt+2, minInt)).Puts(minInt+2, minInt+1),
72		// Invalid step given the "start" and "end" values of the range.
73		That("range &step=-1 1").
74			Throws(errs.BadValue{What: "step", Valid: "positive", Actual: "-1"}),
75		That("range &step=1 1 0").
76			Throws(errs.BadValue{What: "step", Valid: "negative", Actual: "1"}),
77		thatOutputErrorIsBubbled("range 2"),
78
79		// Big int count up.
80		That("range "+z+" "+z3).Puts(bigInt(z), bigInt(z1), bigInt(z2)),
81		That("range "+z+" "+z3+" &step=2").Puts(bigInt(z), bigInt(z2)),
82		// Big int count down.
83		That("range "+z3+" "+z).Puts(bigInt(z3), bigInt(z2), bigInt(z1)),
84		That("range "+z3+" "+z+" &step=-2").Puts(bigInt(z3), bigInt(z1)),
85		// Invalid big int step.
86		That("range &step=-"+z+" 10").
87			Throws(errs.BadValue{What: "step", Valid: "positive", Actual: "-" + z}),
88		That("range &step="+z+" 10 0").
89			Throws(errs.BadValue{What: "step", Valid: "negative", Actual: z}),
90		thatOutputErrorIsBubbled("range "+z+" "+z1),
91
92		// Rational count up.
93		That("range 23/10").Puts(0, 1, 2),
94		That("range 1/10 23/10").Puts(
95			big.NewRat(1, 10), big.NewRat(11, 10), big.NewRat(21, 10)),
96		That("range 23/10 1/10").Puts(
97			big.NewRat(23, 10), big.NewRat(13, 10), big.NewRat(3, 10)),
98		That("range 1/10 9/10 &step=3/10").Puts(
99			big.NewRat(1, 10), big.NewRat(4, 10), big.NewRat(7, 10)),
100		// Rational count down.
101		That("range 9/10 0/10 &step=-3/10").Puts(
102			big.NewRat(9, 10), big.NewRat(6, 10), big.NewRat(3, 10)),
103		// Invalid rational step.
104		That("range &step=-1/2 10").
105			Throws(errs.BadValue{What: "step", Valid: "positive", Actual: "-1/2"}),
106		That("range &step=1/2 10 0").
107			Throws(errs.BadValue{What: "step", Valid: "negative", Actual: "1/2"}),
108		thatOutputErrorIsBubbled("range 1/2 3/2"),
109
110		// Float64 count up.
111		That("range 1.2").Puts(0.0, 1.0),
112		That("range &step=0.5 1 3").Puts(1.0, 1.5, 2.0, 2.5),
113		// Float64 count down.
114		That("range 1.2 -1.2").Puts(1.2, Approximately{F: 0.2}, Approximately{F: -0.8}),
115		That("range &step=-0.5 3 1").Puts(3.0, 2.5, 2.0, 1.5),
116		// Near maxDenseIntInFloat.
117		That("range "+args(maxDenseIntInFloat-2, "+inf")).
118			Puts(maxDenseIntInFloat-2, maxDenseIntInFloat-1, maxDenseIntInFloat),
119		That("range "+args(maxDenseIntInFloat, maxDenseIntInFloat-2)).
120			Puts(maxDenseIntInFloat, maxDenseIntInFloat-1),
121		// Invalid float64 step.
122		That("range &step=-0.5 10").
123			Throws(errs.BadValue{What: "step", Valid: "positive", Actual: "-0.5"}),
124		That("range &step=0.5 10 0").
125			Throws(errs.BadValue{What: "step", Valid: "negative", Actual: "0.5"}),
126		thatOutputErrorIsBubbled("range 1.2"),
127	)
128}
129
130func TestRepeat(t *testing.T) {
131	Test(t,
132		That(`repeat 4 foo`).Puts("foo", "foo", "foo", "foo"),
133		thatOutputErrorIsBubbled("repeat 1 foo"),
134	)
135}
136
137func TestAssoc(t *testing.T) {
138	Test(t,
139		That(`put (assoc [0] 0 zero)[0]`).Puts("zero"),
140		That(`put (assoc [&] k v)[k]`).Puts("v"),
141		That(`put (assoc [&k=v] k v2)[k]`).Puts("v2"),
142	)
143}
144
145func TestDissoc(t *testing.T) {
146	Test(t,
147		That(`has-key (dissoc [&k=v] k) k`).Puts(false),
148		That("dissoc foo 0").Throws(ErrorWithMessage("cannot dissoc")),
149	)
150}
151
152func TestAll(t *testing.T) {
153	Test(t,
154		That(`put foo bar | all`).Puts("foo", "bar"),
155		That(`echo foobar | all`).Puts("foobar"),
156		That(`all [foo bar]`).Puts("foo", "bar"),
157		thatOutputErrorIsBubbled("all [foo bar]"),
158	)
159}
160
161func TestOne(t *testing.T) {
162	Test(t,
163		That(`put foo | one`).Puts("foo"),
164		That(`put | one`).Throws(AnyError),
165		That(`put foo bar | one`).Throws(AnyError),
166		That(`one [foo]`).Puts("foo"),
167		That(`one []`).Throws(AnyError),
168		That(`one [foo bar]`).Throws(AnyError),
169		thatOutputErrorIsBubbled("one [foo]"),
170	)
171}
172
173func TestTake(t *testing.T) {
174	Test(t,
175		That(`range 100 | take 2`).Puts(0, 1),
176		thatOutputErrorIsBubbled("take 1 [foo bar]"),
177	)
178}
179
180func TestDrop(t *testing.T) {
181	Test(t,
182		That(`range 100 | drop 98`).Puts(98, 99),
183		thatOutputErrorIsBubbled("drop 1 [foo bar lorem]"),
184	)
185}
186
187func TestHasKey(t *testing.T) {
188	Test(t,
189		That(`has-key [foo bar] 0`).Puts(true),
190		That(`has-key [foo bar] 0..1`).Puts(true),
191		That(`has-key [foo bar] 0..20`).Puts(false),
192		That(`has-key [&lorem=ipsum &foo=bar] lorem`).Puts(true),
193		That(`has-key [&lorem=ipsum &foo=bar] loremwsq`).Puts(false),
194	)
195}
196
197func TestHasValue(t *testing.T) {
198	Test(t,
199		That(`has-value [&lorem=ipsum &foo=bar] lorem`).Puts(false),
200		That(`has-value [&lorem=ipsum &foo=bar] bar`).Puts(true),
201		That(`has-value [foo bar] bar`).Puts(true),
202		That(`has-value [foo bar] badehose`).Puts(false),
203		That(`has-value "foo" o`).Puts(true),
204		That(`has-value "foo" d`).Puts(false),
205	)
206}
207
208func TestCount(t *testing.T) {
209	Test(t,
210		That(`range 100 | count`).Puts(100),
211		That(`count [(range 100)]`).Puts(100),
212		That(`count 123`).Puts(3),
213		That(`count 1 2 3`).Throws(
214			errs.ArityMismatch{What: "arguments", ValidLow: 0, ValidHigh: 1, Actual: 3},
215			"count 1 2 3"),
216		That(`count $true`).Throws(ErrorWithMessage("cannot get length of a bool")),
217	)
218}
219
220func TestKeys(t *testing.T) {
221	Test(t,
222		That(`keys [&]`).DoesNothing(),
223		That(`keys [&a=foo]`).Puts("a"),
224		// Windows does not have an external sort command. Disabled until we have a
225		// builtin sort command.
226		That(`keys [&a=foo &b=bar] | order`).Puts("a", "b"),
227		That("keys (num 1)").Throws(ErrorWithMessage("cannot iterate keys of number")),
228		thatOutputErrorIsBubbled("keys [&a=foo]"),
229	)
230}
231
232func TestCompare(t *testing.T) {
233	Test(t,
234		// Comparing strings.
235		That("compare a b").Puts(-1),
236		That("compare b a").Puts(1),
237		That("compare x x").Puts(0),
238
239		// Comparing numbers.
240		That("compare (num 1) (num 2)").Puts(-1),
241		That("compare (num 2) (num 1)").Puts(1),
242		That("compare (num 3) (num 3)").Puts(0),
243
244		That("compare (num 1/4) (num 1/2)").Puts(-1),
245		That("compare (num 1/3) (num 0.2)").Puts(1),
246		That("compare (num 3.0) (num 3)").Puts(0),
247
248		That("compare (num nan) (num 3)").Puts(-1),
249		That("compare (num 3) (num nan)").Puts(1),
250		That("compare (num nan) (num nan)").Puts(0),
251
252		// Comparing lists.
253		That("compare [a, b] [a, a]").Puts(1),
254		That("compare [a, a] [a, b]").Puts(-1),
255		That("compare [x, y] [x, y]").Puts(0),
256
257		// Uncomparable values.
258		That("compare 1 (num 1)").Throws(ErrUncomparable),
259		That("compare x [x]").Throws(ErrUncomparable),
260		That("compare a [&a=x]").Throws(ErrUncomparable),
261	)
262}
263
264func TestOrder(t *testing.T) {
265	Test(t,
266		// Ordering strings
267		That("put foo bar ipsum | order").Puts("bar", "foo", "ipsum"),
268		That("put foo bar bar | order").Puts("bar", "bar", "foo"),
269		That("put 10 1 5 2 | order").Puts("1", "10", "2", "5"),
270
271		// Ordering typed numbers
272		// Only small integers
273		That("put 10 1 1 | each $num~ | order").Puts(1, 1, 10),
274		That("put 10 1 5 2 -1 | each $num~ | order").Puts(-1, 1, 2, 5, 10),
275		// Small and large integers
276		That("put 1 "+z+" 2 "+z+" | each $num~ | order").Puts(1, 2, bigInt(z), bigInt(z)),
277		// Integers and rationals
278		That("put 1 2 3/2 3/2 | each $num~ | order").
279			Puts(1, big.NewRat(3, 2), big.NewRat(3, 2), 2),
280		// Integers and floats
281		That("put 1 1.5 2 1.5 | each $num~ | order").
282			Puts(1, 1.5, 1.5, 2),
283		// Mixed integers and floats.
284		That("put (num 1) (float64 1.5) (float64 2) (num 1.5) | order").
285			Puts(1, 1.5, 1.5, 2.0),
286		// For the sake of ordering, NaN's are considered smaller than other numbers
287		That("put NaN -1 NaN | each $num~ | order").Puts(math.NaN(), math.NaN(), -1),
288
289		// Ordering lists
290		That("put [b] [a] | order").Puts(vals.MakeList("a"), vals.MakeList("b")),
291		That("put [a] [b] [a] | order").
292			Puts(vals.MakeList("a"), vals.MakeList("a"), vals.MakeList("b")),
293		That("put [(float64 10)] [(float64 2)] | order").
294			Puts(vals.MakeList(2.0), vals.MakeList(10.0)),
295		That("put [a b] [b b] [a c] | order").
296			Puts(
297				vals.MakeList("a", "b"),
298				vals.MakeList("a", "c"), vals.MakeList("b", "b")),
299		That("put [a] [] [a (float64 2)] [a (float64 1)] | order").
300			Puts(vals.EmptyList, vals.MakeList("a"),
301				vals.MakeList("a", 1.0), vals.MakeList("a", 2.0)),
302
303		// Attempting to order uncomparable values
304		That("put (num 1) 1 | order").
305			Throws(ErrUncomparable, "order"),
306		That("put 1 (float64 1) | order").
307			Throws(ErrUncomparable, "order"),
308		That("put 1 (float64 1) b | order").
309			Throws(ErrUncomparable, "order"),
310		That("put [a] a | order").
311			Throws(ErrUncomparable, "order"),
312		That("put [a] [(float64 1)] | order").
313			Throws(ErrUncomparable, "order"),
314
315		// &reverse
316		That("put foo bar ipsum | order &reverse").Puts("ipsum", "foo", "bar"),
317
318		// &less-than
319		That("put 1 10 2 5 | order &less-than={|a b| < $a $b }").
320			Puts("1", "2", "5", "10"),
321
322		// &less-than writing more than one value
323		That("put 1 10 2 5 | order &less-than={|a b| put $true $false }").
324			Throws(
325				errs.BadValue{
326					What:  "output of the &less-than callback",
327					Valid: "a single boolean", Actual: "2 values"},
328				"order &less-than={|a b| put $true $false }"),
329
330		// &less-than writing non-boolean value
331		That("put 1 10 2 5 | order &less-than={|a b| put x }").
332			Throws(
333				errs.BadValue{
334					What:  "output of the &less-than callback",
335					Valid: "boolean", Actual: "string"},
336				"order &less-than={|a b| put x }"),
337
338		// &less-than throwing an exception
339		That("put 1 10 2 5 | order &less-than={|a b| fail bad }").
340			Throws(
341				FailError{"bad"},
342				"fail bad ", "order &less-than={|a b| fail bad }"),
343
344		// &less-than and &reverse
345		That("put 1 10 2 5 | order &reverse &less-than={|a b| < $a $b }").
346			Puts("10", "5", "2", "1"),
347
348		// Sort should be stable - test by pretending that all values but one
349		// are equal, an check that the order among them has not changed.
350		That("put l x o x r x e x m | order &less-than={|a b| eq $a x }").
351			Puts("x", "x", "x", "x", "l", "o", "r", "e", "m"),
352
353		thatOutputErrorIsBubbled("order [foo]"),
354	)
355}
356