Implement assigning into dictionaries
It's now possible to assign to a key of a dictionary and even to a nested key path. This patch changes the way matchers work a bit: First, a function call stack frame now has a stack of matchers that are manipulateable instead of a single matcher. Second, the matcher is now in charge of setting the matched values to the variables (previously the caller of the matcher needed to extract the matched values and assign them itself). This change simplifies code generation, especially for chained assignments and dictionary key paths. This removes the last usage of APFL_ERR_NOT_IMPLEMENTED :)
This commit is contained in:
parent
f0811ba8fe
commit
20c2880f4c
17 changed files with 1034 additions and 613 deletions
|
|
@ -13,7 +13,6 @@ add_library(apfl
|
|||
gc.c
|
||||
hashmap.c
|
||||
globals.c
|
||||
matcher.c
|
||||
messages.c
|
||||
parser.c
|
||||
position.c
|
||||
|
|
@ -67,6 +66,7 @@ functionaltest("if")
|
|||
functionaltest("while")
|
||||
functionaltest("eq")
|
||||
functionaltest("chained-assignments")
|
||||
functionaltest("dictionary-assignments")
|
||||
|
||||
install(TARGETS apfl DESTINATION lib)
|
||||
install(TARGETS apfl-bin DESTINATION bin)
|
||||
|
|
|
|||
|
|
@ -211,7 +211,6 @@ enum apfl_error_type {
|
|||
APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS,
|
||||
APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS,
|
||||
APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS,
|
||||
APFL_ERR_NOT_IMPLEMENTED,
|
||||
};
|
||||
|
||||
const char *apfl_error_type_name(enum apfl_error_type);
|
||||
|
|
@ -682,6 +681,8 @@ void apfl_dict_create(apfl_ctx);
|
|||
void apfl_dict_set(apfl_ctx, apfl_stackidx dict, apfl_stackidx k, apfl_stackidx v);
|
||||
// Get a value from a container (list or dict) and push it on the stack. container and k will be dropped.
|
||||
void apfl_get_member(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
|
||||
// Get a value from a container (list or dict) and push it on the stack, if it exists. container and k will be dropped.
|
||||
bool apfl_get_member_if_exists(apfl_ctx, apfl_stackidx container, apfl_stackidx k);
|
||||
// Get a value from a list and push it on the stack. The list will stay on the stack.
|
||||
void apfl_get_list_member_by_index(apfl_ctx, apfl_stackidx list, size_t index_in_list);
|
||||
// Get the length of a string/list/dict. The value stays on the stack,
|
||||
|
|
|
|||
118
src/bytecode.c
118
src/bytecode.c
|
|
@ -54,9 +54,9 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi
|
|||
case INSN_GET_MEMBER:
|
||||
case INSN_NEXT_LINE:
|
||||
case INSN_DROP:
|
||||
case INSN_DUP:
|
||||
case INSN_CALL:
|
||||
case INSN_MATCHER_MUST_MATCH:
|
||||
case INSN_MATCHER_DROP:
|
||||
break;
|
||||
case INSN_NUMBER:
|
||||
case INSN_LIST:
|
||||
|
|
@ -80,14 +80,41 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi
|
|||
GET_ARGUMENT(ilist, i, arg);
|
||||
cb(opaque, GC_OBJECT_FROM(arg.body, GC_TYPE_INSTRUCTIONS));
|
||||
break;
|
||||
case INSN_MATCHER_LOAD:
|
||||
case INSN_MATCHER_PUSH:
|
||||
GET_ARGUMENT(ilist, i, arg);
|
||||
cb(opaque, GC_OBJECT_FROM(arg.matcher, GC_TYPE_MATCHER_INSTRUCTIONS));
|
||||
break;
|
||||
case INSN_VAR_SET_FROM_MATCHER:
|
||||
case INSN_VAR_SET_LOCAL_FROM_MATCHER:
|
||||
GET_ARGUMENT(ilist, i, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
apfl_gc_matcher_instructions_traverse(struct matcher_instruction_list *milist, gc_visitor cb, void *opaque)
|
||||
{
|
||||
union matcher_instruction_or_arg arg;
|
||||
|
||||
for (size_t i = 0; i < milist->len; i++) {
|
||||
switch (milist->instructions[i].instruction) {
|
||||
case MATCHER_IGNORE:
|
||||
case MATCHER_ENTER_LIST:
|
||||
case MATCHER_LEAVE_LIST:
|
||||
case MATCHER_CONTINUE_FROM_END:
|
||||
case MATCHER_REMAINDING:
|
||||
break;
|
||||
case MATCHER_CHECK_CONST: // with index as values index
|
||||
case MATCHER_CHECK_PRED: // with index as values index
|
||||
i++;
|
||||
break;
|
||||
case MATCHER_CAPTURE_TO_VAR: // with name
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL: // with name
|
||||
GET_ARGUMENT(milist, i, arg);
|
||||
cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING));
|
||||
break;
|
||||
case MATCHER_CAPTURE_TO_VAR_WITH_PATH: // with name, index and len
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH: // with name, index and len
|
||||
GET_ARGUMENT(milist, i, arg);
|
||||
cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING));
|
||||
i++;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
|
@ -140,24 +167,20 @@ apfl_instruction_to_string(enum instruction insn)
|
|||
return "INSN_GET_BY_INDEX_KEEP";
|
||||
case INSN_DROP:
|
||||
return "INSN_DROP";
|
||||
case INSN_DUP:
|
||||
return "INSN_DUP";
|
||||
case INSN_CALL:
|
||||
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_PUSH:
|
||||
return "INSN_MATCHER_PUSH";
|
||||
case INSN_MATCHER_SET_VAL:
|
||||
return "INSN_MATCHER_SET_VAL";
|
||||
case INSN_MATCHER_MUST_MATCH:
|
||||
return "INSN_MATCHER_MUST_MATCH";
|
||||
case INSN_MATCHER_DROP:
|
||||
return "INSN_MATCHER_DROP";
|
||||
case INSN_VAR_SET_FROM_MATCHER:
|
||||
return "INSN_VAR_SET_FROM_MATCHER";
|
||||
case INSN_VAR_SET_LOCAL_FROM_MATCHER:
|
||||
return "INSN_VAR_SET_LOCAL_FROM_MATCHER";
|
||||
}
|
||||
|
||||
return "??";
|
||||
|
|
@ -169,8 +192,14 @@ apfl_matcher_instruction_to_string(enum matcher_instruction insn)
|
|||
switch (insn) {
|
||||
case MATCHER_IGNORE:
|
||||
return "MATCHER_IGNORE";
|
||||
case MATCHER_CAPTURE:
|
||||
return "MATCHER_CAPTURE";
|
||||
case MATCHER_CAPTURE_TO_VAR:
|
||||
return "MATCHER_CAPTURE_TO_VAR";
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL:
|
||||
return "MATCHER_CAPTURE_TO_VAR_LOCAL";
|
||||
case MATCHER_CAPTURE_TO_VAR_WITH_PATH:
|
||||
return "MATCHER_CAPTURE_TO_VAR_WITH_PATH";
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH:
|
||||
return "MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH";
|
||||
case MATCHER_CHECK_CONST:
|
||||
return "MATCHER_CHECK_CONST";
|
||||
case MATCHER_CHECK_PRED:
|
||||
|
|
@ -211,10 +240,21 @@ apfl_matcher_instructions_deinit(struct apfl_allocator allocator, struct matcher
|
|||
FREE_LIST(allocator, milist->instructions, milist->cap);
|
||||
}
|
||||
|
||||
#define GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg) \
|
||||
do { \
|
||||
if (i >= ilist->len) { \
|
||||
FMT_TRY(apfl_format_put_string(w, "Bytecode corrupted")); \
|
||||
return false; \
|
||||
} \
|
||||
arg = ilist->instructions[++i]; \
|
||||
} while (0)
|
||||
|
||||
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++) {
|
||||
union matcher_instruction_or_arg arg;
|
||||
|
||||
FMT_TRY(apfl_format_put_indent(w, indent));
|
||||
FMT_TRY(apfl_format_put_string(w, apfl_matcher_instruction_to_string(milist->instructions[i].instruction)));
|
||||
|
||||
|
|
@ -225,15 +265,29 @@ apfl_bytecode_dump_matcher(unsigned indent, struct apfl_format_writer w, struct
|
|||
case MATCHER_CONTINUE_FROM_END:
|
||||
case MATCHER_REMAINDING:
|
||||
break;
|
||||
case MATCHER_CAPTURE:
|
||||
case MATCHER_CAPTURE_TO_VAR:
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL:
|
||||
GET_ARGUMENT_FOR_DUMP(w, milist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, " "));
|
||||
FMT_TRY(apfl_format_put_string(w, *arg.string));
|
||||
break;
|
||||
case MATCHER_CAPTURE_TO_VAR_WITH_PATH: // with string, index and len
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH: // with string, index and len
|
||||
GET_ARGUMENT_FOR_DUMP(w, milist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, " "));
|
||||
FMT_TRY(apfl_format_put_string(w, *arg.string));
|
||||
GET_ARGUMENT_FOR_DUMP(w, milist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, ", "));
|
||||
FMT_TRY(apfl_format_put_int(w, (int)arg.index));
|
||||
GET_ARGUMENT_FOR_DUMP(w, milist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, ", "));
|
||||
FMT_TRY(apfl_format_put_int(w, (int)arg.len));
|
||||
break;
|
||||
case MATCHER_CHECK_CONST:
|
||||
case MATCHER_CHECK_PRED:
|
||||
if (i >= milist->len) {
|
||||
FMT_TRY(apfl_format_put_string(w, "Bytecode corrupted"));
|
||||
return false;
|
||||
}
|
||||
GET_ARGUMENT_FOR_DUMP(w, milist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, " "));
|
||||
FMT_TRY(apfl_format_put_int(w, (int)milist->instructions[++i].index));
|
||||
FMT_TRY(apfl_format_put_int(w, (int)arg.index));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -243,15 +297,6 @@ apfl_bytecode_dump_matcher(unsigned indent, struct apfl_format_writer w, struct
|
|||
return true;
|
||||
}
|
||||
|
||||
#define GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg) \
|
||||
do { \
|
||||
if (i >= ilist->len) { \
|
||||
FMT_TRY(apfl_format_put_string(w, "Bytecode corrupted")); \
|
||||
return false; \
|
||||
} \
|
||||
arg = ilist->instructions[++i]; \
|
||||
} while (0)
|
||||
|
||||
bool
|
||||
apfl_bytecode_dump(unsigned indent, struct apfl_format_writer w, struct instruction_list *ilist)
|
||||
{
|
||||
|
|
@ -272,9 +317,9 @@ apfl_bytecode_dump(unsigned indent, struct apfl_format_writer w, struct instruct
|
|||
case INSN_GET_MEMBER:
|
||||
case INSN_NEXT_LINE:
|
||||
case INSN_DROP:
|
||||
case INSN_DUP:
|
||||
case INSN_CALL:
|
||||
case INSN_MATCHER_MUST_MATCH:
|
||||
case INSN_MATCHER_DROP:
|
||||
break;
|
||||
case INSN_NUMBER:
|
||||
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
|
||||
|
|
@ -312,22 +357,13 @@ apfl_bytecode_dump(unsigned indent, struct apfl_format_writer w, struct instruct
|
|||
FMT_TRY(apfl_format_put_indent(w, indent));
|
||||
FMT_TRY(apfl_format_put_string(w, "}"));
|
||||
break;
|
||||
case INSN_MATCHER_LOAD:
|
||||
case INSN_MATCHER_PUSH:
|
||||
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, " milist{\n"));
|
||||
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;
|
||||
case INSN_VAR_SET_FROM_MATCHER:
|
||||
case INSN_VAR_SET_LOCAL_FROM_MATCHER:
|
||||
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
|
||||
FMT_TRY(apfl_format_put_string(w, " "));
|
||||
FMT_TRY(apfl_format_put_string(w, *arg.string));
|
||||
FMT_TRY(apfl_format_put_string(w, ", "));
|
||||
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
|
||||
FMT_TRY(apfl_format_put_int(w, (int)arg.index));
|
||||
break;
|
||||
}
|
||||
|
||||
FMT_TRY(apfl_format_put_char(w, '\n'));
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ extern "C" {
|
|||
|
||||
enum matcher_instruction {
|
||||
MATCHER_IGNORE,
|
||||
MATCHER_CAPTURE, // with index as capture index
|
||||
MATCHER_CAPTURE_TO_VAR, // with name
|
||||
MATCHER_CAPTURE_TO_VAR_LOCAL, // with name
|
||||
MATCHER_CAPTURE_TO_VAR_WITH_PATH, // with name, index and len
|
||||
MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH, // with name, index and len
|
||||
MATCHER_CHECK_CONST, // with index as values index
|
||||
MATCHER_CHECK_PRED, // with index as values index
|
||||
MATCHER_ENTER_LIST,
|
||||
|
|
@ -23,14 +26,16 @@ enum matcher_instruction {
|
|||
union matcher_instruction_or_arg {
|
||||
enum matcher_instruction instruction;
|
||||
size_t index;
|
||||
size_t len;
|
||||
struct apfl_string *string;
|
||||
};
|
||||
|
||||
struct matcher_instruction_list {
|
||||
union matcher_instruction_or_arg *instructions;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
size_t capture_count;
|
||||
size_t value_count;
|
||||
size_t capture_count;
|
||||
};
|
||||
|
||||
enum instruction {
|
||||
|
|
@ -55,15 +60,13 @@ enum instruction {
|
|||
INSN_NEXT_LINE, // ( -- )
|
||||
INSN_SET_LINE, // ( -- ), arg: count (new line number)
|
||||
INSN_DROP, // ( value -- )
|
||||
INSN_DUP, // ( value -- value value)
|
||||
INSN_CALL, // ( func list -- value )
|
||||
INSN_FUNC, // ( -- func ), arg: count
|
||||
INSN_FUNC_ADD_SUBFUNC, // ( func -- func' ), arg: body
|
||||
INSN_MATCHER_LOAD, // ( -- ), arg: matcher
|
||||
INSN_FUNC_ADD_SUBFUNC, // ( func -- func' ), arg: body; pops a matcher from the matcher stack
|
||||
INSN_MATCHER_PUSH, // ( -- ), arg: matcher; pushes a matcher onto the matcher stack
|
||||
INSN_MATCHER_SET_VAL, // ( val -- ), arg: index
|
||||
INSN_MATCHER_MUST_MATCH, // ( val -- )
|
||||
INSN_MATCHER_DROP, // ( -- )
|
||||
INSN_VAR_SET_FROM_MATCHER, // ( -- ), args: string, index
|
||||
INSN_VAR_SET_LOCAL_FROM_MATCHER, // ( -- ), args: string, index
|
||||
INSN_MATCHER_MUST_MATCH, // ( val -- ); pops a matcher from the matcher stack
|
||||
};
|
||||
|
||||
union instruction_or_arg {
|
||||
|
|
@ -91,6 +94,7 @@ struct instruction_list *apfl_instructions_new(struct gc *, int line);
|
|||
void apfl_instructions_deinit(struct apfl_allocator, struct instruction_list *);
|
||||
|
||||
void apfl_gc_instructions_traverse(struct instruction_list *, gc_visitor, void *);
|
||||
void apfl_gc_matcher_instructions_traverse(struct matcher_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 *);
|
||||
|
|
|
|||
396
src/compile.c
396
src/compile.c
|
|
@ -112,28 +112,37 @@ milist_ensure_cap(struct compiler *compiler, struct matcher_instruction_list *mi
|
|||
));
|
||||
}
|
||||
|
||||
#define MILIST_PUT(milist, member, value) \
|
||||
assert(milist->cap > 0); \
|
||||
assert(milist->len < milist->cap); \
|
||||
\
|
||||
milist->instructions[milist->len] = (union matcher_instruction_or_arg) { \
|
||||
.member = value, \
|
||||
}; \
|
||||
milist->len++;
|
||||
|
||||
static void
|
||||
milist_put_insn(struct matcher_instruction_list *milist, enum matcher_instruction instruction)
|
||||
{
|
||||
assert(milist->cap > 0);
|
||||
assert(milist->len < milist->cap);
|
||||
|
||||
milist->instructions[milist->len] = (union matcher_instruction_or_arg) {
|
||||
.instruction = instruction,
|
||||
};
|
||||
milist->len++;
|
||||
MILIST_PUT(milist, instruction, instruction)
|
||||
}
|
||||
|
||||
static void
|
||||
milist_put_index(struct matcher_instruction_list *milist, size_t index)
|
||||
{
|
||||
assert(milist->cap > 0);
|
||||
assert(milist->len < milist->cap);
|
||||
MILIST_PUT(milist, index, index)
|
||||
}
|
||||
|
||||
milist->instructions[milist->len] = (union matcher_instruction_or_arg) {
|
||||
.index = index,
|
||||
};
|
||||
milist->len++;
|
||||
static void
|
||||
milist_put_len(struct matcher_instruction_list *milist, size_t len)
|
||||
{
|
||||
MILIST_PUT(milist, len, len)
|
||||
}
|
||||
|
||||
static void
|
||||
milist_put_string(struct matcher_instruction_list *milist, struct apfl_string *string)
|
||||
{
|
||||
MILIST_PUT(milist, string, string)
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -320,36 +329,88 @@ tmp_milist(struct compiler *compiler)
|
|||
return milist;
|
||||
}
|
||||
|
||||
struct compile_matchable_ilists {
|
||||
struct instruction_list *prelude;
|
||||
struct instruction_list *newvars;
|
||||
struct instruction_list *setvars;
|
||||
struct matcher_instruction_list *matcher;
|
||||
struct compile_matchable_args {
|
||||
struct instruction_list *ilist;
|
||||
struct matcher_instruction_list *milist;
|
||||
bool local;
|
||||
};
|
||||
|
||||
static bool
|
||||
compile_assignable(
|
||||
struct compiler *compiler,
|
||||
bool local,
|
||||
struct apfl_position position,
|
||||
struct apfl_expr_assignable *assignable,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
);
|
||||
|
||||
static enum matcher_instruction
|
||||
matcher_capture_to_var(bool local)
|
||||
{
|
||||
return local ? MATCHER_CAPTURE_TO_VAR_LOCAL : MATCHER_CAPTURE_TO_VAR;
|
||||
}
|
||||
|
||||
static enum matcher_instruction
|
||||
matcher_capture_to_var_with_path(bool local)
|
||||
{
|
||||
return local ? MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH : MATCHER_CAPTURE_TO_VAR_WITH_PATH;
|
||||
}
|
||||
|
||||
static enum instruction
|
||||
insn_var_new(bool local)
|
||||
{
|
||||
return local ? INSN_VAR_NEW_LOCAL : INSN_VAR_NEW;
|
||||
}
|
||||
|
||||
static bool
|
||||
compile_assignable_var_path(
|
||||
struct compiler *compiler,
|
||||
struct apfl_expr_assignable_var_or_member *var_or_member,
|
||||
struct compile_matchable_args args,
|
||||
size_t *path_len,
|
||||
struct apfl_string **varname
|
||||
) {
|
||||
size_t index;
|
||||
struct apfl_string *dot_rhs = NULL;
|
||||
|
||||
switch (var_or_member->type) {
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
||||
TRY(ilist_ensure_cap(compiler, args.ilist, 2));
|
||||
TRY(string_move_into_new(compiler, varname, &var_or_member->var));
|
||||
ilist_put_insn(args.ilist, insn_var_new(args.local));
|
||||
ilist_put_string(args.ilist, *varname);
|
||||
return true;
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
||||
TRY(compile_assignable_var_path(compiler, var_or_member->dot.lhs, args, path_len, varname));
|
||||
index = args.milist->value_count++;
|
||||
++*path_len;
|
||||
TRY(ilist_ensure_cap(compiler, args.ilist, 4));
|
||||
TRY(string_move_into_new(compiler, &dot_rhs, &var_or_member->dot.rhs));
|
||||
ilist_put_insn(args.ilist, INSN_STRING);
|
||||
ilist_put_string(args.ilist, dot_rhs);
|
||||
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL);
|
||||
ilist_put_insn(args.ilist, index);
|
||||
return true;
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
||||
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(ilist_ensure_cap(compiler, args.ilist, 2));
|
||||
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL);
|
||||
ilist_put_index(args.ilist, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
compile_assignable_var_or_member(
|
||||
struct compiler *compiler,
|
||||
bool local,
|
||||
struct apfl_expr_assignable_var_or_member *var_or_member,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
) {
|
||||
size_t index = ilists.matcher->capture_count++;
|
||||
|
||||
if (var_or_member->type != APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR) {
|
||||
compiler->error = apfl_error_simple(APFL_ERR_NOT_IMPLEMENTED); // TODO: Implement me
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
|
@ -362,22 +423,27 @@ compile_assignable_var_or_member(
|
|||
* }
|
||||
*/
|
||||
|
||||
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.newvars, 2));
|
||||
args.milist->capture_count++;
|
||||
|
||||
size_t path_start = args.milist->value_count;
|
||||
size_t path_len = 0;
|
||||
struct apfl_string *varname = NULL;
|
||||
TRY(string_move_into_new(compiler, &varname, &var_or_member->var));
|
||||
|
||||
ilist_put_insn(ilists.newvars, local ? INSN_VAR_NEW_LOCAL : INSN_VAR_NEW);
|
||||
ilist_put_string(ilists.newvars, varname);
|
||||
TRY(compile_assignable_var_path(compiler, var_or_member, args, &path_len, &varname));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, ilists.setvars, 3));
|
||||
ilist_put_insn(ilists.setvars, local ? INSN_VAR_SET_LOCAL_FROM_MATCHER : INSN_VAR_SET_FROM_MATCHER);
|
||||
ilist_put_string(ilists.setvars, varname);
|
||||
ilist_put_index(ilists.setvars, index);
|
||||
assert(varname != NULL /* if compile_assignable_var_path succeeded, it should have set varname */);
|
||||
|
||||
if (path_len == 0) {
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 2));
|
||||
milist_put_insn(args.milist, matcher_capture_to_var(args.local));
|
||||
milist_put_string(args.milist, varname);
|
||||
} else {
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 4));
|
||||
milist_put_insn(args.milist, matcher_capture_to_var_with_path(args.local));
|
||||
milist_put_string(args.milist, varname);
|
||||
milist_put_index(args.milist, path_start);
|
||||
milist_put_len(args.milist, path_len);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -386,45 +452,44 @@ static bool
|
|||
compile_matchable_constant(
|
||||
struct compiler *compiler,
|
||||
struct apfl_expr_const *constant,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
) {
|
||||
size_t index = ilists.matcher->value_count++;
|
||||
size_t index = args.milist->value_count++;
|
||||
|
||||
TRY(compile_constant(compiler, constant, 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(compile_constant(compiler, constant, args.ilist));
|
||||
TRY(ilist_ensure_cap(compiler, args.ilist, 2));
|
||||
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL);
|
||||
ilist_put_index(args.ilist, index);
|
||||
|
||||
TRY(milist_ensure_cap(compiler, ilists.matcher, 3));
|
||||
milist_put_insn(ilists.matcher, MATCHER_CHECK_CONST);
|
||||
milist_put_index(ilists.matcher, index);
|
||||
milist_put_insn(ilists.matcher, MATCHER_IGNORE);
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 3));
|
||||
milist_put_insn(args.milist, MATCHER_CHECK_CONST);
|
||||
milist_put_index(args.milist, index);
|
||||
milist_put_insn(args.milist, MATCHER_IGNORE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#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); \
|
||||
} \
|
||||
#define DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(name, compile_matchable, predtype) \
|
||||
static bool \
|
||||
name( \
|
||||
struct compiler *compiler, \
|
||||
struct apfl_position position, \
|
||||
predtype *predicate, \
|
||||
struct compile_matchable_args args \
|
||||
) { \
|
||||
size_t index = args.milist->value_count++; \
|
||||
\
|
||||
TRY(compile_expr(compiler, predicate->rhs, args.ilist)); \
|
||||
TRY(ilist_ensure_cap(compiler, args.ilist, 2)); \
|
||||
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL); \
|
||||
ilist_put_index(args.ilist, index); \
|
||||
\
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 2)); \
|
||||
milist_put_insn(args.milist, MATCHER_CHECK_PRED); \
|
||||
milist_put_index(args.milist, index); \
|
||||
\
|
||||
return compile_matchable(compiler, position, predicate->lhs, args); \
|
||||
} \
|
||||
|
||||
DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(
|
||||
compile_assignable_predicate,
|
||||
|
|
@ -436,18 +501,17 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(
|
|||
static bool \
|
||||
name##_expand( \
|
||||
struct compiler *compiler, \
|
||||
bool local, \
|
||||
struct apfl_position position, \
|
||||
listtype *list, \
|
||||
struct compile_matchable_ilists ilists, \
|
||||
struct compile_matchable_args args, \
|
||||
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); \
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 1)); \
|
||||
milist_put_insn(args.milist, MATCHER_CONTINUE_FROM_END); \
|
||||
\
|
||||
for (size_t i = list->len; i-- > expand_at+1; ) { \
|
||||
listitemtype *item = &list->listmemb[i]; \
|
||||
|
|
@ -460,13 +524,13 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(
|
|||
return false; \
|
||||
} \
|
||||
\
|
||||
TRY(compile_matchable(compiler, local, position, &item->listitemmemb, ilists)); \
|
||||
TRY(compile_matchable(compiler, position, &item->listitemmemb, args)); \
|
||||
} \
|
||||
\
|
||||
TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \
|
||||
milist_put_insn(ilists.matcher, MATCHER_REMAINDING); \
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 1)); \
|
||||
milist_put_insn(args.milist, MATCHER_REMAINDING); \
|
||||
\
|
||||
TRY(compile_matchable(compiler, local, position, &expand_item->listitemmemb, ilists)); \
|
||||
TRY(compile_matchable(compiler, position, &expand_item->listitemmemb, args)); \
|
||||
\
|
||||
return true; \
|
||||
} \
|
||||
|
|
@ -474,13 +538,12 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(
|
|||
static bool \
|
||||
name( \
|
||||
struct compiler *compiler, \
|
||||
bool local, \
|
||||
struct apfl_position position, \
|
||||
listtype *list, \
|
||||
struct compile_matchable_ilists ilists \
|
||||
struct compile_matchable_args args \
|
||||
) { \
|
||||
TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \
|
||||
milist_put_insn(ilists.matcher, MATCHER_ENTER_LIST); \
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 1)); \
|
||||
milist_put_insn(args.milist, MATCHER_ENTER_LIST); \
|
||||
\
|
||||
for (size_t i = 0; i < list->len; i++) { \
|
||||
listitemtype *item = &list->listmemb[i]; \
|
||||
|
|
@ -488,19 +551,18 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_PREDICATE(
|
|||
if (item->expand) { \
|
||||
return name##_expand( \
|
||||
compiler, \
|
||||
local, \
|
||||
position, \
|
||||
list, \
|
||||
ilists, \
|
||||
args, \
|
||||
i \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
TRY(compile_matchable(compiler, local, position, &item->listitemmemb, ilists)); \
|
||||
TRY(compile_matchable(compiler, position, &item->listitemmemb, args)); \
|
||||
} \
|
||||
\
|
||||
TRY(milist_ensure_cap(compiler, ilists.matcher, 1)); \
|
||||
milist_put_insn(ilists.matcher, MATCHER_LEAVE_LIST); \
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 1)); \
|
||||
milist_put_insn(args.milist, MATCHER_LEAVE_LIST); \
|
||||
return true; \
|
||||
} \
|
||||
|
||||
|
|
@ -514,32 +576,31 @@ DEF_COMPILE_ABSTRACT_MATCHABLE_LIST(
|
|||
)
|
||||
|
||||
static bool
|
||||
compile_matchable_blank(struct compiler *compiler, struct compile_matchable_ilists ilists)
|
||||
compile_matchable_blank(struct compiler *compiler, struct matcher_instruction_list *milist)
|
||||
{
|
||||
TRY(milist_ensure_cap(compiler, ilists.matcher, 1));
|
||||
milist_put_insn(ilists.matcher, MATCHER_IGNORE);
|
||||
TRY(milist_ensure_cap(compiler, milist, 1));
|
||||
milist_put_insn(milist, MATCHER_IGNORE);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
compile_assignable(
|
||||
struct compiler *compiler,
|
||||
bool local,
|
||||
struct apfl_position position,
|
||||
struct apfl_expr_assignable *assignable,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
) {
|
||||
switch (assignable->type) {
|
||||
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
||||
return compile_assignable_var_or_member(compiler, local, &assignable->var_or_member, ilists);
|
||||
return compile_assignable_var_or_member(compiler, &assignable->var_or_member, args);
|
||||
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
||||
return compile_matchable_constant(compiler, &assignable->constant, ilists);
|
||||
return compile_matchable_constant(compiler, &assignable->constant, args);
|
||||
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
||||
return compile_assignable_predicate(compiler, local, position, &assignable->predicate, ilists);
|
||||
return compile_assignable_predicate(compiler, position, &assignable->predicate, args);
|
||||
case APFL_EXPR_ASSIGNABLE_LIST:
|
||||
return compile_assignable_list(compiler, local, position, &assignable->list, ilists);
|
||||
return compile_assignable_list(compiler, position, &assignable->list, args);
|
||||
case APFL_EXPR_ASSIGNABLE_BLANK:
|
||||
return compile_matchable_blank(compiler, ilists);
|
||||
return compile_matchable_blank(compiler, args.milist);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
|
@ -547,16 +608,28 @@ compile_assignable(
|
|||
}
|
||||
|
||||
static bool
|
||||
concat_ilists(struct compiler *compiler, struct instruction_list *out, const struct instruction_list *in)
|
||||
{
|
||||
TRY(ilist_ensure_cap(compiler, out, in->len));
|
||||
memcpy(
|
||||
out->instructions + out->len,
|
||||
in->instructions,
|
||||
sizeof(union instruction_or_arg) * in->len
|
||||
);
|
||||
out->len += in->len;
|
||||
return true;
|
||||
compile_assignment_lhs(
|
||||
struct compiler *compiler,
|
||||
struct apfl_expr_assignable *assignable,
|
||||
bool local,
|
||||
struct apfl_position position,
|
||||
struct instruction_list *ilist
|
||||
) {
|
||||
// By ensuring the capacity early, we can avoid tmprooting the milist, since
|
||||
// we can immediately append it to the already GC-rooted ilist.
|
||||
TRY(ilist_ensure_cap(compiler, ilist, 2));
|
||||
|
||||
struct matcher_instruction_list *milist = apfl_matcher_instructions_new(compiler->gc);
|
||||
MALLOC_FAIL_IF_NULL(compiler, milist);
|
||||
|
||||
ilist_put_insn(ilist, INSN_MATCHER_PUSH);
|
||||
ilist_put_matcher(ilist, milist);
|
||||
|
||||
return compile_assignable(compiler, position, assignable, (struct compile_matchable_args) {
|
||||
.ilist = ilist,
|
||||
.milist = milist,
|
||||
.local = local,
|
||||
});
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -566,43 +639,19 @@ compile_complex_assignment(
|
|||
struct apfl_position position,
|
||||
struct instruction_list *ilist
|
||||
) {
|
||||
// TODO: This is not entirely correct: By evaluating the RHS first, it might
|
||||
// affect variables used in the matcher. This is only really important when
|
||||
// predicates are used, which are not yet implemented:
|
||||
//
|
||||
// x = {true}
|
||||
// foo?x = ({x={false}})
|
||||
//
|
||||
// This should succeed: The predicate x should be loaded before the
|
||||
// evaluation of the rhs occurs. With our current implementation it will
|
||||
// fail, though.
|
||||
TRY(compile_assignment_lhs(
|
||||
compiler,
|
||||
&assignment->lhs,
|
||||
assignment->local,
|
||||
position,
|
||||
ilist
|
||||
));
|
||||
|
||||
TRY(compile_expr(compiler, assignment->rhs, ilist));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, ilist, 2));
|
||||
|
||||
struct compile_matchable_ilists ilists = {
|
||||
.prelude = ilist,
|
||||
};
|
||||
|
||||
MALLOC_FAIL_IF_NULL(compiler, (ilists.matcher = tmp_milist(compiler)));
|
||||
MALLOC_FAIL_IF_NULL(compiler, (ilists.newvars = tmp_ilist(compiler, position.line)));
|
||||
MALLOC_FAIL_IF_NULL(compiler, (ilists.setvars = tmp_ilist(compiler, position.line)));
|
||||
|
||||
ilist_put_insn(ilist, INSN_MATCHER_LOAD);
|
||||
ilist_put_matcher(ilist, ilists.matcher);
|
||||
|
||||
TRY(compile_assignable(compiler, assignment->local, position, &assignment->lhs, ilists));
|
||||
|
||||
TRY(concat_ilists(compiler, ilist, ilists.newvars));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, ilist, 1));
|
||||
ilist_put_insn(ilist, INSN_DUP);
|
||||
ilist_put_insn(ilist, INSN_MATCHER_MUST_MATCH);
|
||||
|
||||
TRY(concat_ilists(compiler, ilist, ilists.setvars));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, ilist, 1));
|
||||
ilist_put_insn(ilist, INSN_MATCHER_DROP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -647,6 +696,8 @@ compile_call(struct compiler *compiler, struct apfl_expr_call *call, struct inst
|
|||
static bool
|
||||
compile_body(struct compiler *compiler, struct apfl_expr_body *body, struct instruction_list *ilist)
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
|
|
@ -666,16 +717,12 @@ compile_simple_func_inner(struct compiler *compiler, struct apfl_expr_body *func
|
|||
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_MATCHER_DROP);
|
||||
|
||||
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_LOAD);
|
||||
ilist_put_insn(ilist, INSN_MATCHER_PUSH);
|
||||
ilist_put_matcher(ilist, milist);
|
||||
ilist_put_insn(ilist, INSN_FUNC_ADD_SUBFUNC);
|
||||
ilist_put_body(ilist, body_ilist);
|
||||
|
|
@ -686,10 +733,9 @@ compile_simple_func_inner(struct compiler *compiler, struct apfl_expr_body *func
|
|||
static bool
|
||||
compile_param(
|
||||
struct compiler *compiler,
|
||||
bool local,
|
||||
struct apfl_position position,
|
||||
struct apfl_expr_param *param,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
);
|
||||
|
||||
DEF_COMPILE_ABSTRACT_MATCHABLE_LIST(
|
||||
|
|
@ -711,9 +757,9 @@ static bool
|
|||
compile_param_var(
|
||||
struct compiler *compiler,
|
||||
struct apfl_string *var,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
) {
|
||||
size_t index = ilists.matcher->capture_count++;
|
||||
args.milist->capture_count++;
|
||||
|
||||
/* TODO: What should happen, if we try to match the same variable twice?
|
||||
*
|
||||
|
|
@ -727,18 +773,13 @@ compile_param_var(
|
|||
* }
|
||||
*/
|
||||
|
||||
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));
|
||||
TRY(milist_ensure_cap(compiler, args.milist, 2));
|
||||
|
||||
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);
|
||||
milist_put_insn(args.milist, MATCHER_CAPTURE_TO_VAR_LOCAL);
|
||||
milist_put_string(args.milist, varname);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -746,24 +787,21 @@ compile_param_var(
|
|||
static bool
|
||||
compile_param(
|
||||
struct compiler *compiler,
|
||||
bool local,
|
||||
struct apfl_position position,
|
||||
struct apfl_expr_param *param,
|
||||
struct compile_matchable_ilists ilists
|
||||
struct compile_matchable_args args
|
||||
) {
|
||||
(void)local;
|
||||
|
||||
switch (param->type) {
|
||||
case APFL_EXPR_PARAM_VAR:
|
||||
return compile_param_var(compiler, ¶m->var, ilists);
|
||||
return compile_param_var(compiler, ¶m->var, args);
|
||||
case APFL_EXPR_PARAM_CONSTANT:
|
||||
return compile_matchable_constant(compiler, ¶m->constant, ilists);
|
||||
return compile_matchable_constant(compiler, ¶m->constant, args);
|
||||
case APFL_EXPR_PARAM_LIST:
|
||||
return compile_param_list(compiler, true, position, ¶m->list, ilists);
|
||||
return compile_param_list(compiler, position, ¶m->list, args);
|
||||
case APFL_EXPR_PARAM_PREDICATE:
|
||||
return compile_param_predicate(compiler, true, position, ¶m->predicate, ilists);
|
||||
return compile_param_predicate(compiler, position, ¶m->predicate, args);
|
||||
case APFL_EXPR_PARAM_BLANK:
|
||||
return compile_matchable_blank(compiler, ilists);
|
||||
return compile_matchable_blank(compiler, args.milist);
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
|
@ -785,23 +823,19 @@ compile_subfunc(struct compiler *compiler, struct apfl_expr_subfunc *subfunc, st
|
|||
struct instruction_list *body_ilist = NULL;
|
||||
MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, position.line)));
|
||||
|
||||
struct matcher_instruction_list *matcher = NULL;
|
||||
MALLOC_FAIL_IF_NULL(compiler, (matcher = tmp_milist(compiler)));
|
||||
struct matcher_instruction_list *milist = NULL;
|
||||
MALLOC_FAIL_IF_NULL(compiler, (milist = tmp_milist(compiler)));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, ilist, 2));
|
||||
ilist_put_insn(ilist, INSN_MATCHER_LOAD);
|
||||
ilist_put_matcher(ilist, matcher);
|
||||
ilist_put_insn(ilist, INSN_MATCHER_PUSH);
|
||||
ilist_put_matcher(ilist, milist);
|
||||
|
||||
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(compile_param_list(compiler, position, &subfunc->params, (struct compile_matchable_args) {
|
||||
.ilist = ilist,
|
||||
.milist = milist,
|
||||
.local = true,
|
||||
}));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, body_ilist, 1));
|
||||
ilist_put_insn(body_ilist, INSN_MATCHER_DROP);
|
||||
|
||||
TRY(compile_body(compiler, &subfunc->body, body_ilist));
|
||||
|
||||
TRY(ilist_ensure_cap(compiler, ilist, 2));
|
||||
|
|
|
|||
233
src/context.c
233
src/context.c
|
|
@ -445,10 +445,7 @@ static void
|
|||
stack_traverse(struct stack stack, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
for (size_t i = 0; i < stack.len; i++) {
|
||||
struct gc_object *object = apfl_value_get_gc_object(stack.items[i]);
|
||||
if (object != NULL) {
|
||||
visitor(opaque, object);
|
||||
}
|
||||
apfl_value_visit_gc_object(stack.items[i], visitor, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,6 +457,72 @@ visit_nullable_scope(struct scope *scope, gc_visitor visitor, void *opaque)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
visit_scopes(struct scopes scopes, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
visit_nullable_scope(scopes.local, visitor, opaque);
|
||||
visit_nullable_scope(scopes.closure, visitor, opaque);
|
||||
}
|
||||
|
||||
static void
|
||||
visit_matcher_stack(struct matcher_stack matcher_stack, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
for (size_t i = 0; i < matcher_stack.len; i++) {
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(matcher_stack.items[i], GC_TYPE_MATCHER)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
visit_func_cse(struct func_call_stack_entry cse, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.instructions, GC_TYPE_INSTRUCTIONS)
|
||||
);
|
||||
|
||||
visit_scopes(cse.scopes, visitor, opaque);
|
||||
visit_matcher_stack(cse.matcher_stack, visitor, opaque);
|
||||
}
|
||||
|
||||
static void
|
||||
visit_cfunc_cse(struct cfunc_call_stack_entry cse, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.func, GC_TYPE_CFUNC)
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
visit_matcher_cse(struct matcher_call_stack_entry cse, gc_visitor visitor, void* opaque)
|
||||
{
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.matcher, GC_TYPE_MATCHER)
|
||||
);
|
||||
visit_scopes(cse.scopes, visitor, opaque);
|
||||
for (size_t i = 0; i < cse.capture_count; i++) {
|
||||
apfl_value_visit_gc_object(cse.captures[i], visitor, opaque);
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.transfers[i].var, GC_TYPE_STRING)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
visit_func_dispatch_cse(struct func_dispatch_call_stack_entry cse, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.function, GC_TYPE_FUNC)
|
||||
);
|
||||
visit_scopes(cse.scopes, visitor, opaque);
|
||||
}
|
||||
|
||||
static void
|
||||
gc_traverse_call_stack_entry(struct call_stack_entry cse, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
|
|
@ -467,41 +530,18 @@ gc_traverse_call_stack_entry(struct call_stack_entry cse, gc_visitor visitor, vo
|
|||
|
||||
switch (cse.type) {
|
||||
case CSE_FUNCTION:
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.func.instructions, GC_TYPE_INSTRUCTIONS)
|
||||
);
|
||||
|
||||
visit_nullable_scope(cse.func.scope, visitor, opaque);
|
||||
visit_nullable_scope(cse.func.closure_scope, visitor, opaque);
|
||||
|
||||
if (cse.func.matcher != NULL) {
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.func.matcher, GC_TYPE_MATCHER)
|
||||
);
|
||||
}
|
||||
visit_func_cse(cse.func, visitor, opaque);
|
||||
break;
|
||||
case CSE_CFUNCTION:
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.cfunc.func, GC_TYPE_CFUNC)
|
||||
);
|
||||
visit_cfunc_cse(cse.cfunc, visitor, opaque);
|
||||
break;
|
||||
case CSE_MATCHER:
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.matcher.matcher, GC_TYPE_MATCHER)
|
||||
);
|
||||
visit_matcher_cse(cse.matcher, visitor, opaque);
|
||||
break;
|
||||
case CSE_FUNCTION_DISPATCH:
|
||||
visitor(
|
||||
opaque,
|
||||
GC_OBJECT_FROM(cse.func_dispatch.function, GC_TYPE_FUNC)
|
||||
);
|
||||
visit_func_dispatch_cse(cse.func_dispatch, visitor, opaque);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -541,6 +581,23 @@ deinit_stack(struct apfl_allocator allocator, struct stack *stack)
|
|||
*stack = apfl_stack_new();
|
||||
}
|
||||
|
||||
static void
|
||||
func_call_stack_entry_deinit(struct apfl_allocator allocator, struct func_call_stack_entry *cse)
|
||||
{
|
||||
FREE_LIST(allocator, cse->matcher_stack.items, cse->matcher_stack.cap);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_matcher_call_stack_entry_deinit(struct apfl_allocator allocator, struct matcher_call_stack_entry *cse)
|
||||
{
|
||||
FREE_LIST(allocator, cse->captures, cse->capture_count);
|
||||
cse->captures = NULL;
|
||||
FREE_LIST(allocator, cse->transfers, cse->capture_count);
|
||||
cse->transfers = NULL;
|
||||
FREE_LIST(allocator, cse->matcher_state_stack, cse->matcher_state_stack_cap);
|
||||
cse->matcher_state_stack = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_call_stack_entry_deinit(struct apfl_allocator allocator, struct call_stack_entry *entry)
|
||||
{
|
||||
|
|
@ -548,11 +605,13 @@ apfl_call_stack_entry_deinit(struct apfl_allocator allocator, struct call_stack_
|
|||
|
||||
switch (entry->type) {
|
||||
case CSE_FUNCTION:
|
||||
func_call_stack_entry_deinit(allocator, &entry->func);
|
||||
break;
|
||||
case CSE_CFUNCTION:
|
||||
case CSE_FUNCTION_DISPATCH:
|
||||
break;
|
||||
case CSE_MATCHER:
|
||||
FREE_LIST(allocator, entry->matcher.matcher_state_stack, entry->matcher.matcher_state_stack_cap);
|
||||
apfl_matcher_call_stack_entry_deinit(allocator, &entry->matcher);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -988,8 +1047,8 @@ apfl_dict_set(
|
|||
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, v_index}));
|
||||
}
|
||||
|
||||
void
|
||||
apfl_get_member(
|
||||
bool
|
||||
apfl_get_member_if_exists(
|
||||
apfl_ctx ctx,
|
||||
apfl_stackidx container_index,
|
||||
apfl_stackidx k_index
|
||||
|
|
@ -1011,17 +1070,16 @@ apfl_get_member(
|
|||
|
||||
enum get_item_result result = apfl_value_get_item(container, k, value);
|
||||
if (result != GET_ITEM_OK) {
|
||||
assert(apfl_stack_drop(ctx, -1));
|
||||
assert(apfl_stack_drop(ctx, -1)); // Drop the placeholder
|
||||
}
|
||||
|
||||
assert(apfl_stack_drop_multi(ctx, 2, (apfl_stackidx[]){k_index, container_index}));
|
||||
|
||||
switch (result) {
|
||||
case GET_ITEM_OK:
|
||||
break;
|
||||
return true;
|
||||
case GET_ITEM_KEY_DOESNT_EXIST:
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.key_doesnt_exist);
|
||||
break;
|
||||
return false;
|
||||
case GET_ITEM_NOT_A_CONTAINER:
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.value_is_not_a_container);
|
||||
break;
|
||||
|
|
@ -1029,6 +1087,20 @@ apfl_get_member(
|
|||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.wrong_key_type);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_get_member(
|
||||
apfl_ctx ctx,
|
||||
apfl_stackidx container_index,
|
||||
apfl_stackidx k_index
|
||||
) {
|
||||
if (!apfl_get_member_if_exists(ctx, container_index, k_index)) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.key_doesnt_exist);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1180,7 +1252,7 @@ apfl_eq(apfl_ctx ctx, apfl_stackidx _a, apfl_stackidx _b)
|
|||
}
|
||||
|
||||
static struct scope *
|
||||
closure_scope_for_func_inner(apfl_ctx ctx)
|
||||
closure_scope_for_func_inner(apfl_ctx ctx, struct scopes scopes)
|
||||
{
|
||||
struct scope *out = apfl_scope_new(&ctx->gc);
|
||||
if (out == NULL) {
|
||||
|
|
@ -1191,22 +1263,18 @@ closure_scope_for_func_inner(apfl_ctx ctx)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct call_stack_entry *entry = apfl_call_stack_cur_entry(ctx);
|
||||
if (entry == NULL || entry->type != CSE_FUNCTION) {
|
||||
return out;
|
||||
}
|
||||
// The order is important here: by merging the local scope last, we make
|
||||
// sure a variable from the current scope shadows a variable from the
|
||||
// closure scope
|
||||
|
||||
// The order is important here: by merging entry->scope last, we make sure a
|
||||
// variable from the current scope shadows a variable from the closure scope
|
||||
|
||||
if (entry->func.closure_scope != NULL) {
|
||||
if (!apfl_scope_merge_into(out, entry->func.closure_scope)) {
|
||||
if (scopes.closure != NULL) {
|
||||
if (!apfl_scope_merge_into(out, scopes.closure)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->func.scope != NULL) {
|
||||
if (!apfl_scope_merge_into(out, entry->func.scope)) {
|
||||
if (scopes.local != NULL) {
|
||||
if (!apfl_scope_merge_into(out, scopes.local)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1215,10 +1283,10 @@ closure_scope_for_func_inner(apfl_ctx ctx)
|
|||
}
|
||||
|
||||
struct scope *
|
||||
apfl_closure_scope_for_func(apfl_ctx ctx)
|
||||
apfl_closure_scope_for_func(apfl_ctx ctx, struct scopes scopes)
|
||||
{
|
||||
size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc);
|
||||
struct scope *out = closure_scope_for_func_inner(ctx);
|
||||
struct scope *out = closure_scope_for_func_inner(ctx, scopes);
|
||||
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
|
||||
return out;
|
||||
}
|
||||
|
|
@ -1350,3 +1418,64 @@ struct apfl_format_writer apfl_get_output_writer(apfl_ctx ctx)
|
|||
{
|
||||
return ctx->output_writer;
|
||||
}
|
||||
|
||||
static bool
|
||||
init_values_list(struct apfl_allocator allocator, struct apfl_value **list, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((*list = ALLOC_LIST(allocator, struct apfl_value, len)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
(*list)[i] = (struct apfl_value) { .type = VALUE_NIL };
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct matcher *
|
||||
apfl_matcher_new(struct gc *gc, struct matcher_instruction_list *milist)
|
||||
{
|
||||
struct matcher matcher = {
|
||||
.instructions = milist,
|
||||
.value_count = 0,
|
||||
.values = NULL,
|
||||
};
|
||||
|
||||
if (!init_values_list(gc->allocator, &matcher.values, milist->value_count)) {
|
||||
goto error;
|
||||
}
|
||||
matcher.value_count = milist->value_count;
|
||||
|
||||
struct matcher *gc_matcher = apfl_gc_new_matcher(gc);
|
||||
if (gc_matcher == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
*gc_matcher = matcher;
|
||||
|
||||
return gc_matcher;
|
||||
|
||||
error:
|
||||
apfl_matcher_deinit(gc->allocator, &matcher);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_matcher_deinit(struct apfl_allocator allocator, struct matcher *matcher)
|
||||
{
|
||||
FREE_LIST(allocator, matcher->values, matcher->value_count);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_gc_matcher_traverse(struct matcher *matcher, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
visitor(opaque, GC_OBJECT_FROM(matcher->instructions, GC_TYPE_MATCHER_INSTRUCTIONS));
|
||||
|
||||
for (size_t i = 0; i < matcher->instructions->value_count; i++) {
|
||||
apfl_value_visit_gc_object(matcher->values[i], visitor, opaque);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,18 +11,42 @@ extern "C" {
|
|||
#include "bytecode.h"
|
||||
#include "hashmap.h"
|
||||
#include "gc.h"
|
||||
#include "matcher.h"
|
||||
#include "value.h"
|
||||
#include "scope.h"
|
||||
|
||||
#define RESULT_OFF_FOR_LONGJMP 1
|
||||
|
||||
struct scopes {
|
||||
// Both scope and closure_scope can be null
|
||||
struct scope *local;
|
||||
struct scope *closure;
|
||||
};
|
||||
|
||||
struct matcher_capture_transfer {
|
||||
struct apfl_string *var;
|
||||
size_t path_start;
|
||||
size_t path_len;
|
||||
bool local;
|
||||
};
|
||||
|
||||
struct matcher {
|
||||
struct matcher_instruction_list *instructions;
|
||||
size_t value_count;
|
||||
struct apfl_value *values;
|
||||
};
|
||||
|
||||
struct stack {
|
||||
struct apfl_value *items;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct matcher_stack {
|
||||
struct matcher **items;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
enum call_stack_entry_type {
|
||||
CSE_FUNCTION,
|
||||
CSE_CFUNCTION,
|
||||
|
|
@ -34,14 +58,14 @@ struct func_call_stack_entry {
|
|||
size_t pc;
|
||||
struct instruction_list *instructions;
|
||||
|
||||
// Both scope and closure_scope can be null (scope will be created lazily)
|
||||
struct scope *scope;
|
||||
struct scope *closure_scope;
|
||||
// scopes.scope will be created lazily
|
||||
struct scopes scopes;
|
||||
|
||||
int execution_line;
|
||||
|
||||
struct matcher *matcher;
|
||||
struct matcher_stack matcher_stack;
|
||||
bool returning_from_matcher;
|
||||
bool matcher_result;
|
||||
};
|
||||
|
||||
struct cfunc_call_stack_entry {
|
||||
|
|
@ -67,6 +91,12 @@ struct matcher_state {
|
|||
struct matcher_call_stack_entry {
|
||||
size_t pc;
|
||||
struct matcher *matcher;
|
||||
struct scopes scopes;
|
||||
|
||||
size_t capture_index;
|
||||
size_t capture_count;
|
||||
struct apfl_value *captures;
|
||||
struct matcher_capture_transfer *transfers;
|
||||
|
||||
struct matcher_state *matcher_state_stack;
|
||||
size_t matcher_state_stack_len;
|
||||
|
|
@ -75,7 +105,9 @@ struct matcher_call_stack_entry {
|
|||
|
||||
struct func_dispatch_call_stack_entry {
|
||||
size_t subfunc;
|
||||
struct scopes scopes;
|
||||
bool returning_from_matcher;
|
||||
bool matcher_result;
|
||||
struct function *function;
|
||||
};
|
||||
|
||||
|
|
@ -127,6 +159,7 @@ struct apfl_ctx_data {
|
|||
struct apfl_format_writer output_writer;
|
||||
};
|
||||
|
||||
void apfl_matcher_call_stack_entry_deinit(struct apfl_allocator, struct matcher_call_stack_entry *);
|
||||
void apfl_call_stack_entry_deinit(struct apfl_allocator, struct call_stack_entry *);
|
||||
|
||||
struct stack apfl_stack_new(void);
|
||||
|
|
@ -147,13 +180,17 @@ bool apfl_move_string_onto_stack(apfl_ctx, struct apfl_string);
|
|||
|
||||
struct call_stack_entry *apfl_call_stack_cur_entry(apfl_ctx);
|
||||
|
||||
struct scope *apfl_closure_scope_for_func(apfl_ctx);
|
||||
struct scope *apfl_closure_scope_for_func(apfl_ctx, struct scopes);
|
||||
|
||||
struct apfl_format_writer apfl_get_output_writer(apfl_ctx);
|
||||
|
||||
bool apfl_ctx_register_iterative_runner(apfl_ctx, apfl_iterative_runner);
|
||||
void apfl_ctx_unregister_iterative_runner(apfl_ctx, apfl_iterative_runner);
|
||||
|
||||
struct matcher *apfl_matcher_new(struct gc *, struct matcher_instruction_list *);
|
||||
void apfl_matcher_deinit(struct apfl_allocator, struct matcher *);
|
||||
void apfl_gc_matcher_traverse(struct matcher *, gc_visitor, void *);
|
||||
|
||||
void apfl_iterative_runner_visit_gc_objects(apfl_iterative_runner, gc_visitor, void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -58,8 +58,6 @@ apfl_error_type_name(enum apfl_error_type type)
|
|||
return "APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS";
|
||||
case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS:
|
||||
return "APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS";
|
||||
case APFL_ERR_NOT_IMPLEMENTED:
|
||||
return "APFL_ERR_NOT_IMPLEMENTED";
|
||||
}
|
||||
|
||||
return "<unknown error>";
|
||||
|
|
@ -75,8 +73,6 @@ apfl_error_as_const_string(struct apfl_error error)
|
|||
return apfl_messages.input_error_while_parsing;
|
||||
case APFL_ERR_UNEXPECTED_EOF:
|
||||
return apfl_messages.unexpected_end_of_file;
|
||||
case APFL_ERR_NOT_IMPLEMENTED:
|
||||
return apfl_messages.feature_not_implemented;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -186,9 +182,6 @@ format_error(struct apfl_format_writer w, struct apfl_error error)
|
|||
TRY(apfl_format_put_string(w, "Unexpected blank (\"_\") in member access near "));
|
||||
TRY(apfl_format_put_pos(w, error.position));
|
||||
return true;
|
||||
case APFL_ERR_NOT_IMPLEMENTED:
|
||||
TRY(apfl_format_put_string(w, apfl_messages.feature_not_implemented));
|
||||
return true;
|
||||
}
|
||||
|
||||
TRY(apfl_format_put_string(w, "Unknown error "));
|
||||
|
|
|
|||
587
src/eval.c
587
src/eval.c
|
|
@ -8,7 +8,6 @@
|
|||
#include "context.h"
|
||||
#include "format.h"
|
||||
#include "hashmap.h"
|
||||
#include "matcher.h"
|
||||
#include "resizable.h"
|
||||
#include "strings.h"
|
||||
#include "value.h"
|
||||
|
|
@ -16,7 +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 matcher_init_matching(apfl_ctx ctx, struct matcher *matcher, struct scopes scopes);
|
||||
|
||||
static void
|
||||
stack_must_drop(apfl_ctx ctx, apfl_stackidx index)
|
||||
|
|
@ -61,19 +60,6 @@ must_get_matcher_argument(apfl_ctx ctx, size_t *i, struct matcher_instruction_li
|
|||
ABSTRACT_MUST_GET_ARG(get_matcher_argument, ctx, i, milist, arg)
|
||||
}
|
||||
|
||||
static struct func_call_stack_entry *
|
||||
get_current_func_cse(apfl_ctx ctx)
|
||||
{
|
||||
struct call_stack_entry *cse = apfl_call_stack_cur_entry(ctx);
|
||||
if (cse == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cse->type == CSE_FUNCTION
|
||||
? &cse->func
|
||||
: NULL;
|
||||
}
|
||||
|
||||
enum scope_type {
|
||||
SCOPE_LOCAL,
|
||||
SCOPE_CLOSUE,
|
||||
|
|
@ -81,21 +67,13 @@ enum scope_type {
|
|||
};
|
||||
|
||||
static struct scope *
|
||||
get_scope(apfl_ctx ctx, enum scope_type type)
|
||||
get_scope(apfl_ctx ctx, struct scopes scopes, enum scope_type type)
|
||||
{
|
||||
struct func_call_stack_entry *func_cse;
|
||||
|
||||
switch (type) {
|
||||
case SCOPE_LOCAL:
|
||||
if ((func_cse = get_current_func_cse(ctx)) != NULL) {
|
||||
return func_cse->scope;
|
||||
}
|
||||
return NULL;
|
||||
return scopes.local;
|
||||
case SCOPE_CLOSUE:
|
||||
if ((func_cse = get_current_func_cse(ctx)) != NULL) {
|
||||
return func_cse->closure_scope;
|
||||
}
|
||||
return NULL;
|
||||
return scopes.closure;
|
||||
case SCOPE_GLOBAL:
|
||||
return ctx->globals;
|
||||
}
|
||||
|
|
@ -105,29 +83,30 @@ get_scope(apfl_ctx ctx, enum scope_type type)
|
|||
}
|
||||
|
||||
static struct scope *
|
||||
get_or_create_local_scope(apfl_ctx ctx)
|
||||
get_or_create_local_scope(apfl_ctx ctx, struct scopes *scopes)
|
||||
{
|
||||
struct func_call_stack_entry *func_cse = get_current_func_cse(ctx);
|
||||
assert(func_cse != NULL);
|
||||
|
||||
if (func_cse->scope != NULL) {
|
||||
return func_cse->scope;
|
||||
if (scopes->local != NULL) {
|
||||
return scopes->local;
|
||||
}
|
||||
|
||||
if ((func_cse->scope = apfl_scope_new(&ctx->gc)) == NULL) {
|
||||
if ((scopes->local = apfl_scope_new(&ctx->gc)) == NULL) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
return func_cse->scope;
|
||||
return scopes->local;
|
||||
}
|
||||
|
||||
static bool
|
||||
try_variable_get_for_scope_type(apfl_ctx ctx, struct apfl_string *name, enum scope_type type)
|
||||
{
|
||||
try_variable_get_for_scope_type(
|
||||
apfl_ctx ctx,
|
||||
struct scopes scopes,
|
||||
struct apfl_string *name,
|
||||
enum scope_type type
|
||||
) {
|
||||
struct apfl_value value;
|
||||
struct scope *scope;
|
||||
|
||||
if ((scope = get_scope(ctx, type)) != NULL) {
|
||||
if ((scope = get_scope(ctx, scopes, type)) != NULL) {
|
||||
if (apfl_scope_get(scope, name, &value)) {
|
||||
apfl_stack_must_push(ctx, value);
|
||||
return true;
|
||||
|
|
@ -137,15 +116,15 @@ try_variable_get_for_scope_type(apfl_ctx ctx, struct apfl_string *name, enum sco
|
|||
}
|
||||
|
||||
static void
|
||||
variable_get(apfl_ctx ctx, struct apfl_string *name)
|
||||
variable_get(apfl_ctx ctx, struct scopes scopes, struct apfl_string *name, bool global)
|
||||
{
|
||||
if (try_variable_get_for_scope_type(ctx, name, SCOPE_LOCAL)) {
|
||||
if (try_variable_get_for_scope_type(ctx, scopes, name, SCOPE_LOCAL)) {
|
||||
return;
|
||||
}
|
||||
if (try_variable_get_for_scope_type(ctx, name, SCOPE_CLOSUE)) {
|
||||
if (try_variable_get_for_scope_type(ctx, scopes, name, SCOPE_CLOSUE)) {
|
||||
return;
|
||||
}
|
||||
if (try_variable_get_for_scope_type(ctx, name, SCOPE_GLOBAL)) {
|
||||
if (global && try_variable_get_for_scope_type(ctx, scopes, name, SCOPE_GLOBAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -154,33 +133,28 @@ variable_get(apfl_ctx ctx, struct apfl_string *name)
|
|||
|
||||
static bool
|
||||
try_variable_update_existing_for_scope_type(
|
||||
apfl_ctx ctx,
|
||||
struct apfl_string *name,
|
||||
struct apfl_value value,
|
||||
enum scope_type type
|
||||
struct scope *scope
|
||||
) {
|
||||
struct scope *scope;
|
||||
|
||||
if ((scope = get_scope(ctx, type)) != NULL) {
|
||||
if (apfl_scope_update_existing(scope, name, value)) {
|
||||
return true;
|
||||
}
|
||||
if (scope == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return apfl_scope_update_existing(scope, name, value);
|
||||
}
|
||||
|
||||
static void
|
||||
variable_set_value(apfl_ctx ctx, struct apfl_string *name, bool local, struct apfl_value value)
|
||||
variable_set_value(apfl_ctx ctx, struct scopes *scopes, struct apfl_string *name, bool local, struct apfl_value value)
|
||||
{
|
||||
bool was_set = false;
|
||||
if (!local) {
|
||||
was_set = try_variable_update_existing_for_scope_type(ctx, name, value, SCOPE_LOCAL)
|
||||
|| try_variable_update_existing_for_scope_type(ctx, name, value, SCOPE_CLOSUE);
|
||||
was_set = try_variable_update_existing_for_scope_type(name, value, scopes->local)
|
||||
|| try_variable_update_existing_for_scope_type(name, value, scopes->closure);
|
||||
}
|
||||
|
||||
if (!was_set) {
|
||||
struct scope *scope = get_or_create_local_scope(ctx);
|
||||
struct scope *scope = get_or_create_local_scope(ctx, scopes);
|
||||
assert(scope != NULL /*get_or_create_local_scope should never return NULL*/);
|
||||
|
||||
if (!apfl_scope_set(&ctx->gc, scope, name, value)) {
|
||||
|
|
@ -190,11 +164,11 @@ variable_set_value(apfl_ctx ctx, struct apfl_string *name, bool local, struct ap
|
|||
}
|
||||
|
||||
static void
|
||||
variable_set(apfl_ctx ctx, struct apfl_string *name, bool keep_on_stack, bool local)
|
||||
variable_set(apfl_ctx ctx, struct scopes *scopes, struct apfl_string *name, bool keep_on_stack, bool local)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, -1);
|
||||
|
||||
variable_set_value(ctx, name, local, value);
|
||||
variable_set_value(ctx, scopes, name, local, value);
|
||||
|
||||
if (keep_on_stack) {
|
||||
// If the value should be kept on the stack, the value is now in two
|
||||
|
|
@ -206,42 +180,88 @@ variable_set(apfl_ctx ctx, struct apfl_string *name, bool keep_on_stack, bool lo
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
variable_exists_by_scope_type(apfl_ctx ctx, struct apfl_string *name, enum scope_type type)
|
||||
{
|
||||
struct scope *scope;
|
||||
|
||||
if ((scope = get_scope(ctx, type)) != NULL) {
|
||||
if (apfl_scope_has(scope, name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
variable_new(apfl_ctx ctx, struct apfl_string *name, bool local)
|
||||
variable_new(apfl_ctx ctx, struct scopes *scopes, struct apfl_string *name, bool local)
|
||||
{
|
||||
if (!local) {
|
||||
if (variable_exists_by_scope_type(ctx, name, SCOPE_LOCAL)) {
|
||||
if (scopes->local != NULL && apfl_scope_has(scopes->local, name)) {
|
||||
return;
|
||||
}
|
||||
if (variable_exists_by_scope_type(ctx, name, SCOPE_CLOSUE)) {
|
||||
if (scopes->closure != NULL && apfl_scope_has(scopes->closure, name)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct scope *scope = get_or_create_local_scope(ctx);
|
||||
struct scope *scope = get_or_create_local_scope(ctx, scopes);
|
||||
if (!apfl_scope_create_var(&ctx->gc, scope, name)) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
func_inner(apfl_ctx ctx, size_t count)
|
||||
matcher_push(apfl_ctx ctx, struct func_call_stack_entry *cse, struct matcher_instruction_list *milist)
|
||||
{
|
||||
struct scope *scope = apfl_closure_scope_for_func(ctx);
|
||||
struct matcher_stack *matcher_stack = &cse->matcher_stack;
|
||||
if (!apfl_resizable_ensure_cap_for_more_elements(
|
||||
ctx->gc.allocator,
|
||||
sizeof(struct matcher *),
|
||||
(void **)&matcher_stack->items,
|
||||
matcher_stack->len,
|
||||
&matcher_stack->cap,
|
||||
1
|
||||
)) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
if ((matcher_stack->items[matcher_stack->len] = apfl_matcher_new(&ctx->gc, milist)) == NULL) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
matcher_stack->len++;
|
||||
}
|
||||
|
||||
static struct matcher *
|
||||
matcher_stack_top(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
||||
{
|
||||
struct matcher_stack *matcher_stack = &cse->matcher_stack;
|
||||
if (matcher_stack->len == 0) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
struct matcher *matcher = matcher_stack->items[matcher_stack->len-1];
|
||||
|
||||
if (matcher == NULL) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
return matcher;
|
||||
}
|
||||
|
||||
static void
|
||||
matcher_stack_drop(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
||||
{
|
||||
struct matcher_stack *matcher_stack = &cse->matcher_stack;
|
||||
if (matcher_stack->len == 0) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
assert(
|
||||
// We're shrinking, should not fail
|
||||
apfl_resizable_resize(
|
||||
ctx->gc.allocator,
|
||||
sizeof(struct matcher *),
|
||||
(void **)&matcher_stack->items,
|
||||
&matcher_stack->len,
|
||||
&matcher_stack->cap,
|
||||
matcher_stack->len-1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
func_inner(apfl_ctx ctx, struct func_call_stack_entry *cse, size_t count)
|
||||
{
|
||||
struct scope *scope = apfl_closure_scope_for_func(ctx, cse->scopes);
|
||||
if (scope == NULL) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
|
@ -264,10 +284,10 @@ func_inner(apfl_ctx ctx, size_t count)
|
|||
}
|
||||
|
||||
static void
|
||||
func(apfl_ctx ctx, size_t count)
|
||||
func(apfl_ctx ctx, struct func_call_stack_entry *cse, size_t count)
|
||||
{
|
||||
size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc);
|
||||
func_inner(ctx, count);
|
||||
func_inner(ctx, cse, count);
|
||||
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
|
||||
}
|
||||
|
||||
|
|
@ -281,21 +301,17 @@ func_add_subfunc(apfl_ctx ctx, struct func_call_stack_entry *cse, struct instruc
|
|||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
if (cse->matcher == NULL) {
|
||||
if (!apfl_func_add_subfunc(value.func, body, matcher_stack_top(ctx, cse))) {
|
||||
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;
|
||||
matcher_stack_drop(ctx, cse);
|
||||
}
|
||||
|
||||
static void
|
||||
call_stack_push(apfl_ctx ctx, struct call_stack_entry cse)
|
||||
static bool
|
||||
try_call_stack_push(apfl_ctx ctx, struct call_stack_entry cse)
|
||||
{
|
||||
if (!apfl_resizable_append(
|
||||
return apfl_resizable_append(
|
||||
ctx->gc.allocator,
|
||||
sizeof(struct call_stack_entry),
|
||||
(void**)&ctx->call_stack.items,
|
||||
|
|
@ -303,7 +319,13 @@ call_stack_push(apfl_ctx ctx, struct call_stack_entry cse)
|
|||
&ctx->call_stack.cap,
|
||||
&cse,
|
||||
1
|
||||
)) {
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
call_stack_push(apfl_ctx ctx, struct call_stack_entry cse)
|
||||
{
|
||||
if (!try_call_stack_push(ctx, cse)) {
|
||||
apfl_call_stack_entry_deinit(ctx->gc.allocator, &cse);
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
|
@ -425,14 +447,28 @@ call_inner(apfl_ctx ctx, size_t tmproots, apfl_stackidx func_index, apfl_stackid
|
|||
}
|
||||
|
||||
switch (func.type) {
|
||||
case VALUE_FUNC:
|
||||
case VALUE_FUNC: {
|
||||
struct scope *local_scope = apfl_scope_new(&ctx->gc);
|
||||
if (local_scope == NULL) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
if (!apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(local_scope, GC_TYPE_SCOPE))) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
prepare_call(ctx, tmproots, args, (struct call_stack_entry) {
|
||||
.type = CSE_FUNCTION_DISPATCH,
|
||||
.stack = apfl_stack_new(),
|
||||
.func_dispatch = {
|
||||
.subfunc = 0,
|
||||
.scopes = {
|
||||
.local = local_scope,
|
||||
.closure = func.func->scope,
|
||||
},
|
||||
.function = func.func,
|
||||
.returning_from_matcher = false,
|
||||
.matcher_result = false,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -444,6 +480,7 @@ call_inner(apfl_ctx ctx, size_t tmproots, apfl_stackidx func_index, apfl_stackid
|
|||
evaluate_until_call_stack_return(ctx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VALUE_CFUNC:
|
||||
prepare_call(ctx, tmproots, args, (struct call_stack_entry) {
|
||||
.type = CSE_CFUNCTION,
|
||||
|
|
@ -475,23 +512,10 @@ apfl_call(apfl_ctx ctx, apfl_stackidx func_index, apfl_stackidx args_index)
|
|||
call(ctx, func_index, args_index, false);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
matcher_load(apfl_ctx ctx, struct func_call_stack_entry *cse, struct matcher_instruction_list *milist)
|
||||
matcher_set_val(apfl_ctx ctx, struct func_call_stack_entry *cse, size_t index)
|
||||
{
|
||||
assert(cse != NULL);
|
||||
|
||||
if ((cse->matcher = apfl_matcher_new(&ctx->gc, milist)) == NULL) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
matcher_set_val(apfl_ctx ctx, struct matcher *matcher, size_t index)
|
||||
{
|
||||
if (matcher == NULL) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
struct matcher *matcher = matcher_stack_top(ctx, cse);
|
||||
|
||||
if (index >= matcher->instructions->value_count) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
|
|
@ -501,37 +525,17 @@ matcher_set_val(apfl_ctx ctx, struct matcher *matcher, size_t index)
|
|||
}
|
||||
|
||||
static void
|
||||
variable_set_from_matcher_inner(
|
||||
apfl_ctx ctx,
|
||||
struct matcher *matcher,
|
||||
struct apfl_string *varname,
|
||||
size_t index,
|
||||
bool local
|
||||
) {
|
||||
if (matcher == NULL) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
if (index >= matcher->instructions->capture_count) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
struct apfl_value value = apfl_value_move(&matcher->captures[index]);
|
||||
must_tmproot_add_value(ctx, value);
|
||||
|
||||
variable_set_value(ctx, varname, local, value);
|
||||
}
|
||||
|
||||
static void
|
||||
variable_set_from_matcher(
|
||||
apfl_ctx ctx,
|
||||
struct matcher *matcher,
|
||||
struct apfl_string *varname,
|
||||
size_t index,
|
||||
bool local
|
||||
) {
|
||||
matcher_must_match(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
||||
{
|
||||
size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc);
|
||||
variable_set_from_matcher_inner(ctx, matcher, varname, index, local);
|
||||
|
||||
struct matcher *matcher = matcher_stack_top(ctx, cse);
|
||||
if (!apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(matcher, GC_TYPE_MATCHER))) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
matcher_stack_drop(ctx, cse);
|
||||
matcher_init_matching(ctx, matcher, cse->scopes);
|
||||
|
||||
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
|
||||
}
|
||||
|
||||
|
|
@ -539,16 +543,13 @@ static void
|
|||
evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
||||
{
|
||||
if (cse->returning_from_matcher) {
|
||||
assert(cse->matcher != NULL);
|
||||
if (!cse->matcher->result) {
|
||||
cse->matcher = NULL;
|
||||
if (!cse->matcher_result) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.value_doesnt_match);
|
||||
}
|
||||
cse->returning_from_matcher = false;
|
||||
}
|
||||
|
||||
union instruction_or_arg arg;
|
||||
union instruction_or_arg arg2;
|
||||
|
||||
size_t *pc = &cse->pc;
|
||||
struct instruction_list *ilist = cse->instructions;
|
||||
|
|
@ -596,27 +597,27 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
|||
goto continue_loop;
|
||||
case INSN_VAR_NEW:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
variable_new(ctx, arg.string, false);
|
||||
variable_new(ctx, &cse->scopes, arg.string, false);
|
||||
goto continue_loop;
|
||||
case INSN_VAR_NEW_LOCAL:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
variable_new(ctx, arg.string, true);
|
||||
variable_new(ctx, &cse->scopes, arg.string, true);
|
||||
goto continue_loop;
|
||||
case INSN_VAR_GET:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
variable_get(ctx, arg.string);
|
||||
variable_get(ctx, cse->scopes, arg.string, true);
|
||||
goto continue_loop;
|
||||
case INSN_VAR_SET:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
variable_set(ctx, arg.string, true, false);
|
||||
variable_set(ctx, &cse->scopes, arg.string, true, false);
|
||||
goto continue_loop;
|
||||
case INSN_VAR_SET_LOCAL:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
variable_set(ctx, arg.string, true, true);
|
||||
variable_set(ctx, &cse->scopes, arg.string, true, true);
|
||||
goto continue_loop;
|
||||
case INSN_MOVE_TO_LOCAL_VAR:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
variable_set(ctx, arg.string, false, true);
|
||||
variable_set(ctx, &cse->scopes, arg.string, false, true);
|
||||
goto continue_loop;
|
||||
case INSN_NEXT_LINE:
|
||||
cse->execution_line++;
|
||||
|
|
@ -634,6 +635,9 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
|||
apfl_raise_invalid_stackidx(ctx);
|
||||
}
|
||||
goto continue_loop;
|
||||
case INSN_DUP:
|
||||
apfl_copy(ctx, -1);
|
||||
goto continue_loop;
|
||||
case INSN_CALL:
|
||||
call(ctx, -2, -1, true);
|
||||
|
||||
|
|
@ -643,42 +647,28 @@ evaluate(apfl_ctx ctx, struct func_call_stack_entry *cse)
|
|||
return;
|
||||
case INSN_FUNC:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
func(ctx, arg.count);
|
||||
func(ctx, cse, 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:
|
||||
case INSN_MATCHER_PUSH:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
matcher_load(ctx, cse, arg.matcher);
|
||||
matcher_push(ctx, cse, arg.matcher);
|
||||
goto continue_loop;
|
||||
case INSN_MATCHER_SET_VAL:
|
||||
must_get_argument(ctx, pc, ilist, &arg);
|
||||
matcher_set_val(ctx, cse->matcher, arg.index);
|
||||
matcher_set_val(ctx, cse, arg.index);
|
||||
goto continue_loop;
|
||||
case INSN_MATCHER_MUST_MATCH:
|
||||
// matcher_init_matching pushes a new call stack entry for the matcher onto the stack. We rturn from this
|
||||
// matcher_must_match pushes a new call stack entry for the matcher onto the stack. We return from this
|
||||
// So this new CSE gets executed. By setting returning_from_matcher, we know that we came from the matcher,
|
||||
// once it returns.
|
||||
|
||||
cse->returning_from_matcher = true;
|
||||
matcher_init_matching(ctx, cse->matcher);
|
||||
matcher_must_match(ctx, cse);
|
||||
|
||||
return;
|
||||
case INSN_MATCHER_DROP:
|
||||
cse->matcher = NULL;
|
||||
goto continue_loop;
|
||||
case INSN_VAR_SET_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, false);
|
||||
goto continue_loop;
|
||||
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);
|
||||
goto continue_loop;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
|
|
@ -732,41 +722,99 @@ matcher_state_drop(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
|
|||
}
|
||||
|
||||
static void
|
||||
matcher_init_matching_inner(apfl_ctx ctx, struct matcher *matcher)
|
||||
matcher_init_matching_inner(apfl_ctx ctx, struct matcher *matcher, struct scopes scopes)
|
||||
{
|
||||
struct apfl_value value = apfl_stack_must_get(ctx, -1);
|
||||
struct apfl_value value = apfl_stack_must_pop(ctx, -1);
|
||||
must_tmproot_add_value(ctx, value);
|
||||
|
||||
if (matcher == NULL) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.corrupted_bytecode);
|
||||
}
|
||||
|
||||
size_t capture_count = matcher->instructions->capture_count;
|
||||
struct matcher_call_stack_entry matcher_cse = {
|
||||
.pc = 0,
|
||||
.matcher = matcher,
|
||||
.scopes = scopes,
|
||||
.capture_index = 0,
|
||||
.capture_count = capture_count,
|
||||
.captures = NULL,
|
||||
.transfers = NULL,
|
||||
.matcher_state_stack = NULL,
|
||||
.matcher_state_stack_len = 0,
|
||||
.matcher_state_stack_cap = 0,
|
||||
};
|
||||
|
||||
matcher_state_push(ctx, &matcher_cse, (struct matcher_state) {
|
||||
.mode = MATCHER_MODE_VALUE,
|
||||
});
|
||||
if (capture_count > 0) {
|
||||
if ((matcher_cse.captures = ALLOC_LIST(ctx->gc.allocator, struct apfl_value, capture_count)) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
call_stack_push(ctx, (struct call_stack_entry) {
|
||||
for (size_t i = 0; i < capture_count; i++) {
|
||||
matcher_cse.captures[i] = (struct apfl_value) { .type = VALUE_NIL };
|
||||
}
|
||||
|
||||
if ((matcher_cse.transfers = ALLOC_LIST(
|
||||
ctx->gc.allocator,
|
||||
struct matcher_capture_transfer,
|
||||
capture_count
|
||||
)) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < capture_count; i++) {
|
||||
matcher_cse.transfers[i] = (struct matcher_capture_transfer) {
|
||||
.var = NULL,
|
||||
.path_start = 0,
|
||||
.path_len = 0,
|
||||
.local = false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if ((matcher_cse.matcher_state_stack = ALLOC_LIST(
|
||||
ctx->gc.allocator,
|
||||
struct matcher_state,
|
||||
1
|
||||
)) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
matcher_cse.matcher_state_stack[0] = (struct matcher_state) {
|
||||
.mode = MATCHER_MODE_VALUE,
|
||||
};
|
||||
matcher_cse.matcher_state_stack_len = 1;
|
||||
matcher_cse.matcher_state_stack_cap = 1;
|
||||
|
||||
if (!try_call_stack_push(ctx, (struct call_stack_entry) {
|
||||
.type = CSE_MATCHER,
|
||||
.stack = apfl_stack_new(),
|
||||
.matcher = matcher_cse,
|
||||
});
|
||||
})) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// No need for `goto error` on failure here, all dynamically allocated
|
||||
// elements are on the call stack now, so the GC can clean them up in case
|
||||
// of an error.
|
||||
apfl_stack_must_push(ctx, apfl_value_set_cow_flag(value));
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
apfl_matcher_call_stack_entry_deinit(ctx->gc.allocator, &matcher_cse);
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise matching. Pushes a new call stack and pops a value of the current+
|
||||
* value stack.
|
||||
*/
|
||||
static void
|
||||
matcher_init_matching(apfl_ctx ctx, struct matcher *matcher)
|
||||
matcher_init_matching(apfl_ctx ctx, struct matcher *matcher, struct scopes scopes)
|
||||
{
|
||||
size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc);
|
||||
matcher_init_matching_inner(ctx, matcher);
|
||||
matcher_init_matching_inner(ctx, matcher, scopes);
|
||||
apfl_gc_tmproots_restore(&ctx->gc, tmproots);
|
||||
}
|
||||
|
||||
|
|
@ -978,6 +1026,63 @@ matcher_leave_list(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
|
|||
return matcher_next(ctx, cse);
|
||||
}
|
||||
|
||||
static void
|
||||
matcher_transfer(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
|
||||
{
|
||||
for (size_t i = 0; i < cse->capture_count; i++) {
|
||||
struct matcher_capture_transfer transfer = cse->transfers[i];
|
||||
if (transfer.path_len == 0) {
|
||||
variable_set_value(ctx, &cse->scopes, transfer.var, transfer.local, cse->captures[i]);
|
||||
} else {
|
||||
// Set the value at a key path in a (nested) dictionary.
|
||||
|
||||
variable_get(ctx, cse->scopes, transfer.var, false);
|
||||
if (apfl_get_type(ctx, -1) != APFL_VALUE_DICT) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_dict);
|
||||
}
|
||||
|
||||
|
||||
// Get or create intermediary dictionaries along the key path and leave a copy of the previous one on the
|
||||
// stack, so we can set the result in reverse order there later.
|
||||
for (size_t i = 0; i < transfer.path_len - 1; i++) {
|
||||
apfl_copy(ctx, -1);
|
||||
|
||||
size_t value_index = transfer.path_start + i;
|
||||
matcher_check_index(ctx, cse->matcher->value_count, value_index);
|
||||
apfl_stack_must_push(ctx, apfl_value_set_cow_flag(cse->matcher->values[value_index]));
|
||||
if (apfl_get_member_if_exists(ctx, -2, -1)) {
|
||||
if (apfl_get_type(ctx, -1) != APFL_VALUE_DICT) {
|
||||
apfl_raise_const_error(ctx, APFL_RESULT_ERR, apfl_messages.not_a_dict);
|
||||
}
|
||||
} else {
|
||||
apfl_dict_create(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the value to the rightmost dictionary key
|
||||
size_t value_index = transfer.path_start + transfer.path_len - 1;
|
||||
matcher_check_index(ctx, cse->matcher->value_count, value_index);
|
||||
apfl_stack_must_push(ctx, apfl_value_set_cow_flag(cse->matcher->values[value_index]));
|
||||
apfl_stack_must_push(ctx, cse->captures[i]);
|
||||
apfl_dict_set(ctx, -3, -2, -1);
|
||||
|
||||
// Go through the key path (minus the rightmost key) in reverse order and set the value in the intermediary
|
||||
// dictionaries. Note that i has a offset of one here so we can use the i > 0 check in the loop (>= 0 would
|
||||
// not work as size_t is unsigned).
|
||||
for (size_t i = transfer.path_len - 1; i > 0; i--) {
|
||||
size_t value_index = transfer.path_start + i - 1;
|
||||
matcher_check_index(ctx, cse->matcher->value_count, value_index);
|
||||
apfl_stack_must_push(ctx, apfl_value_set_cow_flag(cse->matcher->values[value_index]));
|
||||
|
||||
apfl_dict_set(ctx, -3, -1, -2);
|
||||
}
|
||||
|
||||
// Finally set the copied and modified dictionary to the variable again.
|
||||
variable_set(ctx, &cse->scopes, transfer.var, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
return_from_matcher(apfl_ctx ctx, bool result)
|
||||
{
|
||||
|
|
@ -985,12 +1090,27 @@ return_from_matcher(apfl_ctx ctx, bool result)
|
|||
assert(cse != NULL);
|
||||
assert(cse->type == CSE_MATCHER);
|
||||
|
||||
cse->matcher.matcher->result = result;
|
||||
if (result) {
|
||||
matcher_transfer(ctx, &cse->matcher);
|
||||
}
|
||||
|
||||
call_stack_drop(ctx);
|
||||
|
||||
cse = apfl_call_stack_cur_entry(ctx);
|
||||
assert(cse != NULL);
|
||||
assert(cse->type == CSE_FUNCTION || cse->type == CSE_FUNCTION_DISPATCH);
|
||||
|
||||
switch (cse->type) {
|
||||
case CSE_FUNCTION:
|
||||
cse->func.returning_from_matcher = true;
|
||||
cse->func.matcher_result = result;
|
||||
break;
|
||||
case CSE_FUNCTION_DISPATCH:
|
||||
cse->func_dispatch.returning_from_matcher = true;
|
||||
cse->func_dispatch.matcher_result = result;
|
||||
break;
|
||||
default:
|
||||
assert(false /* Invalid stack entry below matcher stack */);
|
||||
}
|
||||
}
|
||||
|
||||
#define RETURN_WITHOUT_MATCH(ctx) \
|
||||
|
|
@ -1006,6 +1126,53 @@ return_from_matcher(apfl_ctx ctx, bool result)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
static bool
|
||||
matcher_evaluate_capturing_instruction(
|
||||
apfl_ctx ctx,
|
||||
struct matcher_call_stack_entry *cse,
|
||||
bool local,
|
||||
bool with_path
|
||||
) {
|
||||
size_t *pc = &cse->pc;
|
||||
struct matcher *matcher = cse->matcher;
|
||||
struct matcher_instruction_list *milist = matcher->instructions;
|
||||
|
||||
struct matcher_capture_transfer transfer = {
|
||||
.var = NULL,
|
||||
.path_start = 0,
|
||||
.path_len = 0,
|
||||
.local = local,
|
||||
};
|
||||
|
||||
union matcher_instruction_or_arg arg;
|
||||
|
||||
must_get_matcher_argument(ctx, pc, milist, &arg);
|
||||
transfer.var = arg.string;
|
||||
|
||||
if (with_path) {
|
||||
must_get_matcher_argument(ctx, pc, milist, &arg);
|
||||
transfer.path_start = arg.index;
|
||||
must_get_matcher_argument(ctx, pc, milist, &arg);
|
||||
transfer.path_len = arg.index;
|
||||
}
|
||||
|
||||
struct apfl_value cur;
|
||||
|
||||
if (!matcher_current_val(ctx, cse, &cur)) {
|
||||
return_from_matcher(ctx, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t capture = cse->capture_index++;
|
||||
matcher_check_index(ctx, milist->capture_count, capture);
|
||||
|
||||
cse->captures[capture] = apfl_value_set_cow_flag(cur);
|
||||
cse->transfers[capture] = transfer;
|
||||
|
||||
return matcher_next(ctx, cse);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
|
||||
{
|
||||
|
|
@ -1019,14 +1186,29 @@ evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
|
|||
struct apfl_value cur;
|
||||
|
||||
switch (milist->instructions[(*pc)++].instruction) {
|
||||
case MATCHER_CAPTURE:
|
||||
if (!matcher_current_val(ctx, cse, &cur)) {
|
||||
RETURN_WITHOUT_MATCH(ctx);
|
||||
}
|
||||
must_get_matcher_argument(ctx, pc, milist, &arg);
|
||||
matcher_check_index(ctx, milist->capture_count, arg.index);
|
||||
matcher->captures[arg.index] = apfl_value_set_cow_flag(cur);
|
||||
RETURN_WITHOUT_MATCH_ON_FALSE(ctx, matcher_next(ctx, cse));
|
||||
case MATCHER_CAPTURE_TO_VAR:
|
||||
RETURN_WITHOUT_MATCH_ON_FALSE(
|
||||
ctx,
|
||||
matcher_evaluate_capturing_instruction(ctx, cse, false, false)
|
||||
);
|
||||
goto continue_loop;
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL:
|
||||
RETURN_WITHOUT_MATCH_ON_FALSE(
|
||||
ctx,
|
||||
matcher_evaluate_capturing_instruction(ctx, cse, true, false)
|
||||
);
|
||||
goto continue_loop;
|
||||
case MATCHER_CAPTURE_TO_VAR_WITH_PATH:
|
||||
RETURN_WITHOUT_MATCH_ON_FALSE(
|
||||
ctx,
|
||||
matcher_evaluate_capturing_instruction(ctx, cse, false, true)
|
||||
);
|
||||
goto continue_loop;
|
||||
case MATCHER_CAPTURE_TO_VAR_LOCAL_WITH_PATH:
|
||||
RETURN_WITHOUT_MATCH_ON_FALSE(
|
||||
ctx,
|
||||
matcher_evaluate_capturing_instruction(ctx, cse, true, true)
|
||||
);
|
||||
goto continue_loop;
|
||||
case MATCHER_IGNORE:
|
||||
if (!matcher_current_val(ctx, cse, &cur)) {
|
||||
|
|
@ -1075,6 +1257,16 @@ evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse)
|
|||
);
|
||||
}
|
||||
|
||||
struct matcher_stack
|
||||
matcher_stack_new(void)
|
||||
{
|
||||
return (struct matcher_stack) {
|
||||
.items = NULL,
|
||||
.len = 0,
|
||||
.cap = 0,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch(apfl_ctx ctx, struct call_stack_entry *cse)
|
||||
{
|
||||
|
|
@ -1084,19 +1276,20 @@ dispatch(apfl_ctx ctx, struct call_stack_entry *cse)
|
|||
struct function *function = fd_cse->function;
|
||||
|
||||
if (fd_cse->returning_from_matcher) {
|
||||
struct subfunction *subfunction = &function->subfunctions[fd_cse->subfunc];
|
||||
if (subfunction->matcher->result) {
|
||||
if (fd_cse->matcher_result) {
|
||||
struct subfunction *subfunction = &function->subfunctions[fd_cse->subfunc];
|
||||
|
||||
// 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,
|
||||
.scopes = fd_cse->scopes,
|
||||
.execution_line = subfunction->body->line,
|
||||
.matcher = subfunction->matcher,
|
||||
.matcher_stack = matcher_stack_new(),
|
||||
.returning_from_matcher = false,
|
||||
.matcher_result = false,
|
||||
};
|
||||
|
||||
return;
|
||||
|
|
@ -1110,8 +1303,14 @@ dispatch(apfl_ctx ctx, struct call_stack_entry *cse)
|
|||
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);
|
||||
// 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);
|
||||
matcher_init_matching(
|
||||
ctx,
|
||||
function->subfunctions[fd_cse->subfunc].matcher,
|
||||
fd_cse->scopes
|
||||
);
|
||||
}
|
||||
|
||||
struct apfl_iterative_runner_data {
|
||||
|
|
@ -1149,11 +1348,13 @@ iterative_runner_eval_expr_inner(apfl_iterative_runner runner, struct apfl_expr
|
|||
.func = {
|
||||
.pc = 0,
|
||||
.instructions = ilist,
|
||||
.scope = runner->scope,
|
||||
.closure_scope = NULL,
|
||||
.scopes = {
|
||||
.local = runner->scope,
|
||||
.closure = NULL,
|
||||
},
|
||||
.execution_line = ilist->line,
|
||||
.matcher_stack = matcher_stack_new(),
|
||||
.returning_from_matcher = false,
|
||||
.matcher = NULL,
|
||||
},
|
||||
});
|
||||
evaluate_until_call_stack_return(ctx);
|
||||
|
|
|
|||
78
src/functional-tests/dictionary-assignments.at
Normal file
78
src/functional-tests/dictionary-assignments.at
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
===== script =====
|
||||
foo = [->]
|
||||
foo.a = 1
|
||||
print foo.a
|
||||
|
||||
print ""
|
||||
|
||||
# Like every value in apfl, dictionaries are copied when passed around.
|
||||
# Let's check if this semantic works by copying the dictionary and then modify
|
||||
# the copy.
|
||||
bar = foo
|
||||
bar.a = 2
|
||||
print foo.a
|
||||
print bar.a
|
||||
|
||||
print ""
|
||||
|
||||
# Same thing with passing as argument
|
||||
{ d ->
|
||||
d.a = 3
|
||||
print d.a
|
||||
} foo
|
||||
print foo.a
|
||||
|
||||
print ""
|
||||
|
||||
# And the other way around
|
||||
|
||||
printcopy = { d ->
|
||||
{print d.a}
|
||||
} foo
|
||||
foo.a = 4
|
||||
print foo.a
|
||||
(printcopy)
|
||||
|
||||
print ""
|
||||
|
||||
# What about nested element access?
|
||||
foo.x@(+ 1 2).y = "bar"
|
||||
print foo.x
|
||||
|
||||
print ""
|
||||
|
||||
# And as part of List destructuring
|
||||
k := 10
|
||||
l = [a foo.zzz@k] = [10 ({ k = 20 })]
|
||||
print k
|
||||
print l
|
||||
print a
|
||||
print foo.zzz
|
||||
|
||||
===== output =====
|
||||
1
|
||||
|
||||
1
|
||||
2
|
||||
|
||||
3
|
||||
1
|
||||
|
||||
4
|
||||
1
|
||||
|
||||
[
|
||||
3 -> [
|
||||
"y" -> "bar"
|
||||
]
|
||||
]
|
||||
|
||||
20
|
||||
[
|
||||
10
|
||||
20
|
||||
]
|
||||
10
|
||||
[
|
||||
10 -> 20
|
||||
]
|
||||
|
|
@ -13,6 +13,21 @@ print "Outside:"
|
|||
print foo
|
||||
print bar
|
||||
|
||||
print ""
|
||||
|
||||
# Do the same thing but now use complex assignables. They are compiled into different bytecode, so let's test this too
|
||||
({
|
||||
[~_ foo] = [3]
|
||||
[~_ bar] := [3]
|
||||
|
||||
print "Inside:"
|
||||
print foo
|
||||
print bar
|
||||
})
|
||||
print "Outside:"
|
||||
print foo
|
||||
print bar
|
||||
|
||||
===== output =====
|
||||
Inside:
|
||||
2
|
||||
|
|
@ -20,3 +35,10 @@ Inside:
|
|||
Outside:
|
||||
2
|
||||
1
|
||||
|
||||
Inside:
|
||||
3
|
||||
3
|
||||
Outside:
|
||||
3
|
||||
1
|
||||
|
|
|
|||
4
src/gc.c
4
src/gc.c
|
|
@ -5,8 +5,8 @@
|
|||
#include "alloc.h"
|
||||
#include "bytecode.h"
|
||||
#include "context.h"
|
||||
#include "format.h"
|
||||
#include "gc.h"
|
||||
#include "matcher.h"
|
||||
#include "resizable.h"
|
||||
#include "scope.h"
|
||||
#include "value.h"
|
||||
|
|
@ -287,7 +287,7 @@ visit_children(struct gc_object *object, gc_visitor cb, void *opaque)
|
|||
apfl_gc_cfunc_traverse(&object->cfunction, cb, opaque);
|
||||
return;
|
||||
case GC_TYPE_MATCHER_INSTRUCTIONS:
|
||||
// Intentionally left blank. Object doesn't reference other objects.
|
||||
apfl_gc_matcher_instructions_traverse(&object->matcher_instructions, cb, opaque);
|
||||
return;
|
||||
case GC_TYPE_MATCHER:
|
||||
apfl_gc_matcher_traverse(&object->matcher, cb, opaque);
|
||||
|
|
|
|||
|
|
@ -1,83 +0,0 @@
|
|||
#include "alloc.h"
|
||||
#include "gc.h"
|
||||
#include "matcher.h"
|
||||
|
||||
static bool
|
||||
init_values_list(struct apfl_allocator allocator, struct apfl_value **list, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((*list = ALLOC_LIST(allocator, struct apfl_value, len)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
(*list)[i] = (struct apfl_value) { .type = VALUE_NIL };
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct matcher *
|
||||
apfl_matcher_new(struct gc *gc, struct matcher_instruction_list *milist)
|
||||
{
|
||||
struct matcher matcher = {
|
||||
.instructions = milist,
|
||||
.value_count = 0,
|
||||
.capture_count = 0,
|
||||
.values = NULL,
|
||||
.captures = NULL,
|
||||
.result = false,
|
||||
};
|
||||
|
||||
if (!init_values_list(gc->allocator, &matcher.values, milist->value_count)) {
|
||||
goto error;
|
||||
}
|
||||
matcher.value_count = milist->value_count;
|
||||
|
||||
if (!init_values_list(gc->allocator, &matcher.captures, milist->capture_count)) {
|
||||
goto error;
|
||||
}
|
||||
matcher.capture_count = milist->capture_count;
|
||||
|
||||
struct matcher *gc_matcher = apfl_gc_new_matcher(gc);
|
||||
if (gc_matcher == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
*gc_matcher = matcher;
|
||||
|
||||
return gc_matcher;
|
||||
|
||||
error:
|
||||
apfl_matcher_deinit(gc->allocator, &matcher);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_matcher_deinit(struct apfl_allocator allocator, struct matcher *matcher)
|
||||
{
|
||||
FREE_LIST(allocator, matcher->values, matcher->value_count);
|
||||
FREE_LIST(allocator, matcher->captures, matcher->capture_count);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_gc_matcher_traverse(struct matcher *matcher, gc_visitor visitor, void *opaque)
|
||||
{
|
||||
visitor(opaque, GC_OBJECT_FROM(matcher->instructions, GC_TYPE_MATCHER_INSTRUCTIONS));
|
||||
|
||||
for (size_t i = 0; i < matcher->instructions->value_count; i++) {
|
||||
struct gc_object *object = apfl_value_get_gc_object(matcher->values[i]);
|
||||
if (object != NULL) {
|
||||
visitor(opaque, object);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < matcher->instructions->capture_count; i++) {
|
||||
struct gc_object *object = apfl_value_get_gc_object(matcher->captures[i]);
|
||||
if (object != NULL) {
|
||||
visitor(opaque, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef APFL_MATCHER_H
|
||||
#define APFL_MATCHER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "bytecode.h"
|
||||
#include "gc.h"
|
||||
#include "value.h"
|
||||
|
||||
struct matcher {
|
||||
struct matcher_instruction_list *instructions;
|
||||
size_t value_count;
|
||||
size_t capture_count;
|
||||
struct apfl_value *values;
|
||||
struct apfl_value *captures;
|
||||
bool result;
|
||||
};
|
||||
|
||||
struct matcher *apfl_matcher_new(struct gc *, struct matcher_instruction_list *);
|
||||
void apfl_matcher_deinit(struct apfl_allocator, struct matcher *);
|
||||
void apfl_gc_matcher_traverse(struct matcher *, gc_visitor, void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -65,10 +65,7 @@ apfl_scope_new(struct gc *gc)
|
|||
void
|
||||
apfl_gc_var_traverse(struct apfl_value *var, gc_visitor cb, void *opaque)
|
||||
{
|
||||
struct gc_object *child = apfl_value_get_gc_object(*var);
|
||||
if (child != NULL) {
|
||||
cb(opaque, child);
|
||||
}
|
||||
apfl_value_visit_gc_object(*var, cb, opaque);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
10
src/value.c
10
src/value.c
|
|
@ -661,8 +661,8 @@ apfl_value_get_gc_object(struct apfl_value value)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
call_visitor_for_value(gc_visitor cb, void *opaque, struct apfl_value value)
|
||||
void
|
||||
apfl_value_visit_gc_object(struct apfl_value value, gc_visitor cb, void *opaque)
|
||||
{
|
||||
struct gc_object *child = apfl_value_get_gc_object(value);
|
||||
if (child != NULL) {
|
||||
|
|
@ -674,7 +674,7 @@ void
|
|||
apfl_gc_list_traverse(struct list_header *list, gc_visitor cb, void *opaque)
|
||||
{
|
||||
for (size_t i = 0; i < list->len; i++) {
|
||||
call_visitor_for_value(cb, opaque, list->items[i]);
|
||||
apfl_value_visit_gc_object(list->items[i], cb, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -687,8 +687,8 @@ apfl_gc_dict_traverse(struct dict_header *dict, gc_visitor cb, void *opaque)
|
|||
struct apfl_value *v = apfl_hashmap_cursor_peek_value(cur);
|
||||
assert(v != NULL);
|
||||
|
||||
call_visitor_for_value(cb, opaque, *k);
|
||||
call_visitor_for_value(cb, opaque, *v);
|
||||
apfl_value_visit_gc_object(*k, cb, opaque);
|
||||
apfl_value_visit_gc_object(*v, cb, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ void apfl_cfunction_deinit(struct apfl_allocator, struct cfunction *);
|
|||
|
||||
// Functions for garbage collection
|
||||
struct gc_object *apfl_value_get_gc_object(struct apfl_value);
|
||||
void apfl_value_visit_gc_object(struct apfl_value value, gc_visitor cb, void *opaque);
|
||||
void apfl_gc_list_traverse(struct list_header *, gc_visitor, void *);
|
||||
void apfl_gc_dict_traverse(struct dict_header *, gc_visitor, void *);
|
||||
void apfl_gc_func_traverse(struct function*, gc_visitor, void *);
|
||||
|
|
|
|||
Loading…
Reference in a new issue