altos/lisp: Evaluate macros once, then smash them into place
[fw/altos] / src / lisp / ao_lisp_eval.c
index f41962195ee7bd4227d1ce91e5cbc87138d807ae..3af567964deab8a4f87d13a26d13425e3508e901 100644 (file)
  * General Public License for more details.
  */
 
-#define DBG_EVAL 1
+#define DBG_EVAL 0
 #include "ao_lisp.h"
 #include <assert.h>
 
+const struct ao_lisp_type ao_lisp_stack_type;
+
 static int
 stack_size(void *addr)
 {
@@ -32,47 +34,50 @@ stack_mark(void *addr)
                ao_lisp_poly_mark(stack->values, 0);
                /* no need to mark values_tail */
                ao_lisp_poly_mark(stack->frame, 0);
-               ao_lisp_poly_mark(stack->macro_frame, 0);
+               ao_lisp_poly_mark(stack->list, 0);
                stack = ao_lisp_poly_stack(stack->prev);
-               if (ao_lisp_mark_memory(stack, sizeof (struct ao_lisp_stack)))
+               if (ao_lisp_mark_memory(&ao_lisp_stack_type, stack))
                        break;
        }
 }
 
-static const struct ao_lisp_type ao_lisp_stack_type;
-
 static void
 stack_move(void *addr)
 {
        struct ao_lisp_stack    *stack = addr;
 
        while (stack) {
-               void    *prev;
-               int     ret;
+               struct ao_lisp_stack    *prev;
+               int                     ret;
                (void) ao_lisp_poly_move(&stack->sexprs, 0);
                (void) ao_lisp_poly_move(&stack->values, 0);
                (void) ao_lisp_poly_move(&stack->values_tail, 0);
                (void) ao_lisp_poly_move(&stack->frame, 0);
-               (void) ao_lisp_poly_move(&stack->macro_frame, 0);
+               (void) ao_lisp_poly_move(&stack->list, 0);
                prev = ao_lisp_poly_stack(stack->prev);
-               ret = ao_lisp_move(&ao_lisp_stack_type, &prev);
+               if (!prev)
+                       break;
+               ret = ao_lisp_move_memory(&ao_lisp_stack_type, (void **) &prev);
                if (prev != ao_lisp_poly_stack(stack->prev))
                        stack->prev = ao_lisp_stack_poly(prev);
                if (ret)
                        break;
-               stack = ao_lisp_poly_stack(stack->prev);
+               stack = prev;
        }
 }
 
-static const struct ao_lisp_type ao_lisp_stack_type = {
+const struct ao_lisp_type ao_lisp_stack_type = {
        .size = stack_size,
        .mark = stack_mark,
-       .move = stack_move
+       .move = stack_move,
+       .name = "stack"
 };
 
 struct ao_lisp_stack           *ao_lisp_stack;
 ao_poly                                ao_lisp_v;
 
+struct ao_lisp_stack           *ao_lisp_stack_free_list;
+
 ao_poly
 ao_lisp_set_cond(struct ao_lisp_cons *c)
 {
@@ -85,50 +90,54 @@ static void
 ao_lisp_stack_reset(struct ao_lisp_stack *stack)
 {
        stack->state = eval_sexpr;
-       stack->macro = 0;
        stack->sexprs = AO_LISP_NIL;
        stack->values = AO_LISP_NIL;
        stack->values_tail = AO_LISP_NIL;
 }
 
-static void
-ao_lisp_frames_dump(void)
-{
-       struct ao_lisp_stack *s;
-       DBGI(".. current frame: "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
-       for (s = ao_lisp_stack; s; s = ao_lisp_poly_stack(s->prev)) {
-               DBGI(".. stack frame: "); DBG_POLY(s->frame); DBG("\n");
-               DBGI(".. macro frame: "); DBG_POLY(s->frame); DBG("\n");
-       }
-}
 
 static int
 ao_lisp_stack_push(void)
 {
-       DBGI("stack push\n");
-       DBG_IN();
-       struct ao_lisp_stack    *stack = ao_lisp_alloc(sizeof (struct ao_lisp_stack));
-       if (!stack)
-               return 0;
+       struct ao_lisp_stack    *stack;
+       if (ao_lisp_stack_free_list) {
+               stack = ao_lisp_stack_free_list;
+               ao_lisp_stack_free_list = ao_lisp_poly_stack(stack->prev);
+       } else {
+               stack = ao_lisp_alloc(sizeof (struct ao_lisp_stack));
+               if (!stack)
+                       return 0;
+       }
        stack->prev = ao_lisp_stack_poly(ao_lisp_stack);
        stack->frame = ao_lisp_frame_poly(ao_lisp_frame_current);
        stack->list = AO_LISP_NIL;
        ao_lisp_stack = stack;
        ao_lisp_stack_reset(stack);
-       ao_lisp_frames_dump();
+       DBGI("stack push\n");
+       DBG_FRAMES();
+       DBG_IN();
        return 1;
 }
 
 static void
 ao_lisp_stack_pop(void)
 {
+       ao_poly prev;
+
        if (!ao_lisp_stack)
                return;
-       ao_lisp_frame_current = ao_lisp_poly_frame(ao_lisp_stack->frame);
-       ao_lisp_stack = ao_lisp_poly_stack(ao_lisp_stack->prev);
+       prev = ao_lisp_stack->prev;
+       ao_lisp_stack->prev = ao_lisp_stack_poly(ao_lisp_stack_free_list);
+       ao_lisp_stack_free_list = ao_lisp_stack;
+
+       ao_lisp_stack = ao_lisp_poly_stack(prev);
+       if (ao_lisp_stack)
+               ao_lisp_frame_current = ao_lisp_poly_frame(ao_lisp_stack->frame);
+       else
+               ao_lisp_frame_current = NULL;
        DBG_OUT();
        DBGI("stack pop\n");
-       ao_lisp_frames_dump();
+       DBG_FRAMES();
 }
 
 static void
@@ -146,7 +155,7 @@ func_type(ao_poly func)
                return ao_lisp_error(AO_LISP_INVALID, "func is nil");
        switch (ao_lisp_poly_type(func)) {
        case AO_LISP_BUILTIN:
-               return ao_lisp_poly_builtin(func)->args;
+               return ao_lisp_poly_builtin(func)->args & AO_LISP_FUNC_MASK;
        case AO_LISP_LAMBDA:
                return ao_lisp_poly_lambda(func)->args;
        default:
@@ -246,33 +255,11 @@ static int
 ao_lisp_eval_val(void)
 {
        DBGI("val: "); DBG_POLY(ao_lisp_v); DBG("\n");
-       if (ao_lisp_stack->macro) {
-               DBGI("..macro %d\n", ao_lisp_stack->macro);
-               DBGI("..current frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
-               DBGI("..saved frame   "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
-               DBGI("..macro frame   "); DBG_POLY(ao_lisp_stack->macro_frame); DBG("\n");
-               DBGI("..sexprs       "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
-               DBGI("..values       "); DBG_POLY(ao_lisp_stack->values); DBG("\n");
-               /*
-                * Re-use the current stack to evaluate
-                * the value from the macro
-                */
-               ao_lisp_stack->state = eval_sexpr;
-//             assert(ao_lisp_stack->frame == ao_lisp_stack->macro_frame);
-               ao_lisp_frame_current = ao_lisp_poly_frame(ao_lisp_stack->macro_frame);
-               ao_lisp_stack->frame = ao_lisp_stack->macro_frame;
-               ao_lisp_stack->macro = 0;
-               ao_lisp_stack->macro_frame = AO_LISP_NIL;
-               ao_lisp_stack->sexprs = AO_LISP_NIL;
-               ao_lisp_stack->values = AO_LISP_NIL;
-               ao_lisp_stack->values_tail = AO_LISP_NIL;
-       } else {
-               /*
-                * Value computed, pop the stack
-                * to figure out what to do with the value
-                */
-               ao_lisp_stack_pop();
-       }
+       /*
+        * Value computed, pop the stack
+        * to figure out what to do with the value
+        */
+       ao_lisp_stack_pop();
        DBGI("..state %d\n", ao_lisp_stack ? ao_lisp_stack->state : -1);
        return 1;
 }
@@ -280,22 +267,25 @@ ao_lisp_eval_val(void)
 /*
  * A formal has been computed.
  *
- * If this is the first formal, then
- * check to see if we've got a lamda/lexpr or
- * macro/nlambda.
+ * If this is the first formal, then check to see if we've got a
+ * lamda/lexpr or macro/nlambda.
+ *
+ * For lambda/lexpr, go compute another formal.  This will terminate
+ * when the sexpr state sees nil.
  *
- * For lambda/lexpr, go compute another formal.
- * This will terminate when the sexpr state
- * sees nil.
+ * For macro/nlambda, we're done, so move the sexprs into the values
+ * and go execute it.
  *
- * For macro/nlambda, we're done, so move the
- * sexprs into the values and go execute it.
+ * Macros have an additional step of saving a stack frame holding the
+ * macro value execution context, which then gets the result of the
+ * macro to run
  */
 
 static int
 ao_lisp_eval_formal(void)
 {
-       ao_poly formal;
+       ao_poly                 formal;
+       struct ao_lisp_stack    *prev;
 
        DBGI("formal: "); DBG_POLY(ao_lisp_v); DBG("\n");
 
@@ -307,17 +297,33 @@ ao_lisp_eval_formal(void)
                        DBGI(".. lambda or lexpr\n");
                        break;
                case AO_LISP_FUNC_MACRO:
-                       ao_lisp_stack->macro = 1;
-                       DBGI(".. macro %d\n", ao_lisp_stack->macro);
-                       DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
-                       DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
-                       ao_lisp_stack->macro_frame = ao_lisp_stack->frame;
+                       /* Evaluate the result once more */
+                       ao_lisp_stack->state = eval_macro;
+                       if (!ao_lisp_stack_push())
+                               return 0;
+
+                       /* After the function returns, take that
+                        * value and re-evaluate it
+                        */
+                       prev = ao_lisp_poly_stack(ao_lisp_stack->prev);
+                       ao_lisp_stack->state = eval_sexpr;
+                       ao_lisp_stack->sexprs = prev->sexprs;
+
+                       DBGI(".. start macro\n");
+                       DBGI(".. sexprs       "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+                       DBGI(".. values       "); DBG_POLY(ao_lisp_stack->values); DBG("\n");
+                       DBG_FRAMES();
+
                        /* fall through ... */
                case AO_LISP_FUNC_NLAMBDA:
                        DBGI(".. nlambda or macro\n");
+
+                       /* use the raw sexprs as values */
                        ao_lisp_stack->values = ao_lisp_stack->sexprs;
                        ao_lisp_stack->values_tail = AO_LISP_NIL;
                        ao_lisp_stack->state = eval_exec;
+
+                       /* ready to execute now */
                        return 1;
                case -1:
                        return 0;
@@ -366,12 +372,15 @@ static int
 ao_lisp_eval_exec(void)
 {
        ao_poly v;
+       struct ao_lisp_builtin  *builtin;
+
        DBGI("exec: "); DBG_POLY(ao_lisp_v); DBG(" values "); DBG_POLY(ao_lisp_stack->values); DBG ("\n");
        ao_lisp_stack->sexprs = AO_LISP_NIL;
        switch (ao_lisp_poly_type(ao_lisp_v)) {
        case AO_LISP_BUILTIN:
                ao_lisp_stack->state = eval_val;
-               v = ao_lisp_func(ao_lisp_poly_builtin(ao_lisp_v)) (
+               builtin = ao_lisp_poly_builtin(ao_lisp_v);
+               v = ao_lisp_func(builtin) (
                        ao_lisp_poly_cons(ao_lisp_poly_cons(ao_lisp_stack->values)->cdr));
                DBG_DO(if (!ao_lisp_exception && ao_lisp_poly_builtin(ao_lisp_v)->func == builtin_set) {
                                struct ao_lisp_cons *cons = ao_lisp_poly_cons(ao_lisp_stack->values);
@@ -379,6 +388,10 @@ ao_lisp_eval_exec(void)
                                ao_poly val = ao_lisp_arg(cons, 2);
                                DBGI("set "); DBG_POLY(atom); DBG(" = "); DBG_POLY(val); DBG("\n");
                        });
+               builtin = ao_lisp_poly_builtin(ao_lisp_v);
+               if (builtin->args & AO_LISP_FUNC_FREE_ARGS)
+                       ao_lisp_cons_free(ao_lisp_poly_cons(ao_lisp_stack->values));
+
                ao_lisp_v = v;
                DBGI(".. result "); DBG_POLY(ao_lisp_v); DBG ("\n");
                DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
@@ -386,8 +399,7 @@ ao_lisp_eval_exec(void)
        case AO_LISP_LAMBDA:
                ao_lisp_stack->state = eval_sexpr;
                DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
-               ao_lisp_v = ao_lisp_lambda_eval(ao_lisp_poly_lambda(ao_lisp_v),
-                                               ao_lisp_poly_cons(ao_lisp_stack->values));
+               ao_lisp_v = ao_lisp_lambda_eval();
                DBGI(".. sexpr "); DBG_POLY(ao_lisp_v); DBG("\n");
                DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
                break;
@@ -397,14 +409,6 @@ ao_lisp_eval_exec(void)
        return 1;
 }
 
-static int
-ao_lisp_eval_lambda_done(void)
-{
-       DBGI("lambda_done: "); DBG_POLY(ao_lisp_v); DBG("\n");
-       DBG_STACK();
-       return 1;
-}
-
 /*
  * Start evaluating the next cond clause
  *
@@ -458,12 +462,11 @@ ao_lisp_eval_cond_test(void)
                struct ao_lisp_cons *car = ao_lisp_poly_cons(ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car);
                struct ao_lisp_cons *c = ao_lisp_poly_cons(car->cdr);
 
-               ao_lisp_stack->state = eval_val;
                if (c) {
+                       ao_lisp_stack->state = eval_sexpr;
                        ao_lisp_v = c->car;
-                       if (!ao_lisp_stack_push())
-                               return 0;
-               }
+               } else
+                       ao_lisp_stack->state = eval_val;
        } else {
                ao_lisp_stack->sexprs = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
                DBGI("next cond: "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
@@ -472,6 +475,104 @@ ao_lisp_eval_cond_test(void)
        return 1;
 }
 
+/*
+ * Evaluate a list of sexprs, returning the value from the last one.
+ *
+ * ao_lisp_progn records the list in stack->sexprs, so we just need to
+ * walk that list. Set ao_lisp_v to the car of the list and jump to
+ * eval_sexpr. When that's done, it will land in eval_val. For all but
+ * the last, leave a stack frame with eval_progn set so that we come
+ * back here. For the last, don't add a stack frame so that we can
+ * just continue on.
+ */
+static int
+ao_lisp_eval_progn(void)
+{
+       DBGI("progn: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+       DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+       DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+
+       if (!ao_lisp_stack->sexprs) {
+               ao_lisp_v = AO_LISP_NIL;
+               ao_lisp_stack->state = eval_val;
+       } else {
+               ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car;
+               ao_lisp_stack->sexprs = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
+               if (ao_lisp_stack->sexprs) {
+                       ao_lisp_stack->state = eval_progn;
+                       if (!ao_lisp_stack_push())
+                               return 0;
+               }
+               ao_lisp_stack->state = eval_sexpr;
+       }
+       return 1;
+}
+
+/*
+ * Conditionally execute a list of sexprs while the first is true
+ */
+static int
+ao_lisp_eval_while(void)
+{
+       DBGI("while: "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+       DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+       DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+
+       if (!ao_lisp_stack->sexprs) {
+               ao_lisp_v = AO_LISP_NIL;
+               ao_lisp_stack->state = eval_val;
+       } else {
+               ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->car;
+               ao_lisp_stack->state = eval_while_test;
+               if (!ao_lisp_stack_push())
+                       return 0;
+               ao_lisp_stack->state = eval_sexpr;
+       }
+       return 1;
+}
+
+/*
+ * Check the while condition, terminate the loop if nil. Otherwise keep going
+ */
+static int
+ao_lisp_eval_while_test(void)
+{
+       DBGI("while_test: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+       DBGI(".. frame "); DBG_POLY(ao_lisp_frame_poly(ao_lisp_frame_current)); DBG("\n");
+       DBGI(".. saved frame "); DBG_POLY(ao_lisp_stack->frame); DBG("\n");
+
+       if (ao_lisp_v) {
+               ao_lisp_v = ao_lisp_poly_cons(ao_lisp_stack->sexprs)->cdr;
+               if (ao_lisp_v)
+                       ao_lisp_v = ao_lisp_poly_cons(ao_lisp_v)->car;
+               ao_lisp_stack->state = eval_while;
+               if (!ao_lisp_stack_push())
+                       return 0;
+       }
+       else
+               ao_lisp_stack->state = eval_val;
+       return 1;
+}
+
+/*
+ * Replace the original sexpr with the macro expansion, then
+ * execute that
+ */
+static int
+ao_lisp_eval_macro(void)
+{
+       DBGI("macro: "); DBG_POLY(ao_lisp_v); DBG(" sexprs "); DBG_POLY(ao_lisp_stack->sexprs); DBG("\n");
+
+       if (ao_lisp_poly_type(ao_lisp_v) == AO_LISP_CONS) {
+               *ao_lisp_poly_cons(ao_lisp_stack->sexprs) = *ao_lisp_poly_cons(ao_lisp_v);
+               ao_lisp_v = ao_lisp_stack->sexprs;
+               DBGI("sexprs rewritten to: "); DBG_POLY(ao_lisp_v); DBG("\n");
+       }
+       ao_lisp_stack->sexprs = AO_LISP_NIL;
+       ao_lisp_stack->state = eval_sexpr;
+       return 1;
+}
+
 static int (*const evals[])(void) = {
        [eval_sexpr] = ao_lisp_eval_sexpr,
        [eval_val] = ao_lisp_eval_val,
@@ -479,25 +580,39 @@ static int (*const evals[])(void) = {
        [eval_exec] = ao_lisp_eval_exec,
        [eval_cond] = ao_lisp_eval_cond,
        [eval_cond_test] = ao_lisp_eval_cond_test,
+       [eval_progn] = ao_lisp_eval_progn,
+       [eval_while] = ao_lisp_eval_while,
+       [eval_while_test] = ao_lisp_eval_while_test,
+       [eval_macro] = ao_lisp_eval_macro,
 };
 
+/*
+ * Called at restore time to reset all execution state
+ */
+
+void
+ao_lisp_eval_clear_globals(void)
+{
+       ao_lisp_stack = NULL;
+       ao_lisp_frame_current = NULL;
+       ao_lisp_v = AO_LISP_NIL;
+}
+
+int
+ao_lisp_eval_restart(void)
+{
+       return ao_lisp_stack_push();
+}
+
 ao_poly
 ao_lisp_eval(ao_poly _v)
 {
-       static uint8_t been_here;
-
        ao_lisp_v = _v;
-       if (!been_here) {
-               been_here = 1;
-               ao_lisp_root_add(&ao_lisp_stack_type, &ao_lisp_stack);
-               ao_lisp_root_poly_add(&ao_lisp_v);
-       }
 
        if (!ao_lisp_stack_push())
                return AO_LISP_NIL;
 
        while (ao_lisp_stack) {
-//             DBG_STACK();
                if (!(*evals[ao_lisp_stack->state])() || ao_lisp_exception) {
                        ao_lisp_stack_clear();
                        return AO_LISP_NIL;