apfl/src/bytecode.c
Laria Carolin Chabowski 41d0ee6132 bytecode: Add a function to dump bytecode
This also exposes it as a global function and removes the debug statements
in the compiler.
2022-07-28 20:49:29 +02:00

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);
}