From 298883939b3cd4b0c02d1fcaf9ba45a153ea9d6e Mon Sep 17 00:00:00 2001 From: r4 Date: Thu, 23 Dec 2021 15:56:12 +0100 Subject: [PATCH] rewrite expression parser with parentheses --- calculate_pi.script | 5 +- example.script | 1 + ir.h | 5 - parse.c | 428 ++++++++++++++++++++++++++------------------ vm.c | 10 +- 5 files changed, 266 insertions(+), 183 deletions(-) diff --git a/calculate_pi.script b/calculate_pi.script index 2f9ed6e..1bf4a5e 100644 --- a/calculate_pi.script +++ b/calculate_pi.script @@ -4,9 +4,8 @@ k := 0 iterations := 100 while k - iterations { - // This is only so messy because I haven't implemented parentheses yet so I - // have to wrap everything into functions instead. - 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_f := float(k) + 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)) k = k + 1 } diff --git a/example.script b/example.script index 64a1509..8ff88c6 100644 --- a/example.script +++ b/example.script @@ -2,6 +2,7 @@ x := 1 y := 1 i := 60 + while i { z := x + y y = x diff --git a/ir.h b/ir.h index 1be102f..757ecc1 100644 --- a/ir.h +++ b/ir.h @@ -61,11 +61,6 @@ typedef struct IRTok { IRParam lhs, rhs; } Arith; - struct { - IRArgs *args; - size_t args_size; - } Print; - struct { size_t iaddr; } Jmp; diff --git a/parse.c b/parse.c index 11100e8..df7973a 100644 --- a/parse.c +++ b/parse.c @@ -12,25 +12,27 @@ typedef struct Scope { Map ident_addrs; } Scope; -typedef struct ExprMode { - bool ignore_newln; - +typedef struct ExprRet { enum { - ExprModeJustCollapse, /* should leave either a literal or an own address as result */ - ExprModeStorageAddr, /* should use the supplied storage address in any case; should leave no token behind */ + ExprRetVal, + ExprRetIdent, + ExprRetLastInstr, } kind; union { - size_t StorageAddr; + IRTok LastInstr; }; -} ExprMode; +} ExprRet; 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 IRParam tok_to_irparam(Scope *sc, Tok *t); static Scope make_scope(Scope *parent, bool with_idents); 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 mark_err(const Tok *t) { @@ -38,6 +40,26 @@ static void mark_err(const Tok *t) { 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) { size_t addr; bool exists = false; @@ -91,46 +113,60 @@ static void term_scope(Scope *sc) { map_term(&sc->ident_addrs); } -static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, ExprMode mode) { - /* A simplified example of how the operator precedence parsing works: - * ________________________________ - * Where t points to (between l_op and r_op in each step) - * | - * v - * 5 + 2 * 2 \n - * ^ ^ - * | | - * l_op r_op - * precedence of '+' is higher than that of the front delimiter => move forward - * ________________________________ - * 5 + 2 * 2 \n - * ^ ^ - * | | - * l_op r_op - * precedence of '*' is higher than that of '+' => move forward - * ________________________________ - * 5 + 2 * 2 \n - * ^ ^ - * | | - * l_op r_op - * precedence of '\n' (a delimiter) is lower than that of '*' => evaluate and move l_op 2 back - * ________________________________ - * 5 + 4 \n - * ^ ^ - * | | - * l_op r_op - * precedence of '\n' (a delimiter) is lower than that of '+' => evaluate and move l_op 2 back - * ________________________________ - * 9 \n - * ^ ^ - * | | - * l_op r_op - * both l_op and r_op are delimiters (their precedence is PREC_DELIM) => done - */ - +/* The job of this function is to reduce the expression to the most simple form + * 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) + * | + * v + * 5 + 2 * 2 \n + * ^ ^ + * | | + * l_op r_op + * precedence of '+' is higher than that of the front delimiter => move forward + * ________________________________ + * 5 + 2 * 2 \n + * ^ ^ + * | | + * l_op r_op + * precedence of '*' is higher than that of '+' => move forward + * ________________________________ + * 5 + 2 * 2 \n + * ^ ^ + * | | + * l_op r_op + * precedence of '\n' (a delimiter) is lower than that of '*' => evaluate and move l_op 2 back + * ________________________________ + * 5 + 4 \n + * ^ ^ + * | | + * l_op r_op + * precedence of '\n' (a delimiter) is lower than that of '+' => evaluate and move l_op 2 back + * ________________________________ + * 9 \n + * ^ ^ + * | | + * l_op r_op + * 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; - /* Each expression and subexpression has its own scope. */ Scope sc = make_scope(parent_sc, false); for (;;) { @@ -141,88 +177,101 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To negate = true; } - /* Ignore newlines if told to do so. */ - if (mode.ignore_newln && t->next->tok.kind == TokOp && t->next->tok.Op == OpNewLn) + /* Collapse parentheses. */ + 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); + } else + ASSERT_UNREACHED(); toklist_del(toks, t->next, t->next); - + } /* 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; bool exists = map_get(funcs, t->tok.Ident.Name, &func); if (!exists) { mark_err(&t->tok); set_err("Unrecognized function: %s()", t->tok.Ident.Name); - return; + return (ExprRet){0}; } TokListItem *func_ident = t; - t = t->next->next; - toklist_del(toks, t->prev, t->prev); + t = func_ident->next; - 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; + + size_t args_len = 0; + size_t args_cap = 16; + IRParam *args = xmalloc(sizeof(IRParam) * args_cap); for (;;) { - n_args++; - TRY(expr(out_ir, toks, funcs, &sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = true })); - if (t->tok.kind == TokIdent) + if (args_len+1 > args_cap) + args = xrealloc(args, (args_cap *= 2)); + 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; if (t->next->tok.kind == TokOp) { if (t->next->tok.Op == OpComma) { toklist_del(toks, t->next, t->next); - t = t->next; continue; } else if (t->next->tok.Op == OpRParen) { toklist_del(toks, t->next, t->next); - last_arg = t; break; } } mark_err(&t->next->tok); 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); 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); - return; + set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.n_args, plural, args_len); + free(args); + return (ExprRet){0}; } if (eval_func_in_place) { - Value *args = xmalloc(sizeof(Value) * n_args); - TokListItem *itm = first_arg; - for (size_t i = 0;; i++) { - args[i] = itm->tok.Val; - if (itm == last_arg) - break; - itm = itm->next; - } + /* evaluate the function in place */ + Value *arg_vals = xmalloc(sizeof(Value) * args_len); + for (size_t i = 0; i < args_len; i++) + arg_vals[i] = args[i].Literal; func_ident->tok = (Tok) { .kind = TokVal, - .Val = func.func(args), + .Val = func.func(arg_vals), }; + free(arg_vals); free(args); } 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); - 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 = 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++; - irtoks_app(out_ir, (IRTok){ + /* function call instruction */ + IRTok ir = { .ln = func_ident->tok.ln, .col = func_ident->tok.col, .instr = IRCallInternal, @@ -231,12 +280,18 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To .fid = func.fid, .args = args, }, - }); - if (mode.kind == ExprModeStorageAddr && is_last_operation) { - toklist_del(toks, func_ident, func_ident); - break; + }; + + if (is_last_operation) { + /* done */ + toklist_del(toks, t, t); + return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir }; } else { - func_ident->tok = (Tok) { + /* write IR instruction */ + irtoks_app(out_ir, ir); + + /* leave new memory address as result */ + t->tok = (Tok){ .kind = TokIdent, .Ident = { .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. */ 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; - t = t->prev; - toklist_del(toks, t->next, t->next); + Tok *v = &t->tok; /* what we want to negate */ + t = t->prev; /* go back to the '-' sign */ + 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) { /* immediately negate value */ t->tok.kind = TokVal; t->tok.Val = eval_unary(IRNeg, &v->Val); } else { - /* use the predefined storage address if it was requested and we're on the last operation */ - size_t res_addr; - if (mode.kind == ExprModeStorageAddr && is_last_operation) - res_addr = mode.StorageAddr; - else - res_addr = sc.mem_addr++; + size_t res_addr = is_last_operation ? 0 : sc.mem_addr++; - /* add IR instruction to negate the value */ + /* Instruction to negate. */ IRParam v_irparam; - TRY(v_irparam = tok_to_irparam(&sc, v)); - irtoks_app(out_ir, (IRTok){ + TRY_RET(v_irparam = tok_to_irparam(&sc, v), (ExprRet){0}); + IRTok ir = { .ln = t->tok.ln, .col = t->tok.col, .instr = IRNeg, @@ -280,13 +328,16 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To .addr = res_addr, .val = v_irparam, }, - }); + }; - if (mode.kind == ExprModeStorageAddr && is_last_operation) { + if (is_last_operation) { /* done */ toklist_del(toks, t, t); - return; + return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir }; } else { + /* write IR instruction */ + irtoks_app(out_ir, ir); + /* leave new memory address as result */ t->tok.kind = TokIdent; 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) { mark_err(l_op); set_err("Expected operator"); - return; + return (ExprRet){0}; } 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) { mark_err(r_op); set_err("Expected operator"); - return; + return (ExprRet){0}; } r_op_prec = op_prec[r_op->Op]; - /* If l_op and r_op are both delimiters, the expression is fully evaluated. - * NOTE: Sometimes, we don't reach this point because the function already - * exits directly after the last operation. */ + /* If l_op and r_op are both delimiters, we don't have to evaluate + * anything. */ 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); 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 actual operator precedence parser as described above. */ + /* This is the operator precedence parser described above. */ if (r_op_prec > l_op_prec) t = t->next->next; else { - /* some basic checks */ Tok *rhs = &t->tok; if (rhs->kind != TokVal && rhs->kind != TokIdent) { mark_err(rhs); set_err("Expected literal or identifier"); - return; + return (ExprRet){0}; } + t = t->prev->prev; + Tok *lhs = &t->tok; if (lhs->kind != TokVal && lhs->kind != TokIdent) { mark_err(lhs); set_err("Expected literal or identifier"); - return; + return (ExprRet){0}; } /* 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: mark_err(l_op); set_err("Unknown operation: '%s'", op_str[l_op->Op]); - return; + return (ExprRet){0}; } + if (lhs->kind == TokVal && rhs->kind == TokVal) { /* evaluate the constant expression immediately */ 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 { + bool is_last_operation = t == start && r_op_prec == PREC_DELIM; + IRParam lhs_irparam, rhs_irparam; - TRY(lhs_irparam = tok_to_irparam(&sc, lhs)); - TRY(rhs_irparam = tok_to_irparam(&sc, rhs)); + TRY_RET(lhs_irparam = tok_to_irparam(&sc, lhs), (ExprRet){0}); + 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; - if (mode.kind == ExprModeStorageAddr && is_last_operation) - res_addr = mode.StorageAddr; - else - res_addr = sc.mem_addr++; + size_t res_addr = is_last_operation ? 0 : sc.mem_addr++; - /* emit IR code to evaluate the non-constant expression */ - irtoks_app(out_ir, (IRTok){ + IRTok ir = { .ln = l_op->ln, .col = l_op->col, .instr = instr, @@ -410,13 +445,16 @@ static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, To .lhs = lhs_irparam, .rhs = rhs_irparam, }, - }); - - if (mode.kind == ExprModeStorageAddr && is_last_operation) { + }; + + if (is_last_operation) { /* done */ toklist_del(toks, t, t); - break; + return (ExprRet){ .kind = ExprRetLastInstr, .LastInstr = ir }; } else { + /* write IR instruction */ + irtoks_app(out_ir, ir); + /* leave new memory address as result */ lhs->kind = TokIdent; 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) { TokListItem *start = t; 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; t = t->next; if (t->tok.kind == TokDeclare) { - t = t->next; size_t addr = sc->mem_addr++; bool replaced = map_insert(&sc->ident_addrs, name, &addr); 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); 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) { - t = t->next; size_t addr; 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 ASSERT_UNREACHED(); } 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 */ IRToks 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; - TRY_ELSE(cond_irparam = tok_to_irparam(sc, &t->tok), irtoks_term(&cond_ir)); + IRParam cond; + TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); + /* add conditional jump */ irtoks_app(&cond_ir, (IRTok){ - .ln = t->tok.ln, - .col = t->tok.col, + .ln = t->next->tok.ln, + .col = t->next->tok.col, .instr = IRJnz, .CJmp = { .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 { /* 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); } diff --git a/vm.c b/vm.c index 446b3d4..0b8e384 100644 --- a/vm.c +++ b/vm.c @@ -46,21 +46,23 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { Stack s = stack_make(); for (size_t i = 0; i < ir->len;) { IRTok *instr = &ir->toks[i]; + err_ln = instr->ln; + err_col = instr->col; switch (instr->instr) { case IRSet: case IRNeg: 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; case IRAdd: case IRSub: case IRDiv: case IRMul: 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.rhs) - ); + )); break; case IRJmp: 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]); 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); break;