aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-04-26 19:03:48 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-04-26 19:03:48 +0300
commit6bc52441237fe182c05703d3a7e1d93d208ce5e7 (patch)
tree4e44c58824feb59e822fcd9b87655f9e6cf1f463
parent419e021d3db3758dd17daef925ec47f2861f4026 (diff)
downloadposthaste-6bc52441237fe182c05703d3a7e1d93d208ce5e7.tar.gz
posthaste-6bc52441237fe182c05703d3a7e1d93d208ce5e7.zip
rewrite interpeter
-rw-r--r--src/check.c35
-rw-r--r--src/compile.c30
-rw-r--r--src/core.c7
-rw-r--r--src/execute.c365
-rw-r--r--src/interpret.c407
-rw-r--r--src/lower.c24
-rw-r--r--src/scope.c1
7 files changed, 466 insertions, 403 deletions
diff --git a/src/check.c b/src/check.c
index 880f71f..b4ea2b1 100644
--- a/src/check.c
+++ b/src/check.c
@@ -1,8 +1,10 @@
#include <posthaste/check.h>
#include <posthaste/debug.h>
+#include <posthaste/utils.h>
-#define UNUSED(x) (void)x
-
+/* state required to implement semantic checking. In this case it turned out
+ * that I only needed a pointer to the function/procedure the checker is
+ * currently in, mainly for return type matching. */
struct state {
struct ast *parent;
};
@@ -21,6 +23,7 @@ int analyze_visibility(struct scope *scope, struct ast *n)
return -1;
break;
+
default:
}
@@ -46,6 +49,9 @@ static int analyze_list(struct state *state, struct scope *scope, struct ast *l)
static struct ast *file_scope_find_analyzed(struct state *state,
struct scope *scope, char *id)
{
+ /* look up a definition (func/proc/variable) in this scope and possible
+ * parent scopes up to and including the file scope, and make sure that
+ * whatever we find has been analyzed to completeness. */
struct ast *exists = file_scope_find(scope, id);
if (!exists)
return NULL;
@@ -87,7 +93,7 @@ static int analyze_func_def(struct state *state, struct scope *scope,
if (analyze_type(scope, f))
return -1;
- /* slightly hacky, but allows recursion */
+ /* slightly hacky, but easiest way to allow recursion */
ast_set_flags(f, AST_FLAG_CHECKED);
struct scope *func_scope = create_scope();
@@ -119,7 +125,6 @@ static int analyze_proc_def(struct state *state, struct scope *scope,
if (analyze_type(scope, p))
return -1;
- /* slightly hacky, but allows recursion */
ast_set_flags(p, AST_FLAG_CHECKED);
struct scope *proc_scope = create_scope();
@@ -137,8 +142,11 @@ static int analyze_proc_def(struct state *state, struct scope *scope,
return -1;
struct ast *last = ast_last(proc_body(p));
+ /* here we could for example implement checking that all branches in an unless
+ * return, but for simplicity require user to add explicit return as
+ * last statement in body */
if (p->t != TYPE_VOID && last->k != AST_RETURN) {
- semantic_error(scope, p, "can't prove that proc returns");
+ semantic_error(scope, p, "can't prove that proc reaches return statement");
return -1;
}
@@ -522,10 +530,8 @@ static int analyze_attr(struct state *state, struct scope *scope, struct ast *d)
if (same_id(d->id, "year"))
return 0;
- if (same_id(d->id, "weekday")) {
- d->t = TYPE_STRING;
+ if (same_id(d->id, "weekday"))
return 0;
- }
if (same_id(d->id, "weeknum"))
return 0;
@@ -788,9 +794,13 @@ static int analyze(struct state *state, struct scope *scope, struct ast *n)
case AST_UNLESS: ret = analyze_unless(state, scope, n); break;
case AST_UNLESS_EXPR: ret = analyze_unless_expr(state, scope, n); break;
default: break;
+ /* not all nodes are in this switch statement, as the internal
+ * ones are assumed to be correct */
}
if (ret == 0) {
+ /* even though sometimes we might not need the type, this is a
+ * fairly effective sanity check */
assert(has_type(n));
}
@@ -798,13 +808,22 @@ static int analyze(struct state *state, struct scope *scope, struct ast *n)
return ret;
}
+/* this I guess would be classified as a two-phase semantic checker, where the
+ * first phase checks that there aren't any multiply defined
+ * procs/funcs/variables, and the second phase does (mainly) type checking. I didn't
+ * explicitly call it type checking as it technically also checks date
+ * attributes, date +- etc. which were fairly easy to implement in the same
+ * phase. */
int check(struct scope *scope, struct ast *root)
{
+ /* first add all procedures/functions to the top level scope so we can
+ * find them later */
foreach_node(n, root) {
if (analyze_visibility(scope, n))
return -1;
}
+ /* actually analyze all nodes */
foreach_node(n, root) {
struct state state = {0};
if (analyze(&state, scope, n))
diff --git a/src/compile.c b/src/compile.c
index 11b75a5..dd4c73d 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -4,12 +4,11 @@
#include <posthaste/compile.h>
#include <posthaste/lower.h>
+#include <posthaste/utils.h>
#include <posthaste/date.h>
#include <lightening/lightening.h>
-#define UNUSED(x) (void)x
-
static void compile_fn(struct fn *f);
static void *alloc_arena(size_t size)
@@ -393,19 +392,7 @@ static void compile_load_day(jit_state_t *j, struct insn i)
static int64_t load_weekday(int64_t i0)
{
struct tm time = tm_from_date((ph_date_t)i0);
-
- const char *day = "Sunday";
- switch (time.tm_wday) {
- case 0: day = "Sunday"; break;
- case 1: day = "Monday"; break;
- case 2: day = "Tuesday"; break;
- case 3: day = "Wednesday"; break;
- case 4: day = "Thursday"; break;
- case 5: day = "Friday"; break;
- case 6: day = "Saturday"; break;
- }
-
- return (int64_t)day;
+ return (int64_t)time.tm_wday;
}
static void compile_load_weekday(jit_state_t *j, struct insn i)
@@ -638,16 +625,3 @@ void compile()
struct fn *f = find_fn(0);
compile_fn(f);
}
-
-void execute_compilation()
-{
- struct fn *f = find_fn(0);
- void (*p)(int64_t *globals) = f->arena;
-
- struct vec globals = vec_create(sizeof(int64_t));
- vec_reserve(&globals, 65535);
-
- p(globals.buf);
-
- vec_destroy(&globals);
-}
diff --git a/src/core.c b/src/core.c
index 6d01068..00dc3a7 100644
--- a/src/core.c
+++ b/src/core.c
@@ -3,8 +3,9 @@
#include <limits.h>
#include <errno.h>
-#include <posthaste/compile.h>
+#include <posthaste/interpret.h>
#include <posthaste/execute.h>
+#include <posthaste/compile.h>
#include <posthaste/parser.h>
#include <posthaste/debug.h>
#include <posthaste/scope.h>
@@ -87,9 +88,9 @@ int run(const char *fname)
#ifdef JIT
compile(ast);
- execute_compilation();
-#else
execute();
+#else
+ interpret();
#endif
out:
diff --git a/src/execute.c b/src/execute.c
index ace4adc..7da1a1a 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -1,373 +1,16 @@
-#include <math.h>
-#include <stdio.h>
-
#include <posthaste/execute.h>
#include <posthaste/lower.h>
-#include <posthaste/date.h>
#include <posthaste/vec.h>
-#define UNUSED(x) (void)x
-
-#define DEF(x) \
- static void exec_##x(struct insn i, size_t sp, struct vec *stack, \
- struct vec *globals)
-
-static int64_t load(struct loc l, size_t sp, struct vec *stack,
- struct vec *globals)
-{
- 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;
-}
-
-#define get(l) \
- load(l, sp, stack, globals)
-
-#define put(l, v) \
- store(l, v, sp, stack, globals)
-
-DEF(MOVE) {
- int64_t i0 = get(i.i0);
- put(i.o, i0);
-}
-
-DEF(ADD) {
- int64_t i0 = get(i.i0);
- int64_t i1 = get(i.i1);
- int64_t o = i0 + i1;
- put(i.o, o);
-}
-
-DEF(SUB) {
- int64_t i0 = get(i.i0);
- int64_t i1 = get(i.i1);
- int64_t o = i0 - i1;
- put(i.o, o);
-}
-
-DEF(MUL) {
- int64_t i0 = get(i.i0);
- int64_t i1 = get(i.i1);
- int64_t o = i0 * i1;
- put(i.o, o);
-}
-
-DEF(DIV) {
- int64_t i0 = get(i.i0);
- int64_t i1 = get(i.i1);
- int64_t o = i0 / i1;
- put(i.o, o);
-}
-
-DEF(PRINT_DATE) {
- int64_t i0 = get(i.i0);
- char str[11] = {0};
- date_to_string(str, i0);
- printf("%s", str);
-}
-
-DEF(PRINT_INT) {
- int64_t i0 = get(i.i0);
- printf("%lli", (long long)i0);
-}
-
-DEF(PRINT_STRING) {
- int64_t i0 = get(i.i0);
- printf("%s", (const char *)i0);
-}
-
-DEF(PRINT_BOOL) {
- int64_t i0 = get(i.i0);
- printf("%s", i0 ? "true" : "false");
-}
-
-DEF(PRINT_NEWLINE) {
- UNUSED(i);
- UNUSED(sp);
- UNUSED(stack);
- UNUSED(globals);
- printf("\n");
-}
-
-DEF(PRINT_SPACE) {
- UNUSED(i);
- UNUSED(sp);
- UNUSED(stack);
- UNUSED(globals);
- printf(" ");
-}
-
-DEF(CONST) {
- put(i.o, i.v);
-}
-
-DEF(EQ) {
- int64_t i0 = get(i.i0);
- int64_t i1 = get(i.i1);
- int64_t b = i0 == i1;
- put(i.o, b);
-}
-
-DEF(LT) {
- int64_t i0 = get(i.i0);
- int64_t i1 = get(i.i1);
- int64_t b = i0 < i1;
- put(i.o, b);
-}
-
-DEF(NEG) {
- int64_t i0 = get(i.i0);
- put(i.o, -i0);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-DEF(LOAD_WEEKDAY) {
- int64_t i0 = get(i.i0);
- struct tm time = tm_from_date((ph_date_t)i0);
-
- const char *day = "Sunday";
- switch (time.tm_wday) {
- case 0: day = "Sunday"; break;
- case 1: day = "Monday"; break;
- case 2: day = "Tuesday"; break;
- case 3: day = "Wednesday"; break;
- case 4: day = "Thursday"; break;
- case 5: day = "Friday"; break;
- case 6: day = "Saturday"; break;
- }
-
- put(i.o, (int64_t)day);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-DEF(TODAY) {
- ph_date_t date = current_date();
- put(i.o, (int64_t)date);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-DEF(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);
-}
-
-static int64_t exec_func(struct fn *f, struct vec *args, size_t sp,
- struct vec *stack, struct vec *globals)
-{
- /* move args to formal locations */
- size_t i = 0;
- foreach_vec(ai, *args) {
- int64_t a = vect_at(int64_t, *args, ai);
- vect_at(int64_t, *stack, sp + i) = a;
- i += 1;
- }
-
- vec_reset(args);
-
- size_t pc = 0;
- int64_t retval = 0;
- struct vec insns = f->insns;
- while (1) {
- struct insn i = vect_at(struct insn, insns, pc);
-
-#define DO(x) case x: exec_##x(i, sp, stack, globals); break;
- switch (i.k) {
- /* special cases first */
- case LABEL: break;
- case STOP: return 0;
- case RET: return get(i.i0);
- case RETVAL: put(i.o, retval); break;
- case J: pc = i.v; continue;
- case B: {
- int64_t i0 = get(i.i0);
- if (i0) {
- pc = i.v;
- continue;
- }
-
- break;
- }
-
- case BZ: {
- int64_t i0 = get(i.i0);
- if (!i0) {
- pc = i.v;
- continue;
- }
-
- break;
- }
-
- case ARG: {
- int64_t a = get(i.i0);
- vect_append(int64_t, *args, &a);
- break;
- }
-
- case CALL: {
- struct fn *cf = find_fn(i.v);
- assert(cf);
-
- retval = exec_func(cf, args, sp + f->max_sp, stack,
- globals);
- break;
- }
-
- DO(MOVE);
- DO(ADD);
- DO(SUB);
- DO(MUL);
- DO(DIV);
- DO(CONST);
- DO(PRINT_DATE);
- DO(PRINT_INT);
- DO(PRINT_BOOL);
- DO(PRINT_STRING);
- DO(PRINT_NEWLINE);
- DO(PRINT_SPACE);
- DO(LOAD_DAY);
- DO(LOAD_MONTH);
- DO(LOAD_YEAR);
- DO(LOAD_WEEKDAY);
- DO(LOAD_WEEKNUM);
- DO(STORE_DAY);
- DO(STORE_MONTH);
- DO(STORE_YEAR);
- DO(DATE_ADD);
- DO(DATE_SUB);
- DO(DATE_DIFF);
- DO(TODAY);
- DO(EQ);
- DO(LT);
- DO(NEG);
- }
-#undef DO
-
- pc += 1;
- }
-}
-
void execute()
{
- struct fn *main = find_fn(0);
- assert(main);
-
- struct vec stack = vec_create(sizeof(int64_t));
- /* arbitrary amount */
- vec_reserve(&stack, 65535);
+ struct fn *f = find_fn(0);
+ void (*p)(int64_t *globals) = f->arena;
struct vec globals = vec_create(sizeof(int64_t));
- /* should really take the value calculated during lowering */
- vec_reserve(&globals, 65535);
-
- struct vec args = vec_create(sizeof(int64_t));
+ vec_reserve(&globals, num_globals());
- exec_func(main, &args, 0, &stack, &globals);
+ p(globals.buf);
- vec_destroy(&args);
- vec_destroy(&stack);
vec_destroy(&globals);
}
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);
+}
diff --git a/src/lower.c b/src/lower.c
index 02efb1b..0a6c07d 100644
--- a/src/lower.c
+++ b/src/lower.c
@@ -4,13 +4,24 @@
#include <posthaste/lower.h>
#include <posthaste/scope.h>
+#include <posthaste/utils.h>
-#define UNUSED(x) (void)x
-
+/* globals are kind of ugly and could/should be made into parameters (or
+ * potentially even members of some all-encompassing state) but this works for
+ * this simple implementation, implementing the change would mosty just require
+ * changing function signatures which is boring and I don't want to do it right
+ * now */
static struct vec fns = {0};
-/* zero is unintialized, global 1 reserved as null, so skip two first globals */
+
+/* locs use global 0 to mean unitialized and global 1 is reserved as null_loc(),
+ * so skip two first globals */
static size_t globals = 2;
+size_t num_globals()
+{
+ return globals;
+}
+
static void lower(struct fn *f, struct ast *n);
static void lower_list(struct fn *f, struct ast *l)
{
@@ -698,6 +709,8 @@ int lower_ast(struct ast *tree)
{
fns = vec_create(sizeof(struct fn));
+ /* make body of file out to be a kind of main function, it will always
+ * be at index 0 */
struct fn main = {.name = "main",
.idx = 0,
.sp = 0,
@@ -705,6 +718,7 @@ int lower_ast(struct ast *tree)
vect_append(struct fn, fns, &main);
+ /* first create function nodes in fns to assign each procedure an index */
foreach_node(n, tree) {
switch (n->k) {
case AST_PROC_DEF: add_proc(n); break;
@@ -714,6 +728,9 @@ int lower_ast(struct ast *tree)
}
struct fn *f = &vect_at(struct fn, fns, 0);
+ /* we can't treat file scope as a regular function, as all variable
+ * definitions must be made global, so we have a little bit of
+ * duplicated code here but that's fine */
foreach_node(n, tree) {
switch (n->k) {
case AST_VAR_DEF: lower_global_var(f, n); break;
@@ -723,6 +740,7 @@ int lower_ast(struct ast *tree)
}
}
+ /* append a return without value so we know when to stop interpretation */
output_insn(f, STOP, null_loc(), null_loc(), null_loc(), 0);
#ifdef DEBUG
diff --git a/src/scope.c b/src/scope.c
index d2521c8..4cec492 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -3,6 +3,7 @@
#include <posthaste/scope.h>
#include <posthaste/debug.h>
+#include <posthaste/utils.h>
#include <posthaste/vec.h>
struct vec scopes = {0};