diff --git a/ir.c b/ir.c index 290a27d..852e310 100644 --- a/ir.c +++ b/ir.c @@ -20,6 +20,7 @@ const char *irinstr_str[IRInstrEnumSize] = { [IRJmp] = "jmp", [IRJnz] = "jnz", [IRCallInternal] = "calli", + [IRAddrOf] = "addrof", }; #define IRTOKS_INIT_CAP_LONG 4096 @@ -91,6 +92,7 @@ void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) { case IRSet: case IRNeg: case IRNot: + case IRAddrOf: printf(" %%%zx ", v->toks[i].Unary.addr); print_irparam(&v->toks[i].Unary.val); break; @@ -119,8 +121,10 @@ void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) { break; 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++) { + if (f->returns) + printf(" %%%zx", v->toks[i].CallI.ret_addr); + printf(" %s", f->name); + for (size_t j = 0; j < v->toks[i].CallI.n_args; j++) { printf(" "); print_irparam(&v->toks[i].CallI.args[j]); } diff --git a/ir.h b/ir.h index adb2ab9..e1fe947 100644 --- a/ir.h +++ b/ir.h @@ -4,11 +4,33 @@ #include "tok.h" typedef struct BuiltinFunc { - char *name; + enum { + FuncFixedArgs, + FuncVarArgs, + } kind; + + bool returns : 1; bool side_effects : 1; - size_t n_args; - Value (*func)(Value *args); + char *name; size_t fid; /* function ID, assigned automatically */ + + union { + struct { + size_t n_args; + union { + struct { Value (*func)(Value *args); } WithRet; + struct { void (*func)(Value *args); } NoRet; + }; + } FixedArgs; + + struct { + size_t min_args; + union { + struct { Value (*func)(size_t extra_args, Value *args); } WithRet; + struct { void (*func)(size_t extra_args, Value *args); } NoRet; + }; + } VarArgs; + }; } BuiltinFunc; enum IRInstr { @@ -28,6 +50,7 @@ enum IRInstr { IRJmp, IRJnz, IRCallInternal, + IRAddrOf, IRInstrEnumSize, }; typedef enum IRInstr IRInstr; @@ -80,6 +103,7 @@ typedef struct IRTok { struct { size_t ret_addr; size_t fid; + size_t n_args; IRParam *args; } CallI; }; diff --git a/lex.c b/lex.c index 5234ea5..ef480f6 100644 --- a/lex.c +++ b/lex.c @@ -222,6 +222,15 @@ TokList lex(const char *s, Pool *static_vars) { continue; } break; + case '&': + consume(&pos, *(s++)); + if (s[0] == '&') + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAnd }); + else { + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAddrOf }); + continue; + } + break; case '!': consume(&pos, *(s++)); if (s[0] == '=') @@ -231,13 +240,6 @@ TokList lex(const char *s, Pool *static_vars) { continue; } break; - case '&': - consume(&pos, *(s++)); - if (s[0] == '&') - emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAnd }); - else - continue; - break; case '|': consume(&pos, *(s++)); if (s[0] == '|') diff --git a/main.c b/main.c index 6c94973..9fc0dc0 100644 --- a/main.c +++ b/main.c @@ -33,15 +33,18 @@ static void die(const char *fmt, ...) { exit(1); } -static Value fn_put(Value *args) { - print_value(&args[0], true); - return (Value){0}; +static void fn_put(size_t extra_args, Value *args) { + for (size_t i = 0;; i++) { + print_value(&args[i], true); + if (i+1 >= extra_args) + break; + printf(" "); + } } -static Value fn_putln(Value *args) { - fn_put(args); +static void fn_putln(size_t extra_args, Value *args) { + fn_put(extra_args, args); printf("\n"); - return (Value){0}; } static Value fn_int(Value *args) { @@ -107,13 +110,12 @@ static Value fn_pow(Value *args) { }; } -static Value fn_sleep(Value *args) { +static void fn_sleep(Value *args) { if (!(args[0].type.kind == TypeFloat && args[0].Float >= 0.0)) { set_err("sleep() requires a positive float"); - return (Value){0}; + return; } sleep_secs(args[0].Float); - return (Value){0}; } static Value fn_getln(Value *args) { @@ -203,13 +205,13 @@ int main(int argc, const char **argv) { print_toks(&tokens); /* parse tokens into IR code */ BuiltinFunc funcs[] = { - { .name = "put", .side_effects = true, .n_args = 1, .func = fn_put, }, - { .name = "putln", .side_effects = true, .n_args = 1, .func = fn_putln, }, - { .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, }, - { .name = "sleep", .side_effects = true, .n_args = 1, .func = fn_sleep, }, - { .name = "getln", .side_effects = true, .n_args = 0, .func = fn_getln, }, + { .name = "put", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_put, }}, + { .name = "putln", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_putln, }}, + { .name = "int", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_int, }}, + { .name = "float", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_float, }}, + { .name = "pow", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 2, .WithRet.func = fn_pow, }}, + { .name = "sleep", .kind = FuncFixedArgs, .returns = false, .side_effects = true, .FixedArgs = { .n_args = 1, .NoRet.func = fn_sleep, }}, + { .name = "getln", .kind = FuncFixedArgs, .returns = true, .side_effects = true, .FixedArgs = { .n_args = 0, .WithRet.func = fn_getln, }}, }; IRToks ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0])); if (err) { diff --git a/parse.c b/parse.c index 55be65a..3dcf387 100644 --- a/parse.c +++ b/parse.c @@ -49,6 +49,7 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) { case IRSet: case IRNeg: case IRNot: + case IRAddrOf: t->Unary.addr = addr; break; case IRAdd: @@ -230,6 +231,10 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, t = t->next; perform_unary = true; unary_op = IRNot; + } else if (t->tok.Op == OpAddrOf) { + t = t->next; + perform_unary = true; + unary_op = IRAddrOf; } } @@ -319,10 +324,17 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, t = func_ident; toklist_del(toks, t->next, t->next); /* delete left parenthesis */ - if (func.n_args != args_len) { + if (func.kind == FuncFixedArgs && args_len != func.FixedArgs.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, args_len); + const char *plural = func.FixedArgs.n_args == 1 ? "" : "s"; + set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.FixedArgs.n_args, plural, args_len); + if (args) + free(args); + return (ExprRet){0}; + } else if (func.kind == FuncVarArgs && args_len < func.VarArgs.min_args) { + mark_err(&func_ident->tok); + const char *plural = func.VarArgs.min_args == 1 ? "" : "s"; + set_err("Function %s() requires at least %zu argument%s but only got %zu", func.name, func.VarArgs.min_args, plural, args_len); if (args) free(args); return (ExprRet){0}; @@ -330,13 +342,20 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, if (eval_func_in_place) { /* evaluate the function in place */ + if (!func.returns) + /* If the function had no side effects and returned nothing, + * that function would do absolutely nothing, which would + * make no sense. */ + ASSERT_UNREACHED(); Value *arg_vals = args_len ? xmalloc(sizeof(Value) * args_len) : NULL; for (size_t i = 0; i < args_len; i++) arg_vals[i] = args[i].Literal; mark_err(&func_ident->tok); func_ident->tok = (Tok) { .kind = TokVal, - .Val = func.func(arg_vals), + .Val = func.kind == FuncVarArgs ? + func.VarArgs.WithRet.func(args_len - func.VarArgs.min_args, arg_vals) + : func.FixedArgs.WithRet.func(arg_vals), }; if (arg_vals) free(arg_vals); @@ -351,6 +370,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, .CallI = { .ret_addr = 0, .fid = func.fid, + .n_args = args_len, .args = args, }, }; diff --git a/runtime.c b/runtime.c index ed81af2..4eeef68 100644 --- a/runtime.c +++ b/runtime.c @@ -120,6 +120,9 @@ Value eval_unary(IRInstr instr, const Value *v) { set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type.kind]); return (Value){0}; } + case IRAddrOf: + set_err("Unable to take the address of a literal"); + return (Value){0}; default: ASSERT_UNREACHED(); } diff --git a/tok.c b/tok.c index 5da8184..1f948a2 100644 --- a/tok.c +++ b/tok.c @@ -20,6 +20,7 @@ const char *type_str[TypeEnumSize] = { [TypeInt] = "int", [TypeBool] = "bool", [TypeChar] = "char", + [TypePtr] = "ptr", [TypeArr] = "arr", }; @@ -46,6 +47,11 @@ void print_value(const Value *v, bool raw) { else printf("'%c'", v->Char); } break; + case TypePtr: + printf("ptr<%s>(", type_str[v->Ptr.type.kind]); + print_value(v->Ptr.val, false); + printf(")"); + break; case TypeArr: if (v->Arr.is_string) { if (v->Arr.type.kind != TypeChar) @@ -72,7 +78,7 @@ void print_value(const Value *v, bool raw) { Value ty_val = { .type = v->Arr.type }; memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz); print_value(&ty_val, false); - if (i == v->Arr.len-1) break; + if (i+1 >= v->Arr.len) break; printf(", "); } printf("]"); diff --git a/tok.h b/tok.h index f935798..50cb06f 100644 --- a/tok.h +++ b/tok.h @@ -2,7 +2,6 @@ #define __TOK_H__ #include -#include #include "util.h" @@ -13,6 +12,7 @@ typedef struct Type { TypeInt, TypeBool, TypeChar, + TypePtr, TypeArr, TypeEnumSize, } kind; @@ -33,6 +33,10 @@ typedef struct Value { ssize_t Int; bool Bool; char Char; + struct { + Type type; + struct Value *val; + } Ptr; struct { bool is_string : 1; Type type; @@ -55,6 +59,7 @@ enum Operator { OpMul = '*', OpDiv = '/', OpNot = '!', + OpAddrOf = '&', OpBeginNonchars = 256, OpEq, OpNeq, diff --git a/vm.c b/vm.c index bf323ef..f22e8e9 100644 --- a/vm.c +++ b/vm.c @@ -60,6 +60,20 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), {free(fn_args); stack_term(&s);}); break; + case IRAddrOf: + if (instr->Unary.val.kind != IRParamAddr) { + set_err("Unable to take the address of a literal"); + return; + } + Value *v = &s.mem[instr->Unary.val.Addr]; + s.mem[instr->Unary.addr] = (Value){ + .type.kind = TypePtr, + .Ptr = { + .type.kind = v->type.kind, + .val = v, + }, + }; + break; case IRAdd: case IRSub: case IRDiv: @@ -87,16 +101,36 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { break; case IRCallInternal: { const BuiltinFunc *f = &builtin_funcs[instr->CallI.fid]; + size_t n_args = instr->CallI.n_args; /* make sure enough space for our arguments is allocated */ - if (f->n_args > fn_args_cap) - fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = f->n_args)); + if (n_args > fn_args_cap) + fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = n_args)); /* copy arguments into buffer */ - for (size_t i = 0; i < f->n_args; i++) + for (size_t i = 0; i < n_args; i++) fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]); - stack_fit(&s, instr->CallI.ret_addr); - TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->func(fn_args), - {free(fn_args); stack_term(&s);}); + if (f->returns) { + stack_fit(&s, instr->CallI.ret_addr); + if (f->kind == FuncVarArgs) { + size_t min_args = f->VarArgs.min_args; + TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->VarArgs.WithRet.func(n_args - min_args, fn_args), + {free(fn_args); stack_term(&s);}); + } else if (f->kind == FuncFixedArgs) { + TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->FixedArgs.WithRet.func(fn_args), + {free(fn_args); stack_term(&s);}); + } else + ASSERT_UNREACHED(); + } else { + if (f->kind == FuncVarArgs) { + size_t min_args = f->VarArgs.min_args; + TRY_ELSE(f->VarArgs.NoRet.func(n_args - min_args, fn_args), + {free(fn_args); stack_term(&s);}); + } else if (f->kind == FuncFixedArgs) { + TRY_ELSE(f->FixedArgs.NoRet.func(fn_args), + {free(fn_args); stack_term(&s);}); + } else + ASSERT_UNREACHED(); + } break; } default: