-
+ 97B22557F90FE3BB7B5889A5976AFEE0788CC7063F955194BB78287D7C4A6924F99A400271A0DB241DA7D19C4F210AF1AFDA511E533C4B9C20A956BE87C9F6DD
adalisp/src/evaler.adb
(0 . 0)(1 . 1014)
57 -- LispM evaluator implementation.
58
59 with Ada.Text_IO; use Ada.Text_IO; -- for error reporting
60
61 package body Evaler is
62
63 -- An accumulator register for arithmetic/logic ops.
64 ALU_Acc : Long_Integer := 0;
65
66 -- Apply arithmetic and logic function, i.e. +, -, * or /
67 procedure Apply_ALU_Func(Func : in ALUFunc;
68 Args : in MemPtr;
69 Env : in MemPtr; OutP : out MemPtr) is
70 P, CarP : MemPtr;
71 begin
72 -- General form: (f e1 e2 ...) where f is one of +, -, * or
73 -- /. Special cases:
74 --
75 -- - (+) --> 0
76 -- - (- x) --> -x
77 -- - (*) --> 1
78 -- - (/ x) --> 1/x (always 0 for Fixnums)
79
80 -- Initialize loop variables and perform argument count checks
81 -- where needed.
82 P := Args;
83 case Func is
84 when ALU_Add =>
85 -- Identity element for +
86 ALU_Acc := 0;
87 when ALU_Sub =>
88 -- - needs at least one argument
89 pragma Assert(P /= 0, "- needs at least 1 argument");
90 CarP := Get_Car(AMem(P));
91 P := Get_Cdr(AMem(P));
92
93 -- (- x) is arithmetic negation; (- x y ...) is equivalent
94 -- to x - y - ...
95 if (P = 0) then
96 ALU_Acc := -Get_Fixnum(AMem(CarP));
97 else
98 ALU_Acc := Get_Fixnum(AMem(CarP));
99 end if;
100 when ALU_Mul =>
101 -- Identity element for *
102 ALU_Acc := 1;
103 when ALU_Div =>
104 -- / needs at least 1 argument
105 pragma Assert(P /= 0, "/ needs at least 1 argument");
106 CarP := Get_Car(AMem(P));
107 P := Get_Cdr(AMem(P));
108
109 -- (/ x) is 1 / x; (/ x y ...) is equivalent to x / y /
110 -- ... ; we don't support floats, so 1 / x should always
111 -- yield 0.
112 if (P = 0) then
113 ALU_Acc := 1 / Get_Fixnum(AMem(CarP));
114 else
115 ALU_Acc := Get_Fixnum(AMem(CarP));
116 end if;
117 end case;
118
119 -- Loop through the arg list and accumulate.
120 while P /= 0 loop
121 -- Get car and accumulate it
122 CarP := Get_Car(AMem(P));
123 pragma Assert(AMem(CarP).T = Fixnum,
124 "Expected a number.");
125 case Func is
126 when ALU_Add =>
127 ALU_Acc := ALU_Acc + Get_Fixnum(AMem(CarP));
128 when ALU_Sub =>
129 ALU_Acc := ALU_Acc - Get_Fixnum(AMem(CarP));
130 when ALU_Mul =>
131 ALU_Acc := ALU_Acc * Get_Fixnum(AMem(CarP));
132 when ALU_Div =>
133 pragma Assert(Get_Fixnum(AMem(CarP)) /= 0,
134 "Division by zero!");
135 ALU_Acc := ALU_Acc / Get_Fixnum(AMem(CarP));
136 end case;
137
138 -- Continue
139 P := Get_Cdr(AMem(P));
140 end loop;
141 -- Store value in a new cell and return it.
142 Alloc_Fixnum(ALU_Acc, OutP);
143 end Apply_ALU_Func;
144
145 -- Apply unary predicate.
146 procedure Apply_UPred(Pred : in UPred;
147 Args : in MemPtr;
148 Env : in MemPtr;
149 OutP : out MemPtr) is
150 P : MemPtr := Args;
151 ArgP : MemPtr;
152 begin
153 -- General form: (pred val) where pred is one of pair?, boolean?,
154 -- number?, symbol?, null? or list?. Read below for
155 -- particularities.
156
157 -- Argument sanity checking
158 pragma Assert(P /= 0, "Function requires 1 argument.");
159 ArgP := Get_Car(AMem(P));
160 P := Get_Cdr(AMem(P));
161 pragma Assert(P = 0, "Function requires 1 argument.");
162
163 -- What predicate op are we applying?
164 case Pred is
165 when UPred_Pair =>
166 -- (pair? '()) --> #f
167 -- (pair? anything-else) --> anything-else is a cons
168 if ArgP = 0 then
169 Alloc_Bool(False, OutP);
170 else
171 Alloc_Bool(AMem(ArgP).T = Cons, OutP);
172 end if;
173 when UPred_Bool =>
174 -- (boolean? '()) --> #f
175 -- (boolean? anything-else) --> anything-else is #t or #f
176 if ArgP = 0 then
177 Alloc_Bool(False, OutP);
178 else
179 Alloc_Bool(AMem(ArgP).T = Bool, OutP);
180 end if;
181 when UPred_Num =>
182 -- (number? '()) --> #f
183 -- (number? anything-else) --> anything-else is a fixnum
184 if ArgP = 0 then
185 Alloc_Bool(False, OutP);
186 else
187 Alloc_Bool(AMem(ArgP).T = Fixnum, OutP);
188 end if;
189 when UPred_Sym =>
190 -- (symbol? '()) --> #f
191 -- (symbol? 'anything-else) --> anything else is a symbol
192 if ArgP = 0 then
193 Alloc_Bool(False, OutP);
194 else
195 Alloc_Bool(AMem(ArgP).T = Symbol, OutP);
196 end if;
197 when UPred_Nil =>
198 -- (null? '()) --> #t
199 -- (null? anything-else) --> #f
200 Alloc_Bool(ArgP = 0, OutP);
201 when UPred_List =>
202 -- (list? x) --> x is a proper-list, i.e. NIL or a form
203 -- (cons e1 .. (cons en NIL))
204
205 -- try walking through a list until NIL
206 loop
207 exit when ArgP = 0;
208 exit when AMem(ArgP).T /= Cons;
209 ArgP := Get_Cdr(AMem(ArgP));
210 end loop;
211 -- if a non-NIL is encountered anywhere in a cdr (or in the
212 -- main object), then not a list.
213 Alloc_Bool(ArgP = 0, OutP);
214 end case;
215 end Apply_UPred;
216
217 -- Apply and/or special form.
218 procedure Apply_AndOr(Cond : in AndOr;
219 Args : in MemPtr;
220 Env : in MemPtr;
221 OutP : out MemPtr) is
222 P, ArgP : MemPtr;
223 ReachedEnd : Boolean := False;
224 begin
225 -- General form: (cond e1 e2 ...) where cond is one of and or
226 -- or. Particularities:
227 --
228 -- - and evaluates until the end or the first #f encountered
229 -- - or evaluates until the end or the first non-#f encountered
230 --
231 -- More details below.
232
233 P := Args;
234 if P = 0 then
235 -- vacuous truth/falsity:
236 -- (and) --> #t
237 -- (or) --> #f
238 ReachedEnd := True;
239 Alloc_Bool(Cond = AndOr_And, ArgP);
240 end if;
241 loop
242 -- have we reached the end?
243 if P = 0 then
244 ReachedEnd := True;
245 exit;
246 end if;
247 -- eval lazily:
248 -- - and stops at the first argument evaluated to #f
249 -- - or stops at the first argument evaluated to #t
250 ArgP := Get_Car(AMem(P));
251 Eval(ArgP, Env, ArgP);
252
253 exit when Cond = AndOr_And and Boolean_Value(ArgP) = False;
254 exit when Cond = AndOr_Or and Boolean_Value(ArgP) = True;
255
256 -- continue
257 P := Get_Cdr(AMem(P));
258 end loop;
259
260 -- Returned value:
261 -- (and e1 e2 ...) returns #f or the last element
262 -- (or e1 e2 ...) returns #f or the first non-#f element
263 case Cond is
264 when AndOr_And =>
265 if ReachedEnd then
266 OutP := ArgP;
267 else
268 Alloc_Bool(False, OutP);
269 end if;
270 when AndOr_Or =>
271 if ReachedEnd then
272 Alloc_Bool(False, OutP);
273 else
274 OutP := ArgP;
275 end if;
276 end case;
277 end Apply_AndOr;
278
279 -- Apply quote.
280 procedure Apply_QuoteB(Args : in MemPtr; OutP : out MemPtr) is
281 begin
282 -- General form:
283 --
284 -- (quote '()) --> ()
285 -- (quote expr) --> expr
286
287 OutP := (if Args = 0 then 0 else Get_Car(AMem(Args)));
288 end Apply_QuoteB;
289
290 -- Apply eval.
291 procedure Apply_EvalB(Args, Env : in MemPtr; OutP : out MemPtr) is
292 Arg : MemPtr;
293 begin
294 -- General form: (eval expr env), where expr is any S-expression
295 -- and env is an optional environment (currently unimplemented).
296
297 -- XXX: Need to do eval environments.
298 pragma Assert(Args /= 0, "Eval needs at least 1 argument.");
299 Arg := Get_Car(AMem(Args));
300 -- Just call eval on arg
301 Eval(Arg, Env, OutP);
302 end Apply_EvalB;
303
304 -- Apply if.
305 procedure Apply_IfB(Args, Env : in MemPtr; OutP : out MemPtr) is
306 P : MemPtr := Args;
307 PredP : MemPtr;
308 PredVal : Boolean;
309 begin
310 -- General form: (if pred a b) where pred, a and b are optional
311 -- S-expressions. The evaluation rules are:
312 --
313 -- - (if) --> ()
314 -- - (if pred) --> pred is evaluated and () is returned
315 -- - (if pred a) --> pred is evaluated
316 -- . if pred evals to #t, then a is evaluated and returned
317 -- . otherwise, () is returned
318 -- - (if pred a b) --> pred is evaluated
319 -- . if pred evals to #t, then a is evaluated and returned
320 -- . otherwise, b is evaluated and returned
321
322 -- no args: (if) --> ()
323 if P = 0 then
324 OutP := 0;
325 return;
326 end if;
327
328 -- get predicate, evaluate it and determine its boolean value
329 -- (implicitly true for non-booleans)
330 PredP := Get_Car(AMem(P));
331 Eval(PredP, Env, PredP);
332 PredVal := Boolean_Value(PredP);
333
334 -- look for branches: P points to () or (a) or (a b)
335 P := Get_Cdr(AMem(P));
336
337 -- select branch: if pred evaluated to #f and the user specified
338 -- (at least) the #t branch, then we cdr to the #f branch;
339 -- otherwise, if no branches are specified, we return.
340 if not PredVal and P /= 0 then
341 P := Get_Cdr(AMem(P));
342 elsif P = 0 then
343 OutP := 0;
344 return;
345 end if;
346 -- evaluate taken branch
347 P := Get_Car(AMem(P));
348 Eval(P, Env, OutP);
349 end Apply_IfB;
350
351 -- Apply cons.
352 procedure Apply_ConsB(Args, Env : in MemPtr; OutP : out MemPtr) is
353 P : MemPtr := Args;
354 CarP, CdrP : MemPtr;
355 begin
356 -- General form: (cons a b) where a and b are S-expressions.
357
358 pragma Assert(P /= 0, "Cons needs exactly 2 arguments.");
359 -- get car
360 CarP := Get_Car(AMem(P));
361 -- get cdr
362 P := Get_Cdr(AMem(P));
363 pragma Assert(P /= 0, "Cons needs exactly 2 arguments.");
364 CdrP := Get_Car(AMem(P));
365
366 -- Rest of P needs to be nil now.
367 P := Get_Cdr(AMem(P));
368 pragma Assert(P = 0, "Cons needs exactly 2 arguments.");
369
370 -- Cons the two
371 Alloc_Cons(CarP, CdrP, OutP);
372 end Apply_ConsB;
373
374 -- Apply car.
375 procedure Apply_CarB(Args, Env : in MemPtr; OutP : out MemPtr) is
376 P : MemPtr := Args;
377 ConsP : MemPtr;
378 begin
379 -- General form: (car x) where x is a cons.
380
381 pragma Assert(P /= 0, "car needs exactly 1 argument.");
382 -- Get x
383 ConsP := Get_Car(AMem(P));
384 pragma Assert (AMem(ConsP).T = Cons, "Expected pair.");
385 OutP := Get_Car(AMem(ConsP));
386
387 -- Rest of P needs to be nil
388 P := Get_Cdr(AMem(P));
389 pragma Assert (P = 0, "car needs exactly 1 argument.");
390 end Apply_CarB;
391
392 -- Apply cdr.
393 procedure Apply_CdrB(Args, Env : in MemPtr; OutP : out MemPtr) is
394 P : MemPtr := Args;
395 ConsP : MemPtr;
396 begin
397 -- General form: (cdr x) where x is a cons.
398
399 pragma Assert(P /= 0, "cdr needs exactly 1 argument.");
400 -- Get x
401 ConsP := Get_Car(AMem(P));
402 pragma Assert (AMem(ConsP).T = Cons, "Expected pair.");
403 OutP := Get_Cdr(AMem(ConsP));
404
405 -- Rest of P needs to be nil
406 P := Get_Cdr(AMem(P));
407 pragma Assert (P = 0, "cdr needs exactly 1 argument.");
408 end Apply_CdrB;
409
410 -- Apply list.
411 procedure Apply_ListB(Args, Env : in MemPtr; OutP : out MemPtr) is
412 begin
413 -- General form: (list e1 e2 ...) where e1 e2 ... are optional
414 -- S-expressions.
415
416 -- Applicative order evaluation is done by Apply_Func, so we just
417 -- propagate the arguments.
418 OutP := Args;
419 end Apply_ListB;
420
421 -- Apply apply.
422 procedure Apply_ApplyB(Args, Env : in MemPtr; OutP : out MemPtr) is
423 P : MemPtr := Args;
424 OpP, ArgsP, LastArgP : MemPtr;
425 begin
426 -- General form: (apply f a1 a2 ... args) where f is a function,
427 -- a1 a2 ... are S-expressions and args is a list.
428 --
429 -- The result is the same as applying f with (append (list a1 a2
430 -- ...) args) as arguments.
431
432 pragma Assert(P /= 0, "apply needs at least 1 argument.");
433
434 -- get op
435 OpP := Get_Car(AMem(P));
436
437 -- get args: this section is roughly equivalent to list* (or
438 -- cons*), i.e. (list* a1 a2 a3 ... args) --> (a1 a2 a3
439 -- ... . args), i.e. we stick args in the butt of (a1 a2 a3 ...).
440 P := Get_Cdr(AMem(P));
441 -- first, we check if we have any args at all
442 if P = 0 then goto DoApply; end if;
443 -- if so, we do a shallow (reversed) copy of the list, accumulated
444 -- in ArgsP; we put the car (the "args" above) in LastArgP and we
445 -- keep the cdr in ArgsP.
446 ArgsP := 0;
447 while P /= 0 loop
448 exit when AMem(P).T /= Cons;
449 Alloc_Cons(Get_Car(AMem(P)), ArgsP, ArgsP);
450 P := Get_Cdr(AMem(P));
451 end loop;
452 -- ArgsP has at least one element now!
453 LastArgP := Get_Car(AMem(ArgsP));
454 ArgsP := Get_Cdr(AMem(ArgsP));
455 -- now put this in the proper form
456 Rev_In_Place(ArgsP, LastArgP, P);
457
458 <<DoApply>>
459 -- Do the actual application
460 Apply_Func(OpP, P, Env, True, OutP);
461 end Apply_ApplyB;
462
463 -- Apply define.
464 procedure Apply_DefineB(Args, Env : in MemPtr; OutP : out MemPtr) is
465 P : MemPtr := Args;
466 SymP, ValP : MemPtr;
467 begin
468 -- General form: (define sym val) where sym is a symbol and val is
469 -- an optional S-expression.
470 --
471 -- XXX we need to split this into two types of defines:
472 -- symbol-defines (such as the one described here) and
473 -- lambda-defines, e.g. (define (func arg1 ...) val).
474
475 -- get sym
476 SymP := Get_Car(AMem(P));
477 pragma Assert (SymP /= 0, "Define: expected symbol for arg 1!");
478 pragma Assert (AMem(SymP).T = Symbol,
479 "Define: expected symbol for arg 1!");
480
481 -- get val: (define sym) binds sym to NIL.
482 P := Get_Cdr(AMem(P));
483 ValP := (if P = 0 then 0 else Get_Car(AMem(P)));
484
485 -- evaluate val
486 Eval(ValP, Env, ValP);
487 -- make (top-level!) binding
488 Bind_Env(SymP, ValP, Global_Env, P);
489 -- return symbol name
490 OutP := Get_Car(AMem(P));
491 end Apply_DefineB;
492
493 -- Apply set.
494 procedure Apply_SetB(Args, Env : in MemPtr; OutP : out MemPtr) is
495 P : MemPtr := Args;
496 SymP, ValP : MemPtr;
497 BindingP : MemPtr;
498 begin
499 -- General form: (set! sym val) where sym is a bound symbol and
500 -- val is an optional S-expression. set! returns the evaluated
501 -- val.
502
503 pragma Assert(P /= 0, "set! requires at least 1 argument.");
504 -- get sym
505 SymP := Get_Car(AMem(P));
506 -- and look it up in the scoped Envs
507 Lookup_Env_Or_Global(SymP, Env, BindingP);
508 -- binding must exist
509 pragma Assert(BindingP /= 0, "set! got an unbound variable.");
510
511 -- get value
512 P := Get_Cdr(AMem(P));
513 ValP := (if P = 0 then 0 else Get_Car(AMem(P)));
514 -- eval it
515 Eval(ValP, Env, ValP);
516 -- and modify the binding
517 Set_Cdr(AMem(BindingP), ValP);
518 -- return the value
519 OutP := ValP;
520 end Apply_SetB;
521
522 -- Apply numeric equality.
523 procedure Apply_EqnB(Args, Env : in MemPtr; OutP : out MemPtr) is
524 P : MemPtr := Args;
525 Fst, Other : MemPtr;
526 Result : MemPtr;
527 begin
528 -- General form: (= n1 n2 ...) where n1, n2, ... are numbers. =
529 -- expects at least two numbers as parameters.
530
531 pragma Assert(P /= 0, "= requires at least 2 arguments");
532 -- get first number
533 Fst := Get_Car(AMem(P));
534 pragma Assert(AMem(Fst).T = Fixnum, "Expected numeric arguments.");
535 -- move on to rest
536 P := Get_Cdr(AMem(P));
537 pragma Assert(P /= 0, "= requires at least 2 arguments");
538
539 -- allocate result: assume all numbers are equal until found
540 -- otherwise.
541 Alloc_Bool(True, Result);
542 -- loop through the other numbers
543 while P /= 0 loop
544 -- get other
545 Other := Get_Car(AMem(P));
546 pragma Assert(AMem(Other).T = Fixnum, "Expected numeric arguments.");
547 -- check equality: we assume two's complement representation
548 if AMem(Fst).Data /= AMem(Other).Data then
549 Set_Bool(AMem(Result), False);
550 end if;
551 -- move on to next element of the arg list
552 P := Get_Cdr(AMem(P));
553 end loop;
554 -- store result
555 OutP := Result;
556 end Apply_EqnB;
557
558 -- Apply pointer equality.
559 procedure Apply_EqB(Args, Env : in MemPtr; OutP : out MemPtr) is
560 P : MemPtr := Args;
561 P1, P2 : MemPtr;
562 begin
563 -- General form: (eq? x y) where x and y are S-expressions.
564
565 -- get x
566 pragma Assert(P /= 0, "eq? requires 2 arguments.");
567 P1 := Get_Car(AMem(P));
568 P := Get_Cdr(AMem(P));
569
570 -- get y
571 pragma Assert(P /= 0, "eq? requires 2 arguments.");
572 P2 := Get_Car(AMem(P));
573 P := Get_Cdr(AMem(P));
574 pragma Assert(P = 0, "eq? requires 2 arguments.");
575
576 -- compare x and y. XXX this is a hack, but eq? guarantees that
577 -- matching boolean values match, e.g. (eq? #f #f) --> #t. As an
578 -- alternative, we could reserve two special cells for #f and #t,
579 -- or give up the schemism altogether and use nil and everything
580 -- else as booleans.
581 --
582 -- (eq? '() '()) --> #t
583 -- (eq? 1 1) --> may be #f if the two instances of 1 have
584 -- different memory locations.
585 if P1 /= 0 and P2 /= 0 then
586 if AMem(P1).T = Bool and AMem(P2).T = Bool then
587 Alloc_Bool(AMem(P1).Data = AMem(P2).Data, OutP);
588 else
589 Alloc_Bool(P1 = P2, OutP);
590 end if;
591 else
592 Alloc_Bool(P1 = P2, OutP);
593 end if;
594 end Apply_EqB;
595
596 -- Apply value-wise equality.
597 procedure Apply_EqvB(Args, Env : in MemPtr; OutP : out MemPtr) is
598 P : MemPtr := Args;
599 Val1, Val2 : MemPtr;
600 Result : Boolean;
601 begin
602 -- General form: (eqv? x y) where x and y are
603 -- S-expressions. Unlike eq?, eqv? compares the data found in the
604 -- cells pointed to by x and y, with the exception of NIL
605 -- pointers, which are compared pointer-wise.
606
607 -- get x
608 pragma Assert(P /= 0, "eqv? requires 2 arguments.");
609 Val1 := Get_Car(AMem(P));
610 P := Get_Cdr(AMem(P));
611
612 -- get y
613 pragma Assert(P /= 0, "eqv? requires 2 arguments.");
614 Val2 := Get_Car(AMem(P));
615 P := Get_Cdr(AMem(P));
616 pragma Assert(P = 0, "eqv? requires 2 arguments.");
617
618 -- (eqv? '() y) --> (null? y)
619 -- (eqv? x y) (where x is non-NIL) --> values are equal, e.g.
620 -- - (eqv? 1 1) --> #t
621 -- - (eqv? '(1) '(1)) --> #f (comparison between values of cons cells)
622 if Val1 = 0 then
623 Result := Val2 = 0;
624 else
625 Result := AMem(Val1).Data = AMem(Val2).Data;
626 end if;
627 -- set result
628 Alloc_Bool(Result, OutP);
629 end Apply_EqvB;
630
631 -- Apply not.
632 procedure Apply_NotB(Args, Env : in MemPtr; OutP : out MemPtr) is
633 P : MemPtr := Args;
634 Val : MemPtr;
635 begin
636 -- General form: (not x) where x is a S-expression. not is
637 -- evaluated using the following rules:
638 -- - (not #f) --> #t
639 -- - (not x) (x /= #f) --> #f
640
641 -- get argument
642 pragma Assert (P /= 0, "not requires 1 argument.");
643 Val := Get_Car(AMem(P));
644 P := Get_Cdr(AMem(P));
645 pragma Assert (P = 0, "not requires 1 argument.");
646
647 -- perform logic negation on boolean value.
648 Alloc_Bool(not Boolean_Value(Val), OutP);
649 end Apply_NotB;
650
651 -- Apply lambda.
652 procedure Apply_LambdaB(Args, Env : in MemPtr; OutP : out MemPtr) is
653 begin
654 -- General form: (lambda args e1 e2 ...) where args is a list of
655 -- formal arguments (symbols) and e1 e2 ... are optional
656 -- S-expressions that may contain references to the formal
657 -- arguments; i.e., e1 e2 ... forms a lexical scope where the
658 -- formal arguments are bound.
659 --
660 -- See Alloc_Closure for more details.
661 Alloc_Closure(Args, Env, OutP);
662 end Apply_LambdaB;
663
664 -- Apply let.
665 procedure Apply_LetB(Args, Env : in MemPtr; OutP : out MemPtr) is
666 BndsP, CodeP : MemPtr;
667 ArgsP, ValuesP, ClosureP : MemPtr;
668 begin
669 -- General form: (let bnds . code) where bnds is a list (bnd1 bnd2
670 -- ...) and code is a list (e1 e2 ...). More precisely:
671 --
672 -- - bndi is a list of the form (si vi) where si is a symbol and
673 -- vi is a mandatory S-expression.
674 -- - ek is an optional S-expression that may contain references to
675 -- si.
676 --
677 -- Lets do the following: 1. every vi is evaluated; 2. every
678 -- evaluated vi is used as a lexical binding for the
679 -- corresponding si; and 3. in the newly-created lexical scope,
680 -- ek are evaluated in the order they appear in code.
681 --
682 -- More generally, any expression of the form:
683 --
684 -- (let ((s1 v1) (s2 v2) ... (sn vn)) e1 e2 ... em)
685 --
686 -- is equivalent to:
687 --
688 -- ((lambda (s1 s2 ... sn) e1 e2 ... em) v1 v2 ... vn).
689 --
690 -- Thus this implementation: 1. collects the formal arguments
691 -- (names) in ArgsP and the effective arguments (values) in
692 -- ValuesP; 2. collects the code in CodeP; 3. creates a closure
693 -- ClosureP from the list (ArgsP . CodeP); 4. applies ClosureP on
694 -- ValuesP.
695
696 -- (let) --> ()
697 if Args = 0 then
698 -- nothing to do here
699 OutP := 0;
700 return;
701 end if;
702
703 -- get bindings and code; initialize arglist and valuelist
704 BndsP := Get_Car(AMem(Args));
705 CodeP := Get_Cdr(AMem(Args));
706 ArgsP := 0; ValuesP := 0;
707
708 -- collect formal args and effective values
709 while BndsP /= 0 loop
710 declare
711 BndP : MemPtr := Get_Car(AMem(BndsP));
712 SymP, ValP : MemPtr;
713 begin
714 pragma Assert(BndP /= 0, "Bad syntax of let spec.");
715 pragma Assert(AMem(BndP).T = Cons, "Bad syntax of let spec.");
716
717 -- get symbol and advance in BndP
718 SymP := Get_Car(AMem(BndP));
719 ValP := Get_Cdr(AMem(BndP));
720 -- XXX: this is the stricter version
721 pragma Assert (ValP /= 0, "Bad syntax of binding in let.");
722 -- get val and evaluate it
723 ValP := Get_Car(AMem(ValP));
724 Eval(ValP, Env, ValP);
725 -- add symbol to ArgsP, value to ValuesP
726 Alloc_Cons(SymP, ArgsP, ArgsP);
727 Alloc_Cons(ValP, ValuesP, ValuesP);
728 -- continue
729 BndsP := Get_Cdr(AMem(BndsP));
730 end;
731 end loop;
732
733 -- cons args to code
734 Alloc_Cons(ArgsP, CodeP, CodeP);
735 -- make closure
736 Apply_LambdaB(CodeP, Env, ClosureP);
737 -- apply closure
738 Apply_Closure(ClosureP, ValuesP, Env, OutP);
739 end Apply_LetB;
740
741 -- Apply reverse.
742 procedure Apply_ReverseB(Args, Env : in MemPtr; OutP : out MemPtr) is
743 P : MemPtr := Args;
744 List : MemPtr;
745 begin
746 -- General form: (reverse x) where x is a list.
747
748 -- get x
749 pragma Assert (P /= 0, "reverse requires 1 argument.");
750 List := Get_Car(AMem(P));
751 P := Get_Cdr(AMem(P));
752 pragma Assert (P = 0, "reverse requires 1 argument.");
753
754 -- reverse x
755 Rev_Append(0, List, OutP);
756 end Apply_ReverseB;
757
758 -- Apply append.
759 procedure Apply_AppendB(Args, Env : in MemPtr; OutP : out MemPtr) is
760 Lists : MemPtr := Args;
761 Acc : MemPtr := 0;
762 begin
763 -- General form: (append x1 x2 ...) where x1 x2 ... are lists. In
764 -- particular, the last xi may be any object, in which case the
765 -- result of append is not a proper list, e.g.
766 --
767 -- - (append '(x) '(y)) --> (x y)
768 -- - (append '(x y) 'z) --> (x y . z)
769
770 -- (append) --> ()
771 if Lists = 0 then
772 OutP := 0;
773 return;
774 end if;
775
776 -- accumulate in acc; stop when Lists has one element, so that we
777 -- don't lose the reference to the last element.
778 while Get_Cdr(AMem(Lists)) /= 0 loop
779 -- prepend in reverse to Acc
780 Rev_Append(Acc, Get_Car(AMem(Lists)), Acc);
781 -- continue
782 Lists := Get_Cdr(AMem(Lists));
783 end loop;
784
785 -- reverse Acc in place, adding the last element in Lists to the
786 -- tail.
787 Rev_In_Place(Acc, Get_Car(AMem(Lists)), Acc);
788 OutP := Acc;
789 end Apply_AppendB;
790
791 -- Apply closure.
792 procedure Apply_Closure(Op, Args, Env : in MemPtr; OutP : out MemPtr) is
793 EArgs : MemPtr := Args;
794 CArgs, CCode, CEnv : MemPtr;
795 begin
796 -- General form: (f a1 a2 ... an) where:
797 --
798 -- - f is a closure object, comprising an environment env (a list
799 -- of bindings) and a code; the code is itself made of a list of
800 -- formal parameters and a code body;
801 -- - a1, a2 ... an, are arguments, i.e. effective parameters, to
802 -- f, such that n is equal to the length of the list of formal
803 -- parameters.
804 --
805 -- The application of f on the arguments is performed by binding
806 -- each formal parameter to each corresponding argument, adding
807 -- them to env and evaluating each expression in the code body in
808 -- the (lexical) context of env.
809
810 -- Initialize CArgs, CCode, CEnv.
811 CEnv := Get_Closure_Env(AMem(Op));
812 CCode := Get_Closure_Code(AMem(Op));
813 -- Do we have an arglist and code?
814 if CCode = 0 then
815 CArgs := 0;
816 else
817 CArgs := Get_Car(AMem(CCode));
818 CCode := Get_Cdr(AMem(CCode));
819 end if;
820
821 -- ((lambda () ...) ...)
822 if CArgs = 0 then goto DoEval; end if;
823
824 -- CArgs can be a:
825 -- - symbol, e.g. ((lambda x (cdr x)) 1 2) --> (2)
826 -- - list, e.g. ((lambda (x y) (+ x y)) 1 2) --> 3
827 if AMem(CArgs).T = Symbol then
828 -- extend env with a binding to effective arglist
829 Alloc_Cons(CArgs, EArgs, CArgs);
830 Alloc_Cons(CArgs, CEnv, CEnv);
831 elsif AMem(CArgs).T = Cons then
832 -- for each argument in CArgs corresponding to an actual value
833 -- in EArgs, add a binding in CEnv.
834 while CArgs /= 0 loop
835 declare
836 ArgP, ValP, BindingP : MemPtr;
837 begin
838 -- assert: (= (length CArgs) (length EArgs))
839 pragma Assert(EArgs /= 0,
840 "Not enough arguments.");
841 -- (cons (car CArgs) (car EArgs))
842 ArgP := Get_Car(AMem(CArgs));
843 ValP := Get_Car(AMem(EArgs));
844 -- add binding to env, ignore non-symbols
845 if ArgP /= 0 then
846 if AMem(ArgP).T = Symbol then
847 Alloc_Cons(ArgP, ValP, BindingP);
848 Alloc_Cons(BindingP, CEnv, CEnv);
849 end if;
850 end if;
851 -- continue with next argument
852 CArgs := Get_Cdr(AMem(CArgs));
853 EArgs := Get_Cdr(AMem(EArgs));
854 end;
855 end loop;
856 else
857 pragma Assert(False, "Expected symbol or cons.");
858 end if;
859
860 <<DoEval>>
861 -- eval all coads
862 while CCode /= 0 loop
863 declare
864 E : MemPtr;
865 begin
866 -- get current coad
867 E := Get_Car(AMem(CCode));
868 -- eval it, put result in OutP
869 Eval(E, CEnv, OutP);
870 -- continue
871 CCode := Get_Cdr(AMem(CCode));
872 end;
873 end loop;
874 end Apply_Closure;
875
876 -- Apply a function on argument list.
877 procedure Apply_Func(Op, Args, Env : in MemPtr;
878 Meta : in Boolean;
879 OutP : out MemPtr) is
880
881 -- XXX: This should actually delimit between built-in functions
882 -- and keywords; other functions (e.g. apply) may need to use
883 -- this to provide relevant errors.
884 Applicative_OrderP : constant array(BuiltinID) of Boolean :=
885 (QuoteB | IfB | DefineB | SetB | AndB | OrB | LambdaB |
886 LetB => False,
887 others => True);
888
889 BID : BuiltinID;
890 EvaledArgs : MemPtr;
891 begin
892 pragma Assert(Op /= 0, "NIL op!");
893
894 -- Is Op a builtin?
895 if AMem(Op).T = Builtin then
896 BID := Get_Builtin(AMem(Op));
897
898 -- We want to evaluate the arguments before applying the
899 -- function if:
900 -- . the function permits it, or
901 -- . Apply_Func was not called by apply (who already evals)
902 if Applicative_OrderP(BID) and (not Meta) then
903 Eval_List(Args, Env, EvaledArgs);
904 else
905 EvaledArgs := Args;
906 end if;
907 -- What builtin Op do we evaluate?
908 case BID is
909 when AddB => Apply_ALU_Func(ALU_Add, EvaledArgs, Env, OutP);
910 when SubB => Apply_ALU_Func(ALU_Sub, EvaledArgs, Env, OutP);
911 when MulB => Apply_ALU_Func(ALU_Mul, EvaledArgs, Env, OutP);
912 when DivB => Apply_ALU_Func(ALU_Div, EvaledArgs, Env, OutP);
913 when QuoteB => Apply_QuoteB(EvaledArgs, OutP);
914 when EvalB => Apply_EvalB(EvaledArgs, Env, OutP);
915 when IfB => Apply_IfB(EvaledArgs, Env, OutP);
916 when ConsB => Apply_ConsB(EvaledArgs, Env, OutP);
917 when CarB => Apply_CarB(EvaledArgs, Env, OutP);
918 when CdrB => Apply_CdrB(EvaledArgs, Env, OutP);
919 when ListB => Apply_ListB(EvaledArgs, Env, OutP);
920 when ApplyB => Apply_ApplyB(EvaledArgs, Env, OutP);
921 when DefineB => Apply_DefineB(EvaledArgs, Env, OutP);
922 when SetB => Apply_SetB(EvaledArgs, Env, OutP);
923 when EqnB => Apply_EqnB(EvaledArgs, Env, OutP);
924 when EqB => Apply_EqB(EvaledArgs, Env, OutP);
925 when EqvB => Apply_EqvB(EvaledArgs, Env, OutP);
926 when PairPB => Apply_UPred(UPred_Pair, EvaledArgs, Env, OutP);
927 when BooleanPB => Apply_UPred(UPred_Bool, EvaledArgs, Env, OutP);
928 when NumberPB => Apply_UPred(UPred_Num, EvaledArgs, Env, OutP);
929 when SymbolPB => Apply_UPred(UPred_Sym, EvaledArgs, Env, OutP);
930 when NullPB => Apply_UPred(UPred_Nil, EvaledArgs, Env, OutP);
931 when ListPB => Apply_UPred(UPred_List, EvaledArgs, Env, OutP);
932 when AndB => Apply_AndOr(AndOr_And, EvaledArgs, Env, OutP);
933 when OrB => Apply_AndOr(AndOr_Or, EvaledArgs, Env, OutP);
934 when NotB => Apply_NotB(EvaledArgs, Env, OutP);
935 when LambdaB => Apply_LambdaB(Args, Env, OutP);
936 when LetB => Apply_LetB(Args, Env, OutP);
937 when ReverseB => Apply_ReverseB(EvaledArgs, Env, OutP);
938 when AppendB => Apply_AppendB(EvaledArgs, Env, OutP);
939 end case;
940 elsif AMem(Op).T = Closure then
941 -- We evaluate the argument list only if this is not a
942 -- meta-application (e.g. called by apply).
943 if not Meta then
944 Eval_List(Args, Env, EvaledArgs);
945 else
946 EvaledArgs := Args;
947 end if;
948 -- Apply closure.
949 Apply_Closure(Op, EvaledArgs, Env, OutP);
950 else
951 OutP := 0;
952 pragma Assert(False, "Trying to apply a non-function.");
953 end if;
954 end Apply_Func;
955
956 -- Evaluate a list element by element.
957 procedure Eval_List(List, Env : in MemPtr; OutP : out MemPtr) is
958 LP : MemPtr := List;
959 Result : MemPtr := 0;
960 Default : MemPtr := 0;
961 begin
962 -- eval elements one by one
963 while LP /= 0 loop
964 declare
965 TempP : MemPtr;
966 begin
967 -- degenerate case: cdr is neither list nor nil
968 exit when AMem(LP).T /= Cons;
969 -- eval current element in LP
970 Eval(Get_Car(AMem(LP)), Env, TempP);
971 -- cons result to Result
972 Alloc_Cons(TempP, Result, Result);
973 -- advance in LP
974 LP := Get_Cdr(AMem(LP));
975 end;
976 end loop;
977
978 -- also eval in the degenerate case
979 if LP /= 0 then
980 if AMem(LP).T /= Cons then
981 Eval(LP, Env, Default);
982 end if;
983 end if;
984
985 -- result is the reverse-in-place of our computation
986 Rev_In_Place(Result, Default, OutP);
987 end Eval_List;
988
989 -- Evaluate a given S-expression
990 procedure Eval(InP, Env : in MemPtr; OutP : out MemPtr) is
991 TempP, OpP, ArgsP : MemPtr;
992 begin
993 -- NIL.
994 if (InP = 0) then
995 OutP := 0;
996 return;
997 end if;
998
999 -- Non-NIL data.
1000 case AMem(InP).T is
1001 when Free => -- this is illegal
1002 pragma Assert(False, "Trying to eval free cell!");
1003 when Cons =>
1004 -- Eval car to get Op
1005 TempP := Get_Car(AMem(InP));
1006 Eval(TempP, Env, OpP);
1007 -- Get arglist
1008 ArgsP := Get_Cdr(AMem(InP));
1009 -- Apply op on arglist
1010 Apply_Func(OpP, ArgsP, Env, False, OutP);
1011 when Bool | Fixnum | Char | Builtin | Closure =>
1012 -- Constants are returned as they are.
1013 OutP := InP;
1014 when Symbol =>
1015 -- Lookup symbol value in Env.
1016 Lookup_Env_Or_Global(InP, Env, TempP);
1017 -- If found return it, otherwise report error.
1018 if TempP = 0 then
1019 Put("Not found: "); Dump_Cell(InP);
1020 pragma Assert(False, "No binding for symbol.");
1021 end if;
1022 OutP := Get_Cdr(AMem(TempP));
1023 end case;
1024 end Eval;
1025
1026 -- Prepend the elements of B to A, in reverse.
1027 procedure Rev_Append(A, B : in MemPtr; OutP : out MemPtr) is
1028 Acc : MemPtr := A;
1029 P : MemPtr := B;
1030 begin
1031 while P /= 0 loop
1032 exit when AMem(P).T /= Cons;
1033 Alloc_Cons(Get_Car(AMem(P)), Acc, Acc);
1034 P := Get_Cdr(AMem(P));
1035 end loop;
1036
1037 pragma Assert (P = 0, "Non-list argument to append");
1038
1039 OutP := Acc;
1040 end Rev_Append;
1041
1042 procedure Rev_In_Place(List, Default : in MemPtr; OutP : out MemPtr) is
1043 P : MemPtr := List;
1044 Result : MemPtr := Default;
1045 Temp : MemPtr;
1046 begin
1047 while P /= 0 loop
1048 Temp := Get_Cdr(AMem(P)); -- save cdr
1049 Set_Cdr(AMem(P), Result); -- put partial result in tail
1050 Result := P; -- update result
1051 P := Temp; -- get cdr
1052 end loop;
1053
1054 OutP := Result;
1055 end Rev_In_Place;
1056
1057 -- Return the actual boolean associated with a Lisp value.
1058 function Boolean_Value(P : MemPtr) return Boolean is
1059 begin
1060 -- Non-boolean values (including NIL) default to True. Boolean
1061 -- values get the value of Get_Bool;
1062 if P = 0 then
1063 return True;
1064 elsif AMem(P).T = Bool then
1065 return Get_Bool(AMem(P));
1066 else
1067 return True;
1068 end if;
1069 end Boolean_Value;
1070 end Evaler;