apfl/src/apflc.c

242 lines
6.1 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"
);
}
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);
}
}
struct protected_compiling_data {
const char *name;
struct apfl_io_reader *r;
struct apfl_io_writer *w;
};
static void
protected_compiling(apfl_ctx ctx, void *opaque)
{
struct protected_compiling_data *data = opaque;
apfl_push_const_string(ctx, data->name);
apfl_load(ctx, *data->r, -1);
apfl_bytecode_save(ctx, *data->w, -1);
}
#define MAIN_TRY_FMT(x) if (!(x)) { goto error; }
int
main(int argc, const char *argv[])
{
(void)argc;
const char **argp = argv+1;
int rv = 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_ctx;
}
}
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_ctx;
}
if (
(in = openfile(argv[0], name_in, "rb")) == NULL
|| (out = openfile(argv[0], name_out, "wb")) == NULL
) {
goto error_before_ctx;
}
struct apfl_io_reader r = apfl_io_file_reader(in);
struct apfl_io_writer w_raw = apfl_io_file_writer(out);
if (c_function_name != NULL && !c_source_prelude(w_raw)) {
fprintf(stderr, "%s: IO error\n", argv[0]);
goto error_before_ctx;
}
struct apfl_io_writer w_err = apfl_io_file_writer(stderr);
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,
};
}
apfl_ctx ctx = apfl_ctx_new((struct apfl_config) {
.allocator = apfl_allocator_default(),
.output_writer = apfl_io_file_writer(stdout),
.no_standard_modules = true,
});
if (ctx == NULL) {
fprintf(stderr, "Could not init context\n");
goto error;
}
struct protected_compiling_data data = {
.name = name_in,
.r = &r,
.w = &bytecode_writer,
};
switch (apfl_do_protected(ctx, protected_compiling, &data, NULL)) {
case APFL_RESULT_OK :
break;
case APFL_RESULT_ERR:
MAIN_TRY_FMT(apfl_io_write_string(w_err, "Error occurred during compilation:\n"));
if (apfl_get_type(ctx, -1) == APFL_VALUE_STRING) {
MAIN_TRY_FMT(apfl_io_write_string(w_err, apfl_get_string(ctx, -1)));
} else {
MAIN_TRY_FMT(apfl_debug_print_val(ctx, -1, w_err));
}
MAIN_TRY_FMT(apfl_io_write_byte(w_err, '\n'));
break;
case APFL_RESULT_ERRERR:
MAIN_TRY_FMT(apfl_io_write_string(w_err, "Error occurred during error handling.\n"));
break;
case APFL_RESULT_ERR_ALLOC:
MAIN_TRY_FMT(apfl_io_write_string(w_err, "Fatal: Could not allocate memory.\n"));
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_ctx_destroy(ctx);
error_before_ctx:
closefile(in);
closefile(out);
return rv;
}