#include #include #include #include #include "alloc.h" #include "repl.h" struct repl_data { apfl_ctx ctx; struct apfl_allocator alloc; char *buf; size_t buf_len; size_t buf_cap; struct apfl_io_writer w_out; struct apfl_io_writer w_err; }; static bool null_writer(void *opaque, const unsigned char *buf, size_t len) { (void)opaque; (void)buf; (void)len; return true; } static bool redirect_writer(void *opaque, const unsigned char *buf, size_t len) { repl r = opaque; return r->w_out.write(r->w_out.opaque, buf, len); } repl repl_new(struct apfl_allocator alloc) { repl r = ALLOC_OBJ(alloc, struct repl_data); if (r == NULL) { return NULL; } r->alloc = alloc; r->buf = NULL; r->buf_len = 0; r->buf_cap = 0; r->w_out = (struct apfl_io_writer){.write = null_writer}; r->w_err = (struct apfl_io_writer){.write = null_writer}; r->ctx = apfl_ctx_new((struct apfl_config) { .allocator = alloc, .output_writer = (struct apfl_io_writer){.write = redirect_writer, .opaque = r}, }); if (r->ctx == NULL) { repl_destroy(r); return NULL; } return r; } void repl_set_w_out(repl r, struct apfl_io_writer w) { r->w_out = w; } void repl_set_w_err(repl r, struct apfl_io_writer w) { r->w_err = w; } static void protected_callback(apfl_ctx ctx, void *opaque) { (void)opaque; apfl_run_in_top_scope(ctx, -1); } static enum repl_result report_error(repl r, struct apfl_error error) { apfl_error_print(error, r->w_err); return (error.type == APFL_ERR_MALLOC_FAILED || error.type == APFL_ERR_INPUT_ERROR) ? REPL_FATAL : REPL_ERR; } enum repl_result repl_run(repl r, char *input) { if (input != NULL) { size_t input_len = strlen(input); size_t needed = r->buf_len + input_len; if (r->buf_cap < needed) { char *newbuf = REALLOC_BYTES(r->alloc, r->buf, r->buf_cap, needed); if (newbuf == NULL) { return REPL_FATAL; } r->buf = newbuf; r->buf_cap = needed; } memcpy(r->buf + r->buf_len, input, input_len); r->buf_len += input_len; } struct apfl_io_string_reader_data sr = apfl_io_string_reader_create( (struct apfl_string_view) { (unsigned char *)r->buf, r->buf_len, } ); struct apfl_error error; apfl_push_const_string(r->ctx, ""); if (!apfl_load_try(r->ctx, apfl_io_string_reader(&sr), -1, &error)) { if ( error.type == APFL_ERR_UNEXPECTED_EOF || error.type == APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN ) { if (input == NULL) { (void)report_error(r, error); return REPL_FATAL; } else { return REPL_MORE_INPUT; } } else { return report_error(r, error); } } r->buf_len = 0; switch (apfl_do_protected(r->ctx, protected_callback, NULL, apfl_error_decorate_with_backtrace)) { case APFL_RESULT_OK : if (apfl_get_type(r->ctx, -1) == APFL_VALUE_NIL) { apfl_drop(r->ctx, -1); } else { apfl_debug_print_val(r->ctx, -1, r->w_out); } return REPL_OK; case APFL_RESULT_ERR: apfl_io_write_string(r->w_err, "Error occurred during evaluation:\n"); if (apfl_get_type(r->ctx, -1) == APFL_VALUE_STRING) { apfl_io_write_string(r->w_err, apfl_get_string(r->ctx, -1)); apfl_drop(r->ctx, -1); } else { apfl_debug_print_val(r->ctx, -1, r->w_err); } apfl_io_write_byte(r->w_err, '\n'); return REPL_ERR; case APFL_RESULT_ERRERR: apfl_io_write_string(r->w_err, "Error occurred during error handling.\n"); return REPL_FATAL; case APFL_RESULT_ERR_ALLOC: apfl_io_write_string(r->w_err, "Fatal: Could not allocate memory.\n"); return REPL_FATAL; } assert(false && "unreachable"); return REPL_FATAL; } void repl_destroy(repl r) { apfl_ctx_destroy(r->ctx); FREE_BYTES(r->alloc, r->buf, r->buf_cap); FREE_OBJ(r->alloc, r); }