diff --git a/example.script b/example.script index af78fb1..ce2dd49 100644 --- a/example.script +++ b/example.script @@ -1,4 +1,4 @@ -x := 1 +/*x := 1 y := 1 i := 60 @@ -6,6 +6,12 @@ while i { z := x + y y = x x = z - //print(z) + print(z) i = i - 1 -} +}*/ + +f := 2.0 + +a := int(pow(f, 2.0)) + 1 + +print(a) diff --git a/ir.c b/ir.c index 735569c..0758307 100644 --- a/ir.c +++ b/ir.c @@ -10,9 +10,9 @@ const char *irinstr_str[IRInstrEnumSize] = { [IRSub] = "sub", [IRMul] = "mul", [IRDiv] = "div", - [IRPrint] = "print", [IRJmp] = "jmp", [IRJnz] = "jnz", + [IRCallInternal] = "calli", }; #define IRTOKS_INIT_CAP_LONG 4096 @@ -37,13 +37,8 @@ void irtoks_init_short(IRToks *v) { void irtoks_term(IRToks *v) { for (size_t i = 0; i < v->len; i++) { - if (v->toks[i].instr == IRPrint) { - for (IRArgs *a = v->toks[i].Print.args; a != NULL;) { - IRArgs *next = a->next; - free(a); - a = next; - } - } + if (v->toks[i].instr == IRCallInternal) + free(v->toks[i].CallI.args); } free(v->toks); } @@ -86,31 +81,25 @@ static void print_irparam(const IRParam *p) { } } -void print_ir(IRToks *v) { +void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) { for (size_t i = 0; i < v->len; i++) { printf("%04zx ", i); printf("%s", irinstr_str[v->toks[i].instr]); switch (v->toks[i].instr) { case IRSet: case IRNeg: - printf(" %%%zu ", v->toks[i].Unary.addr); + printf(" %%%zx ", v->toks[i].Unary.addr); print_irparam(&v->toks[i].Unary.val); break; case IRAdd: case IRSub: case IRDiv: case IRMul: - printf(" %%%zu ", v->toks[i].Arith.addr); + printf(" %%%zx ", v->toks[i].Arith.addr); print_irparam(&v->toks[i].Arith.lhs); printf(" "); print_irparam(&v->toks[i].Arith.rhs); break; - case IRPrint: - for (IRArgs *a = v->toks[i].Print.args; a != NULL; a = a->next) { - printf(" "); - print_irparam(&a->param); - } - break; case IRJmp: printf(" %zx", v->toks[i].Jmp.iaddr); break; @@ -119,8 +108,16 @@ void print_ir(IRToks *v) { print_irparam(&v->toks[i].CJmp.condition); printf(" %zx", v->toks[i].CJmp.iaddr); break; - default: + case IRCallInternal: { + const BuiltinFunc *f = &builtin_funcs[v->toks[i].CallI.fid]; + printf(" %s %%%zx", f->name, v->toks[i].CallI.ret_addr); + for (size_t j = 0; j < f->n_args; j++) { + printf(" "); + print_irparam(&v->toks[i].CallI.args[j]); + } break; + } + default: ASSERT_UNREACHED(); break; } printf(" ; %zu:%zu", v->toks[i].ln, v->toks[i].col); printf("\n"); diff --git a/ir.h b/ir.h index f74447c..1be102f 100644 --- a/ir.h +++ b/ir.h @@ -3,6 +3,14 @@ #include "tok.h" +typedef struct BuiltinFunc { + char *name; + bool side_effects : 1; + size_t n_args; + Value (*func)(Value *args); + size_t fid; /* function ID, assigned automatically */ +} BuiltinFunc; + enum IRInstr { IRSet, IRNeg, @@ -10,9 +18,9 @@ enum IRInstr { IRSub, IRMul, IRDiv, - IRPrint, IRJmp, IRJnz, + IRCallInternal, IRInstrEnumSize, }; typedef enum IRInstr IRInstr; @@ -66,6 +74,12 @@ typedef struct IRTok { size_t iaddr; IRParam condition; } CJmp; + + struct { + size_t ret_addr; + size_t fid; + IRParam *args; + } CallI; }; } IRTok; @@ -80,6 +94,6 @@ void irtoks_term(IRToks *v); void irtoks_app(IRToks *v, IRTok t); void irtoks_app_irtoks(IRToks *v, IRToks *other); -void print_ir(IRToks *v); +void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs); #endif /* IR_H */ diff --git a/main.c b/main.c index 090ed17..e5a8669 100644 --- a/main.c +++ b/main.c @@ -32,6 +32,56 @@ static void die(const char *fmt, ...) { exit(1); } +static Value fn_print(Value *args) { + switch (args[0].type.kind) { + case TypeVoid: printf("(void)\n"); break; + case TypeFloat: printf("%f\n", args[0].Float); break; + case TypeInt: printf("%zd\n", args[0].Int); break; + default: + ASSERT_UNREACHED(); + } + return (Value){0}; +} + +static Value fn_int(Value *args) { + Value ret = { + .type.kind = TypeInt, + .Int = 0, + }; + switch (args[0].type.kind) { + case TypeVoid: break; + case TypeFloat: ret.Int = (ssize_t)args[0].Float; break; + case TypeInt: ret.Int = args[0].Int; break; + default: ASSERT_UNREACHED(); + } + return ret; +} + +static Value fn_float(Value *args) { + Value ret = { + .type.kind = TypeFloat, + .Float = 0.0, + }; + switch (args[0].type.kind) { + case TypeVoid: break; + case TypeFloat: ret.Float = args[0].Float; break; + case TypeInt: ret.Float = (double)args[0].Int; break; + default: ASSERT_UNREACHED(); + } + return ret; +} + +static Value fn_pow(Value *args) { + if (!(args[0].type.kind == TypeFloat && args[1].type.kind == TypeFloat)) { + set_err("pow() requires arguments of type float"); + return (Value){0}; + } + return (Value) { + .type.kind = TypeFloat, + .Float = pow(args[0].Float, args[1].Float), + }; +} + int main(int argc, const char **argv) { /* parse arguments */ size_t nargs = argc - 1; @@ -88,7 +138,13 @@ int main(int argc, const char **argv) { if (opt_emit_tokens) print_toks(&tokens); /* parse tokens into IR code */ - IRToks ir = parse(&tokens); + BuiltinFunc funcs[] = { + { .name = "print", .side_effects = true, .n_args = 1, .func = fn_print, }, + { .name = "int", .side_effects = false, .n_args = 1, .func = fn_int, }, + { .name = "float", .side_effects = false, .n_args = 1, .func = fn_float, }, + { .name = "pow", .side_effects = false, .n_args = 2, .func = fn_pow, }, + }; + IRToks ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0])); if (err) { irtoks_term(&ir); toklist_term(&tokens); @@ -97,7 +153,7 @@ int main(int argc, const char **argv) { } toklist_term(&tokens); if (opt_emit_ir) - print_ir(&ir); + print_ir(&ir, funcs); /* run the IR */ /* TODO... */ irtoks_term(&ir); diff --git a/parse.c b/parse.c index 92d3f9c..3efd4f1 100644 --- a/parse.c +++ b/parse.c @@ -30,8 +30,8 @@ static size_t get_ident_addr(const Scope *sc, const char *name, const Tok *errpo 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, Scope *parent_sc, TokListItem *t, ExprMode mode); -static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t); +static void expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, ExprMode mode); +static void stmt(IRToks *out_ir, TokList *toks, Map *funcs, Scope *sc, TokListItem *t); static void mark_err(const Tok *t) { err_ln = t->ln; @@ -91,7 +91,7 @@ static void term_scope(Scope *sc) { map_term(&sc->ident_addrs); } -static void expr(IRToks *out_ir, TokList *toks, Scope *parent_sc, TokListItem *t, ExprMode mode) { +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) @@ -145,6 +145,111 @@ static void expr(IRToks *out_ir, TokList *toks, Scope *parent_sc, TokListItem *t if (mode.ignore_newln && t->next->tok.kind == TokOp && t->next->tok.Op == OpNewLn) 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) { + 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; + } + TokListItem *func_ident = t; + t = t->next->next; + toklist_del(toks, t->prev, t->prev); + + size_t n_args = 0; + TokListItem *first_arg = t; + TokListItem *last_arg = NULL; + + bool eval_func_in_place = !func.side_effects; + for (;;) { + n_args++; + TRY(expr(out_ir, toks, funcs, &sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = true })); + if (t->tok.kind == TokIdent) + 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; + } + + if (func.n_args != n_args) { + 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; + } + + 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; + } + func_ident->tok = (Tok) { + .kind = TokVal, + .Val = func.func(args), + }; + 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; + + 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; + if (mode.kind == ExprModeStorageAddr && is_last_operation) + res_addr = mode.StorageAddr; + else + res_addr = sc.mem_addr++; + irtoks_app(out_ir, (IRTok){ + .ln = func_ident->tok.ln, + .col = func_ident->tok.col, + .instr = IRCallInternal, + .CallI = { + .ret_addr = res_addr, + .fid = func.fid, + .args = args, + }, + }); + if (mode.kind == ExprModeStorageAddr && is_last_operation) { + toklist_del(toks, func_ident, func_ident); + break; + } else { + func_ident->tok = (Tok) { + .kind = TokIdent, + .Ident = { + .kind = IdentAddr, + .Addr = res_addr, + }, + }; + } + } + + 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; @@ -329,9 +434,9 @@ static void expr(IRToks *out_ir, TokList *toks, Scope *parent_sc, TokListItem *t } } -static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t) { +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) { + 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) { @@ -343,13 +448,14 @@ static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t) { set_err("'%s' already declared in this scope", name); return; } - TRY(expr(out_ir, toks, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr })); + TRY(expr(out_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = 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, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr })); - } + TRY(expr(out_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeStorageAddr, .ignore_newln = false, .StorageAddr = addr })); + } else + ASSERT_UNREACHED(); } else if (t->tok.kind == TokOp && t->tok.Op == OpLCurl) { Scope inner_sc = make_scope(sc, true); for (;;) { @@ -363,7 +469,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t) { if (t->next->tok.Op == OpRCurl) break; } - TRY_ELSE(stmt(out_ir, toks, &inner_sc, t->next), term_scope(&inner_sc)); + TRY_ELSE(stmt(out_ir, toks, funcs, &inner_sc, t->next), term_scope(&inner_sc)); } term_scope(&inner_sc); t = t->next; @@ -392,7 +498,7 @@ static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t) { /* parse condition */ IRToks cond_ir; irtoks_init_short(&cond_ir); - TRY_ELSE(expr(&cond_ir, toks, sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = false }), irtoks_term(&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)); /* add conditional jump */ @@ -406,10 +512,8 @@ static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t) { }, }); - t = t->next; - /* parse loop body */ - TRY_ELSE(stmt(out_ir, toks, sc, t), irtoks_term(&cond_ir)); + 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; @@ -417,19 +521,40 @@ static void stmt(IRToks *out_ir, TokList *toks, Scope *sc, TokListItem *t) { /* 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 == TokOp && t->tok.Op == OpNewLn) { + } else { + /* assume expression */ + TRY(expr(out_ir, toks, funcs, sc, t, (ExprMode){ .kind = ExprModeJustCollapse, .ignore_newln = false })); } toklist_del(toks, start, t); } -IRToks parse(TokList *toks) { +IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs) { + Map funcs; + map_init(&funcs, sizeof(BuiltinFunc)); + for (size_t i = 0; i < n_builtin_funcs; i++) { + builtin_funcs[i].fid = i; + bool replaced = map_insert(&funcs, builtin_funcs[i].name, &builtin_funcs[i]); + if (replaced) { + err_ln = 0; err_col = 0; + set_err("Builtin function %s() declared more than once", builtin_funcs[i].name); + map_term(&funcs); + return (IRToks){0}; + } + } + IRToks ir; irtoks_init_long(&ir); Scope global_scope = make_scope(NULL, true); for (;;) { if (toks->begin->tok.kind == TokOp && toks->begin->tok.Op == OpEOF) break; - TRY_RET_ELSE(stmt(&ir, toks, &global_scope, toks->begin), ir, term_scope(&global_scope)); + TRY_RET_ELSE(stmt(&ir, toks, &funcs, &global_scope, toks->begin), ir, + { term_scope(&global_scope); map_term(&funcs); }); } term_scope(&global_scope); + map_term(&funcs); return ir; } diff --git a/parse.h b/parse.h index 9707fb7..89d6704 100644 --- a/parse.h +++ b/parse.h @@ -5,6 +5,6 @@ #include "tok.h" #include "util.h" -IRToks parse(TokList *toks); +IRToks parse(TokList *toks, BuiltinFunc *builtin_funcs, size_t n_builtin_funcs); #endif /* PARSE_H */