#include #include #include #include #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 \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; }