#include #include #include #include #include #include #include "apfl.h" enum main_mode { MODE_TOKENIZER, MODE_PARSER, MODE_EVAL, }; static bool source_reader_without_prompt_cb(void *context, unsigned char *buf, size_t *len, bool need) { (void)need; FILE *f = context; size_t maxlen = *len; if (fgets((char *)buf, maxlen, f) == NULL) { if (feof(f)) { *len = 0; return true; } else { return false; } } *len = strlen((char *)buf); 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 build_source_reader(FILE *f, bool prompt) { return (struct apfl_source_reader) { .callback = prompt ? source_reader_with_prompt_cb : source_reader_without_prompt_cb, .opaque = f, }; } static int run_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) { while (true) { struct apfl_error err; struct apfl_token token; switch (apfl_tokenizer_next(tokenizer, false)) { case APFL_PARSE_OK: token = apfl_tokenizer_get_token(tokenizer); apfl_token_print(token, stdout); apfl_token_deinit(allocator, &token); break; case APFL_PARSE_EOF: return 0; case APFL_PARSE_ERROR: err = apfl_tokenizer_get_error(tokenizer); apfl_error_print(err, stderr); if (err.type == APFL_ERR_INPUT_ERROR || err.type == APFL_ERR_MALLOC_FAILED) { return 1; } break; } } } static int run_parser(struct apfl_allocator allocator, apfl_parser_ptr parser) { while (true) { struct apfl_expr expr; struct apfl_error err; switch (apfl_parser_next(parser)) { case APFL_PARSE_OK: expr = apfl_parser_get_expr(parser); assert(apfl_expr_print(expr, stdout)); apfl_expr_deinit(allocator, &expr); break; case APFL_PARSE_EOF: return 0; case APFL_PARSE_ERROR: err = apfl_parser_get_error(parser); apfl_error_print(err, stderr); if (err.type == APFL_ERR_INPUT_ERROR || err.type == APFL_ERR_MALLOC_FAILED) { return 1; } break; } } } static int parser_or_tokenizer(enum main_mode mode, struct apfl_source_reader source_reader) { int rv = 0; struct apfl_allocator allocator = apfl_allocator_default(); apfl_tokenizer_ptr tokenizer = NULL; apfl_parser_ptr parser = NULL; if ((tokenizer = apfl_tokenizer_new(allocator, source_reader)) == NULL) { fprintf(stderr, "Failed initializing tokenizer\n"); return 1; } 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; goto exit; } } switch (mode) { case MODE_TOKENIZER: rv = run_tokenizer(allocator, tokenizer); break; case MODE_PARSER: rv = run_parser(allocator, parser); break; default: assert(false /* MODE_parser_or_tokenizer called with wrong mode */); } exit: apfl_parser_destroy(parser); apfl_tokenizer_destroy(tokenizer); return rv; } 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); apfl_ctx ctx = apfl_ctx_new((struct apfl_config) { .allocator = apfl_allocator_default(), .output_writer = w_out, }); if (ctx == NULL) { fprintf(stderr, "Failed to init the context\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; } } 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) { (void)argc; const char *arg0 = *(argv++); 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; } 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; }