rewrite expression parser with parentheses
This commit is contained in:
		@@ -4,9 +4,8 @@ k := 0
 | 
				
			|||||||
iterations := 100
 | 
					iterations := 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
while k - iterations {
 | 
					while k - iterations {
 | 
				
			||||||
	// This is only so messy because I haven't implemented parentheses yet so I
 | 
						k_f := float(k)
 | 
				
			||||||
	// have to wrap everything into functions instead.
 | 
						sum = sum + 1.0 / pow(16.0, k_f) * (4.0 / (8.0 * k_f + 1.0) - 2.0 / (8.0 * k_f + 4.0) - 1.0 / (8.0 * k_f + 5.0) - 1.0 / (8.0 * k_f + 6.0))
 | 
				
			||||||
	sum = sum + 1.0 / pow(16.0, float(k)) * float(4.0 / float(8.0 * float(k) + 1.0) - 2.0 / float(8.0 * float(k) + 4.0) - 1.0 / float(8.0 * float(k) + 5.0) - 1.0 / float(8.0 * float(k) + 6.0))
 | 
					 | 
				
			||||||
	k = k + 1
 | 
						k = k + 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ x := 1
 | 
				
			|||||||
y := 1
 | 
					y := 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
i := 60
 | 
					i := 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
while i {
 | 
					while i {
 | 
				
			||||||
	z := x + y
 | 
						z := x + y
 | 
				
			||||||
	y = x
 | 
						y = x
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								ir.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								ir.h
									
									
									
									
									
								
							@@ -61,11 +61,6 @@ typedef struct IRTok {
 | 
				
			|||||||
			IRParam lhs, rhs;
 | 
								IRParam lhs, rhs;
 | 
				
			||||||
		} Arith;
 | 
							} Arith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		struct {
 | 
					 | 
				
			||||||
			IRArgs *args;
 | 
					 | 
				
			||||||
			size_t args_size;
 | 
					 | 
				
			||||||
		} Print;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		struct {
 | 
							struct {
 | 
				
			||||||
			size_t iaddr;
 | 
								size_t iaddr;
 | 
				
			||||||
		} Jmp;
 | 
							} Jmp;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										358
									
								
								parse.c
									
									
									
									
									
								
							
							
						
						
									
										358
									
								
								parse.c
									
									
									
									
									
								
							@@ -12,25 +12,27 @@ typedef struct Scope {
 | 
				
			|||||||
	Map ident_addrs;
 | 
						Map ident_addrs;
 | 
				
			||||||
} Scope;
 | 
					} Scope;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct ExprMode {
 | 
					typedef struct ExprRet {
 | 
				
			||||||
	bool ignore_newln;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enum {
 | 
						enum {
 | 
				
			||||||
		ExprModeJustCollapse, /* should leave either a literal or an own address as result */
 | 
							ExprRetVal,
 | 
				
			||||||
		ExprModeStorageAddr,  /* should use the supplied storage address in any case; should leave no token behind */
 | 
							ExprRetIdent,
 | 
				
			||||||
 | 
							ExprRetLastInstr,
 | 
				
			||||||
	} kind;
 | 
						} kind;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	union {
 | 
						union {
 | 
				
			||||||
		size_t StorageAddr;
 | 
							IRTok LastInstr;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
} ExprMode;
 | 
					} ExprRet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void mark_err(const Tok *t);
 | 
					static void mark_err(const Tok *t);
 | 
				
			||||||
 | 
					static void set_irtok_dest_addr(IRTok *t, size_t addr);
 | 
				
			||||||
static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos);
 | 
					static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos);
 | 
				
			||||||
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 void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, ExprMode mode);
 | 
					static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t);
 | 
				
			||||||
 | 
					static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr);
 | 
				
			||||||
 | 
					static IRParam expr_into_irparam(IRToks *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 stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void mark_err(const Tok *t) {
 | 
					static void mark_err(const Tok *t) {
 | 
				
			||||||
@@ -38,6 +40,26 @@ static void mark_err(const Tok *t) {
 | 
				
			|||||||
	err_col = t->col;
 | 
						err_col = t->col;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void set_irtok_dest_addr(IRTok *t, size_t addr) {
 | 
				
			||||||
 | 
						switch (t->instr) {
 | 
				
			||||||
 | 
							case IRSet:
 | 
				
			||||||
 | 
							case IRNeg:
 | 
				
			||||||
 | 
								t->Unary.addr = addr;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case IRAdd:
 | 
				
			||||||
 | 
							case IRSub:
 | 
				
			||||||
 | 
							case IRMul:
 | 
				
			||||||
 | 
							case IRDiv:
 | 
				
			||||||
 | 
								t->Arith.addr = addr;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case IRCallInternal:
 | 
				
			||||||
 | 
								t->CallI.ret_addr = addr;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								ASSERT_UNREACHED();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos) {
 | 
					static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpos) {
 | 
				
			||||||
	size_t addr;
 | 
						size_t addr;
 | 
				
			||||||
	bool exists = false;
 | 
						bool exists = false;
 | 
				
			||||||
@@ -91,8 +113,23 @@ static void term_scope(Scope *sc) {
 | 
				
			|||||||
		map_term(&sc->ident_addrs);
 | 
							map_term(&sc->ident_addrs);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, ExprMode mode) {
 | 
					/* The job of this function is to reduce the expression to the most simple form
 | 
				
			||||||
	/* A simplified example of how the operator precedence parsing works:
 | 
					 * writing the least IR instructions possible (without overanalyzing).
 | 
				
			||||||
 | 
					 * This means that the only IR instructions it will be writing are those for
 | 
				
			||||||
 | 
					 * calculating intermediate values.
 | 
				
			||||||
 | 
					 * In the case of ExprRetVal and ExprRetIdent, the value isn't 'returned' in
 | 
				
			||||||
 | 
					 * the traditional sense, but rather the result is left in the token stream.
 | 
				
			||||||
 | 
					 * The 'return' value can be of 3 different types:
 | 
				
			||||||
 | 
					 * - ExprRetVal: The expression yields a constant value as a result.
 | 
				
			||||||
 | 
					 *   Examples: '5', '5 + 2 * 3' or '5 + (2 + 1) * 3'
 | 
				
			||||||
 | 
					 * - ExprRetIdent: The expression yields an identifier as a result.
 | 
				
			||||||
 | 
					 *   Examples: 'a' or '(((a)))'
 | 
				
			||||||
 | 
					 * - ExprRetLastInstr: The expression is a more complex sequence of
 | 
				
			||||||
 | 
					 *   instructions. Here the last instruction is returned so the caller can
 | 
				
			||||||
 | 
					 *   manually set the destination address.
 | 
				
			||||||
 | 
					 *   Examples: 'a + 1', '2 + a * b' or '2 + 4 * (b * b) / 5'
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Here is also a simplified example of how the operator precedence parsing works:
 | 
				
			||||||
 * ________________________________
 | 
					 * ________________________________
 | 
				
			||||||
 *  Where t points to (between l_op and r_op in each step)
 | 
					 *  Where t points to (between l_op and r_op in each step)
 | 
				
			||||||
 *     |
 | 
					 *     |
 | 
				
			||||||
@@ -127,10 +164,9 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
 *  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) {
 | 
				
			||||||
	TokListItem *start = t;
 | 
						TokListItem *start = t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Each expression and subexpression has its own scope. */
 | 
					 | 
				
			||||||
	Scope sc = make_scope(parent_sc, false);
 | 
						Scope sc = make_scope(parent_sc, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (;;) {
 | 
						for (;;) {
 | 
				
			||||||
@@ -141,88 +177,101 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
			negate = true;
 | 
								negate = true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Ignore newlines if told to do so. */
 | 
							/* Collapse parentheses. */
 | 
				
			||||||
		if (mode.ignore_newln && t->next->tok.kind == TokOp && t->next->tok.Op == OpNewLn)
 | 
							if (t->tok.kind == TokOp && t->tok.Op == OpLParen) {
 | 
				
			||||||
 | 
								ExprRet r;
 | 
				
			||||||
 | 
								TRY_RET(r = expr(out_ir, toks, funcs, &sc, t->next), (ExprRet){0});
 | 
				
			||||||
 | 
								if (r.kind == ExprRetLastInstr) {
 | 
				
			||||||
 | 
									size_t res_addr = sc.mem_addr++;
 | 
				
			||||||
 | 
									set_irtok_dest_addr(&r.LastInstr, res_addr);
 | 
				
			||||||
 | 
									irtoks_app(out_ir, r.LastInstr);
 | 
				
			||||||
 | 
									t->tok = (Tok){
 | 
				
			||||||
 | 
										.ln = t->tok.ln,
 | 
				
			||||||
 | 
										.col = t->tok.col,
 | 
				
			||||||
 | 
										.kind = TokIdent,
 | 
				
			||||||
 | 
										.Ident = {
 | 
				
			||||||
 | 
											.kind = IdentAddr,
 | 
				
			||||||
 | 
											.Addr = res_addr,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
								} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) {
 | 
				
			||||||
 | 
									t->tok = t->next->tok;
 | 
				
			||||||
				toklist_del(toks, t->next, t->next);
 | 
									toklist_del(toks, t->next, t->next);
 | 
				
			||||||
 | 
								} else
 | 
				
			||||||
 | 
									ASSERT_UNREACHED();
 | 
				
			||||||
 | 
								toklist_del(toks, t->next, t->next);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		/* Collapse function call. */
 | 
							/* Collapse function call. */
 | 
				
			||||||
		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 */
 | 
				
			||||||
			BuiltinFunc func;
 | 
								BuiltinFunc func;
 | 
				
			||||||
			bool exists = map_get(funcs, t->tok.Ident.Name, &func);
 | 
								bool exists = map_get(funcs, t->tok.Ident.Name, &func);
 | 
				
			||||||
			if (!exists) {
 | 
								if (!exists) {
 | 
				
			||||||
				mark_err(&t->tok);
 | 
									mark_err(&t->tok);
 | 
				
			||||||
				set_err("Unrecognized function: %s()", t->tok.Ident.Name);
 | 
									set_err("Unrecognized function: %s()", t->tok.Ident.Name);
 | 
				
			||||||
				return;
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			TokListItem *func_ident = t;
 | 
								TokListItem *func_ident = t;
 | 
				
			||||||
			t = t->next->next;
 | 
								t = func_ident->next;
 | 
				
			||||||
			toklist_del(toks, t->prev, t->prev);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			size_t n_args = 0;
 | 
					 | 
				
			||||||
			TokListItem *first_arg = t;
 | 
					 | 
				
			||||||
			TokListItem *last_arg = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* we want to try to eliminate function calls at runtime if possible */
 | 
				
			||||||
			bool eval_func_in_place = !func.side_effects;
 | 
								bool eval_func_in_place = !func.side_effects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								size_t args_len = 0;
 | 
				
			||||||
 | 
								size_t args_cap = 16;
 | 
				
			||||||
 | 
								IRParam *args = xmalloc(sizeof(IRParam) * args_cap);
 | 
				
			||||||
			for (;;) {
 | 
								for (;;) {
 | 
				
			||||||
				n_args++;
 | 
									if (args_len+1 > args_cap)
 | 
				
			||||||
				TRY(expr(out_ir, toks, funcs, &sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = true }));
 | 
										args = xrealloc(args, (args_cap *= 2));
 | 
				
			||||||
				if (t->tok.kind == TokIdent)
 | 
									IRParam a;
 | 
				
			||||||
 | 
									TRY_RET_ELSE(a = expr_into_irparam(out_ir, toks, funcs, &sc, t->next), (ExprRet){0}, free(args));
 | 
				
			||||||
 | 
									args[args_len++] = a;
 | 
				
			||||||
 | 
									if (a.kind != IRParamLiteral)
 | 
				
			||||||
					eval_func_in_place = false;
 | 
										eval_func_in_place = false;
 | 
				
			||||||
				if (t->next->tok.kind == TokOp) {
 | 
									if (t->next->tok.kind == TokOp) {
 | 
				
			||||||
					if (t->next->tok.Op == OpComma) {
 | 
										if (t->next->tok.Op == OpComma) {
 | 
				
			||||||
						toklist_del(toks, t->next, t->next);
 | 
											toklist_del(toks, t->next, t->next);
 | 
				
			||||||
						t = t->next;
 | 
					 | 
				
			||||||
						continue;
 | 
											continue;
 | 
				
			||||||
					} else if (t->next->tok.Op == OpRParen) {
 | 
										} else if (t->next->tok.Op == OpRParen) {
 | 
				
			||||||
						toklist_del(toks, t->next, t->next);
 | 
											toklist_del(toks, t->next, t->next);
 | 
				
			||||||
						last_arg = t;
 | 
					 | 
				
			||||||
						break;
 | 
											break;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				mark_err(&t->next->tok);
 | 
									mark_err(&t->next->tok);
 | 
				
			||||||
				set_err("Expected ',' or ')' after function argument");
 | 
									set_err("Expected ',' or ')' after function argument");
 | 
				
			||||||
				return;
 | 
									free(args);
 | 
				
			||||||
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (func.n_args != n_args) {
 | 
								t = func_ident;
 | 
				
			||||||
 | 
								toklist_del(toks, t->next, t->next); /* delete left parenthesis */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (func.n_args != args_len) {
 | 
				
			||||||
				mark_err(&func_ident->tok);
 | 
									mark_err(&func_ident->tok);
 | 
				
			||||||
				const char *plural = func.n_args == 1 ? "" : "s";
 | 
									const char *plural = func.n_args == 1 ? "" : "s";
 | 
				
			||||||
				set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.n_args, plural, n_args);
 | 
									set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.n_args, plural, args_len);
 | 
				
			||||||
				return;
 | 
									free(args);
 | 
				
			||||||
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (eval_func_in_place) {
 | 
								if (eval_func_in_place) {
 | 
				
			||||||
				Value *args = xmalloc(sizeof(Value) * n_args);
 | 
									/* evaluate the function in place */
 | 
				
			||||||
				TokListItem *itm = first_arg;
 | 
									Value *arg_vals = xmalloc(sizeof(Value) * args_len);
 | 
				
			||||||
				for (size_t i = 0;; i++) {
 | 
									for (size_t i = 0; i < args_len; i++)
 | 
				
			||||||
					args[i] = itm->tok.Val;
 | 
										arg_vals[i] = args[i].Literal;
 | 
				
			||||||
					if (itm == last_arg)
 | 
					 | 
				
			||||||
						break;
 | 
					 | 
				
			||||||
					itm = itm->next;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				func_ident->tok = (Tok) {
 | 
									func_ident->tok = (Tok) {
 | 
				
			||||||
					.kind = TokVal,
 | 
										.kind = TokVal,
 | 
				
			||||||
					.Val = func.func(args),
 | 
										.Val = func.func(arg_vals),
 | 
				
			||||||
				};
 | 
									};
 | 
				
			||||||
 | 
									free(arg_vals);
 | 
				
			||||||
				free(args);
 | 
									free(args);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				bool is_last_operation = func_ident == start && last_arg->next->tok.kind == TokOp && op_prec[last_arg->next->tok.Op] == PREC_DELIM;
 | 
									bool is_last_operation = t == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				IRParam *args = xmalloc(sizeof(IRParam) * n_args);
 | 
									size_t res_addr = is_last_operation ? 0 : sc.mem_addr++;
 | 
				
			||||||
				TokListItem *itm = first_arg;
 | 
					 | 
				
			||||||
				for (size_t i = 0;; i++) {
 | 
					 | 
				
			||||||
					TRY_ELSE(args[i] = tok_to_irparam(&sc, &itm->tok), free(args));
 | 
					 | 
				
			||||||
					if (itm == last_arg)
 | 
					 | 
				
			||||||
						break;
 | 
					 | 
				
			||||||
					itm = itm->next;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				size_t res_addr;
 | 
									/* function call instruction */
 | 
				
			||||||
				if (mode.kind == ExprModeStorageAddr && is_last_operation)
 | 
									IRTok ir = {
 | 
				
			||||||
					res_addr = mode.StorageAddr;
 | 
					 | 
				
			||||||
				else
 | 
					 | 
				
			||||||
					res_addr = sc.mem_addr++;
 | 
					 | 
				
			||||||
				irtoks_app(out_ir, (IRTok){
 | 
					 | 
				
			||||||
					.ln =  func_ident->tok.ln,
 | 
										.ln =  func_ident->tok.ln,
 | 
				
			||||||
					.col = func_ident->tok.col,
 | 
										.col = func_ident->tok.col,
 | 
				
			||||||
					.instr = IRCallInternal,
 | 
										.instr = IRCallInternal,
 | 
				
			||||||
@@ -231,12 +280,18 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
						.fid = func.fid,
 | 
											.fid = func.fid,
 | 
				
			||||||
						.args = args,
 | 
											.args = args,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				});
 | 
									};
 | 
				
			||||||
				if (mode.kind == ExprModeStorageAddr && is_last_operation) {
 | 
					
 | 
				
			||||||
					toklist_del(toks, func_ident, func_ident);
 | 
									if (is_last_operation) {
 | 
				
			||||||
					break;
 | 
										/* done */
 | 
				
			||||||
 | 
										toklist_del(toks, t, t);
 | 
				
			||||||
 | 
										return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir };
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					func_ident->tok = (Tok) {
 | 
										/* write IR instruction */
 | 
				
			||||||
 | 
										irtoks_app(out_ir, ir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										/* leave new memory address as result */
 | 
				
			||||||
 | 
										t->tok = (Tok){
 | 
				
			||||||
						.kind = TokIdent,
 | 
											.kind = TokIdent,
 | 
				
			||||||
						.Ident = {
 | 
											.Ident = {
 | 
				
			||||||
							.kind = IdentAddr,
 | 
												.kind = IdentAddr,
 | 
				
			||||||
@@ -245,34 +300,27 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
					};
 | 
										};
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			t = func_ident;
 | 
					 | 
				
			||||||
			toklist_del(toks, first_arg, last_arg);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Collapse negative factor. */
 | 
							/* Collapse negative factor. */
 | 
				
			||||||
		if (negate) {
 | 
							if (negate) {
 | 
				
			||||||
			bool is_last_operation = t->prev == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM;
 | 
								Tok *v = &t->tok; /* what we want to negate */
 | 
				
			||||||
			Tok *v = &t->tok;
 | 
								t = t->prev; /* go back to the '-' sign */
 | 
				
			||||||
			t = t->prev;
 | 
								toklist_del(toks, t->next, t->next); /* again, just removing the reference */
 | 
				
			||||||
			toklist_del(toks, t->next, t->next);
 | 
					
 | 
				
			||||||
 | 
								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 negate value */
 | 
									/* immediately negate value */
 | 
				
			||||||
				t->tok.kind = TokVal;
 | 
									t->tok.kind = TokVal;
 | 
				
			||||||
				t->tok.Val = eval_unary(IRNeg, &v->Val);
 | 
									t->tok.Val = eval_unary(IRNeg, &v->Val);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				/* use the predefined storage address if it was requested and we're on the last operation */
 | 
									size_t res_addr = is_last_operation ? 0 : sc.mem_addr++;
 | 
				
			||||||
				size_t res_addr;
 | 
					 | 
				
			||||||
				if (mode.kind == ExprModeStorageAddr && is_last_operation)
 | 
					 | 
				
			||||||
					res_addr = mode.StorageAddr;
 | 
					 | 
				
			||||||
				else
 | 
					 | 
				
			||||||
					res_addr = sc.mem_addr++;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* add IR instruction to negate the value */
 | 
									/* Instruction to negate. */
 | 
				
			||||||
				IRParam v_irparam;
 | 
									IRParam v_irparam;
 | 
				
			||||||
				TRY(v_irparam = tok_to_irparam(&sc, v));
 | 
									TRY_RET(v_irparam = tok_to_irparam(&sc, v), (ExprRet){0});
 | 
				
			||||||
				irtoks_app(out_ir, (IRTok){
 | 
									IRTok ir = {
 | 
				
			||||||
					.ln = t->tok.ln,
 | 
										.ln = t->tok.ln,
 | 
				
			||||||
					.col = t->tok.col,
 | 
										.col = t->tok.col,
 | 
				
			||||||
					.instr = IRNeg,
 | 
										.instr = IRNeg,
 | 
				
			||||||
@@ -280,13 +328,16 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
						.addr = res_addr,
 | 
											.addr = res_addr,
 | 
				
			||||||
						.val = v_irparam,
 | 
											.val = v_irparam,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				});
 | 
									};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (mode.kind == ExprModeStorageAddr && is_last_operation)  {
 | 
									if (is_last_operation) {
 | 
				
			||||||
					/* done */
 | 
										/* done */
 | 
				
			||||||
					toklist_del(toks, t, t);
 | 
										toklist_del(toks, t, t);
 | 
				
			||||||
					return;
 | 
										return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir };
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
 | 
										/* write IR instruction */
 | 
				
			||||||
 | 
										irtoks_app(out_ir, ir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					/* leave new memory address as result */
 | 
										/* leave new memory address as result */
 | 
				
			||||||
					t->tok.kind = TokIdent;
 | 
										t->tok.kind = TokIdent;
 | 
				
			||||||
					t->tok.Ident = (Identifier){
 | 
										t->tok.Ident = (Identifier){
 | 
				
			||||||
@@ -308,7 +359,7 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
			if (l_op->kind != TokOp) {
 | 
								if (l_op->kind != TokOp) {
 | 
				
			||||||
				mark_err(l_op);
 | 
									mark_err(l_op);
 | 
				
			||||||
				set_err("Expected operator");
 | 
									set_err("Expected operator");
 | 
				
			||||||
				return;
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			l_op_prec = op_prec[l_op->Op];
 | 
								l_op_prec = op_prec[l_op->Op];
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -317,55 +368,42 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
		if (r_op->kind != TokOp) {
 | 
							if (r_op->kind != TokOp) {
 | 
				
			||||||
			mark_err(r_op);
 | 
								mark_err(r_op);
 | 
				
			||||||
			set_err("Expected operator");
 | 
								set_err("Expected operator");
 | 
				
			||||||
			return;
 | 
								return (ExprRet){0};
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		r_op_prec = op_prec[r_op->Op];
 | 
							r_op_prec = op_prec[r_op->Op];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* If l_op and r_op are both delimiters, the expression is fully evaluated.
 | 
							/* If l_op and r_op are both delimiters, we don't have to evaluate
 | 
				
			||||||
		 * NOTE: Sometimes, we don't reach this point because the function already
 | 
							 * anything. */
 | 
				
			||||||
		 * exits directly after the last operation. */
 | 
					 | 
				
			||||||
		if (l_op_prec == PREC_DELIM && r_op_prec == PREC_DELIM) {
 | 
							if (l_op_prec == PREC_DELIM && r_op_prec == PREC_DELIM) {
 | 
				
			||||||
			if (t->tok.kind != TokVal && t->tok.kind != TokIdent) {
 | 
								if (t->tok.kind == TokIdent) {
 | 
				
			||||||
 | 
									return (ExprRet){ .kind = ExprRetIdent };
 | 
				
			||||||
 | 
								} else if (t->tok.kind == TokVal) {
 | 
				
			||||||
 | 
									return (ExprRet){ .kind = ExprRetVal };
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
				mark_err(&t->tok);
 | 
									mark_err(&t->tok);
 | 
				
			||||||
				set_err("Expected literal or identifier");
 | 
									set_err("Expected literal or identifier");
 | 
				
			||||||
				return;
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (mode.kind == ExprModeStorageAddr) {
 | 
					 | 
				
			||||||
				IRParam res;
 | 
					 | 
				
			||||||
				TRY(res = tok_to_irparam(&sc, &t->tok));
 | 
					 | 
				
			||||||
				irtoks_app(out_ir, (IRTok){
 | 
					 | 
				
			||||||
					.ln = t->tok.ln,
 | 
					 | 
				
			||||||
					.col = t->tok.col,
 | 
					 | 
				
			||||||
					.instr = IRSet,
 | 
					 | 
				
			||||||
					.Unary = {
 | 
					 | 
				
			||||||
						.addr = mode.StorageAddr,
 | 
					 | 
				
			||||||
						.val = res,
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
				toklist_del(toks, t, t);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bool is_last_operation = t->prev && t->prev->prev == start && r_op_prec == PREC_DELIM;
 | 
							/* This is the operator precedence parser described above. */
 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* This is the actual operator precedence parser as described above. */
 | 
					 | 
				
			||||||
		if (r_op_prec > l_op_prec)
 | 
							if (r_op_prec > l_op_prec)
 | 
				
			||||||
			t = t->next->next;
 | 
								t = t->next->next;
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
			/* some basic checks */
 | 
					 | 
				
			||||||
			Tok *rhs = &t->tok;
 | 
								Tok *rhs = &t->tok;
 | 
				
			||||||
			if (rhs->kind != TokVal && rhs->kind != TokIdent) {
 | 
								if (rhs->kind != TokVal && rhs->kind != TokIdent) {
 | 
				
			||||||
				mark_err(rhs);
 | 
									mark_err(rhs);
 | 
				
			||||||
				set_err("Expected literal or identifier");
 | 
									set_err("Expected literal or identifier");
 | 
				
			||||||
				return;
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			t = t->prev->prev;
 | 
								t = t->prev->prev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Tok *lhs = &t->tok;
 | 
								Tok *lhs = &t->tok;
 | 
				
			||||||
			if (lhs->kind != TokVal && lhs->kind != TokIdent) {
 | 
								if (lhs->kind != TokVal && lhs->kind != TokIdent) {
 | 
				
			||||||
				mark_err(lhs);
 | 
									mark_err(lhs);
 | 
				
			||||||
				set_err("Expected literal or identifier");
 | 
									set_err("Expected literal or identifier");
 | 
				
			||||||
				return;
 | 
									return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* delete the tokens that fall away from collapsing the expression 
 | 
								/* delete the tokens that fall away from collapsing the expression 
 | 
				
			||||||
@@ -382,26 +420,23 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
				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]);
 | 
				
			||||||
					return;
 | 
										return (ExprRet){0};
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (lhs->kind == TokVal && rhs->kind == TokVal) {
 | 
								if (lhs->kind == TokVal && rhs->kind == TokVal) {
 | 
				
			||||||
				/* evaluate the constant expression immediately */
 | 
									/* evaluate the constant expression immediately */
 | 
				
			||||||
				lhs->kind = TokVal;
 | 
									lhs->kind = TokVal;
 | 
				
			||||||
				TRY(lhs->Val = eval_arith(instr, &lhs->Val, &rhs->Val));
 | 
									TRY_RET(lhs->Val = eval_arith(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(lhs_irparam = tok_to_irparam(&sc, lhs));
 | 
									TRY_RET(lhs_irparam = tok_to_irparam(&sc, lhs), (ExprRet){0});
 | 
				
			||||||
				TRY(rhs_irparam = tok_to_irparam(&sc, rhs));
 | 
									TRY_RET(rhs_irparam = tok_to_irparam(&sc, rhs), (ExprRet){0});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* use the predefined storage address if it was requested and we're on the last operation */
 | 
									size_t res_addr = is_last_operation ? 0 : sc.mem_addr++;
 | 
				
			||||||
				size_t res_addr;
 | 
					 | 
				
			||||||
				if (mode.kind == ExprModeStorageAddr && is_last_operation)
 | 
					 | 
				
			||||||
					res_addr = mode.StorageAddr;
 | 
					 | 
				
			||||||
				else
 | 
					 | 
				
			||||||
					res_addr = sc.mem_addr++;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* emit IR code to evaluate the non-constant expression */
 | 
									IRTok ir = {
 | 
				
			||||||
				irtoks_app(out_ir, (IRTok){
 | 
					 | 
				
			||||||
					.ln = l_op->ln,
 | 
										.ln = l_op->ln,
 | 
				
			||||||
					.col = l_op->col,
 | 
										.col = l_op->col,
 | 
				
			||||||
					.instr = instr,
 | 
										.instr = instr,
 | 
				
			||||||
@@ -410,13 +445,16 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
						.lhs = lhs_irparam,
 | 
											.lhs = lhs_irparam,
 | 
				
			||||||
						.rhs = rhs_irparam,
 | 
											.rhs = rhs_irparam,
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				});
 | 
									};
 | 
				
			||||||
				
 | 
									
 | 
				
			||||||
				if (mode.kind == ExprModeStorageAddr && is_last_operation) {
 | 
									if (is_last_operation) {
 | 
				
			||||||
					/* done */
 | 
										/* done */
 | 
				
			||||||
					toklist_del(toks, t, t);
 | 
										toklist_del(toks, t, t);
 | 
				
			||||||
					break;
 | 
										return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir };
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
 | 
										/* write IR instruction */
 | 
				
			||||||
 | 
										irtoks_app(out_ir, ir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					/* leave new memory address as result */
 | 
										/* leave new memory address as result */
 | 
				
			||||||
					lhs->kind = TokIdent;
 | 
										lhs->kind = TokIdent;
 | 
				
			||||||
					lhs->Ident = (Identifier){
 | 
										lhs->Ident = (Identifier){
 | 
				
			||||||
@@ -429,13 +467,63 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void expr_into_addr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr) {
 | 
				
			||||||
 | 
						ExprRet r;
 | 
				
			||||||
 | 
						TRY(r = expr(out_ir, toks, funcs, parent_sc, t));
 | 
				
			||||||
 | 
						if (r.kind == ExprRetLastInstr) {
 | 
				
			||||||
 | 
							set_irtok_dest_addr(&r.LastInstr, addr);
 | 
				
			||||||
 | 
							irtoks_app(out_ir, r.LastInstr);
 | 
				
			||||||
 | 
							t->tok = (Tok){
 | 
				
			||||||
 | 
								.ln = t->tok.ln,
 | 
				
			||||||
 | 
								.col = t->tok.col,
 | 
				
			||||||
 | 
								.kind = TokIdent,
 | 
				
			||||||
 | 
								.Ident = {
 | 
				
			||||||
 | 
									.kind = IdentAddr,
 | 
				
			||||||
 | 
									.Addr = addr,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) {
 | 
				
			||||||
 | 
							IRParam res;
 | 
				
			||||||
 | 
							TRY(res = tok_to_irparam(parent_sc, &t->tok));
 | 
				
			||||||
 | 
							irtoks_app(out_ir, (IRTok){
 | 
				
			||||||
 | 
								.instr = IRSet,
 | 
				
			||||||
 | 
								.Unary = {
 | 
				
			||||||
 | 
									.addr = addr,
 | 
				
			||||||
 | 
									.val = res,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							toklist_del(toks, t, t);
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							ASSERT_UNREACHED();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static IRParam expr_into_irparam(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t) {
 | 
				
			||||||
 | 
						ExprRet r;
 | 
				
			||||||
 | 
						TRY_RET(r = expr(out_ir, toks, funcs, parent_sc, t), (IRParam){0});
 | 
				
			||||||
 | 
						if (r.kind == ExprRetLastInstr) {
 | 
				
			||||||
 | 
							Scope sc = make_scope(parent_sc, false);
 | 
				
			||||||
 | 
							size_t addr = sc.mem_addr++;
 | 
				
			||||||
 | 
							set_irtok_dest_addr(&r.LastInstr, addr);
 | 
				
			||||||
 | 
							irtoks_app(out_ir, r.LastInstr);
 | 
				
			||||||
 | 
							return (IRParam){
 | 
				
			||||||
 | 
								.kind = IRParamAddr,
 | 
				
			||||||
 | 
								.Addr = addr,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						} else if (r.kind == ExprRetVal || r.kind == ExprRetIdent) {
 | 
				
			||||||
 | 
							IRParam ret;
 | 
				
			||||||
 | 
							TRY_RET(ret = tok_to_irparam(parent_sc, &t->tok), (IRParam){0});
 | 
				
			||||||
 | 
							toklist_del(toks, t, t);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							ASSERT_UNREACHED();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t) {
 | 
					static void stmt(IRToks *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) {
 | 
				
			||||||
			t = t->next;
 | 
					 | 
				
			||||||
			size_t addr = sc->mem_addr++;
 | 
								size_t addr = sc->mem_addr++;
 | 
				
			||||||
			bool replaced = map_insert(&sc->ident_addrs, name, &addr);
 | 
								bool replaced = map_insert(&sc->ident_addrs, name, &addr);
 | 
				
			||||||
			if (replaced) {
 | 
								if (replaced) {
 | 
				
			||||||
@@ -443,12 +531,11 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
 | 
				
			|||||||
				set_err("'%s' already declared in this scope", name);
 | 
									set_err("'%s' already declared in this scope", name);
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			TRY(expr(out_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr }));
 | 
								TRY(expr_into_addr(out_ir, toks, funcs, sc, t->next, addr));
 | 
				
			||||||
		} else if (t->tok.kind == TokAssign) {
 | 
							} else if (t->tok.kind == TokAssign) {
 | 
				
			||||||
			t = t->next;
 | 
					 | 
				
			||||||
			size_t addr;
 | 
								size_t addr;
 | 
				
			||||||
			TRY(addr = get_ident_addr(sc, name, &start->tok));
 | 
								TRY(addr = get_ident_addr(sc, name, &start->tok));
 | 
				
			||||||
			TRY(expr(out_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr }));
 | 
								TRY(expr_into_addr(out_ir, toks, funcs, sc, t->next, addr));
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			ASSERT_UNREACHED();
 | 
								ASSERT_UNREACHED();
 | 
				
			||||||
	} else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) {
 | 
						} else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) {
 | 
				
			||||||
@@ -488,22 +575,20 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		t = t->next;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* parse condition */
 | 
							/* parse condition */
 | 
				
			||||||
		IRToks cond_ir;
 | 
							IRToks cond_ir;
 | 
				
			||||||
		irtoks_init_short(&cond_ir);
 | 
							irtoks_init_short(&cond_ir);
 | 
				
			||||||
		TRY_ELSE(expr(&cond_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = true }), irtoks_term(&cond_ir));
 | 
							IRParam cond;
 | 
				
			||||||
		IRParam cond_irparam;
 | 
							TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir));
 | 
				
			||||||
		TRY_ELSE(cond_irparam = tok_to_irparam(sc, &t->tok), irtoks_term(&cond_ir));
 | 
					
 | 
				
			||||||
		/* add conditional jump */
 | 
							/* add conditional jump */
 | 
				
			||||||
		irtoks_app(&cond_ir, (IRTok){
 | 
							irtoks_app(&cond_ir, (IRTok){
 | 
				
			||||||
			.ln = t->tok.ln,
 | 
								.ln = t->next->tok.ln,
 | 
				
			||||||
			.col = t->tok.col,
 | 
								.col = t->next->tok.col,
 | 
				
			||||||
			.instr = IRJnz,
 | 
								.instr = IRJnz,
 | 
				
			||||||
			.CJmp = {
 | 
								.CJmp = {
 | 
				
			||||||
				.iaddr = jmp_instr_iaddr + 1,
 | 
									.iaddr = jmp_instr_iaddr + 1,
 | 
				
			||||||
				.condition = cond_irparam,
 | 
									.condition = cond,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -521,7 +606,8 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt
 | 
				
			|||||||
	} else if (t->tok.kind == TokOp && t->tok.Op == OpNewLn) {
 | 
						} else if (t->tok.kind == TokOp && t->tok.Op == OpNewLn) {
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* assume expression */
 | 
							/* assume expression */
 | 
				
			||||||
		TRY(expr(out_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = false }));
 | 
							TRY(expr_into_irparam(out_ir, toks, funcs, sc, t));
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	toklist_del(toks, start, t);
 | 
						toklist_del(toks, start, t);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								vm.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								vm.c
									
									
									
									
									
								
							@@ -46,21 +46,23 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) {
 | 
				
			|||||||
	Stack s = stack_make();
 | 
						Stack s = stack_make();
 | 
				
			||||||
	for (size_t i = 0; i < ir->len;) {
 | 
						for (size_t i = 0; i < ir->len;) {
 | 
				
			||||||
		IRTok *instr = &ir->toks[i];
 | 
							IRTok *instr = &ir->toks[i];
 | 
				
			||||||
 | 
							err_ln = instr->ln;
 | 
				
			||||||
 | 
							err_col = instr->col;
 | 
				
			||||||
		switch (instr->instr) {
 | 
							switch (instr->instr) {
 | 
				
			||||||
			case IRSet:
 | 
								case IRSet:
 | 
				
			||||||
			case IRNeg:
 | 
								case IRNeg:
 | 
				
			||||||
				stack_fit(&s, instr->Unary.addr);
 | 
									stack_fit(&s, instr->Unary.addr);
 | 
				
			||||||
				s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val));
 | 
									TRY(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)));
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case IRAdd:
 | 
								case IRAdd:
 | 
				
			||||||
			case IRSub:
 | 
								case IRSub:
 | 
				
			||||||
			case IRDiv:
 | 
								case IRDiv:
 | 
				
			||||||
			case IRMul:
 | 
								case IRMul:
 | 
				
			||||||
				stack_fit(&s, instr->Arith.addr);
 | 
									stack_fit(&s, instr->Arith.addr);
 | 
				
			||||||
				s.mem[instr->Arith.addr] = eval_arith(instr->instr,
 | 
									TRY(s.mem[instr->Arith.addr] = eval_arith(instr->instr,
 | 
				
			||||||
					irparam_to_val(&s, &instr->Arith.lhs),
 | 
										irparam_to_val(&s, &instr->Arith.lhs),
 | 
				
			||||||
					irparam_to_val(&s, &instr->Arith.rhs)
 | 
										irparam_to_val(&s, &instr->Arith.rhs)
 | 
				
			||||||
				);
 | 
									));
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case IRJmp:
 | 
								case IRJmp:
 | 
				
			||||||
				i = instr->Jmp.iaddr;
 | 
									i = instr->Jmp.iaddr;
 | 
				
			||||||
@@ -78,7 +80,7 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) {
 | 
				
			|||||||
					args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
 | 
										args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				stack_fit(&s, instr->CallI.ret_addr);
 | 
									stack_fit(&s, instr->CallI.ret_addr);
 | 
				
			||||||
				s.mem[instr->CallI.ret_addr] = f->func(args);
 | 
									TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->func(args), free(args));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				free(args);
 | 
									free(args);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user