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:
parent
a4f7f0884d
commit
4d840fd817
7 changed files with 71 additions and 36 deletions
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
75
src/eval.c
75
src/eval.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue