Compare commits
3 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5fbd92f75 | |||
| 8d1f1e4ada | |||
| 7a26a7793b |
26 changed files with 717 additions and 1012 deletions
|
|
@ -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
|
||||
--------------------
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
60
src/apfl.h
60
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
180
src/context.c
180
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) { \
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
308
src/eval.c
308
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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
294
src/main.c
294
src/main.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
85
src/parser.c
85
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;
|
||||
|
|
|
|||
|
|
@ -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
161
src/repl.c
Normal 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
21
src/repl.h
Normal 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
|
||||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
42
src/token.c
42
src/token.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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">></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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
167
webpage/playground/web_playground.js
Normal file
167
webpage/playground/web_playground.js
Normal 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;
|
||||
};
|
||||
})();
|
||||
Loading…
Reference in a new issue