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}; | 
