altos: Add lisp reader
[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 /*
18  * Non-recursive eval
19  *
20  * Plan: walk actuals, construct formals
21  *
22  * stack >  save  > actuals > actual_1
23  *           v         v
24  *         formals     .    > actual_2
25  */
26
27 static struct ao_lisp_cons      *stack;
28 static struct ao_lisp_cons      *actuals;
29 static struct ao_lisp_cons      *formals;
30 static struct ao_lisp_cons      *formals_tail;
31 static uint8_t been_here;
32
33 #if 0
34 #define DBG(...) printf(__VA_ARGS__)
35 #define DBG_CONS(a)     ao_lisp_cons_print(a)
36 #define DBG_POLY(a)     ao_lisp_poly_print(a)
37 #else
38 #define DBG(...)
39 #define DBG_CONS(a)
40 #define DBG_POLY(a)
41 #endif
42
43 ao_lisp_poly
44 ao_lisp_eval(ao_lisp_poly v)
45 {
46         struct ao_lisp_cons     *formal;
47         int                     cons = 0;
48
49         if (!been_here) {
50                 been_here = 1;
51                 ao_lisp_root_add(&ao_lisp_cons_type, &stack);
52                 ao_lisp_root_add(&ao_lisp_cons_type, &actuals);
53                 ao_lisp_root_add(&ao_lisp_cons_type, &formals);
54                 ao_lisp_root_add(&ao_lisp_cons_type, &formals_tail);
55         }
56         stack = 0;
57         actuals = 0;
58         formals = 0;
59         formals_tail = 0;
60         for (;;) {
61
62                 /* Build stack frames for each list */
63                 while (ao_lisp_poly_type(v) == AO_LISP_CONS) {
64                         if (v == AO_LISP_NIL)
65                                 break;
66
67                         /* Push existing frame on the stack */
68                         if (cons++) {
69                                 struct ao_lisp_cons *frame;
70
71                                 frame = ao_lisp_cons(ao_lisp_cons_poly(actuals), formals);
72                                 stack = ao_lisp_cons(ao_lisp_cons_poly(frame), stack);
73                         }
74                         actuals = ao_lisp_poly_cons(v);
75                         formals = NULL;
76                         formals_tail = NULL;
77                         v = actuals->car;
78
79                         DBG("start: stack"); DBG_CONS(stack); DBG("\n");
80                         DBG("start: actuals"); DBG_CONS(actuals); DBG("\n");
81                         DBG("start: formals"); DBG_CONS(formals); DBG("\n");
82                 }
83
84                 /* Evaluate primitive types */
85
86                 switch (ao_lisp_poly_type(v)) {
87                 case AO_LISP_INT:
88                 case AO_LISP_STRING:
89                         break;
90                 case AO_LISP_ATOM:
91                         v = ao_lisp_poly_atom(v)->val;
92                         break;
93                 }
94
95                 for (;;) {
96                         DBG("add formal: "); DBG_POLY(v); DBG("\n");
97
98                         formal = ao_lisp_cons(v, NULL);
99                         if (formals_tail)
100                                 formals_tail->cdr = formal;
101                         else
102                                 formals = formal;
103                         formals_tail = formal;
104                         actuals = actuals->cdr;
105
106                         DBG("formals: ");
107                         DBG_CONS(formals);
108                         DBG("\n");
109                         DBG("actuals: ");
110                         DBG_CONS(actuals);
111                         DBG("\n");
112
113                         /* Process all of the arguments */
114                         if (actuals) {
115                                 v = actuals->car;
116                                 DBG ("actual: "); DBG_POLY(v); DBG("\n");
117                                 break;
118                         }
119
120                         v = formals->car;
121
122                         /* Evaluate the resulting list */
123                         if (ao_lisp_poly_type(v) == AO_LISP_BUILTIN) {
124                                 struct ao_lisp_builtin *b = ao_lisp_poly_builtin(v);
125
126                                 v = b->func(formals->cdr);
127
128                                 DBG ("eval: ");
129                                 DBG_CONS(formals);
130                                 DBG(" -> ");
131                                 DBG_POLY(v);
132                                 DBG ("\n");
133                         } else {
134                                 DBG ("invalid eval\n");
135                         }
136
137                         if (--cons) {
138                                 struct ao_lisp_cons     *frame;
139
140                                 /* Pop the previous frame off the stack */
141                                 frame = ao_lisp_poly_cons(stack->car);
142                                 actuals = ao_lisp_poly_cons(frame->car);
143                                 formals = frame->cdr;
144
145                                 /* Recompute the tail of the formals list */
146                                 for (formal = formals; formal->cdr != NULL; formal = formal->cdr);
147                                 formals_tail = formal;
148
149                                 stack = stack->cdr;
150                                 DBG("stack pop: stack"); DBG_CONS(stack); DBG("\n");
151                                 DBG("stack pop: actuals"); DBG_CONS(actuals); DBG("\n");
152                                 DBG("stack pop: formals"); DBG_CONS(formals); DBG("\n");
153                         } else {
154                                 DBG("done func\n");
155                                 break;
156                         }
157                 }
158                 if (!cons)
159                         break;
160         }
161         return v;
162 }