Allow NULL as subfunction matcher

This will match all arguments and discard them. This makes the bytecode
for simple functions easier and will make it easier to construct simple
function programmatically.
This commit is contained in:
Laria 2023-02-25 23:19:45 +01:00
parent a4f7f0884d
commit 4d840fd817
7 changed files with 71 additions and 36 deletions

View file

@ -83,6 +83,7 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi
cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING));
break;
case INSN_FUNC_ADD_SUBFUNC:
case INSN_FUNC_ADD_SUBFUNC_ANYARGS:
GET_ARGUMENT(ilist, i, arg);
cb(opaque, GC_OBJECT_FROM(arg.body, GC_TYPE_INSTRUCTIONS));
break;
@ -181,6 +182,8 @@ apfl_instruction_to_string(enum instruction insn)
return "INSN_FUNC";
case INSN_FUNC_ADD_SUBFUNC:
return "INSN_FUNC_ADD_SUBFUNC";
case INSN_FUNC_ADD_SUBFUNC_ANYARGS:
return "INSN_FUNC_ADD_SUBFUNC_ANYARGS";
case INSN_FUNC_SET_NAME:
return "INSN_FUNC_SET_NAME";
case INSN_MATCHER_PUSH:
@ -360,6 +363,7 @@ apfl_bytecode_dump(unsigned indent, struct apfl_io_writer w, struct instruction_
FMT_TRY(apfl_io_write_string(w, *arg.string));
break;
case INSN_FUNC_ADD_SUBFUNC:
case INSN_FUNC_ADD_SUBFUNC_ANYARGS:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_io_write_string(w, " ilist{\n"));
FMT_TRY(apfl_bytecode_dump(indent+1, w, arg.body));

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_ADD_SUBFUNC_ANYARGS, // ( func -- func' ), arg: body
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

View file

@ -716,20 +716,12 @@ compile_simple_func_inner(
struct instruction_list *body_ilist = NULL;
MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, line, ilist->filename)));
struct matcher_instruction_list *milist = NULL;
MALLOC_FAIL_IF_NULL(compiler, (milist = tmp_milist(compiler)));
TRY(milist_ensure_cap(compiler, milist, 1));
milist_put_insn(milist, MATCHER_IGNORE); // Ignore all arguments
TRY(compile_body(compiler, func, body_ilist));
TRY(ilist_ensure_cap(compiler, ilist, 6));
ilist_put_insn(ilist, INSN_FUNC);
ilist_put_count(ilist, 1);
ilist_put_insn(ilist, INSN_MATCHER_PUSH);
ilist_put_matcher(ilist, milist);
ilist_put_insn(ilist, INSN_FUNC_ADD_SUBFUNC);
ilist_put_insn(ilist, INSN_FUNC_ADD_SUBFUNC_ANYARGS);
ilist_put_body(ilist, body_ilist);
if (name != NULL) {

View file

@ -298,8 +298,12 @@ func(apfl_ctx ctx, struct func_call_stack_entry *cse, size_t count)
}
static void
func_add_subfunc(apfl_ctx ctx, struct func_call_stack_entry *cse, struct instruction_list *body)
{
func_add_subfunc(
apfl_ctx ctx,
struct func_call_stack_entry *cse,
struct instruction_list *body,
bool with_matcher
) {
// TODO: Better error messsages
struct apfl_value value = apfl_stack_must_get(ctx, -1);
@ -307,11 +311,17 @@ func_add_subfunc(apfl_ctx ctx, struct func_call_stack_entry *cse, struct instruc
apfl_raise_const_error(ctx, apfl_messages.corrupted_bytecode);
}
if (!apfl_func_add_subfunc(value.func, body, matcher_stack_top(ctx, cse))) {
if (!apfl_func_add_subfunc(
value.func,
body,
with_matcher ? matcher_stack_top(ctx, cse) : NULL
)) {
apfl_raise_const_error(ctx, apfl_messages.corrupted_bytecode);
}
matcher_stack_drop(ctx, cse);
if (with_matcher) {
matcher_stack_drop(ctx, cse);
}
}
static void
@ -665,7 +675,11 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
goto continue_loop;
case INSN_FUNC_ADD_SUBFUNC:
must_get_argument(ctx, pc, ilist, &arg);
func_add_subfunc(ctx, cse, arg.body);
func_add_subfunc(ctx, cse, arg.body, true);
goto continue_loop;
case INSN_FUNC_ADD_SUBFUNC_ANYARGS:
must_get_argument(ctx, pc, ilist, &arg);
func_add_subfunc(ctx, cse, arg.body, false);
goto continue_loop;
case INSN_FUNC_SET_NAME:
must_get_argument(ctx, pc, ilist, &arg);
@ -1300,6 +1314,32 @@ matcher_stack_new(void)
};
}
static void
dispatch_accept(struct call_stack_entry *cse)
{
assert(cse->type == APFL_CSE_FUNCTION_DISPATCH);
struct func_dispatch_call_stack_entry *fd_cse = &cse->func_dispatch;
struct function *function = fd_cse->function;
struct subfunction *subfunction = &function->subfunctions[fd_cse->subfunc];
// Replace the current CSE with a function CSE
cse->type = APFL_CSE_FUNCTION;
cse->stack.len = 0;
cse->func = (struct func_call_stack_entry) {
.pc = 0,
.instructions = subfunction->body,
.scopes = fd_cse->scopes,
.execution_line = subfunction->body->line,
.matcher_stack = matcher_stack_new(),
.returning_from_matcher = false,
.matcher_result = false,
.function = function,
.subfunction_index = fd_cse->subfunc,
};
}
static void
dispatch(apfl_ctx ctx, struct call_stack_entry *cse)
{
@ -1310,23 +1350,7 @@ dispatch(apfl_ctx ctx, struct call_stack_entry *cse)
if (fd_cse->returning_from_matcher) {
if (fd_cse->matcher_result) {
struct subfunction *subfunction = &function->subfunctions[fd_cse->subfunc];
// Replace the current CSE with a function CSE
cse->type = APFL_CSE_FUNCTION;
cse->stack.len = 0;
cse->func = (struct func_call_stack_entry) {
.pc = 0,
.instructions = subfunction->body,
.scopes = fd_cse->scopes,
.execution_line = subfunction->body->line,
.matcher_stack = matcher_stack_new(),
.returning_from_matcher = false,
.matcher_result = false,
.function = function,
.subfunction_index = fd_cse->subfunc,
};
dispatch_accept(cse);
return;
}
@ -1338,6 +1362,13 @@ dispatch(apfl_ctx ctx, struct call_stack_entry *cse)
apfl_raise_const_error(ctx, apfl_messages.no_matching_subfunction);
}
struct matcher *matcher = function->subfunctions[fd_cse->subfunc].matcher;
if (matcher == NULL) {
dispatch_accept(cse);
return;
}
// matcher_init_matching consumes the value on the top of the stack, we need
// to copy the value for further subfunctions.
apfl_copy(ctx, -1);

View file

@ -248,9 +248,14 @@ disasm(apfl_ctx ctx)
TRY_FORMAT(ctx, apfl_io_write_string(w, "Subfunction #"));
TRY_FORMAT(ctx, apfl_format_put_number(w, (int)i));
TRY_FORMAT(ctx, apfl_io_write_string(w, "\n"));
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
TRY_FORMAT(ctx, apfl_io_write_string(w, "Matcher\n"));
TRY_FORMAT(ctx, apfl_bytecode_dump_matcher(2, w, subfunction->matcher->instructions));
if (subfunction->matcher == NULL) {
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
TRY_FORMAT(ctx, apfl_io_write_string(w, "No matcher (matches everything)\n"));
} else {
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
TRY_FORMAT(ctx, apfl_io_write_string(w, "Matcher\n"));
TRY_FORMAT(ctx, apfl_bytecode_dump_matcher(2, w, subfunction->matcher->instructions));
}
TRY_FORMAT(ctx, apfl_format_put_indent(w, 1));
TRY_FORMAT(ctx, apfl_io_write_string(w, "Instructions\n"));
TRY_FORMAT(ctx, apfl_bytecode_dump(2, w, subfunction->body));

View file

@ -852,7 +852,9 @@ apfl_gc_func_traverse(struct function* function, gc_visitor cb, void *opaque)
for (size_t i = 0; i < function->subfunctions_len; i++) {
struct subfunction *sub = &function->subfunctions[i];
cb(opaque, GC_OBJECT_FROM(sub->body, GC_TYPE_INSTRUCTIONS));
cb(opaque, GC_OBJECT_FROM(sub->matcher, GC_TYPE_MATCHER));
if (sub->matcher != NULL) {
cb(opaque, GC_OBJECT_FROM(sub->matcher, GC_TYPE_MATCHER));
}
}
cb(opaque, GC_OBJECT_FROM(function->scope, GC_TYPE_SCOPE));
if (function->name != NULL) {

View file

@ -43,7 +43,7 @@ struct dict_header {
};
struct subfunction {
struct matcher *matcher;
struct matcher *matcher; // Can be NULL, in which case all arguments are accepted but discarded
struct instruction_list *body;
};