Add named functions

An `name = {}` assignment now sets the name of the function to `name`.
Also the globally defined functions are named too now.
This commit is contained in:
Laria 2023-01-24 21:59:54 +01:00
parent dd580a1519
commit 1afec3c7a0
7 changed files with 116 additions and 25 deletions

View file

@ -73,6 +73,7 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi
case INSN_VAR_NEW:
case INSN_VAR_NEW_LOCAL:
case INSN_MOVE_TO_LOCAL_VAR:
case INSN_FUNC_SET_NAME:
GET_ARGUMENT(ilist, i, arg);
cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING));
break;
@ -175,6 +176,8 @@ apfl_instruction_to_string(enum instruction insn)
return "INSN_FUNC";
case INSN_FUNC_ADD_SUBFUNC:
return "INSN_FUNC_ADD_SUBFUNC";
case INSN_FUNC_SET_NAME:
return "INSN_FUNC_SET_NAME";
case INSN_MATCHER_PUSH:
return "INSN_MATCHER_PUSH";
case INSN_MATCHER_SET_VAL:
@ -346,6 +349,7 @@ apfl_bytecode_dump(unsigned indent, struct apfl_format_writer w, struct instruct
case INSN_VAR_NEW:
case INSN_VAR_NEW_LOCAL:
case INSN_MOVE_TO_LOCAL_VAR:
case INSN_FUNC_SET_NAME:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " "));
FMT_TRY(apfl_format_put_string(w, *arg.string));

View file

@ -64,6 +64,7 @@ enum instruction {
INSN_CALL, // ( func list -- value )
INSN_FUNC, // ( -- func ), arg: count
INSN_FUNC_ADD_SUBFUNC, // ( func -- func' ), arg: body; pops a matcher from the matcher stack
INSN_FUNC_SET_NAME, // ( func -- func' ), arg: string
INSN_MATCHER_PUSH, // ( -- ), arg: matcher; pushes a matcher onto the matcher stack
INSN_MATCHER_SET_VAL, // ( val -- ), arg: index
INSN_MATCHER_MUST_MATCH, // ( val -- ); pops a matcher from the matcher stack

View file

@ -24,7 +24,7 @@ struct compiler {
} \
} while (0)
static bool compile_expr(struct compiler *, struct apfl_expr *, struct instruction_list *);
static bool compile_expr(struct compiler *, struct apfl_expr *, struct instruction_list *, struct apfl_string *name);
static bool
malloc_failure_on_false(struct compiler *compiler, bool b)
@ -200,7 +200,7 @@ compile_list(struct compiler *compiler, struct apfl_expr_list *list, struct inst
for (size_t i = 0; i < list->len; i++) {
struct apfl_expr_list_item *item = &list->items[i];
TRY(compile_expr(compiler, item->expr, ilist));
TRY(compile_expr(compiler, item->expr, ilist, NULL));
TRY(ilist_ensure_cap(compiler, ilist, 1));
ilist_put_insn(ilist, item->expand ? INSN_LIST_EXPAND_INTO : INSN_LIST_APPEND);
}
@ -217,8 +217,8 @@ compile_dict(struct compiler *compiler, struct apfl_expr_dict *dict, struct inst
for (size_t i = 0; i < dict->len; i++) {
struct apfl_expr_dict_pair *pair = &dict->items[i];
TRY(compile_expr(compiler, pair->k, ilist));
TRY(compile_expr(compiler, pair->v, ilist));
TRY(compile_expr(compiler, pair->k, ilist, NULL));
TRY(compile_expr(compiler, pair->v, ilist, NULL));
TRY(ilist_ensure_cap(compiler, ilist, 1));
ilist_put_insn(ilist, INSN_DICT_APPEND_KVPAIR);
}
@ -229,7 +229,7 @@ compile_dict(struct compiler *compiler, struct apfl_expr_dict *dict, struct inst
static bool
compile_dot(struct compiler *compiler, struct apfl_expr_dot *dot, struct instruction_list *ilist)
{
TRY(compile_expr(compiler, dot->lhs, ilist));
TRY(compile_expr(compiler, dot->lhs, ilist, NULL));
TRY(ilist_ensure_cap(compiler, ilist, 3));
@ -246,8 +246,8 @@ compile_dot(struct compiler *compiler, struct apfl_expr_dot *dot, struct instruc
static bool
compile_at(struct compiler *compiler, struct apfl_expr_at *at, struct instruction_list *ilist)
{
TRY(compile_expr(compiler, at->lhs, ilist));
TRY(compile_expr(compiler, at->rhs, ilist));
TRY(compile_expr(compiler, at->lhs, ilist, NULL));
TRY(compile_expr(compiler, at->rhs, ilist, NULL));
TRY(ilist_ensure_cap(compiler, ilist, 1));
ilist_put_insn(ilist, INSN_GET_MEMBER);
@ -286,7 +286,7 @@ compile_simple_assignment(
ilist_put_insn(ilist, new_insn);
ilist_put_string(ilist, str);
TRY(compile_expr(compiler, rhs, ilist));
TRY(compile_expr(compiler, rhs, ilist, str));
TRY(ilist_ensure_cap(compiler, ilist, 2));
ilist_put_insn(ilist, set_insn);
@ -394,7 +394,7 @@ compile_assignable_var_path(
TRY(compile_assignable_var_path(compiler, var_or_member->dot.lhs, args, path_len, varname));
index = args.milist->value_count++;
++*path_len;
TRY(compile_expr(compiler, var_or_member->at.rhs, args.ilist));
TRY(compile_expr(compiler, var_or_member->at.rhs, args.ilist, NULL));
TRY(ilist_ensure_cap(compiler, args.ilist, 2));
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL);
ilist_put_index(args.ilist, index);
@ -479,7 +479,7 @@ compile_matchable_constant(
) { \
size_t index = args.milist->value_count++; \
\
TRY(compile_expr(compiler, predicate->rhs, args.ilist)); \
TRY(compile_expr(compiler, predicate->rhs, args.ilist, NULL)); \
TRY(ilist_ensure_cap(compiler, args.ilist, 2)); \
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL); \
ilist_put_index(args.ilist, index); \
@ -647,7 +647,7 @@ compile_complex_assignment(
ilist
));
TRY(compile_expr(compiler, assignment->rhs, ilist));
TRY(compile_expr(compiler, assignment->rhs, ilist, NULL));
TRY(ilist_ensure_cap(compiler, ilist, 2));
ilist_put_insn(ilist, INSN_DUP);
@ -686,7 +686,7 @@ compile_assignment(
static bool
compile_call(struct compiler *compiler, struct apfl_expr_call *call, struct instruction_list *ilist)
{
TRY(compile_expr(compiler, call->callee, ilist));
TRY(compile_expr(compiler, call->callee, ilist, NULL));
TRY(compile_list(compiler, &call->arguments, ilist));
TRY(ilist_ensure_cap(compiler, ilist, 1));
ilist_put_insn(ilist, INSN_CALL);
@ -699,15 +699,20 @@ compile_body(struct compiler *compiler, struct apfl_expr_body *body, struct inst
// TODO: This leaves the results of all expressions on the value stack.
// The runtime will clean this up, but we van be less wasteful here.
for (size_t i = 0; i < body->len; i++) {
TRY(compile_expr(compiler, &body->items[i], ilist));
TRY(compile_expr(compiler, &body->items[i], ilist, NULL));
}
return true;
}
static bool
compile_simple_func_inner(struct compiler *compiler, struct apfl_expr_body *func, struct instruction_list *ilist, size_t line)
{
compile_simple_func_inner(
struct compiler *compiler,
struct apfl_expr_body *func,
struct instruction_list *ilist,
size_t line,
struct apfl_string *name
) {
struct instruction_list *body_ilist = NULL;
MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, line)));
@ -727,6 +732,12 @@ compile_simple_func_inner(struct compiler *compiler, struct apfl_expr_body *func
ilist_put_insn(ilist, INSN_FUNC_ADD_SUBFUNC);
ilist_put_body(ilist, body_ilist);
if (name != NULL) {
TRY(ilist_ensure_cap(compiler, ilist, 2));
ilist_put_insn(ilist, INSN_FUNC_SET_NAME);
ilist_put_string(ilist, name);
}
return true;
}
@ -809,10 +820,15 @@ compile_param(
}
static bool
compile_simple_func(struct compiler *compiler, struct apfl_expr_body *func, struct instruction_list *ilist, size_t line)
{
compile_simple_func(
struct compiler *compiler,
struct apfl_expr_body *func,
struct instruction_list *ilist,
size_t line,
struct apfl_string *name
) {
size_t tmproots = apfl_gc_tmproots_begin(compiler->gc);
bool ok = compile_simple_func_inner(compiler, func, ilist, line);
bool ok = compile_simple_func_inner(compiler, func, ilist, line, name);
apfl_gc_tmproots_restore(compiler->gc, tmproots);
return ok;
}
@ -846,8 +862,13 @@ compile_subfunc(struct compiler *compiler, struct apfl_expr_subfunc *subfunc, st
}
static bool
compile_complex_func(struct compiler *compiler, struct apfl_expr_complex_func *func, struct instruction_list *ilist, struct apfl_position position)
{
compile_complex_func(
struct compiler *compiler,
struct apfl_expr_complex_func *func,
struct instruction_list *ilist,
struct apfl_position position,
struct apfl_string *name
) {
TRY(ilist_ensure_cap(compiler, ilist, 2));
ilist_put_insn(ilist, INSN_FUNC);
ilist_put_count(ilist, func->len);
@ -862,12 +883,22 @@ compile_complex_func(struct compiler *compiler, struct apfl_expr_complex_func *f
TRY(ok);
}
if (name != NULL) {
TRY(ilist_ensure_cap(compiler, ilist, 2));
ilist_put_insn(ilist, INSN_FUNC_SET_NAME);
ilist_put_string(ilist, name);
}
return true;
}
static bool
compile_expr(struct compiler *compiler, struct apfl_expr *expr, struct instruction_list *ilist)
{
compile_expr(
struct compiler *compiler,
struct apfl_expr *expr,
struct instruction_list *ilist,
struct apfl_string *name
) {
size_t new_line = (size_t)expr->position.line;
if (new_line != compiler->line) {
if (new_line == compiler->line + 1) {
@ -886,9 +917,9 @@ compile_expr(struct compiler *compiler, struct apfl_expr *expr, struct instructi
case APFL_EXPR_CALL:
return compile_call(compiler, &expr->call, ilist);
case APFL_EXPR_SIMPLE_FUNC:
return compile_simple_func(compiler, &expr->simple_func, ilist, new_line);
return compile_simple_func(compiler, &expr->simple_func, ilist, new_line, name);
case APFL_EXPR_COMPLEX_FUNC:
return compile_complex_func(compiler, &expr->complex_func, ilist, expr->position);
return compile_complex_func(compiler, &expr->complex_func, ilist, expr->position, name);
case APFL_EXPR_CONSTANT:
return compile_constant(compiler, &expr->constant, ilist);
case APFL_EXPR_BLANK:
@ -921,7 +952,7 @@ apfl_compile(struct gc *gc, struct apfl_expr expr, struct apfl_error *error_out,
.line = out->line,
};
bool ok = compile_expr(&compiler, &expr, out);
bool ok = compile_expr(&compiler, &expr, out, NULL);
apfl_expr_deinit(gc->allocator, &expr);
if (!ok) {

View file

@ -798,6 +798,9 @@ init_globals(apfl_ctx ctx)
goto error;
}
// TODO: There should also be an API for setting the name of a cfunc
cfunc->name = name;
if (!apfl_scope_set(gc, ctx->globals, name, (struct apfl_value) {
.type = VALUE_CFUNC,
.cfunc = cfunc,

View file

@ -308,6 +308,17 @@ func_add_subfunc(apfl_ctx ctx, struct func_call_stack_entry *cse, struct instruc
matcher_stack_drop(ctx, cse);
}
static void
func_set_name(apfl_ctx ctx, struct apfl_string *name)
{
struct apfl_value value = apfl_stack_must_get(ctx, -1);
if (value.type != VALUE_FUNC) {
apfl_raise_const_error(ctx, apfl_messages.corrupted_bytecode);
}
apfl_func_set_name(value.func, name);
}
static bool
try_call_stack_push(apfl_ctx ctx, struct call_stack_entry cse)
{
@ -650,6 +661,10 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
must_get_argument(ctx, pc, ilist, &arg);
func_add_subfunc(ctx, cse, arg.body);
goto continue_loop;
case INSN_FUNC_SET_NAME:
must_get_argument(ctx, pc, ilist, &arg);
func_set_name(ctx, arg.string);
goto continue_loop;
case INSN_MATCHER_PUSH:
must_get_argument(ctx, pc, ilist, &arg);
matcher_push(ctx, cse, arg.matcher);

View file

@ -144,9 +144,17 @@ format(unsigned indent, struct apfl_format_writer w, struct apfl_value value, bo
return true;
case VALUE_FUNC:
TRY(apfl_format_put_string(w, "{ ... }"));
if (value.func->name != NULL) {
TRY(apfl_format_put_string(w, " # "));
TRY(apfl_format_put_string(w, *value.func->name));
}
return true;
case VALUE_CFUNC:
TRY(apfl_format_put_string(w, "{ native code }"));
if (value.cfunc->name != NULL) {
TRY(apfl_format_put_string(w, " # "));
TRY(apfl_format_put_string(w, *value.cfunc->name));
}
return true;
case VALUE_USERDATA:
TRY(apfl_format_put_string(w, "userdata"));
@ -230,6 +238,7 @@ apfl_func_new(struct gc *gc, size_t cap, struct scope *scope)
.subfunctions_len = 0,
.subfunctions_cap = cap,
.scope = scope,
.name = NULL,
};
return function;
@ -252,6 +261,22 @@ apfl_func_add_subfunc(struct function *function, struct instruction_list *body,
return true;
}
bool
apfl_func_get_name(struct function *function, struct apfl_string_view *name)
{
if (function->name == NULL) {
return false;
}
*name = apfl_string_view_from(*function->name);
return true;
}
void
apfl_func_set_name(struct function *function, struct apfl_string *name)
{
function->name = name;
}
void
apfl_function_deinit(struct apfl_allocator allocator, struct function *function)
{
@ -281,6 +306,7 @@ apfl_cfunc_new(struct gc *gc, apfl_cfunc func, size_t nslots)
cfunction->func = func;
cfunction->slots = slots;
cfunction->slots_len = nslots;
cfunction->name = NULL;
return cfunction;
}
@ -787,6 +813,9 @@ apfl_gc_func_traverse(struct function* function, gc_visitor cb, void *opaque)
cb(opaque, GC_OBJECT_FROM(sub->matcher, GC_TYPE_MATCHER));
}
cb(opaque, GC_OBJECT_FROM(function->scope, GC_TYPE_SCOPE));
if (function->name != NULL) {
cb(opaque, GC_OBJECT_FROM(function->name, GC_TYPE_STRING));
}
}
void
@ -797,4 +826,8 @@ apfl_gc_cfunc_traverse(struct cfunction* cfunc, gc_visitor cb, void *opaque)
cb(opaque, GC_OBJECT_FROM(cfunc->slots[i], GC_TYPE_VAR));
}
}
if (cfunc->name != NULL) {
cb(opaque, GC_OBJECT_FROM(cfunc->name, GC_TYPE_STRING));
}
}

View file

@ -51,12 +51,14 @@ struct function {
size_t subfunctions_len;
size_t subfunctions_cap;
struct scope *scope;
struct apfl_string *name;
};
struct cfunction {
apfl_cfunc func;
struct apfl_value **slots;
size_t slots_len;
struct apfl_string *name;
};
typedef struct dict_header *apfl_dict;
@ -136,6 +138,8 @@ void apfl_dict_deinit(struct dict_header *);
struct function *apfl_func_new(struct gc *, size_t cap, struct scope *);
bool apfl_func_add_subfunc(struct function *, struct instruction_list *, struct matcher *);
bool apfl_func_get_name(struct function *, struct apfl_string_view *name);
void apfl_func_set_name(struct function *, struct apfl_string *name);
void apfl_function_deinit(struct apfl_allocator, struct function *);
struct cfunction *apfl_cfunc_new(struct gc *, apfl_cfunc, size_t nslots);