rewrite expression parser with parentheses

This commit is contained in:
r4 2021-12-23 15:56:12 +01:00
parent 052e78bf2e
commit 298883939b
5 changed files with 266 additions and 183 deletions

View File

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

View File

@ -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
View File

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

428
parse.c
View File

@ -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,46 +113,60 @@ 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
* Where t points to (between l_op and r_op in each step) * calculating intermediate values.
* | * In the case of ExprRetVal and ExprRetIdent, the value isn't 'returned' in
* v * the traditional sense, but rather the result is left in the token stream.
* 5 + 2 * 2 \n * 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'
* l_op r_op * - ExprRetIdent: The expression yields an identifier as a result.
* precedence of '+' is higher than that of the front delimiter => move forward * Examples: 'a' or '(((a)))'
* ________________________________ * - ExprRetLastInstr: The expression is a more complex sequence of
* 5 + 2 * 2 \n * 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'
* l_op r_op *
* precedence of '*' is higher than that of '+' => move forward * Here is also a simplified example of how the operator precedence parsing works:
* ________________________________ * ________________________________
* 5 + 2 * 2 \n * Where t points to (between l_op and r_op in each step)
* ^ ^ * |
* | | * v
* l_op r_op * 5 + 2 * 2 \n
* 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 '+' is higher than that of the front delimiter => move forward
* | | * ________________________________
* l_op r_op * 5 + 2 * 2 \n
* precedence of '\n' (a delimiter) is lower than that of '+' => evaluate and move l_op 2 back * ^ ^
* ________________________________ * | |
* 9 \n * l_op r_op
* ^ ^ * precedence of '*' is higher than that of '+' => move forward
* | | * ________________________________
* l_op r_op * 5 + 2 * 2 \n
* both l_op and r_op are delimiters (their precedence is PREC_DELIM) => done * ^ ^
*/ * | |
* 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; 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);
} else
ASSERT_UNREACHED();
toklist_del(toks, t->next, t->next); 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; /* we want to try to eliminate function calls at runtime if possible */
TokListItem *first_arg = t;
TokListItem *last_arg = NULL;
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
View File

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