apfl/src/apflc.c

277 lines
6.6 KiB
C
Raw Normal View History

#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "apfl.h"
#include "bytecode.h"
#include "compile.h"
#include "format.h"
#include "gc.h"
#include "strings.h"
struct c_writer_data {
struct apfl_io_writer w;
size_t written_len;
};
static const unsigned char hex[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
};
static bool
write_as_c_source(void *opaque, const unsigned char *buf, size_t len)
{
struct c_writer_data *data = opaque;
for (size_t i = 0; i < len; i++) {
if (data->written_len % 16 == 0) {
if (!apfl_io_write_byte(data->w, '\n')) {
return false;
}
}
unsigned char out[] = {'0', 'x', hex[(buf[i] >> 4) & 0x0F], hex[buf[i] & 0x0F], ','};
if (!apfl_io_write_string_view(
data->w,
(struct apfl_string_view) { .bytes = out, .len = sizeof(out), }
)) {
return false;
}
(data->written_len)++;
}
return true;
}
static bool
c_source_prelude(struct apfl_io_writer w)
{
return apfl_io_write_string(
w,
"#include <stddef.h>\n"
// TODO: This is a bit hackish, the generated C code should include
// apfl.h instead of declaring the string view struct itself. However,
// since the code is generated, it does not live in the same directory
// as apfl.h, so our usual `#include "apfl.h"` doesn't work.
"struct apfl_string_view {\n"
" const unsigned char *bytes;\n"
" size_t len;\n"
"};\n"
"static const unsigned char bytecode[] = {"
);
}
static bool
c_source_epilogue(struct apfl_io_writer w, const char *function_name)
{
return apfl_io_write_string(w,
"\n};\n"
"struct apfl_string_view\n"
)
&& apfl_io_write_string(w, function_name)
&& apfl_io_write_string(w,
"(void)\n"
"{\n"
" return (struct apfl_string_view) {\n"
" .bytes = bytecode,\n"
" .len = sizeof(bytecode),\n"
" };\n"
"}\n"
);
}
struct roots {
struct instruction_list *ilist;
struct apfl_string *name;
};
static void
getroots(void *own_opaque, gc_visitor visitor, void *visitor_opaque)
{
struct roots *roots = own_opaque;
if (roots->ilist != NULL) {
visitor(visitor_opaque, GC_OBJECT_FROM(roots->ilist, GC_TYPE_INSTRUCTIONS));
}
if (roots->name != NULL) {
visitor(visitor_opaque, GC_OBJECT_FROM(roots->name, GC_TYPE_STRING));
}
}
static const char *
get_next_string_from_arg(const char *progname, const char ***argpp)
{
if (**argpp == NULL) {
fprintf(stderr, "Usage: %s [-c function_name] input output\n", progname);
return NULL;
}
const char *filename = **argpp;
(*argpp)++;
return filename;
}
static FILE *
openfile(const char *progname, const char *filename, const char *mode)
{
FILE *f = fopen(filename, mode);
if (f == NULL) {
fprintf(stderr, "%s: Could not open %s: %s\n", progname, filename, strerror(errno));
return NULL;
}
return f;
}
static void
closefile(FILE *f)
{
if (f != NULL) {
fclose(f);
}
}
static bool
compile(
const char *progname,
struct gc *gc,
struct apfl_io_reader r,
struct instruction_list *ilist
) {
apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(gc->allocator, apfl_io_reader_as_source_reader(&r));
if (tokenizer == NULL) {
return false;
}
apfl_parser_ptr parser = apfl_parser_new(
gc->allocator,
apfl_tokenizer_as_token_source(tokenizer)
);
if (parser == NULL) {
apfl_tokenizer_destroy(tokenizer);
fprintf(stderr, "%s: Failed to init parser\n", progname);
return false;
}
struct apfl_error error;
if (!apfl_compile_whole_file(
gc,
parser,
&error,
ilist
)) {
fprintf(stderr, "%s: Could not compile file:\n", progname);
apfl_error_print(error, stderr);
apfl_parser_destroy(parser);
apfl_tokenizer_destroy(tokenizer);
return false;
}
apfl_parser_destroy(parser);
apfl_tokenizer_destroy(tokenizer);
return true;
}
int
main(int argc, const char *argv[])
{
(void)argc;
const char **argp = argv+1;
FILE *in = NULL;
FILE *out = NULL;
const char *c_function_name = NULL;
if (*argp != NULL && apfl_string_eq(*argp, "-c")) {
argp++;
if ((c_function_name = get_next_string_from_arg(argv[0], &argp)) == NULL) {
goto error_before_gc;
}
}
const char *name_in = NULL;
const char *name_out = NULL;
if (
(name_in = get_next_string_from_arg(argv[0], &argp)) == NULL
|| (name_out = get_next_string_from_arg(argv[0], &argp)) == NULL
) {
goto error_before_gc;
}
int rv = 1;
if (
(in = openfile(argv[0], name_in, "rb")) == NULL
|| (out = openfile(argv[0], name_out, "wb")) == NULL
) {
goto error_before_gc;
}
struct apfl_io_reader r = apfl_io_file_reader(in);
struct apfl_io_writer w_raw = apfl_io_file_writer(out);
struct roots roots = {
.ilist = NULL,
.name = NULL,
};
struct gc gc;
apfl_gc_init(&gc, apfl_allocator_default(), getroots, &roots);
if ((roots.name = apfl_string_copy_to_new_gc_string(
&gc,
apfl_string_view_from(name_in)
)) == NULL) {
goto error;
}
if ((roots.ilist = apfl_instructions_new(&gc, 1, roots.name)) == NULL) {
goto error;
}
if (!compile(argv[0], &gc, r, roots.ilist)) {
goto error;
}
if (c_function_name != NULL && !c_source_prelude(w_raw)) {
fprintf(stderr, "%s: IO error\n", argv[0]);
goto error;
}
struct apfl_io_writer bytecode_writer = w_raw;
struct c_writer_data c_writer_data = {
.w = w_raw,
.written_len = 0,
};
if (c_function_name != NULL) {
bytecode_writer = (struct apfl_io_writer) {
.opaque = &c_writer_data,
.write = write_as_c_source,
};
}
if (!apfl_bytecode_serialize(
gc.allocator,
bytecode_writer,
roots.ilist
)) {
fprintf(stderr, "%s: Could not serialize bytecode\n", argv[0]);
goto error;
}
if (c_function_name != NULL && !c_source_epilogue(w_raw, c_function_name)) {
fprintf(stderr, "%s: IO error\n", argv[0]);
goto error;
}
rv = 0;
error:
apfl_gc_deinit(&gc);
error_before_gc:
closefile(in);
closefile(out);
return rv;
}