From 97e8e32ebcaf0fbc6ab386602488e009773b491d Mon Sep 17 00:00:00 2001 From: r4 Date: Thu, 23 Dec 2021 19:58:00 +0100 Subject: [PATCH] add if and else, and fix nested loops (hopefully) --- ir.c | 14 ++++++++-- ir.h | 2 +- lex.c | 2 ++ parse.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++-------- tok.c | 1 + tok.h | 1 + 6 files changed, 87 insertions(+), 14 deletions(-) diff --git a/ir.c b/ir.c index 0758307..c660c1a 100644 --- a/ir.c +++ b/ir.c @@ -49,11 +49,21 @@ void irtoks_app(IRToks *v, IRTok t) { v->toks[v->len++] = t; } -void irtoks_app_irtoks(IRToks *v, IRToks *other) { +void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset) { if (v->len+other->len > v->cap) v->toks = xrealloc(v->toks, sizeof(IRTok) * (other->len + (v->cap *= 2))); - for (size_t i = 0; i < other->len; i++) + for (size_t i = 0; i < other->len; i++) { + /* correct for changed jump addresses */ + if (other->toks[i].instr == IRJmp) + other->toks[i].Jmp.iaddr += jmp_offset; + else if (other->toks[i].instr == IRJnz) + other->toks[i].CJmp.iaddr += jmp_offset; + v->toks[v->len++] = other->toks[i]; + } + /* We're not calling irtoks_term() because we don't want associated items + * (for example function arguments) to get deallocated as well. */ + free(other->toks); } static void print_val(const Value *v); diff --git a/ir.h b/ir.h index 757ecc1..58e9e67 100644 --- a/ir.h +++ b/ir.h @@ -87,7 +87,7 @@ void irtoks_init_long(IRToks *v); void irtoks_init_short(IRToks *v); void irtoks_term(IRToks *v); void irtoks_app(IRToks *v, IRTok t); -void irtoks_app_irtoks(IRToks *v, IRToks *other); +void irtoks_eat_irtoks(IRToks *v, IRToks *other, size_t jmp_offset); void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs); diff --git a/lex.c b/lex.c index e233396..deee6c0 100644 --- a/lex.c +++ b/lex.c @@ -54,6 +54,8 @@ TokList lex(const char *s) { } if (streq_0_n("if", start, i)) emit(&toks, &pos, (Tok){ .kind = TokIf }); + else if (streq_0_n("else", start, i)) + emit(&toks, &pos, (Tok){ .kind = TokElse }); else if (streq_0_n("while", start, i)) emit(&toks, &pos, (Tok){ .kind = TokWhile }); else { diff --git a/parse.c b/parse.c index e7ac89f..ffb878a 100644 --- a/parse.c +++ b/parse.c @@ -5,6 +5,8 @@ #include "map.h" #include "runtime.h" +static BuiltinFunc *bf; + typedef struct Scope { struct Scope *parent; size_t mem_addr; @@ -589,8 +591,17 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt IRParam cond; TRY_ELSE(cond = expr_into_irparam(&cond_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); + /* parse loop body */ + TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); + + /* finally we know where the jmp from the beginning has to jump to */ + out_ir->toks[jmp_instr_iaddr].Jmp.iaddr = out_ir->len; + + /* append condition IR to program IR, then terminate condition IR stream */ + irtoks_eat_irtoks(out_ir, &cond_ir, out_ir->len-1); + /* add conditional jump */ - irtoks_app(&cond_ir, (IRTok){ + irtoks_app(out_ir, (IRTok){ .ln = t->next->tok.ln, .col = t->next->tok.col, .instr = IRJnz, @@ -600,17 +611,63 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt }, }); - /* parse loop body */ - TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&cond_ir)); - - /* finally we know where the jmp from the beginning has to jump to */ - out_ir->toks[jmp_instr_iaddr].Jmp.iaddr = out_ir->len; - - /* append condition IR to program IR, then terminate condition IR stream */ - irtoks_app_irtoks(out_ir, &cond_ir); - irtoks_term(&cond_ir); - t = t->next; + } else if (t->tok.kind == TokIf) { + /* How if is generally implemented in IR: + * 0: some stuff evaluating condition xyz + * 1: jmp to 5 if condition xyz is met + * 2: some_code in else + * 4: jmp to 6 + * 5: some_code in if + * */ + + /* parse condition */ + IRParam cond; + TRY(cond = expr_into_irparam(out_ir, toks, funcs, sc, t->next)); + + /* add conditional jmp instruction */ + size_t if_cjmp_instr_iaddr = out_ir->len; + irtoks_app(out_ir, (IRTok){ + .ln = t->tok.ln, + .col = t->tok.col, + .instr = IRJnz, + .CJmp = { + .iaddr = 0, /* unknown for now */ + .condition = cond, + }, + }); + + /* parse if body */ + IRToks if_body; + irtoks_init_short(&if_body); + TRY_ELSE(stmt(&if_body, toks, funcs, sc, t->next), irtoks_term(&if_body)); + + if (t->next->tok.kind == TokElse) { + toklist_del(toks, t->next, t->next); + + /* parse and add else body */ + TRY_ELSE(stmt(out_ir, toks, funcs, sc, t->next), irtoks_term(&if_body)); + } + + /* add jmp instruction to jump back to common code */ + size_t else_jmp_instr_iaddr = out_ir->len; + irtoks_app(out_ir, (IRTok){ + .ln = t->tok.ln, + .col = t->tok.col, + .instr = IRJmp, + .Jmp = { + .iaddr = 0, /* unknown for now */ + }, + }); + + /* set if condition jmp target */ + out_ir->toks[if_cjmp_instr_iaddr].CJmp.iaddr = out_ir->len; + + /* add if body */ + irtoks_eat_irtoks(out_ir, &if_body, out_ir->len-1); + + /* set else jmp target */ + out_ir->toks[else_jmp_instr_iaddr].CJmp.iaddr = out_ir->len; } else if (t->tok.kind == TokOp && t->tok.Op == OpNewLn) { } else { /* assume expression */ @@ -621,6 +678,8 @@ static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListIt } IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) { + bf = builtin_funcs; + Map funcs; map_init(&funcs, sizeof(BuiltinFunc)); for (size_t i = 0; i < n_builtin_funcs; i++) { diff --git a/tok.c b/tok.c index 91249d2..6756fe4 100644 --- a/tok.c +++ b/tok.c @@ -35,6 +35,7 @@ const char *tok_str[TokKindEnumSize] = { [TokAssign] = "=", [TokDeclare] = ":=", [TokIf] = "if", + [TokElse] = "else", [TokWhile] = "while", }; diff --git a/tok.h b/tok.h index 03d5bf2..54ec65d 100644 --- a/tok.h +++ b/tok.h @@ -69,6 +69,7 @@ typedef struct Tok { TokAssign, TokDeclare, TokIf, + TokElse, TokWhile, TokKindEnumSize, } kind;