Commit Diff


commit - 871eec082d922a94977ef7f38b63e6678ffc92c5
commit + f9cd3e06af84c7a1ce4408cda517310d99e5a23e
blob - c05a341d1b5e223431a9baa834770897fcb445cb
blob + d3adcb028212b28d8438a72bb0af388cec6883bd
--- script.c
+++ script.c
@@ -39,9 +39,42 @@ static struct opstacks args   = TAILQ_HEAD_INITIALIZER
 static struct value	vstack[STACK_HEIGHT];
 static int		stackh;
 
+static struct envs envs = TAILQ_HEAD_INITIALIZER(envs);
+
 static struct value v_false = {.type = V_NUM, .v = {.num = 0}};
 static struct value v_true  = {.type = V_NUM, .v = {.num = 1}};
 
+static inline void
+popv(struct value *v)
+{
+	if (stackh == 0)
+		errx(1, "can't pop the stack: underflow");
+	memcpy(v, &vstack[--stackh], sizeof(*v));
+
+#if DEBUG
+	printf("popping "); pp_val(v); printf("\n");
+#endif
+}
+
+static inline void
+pushv(struct value *v)
+{
+	if (stackh == STACK_HEIGHT)
+		errx(1, "can't push the stack: overflow");
+
+#if DEBUG
+	printf("pushing "); pp_val(v); printf("\n");
+#endif
+
+	memcpy(&vstack[stackh++], v, sizeof(*v));
+}
+
+static inline void
+pushbool(int n)
+{
+	pushv(n ? &v_true : &v_false);
+}
+
 static inline struct opstack *
 pushstack(struct opstacks *stack)
 {
@@ -98,10 +131,103 @@ push(struct opstacks *stack, struct op *op)
 	ops->counter++;
 }
 
-void
+static inline void
+pushenv(void)
+{
+	struct env	*e;
+
+	e = xcalloc(1, sizeof(*e));
+	TAILQ_INSERT_HEAD(&envs, e, entry);
+}
+
+static inline struct env *
+currentenv(void)
+{
+	assert(!TAILQ_EMPTY(&envs));
+	return TAILQ_FIRST(&envs);
+}
+
+static void
+popenv(void)
+{
+	struct env	*e;
+	struct binding	*b, *tb;
+
+	e = currentenv();
+	TAILQ_REMOVE(&envs, e, entry);
+
+	TAILQ_FOREACH_SAFE(b, &e->bindings, entry, tb) {
+                free(b->name);
+		switch (b->val.type) {
+		case V_SYM:
+		case V_STR:
+			free(b->val.v.str);
+			break;
+		}
+		free(b);
+	}
+
+	free(e);
+}
+
+static inline int
+setvar(char *sym, struct op *op)
+{
+	struct binding	*b;
+	struct env	*e;
+	int		 ret;
+
+	if ((ret = eval(op)) != EVAL_OK)
+		return ret;
+
+	b = xcalloc(1, sizeof(*b));
+	b->name = sym;
+	popv(&b->val);
+
+	e = TAILQ_FIRST(&envs);
+	TAILQ_INSERT_HEAD(&e->bindings, b, entry);
+
+	return EVAL_OK;
+}
+
+static inline int
+getvar(const char *sym, struct value *v)
+{
+	struct env	*e;
+	struct binding	*b;
+
+	TAILQ_FOREACH(e, &envs, entry) {
+		TAILQ_FOREACH(b, &e->bindings, entry) {
+			if (!strcmp(sym, b->name)) {
+				memcpy(v, &b->val, sizeof(*v));
+				return EVAL_OK;
+			}
+		}
+	}
+
+	fprintf(stderr, "unbound variable %s\n", sym);
+	return EVAL_ERR;
+}
+
+int
 global_set(char *sym, struct op *op)
 {
-	assert(op->type == OP_LITERAL);
+	struct binding	*b;
+	struct env	*e;
+
+	/* TODO: check for duplicates */
+
+	if (op->type != OP_LITERAL)
+		return 0;
+
+	b = xcalloc(1, sizeof(*b));
+	b->name = sym;
+	memcpy(&b->val, &op->v.literal, sizeof(b->val));
+
+	e = TAILQ_LAST(&envs, envs);
+	TAILQ_INSERT_HEAD(&e->bindings, b, entry);
+
+	return 1;
 }
 
 struct op *
@@ -373,38 +499,7 @@ pp_block(struct op *op)
 		op = op->next;
 	}
 }
-
-static inline void
-popv(struct value *v)
-{
-	if (stackh == 0)
-		errx(1, "can't pop the stack: underflow");
-	memcpy(v, &vstack[--stackh], sizeof(*v));
 
-#if DEBUG
-	printf("popping "); pp_val(v); printf("\n");
-#endif
-}
-
-static inline void
-pushv(struct value *v)
-{
-	if (stackh == STACK_HEIGHT)
-		errx(1, "can't push the stack: overflow");
-
-#if DEBUG
-	printf("pushing "); pp_val(v); printf("\n");
-#endif
-
-	memcpy(&vstack[stackh++], v, sizeof(*v));
-}
-
-static inline void
-pushbool(int n)
-{
-	pushv(n ? &v_true : &v_false);
-}
-
 int
 eval(struct op *op)
 {
@@ -420,7 +515,9 @@ eval(struct op *op)
 
 	switch (op->type) {
 	case OP_ASSIGN:
-		printf("TODO: assignment\n");
+		ret = setvar(op->v.assign.name, op->v.assign.expr);
+		if (ret != EVAL_OK)
+			return ret;
 		break;
 
 	case OP_ASSERT:
@@ -436,19 +533,23 @@ eval(struct op *op)
 		break;
 
 	case OP_FUNCALL:
-		/* TODO: arity check! */
+                /* assume airity matches */
 
+		pushenv();
+
+		proc = op->v.funcall.proc;
 		for (i = 0; i < op->v.funcall.argc; ++i) {
 			t = &op->v.funcall.argv[i];
-			if ((ret = eval(t)) != EVAL_OK)
+			if ((ret = setvar(proc->args[i], t)) != EVAL_OK)
 				return ret;
 		}
 
-                proc = op->v.funcall.proc;
 		if (proc->nativefn != NULL)
 			proc->nativefn(i);
 		else if ((ret = eval(proc->body)) != EVAL_OK)
 				return ret;
+
+		popenv();
 		break;
 
 	case OP_LITERAL:
@@ -456,7 +557,9 @@ eval(struct op *op)
 		break;
 
 	case OP_VAR:
-		printf("TODO: load variable\n");
+                if ((ret = getvar(op->v.var, &a)) != EVAL_OK)
+			return ret;
+		pushv(&a);
 		break;
 
 	case OP_CAST:
@@ -651,12 +754,16 @@ run_test(struct test *t)
 int
 main(int argc, char **argv)
 {
+	struct env	*e;
 	struct test	*t;
 	int		 i, passed = 0, failed = 0, skipped = 0;
 
 	log_init(1, LOG_DAEMON);
 	log_setverbose(1);
 
+	/* prepare the global env */
+	pushenv();
+
 	add_builtin_proc("dummy", builtin_dummy);
 
 	for (i = 1; i < argc; ++i)
@@ -690,5 +797,7 @@ main(int argc, char **argv)
 	printf("failed %d\n", failed);
 	printf("skipped %d\n", skipped);
 
+	popenv();
+
 	return failed != 0;
 }
blob - 7ffb5c8f3b19f814fd697398a1862a7feaff2bfc
blob + 3c3af1629dee81f6acfee368cc87bb847f40a8c1
--- script.h
+++ script.h
@@ -86,6 +86,19 @@ struct op {
 	} v;
 };
 
+TAILQ_HEAD(bindings, binding);
+struct binding {
+	char			*name;
+	struct value		 val;
+	TAILQ_ENTRY(binding)	 entry;
+};
+
+TAILQ_HEAD(envs, env);
+struct env {
+	TAILQ_ENTRY(env)	 entry;
+	struct bindings		 bindings;
+};
+
 TAILQ_HEAD(opstacks, opstack);
 struct opstack {
 	TAILQ_ENTRY(opstack)	 entry;
@@ -119,7 +132,7 @@ enum {
 	EVAL_SKIP,
 };
 
-void		 global_set(char *, struct op *);
+int		 global_set(char *, struct op *);
 
 struct op	*newop(int);
 void		 free_op(struct op *);