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