aboutsummaryrefslogtreecommitdiff
path: root/src/interpret.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpret.c')
-rw-r--r--src/interpret.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/src/interpret.c b/src/interpret.c
new file mode 100644
index 0000000..580303e
--- /dev/null
+++ b/src/interpret.c
@@ -0,0 +1,407 @@
+#include <math.h>
+#include <stdio.h>
+
+#include <posthaste/execute.h>
+#include <posthaste/lower.h>
+#include <posthaste/utils.h>
+#include <posthaste/date.h>
+#include <posthaste/vec.h>
+
+static int64_t load(struct loc l, size_t sp, struct vec *stack,
+ struct vec *globals)
+{
+ /* extra runtime branches is another reason this bytecode instruction
+ * set is slow to interpret. To get rid of this if (), one
+ * option would be to add LOAD/STORE_GLOBAL instructions that move data
+ * between the global array and the stack, but this would make the JIT
+ * slower */
+ if (l.l)
+ return vect_at(int64_t, *stack, l.o + sp);
+
+ return vect_at(int64_t, *globals, l.o);
+}
+
+static void store(struct loc l, int64_t v, size_t sp, struct vec *stack,
+ struct vec *globals)
+{
+ if (l.l) {
+ vect_at(int64_t, *stack, l.o + sp) = v;
+ return;
+ }
+
+ vect_at(int64_t, *globals, l.o) = v;
+}
+
+static int64_t interpret_func(struct fn *f, struct vec *args, size_t sp,
+ struct vec *stack, struct vec *globals)
+{
+ /* move args to formal locations */
+ size_t formal = 0;
+ foreach_vec(ai, *args) {
+ int64_t a = vect_at(int64_t, *args, ai);
+ vect_at(int64_t, *stack, sp + formal) = a;
+ formal += 1;
+ }
+
+ vec_reset(args);
+
+ /* load/store from location, selecting between global array and stack. Assumes
+ * parameter names, generally frowned upon. */
+#define get(l) \
+ load(l, sp, stack, globals)
+
+#define put(l, v) \
+ store(l, v, sp, stack, globals)
+
+ /* use computed gotos to avoid extra overhead of loops/switch statements.
+ * Usually I'm not a huge fan of massive single functions and from a
+ * stylistic viewpoint maybe prefer breaking each instruction out into
+ * its own procedure, but this at least saves a bit of typing */
+#define LINK(x) [x] = &&CASE_##x
+ static void *labels[] = {
+ LINK(LABEL),
+ LINK(CALL),
+ LINK(MOVE),
+ LINK(ADD),
+ LINK(SUB),
+ LINK(MUL),
+ LINK(DIV),
+ LINK(ARG),
+ LINK(RETVAL),
+ LINK(PRINT_STRING),
+ LINK(PRINT_INT),
+ LINK(PRINT_BOOL),
+ LINK(PRINT_DATE),
+ LINK(PRINT_NEWLINE),
+ LINK(PRINT_SPACE),
+ LINK(DATE_ADD),
+ LINK(DATE_SUB),
+ LINK(DATE_DIFF),
+ LINK(STORE_DAY),
+ LINK(STORE_MONTH),
+ LINK(STORE_YEAR),
+ LINK(LOAD_DAY),
+ LINK(LOAD_MONTH),
+ LINK(LOAD_YEAR),
+ LINK(LOAD_WEEKDAY),
+ LINK(LOAD_WEEKNUM),
+ LINK(TODAY),
+ LINK(RET),
+ LINK(STOP),
+ LINK(CONST),
+ LINK(EQ),
+ LINK(LT),
+ LINK(NEG),
+ LINK(B),
+ LINK(BZ),
+ LINK(J),
+ };
+#undef LINK
+
+#define JUMP() i = vect_at(struct insn, insns, pc); goto *labels[i.k];
+#define DISPATCH() pc++; JUMP();
+
+#define CASE(x) CASE_##x
+
+ size_t pc = 0;
+ int64_t retval = 0;
+ struct vec insns = f->insns;
+ struct insn i = {0};
+ /* jump to first instruction */
+ JUMP();
+
+ CASE(STOP) : {return 0;}
+ CASE(RET) : {return get(i.i0);}
+
+ CASE(LABEL) : {DISPATCH();}
+ CASE(RETVAL) : {put(i.o, retval); DISPATCH();}
+
+ CASE(J) : {pc = i.v; JUMP();}
+
+ CASE(B) : {
+ int64_t i0 = get(i.i0);
+ if (i0) {
+ pc = i.v;
+ JUMP();
+ }
+
+ DISPATCH();
+ }
+
+ CASE(BZ) : {
+ int64_t i0 = get(i.i0);
+ if (!i0) {
+ pc = i.v;
+ JUMP();
+ }
+
+ DISPATCH();
+ }
+
+ CASE(ARG) : {
+ int64_t a = get(i.i0);
+ vect_append(int64_t, *args, &a);
+ DISPATCH();
+ }
+
+ CASE(CALL) : {
+ struct fn *cf = find_fn(i.v);
+ assert(cf);
+
+ retval = interpret_func(cf, args, sp + f->max_sp + 1, stack,
+ globals);
+ DISPATCH();
+ }
+
+ CASE(MOVE) : {
+ int64_t i0 = get(i.i0);
+ put(i.o, i0);
+ DISPATCH();
+ }
+
+
+ CASE(ADD) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 + i1;
+ put(i.o, o);
+ DISPATCH();
+ }
+
+ CASE(SUB) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 - i1;
+ put(i.o, o);
+ DISPATCH();
+ }
+
+ CASE(MUL) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 * i1;
+ put(i.o, o);
+ DISPATCH();
+ }
+
+ CASE(DIV) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t o = i0 / i1;
+ put(i.o, o);
+ DISPATCH();
+ }
+
+ CASE(CONST) : {
+ put(i.o, i.v);
+ DISPATCH();
+ }
+
+ CASE(PRINT_DATE) : {
+ int64_t i0 = get(i.i0);
+ char str[11] = {0};
+ date_to_string(str, i0);
+ printf("%s", str);
+ DISPATCH();
+ }
+
+ CASE(PRINT_INT) : {
+ int64_t i0 = get(i.i0);
+ printf("%lli", (long long)i0);
+ DISPATCH();
+ }
+
+ CASE(PRINT_BOOL) : {
+ int64_t i0 = get(i.i0);
+ printf("%s", i0 ? "true" : "false");
+ DISPATCH();
+ }
+
+ CASE(PRINT_STRING) : {
+ int64_t i0 = get(i.i0);
+ printf("%s", (const char *)i0);
+ DISPATCH();
+ }
+
+ CASE(PRINT_NEWLINE) : {
+ putchar('\n');
+ DISPATCH();
+ }
+
+ CASE(PRINT_SPACE) : {
+ putchar(' ');
+ DISPATCH();
+ }
+
+ CASE(LOAD_DAY) : {
+ int64_t i0 = get(i.i0);
+ unsigned day = 0;
+ date_split((ph_date_t)i0, NULL, NULL, &day);
+ put(i.o, (int64_t)day);
+ DISPATCH();
+ }
+
+ CASE(LOAD_MONTH) : {
+ int64_t i0 = get(i.i0);
+ unsigned month = 0;
+ date_split((ph_date_t)i0, NULL, &month, NULL);
+ put(i.o, (int64_t)month);
+ DISPATCH();
+ }
+
+ CASE(LOAD_YEAR) : {
+ int64_t i0 = get(i.i0);
+ unsigned year = 0;
+ date_split((ph_date_t)i0, &year, NULL, NULL);
+ put(i.o, (int64_t)year);
+ DISPATCH();
+ }
+
+ CASE(LOAD_WEEKDAY) : {
+ int64_t i0 = get(i.i0);
+ struct tm time = tm_from_date((ph_date_t)i0);
+ put(i.o, (int64_t)time.tm_wday);
+ DISPATCH();
+ }
+
+ CASE(LOAD_WEEKNUM) : {
+ int64_t i0 = get(i.i0);
+ struct tm time = tm_from_date((ph_date_t)i0);
+ put(i.o, time.tm_yday / 7);
+ DISPATCH();
+ }
+
+ CASE(STORE_DAY) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ unsigned year = 0;
+ unsigned month = 0;
+ date_split((ph_date_t)i0, &year, &month, NULL);
+ ph_date_t date = date_from_numbers(year, month, i1);
+ put(i.o, (int64_t)date);
+ DISPATCH();
+ }
+
+ CASE(STORE_MONTH) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ unsigned year = 0;
+ unsigned day = 0;
+ date_split((ph_date_t)i0, &year, NULL, &day);
+ ph_date_t date = date_from_numbers(year, i1, day);
+ put(i.o, (int64_t)date);
+ DISPATCH();
+ }
+
+ CASE(STORE_YEAR) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ unsigned month = 0;
+ unsigned day = 0;
+ date_split((ph_date_t)i0, NULL, &month, &day);
+ ph_date_t date = date_from_numbers(i1, month, day);
+ put(i.o, (int64_t)date);
+ DISPATCH();
+ }
+
+ CASE(DATE_ADD) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ struct tm time = tm_from_date((ph_date_t)i0);
+ time.tm_mday += i1;
+ mktime(&time);
+
+ ph_date_t date = date_from_tm(time);
+ put(i.o, (int64_t)date);
+ DISPATCH();
+ }
+
+ CASE(DATE_SUB) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ struct tm time = tm_from_date((ph_date_t)i0);
+ time.tm_mday -= i1;
+ mktime(&time);
+
+ ph_date_t date = date_from_tm(time);
+ put(i.o, (int64_t)date);
+ DISPATCH();
+ }
+
+ CASE(DATE_DIFF) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+
+ struct tm time0 = tm_from_date((ph_date_t)i0);
+ struct tm time1 = tm_from_date((ph_date_t)i1);
+
+ /* close enough at least */
+ time_t t0 = mktime(&time0);
+ time_t t1 = mktime(&time1);
+ double seconds = difftime(t0, t1);
+ int64_t days = round(seconds / 86400);
+ put(i.o, days);
+ DISPATCH();
+ }
+
+ CASE(TODAY) : {
+ ph_date_t date = current_date();
+ put(i.o, (int64_t)date);
+ DISPATCH();
+ }
+
+ CASE(EQ) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t b = i0 == i1;
+ put(i.o, b);
+ DISPATCH();
+ }
+
+ CASE(LT) : {
+ int64_t i0 = get(i.i0);
+ int64_t i1 = get(i.i1);
+ int64_t b = i0 < i1;
+ put(i.o, b);
+ DISPATCH();
+ }
+
+ CASE(NEG) : {
+ int64_t i0 = get(i.i0);
+ put(i.o, -i0);
+ DISPATCH();
+ }
+
+#undef CASE
+#undef JUMP
+#undef DISPATCH
+#undef get
+#undef put
+}
+
+void interpret()
+{
+ struct fn *f = find_fn(0);
+ assert(f);
+
+ struct vec stack = vec_create(sizeof(int64_t));
+ /* arbitrary amount of stack space, could potentially even be expanded
+ * if we run out but that's currently not implemented */
+ vec_reserve(&stack, 65535);
+
+ struct vec globals = vec_create(sizeof(int64_t));
+ vec_reserve(&globals, num_globals());
+
+ struct vec args = vec_create(sizeof(int64_t));
+
+ interpret_func(f, &args, 0, &stack, &globals);
+
+ vec_destroy(&args);
+ vec_destroy(&stack);
+ vec_destroy(&globals);
+}