174 lines
4.2 KiB
C
174 lines
4.2 KiB
C
|
|
#include <assert.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
|
||
|
|
#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, "<toplevel>");
|
||
|
|
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);
|
||
|
|
}
|