This also exposes it as a global function and removes the debug statements in the compiler.
339 lines
10 KiB
C
339 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:
|
|
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:
|
|
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_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);
|
|
}
|
|
|
|
static bool
|
|
bytecode_dump_milist(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)
|
|
|
|
static bool
|
|
bytecode_dump_ilist(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:
|
|
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:
|
|
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
|
|
FMT_TRY(apfl_format_put_string(w, " ilist{\n"));
|
|
FMT_TRY(bytecode_dump_ilist(indent+1, w, arg.body));
|
|
FMT_TRY(apfl_format_put_indent(w, indent));
|
|
FMT_TRY(apfl_format_put_string(w, "}"));
|
|
break;
|
|
case INSN_MATCHER_LOAD:
|
|
GET_ARGUMENT_FOR_DUMP(w, ilist, i, arg);
|
|
FMT_TRY(apfl_format_put_string(w, " milist{\n"));
|
|
FMT_TRY(bytecode_dump_milist(indent+1, w, arg.matcher));
|
|
FMT_TRY(apfl_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;
|
|
}
|
|
|
|
bool
|
|
apfl_bytecode_dump(struct apfl_format_writer w, struct instruction_list *ilist)
|
|
{
|
|
return bytecode_dump_ilist(0, w, ilist);
|
|
}
|