Compare commits
	
		
			33 Commits
		
	
	
		
			84785dc3cf
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 99489dfeb6 | ||
|  | e7da8dfe38 | ||
|  | f6b74f8f97 | ||
|  | 6f91a71306 | ||
|  | 4d5cd93354 | ||
|  | 7773cc6c14 | ||
|  | 45feb3fe1d | ||
|  | ba8d2f0702 | ||
|  | befce544e7 | ||
|  | 0d5313a063 | ||
|  | 22f71d7e56 | ||
|  | a706ea6a3f | ||
|  | 0b2741f73f | ||
|  | cda56d5b9c | ||
|  | 46e7487cad | ||
|  | 18d6e7b7df | ||
|  | 1f47b5e16c | ||
|  | dfe1ac90e8 | ||
|  | d8b470f0eb | ||
|  | cf93109f1e | ||
|  | 850dafbbc9 | ||
|  | 803368a264 | ||
|  | b4c369e1d9 | ||
|  | dd67a1bf5d | ||
|  | b58810e822 | ||
|  | f02dae603d | ||
|  | 92c4c5c991 | ||
|  | 9bd4d4e0c4 | ||
| bb75b78a36 | |||
|  | ca232fbf6a | ||
|  | 6bdc4e3210 | ||
|  | d67008cfbf | ||
|  | a0842424ec | 
							
								
								
									
										5
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,3 +1,6 @@ | |||||||
|  | ifneq ($(OS),Windows_NT) | ||||||
|  | CPPFLAGS = -D_POSIX_C_SOURCE=200112L | ||||||
|  | endif | ||||||
| CFLAGS  = -ggdb -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition | CFLAGS  = -ggdb -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition | ||||||
| #CFLAGS  = -pg -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition | #CFLAGS  = -pg -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition | ||||||
| #CFLAGS  = -O3 -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition | #CFLAGS  = -O3 -std=c11 -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition | ||||||
| @@ -12,7 +15,7 @@ $(EXE): $(OBJ) | |||||||
| 	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) | 	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) | ||||||
|  |  | ||||||
| %.o: %.c | %.o: %.c | ||||||
| 	$(CC) -c -o $@ $< $(CFLAGS) | 	$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS) | ||||||
|  |  | ||||||
| deps.mk: $(SOURCE) $(HEADERS) | deps.mk: $(SOURCE) $(HEADERS) | ||||||
| 	@echo "# Automatically generated by $(CC) -MM." > $@ | 	@echo "# Automatically generated by $(CC) -MM." > $@ | ||||||
|   | |||||||
| @@ -1,3 +1 @@ | |||||||
| # lang | # lang | ||||||
|  |  | ||||||
| Yet another useless programming language (a lot of stuff not yet implemented). |  | ||||||
							
								
								
									
										21
									
								
								examples/calculator.script
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								examples/calculator.script
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | put("Enter an operation (+, -, *, /): ") | ||||||
|  | op := getln() | ||||||
|  | if (!(op == "+" || op == "-" || op == "*" || op == "/")) { | ||||||
|  | 	put("Unknown operation: ") | ||||||
|  | 	putln(op) | ||||||
|  | } else { | ||||||
|  | 	put("1st number: ") | ||||||
|  | 	n1 := float(getln()) | ||||||
|  | 	put("2nd number: ") | ||||||
|  | 	n2 := float(getln()) | ||||||
|  |  | ||||||
|  | 	put("Result: ") | ||||||
|  | 	if (op == "+") | ||||||
|  | 		putln(n1 + n2) | ||||||
|  | 	else if (op == "-") | ||||||
|  | 		putln(n1 - n2) | ||||||
|  | 	else if (op == "*") | ||||||
|  | 		putln(n1 * n2) | ||||||
|  | 	else if (op == "/") | ||||||
|  | 		putln(n1 / n2) | ||||||
|  | } | ||||||
| @@ -7,6 +7,6 @@ while i { | |||||||
| 	z := x + y | 	z := x + y | ||||||
| 	y = x | 	y = x | ||||||
| 	x = z | 	x = z | ||||||
| 	print(z) | 	putln(z) | ||||||
| 	i = i - 1 | 	i = i - 1 | ||||||
| } | } | ||||||
							
								
								
									
										86
									
								
								examples/mandelbrot.script
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								examples/mandelbrot.script
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | width := 280 | ||||||
|  | height := 100 | ||||||
|  |  | ||||||
|  | iterations := 100 | ||||||
|  | xmin := -2.0 | ||||||
|  | xmax := 0.5 | ||||||
|  | ymin := -1.0 | ||||||
|  | ymax := 1.0 | ||||||
|  |  | ||||||
|  | // Some further coordinates: | ||||||
|  | /*iterations := 1000 | ||||||
|  | xmin := -0.9072945999 | ||||||
|  | xmax := -0.8984310833 | ||||||
|  | ymin := 0.2304178999 | ||||||
|  | ymax := 0.2370858666*/ | ||||||
|  |  | ||||||
|  | /*iterations := 100 | ||||||
|  | xmin := -0.193596288 | ||||||
|  | xmax := -0.119260320 | ||||||
|  | ymin := 1.006960992 | ||||||
|  | ymax := 1.062687264*/ | ||||||
|  |  | ||||||
|  | /*iterations := 800 | ||||||
|  | xmin := -0.1675326254 | ||||||
|  | xmax := -0.1675148625 | ||||||
|  | ymin := 1.0413005672 | ||||||
|  | ymax := 1.0413138086*/ | ||||||
|  |  | ||||||
|  | /*iterations := 918 | ||||||
|  | xmin := -0.7506201104 | ||||||
|  | xmax := -0.7503409687 | ||||||
|  | ymin := 0.0170447020 | ||||||
|  | ymax := 0.0172540583*/ | ||||||
|  |  | ||||||
|  | /*iterations := 400 | ||||||
|  | xmin := -0.7548484315 | ||||||
|  | xmax := -0.7540548595 | ||||||
|  | ymin := 0.0530077004 | ||||||
|  | ymax := 0.0536039518*/ | ||||||
|  |  | ||||||
|  | y := 0 | ||||||
|  | while y < height { | ||||||
|  | 	c_im := (float(height - y) / float(height)) * (ymax - ymin) + ymin | ||||||
|  | 	x := 0 | ||||||
|  | 	while x < width { | ||||||
|  | 		c_re := (float(x) / float(width)) * (xmax - xmin) + xmin | ||||||
|  |  | ||||||
|  | 		z_re := 0.0 | ||||||
|  | 		z_im := 0.0 | ||||||
|  |  | ||||||
|  | 		it := 0 | ||||||
|  | 		loop := true | ||||||
|  | 		while it < iterations && loop { | ||||||
|  | 			/* z = z*z + c */ | ||||||
|  | 			/* (a + bi)^2 = a^2 + 2abi - b^2 */ | ||||||
|  | 			z_re_tmp := z_re * z_re - z_im * z_im + c_re | ||||||
|  | 			z_im = 2.0 * z_re * z_im + c_im | ||||||
|  | 			z_re = z_re_tmp | ||||||
|  |  | ||||||
|  | 			/* Break if the number shoots off to infinity. */ | ||||||
|  | 			if z_re * z_re + z_im * z_im > 4.0 { | ||||||
|  | 				loop = false | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			it = it + 1 | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if it <= iterations / 5 | ||||||
|  | 			put(' ') | ||||||
|  | 		else if it <= iterations / 5 * 2 | ||||||
|  | 			put('.') | ||||||
|  | 		else if it <= iterations / 5 * 3 | ||||||
|  | 			put(',') | ||||||
|  | 		else if it <= iterations / 5 * 4 | ||||||
|  | 			put('*') | ||||||
|  | 		else if it < iterations | ||||||
|  | 			put('+') | ||||||
|  | 		else if it == iterations | ||||||
|  | 			put('#') | ||||||
|  |  | ||||||
|  | 		x = x + 1 | ||||||
|  | 	} | ||||||
|  | 	put('\n') | ||||||
|  |  | ||||||
|  | 	y = y + 1 | ||||||
|  | } | ||||||
| @@ -13,4 +13,5 @@ while k < iterations { | |||||||
| 	k = k + 1 | 	k = k + 1 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| print(sum) | put("π ≈ ") | ||||||
|  | putln(sum) | ||||||
							
								
								
									
										285
									
								
								ir.c
									
									
									
									
									
								
							
							
						
						
									
										285
									
								
								ir.c
									
									
									
									
									
								
							| @@ -4,142 +4,231 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  |  | ||||||
| const char *irinstr_str[IRInstrEnumSize] = { | const char *irinstr_str[IRInstrEnumSize] = { | ||||||
| 	[IRSet] = "set", | 	[IRSet]          = "set", | ||||||
| 	[IRNeg] = "neg", | 	[IRNeg]          = "neg", | ||||||
| 	[IRAdd] = "add", | 	[IRAdd]          = "add", | ||||||
| 	[IRSub] = "sub", | 	[IRSub]          = "sub", | ||||||
| 	[IRMul] = "mul", | 	[IRMul]          = "mul", | ||||||
| 	[IRDiv] = "div", | 	[IRDiv]          = "div", | ||||||
| 	[IREq]  = "eq", | 	[IREq]           = "eq", | ||||||
| 	[IRLt]  = "lt", | 	[IRNeq]          = "neq", | ||||||
| 	[IRLe]  = "le", | 	[IRLt]           = "lt", | ||||||
| 	[IRNot] = "not", | 	[IRLe]           = "le", | ||||||
| 	[IRJmp] = "jmp", | 	[IRNot]          = "not", | ||||||
| 	[IRJnz] = "jnz", | 	[IRAnd]          = "and", | ||||||
|  | 	[IROr]           = "or", | ||||||
|  | 	[IRJmp]          = "jmp", | ||||||
|  | 	[IRJnz]          = "jnz", | ||||||
| 	[IRCallInternal] = "calli", | 	[IRCallInternal] = "calli", | ||||||
|  | 	[IRAddrOf]       = "addrof", | ||||||
|  | 	[IRArrMake]      = "mkarr", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #define IRTOKS_INIT_CAP_LONG 4096 | #define IRLIST_INIT_CAP_LONG 4096 | ||||||
| #define IRTOKS_INIT_CAP_SHORT 16 | #define IRLIST_INIT_CAP_SHORT 16 | ||||||
|  |  | ||||||
| static void irtoks_init_with_cap(IRToks *v, size_t cap); | static void irlist_init_with_cap(IRList *v, size_t cap); | ||||||
|  | static IRItem *irlist_new_item(IRList *v); | ||||||
|  |  | ||||||
| static void irtoks_init_with_cap(IRToks *v, size_t cap) { | static void irlist_init_with_cap(IRList *v, size_t cap) { | ||||||
| 	v->toks = xmalloc(sizeof(IRTok) * cap); | 	v->begin = NULL; | ||||||
|  | 	v->end = NULL; | ||||||
|  | 	v->p = pool_new(sizeof(IRItem) * cap); | ||||||
|  | 	v->index = NULL; | ||||||
| 	v->len = 0; | 	v->len = 0; | ||||||
| 	v->cap = cap; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void irtoks_init_long(IRToks *v) { | static IRItem *irlist_new_item(IRList *v) { | ||||||
| 	irtoks_init_with_cap(v, IRTOKS_INIT_CAP_LONG); | 	IRItem *ret = pool_alloc(v->p, sizeof(IRItem)); | ||||||
|  | 	ret->next = NULL; | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| void irtoks_init_short(IRToks *v) { | void irlist_init_long(IRList *v) { | ||||||
| 	irtoks_init_with_cap(v, IRTOKS_INIT_CAP_SHORT); | 	irlist_init_with_cap(v, IRLIST_INIT_CAP_LONG); | ||||||
| } | } | ||||||
|  |  | ||||||
| void irtoks_term(IRToks *v) { | void irlist_init_short(IRList *v) { | ||||||
| 	for (size_t i = 0; i < v->len; i++) { | 	irlist_init_with_cap(v, IRLIST_INIT_CAP_SHORT); | ||||||
| 		if (v->toks[i].instr == IRCallInternal) |  | ||||||
| 			free(v->toks[i].CallI.args); |  | ||||||
| 	} |  | ||||||
| 	free(v->toks); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void irtoks_app(IRToks *v, IRTok t) { | static void free_irparam(IRParam *v, bool purge); | ||||||
| 	if (v->len+1 > v->cap) |  | ||||||
| 		v->toks = xrealloc(v->toks, sizeof(IRTok) * (v->cap *= 2)); | /* if purge is set, even statically allocated literals are freed */ | ||||||
| 	v->toks[v->len++] = t; | static void free_irparam(IRParam *v, bool purge) { | ||||||
|  | 	if (v->kind == IRParamLiteral) | ||||||
|  | 		free_value(&v->Literal, purge); | ||||||
| } | } | ||||||
|  |  | ||||||
| void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset) { | void irlist_term(IRList *v) { | ||||||
| 	if (v->len+other->len > v->cap) | 	for (IRItem *i = v->begin; i; i = i->next) { | ||||||
| 		v->toks = xrealloc(v->toks, sizeof(IRTok) * (other->len + (v->cap *= 2))); | 		switch (i->tok.instr) { | ||||||
| 	for (size_t i = 0; i < other->len; i++) { |  | ||||||
| 		/* correct for changed jump addresses */ |  | ||||||
| 		if (other->toks[i].instr == IRJmp) |  | ||||||
| 			other->toks[i].Jmp.iaddr += jmp_offset; |  | ||||||
| 		else if (other->toks[i].instr == IRJnz) |  | ||||||
| 			other->toks[i].CJmp.iaddr += jmp_offset; |  | ||||||
|  |  | ||||||
| 		v->toks[v->len++] = other->toks[i]; |  | ||||||
| 	} |  | ||||||
| 	/* We're not calling irtoks_term() because we don't want associated items |  | ||||||
| 	 * (for example function arguments) to get deallocated as well. */ |  | ||||||
| 	free(other->toks); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void print_val(const Value *v); |  | ||||||
| static void print_irparam(const IRParam *p); |  | ||||||
|  |  | ||||||
| static void print_val(const Value *v) { |  | ||||||
| 	switch (v->type.kind) { |  | ||||||
| 		case TypeFloat: |  | ||||||
| 			printf("%f", v->Float); |  | ||||||
| 			break; |  | ||||||
| 		case TypeInt: |  | ||||||
| 			printf("%zd", v->Int); |  | ||||||
| 			break; |  | ||||||
| 		case TypeBool: |  | ||||||
| 			printf("%s", v->Bool ? "true" : "false"); |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			printf("(unknown type)"); |  | ||||||
| 			break; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void print_irparam(const IRParam *p) { |  | ||||||
| 	if (p->kind == IRParamLiteral) { |  | ||||||
| 		print_val(&p->Literal); |  | ||||||
| 	} else if (p->kind == IRParamAddr) { |  | ||||||
| 		printf("%%%zd", p->Addr); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) { |  | ||||||
| 	for (size_t i = 0; i < v->len; i++) { |  | ||||||
| 		printf("%04zx ", i); |  | ||||||
| 		printf("%s", irinstr_str[v->toks[i].instr]); |  | ||||||
| 		switch (v->toks[i].instr) { |  | ||||||
| 			case IRSet: | 			case IRSet: | ||||||
| 			case IRNeg: | 			case IRNeg: | ||||||
| 				printf(" %%%zx ", v->toks[i].Unary.addr); | 			case IRNot: | ||||||
| 				print_irparam(&v->toks[i].Unary.val); | 			case IRAddrOf: | ||||||
|  | 				free_irparam(&i->tok.Unary.val, true); | ||||||
| 				break; | 				break; | ||||||
| 			case IRAdd: | 			case IRAdd: | ||||||
| 			case IRSub: | 			case IRSub: | ||||||
| 			case IRDiv: | 			case IRDiv: | ||||||
| 			case IRMul: | 			case IRMul: | ||||||
| 			case IREq: | 			case IREq: | ||||||
|  | 			case IRNeq: | ||||||
| 			case IRLt: | 			case IRLt: | ||||||
| 			case IRLe: | 			case IRLe: | ||||||
| 				printf(" %%%zx ", v->toks[i].Binary.addr); | 			case IRAnd: | ||||||
| 				print_irparam(&v->toks[i].Binary.lhs); | 			case IROr: | ||||||
| 				printf(" "); | 				free_irparam(&i->tok.Binary.lhs, true); | ||||||
| 				print_irparam(&v->toks[i].Binary.rhs); | 				free_irparam(&i->tok.Binary.rhs, true); | ||||||
| 				break; | 				break; | ||||||
| 			case IRJmp: | 			case IRJmp: | ||||||
| 				printf(" %zx", v->toks[i].Jmp.iaddr); | 				break; | ||||||
|  | 			case IRJnz: | ||||||
|  | 				free_irparam(&i->tok.CJmp.condition, true); | ||||||
|  | 				break; | ||||||
|  | 			case IRCallInternal: | ||||||
|  | 				for (size_t j = 0; j < i->tok.CallI.n_args; j++) | ||||||
|  | 					free_irparam(&i->tok.CallI.args[j], true); | ||||||
|  | 				free(i->tok.CallI.args); | ||||||
|  | 				break; | ||||||
|  | 			case IRArrMake: | ||||||
|  | 				for (size_t j = 0; j < i->tok.ArrMake.len; j++) | ||||||
|  | 					free_irparam(&i->tok.ArrMake.vals[j], true); | ||||||
|  | 				free(i->tok.ArrMake.vals); | ||||||
|  | 				break; | ||||||
|  | 			default: | ||||||
|  | 				ASSERT_UNREACHED(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	pool_term(v->p); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void irlist_app(IRList *v, IRTok t) { | ||||||
|  | 	v->index = NULL; /* invalidate index */ | ||||||
|  | 	IRItem *itm = irlist_new_item(v); | ||||||
|  | 	itm->tok = t; | ||||||
|  |  | ||||||
|  | 	if (!v->begin && !v->end) | ||||||
|  | 		v->begin = v->end = itm; | ||||||
|  | 	else { | ||||||
|  | 		v->end->next = itm; | ||||||
|  | 		v->end = itm; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	v->len++; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void irlist_eat_irlist(IRList *v, IRList *other) { | ||||||
|  | 	v->index = NULL; /* invalidate index */ | ||||||
|  | 	size_t jmp_offset = v->len-1; | ||||||
|  | 	for (IRItem *i = other->begin; i; i = i->next) { | ||||||
|  | 		/* correct for changed jump addresses */ | ||||||
|  | 		if (i->tok.instr == IRJmp) | ||||||
|  | 			i->tok.Jmp.iaddr += jmp_offset; | ||||||
|  | 		else if (i->tok.instr == IRJnz) | ||||||
|  | 			i->tok.CJmp.iaddr += jmp_offset; | ||||||
|  |  | ||||||
|  | 		irlist_app(v, i->tok); | ||||||
|  | 	} | ||||||
|  | 	/* We're not calling irlist_term() because we don't want associated items | ||||||
|  | 	 * (for example function arguments) to get deallocated as well. */ | ||||||
|  | 	pool_term(other->p); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void irlist_update_index(IRList *v) { | ||||||
|  | 	if (v->index) | ||||||
|  | 		return; | ||||||
|  | 	v->index = pool_alloc(v->p, sizeof(size_t) * v->len); | ||||||
|  | 	size_t num_idx = 0; | ||||||
|  | 	for (IRItem *i = v->begin; i; i = i->next, num_idx++) | ||||||
|  | 		v->index[num_idx] = i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void print_irparam(const IRParam *p); | ||||||
|  |  | ||||||
|  | static void print_irparam(const IRParam *p) { | ||||||
|  | 	if (p->kind == IRParamLiteral) { | ||||||
|  | 		print_value(&p->Literal, false); | ||||||
|  | 	} else if (p->kind == IRParamAddr) { | ||||||
|  | 		printf("%%%zd", p->Addr); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void print_ir(IRList *v, const BuiltinFunc *builtin_funcs) { | ||||||
|  | 	size_t iaddr = 0; | ||||||
|  | 	for (IRItem *i = v->begin; i; i = i->next, iaddr++) { | ||||||
|  | 		printf("%04zx ", iaddr); | ||||||
|  | 		printf("%s", irinstr_str[i->tok.instr]); | ||||||
|  | 		switch (i->tok.instr) { | ||||||
|  | 			case IRSet: | ||||||
|  | 			case IRNeg: | ||||||
|  | 			case IRNot: | ||||||
|  | 			case IRAddrOf: | ||||||
|  | 				printf(" %%%zx ", i->tok.Unary.addr); | ||||||
|  | 				print_irparam(&i->tok.Unary.val); | ||||||
|  | 				break; | ||||||
|  | 			case IRAdd: | ||||||
|  | 			case IRSub: | ||||||
|  | 			case IRDiv: | ||||||
|  | 			case IRMul: | ||||||
|  | 			case IREq: | ||||||
|  | 			case IRNeq: | ||||||
|  | 			case IRLt: | ||||||
|  | 			case IRLe: | ||||||
|  | 			case IRAnd: | ||||||
|  | 			case IROr: | ||||||
|  | 				printf(" %%%zx ", i->tok.Binary.addr); | ||||||
|  | 				print_irparam(&i->tok.Binary.lhs); | ||||||
|  | 				printf(" "); | ||||||
|  | 				print_irparam(&i->tok.Binary.rhs); | ||||||
|  | 				break; | ||||||
|  | 			case IRJmp: | ||||||
|  | 				printf(" %zx", i->tok.Jmp.iaddr); | ||||||
| 				break; | 				break; | ||||||
| 			case IRJnz: | 			case IRJnz: | ||||||
| 				printf(" "); | 				printf(" "); | ||||||
| 				print_irparam(&v->toks[i].CJmp.condition); | 				print_irparam(&i->tok.CJmp.condition); | ||||||
| 				printf(" %zx", v->toks[i].CJmp.iaddr); | 				printf(" %zx", i->tok.CJmp.iaddr); | ||||||
| 				break; | 				break; | ||||||
| 			case IRCallInternal: { | 			case IRCallInternal: { | ||||||
| 				const BuiltinFunc *f = &builtin_funcs[v->toks[i].CallI.fid]; | 				const BuiltinFunc *f = &builtin_funcs[i->tok.CallI.fid]; | ||||||
| 				printf(" %s %%%zx", f->name, v->toks[i].CallI.ret_addr); | 				if (f->returns) | ||||||
| 				for (size_t j = 0; j < f->n_args; j++) { | 					printf(" %%%zx", i->tok.CallI.ret_addr); | ||||||
|  | 				printf(" %s", f->name); | ||||||
|  | 				for (size_t j = 0; j < i->tok.CallI.n_args; j++) { | ||||||
| 					printf(" "); | 					printf(" "); | ||||||
| 					print_irparam(&v->toks[i].CallI.args[j]); | 					print_irparam(&i->tok.CallI.args[j]); | ||||||
| 				} | 				} | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 			default: ASSERT_UNREACHED(); break; | 			case IRArrMake: { | ||||||
|  | 				printf(" %%%zx", i->tok.ArrMake.arr_addr); | ||||||
|  | 				for (size_t j = 0; j < i->tok.ArrMake.len; j++) { | ||||||
|  | 					printf(" "); | ||||||
|  | 					print_irparam(&i->tok.ArrMake.vals[j]); | ||||||
|  | 				} | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			default: ASSERT_UNREACHED(); | ||||||
| 		} | 		} | ||||||
| 		printf(" ; %zu:%zu", v->toks[i].ln, v->toks[i].col); | 		printf(" ; %zu:%zu", i->tok.ln, i->tok.col); | ||||||
| 		printf("\n"); | 		printf("\n"); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void optimize_ir(IRList *v) { | ||||||
|  | 	irlist_update_index(v); | ||||||
|  | 	for (IRItem *i = v->begin; i; i = i->next) { | ||||||
|  | 		switch (i->tok.instr) { | ||||||
|  | 			case IRJmp: { | ||||||
|  | 				/* resolve jump chains (esp. produced by if-else-if... statements) */ | ||||||
|  | 				size_t ja = i->tok.Jmp.iaddr; | ||||||
|  | 				while (ja < v->len && v->index[ja]->tok.instr == IRJmp) | ||||||
|  | 					ja = v->index[ja]->tok.Jmp.iaddr; | ||||||
|  | 				i->tok.Jmp.iaddr = ja; | ||||||
|  | 			} | ||||||
|  | 			default: break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								ir.h
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								ir.h
									
									
									
									
									
								
							| @@ -4,11 +4,33 @@ | |||||||
| #include "tok.h" | #include "tok.h" | ||||||
|  |  | ||||||
| typedef struct BuiltinFunc { | typedef struct BuiltinFunc { | ||||||
| 	char *name; | 	enum { | ||||||
|  | 		FuncFixedArgs, | ||||||
|  | 		FuncVarArgs, | ||||||
|  | 	} kind; | ||||||
|  |  | ||||||
|  | 	bool returns : 1; | ||||||
| 	bool side_effects : 1; | 	bool side_effects : 1; | ||||||
| 	size_t n_args; | 	char *name; | ||||||
| 	Value (*func)(Value *args); |  | ||||||
| 	size_t fid; /* function ID, assigned automatically */ | 	size_t fid; /* function ID, assigned automatically */ | ||||||
|  |  | ||||||
|  | 	union { | ||||||
|  | 		struct { | ||||||
|  | 			size_t n_args; | ||||||
|  | 			union { | ||||||
|  | 				struct { Value (*func)(Value *args); } WithRet; | ||||||
|  | 				struct { void  (*func)(Value *args); } NoRet; | ||||||
|  | 			}; | ||||||
|  | 		} FixedArgs; | ||||||
|  |  | ||||||
|  | 		struct { | ||||||
|  | 			size_t min_args; | ||||||
|  | 			union { | ||||||
|  | 				struct { Value (*func)(size_t extra_args, Value *args); } WithRet; | ||||||
|  | 				struct { void  (*func)(size_t extra_args, Value *args); } NoRet; | ||||||
|  | 			}; | ||||||
|  | 		} VarArgs; | ||||||
|  | 	}; | ||||||
| } BuiltinFunc; | } BuiltinFunc; | ||||||
|  |  | ||||||
| enum IRInstr { | enum IRInstr { | ||||||
| @@ -19,12 +41,17 @@ enum IRInstr { | |||||||
| 	IRMul, | 	IRMul, | ||||||
| 	IRDiv, | 	IRDiv, | ||||||
| 	IREq, | 	IREq, | ||||||
|  | 	IRNeq, | ||||||
| 	IRLt, | 	IRLt, | ||||||
| 	IRLe, | 	IRLe, | ||||||
| 	IRNot, | 	IRNot, | ||||||
|  | 	IRAnd, | ||||||
|  | 	IROr, | ||||||
| 	IRJmp, | 	IRJmp, | ||||||
| 	IRJnz, | 	IRJnz, | ||||||
| 	IRCallInternal, | 	IRCallInternal, | ||||||
|  | 	IRAddrOf, | ||||||
|  | 	IRArrMake, | ||||||
| 	IRInstrEnumSize, | 	IRInstrEnumSize, | ||||||
| }; | }; | ||||||
| typedef enum IRInstr IRInstr; | typedef enum IRInstr IRInstr; | ||||||
| @@ -77,22 +104,39 @@ typedef struct IRTok { | |||||||
| 		struct { | 		struct { | ||||||
| 			size_t ret_addr; | 			size_t ret_addr; | ||||||
| 			size_t fid; | 			size_t fid; | ||||||
|  | 			size_t n_args; | ||||||
| 			IRParam *args; | 			IRParam *args; | ||||||
| 		} CallI; | 		} CallI; | ||||||
|  |  | ||||||
|  | 		struct { | ||||||
|  | 			size_t arr_addr; | ||||||
|  | 			size_t len, cap; | ||||||
|  | 			IRParam *vals; | ||||||
|  | 		} ArrMake; | ||||||
| 	}; | 	}; | ||||||
| } IRTok; | } IRTok; | ||||||
|  |  | ||||||
| typedef struct IRToks { | typedef struct IRItem { | ||||||
| 	size_t len, cap; | 	struct IRItem *next; | ||||||
| 	IRTok *toks; | 	IRTok tok; | ||||||
| } IRToks; | } IRItem; | ||||||
|  |  | ||||||
| void irtoks_init_long(IRToks *v); | typedef struct IRList { | ||||||
| void irtoks_init_short(IRToks *v); | 	IRItem *begin, *end; | ||||||
| void irtoks_term(IRToks *v); | 	Pool *p; | ||||||
| void irtoks_app(IRToks *v, IRTok t); | 	IRItem **index; /* index to pointer, irlist_update_index() must be called before use */ | ||||||
| void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset); | 	size_t len; | ||||||
|  | } IRList; | ||||||
|  |  | ||||||
| void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs); | void irlist_init_long(IRList *v); | ||||||
|  | void irlist_init_short(IRList *v); | ||||||
|  | void irlist_term(IRList *v); | ||||||
|  | void irlist_app(IRList *v, IRTok t); | ||||||
|  | void irlist_eat_irlist(IRList *v, IRList *other); | ||||||
|  | void irlist_update_index(IRList *v); /* should be used very conservatively */ | ||||||
|  |  | ||||||
|  | void print_ir(IRList *v, const BuiltinFunc *builtin_funcs); | ||||||
|  |  | ||||||
|  | void optimize_ir(IRList *v); | ||||||
|  |  | ||||||
| #endif /* IR_H */ | #endif /* IR_H */ | ||||||
|   | |||||||
							
								
								
									
										131
									
								
								lex.c
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								lex.c
									
									
									
									
									
								
							| @@ -2,6 +2,8 @@ | |||||||
|  |  | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  |  | ||||||
|  | #define TAB_WIDTH 4 | ||||||
|  |  | ||||||
| typedef struct Pos { | typedef struct Pos { | ||||||
| 	size_t ln, col;     /* current position */ | 	size_t ln, col;     /* current position */ | ||||||
| 	size_t m_ln, m_col; /* marked position */ | 	size_t m_ln, m_col; /* marked position */ | ||||||
| @@ -11,12 +13,15 @@ static void consume(Pos *p, char c); | |||||||
| static void emit(TokList *toks, const Pos *p, Tok t); | static void emit(TokList *toks, const Pos *p, Tok t); | ||||||
| static void mark(Pos *p); | static void mark(Pos *p); | ||||||
| static void mark_err(const Pos *p); | static void mark_err(const Pos *p); | ||||||
|  | static char get_esc_char(char c); | ||||||
|  |  | ||||||
| static void consume(Pos *p, char c) { | static void consume(Pos *p, char c) { | ||||||
| 	if (c == '\n') { | 	if (c == '\n') { | ||||||
| 		p->ln++; | 		p->ln++; | ||||||
| 		p->col = 1; | 		p->col = 1; | ||||||
| 	} else | 	} else if (c == '\t') | ||||||
|  | 		p->col += TAB_WIDTH; | ||||||
|  | 	else | ||||||
| 		p->col++; | 		p->col++; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -36,6 +41,23 @@ static void mark_err(const Pos *p) { | |||||||
| 	err_col = p->m_col; | 	err_col = p->m_col; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static char get_esc_char(char c) { | ||||||
|  | 	switch(c) { | ||||||
|  | 		case 'a':  return '\a'; | ||||||
|  | 		case 'b':  return '\b'; | ||||||
|  | 		case 'e':  return '\033'; | ||||||
|  | 		case 'f':  return '\f'; | ||||||
|  | 		case 'n':  return '\n'; | ||||||
|  | 		case 'r':  return '\r'; | ||||||
|  | 		case 't':  return '\t'; | ||||||
|  | 		case 'v':  return '\v'; | ||||||
|  | 		case '\\': return '\\'; | ||||||
|  | 		case '\'': return '\''; | ||||||
|  | 		case '"':  return '\"'; | ||||||
|  | 		default:   return 0; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| TokList lex(const char *s) { | TokList lex(const char *s) { | ||||||
| 	TokList toks; | 	TokList toks; | ||||||
| 	toklist_init(&toks); | 	toklist_init(&toks); | ||||||
| @@ -59,9 +81,9 @@ TokList lex(const char *s) { | |||||||
| 			else if (streq_0_n("while", start, i)) | 			else if (streq_0_n("while", start, i)) | ||||||
| 				emit(&toks, &pos, (Tok){ .kind = TokWhile }); | 				emit(&toks, &pos, (Tok){ .kind = TokWhile }); | ||||||
| 			else if (streq_0_n("true", start, i)) | 			else if (streq_0_n("true", start, i)) | ||||||
| 				emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = true, }, }); | 				emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeBool, .Bool = true }}); | ||||||
| 			else if (streq_0_n("false", start, i)) | 			else if (streq_0_n("false", start, i)) | ||||||
| 				emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = false, }, }); | 				emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeBool, .Bool = false }}); | ||||||
| 			else { | 			else { | ||||||
| 				emit(&toks, &pos, (Tok){ | 				emit(&toks, &pos, (Tok){ | ||||||
| 						.kind = TokIdent, | 						.kind = TokIdent, | ||||||
| @@ -124,9 +146,7 @@ TokList lex(const char *s) { | |||||||
| 				emit(&toks, &pos, (Tok){ | 				emit(&toks, &pos, (Tok){ | ||||||
| 						.kind = TokVal, | 						.kind = TokVal, | ||||||
| 						.Val = { | 						.Val = { | ||||||
| 							.type = { | 							.type = TypeFloat, | ||||||
| 								.kind = TypeFloat, |  | ||||||
| 							}, |  | ||||||
| 							.Float = num, | 							.Float = num, | ||||||
| 						}, | 						}, | ||||||
| 					}); | 					}); | ||||||
| @@ -142,9 +162,7 @@ TokList lex(const char *s) { | |||||||
| 				emit(&toks, &pos, (Tok){ | 				emit(&toks, &pos, (Tok){ | ||||||
| 						.kind = TokVal, | 						.kind = TokVal, | ||||||
| 						.Val = { | 						.Val = { | ||||||
| 							.type = { | 							.type = TypeInt, | ||||||
| 								.kind = TypeInt, |  | ||||||
| 							}, |  | ||||||
| 							.Int = num, | 							.Int = num, | ||||||
| 						}, | 						}, | ||||||
| 					}); | 					}); | ||||||
| @@ -200,15 +218,41 @@ TokList lex(const char *s) { | |||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 				break; | 				break; | ||||||
|  | 			case '&': | ||||||
|  | 				consume(&pos, *(s++)); | ||||||
|  | 				if (s[0] == '&') | ||||||
|  | 					emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAnd }); | ||||||
|  | 				else { | ||||||
|  | 					emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAddrOf }); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				break; | ||||||
|  | 			case '!': | ||||||
|  | 				consume(&pos, *(s++)); | ||||||
|  | 				if (s[0] == '=') | ||||||
|  | 					emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNeq }); | ||||||
|  | 				else { | ||||||
|  | 					emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpNot }); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				break; | ||||||
|  | 			case '|': | ||||||
|  | 				consume(&pos, *(s++)); | ||||||
|  | 				if (s[0] == '|') | ||||||
|  | 					emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpOr }); | ||||||
|  | 				else | ||||||
|  | 					continue; | ||||||
|  | 				break; | ||||||
| 			case '{': | 			case '{': | ||||||
| 			case '}': | 			case '}': | ||||||
| 			case '(': | 			case '(': | ||||||
| 			case ')': | 			case ')': | ||||||
|  | 			case '[': | ||||||
|  | 			case ']': | ||||||
| 			case ',': | 			case ',': | ||||||
| 			case '+': | 			case '+': | ||||||
| 			case '-': | 			case '-': | ||||||
| 			case '*': | 			case '*': | ||||||
| 			case '!': |  | ||||||
| 				emit(&toks, &pos, (Tok){ | 				emit(&toks, &pos, (Tok){ | ||||||
| 						.kind = TokOp, | 						.kind = TokOp, | ||||||
| 						.Op = s[0], | 						.Op = s[0], | ||||||
| @@ -248,6 +292,73 @@ TokList lex(const char *s) { | |||||||
| 						}); | 						}); | ||||||
| 				} | 				} | ||||||
| 				continue; | 				continue; | ||||||
|  | 			case '\'': { | ||||||
|  | 				consume(&pos, *(s++)); | ||||||
|  | 				char c = s[0]; | ||||||
|  | 				if (c == '\\') { | ||||||
|  | 					consume(&pos, *(s++)); | ||||||
|  | 					c = get_esc_char(s[0]); | ||||||
|  | 					if (!c) { | ||||||
|  | 						set_err("Unrecognized escape sequence: '\\%c'", s[0]); | ||||||
|  | 						return toks; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				consume(&pos, *(s++)); | ||||||
|  | 				if (s[0] != '\'') { | ||||||
|  | 					set_err("Unclosed char literal"); | ||||||
|  | 					return toks; | ||||||
|  | 				} | ||||||
|  | 				emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = TypeChar, .Char = c }}); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			case '"': { | ||||||
|  | 				consume(&pos, *(s++)); | ||||||
|  | 				const char *start = s; | ||||||
|  | 				Pos start_pos = pos; | ||||||
|  | 				size_t size = 0; | ||||||
|  |  | ||||||
|  | 				/* count the string size before allocating */ | ||||||
|  | 				while (s[0] != '"') { | ||||||
|  | 					if (!s[0]) { | ||||||
|  | 						set_err("Unexpected EOF in string literal"); | ||||||
|  | 						return toks; | ||||||
|  | 					} else if (s[0] == '\\') | ||||||
|  | 						consume(&pos, *(s++)); | ||||||
|  | 					consume(&pos, *(s++)); | ||||||
|  | 					size++; | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				/* go through the actual string */ | ||||||
|  | 				s = start; | ||||||
|  | 				pos = start_pos; | ||||||
|  | 				char *str = xmalloc(size); | ||||||
|  | 				for (size_t i = 0; i < size; i++) { | ||||||
|  | 					char c = s[0]; | ||||||
|  | 					if (c == '\\') { | ||||||
|  | 						consume(&pos, *(s++)); | ||||||
|  | 						c = get_esc_char(s[0]); | ||||||
|  | 						if (!c) { | ||||||
|  | 							set_err("Unrecognized escape sequence: '\\%c'", s[0]); | ||||||
|  | 							free(str); | ||||||
|  | 							return toks; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 					consume(&pos, *(s++)); | ||||||
|  | 					str[i] = c; | ||||||
|  | 				} | ||||||
|  | 				emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { | ||||||
|  | 						.type = TypeArr, | ||||||
|  | 						.Arr = { | ||||||
|  | 							.is_string = true, | ||||||
|  | 							.dynamically_allocated = false, | ||||||
|  | 							.type = TypeChar, | ||||||
|  | 							.vals = str, | ||||||
|  | 							.len = size, | ||||||
|  | 							.cap = size, | ||||||
|  | 						}, | ||||||
|  | 					},}); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
| 			default: | 			default: | ||||||
| 				set_err("Unrecognized character: '%c'", s[0]); | 				set_err("Unrecognized character: '%c'", s[0]); | ||||||
| 				return toks; | 				return toks; | ||||||
|   | |||||||
							
								
								
									
										189
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										189
									
								
								main.c
									
									
									
									
									
								
							| @@ -8,6 +8,7 @@ | |||||||
| #include "ir.h" | #include "ir.h" | ||||||
| #include "lex.h" | #include "lex.h" | ||||||
| #include "parse.h" | #include "parse.h" | ||||||
|  | #include "runtime.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
| #include "vm.h" | #include "vm.h" | ||||||
|  |  | ||||||
| @@ -33,27 +34,42 @@ static void die(const char *fmt, ...) { | |||||||
| 	exit(1); | 	exit(1); | ||||||
| } | } | ||||||
|  |  | ||||||
| static Value fn_print(Value *args) { | static void fn_put(size_t extra_args, Value *args) { | ||||||
| 	switch (args[0].type.kind) { | 	for (size_t i = 0;; i++) { | ||||||
| 		case TypeVoid:  printf("(void)\n");                              break; | 		print_value(&args[i], true); | ||||||
| 		case TypeFloat: printf("%f\n", args[0].Float);                   break; | 		if (i+1 >= extra_args) | ||||||
| 		case TypeInt:   printf("%zd\n", args[0].Int);                    break; | 			break; | ||||||
| 		case TypeBool:  printf("%s\n", args[0].Bool ? "true" : "false"); break; | 		printf(" "); | ||||||
| 		default: |  | ||||||
| 			ASSERT_UNREACHED(); |  | ||||||
| 	} | 	} | ||||||
| 	return (Value){0}; | } | ||||||
|  |  | ||||||
|  | static void fn_putln(size_t extra_args, Value *args) { | ||||||
|  | 	fn_put(extra_args, args); | ||||||
|  | 	printf("\n"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static Value fn_int(Value *args) { | static Value fn_int(Value *args) { | ||||||
| 	Value ret = { | 	Value ret = { | ||||||
| 		.type.kind = TypeInt, | 		.type = TypeInt, | ||||||
| 		.Int = 0, | 		.Int = 0, | ||||||
| 	}; | 	}; | ||||||
| 	switch (args[0].type.kind) { | 	switch (args[0].type) { | ||||||
| 		case TypeVoid: break; | 		case TypeVoid:  break; | ||||||
| 		case TypeFloat: ret.Int = (ssize_t)args[0].Float; break; | 		case TypeFloat: ret.Int = (ssize_t)args[0].Float; break; | ||||||
| 		case TypeInt:   ret.Int = args[0].Int;            break; | 		case TypeInt:   ret.Int = args[0].Int;            break; | ||||||
|  | 		case TypeBool:  ret.Int = (ssize_t)args[0].Bool;  break; | ||||||
|  | 		case TypeChar:  ret.Int = (ssize_t)args[0].Char;  break; | ||||||
|  | 		case TypeArr: | ||||||
|  | 			if (args[0].Arr.is_string && args[0].Arr.type == TypeChar) { | ||||||
|  | 				ssize_t endpos; | ||||||
|  | 				ret.Int = stoimax((char*)args[0].Arr.vals, args[0].Arr.len, 10, &endpos); | ||||||
|  | 				if (endpos != -1) { | ||||||
|  | 					set_err("Error converting from string to int"); | ||||||
|  | 					return (Value){0}; | ||||||
|  | 				} | ||||||
|  | 			} else | ||||||
|  | 				ASSERT_UNREACHED(); | ||||||
|  | 			break; | ||||||
| 		default: ASSERT_UNREACHED(); | 		default: ASSERT_UNREACHED(); | ||||||
| 	} | 	} | ||||||
| 	return ret; | 	return ret; | ||||||
| @@ -61,29 +77,138 @@ static Value fn_int(Value *args) { | |||||||
|  |  | ||||||
| static Value fn_float(Value *args) { | static Value fn_float(Value *args) { | ||||||
| 	Value ret = { | 	Value ret = { | ||||||
| 		.type.kind = TypeFloat, | 		.type = TypeFloat, | ||||||
| 		.Float = 0.0, | 		.Float = 0.0, | ||||||
| 	}; | 	}; | ||||||
| 	switch (args[0].type.kind) { | 	switch (args[0].type) { | ||||||
| 		case TypeVoid: break; | 		case TypeVoid:  break; | ||||||
| 		case TypeFloat: ret.Float = args[0].Float;       break; | 		case TypeFloat: ret.Float = args[0].Float;        break; | ||||||
| 		case TypeInt:   ret.Float = (double)args[0].Int; break; | 		case TypeInt:   ret.Float = (double)args[0].Int;  break; | ||||||
|  | 		case TypeBool:  ret.Float = (double)args[0].Bool; break; | ||||||
|  | 		case TypeChar:  ret.Float = (double)args[0].Char; break; | ||||||
|  | 		case TypeArr: | ||||||
|  | 			if (args[0].Arr.is_string && args[0].Arr.type == TypeChar) { | ||||||
|  | 				ssize_t endpos; | ||||||
|  | 				ret.Float = stod((char*)args[0].Arr.vals, args[0].Arr.len, &endpos); | ||||||
|  | 				if (endpos != -1) { | ||||||
|  | 					set_err("Error converting from string to float"); | ||||||
|  | 					return (Value){0}; | ||||||
|  | 				} | ||||||
|  | 			} else | ||||||
|  | 				ASSERT_UNREACHED(); | ||||||
|  | 			break; | ||||||
| 		default: ASSERT_UNREACHED(); | 		default: ASSERT_UNREACHED(); | ||||||
| 	} | 	} | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static Value fn_bool(Value *args) { | ||||||
|  | 	return (Value){ .type = TypeBool, .Bool = is_nonzero(&args[0]) }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Value fn_char(Value *args) { | ||||||
|  | 	Value ret = { | ||||||
|  | 		.type = TypeChar, | ||||||
|  | 		.Float = 0.0, | ||||||
|  | 	}; | ||||||
|  | 	switch (args[0].type) { | ||||||
|  | 		case TypeVoid:  break; | ||||||
|  | 		case TypeFloat: ret.Char = (char)args[0].Float; break; | ||||||
|  | 		case TypeInt:   ret.Char = (char)args[0].Int;   break; | ||||||
|  | 		case TypeBool:  ret.Char = (char)args[0].Bool;  break; | ||||||
|  | 		case TypeChar:  ret.Char = args[0].Char;        break; | ||||||
|  | 		default: ASSERT_UNREACHED(); | ||||||
|  | 	} | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Value fn_ptr(Value *args) { | ||||||
|  | 	(void)args; | ||||||
|  | 	return (Value){ .type = TypePtr, .Ptr = { .type = TypeVoid, .val = NULL }}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Value fn_string(Value *args) { | ||||||
|  | 	char *res = xmalloc(64); | ||||||
|  | 	size_t len; | ||||||
|  |  | ||||||
|  | 	switch (args[0].type) { | ||||||
|  | 		case TypeVoid:  strcpy(res, "(void)"); len = 6;               break; | ||||||
|  | 		case TypeFloat: len = snprintf(res, 64, "%f", args[0].Float); break; | ||||||
|  | 		case TypeInt:   len = snprintf(res, 64, "%zd", args[0].Int);  break; | ||||||
|  | 		case TypeBool: | ||||||
|  | 			if (args[0].Bool) { | ||||||
|  | 				strcpy(res, "true"); | ||||||
|  | 				len = 4; | ||||||
|  | 			} else { | ||||||
|  | 				strcpy(res, "false"); | ||||||
|  | 				len = 5; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		case TypeChar:  res[0] = args[0].Char; len = 1;               break; | ||||||
|  | 		default: ASSERT_UNREACHED(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return (Value){ | ||||||
|  | 		.type = TypeArr, | ||||||
|  | 		.Arr = { | ||||||
|  | 			.is_string = true, | ||||||
|  | 			.dynamically_allocated = true, | ||||||
|  | 			.type = TypeChar, | ||||||
|  | 			.vals = res, | ||||||
|  | 			.len = len, | ||||||
|  | 			.cap = 64, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  |  | ||||||
| static Value fn_pow(Value *args) { | static Value fn_pow(Value *args) { | ||||||
| 	if (!(args[0].type.kind == TypeFloat && args[1].type.kind == TypeFloat)) { | 	if (!(args[0].type == TypeFloat && args[1].type == TypeFloat)) { | ||||||
| 		set_err("pow() requires arguments of type float"); | 		set_err("pow() requires arguments of type float"); | ||||||
| 		return (Value){0}; | 		return (Value){0}; | ||||||
| 	} | 	} | ||||||
| 	return (Value) { | 	return (Value){ | ||||||
| 		.type.kind = TypeFloat, | 		.type = TypeFloat, | ||||||
| 		.Float = pow(args[0].Float, args[1].Float), | 		.Float = pow(args[0].Float, args[1].Float), | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void fn_sleep(Value *args) { | ||||||
|  | 	if (!(args[0].type == TypeFloat && args[0].Float >= 0.0)) { | ||||||
|  | 		set_err("sleep() requires a positive float"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	sleep_secs(args[0].Float); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Value fn_getln(Value *args) { | ||||||
|  | 	(void)args; | ||||||
|  |  | ||||||
|  | 	char *line = xmalloc(64); | ||||||
|  | 	size_t len = 0, cap = 64; | ||||||
|  | 	for (;;) { | ||||||
|  | 		int c = fgetc(stdin); | ||||||
|  | 		if (c == EOF) | ||||||
|  | 			break; | ||||||
|  | 		else if (c == '\n') | ||||||
|  | 			break; | ||||||
|  | 		if (len+1 > cap) | ||||||
|  | 			line = xrealloc(line, (cap *= 2)); | ||||||
|  | 		line[len++] = c; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return (Value){ | ||||||
|  | 		.type = TypeArr, | ||||||
|  | 		.Arr = { | ||||||
|  | 			.is_string = true, | ||||||
|  | 			.dynamically_allocated = true, | ||||||
|  | 			.type = TypeChar, | ||||||
|  | 			.vals = line, | ||||||
|  | 			.len = len, | ||||||
|  | 			.cap = cap, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  |  | ||||||
| int main(int argc, const char **argv) { | int main(int argc, const char **argv) { | ||||||
| 	/* parse arguments */ | 	/* parse arguments */ | ||||||
| 	size_t nargs = argc - 1; | 	size_t nargs = argc - 1; | ||||||
| @@ -141,29 +266,37 @@ int main(int argc, const char **argv) { | |||||||
| 		print_toks(&tokens); | 		print_toks(&tokens); | ||||||
| 	/* parse tokens into IR code */ | 	/* parse tokens into IR code */ | ||||||
| 	BuiltinFunc funcs[] = { | 	BuiltinFunc funcs[] = { | ||||||
| 		{ .name = "print", .side_effects = true,  .n_args = 1, .func = fn_print, }, | 		{ .name = "put",    .kind = FuncVarArgs,   .returns = false, .side_effects = true,  .VarArgs   = { .min_args = 0, .NoRet.func   = fn_put,    }}, | ||||||
| 		{ .name = "int",   .side_effects = false, .n_args = 1, .func = fn_int,   }, | 		{ .name = "putln",  .kind = FuncVarArgs,   .returns = false, .side_effects = true,  .VarArgs   = { .min_args = 0, .NoRet.func   = fn_putln,  }}, | ||||||
| 		{ .name = "float", .side_effects = false, .n_args = 1, .func = fn_float, }, | 		{ .name = "int",    .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 1,   .WithRet.func = fn_int,    }}, | ||||||
| 		{ .name = "pow",   .side_effects = false, .n_args = 2, .func = fn_pow,   }, | 		{ .name = "float",  .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 1,   .WithRet.func = fn_float,  }}, | ||||||
|  | 		{ .name = "bool",   .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 1,   .WithRet.func = fn_bool,   }}, | ||||||
|  | 		{ .name = "char",   .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 1,   .WithRet.func = fn_char,   }}, | ||||||
|  | 		{ .name = "ptr",    .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 0,   .WithRet.func = fn_ptr,    }}, | ||||||
|  | 		{ .name = "string", .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 1,   .WithRet.func = fn_string, }}, | ||||||
|  | 		{ .name = "pow",    .kind = FuncFixedArgs, .returns = true,  .side_effects = false, .FixedArgs = { .n_args = 2,   .WithRet.func = fn_pow,    }}, | ||||||
|  | 		{ .name = "sleep",  .kind = FuncFixedArgs, .returns = false, .side_effects = true,  .FixedArgs = { .n_args = 1,   .NoRet.func   = fn_sleep,  }}, | ||||||
|  | 		{ .name = "getln",  .kind = FuncFixedArgs, .returns = true,  .side_effects = true,  .FixedArgs = { .n_args = 0,   .WithRet.func = fn_getln,  }}, | ||||||
| 	}; | 	}; | ||||||
| 	IRToks ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0])); | 	IRList ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0])); | ||||||
| 	if (err) { | 	if (err) { | ||||||
| 		irtoks_term(&ir); | 		irlist_term(&ir); | ||||||
| 		toklist_term(&tokens); | 		toklist_term(&tokens); | ||||||
| 		fprintf(stderr, C_IRED "Parser error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); | 		fprintf(stderr, C_IRED "Parser error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); | ||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
| 	toklist_term(&tokens); | 	toklist_term(&tokens); | ||||||
|  | 	optimize_ir(&ir); | ||||||
| 	if (opt_emit_ir) | 	if (opt_emit_ir) | ||||||
| 		print_ir(&ir, funcs); | 		print_ir(&ir, funcs); | ||||||
| 	/* run the IR */ | 	/* run the IR */ | ||||||
| 	if (!opt_dry) { | 	if (!opt_dry) { | ||||||
| 		run(&ir, funcs); | 		run(&ir, funcs); | ||||||
| 		if (err) { | 		if (err) { | ||||||
| 			irtoks_term(&ir); | 			irlist_term(&ir); | ||||||
| 			fprintf(stderr, C_IRED "Runtime error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); | 			fprintf(stderr, C_IRED "Runtime error" C_RESET " in " C_CYAN "%s" C_RESET ":%zu:%zu: %s\n", filename, err_ln, err_col, errbuf); | ||||||
| 			return 1; | 			return 1; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	irtoks_term(&ir); | 	irlist_term(&ir); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										432
									
								
								parse.c
									
									
									
									
									
								
							
							
						
						
									
										432
									
								
								parse.c
									
									
									
									
									
								
							| @@ -5,8 +5,6 @@ | |||||||
| #include "map.h" | #include "map.h" | ||||||
| #include "runtime.h" | #include "runtime.h" | ||||||
|  |  | ||||||
| static BuiltinFunc *bf; |  | ||||||
|  |  | ||||||
| typedef struct Scope { | typedef struct Scope { | ||||||
| 	struct Scope *parent; | 	struct Scope *parent; | ||||||
| 	size_t mem_addr; | 	size_t mem_addr; | ||||||
| @@ -32,10 +30,13 @@ static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpo | |||||||
| static IRParam tok_to_irparam(Scope *sc, Tok *t); | static IRParam tok_to_irparam(Scope *sc, Tok *t); | ||||||
| static Scope make_scope(Scope *parent, bool with_idents); | static Scope make_scope(Scope *parent, bool with_idents); | ||||||
| static void term_scope(Scope *sc); | static void term_scope(Scope *sc); | ||||||
| static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); | static bool expr_flush_ir_and_maybe_return(IRList *out_ir, TokList *toks, IRTok instr, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret); | ||||||
| static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr); | static void make_value_statically_allocated(Value *v); | ||||||
| static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); | static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); | ||||||
| static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t); | static void expr_into_addr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr); | ||||||
|  | static IRParam expr_into_irparam(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); | ||||||
|  | static void skip_newlns(TokList *toks, TokListItem *from); | ||||||
|  | static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t); | ||||||
|  |  | ||||||
| static void mark_err(const Tok *t) { | static void mark_err(const Tok *t) { | ||||||
| 	err_ln = t->ln; | 	err_ln = t->ln; | ||||||
| @@ -47,6 +48,7 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) { | |||||||
| 		case IRSet: | 		case IRSet: | ||||||
| 		case IRNeg: | 		case IRNeg: | ||||||
| 		case IRNot: | 		case IRNot: | ||||||
|  | 		case IRAddrOf: | ||||||
| 			t->Unary.addr = addr; | 			t->Unary.addr = addr; | ||||||
| 			break; | 			break; | ||||||
| 		case IRAdd: | 		case IRAdd: | ||||||
| @@ -54,13 +56,19 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) { | |||||||
| 		case IRMul: | 		case IRMul: | ||||||
| 		case IRDiv: | 		case IRDiv: | ||||||
| 		case IREq: | 		case IREq: | ||||||
|  | 		case IRNeq: | ||||||
| 		case IRLt: | 		case IRLt: | ||||||
| 		case IRLe: | 		case IRLe: | ||||||
|  | 		case IRAnd: | ||||||
|  | 		case IROr: | ||||||
| 			t->Binary.addr = addr; | 			t->Binary.addr = addr; | ||||||
| 			break; | 			break; | ||||||
| 		case IRCallInternal: | 		case IRCallInternal: | ||||||
| 			t->CallI.ret_addr = addr; | 			t->CallI.ret_addr = addr; | ||||||
| 			break; | 			break; | ||||||
|  | 		case IRArrMake: | ||||||
|  | 			t->ArrMake.arr_addr = addr; | ||||||
|  | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			ASSERT_UNREACHED(); | 			ASSERT_UNREACHED(); | ||||||
| 	} | 	} | ||||||
| @@ -119,6 +127,50 @@ static void term_scope(Scope *sc) { | |||||||
| 		map_term(&sc->ident_addrs); | 		map_term(&sc->ident_addrs); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* If ir_tok is the underlying expr() call's last evaluation, this function | ||||||
|  |  * deletes t from toks, sets *out_ret and tells the caller it can return | ||||||
|  |  * *out_ret by returning true. | ||||||
|  |  * | ||||||
|  |  * If ir_tok is not the expression's last instruction, ir_tok is written to | ||||||
|  |  * out_ir and t is replaced by a pointer to the result's memory address. | ||||||
|  |  *  */ | ||||||
|  | static bool expr_flush_ir_and_maybe_return(IRList *out_ir, TokList *toks, IRTok ir_tok, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret) { | ||||||
|  | 	if (t == expr_start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM) { | ||||||
|  | 		/* ir_tok was the expression's last IR instruction. */ | ||||||
|  |  | ||||||
|  | 		toklist_del(toks, t, t); | ||||||
|  |  | ||||||
|  | 		*out_ret = (ExprRet){ | ||||||
|  | 			.kind = ExprRetLastInstr, | ||||||
|  | 			.LastInstr = ir_tok, | ||||||
|  | 		}; | ||||||
|  | 		return true; | ||||||
|  | 	} else { | ||||||
|  | 		/* ir_tok was not the expression's last IR instruction. */ | ||||||
|  |  | ||||||
|  | 		size_t dest_addr = expr_scope->mem_addr++; | ||||||
|  |  | ||||||
|  | 		set_irtok_dest_addr(&ir_tok, dest_addr); | ||||||
|  | 		irlist_app(out_ir, ir_tok); | ||||||
|  |  | ||||||
|  | 		t->tok = (Tok){ | ||||||
|  | 			.kind = TokIdent, | ||||||
|  | 			.Ident = { | ||||||
|  | 				.kind = IdentAddr, | ||||||
|  | 				.Addr = dest_addr, | ||||||
|  | 			}, | ||||||
|  | 		}; | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void make_value_statically_allocated(Value *v) { | ||||||
|  | 	switch (v->type) { | ||||||
|  | 		case TypeArr: v->Arr.dynamically_allocated = false; break; | ||||||
|  | 		default: break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| /* The job of this function is to reduce the expression to the most simple form | /* The job of this function is to reduce the expression to the most simple form | ||||||
|  * writing the least IR instructions possible (without overanalyzing). |  * writing the least IR instructions possible (without overanalyzing). | ||||||
|  * This means that the only IR instructions it will be writing are those for |  * This means that the only IR instructions it will be writing are those for | ||||||
| @@ -170,7 +222,7 @@ static void term_scope(Scope *sc) { | |||||||
|  *  l_op  r_op |  *  l_op  r_op | ||||||
|  *  both l_op and r_op are delimiters (their precedence is PREC_DELIM) => done |  *  both l_op and r_op are delimiters (their precedence is PREC_DELIM) => done | ||||||
|  */ |  */ | ||||||
| static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) { | static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) { | ||||||
| 	TokListItem *start = t; | 	TokListItem *start = t; | ||||||
|  |  | ||||||
| 	Scope sc = make_scope(parent_sc, false); | 	Scope sc = make_scope(parent_sc, false); | ||||||
| @@ -188,6 +240,10 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 				t = t->next; | 				t = t->next; | ||||||
| 				perform_unary = true; | 				perform_unary = true; | ||||||
| 				unary_op = IRNot; | 				unary_op = IRNot; | ||||||
|  | 			} else if (t->tok.Op == OpAddrOf) { | ||||||
|  | 				t = t->next; | ||||||
|  | 				perform_unary = true; | ||||||
|  | 				unary_op = IRAddrOf; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -206,7 +262,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 			if (r.kind == ExprRetLastInstr) { | 			if (r.kind == ExprRetLastInstr) { | ||||||
| 				size_t res_addr = sc.mem_addr++; | 				size_t res_addr = sc.mem_addr++; | ||||||
| 				set_irtok_dest_addr(&r.LastInstr, res_addr); | 				set_irtok_dest_addr(&r.LastInstr, res_addr); | ||||||
| 				irtoks_app(out_ir, r.LastInstr); | 				irlist_app(out_ir, r.LastInstr); | ||||||
| 				t->tok = (Tok){ | 				t->tok = (Tok){ | ||||||
| 					.ln = t->tok.ln, | 					.ln = t->tok.ln, | ||||||
| 					.col = t->tok.col, | 					.col = t->tok.col, | ||||||
| @@ -223,6 +279,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 				ASSERT_UNREACHED(); | 				ASSERT_UNREACHED(); | ||||||
| 			toklist_del(toks, t->next, t->next); | 			toklist_del(toks, t->next, t->next); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/* Collapse function call. */ | 		/* Collapse function call. */ | ||||||
| 		else if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && t->next->tok.kind == TokOp && t->next->tok.Op == OpLParen) { | 		else if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && t->next->tok.kind == TokOp && t->next->tok.Op == OpLParen) { | ||||||
| 			/* get function */ | 			/* get function */ | ||||||
| @@ -240,88 +297,197 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 			bool eval_func_in_place = !func.side_effects; | 			bool eval_func_in_place = !func.side_effects; | ||||||
|  |  | ||||||
| 			size_t args_len = 0; | 			size_t args_len = 0; | ||||||
| 			size_t args_cap = 16; | 			IRParam *args = NULL; | ||||||
| 			IRParam *args = xmalloc(sizeof(IRParam) * args_cap); |  | ||||||
| 			for (;;) { | 			if (t->next->tok.kind == TokOp && t->next->tok.Op == OpRParen) { | ||||||
| 				if (args_len+1 > args_cap) | 				/* no args */ | ||||||
| 					args = xrealloc(args, (args_cap *= 2)); | 				toklist_del(toks, t->next, t->next); /* delete right parenthesis */ | ||||||
| 				IRParam a; | 			} else { | ||||||
| 				TRY_RET_ELSE(a = expr_into_irparam(out_ir, toks, funcs, &sc, t->next), (ExprRet){0}, free(args)); | 				/* go through the arguments, evaluate them and put them into the args array */ | ||||||
| 				args[args_len++] = a; | 				size_t args_cap = 16; | ||||||
| 				if (a.kind != IRParamLiteral) | 				args = xmalloc(sizeof(IRParam) * args_cap); | ||||||
| 					eval_func_in_place = false; | 				for (;;) { | ||||||
| 				if (t->next->tok.kind == TokOp) { | 					if (args_len+1 > args_cap) | ||||||
| 					if (t->next->tok.Op == OpComma) { | 						args = xrealloc(args, (args_cap *= 2)); | ||||||
| 						toklist_del(toks, t->next, t->next); | 					IRParam a; | ||||||
| 						continue; | 					TRY_RET_ELSE(a = expr_into_irparam(out_ir, toks, funcs, &sc, t->next), (ExprRet){0}, free(args)); | ||||||
| 					} else if (t->next->tok.Op == OpRParen) { | 					args[args_len++] = a; | ||||||
| 						toklist_del(toks, t->next, t->next); | 					if (a.kind != IRParamLiteral) | ||||||
| 						break; | 						eval_func_in_place = false; | ||||||
|  | 					if (t->next->tok.kind == TokOp) { | ||||||
|  | 						if (t->next->tok.Op == OpComma) { | ||||||
|  | 							toklist_del(toks, t->next, t->next); /* delete comma */ | ||||||
|  | 							continue; | ||||||
|  | 						} else if (t->next->tok.Op == OpRParen) { | ||||||
|  | 							toklist_del(toks, t->next, t->next); /* delete right parenthesis */ | ||||||
|  | 							break; | ||||||
|  | 						} | ||||||
| 					} | 					} | ||||||
|  | 					mark_err(&t->next->tok); | ||||||
|  | 					set_err("Expected ',' or ')' after function argument"); | ||||||
|  | 					free(args); | ||||||
|  | 					return (ExprRet){0}; | ||||||
| 				} | 				} | ||||||
| 				mark_err(&t->next->tok); |  | ||||||
| 				set_err("Expected ',' or ')' after function argument"); |  | ||||||
| 				free(args); |  | ||||||
| 				return (ExprRet){0}; |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			t = func_ident; | 			t = func_ident; | ||||||
| 			toklist_del(toks, t->next, t->next); /* delete left parenthesis */ | 			toklist_del(toks, t->next, t->next); /* delete left parenthesis */ | ||||||
|  |  | ||||||
| 			if (func.n_args != args_len) { | 			if (func.kind == FuncFixedArgs && args_len != func.FixedArgs.n_args) { | ||||||
| 				mark_err(&func_ident->tok); | 				mark_err(&func_ident->tok); | ||||||
| 				const char *plural = func.n_args == 1 ? "" : "s"; | 				const char *plural = func.FixedArgs.n_args == 1 ? "" : "s"; | ||||||
| 				set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.n_args, plural, args_len); | 				set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.FixedArgs.n_args, plural, args_len); | ||||||
| 				free(args); | 				if (args) | ||||||
|  | 					free(args); | ||||||
|  | 				return (ExprRet){0}; | ||||||
|  | 			} else if (func.kind == FuncVarArgs && args_len < func.VarArgs.min_args) { | ||||||
|  | 				mark_err(&func_ident->tok); | ||||||
|  | 				const char *plural = func.VarArgs.min_args == 1 ? "" : "s"; | ||||||
|  | 				set_err("Function %s() requires at least %zu argument%s but only got %zu", func.name, func.VarArgs.min_args, plural, args_len); | ||||||
|  | 				if (args) | ||||||
|  | 					free(args); | ||||||
| 				return (ExprRet){0}; | 				return (ExprRet){0}; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if (eval_func_in_place) { | 			if (eval_func_in_place) { | ||||||
| 				/* evaluate the function in place */ | 				/* evaluate the function in place */ | ||||||
| 				Value *arg_vals = xmalloc(sizeof(Value) * args_len); | 				if (!func.returns) | ||||||
|  | 					/* If the function had no side effects and returned nothing, | ||||||
|  | 					 * that function would do absolutely nothing, which would | ||||||
|  | 					 * make no sense. */ | ||||||
|  | 					ASSERT_UNREACHED(); | ||||||
|  | 				Value *arg_vals = args_len ? xmalloc(sizeof(Value) * args_len) : NULL; | ||||||
| 				for (size_t i = 0; i < args_len; i++) | 				for (size_t i = 0; i < args_len; i++) | ||||||
| 					arg_vals[i] = args[i].Literal; | 					arg_vals[i] = args[i].Literal; | ||||||
|  | 				mark_err(&func_ident->tok); | ||||||
| 				func_ident->tok = (Tok) { | 				func_ident->tok = (Tok) { | ||||||
| 					.kind = TokVal, | 					.kind = TokVal, | ||||||
| 					.Val = func.func(arg_vals), | 					.Val = func.kind == FuncVarArgs ? | ||||||
|  | 						func.VarArgs.WithRet.func(args_len - func.VarArgs.min_args, arg_vals) | ||||||
|  | 						: func.FixedArgs.WithRet.func(arg_vals), | ||||||
| 				}; | 				}; | ||||||
| 				free(arg_vals); | 				/* since we have a literal return value, we want it to be fully treated like one by the memory manager */ | ||||||
| 				free(args); | 				make_value_statically_allocated(&func_ident->tok.Val); | ||||||
|  | 				/* immediately free any heap-allocated literals that are no longer needed */ | ||||||
|  | 				for (size_t i = 0; i < args_len; i++) | ||||||
|  | 					free_value(&arg_vals[i], true); | ||||||
|  | 				/* free buffers */ | ||||||
|  | 				if (arg_vals) | ||||||
|  | 					free(arg_vals); | ||||||
|  | 				if (args) | ||||||
|  | 					free(args); | ||||||
| 			} else { | 			} else { | ||||||
| 				bool is_last_operation = t == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM; | 				/* function call IR instruction */ | ||||||
|  | 				IRTok ir_tok = { | ||||||
| 				size_t res_addr = is_last_operation ? 0 : sc.mem_addr++; |  | ||||||
|  |  | ||||||
| 				/* function call instruction */ |  | ||||||
| 				IRTok ir = { |  | ||||||
| 					.ln =  func_ident->tok.ln, | 					.ln =  func_ident->tok.ln, | ||||||
| 					.col = func_ident->tok.col, | 					.col = func_ident->tok.col, | ||||||
| 					.instr = IRCallInternal, | 					.instr = IRCallInternal, | ||||||
| 					.CallI = { | 					.CallI = { | ||||||
| 						.ret_addr = res_addr, | 						.ret_addr = 0, | ||||||
| 						.fid = func.fid, | 						.fid = func.fid, | ||||||
|  | 						.n_args = args_len, | ||||||
| 						.args = args, | 						.args = args, | ||||||
| 					}, | 					}, | ||||||
| 				}; | 				}; | ||||||
|  |  | ||||||
| 				if (is_last_operation) { | 				/* return if we've just evaluated the last instruction */ | ||||||
| 					/* done */ | 				ExprRet ret; | ||||||
| 					toklist_del(toks, t, t); | 				if (expr_flush_ir_and_maybe_return(out_ir, toks, ir_tok, start, &sc, func_ident, &ret)) | ||||||
| 					return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir }; | 					return ret; | ||||||
| 				} else { | 			} | ||||||
| 					/* write IR instruction */ | 		} | ||||||
| 					irtoks_app(out_ir, ir); |  | ||||||
|  |  | ||||||
| 					/* leave new memory address as result */ | 		/* Collapse array. */ | ||||||
| 					t->tok = (Tok){ | 		else if (t->tok.kind == TokOp && t->tok.Op == OpLBrack) { | ||||||
| 						.kind = TokIdent, | 			TokListItem *lbrack = t; | ||||||
| 						.Ident = { | 			bool eval_immediately = true; | ||||||
| 							.kind = IdentAddr, | 			size_t elems_len = 0; | ||||||
| 							.Addr = res_addr, | 			size_t elems_cap = 0; | ||||||
| 						}, | 			IRParam *elems = NULL; | ||||||
| 					}; |  | ||||||
|  | 			if (t->next->tok.kind == TokOp && t->next->tok.Op == OpRBrack) { | ||||||
|  | 				/* empty array */ | ||||||
|  | 				toklist_del(toks, t->next, t->next); /* delete right bracket */ | ||||||
|  | 			} else { | ||||||
|  | 				elems_cap = 16; | ||||||
|  | 				elems = xmalloc(sizeof(IRParam) * elems_cap); | ||||||
|  | 				for (;;) { | ||||||
|  | 					if (elems_len+1 > elems_cap) | ||||||
|  | 						elems = xrealloc(elems, (elems_cap *= 2)); | ||||||
|  | 					IRParam e; | ||||||
|  | 					TRY_RET_ELSE(e = expr_into_irparam(out_ir, toks, funcs, &sc, t->next), (ExprRet){0}, free(elems)); | ||||||
|  | 					if (e.kind != IRParamLiteral) | ||||||
|  | 						eval_immediately = false; | ||||||
|  | 					elems[elems_len++] = e; | ||||||
|  | 					if (t->next->tok.kind == TokOp) { | ||||||
|  | 						if (t->next->tok.Op == OpComma) { | ||||||
|  | 							toklist_del(toks, t->next, t->next); /* delete comma */ | ||||||
|  | 							continue; | ||||||
|  | 						} else if (t->next->tok.Op == OpRBrack) { | ||||||
|  | 							toklist_del(toks, t->next, t->next); /* delete right bracket */ | ||||||
|  | 							break; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 					mark_err(&t->next->tok); | ||||||
|  | 					set_err("Expected ',' or ']' after array element"); | ||||||
|  | 					free(elems); | ||||||
|  | 					return (ExprRet){0}; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			if (eval_immediately) { | ||||||
|  | 				/* turn array into value */ | ||||||
|  | 				Value arr = { | ||||||
|  | 					.type = TypeArr, | ||||||
|  | 					.Arr = { | ||||||
|  | 						.type = TypeVoid, | ||||||
|  | 						.is_string = false, | ||||||
|  | 						.dynamically_allocated = false, | ||||||
|  | 						.vals = NULL, | ||||||
|  | 						.len = elems_len, | ||||||
|  | 						.cap = elems_len ? elems_cap : 0, | ||||||
|  | 					}, | ||||||
|  | 				}; | ||||||
|  | 				if (elems_len) { | ||||||
|  | 					Type arr_ty = elems[0].Literal.type; | ||||||
|  | 					void *arr_vals = xmalloc(type_size[arr_ty] * elems_cap); | ||||||
|  | 					for (size_t i = 0; i < elems_len; i++) { | ||||||
|  | 						Value *v = &elems[i].Literal; | ||||||
|  | 						if (v->type != arr_ty) { | ||||||
|  | 							set_err("Type of array item %zu (%s) differs from array type (%s)", i, type_str[v->type], type_str[arr_ty]); | ||||||
|  | 							free(arr_vals); | ||||||
|  | 							free(elems); | ||||||
|  | 							return (ExprRet){0}; | ||||||
|  | 						} | ||||||
|  | 						memcpy((uint8_t*)arr_vals + type_size[arr_ty] * i, &v->Void, type_size[arr_ty]); | ||||||
|  | 					} | ||||||
|  | 					arr.Arr.type = arr_ty; | ||||||
|  | 					arr.Arr.vals = arr_vals; | ||||||
|  | 				} | ||||||
|  | 				/* set lbracket to collapsed array value */ | ||||||
|  | 				lbrack->tok.kind = TokVal; | ||||||
|  | 				lbrack->tok.Val = arr; | ||||||
|  | 				/* free the now no longer needed element IRParam values */ | ||||||
|  | 				free(elems); | ||||||
|  | 			} else { | ||||||
|  | 				/* array initialization IR instruction */ | ||||||
|  | 				IRTok ir_tok = { | ||||||
|  | 					.ln =  lbrack->tok.ln, | ||||||
|  | 					.col = lbrack->tok.col, | ||||||
|  | 					.instr = IRArrMake, | ||||||
|  | 					.ArrMake = { | ||||||
|  | 						.arr_addr = 0, | ||||||
|  | 						.len = elems_len, | ||||||
|  | 						.cap = elems_cap, | ||||||
|  | 						.vals = elems, | ||||||
|  | 					}, | ||||||
|  | 				}; | ||||||
|  |  | ||||||
|  | 				/* return if we've just evaluated the last instruction */ | ||||||
|  | 				ExprRet ret; | ||||||
|  | 				if (expr_flush_ir_and_maybe_return(out_ir, toks, ir_tok, start, &sc, lbrack, &ret)) | ||||||
|  | 					return ret; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/* Collapse unary operation. */ | 		/* Collapse unary operation. */ | ||||||
| @@ -330,43 +496,29 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 			t = t->prev; /* go back to the '-' sign */ | 			t = t->prev; /* go back to the '-' sign */ | ||||||
| 			toklist_del(toks, t->next, t->next); /* again, just removing the reference */ | 			toklist_del(toks, t->next, t->next); /* again, just removing the reference */ | ||||||
|  |  | ||||||
| 			bool is_last_operation = t == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM; |  | ||||||
|  |  | ||||||
| 			if (v->kind == TokVal) { | 			if (v->kind == TokVal) { | ||||||
| 				/* immediately perform operation */ | 				/* immediately perform operation */ | ||||||
| 				t->tok.kind = TokVal; | 				t->tok.kind = TokVal; | ||||||
|  | 				mark_err(&t->tok); | ||||||
| 				TRY_RET(t->tok.Val = eval_unary(unary_op, &v->Val), (ExprRet){0}); | 				TRY_RET(t->tok.Val = eval_unary(unary_op, &v->Val), (ExprRet){0}); | ||||||
| 			} else { | 			} else { | ||||||
| 				size_t res_addr = is_last_operation ? 0 : sc.mem_addr++; |  | ||||||
|  |  | ||||||
| 				/* unary IR instruction */ | 				/* unary IR instruction */ | ||||||
| 				IRParam v_irparam; | 				IRParam v_irparam; | ||||||
| 				TRY_RET(v_irparam = tok_to_irparam(&sc, v), (ExprRet){0}); | 				TRY_RET(v_irparam = tok_to_irparam(&sc, v), (ExprRet){0}); | ||||||
| 				IRTok ir = { | 				IRTok ir_tok = { | ||||||
| 					.ln = t->tok.ln, | 					.ln = t->tok.ln, | ||||||
| 					.col = t->tok.col, | 					.col = t->tok.col, | ||||||
| 					.instr = unary_op, | 					.instr = unary_op, | ||||||
| 					.Unary = { | 					.Unary = { | ||||||
| 						.addr = res_addr, | 						.addr = 0, | ||||||
| 						.val = v_irparam, | 						.val = v_irparam, | ||||||
| 					}, | 					}, | ||||||
| 				}; | 				}; | ||||||
|  |  | ||||||
| 				if (is_last_operation) { | 				/* return if we've just evaluated the last instruction */ | ||||||
| 					/* done */ | 				ExprRet ret; | ||||||
| 					toklist_del(toks, t, t); | 				if (expr_flush_ir_and_maybe_return(out_ir, toks, ir_tok, start, &sc, t, &ret)) | ||||||
| 					return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir }; | 					return ret; | ||||||
| 				} else { |  | ||||||
| 					/* write IR instruction */ |  | ||||||
| 					irtoks_app(out_ir, ir); |  | ||||||
|  |  | ||||||
| 					/* leave new memory address as result */ |  | ||||||
| 					t->tok.kind = TokIdent; |  | ||||||
| 					t->tok.Ident = (Identifier){ |  | ||||||
| 						.kind = IdentAddr, |  | ||||||
| 						.Addr = res_addr, |  | ||||||
| 					}; |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -442,10 +594,13 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 				case OpMul: instr = IRMul; break; | 				case OpMul: instr = IRMul; break; | ||||||
| 				case OpDiv: instr = IRDiv; break; | 				case OpDiv: instr = IRDiv; break; | ||||||
| 				case OpEq:  instr = IREq;  break; | 				case OpEq:  instr = IREq;  break; | ||||||
|  | 				case OpNeq: instr = IRNeq; break; | ||||||
| 				case OpLt:  instr = IRLt;  break; | 				case OpLt:  instr = IRLt;  break; | ||||||
| 				case OpLe:  instr = IRLe;  break; | 				case OpLe:  instr = IRLe;  break; | ||||||
| 				case OpGt:  instr = IRLt; swap_operands = true; break; | 				case OpGt:  instr = IRLt; swap_operands = true; break; | ||||||
| 				case OpGe:  instr = IRLe; swap_operands = true; break; | 				case OpGe:  instr = IRLe; swap_operands = true; break; | ||||||
|  | 				case OpAnd: instr = IRAnd; break; | ||||||
|  | 				case OpOr:  instr = IROr;  break; | ||||||
| 				default: | 				default: | ||||||
| 					mark_err(l_op); | 					mark_err(l_op); | ||||||
| 					set_err("Unknown operation: '%s'", op_str[l_op->Op]); | 					set_err("Unknown operation: '%s'", op_str[l_op->Op]); | ||||||
| @@ -457,53 +612,40 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, | |||||||
| 				Value *lhs_val = swap_operands ? &rhs->Val : &lhs->Val; | 				Value *lhs_val = swap_operands ? &rhs->Val : &lhs->Val; | ||||||
| 				Value *rhs_val = swap_operands ? &lhs->Val : &rhs->Val; | 				Value *rhs_val = swap_operands ? &lhs->Val : &rhs->Val; | ||||||
| 				lhs->kind = TokVal; | 				lhs->kind = TokVal; | ||||||
|  | 				mark_err(l_op); | ||||||
| 				TRY_RET(lhs->Val = eval_binary(instr, lhs_val, rhs_val), (ExprRet){0}); | 				TRY_RET(lhs->Val = eval_binary(instr, lhs_val, rhs_val), (ExprRet){0}); | ||||||
| 			} else { | 			} else { | ||||||
| 				bool is_last_operation = t == start && r_op_prec == PREC_DELIM; |  | ||||||
|  |  | ||||||
| 				IRParam lhs_irparam, rhs_irparam; | 				IRParam lhs_irparam, rhs_irparam; | ||||||
| 				TRY_RET(lhs_irparam = tok_to_irparam(&sc, lhs), (ExprRet){0}); | 				TRY_RET(lhs_irparam = tok_to_irparam(&sc, lhs), (ExprRet){0}); | ||||||
| 				TRY_RET(rhs_irparam = tok_to_irparam(&sc, rhs), (ExprRet){0}); | 				TRY_RET(rhs_irparam = tok_to_irparam(&sc, rhs), (ExprRet){0}); | ||||||
|  |  | ||||||
| 				size_t res_addr = is_last_operation ? 0 : sc.mem_addr++; | 				/* binary IR instruction */ | ||||||
|  | 				IRTok ir_tok = { | ||||||
| 				IRTok ir = { |  | ||||||
| 					.ln = l_op->ln, | 					.ln = l_op->ln, | ||||||
| 					.col = l_op->col, | 					.col = l_op->col, | ||||||
| 					.instr = instr, | 					.instr = instr, | ||||||
| 					.Binary = { | 					.Binary = { | ||||||
| 						.addr = res_addr, | 						.addr = 0, | ||||||
| 						.lhs = swap_operands ? rhs_irparam : lhs_irparam, | 						.lhs = swap_operands ? rhs_irparam : lhs_irparam, | ||||||
| 						.rhs = swap_operands ? lhs_irparam : rhs_irparam, | 						.rhs = swap_operands ? lhs_irparam : rhs_irparam, | ||||||
| 					}, | 					}, | ||||||
| 				}; | 				}; | ||||||
| 				 |  | ||||||
| 				if (is_last_operation) { |  | ||||||
| 					/* done */ |  | ||||||
| 					toklist_del(toks, t, t); |  | ||||||
| 					return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir }; |  | ||||||
| 				} else { |  | ||||||
| 					/* write IR instruction */ |  | ||||||
| 					irtoks_app(out_ir, ir); |  | ||||||
|  |  | ||||||
| 					/* leave new memory address as result */ | 				/* return if we've just evaluated the last instruction */ | ||||||
| 					lhs->kind = TokIdent; | 				ExprRet ret; | ||||||
| 					lhs->Ident = (Identifier){ | 				if (expr_flush_ir_and_maybe_return(out_ir, toks, ir_tok, start, &sc, t, &ret)) | ||||||
| 						.kind = IdentAddr, | 					return ret; | ||||||
| 						.Addr = res_addr, |  | ||||||
| 					}; |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr) { | static void expr_into_addr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr) { | ||||||
| 	ExprRet r; | 	ExprRet r; | ||||||
| 	TRY(r = expr(out_ir, toks, funcs, parent_sc, t)); | 	TRY(r = expr(out_ir, toks, funcs, parent_sc, t)); | ||||||
| 	if (r.kind == ExprRetLastInstr) { | 	if (r.kind == ExprRetLastInstr) { | ||||||
| 		set_irtok_dest_addr(&r.LastInstr, addr); | 		set_irtok_dest_addr(&r.LastInstr, addr); | ||||||
| 		irtoks_app(out_ir, r.LastInstr); | 		irlist_app(out_ir, r.LastInstr); | ||||||
| 		t->tok = (Tok){ | 		t->tok = (Tok){ | ||||||
| 			.ln = t->tok.ln, | 			.ln = t->tok.ln, | ||||||
| 			.col = t->tok.col, | 			.col = t->tok.col, | ||||||
| @@ -516,7 +658,9 @@ static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *par | |||||||
| 	} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) { | 	} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) { | ||||||
| 		IRParam res; | 		IRParam res; | ||||||
| 		TRY(res = tok_to_irparam(parent_sc, &t->tok)); | 		TRY(res = tok_to_irparam(parent_sc, &t->tok)); | ||||||
| 		irtoks_app(out_ir, (IRTok){ | 		irlist_app(out_ir, (IRTok){ | ||||||
|  | 			.ln = t->tok.ln, | ||||||
|  | 			.col = t->tok.col, | ||||||
| 			.instr = IRSet, | 			.instr = IRSet, | ||||||
| 			.Unary = { | 			.Unary = { | ||||||
| 				.addr = addr, | 				.addr = addr, | ||||||
| @@ -528,14 +672,14 @@ static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *par | |||||||
| 		ASSERT_UNREACHED(); | 		ASSERT_UNREACHED(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) { | static IRParam expr_into_irparam(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) { | ||||||
| 	ExprRet r; | 	ExprRet r; | ||||||
| 	TRY_RET(r = expr(out_ir, toks, funcs, parent_sc, t), (IRParam){0}); | 	TRY_RET(r = expr(out_ir, toks, funcs, parent_sc, t), (IRParam){0}); | ||||||
| 	if (r.kind == ExprRetLastInstr) { | 	if (r.kind == ExprRetLastInstr) { | ||||||
| 		Scope sc = make_scope(parent_sc, false); | 		Scope sc = make_scope(parent_sc, false); | ||||||
| 		size_t addr = sc.mem_addr++; | 		size_t addr = sc.mem_addr++; | ||||||
| 		set_irtok_dest_addr(&r.LastInstr, addr); | 		set_irtok_dest_addr(&r.LastInstr, addr); | ||||||
| 		irtoks_app(out_ir, r.LastInstr); | 		irlist_app(out_ir, r.LastInstr); | ||||||
| 		return (IRParam){ | 		return (IRParam){ | ||||||
| 			.kind = IRParamAddr, | 			.kind = IRParamAddr, | ||||||
| 			.Addr = addr, | 			.Addr = addr, | ||||||
| @@ -549,20 +693,30 @@ static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scop | |||||||
| 		ASSERT_UNREACHED(); | 		ASSERT_UNREACHED(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) { | /* This WILL invalidate *from, so the caller should only call it on a | ||||||
|  |  * TokListItem after any ones that are in use (e.g. skip_newlns(t->next)). */ | ||||||
|  | static void skip_newlns(TokList *toks, TokListItem *from) { | ||||||
|  | 	TokListItem *curr = from; | ||||||
|  | 	while (curr->tok.kind == TokOp && curr->tok.Op == OpNewLn) | ||||||
|  | 		curr = curr->next; | ||||||
|  | 	if (curr != from) | ||||||
|  | 		toklist_del(toks, from, curr->prev); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void stmt(IRList *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) { | ||||||
| 	TokListItem *start = t; | 	TokListItem *start = t; | ||||||
| 	if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && (t->next->tok.kind == TokDeclare || t->next->tok.kind == TokAssign)) { | 	if (t->tok.kind == TokIdent && t->tok.Ident.kind == IdentName && (t->next->tok.kind == TokDeclare || t->next->tok.kind == TokAssign)) { | ||||||
| 		char *name = t->tok.Ident.Name; | 		char *name = t->tok.Ident.Name; | ||||||
| 		t = t->next; | 		t = t->next; | ||||||
| 		if (t->tok.kind == TokDeclare) { | 		if (t->tok.kind == TokDeclare) { | ||||||
| 			size_t addr = sc->mem_addr++; | 			size_t addr = sc->mem_addr++; | ||||||
|  | 			TRY(expr_into_addr(out_ir, toks, funcs, sc, t->next, addr)); | ||||||
| 			bool replaced = map_insert(&sc->ident_addrs, name, &addr); | 			bool replaced = map_insert(&sc->ident_addrs, name, &addr); | ||||||
| 			if (replaced) { | 			if (replaced) { | ||||||
| 				mark_err(&start->tok); | 				mark_err(&start->tok); | ||||||
| 				set_err("'%s' already declared in this scope", name); | 				set_err("'%s' already declared in this scope", name); | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 			TRY(expr_into_addr(out_ir, toks, funcs, sc, t->next, addr)); |  | ||||||
| 		} else if (t->tok.kind == TokAssign) { | 		} else if (t->tok.kind == TokAssign) { | ||||||
| 			size_t addr; | 			size_t addr; | ||||||
| 			TRY(addr = get_ident_addr(sc, name, &start->tok)); | 			TRY(addr = get_ident_addr(sc, name, &start->tok)); | ||||||
| @@ -572,11 +726,12 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 	} else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) { | 	} else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) { | ||||||
| 		Scope inner_sc = make_scope(sc, true); | 		Scope inner_sc = make_scope(sc, true); | ||||||
| 		for (;;) { | 		for (;;) { | ||||||
|  | 			skip_newlns(toks, t->next); | ||||||
| 			if (t->next->tok.kind == TokOp) { | 			if (t->next->tok.kind == TokOp) { | ||||||
| 				if (t->next->tok.Op == OpEOF) { | 				if (t->next->tok.Op == OpEOF) { | ||||||
| 					term_scope(&inner_sc); |  | ||||||
| 					mark_err(&start->tok); | 					mark_err(&start->tok); | ||||||
| 					set_err("Unclosed '{'"); | 					set_err("Unclosed '{'"); | ||||||
|  | 					term_scope(&inner_sc); | ||||||
| 					return; | 					return; | ||||||
| 				} | 				} | ||||||
| 				if (t->next->tok.Op == OpRCurl) | 				if (t->next->tok.Op == OpRCurl) | ||||||
| @@ -596,8 +751,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 		 * */ | 		 * */ | ||||||
|  |  | ||||||
| 		/* add initial jmp instruction */ | 		/* add initial jmp instruction */ | ||||||
| 		size_t jmp_instr_iaddr = out_ir->len; | 		irlist_app(out_ir, (IRTok){ | ||||||
| 		irtoks_app(out_ir, (IRTok){ |  | ||||||
| 			.ln = t->tok.ln, | 			.ln = t->tok.ln, | ||||||
| 			.col = t->tok.col, | 			.col = t->tok.col, | ||||||
| 			.instr = IRJmp, | 			.instr = IRJmp, | ||||||
| @@ -605,29 +759,32 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 				.iaddr = 0, /* unknown for now */ | 				.iaddr = 0, /* unknown for now */ | ||||||
| 			}, | 			}, | ||||||
| 		}); | 		}); | ||||||
|  | 		IRItem *jmp_instr = out_ir->end; | ||||||
|  | 		size_t body_iaddr = out_ir->len; | ||||||
|  |  | ||||||
| 		/* parse condition */ | 		/* parse condition */ | ||||||
| 		IRToks cond_ir; | 		IRList cond_ir; | ||||||
| 		irtoks_init_short(&cond_ir); | 		irlist_init_short(&cond_ir); | ||||||
| 		IRParam cond; | 		IRParam cond; | ||||||
| 		TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); | 		TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irlist_term(&cond_ir)); | ||||||
|  |  | ||||||
| 		/* parse loop body */ | 		/* parse loop body */ | ||||||
| 		TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); | 		skip_newlns(toks, t->next); | ||||||
|  | 		TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irlist_term(&cond_ir)); | ||||||
|  |  | ||||||
| 		/* finally we know where the jmp from the beginning has to jump to */ | 		/* finally we know where the jmp from the beginning has to jump to */ | ||||||
| 		out_ir->toks[jmp_instr_iaddr].Jmp.iaddr = out_ir->len; | 		jmp_instr->tok.Jmp.iaddr = out_ir->len; | ||||||
|  |  | ||||||
| 		/* append condition IR to program IR, then terminate condition IR stream */ | 		/* append condition IR to program IR, then terminate condition IR stream */ | ||||||
| 		irtoks_eat_irtoks(out_ir, &cond_ir, out_ir->len-1); | 		irlist_eat_irlist(out_ir, &cond_ir); | ||||||
|  |  | ||||||
| 		/* add conditional jump */ | 		/* add conditional jump */ | ||||||
| 		irtoks_app(out_ir, (IRTok){ | 		irlist_app(out_ir, (IRTok){ | ||||||
| 			.ln = t->next->tok.ln, | 			.ln = t->next->tok.ln, | ||||||
| 			.col = t->next->tok.col, | 			.col = t->next->tok.col, | ||||||
| 			.instr = IRJnz, | 			.instr = IRJnz, | ||||||
| 			.CJmp = { | 			.CJmp = { | ||||||
| 				.iaddr = jmp_instr_iaddr + 1, | 				.iaddr = body_iaddr, | ||||||
| 				.condition = cond, | 				.condition = cond, | ||||||
| 			}, | 			}, | ||||||
| 		}); | 		}); | ||||||
| @@ -647,8 +804,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 		TRY(cond = expr_into_irparam(out_ir, toks, funcs, sc, t->next)); | 		TRY(cond = expr_into_irparam(out_ir, toks, funcs, sc, t->next)); | ||||||
|  |  | ||||||
| 		/* add conditional jmp instruction */ | 		/* add conditional jmp instruction */ | ||||||
| 		size_t if_cjmp_instr_iaddr = out_ir->len; | 		irlist_app(out_ir, (IRTok){ | ||||||
| 		irtoks_app(out_ir, (IRTok){ |  | ||||||
| 			.ln = t->tok.ln, | 			.ln = t->tok.ln, | ||||||
| 			.col = t->tok.col, | 			.col = t->tok.col, | ||||||
| 			.instr = IRJnz, | 			.instr = IRJnz, | ||||||
| @@ -657,22 +813,25 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 				.condition = cond, | 				.condition = cond, | ||||||
| 			}, | 			}, | ||||||
| 		}); | 		}); | ||||||
|  | 		IRItem *if_cjmp_instr = out_ir->end; | ||||||
|  |  | ||||||
| 		/* parse if body */ | 		/* parse if body */ | ||||||
| 		IRToks if_body; | 		skip_newlns(toks, t->next); | ||||||
| 		irtoks_init_short(&if_body); | 		IRList if_body; | ||||||
| 		TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irtoks_term(&if_body)); | 		irlist_init_short(&if_body); | ||||||
|  | 		TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irlist_term(&if_body)); | ||||||
|  |  | ||||||
|  | 		skip_newlns(toks, t->next); | ||||||
| 		if (t->next->tok.kind == TokElse) { | 		if (t->next->tok.kind == TokElse) { | ||||||
| 			toklist_del(toks, t->next, t->next); | 			toklist_del(toks, t->next, t->next); | ||||||
|  |  | ||||||
| 			/* parse and add else body */ | 			/* parse and add else body */ | ||||||
| 			TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&if_body)); | 			skip_newlns(toks, t->next); | ||||||
|  | 			TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irlist_term(&if_body)); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/* add jmp instruction to jump back to common code */ | 		/* add jmp instruction to jump back to common code */ | ||||||
| 		size_t else_jmp_instr_iaddr = out_ir->len; | 		irlist_app(out_ir, (IRTok){ | ||||||
| 		irtoks_app(out_ir, (IRTok){ |  | ||||||
| 			.ln = t->tok.ln, | 			.ln = t->tok.ln, | ||||||
| 			.col = t->tok.col, | 			.col = t->tok.col, | ||||||
| 			.instr = IRJmp, | 			.instr = IRJmp, | ||||||
| @@ -680,16 +839,16 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 				.iaddr = 0, /* unknown for now */ | 				.iaddr = 0, /* unknown for now */ | ||||||
| 			}, | 			}, | ||||||
| 		}); | 		}); | ||||||
|  | 		IRItem *else_jmp_instr = out_ir->end; | ||||||
|  |  | ||||||
| 		/* set if condition jmp target */ | 		/* set if condition jmp target */ | ||||||
| 		out_ir->toks[if_cjmp_instr_iaddr].CJmp.iaddr = out_ir->len; | 		if_cjmp_instr->tok.CJmp.iaddr = out_ir->len; | ||||||
| 		 | 		 | ||||||
| 		/* add if body */ | 		/* add if body */ | ||||||
| 		irtoks_eat_irtoks(out_ir, &if_body, out_ir->len-1); | 		irlist_eat_irlist(out_ir, &if_body); | ||||||
|  |  | ||||||
| 		/* set else jmp target */ | 		/* set else jmp target */ | ||||||
| 		out_ir->toks[else_jmp_instr_iaddr].CJmp.iaddr = out_ir->len; | 		else_jmp_instr->tok.CJmp.iaddr = out_ir->len; | ||||||
| 	} else if (t->tok.kind == TokOp && t->tok.Op == OpNewLn) { |  | ||||||
| 	} else { | 	} else { | ||||||
| 		/* assume expression */ | 		/* assume expression */ | ||||||
| 		TRY(expr_into_irparam(out_ir, toks, funcs, sc, t)); | 		TRY(expr_into_irparam(out_ir, toks, funcs, sc, t)); | ||||||
| @@ -698,9 +857,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt | |||||||
| 	toklist_del(toks, start, t); | 	toklist_del(toks, start, t); | ||||||
| } | } | ||||||
|  |  | ||||||
| IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) { | IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) { | ||||||
| 	bf = builtin_funcs; |  | ||||||
|  |  | ||||||
| 	Map funcs; | 	Map funcs; | ||||||
| 	map_init(&funcs, sizeof(BuiltinFunc)); | 	map_init(&funcs, sizeof(BuiltinFunc)); | ||||||
| 	for (size_t i = 0; i < n_builtin_funcs; i++) { | 	for (size_t i = 0; i < n_builtin_funcs; i++) { | ||||||
| @@ -710,14 +867,15 @@ IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) | |||||||
| 			err_ln = 0; err_col = 0; | 			err_ln = 0; err_col = 0; | ||||||
| 			set_err("Builtin function %s() declared more than once", builtin_funcs[i].name); | 			set_err("Builtin function %s() declared more than once", builtin_funcs[i].name); | ||||||
| 			map_term(&funcs); | 			map_term(&funcs); | ||||||
| 			return (IRToks){0}; | 			return (IRList){0}; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	IRToks ir; | 	IRList ir; | ||||||
| 	irtoks_init_long(&ir); | 	irlist_init_long(&ir); | ||||||
| 	Scope global_scope = make_scope(NULL, true); | 	Scope global_scope = make_scope(NULL, true); | ||||||
| 	for (;;) { | 	for (;;) { | ||||||
|  | 		skip_newlns(toks, toks->begin); | ||||||
| 		if (toks->begin->tok.kind == TokOp && toks->begin->tok.Op == OpEOF) | 		if (toks->begin->tok.kind == TokOp && toks->begin->tok.Op == OpEOF) | ||||||
| 			break; | 			break; | ||||||
| 		TRY_RET_ELSE(stmt(&ir, toks, &funcs, &global_scope, toks->begin), ir, | 		TRY_RET_ELSE(stmt(&ir, toks, &funcs, &global_scope, toks->begin), ir, | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								parse.h
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								parse.h
									
									
									
									
									
								
							| @@ -5,6 +5,6 @@ | |||||||
| #include "tok.h" | #include "tok.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  |  | ||||||
| IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs); | IRList parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs); | ||||||
|  |  | ||||||
| #endif /* PARSE_H */ | #endif /* PARSE_H */ | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								runtime.c
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								runtime.c
									
									
									
									
									
								
							| @@ -8,7 +8,7 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) { | |||||||
| 		case IRSub: | 		case IRSub: | ||||||
| 		case IRMul: | 		case IRMul: | ||||||
| 		case IRDiv: { | 		case IRDiv: { | ||||||
| 			if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) { | 			if (lhs->type == TypeInt && rhs->type == TypeInt) { | ||||||
| 				ssize_t res; | 				ssize_t res; | ||||||
| 				switch (instr) { | 				switch (instr) { | ||||||
| 					case IRAdd: res = lhs->Int + rhs->Int; break; | 					case IRAdd: res = lhs->Int + rhs->Int; break; | ||||||
| @@ -18,11 +18,11 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) { | |||||||
| 					default: ASSERT_UNREACHED(); | 					default: ASSERT_UNREACHED(); | ||||||
| 				} | 				} | ||||||
| 				return (Value){ | 				return (Value){ | ||||||
| 					.type.kind = TypeInt, | 					.type = TypeInt, | ||||||
| 					.Int = res, | 					.Int = res, | ||||||
| 				}; | 				}; | ||||||
| 			} else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) { | 			} else if (lhs->type == TypeFloat && rhs->type == TypeFloat) { | ||||||
| 				float res; | 				double res; | ||||||
| 				switch (instr) { | 				switch (instr) { | ||||||
| 					case IRAdd: res = lhs->Float + rhs->Float; break; | 					case IRAdd: res = lhs->Float + rhs->Float; break; | ||||||
| 					case IRSub: res = lhs->Float - rhs->Float; break; | 					case IRSub: res = lhs->Float - rhs->Float; break; | ||||||
| @@ -31,40 +31,69 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) { | |||||||
| 					default: ASSERT_UNREACHED(); | 					default: ASSERT_UNREACHED(); | ||||||
| 				} | 				} | ||||||
| 				return (Value){ | 				return (Value){ | ||||||
| 					.type.kind = TypeFloat, | 					.type = TypeFloat, | ||||||
| 					.Float = res, | 					.Float = res, | ||||||
| 				}; | 				}; | ||||||
| 			} else { | 			} else { | ||||||
| 				set_err("Unsupported types for operation '%s'", irinstr_str[instr]); | 				set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type], type_str[rhs->type]); | ||||||
| 				return (Value){0}; | 				return (Value){0}; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		case IREq: | 		case IREq: | ||||||
|  | 		case IRNeq: | ||||||
| 		case IRLt: | 		case IRLt: | ||||||
| 		case IRLe: | 		case IRLe: { | ||||||
| 			bool res; | 			bool res; | ||||||
| 			if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) { | 			if (lhs->type == TypeInt && rhs->type == TypeInt) { | ||||||
| 				switch (instr) { | 				switch (instr) { | ||||||
| 					case IREq: res = lhs->Int == rhs->Int; break; | 					case IREq:  res = lhs->Int == rhs->Int; break; | ||||||
| 					case IRLt: res = lhs->Int <  rhs->Int; break; | 					case IRNeq: res = lhs->Int != rhs->Int; break; | ||||||
| 					case IRLe: res = lhs->Int <= rhs->Int; break; | 					case IRLt:  res = lhs->Int <  rhs->Int; break; | ||||||
|  | 					case IRLe:  res = lhs->Int <= rhs->Int; break; | ||||||
| 					default: ASSERT_UNREACHED(); | 					default: ASSERT_UNREACHED(); | ||||||
| 				}; | 				}; | ||||||
| 			} else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) { | 			} else if (lhs->type == TypeFloat && rhs->type == TypeFloat) { | ||||||
| 				switch (instr) { | 				switch (instr) { | ||||||
| 					case IREq: res = lhs->Float == rhs->Float; break; | 					case IREq:  res = lhs->Float == rhs->Float; break; | ||||||
| 					case IRLt: res = lhs->Float <  rhs->Float; break; | 					case IRNeq: res = lhs->Float != rhs->Float; break; | ||||||
| 					case IRLe: res = lhs->Float <= rhs->Float; break; | 					case IRLt:  res = lhs->Float <  rhs->Float; break; | ||||||
|  | 					case IRLe:  res = lhs->Float <= rhs->Float; break; | ||||||
| 					default: ASSERT_UNREACHED(); | 					default: ASSERT_UNREACHED(); | ||||||
| 				}; | 				}; | ||||||
|  | 			} else if (lhs->type == TypeArr && lhs->Arr.type == TypeChar && lhs->Arr.is_string && | ||||||
|  | 					rhs->type == TypeArr && rhs->Arr.type == TypeChar && rhs->Arr.is_string) { | ||||||
|  | 				switch (instr) { | ||||||
|  | 					case IREq: | ||||||
|  | 						res = lhs->Arr.len == rhs->Arr.len ? strncmp(lhs->Arr.vals, rhs->Arr.vals, lhs->Arr.len) == 0 : false; | ||||||
|  | 						break; | ||||||
|  | 					case IRNeq: | ||||||
|  | 						res = lhs->Arr.len == rhs->Arr.len ? strncmp(lhs->Arr.vals, rhs->Arr.vals, lhs->Arr.len) != 0 : true; | ||||||
|  | 						break; | ||||||
|  | 					default: | ||||||
|  | 						set_err("String operation '%s' not supported", irinstr_str[instr]); | ||||||
|  | 						break; | ||||||
|  | 				}; | ||||||
| 			} else { | 			} else { | ||||||
| 				set_err("Unsupported types for operation '%s'", irinstr_str[instr]); | 				set_err("Unsupported types for operation '%s': %s and %s", irinstr_str[instr], type_str[lhs->type], type_str[rhs->type]); | ||||||
| 				return (Value){0}; | 				return (Value){0}; | ||||||
| 			} | 			} | ||||||
| 			return (Value){ | 			return (Value){ | ||||||
| 				.type.kind = TypeBool, | 				.type = TypeBool, | ||||||
| 				.Bool = res, | 				.Bool = res, | ||||||
| 			}; | 			}; | ||||||
|  | 		} | ||||||
|  | 		case IRAnd: | ||||||
|  | 			return (Value){ | ||||||
|  | 				.type = TypeBool, | ||||||
|  | 				.Bool = is_nonzero(lhs) && is_nonzero(rhs), | ||||||
|  | 			}; | ||||||
|  | 			break; | ||||||
|  | 		case IROr: | ||||||
|  | 			return (Value){ | ||||||
|  | 				.type = TypeBool, | ||||||
|  | 				.Bool = is_nonzero(lhs) || is_nonzero(rhs), | ||||||
|  | 			}; | ||||||
|  | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			ASSERT_UNREACHED(); | 			ASSERT_UNREACHED(); | ||||||
| 	} | 	} | ||||||
| @@ -76,31 +105,37 @@ Value eval_unary(IRInstr instr, const Value *v) { | |||||||
| 		case IRSet: | 		case IRSet: | ||||||
| 			return *v; | 			return *v; | ||||||
| 		case IRNeg: | 		case IRNeg: | ||||||
| 			if (v->type.kind == TypeInt) | 			if (v->type == TypeInt) | ||||||
| 				return (Value){ .type.kind = TypeInt, .Int = -v->Int }; | 				return (Value){ .type = TypeInt, .Int = -v->Int }; | ||||||
| 			else if (v->type.kind == TypeFloat) | 			else if (v->type == TypeFloat) | ||||||
| 				return (Value){ .type.kind = TypeFloat, .Float = -v->Float }; | 				return (Value){ .type = TypeFloat, .Float = -v->Float }; | ||||||
| 			else { | 			else { | ||||||
| 				set_err("Unsupported type for operation '%s'", irinstr_str[instr]); | 				set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type]); | ||||||
| 				return (Value){0}; | 				return (Value){0}; | ||||||
| 			} | 			} | ||||||
| 		case IRNot: | 		case IRNot: | ||||||
| 			if (v->type.kind == TypeBool) { | 			if (v->type == TypeBool) { | ||||||
| 				return (Value){ .type.kind = TypeBool, .Bool = !v->Bool }; | 				return (Value){ .type = TypeBool, .Bool = !v->Bool }; | ||||||
| 			} else { | 			} else { | ||||||
| 				set_err("Unsupported type for operation '%s'", irinstr_str[instr]); | 				set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type]); | ||||||
| 				return (Value){0}; | 				return (Value){0}; | ||||||
| 			} | 			} | ||||||
|  | 		case IRAddrOf: | ||||||
|  | 			set_err("Unable to take the address of a literal"); | ||||||
|  | 			return (Value){0}; | ||||||
| 		default: | 		default: | ||||||
| 			ASSERT_UNREACHED(); | 			ASSERT_UNREACHED(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| bool is_nonzero(const Value *v) { | bool is_nonzero(const Value *v) { | ||||||
| 	switch (v->type.kind) { | 	switch (v->type) { | ||||||
| 		case TypeInt:   return v->Int   != 0; | 		case TypeInt:   return v->Int     != 0; | ||||||
| 		case TypeFloat: return v->Float != 0.0; | 		case TypeFloat: return v->Float   != 0.0; | ||||||
| 		case TypeBool:  return v->Bool; | 		case TypeBool:  return v->Bool; | ||||||
|  | 		case TypeChar:  return v->Char    != 0; | ||||||
|  | 		case TypePtr:   return v->Ptr.val != NULL; | ||||||
|  | 		case TypeArr:   return v->Arr.len != 0; | ||||||
| 		default: ASSERT_UNREACHED(); | 		default: ASSERT_UNREACHED(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -108,7 +143,7 @@ bool is_nonzero(const Value *v) { | |||||||
| Value zero_val(Type ty) { | Value zero_val(Type ty) { | ||||||
| 	Value ret; | 	Value ret; | ||||||
| 	ret.type = ty; | 	ret.type = ty; | ||||||
| 	switch (ty.kind) { | 	switch (ty) { | ||||||
| 		case TypeInt:   ret.Int   = 0;     break; | 		case TypeInt:   ret.Int   = 0;     break; | ||||||
| 		case TypeFloat: ret.Float = 0.0;   break; | 		case TypeFloat: ret.Float = 0.0;   break; | ||||||
| 		case TypeBool:  ret.Bool  = false; break; | 		case TypeBool:  ret.Bool  = false; break; | ||||||
|   | |||||||
							
								
								
									
										152
									
								
								tok.c
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								tok.c
									
									
									
									
									
								
							| @@ -5,21 +5,132 @@ | |||||||
|  |  | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  |  | ||||||
|  | size_t type_size[TypeEnumSize] = { | ||||||
|  | 	[TypeVoid]  = 0, | ||||||
|  | 	[TypeFloat] = sizeof(((Value*)NULL)->Float), | ||||||
|  | 	[TypeInt]   = sizeof(((Value*)NULL)->Int), | ||||||
|  | 	[TypeBool]  = sizeof(((Value*)NULL)->Bool), | ||||||
|  | 	[TypeChar]  = sizeof(((Value*)NULL)->Char), | ||||||
|  | 	[TypePtr]   = sizeof(((Value*)NULL)->Ptr), | ||||||
|  | 	[TypeArr]   = sizeof(((Value*)NULL)->Arr), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const char *type_str[TypeEnumSize] = { | ||||||
|  | 	[TypeVoid]  = "void", | ||||||
|  | 	[TypeFloat] = "float", | ||||||
|  | 	[TypeInt]   = "int", | ||||||
|  | 	[TypeBool]  = "bool", | ||||||
|  | 	[TypeChar]  = "char", | ||||||
|  | 	[TypePtr]   = "ptr", | ||||||
|  | 	[TypeArr]   = "arr", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* if purge is set, even statically allocated literals will be freed */ | ||||||
|  | void free_value(Value *v, bool purge) { | ||||||
|  | 	switch (v->type) { | ||||||
|  | 		case TypeArr: | ||||||
|  | 			if (v->Arr.vals && (purge || v->Arr.dynamically_allocated)) { | ||||||
|  | 				free(v->Arr.vals); | ||||||
|  | 				v->Arr.vals = NULL; | ||||||
|  | 				v->Arr.len = 0; | ||||||
|  | 				v->Arr.cap = 0; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void print_value(const Value *v, bool raw) { | ||||||
|  | 	switch (v->type) { | ||||||
|  | 		case TypeVoid: | ||||||
|  | 			printf("(void)"); | ||||||
|  | 			break; | ||||||
|  | 		case TypeFloat: | ||||||
|  | 			printf("%f", v->Float); | ||||||
|  | 			break; | ||||||
|  | 		case TypeInt: | ||||||
|  | 			printf("%zd", v->Int); | ||||||
|  | 			break; | ||||||
|  | 		case TypeBool: | ||||||
|  | 			printf("%s", v->Bool ? "true" : "false"); | ||||||
|  | 			break; | ||||||
|  | 		case TypeChar: | ||||||
|  | 			if (raw) | ||||||
|  | 				printf("%c", v->Char); | ||||||
|  | 			else { | ||||||
|  | 				const char *esc = unescape_char(v->Char); | ||||||
|  | 				if (esc) printf("'%s'", esc); | ||||||
|  | 				else     printf("'%c'", v->Char); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		case TypePtr: { | ||||||
|  | 			if (v->Ptr.val) { | ||||||
|  | 				printf("ptr<%s>(", type_str[v->Ptr.type]); | ||||||
|  | 				Value deref = { .type = v->Ptr.type }; | ||||||
|  | 				memcpy(&deref.Void, v->Ptr.val, type_size[v->Ptr.type]); | ||||||
|  | 				print_value(&deref, false); | ||||||
|  | 				printf(")"); | ||||||
|  | 			} else | ||||||
|  | 				printf("ptr<%s>(nil)", type_str[v->Ptr.type]); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		case TypeArr: | ||||||
|  | 			if (v->Arr.is_string) { | ||||||
|  | 				if (v->Arr.type != TypeChar) | ||||||
|  | 					ASSERT_UNREACHED(); | ||||||
|  | 				char *str = v->Arr.vals; | ||||||
|  | 				if (!raw) | ||||||
|  | 					printf("\""); | ||||||
|  | 				for (size_t i = 0; i < v->Arr.len; i++) { | ||||||
|  | 					char c = str[i]; | ||||||
|  | 					if (raw) | ||||||
|  | 						printf("%c", c); | ||||||
|  | 					else { | ||||||
|  | 						const char *esc = unescape_char(c); | ||||||
|  | 						if (esc) printf("%s", esc); | ||||||
|  | 						else     printf("%c", c); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				if (!raw) | ||||||
|  | 					printf("\""); | ||||||
|  | 			} else { | ||||||
|  | 				printf("["); | ||||||
|  | 				for (size_t i = 0;; i++) { | ||||||
|  | 					size_t ty_sz = type_size[v->Arr.type]; | ||||||
|  | 					Value ty_val = { .type = v->Arr.type }; | ||||||
|  | 					memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz); | ||||||
|  | 					print_value(&ty_val, false); | ||||||
|  | 					if (i+1 >= v->Arr.len) break; | ||||||
|  | 					printf(", "); | ||||||
|  | 				} | ||||||
|  | 				printf("]"); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			ASSERT_UNREACHED(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| int8_t op_prec[OperatorEnumSize] = { | int8_t op_prec[OperatorEnumSize] = { | ||||||
| 	[OpEOF]    = PREC_DELIM, | 	[OpEOF]    = PREC_DELIM, | ||||||
| 	[OpNewLn]  = PREC_DELIM, | 	[OpNewLn]  = PREC_DELIM, | ||||||
| 	[OpLCurl]  = PREC_DELIM, | 	[OpLCurl]  = PREC_DELIM, | ||||||
| 	[OpRParen] = PREC_DELIM, | 	[OpRParen] = PREC_DELIM, | ||||||
|  | 	[OpRBrack] = PREC_DELIM, | ||||||
| 	[OpComma]  = PREC_DELIM, | 	[OpComma]  = PREC_DELIM, | ||||||
| 	[OpEq]     = 0, | 	[OpAnd]    = 0, | ||||||
| 	[OpLt]     = 0, | 	[OpOr]     = 0, | ||||||
| 	[OpGt]     = 0, | 	[OpEq]     = 1, | ||||||
| 	[OpLe]     = 0, | 	[OpNeq]    = 1, | ||||||
| 	[OpGe]     = 0, | 	[OpLt]     = 1, | ||||||
| 	[OpAdd]    = 1, | 	[OpGt]     = 1, | ||||||
| 	[OpSub]    = 1, | 	[OpLe]     = 1, | ||||||
| 	[OpMul]    = 2, | 	[OpGe]     = 1, | ||||||
| 	[OpDiv]    = 2, | 	[OpAdd]    = 2, | ||||||
|  | 	[OpSub]    = 2, | ||||||
|  | 	[OpMul]    = 3, | ||||||
|  | 	[OpDiv]    = 3, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const char *op_str[OperatorEnumSize] = { | const char *op_str[OperatorEnumSize] = { | ||||||
| @@ -27,6 +138,8 @@ const char *op_str[OperatorEnumSize] = { | |||||||
| 	[OpRCurl]  = "}", | 	[OpRCurl]  = "}", | ||||||
| 	[OpLParen] = "(", | 	[OpLParen] = "(", | ||||||
| 	[OpRParen] = ")", | 	[OpRParen] = ")", | ||||||
|  | 	[OpLBrack] = "[", | ||||||
|  | 	[OpRBrack] = "]", | ||||||
| 	[OpComma]  = ",", | 	[OpComma]  = ",", | ||||||
| 	[OpAdd]    = "+", | 	[OpAdd]    = "+", | ||||||
| 	[OpSub]    = "-", | 	[OpSub]    = "-", | ||||||
| @@ -36,10 +149,13 @@ const char *op_str[OperatorEnumSize] = { | |||||||
| 	[OpNewLn]  = "\\n", | 	[OpNewLn]  = "\\n", | ||||||
| 	[OpEOF]    = "EOF", | 	[OpEOF]    = "EOF", | ||||||
| 	[OpEq]     = "==", | 	[OpEq]     = "==", | ||||||
|  | 	[OpNeq]    = "!=", | ||||||
| 	[OpLt]     = "<", | 	[OpLt]     = "<", | ||||||
| 	[OpGt]     = ">", | 	[OpGt]     = ">", | ||||||
| 	[OpLe]     = "<=", | 	[OpLe]     = "<=", | ||||||
| 	[OpGe]     = ">=", | 	[OpGe]     = ">=", | ||||||
|  | 	[OpAnd]    = "&&", | ||||||
|  | 	[OpOr]     = "||", | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const char *tok_str[TokKindEnumSize] = { | const char *tok_str[TokKindEnumSize] = { | ||||||
| @@ -104,21 +220,9 @@ void print_toks(TokList *l) { | |||||||
| 				printf(": " C_ICYAN "%s" C_RESET, op_str[i->tok.Op]); | 				printf(": " C_ICYAN "%s" C_RESET, op_str[i->tok.Op]); | ||||||
| 				break; | 				break; | ||||||
| 			case TokVal: | 			case TokVal: | ||||||
| 				printf(C_IYELLOW "Val" C_RESET); | 				printf(C_IYELLOW "Val" C_RESET ": " C_ICYAN); | ||||||
| 				switch (i->tok.Val.type.kind) { | 				print_value(&i->tok.Val, false); | ||||||
| 					case TypeFloat: | 				printf(C_RESET); | ||||||
| 						printf(": " C_ICYAN "%f" C_RESET, i->tok.Val.Float); |  | ||||||
| 						break; |  | ||||||
| 					case TypeInt: |  | ||||||
| 						printf(": " C_ICYAN "%zd" C_RESET, i->tok.Val.Int); |  | ||||||
| 						break; |  | ||||||
| 					case TypeBool: |  | ||||||
| 						printf(": " C_ICYAN "%s" C_RESET, i->tok.Val.Bool ? "true" : "false"); |  | ||||||
| 						break; |  | ||||||
| 					default: |  | ||||||
| 						printf(" " C_ICYAN "(unknown type)" C_RESET); |  | ||||||
| 						break; |  | ||||||
| 				} |  | ||||||
| 				break; | 				break; | ||||||
| 			case TokIdent: | 			case TokIdent: | ||||||
| 				printf(C_IYELLOW "Ident" C_RESET); | 				printf(C_IYELLOW "Ident" C_RESET); | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								tok.h
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								tok.h
									
									
									
									
									
								
							| @@ -2,49 +2,73 @@ | |||||||
| #define __TOK_H__ | #define __TOK_H__ | ||||||
|  |  | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <unistd.h> |  | ||||||
|  |  | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  |  | ||||||
| typedef struct Type { | enum Type { | ||||||
| 	enum { | 	TypeVoid = 0, | ||||||
| 		TypeVoid = 0, | 	TypeFloat, | ||||||
| 		TypeFloat, | 	TypeInt, | ||||||
| 		TypeInt, | 	TypeBool, | ||||||
| 		TypeBool, | 	TypeChar, | ||||||
| 	} kind; | 	TypePtr, | ||||||
|  | 	TypeArr, | ||||||
|  | 	TypeEnumSize, | ||||||
|  | }; | ||||||
|  | typedef enum Type Type; | ||||||
|  |  | ||||||
| 	/*union { | extern size_t type_size[TypeEnumSize]; | ||||||
| 	};*/ | extern const char *type_str[TypeEnumSize]; | ||||||
| } Type; |  | ||||||
|  |  | ||||||
| typedef struct Value { | typedef struct Value { | ||||||
| 	Type type; | 	Type type; | ||||||
|  |  | ||||||
| 	union { | 	union { | ||||||
|  | 		pseudo_void Void; | ||||||
| 		double Float; | 		double Float; | ||||||
| 		ssize_t Int; | 		ssize_t Int; | ||||||
| 		bool Bool; | 		bool Bool; | ||||||
|  | 		char Char; | ||||||
|  | 		struct { | ||||||
|  | 			Type type; | ||||||
|  | 			void *val; | ||||||
|  | 		} Ptr; | ||||||
|  | 		struct { | ||||||
|  | 			Type type; | ||||||
|  | 			bool is_string : 1; | ||||||
|  | 			bool dynamically_allocated : 1; | ||||||
|  | 			void *vals; | ||||||
|  | 			size_t len, cap; | ||||||
|  | 		} Arr; | ||||||
| 	}; | 	}; | ||||||
| } Value; | } Value; | ||||||
|  |  | ||||||
|  | void free_value(Value *v, bool purge); | ||||||
|  | void print_value(const Value *v, bool raw); | ||||||
|  |  | ||||||
| enum Operator { | enum Operator { | ||||||
| 	OpLCurl  = '{', | 	OpLCurl  = '{', | ||||||
| 	OpRCurl  = '}', | 	OpRCurl  = '}', | ||||||
| 	OpLParen = '(', | 	OpLParen = '(', | ||||||
| 	OpRParen = ')', | 	OpRParen = ')', | ||||||
|  | 	OpLBrack = '[', | ||||||
|  | 	OpRBrack = ']', | ||||||
| 	OpComma  = ',', | 	OpComma  = ',', | ||||||
| 	OpAdd    = '+', | 	OpAdd    = '+', | ||||||
| 	OpSub    = '-', | 	OpSub    = '-', | ||||||
| 	OpMul    = '*', | 	OpMul    = '*', | ||||||
| 	OpDiv    = '/', | 	OpDiv    = '/', | ||||||
| 	OpNot    = '!', | 	OpNot    = '!', | ||||||
|  | 	OpAddrOf = '&', | ||||||
| 	OpBeginNonchars = 256, | 	OpBeginNonchars = 256, | ||||||
| 	OpEq, | 	OpEq, | ||||||
|  | 	OpNeq, | ||||||
| 	OpLt, | 	OpLt, | ||||||
| 	OpGt, | 	OpGt, | ||||||
| 	OpLe, | 	OpLe, | ||||||
| 	OpGe, | 	OpGe, | ||||||
|  | 	OpAnd, | ||||||
|  | 	OpOr, | ||||||
| 	OpNewLn, | 	OpNewLn, | ||||||
| 	OpEOF, | 	OpEOF, | ||||||
| 	OperatorEnumSize, | 	OperatorEnumSize, | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								util.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								util.c
									
									
									
									
									
								
							| @@ -2,6 +2,23 @@ | |||||||
|  |  | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
|  |  | ||||||
|  | #ifdef WIN32 | ||||||
|  | #include <windows.h> /* Sleep */ | ||||||
|  | #else | ||||||
|  | #include <time.h> /* nanosleep */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | void sleep_secs(double secs) { | ||||||
|  | #if defined(_WIN32) || defined(WIN32) | ||||||
|  | 	Sleep(secs * 1000.0); | ||||||
|  | #else | ||||||
|  | 	struct timespec ts; | ||||||
|  | 	ts.tv_sec = (time_t)secs; | ||||||
|  | 	ts.tv_nsec = (secs - (double)ts.tv_sec) * 1000000000.0; | ||||||
|  | 	nanosleep(&ts, NULL); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
| char errbuf[ERRSZ]; | char errbuf[ERRSZ]; | ||||||
| bool err; | bool err; | ||||||
| size_t err_ln, err_col; | size_t err_ln, err_col; | ||||||
| @@ -168,6 +185,23 @@ double stod(const char *s, size_t n, ssize_t *endpos) { | |||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const char *unescape_char(char c) { | ||||||
|  | 	switch (c) { | ||||||
|  | 		case '\a':   return "\\a"; | ||||||
|  | 		case '\b':   return "\\b"; | ||||||
|  | 		case '\033': return "\\e"; | ||||||
|  | 		case '\f':   return "\\f"; | ||||||
|  | 		case '\n':   return "\\n"; | ||||||
|  | 		case '\r':   return "\\r"; | ||||||
|  | 		case '\t':   return "\\t"; | ||||||
|  | 		case '\v':   return "\\v"; | ||||||
|  | 		case '\\':   return "\\\\"; | ||||||
|  | 		case '\'':   return "\\'"; | ||||||
|  | 		case '"':    return "\\\""; | ||||||
|  | 		default:     return NULL; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| char *mreadfile(FILE *fp) { | char *mreadfile(FILE *fp) { | ||||||
| 	if (fseek(fp, 0l, SEEK_END) == -1) | 	if (fseek(fp, 0l, SEEK_END) == -1) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								util.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								util.h
									
									
									
									
									
								
							| @@ -6,7 +6,15 @@ | |||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <unistd.h> |  | ||||||
|  | #if defined(_WIN32) || defined(WIN32) | ||||||
|  | #include <windows.h> /* SSIZE_T */ | ||||||
|  | typedef SSIZE_T ssize_t; | ||||||
|  | #else | ||||||
|  | #include <unistd.h> /* ssize_t */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef uint8_t pseudo_void; | ||||||
|  |  | ||||||
| /* some ANSI color codes */ | /* some ANSI color codes */ | ||||||
| #define C_RED     "\x1b[31m" | #define C_RED     "\x1b[31m" | ||||||
| @@ -27,6 +35,8 @@ | |||||||
|  |  | ||||||
| #define C_RESET   "\x1b[m" | #define C_RESET   "\x1b[m" | ||||||
|  |  | ||||||
|  | void sleep_secs(double secs); | ||||||
|  |  | ||||||
| #define ERRSZ 4096 | #define ERRSZ 4096 | ||||||
| extern char errbuf[ERRSZ]; | extern char errbuf[ERRSZ]; | ||||||
| extern bool err; | extern bool err; | ||||||
| @@ -67,6 +77,8 @@ char *psndup(Pool *p, const char *s, size_t n); | |||||||
| intmax_t stoimax(const char *s, size_t n, size_t base, ssize_t *endpos /* -1 on success */); | intmax_t stoimax(const char *s, size_t n, size_t base, ssize_t *endpos /* -1 on success */); | ||||||
| /* convert a non-null-terminated string to a double */ | /* convert a non-null-terminated string to a double */ | ||||||
| double stod(const char *s, size_t n, ssize_t *endpos /* -1 on success */); | double stod(const char *s, size_t n, ssize_t *endpos /* -1 on success */); | ||||||
|  | /* return the escape sequence for a given character; return NULL if there is none */ | ||||||
|  | const char *unescape_char(char c); | ||||||
|  |  | ||||||
| /* sets errno on failure */ | /* sets errno on failure */ | ||||||
| char *mreadfile(FILE *fp); | char *mreadfile(FILE *fp); | ||||||
|   | |||||||
							
								
								
									
										151
									
								
								vm.c
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								vm.c
									
									
									
									
									
								
							| @@ -3,33 +3,50 @@ | |||||||
| #include "runtime.h" | #include "runtime.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  |  | ||||||
| #define INIT_STACK_CAP 256 | #define INIT_STACK_CAP 128 | ||||||
|  |  | ||||||
| typedef struct Stack { | typedef struct Stack { | ||||||
| 	Value *mem; | 	Value *mem; | ||||||
|  | 	bool *holds_value; | ||||||
| 	size_t len, cap; | 	size_t len, cap; | ||||||
| } Stack; | } Stack; | ||||||
|  |  | ||||||
| static Stack stack_make(void); | static Stack stack_make(void); | ||||||
| static void stack_term(Stack *s); | static void stack_term(Stack *s); | ||||||
| static void stack_fit(Stack *s, size_t idx); | static void stack_fit(Stack *s, size_t idx); | ||||||
|  | static void stack_assign(Stack *s, size_t idx, const Value *v); | ||||||
|  |  | ||||||
| static Stack stack_make(void) { | static Stack stack_make(void) { | ||||||
| 	Stack s; | 	Stack s; | ||||||
| 	s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP); | 	s.mem = xmalloc(sizeof(Value) * INIT_STACK_CAP); | ||||||
|  | 	s.holds_value = xmalloc(sizeof(bool) * INIT_STACK_CAP); | ||||||
| 	s.cap = INIT_STACK_CAP; | 	s.cap = INIT_STACK_CAP; | ||||||
| 	s.len = 0; | 	s.len = 0; | ||||||
|  | 	for (size_t i = 0; i < s.cap; i++) | ||||||
|  | 		s.holds_value[i] = false; | ||||||
| 	return s; | 	return s; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void stack_term(Stack *s) { | static void stack_term(Stack *s) { | ||||||
|  | 	/* free any dynamically allocated objects still alive */ | ||||||
|  | 	for (size_t i = 0; i < s->cap; i++) { | ||||||
|  | 		if (s->holds_value[i]) | ||||||
|  | 			free_value(&s->mem[i], false); | ||||||
|  | 	} | ||||||
|  | 	/* free the stack memory itself */ | ||||||
| 	free(s->mem); | 	free(s->mem); | ||||||
|  | 	free(s->holds_value); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void stack_fit(Stack *s, size_t idx) { | static void stack_fit(Stack *s, size_t idx) { | ||||||
| 	size_t size = idx+1; | 	size_t size = idx+1; | ||||||
| 	if (size > s->cap) { | 	if (size > s->cap) { | ||||||
| 		s->mem = xrealloc(s->mem, sizeof(Value) * (size + (s->cap *= 2))); | 		size_t new_cap = size + s->cap * 2; | ||||||
|  | 		s->mem = xrealloc(s->mem, sizeof(Value) * new_cap); | ||||||
|  | 		s->holds_value = xrealloc(s->holds_value, sizeof(bool) * new_cap); | ||||||
|  | 		for (size_t i = s->cap; i < new_cap; i++) | ||||||
|  | 			s->holds_value[i] = false; | ||||||
|  | 		s->cap = new_cap; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -42,65 +59,161 @@ static Value *irparam_to_val(Stack *s, IRParam *v) { | |||||||
| 		ASSERT_UNREACHED(); | 		ASSERT_UNREACHED(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { | static void stack_assign(Stack *s, size_t idx, const Value *v) { | ||||||
|  | 	stack_fit(s, idx); | ||||||
|  | 	if (s->holds_value[idx]) | ||||||
|  | 		free_value(&s->mem[idx], false); /* free any overwritten heap-allocated values */ | ||||||
|  | 	s->mem[idx] = *v; | ||||||
|  | 	s->holds_value[idx] = true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void run(IRList *ir, const BuiltinFunc *builtin_funcs) { | ||||||
| 	/* so we don't have to call malloc on every function call */ | 	/* so we don't have to call malloc on every function call */ | ||||||
| 	size_t fn_args_cap = 16; | 	size_t fn_args_cap = 16; | ||||||
| 	Value *fn_args = xmalloc(sizeof(Value) * fn_args_cap); | 	Value *fn_args = xmalloc(sizeof(Value) * fn_args_cap); | ||||||
|  |  | ||||||
|  | 	/* so we can use index-based addressing */ | ||||||
|  | 	irlist_update_index(ir); | ||||||
|  |  | ||||||
| 	Stack s = stack_make(); | 	Stack s = stack_make(); | ||||||
| 	for (size_t i = 0; i < ir->len;) { | 	for (IRItem *i = ir->begin; i;) { | ||||||
| 		IRTok *instr = &ir->toks[i]; | 		IRTok *instr = &i->tok; | ||||||
| 		err_ln = instr->ln; | 		err_ln = instr->ln; | ||||||
| 		err_col = instr->col; | 		err_col = instr->col; | ||||||
| 		switch (instr->instr) { | 		switch (instr->instr) { | ||||||
| 			case IRSet: | 			case IRSet: | ||||||
| 			case IRNeg: | 			case IRNeg: | ||||||
| 			case IRNot: | 			case IRNot: { | ||||||
| 				stack_fit(&s, instr->Unary.addr); | 				Value res; | ||||||
| 				TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), | 				TRY_ELSE(res = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), | ||||||
| 					{free(fn_args); stack_term(&s);}); | 					{free(fn_args); stack_term(&s);}); | ||||||
|  | 				stack_assign(&s, instr->Unary.addr, &res); | ||||||
| 				break; | 				break; | ||||||
|  | 			} | ||||||
|  | 			case IRAddrOf: { | ||||||
|  | 				if (instr->Unary.val.kind != IRParamAddr) { | ||||||
|  | 					set_err("Unable to take the address of a literal"); | ||||||
|  | 					free(fn_args); | ||||||
|  | 					stack_term(&s); | ||||||
|  | 					return; | ||||||
|  | 				} | ||||||
|  | 				Value *v = &s.mem[instr->Unary.val.Addr]; | ||||||
|  | 				Value res = { | ||||||
|  | 					.type = TypePtr, | ||||||
|  | 					.Ptr = { | ||||||
|  | 						.type = v->type, | ||||||
|  | 						.val = &v->Void, | ||||||
|  | 					}, | ||||||
|  | 				}; | ||||||
|  | 				stack_assign(&s, instr->Unary.addr, &res); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
| 			case IRAdd: | 			case IRAdd: | ||||||
| 			case IRSub: | 			case IRSub: | ||||||
| 			case IRDiv: | 			case IRDiv: | ||||||
| 			case IRMul: | 			case IRMul: | ||||||
| 			case IREq: | 			case IREq: | ||||||
|  | 			case IRNeq: | ||||||
| 			case IRLt: | 			case IRLt: | ||||||
| 			case IRLe: | 			case IRLe: | ||||||
| 				stack_fit(&s, instr->Binary.addr); | 			case IRAnd: | ||||||
| 				TRY_ELSE(s.mem[instr->Binary.addr] = eval_binary(instr->instr, | 			case IROr: { | ||||||
|  | 				Value res; | ||||||
|  | 				TRY_ELSE(res = eval_binary(instr->instr, | ||||||
| 					irparam_to_val(&s, &instr->Binary.lhs), | 					irparam_to_val(&s, &instr->Binary.lhs), | ||||||
| 					irparam_to_val(&s, &instr->Binary.rhs)), | 					irparam_to_val(&s, &instr->Binary.rhs)), | ||||||
| 					{free(fn_args); stack_term(&s);}); | 					{free(fn_args); stack_term(&s);}); | ||||||
|  | 				stack_assign(&s, instr->Binary.addr, &res); | ||||||
| 				break; | 				break; | ||||||
|  | 			} | ||||||
| 			case IRJmp: | 			case IRJmp: | ||||||
| 				i = instr->Jmp.iaddr; | 				if (instr->Jmp.iaddr < ir->len) | ||||||
|  | 					i = ir->index[instr->Jmp.iaddr]; | ||||||
|  | 				else | ||||||
|  | 					i = NULL; | ||||||
| 				continue; | 				continue; | ||||||
| 			case IRJnz: | 			case IRJnz: | ||||||
| 				if (is_nonzero(irparam_to_val(&s, &instr->CJmp.condition))) { | 				if (is_nonzero(irparam_to_val(&s, &instr->CJmp.condition))) { | ||||||
| 					i = instr->Jmp.iaddr; | 					if (instr->Jmp.iaddr < ir->len) | ||||||
|  | 						i = ir->index[instr->CJmp.iaddr]; | ||||||
|  | 					else | ||||||
|  | 						i = NULL; | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 				break; | 				break; | ||||||
| 			case IRCallInternal: { | 			case IRCallInternal: { | ||||||
| 				const BuiltinFunc *f = &builtin_funcs[instr->CallI.fid]; | 				const BuiltinFunc *f = &builtin_funcs[instr->CallI.fid]; | ||||||
|  | 				size_t n_args = instr->CallI.n_args; | ||||||
| 				/* make sure enough space for our arguments is allocated */ | 				/* make sure enough space for our arguments is allocated */ | ||||||
| 				if (f->n_args > fn_args_cap) | 				if (n_args > fn_args_cap) | ||||||
| 					fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = f->n_args)); | 					fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = n_args)); | ||||||
| 				/* copy arguments into buffer */ | 				/* copy arguments into buffer */ | ||||||
| 				for (size_t i = 0; i < f->n_args; i++) | 				for (size_t i = 0; i < n_args; i++) | ||||||
| 					fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]); | 					fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]); | ||||||
|  |  | ||||||
| 				stack_fit(&s, instr->CallI.ret_addr); | 				if (f->returns) { | ||||||
| 				TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->func(fn_args), | 					Value res; | ||||||
| 					{free(fn_args); stack_term(&s);}); | 					if (f->kind == FuncVarArgs) { | ||||||
|  | 						size_t min_args = f->VarArgs.min_args; | ||||||
|  | 						TRY_ELSE(res = f->VarArgs.WithRet.func(n_args - min_args, fn_args), | ||||||
|  | 							{free(fn_args); stack_term(&s);}); | ||||||
|  | 					} else if (f->kind == FuncFixedArgs) { | ||||||
|  | 						TRY_ELSE(res = f->FixedArgs.WithRet.func(fn_args), | ||||||
|  | 							{free(fn_args); stack_term(&s);}); | ||||||
|  | 					} else | ||||||
|  | 						ASSERT_UNREACHED(); | ||||||
|  | 					stack_assign(&s, instr->CallI.ret_addr, &res); | ||||||
|  | 				} else { | ||||||
|  | 					if (f->kind == FuncVarArgs) { | ||||||
|  | 						size_t min_args = f->VarArgs.min_args; | ||||||
|  | 						TRY_ELSE(f->VarArgs.NoRet.func(n_args - min_args, fn_args), | ||||||
|  | 							{free(fn_args); stack_term(&s);}); | ||||||
|  | 					} else if (f->kind == FuncFixedArgs) { | ||||||
|  | 						TRY_ELSE(f->FixedArgs.NoRet.func(fn_args), | ||||||
|  | 							{free(fn_args); stack_term(&s);}); | ||||||
|  | 					} else | ||||||
|  | 						ASSERT_UNREACHED(); | ||||||
|  | 				} | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			case IRArrMake: { | ||||||
|  | 				size_t arr_len = instr->ArrMake.len, arr_cap = instr->ArrMake.cap; | ||||||
|  | 				Value arr = { | ||||||
|  | 					.type = TypeArr, | ||||||
|  | 					.Arr = { | ||||||
|  | 						.type = TypeVoid, | ||||||
|  | 						.is_string = false, | ||||||
|  | 						.dynamically_allocated = true, | ||||||
|  | 						.vals = NULL, | ||||||
|  | 						.len = arr_len, | ||||||
|  | 						.cap = arr_len ? arr_cap : 0, | ||||||
|  | 					}, | ||||||
|  | 				}; | ||||||
|  | 				if (arr_len) { | ||||||
|  | 					Type arr_ty = irparam_to_val(&s, &instr->ArrMake.vals[0])->type; | ||||||
|  | 					void *arr_vals = xmalloc(type_size[arr_ty] * arr_cap); | ||||||
|  | 					for (size_t j = 0; j < arr_len; j++) { | ||||||
|  | 						Value *v = irparam_to_val(&s, &instr->ArrMake.vals[j]); | ||||||
|  | 						if (v->type != arr_ty) { | ||||||
|  | 							set_err("Type of array item %zu (%s) differs from array type (%s)", j, type_str[v->type], type_str[arr_ty]); | ||||||
|  | 							free(arr_vals); | ||||||
|  | 							free(fn_args); | ||||||
|  | 							stack_term(&s); | ||||||
|  | 							return; | ||||||
|  | 						} | ||||||
|  | 						memcpy((uint8_t*)arr_vals + type_size[arr_ty] * j, &v->Void, type_size[arr_ty]); | ||||||
|  | 					} | ||||||
|  | 					arr.Arr.type = arr_ty; | ||||||
|  | 					arr.Arr.vals = arr_vals; | ||||||
|  | 				} | ||||||
|  | 				stack_assign(&s, instr->ArrMake.arr_addr, &arr); | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 			default: | 			default: | ||||||
| 				ASSERT_UNREACHED(); | 				ASSERT_UNREACHED(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		i++; | 		i = i->next; | ||||||
| 	} | 	} | ||||||
| 	stack_term(&s); | 	stack_term(&s); | ||||||
| 	free(fn_args); | 	free(fn_args); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user