diff --git a/src/apfl.h b/src/apfl.h index ac234bd..f1e0ba4 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -722,6 +722,7 @@ struct apfl_messages { const char *io_error; const char *value_doesnt_match; const char *invalid_matcher_state; + const char *no_matching_subfunction; }; extern const struct apfl_messages apfl_messages; diff --git a/src/bytecode.c b/src/bytecode.c index 4996fae..580bf11 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -63,6 +63,7 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi case INSN_SET_LINE: case INSN_GET_BY_INDEX_KEEP: case INSN_MATCHER_SET_VAL: + case INSN_FUNC: i++; break; case INSN_STRING: @@ -75,7 +76,7 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi GET_ARGUMENT(ilist, i, arg); cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING)); break; - case INSN_FUNC: + case INSN_FUNC_ADD_SUBFUNC: GET_ARGUMENT(ilist, i, arg); cb(opaque, GC_OBJECT_FROM(arg.body, GC_TYPE_INSTRUCTIONS)); break; @@ -143,6 +144,8 @@ apfl_instruction_to_string(enum instruction insn) return "INSN_CALL"; case INSN_FUNC: return "INSN_FUNC"; + case INSN_FUNC_ADD_SUBFUNC: + return "INSN_FUNC_ADD_SUBFUNC"; case INSN_MATCHER_LOAD: return "INSN_MATCHER_LOAD"; case INSN_MATCHER_SET_VAL: @@ -208,8 +211,8 @@ apfl_matcher_instructions_deinit(struct apfl_allocator allocator, struct matcher FREE_LIST(allocator, milist->instructions, milist->cap); } -static bool -bytecode_dump_milist(unsigned indent, struct apfl_format_writer w, struct matcher_instruction_list *milist) +bool +apfl_bytecode_dump_matcher(unsigned indent, struct apfl_format_writer w, struct matcher_instruction_list *milist) { for (size_t i = 0; i < milist->len; i++) { FMT_TRY(apfl_format_put_indent(w, indent)); @@ -249,8 +252,8 @@ bytecode_dump_milist(unsigned indent, struct apfl_format_writer w, struct matche arg = ilist->instructions[++i]; \ } while (0) -static bool -bytecode_dump_ilist(unsigned indent, struct apfl_format_writer w, struct instruction_list *ilist) +bool +apfl_bytecode_dump(unsigned indent, struct apfl_format_writer w, struct instruction_list *ilist) { union instruction_or_arg arg; @@ -280,6 +283,7 @@ bytecode_dump_ilist(unsigned indent, struct apfl_format_writer w, struct instruc break; case INSN_LIST: case INSN_SET_LINE: + case INSN_FUNC: GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg); FMT_TRY(apfl_format_put_string(w, " ")); FMT_TRY(apfl_format_put_int(w, (int)arg.count)); @@ -301,17 +305,17 @@ bytecode_dump_ilist(unsigned indent, struct apfl_format_writer w, struct instruc FMT_TRY(apfl_format_put_string(w, " ")); FMT_TRY(apfl_format_put_string(w, *arg.string)); break; - case INSN_FUNC: + case INSN_FUNC_ADD_SUBFUNC: GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg); FMT_TRY(apfl_format_put_string(w, " ilist{\n")); - FMT_TRY(bytecode_dump_ilist(indent+1, w, arg.body)); + FMT_TRY(apfl_bytecode_dump(indent+1, w, arg.body)); FMT_TRY(apfl_format_put_indent(w, indent)); FMT_TRY(apfl_format_put_string(w, "}")); break; case INSN_MATCHER_LOAD: GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg); FMT_TRY(apfl_format_put_string(w, " milist{\n")); - FMT_TRY(bytecode_dump_milist(indent+1, w, arg.matcher)); + FMT_TRY(apfl_bytecode_dump_matcher(indent+1, w, arg.matcher)); FMT_TRY(apfl_format_put_indent(w, indent)); FMT_TRY(apfl_format_put_string(w, "}")); break; @@ -331,9 +335,3 @@ bytecode_dump_ilist(unsigned indent, struct apfl_format_writer w, struct instruc return true; } - -bool -apfl_bytecode_dump(struct apfl_format_writer w, struct instruction_list *ilist) -{ - return bytecode_dump_ilist(0, w, ilist); -} diff --git a/src/bytecode.h b/src/bytecode.h index d4ee9f1..e4a1184 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -56,7 +56,8 @@ enum instruction { INSN_SET_LINE, // ( -- ), arg: count (new line number) INSN_DROP, // ( value -- ) INSN_CALL, // ( func list -- value ) - INSN_FUNC, // ( -- func ), arg: body + INSN_FUNC, // ( -- func ), arg: count + INSN_FUNC_ADD_SUBFUNC, // ( func -- func' ), arg: body INSN_MATCHER_LOAD, // ( -- ), arg: matcher INSN_MATCHER_SET_VAL, // ( val -- ), arg: index INSN_MATCHER_MUST_MATCH, // ( val -- ) @@ -94,7 +95,8 @@ void apfl_gc_instructions_traverse(struct instruction_list *, gc_visitor, void * struct matcher_instruction_list *apfl_matcher_instructions_new(struct gc *); void apfl_matcher_instructions_deinit(struct apfl_allocator, struct matcher_instruction_list *); -bool apfl_bytecode_dump(struct apfl_format_writer, struct instruction_list *); +bool apfl_bytecode_dump_matcher(unsigned indent, struct apfl_format_writer w, struct matcher_instruction_list *milist); +bool apfl_bytecode_dump(unsigned indent, struct apfl_format_writer w, struct instruction_list *ilist); #ifdef __cplusplus } diff --git a/src/compile.c b/src/compile.c index 57a6255..86f9679 100644 --- a/src/compile.c +++ b/src/compile.c @@ -320,7 +320,7 @@ tmp_milist(struct compiler *compiler) return milist; } -struct compile_assignable_ilists { +struct compile_matchable_ilists { struct instruction_list *prelude; struct instruction_list *newvars; struct instruction_list *setvars; @@ -333,7 +333,7 @@ compile_assignable( bool local, struct apfl_position position, struct apfl_expr_assignable *assignable, - struct compile_assignable_ilists ilists + struct compile_matchable_ilists ilists ); static bool @@ -341,7 +341,7 @@ compile_assignable_var_or_member( struct compiler *compiler, bool local, struct apfl_expr_assignable_var_or_member *var_or_member, - struct compile_assignable_ilists ilists + struct compile_matchable_ilists ilists ) { size_t index = ilists.matcher->capture_count++; @@ -383,10 +383,10 @@ compile_assignable_var_or_member( } static bool -compile_assignable_constant( +compile_matchable_constant( struct compiler *compiler, struct apfl_expr_const *constant, - struct compile_assignable_ilists ilists + struct compile_matchable_ilists ilists ) { size_t index = ilists.matcher->value_count++; @@ -403,96 +403,121 @@ compile_assignable_constant( return true; } -static bool -compile_assignable_predicate( - struct compiler *compiler, - bool local, - struct apfl_position position, - struct apfl_expr_assignable_predicate *predicate, - struct compile_assignable_ilists ilists -) { - size_t index = ilists.matcher->value_count++; +#define DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(name, compile_matchable, predtype) \ + static bool \ + name( \ + struct compiler *compiler, \ + bool local, \ + struct apfl_position position, \ + predtype *predicate, \ + struct compile_matchable_ilists ilists \ + ) { \ + size_t index = ilists.matcher->value_count++; \ + \ + TRY(compile_expr(compiler, predicate->rhs, ilists.prelude)); \ + TRY(ilist_ensure_cap(compiler, ilists.prelude, 2)); \ + ilist_put_insn(ilists.prelude, INSN_MATCHER_SET_VAL); \ + ilist_put_index(ilists.prelude, index); \ + \ + TRY(milist_ensure_cap(compiler, ilists.matcher, 2)); \ + milist_put_insn(ilists.matcher, MATCHER_CHECK_PRED); \ + milist_put_index(ilists.matcher, index); \ + \ + return compile_matchable(compiler, local, position, predicate->lhs, ilists); \ + } \ - TRY(compile_expr(compiler, predicate->rhs, ilists.prelude)); - TRY(ilist_ensure_cap(compiler, ilists.prelude, 2)); - ilist_put_insn(ilists.prelude, INSN_MATCHER_SET_VAL); - ilist_put_index(ilists.prelude, index); +DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE( + compile_assignable_predicate, + compile_assignable, + struct apfl_expr_assignable_predicate +) - TRY(milist_ensure_cap(compiler, ilists.matcher, 2)); - milist_put_insn(ilists.matcher, MATCHER_CHECK_PRED); - milist_put_index(ilists.matcher, index); +#define DEF_COMPILE_ABSTRACT_MATCHABLE_LIST(name, compile_matchable, listtype, listmemb, listitemtype, listitemmemb) \ + static bool \ + name##_expand( \ + struct compiler *compiler, \ + bool local, \ + struct apfl_position position, \ + listtype *list, \ + struct compile_matchable_ilists ilists, \ + size_t expand_at \ + ) { \ + assert(expand_at < list->len); \ + listitemtype *expand_item = &list->listmemb[expand_at]; \ + assert(expand_item->expand); \ + \ + TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \ + milist_put_insn(ilists.matcher, MATCHER_CONTINUE_FROM_END); \ + \ + for (size_t i = list->len; i-- > expand_at+1; ) { \ + listitemtype *item = &list->listmemb[i]; \ + \ + if (item->expand) { \ + compiler->error = (struct apfl_error) { \ + .type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED, \ + .position = position, \ + }; \ + return false; \ + } \ + \ + TRY(compile_matchable(compiler, local, position, &item->listitemmemb, ilists)); \ + } \ + \ + TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \ + milist_put_insn(ilists.matcher, MATCHER_REMAINDING); \ + \ + TRY(compile_matchable(compiler, local, position, &expand_item->listitemmemb, ilists)); \ + \ + return true; \ + } \ + \ + static bool \ + name( \ + struct compiler *compiler, \ + bool local, \ + struct apfl_position position, \ + listtype *list, \ + struct compile_matchable_ilists ilists \ + ) { \ + TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \ + milist_put_insn(ilists.matcher, MATCHER_ENTER_LIST); \ + \ + for (size_t i = 0; i < list->len; i++) { \ + listitemtype *item = &list->listmemb[i]; \ + \ + if (item->expand) { \ + return name##_expand( \ + compiler, \ + local, \ + position, \ + list, \ + ilists, \ + i \ + ); \ + } \ + \ + TRY(compile_matchable(compiler, local, position, &item->listitemmemb, ilists)); \ + } \ + \ + TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \ + milist_put_insn(ilists.matcher, MATCHER_LEAVE_LIST); \ + return true; \ + } \ - return compile_assignable(compiler, local, position, predicate->lhs, ilists); -} +DEF_COMPILE_ABSTRACT_MATCHABLE_LIST( + compile_assignable_list, + compile_assignable, + struct apfl_expr_assignable_list, + items, + struct apfl_expr_assignable_list_item, + assignable +) static bool -compile_assignable_list_expand( - struct compiler *compiler, - bool local, - struct apfl_position position, - struct apfl_expr_assignable_list *list, - struct compile_assignable_ilists ilists, - size_t expand_at -) { - assert(expand_at < list->len); - struct apfl_expr_assignable_list_item *expand_item = &list->items[expand_at]; - assert(expand_item->expand); - +compile_matchable_blank(struct compiler *compiler, struct compile_matchable_ilists ilists) +{ TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); - milist_put_insn(ilists.matcher, MATCHER_CONTINUE_FROM_END); - - for (size_t i = list->len; i-- > expand_at+1; ) { - struct apfl_expr_assignable_list_item *item = &list->items[i]; - - if (item->expand) { - compiler->error = (struct apfl_error) { - .type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED, - .position = position, - }; - return false; - } - - TRY(compile_assignable(compiler, local, position, &item->assignable, ilists)); - } - - TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); - milist_put_insn(ilists.matcher, MATCHER_REMAINDING); - - TRY(compile_assignable(compiler, local, position, &expand_item->assignable, ilists)); - - return true; -} - -static bool -compile_assignable_list( - struct compiler *compiler, - bool local, - struct apfl_position position, - struct apfl_expr_assignable_list *list, - struct compile_assignable_ilists ilists -) { - TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); - milist_put_insn(ilists.matcher, MATCHER_ENTER_LIST); - - for (size_t i = 0; i < list->len; i++) { - struct apfl_expr_assignable_list_item *item = &list->items[i]; - - if (item->expand) { - return compile_assignable_list_expand( - compiler, - local, - position, - list, - ilists, - i - ); - } - - TRY(compile_assignable(compiler, local, position, &item->assignable, ilists)); - } - - TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); - milist_put_insn(ilists.matcher, MATCHER_LEAVE_LIST); + milist_put_insn(ilists.matcher, MATCHER_IGNORE); return true; } @@ -502,21 +527,19 @@ compile_assignable( bool local, struct apfl_position position, struct apfl_expr_assignable *assignable, - struct compile_assignable_ilists ilists + struct compile_matchable_ilists ilists ) { switch (assignable->type) { case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER: return compile_assignable_var_or_member(compiler, local, &assignable->var_or_member, ilists); case APFL_EXPR_ASSIGNABLE_CONSTANT: - return compile_assignable_constant(compiler, &assignable->constant, ilists); + return compile_matchable_constant(compiler, &assignable->constant, ilists); case APFL_EXPR_ASSIGNABLE_PREDICATE: return compile_assignable_predicate(compiler, local, position, &assignable->predicate, ilists); case APFL_EXPR_ASSIGNABLE_LIST: return compile_assignable_list(compiler, local, position, &assignable->list, ilists); case APFL_EXPR_ASSIGNABLE_BLANK: - TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); - milist_put_insn(ilists.matcher, MATCHER_IGNORE); - return true; + return compile_matchable_blank(compiler, ilists); } assert(false); @@ -557,7 +580,7 @@ compile_complex_assignment( TRY(ilist_ensure_cap(compiler, ilist, 2)); - struct compile_assignable_ilists ilists = { + struct compile_matchable_ilists ilists = { .prelude = ilist, }; @@ -640,19 +663,116 @@ compile_simple_func_inner(struct compiler *compiler, struct apfl_expr_body *func struct instruction_list *body_ilist = NULL; MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, line))); - // Drop the argument list, we ignore it in simple functions + 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 + + // Drop the matcher, we ignore arguments in simple functions TRY(ilist_ensure_cap(compiler, body_ilist, 1)); - ilist_put_insn(body_ilist, INSN_DROP); + ilist_put_insn(body_ilist, INSN_MATCHER_DROP); TRY(compile_body(compiler, func, body_ilist)); - TRY(ilist_ensure_cap(compiler, ilist, 2)); + TRY(ilist_ensure_cap(compiler, ilist, 6)); ilist_put_insn(ilist, INSN_FUNC); + ilist_put_count(ilist, 1); + ilist_put_insn(ilist, INSN_MATCHER_LOAD); + ilist_put_matcher(ilist, milist); + ilist_put_insn(ilist, INSN_FUNC_ADD_SUBFUNC); ilist_put_body(ilist, body_ilist); return true; } +static bool +compile_param( + struct compiler *compiler, + bool local, + struct apfl_position position, + struct apfl_expr_param *param, + struct compile_matchable_ilists ilists +); + +DEF_COMPILE_ABSTRACT_MATCHABLE_LIST( + compile_param_list, + compile_param, + struct apfl_expr_params, + params, + struct apfl_expr_params_item, + param +) + +DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE( + compile_param_predicate, + compile_param, + struct apfl_expr_param_predicate +) + +static bool +compile_param_var( + struct compiler *compiler, + struct apfl_string *var, + struct compile_matchable_ilists ilists +) { + size_t index = ilists.matcher->capture_count++; + + /* TODO: What should happen, if we try to match the same variable twice? + * + * Right now, the second capture will overwrite the first one, but it would + * be pretty sweet if this would actually compare the value with the first + * capture, you could write == in apfl itself then: + * + * == := { + * x x -> true + * x y -> false + * } + */ + + TRY(milist_ensure_cap(compiler, ilists.matcher, 2)); + milist_put_insn(ilists.matcher, MATCHER_CAPTURE); + milist_put_index(ilists.matcher, index); + + TRY(ilist_ensure_cap(compiler, ilists.setvars, 3)); + + struct apfl_string *varname = NULL; + TRY(string_move_into_new(compiler, &varname, var)); + + ilist_put_insn(ilists.setvars, INSN_VAR_SET_LOCAL_FROM_MATCHER); + ilist_put_string(ilists.setvars, varname); + ilist_put_index(ilists.setvars, index); + + return true; +} + +static bool +compile_param( + struct compiler *compiler, + bool local, + struct apfl_position position, + struct apfl_expr_param *param, + struct compile_matchable_ilists ilists +) { + (void)local; + + switch (param->type) { + case APFL_EXPR_PARAM_VAR: + return compile_param_var(compiler, ¶m->var, ilists); + case APFL_EXPR_PARAM_CONSTANT: + return compile_matchable_constant(compiler, ¶m->constant, ilists); + case APFL_EXPR_PARAM_LIST: + return compile_param_list(compiler, true, position, ¶m->list, ilists); + case APFL_EXPR_PARAM_PREDICATE: + return compile_param_predicate(compiler, true, position, ¶m->predicate, ilists); + case APFL_EXPR_PARAM_BLANK: + return compile_matchable_blank(compiler, ilists); + } + + assert(false); + return false; +} + static bool compile_simple_func(struct compiler *compiler, struct apfl_expr_body *func, struct instruction_list *ilist, size_t line) { @@ -663,68 +783,55 @@ compile_simple_func(struct compiler *compiler, struct apfl_expr_body *func, stru } static bool -compile_complex_func_inner(struct compiler *compiler, struct apfl_expr_complex_func *func, struct instruction_list *ilist, size_t line) +compile_subfunc(struct compiler *compiler, struct apfl_expr_subfunc *subfunc, struct instruction_list *ilist, struct apfl_position position) { - if (func->len != 1) { - compiler->error = apfl_error_simple(APFL_ERR_NOT_IMPLEMENTED); - return false; - } - - struct apfl_expr_subfunc *subfunc = &func->subfuncs[0]; - struct instruction_list *body_ilist = NULL; - MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, line))); + MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, position.line))); - for (size_t i = 0; i < subfunc->params.len; i++) { - struct apfl_expr_params_item *param = &subfunc->params.params[i]; - if (param->expand) { - compiler->error = apfl_error_simple(APFL_ERR_NOT_IMPLEMENTED); - return false; - } + struct matcher_instruction_list *matcher = NULL; + MALLOC_FAIL_IF_NULL(compiler, (matcher = tmp_milist(compiler))); - switch (param->param.type) { - case APFL_EXPR_PARAM_VAR: - { - TRY(ilist_ensure_cap(compiler, body_ilist, 4)); + TRY(ilist_ensure_cap(compiler, ilist, 2)); + ilist_put_insn(ilist, INSN_MATCHER_LOAD); + ilist_put_matcher(ilist, matcher); - struct apfl_string *str; - TRY(string_move_into_new(compiler, &str, ¶m->param.var)); - - ilist_put_insn(body_ilist, INSN_GET_BY_INDEX_KEEP); - ilist_put_index(body_ilist, i); - ilist_put_insn(body_ilist, INSN_MOVE_TO_LOCAL_VAR); - ilist_put_string(body_ilist, str); - break; - } - case APFL_EXPR_PARAM_CONSTANT: - case APFL_EXPR_PARAM_PREDICATE: - case APFL_EXPR_PARAM_LIST: - compiler->error = apfl_error_simple(APFL_ERR_NOT_IMPLEMENTED); - return false; - case APFL_EXPR_PARAM_BLANK: - break; - } - } + TRY(compile_param_list(compiler, true, position, &subfunc->params, (struct compile_matchable_ilists) { + .prelude = ilist, + .newvars = NULL, // We don't use newvars when compiling parameters + .setvars = body_ilist, + .matcher = matcher, + })); TRY(ilist_ensure_cap(compiler, body_ilist, 1)); - ilist_put_insn(body_ilist, INSN_DROP); // Drop the argument list + ilist_put_insn(body_ilist, INSN_MATCHER_DROP); TRY(compile_body(compiler, &subfunc->body, body_ilist)); TRY(ilist_ensure_cap(compiler, ilist, 2)); - ilist_put_insn(ilist, INSN_FUNC); + ilist_put_insn(ilist, INSN_FUNC_ADD_SUBFUNC); ilist_put_body(ilist, body_ilist); return true; } static bool -compile_complex_func(struct compiler *compiler, struct apfl_expr_complex_func *func, struct instruction_list *ilist, size_t line) +compile_complex_func(struct compiler *compiler, struct apfl_expr_complex_func *func, struct instruction_list *ilist, struct apfl_position position) { - size_t tmproots = apfl_gc_tmproots_begin(compiler->gc); - bool ok = compile_complex_func_inner(compiler, func, ilist, line); - apfl_gc_tmproots_restore(compiler->gc, tmproots); - return ok; + TRY(ilist_ensure_cap(compiler, ilist, 2)); + ilist_put_insn(ilist, INSN_FUNC); + ilist_put_count(ilist, func->len); + + for (size_t i = 0; i < func->len; i++) { + struct apfl_expr_subfunc *subfunc = &func->subfuncs[i]; + + size_t tmproots = apfl_gc_tmproots_begin(compiler->gc); + bool ok = compile_subfunc(compiler, subfunc, ilist, position); + apfl_gc_tmproots_restore(compiler->gc, tmproots); + + TRY(ok); + } + + return true; } static bool @@ -750,7 +857,7 @@ compile_expr(struct compiler *compiler, struct apfl_expr *expr, struct instructi case APFL_EXPR_SIMPLE_FUNC: return compile_simple_func(compiler, &expr->simple_func, ilist, new_line); case APFL_EXPR_COMPLEX_FUNC: - return compile_complex_func(compiler, &expr->complex_func, ilist, new_line); + return compile_complex_func(compiler, &expr->complex_func, ilist, expr->position); case APFL_EXPR_CONSTANT: return compile_constant(compiler, &expr->constant, ilist); case APFL_EXPR_BLANK: diff --git a/src/context.c b/src/context.c index fab0752..f28c200 100644 --- a/src/context.c +++ b/src/context.c @@ -479,6 +479,12 @@ gc_traverse_call_stack_entry(struct call_stack_entry cse, gc_visitor visitor, vo GC_OBJECT_FROM(cse.matcher.matcher, GC_TYPE_MATCHER) ); break; + case CSE_FUNCTION_DISPATCH: + visitor( + opaque, + GC_OBJECT_FROM(cse.func_dispatch.function, GC_TYPE_FUNC) + ); + break; } } @@ -528,6 +534,7 @@ apfl_call_stack_entry_deinit(struct apfl_allocator allocator, struct call_stack_ switch (entry->type) { case CSE_FUNCTION: case CSE_CFUNCTION: + case CSE_FUNCTION_DISPATCH: break; case CSE_MATCHER: FREE_LIST(allocator, entry->matcher.matcher_stack, entry->matcher.matcher_stack_cap); diff --git a/src/context.h b/src/context.h index 70dfdf2..7329bbf 100644 --- a/src/context.h +++ b/src/context.h @@ -27,6 +27,7 @@ enum call_stack_entry_type { CSE_FUNCTION, CSE_CFUNCTION, CSE_MATCHER, + CSE_FUNCTION_DISPATCH, }; struct func_call_stack_entry { @@ -72,6 +73,12 @@ struct matcher_call_stack_entry { size_t matcher_stack_cap; }; +struct func_dispatch_call_stack_entry { + size_t subfunc; + bool returning_from_matcher; + struct function *function; +}; + struct call_stack_entry { enum call_stack_entry_type type; @@ -81,6 +88,7 @@ struct call_stack_entry { struct func_call_stack_entry func; struct cfunc_call_stack_entry cfunc; struct matcher_call_stack_entry matcher; + struct func_dispatch_call_stack_entry func_dispatch; }; }; diff --git a/src/eval.c b/src/eval.c index fc23a2f..5fde49a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -15,6 +15,7 @@ static void evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse); static void evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse); +static void dispatch(apfl_ctx ctx, struct call_stack_entry *cse); static void matcher_init_matching(apfl_ctx ctx, struct matcher *matcher); static void @@ -238,7 +239,7 @@ variable_new(apfl_ctx ctx, struct apfl_string *name, bool local) } static void -func_inner(apfl_ctx ctx, struct instruction_list *body) +func_inner(apfl_ctx ctx, size_t count) { struct scope *scope = apfl_closure_scope_for_func(ctx); if (scope == NULL) { @@ -254,7 +255,7 @@ func_inner(apfl_ctx ctx, struct instruction_list *body) apfl_raise_alloc_error(ctx); } - if ((func_value->func = apfl_func_new(&ctx->gc, body, scope)) == NULL) { + if ((func_value->func = apfl_func_new(&ctx->gc, count, scope)) == NULL) { stack_must_drop(ctx, -1); apfl_raise_alloc_error(ctx); } @@ -263,13 +264,34 @@ func_inner(apfl_ctx ctx, struct instruction_list *body) } static void -func(apfl_ctx ctx, struct instruction_list *body) +func(apfl_ctx ctx, size_t count) { size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); - func_inner(ctx, body); + func_inner(ctx, count); apfl_gc_tmproots_restore(&ctx->gc, tmproots); } +static void +func_add_subfunc(apfl_ctx ctx, struct func_call_stack_entry *cse, struct instruction_list *body) +{ + // TODO: Better error messsages + + struct apfl_value value = apfl_stack_must_get(ctx, -1); + if (value.type != VALUE_FUNC) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode); + } + + if (cse->matcher == NULL) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode); + } + + if (!apfl_func_add_subfunc(value.func, body, cse->matcher)) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode); + } + + cse->matcher = NULL; +} + static void call_stack_push(apfl_ctx ctx, struct call_stack_entry cse) { @@ -365,6 +387,9 @@ evaluate_until_call_stack_return(apfl_ctx ctx) case CSE_MATCHER: evaluate_matcher(ctx, &cse->matcher); break; + case CSE_FUNCTION_DISPATCH: + dispatch(ctx, cse); + break; } } } @@ -402,16 +427,12 @@ call_inner(apfl_ctx ctx, size_t tmproots, apfl_stackidx func_index, apfl_stackid switch (func.type) { case VALUE_FUNC: prepare_call(ctx, tmproots, args, (struct call_stack_entry) { - .type = CSE_FUNCTION, + .type = CSE_FUNCTION_DISPATCH, .stack = apfl_stack_new(), - .func = { - .pc = 0, - .instructions = func.func->body, - .scope = NULL, - .closure_scope = func.func->scope, - .execution_line = func.func->body->line, + .func_dispatch = { + .subfunc = 0, + .function = func.func, .returning_from_matcher = false, - .matcher = NULL, }, }); @@ -622,7 +643,11 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse) return; case INSN_FUNC: must_get_argument(ctx, pc, ilist, &arg); - func(ctx, arg.body); + func(ctx, arg.count); + goto continue_loop; + case INSN_FUNC_ADD_SUBFUNC: + must_get_argument(ctx, pc, ilist, &arg); + func_add_subfunc(ctx, cse, arg.body); goto continue_loop; case INSN_MATCHER_LOAD: must_get_argument(ctx, pc, ilist, &arg); @@ -649,7 +674,7 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse) must_get_argument(ctx, pc, ilist, &arg2); variable_set_from_matcher(ctx, cse->matcher, arg.string, arg2.index, false); goto continue_loop; - case INSN_VAR_SET_LOCAL_FROM_MATCHER: + case INSN_VAR_SET_LOCAL_FROM_MATCHER: must_get_argument(ctx, pc, ilist, &arg); must_get_argument(ctx, pc, ilist, &arg2); variable_set_from_matcher(ctx, cse->matcher, arg.string, arg2.index, true); @@ -965,7 +990,7 @@ return_from_matcher(apfl_ctx ctx, bool result) cse = apfl_call_stack_cur_entry(ctx); assert(cse != NULL); - assert(cse->type == CSE_FUNCTION); + assert(cse->type == CSE_FUNCTION || cse->type == CSE_FUNCTION_DISPATCH); } #define RETURN_WITHOUT_MATCH(ctx) \ @@ -1050,6 +1075,45 @@ evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse) ); } +static void +dispatch(apfl_ctx ctx, struct call_stack_entry *cse) +{ + assert(cse->type == CSE_FUNCTION_DISPATCH); + struct func_dispatch_call_stack_entry *fd_cse = &cse->func_dispatch; + + struct function *function = fd_cse->function; + + if (fd_cse->returning_from_matcher) { + struct subfunction *subfunction = &function->subfunctions[fd_cse->subfunc]; + if (subfunction->matcher->result) { + // Replace the current CSE with a function CSE + cse->type = CSE_FUNCTION; + cse->stack.len = 0; + cse->func = (struct func_call_stack_entry) { + .pc = 0, + .instructions = subfunction->body, + .scope = NULL, + .closure_scope = function->scope, + .execution_line = subfunction->body->line, + .matcher = subfunction->matcher, + .returning_from_matcher = false, + }; + + return; + } + + fd_cse->subfunc++; + fd_cse->returning_from_matcher = false; + } + + if (fd_cse->subfunc >= function->subfunctions_len) { + apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.no_matching_subfunction); + } + + fd_cse->returning_from_matcher = true; + matcher_init_matching(ctx, function->subfunctions[fd_cse->subfunc].matcher); +} + struct apfl_iterative_runner_data { apfl_ctx ctx; apfl_tokenizer_ptr tokenizer; diff --git a/src/globals.c b/src/globals.c index b696988..aad8b94 100644 --- a/src/globals.c +++ b/src/globals.c @@ -187,7 +187,21 @@ disasm(apfl_ctx ctx) apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_type); } - TRY_FORMAT(ctx, apfl_bytecode_dump(w, value.func->body)); + struct function *func = value.func; + + for (size_t i = 0; i < func->subfunctions_len; i++) { + struct subfunction *subfunction = &func->subfunctions[i]; + + TRY_FORMAT(ctx, apfl_format_put_string(w, "Subfunction #")); + TRY_FORMAT(ctx, apfl_format_put_number(w, (int)i)); + TRY_FORMAT(ctx, apfl_format_put_string(w, "\n")); + TRY_FORMAT(ctx, apfl_format_put_indent(w, 1)); + TRY_FORMAT(ctx, apfl_format_put_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_format_put_string(w, "Instructions\n")); + TRY_FORMAT(ctx, apfl_bytecode_dump(2, w, subfunction->body)); + } } static const struct global_def globals[] = { diff --git a/src/messages.c b/src/messages.c index 0e57e41..bdb4e9d 100644 --- a/src/messages.c +++ b/src/messages.c @@ -20,4 +20,5 @@ const struct apfl_messages apfl_messages = { .io_error = "I/O error", .value_doesnt_match = "Value does not match", .invalid_matcher_state = "Matcher is in invalid state", + .no_matching_subfunction = "No matching subfunction", }; diff --git a/src/value.c b/src/value.c index 10c74ee..2841b20 100644 --- a/src/value.c +++ b/src/value.c @@ -188,26 +188,49 @@ apfl_value_type_to_abstract_type(enum value_type type) } struct function * -apfl_func_new(struct gc *gc, struct instruction_list *body, struct scope *scope) +apfl_func_new(struct gc *gc, size_t cap, struct scope *scope) { + struct subfunction *subfunctions = ALLOC_LIST(gc->allocator, struct subfunction, cap); + if (subfunctions == NULL) { + return NULL; + } + struct function *function = apfl_gc_new_func(gc); if (function == NULL) { return NULL; } *function = (struct function) { - .body = body, + .subfunctions = subfunctions, + .subfunctions_len = 0, + .subfunctions_cap = cap, .scope = scope, }; return function; } +bool +apfl_func_add_subfunc(struct function *function, struct instruction_list *body, struct matcher *matcher) +{ + if (function->subfunctions_len >= function->subfunctions_cap) { + return false; + } + + function->subfunctions[function->subfunctions_len] = (struct subfunction) { + .body = body, + .matcher = matcher, + }; + + function->subfunctions_len++; + + return true; +} + void apfl_function_deinit(struct apfl_allocator allocator, struct function *function) { - (void)allocator; - (void)function; + FREE_LIST(allocator, function->subfunctions, function->subfunctions_cap); } struct cfunction * @@ -666,7 +689,11 @@ apfl_gc_dict_traverse(struct dict_header *dict, gc_visitor cb, void *opaque) void apfl_gc_func_traverse(struct function* function, gc_visitor cb, void *opaque) { - cb(opaque, GC_OBJECT_FROM(function->body, GC_TYPE_INSTRUCTIONS)); + 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)); + } cb(opaque, GC_OBJECT_FROM(function->scope, GC_TYPE_SCOPE)); } diff --git a/src/value.h b/src/value.h index 364a0b5..98a2d89 100644 --- a/src/value.h +++ b/src/value.h @@ -41,8 +41,15 @@ struct dict_header { bool copy_on_write; }; -struct function { +struct subfunction { + struct matcher *matcher; struct instruction_list *body; +}; + +struct function { + struct subfunction *subfunctions; + size_t subfunctions_len; + size_t subfunctions_cap; struct scope *scope; }; @@ -116,7 +123,8 @@ bool apfl_dict_set_raw( size_t apfl_dict_len(struct dict_header *); void apfl_dict_deinit(struct dict_header *); -struct function *apfl_func_new(struct gc *, struct instruction_list *, struct scope *); +struct function *apfl_func_new(struct gc *, size_t cap, struct scope *); +bool apfl_func_add_subfunc(struct function *, struct instruction_list *, struct matcher *); void apfl_function_deinit(struct apfl_allocator, struct function *); struct cfunction *apfl_cfunc_new(struct gc *, apfl_cfunc, size_t nslots);