diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1eb726..77519f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,7 +30,6 @@ set(commonfiles registry.c resizable.c scope.c - source_readers.c strings.c symbols.c token.c @@ -49,7 +48,7 @@ add_library(apfl target_link_libraries(apfl PUBLIC m) target_link_libraries(apfl PUBLIC ${PCRE2_LIBRARIES}) -add_executable(apfl-bin main.c) +add_executable(apfl-bin main.c repl.c) target_link_libraries(apfl-bin PUBLIC apfl) if(CMAKE_CROSSCOMPILING) diff --git a/src/apfl.h b/src/apfl.h index 05a8989..d5b0548 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -216,7 +216,7 @@ void apfl_token_deinit(struct apfl_allocator allocator, struct apfl_token *); const char *apfl_token_type_name(enum apfl_token_type); -void apfl_token_print(struct apfl_token, FILE *); +bool apfl_token_print(struct apfl_token, struct apfl_io_writer); // Errors @@ -258,7 +258,7 @@ struct apfl_error { char byte; }; -bool apfl_error_print(struct apfl_error, FILE *); +bool apfl_error_print(struct apfl_error, struct apfl_io_writer); bool apfl_error_as_string(struct apfl_error, struct apfl_allocator, struct apfl_string *out); // Get a constant string for the error, if available (returns NULL otherwise) @@ -504,7 +504,7 @@ struct apfl_expr { struct apfl_position position; }; -bool apfl_expr_print(struct apfl_expr, FILE *); +bool apfl_expr_print(struct apfl_expr, struct apfl_io_writer); bool apfl_expr_eq(struct apfl_expr, struct apfl_expr); @@ -582,30 +582,11 @@ struct apfl_tokenizer; typedef struct apfl_tokenizer *apfl_tokenizer_ptr; -/* An apfl_source_reader is used to read source code for parsing / evaluation. - */ -struct apfl_source_reader { - /* callback gets called repeatedly to get the source code. - * buf points to a buffer to fill that has a size of *len. - * The callback must set len to the number of read bytes and return true - * on success and false on failure. - * - * Setting len to 0 indicates and end of file. - * - * need is true if more input is required for parsing, this is useful for - * implementing a reader for a REPL to indicate to the user if they need - * to type more code (e.g. by changing the prompt). - */ - bool (*callback)(void *opaque, unsigned char *buf, size_t *len, bool need); - - void *opaque; -}; - -apfl_tokenizer_ptr apfl_tokenizer_new(struct apfl_allocator allocator, struct apfl_source_reader); +apfl_tokenizer_ptr apfl_tokenizer_new(struct apfl_allocator allocator, struct apfl_io_reader); void apfl_tokenizer_destroy(apfl_tokenizer_ptr); -enum apfl_parse_result apfl_tokenizer_next(apfl_tokenizer_ptr, bool need); +enum apfl_parse_result apfl_tokenizer_next(apfl_tokenizer_ptr); /* Get the current token. * Return value is undefined when the last call to apfl_tokenizer_next did not @@ -619,22 +600,11 @@ struct apfl_token apfl_tokenizer_get_token(apfl_tokenizer_ptr); */ struct apfl_error apfl_tokenizer_get_error(apfl_tokenizer_ptr); -struct apfl_source_reader apfl_io_reader_as_source_reader(struct apfl_io_reader *); - -struct apfl_parser_token_source { - enum apfl_parse_result (*next)(void *, bool need); - struct apfl_token (*get_token)(void *); - struct apfl_error (*get_error)(void *); - void *opaque; -}; - -struct apfl_parser_token_source apfl_tokenizer_as_token_source(apfl_tokenizer_ptr); - struct apfl_parser; typedef struct apfl_parser *apfl_parser_ptr; -apfl_parser_ptr apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source); +apfl_parser_ptr apfl_parser_new(struct apfl_allocator allocator, apfl_tokenizer_ptr); /* Destroys the parser. * Note that if the token source needs it's own destruction, you'll have to do @@ -700,19 +670,6 @@ void apfl_ctx_set_panic_callback(apfl_ctx, apfl_panic_callback, void *); struct apfl_allocator apfl_get_allocator(apfl_ctx); -typedef struct apfl_iterative_runner_data *apfl_iterative_runner; - -apfl_iterative_runner apfl_iterative_runner_new(apfl_ctx, struct apfl_source_reader); -bool apfl_iterative_runner_next(apfl_iterative_runner); -enum apfl_result apfl_iterative_runner_get_result(apfl_iterative_runner); -bool apfl_iterative_runner_stopped_because_of_error(apfl_iterative_runner); -bool apfl_iterative_runner_run_repl( - apfl_iterative_runner, - struct apfl_io_writer w_out, - struct apfl_io_writer w_err -); -void apfl_iterative_runner_destroy(apfl_iterative_runner); - // Macro to define a C symbol #define APFL_DEFINE_CSYMBOL(name, str) \ void \ @@ -847,6 +804,8 @@ 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); +void apfl_run_in_top_scope(apfl_ctx, apfl_stackidx func); + enum apfl_result apfl_do_protected( apfl_ctx ctx, void (*callback)(apfl_ctx, void *), @@ -858,7 +817,8 @@ enum apfl_result apfl_do_protected( // 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); +void apfl_load(apfl_ctx, struct apfl_io_reader, apfl_stackidx name); +bool apfl_load_try(apfl_ctx, struct apfl_io_reader, apfl_stackidx name, struct apfl_error *); void apfl_bytecode_save(apfl_ctx, struct apfl_io_writer, apfl_stackidx func); diff --git a/src/apflc.c b/src/apflc.c index d306aca..6004834 100644 --- a/src/apflc.c +++ b/src/apflc.c @@ -127,7 +127,7 @@ protected_compiling(apfl_ctx ctx, void *opaque) struct protected_compiling_data *data = opaque; apfl_push_const_string(ctx, data->name); - apfl_load(ctx, apfl_io_reader_as_source_reader(data->r), -1); + apfl_load(ctx, *data->r, -1); apfl_bytecode_save(ctx, *data->w, -1); } @@ -139,7 +139,6 @@ main(int argc, const char *argv[]) (void)argc; const char **argp = argv+1; - int rv = 1; FILE *in = NULL; diff --git a/src/context.c b/src/context.c index c9e4032..8cd0bd8 100644 --- a/src/context.c +++ b/src/context.c @@ -747,6 +747,9 @@ apfl_gc_roots_traverse(apfl_ctx ctx, gc_visitor visitor, void *visitor_opaque) if (ctx->globals != NULL) { visitor(visitor_opaque, GC_OBJECT_FROM(ctx->globals, GC_TYPE_SCOPE)); } + if (ctx->toplevel_scope != NULL) { + visitor(visitor_opaque, GC_OBJECT_FROM(ctx->toplevel_scope, GC_TYPE_SCOPE)); + } stack_traverse(ctx->toplevel_stack, visitor, visitor_opaque); @@ -754,10 +757,6 @@ apfl_gc_roots_traverse(apfl_ctx ctx, gc_visitor visitor, void *visitor_opaque) gc_traverse_call_stack_entry(ctx->call_stack.items[i], visitor, visitor_opaque); } - for (size_t i = 0; i < ctx->iterative_runners.len; i++) { - apfl_iterative_runner_visit_gc_objects(ctx->iterative_runners.items[i], visitor, visitor_opaque); - } - apfl_registry_visit_gc_objects(ctx->registry, visitor, visitor_opaque); } @@ -829,16 +828,6 @@ apfl_call_stack_cur_entry(apfl_ctx ctx) : &ctx->call_stack.items[ctx->call_stack.len - 1]; } -static struct iterative_runners_list -iterative_runners_list_new(void) -{ - return (struct iterative_runners_list) { - .items = NULL, - .len = 0, - .cap = 0, - }; -} - static void init_globals_protected(apfl_ctx ctx, void *opaque) { @@ -943,7 +932,7 @@ apfl_ctx_new(struct apfl_config config) ctx->toplevel_stack = apfl_stack_new(); ctx->call_stack = call_stack_new(); ctx->globals = NULL; - ctx->iterative_runners = iterative_runners_list_new(); + ctx->toplevel_scope = NULL; ctx->registry = NULL; apfl_gc_init(ctx, config.allocator); @@ -952,6 +941,10 @@ apfl_ctx_new(struct apfl_config config) goto error; } + if ((ctx->toplevel_scope = apfl_scope_new(&ctx->gc)) == NULL) { + goto error; + } + ctx->output_writer = config.output_writer; if ((ctx->registry = apfl_registry_new(ctx->gc.allocator)) == NULL) { @@ -993,9 +986,6 @@ apfl_ctx_destroy(apfl_ctx ctx) ctx->call_stack = call_stack_new(); - FREE_LIST(ctx->gc.allocator, ctx->iterative_runners.items, ctx->iterative_runners.cap); - ctx->iterative_runners = iterative_runners_list_new(); - apfl_registry_destroy(ctx->registry); ctx->registry = NULL; @@ -1007,101 +997,6 @@ apfl_ctx_destroy(apfl_ctx ctx) FREE_OBJ(base_allocator, ctx); } -static bool -find_iterative_runner(apfl_ctx ctx, apfl_iterative_runner runner, size_t *index) -{ - // It should be very uncommon that there are a lot of iterative runners - // existing on the same apfl_ctx at the same time, so a linear scan should - // be good enough :) - for (size_t i = 0; i < ctx->iterative_runners.len; i++) { - if (ctx->iterative_runners.items[i] == runner) { - if (index != NULL) { - *index = i; - } - return true; - } - } - return false; -} - -struct iterative_runner_tmproot_data { - struct gc *gc; - bool ok; -}; - -static void -ctx_register_iterative_runner_tmproot(void *opaque, struct gc_object *object) -{ - struct iterative_runner_tmproot_data *data = opaque; - if (!data->ok) { - return; - } - data->ok = apfl_gc_tmproot_add(data->gc, object); -} - -static bool -ctx_register_iterative_runner_inner(apfl_ctx ctx, apfl_iterative_runner runner) -{ - struct iterative_runner_tmproot_data data = { - .gc = &ctx->gc, - .ok = true - }; - apfl_iterative_runner_visit_gc_objects(runner, ctx_register_iterative_runner_tmproot, &data); - - if (!data.ok) { - return false; - } - - if (find_iterative_runner(ctx, runner, NULL)) { - return true; - } - - return apfl_resizable_append( - ctx->gc.allocator, - sizeof(apfl_iterative_runner), - (void **)&ctx->iterative_runners.items, - &ctx->iterative_runners.len, - &ctx->iterative_runners.cap, - &runner, - 1 - ); -} - -bool -apfl_ctx_register_iterative_runner(apfl_ctx ctx, apfl_iterative_runner runner) -{ - size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); - bool out = ctx_register_iterative_runner_inner(ctx, runner); - apfl_gc_tmproots_restore(&ctx->gc, tmproots); - return out; -} - -void -apfl_ctx_unregister_iterative_runner(apfl_ctx ctx, apfl_iterative_runner runner) -{ - size_t i; - if (!find_iterative_runner(ctx, runner, &i)) { - return; - } - - bool ok = apfl_resizable_splice( - ctx->gc.allocator, - sizeof(apfl_iterative_runner), - (void **)&ctx->iterative_runners.items, - &ctx->iterative_runners.len, - &ctx->iterative_runners.cap, - i, - 1, - NULL, - 0 - ); - assert( - // We're only removing elements, the buffer should not grow, - // therefore there should be no allocation errors - ok - ); -} - #define CREATE_GC_OBJECT_VALUE_ON_STACK(ctx, TYPE, MEMB, NEW) \ struct apfl_value *value = apfl_stack_push_placeholder(ctx); \ if (value == NULL) { \ @@ -2377,44 +2272,55 @@ setup_function_for_load(apfl_ctx ctx, struct apfl_string *filename) } void -apfl_load(apfl_ctx ctx, struct apfl_source_reader reader, apfl_stackidx name) +apfl_load(apfl_ctx ctx, struct apfl_io_reader reader, apfl_stackidx name) +{ + struct apfl_error err; + if (!apfl_load_try(ctx, reader, name, &err)) { + apfl_raise_error_object(ctx, err); + } +} + +bool +apfl_load_try(apfl_ctx ctx, struct apfl_io_reader reader, apfl_stackidx name, struct apfl_error *error_out) { struct apfl_string *filename = apfl_to_dynamic_string(ctx, name); apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(ctx->gc.allocator, reader); if (tokenizer == NULL) { - apfl_raise_alloc_error(ctx); + *error_out = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + return false; } apfl_parser_ptr parser = apfl_parser_new( ctx->gc.allocator, - apfl_tokenizer_as_token_source(tokenizer) + tokenizer ); if (parser == NULL) { apfl_tokenizer_destroy(tokenizer); - apfl_raise_alloc_error(ctx); + *error_out = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + return false; } struct instruction_list *ilist = setup_function_for_load(ctx, filename); if (ilist == NULL) { apfl_parser_destroy(parser); apfl_tokenizer_destroy(tokenizer); - apfl_raise_alloc_error(ctx); + *error_out = apfl_error_simple(APFL_ERR_MALLOC_FAILED); + return false; } - struct apfl_error err; bool ok = apfl_compile_whole_file( &ctx->gc, parser, - &err, + error_out, ilist ); apfl_parser_destroy(parser); apfl_tokenizer_destroy(tokenizer); if (!ok) { apfl_drop(ctx, -1); - apfl_raise_error_object(ctx, err); } + return ok; } static void diff --git a/src/context.h b/src/context.h index 26f8155..ba2b865 100644 --- a/src/context.h +++ b/src/context.h @@ -142,18 +142,13 @@ struct error_handler { jmp_buf jump; }; -struct iterative_runners_list { - apfl_iterative_runner *items; - size_t len; - size_t cap; -}; - struct apfl_ctx_data { struct gc gc; apfl_panic_callback panic_callback; void *panic_callback_data; + struct scope *toplevel_scope; struct stack toplevel_stack; struct scope *globals; @@ -161,8 +156,6 @@ struct apfl_ctx_data { struct error_handler *error_handler; - struct iterative_runners_list iterative_runners; - struct apfl_io_writer output_writer; struct apfl_hashmap *registry; @@ -216,14 +209,9 @@ struct scope *apfl_closure_scope_for_func(apfl_ctx, struct scopes); struct apfl_io_writer apfl_get_output_writer(apfl_ctx); -bool apfl_ctx_register_iterative_runner(apfl_ctx, apfl_iterative_runner); -void apfl_ctx_unregister_iterative_runner(apfl_ctx, apfl_iterative_runner); - struct matcher *apfl_matcher_new(struct gc *, struct matcher_instruction_list *); void apfl_matcher_deinit(struct apfl_allocator, struct matcher *); -void apfl_iterative_runner_visit_gc_objects(apfl_iterative_runner, gc_visitor, void *); - void apfl_load_bytecode(apfl_ctx, struct apfl_io_reader); #ifdef __cplusplus diff --git a/src/error.c b/src/error.c index cb1b0ac..9c64136 100644 --- a/src/error.c +++ b/src/error.c @@ -191,9 +191,8 @@ format_error(struct apfl_io_writer w, struct apfl_error error) } bool -apfl_error_print(struct apfl_error error, FILE *file) +apfl_error_print(struct apfl_error error, struct apfl_io_writer w) { - struct apfl_io_writer w = apfl_io_file_writer(file); TRY(format_error(w, error)); TRY(apfl_io_write_byte(w, '\n')); return true; diff --git a/src/eval.c b/src/eval.c index cb08372..93e39bf 100644 --- a/src/eval.c +++ b/src/eval.c @@ -515,6 +515,67 @@ apfl_call(apfl_ctx ctx, apfl_stackidx func_index, apfl_stackidx args_index) call(ctx, func_index, args_index, false); } +struct matcher_stack +matcher_stack_new(void) +{ + return (struct matcher_stack) { + .items = NULL, + .len = 0, + .cap = 0, + }; +} + +static void +run_in_top_scope_inner(apfl_ctx ctx, apfl_stackidx func_index) +{ + struct apfl_value func = apfl_stack_must_pop(ctx, func_index); + must_tmproot_add_value(ctx, func); + + if (func.type != VALUE_FUNC) { + apfl_raise_errorfmt(ctx, "Can only run apfl functions in toplevel, got a {value:type} instead", func); + } + + struct scope *local_scope = apfl_scope_new(&ctx->gc); + if (local_scope == NULL) { + apfl_raise_alloc_error(ctx); + } + + if (!apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(local_scope, GC_TYPE_SCOPE))) { + apfl_raise_alloc_error(ctx); + } + + if (func.func->subfunctions_len == 0) { + apfl_raise_const_error(ctx, apfl_messages.corrupted_bytecode); + } + + struct subfunction *subfunc = &func.func->subfunctions[0]; + + apfl_call_stack_push(ctx, (struct call_stack_entry) { + .type = APFL_CSE_FUNCTION, + .stack = apfl_stack_new(), + .func = { + .pc = 0, + .instructions = subfunc->body, + .scopes = {.local = ctx->toplevel_scope}, + .execution_line = subfunc->body->line, + .function = NULL, + .matcher_stack = matcher_stack_new(), + .returning_from_matcher = false, + .matcher_result = false, + }, + }); + + evaluate_until_call_stack_return(ctx); +} + +void +apfl_run_in_top_scope(apfl_ctx ctx, apfl_stackidx func_index) +{ + size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); + run_in_top_scope_inner(ctx, func_index); + apfl_gc_tmproots_restore(&ctx->gc, tmproots); +} + static void matcher_set_val(apfl_ctx ctx, struct func_call_stack_entry *cse, size_t index) { @@ -1323,16 +1384,6 @@ evaluate_matcher(apfl_ctx ctx, struct matcher_call_stack_entry *cse) ); } -struct matcher_stack -matcher_stack_new(void) -{ - return (struct matcher_stack) { - .items = NULL, - .len = 0, - .cap = 0, - }; -} - static void dispatch_accept(struct call_stack_entry *cse) { @@ -1398,67 +1449,6 @@ dispatch(apfl_ctx ctx, struct call_stack_entry *cse) ); } -enum interative_runner_state { - IRUNNER_OK, - IRUNNER_EOF, - IRUNNER_ERR, -}; - -struct apfl_iterative_runner_data { - apfl_ctx ctx; - apfl_tokenizer_ptr tokenizer; - apfl_parser_ptr parser; - enum apfl_result result; - enum interative_runner_state state; - struct scope *scope; -}; - -static void -iterative_runner_eval_expr_inner(apfl_iterative_runner runner, struct apfl_expr expr) -{ - apfl_ctx ctx = runner->ctx; - - struct instruction_list *ilist = apfl_instructions_new(&ctx->gc, expr.position.line, NULL); - if (ilist == NULL) { - apfl_raise_alloc_error(ctx); - } - - if (!apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(ilist, GC_TYPE_INSTRUCTIONS))) { - apfl_raise_alloc_error(ctx); - } - - struct apfl_error error; - if (!apfl_compile(&ctx->gc, expr, &error, ilist)) { - apfl_raise_error_object(ctx, error); - } - - apfl_call_stack_push(ctx, (struct call_stack_entry) { - .type = APFL_CSE_FUNCTION, - .stack = apfl_stack_new(), - .func = (struct func_call_stack_entry) { - .pc = 0, - .instructions = ilist, - .scopes = { - .local = runner->scope, - .closure = NULL, - }, - .execution_line = ilist->line, - .matcher_stack = matcher_stack_new(), - .returning_from_matcher = false, - }, - }); - evaluate_until_call_stack_return(ctx); -} - -static void -iterative_runner_eval_expr(apfl_iterative_runner runner, struct apfl_expr expr) -{ - apfl_ctx ctx = runner->ctx; - size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); - iterative_runner_eval_expr_inner(runner, expr); - apfl_gc_tmproots_restore(&ctx->gc, tmproots); -} - bool apfl_debug_print_val(apfl_ctx ctx, apfl_stackidx index, struct apfl_io_writer w) { @@ -1473,82 +1463,6 @@ apfl_debug_print_val(apfl_ctx ctx, apfl_stackidx index, struct apfl_io_writer w) return apfl_value_print(value, w); } -apfl_iterative_runner -apfl_iterative_runner_new(apfl_ctx ctx, struct apfl_source_reader reader) -{ - apfl_iterative_runner runner = NULL; - apfl_tokenizer_ptr tokenizer = NULL; - apfl_parser_ptr parser = NULL; - - runner = ALLOC_OBJ(ctx->gc.allocator, struct apfl_iterative_runner_data); - if (runner == NULL) { - return NULL; - } - - tokenizer = apfl_tokenizer_new(ctx->gc.allocator, reader); - if (tokenizer == NULL) { - goto error; - } - - parser = apfl_parser_new(ctx->gc.allocator, apfl_tokenizer_as_token_source(tokenizer)); - if (parser == NULL) { - goto error; - } - - struct scope *scope = apfl_scope_new(&ctx->gc); - if (scope == NULL) { - goto error; - } - - *runner = (struct apfl_iterative_runner_data) { - .ctx = ctx, - .tokenizer = tokenizer, - .parser = parser, - .result = APFL_RESULT_OK, - .state = IRUNNER_OK, - .scope = scope, - }; - - if (!apfl_ctx_register_iterative_runner(ctx, runner)) { - goto error; - } - - return runner; - -error: - FREE_OBJ(ctx->gc.allocator, runner); - apfl_tokenizer_destroy(tokenizer); - apfl_parser_destroy(parser); - - return NULL; -} - -static void -iterative_runner_next_protected(apfl_ctx ctx, void *opaque) -{ - (void)ctx; - apfl_iterative_runner runner = opaque; - - switch (apfl_parser_next(runner->parser)) { - case APFL_PARSE_OK: - iterative_runner_eval_expr(runner, apfl_parser_get_expr(runner->parser)); - return; - case APFL_PARSE_ERROR: { - struct apfl_error err = apfl_parser_get_error(runner->parser); - if (err.type == APFL_ERR_INPUT_ERROR) { - runner->state = IRUNNER_ERR; - } - apfl_raise_error_object(runner->ctx, err); - return; - } - case APFL_PARSE_EOF: - runner->state = IRUNNER_EOF; - return; - } - - assert(false); -} - #define DECORATE_TRY_FMT(ctx, x) do { if (!(x)) { apfl_raise_alloc_error(ctx); } } while (0) void @@ -1596,103 +1510,3 @@ fail: apfl_string_builder_deinit(&sb); apfl_raise_alloc_error(ctx); } - -bool -apfl_iterative_runner_next(apfl_iterative_runner runner) -{ - if (runner->state != IRUNNER_OK) { - return false; - } - - apfl_stack_clear(runner->ctx); - - runner->result = apfl_do_protected( - runner->ctx, - iterative_runner_next_protected, - runner, - apfl_error_decorate_with_backtrace - ); - - return runner->state == IRUNNER_OK; -} - -enum apfl_result -apfl_iterative_runner_get_result(apfl_iterative_runner runner) -{ - return runner->result; -} - -bool -apfl_iterative_runner_stopped_because_of_error(apfl_iterative_runner runner) -{ - return runner->state == IRUNNER_ERR; -} - -bool -apfl_iterative_runner_run_repl( - apfl_iterative_runner runner, - struct apfl_io_writer w_out, - struct apfl_io_writer w_err -) { - apfl_ctx ctx = runner->ctx; - - while (apfl_iterative_runner_next(runner)) { - switch (apfl_iterative_runner_get_result(runner)) { - case APFL_RESULT_OK : - if (apfl_get_type(ctx, -1) == APFL_VALUE_NIL) { - apfl_drop(ctx, -1); - } else { - FMT_TRY(apfl_debug_print_val(ctx, -1, w_out)); - } - 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; - } - } - - if (apfl_iterative_runner_stopped_because_of_error(runner)) { - return false; - } - - return true; -} - -void -apfl_iterative_runner_destroy(apfl_iterative_runner runner) -{ - if (runner == NULL) { - return; - } - - apfl_parser_destroy(runner->parser); - apfl_tokenizer_destroy(runner->tokenizer); - - apfl_ctx_unregister_iterative_runner(runner->ctx, runner); - FREE_OBJ(runner->ctx->gc.allocator, runner); -} - - -void -apfl_iterative_runner_visit_gc_objects(apfl_iterative_runner runner, gc_visitor visitor, void *opaque) -{ - // TODO: It's a bit awkward that this function is defined here but the - // prototype lives in context.h... Maybe we should just merge context - // and eval together? The separation is rather arbitrary anyway :/ - - if (runner->scope != NULL) { - visitor(opaque, GC_OBJECT_FROM(runner->scope, GC_TYPE_SCOPE)); - } -} diff --git a/src/expr.c b/src/expr.c index 601e1c5..a624e78 100644 --- a/src/expr.c +++ b/src/expr.c @@ -953,9 +953,9 @@ format_expr(struct apfl_io_writer w, struct apfl_expr *expr, unsigned indent) } bool -apfl_expr_print(struct apfl_expr expr, FILE *f) +apfl_expr_print(struct apfl_expr expr, struct apfl_io_writer w) { - return format_expr(apfl_io_file_writer(f), &expr, 0); + return format_expr(w, &expr, 0); } static bool diff --git a/src/functional-test-runner.c b/src/functional-test-runner.c index 23aa081..d10cdfa 100644 --- a/src/functional-test-runner.c +++ b/src/functional-test-runner.c @@ -117,15 +117,6 @@ parse_test(const struct apfl_string str, struct test_parts *parts) return true; } -static void -dump_stack_error(apfl_ctx ctx, apfl_iterative_runner runner) -{ - if (apfl_iterative_runner_get_result(runner) == APFL_RESULT_ERR) { - bool ok = apfl_debug_print_val(ctx, -1, apfl_io_file_writer(stderr)); - assert(ok); - } -} - enum testresult runtest(const char *filename) { @@ -171,32 +162,25 @@ runtest(const char *filename) struct apfl_io_string_reader_data src_data = apfl_io_string_reader_create(parts.script); struct apfl_io_reader r = apfl_io_string_reader(&src_data); - apfl_iterative_runner runner = apfl_iterative_runner_new(ctx, apfl_io_reader_as_source_reader(&r)); - assert(runner != NULL); + apfl_push_const_string(ctx, "filename"); + apfl_load(ctx, r, -1); + apfl_list_create(ctx, 0); - while (apfl_iterative_runner_next(runner)) { - switch (apfl_iterative_runner_get_result(runner)) { - case APFL_RESULT_OK : - break; - case APFL_RESULT_ERR: - fprintf(stderr, "Error occurred during evaluation.\n"); - dump_stack_error(ctx, runner); - return T_ERR; - case APFL_RESULT_ERRERR: - fprintf(stderr, "Error while handling error.\n"); - return T_ERR; - case APFL_RESULT_ERR_ALLOC: - fprintf(stderr, "Fatal: Could not allocate memory.\n"); - return T_FATAL; - } - } - - if (apfl_iterative_runner_stopped_because_of_error(runner)) { - fprintf(stderr, "Runner stopped due to error.\n"); + switch (apfl_call_protected(ctx, -2, -1)) { + case APFL_RESULT_OK : + break; + case APFL_RESULT_ERR: + fprintf(stderr, "Error occurred during evaluation.\n"); + apfl_debug_print_val(ctx, -1, apfl_io_file_writer(stderr)); + return T_ERR; + case APFL_RESULT_ERRERR: + fprintf(stderr, "Error while handling error.\n"); + return T_ERR; + case APFL_RESULT_ERR_ALLOC: + fprintf(stderr, "Fatal: Could not allocate memory.\n"); return T_FATAL; } - apfl_iterative_runner_destroy(runner); apfl_ctx_destroy(ctx); struct apfl_string output_string = apfl_string_builder_move_string(&output); diff --git a/src/globals.c b/src/globals.c index 86d27ee..a1f41ca 100644 --- a/src/globals.c +++ b/src/globals.c @@ -584,7 +584,7 @@ loadfile(apfl_ctx ctx) apfl_drop(ctx, -2); // drop cstring struct apfl_io_reader r = apfl_io_file_reader(*fh); - apfl_load(ctx, apfl_io_reader_as_source_reader(&r), -2); + apfl_load(ctx, r, -2); closefile(fh); apfl_drop(ctx, -2); } @@ -598,7 +598,7 @@ loadstring(apfl_ctx ctx) struct apfl_io_string_reader_data reader_data = apfl_io_string_reader_create(apfl_get_string(ctx, -2)); struct apfl_io_reader r = apfl_io_string_reader(&reader_data); - apfl_load(ctx, apfl_io_reader_as_source_reader(&r), -1); + apfl_load(ctx, r, -1); apfl_drop(ctx, -2); } diff --git a/src/main.c b/src/main.c index 58763ae..f2d6de3 100644 --- a/src/main.c +++ b/src/main.c @@ -6,181 +6,103 @@ #include #include "apfl.h" +#include "repl.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, - }; -} +#define REPL_BUFLEN 4096 static int -run_tokenizer(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) +run_parser(FILE *file) { - 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); - bool ok = apfl_expr_print(expr, stdout); - assert(ok); - 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; + bool success = false; 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) { + if ((tokenizer = apfl_tokenizer_new(allocator, apfl_io_file_reader(file))) == NULL) { fprintf(stderr, "Failed initializing tokenizer\n"); - return 1; + goto exit; } - 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; + if ((parser = apfl_parser_new(allocator, tokenizer)) == NULL) { + fprintf(stderr, "Failed initializing parser\n"); + goto exit; + } + + while (true) { + struct apfl_expr expr; + + switch (apfl_parser_next(parser)) { + case APFL_PARSE_OK: + expr = apfl_parser_get_expr(parser); + bool ok = apfl_expr_print(expr, apfl_io_file_writer(stdout)); + assert(ok); + apfl_expr_deinit(allocator, &expr); + break; + case APFL_PARSE_EOF: + success = true; + goto exit; + case APFL_PARSE_ERROR: + apfl_error_print(apfl_parser_get_error(parser), apfl_io_file_writer(stderr)); 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; + return success ? 0 : 1; } -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) +eval_protected_callback(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); + (void)opaque; apfl_call(ctx, -2, -1); - apfl_drop(ctx, -1); -} - -#define FMT_TRY(x) if (!(x)) { rv = 1; goto err; } - -static void -foomod(apfl_ctx ctx) -{ - apfl_dict_create(ctx); - apfl_push_const_string(ctx, "bar"); - apfl_push_const_string(ctx, "baz"); - apfl_dict_set(ctx, -3, -2, -1); } static int -eval( - struct apfl_source_reader source_reader, +run_repl(FILE *f) +{ + repl r = repl_new(apfl_allocator_default()); + if (r == NULL) { + fprintf(stderr, "Could not initialize repl\n"); + return 1; + } + + repl_set_w_out(r, apfl_io_file_writer(stdout)); + repl_set_w_err(r, apfl_io_file_writer(stderr)); + + bool is_eof = false; + char line[REPL_BUFLEN]; + enum repl_result result = REPL_OK; + + while (!feof(f) && result != REPL_FATAL) { + fputs(result == REPL_MORE_INPUT ? "... " : "> ", stdout); + + if (fgets(line, REPL_BUFLEN, f) == NULL) { + if (feof(f)) { + is_eof = true; + } else { + fprintf(stderr, "Failed to read input\n"); + result = REPL_FATAL; + break; + } + } + + result = repl_run(r, is_eof ? NULL : line); + } + + repl_destroy(r); + + return result == REPL_OK ? 0 : 1; +} + +static int +run_eval( + FILE *file, const char *input_file_name, - bool run_as_repl, const char **argv ) { struct apfl_io_writer w_out = apfl_io_file_writer(stdout); @@ -203,48 +125,33 @@ eval( } apfl_argv_set(ctx, -1); - apfl_push_cfunc(ctx, foomod, 0); - apfl_modules_register(ctx, "foo", -1); + apfl_push_const_string(ctx, input_file_name); + apfl_load(ctx, apfl_io_file_reader(file), -1); + apfl_list_create(ctx, 0); - 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 = 1; + switch (apfl_do_protected(ctx, eval_protected_callback, NULL, apfl_error_decorate_with_backtrace)) { + case APFL_RESULT_OK : + rv = 0; + break; + case APFL_RESULT_ERR: + apfl_io_write_string(w_err, "Error occurred during evaluation:\n"); + if (apfl_get_type(ctx, -1) == APFL_VALUE_STRING) { + apfl_io_write_string(w_err, apfl_get_string(ctx, -1)); + apfl_drop(ctx, -1); + } else { + apfl_debug_print_val(ctx, -1, w_err); } + apfl_io_write_byte(w_err, '\n'); + break; + case APFL_RESULT_ERRERR: + apfl_io_write_string(w_err, "Error occurred during error handling.\n"); + break; + case APFL_RESULT_ERR_ALLOC: + apfl_io_write_string(w_err, "Fatal: Could not allocate memory.\n"); + break; } -err: apfl_ctx_destroy(ctx); return rv; } @@ -254,7 +161,6 @@ 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"); @@ -268,8 +174,8 @@ main(int argc, const char **argv) const char *arg0 = *(argv++); const char *input_file = NULL; - enum main_mode mode = MODE_EVAL; bool force_repl = false; + bool parser_mode = false; while (*argv) { if (**argv != '-' || apfl_string_eq(*argv, "-")) { @@ -278,14 +184,11 @@ main(int argc, const char **argv) for (const char *arg = (*argv) + 1; *arg != '\0'; arg++) { switch (*arg) { - case 'T': - mode = MODE_TOKENIZER; - break; case 'P': - mode = MODE_PARSER; + parser_mode = true; break; case 'E': - mode = MODE_EVAL; + parser_mode = false; break; case 'h': help(arg0); @@ -318,11 +221,12 @@ main(int argc, const char **argv) close = true; } - struct apfl_source_reader source_reader = build_source_reader(input, use_repl); + int rv = parser_mode + ? run_parser(input) + : use_repl + ? run_repl(input) + : run_eval(input, input_file, argv); - int rv = mode == MODE_EVAL - ? eval(source_reader, input_file, use_repl, argv) - : parser_or_tokenizer(mode, source_reader); if (close) { fclose(input); } diff --git a/src/parser.c b/src/parser.c index 939d8dd..0ffc7f0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -10,7 +10,7 @@ struct apfl_parser { struct apfl_allocator allocator; - struct apfl_parser_token_source token_source; + apfl_tokenizer_ptr tokenizer; bool has_expr; struct apfl_expr expr; @@ -81,7 +81,7 @@ struct fragment { struct apfl_position position; }; -static enum parse_fragment_result parse_fragment(apfl_parser_ptr, struct fragment*, bool need, enum parse_fragment_flags); +static enum parse_fragment_result parse_fragment(apfl_parser_ptr, struct fragment*, enum parse_fragment_flags); static bool grow_fragment_cap(struct apfl_allocator allocator, struct fragment_list *list, size_t inc) @@ -242,7 +242,7 @@ fragment_move(struct fragment *in) } apfl_parser_ptr -apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source token_source) +apfl_parser_new(struct apfl_allocator allocator, apfl_tokenizer_ptr tokenizer) { apfl_parser_ptr p = ALLOC_OBJ(allocator, struct apfl_parser); if (p == NULL) { @@ -250,7 +250,7 @@ apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source } p->allocator = allocator; - p->token_source = token_source; + p->tokenizer = tokenizer; p->eof = false; p->has_token = false; p->has_unread = false; @@ -260,18 +260,16 @@ apfl_parser_new(struct apfl_allocator allocator, struct apfl_parser_token_source } static enum apfl_parse_result -get_raw_token(apfl_parser_ptr p, struct apfl_token *token, bool need) +get_raw_token(apfl_parser_ptr p, struct apfl_token *token) { - struct apfl_parser_token_source *src = &(p->token_source); - - enum apfl_parse_result result = src->next(src->opaque, need); + enum apfl_parse_result result = apfl_tokenizer_next(p->tokenizer); switch (result) { case APFL_PARSE_ERROR: - p->error = src->get_error(src->opaque); + p->error = apfl_tokenizer_get_error(p->tokenizer); break; case APFL_PARSE_OK: - *token = src->get_token(src->opaque); + *token = apfl_tokenizer_get_token(p->tokenizer); break; default: // nop @@ -282,10 +280,10 @@ get_raw_token(apfl_parser_ptr p, struct apfl_token *token, bool need) } static enum apfl_parse_result -get_non_comment_token(apfl_parser_ptr p, struct apfl_token *token, bool need) +get_non_comment_token(apfl_parser_ptr p, struct apfl_token *token) { for (;;) { - enum apfl_parse_result result = get_raw_token(p, token, need); + enum apfl_parse_result result = get_raw_token(p, token); if (result != APFL_PARSE_OK) { return result; @@ -300,12 +298,12 @@ get_non_comment_token(apfl_parser_ptr p, struct apfl_token *token, bool need) } static enum apfl_parse_result -get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) +get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token) { enum apfl_parse_result result; for (;;) { - result = get_non_comment_token(p, token, need); + result = get_non_comment_token(p, token); if (result != APFL_PARSE_OK) { return result; @@ -319,7 +317,7 @@ get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) apfl_token_deinit(p->allocator, token); - result = get_non_comment_token(p, token, true); + result = get_non_comment_token(p, token); if (result != APFL_PARSE_OK) { return result; } @@ -340,7 +338,7 @@ get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) } static enum apfl_parse_result -read_token(apfl_parser_ptr p, bool need) +read_token(apfl_parser_ptr p) { if (p->eof) { return APFL_PARSE_EOF; @@ -356,7 +354,7 @@ read_token(apfl_parser_ptr p, bool need) apfl_token_deinit(p->allocator, &p->token); } - enum apfl_parse_result result = get_preprocessed_token(p, &p->token, need); + enum apfl_parse_result result = get_preprocessed_token(p, &p->token); p->eof = result == APFL_PARSE_EOF; p->has_token = result == APFL_PARSE_OK; return result; @@ -377,7 +375,7 @@ read_token_after_cant_handle(apfl_parser_ptr p) { // A function that returns PF_CANT_HANDLE always unreads a token, so we are // guaranteed to have at least one token. - enum apfl_parse_result result = read_token(p, true); + enum apfl_parse_result result = read_token(p); assert(result == APFL_PARSE_OK); } @@ -394,7 +392,7 @@ err_unexpected_token(enum apfl_token_type token_type, struct apfl_position pos) #define ERR_UNEXPECTED_TOKEN(t) (err_unexpected_token((t).type, (t).position)) static enum parse_fragment_result -parse_fragment_into_list(apfl_parser_ptr p, struct fragment_list *list, bool need, enum parse_fragment_flags flags) +parse_fragment_into_list(apfl_parser_ptr p, struct fragment_list *list, enum parse_fragment_flags flags) { if (!grow_fragment_cap(p->allocator, list, 1)) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); @@ -403,7 +401,7 @@ parse_fragment_into_list(apfl_parser_ptr p, struct fragment_list *list, bool nee struct fragment *elem = &list->children[list->len]; - enum parse_fragment_result result = parse_fragment(p, elem, need, flags); + enum parse_fragment_result result = parse_fragment(p, elem, flags); if (result != PF_OK) { return result; } @@ -435,7 +433,7 @@ parse_parens_head( struct fragment_list *children, struct apfl_position position ) { - switch (parse_fragment_into_list(p, children, true, FFLAG_NO_EXPAND)) { + switch (parse_fragment_into_list(p, children, FFLAG_NO_EXPAND)) { case PF_OK: return true; case PF_EOF: @@ -456,7 +454,7 @@ static bool parse_parens_tail(apfl_parser_ptr p, struct fragment_list *children, struct apfl_position position) { for (;;) { - switch (parse_fragment_into_list(p, children, true, 0)) { + switch (parse_fragment_into_list(p, children, 0)) { case PF_OK: break; case PF_EOF: @@ -512,7 +510,7 @@ static bool skip_inner_bracket_separators(apfl_parser_ptr p) { for (;;) { - switch (read_token(p, true)) { + switch (read_token(p)) { case APFL_PARSE_OK: if ( p->token.type == APFL_TOK_COMMA @@ -542,7 +540,7 @@ parse_empty_dict(apfl_parser_ptr p, struct fragment *out, struct apfl_position p return false; } - switch (read_token(p, true)) { + switch (read_token(p)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -748,7 +746,7 @@ parse_dict( goto error; } - switch (parse_fragment(p, &key, true, FFLAG_NO_EXPAND)) { + switch (parse_fragment(p, &key, FFLAG_NO_EXPAND)) { case PF_OK: cleanup_key = true; break; @@ -765,7 +763,7 @@ parse_dict( goto error; } - switch (read_token(p, true)) { + switch (read_token(p)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -788,7 +786,7 @@ after_mapsto: } struct fragment value; - switch (parse_fragment(p, &value, true, FFLAG_NO_EXPAND)) { + switch (parse_fragment(p, &value, FFLAG_NO_EXPAND)) { case PF_OK: cleanup_value = true; break; @@ -878,7 +876,7 @@ parse_list( } for (;;) { - switch (parse_fragment_into_list(p, &list, true, 0)) { + switch (parse_fragment_into_list(p, &list, 0)) { case PF_OK: break; case PF_EOF: @@ -921,7 +919,7 @@ parse_brackets(apfl_parser_ptr p, struct fragment *out, struct apfl_position sta } struct fragment first; - switch (parse_fragment(p, &first, true, 0)) { + switch (parse_fragment(p, &first, 0)) { case PF_OK: break; case PF_CANT_HANDLE: @@ -937,7 +935,7 @@ parse_brackets(apfl_parser_ptr p, struct fragment *out, struct apfl_position sta goto error; } - switch (read_token(p, true)) { + switch (read_token(p)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -969,7 +967,7 @@ parse_expand(apfl_parser_ptr p, struct fragment *fragment, struct apfl_position return false; } - enum parse_fragment_result result = parse_fragment(p, inner, true, FFLAG_NO_EXPAND); + enum parse_fragment_result result = parse_fragment(p, inner, FFLAG_NO_EXPAND); if (result == PF_OK) { fragment->type = FRAG_EXPAND; fragment->expand = inner; @@ -1002,7 +1000,7 @@ must_read_token_after(apfl_parser_ptr p, enum apfl_token_type want_type) enum apfl_token_type cur_type = p->token.type; struct apfl_position cur_pos = p->token.position; - switch (read_token(p, true)) { + switch (read_token(p)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -1806,18 +1804,14 @@ parse_body_or_toplevel( apfl_parser_ptr p, bool handle_eof, struct fragment_list *fragments, - struct apfl_expr *out, - bool need + struct apfl_expr *out ) { struct partial_assignment_list partial_assignments; apfl_resizable_init(APFL_RESIZABLE_ARGS(partial_assignments, items)); - bool first; - - first = true; for (;;) { for (;;) { - switch (parse_fragment_into_list(p, fragments, need || !first, 0)) { + switch (parse_fragment_into_list(p, fragments, 0)) { case PF_OK: break; case PF_CANT_HANDLE: @@ -1843,8 +1837,6 @@ parse_body_or_toplevel( case PF_ERROR: goto error; } - - first = false; } break_inner: @@ -1984,8 +1976,7 @@ parse_braces( p, false, &fragments, - &expr, - true + &expr )) { case PF_OK: if (!apfl_resizable_append( @@ -2130,14 +2121,14 @@ new_empty_fragment(struct apfl_allocator allocator) } static enum parse_fragment_result -parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum parse_fragment_flags flags) +parse_fragment(apfl_parser_ptr p, struct fragment *fragment, enum parse_fragment_flags flags) { *fragment = empty_fragment(); struct fragment *lhs = NULL; struct fragment *rhs = NULL; - switch (read_token(p, need)) { + switch (read_token(p)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -2225,7 +2216,7 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par } for (; !(flags & FFLAG_NO_POSTFIXS); ) { - switch (read_token(p, need)) { + switch (read_token(p)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -2270,7 +2261,6 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par switch (parse_fragment( p, rhs, - true, p->token.type == APFL_TOK_DOUBLE_COLON || p->token.type == APFL_TOK_COLON ? FFLAG_NO_EXPAND : FFLAG_NO_EXPAND | FFLAG_NO_POSTFIXS @@ -2343,8 +2333,7 @@ apfl_parser_next(apfl_parser_ptr p) p, true, &fragments, - &p->expr, - false + &p->expr )) { case PF_OK: p->has_expr = true; diff --git a/src/parser_test.c b/src/parser_test.c index 1405e98..cc37fd6 100644 --- a/src/parser_test.c +++ b/src/parser_test.c @@ -29,12 +29,12 @@ new_parser_test(testctx t, const char *source) if ((pt->tokenizer = apfl_tokenizer_new( allocator, - apfl_io_reader_as_source_reader(&pt->reader) + pt->reader )) == NULL) { test_fatalf(t, "Failed initializing the tokenizer"); } - if ((pt->parser = apfl_parser_new(allocator, apfl_tokenizer_as_token_source(pt->tokenizer))) == NULL) { + if ((pt->parser = apfl_parser_new(allocator, pt->tokenizer)) == NULL) { test_fatalf(t, "Failed initializing the parser"); } @@ -60,7 +60,7 @@ expect_eof(struct parser_test *pt) break; case APFL_PARSE_ERROR: test_failf(pt->t, "Got an error instead of an EOF"); - apfl_error_print(apfl_parser_get_error(pt->parser), stderr); + apfl_error_print(apfl_parser_get_error(pt->parser), apfl_io_file_writer(stderr)); test_fatal(pt->t); break; } @@ -75,11 +75,12 @@ expect_expr(struct parser_test *pt, struct apfl_expr expected) case APFL_PARSE_OK: expr = apfl_parser_get_expr(pt->parser); if (!apfl_expr_eq(expr, expected)) { + struct apfl_io_writer w_err = apfl_io_file_writer(stderr); test_failf(pt->t, "Expected expression differs from actual expression"); test_failf(pt->t, "Expected:"); - apfl_expr_print(expected, stderr); + apfl_expr_print(expected, w_err); test_failf(pt->t, "Have:"); - apfl_expr_print(expr, stderr); + apfl_expr_print(expr, w_err); } apfl_expr_deinit(pt->allocator, &expr); apfl_expr_deinit(pt->allocator, &expected); @@ -89,7 +90,7 @@ expect_expr(struct parser_test *pt, struct apfl_expr expected) break; case APFL_PARSE_ERROR: test_failf(pt->t, "Got an error instead of an expression"); - apfl_error_print(apfl_parser_get_error(pt->parser), stderr); + apfl_error_print(apfl_parser_get_error(pt->parser), apfl_io_file_writer(stderr)); test_fatal(pt->t); break; } @@ -110,7 +111,7 @@ expect_error_of_type(struct parser_test *pt, enum apfl_error_type want) have = apfl_parser_get_error(pt->parser); if (have.type != want) { test_failf(pt->t, "Expected error of type %s, got this error instead:", apfl_error_type_name(want)); - apfl_error_print(have, stderr); + apfl_error_print(have, apfl_io_file_writer(stderr)); } break; } diff --git a/src/repl.c b/src/repl.c new file mode 100644 index 0000000..3b4d859 --- /dev/null +++ b/src/repl.c @@ -0,0 +1,161 @@ +#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; +} + +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 = r->w_out, + }); + + 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); + if (r->buf_cap < r->buf_len + input_len) { + size_t newcap = r->buf_len + input_len; + char *newbuf = REALLOC_BYTES(r->alloc, r->buf, r->buf_cap, newcap); + if (newbuf == NULL) { + return REPL_FATAL; + } + r->buf = newbuf; + r->buf_cap = newcap; + } + + 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); +} diff --git a/src/repl.h b/src/repl.h new file mode 100644 index 0000000..031f5b1 --- /dev/null +++ b/src/repl.h @@ -0,0 +1,21 @@ +#ifndef APFL_REPL_H +#define APFL_REPL_H + +#include "apfl.h" + +enum repl_result { + REPL_OK = 0, + REPL_MORE_INPUT = 1, + REPL_ERR = 2, + REPL_FATAL = 3, +}; + +typedef struct repl_data *repl; + +repl repl_new(struct apfl_allocator); +void repl_set_w_out(repl, struct apfl_io_writer); +void repl_set_w_err(repl, struct apfl_io_writer); +enum repl_result repl_run(repl, char *); +void repl_destroy(repl); + +#endif diff --git a/src/source_readers.c b/src/source_readers.c deleted file mode 100644 index 052cfd7..0000000 --- a/src/source_readers.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -#include "apfl.h" - -static bool -io_reader_callback(void *opaque, unsigned char *buf, size_t *len, bool need) -{ - (void)need; - struct apfl_io_reader *r = opaque; - return apfl_io_read_bytes(*r, buf, len); -} - -struct apfl_source_reader -apfl_io_reader_as_source_reader(struct apfl_io_reader *r) -{ - return (struct apfl_source_reader) { - .callback = io_reader_callback, - .opaque = r, - }; -} diff --git a/src/token.c b/src/token.c index 3bd54ad..fd38566 100644 --- a/src/token.c +++ b/src/token.c @@ -4,6 +4,7 @@ #include #include "apfl.h" +#include "format.h" static bool has_text_data(enum apfl_token_type type) @@ -94,34 +95,23 @@ apfl_token_type_name(enum apfl_token_type type) return "(unknown token)"; } -void -apfl_token_print(struct apfl_token token, FILE *file) +bool +apfl_token_print(struct apfl_token token, struct apfl_io_writer w) { + FMT_TRY(apfl_io_write_string(w, apfl_token_type_name(token.type))); if (has_text_data(token.type)) { - fprintf( - file, - "%s (" APFL_STR_FMT ") @ (%" PRIuMAX ":%" PRIuMAX ")\n", - apfl_token_type_name(token.type), - APFL_STR_FMT_ARGS(apfl_string_view_from(token.text)), - (uintmax_t)token.position.line, - (uintmax_t)token.position.col - ); + FMT_TRY(apfl_io_write_string(w, " (")); + FMT_TRY(apfl_io_write_string(w, token.text)); + FMT_TRY(apfl_io_write_string(w, ")")); } else if (has_numeric_data(token.type)) { - fprintf( - file, - "%s (%f) @ (%" PRIuMAX ":%" PRIuMAX ")\n", - apfl_token_type_name(token.type), - token.number, - (uintmax_t)token.position.line, - (uintmax_t)token.position.col - ); - } else { - fprintf( - file, - "%s @ (%" PRIuMAX ":%" PRIuMAX ")\n", - apfl_token_type_name(token.type), - (uintmax_t)token.position.line, - (uintmax_t)token.position.col - ); + FMT_TRY(apfl_io_write_string(w, " (")); + FMT_TRY(apfl_format_put_number(w, token.number)); + FMT_TRY(apfl_io_write_string(w, ")")); } + + FMT_TRY(apfl_io_write_string(w, "@ (")); + FMT_TRY(apfl_format_put_pos(w, token.position)); + FMT_TRY(apfl_io_write_string(w, ")\n")); + + return true; } diff --git a/src/tokenizer.c b/src/tokenizer.c index e202560..b4ea143 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -18,7 +18,7 @@ static_assert(BUFSIZE >= 2, "BUFSIZE must be at least 2"); struct apfl_tokenizer { struct apfl_allocator allocator; - struct apfl_source_reader source_reader; + struct apfl_io_reader source_reader; unsigned char *buf; buf_offset buf_pos; buf_offset buf_len; @@ -43,7 +43,7 @@ struct apfl_tokenizer { }; apfl_tokenizer_ptr -apfl_tokenizer_new(struct apfl_allocator allocator, struct apfl_source_reader source_reader) +apfl_tokenizer_new(struct apfl_allocator allocator, struct apfl_io_reader source_reader) { apfl_tokenizer_ptr tokenizer = ALLOC_OBJ(allocator, struct apfl_tokenizer); if (tokenizer == NULL) { @@ -97,7 +97,7 @@ apfl_tokenizer_get_error(apfl_tokenizer_ptr tokenizer) } static enum read_result -read_byte(apfl_tokenizer_ptr tokenizer, unsigned char *byte, bool need) +read_byte(apfl_tokenizer_ptr tokenizer, unsigned char *byte) { if (tokenizer->buf_pos >= tokenizer->buf_len) { size_t off = 0; @@ -111,7 +111,7 @@ read_byte(apfl_tokenizer_ptr tokenizer, unsigned char *byte, bool need) tokenizer->buf_pos = off; tokenizer->buf_len = off; - if (!tokenizer->source_reader.callback(tokenizer->source_reader.opaque, tokenizer->buf+off, &len, need)) { + if (!apfl_io_read_bytes(tokenizer->source_reader, tokenizer->buf+off, &len)) { tokenizer->error.type = APFL_ERR_INPUT_ERROR; return RR_ERR; } @@ -166,7 +166,7 @@ static enum apfl_parse_result comment(apfl_tokenizer_ptr); static enum apfl_parse_result colon(apfl_tokenizer_ptr); static enum apfl_parse_result string(apfl_tokenizer_ptr); static enum apfl_parse_result backtick_string(apfl_tokenizer_ptr); -static enum apfl_parse_result maybe_name(apfl_tokenizer_ptr, bool, unsigned char); +static enum apfl_parse_result maybe_name(apfl_tokenizer_ptr, unsigned char); static enum apfl_parse_result number(apfl_tokenizer_ptr, unsigned, struct apfl_position, bool); static enum apfl_parse_result zero(apfl_tokenizer_ptr, struct apfl_position, bool); @@ -182,7 +182,7 @@ minus(apfl_tokenizer_ptr tokenizer) struct apfl_position pos = tokenizer->position; unsigned char byte; - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -212,13 +212,13 @@ minus(apfl_tokenizer_ptr tokenizer) if (isdigit(byte)) { return number(tokenizer, 10, pos, true); } else { - return maybe_name(tokenizer, true, '-'); + return maybe_name(tokenizer, '-'); } } } enum apfl_parse_result -apfl_tokenizer_next(apfl_tokenizer_ptr tokenizer, bool need) +apfl_tokenizer_next(apfl_tokenizer_ptr tokenizer) { switch (tokenizer->next_mode) { case NM_REGULAR: @@ -236,7 +236,7 @@ apfl_tokenizer_next(apfl_tokenizer_ptr tokenizer, bool need) unsigned char byte; for (;;) { - switch (read_byte(tokenizer, &byte, need)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -308,7 +308,7 @@ apfl_tokenizer_next(apfl_tokenizer_ptr tokenizer, bool need) unread_byte(tokenizer); return number(tokenizer, 10, position, false); } else { - return maybe_name(tokenizer, need, byte); + return maybe_name(tokenizer, byte); } } } @@ -324,7 +324,7 @@ comment(apfl_tokenizer_ptr tokenizer) struct apfl_string_builder text = apfl_string_builder_init(tokenizer->allocator); for (;;) { - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -363,7 +363,7 @@ colon(apfl_tokenizer_ptr tokenizer) unsigned char byte; struct apfl_position pos = tokenizer->position; - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -407,7 +407,7 @@ hex_escape( for (int i = 0; i < 2; i++) { unsigned char byte; - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -441,7 +441,7 @@ escape_sequence(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder *text) unsigned char byte; - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -491,7 +491,7 @@ inner_string(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder *text) enum apfl_parse_result subresult; for (;;) { - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -544,7 +544,7 @@ inner_backtick_string(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder * unsigned char byte; for (;;) { - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -563,7 +563,7 @@ inner_backtick_string(apfl_tokenizer_ptr tokenizer, struct apfl_string_builder * continue; } - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -641,7 +641,6 @@ is_word_byte(unsigned char byte) static enum apfl_parse_result maybe_name_inner( apfl_tokenizer_ptr tokenizer, - bool need, unsigned char byte, struct apfl_string_builder *text ) { @@ -658,7 +657,7 @@ maybe_name_inner( last_byte = byte; last_pos = tokenizer->position; - switch (read_byte(tokenizer, &byte, need)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -731,11 +730,11 @@ maybe_name_inner( } static enum apfl_parse_result -maybe_name(apfl_tokenizer_ptr tokenizer, bool need, unsigned char first_byte) +maybe_name(apfl_tokenizer_ptr tokenizer, unsigned char first_byte) { struct apfl_string_builder text = apfl_string_builder_init(tokenizer->allocator); - enum apfl_parse_result out = maybe_name_inner(tokenizer, need, first_byte, &text); + enum apfl_parse_result out = maybe_name_inner(tokenizer, first_byte, &text); apfl_string_builder_deinit(&text); @@ -760,7 +759,7 @@ static enum apfl_parse_result zero(apfl_tokenizer_ptr tokenizer, struct apfl_position position, bool negative) { unsigned char byte; - switch (read_byte(tokenizer, &byte, true)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -791,7 +790,7 @@ static enum read_result read_for_parse_number(void *opaque, unsigned char *byte) { apfl_tokenizer_ptr tokenizer = opaque; - return read_byte(tokenizer, byte, true); + return read_byte(tokenizer, byte); } static void @@ -816,7 +815,7 @@ number(apfl_tokenizer_ptr tokenizer, unsigned base, struct apfl_position pos, bo } unsigned char byte; - switch (read_byte(tokenizer, &byte, false)) { + switch (read_byte(tokenizer, &byte)) { case RR_OK: break; case RR_ERR: @@ -840,33 +839,3 @@ number(apfl_tokenizer_ptr tokenizer, unsigned base, struct apfl_position pos, bo tokenizer->token = build_number_token(num, pos, negative); return APFL_PARSE_OK; } - -static enum apfl_parse_result -token_source_wrap_next(void *opaque, bool need) -{ - return apfl_tokenizer_next(opaque, need); -} - -static struct apfl_token -token_source_wrap_get_token(void *opaque) -{ - return apfl_tokenizer_get_token(opaque); -} - -static struct apfl_error -token_source_wrap_get_error(void *opaque) -{ - return apfl_tokenizer_get_error(opaque); -} - - -struct apfl_parser_token_source -apfl_tokenizer_as_token_source(apfl_tokenizer_ptr p) -{ - return (struct apfl_parser_token_source) { - .next = token_source_wrap_next, - .get_token = token_source_wrap_get_token, - .get_error = token_source_wrap_get_error, - .opaque = p, - }; -} diff --git a/src/tokenizer_test.c b/src/tokenizer_test.c index 0595088..349fde1 100644 --- a/src/tokenizer_test.c +++ b/src/tokenizer_test.c @@ -28,7 +28,7 @@ new_tokenizer_test_sv(testctx t, struct apfl_string_view text) if ((tt->tokenizer = apfl_tokenizer_new( allocator, - apfl_io_reader_as_source_reader(&tt->reader) + tt->reader )) == NULL) { test_fatalf(t, "Failed to initialize the tokenizer"); } @@ -53,7 +53,7 @@ destroy_tokenizer_test(struct tokenizer_test *tt) static void expect_eof(struct tokenizer_test *tt) { - switch (apfl_tokenizer_next(tt->tokenizer, false)) { + switch (apfl_tokenizer_next(tt->tokenizer)) { case APFL_PARSE_OK: test_fatalf(tt->t, "Expected EOF but got a token"); break; @@ -61,7 +61,7 @@ expect_eof(struct tokenizer_test *tt) break; case APFL_PARSE_ERROR: test_failf(tt->t, "Got an error instead of an EOF"); - apfl_error_print(apfl_tokenizer_get_error(tt->tokenizer), stderr); + apfl_error_print(apfl_tokenizer_get_error(tt->tokenizer), apfl_io_file_writer(stderr)); test_fatal(tt->t); break; } @@ -70,7 +70,7 @@ expect_eof(struct tokenizer_test *tt) static bool expect_token(struct tokenizer_test *tt, size_t line, size_t col, enum apfl_token_type type, struct apfl_token *tok) { - switch (apfl_tokenizer_next(tt->tokenizer, false)) { + switch (apfl_tokenizer_next(tt->tokenizer)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: @@ -78,7 +78,7 @@ expect_token(struct tokenizer_test *tt, size_t line, size_t col, enum apfl_token break; case APFL_PARSE_ERROR: test_failf(tt->t, "Got an error instead of a token"); - apfl_error_print(apfl_tokenizer_get_error(tt->tokenizer), stderr); + apfl_error_print(apfl_tokenizer_get_error(tt->tokenizer), apfl_io_file_writer(stderr)); test_fatal(tt->t); break; } @@ -164,7 +164,7 @@ expect_error(struct tokenizer_test *tt, enum apfl_error_type want) { struct apfl_token tok; - switch (apfl_tokenizer_next(tt->tokenizer, false)) { + switch (apfl_tokenizer_next(tt->tokenizer)) { case APFL_PARSE_OK: tok = apfl_tokenizer_get_token(tt->tokenizer); test_failf(tt->t, "Expected error, got token of type %s instead", apfl_token_type_name(tok.type)); diff --git a/webpage/build.sh b/webpage/build.sh index f390c88..e9968af 100755 --- a/webpage/build.sh +++ b/webpage/build.sh @@ -1,8 +1,17 @@ #!/bin/sh set -e +headline() { + printf '\033[34m\033[1m===== %s =====\e[0m\n' "$1" +} + PCRE2VER=10.42 +webpage_dir="$(pwd)" +apfl_root="$webpage_dir/.." + +headline 'Building native apflc' + cd playground rm -rf build-native mkdir build-native @@ -11,24 +20,38 @@ cmake ../../../CMakeLists.txt make -j"$(nproc)" apflc cd .. +headline 'Building PCRE' + rm -rf deps mkdir deps cd deps curl -L -o pcre2.tar.bz2 "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2VER}/pcre2-${PCRE2VER}.tar.bz2" +sha256sum pcre2.tar.bz2 tar xjf pcre2.tar.bz2 cd "pcre2-${PCRE2VER}" mkdir build cd build -emcmake cmake -DCMAKE_INSTALL_PREFIX="/home/laria/src/apfl/webpage/playground/deps/prefix" ../CMakeLists.txt +emcmake cmake -DCMAKE_INSTALL_PREFIX="$webpage_dir/playground/deps/prefix" ../CMakeLists.txt emmake make -j"$(nproc)" pcre2-8-static emmake make -j"$(nproc)" install cd ../../.. +headline 'Building apfl lib' + rm -rf build mkdir build cd build -emcmake cmake -DCMAKE_C_FLAGS="-O2" -DBUILD_SHARED_LIBS=NO -DApflApflcNative_DIR="$(pwd)/../build-native/" -DCMAKE_PREFIX_PATH="/home/laria/src/apfl/webpage/playground/deps/prefix" ../../../CMakeLists.txt +emcmake cmake -DCMAKE_C_FLAGS="-O2" -DBUILD_SHARED_LIBS=NO -DApflApflcNative_DIR="$(pwd)/../build-native/" -DCMAKE_PREFIX_PATH="$webpage_dir/playground/deps/prefix" ../../../CMakeLists.txt emmake make -j"$(nproc)" apfl cd .. -emcc -sASYNCIFY `PKG_CONFIG_PATH="/home/laria/src/apfl/webpage/playground/deps/prefix/lib/pkgconfig" pkg-config --static --cflags --libs libpcre2-8` -O3 -oplayground.js playground.c build/src/libapfl.a + +headline 'Building apfl playground' +emcc \ + `PKG_CONFIG_PATH="$webpage_dir/playground/deps/prefix/lib/pkgconfig" pkg-config --static --cflags --libs libpcre2-8` \ + -O3 \ + -oplayground.js \ + -sEXPORTED_FUNCTIONS=_repl_new_for_playground,_repl_run,_repl_destroy \ + -sEXPORTED_RUNTIME_METHODS=ccall,cwrap \ + -sMODULARIZE -sEXPORT_NAME=WasmApflPlayground \ + playground.c "$apfl_root/src/repl.c" build/src/libapfl.a diff --git a/webpage/playground/index.html b/webpage/playground/index.html index 6d49899..465a34b 100644 --- a/webpage/playground/index.html +++ b/webpage/playground/index.html @@ -76,74 +76,20 @@ padding-right: 1ch; } + + - - -
-

-        
- > - -
+ +
+ Loading playground....
diff --git a/webpage/playground/playground.c b/webpage/playground/playground.c index d61ec90..1b0323e 100644 --- a/webpage/playground/playground.c +++ b/webpage/playground/playground.c @@ -5,99 +5,35 @@ #include #include -#include "../../src/apfl.h" -#include "../../src/format.h" +#include "../../src/repl.h" -struct playground_source_reader_data { - char *str; - size_t len; - size_t off; - bool eof; -}; - -EM_ASYNC_JS(char *, get_input, (int need), { - var s = await window.playground_get_input(!!need); - var lengthBytes = lengthBytesUTF8(s)+1; - var stringOnWasmHeap = _malloc(lengthBytes); - stringToUTF8(s, stringOnWasmHeap, lengthBytes); - return stringOnWasmHeap; -}); - -EM_JS(void, web_writer, (int error, const unsigned char* str, int size), { - window.playground_write(!!error, UTF8ToString(str, size)); +EM_JS(void, web_writer, (int error, int handle, const unsigned char* str, int size), { + window.playground_write(handle, !!error, UTF8ToString(str, size)); }); static bool -web_fmt_write(void *opaque, const unsigned char *buf, size_t len) +web_fmt_write_ok(void *opaque, const unsigned char *buf, size_t len) { - int error = (int)opaque; - web_writer(error, buf, (int)len); + web_writer(false, (int)opaque, buf, len); return true; } static bool -playground_source_reader_cb(void *context, unsigned char *buf, size_t *len, bool need) +web_fmt_write_err(void *opaque, const unsigned char *buf, size_t len) { - struct playground_source_reader_data *r = context; - - size_t remain = r->len - r->off; - if (remain == 0) { - free(r->str); - r->str = get_input(need ? 1 : 0); - assert(r->str != NULL); - - r->len = strlen(r->str); - r->off = 0; - } - - remain = r->len - r->off; - assert(remain > 0); - - if (remain < *len) { - *len = remain; - } - memcpy(buf, r->str+r->off, *len); - r->off += *len; - + web_writer(true, (int)opaque, buf, len); return true; } -int -main(void) +repl +repl_new_for_playground(void) { - struct apfl_io_writer w_ok = {.write = web_fmt_write, .opaque = (void *)0}; - struct apfl_io_writer w_err = {.write = web_fmt_write, .opaque = (void *)1}; - - apfl_ctx ctx = apfl_ctx_new((struct apfl_config) { - .allocator = apfl_allocator_default(), - .output_writer = w_ok, - }); - if (ctx == NULL) { - apfl_io_write_string(w_err, "Failed to init the context\n"); - return 1; + repl r = repl_new(apfl_allocator_default()); + if (r == NULL) { + return NULL; } + repl_set_w_out(r, (struct apfl_io_writer){.write = web_fmt_write_ok, .opaque = (void *)r}); + repl_set_w_err(r, (struct apfl_io_writer){.write = web_fmt_write_err, .opaque = (void *)r}); - struct playground_source_reader_data source_reader_data = { - .str = NULL, - .len = 0, - .off = 0, - .eof = false, - }; - - apfl_iterative_runner runner = apfl_iterative_runner_new(ctx, (struct apfl_source_reader) { - .callback = playground_source_reader_cb, - .opaque = &source_reader_data, - }); - if (runner == NULL) { - apfl_ctx_destroy(ctx); - apfl_io_write_string(w_err, "Failed to init runner\n"); - return 1; - } - - int rv = apfl_iterative_runner_run_repl(runner, w_ok, w_err) ? 0 : 1; - - free(source_reader_data.str); - apfl_iterative_runner_destroy(runner); - apfl_ctx_destroy(ctx); - return rv; + return r; } diff --git a/webpage/playground/web_playground.js b/webpage/playground/web_playground.js new file mode 100644 index 0000000..ea78560 --- /dev/null +++ b/webpage/playground/web_playground.js @@ -0,0 +1,167 @@ +window.apfl_playground = (() => { + let instances = new Map(); + + function playground_write(handle, error, s) { + let inst = instances.get(handle); + if (inst) { + inst.write(error, s); + } + } + + window.playground_write = playground_write; + + let repl_new_for_playground = undefined; + let repl_run = undefined; + let repl_destroy = undefined; + + function Playground() { + let h = repl_new_for_playground(); + if (!h) { + throw new Error("Could not init playground"); + } + + this.echoSource = true; + + this._h = h; + this._onOutput = null; + this._onError = null; + this._lastResult = Playground.REPL_OK; + instances.set(h, this); + } + + Playground.REPL_OK = 0; + Playground.REPL_MORE_INPUT = 1; + Playground.REPL_ERR = 2; + Playground.REPL_FATAL = 3; + + Playground.prototype.destroy = function () { + repl_destroy(this._h); + instances.delete(this._h); + }; + + Playground.prototype.onOutput = function (f) { + this._onOutput = f; + }; + + Playground.prototype.onError = function (f) { + this._onError = f; + }; + + Playground.prototype.write = function (error, s) { + let writer = error ? this._onError : this._onOutput; + if (writer) { + writer(s); + } + } + + Playground.prototype.getPrompt = function () { + return this._lastResult === Playground.REPL_MORE_INPUT ? "..." : ">"; + }; + + Playground.prototype.runCode = function (source) { + source += ""; + if (this.echoSource) { + this.write(false, `${this.getPrompt()} ${source}`); + } + + return (this._lastResult = repl_run(this._h, source)); + }; + + Playground.prototype.interactive = function (parent) { + const elem = (name, inner) => { + let out = document.createElement(name); + if (inner) { + inner(out); + } + return out; + }; + + let output; + let form; + let prompt; + let input; + + parent.appendChild(elem("DIV", outer => { + outer.classList = "apfl_playground"; + outer.appendChild(output = elem("PRE", output => { + output.classList = "apfl_playground_output"; + })) + outer.appendChild(form = elem("FORM", form => { + form.appendChild(prompt = elem("SPAN", prompt => { + prompt.className = "apfl_playground_prompt"; + prompt.innerText = this.getPrompt(); + })); + form.appendChild(input = elem("INPUT", input => { + input.type = "text"; + input.className = "apfl_playground_input"; + input.placeholder = "code ..."; + })); + })) + })); + + form.onsubmit = (e) => { + e.preventDefault(); + let code = input.value; + input.value = ""; + + if (!code) { + return; + } + + this.runCode(code + "\n"); + + prompt.innerText = this.getPrompt(); + }; + + const write = (error, s) => { + var span = elem("SPAN"); + if (error) { + span.classList.add("error"); + } + var lines = s.split("\n"); + var scroll = false; + for (var i = 0 ; i < lines.length; i++) { + if (i > 0) { + span.appendChild(elem("BR")); + scroll = true; + } + var line = lines[i]; + if (line != "") { + span.appendChild(document.createTextNode(lines[i])); + } + } + output.append(span); + if (scroll) { + output.scrollTop = output.scrollHeight - output.clientHeight; + } + }; + + this.onOutput(s => write(false, s)); + this.onError(s => write(true, s)); + }; + + let ok = false + let onModLoaded = []; + + return async function () { + if (ok) { + return Playground; + } + + let p = new Promise(resolve => { + onModLoaded.push(resolve); + }); + + WasmApflPlayground().then(instance => { + repl_new_for_playground = instance.cwrap("repl_new_for_playground", "number", []); + repl_run = instance.cwrap("repl_run", "number", ["number", "string"]); + repl_destroy = instance.cwrap("repl_destroy", "void", ["number"]); + + ok = true; + + onModLoaded.forEach(l => l(Playground)); + }); + + return await p; + }; +})();