From 906ae3eac43afca0d4b3062a72a253aad15fec8e Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Tue, 7 Mar 2023 23:05:09 +0100 Subject: [PATCH] Make main slightly more sophisticated The main apfl program can now take a script file name as an argument to evaluate instead of stdin. In that case, the whole script is read at once and there are no `>` / `...` prompts then. The tokenizer and parser are still accessible but are now behind the `-T` / `-P` flags. --- src/apfl.h | 11 +++ src/eval.c | 6 +- src/main.c | 212 +++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 179 insertions(+), 50 deletions(-) diff --git a/src/apfl.h b/src/apfl.h index 83e0b53..3c81c7b 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -757,6 +757,17 @@ void *apfl_get_native_object(apfl_ctx, const struct apfl_native_object_type *typ void apfl_call(apfl_ctx, apfl_stackidx func, apfl_stackidx args); enum apfl_result apfl_call_protected(apfl_ctx, apfl_stackidx func, apfl_stackidx args); +enum apfl_result apfl_do_protected( + apfl_ctx ctx, + void (*callback)(apfl_ctx, void *), + void *opaque, + void (*errcallback)(apfl_ctx, void *) +); + +// errcallback for apfl_do_protected, that will turn the error into a string and +// decoreate it with a textual backtrace. +void apfl_error_decorate_with_backtrace(apfl_ctx ctx, void *ignored); + void apfl_load(apfl_ctx, struct apfl_source_reader, apfl_stackidx name); enum apfl_call_stack_entry_type { diff --git a/src/eval.c b/src/eval.c index ddfc1ec..e9ff98d 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1532,8 +1532,8 @@ iterative_runner_next_protected(apfl_ctx ctx, void *opaque) #define DECORATE_TRY_FMT(ctx, x) do { if (!(x)) { apfl_raise_alloc_error(ctx); } } while (0) -static void -iterative_runner_decorate_error(apfl_ctx ctx, void *opaque) +void +apfl_error_decorate_with_backtrace(apfl_ctx ctx, void *opaque) { (void)opaque; @@ -1591,7 +1591,7 @@ apfl_iterative_runner_next(apfl_iterative_runner runner) runner->ctx, iterative_runner_next_protected, runner, - iterative_runner_decorate_error + apfl_error_decorate_with_backtrace ); return runner->state == IRUNNER_OK; diff --git a/src/main.c b/src/main.c index 73e6012..e6c5509 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,24 +7,23 @@ #include "apfl.h" -enum repl_mode { - REPL_TOKENIZER, - REPL_PARSER, - REPL_EVAL, +enum main_mode { + MODE_TOKENIZER, + MODE_PARSER, + MODE_EVAL, }; static bool -repl_source_reader_cb(void *context, unsigned char *buf, size_t *len, bool need) +source_reader_without_prompt_cb(void *context, unsigned char *buf, size_t *len, bool need) { - (void)context; + (void)need; - printf(need ? "... " : "> "); - fflush(stdout); + FILE *f = context; size_t maxlen = *len; - if (fgets((char *)buf, maxlen, stdin) == NULL) { - if (feof(stdin)) { + if (fgets((char *)buf, maxlen, f) == NULL) { + if (feof(f)) { *len = 0; return true; } else { @@ -35,17 +35,26 @@ repl_source_reader_cb(void *context, unsigned char *buf, size_t *len, bool need) return true; } +static bool +source_reader_with_prompt_cb(void *context, unsigned char *buf, size_t *len, bool need) +{ + printf(need ? "... " : "> "); + return source_reader_without_prompt_cb(context, buf, len, need); +} + static struct apfl_source_reader -repl_source_reader() +build_source_reader(FILE *f, bool prompt) { return (struct apfl_source_reader) { - .callback = repl_source_reader_cb, - .opaque = NULL, + .callback = prompt + ? source_reader_with_prompt_cb + : source_reader_without_prompt_cb, + .opaque = f, }; } static int -repl_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) +run_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) { while (true) { struct apfl_error err; @@ -73,7 +82,7 @@ repl_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) } static int -repl_parser(struct apfl_allocator allocator, apfl_parser_ptr parser) +run_parser(struct apfl_allocator allocator, apfl_parser_ptr parser) { while (true) { struct apfl_expr expr; @@ -99,7 +108,8 @@ repl_parser(struct apfl_allocator allocator, apfl_parser_ptr parser) } } -static int repl_parser_or_tokenizer(enum repl_mode mode) +static int +parser_or_tokenizer(enum main_mode mode, struct apfl_source_reader source_reader) { int rv = 0; @@ -107,12 +117,12 @@ static int repl_parser_or_tokenizer(enum repl_mode mode) apfl_tokenizer_ptr tokenizer = NULL; apfl_parser_ptr parser = NULL; - if ((tokenizer = apfl_tokenizer_new(allocator, repl_source_reader())) == NULL) { + if ((tokenizer = apfl_tokenizer_new(allocator, source_reader)) == NULL) { fprintf(stderr, "Failed initializing tokenizer\n"); return 1; } - if (mode == REPL_PARSER) { + if (mode == MODE_PARSER) { if ((parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(tokenizer))) == NULL) { fprintf(stderr, "Failed initializing parser\n"); rv = 1; @@ -121,14 +131,14 @@ static int repl_parser_or_tokenizer(enum repl_mode mode) } switch (mode) { - case REPL_TOKENIZER: - rv = repl_tokenizer(allocator, tokenizer); + case MODE_TOKENIZER: + rv = run_tokenizer(allocator, tokenizer); break; - case REPL_PARSER: - rv = repl_parser(allocator, parser); + case MODE_PARSER: + rv = run_parser(allocator, parser); break; default: - assert(false /* repl_parser_or_tokenizer called with wrong mode */); + assert(false /* MODE_parser_or_tokenizer called with wrong mode */); } exit: @@ -138,9 +148,33 @@ exit: return rv; } -static int -repl_eval(void) +struct eval_whole_file_data { + const char *input_file_name; + struct apfl_source_reader source_reader; +}; + +static void +eval_whole_file(apfl_ctx ctx, void *opaque) { + struct eval_whole_file_data *data = opaque; + apfl_push_const_string(ctx, data->input_file_name == NULL ? "-" : data->input_file_name); + apfl_load(ctx, data->source_reader, -1); + apfl_list_create(ctx, 0); + apfl_call(ctx, -2, -1); + apfl_drop(ctx, -1); +} + +#define FMT_TRY(x) if (!(x)) { rv = 1; goto err; } + +static int +eval( + struct apfl_source_reader source_reader, + const char *input_file_name, + bool run_as_repl, + const char **argv +) { + (void)argv; + struct apfl_io_writer w_out = apfl_io_file_writer(stdout); struct apfl_io_writer w_err = apfl_io_file_writer(stderr); @@ -153,41 +187,125 @@ repl_eval(void) return 1; } - apfl_iterative_runner runner = apfl_iterative_runner_new(ctx, repl_source_reader()); - if (runner == NULL) { - apfl_ctx_destroy(ctx); - fprintf(stderr, "Failed to init runner\n"); - return 1; + int rv = 0; + if (run_as_repl) { + apfl_iterative_runner runner = apfl_iterative_runner_new(ctx, source_reader); + if (runner == NULL) { + apfl_ctx_destroy(ctx); + fprintf(stderr, "Failed to init runner\n"); + return 1; + } + + rv = apfl_iterative_runner_run_repl(runner, w_out, w_err) ? 0 : 1; + + apfl_iterative_runner_destroy(runner); + } else { + struct eval_whole_file_data data = { + .input_file_name = input_file_name, + .source_reader = source_reader, + }; + switch (apfl_do_protected(ctx, eval_whole_file, &data, apfl_error_decorate_with_backtrace)) { + case APFL_RESULT_OK : + break; + case APFL_RESULT_ERR: + FMT_TRY(apfl_io_write_string(w_err, "Error occurred during evaluation:\n")); + if (apfl_get_type(ctx, -1) == APFL_VALUE_STRING) { + FMT_TRY(apfl_io_write_string(w_err, apfl_get_string(ctx, -1))); + } else { + FMT_TRY(apfl_debug_print_val(ctx, -1, w_err)); + } + FMT_TRY(apfl_io_write_byte(w_err, '\n')); + break; + case APFL_RESULT_ERRERR: + FMT_TRY(apfl_io_write_string(w_err, "Error occurred during error handling.\n")); + break; + case APFL_RESULT_ERR_ALLOC: + FMT_TRY(apfl_io_write_string(w_err, "Fatal: Could not allocate memory.\n")); + return false; + } } - int rv = apfl_iterative_runner_run_repl(runner, w_out, w_err) ? 0 : 1; - - apfl_iterative_runner_destroy(runner); +err: apfl_ctx_destroy(ctx); return rv; } +static void +help(const char *name) +{ + fprintf(stderr, "usage: %s [-TPEhr] [] ...\n", name); + fprintf(stderr, "\n"); + fprintf(stderr, " -T Run in tokenizer mode\n"); + fprintf(stderr, " -P Run in parser mode\n"); + fprintf(stderr, " -E Run in evaluation mode (default)\n"); + fprintf(stderr, " -h Print this help\n"); + fprintf(stderr, " -r Force REPL mode\n"); +} + int main(int argc, const char **argv) { - enum repl_mode mode = REPL_EVAL; + (void)argc; + const char *arg0 = *(argv++); - if (argc > 1) { - if (apfl_string_eq(argv[1], "tokenizer")) { - mode = REPL_TOKENIZER; - } else if (apfl_string_eq(argv[1], "parser")) { - mode = REPL_PARSER; - } else if (apfl_string_eq(argv[1], "eval")) { - mode = REPL_EVAL; - } else { - fprintf(stderr, "Unknown mode: %s\n", argv[1]); + const char *input_file = NULL; + enum main_mode mode = MODE_EVAL; + bool force_repl = false; + + while (*argv) { + if (**argv != '-' || apfl_string_eq(*argv, "-")) { + break; + } + + for (const char *arg = (*argv) + 1; *arg != '\0'; arg++) { + switch (*arg) { + case 'T': + mode = MODE_TOKENIZER; + break; + case 'P': + mode = MODE_PARSER; + break; + case 'E': + mode = MODE_EVAL; + break; + case 'h': + help(arg0); + return 0; + case 'r': + force_repl = true; + break; + } + } + + argv++; + } + + if (*argv) { + input_file = *argv; + argv++; + } + + bool use_repl = force_repl || input_file == NULL; + FILE *input = NULL; + bool close = false; + if (input_file == NULL || apfl_string_eq(input_file, "-")) { + input = stdin; + } else { + input = fopen(input_file, "rb"); + if (input == NULL) { + printf("%s: Could not open %s: %s\n", arg0, input_file, strerror(errno)); return 1; } + close = true; } - if (mode == REPL_EVAL) { - return repl_eval(); - } else { - return repl_parser_or_tokenizer(mode); + struct apfl_source_reader source_reader = build_source_reader(input, use_repl); + + int rv = mode == MODE_EVAL + ? eval(source_reader, input_file, use_repl, argv) + : parser_or_tokenizer(mode, source_reader); + if (close) { + fclose(input); } + return rv; }