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",
|
[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
30
ir.h
@ -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
16
lex.c
@ -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
34
main.c
@ -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
28
parse.c
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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
8
tok.c
@ -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
7
tok.h
@ -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
42
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)),
|
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:
|
||||||
|
Loading…
Reference in New Issue
Block a user