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", [IRJmp] = "jmp",
[IRJnz] = "jnz", [IRJnz] = "jnz",
[IRCallInternal] = "calli", [IRCallInternal] = "calli",
[IRAddrOf] = "addrof",
}; };
#define IRTOKS_INIT_CAP_LONG 4096 #define IRTOKS_INIT_CAP_LONG 4096
@ -91,6 +92,7 @@ void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) {
case IRSet: case IRSet:
case IRNeg: case IRNeg:
case IRNot: case IRNot:
case IRAddrOf:
printf(" %%%zx ", v->toks[i].Unary.addr); printf(" %%%zx ", v->toks[i].Unary.addr);
print_irparam(&v->toks[i].Unary.val); print_irparam(&v->toks[i].Unary.val);
break; break;
@ -119,8 +121,10 @@ void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) {
break; break;
case IRCallInternal: { case IRCallInternal: {
const BuiltinFunc *f = &builtin_funcs[v->toks[i].CallI.fid]; const BuiltinFunc *f = &builtin_funcs[v->toks[i].CallI.fid];
printf(" %s %%%zx", f->name, v->toks[i].CallI.ret_addr); if (f->returns)
for (size_t j = 0; j < f->n_args; j++) { 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(" "); printf(" ");
print_irparam(&v->toks[i].CallI.args[j]); print_irparam(&v->toks[i].CallI.args[j]);
} }

30
ir.h
View File

@ -4,11 +4,33 @@
#include "tok.h" #include "tok.h"
typedef struct BuiltinFunc { typedef struct BuiltinFunc {
char *name; enum {
FuncFixedArgs,
FuncVarArgs,
} kind;
bool returns : 1;
bool side_effects : 1; bool side_effects : 1;
size_t n_args; char *name;
Value (*func)(Value *args);
size_t fid; /* function ID, assigned automatically */ 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; } BuiltinFunc;
enum IRInstr { enum IRInstr {
@ -28,6 +50,7 @@ enum IRInstr {
IRJmp, IRJmp,
IRJnz, IRJnz,
IRCallInternal, IRCallInternal,
IRAddrOf,
IRInstrEnumSize, IRInstrEnumSize,
}; };
typedef enum IRInstr IRInstr; typedef enum IRInstr IRInstr;
@ -80,6 +103,7 @@ typedef struct IRTok {
struct { struct {
size_t ret_addr; size_t ret_addr;
size_t fid; size_t fid;
size_t n_args;
IRParam *args; IRParam *args;
} CallI; } CallI;
}; };

16
lex.c
View File

@ -222,6 +222,15 @@ TokList lex(const char *s, Pool *static_vars) {
continue; continue;
} }
break; 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 '!': case '!':
consume(&pos, *(s++)); consume(&pos, *(s++));
if (s[0] == '=') if (s[0] == '=')
@ -231,13 +240,6 @@ TokList lex(const char *s, Pool *static_vars) {
continue; continue;
} }
break; break;
case '&':
consume(&pos, *(s++));
if (s[0] == '&')
emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpAnd });
else
continue;
break;
case '|': case '|':
consume(&pos, *(s++)); consume(&pos, *(s++));
if (s[0] == '|') if (s[0] == '|')

34
main.c
View File

@ -33,15 +33,18 @@ static void die(const char *fmt, ...) {
exit(1); exit(1);
} }
static Value fn_put(Value *args) { static void fn_put(size_t extra_args, Value *args) {
print_value(&args[0], true); for (size_t i = 0;; i++) {
return (Value){0}; print_value(&args[i], true);
if (i+1 >= extra_args)
break;
printf(" ");
}
} }
static Value fn_putln(Value *args) { static void fn_putln(size_t extra_args, Value *args) {
fn_put(args); fn_put(extra_args, args);
printf("\n"); printf("\n");
return (Value){0};
} }
static Value fn_int(Value *args) { 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)) { if (!(args[0].type.kind == TypeFloat && args[0].Float >= 0.0)) {
set_err("sleep() requires a positive float"); set_err("sleep() requires a positive float");
return (Value){0}; return;
} }
sleep_secs(args[0].Float); sleep_secs(args[0].Float);
return (Value){0};
} }
static Value fn_getln(Value *args) { static Value fn_getln(Value *args) {
@ -203,13 +205,13 @@ int main(int argc, const char **argv) {
print_toks(&tokens); print_toks(&tokens);
/* parse tokens into IR code */ /* parse tokens into IR code */
BuiltinFunc funcs[] = { BuiltinFunc funcs[] = {
{ .name = "put", .side_effects = true, .n_args = 1, .func = fn_put, }, { .name = "put", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_put, }},
{ .name = "putln", .side_effects = true, .n_args = 1, .func = fn_putln, }, { .name = "putln", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_putln, }},
{ .name = "int", .side_effects = false, .n_args = 1, .func = fn_int, }, { .name = "int", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_int, }},
{ .name = "float", .side_effects = false, .n_args = 1, .func = fn_float, }, { .name = "float", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_float, }},
{ .name = "pow", .side_effects = false, .n_args = 2, .func = fn_pow, }, { .name = "pow", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 2, .WithRet.func = fn_pow, }},
{ .name = "sleep", .side_effects = true, .n_args = 1, .func = fn_sleep, }, { .name = "sleep", .kind = FuncFixedArgs, .returns = false, .side_effects = true, .FixedArgs = { .n_args = 1, .NoRet.func = fn_sleep, }},
{ .name = "getln", .side_effects = true, .n_args = 0, .func = fn_getln, }, { .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])); IRToks ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0]));
if (err) { 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 IRSet:
case IRNeg: case IRNeg:
case IRNot: case IRNot:
case IRAddrOf:
t->Unary.addr = addr; t->Unary.addr = addr;
break; break;
case IRAdd: case IRAdd:
@ -230,6 +231,10 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
t = t->next; t = t->next;
perform_unary = true; perform_unary = true;
unary_op = IRNot; 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; t = func_ident;
toklist_del(toks, t->next, t->next); /* delete left parenthesis */ 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); mark_err(&func_ident->tok);
const char *plural = func.n_args == 1 ? "" : "s"; const char *plural = func.FixedArgs.n_args == 1 ? "" : "s";
set_err("Function %s() takes %zu argument%s but got %zu", func.name, func.n_args, plural, args_len); 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) if (args)
free(args); free(args);
return (ExprRet){0}; 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) { if (eval_func_in_place) {
/* evaluate the function 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; Value *arg_vals = args_len ? xmalloc(sizeof(Value) * args_len) : NULL;
for (size_t i = 0; i < args_len; i++) for (size_t i = 0; i < args_len; i++)
arg_vals[i] = args[i].Literal; arg_vals[i] = args[i].Literal;
mark_err(&func_ident->tok); mark_err(&func_ident->tok);
func_ident->tok = (Tok) { func_ident->tok = (Tok) {
.kind = TokVal, .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) if (arg_vals)
free(arg_vals); free(arg_vals);
@ -351,6 +370,7 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc,
.CallI = { .CallI = {
.ret_addr = 0, .ret_addr = 0,
.fid = func.fid, .fid = func.fid,
.n_args = args_len,
.args = args, .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]); set_err("Unsupported type for operation '%s': %s", irinstr_str[instr], type_str[v->type.kind]);
return (Value){0}; return (Value){0};
} }
case IRAddrOf:
set_err("Unable to take the address of a literal");
return (Value){0};
default: default:
ASSERT_UNREACHED(); ASSERT_UNREACHED();
} }

8
tok.c
View File

@ -20,6 +20,7 @@ const char *type_str[TypeEnumSize] = {
[TypeInt] = "int", [TypeInt] = "int",
[TypeBool] = "bool", [TypeBool] = "bool",
[TypeChar] = "char", [TypeChar] = "char",
[TypePtr] = "ptr",
[TypeArr] = "arr", [TypeArr] = "arr",
}; };
@ -46,6 +47,11 @@ void print_value(const Value *v, bool raw) {
else printf("'%c'", v->Char); else printf("'%c'", v->Char);
} }
break; break;
case TypePtr:
printf("ptr<%s>(", type_str[v->Ptr.type.kind]);
print_value(v->Ptr.val, false);
printf(")");
break;
case TypeArr: case TypeArr:
if (v->Arr.is_string) { if (v->Arr.is_string) {
if (v->Arr.type.kind != TypeChar) 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 }; Value ty_val = { .type = v->Arr.type };
memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz); memcpy(&ty_val.Void, (uint8_t*)v->Arr.vals + ty_sz * i, ty_sz);
print_value(&ty_val, false); print_value(&ty_val, false);
if (i == v->Arr.len-1) break; if (i+1 >= v->Arr.len) break;
printf(", "); printf(", ");
} }
printf("]"); printf("]");

7
tok.h
View File

@ -2,7 +2,6 @@
#define __TOK_H__ #define __TOK_H__
#include <stdint.h> #include <stdint.h>
#include <unistd.h>
#include "util.h" #include "util.h"
@ -13,6 +12,7 @@ typedef struct Type {
TypeInt, TypeInt,
TypeBool, TypeBool,
TypeChar, TypeChar,
TypePtr,
TypeArr, TypeArr,
TypeEnumSize, TypeEnumSize,
} kind; } kind;
@ -33,6 +33,10 @@ typedef struct Value {
ssize_t Int; ssize_t Int;
bool Bool; bool Bool;
char Char; char Char;
struct {
Type type;
struct Value *val;
} Ptr;
struct { struct {
bool is_string : 1; bool is_string : 1;
Type type; Type type;
@ -55,6 +59,7 @@ enum Operator {
OpMul = '*', OpMul = '*',
OpDiv = '/', OpDiv = '/',
OpNot = '!', OpNot = '!',
OpAddrOf = '&',
OpBeginNonchars = 256, OpBeginNonchars = 256,
OpEq, OpEq,
OpNeq, OpNeq,

42
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)), TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)),
{free(fn_args); stack_term(&s);}); {free(fn_args); stack_term(&s);});
break; 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 IRAdd:
case IRSub: case IRSub:
case IRDiv: case IRDiv:
@ -87,16 +101,36 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) {
break; break;
case IRCallInternal: { case IRCallInternal: {
const BuiltinFunc *f = &builtin_funcs[instr->CallI.fid]; 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 */ /* make sure enough space for our arguments is allocated */
if (f->n_args > fn_args_cap) if (n_args > fn_args_cap)
fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = f->n_args)); fn_args = xrealloc(fn_args, sizeof(Value) * (fn_args_cap = n_args));
/* copy arguments into buffer */ /* 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]); fn_args[i] = *irparam_to_val(&s, &instr->CallI.args[i]);
if (f->returns) {
stack_fit(&s, instr->CallI.ret_addr); stack_fit(&s, instr->CallI.ret_addr);
TRY_ELSE(s.mem[instr->CallI.ret_addr] = f->func(fn_args), 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);}); {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; break;
} }
default: default: