add pointers, variable arguments and void functions
This commit is contained in:
parent
a706ea6a3f
commit
22f71d7e56
8
ir.c
8
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]);
|
||||
}
|
||||
|
30
ir.h
30
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;
|
||||
};
|
||||
|
16
lex.c
16
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] == '|')
|
||||
|
34
main.c
34
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) {
|
||||
|
28
parse.c
28
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,
|
||||
},
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
8
tok.c
8
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("]");
|
||||
|
7
tok.h
7
tok.h
@ -2,7 +2,6 @@
|
||||
#define __TOK_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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,
|
||||
|
46
vm.c
46
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:
|
||||
|
Loading…
Reference in New Issue
Block a user