1 /* A nice test of the translator would be to translate the entire test suite ...
2 * In the meantime here are some tests to verify some specific bugs are fixed.
8 /* There are various cases where (by design) translate and compile don't
9 * signal an error when a function fails to translate. So here we define
10 * translate_or_lose and compile_or_lose so we don't have to explicitly
11 * check the return values of translate and compile in every test.
13 * Passing `all' or `functions' is not supported. I'm not sure how much
14 * sense those make here for tests, but something could be added later if
15 * a test really wants them for some reason.
17 block ([translate : false],
19 make_tester (trfun) ::=
20 buildq ([trfun, name : concat (trfun, '_or_lose)],
22 block ([ret : apply ('trfun, fns), losers : []],
24 /* Report failures in the same order as the given args */
25 (for f in fns do if not (member (f, ret)) then push (f, losers),
26 error ('trfun, "failed unexpectedly for", reverse (losers))),
28 /* Prevent this from being killed during kill(all), etc.
29 * We can remove this later if we're more precise about
30 * what we're killing throughout this file.
32 ?mfunction\-delete ('name, functions))),
33 make_tester (translate),
34 make_tester (compile),
38 /* SF [ 1728888 ] translator bugs: no mnot mprogn */
40 (foo (e,v) := block([vi], for vi in v while not(emptyp(e)) do (print(vi), e : rest(e)), e),
41 foo ([1, 2, 3], [a, b]));
44 (translate_or_lose (foo), foo ([1, 2, 3], [a, b]));
47 /* simpler function featuring mprogn and mnot */
49 (bar (x) := (x : 2*x, x : 3*x, not (x < 100)), bar (3));
52 (translate_or_lose (bar), bar (3));
55 /* SF [ 1646525 ] no function mdoin */
57 (try_me(x) := block([acc : 0], for i in x while i > 5 do acc : acc + i, acc),
58 try_me ([10, 9, 8, 7, 6, 5, 4, 5, 6, 7, 8, 9, 10]));
61 (translate_or_lose (try_me), try_me ([10, 9, 8, 7, 6, 5, 4, 5, 6, 7, 8, 9, 10]));
64 /* SF [ 1818645 ] Compiled maxima code containing $ARRAY gets a Lisp error. */
66 (test_array_comp (x) :=
69 for i thru 3 do (abc[i]: i*i),
71 [abc, abc[3], abc[2]]),
72 test_array_comp (100));
75 (translate_or_lose (test_array_comp), test_array_comp (100));
78 /* SF [ 545794 ] Local Array does not compile properly */
84 for i : 0 thru 7 do myvar [i] : a^i,
85 [member (myvar, arrays), listarray (myvar)]),
87 [true, [1, 2, 4, 8, 16, 32, 64, 128]];
89 (translate_or_lose (trial), trial (2));
90 [true, [1, 2, 4, 8, 16, 32, 64, 128]];
92 /* This next test used to fail because local properties would leak out
93 * in the translated case. This test is expected to pass now. There
94 * are more local tests below for bug #2976.
97 [member (myvar, arrays), errcatch (listarray (myvar))];
100 /* for loop variable not special
101 * reported to mailing list 2009-08-13 "Why won't this compile?"
106 baz1 (n) := block ([S : 0], for bar1:0 thru n - 1 do S : S + foo1 (), S),
107 translate_or_lose (baz1),
111 /* original example */
113 (fun(A,b,s,VF,x,h):= block
117 Y[i]: x + h*(sum(A[i,j]*VF(Y[j]),j,1,i-1)),
118 x: expand(x + h*sum(b[i]*VF(Y[i]),i,1,s))),
119 A: matrix([1,1],[1,1]),
124 fun(A,b,2,f,[1,1],.01);
125 0.01*f(0.01*f([1,1])+[1,1])+0.01*f([1,1])+[1,1]$
127 (translate_or_lose (fun), fun(A,b,2,f,[1,1],.01));
128 0.01*f(0.01*f([1,1])+[1,1])+0.01*f([1,1])+[1,1]$
130 /* incorrect code emitted for call from translated function to untranslated
131 * SF bug # 2934064 "problem loading ezunits"
134 (f0001 (x) := [f0002 (x), f0003 (x)],
137 translate_or_lose (f0002, f0001),
141 (translate_or_lose (f0003), f0001 (1));
144 (compile_or_lose (f0003), f0001 (1));
147 (compile_or_lose (f0003, f0002, f0001), f0001 (1));
150 /* SF bug # 2938716 "too much evaluation in translated code"
153 (g0001 (x) := [g0002 (x), g0003 (x)],
156 translate_or_lose (g0002, g0001),
163 (translate_or_lose (g0003), g0001 (aa));
166 (compile_or_lose (g0003), g0001 (aa));
169 (compile_or_lose (g0003, g0002, g0001), g0001 (aa));
172 /* SF bug # 3035313 "some array references translated incorrectly"
175 (kill (aa1, aa3, bb1, bb3, cc1, cc3),
177 array (aa3, 12, 4, 6),
178 array (bb1, flonum, 15),
179 array (bb3, flonum, 5, 6, 7),
180 array (cc1, fixnum, 8),
181 array (cc3, fixnum, 6, 10, 4),
185 (kill (faa, gaa, fbb, gbb, fcc, gcc),
186 faa (n) := aa1[n] + aa3[n, n - 1, n - 2],
187 gaa (n) := (aa1[n] : 123, aa3[n, n - 1, n - 2] : 321),
188 fbb (n) := bb1[n] + bb3[n, n - 1, n - 2],
189 gbb (n) := (bb1[n] : 123, bb3[n, n - 1, n - 2] : 321),
190 fcc (n) := cc1[n] + cc3[n, n - 1, n - 2],
191 gcc (n) := (cc1[n] : 123, cc3[n, n - 1, n - 2] : 321),
195 [gaa (4), gbb (4), gcc (4)];
198 [faa (4), fbb (4), fcc (4)];
201 translate_or_lose (faa, gaa, fbb, gbb, fcc, gcc);
202 [faa, gaa, fbb, gbb, fcc, gcc];
204 [gaa (4), gbb (4), gcc (4)];
207 [faa (4), fbb (4), fcc (4)];
210 compile_or_lose (faa, gaa, fbb, gbb, fcc, gcc);
211 [faa, gaa, fbb, gbb, fcc, gcc];
213 [gaa (4), gbb (4), gcc (4)];
216 [faa (4), fbb (4), fcc (4)];
219 /* try same stuff again w/ undeclared arrays ...
220 * no type spec => only one kind of array
223 (kill (aa1, aa3, bb1, bb3, cc1, cc3),
229 (translate_or_lose (faa, gaa), [gaa (4), faa (4)]);
232 (compile_or_lose (faa, gaa), [gaa (4), faa (4)]);
235 /* try same stuff again w/ Lisp arrays */
237 (kill (aa1, aa3, bb1, bb3, cc1, cc3),
238 map (?fmakunbound, [faa, fbb, fcc, gaa, gbb, gcc]),
239 aa1 : make_array (any, 15),
240 aa3 : make_array (any, 12, 4, 6),
241 bb1 : make_array (flonum, 15),
242 bb3 : make_array (flonum, 5, 6, 7),
243 cc1 : make_array (fixnum, 8),
244 cc3 : make_array (fixnum, 6, 10, 4),
248 [gaa (4), gbb (4), gcc (4)];
251 [faa (4), fbb (4), fcc (4)];
254 translate_or_lose (faa, gaa, fbb, gbb, fcc, gcc);
255 [faa, gaa, fbb, gbb, fcc, gcc];
257 [gaa (4), gbb (4), gcc (4)];
260 [faa (4), fbb (4), fcc (4)];
263 compile_or_lose (faa, gaa, fbb, gbb, fcc, gcc);
264 [faa, gaa, fbb, gbb, fcc, gcc];
266 [gaa (4), gbb (4), gcc (4)];
269 [faa (4), fbb (4), fcc (4)];
272 /* SF bug # 2569: "translate rat(1,x) and rat([1]) incorrect" */
274 (kill (f), f () := rat (x, x), translate_or_lose (f), f ());
277 (kill (f), f () := rat ([1]), translate_or_lose (f), f ());
280 (kill (foo, y1a, y1b, y2a, y2b),
281 foo(x) := block (mode_declare (x, float),
282 [tanh (x), tan (x), sech (x), sec (x), acos (x), acot (x), sin (x),
283 acsc (x), asinh (x), acsch (x), cosh (x), coth (x), realpart (x),
284 asec (x), asin (x), erf (x), log (x), cos (x), cot (x), csc (x),
285 sinh (x), csch (x), sqrt (x), exp (x)]),
290 [.4621171572600097,.5463024898437905,0.886818883970074,1.139493927324549,
291 1.047197551196597,1.107148717794091,0.479425538604203,
292 1.570796326794897-1.316957896924817*%i,.4812118250596035,1.44363547517881,
293 1.127625965206381,2.163953413738653,0.5,1.316957896924817*%i,
294 0.523598775598299,.5204998778130465,-.6931471805599453,.8775825618903728,
295 1.830487721712452,2.085829642933488,.5210953054937474,1.919034751334944,
296 .7071067811865476,1.648721270700128]$
299 [.9051482536448664,14.10141994717172,.4250960349422805,14.1368329029699,
300 .9624236501192069*%i,.5880026035475675,.9974949866040544,.7297276562269662,
301 1.194763217287109,.6251451172504168,2.352409615243247,1.104791392982512,1.5,
302 .8410686705679303,1.570796326794897-.9624236501192069*%i,.9661051464753108,
303 .4054651081081644,0.0707372016677029,.07091484430265245,1.002511304246725,
304 2.129279455094817,.4696424405952246,1.224744871391589,4.481689070338065]$
307 [0.7615941559557649,1.557407724654902,0.6480542736638855,
308 1.850815717680925,0.0,0.7853981633974483,0.8414709848078965,
309 1.570796326794897,0.881373587019543,0.881373587019543,
310 1.543080634815244,1.313035285499331,1.0,0.0,1.570796326794897,
311 0.8427007929497148,0.0,0.5403023058681398,0.6420926159343306,
312 1.188395105778121,1.175201193643801,0.8509181282393216,1.0,
315 (translate_or_lose (foo), y2a : foo (0.5), y2b : foo (1.5), 0);
324 block ([tr_float_can_branch_complex : false],
325 translate_or_lose (foo),
333 /* verify that save/compfile/compile_file/translate_file preserves upper/lower case in symbol names */
338 foo (x) := my_foo * x,
339 Foo (x) := my_Foo * x,
340 FOO (x) := my_FOO * x,
341 [my_foo, my_Foo, my_FOO] : [123, 456, 789],
342 results : [foo (2), Foo (3), FOO (4)],
343 my_test () := is (results = [2*123, 3*456, 4*789]),
344 lisp_name : ssubst ("_", " ", build_info()@lisp_name),
345 lisp_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-save-", lisp_name, ".lisp"),
346 save (lisp_filename, values, functions),
347 kill (allbut (lisp_filename)),
348 load (lisp_filename),
355 foo (x) := my_foo * x,
356 Foo (x) := my_Foo * x,
357 FOO (x) := my_FOO * x,
358 lisp_name : ssubst ("_", " ", build_info()@lisp_name),
359 lisp_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-compfile-", lisp_name, ".lisp"),
360 compfile (lisp_filename, functions),
362 load (lisp_filename),
363 [my_foo, my_Foo, my_FOO] : [123, 456, 789],
364 results : [foo (2), Foo (3), FOO (4)],
365 my_test () := is (results = [2*123, 3*456, 4*789]),
371 /* skip this test (and fail) if Lisp = ECL because stream i/o causes "stack smashing" error
372 * see: https://sourceforge.net/p/maxima/bugs/3291/
374 if build_info()@lisp_name # "ECL" then
376 lisp_name : ssubst ("_", " ", build_info()@lisp_name),
377 maxima_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-compile_file-", lisp_name, ".mac"),
378 fasl_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-compile_file-", lisp_name, ".fasl"),
379 lisp_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-compile_file-", lisp_name, ".LISP"),
380 maxima_output : openw (maxima_filename),
382 "foo (x) := my_foo * x;
383 Foo (x) := my_Foo * x;
384 FOO (x) := my_FOO * x;
385 [my_foo, my_Foo, my_FOO] : [123, 456, 789];
386 results : [foo (2), Foo (3), FOO (4)];
387 my_test () := is (results = [2*123, 3*456, 4*789]);",
388 printf (maxima_output, maxima_content),
389 close (maxima_output),
390 compile_file (maxima_filename, fasl_filename, lisp_filename),
391 kill (allbut (lisp_filename)),
392 load (lisp_filename),
398 /* skip this test (and fail) if Lisp = ECL because stream i/o causes "stack smashing" error
399 * see: https://sourceforge.net/p/maxima/bugs/3291/
401 if build_info()@lisp_name # "ECL" then
403 lisp_name : ssubst ("_", " ", build_info()@lisp_name),
404 maxima_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-translate_file-", lisp_name, ".mac"),
405 lisp_filename : sconcat (maxima_tempdir, "/tmp-rtest_translator-translate_file-", lisp_name, ".LISP"),
406 maxima_output : openw (maxima_filename),
408 "foo (x) := my_foo * x;
409 Foo (x) := my_Foo * x;
410 FOO (x) := my_FOO * x;
411 [my_foo, my_Foo, my_FOO] : [123, 456, 789];
412 results : [foo (2), Foo (3), FOO (4)];
413 my_test () := is (results = [2*123, 3*456, 4*789]);",
414 printf (maxima_output, maxima_content),
415 close (maxima_output),
416 translate_file (maxima_filename, lisp_filename),
417 kill (allbut (lisp_filename)),
418 load (lisp_filename),
424 Translating a literal exponent that comes out as a float shouldn't
425 produce assigned type any. This test runs the translation for a
426 trivial function that triggered the bug then looks in the unlisp
427 file (which contains messages from the translator) and checks that
428 there aren't any warnings.
430 /* skip this test (and fail) if Lisp = ECL because stream i/o causes "stack smashing" error
431 * see: https://sourceforge.net/p/maxima/bugs/3291/
433 if build_info()@lisp_name # "ECL" then
435 lisp_name : ssubst ("_", " ", build_info()@lisp_name),
436 basename: sconcat (maxima_tempdir, "/tmp-rtest_translator-2934-", lisp_name),
437 maxima_filename : sconcat (basename, ".mac"),
438 lisp_filename : sconcat (basename, ".LISP"),
439 maxima_output : openw (maxima_filename),
440 maxima_content : "f () := block([y], mode_declare(y,float), y: 3^0.33, y)$",
441 printf (maxima_output, maxima_content),
442 close (maxima_output),
443 translate_file (maxima_filename, lisp_filename),
444 kill (allbut(basename)),
445 /* Any warning messages end up at .UNLISP */
446 block ([unlisp: openr (sconcat (basename, ".UNLISP")),
448 while stringp (line: readline(unlisp)) do
449 if is ("warning" = split(line, ":")[1]) then push(line, acc),
453 /* makelist translated incorrectly
454 * SF bug #3083: "Error on compiling a working maxima function"
458 f1(n) := makelist (1, n),
459 f2(n) := makelist (i^2, i, n),
460 f3(l) := makelist (i^3, i, l),
461 f4(n) := makelist (i^4, i, 1, n),
462 f5(m, n) := makelist (i^5, i, 1, n, m),
463 translate_or_lose(f1, f2, f3, f4, f5),
480 [1, 243, 3125, 16807, 59049];
482 /* original function from bug report */
484 (ordersort(lis,vars,oper):=block([negsumdispflag:false,liss:lis,varlist:vars,temp], /*Does lexicographical sort */
485 for i:1 thru length(varlist) do (
487 liss:sort(liss,lambda([x,y],apply("and",map(oper,makelist(part(x,2)[k],k,1,i)
488 ,makelist(part(y,2)[k],k,1,i)))))
490 translate_or_lose (ordersort),
491 [member ('transfun, properties (ordersort)),
492 ordersort([[-7,[0,2,1]],[3,[1,2,1]],[1,[0,4,1]],[6,[4,3,3]],[6,[4,4,3]],
493 [-7,[3,5,4]],[2,[0,0,5]],[-10,[2,2,5]],[-10,[3,4,7]],[7,[3,8,9]]],
497 [[6,[4,4,3]],[6,[4,3,3]],[7,[3,8,9]],[-7,[3,5,4]],[-10,[3,4,7]],
498 [-10,[2,2,5]],[3,[1,2,1]],[1,[0,4,1]],[-7,[0,2,1]],[2,[0,0,5]]]];
500 /* 'define' translated incorrectly, reported to mailing list circa 2017-01-24 */
502 (kill(foo, bar, baz, quux, mumble, blurf, umm, f, x, y),
503 foo(y) := define(bar(x), x + y),
504 baz(f, y) := define(funmake(f, [x]), x + y),
505 quux() := (mumble(x) := 1 + x),
506 [foo(10), baz(blurf, 20), quux()]);
507 /* note that results match because rhs of ":=" isn't simplified */
508 [bar(x) := 10 + x, blurf(x) := 20 + x, mumble(x) := 1 + x];
510 [bar(5), blurf(5), mumble(5)];
513 (kill(bar, blurf, mumble),
514 translate_or_lose(foo, baz, quux),
515 [foo(11), baz(umm, 21), quux()]);
516 /* note that results match because rhs of ":=" isn't simplified */
517 [bar(x) := 11 + x, umm(x) := 21 + x, mumble(x) := 1 + x];
519 makelist (is (x # false), x, map (?fboundp, [foo, baz, quux])); /* test for generalized Boolean value */
522 [bar(5), umm(5), mumble(5)];
525 /* mailing list 2017-03-04: "An example that is broken by compile()"
526 * translated code tickles a bug elsewhere (bug not in translator)
529 (kill(fun, trigfunc, t1),
530 fun():=block([trigfunc],
531 trigfunc:lambda([cur],cur>t1),
532 apply('trigfunc,[1])),
536 /* I (Robert Dodier) believe this result should be trigfunc(1),
537 * but, in any event, interpreted and compiled code should agree.
538 * But if MAPPLY1 is ever changed, we can adjust these results.
543 (compile_or_lose(fun), fun());
546 (kill(fun, trigfunc, t1),
547 fun():=block([trigfunc],
548 trigfunc:lambda([cur],cur>t1),
549 apply(trigfunc,[1])),
556 (compile_or_lose(fun), fun());
559 /* Verify that we catch malformed lambda expressions during translation.
560 * More checks need to be added to the translator and more tests need to
564 /* no parameter list */
572 f () := lambda ([x]),
576 /* non-symbol in parameter list */
578 f () := lambda ([42], 'foo),
582 /* misplaced "rest" parameter */
584 f () := lambda ([[l], x], 'foo),
588 /* invalid "rest" parameter */
590 f () := lambda ([[l1, l2]], 'foo),
594 /* attempting to bind a constant;
595 * now OK, after commit 0517895
599 declare (c, constant),
600 f () := lambda ([c], c),
601 translate_or_lose (f))$
604 /* Verify that parameter/variable lists cannot contain duplicate variables.
606 * We only test a couple of cases here. Many more tests for non-translated
607 * code are in rtest2. Do we want to test them all here as well?
611 f () := lambda ([x, [x]], x),
616 f () := block ([x, x:'foo], x),
620 /* ensure that a null OPERATORS property doesn't interfere with
621 * translation of local variable used as a function name.
622 * This is the bug that caused failures in rtest_fractals when executed after run_testsuite.
625 (kill(aa, foobarbaz, mumbleblurf, hhh),
626 matchdeclare (aa, all),
627 tellsimp (mumbleblurf(aa), 1 - aa),
628 kill (mumbleblurf), /* as of 2018-01-28, this leaves (OPERATORS NIL) in property list */
629 hhh(mumbleblurf, u) := mumbleblurf(u),
630 foobarbaz(x) := 100 + x,
631 translate_or_lose (hhh),
632 hhh (foobarbaz, 11));
635 /* SF bug report #3402: "Unbinding defined variable generates error in compiled functions" */
637 define_variable (zorble, 0, fixnum);
640 (kill(f), f() := block ([zorble], 42), f());
643 (translate_or_lose(f), f());
646 /* bug reported to mailing list 2018-12-03: "error in compiling function with global variable" */
648 (test_f():= block( [a,b,c,d], niceindicespref:[a,b,c,d], disp("ciao"), return() ),
652 (test_f (), niceindicespref);
655 (reset (niceindicespref),
659 (translate_or_lose (test_f),
664 (reset (niceindicespref), 0);
667 /* additional tests with variables which have ASSIGN property */
669 (set_error_stuff_permanently () :=
670 block (error_syms : '[myerr1, myerr2, myerr3], error_size : 40),
671 set_error_stuff_temporarily() :=
672 block([error_syms : '[myerror1, myerror2, myerror3], error_size : 55],
673 [error_syms, error_size]),
677 (reset (error_syms, error_size),
678 set_error_stuff_permanently (),
679 [error_syms, error_size]);
680 [[myerr1, myerr2, myerr3], 40];
682 (reset (error_syms, error_size),
683 translate_or_lose (set_error_stuff_permanently),
684 set_error_stuff_permanently (),
685 [error_syms, error_size]);
686 [[myerr1, myerr2, myerr3], 40];
688 (reset (error_syms, error_size),
689 set_error_stuff_temporarily());
690 [[myerror1, myerror2, myerror3], 55];
692 [error_syms, error_size];
693 [[errexp1, errexp2, errexp3], 60];
695 (translate_or_lose (set_error_stuff_temporarily),
696 set_error_stuff_temporarily());
697 [[myerror1, myerror2, myerror3], 55];
699 [error_syms, error_size];
700 [[errexp1, errexp2, errexp3], 60];
702 (kill(all), reset(), 0);
705 /* SF bug #3412: "Bug when translating functions that contain an \"if\" (in my case an implicit if)" */
707 (f(x):=if cabs(1/(x+1)) < 1 then 1/(x+1) else 1,
709 if 1/sqrt(y^2+(x+1)^2) < 1 then 1/(%i*y+x+1) else 1;
711 makelist (f(xy[1] + %i*xy[2]), xy, [[0, 0], [0, 1], [1, 1], [1, 0], [0, 2], [2, 2], [2, 0]]);
712 [1, 1/(%i+1), 1/(%i+2), 1/2, 1/(2*%i+1), 1/(2*%i+3), 1/3]$
714 (compile_or_lose (f),
715 errcatch (f(x + %i*y)));
721 makelist (f(xy[1] + %i*xy[2]), xy, [[0, 0], [0, 1], [1, 1], [1, 0], [0, 2], [2, 2], [2, 0]]);
722 [1, 1/(%i+1), 1/(%i+2), 1/2, 1/(2*%i+1), 1/(2*%i+3), 1/3]$
724 (if draw_version = 'draw_version then load (draw),
726 proportional_axes=xy,
729 explicit('(f(x+%i*y)),x,-2,2,y,-2,2)),
733 /* nested if example -- note top-level "if" doesn't have an "else" clause, so the result is sometimes 'false' */
735 (g(a, b, c) := if a + b > c
746 (aa: [3,9/4,5/4,11/4,1,0,9/4,1/4,5/2,9/4,3,5/2],
747 bb: [3/2,3,1/4,3/4,5/2,7/4,5/2,3/4,1/2,3,13/4,7/2],
748 cc: [7/2,15/4,5/2,5/4,15/4,1,1/4,7/4,2,11/4,1/4,7/4],
749 map (g, aa, bb, cc));
750 [3/2,9/8,false,3/4,false,0,5,false,-4,9/8,13/2,31/4]$
752 (translate_or_lose (g),
753 map (g, aa, bb, cc));
754 [3/2,9/8,false,3/4,false,0,5,false,-4,9/8,13/2,31/4]$
756 errcatch (g(1, 1, z));
759 /* SF bug #3556: "5.43.0 translate / compile error"
760 * Ensure that "if" within lambda is translated correctly.
761 * The fix for #3412 tickled this bug.
765 f(x, m) := map (lambda ([u], if m > 0 then u^m), [x, 2*x, 3*x]),
769 is (?fboundp (f) # false);
773 [f(y, 2), f(y, -2)]);
774 [[y^2, 4*y^2, 9*y^2], [false, false, false]];
777 errcatch (f(10, n)));
778 /* ensure that conditional expressions get simplified to expunge $FALSE ... sigh. */
779 ''([[if n > 0 then 10^n, if n > 0 then 20^n, if n > 0 then 30^n]]);
781 (translate_or_lose (f),
782 is (?fboundp (f) # false)); /* test for generalized Boolean value */
786 [[y^2, 4*y^2, 9*y^2], [false, false, false]];
791 /* apply2 was translated incorrectly for several years. applyb2
792 * was translated incorrectly for decades.
795 (defrule (foorule, foo (), 1),
796 f () := apply2 ('(foo ()), foorule),
797 translate_or_lose (f),
801 (defrule (barrule, bar (), 2),
802 g () := applyb2 ('(bar ()), barrule),
803 translate_or_lose (g),
807 (kill (foorule, f, barrule, g), 0);
810 /* atan and atan2 calls with float arguments were translated
811 * incorrectly for over a decade. atan always caused a lisp error
812 * and atan2 had a range between 0 and 2*%pi that was inconsistent
813 * with the interpreted and non-float cases (where the range is
814 * between -%pi and %pi).
817 (foo () := [atan2 (-1.0, -1.0), atan2 (-1.0, 1.0)],
818 translate_or_lose (foo),
820 [-2.356194490192345, -0.7853981633974483];
822 (bar () := atan (-1.0),
823 translate_or_lose (bar),
827 (kill (foo, bar), 0);
830 /* The translation of a signum call with a float argument was
831 * inconsistent when compared to the interpreted case and other
832 * translated cases. signum should return an integer or a float
833 * when given an integer or a float argument, respectively.
836 (foo () := [signum (0), signum (0.0),
837 signum (2), signum (2.0),
838 signum (-3), signum (-3.0)],
839 translate_or_lose (foo),
841 [0, 0.0, 1, 1.0, -1, -1.0];
846 /* The translation of declare was broken for decades. It worked
847 * under Maclisp, but it had never worked under Common Lisp.
850 (foo () := declare (n, integer, [x, y], noninteger),
851 translate_or_lose (foo),
853 [?kindp (n, integer),
854 ?kindp (n, noninteger),
856 ?kindp (x, noninteger),
858 ?kindp (y, noninteger)]);
859 [true, false, false, true, false, true];
861 (kill (foo, n, x, y), 0);
864 /* If a variable was declared to be of mode rational, then a lisp
865 * error could occur during translation when attempting to convert
869 (foo (x) := (mode_declare (x, rational), float (x)),
870 bar (y) := (mode_declare (y, rational), 1.0 + y),
871 translate_or_lose (foo, bar),
872 [foo (1/4), bar (1/2)]);
875 (kill (foo, bar, x, y), 0);
878 /* The translation of an atan2 call with one float and one rational
879 * argument was broken because the rational was not converted to a
880 * float before calling ATAN.
888 bar (y, x) := (mode_declare (x, float, y, rational), atan2 (y, x)),
889 l1 : [foo (), bar (1/3, 0.0)],
890 translate_or_lose (foo, bar),
891 l2 : [foo (), bar (1/3, 0.0)],
895 (kill (foo, bar, x, y, l1, l2), 0);
898 /* When attempting to apply float contagion to the arguments, some
899 * translations of max and min with mixed float and rational arguments
900 * were broken because the rationals were not converted to floats before
901 * calling MAX or MIN (like atan2 above). Also, due to implementation-
902 * dependent behavior in the underlying lisp regarding what to return
903 * from MAX and MIN, the wrong mode could be used during translation and
904 * some of the translations were possibly inconsistent with interpreted
909 (mode_declare (x, rational),
912 max (1.0), min (1.0),
913 max (9/10), min (9/10),
915 max (0.0, 1), min (0.0, 1),
916 max (0, 1), min (0, 1),
917 max (1.0, 1), min (1.0, 1),
918 max (1, 1.0), min (1, 1.0),
919 max (2.0, 3.0), min (2.0, 3.0),
920 max (-1, 1/2), min (-1, 1/2),
921 max (3/4, 1/2), min (3/4, 1/2),
922 max (0.0, 1/2), min (0.0, 1/2),
923 max (0, x), min (0, x),
924 max (-1.0, x), min (-1.0, x),
925 max (5/6, x), min (5/6, x),
926 max (x, 1), min (x, 1)]),
928 translate_or_lose (foo),
933 (kill (foo, x, l1, l2), 0);
936 /* log and sqrt did not honor tr_float_can_branch_complex */
939 (mode_declare (x, float),
940 [log (-1.0), log (x),
941 sqrt (-1.0), sqrt (x)]),
942 /* l1 is a list of Maxima complex numbers */
944 some (lambda ([x], freeof (%i, x)), l1));
947 block ([tr_float_can_branch_complex : false],
948 translate_or_lose (foo),
949 /* l2 is a list of lisp complex numbers because we told the
950 * translator to assume the return values of log and sqrt
951 * would not be complex, and it correctly returned the complex
952 * numbers returned by LOG and SQRT directly.
955 [every (?complexp, l2),
956 every ("#", l1, l2)]);
960 block ([tr_float_can_branch_complex : true],
961 translate_or_lose (foo),
962 /* l3 is a list of Maxima complex numbers because we told the
963 * translator to assume the return values of log and sqrt
964 * could be complex, and it converted the lisp complex numbers
965 * returned by LOG and SQRT to Maxima complex numbers.
968 every (lambda ([x, y], cabs (x - y) < 1e-15), l1, l3));
971 (kill (foo, x, l1, l2, l3), 0);
974 /* The translations for evaluating = and # expressions to boolean
975 * values with one float argument and a different numerical argument
976 * (e.g. a fixnum) gave bogus results because the translator was
977 * incorrectly applying float contagion to the arguments.
980 (foo (s, w, x, y, z) :=
981 (mode_declare (w, number, x, fixnum, y, flonum),
982 [/* These translate to EQL comparisons */
983 is (1 = 1), is (1 # 1),
984 is (1 = 1.0), is (1 # 1.0),
985 is (1 = float (1)), is (1 # float (1)),
986 is (1.0 = float (1)), is (1.0 # float (1)),
987 is (w = 2), is (w # 2),
988 is (w = 2.0), is (w # 2.0),
989 is (x = 3), is (x # 3),
990 is (x = 3.0), is (x # 3.0),
991 is (x = float (3)), is (x # float (3)),
992 is (x = float (x)), is (x # float (x)),
993 is (y = 4), is (y # 4),
994 is (y = 4.0), is (y # 4.0),
995 is (y = float (4)), is (y # float (4)),
996 is (y = float (y)), is (y # float (y)),
997 /* These translate to LIKE comparisons */
998 is (z = 5), is (z # 5),
999 is (z = 5.0), is (z # 5.0),
1000 is (z = float (5)), is (z # float (5)),
1001 is (z = float (z)), is (z # float (z)),
1002 is (1/2 = 1/2), is (1/2 # 1/2),
1003 is (1/2 = rat (1/2)), is (1/2 # rat (1/2)),
1004 is (rat (1/2) = rat (1/2)), is (rat (1/2) # rat (1/2)),
1005 is (1/2 = 0.5), is (1/2 # 0.5),
1006 is (1/2 = float (1/2)), is (1/2 # float (1/2)),
1007 is (%i = %i), is (%i # %i),
1008 is (1 + %i = 1 + %i), is (1 + %i # 1 + %i),
1009 is (s = s), is (s # s),
1010 is (s = 'bar), is (s # 'bar),
1011 is (s = 1), is (s # 1),
1012 is (s = 1.0), is (s # 1.0),
1013 is (s = 1/2), is (s # 1/2),
1014 is ('f (0) = 'f (0)), is ('f (0) # 'f (0)),
1015 is ('g (s) = 'g (s)), is ('g (s) # 'g (s)),
1016 is ('h (w) = 'h (w)), is ('h (w) # 'h (w)),
1017 is ('i (x) = 'i (x)), is ('i (x) # 'i (x)),
1018 is ('j (y) = 'j (y)), is ('j (y) # 'j (y)),
1019 is ('k (z) = 'k (z)), is ('k (z) # 'k (z))]),
1020 l1 : foo ('bar, 2, 3, 4.0, 5),
1021 translate_or_lose (foo),
1022 l2 : foo ('bar, 2, 3, 4.0, 5),
1023 [every (lambda ([x], ?typep (x, ?boolean)), l2),
1028 (kill (foo, w, x, y, l1, l2), 0);
1031 /* Bug #3048: notequal is not translated properly
1033 * notequal expressions were only generically translated like user
1034 * function calls and the use of notequal in translated code caused
1035 * a runtime warning about it being totally undefined. Also the
1036 * evaluation of notequal expressions to boolean values (via is, if,
1037 * etc.) were translated like the evaluation of an unknown predicate.
1040 (assume (equal (a, b), notequal (c, d)),
1043 is (notequal (1, 1)),
1044 is (equal (1, 1.0)),
1045 is (notequal (1, 1.0)),
1046 is (equal (1, 1.0b0)),
1047 is (notequal (1, 1.0b0)),
1048 is (equal (1/2, 0.5)),
1049 is (notequal (1/2, 0.5)),
1050 is (equal (1/2, 0.5b0)),
1051 is (notequal (1/2, 0.5b0)),
1053 is (notequal (1, 2)),
1054 is (equal ('ind, 'ind)),
1055 is (notequal ('ind, 'ind)),
1056 is (equal ('und, 'und)),
1057 is (notequal ('und, 'und)),
1058 is (equal ('a, 'b)),
1059 is (notequal ('a, 'b)),
1060 is (equal ('c, 'd)),
1061 is (notequal ('c, 'd)),
1062 is (equal (x^2 - 1, (x + 1) * (x - 1))),
1063 is (notequal (x^2 - 1, (x + 1) * (x - 1)))],
1065 translate_or_lose (foo),
1067 [every (lambda ([x], ?typep (x, ?boolean)), l2),
1072 (kill (foo, l1, l2),
1073 forget (equal (a, b), notequal (c, d)),
1077 /* The translation of a call to random with a float argument could
1078 * cause the generation of bogus code because this always had the
1082 (foo (w, x, y, z) :=
1083 (mode_declare (w, fixnum, x, float),
1092 1 / (1 + random (x))],
1096 1 / (1 + random (w)),
1097 1 / (1 + random (y))]]),
1098 translate_or_lose (foo),
1099 l : foo (50, 5.0, 100, 10.0),
1100 [every (integerp, first (l)),
1101 every (floatnump, second (l)),
1102 every (ratnump, third (l))]);
1107 (kill (foo, w, x, l), 0);
1110 /* acosh, asech, atanh and acoth now have special translations for
1111 * float arguments. These all honor tr_float_can_branch_complex.
1115 (mode_declare (x, float),
1116 [acosh (x), asech (x), atanh (x)]),
1118 (mode_declare (x, float),
1120 /* l1 is a list of Maxima complex numbers */
1121 l1 : append (foo (-3.0), foo (-2.0), bar (-0.5), bar (0.5)),
1122 some (lambda ([x], freeof (%i, x)), l1));
1125 block ([tr_float_can_branch_complex : false],
1126 translate_or_lose (foo, bar),
1127 /* l2 is a list of lisp complex numbers because we told the
1128 * translator to assume the return values would not be complex,
1129 * and it correctly returned the lisp complex numbers directly.
1131 l2 : append (foo (-3.0), foo (-2.0), bar (-0.5), bar (0.5)),
1132 [every (?complexp, l2),
1133 every ("#", l1, l2),
1134 every ("=", l1, map (?complexify, l2))]);
1139 block ([tr_float_can_branch_complex : true],
1140 translate_or_lose (foo, bar),
1141 /* l3 is a list of Maxima complex numbers because we told the
1142 * translator to assume the return values could be complex, and
1143 * it converted the lisp complex numbers to Maxima complex numbers.
1145 l3 : append (foo (-3.0), foo (-2.0), bar (-0.5), bar (0.5)),
1146 every ("=", l1, l3));
1149 (kill (foo, bar, x, l1, l2, l3), 0);
1152 /* Bug #3642: Lisp error when translating assume
1154 * Translating an assume call with an atomic argument would cause a
1155 * lisp error during translation.
1159 block ([ctx : supcontext (),
1163 assume (x, y, equal (c, 0)),
1164 r : [is (a > 0), is (b < 0), is (equal (c, 0))],
1167 translate_or_lose (foo),
1169 [true, false, true];
1174 /* The translation of errcatch was broken because the mode of the
1175 * whole form was always assumed to be the same as the mode of the
1176 * last subform. Since errcatch always yields a list, lisp errors
1177 * could easily occur.
1181 block ([listarith : true],
1184 1.0 * errcatch (2.0),
1185 errcatch (error ("oops")),
1186 errcatch (?error ("oops")),
1188 translate_or_lose (foo),
1200 /* Attempting to translate multiple functions containing local would
1201 * cause an error. Similarly, translating the same function multiple
1202 * times would cause an error if that function contained local.
1205 (foo () := local (), /* just something with local (not within a block) */
1206 bar () := local (), /* something else with local (not within a block) */
1207 translate_or_lose (foo),
1208 translate_or_lose (bar),
1209 translate_or_lose (foo, bar));
1212 (kill (foo, bar), 0);
1215 /* Bug #2976: "local" doesn't work in translated code
1217 * For decades no attempt was being made to clean up any local
1221 /* The internal LOCLIST used by local should be empty right now */
1230 translate_or_lose (foo1),
1231 block ([v : foo1 ()],
1235 (kill (f0, foo1), 0);
1238 (arr1 [0] : "three",
1240 block ([g : lambda ([],
1246 arrayinfo (arr2)])],
1248 translate_or_lose (foo2),
1249 block ([v : foo2 ()],
1252 errcatch (arrayinfo (arr2))]));
1259 (kill (arr1, foo2), 0);
1269 translate_or_lose (foo3),
1276 /* The internal LOCLIST used by local should be empty right now */
1280 /* The fpprintprec itself is not important in this test. I'm
1281 * just picking something that has an ASSIGN property because
1282 * that's a separate internal case in the translator.
1284 * This test is ugly, but it's testing different cases and
1285 * their interactions.
1288 local (f1, f2, arr),
1292 bar (fpprintprec) :=
1301 [f1 (), f2 (), arr [1]]),
1302 [f1 (), f2 (), arr [1]]]),
1303 translate_or_lose (bar),
1305 [is (?get ('fpprintprec, '?assign) = false),
1307 [f1 (), f2 (), arr [1]]]);
1316 /* This is testing to make sure there are no bad interactions
1317 * between the usual local cleanup and errcatch cleanup (this
1318 * also mixes the interpreted and translated cases). This test
1321 * The original implementation of local properties (from decades
1322 * ago) not only failed to clean up local properties at all, but
1323 * it wasn't even setting up the internal state to keep up with
1324 * these properties correctly. An initial attempt at fixing bug
1325 * #2976 made this problem clear because with that it was easy to
1326 * cause an infinite loop during certain things like errcatch
1329 block ([translate : false,
1341 translate_or_lose (baz1, baz2),
1354 translate_or_lose (baz_test),
1367 /* The internal LOCLIST used by local should be empty right now */
1371 (kill (baz1, baz2, baz_test), 0);
1374 /***** This ends the bug #2976 tests *****/
1376 /* compile wasn't always compiling the correct function
1378 * This test not only depends on the internal details of how certain
1379 * functions are currently translated, but it also depends on internal
1380 * details about how DEFMFUN defines functions. This also doesn't
1381 * really test that the correct function gets compiled because the
1382 * lisp implementation could have just compiled it itself anyway. Ugh.
1386 compile_or_lose (foo),
1387 ?compiled\-function\-p (?symbol\-function (?get (foo, ?impl\-name))));
1393 /* Some internal function definitions and compiler macros were not
1394 * being cleaned up, and this could cause confusing and bogus results
1395 * when an outdated compiler macro was being used.
1397 * Specifically one problem we had involved translating a function,
1398 * redefining it and then translating the new definition. The internal
1399 * function and compiler macro from the original function could be used
1400 * when compiling calls to the new function if they were not overwritten.
1402 * This all depended on lisp implementation-dependent behavior because
1403 * implementations are not required to ever use compiler macros. Ugh.
1405 * This test also depends on internal details of how certain functions
1406 * are currently translated. Double ugh.
1410 translate_or_lose (foo),
1413 translate_or_lose (foo),
1415 test2 () := foo (1, 2, 3),
1416 compile_or_lose (test1, test2),
1417 /* Previously we observed test1 returning 0 and test2 causing a lisp
1418 * error because the compiler macro and old internal function from
1419 * the first foo were being used.
1421 [test1 (), test2 ()]);
1424 (kill (foo, test1, test2), 0);
1427 /* https://stackoverflow.com/questions/64631208/compilation-global-variables-with-warning
1429 * First verify that error_syms and niceindicespref assignments work as expected.
1435 errcatch (error_syms: 123);
1438 errcatch (error_syms: [aa, bb, 123]);
1441 error_syms: [aa, bb, cc];
1444 errcatch (niceindicespref: 123);
1447 errcatch (niceindicespref: []);
1450 niceindicespref: [aa, bb, cc];
1453 (reset (error_syms, niceindicespref), 0);
1456 /* now the example from the Stackoverflow question */
1459 "define_variable(foo, true, boolean)$
1462 exprp(that) := if foo = false and listp(that) and not emptyp(that) and member(that[1], [\"+\", \"*\"]) then(foo: true, true)$
1463 matchdeclare(exprm, exprp)$
1464 defrule(rule_1, exprm, subst(exprm[1], \"[\", exprm[2]))$
1466 calc(list) := block([steps: []],
1468 steps: endcons(list, steps),
1470 list: applyb1(list, rule_1)
1475 calc_result: calc([\"+\", [[\"*\", [1, 2, 3]], [\"+\", [3, 4, 6]]]]);",
1476 program_file_name: sconcat (maxima_tempdir, "/tmp_program.mac"),
1477 with_stdout (program_file_name, print (program_content)),
1484 ([file_name_original, file_name_LISP, file_name_UNLISP, file_name_compiled]: compile_file (program_file_name), 0);
1487 stringp (file_name_compiled);
1493 (load (file_name_compiled),
1495 [["+", [["*", [1, 2, 3]], ["+", [3, 4, 6]]]],
1496 ["+", [6, ["+", [3, 4, 6]]]],
1500 (kill (program_content, program_file_name, file_name_original, file_name_LISP, file_name_UNLISP, file_name_compiled, calc_result), 0);
1504 /* Some additional basic tests for functions with rest args */
1506 block ([translate : false],
1508 bar (a, b, [c]) := [a, b, c],
1517 bar (1, 2, 3, 4, 5)],
1519 /* l1: foo, bar and test are interpreted */
1522 /* l2: foo and bar are translated, and test is interpreted */
1523 translate_or_lose (foo, bar),
1526 /* l3: foo, bar and test are translated */
1527 translate_or_lose (test),
1542 [1, 2, [3, 4, 5]]]];
1544 (kill (foo, bar, test, l1, l2, l3), 0);
1547 /* Attempting to translate a macro with a rest arg always caused an
1548 * error during translation because the translator was constructing
1549 * bogus Maclisp-style lexpr lambda expressions.
1552 block ([translate : false],
1554 buildq ([r], ['r, r]),
1556 buildq ([a, b, c], ['a, a, 'b, b, 'c, c]),
1558 block ([x : 1, z : 3],
1564 bar (x, y, z, 4, 5, 6)]),
1565 /* test2 cannot be translated due to the WNA error during macro
1566 * expansion, but we can call and test it in the interpreter
1570 errcatch (bar (1))],
1572 /* l1: foo, bar and test1 are interpreted */
1575 /* l2: foo and bar are translated, and test1 is interpreted */
1576 translate_or_lose (foo, bar),
1579 /* l3: foo, bar and test1 are translated */
1580 translate_or_lose (test1),
1592 [['x, 'y, 'z], [1, 'y, 3]],
1593 ['x, 1, 'y, 'y, [], []],
1594 ['x, 1, 'y, 'y, ['z], [3]],
1595 ['x, 1, 'y, 'y, ['z, 4, 5, 6], [3, 4, 5, 6]]]];
1597 (kill (foo, bar, test1, test2, l1, l2, l3), 0);
1600 /* Some additional basic tests for conditionals.
1602 * We test both elseif and else-if ("else if").
1605 block ([translate : false],
1606 mysignum1 (x) := if x > 0 then 1 elseif x < 0 then -1 else 0,
1607 mysignum2 (x) := if x > 0 then 1 else if x < 0 then -1 else 0,
1611 if true then 1 else 2,
1612 if false then 1 else 2,
1614 if 1 < 2 then 'y else 'n,
1616 if 1 > 2 then 'n else 'y,
1617 if 1 > 2 then 'n elseif 1 = 2 then 'n else 'y,
1618 if 1 > 2 then 'n else if 1 = 2 then 'n else 'y,
1626 translate_or_lose (mysignum1, mysignum2, foo),
1631 [1, false, 1, 2, 'y, 'y, false, 'y, 'y, 'y, -1, -1, 0, 0, 1, 1]];
1633 (kill (mysignum1, mysignum2, foo, l1, l2), 0);
1636 /* Bogus translations of nested conditionals in elseif clauses
1638 * The translation of a conditional with another conditional nested
1639 * directly under an elseif clause was totally wrong. Using else-if
1640 * ("else if") instead of elseif would work fine.
1643 * We use the with_both_elseifs macro so we can test both elseif and
1644 * else-if without having to duplicate portions of the tests below.
1645 * Give this macro a conditional expression with elseifs and it will
1646 * expand into a list: the first element is the same expression given
1647 * to it (with elseifs), and the second element is that same expression
1648 * rewritten to use else-ifs instead of elseifs.
1651 (to_else_if (expr) :=
1652 if mapatom (expr) then
1655 block ([op : op (expr), args : args (expr)],
1656 if op = "if" and length (args) > 4 then
1657 funmake (op, map ('to_else_if, append (firstn (args, 2), [true, funmake (op, rest (args, 2))])))
1659 funmake (op, map ('to_else_if, args))),
1660 with_both_elseifs (expr) ::=
1661 buildq ([expr, texpr : to_else_if (expr)],
1666 block ([translate : false],
1681 /* l1: foo is interpreted */
1684 translate_or_lose (foo),
1686 /* l2: foo is translated
1688 * foo used to give lose3 instead of win in the elseif case.
1697 block ([translate : false],
1698 /* There is nothing special about bar here. This is just some
1699 * function that has several branches with nested conditionals.
1727 /* We test bar with the integers -2 to 9 */
1728 inputs : makelist (k, k, -2, 9),
1730 /* l1: bar is interpreted */
1731 l1 : map (bar, inputs),
1733 translate_or_lose (bar),
1735 /* l2: bar is translated
1737 * bar used to give incorrect results in the elseif case for every
1738 * number less than or equal to 2 (which means we got incorrect
1739 * results for the integers -2 to 2 in this test).
1741 l2 : map (bar, inputs),
1746 [['negative, 'negative],
1747 ['negative, 'negative],
1756 ['more_than_seven, 'more_than_seven],
1757 ['more_than_seven, 'more_than_seven]]];
1759 (kill (foo, bar, l1, l2, inputs, to_else_if, with_both_elseifs), 0);
1762 /* Bogus translations of conditionals with tests that translated to T
1763 * and consequents that translated to NIL.
1766 block ([translate : false],
1768 [if true then false else 1,
1769 if true then false elseif true then 1 else 2,
1770 if false then true elseif true then false else 1],
1772 /* l1: foo is interpreted */
1775 translate_or_lose (foo),
1777 /* l2: foo is translated
1779 * foo used to return [1, 1, 1]
1786 [false, false, false]];
1788 (kill (foo, l1, l2), 0);
1791 /* Bug #3704: Translator gives internal error
1793 * The hyper_to_summand function is from the bug report.
1796 (hyper_to_summand(e,k) := subst(hypergeometric = lambda([P,Q,x],
1797 P : xreduce("*", map(lambda([zz], pochhammer(zz,k)),P)),
1798 Q : xreduce("*", map(lambda([zz], pochhammer(zz,k)),Q)),
1800 l1 : hyper_to_summand (hypergeometric ([1/2], [2/5], x), 2),
1801 translate_or_lose (hyper_to_summand),
1802 l2 : hyper_to_summand (hypergeometric ([1/2], [2/5], x), 2),
1803 [is (l1 = l2), l2]);
1804 [true, 75 * x^2 / 112];
1806 (foo () := lambda ([], x!),
1807 translate_or_lose (foo),
1808 block ([x : 5], foo () ()));
1811 (kill (hyper_to_summand, foo, l1, l2), 0);
1814 /* go tags can be integers
1816 * This has been allowed, but it used to give a warning and an extra
1817 * trivial run through the translator to translate the integer go tags.
1818 * Now we allow integers directly without giving a warning.
1820 * We don't actually bother to check for warnings in the test below.
1821 * We're really just verifying that using an integer go tag works.
1824 block ([translate : false],
1825 foo () := block ([i : 0], tag, i : i + 1, if i < 5 then go (tag), i),
1826 bar () := block ([i : 0], 123, i : i + 1, if i < 5 then go (123), i),
1827 l1 : [foo (), bar ()],
1828 translate_or_lose (foo, bar),
1829 l2 : [foo (), bar ()],
1830 [is (l1 = l2), l2]);
1833 (kill (foo, bar, l1, l2), 0);
1836 /* A bug in MARRAYREF caused things like translated array references
1837 * to yield MQAPPLY expressions with an incorrect header.
1840 block ([translate : false],
1841 foo () := 'baz () [1],
1842 bar () := 'baz () [1, 2, 3],
1843 l1 : [foo (), bar ()],
1844 translate_or_lose (foo, bar),
1845 l2 : [foo (), bar ()],
1846 [is (l1 = l2), l2]);
1847 [true, ['baz () [1], 'baz () [1, 2, 3]]];
1849 (kill (foo, bar, l1, l2), 0);
1852 /* A bug in MARRAYREF caused bogus indexing into hash tables and fast
1853 * arrays. This affected things like translated array references.
1856 block ([translate : false,
1857 use_fast_arrays : true],
1859 foo () := block ([a],
1864 /* This would correctly yield 2 */
1867 translate_or_lose (foo),
1869 /* This used to incorrectly yield wtf */
1872 [is (l1 = l2), l2]);
1875 (kill (foo, l1, l2), 0);
1878 /* A bug in MARRAYREF caused things like translated array references
1879 * to yield expressions with an incorrect header.
1882 block ([translate : false],
1883 foo () := block ([a],
1885 array (a, complete, 5),
1888 /* This would correctly yield a[3] */
1891 translate_or_lose (foo),
1893 /* This would incorrectly yield a(3) */
1896 [is (l1 = l2), l2]);
1899 (kill (foo, l1, l2), 0);
1902 /* When translate_fast_arrays:true, a lisp error would occur at runtime
1903 * during an attempted MQAPPLY array assignment
1906 block ([translate : false],
1907 foo () := block ([a],
1909 a : make_array ('fixnum, 5),
1914 /* This would correctly yield 17 */
1917 block ([translate_fast_arrays : false],
1918 translate_or_lose (foo)),
1920 /* This would correctly yield 17 */
1923 block ([translate_fast_arrays : true],
1924 translate_or_lose (foo)),
1926 /* This would cause a lisp error */
1929 [is (l1 = l2), is (l2 = l3), l3]);
1932 (kill (foo, l1, l2, l3), 0);
1935 /* The string "**" no longer translates to the string "^".
1936 * This test compares the interpreted and translated results.
1939 block ([translate : false],
1940 foo () := ["^", "^" (2, 3), apply ("^", [2, 3]),
1941 "**", "**" (2, 3), apply ("**", [2, 3])],
1943 translate_or_lose (foo),
1945 [l2, is (l1 = l2)]);
1946 [["^", 8, 8, "**", 8, 8], true];
1948 (kill (foo, l1, l2), 0);
1951 /* Attempting to translate some atoms like lisp arrays would
1952 * cause lisp errors during translation.
1955 (a : make_array (fixnum, 1),
1958 translate_or_lose (foo),
1959 listarray (foo ()));
1965 /* Simple tests for catch and throw */
1967 block ([translate : false, l1, l2],
1968 local (foo, bar, baz),
1970 foo (p) := if p then throw (13) else 2,
1971 bar () := catch (1, foo (false), 3),
1972 baz () := catch (1, foo (true), 3),
1974 l1 : [bar (), baz ()],
1976 translate_or_lose (foo, bar, baz),
1978 l2 : [bar (), baz ()],
1980 [l2, is (l1 = l2)]);
1983 /* Translating a define_variable form with translate (but not
1984 * translate_file or compfile) used to invoke undefined behavior.
1985 * This would cause a lisp error during translation under some
1986 * (but not all) lisp implementations.
1989 block ([translate : false],
1991 foo () := (define_variable (x, 1, fixnum), x),
1992 translate_or_lose (foo),
1999 /* If local was used on a matchdeclared pattern variable, and this
2000 * was all translated with something besides translate_file (e.g.,
2001 * translate, compfile, etc.), then the MATCHDECLARE property would
2002 * not be on the pattern variable.
2005 block ([translate : false, l1, l2],
2008 foo () := (local (x), matchdeclare (x, true), let (a * x, x), letsimp (a * q)),
2010 /* This would yield q */
2013 translate_or_lose (foo),
2015 /* This used to yield a*q */
2018 [l2, is (l1 = l2)]);
2024 /* Rest args are now allowed in lambda expressions in MQAPPLY
2028 block ([translate : false, l1, l2],
2029 local (foo, bar, baz),
2031 /* foo used to fail to translate due to the rest arg */
2033 block ([x : 1, z : 3],
2034 lambda ([[x]], x) (x, x + 1, z)),
2036 block ([x : 2, z : 4],
2037 apply (lambda ([[x]], x), [x, x + 1, z])),
2039 block ([x : 3, z : 5],
2040 block ([f : lambda ([[x]], x)],
2043 l1 : [foo (), bar (), baz ()],
2045 translate_or_lose (foo, bar, baz),
2047 l2 : [foo (), bar (), baz ()],
2049 [l1, is (l1 = l2)]);
2050 [[[1, 2, 3],[2, 3, 4],[3, 4, 5]], true];
2052 /* Validation has been improved for lambda expressions in MQAPPLY
2056 block ([translate : false],
2059 /* These should both fail to translate */
2060 foo () := lambda ([]) (),
2061 bar () := lambda ([x, x], x) (1, 2),
2063 translate (foo, bar));
2066 /* The translation of array functions was broken for decades */
2068 block ([translate : false, l1, l2],
2072 bar[n] := if n = 1 then 1 else n * bar[n - 1],
2074 l1 : [foo[0], foo[5], bar[5], bar[10]],
2076 translate_or_lose (foo, bar),
2078 l2 : [foo[0], foo[5], bar[5], bar[10]],
2080 [l1, is (l1 = l2)]);
2081 [[0, 5, 120, 3628800], true];
2083 (kill (foo, bar), 0);
2086 /* The translation of upward funargs (including those created by
2087 * subscripted functions) easily lead to lisp errors.
2090 /* Tests involving returned lambdas without free vars that were
2091 * bound during definition
2096 local (foo, bar, test),
2098 foo () := lambda ([x], 2 * x + q),
2099 bar () := lambda ([x, [y]], x * y + q),
2104 [f (3), f (5), b (2, 3, 4), b (5, 6, 7)]),
2108 translate_or_lose (foo, bar),
2112 [l2, is (l1 = l2)]);
2113 [['q + 6, 'q + 10, ['q + 6, 'q + 8], ['q + 30, 'q + 35]],
2116 (kill (foo, bar), 0);
2119 /* Tests involving returned lambdas with free vars that were
2120 * bound during definition. These do not cause the capture of
2127 local (foo, bar, baz, test),
2129 foo (x) := lambda ([y], x + y + q),
2130 bar (x) := lambda ([y, [z]], q + x + y * z),
2131 baz (v) := lambda ([], v),
2134 block ([f : foo (3),
2137 [f (5), b (2, 3, 4), c ()]),
2141 translate_or_lose (foo, bar, baz),
2145 [l2, is (l1 = l2)]);
2147 ['q + 'ux + 6, 'q + 'ux + 8],
2151 (kill (foo, bar, baz), 0);
2154 /* Tests involving subscripted functions. These do cause the capture
2160 local (foo, bar, baz, def, test),
2163 foo[x, y](a, b) := [x, y, a, b, q],
2164 bar[x, y](a, [b]) := [x, y, a, b, q],
2168 block ([f : foo[1, 2],
2171 [f (6, 7), b (8, 9, 10), c ()]),
2177 /* just kill and redefine */
2179 kill (foo, bar, baz),
2183 translate_or_lose (foo, bar, baz),
2187 [l2, is (l1 = l2)]);
2189 [3, 4, 8, [9, 10], 'q],
2193 (kill (foo, bar, baz), 0);
2196 /* More tests involving multiple nested lambdas */
2198 x : 'ux, y : 'uy, z : 'uz,
2200 local (foo, bar, baz, quux, def, test),
2203 /* nothing should be captured */
2204 foo (x) := lambda ([y], lambda ([z], [x, y, z])),
2205 /* x should be captured and used */
2206 bar[x](y) := lambda ([z], [x, y, z]),
2207 /* x should be captured and used */
2208 baz[x](y) := lambda ([z], lambda ([], [x, y, z])),
2209 /* nothing should be captured since x is bound by the inner lambda */
2210 quux[x](y) := lambda ([x], [x, y])),
2213 block ([a : foo (1),
2217 [a (10) (11), b (12) (13), c (14) (15) (), d (16) (17)]),
2223 /* just kill and redefine */
2225 kill (foo, bar, baz, quux),
2229 translate_or_lose (foo, bar, baz, quux),
2233 [l2, is (l1 = l2)]);
2240 (kill (foo, bar, baz, quux), 0);
2247 /*** KILLING THESE FUNCTIONS SHOULD BE THE LAST TEST IN THIS FILE ***/
2248 (kill (translate_or_lose, compile_or_lose), 0);
2250 /*** THERE SHOULD BE NO TESTS BEYOND THIS POINT ***/