From 1afec3c7a099d38fd47da715f9c08dc2b09cf71a Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Tue, 24 Jan 2023 21:59:54 +0100 Subject: [PATCH] Add named functions An `name = {}` assignment now sets the name of the function to `name`. Also the globally defined functions are named too now. --- src/bytecode.c | 4 +++ src/bytecode.h | 1 + src/compile.c | 81 ++++++++++++++++++++++++++++++++++---------------- src/context.c | 3 ++ src/eval.c | 15 ++++++++++ src/value.c | 33 ++++++++++++++++++++ src/value.h | 4 +++ 7 files changed, 116 insertions(+), 25 deletions(-) diff --git a/src/bytecode.c b/src/bytecode.c index f4eb172..36b2bfe 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -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)); diff --git a/src/bytecode.h b/src/bytecode.h index de2f828..5c32255 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -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 diff --git a/src/compile.c b/src/compile.c index e01782b..917f0d2 100644 --- a/src/compile.c +++ b/src/compile.c @@ -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) { diff --git a/src/context.c b/src/context.c index 3ca7629..38b1ce8 100644 --- a/src/context.c +++ b/src/context.c @@ -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, diff --git a/src/eval.c b/src/eval.c index a9e933d..582d25a 100644 --- a/src/eval.c +++ b/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); diff --git a/src/value.c b/src/value.c index c43d5fb..58753b3 100644 --- a/src/value.c +++ b/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)); + } } diff --git a/src/value.h b/src/value.h index 5a9eb7c..b9dff44 100644 --- a/src/value.h +++ b/src/value.h @@ -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);