apfl/src/bytecode.c
Laria Carolin Chabowski 8ca24bcd49 Implement subfunctions and argument matching
A function can now have multiple subfunctions with their own parameter
list. These parameters are now no longer constrained to variables and
blanks only, but can also be consts and list destructurings (predicates
are also already compiled but don't get evaluated yet). The first
subfunction that matches the argument list gets evaluated.
2022-08-13 00:50:26 +02:00

337 lines
10 KiB
C

#include <assert.h>
#include "apfl.h"
#include "alloc.h"
#include "bytecode.h"
#include "format.h"
#include "gc.h"
struct instruction_list *
apfl_instructions_new(struct gc *gc, int line)
{
struct instruction_list *ilist = apfl_gc_new_instructions(gc);
if (ilist == NULL) {
return NULL;
}
*ilist = (struct instruction_list) {
.instructions = NULL,
.len = 0,
.cap = 0,
.line = line,
};
return ilist;
}
void
apfl_instructions_deinit(struct apfl_allocator allocator, struct instruction_list *ilist)
{
FREE_LIST(allocator, ilist->instructions, ilist->cap);
}
#define GET_ARGUMENT(ilist, i, arg) \
do { \
if (i >= ilist->len) { \
return; \
} \
arg = ilist->instructions[++i]; \
} while (0)
void
apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, void *opaque)
{
union instruction_or_arg arg;
for (size_t i = 0; i < ilist->len; i++) {
switch (ilist->instructions[i].instruction) {
case INSN_NIL:
case INSN_TRUE:
case INSN_FALSE:
case INSN_LIST_APPEND:
case INSN_LIST_EXPAND_INTO:
case INSN_DICT:
case INSN_DICT_APPEND_KVPAIR:
case INSN_GET_MEMBER:
case INSN_NEXT_LINE:
case INSN_DROP:
case INSN_CALL:
case INSN_MATCHER_MUST_MATCH:
case INSN_MATCHER_DROP:
break;
case INSN_NUMBER:
case INSN_LIST:
case INSN_SET_LINE:
case INSN_GET_BY_INDEX_KEEP:
case INSN_MATCHER_SET_VAL:
case INSN_FUNC:
i++;
break;
case INSN_STRING:
case INSN_VAR_GET:
case INSN_VAR_SET:
case INSN_VAR_SET_LOCAL:
case INSN_VAR_NEW:
case INSN_VAR_NEW_LOCAL:
case INSN_MOVE_TO_LOCAL_VAR:
GET_ARGUMENT(ilist, i, arg);
cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING));
break;
case INSN_FUNC_ADD_SUBFUNC:
GET_ARGUMENT(ilist, i, arg);
cb(opaque, GC_OBJECT_FROM(arg.body, GC_TYPE_INSTRUCTIONS));
break;
case INSN_MATCHER_LOAD:
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);
cb(opaque, GC_OBJECT_FROM(arg.string, GC_TYPE_STRING));
i++;
break;
}
}
}
const char *
apfl_instruction_to_string(enum instruction insn)
{
switch (insn) {
case INSN_NIL:
return "INSN_NIL";
case INSN_TRUE:
return "INSN_TRUE";
case INSN_FALSE:
return "INSN_FALSE";
case INSN_NUMBER:
return "INSN_NUMBER";
case INSN_STRING:
return "INSN_STRING";
case INSN_LIST:
return "INSN_LIST";
case INSN_LIST_APPEND:
return "INSN_LIST_APPEND";
case INSN_LIST_EXPAND_INTO:
return "INSN_LIST_EXPAND_INTO";
case INSN_DICT:
return "INSN_DICT";
case INSN_DICT_APPEND_KVPAIR:
return "INSN_DICT_APPEND_KVPAIR";
case INSN_GET_MEMBER:
return "INSN_GET_MEMBER";
case INSN_VAR_GET:
return "INSN_VAR_GET";
case INSN_VAR_SET:
return "INSN_VAR_SET";
case INSN_VAR_SET_LOCAL:
return "INSN_VAR_SET_LOCAL";
case INSN_VAR_NEW:
return "INSN_VAR_NEW";
case INSN_VAR_NEW_LOCAL:
return "INSN_VAR_NEW_LOCAL";
case INSN_MOVE_TO_LOCAL_VAR:
return "INSN_MOVE_TO_LOCAL_VAR";
case INSN_NEXT_LINE:
return "INSN_NEXT_LINE";
case INSN_SET_LINE:
return "INSN_SET_LINE";
case INSN_GET_BY_INDEX_KEEP:
return "INSN_GET_BY_INDEX_KEEP";
case INSN_DROP:
return "INSN_DROP";
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_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 "??";
}
const char *
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_CHECK_CONST:
return "MATCHER_CHECK_CONST";
case MATCHER_CHECK_PRED:
return "MATCHER_CHECK_PRED";
case MATCHER_ENTER_LIST:
return "MATCHER_ENTER_LIST";
case MATCHER_LEAVE_LIST:
return "MATCHER_LEAVE_LIST";
case MATCHER_CONTINUE_FROM_END:
return "MATCHER_CONTINUE_FROM_END";
case MATCHER_REMAINDING:
return "MATCHER_REMAINDING";
}
return "??";
}
struct matcher_instruction_list *
apfl_matcher_instructions_new(struct gc *gc)
{
struct matcher_instruction_list *milist = apfl_gc_new_matcher_instructions(gc);
if (milist == NULL) {
return NULL;
}
*milist = (struct matcher_instruction_list) {
.instructions = NULL,
.len = 0,
.cap = 0,
.capture_count = 0,
.value_count = 0,
};
return milist;
}
void
apfl_matcher_instructions_deinit(struct apfl_allocator allocator, struct matcher_instruction_list *milist)
{
FREE_LIST(allocator, milist->instructions, milist->cap);
}
bool
apfl_bytecode_dump_matcher(unsigned indent, struct apfl_format_writer w, struct matcher_instruction_list *milist)
{
for (size_t i = 0; i < milist->len; i++) {
FMT_TRY(apfl_format_put_indent(w, indent));
FMT_TRY(apfl_format_put_string(w, apfl_matcher_instruction_to_string(milist->instructions[i].instruction)));
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_CAPTURE:
case MATCHER_CHECK_CONST:
case MATCHER_CHECK_PRED:
if (i >= milist->len) {
FMT_TRY(apfl_format_put_string(w, "Bytecode corrupted"));
return false;
}
FMT_TRY(apfl_format_put_string(w, " "));
FMT_TRY(apfl_format_put_int(w, (int)milist->instructions[++i].index));
break;
}
FMT_TRY(apfl_format_put_char(w, '\n'));
}
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)
{
union instruction_or_arg arg;
for (size_t i = 0; i < ilist->len; i++) {
FMT_TRY(apfl_format_put_indent(w, indent));
FMT_TRY(apfl_format_put_string(w, apfl_instruction_to_string(ilist->instructions[i].instruction)));
switch (ilist->instructions[i].instruction) {
case INSN_NIL:
case INSN_TRUE:
case INSN_FALSE:
case INSN_LIST_APPEND:
case INSN_LIST_EXPAND_INTO:
case INSN_DICT:
case INSN_DICT_APPEND_KVPAIR:
case INSN_GET_MEMBER:
case INSN_NEXT_LINE:
case INSN_DROP:
case INSN_CALL:
case INSN_MATCHER_MUST_MATCH:
case INSN_MATCHER_DROP:
break;
case INSN_NUMBER:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " "));
FMT_TRY(apfl_format_put_number(w, arg.number));
break;
case INSN_LIST:
case INSN_SET_LINE:
case INSN_FUNC:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " "));
FMT_TRY(apfl_format_put_int(w, (int)arg.count));
break;
case INSN_GET_BY_INDEX_KEEP:
case INSN_MATCHER_SET_VAL:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " "));
FMT_TRY(apfl_format_put_int(w, (int)arg.index));
break;
case INSN_STRING:
case INSN_VAR_GET:
case INSN_VAR_SET:
case INSN_VAR_SET_LOCAL:
case INSN_VAR_NEW:
case INSN_VAR_NEW_LOCAL:
case INSN_MOVE_TO_LOCAL_VAR:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " "));
FMT_TRY(apfl_format_put_string(w, *arg.string));
break;
case INSN_FUNC_ADD_SUBFUNC:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " ilist{\n"));
FMT_TRY(apfl_bytecode_dump(indent+1, w, arg.body));
FMT_TRY(apfl_format_put_indent(w, indent));
FMT_TRY(apfl_format_put_string(w, "}"));
break;
case INSN_MATCHER_LOAD:
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
FMT_TRY(apfl_format_put_string(w, " milist{\n"));
FMT_TRY(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'));
}
return true;
}