add pointers, variable arguments and void functions

This commit is contained in:
r4 2021-12-28 13:39:12 +01:00
parent a706ea6a3f
commit 22f71d7e56
9 changed files with 140 additions and 40 deletions

8
ir.c
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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,
},
};

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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: