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:
parent
dd580a1519
commit
1afec3c7a0
7 changed files with 116 additions and 25 deletions
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
15
src/eval.c
15
src/eval.c
|
|
@ -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);
|
||||
|
|
|
|||
33
src/value.c
33
src/value.c
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue