1 // eval3.cpp Copyright (C) 1991-2021 Codemist
2
3 //
4 // Interpreter (part 3).
5 // Implementations of special forms (interpreted versions).
6 //
7 //
8
9 /**************************************************************************
10 * Copyright (C) 2021, Codemist. A C Norman *
11 * *
12 * Redistribution and use in source and binary forms, with or without *
13 * modification, are permitted provided that the following conditions are *
14 * met: *
15 * *
16 * * Redistributions of source code must retain the relevant *
17 * copyright notice, this list of conditions and the following *
18 * disclaimer. *
19 * * Redistributions in binary form must reproduce the above *
20 * copyright notice, this list of conditions and the following *
21 * disclaimer in the documentation and/or other materials provided *
22 * with the distribution. *
23 * *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
28 * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
31 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF *
34 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
35 * DAMAGE. *
36 *************************************************************************/
37
38 // $Id: eval3.cpp 5609 2021-01-23 22:02:30Z arthurcnorman $
39
40
41 #include "headers.h"
42
43
44 #define BODY_LET 0
45 #define BODY_COMPILER_LET 1
46 #define BODY_PROG 2
47
macrolet_fn(LispObject args,LispObject env)48 static LispObject macrolet_fn(LispObject args, LispObject env)
49 { LispObject d;
50 STACK_SANITY;
51 if (!consp(args)) return onevalue(nil);
52 stackcheck(args, env);
53 d = car(args); // The bunch of definitions
54 while (consp(d))
55 { LispObject w = car(d); // w = (name bvl ...)
56 if (consp(w) && consp(cdr(w)))
57 {
58 // Here I need to call (expand-definer <form> nil) to map
59 // macro specifications with all the possible magic options into ones
60 // which just take 2 args, a form and an environment.
61 Save save(args, env);
62 w = cons(expand_def_symbol, w);
63 errexit();
64 w = Lfuncall_3(nil, expand_def_symbol, w, nil);
65 errexit();
66 // I expect expand-definer to return either
67 // (~~defmacro name bvl ...)
68 // OR (progn XXX (~~defmacro name bvl ...))
69 // where XXX is exactly one form.
70 if (car(w) == progn_symbol)
71 w = car(cdr(cdr(w)));
72 w = cdr(w);
73 w = cons(cdr(w), car(w));
74 errexit();
75 save.restore(args, env);
76 env = cons(w, env);
77 errexit();
78 }
79 d = cdr(d);
80 }
81 return let_fn_1(nil, cdr(args), env, BODY_LET);
82 }
83
mv_prog1_fn(LispObject args,LispObject env)84 static LispObject mv_prog1_fn(LispObject args, LispObject env)
85 { LispObject r, rl;
86 STACK_SANITY;
87 int nargs, i;
88 if (!consp(args)) return onevalue(nil);
89 stackcheck(args, env);
90 { Save save(args, env);
91 r = eval(car(args), env);
92 errexit();
93 save.restore(args, env);
94 }
95 rl = nil;
96 nargs = exit_count;
97 { Save save(r);
98 // I could use the Lisp stack to save things here, but I hope that this
99 // function is not used much and performance will not matter.
100 for (i=nargs; i>=2; i--)
101 rl = cons_no_gc((&mv_2)[i-2], rl);
102 rl = cons_gc_test(rl);
103 errexit();
104 { Save save1(rl);
105 while (is_cons(args = cdr(args)) && args!=nil)
106 { Save save2(args, env);
107 eval(car(args), env);
108 errexit();
109 save2.restore(args, env);
110 }
111 save1.restore(rl);
112 }
113 for (i = 2; i<=nargs; i++)
114 { (&mv_2)[i-2] = car(rl);
115 rl = cdr(rl);
116 }
117 save.restore(r);
118 }
119 return nvalues(r, nargs);
120 }
121
or_fn(LispObject args,LispObject env)122 static LispObject or_fn(LispObject args, LispObject env)
123 // also needs to be a macro for Common Lisp
124 { if (!consp(args)) return onevalue(nil);
125 stackcheck(args, env);
126 STACK_SANITY;
127 for (;;)
128 { LispObject v = car(args);
129 args = cdr(args);
130 if (!consp(args)) return eval(v, env);
131 Save save(args, env);
132 v = eval(v, env);
133 errexit();
134 save.restore(args, env);
135 if (v != nil) return onevalue(v);
136 }
137 }
138
139 // Note that (BLOCK ...) also catches LispReturnFrom exceptions, and that
140 // Common Lisp really thinks of PROG as a combination of LET, BLOCK and
141 // TAGBODY.
142
prog_fn(LispObject iargs,LispObject ienv)143 static LispObject prog_fn(LispObject iargs, LispObject ienv)
144 { if (!consp(iargs) || !consp(cdr(iargs))) return onevalue(nil);
145 stackcheck(iargs, ienv);
146 RealSave save(nil, iargs, ienv);
147 LispObject &my_tag = save.val(1);
148 LispObject &args = save.val(2);
149 LispObject &env = save.val(3);
150 // I need to augment the (lexical) environment with a null block
151 // tag so that (return ..) will work as required. See block_fn for
152 // further elaboration since (block ..) is the main way of introducing
153 // new block tags.
154 my_tag = cons(fixnum_of_int(0), nil);
155 errexit();
156 env = cons(my_tag, env);
157 errexit();
158 TRY
159 let_fn_1(car(args), cdr(args), env, BODY_PROG);
160 CATCH(LispReturnFrom)
161 setcar(my_tag, fixnum_of_int(2)); // Invalidate
162 if (exit_tag == my_tag)
163 return exit_value; // exit_count already OK here
164 // It could be that the RETURN(-FROM) is heading to be handled by some
165 // enclosing block.
166 else RETHROW;
167 ANOTHER_CATCH(LispError)
168 int _reason = exit_reason;
169 if (SHOW_FNAME)
170 { err_printf("\nEvaluating: "); // A bit of backtrace on errors
171 errexit();
172 loop_print_error(args);
173 errexit();
174 }
175 exit_reason = _reason;
176 RETHROW;
177 END_CATCH;
178 return onevalue(nil);
179 }
180
progn_fn(LispObject args,LispObject env)181 LispObject progn_fn(LispObject args, LispObject env)
182 { LispObject f;
183 STACK_SANITY;
184 if (!consp(args)) return onevalue(nil);
185 stackcheck(args, env);
186 f = nil;
187 for (;;)
188 { f = car(args);
189 args = cdr(args);
190 if (!consp(args)) break;
191 Save save(args, env, f);
192 on_backtrace(
193 static_cast<void>(eval(f, env)),
194 errexit();
195 // Action for backtrace here...
196 save.restore(args, env, f);
197 if (SHOW_FNAME)
198 { err_printf("\nEvaluating: ");
199 loop_print_error(f);
200 });
201 save.restore(args, env, f);
202 }
203 errexit();
204 return eval(f, env); // tail call on last item in the progn
205 }
206
prog1_fn(LispObject args,LispObject env)207 static LispObject prog1_fn(LispObject args, LispObject env)
208 // prog1 and prog2 will be implemented as macros for Common Lisp,
209 // and are here implemented as special forms too in the expectation
210 // that that will be good for performance.
211 { LispObject f;
212 STACK_SANITY;
213 if (!consp(args)) return onevalue(nil); // (prog1) -> nil
214 stackcheck(args, env);
215 errexit();
216 { Save save(args, env);
217 f = car(args);
218 f = eval(f, env); // first arg
219 errexit();
220 save.restore(args, env);
221 }
222 Save save(f);
223 for (;;)
224 { args = cdr(args);
225 if (!consp(args)) break;
226 Save save1(args, env);
227 static_cast<void>(eval(car(args), env));
228 errexit();
229 save1.restore(args, env);
230 }
231 save.restore(f);
232 return onevalue(f); // always hands back just 1 value
233 }
234
prog2_fn(LispObject args,LispObject env)235 static LispObject prog2_fn(LispObject args, LispObject env)
236 { LispObject f;
237 STACK_SANITY;
238 if (!consp(args)) return onevalue(nil); // (prog2) -> nil
239 stackcheck(args, env);
240 errexit();
241 { Save save(args, env);
242 static_cast<void>(eval(car(args), env)); // eval & discard first arg
243 errexit();
244 save.restore(args, env);
245 }
246 errexit();
247 args = cdr(args);
248 if (!consp(args)) return onevalue(nil); // (prog2 x) -> nil
249 { Save save(args, env);
250 f = eval(car(args), env); // second arg
251 errexit();
252 save.restore(args, env);
253 }
254 Save save(f);
255 for (;;)
256 { args = cdr(args);
257 if (!consp(args)) break;
258 { Save save1(args, env);
259 static_cast<void>(eval(car(args), env));
260 errexit();
261 save1.restore(args, env);
262 }
263 }
264 save.restore(f);
265 return onevalue(f); // always hands back just 1 value
266 }
267
268 #define specenv save.val(5)
269 #define vals save.val(4)
270 #define syms save.val(3)
271 #define env save.val(2)
272 #define args save.val(1)
273
274 class Unbind_progv_specials
275 { LispObject *saveStack;
276 LispObject *specenv_p;
277 public:
Unbind_progv_specials(LispObject * ss)278 Unbind_progv_specials(LispObject *ss)
279 { saveStack = stack;
280 specenv_p = ss;
281 }
~Unbind_progv_specials()282 ~Unbind_progv_specials()
283 { stack = saveStack;
284 while (*specenv_p != nil)
285 { LispObject p = car(*specenv_p);
286 setvalue(car(p), cdr(p));
287 *specenv_p = cdr(*specenv_p);
288 }
289 }
290 };
291
progv_fn(LispObject args_x,LispObject env_x)292 static LispObject progv_fn(LispObject args_x, LispObject env_x)
293 { LispObject syms_x, vals_x, specenv_x, w;
294 STACK_SANITY;
295 if (!consp(args_x)) return onevalue(nil);
296 stackcheck(args_x, env_x);
297 errexit();
298 syms_x = vals_x = specenv_x = nil;
299 syms_x = car(args_x);
300 args_x = cdr(args_x);
301 RealSave save(args_x, env_x, syms_x, vals_x, specenv_x);
302 syms = eval(syms, env);
303 errexit();
304 if (!consp(args)) return nil;
305 w = car(args);
306 args = cdr(args);
307 vals = eval(w, env);
308 errexit();
309 if (!consp(args)) return nil;
310 while (consp(syms))
311 { LispObject v = car(syms);
312 LispObject w1;
313 if (consp(vals))
314 { w = car(vals);
315 vals = cdr(vals);
316 }
317 else w = unset_var;
318 syms = cdr(syms);
319 if (!is_symbol(v) || v==nil || v==lisp_true) continue;
320 w1 = cons(v, qvalue(v));
321 errexit();
322 // If I were to take the error exit here then some variables would have
323 // been set to their new values and some not. That would be a mess! So if the
324 // above CONS fails and triggers an exit things are bad. I may need to
325 // pre-allocate the space, but because PROGV is esoteric (and not used by
326 // Reduce) I am not going to go to the trouble YET.
327 setvalue(v, w);
328 specenv = cons(w1, specenv);
329 }
330 { Unbind_progv_specials unbind_progv_variables(&specenv);
331 args = progn_fn(args, env);
332 }
333 return specenv;
334 }
335
336 #undef specenv
337 #undef vals
338 #undef syms
339 #undef env
340 #undef args
341
quote_fn(LispObject args,LispObject)342 LispObject quote_fn(LispObject args, LispObject)
343 { if (consp(args) && cdr(args) == nil) return onevalue(car(args));
344 return aerror("quote");
345 }
346
return_fn(LispObject args,LispObject env)347 static LispObject return_fn(LispObject args, LispObject env)
348 {
349 // First check that the block name (nil in this case) is lexically available
350 STACK_SANITY;
351 LispObject p;
352 stackcheck(args, env);
353 errexit();
354 for(p=env; consp(p); p=cdr(p))
355 { LispObject w = car(p);
356 if (!consp(w)) continue;
357 if (car(w) == fixnum_of_int(0) && cdr(w) == nil)
358 { p = w;
359 goto tag_found;
360 }
361 }
362 return error(1, err_block_tag, nil);
363 tag_found:
364 if (consp(args))
365 { Save save(p);
366 env = eval(car(args), env);
367 save.restore(p);
368 errexit();
369 exit_value = env;
370 }
371 else
372 { exit_value = nil;
373 exit_count = 1;
374 }
375 exit_tag = p;
376 exit_reason = UNWIND_RETURN;
377 THROW(LispReturnFrom);
378 }
379
return_from_fn(LispObject args,LispObject env)380 static LispObject return_from_fn(LispObject args, LispObject env)
381 { LispObject p, tag;
382 stackcheck(args, env);
383 errexit();
384 STACK_SANITY;
385 if (!consp(args)) tag = nil;
386 else
387 { tag = car(args);
388 args = cdr(args);
389 }
390 for(p=env; consp(p); p=cdr(p))
391 { LispObject w = car(p);
392 if (!consp(w)) continue;
393 if (car(w) == fixnum_of_int(0) && cdr(w) == tag)
394 { p = w;
395 goto tag_found;
396 }
397 }
398 return error(1, err_block_tag, tag);
399 tag_found:
400 if (consp(args))
401 { Save save(p);
402 env = eval(car(args), env);
403 save.restore(p);
404 errexit();
405 exit_value = env;
406 }
407 else
408 { exit_value = nil;
409 exit_count = 1;
410 }
411 exit_tag = p;
412 exit_reason = UNWIND_RETURN;
413 THROW(LispReturnFrom);
414 }
415
setq_fn(LispObject args,LispObject env)416 static LispObject setq_fn(LispObject args, LispObject env)
417 { LispObject var, val = nil;
418 STACK_SANITY;
419 stackcheck(args, env);
420 errexit();
421 while (consp(args))
422 { var = car(args);
423 if (!is_symbol(var) || var == nil || var == lisp_true ||
424 (qheader(var) & SYM_KEYWORD_VAR) == SYM_KEYWORD_VAR)
425 { return aerror1("setq (bad variable)", var);
426 }
427 args = cdr(args);
428 if (consp(args))
429 { { Save save(args, env, var);
430 val = car(args);
431 val = eval(val, env);
432 errexit();
433 save.restore(args, env, var);
434 }
435 errexit();
436 args = cdr(args);
437 }
438 else val = nil;
439 if ((qheader(current_function) & SYM_TRACESET) != 0)
440 { RealSave save(args, env, var, val);
441 // LispObject &args1 = save.val(1);
442 // LispObject &env1 = save.val(2);
443 LispObject &var1 = save.val(3);
444 LispObject &val1 = save.val(4);
445 freshline_trace();
446 errexit();
447 // I want loop_print_trace to avoid exiting with errors!
448 loop_print_trace(current_function);
449 errexit();
450 trace_printf(": ");
451 errexit();
452 loop_print_trace(var1);
453 errexit();
454 trace_printf(" := ");
455 errexit();
456 loop_print_trace(val1);
457 errexit();
458 trace_printf("\n");
459 errexit();
460 save.restore(args, env, var, val);
461 }
462 if ((qheader(var) & SYM_KEYWORD_VAR) == SYM_SPECIAL_VAR ||
463 (qheader(var) & SYM_KEYWORD_VAR) == SYM_GLOBAL_VAR)
464 setvalue(var, val);
465 else
466 { LispObject p = env, w; // Here it seems to be a local variable,
467 // or it could be locally FLUID.
468 for (;;)
469 { if (!consp(p))
470 { setheader(var, qheader(var) | SYM_SPECIAL_VAR);
471 #ifdef SOME_TIME_LATER
472 // If I display this message - which could be viewed as a proper error report -
473 // it leds to multiple failures in the Reduce regressions where scripting
474 // assumes that assignment to a variable is valid without any declaration.
475 Save save(args, env, var);
476 debug_printf("\n+++++ ");
477 errexit();
478 loop_print_debug(var);
479 errexit();
480 debug_printf(" proclaimed SPECIAL by SETQ\n");
481 errexit();
482 save.restore(args, env, var);
483 #endif
484 setvalue(var, val);
485 break;
486 }
487 w = car(p);
488 if (car(w) == var)
489 { if (cdr(w) == work_symbol) setvalue(var, val);
490 else write_barrier(cdraddr(w), val);
491 break;
492 }
493 p = cdr(p);
494 }
495 }
496 }
497 return onevalue(val);
498 }
499
500 // tagbody does the bit of PROG that covers labels.
501
tagbody_fn(LispObject args1,LispObject env1)502 LispObject tagbody_fn(LispObject args1, LispObject env1)
503 {
504 // Bind the labels that occur in this block. Note that I invalidate
505 // these bindings if I ever exit from this block, so that nobody
506 // even thinks that they can use (go xx) to get back in.
507 stackcheck(args1, env1);
508 errexit();
509 STACK_SANITY;
510 RealSave save(args1, env1, nil, env1);
511 LispObject &args = save.val(1);
512 LispObject &env = save.val(2);
513 LispObject &p = save.val(3);
514 LispObject &oldenv = save.val(4);
515 for (p=args; consp(p); p=cdr(p))
516 { LispObject w = car(p);
517 if (!consp(w))
518 { w = cons(fixnum_of_int(1), p);
519 env = cons(w, env);
520 errexit();
521 }
522 }
523 // That has put my new version of env with bindings of the form
524 // (1 . <location in the tagbody>)
525 // for each label present. In other words it goes
526 // env = ( ... (1 labelname <continuation>) ... )
527 // where bindings for variable in the environment would have a symbol
528 // so the integer 1 here distinguishes these as label bindings.
529 // (go xx) sets exit_tag to xx, which is then noticed next time tagbody
530 // is about to do anything.
531 for (p=args; consp(p); p = cdr(p))
532 {
533 // Within this block args will store the particular statement being
534 // processed.
535 args = car(p);
536 if (!is_cons(args)) continue; // Do not evaluate labels
537 TRY
538 eval(args, env);
539 CATCH(LispGo)
540 int _reason = exit_reason;
541 // I need to do this search. Well in the code that implemented GO I checked
542 // that the destination label was bound as a label. That was so that I could
543 // give a decent diagnostic if it was not. The scan here is to see if it is
544 // a label in THIS level of a tagbody... and if not I will hand it upwards.
545 for (p=env; p!=oldenv; p=cdr(p)) // scan label bindings
546 { LispObject w = car(p);
547 if (w != exit_tag) continue;
548 // Now I have found the label I needed to jump to. Hoorah.
549 p = cdr(w);
550 break;
551 }
552 // At the end of the loop either p==oldenv in which case I had not found
553 // the desired label as one associated with this particular tagbody, or
554 // it is the new place within the tagbody for me to resume execution.
555 if (p != oldenv) continue; // take the GOTO
556 // If I drop out of the loop that means that the target label was
557 // not present in this block. Tidy up the label bindings to be very
558 // certain nobody can re-use them. The risk here would be if within a
559 // tagbody somebody has saved the closure of (eg) a lambda-expression that
560 // contained a relevent GO. This is not something that could every happen
561 // in Standard Lisp, but it could in Common Lisp!
562 while (env != oldenv)
563 { setcar(car(env), fixnum_of_int(2));
564 env = cdr(env);
565 }
566 // Because this is a sort of error I will display a message. Well with
567 // Common Lisp one would be entitled to perform a GO that exited through
568 // multiple levels, and a message here would not be appropriate
569 #ifndef COMMON
570 if (SHOW_FNAME)
571 { err_printf("\nEvaluating: ");
572 loop_print_error(args);
573 }
574 #endif // COMMON
575 // Re-throw the LispGo exception to try again at some outer level.
576 exit_reason = _reason;
577 RETHROW;
578 ANOTHER_CATCH(LispError)
579 int _reason = exit_reason;
580 if (SHOW_FNAME)
581 { err_printf("\nEvaluating: ");
582 loop_print_error(args);
583 }
584 // Re-throw some other exception that counted as an error.
585 exit_reason = _reason;
586 RETHROW;
587 END_CATCH;
588 }
589 // This is where I drop off the end of the tagbody, so I tidy up and
590 // return nil.
591 while (env != oldenv)
592 { setcar(car(env), fixnum_of_int(2));
593 env = cdr(env);
594 }
595 return onevalue(nil);
596 }
597
the_fn(LispObject args,LispObject env)598 static LispObject the_fn(LispObject args, LispObject env)
599 // in effect an identity function for the present
600 { if (!consp(args)) return onevalue(nil);
601 args = cdr(args);
602 if (!consp(args)) return onevalue(nil);
603 args = car(args);
604 return eval(args, env);
605 }
606
607 // In Common Lisp mode THROW has to be a special form because of the
608 // case where the information being thrown involves multiple values. For
609 // Standard Lisp I can perfectly well provide a simple function.
610
throw_fn(LispObject args,LispObject env)611 static LispObject throw_fn(LispObject args, LispObject env)
612 { LispObject tag, p;
613 STACK_SANITY;
614 if (!consp(args)) return aerror("throw");
615 stackcheck(args, env);
616 errexit();
617 tag = car(args);
618 args = cdr(args);
619 { Save save(args, env);
620 tag = eval(tag, env);
621 errexit();
622 save.restore(args, env);
623 }
624 for (p = catch_tags; p!=nil; p=cdr(p))
625 if (tag == car(p)) goto tag_found;
626 return aerror("throw: tag not found");
627 tag_found:
628 if (consp(args))
629 { Save save(p);
630 tag = car(args);
631 tag = eval(tag, env);
632 errexit();
633 save.restore(p);
634 exit_value = tag;
635 }
636 else
637 { exit_value = nil;
638 exit_count = 1;
639 }
640 exit_tag = p;
641 exit_reason = UNWIND_THROW;
642 THROW(LispThrow);
643 }
644
Lthrow_one_value(LispObject env,LispObject tag,LispObject val)645 LispObject Lthrow_one_value(LispObject env, LispObject tag, LispObject val)
646 { LispObject p;
647 STACK_SANITY;
648 for (p = catch_tags; p!=nil; p=cdr(p))
649 if (tag == car(p)) goto tag_found;
650 return aerror("throw: tag not found");
651 tag_found:
652 exit_value = val;
653 exit_count = 1;
654 exit_tag = p;
655 exit_reason = UNWIND_THROW;
656 THROW(LispThrow);
657 }
658
Lthrow_nil(LispObject env,LispObject tag)659 LispObject Lthrow_nil(LispObject env, LispObject tag)
660 { return Lthrow_one_value(nil, tag, nil);
661 }
662
unless_fn(LispObject args,LispObject env)663 static LispObject unless_fn(LispObject args, LispObject env)
664 { LispObject w;
665 STACK_SANITY;
666 if (!consp(args)) return onevalue(nil);
667 stackcheck(args, env);
668 errexit();
669 { Save save(args, env);
670 w = eval(car(args), env);
671 errexit();
672 save.restore(args, env);
673 }
674 if (w != nil) return onevalue(nil);
675 else return progn_fn(cdr(args), env);
676 }
677
unwind_protect_fn(LispObject args1,LispObject env1)678 static LispObject unwind_protect_fn(LispObject args1, LispObject env1)
679 { STACK_SANITY;
680 if (!consp(args1)) return onevalue(nil);
681 stackcheck(args1, env1);
682 RealSave save(args1, env1);
683 LispObject &args = save.val(1);
684 LispObject &env = save.val(2);
685 LispObject r;
686 TRY
687 r = eval(car(args), env);
688 CATCH(LispException)
689 LispObject xt, xv;
690 int xc, xr;
691 // Here I am in the process of exiting because of a throw, return-from,
692 // go or error. I need to save all the internal stuff that tells me
693 // what is going on so I can restore it after the clean-up forms have been
694 // processed. The values involved are:
695 // (a) exit_tag marks use of go, return-from or throw
696 // (b) exit_value first result value (throw, return-from)
697 // (c) exit_count number of values (throw, return-from)
698 // (d) mv2,... as indicated by exit_count
699 // (e) exit_reason what it says.
700 LispObject savetraptime = qvalue(trap_time);
701 setvalue(trap_time, nil); // No timeouts in recovery code
702 xv = exit_value;
703 xt = exit_tag;
704 xc = exit_count;
705 xr = exit_reason;
706 Save save1(savetraptime, xv, xt);
707 LispObject rl = nil;
708 for (int i=xc; i>=2; i--)
709 { rl = cons((&mv_2)[i-2], rl);
710 errexit();
711 }
712 // I am going to take the view that if there is a failure during execution
713 // of the cleanup forms then full cleanup will not be complete, and this
714 // can include the case of "cons" failing right before anything else.
715 Save save2(rl);
716 // Now I will obey the cleanup
717 while (is_cons(args = cdr(args)) && args!=nil)
718 { eval(car(args), env);
719 errexit();
720 }
721 save2.restore(rl);
722 save1.restore(savetraptime, xv, xt);
723 for (int i = 2; i<=xc; i++)
724 { (&mv_2)[i-2] = car(rl);
725 rl = cdr(rl);
726 }
727 exit_value = xv;
728 exit_tag = xt;
729 exit_count = xc;
730 exit_reason = xr;
731 setvalue(trap_time, savetraptime);
732 RETHROW; // reinstate the exception
733 END_CATCH;
734 // Now code (just like multiple-value-prog1) that evaluates the
735 // cleanup forms in the case that the protected form exits normally.
736 int nargs = exit_count;
737 LispObject rl = nil;
738 for (int i=nargs; i>=2; i--)
739 { rl = cons((&mv_2)[i-2], rl);
740 errexit();
741 }
742 Save save3(rl);
743 while (is_cons(args = cdr(args)) && args!=nil)
744 { eval(car(args), env);
745 errexit();
746 }
747 save3.restore(rl);
748 for (int i=2; i<=nargs; i++)
749 { (&mv_2)[i-2] = car(rl);
750 rl = cdr(rl);
751 }
752 return nvalues(r, nargs);
753 }
754
755 // Errorset is not defined as part of COMMON Lisp but I want it in
756 // any Lisp system that I use notwithstanding that.
757
758 const volatile char *errorset_msg;
759
unwind_stack(LispObject * entry_stack,bool findcatch)760 void unwind_stack(LispObject *entry_stack, bool findcatch)
761 { LispObject *sp = stack;
762 while (sp != entry_stack)
763 { LispObject bv, w;
764 size_t n;
765 w = *sp--;
766 if (findcatch && w == SPID_CATCH) break;
767 if (w == static_cast<LispObject>(SPID_FBIND))
768 {
769 // Here I have found some fluid binding that need to be unwound. The code
770 // here is similar to that for FREERSTR.
771 bv = *sp--;
772 n = length_of_header(vechdr(bv));
773 while (n>CELL)
774 { LispObject v = *reinterpret_cast<LispObject *>(
775 (intptr_t)bv + n - (CELL + TAG_VECTOR));
776 n -= CELL;
777 setvalue(v, *sp--);
778 }
779 }
780 else if (w == static_cast<LispObject>(SPID_PVBIND))
781 { bv = *sp--;
782 while (bv != nil)
783 { LispObject w = car(bv);
784 setvalue(car(w), cdr(w));
785 bv = cdr(bv);
786 }
787 }
788 }
789 // If "findcatch" is true this code must actually update the stack pointer -
790 // otherwise it must not. Ugly! The only use with findcatch set true is
791 // from the bytecode interpreter (bytes1.c)
792 if (findcatch) stack = sp;
793 }
794
795 bool force_backtrace = false;
796
errorset3(LispObject env,LispObject form,LispObject fg1,LispObject fg2)797 static LispObject errorset3(LispObject env,
798 LispObject form,
799 LispObject fg1,
800 LispObject fg2)
801 { LispObject r;
802 STACK_SANITY;
803 uint32_t flags = miscflags;
804 // See also (ENABLE-BACKTRACE level) and (ENABLE-ERROSET min max)
805 //
806 // (ERRORSET form message traceback)
807 // evaluates the form. If evaluation succeeds it hands back a list of
808 // length 1 containing the value. If it fails it returns an atom.
809 // If message=nil and traceback=nil then no diagnostics should appear.
810 // if message is set then the a 1-line explanation of any error is
811 // displayed. If traceback is set then in addition to that a backtrace
812 // is produced. I believe there is no merit in generating a traceback
813 // without the initial message, so (errorset form nil t) will be treated
814 // as if it had been (errorset form t t).
815 //
816 // CSL has four "levels" of diagnostic:
817 // 0 none at all
818 // 1 displays an inital message but nothing more
819 // 2 initial message + trace of functions that are active
820 // 3 as above but also shows args to functions.
821 //
822 // The "message" and "traceback" args to errorset select a level for
823 // use within the evaluation that is being controlled...
824 // message traceback resulting level
825 // nil any 0
826 // 0-3 any 0-3
827 // t nil 1
828 // t t 3
829 // t 0 1
830 // t 1-3 1-3
831 // any value other then nil and 0-3 counts as t above.
832 //
833 // The level established this way is then limited to be in the range
834 // set by (enable-errorset min max) where the default min and max are
835 // (obviously) 0 and 3. The limits set by enable-errorset are global.
836 // A facility previously called "always_noisy" is now achieved as
837 // by (enable-errorset 3 3).
838 //
839 // Finally within the evaluation of the form that erroset processes it is
840 // possible to call (enable-backtrace level) where level is 0-3 (or nil
841 // for 0, t for 3) and that sets the diagnostic level independent of the
842 // errorset. A level set by enable-backtrace typically just lasts until
843 // you exit from the surrounding errorset, because that resets the level
844 // to its prior value.
845
846 { int n;
847 if (fg1 == nil) n = 0;
848 else if (fg1 == fixnum_of_int(0) ||
849 fg1 == fixnum_of_int(1) ||
850 fg1 == fixnum_of_int(2) ||
851 fg1 == fixnum_of_int(3)) n = int_of_fixnum(fg1);
852 else // now depend on fg2
853 if (fg2 == nil || fg1 == fixnum_of_int(0)) n = 1;
854 else if (fg1 == fixnum_of_int(1) ||
855 fg1 == fixnum_of_int(2) ||
856 fg1 == fixnum_of_int(3)) n = int_of_fixnum(fg1);
857 else n = 3;
858 if (n < errorset_min) n = errorset_min;
859 if (n > errorset_max) n = errorset_max;
860 if (force_backtrace) n = 3;
861 miscflags &= ~BACKTRACE_MSG_BITS;
862 switch (n)
863 { case 0: break;
864 case 1: miscflags |= HEADLINE_FLAG;
865 break;
866 case 2: miscflags |= (HEADLINE_FLAG | FNAME_FLAG);
867 break;
868 default: // case 3:
869 miscflags |= BACKTRACE_MSG_BITS;
870 break;
871 }
872 }
873 { Save save(form, env);
874 stackcheck();
875 errexit();
876 save.restore(form, env);
877 }
878 errorset_msg = nullptr;
879 TRY
880 r = eval(form, nil);
881 CATCH(LispError)
882 // I am not going to catch exceptions such as the ones that restart the
883 // system - only ones that couunt as "errors".
884 miscflags = (flags & BACKTRACE_MSG_BITS) |
885 (miscflags & ~BACKTRACE_MSG_BITS);
886 // Now if within this errorset somebody had gone (enable-errorset min max)
887 // I must reset flags on the way out...
888 switch (errorset_min)
889 { case 0: break;
890 case 1: miscflags |= HEADLINE_FLAG;
891 break;
892 case 2: miscflags |= (HEADLINE_FLAG | FNAME_FLAG);
893 break;
894 default: // case 3:
895 miscflags |= BACKTRACE_MSG_BITS;
896 break;
897 }
898 switch (errorset_max)
899 { case 0: miscflags &= ~BACKTRACE_MSG_BITS;
900 break;
901 case 1: miscflags &= ~(FNAME_FLAG | ARGS_FLAG);
902 break;
903 case 2: miscflags &= ~ARGS_FLAG;
904 default:break;
905 }
906 if (stop_on_error) RETHROW;
907 if (consp(exit_value)) exit_value = nil;
908 return onevalue(exit_value);
909 END_CATCH;
910 // Now the normal exit case...
911 miscflags = (flags & BACKTRACE_MSG_BITS) |
912 (miscflags & ~BACKTRACE_MSG_BITS);
913 switch (errorset_min)
914 { case 0: break;
915 case 1: miscflags |= HEADLINE_FLAG;
916 break;
917 case 2: miscflags |= (HEADLINE_FLAG | FNAME_FLAG);
918 break;
919 default: // case 3:
920 miscflags |= BACKTRACE_MSG_BITS;
921 break;
922 }
923 switch (errorset_max)
924 { case 0: miscflags &= ~BACKTRACE_MSG_BITS;
925 break;
926 case 1: miscflags &= ~(FNAME_FLAG | ARGS_FLAG);
927 break;
928 case 2: miscflags &= ~ARGS_FLAG;
929 default:break;
930 }
931 r = ncons(r);
932 return onevalue(r);
933 }
934
Lerrorset_3(LispObject env,LispObject form,LispObject fg1,LispObject fg2)935 LispObject Lerrorset_3(LispObject env, LispObject form,
936 LispObject fg1, LispObject fg2)
937 // This is not a special form, but is put into the code here because,
938 // like unwind-protect, it has to re-gain control after an evaluation
939 // error.
940 { STACK_SANITY;
941 return errorset3(env, form, fg1, fg2);
942 }
943
Lerrorset_1(LispObject env,LispObject form)944 LispObject Lerrorset_1(LispObject env, LispObject form)
945 { return errorset3(env, form, nil, nil);
946 }
947
948
Lerrorset_2(LispObject env,LispObject form,LispObject ffg1)949 LispObject Lerrorset_2(LispObject env, LispObject form,
950 LispObject ffg1)
951 { return errorset3(env, form, ffg1, nil);
952 }
953
954 // (resource!-limit form time space io errors C_stack Lisp_stack)
955 // Evaluate the given form and if it succeeds return a
956 // list whose first item is its value. If it fails in the ordinary manner
957 // then its failure (error/throw/restart etc) gets passed back through
958 // here in a transparent manner. But if it runs out of resources this
959 // function catches that fact and returns an atomic value.
960 // Resource limits are not precise, and are specified by the
961 // subsequent arguments here:
962 // time: an integer giving a time allowance in seconds
963 // space: an integer giving a measure of memory that may be used,
964 // expressed in units of "megaconses". This may only be
965 // checked for at garbage collection and so small values
966 // will often be substantially overshot. This is space
967 // allocated - the fact that memory gets recycled does not
968 // get it discounted.
969 // io: an integer limiting the number of kilobytes of IO that may
970 // be performed.
971 // errors:an integer limiting the number of times traditional
972 // Lisp errors can occur. Note that if errorset is used
973 // you could have very many errors raised.
974 // C_stack:in integer limiting (in Kbytes) the max depth of C
975 // stack that may be used. The cut-off may be imprecise.
976 // Lisp_stack: an integer limiting (in Kbytes) the max depth of
977 // the Lisp stack that may be used.
978 // In each case specifying a negative limit means that that limit does
979 // not apply. But at least one limit must be specified.
980 // If calls to resource!-limit are nested the inner ones can only
981 // reduce the resources available to their form.
982 //
983 // Note that code within CSL can call the C function resource_exceeded() to
984 // note that resources have expired.
985
986 int64_t time_base = 0, space_base = 0, io_base = 0,
987 errors_base = 0;
988 int64_t time_now = 0, space_now = 0, io_now = 0,
989 errors_now = 0;
990 int64_t time_limit = -1, space_limit = -1, io_limit = -1,
991 errors_limit = 0;
992 int64_t Cstack_base = 0, Lispstack_base = 0;
993 int64_t Cstack_now = 0, Lispstack_now = 0;
994 int64_t Cstack_limit = -1, Lispstack_limit = -1;
995
996 class RAIIresource_variables
997 { int64_t save_time_base,
998 save_space_base,
999 save_io_base,
1000 save_errors_base,
1001 save_time_limit,
1002 save_space_limit,
1003 save_io_limit,
1004 save_errors_limit;
1005 LispObject *save_stack;
1006 public:
RAIIresource_variables()1007 RAIIresource_variables()
1008 { save_time_base = time_base;
1009 save_space_base = space_base;
1010 save_io_base = io_base;
1011 save_errors_base = errors_base;
1012 save_time_limit = time_limit;
1013 save_space_limit = space_limit;
1014 save_io_limit = io_limit;
1015 save_errors_limit = errors_limit;
1016 save_stack = stack;
1017 }
~RAIIresource_variables()1018 ~RAIIresource_variables()
1019 { time_base = save_time_base;
1020 space_base = save_space_base;
1021 io_base = save_io_base;
1022 errors_base = save_errors_base;
1023 time_limit = save_time_limit;
1024 space_limit = save_space_limit;
1025 io_limit = save_io_limit;
1026 errors_limit = save_errors_limit;
1027 stack = save_stack;
1028 }
1029 };
1030
resource_limit7(LispObject env,LispObject form,LispObject ltime,LispObject lspace,LispObject lio,LispObject lerrors,LispObject Csk,LispObject Lsk)1031 static LispObject resource_limit7(LispObject env,
1032 LispObject form,
1033 LispObject ltime,
1034 LispObject lspace,
1035 LispObject lio,
1036 LispObject lerrors,
1037 LispObject Csk,
1038 LispObject Lsk)
1039 {
1040 // This is being extended to make it possible to limit the C and Lisp stack
1041 // usage. At present the controls for that are not in place!
1042 STACK_SANITY;
1043 LispObject r;
1044 int64_t lltime, llspace, llio, llerrors;
1045 RAIIresource_variables RAIIresource_variables_object;
1046 int64_t r0=0, r1=0, r2=0, r3=0;
1047 errorset_msg = nullptr;
1048 // Here I need to do something that actually sets up the limits!
1049 // I only allow limits that are up to 31-bits...
1050 lltime = thirty_two_bits(ltime); // .. or zero if not an integer
1051 llspace = thirty_two_bits(lspace);
1052 llio = thirty_two_bits(lio);
1053 llerrors = thirty_two_bits(lerrors);
1054 // I can get thrown back here in four important ways:
1055 // (1) The calculation succeeds.
1056 // (2) It fails with a regular Lisp error.
1057 // (3) It fails because it raises a C-level signal.
1058 // (4) It fails by raising a resource-exhausted complaint.
1059 TRY
1060 time_base = time_now;
1061 space_base = space_now;
1062 io_base = io_now;
1063 errors_base = errors_now;
1064 if (lltime >= 0)
1065 { int w;
1066 // I make 2 seconds the smallest I can specify as a timeout because with
1067 // my clock resolution at 1 sec if I specified "1" I could do so just a
1068 // smidgin before the clock time and end up with no slack at all.
1069 if (lltime == 0 || lltime == 1) lltime = 2;
1070 w = time_base + lltime;
1071 if (time_limit >= 0 && time_limit < w) w = time_limit;
1072 time_limit = w;
1073 }
1074 if (llspace >= 0)
1075 { int w;
1076 // I make 2 megaconses the smallest request here for much the same
1077 // reason I put a lower limit on time. Actually if go further and make
1078 // 4 megaconses my limit...
1079 if (llspace >= 0 && llspace < 4) llspace = 4;
1080 // Ok, the USER specified things in megaconses, but internally I will
1081 // count in "CSL pages". The standard CSL page size is 4Mbytes, ie 0.5 or
1082 // 0.25 megaconses so I multiply by 2 or 4 here (for 32 or 64-bit systems).
1083 llspace *= (2*sizeof(LispObject)*1024*1024)/PAGE_POWER_OF_TWO;
1084 w = space_base + llspace;
1085 if (space_limit >= 0 && space_limit < w) w = space_limit;
1086 space_limit = w;
1087 }
1088 if (llio >= 0)
1089 { int w;
1090 if (llio == 0 || llio == 1) llio = 2;
1091 w = io_base + llio;
1092 if (io_limit >= 0 && io_limit < w) w = io_limit;
1093 io_limit = w;
1094 }
1095 if (llerrors >= 0)
1096 { int w;
1097 w = errors_base + llerrors;
1098 if (errors_limit >= 0 && errors_limit < w) w = errors_limit;
1099 errors_limit = w;
1100 }
1101 // All the above mess was just establishing the limits to be applied!
1102 // So now I can do the work that has to be constrained.
1103 r = eval(form, nil);
1104 r0 = time_now - time_base;
1105 r1 = (space_now - space_base)/
1106 ((2*sizeof(LispObject)*1024*1024)/PAGE_POWER_OF_TWO);
1107 r2 = io_now - io_base;
1108 r3 = errors_now - errors_base;
1109 CATCH(LispResource)
1110 form = list4(fixnum_of_int(r0),
1111 fixnum_of_int(r1),
1112 fixnum_of_int(r2),
1113 fixnum_of_int(r3));
1114 errexit();
1115 setvalue(resources, form);
1116 // Here I had a resource limit trap
1117 return onevalue(nil);
1118 END_CATCH;
1119 // The guarded code may have exited with some other exception!
1120 errexit();
1121 // I would like the result to show what resources had been used, but for now
1122 // I just use ncons to wrap the resuult up.
1123 r = ncons(r);
1124 errexit();
1125 { Save save(r);
1126 form = list4(fixnum_of_int(r0),
1127 fixnum_of_int(r1),
1128 fixnum_of_int(r2),
1129 fixnum_of_int(r3));
1130 errexit();
1131 save.restore(r);
1132 }
1133 setvalue(resources, form);
1134 return onevalue(r);
1135 }
1136
Lresource_limit_4up(LispObject env,LispObject form,LispObject ltime,LispObject lspace,LispObject a4up)1137 LispObject Lresource_limit_4up(LispObject env, LispObject form,
1138 LispObject ltime,
1139 LispObject lspace, LispObject a4up)
1140 { LispObject lio, lerrors, Csk, Lsk;
1141 STACK_SANITY;
1142 if (!is_fixnum(ltime)) ltime = fixnum_of_int(-1);
1143 if (!is_fixnum(lspace)) lspace = fixnum_of_int(-1);
1144 lio = lerrors = Csk = Lsk = fixnum_of_int(-1);
1145 if (a4up != nil)
1146 { lio = car(a4up);
1147 a4up = cdr(a4up);
1148 if (a4up != nil)
1149 { lerrors = car(a4up);
1150 a4up = cdr(a4up);
1151 if (a4up != nil)
1152 { Csk = car(a4up);
1153 a4up = cdr(a4up);
1154 if (a4up != nil)
1155 { Lsk = car(a4up);
1156 }
1157 }
1158 }
1159 }
1160 return resource_limit7(env, form, ltime, lspace, lio, lerrors, Csk,
1161 Lsk);
1162 }
1163
1164
Lresource_limit_2(LispObject env,LispObject form,LispObject ltime)1165 LispObject Lresource_limit_2(LispObject env, LispObject form,
1166 LispObject ltime)
1167 { return resource_limit7(env, form, ltime,
1168 fixnum_of_int(-1),
1169 fixnum_of_int(-1),
1170 fixnum_of_int(-1),
1171 fixnum_of_int(-1),
1172 fixnum_of_int(-1));
1173 }
1174
Lresource_limit_3(LispObject env,LispObject form,LispObject ltime,LispObject lspace)1175 LispObject Lresource_limit_3(LispObject env, LispObject form,
1176 LispObject ltime, LispObject lspace)
1177 { return resource_limit7(env, form, ltime, lspace,
1178 fixnum_of_int(-1),
1179 fixnum_of_int(-1),
1180 fixnum_of_int(-1),
1181 fixnum_of_int(-1));
1182 }
1183
1184
when_fn(LispObject args,LispObject env)1185 static LispObject when_fn(LispObject args, LispObject env)
1186 { LispObject w;
1187 STACK_SANITY;
1188 if (!consp(args)) return onevalue(nil);
1189 stackcheck(args, env);
1190 { Save save(args, env);
1191 w = eval(car(args), env);
1192 errexit();
1193 save.restore(args, env);
1194 }
1195 if (w == nil) return onevalue(nil);
1196 else return progn_fn(cdr(args), env);
1197 }
1198
bad_specialfn_0(LispObject env)1199 LispObject bad_specialfn_0(LispObject env)
1200 { return aerror1("bad special function", env);
1201 }
1202
bad_specialfn_2(LispObject env,LispObject a,LispObject b)1203 LispObject bad_specialfn_2(LispObject env, LispObject a, LispObject b)
1204 { return aerror1("bad special function", env);
1205 }
1206
bad_specialfn_3(LispObject env,LispObject a,LispObject b,LispObject c)1207 LispObject bad_specialfn_3(LispObject env, LispObject a, LispObject b,
1208 LispObject c)
1209 { return aerror1("bad special function", env);
1210 }
1211
bad_specialfn_4up(LispObject env,LispObject a,LispObject b,LispObject c,LispObject d)1212 LispObject bad_specialfn_4up(LispObject env, LispObject a, LispObject b,
1213 LispObject c, LispObject d)
1214 { return aerror1("bad special function", env);
1215 }
1216
1217 #define DEF_special(name, def) \
1218 {name, bad_specialfn_0, def, bad_specialfn_2, bad_specialfn_3, bad_specialfn_4up}
1219
1220 setup_type const eval3_setup[] =
1221 { DEF_special("or", or_fn),
1222 DEF_special("prog", prog_fn),
1223 DEF_special("prog1", prog1_fn),
1224 DEF_special("prog2", prog2_fn),
1225 DEF_special("progn", progn_fn),
1226 DEF_special("quote", quote_fn),
1227 DEF_special("return", return_fn),
1228 DEF_special("setq", setq_fn),
1229 DEF_special("tagbody", tagbody_fn),
1230 DEF_special("unless", unless_fn),
1231 DEF_special("unwind-protect", unwind_protect_fn),
1232 DEF_special("when", when_fn),
1233 DEF_special("macrolet", macrolet_fn),
1234 DEF_special("multiple-value-call", mv_call_fn),
1235 DEF_special("multiple-value-prog1", mv_prog1_fn),
1236 DEF_special("progv", progv_fn),
1237 DEF_special("return-from", return_from_fn),
1238 DEF_special("the", the_fn),
1239 DEF_special("throw", throw_fn),
1240 {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}
1241 };
1242
1243 // end of eval3.cpp
1244