altos: Add lambda support to lisp
[fw/altos] / src / lisp / ao_lisp_eval.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #include "ao_lisp.h"
16
17 #if 0
18 #define DBG(...) printf(__VA_ARGS__)
19 #define DBG_CONS(a)     ao_lisp_cons_print(ao_lisp_cons_poly(a))
20 #define DBG_POLY(a)     ao_lisp_poly_print(a)
21 #define OFFSET(a)       ((a) ? (int) ((uint8_t *) a - ao_lisp_pool) : -1)
22 #else
23 #define DBG(...)
24 #define DBG_CONS(a)
25 #define DBG_POLY(a)
26 #endif
27
28 struct ao_lisp_stack {
29         ao_poly                 next;
30         ao_poly                 actuals;
31         ao_poly                 formals;
32         ao_poly                 frame;
33         ao_poly                 cond;
34 };
35
36 static struct ao_lisp_stack *
37 ao_lisp_poly_stack(ao_poly p)
38 {
39         return ao_lisp_ref(p);
40 }
41
42 static ao_poly
43 ao_lisp_stack_poly(struct ao_lisp_stack *stack)
44 {
45         return ao_lisp_poly(stack, AO_LISP_OTHER);
46 }
47
48 static int
49 stack_size(void *addr)
50 {
51         (void) addr;
52         return sizeof (struct ao_lisp_stack);
53 }
54
55 static void
56 stack_mark(void *addr)
57 {
58         struct ao_lisp_stack    *stack = addr;
59         for (;;) {
60                 ao_lisp_poly_mark(stack->actuals);
61                 ao_lisp_poly_mark(stack->formals);
62                 ao_lisp_poly_mark(stack->frame);
63                 ao_lisp_poly_mark(stack->cond);
64                 stack = ao_lisp_poly_stack(stack->next);
65                 if (ao_lisp_mark_memory(stack, sizeof (struct ao_lisp_stack)))
66                         break;
67         }
68 }
69
70 static void
71 stack_move(void *addr)
72 {
73         struct ao_lisp_stack    *stack = addr;
74
75         for (;;) {
76                 struct ao_lisp_stack *next;
77                 stack->actuals = ao_lisp_poly_move(stack->actuals);
78                 stack->formals = ao_lisp_poly_move(stack->formals);
79                 stack->frame = ao_lisp_poly_move(stack->frame);
80                 stack->cond = ao_lisp_poly_move(stack->cond);
81                 next = ao_lisp_ref(stack->next);
82                 next = ao_lisp_move_memory(next, sizeof (struct ao_lisp_stack));
83                 stack->next = ao_lisp_stack_poly(next);
84                 stack = next;
85         }
86 }
87
88 static const struct ao_lisp_type ao_lisp_stack_type = {
89         .size = stack_size,
90         .mark = stack_mark,
91         .move = stack_move
92 };
93
94
95 static struct ao_lisp_stack     *stack;
96 static struct ao_lisp_cons      *actuals;
97 static struct ao_lisp_cons      *formals;
98 static struct ao_lisp_cons      *formals_tail;
99 static struct ao_lisp_cons      *cond;
100 struct ao_lisp_frame            *next_frame;
101 static uint8_t been_here;
102
103 ao_poly
104 ao_lisp_set_cond(struct ao_lisp_cons *c)
105 {
106         cond = c;
107         return AO_LISP_NIL;
108 }
109
110 static int
111 ao_lisp_stack_push(void)
112 {
113         struct ao_lisp_stack    *n = ao_lisp_alloc(sizeof (struct ao_lisp_stack));
114         if (!n)
115                 return 0;
116         n->next = ao_lisp_stack_poly(stack);
117         n->actuals = ao_lisp_cons_poly(actuals);
118         n->formals = ao_lisp_cons_poly(formals);
119         n->cond = ao_lisp_cons_poly(cond);
120         n->frame = ao_lisp_frame_poly(ao_lisp_frame_current);
121         DBG("push frame %d\n", OFFSET(ao_lisp_frame_current));
122         stack = n;
123         return 1;
124 }
125
126 static void
127 ao_lisp_stack_pop(void)
128 {
129         actuals = ao_lisp_poly_cons(stack->actuals);
130         formals = ao_lisp_poly_cons(stack->formals);
131         cond = ao_lisp_poly_cons(stack->cond);
132         ao_lisp_frame_current = ao_lisp_poly_frame(stack->frame);
133         DBG("pop frame %d\n", OFFSET(ao_lisp_frame_current));
134         formals_tail = 0;
135
136         /* Recompute the tail of the formals list */
137         if (formals) {
138                 struct ao_lisp_cons *formal;
139                 for (formal = formals; formal->cdr != AO_LISP_NIL; formal = ao_lisp_poly_cons(formal->cdr));
140                 formals_tail = formal;
141         }
142         stack = ao_lisp_poly_stack(stack->next);
143 }
144
145 static void
146 ao_lisp_stack_clear(void)
147 {
148         stack = 0;
149         actuals = formals = formals_tail = 0;
150         cond = 0;
151         ao_lisp_frame_current = 0;
152 }
153
154
155 static ao_poly
156 func_type(ao_poly func)
157 {
158         struct ao_lisp_cons     *cons;
159         struct ao_lisp_cons     *args;
160         int                     f;
161
162         DBG("func type "); DBG_POLY(func); DBG("\n");
163         if (func == AO_LISP_NIL)
164                 return ao_lisp_error(AO_LISP_INVALID, "func is nil");
165         if (ao_lisp_poly_type(func) != AO_LISP_CONS)
166                 return ao_lisp_error(AO_LISP_INVALID, "func is not list");
167         cons = ao_lisp_poly_cons(func);
168         if (!ao_lisp_check_argc(_ao_lisp_atom_lambda, cons, 3, 3))
169                 return AO_LISP_NIL;
170         if (!ao_lisp_check_argt(_ao_lisp_atom_lambda, cons, 0, AO_LISP_ATOM, 0))
171                 return AO_LISP_NIL;
172         if (!ao_lisp_check_argt(_ao_lisp_atom_lambda, cons, 1, AO_LISP_CONS, 1))
173                 return AO_LISP_NIL;
174         args = ao_lisp_poly_cons(ao_lisp_arg(cons, 1));
175         f = 0;
176         while (args) {
177                 if (ao_lisp_poly_type(args->car) != AO_LISP_ATOM) {
178                         return ao_lisp_error(ao_lisp_arg(cons, 0), "formal %d is not an atom", f);
179                 }
180                 args = ao_lisp_poly_cons(args->cdr);
181                 f++;
182         }
183         return ao_lisp_arg(cons, 0);
184 }
185
186 static int
187 ao_lisp_cons_length(struct ao_lisp_cons *cons)
188 {
189         int     len = 0;
190         while (cons) {
191                 len++;
192                 cons = ao_lisp_poly_cons(cons->cdr);
193         }
194         return len;
195 }
196
197 static ao_poly
198 ao_lisp_lambda(struct ao_lisp_cons *cons)
199 {
200         ao_poly                 type;
201         struct ao_lisp_cons     *lambda;
202         struct ao_lisp_cons     *args;
203         int                     args_wanted;
204         int                     args_provided;
205
206         lambda = ao_lisp_poly_cons(ao_lisp_arg(cons, 0));
207         DBG("lambda "); DBG_CONS(lambda); DBG("\n");
208         type = ao_lisp_arg(lambda, 0);
209         args = ao_lisp_poly_cons(ao_lisp_arg(lambda, 1));
210
211         args_wanted = ao_lisp_cons_length(args);
212
213         /* Create a frame to hold the variables
214          */
215         if (type == _ao_lisp_atom_lambda)
216                 args_provided = ao_lisp_cons_length(cons) - 1;
217         else
218                 args_provided = 1;
219         if (args_wanted != args_provided)
220                 return ao_lisp_error(AO_LISP_INVALID, "need %d args, not %d", args_wanted, args_provided);
221         next_frame = ao_lisp_frame_new(args_wanted, 0);
222         DBG("new frame %d\n", OFFSET(next_frame));
223         switch (type) {
224         case _ao_lisp_atom_lambda: {
225                 int                     f;
226                 struct ao_lisp_cons     *vals = ao_lisp_poly_cons(cons->cdr);
227
228                 for (f = 0; f < args_wanted; f++) {
229                         next_frame->vals[f].atom = args->car;
230                         next_frame->vals[f].val = vals->car;
231                         args = ao_lisp_poly_cons(args->cdr);
232                         vals = ao_lisp_poly_cons(vals->cdr);
233                 }
234                 break;
235         }
236         case _ao_lisp_atom_lexpr:
237         case _ao_lisp_atom_nlambda:
238                 next_frame->vals[0].atom = args->car;
239                 next_frame->vals[0].val = cons->cdr;
240                 break;
241         case _ao_lisp_atom_macro:
242                 next_frame->vals[0].atom = args->car;
243                 next_frame->vals[0].val = ao_lisp_cons_poly(cons);
244                 break;
245         }
246         return ao_lisp_arg(lambda, 2);
247 }
248
249 ao_poly
250 ao_lisp_eval(ao_poly v)
251 {
252         struct ao_lisp_cons     *formal;
253         int                     cons = 0;
254
255         if (!been_here) {
256                 been_here = 1;
257                 ao_lisp_root_add(&ao_lisp_stack_type, &stack);
258                 ao_lisp_root_add(&ao_lisp_cons_type, &actuals);
259                 ao_lisp_root_add(&ao_lisp_cons_type, &formals);
260                 ao_lisp_root_add(&ao_lisp_cons_type, &formals_tail);
261         }
262         stack = 0;
263         actuals = 0;
264         formals = 0;
265         formals_tail = 0;
266         cond = 0;
267         for (;;) {
268
269         restart:
270                 if (cond) {
271                         if (cond->car == AO_LISP_NIL) {
272                                 cond = AO_LISP_NIL;
273                                 v = AO_LISP_NIL;
274                         } else {
275                                 if (ao_lisp_poly_type(cond->car) != AO_LISP_CONS) {
276                                         ao_lisp_error(AO_LISP_INVALID, "malformed cond");
277                                         goto bail;
278                                 }
279                                 v = ao_lisp_poly_cons(cond->car)->car;
280                         }
281                 }
282
283                 /* Build stack frames for each list */
284                 while (ao_lisp_poly_type(v) == AO_LISP_CONS) {
285                         if (v == AO_LISP_NIL)
286                                 break;
287
288                         /* Push existing bits on the stack */
289                         if (cons++)
290                                 if (!ao_lisp_stack_push())
291                                         goto bail;
292
293                         actuals = ao_lisp_poly_cons(v);
294                         formals = NULL;
295                         formals_tail = NULL;
296                         cond = NULL;
297
298                         v = actuals->car;
299
300 //                      DBG("start: stack"); DBG_CONS(stack); DBG("\n");
301 //                      DBG("start: actuals"); DBG_CONS(actuals); DBG("\n");
302 //                      DBG("start: formals"); DBG_CONS(formals); DBG("\n");
303                 }
304
305                 /* Evaluate primitive types */
306
307                 DBG ("actual: "); DBG_POLY(v); DBG("\n");
308
309                 switch (ao_lisp_poly_type(v)) {
310                 case AO_LISP_INT:
311                 case AO_LISP_STRING:
312                         break;
313                 case AO_LISP_ATOM:
314                         v = ao_lisp_atom_get(v);
315                         break;
316                 }
317
318                 while (cons) {
319                         DBG("add formal: "); DBG_POLY(v); DBG("\n");
320
321                         /* We've processed the first element of the list, go check
322                          * what kind of function we've got
323                          */
324                         if (formals == NULL) {
325                                 if (ao_lisp_poly_type(v) == AO_LISP_BUILTIN) {
326                                         struct ao_lisp_builtin *b = ao_lisp_poly_builtin(v);
327                                         switch (b->args) {
328                                         case AO_LISP_NLAMBDA:
329                                                 formals = actuals;
330                                                 goto eval;
331
332                                         case AO_LISP_MACRO:
333                                                 v = ao_lisp_func(b)(ao_lisp_poly_cons(actuals->cdr));
334                                                 DBG("macro "); DBG_POLY(ao_lisp_cons_poly(actuals));
335                                                 DBG(" -> "); DBG_POLY(v);
336                                                 DBG("\n");
337                                                 if (ao_lisp_poly_type(v) != AO_LISP_CONS) {
338                                                         ao_lisp_error(AO_LISP_INVALID, "macro didn't return list");
339                                                         goto bail;
340                                                 }
341                                                 /* Reset frame to the new list */
342                                                 actuals = ao_lisp_poly_cons(v);
343                                                 v = actuals->car;
344                                                 goto restart;
345                                         }
346                                 } else {
347                                         switch (func_type(v)) {
348                                         case _ao_lisp_atom_lambda:
349                                         case _ao_lisp_atom_lexpr:
350                                                 break;
351                                         case _ao_lisp_atom_nlambda:
352                                                 formals = actuals;
353                                                 goto eval;
354                                         case _ao_lisp_atom_macro:
355                                                 break;
356                                         default:
357                                                 ao_lisp_error(AO_LISP_INVALID, "operator is not a function");
358                                                 goto bail;
359                                         }
360                                 }
361                         }
362
363                         formal = ao_lisp_cons_cons(v, NULL);
364                         if (formals_tail)
365                                 formals_tail->cdr = ao_lisp_cons_poly(formal);
366                         else
367                                 formals = formal;
368                         formals_tail = formal;
369                         actuals = ao_lisp_poly_cons(actuals->cdr);
370
371                         DBG("formals: ");
372                         DBG_CONS(formals);
373                         DBG("\n");
374                         DBG("actuals: ");
375                         DBG_CONS(actuals);
376                         DBG("\n");
377
378                         /* Process all of the arguments */
379                         if (actuals) {
380                                 v = actuals->car;
381                                 break;
382                         }
383
384                         v = formals->car;
385
386                 eval:
387
388                         /* Evaluate the resulting list */
389                         if (ao_lisp_poly_type(v) == AO_LISP_BUILTIN) {
390                                 struct ao_lisp_builtin *b = ao_lisp_poly_builtin(v);
391
392                                 v = ao_lisp_func(b) (ao_lisp_poly_cons(formals->cdr));
393
394                                 DBG ("eval: ");
395                                 DBG_CONS(formals);
396                                 DBG(" -> ");
397                                 DBG_POLY(v);
398                                 DBG ("\n");
399                                 if (ao_lisp_exception)
400                                         goto bail;
401
402                                 if (cond)
403                                         goto restart;
404                         } else {
405                                 v = ao_lisp_lambda(formals);
406                                 if (ao_lisp_exception)
407                                         goto bail;
408                         }
409
410                         --cons;
411                         if (cons) {
412                                 ao_lisp_stack_pop();
413 //                              DBG("stack pop: stack"); DBG_CONS(stack); DBG("\n");
414 //                              DBG("stack pop: actuals"); DBG_CONS(actuals); DBG("\n");
415 //                              DBG("stack pop: formals"); DBG_CONS(formals); DBG("\n");
416                         } else {
417                                 actuals = 0;
418                                 formals = 0;
419                                 formals_tail = 0;
420                                 ao_lisp_frame_current = 0;
421                         }
422                         if (next_frame) {
423                                 ao_lisp_frame_current = next_frame;
424                                 DBG("next frame %d\n", OFFSET(next_frame));
425                                 next_frame = 0;
426                                 goto restart;
427                         }
428                         if (cond) {
429                                 if (v) {
430                                         v = ao_lisp_poly_cons(cond->car)->cdr;
431                                         if (v != AO_LISP_NIL) {
432                                                 v = ao_lisp_poly_cons(v)->car;
433                                                 goto restart;
434                                         }
435                                 } else {
436                                         cond = ao_lisp_poly_cons(cond->cdr);
437                                         goto restart;
438                                 }
439                         }
440                 }
441                 if (!cons)
442                         break;
443         }
444         DBG("leaving frame at %d\n", OFFSET(ao_lisp_frame_current));
445         return v;
446 bail:
447         ao_lisp_stack_clear();
448         return AO_LISP_NIL;
449 }