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.
This commit is contained in:
parent
2e7fefc7f7
commit
906ae3eac4
3 changed files with 179 additions and 50 deletions
11
src/apfl.h
11
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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
212
src/main.c
212
src/main.c
|
|
@ -1,4 +1,5 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -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] [<file>] ...\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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue