vm.c

(6 * 7)
   BIN *
      VAL: 6
      VAL: 7
   BIN: 42
==> VAL 42
(1 + (2 * 3))
   BIN +
      VAL: 1
      BIN *
         VAL: 2
         VAL: 3
      BIN: 6
   BIN: 7
==> VAL 7
(if (6 < 7) then 6 else 7)
   TRI
      BIN <
         VAL: 6
         VAL: 7
      BIN: 1
   TRI: LEFT
==> VAL 6
(1 + (if (6 < 7) then (2 * 3) else (1 + (2 * 3))))
   BIN +
      VAL: 1
      TRI
         BIN <
            VAL: 6
            VAL: 7
         BIN: 1
      TRI: LEFT
      BIN *
         VAL: 2
         VAL: 3
      BIN: 6
   BIN: 7
==> VAL 7
(if (6 < 7) then 1 else a)
   TRI
      BIN <
         VAL: 6
         VAL: 7
      BIN: 1
   TRI: LEFT
==> VAL 1
(let a = 7 in (if (6 < 7) then 1 else a))
   LET
      TRI
         BIN <
            VAL: 6
            VAL: 7
         BIN: 1
      TRI: LEFT
   LET:
==> VAL 1
(let a = 6, b = 7 in (a * b))
   LET
      BIN *
         VAR a
         VAR: 6
         VAR b
         VAR: 7
      BIN: 42
   LET:
==> VAL 42

#include <stdio.h>
#include <stdlib.h>
enum expr_type { END, ERR, VAL, VAR, BIN, TRI, LET, };

enum bin_op { MUL, ADD, LT, };
static const char* const bin_op_name[] = {
   [MUL] = "*",
   [ADD] = "+",
   [LT]  = "<",
};

struct expr;
struct bin {
   int op;
   const struct expr *left, *right;
};
struct tri {
   const struct expr *cond, *left, *right;
};
struct let_bind {
   const struct expr *left, *right;
   const struct let_bind *next;
};
struct let {
   const struct let_bind *bind;
   const struct expr *in;
};
struct expr {
   int type;
   union {
      int val;
      int var; // index to var array
      struct bin bin;
      struct tri tri;
      struct let let;
   };
};

static const struct expr a = {
   .type = VAL,
   .val  = 6,
}, b = {
   .type = VAL,
   .val  = 7,
}, e0 = {
   .type = BIN,
   .bin = { MUL, &a, &b, }, // 6 * 7
}, c = {
   .type = VAL,
   .val  = 1,
}, d = {
   .type = VAL,
   .val  = 2,
}, e = {
   .type = VAL,
   .val  = 3,
}, f = {
   .type = BIN,
   .bin = { MUL, &d, &e, }, // 2 * 3
}, e1 = {
   .type = BIN,
   .bin = { ADD, &c, &f, }, // 1 + 2 * 3
}, g = {
   .type = BIN,
   .bin = { LT, &a, &b, }, // 6 < 7
}, e2 = {
   .type = TRI,
   .tri = { &g, &a, &b, }, // max(6, 7) = if (6 < 7) then 6 else 7
}, h = {
   .type = TRI,
   .tri = { &g, &f, &e1, }, // if (6 < 7) then (2 * 3) else (1 + 2 * 3)
}, e3 = {
   .type = BIN,
   .bin = { ADD, &c, &h, }, // 1 + (if (6 < 7) then (2 * 3) else (1 + 2 * 3))
}, i = {
   .type = VAR,
   .var = 0,
}, e4 = {
   .type = TRI,
   .tri = { &g, &c, &i, }, // if (6 < 7) then (1) else (a)
}, j = {
   .type = VAR,
   .var = 1,
}, k = {
   .type = BIN,
   .bin = { MUL, &i, &j, }, // 2 * 3
}, ee = {
   .type = END,
};
static const struct let_bind
   lb_a = { &i, &b, NULL, },
   lb_b = { &j, &b, NULL, },
   lb_c = { &i, &a, &lb_b, };
static const struct expr e5 = {
   .type = LET,
   .let = { .bind = &lb_a, .in = &e4, }
}, e6 = {
   .type = LET,
   .let = { .bind = &lb_c, .in = &k, }
};

static const struct expr x[] = {
   e0, e1, e2, e3, e4, e5, e6, ee,
};

static const struct expr err = { .type = ERR, };

static void
die(const char *s)
{
   printf("die: %s\n", s);
   exit(1);
}

static const struct let_bind *var = NULL;
static void
set_var(const struct let_bind *bind)
{
   struct let_bind *t = malloc(sizeof *t);
   if (!t) die("malloc");
   *t = *bind;
   t->next = var;
   var = t;

//   for (const struct let_bind *i = var; i; i = i->next) {
//      if (i->left->type != VAR) continue;
//      printf("set_var: %c = %d\n", 'a' + i->left->var, i->right->val); }
}
static void
del_var(void)
{
// printf("del_var: %c = %d\n", 'a' + var->left->var, var->right->val);
   if (!var) die("del_var");
   struct let_bind *t = (struct let_bind *)var;
   var = var->next;
   free(t);
}
static struct expr
ref_var(const struct expr e)
{
   if (e.type != VAR) return err;
   for (const struct let_bind *i = var; i; i = i->next) {
      if (i->left->type != VAR) continue;
// printf("ref_var: %c = %d\n", 'a' + i->left->var, i->right->val);
      if (i->left->var != e.var) continue;
      return *i->right; }
}

static const struct expr
bincalc(const int bin, const struct expr a, const struct expr b)
{
   struct expr r;

   if (a.type != VAL || b.type != VAL) {
      r.type = ERR; return r; }

   switch (bin) {
   case MUL: r.type = VAL; r.val = a.val * b.val; break;
   case ADD: r.type = VAL; r.val = a.val + b.val; break;
   case LT:  r.type = VAL; r.val = a.val < b.val; break;
   default:  r.type = ERR;
   }
   return r;
}

static const struct expr eval(struct expr e);

static const struct expr
eval_eager(const struct expr e)
{
   const struct expr r = eval(e);

   if (r.type == VAL) return r;
   if (r.type == ERR) return r;

   return eval_eager(r);
}

static int n_indent;
static void indent(void) { n_indent++; }
static void dedent(void) { n_indent--; }
static void put_indent(void) {
   for (int i = 0; i < n_indent; i++) {
      putchar(' ');
      putchar(' ');
      putchar(' '); }
}

static const struct expr
eval(const struct expr e)
{
   struct expr r, t0, t1;
   indent();

   switch (e.type) {
   case VAL:
      put_indent(); printf("VAL: %d\n", e.val);
      r = e; break;
   case VAR:
      put_indent(); printf("VAR %c\n", 'a' + e.var);
      r = ref_var(e);
      put_indent(); printf("VAR: %d\n", r.val);
      break;
   case BIN:
      put_indent(); printf("BIN %s\n", bin_op_name[e.bin.op]);
      t0 = eval_eager(*e.bin.left);
      t1 = eval_eager(*e.bin.right);
      r = bincalc(e.bin.op, t0, t1);
      put_indent(); printf("BIN: %d\n", r.val);
      break;
   case TRI:
      put_indent(); puts("TRI");
      t0 = eval_eager(*e.tri.cond);
      if (t0.type != VAL) { r = err; break; }
      put_indent(); printf("TRI: %s\n", t0.val ? "LEFT" : "RIGHT");
      r = t0.val ? *e.bin.left: *e.bin.right; break;
   case LET: {
      int n_let = 0;
      put_indent(); puts("LET");
      for (const struct let_bind *i = e.let.bind; i; i = i->next) {
         set_var(i);
         n_let++; }
      r = eval_eager(*e.let.in);
      for (int i = 0; i < n_let; i++) del_var();
      put_indent(); puts("LET:");
      break; }
   default:
      put_indent(); puts("ERR");
      r = err; break; }

   dedent();
   return r;
}

static void
show(const struct expr e)
{
   switch (e.type) {
   case VAL:
      printf("%d", e.val); break;
   case VAR:
      printf("%c", 'a' + e.var); break;
   case BIN:
      printf("(");
      show(*e.bin.left);
      printf(" %s ", bin_op_name[e.bin.op]);
      show(*e.bin.right);
      printf(")"); break;
   case TRI:
      printf("(if ");
      show(*e.tri.cond);
      printf(" then ");
      show(*e.tri.left);
      printf(" else ");
      show(*e.tri.right);
      printf(")"); break;
   case LET:
      printf("(let ");
      for (const struct let_bind *i = e.let.bind; i; i = i->next) {
         show(*i->left);
         printf(" = ");
         show(*i->right);
         if (i->next) printf(", "); }
      printf(" in ");
      show(*e.let.in);
      printf(")"); break;
   default:
      printf(" *** ERR *** "); }
}

int
main(void)
{
   for (const struct expr *p = x; p->type != END; p++) {
      show(*p); putchar('\n');
      const struct expr y = eval_eager(*p);
      if (y.type == VAL) printf("==> VAL %d\n", y.val);
      if (y.type == ERR) printf("==> ERR\n"); }
   return 0;
}