rewrite expression parser with parentheses
This commit is contained in:
parent
052e78bf2e
commit
298883939b
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user