diff options
author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-04-26 19:03:48 +0300 |
---|---|---|
committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-04-26 19:03:48 +0300 |
commit | 6bc52441237fe182c05703d3a7e1d93d208ce5e7 (patch) | |
tree | 4e44c58824feb59e822fcd9b87655f9e6cf1f463 | |
parent | 419e021d3db3758dd17daef925ec47f2861f4026 (diff) | |
download | posthaste-6bc52441237fe182c05703d3a7e1d93d208ce5e7.tar.gz posthaste-6bc52441237fe182c05703d3a7e1d93d208ce5e7.zip |
rewrite interpeter
-rw-r--r-- | src/check.c | 35 | ||||
-rw-r--r-- | src/compile.c | 30 | ||||
-rw-r--r-- | src/core.c | 7 | ||||
-rw-r--r-- | src/execute.c | 365 | ||||
-rw-r--r-- | src/interpret.c | 407 | ||||
-rw-r--r-- | src/lower.c | 24 | ||||
-rw-r--r-- | src/scope.c | 1 |
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); -} @@ -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}; |