Implement subfunctions and argument matching
A function can now have multiple subfunctions with their own parameter list. These parameters are now no longer constrained to variables and blanks only, but can also be consts and list destructurings (predicates are also already compiled but don't get evaluated yet). The first subfunction that matches the argument list gets evaluated.
This commit is contained in:
parent
0474a85c6f
commit
8ca24bcd49
11 changed files with 419 additions and 182 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
393
src/compile.c
393
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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
94
src/eval.c
94
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;
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
};
|
||||
|
|
|
|||
37
src/value.c
37
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/value.h
12
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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue