Compare commits

..

3 commits
next ... main

Author SHA1 Message Date
c5fbd92f75 Various cleanup tasks, mostly simplifying the REPL
Instead of passing a flag through the parser and tokenizer for telling
the input source if we need further input or not, we steal a trick from
Lua: In the REPL, we just continue to read lines and append them to the
input, until the input was loaded with no "unexpected EOF" error. After
all, when we didn't expect an EOF is exactly the scenario, when we need
more input.

Doing things this way simplifies a bunch of places and lets us remove
the ugly source_reader and iterative_runner concepts.

To allow the REPL to see the error that happened during loading required
some smaller refactorings, but those were honestly for the better
anyway.

I also decided to get rid of the token_source concept, the parser now
gets the tokenizer directly. This also made things a bit simpler, also
I want to soon-ish implement string interpolation, and for that the
parser needs to do more with the tokenizer than just reading the next
token.

One last thing: This also cleans up the web playground and makes the
playground and REPL share a bunch of code. Nice!
2025-11-30 22:23:40 +01:00
8d1f1e4ada Simplify loading code
Turns out past me allowed a subfunction to not have a matcher at all,
in which case everything is matched. This is exactly what we want to
happen for a apfl_load()ed function, so we don't needt to construct a
matcher here at all and can keep things a bit simpler. Nice :)
2025-11-20 21:31:08 +01:00
7a26a7793b compiler: Use correct put function for index
Thank you, valgrind!
2025-11-18 23:31:46 +01:00
26 changed files with 717 additions and 1012 deletions

View file

@ -7,10 +7,10 @@ apfl
An attempt at creating a programming language
!!VERY MUCH WORK IN PROGRESS!!
!!VERY MUCH NOT READY FOR USE!!
-------------------------------
While most things work, performance and memory usage is sh\*t, the standard library is not really fleshed out, documentation (other than this readme) is missing, and the syntax will likely change. So, maybe don't use it for your next startup :P
We have what looks like a working tokenizer and parser and you can evaluate most expressions that don't call or define a function (so nothing really useful is possible yet).
Building and running
--------------------

View file

@ -10,16 +10,16 @@ V* := { a1::b1 a2::b2 ->
(- (* a1 a2) (* b1 b2))::(+ (* a1 b2) (* b1 a2))
}
Vsquared := { x -> V* x x }
Vsqared := { x -> V* x x }
Vsquared-abs := { a::b ->
Vsqared-abs := { a::b ->
+ (* a a) (* b b)
}
mandel := {
0 _ _ -> true
_ _ _?(has Vsquared-abs > 4) -> false
n c z -> mandel (-- n) c (V+ (Vsquared z) c)
_ _ _?(has Vsqared-abs > 4) -> false
n c z -> mandel (-- n) c (V+ (Vsqared z) c)
}
mindim := if (< width height) {width} {height}

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -400,7 +400,7 @@ compile_assignable_var_path(
ilist_put_insn(args.ilist, INSN_STRING);
ilist_put_string(args.ilist, dot_rhs);
ilist_put_insn(args.ilist, INSN_MATCHER_SET_VAL);
ilist_put_insn(args.ilist, index);
ilist_put_index(args.ilist, index);
return true;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
TRY(compile_assignable_var_path(compiler, var_or_member->dot.lhs, args, path_len, varname));

View file

@ -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) { \
@ -2358,38 +2253,8 @@ setup_function_for_load_inner(apfl_ctx ctx, struct apfl_string *filename)
return NULL;
}
struct matcher_instruction_list *milist = apfl_matcher_instructions_new(&ctx->gc);
if (
milist == NULL
|| !apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(milist, GC_TYPE_MATCHER_INSTRUCTIONS))
) {
return NULL;
}
if (!apfl_resizable_append(
ctx->gc.allocator,
sizeof(union matcher_instruction_or_arg),
(void **)&milist->instructions,
&milist->len,
&milist->cap,
&(union matcher_instruction_or_arg[]) {
{.instruction = MATCHER_IGNORE},
},
1
)) {
return NULL;
}
struct matcher *matcher = apfl_matcher_new(&ctx->gc, milist);
if (
matcher == NULL
|| !apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(matcher, GC_TYPE_MATCHER))
) {
apfl_drop(ctx, -1);
return NULL;
}
if (!apfl_func_add_subfunc(func_value->func, ilist, matcher)) {
if (!apfl_func_add_subfunc(func_value->func, ilist, NULL)) {
apfl_drop(ctx, -1);
return NULL;
}
@ -2407,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

View file

@ -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

View file

@ -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;

View file

@ -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));
}
}

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -6,181 +6,103 @@
#include <string.h>
#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] [<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");
@ -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);
}

View file

@ -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;

View file

@ -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;
}

161
src/repl.c Normal file
View file

@ -0,0 +1,161 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "alloc.h"
#include "repl.h"
struct repl_data {
apfl_ctx ctx;
struct apfl_allocator alloc;
char *buf;
size_t buf_len;
size_t buf_cap;
struct apfl_io_writer w_out;
struct apfl_io_writer w_err;
};
static bool
null_writer(void *opaque, const unsigned char *buf, size_t len)
{
(void)opaque;
(void)buf;
(void)len;
return true;
}
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, "<toplevel>");
if (!apfl_load_try(r->ctx, apfl_io_string_reader(&sr), -1, &error)) {
if (
error.type == APFL_ERR_UNEXPECTED_EOF
|| error.type == APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN
) {
if (input == NULL) {
(void)report_error(r, error);
return REPL_FATAL;
} else {
return REPL_MORE_INPUT;
}
} else {
return report_error(r, error);
}
}
r->buf_len = 0;
switch (apfl_do_protected(r->ctx, protected_callback, NULL, apfl_error_decorate_with_backtrace)) {
case APFL_RESULT_OK :
if (apfl_get_type(r->ctx, -1) == APFL_VALUE_NIL) {
apfl_drop(r->ctx, -1);
} else {
apfl_debug_print_val(r->ctx, -1, r->w_out);
}
return REPL_OK;
case APFL_RESULT_ERR:
apfl_io_write_string(r->w_err, "Error occurred during evaluation:\n");
if (apfl_get_type(r->ctx, -1) == APFL_VALUE_STRING) {
apfl_io_write_string(r->w_err, apfl_get_string(r->ctx, -1));
apfl_drop(r->ctx, -1);
} else {
apfl_debug_print_val(r->ctx, -1, r->w_err);
}
apfl_io_write_byte(r->w_err, '\n');
return REPL_ERR;
case APFL_RESULT_ERRERR:
apfl_io_write_string(r->w_err, "Error occurred during error handling.\n");
return REPL_FATAL;
case APFL_RESULT_ERR_ALLOC:
apfl_io_write_string(r->w_err, "Fatal: Could not allocate memory.\n");
return REPL_FATAL;
}
assert(false && "unreachable");
return REPL_FATAL;
}
void
repl_destroy(repl r)
{
apfl_ctx_destroy(r->ctx);
FREE_BYTES(r->alloc, r->buf, r->buf_cap);
FREE_OBJ(r->alloc, r);
}

21
src/repl.h Normal file
View file

@ -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

View file

@ -1,21 +0,0 @@
#include <assert.h>
#include <string.h>
#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,
};
}

View file

@ -4,6 +4,7 @@
#include <stdlib.h>
#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;
}

View file

@ -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,
};
}

View file

@ -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));

View file

@ -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

View file

@ -76,74 +76,20 @@
padding-right: 1ch;
}
</style>
<script type="text/javascript" src="playground.js"></script>
<script type="text/javascript" src="web_playground.js"></script>
<script>
var input_resolve = null;
var queue = [];
function playground_write(error, s) {
var span = document.createElement("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(document.createElement("br"));
scroll = true;
}
var line = lines[i];
if (line != "") {
span.appendChild(document.createTextNode(lines[i]));
}
}
output = document.getElementById("output");
output.append(span);
if (scroll) {
output.scrollTop = output.scrollHeight - output.clientHeight;
}
}
function enqueue(s) {
if (input_resolve) {
input_resolve(s);
input_resolve = null;
} else {
queue.push(s);
}
}
function playground_get_input(need) {
document.getElementById("prompt").innerText = need ? "..." : ">";
async function playground_init() {
let Playground = await apfl_playground();
playground_container.innerHTML = "";
if (queue.length > 0) {
return Promise.resolve(queue.shift());
} else {
return new Promise(function (resolve, _) {
input_resolve = resolve;
});
}
}
function do_submit(ev) {
ev.preventDefault();
var input = document.getElementById("input");
var s = input.value;
input.value = "";
if (s == "") {
return;
}
s += "\n";
playground_write(false, document.getElementById("prompt").innerText + " " + s);
enqueue(s);
(new Playground()).interactive(playground_container);
}
</script>
<script type="text/javascript" src="playground.js"></script>
</head>
<body>
<div class="apfl_playground" id="terminal">
<pre class="apfl_playground_output" id="output"></pre>
<form onsubmit="do_submit(event)">
<span class="apfl_playground_prompt" id="prompt">&gt;</span>
<input type="text" class="apfl_playground_input" id="input" placeholder="code ...">
</div>
<body onload="playground_init()">
<div id="playground_container">
<span>Loading playground....</span>
</div>
</body>
</html>

View file

@ -5,99 +5,35 @@
#include <stdlib.h>
#include <string.h>
#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;
}

View file

@ -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;
};
})();