Compare commits

..

No commits in common. "9f339ed44d792acf841db46e4892a308a4aca1ed" and "63af3e907b715329b968582cb3eb74e1a42e29d9" have entirely different histories.

6 changed files with 79 additions and 168 deletions

View File

@ -1,11 +1,22 @@
x := 1 a := 1
a = a + 1
b := a - 2 * 3
b = 2 + b * a
c := -b
//a := 1
//b := 1 - 2 * 2 + 5
//c := a + b * 2 * b
//d := a + 4 * b * a
/*x := 1
y := 1 y := 1
i := 60 i := 60
while i + 1 { while i {
z := x + y z := x + y
y = x y = x
x = z x = z
//print(z) print(z)
i = i - 1 i = i - 1
} }*/

7
ir.c
View File

@ -11,7 +11,6 @@ const char *irinstr_str[IRInstrEnumSize] = {
[IRMul] = "mul", [IRMul] = "mul",
[IRDiv] = "div", [IRDiv] = "div",
[IRPrint] = "print", [IRPrint] = "print",
[IRJmp] = "jmp",
[IRJnz] = "jnz", [IRJnz] = "jnz",
}; };
@ -69,7 +68,6 @@ static void print_irparam(const IRParam *p) {
void print_ir(IRToks *v) { void print_ir(IRToks *v) {
for (size_t i = 0; i < v->len; i++) { for (size_t i = 0; i < v->len; i++) {
printf("%04zx ", i);
printf("%s", irinstr_str[v->toks[i].instr]); printf("%s", irinstr_str[v->toks[i].instr]);
switch (v->toks[i].instr) { switch (v->toks[i].instr) {
case IRSet: case IRSet:
@ -92,13 +90,10 @@ void print_ir(IRToks *v) {
print_irparam(&a->param); print_irparam(&a->param);
} }
break; break;
case IRJmp:
printf(" %zx", v->toks[i].Jmp.iaddr);
break;
case IRJnz: case IRJnz:
printf(" "); printf(" ");
print_irparam(&v->toks[i].CJmp.condition); print_irparam(&v->toks[i].CJmp.condition);
printf(" %zx", v->toks[i].CJmp.iaddr); printf(" %zu", v->toks[i].CJmp.iaddr);
break; break;
default: default:
break; break;

5
ir.h
View File

@ -11,7 +11,6 @@ enum IRInstr {
IRMul, IRMul,
IRDiv, IRDiv,
IRPrint, IRPrint,
IRJmp,
IRJnz, IRJnz,
IRInstrEnumSize, IRInstrEnumSize,
}; };
@ -57,10 +56,6 @@ typedef struct IRTok {
IRArgs *args; IRArgs *args;
size_t args_size; size_t args_size;
} Print; } Print;
struct {
size_t iaddr;
} Jmp;
struct { struct {
size_t iaddr; size_t iaddr;

210
parse.c
View File

@ -17,25 +17,12 @@ typedef struct Scope {
Map ident_addrs; Map ident_addrs;
} Scope; } Scope;
typedef struct ExprMode {
bool ignore_newln;
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 */
} kind;
union {
size_t StorageAddr;
};
} ExprMode;
static void mark_err(const Tok *t); static void mark_err(const Tok *t);
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, size_t mem_addr, bool with_idents);
static void term_scope(Scope *sc); static void term_scope(Scope *sc);
static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode); static void expr(State *s, Scope *parent_sc, TokListItem *t, bool toplevel, bool use_storage_addr, size_t storage_addr);
static void stmt(State *s, Scope *sc, TokListItem *t); static void stmt(State *s, Scope *sc, TokListItem *t);
static void mark_err(const Tok *t) { static void mark_err(const Tok *t) {
@ -84,8 +71,8 @@ static IRParam tok_to_irparam(Scope *sc, Tok *t) {
} }
/* term_scope doesn't have to be called if with_idents is set to false. */ /* term_scope doesn't have to be called if with_idents is set to false. */
static Scope make_scope(Scope *parent, bool with_idents) { static Scope make_scope(Scope *parent, size_t mem_addr, bool with_idents) {
Scope s = { .parent = parent, .mem_addr = parent ? parent->mem_addr : 0, .has_idents = with_idents }; Scope s = { .parent = parent, .mem_addr = mem_addr, .has_idents = with_idents };
if (with_idents) if (with_idents)
map_init(&s.ident_addrs, sizeof(size_t)); map_init(&s.ident_addrs, sizeof(size_t));
return s; return s;
@ -96,7 +83,9 @@ static void term_scope(Scope *sc) {
map_term(&sc->ident_addrs); map_term(&sc->ident_addrs);
} }
static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) { /* If toplevel is set, newlines are seen as delimiters ending the expression.
* If use_storage_addr is set, the result is guaranteed to be put into storage_addr. */
static void expr(State *s, Scope *parent_sc, TokListItem *t, bool toplevel, bool use_storage_addr, size_t storage_addr) {
/* A simplified example of how the operator precedence parsing works: /* 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)
@ -134,9 +123,12 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
*/ */
TokListItem *start = t; TokListItem *start = t;
Scope *sc = parent_sc;
/* Each expression and subexpression has its own scope. */ Scope expr_scope_obj;
Scope sc = make_scope(parent_sc, false); if (toplevel) {
expr_scope_obj = make_scope(parent_sc, parent_sc->mem_addr, false);
sc = &expr_scope_obj;
}
for (;;) { for (;;) {
/* Prepare to collapse negative factor. */ /* Prepare to collapse negative factor. */
@ -146,8 +138,8 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
negate = true; negate = true;
} }
/* Ignore newlines if told to do so. */ /* Ignore newlines if the expression is not toplevel. */
if (mode.ignore_newln && t->next->tok.kind == TokOp && t->next->tok.Op == OpNewLn) if (!toplevel && t->next->tok.kind == TokOp && t->next->tok.Op == OpNewLn)
toklist_del(s->toks, t->next, t->next); toklist_del(s->toks, t->next, t->next);
/* Collapse negative factor. */ /* Collapse negative factor. */
@ -169,14 +161,14 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
} else { } else {
/* use the predefined storage address if it was requested and we're on the last operation */ /* use the predefined storage address if it was requested and we're on the last operation */
size_t res_addr; size_t res_addr;
if (mode.kind == ExprModeStorageAddr && is_last_operation) if (use_storage_addr && is_last_operation)
res_addr = mode.StorageAddr; res_addr = storage_addr;
else else
res_addr = sc.mem_addr++; res_addr = sc->mem_addr++;
/* add IR instruction to negate the value */ /* add IR instruction to negate the value */
IRParam v_irparam; IRParam v_irparam;
TRY(v_irparam = tok_to_irparam(&sc, v)); TRY(v_irparam = tok_to_irparam(sc, v));
irtoks_app(s->ir, (IRTok){ irtoks_app(s->ir, (IRTok){
.ln = t->tok.ln, .ln = t->tok.ln,
.col = t->tok.col, .col = t->tok.col,
@ -187,18 +179,17 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
}, },
}); });
if (mode.kind == ExprModeStorageAddr && is_last_operation) { /* leave new memory address as result */
/* done */ t->tok.kind = TokIdent;
toklist_del(s->toks, t, t); t->tok.Ident = (Identifier){
.kind = IdentAddr,
.Addr = res_addr,
};
if (use_storage_addr && is_last_operation)
/* Since the final result was written to the storage address,
* we're done. */
return; return;
} else {
/* leave new memory address as result */
t->tok.kind = TokIdent;
t->tok.Ident = (Identifier){
.kind = IdentAddr,
.Addr = res_addr,
};
}
} }
} }
@ -230,25 +221,18 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
* NOTE: Sometimes, we don't reach this point because the function already * NOTE: Sometimes, we don't reach this point because the function already
* exits directly after the last operation. */ * 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) { IRParam res;
mark_err(&t->tok); TRY(res = tok_to_irparam(sc, &t->tok));
set_err("Expected literal or identifier"); irtoks_app(s->ir, (IRTok){
return; .ln = t->tok.ln,
} .col = t->tok.col,
if (mode.kind == ExprModeStorageAddr) { .instr = IRSet,
IRParam res; .Unary = {
TRY(res = tok_to_irparam(&sc, &t->tok)); .addr = use_storage_addr ? storage_addr : sc->mem_addr++,
irtoks_app(s->ir, (IRTok){ .val = res,
.ln = t->tok.ln, },
.col = t->tok.col, });
.instr = IRSet, toklist_del(s->toks, t, t);
.Unary = {
.addr = mode.StorageAddr,
.val = res,
},
});
toklist_del(s->toks, t, t);
}
return; return;
} }
@ -295,15 +279,15 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
TRY(lhs->Val = eval_arith(instr, &lhs->Val, &rhs->Val)); TRY(lhs->Val = eval_arith(instr, &lhs->Val, &rhs->Val));
} else { } else {
IRParam lhs_irparam, rhs_irparam; IRParam lhs_irparam, rhs_irparam;
TRY(lhs_irparam = tok_to_irparam(&sc, lhs)); TRY(lhs_irparam = tok_to_irparam(sc, lhs));
TRY(rhs_irparam = tok_to_irparam(&sc, rhs)); TRY(rhs_irparam = tok_to_irparam(sc, rhs));
/* use the predefined storage address if it was requested and we're on the last operation */ /* use the predefined storage address if it was requested and we're on the last operation */
size_t res_addr; size_t res_addr;
if (mode.kind == ExprModeStorageAddr && is_last_operation) if (use_storage_addr && is_last_operation)
res_addr = mode.StorageAddr; res_addr = storage_addr;
else else
res_addr = sc.mem_addr++; res_addr = sc->mem_addr++;
/* emit IR code to evaluate the non-constant expression */ /* emit IR code to evaluate the non-constant expression */
irtoks_app(s->ir, (IRTok){ irtoks_app(s->ir, (IRTok){
@ -317,18 +301,17 @@ static void expr(State *s, Scope *parent_sc, TokListItem *t, ExprMode mode) {
}, },
}); });
if (mode.kind == ExprModeStorageAddr && is_last_operation) { /* leave new memory address as result */
/* done */ lhs->kind = TokIdent;
toklist_del(s->toks, t, t); lhs->Ident = (Identifier){
break; .kind = IdentAddr,
} else { .Addr = res_addr,
/* leave new memory address as result */ };
lhs->kind = TokIdent;
lhs->Ident = (Identifier){ if (use_storage_addr && is_last_operation)
.kind = IdentAddr, /* Since the final result was written to the storage address,
.Addr = res_addr, * we're done. */
}; return;
}
} }
} }
} }
@ -348,84 +331,13 @@ static void stmt(State *s, Scope *sc, TokListItem *t) {
set_err("'%s' already declared in this scope", name); set_err("'%s' already declared in this scope", name);
return; return;
} }
TRY(expr(s, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr })); TRY(expr(s, sc, t, true, true, addr));
} else if (t->tok.kind == TokAssign) { } else if (t->tok.kind == TokAssign) {
t = t->next; 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(s, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr })); TRY(expr(s, sc, t, true, true, addr));
} }
} else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) {
Scope inner_sc = make_scope(sc, true);
for (;;) {
if (t->next->tok.kind == TokOp) {
if (t->next->tok.Op == OpEOF) {
term_scope(&inner_sc);
mark_err(&start->tok);
set_err("Unclosed '{'");
return;
}
if (t->next->tok.Op == OpRCurl)
break;
}
TRY_ELSE(stmt(s, &inner_sc, t->next), term_scope(&inner_sc));
}
term_scope(&inner_sc);
t = t->next;
} else if (t->tok.kind == TokWhile) {
/* How while is generally implemented in IR:
* 0: jmp to 3
* 1: some_code
* 2: some_code
* 3: some stuff evaluating condition xyz
* 4: jmp to 1 if condition xyz is met
* */
size_t jmp_instr_iaddr = s->ir->len;
irtoks_app(s->ir, (IRTok){
.ln = t->tok.ln,
.col = t->tok.col,
.instr = IRJmp,
.Jmp = {
.iaddr = 0, /* unknown for now */
},
});
t = t->next;
/* find beginning of while loop body */
TokListItem *lcurl;
for (TokListItem *i = t;; i++) {
if (i == NULL) {
mark_err(&start->tok);
set_err("Expected '{' after 'while' loop condition");
return;
}
if (i->tok.kind == TokOp && i->tok.Op == OpLCurl) {
lcurl = i;
break;
}
}
/* write loop body to IR stream */
TRY(stmt(s, sc, lcurl));
/* finally we know where the jmp from the beginning has to jump to */
s->ir->toks[jmp_instr_iaddr].Jmp.iaddr = s->ir->len;
TRY(expr(s, sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = false }));
IRParam condition;
TRY(condition = tok_to_irparam(sc, &t->tok));
irtoks_app(s->ir, (IRTok){
.ln = t->tok.ln,
.col = t->tok.col,
.instr = IRJnz,
.CJmp = {
.iaddr = jmp_instr_iaddr + 1,
.condition = condition,
},
});
} }
toklist_del(s->toks, start, t); toklist_del(s->toks, start, t);
} }
@ -434,11 +346,11 @@ IRToks parse(TokList *toks) {
IRToks ir; IRToks ir;
irtoks_init(&ir); irtoks_init(&ir);
State s = { .toks = toks, .ir = &ir }; State s = { .toks = toks, .ir = &ir };
Scope global_scope = make_scope(NULL, true); Scope global_scope = make_scope(NULL, 0, true);
for (;;) { for (;;) {
if (toks->begin->tok.kind == TokOp && toks->begin->tok.Op == OpEOF) if (toks->begin->tok.kind == TokOp && toks->begin->tok.Op == OpEOF)
break; break;
TRY_RET_ELSE(stmt(&s, &global_scope, toks->begin), ir, term_scope(&global_scope)); TRY_RET(stmt(&s, &global_scope, toks->begin), ir);
} }
term_scope(&global_scope); term_scope(&global_scope);
return ir; return ir;

View File

@ -15,7 +15,7 @@ Value eval_arith(IRInstr instr, const Value *lhs, const Value *rhs) {
case IRSub: res = lhs->Int - rhs->Int; break; case IRSub: res = lhs->Int - rhs->Int; break;
case IRMul: res = lhs->Int * rhs->Int; break; case IRMul: res = lhs->Int * rhs->Int; break;
case IRDiv: res = lhs->Int / rhs->Int; break; case IRDiv: res = lhs->Int / rhs->Int; break;
default: ASSERT_UNREACHED(); default: break;
} }
return (Value){ return (Value){
.type.kind = TypeInt, .type.kind = TypeInt,
@ -28,7 +28,7 @@ Value eval_arith(IRInstr instr, const Value *lhs, const Value *rhs) {
case IRSub: res = lhs->Float - rhs->Float; break; case IRSub: res = lhs->Float - rhs->Float; break;
case IRMul: res = lhs->Float * rhs->Float; break; case IRMul: res = lhs->Float * rhs->Float; break;
case IRDiv: res = lhs->Float / rhs->Float; break; case IRDiv: res = lhs->Float / rhs->Float; break;
default: ASSERT_UNREACHED(); default: break;
} }
return (Value){ return (Value){
.type.kind = TypeFloat, .type.kind = TypeFloat,

2
util.h
View File

@ -32,9 +32,7 @@ extern char errbuf[ERRSZ];
extern bool err; extern bool err;
extern size_t err_ln, err_col; extern size_t err_ln, err_col;
#define TRY(expr) {expr; if (err) return;} #define TRY(expr) {expr; if (err) return;}
#define TRY_ELSE(expr, onerr) {expr; if (err) {onerr; return;}}
#define TRY_RET(expr, ret) {expr; if (err) return (ret);} #define TRY_RET(expr, ret) {expr; if (err) return (ret);}
#define TRY_RET_ELSE(expr, ret, onerr) {expr; if (err) {onerr; return (ret);}}
void set_err(const char *fmt, ...); void set_err(const char *fmt, ...);
#define ASSERT_UNREACHED() { fprintf(stderr, "Illegal code position reached in %s:%d\n", __FILE__, __LINE__); exit(1); } #define ASSERT_UNREACHED() { fprintf(stderr, "Illegal code position reached in %s:%d\n", __FILE__, __LINE__); exit(1); }