From 3f6e1f14a9c25d89034c93d1b6dfda85586f8230 Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Mon, 30 Jan 2023 22:50:01 +0100 Subject: [PATCH] Implement loading other scripts --- src/apfl.h | 5 ++ src/bytecode.c | 7 +- src/bytecode.h | 3 +- src/compile.c | 8 +- src/context.c | 189 ++++++++++++++++++++++++++++++++++++++++--- src/context.h | 3 + src/eval.c | 10 ++- src/globals.c | 54 ++++++++++--- src/source_readers.c | 29 ++++++- src/value.c | 6 +- src/value.h | 9 ++- 11 files changed, 292 insertions(+), 31 deletions(-) diff --git a/src/apfl.h b/src/apfl.h index a0b375e..74a8108 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -563,6 +563,8 @@ struct apfl_string_source_reader_data apfl_string_source_reader_create(struct ap */ struct apfl_source_reader apfl_string_source_reader(struct apfl_string_source_reader_data *); +struct apfl_source_reader apfl_stdio_source_reader(FILE *f); + struct apfl_parser_token_source { enum apfl_parse_result (*next)(void *, bool need); struct apfl_token (*get_token)(void *); @@ -736,6 +738,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_load(apfl_ctx, struct apfl_source_reader, apfl_stackidx name); + enum apfl_call_stack_entry_type { APFL_CSE_FUNCTION, APFL_CSE_CFUNCTION, @@ -747,6 +751,7 @@ struct apfl_call_stack_entry_info { enum apfl_call_stack_entry_type type; bool toplevel; // Only set for type==APFL_CSE_FUNCTION. size_t subfunction_index; // Only set for type==APFL_CSE_FUNCTION && !toplevel + struct apfl_string_view filename; struct apfl_string_view name; int line_current; // only set for (type==APFL_CSE_FUNCTION && !toplevel) || type == APFL_CSE_FUNCTION_DISPATCH int line_defined; // only set for type==APFL_CSE_FUNCTION diff --git a/src/bytecode.c b/src/bytecode.c index 36b2bfe..de75dc1 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -8,7 +8,7 @@ #include "gc.h" struct instruction_list * -apfl_instructions_new(struct gc *gc, int line) +apfl_instructions_new(struct gc *gc, int line, struct apfl_string *filename) { struct instruction_list *ilist = apfl_gc_new_instructions(gc); if (ilist == NULL) { @@ -19,6 +19,7 @@ apfl_instructions_new(struct gc *gc, int line) .len = 0, .cap = 0, .line = line, + .filename = filename, }; return ilist; } @@ -42,6 +43,10 @@ apfl_gc_instructions_traverse(struct instruction_list *ilist, gc_visitor cb, voi { union instruction_or_arg arg; + if (ilist->filename != NULL) { + cb(opaque, GC_OBJECT_FROM(ilist->filename, GC_TYPE_STRING)); + } + for (size_t i = 0; i < ilist->len; i++) { switch (ilist->instructions[i].instruction) { case INSN_NIL: diff --git a/src/bytecode.h b/src/bytecode.h index 5c32255..dcbb735 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -86,12 +86,13 @@ struct instruction_list { size_t cap; int line; + struct apfl_string *filename; }; const char *apfl_instruction_to_string(enum instruction); const char *apfl_matcher_instruction_to_string(enum matcher_instruction); -struct instruction_list *apfl_instructions_new(struct gc *, int line); +struct instruction_list *apfl_instructions_new(struct gc *, int line, struct apfl_string *filename); void apfl_instructions_deinit(struct apfl_allocator, struct instruction_list *); void apfl_gc_instructions_traverse(struct instruction_list *, gc_visitor, void *); diff --git a/src/compile.c b/src/compile.c index 917f0d2..d0cef4c 100644 --- a/src/compile.c +++ b/src/compile.c @@ -296,11 +296,11 @@ compile_simple_assignment( } static struct instruction_list * -tmp_ilist(struct compiler *compiler, int line) +tmp_ilist(struct compiler *compiler, int line, struct apfl_string *filename) { struct instruction_list *ilist; if ( - (ilist = apfl_instructions_new(compiler->gc, line)) == NULL + (ilist = apfl_instructions_new(compiler->gc, line, filename)) == NULL || !apfl_gc_tmproot_add( compiler->gc, GC_OBJECT_FROM(ilist, GC_TYPE_INSTRUCTIONS) @@ -714,7 +714,7 @@ compile_simple_func_inner( struct apfl_string *name ) { struct instruction_list *body_ilist = NULL; - MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, line))); + MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, line, ilist->filename))); struct matcher_instruction_list *milist = NULL; MALLOC_FAIL_IF_NULL(compiler, (milist = tmp_milist(compiler))); @@ -837,7 +837,7 @@ static bool compile_subfunc(struct compiler *compiler, struct apfl_expr_subfunc *subfunc, struct instruction_list *ilist, struct apfl_position position) { struct instruction_list *body_ilist = NULL; - MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, position.line))); + MALLOC_FAIL_IF_NULL(compiler, (body_ilist = tmp_ilist(compiler, position.line, ilist->filename))); struct matcher_instruction_list *milist = NULL; MALLOC_FAIL_IF_NULL(compiler, (milist = tmp_milist(compiler))); diff --git a/src/context.c b/src/context.c index 9b22833..a63f4db 100644 --- a/src/context.c +++ b/src/context.c @@ -6,6 +6,7 @@ #include "apfl.h" #include "alloc.h" +#include "compile.h" #include "context.h" #include "gc.h" #include "globals.h" @@ -1455,6 +1456,24 @@ apfl_tostring(apfl_ctx ctx, apfl_stackidx index) apfl_stack_drop(ctx, -2); // Drop original value } +struct apfl_string * +apfl_to_dynamic_string(apfl_ctx ctx, apfl_stackidx index) +{ + apfl_tostring(ctx, index); + struct apfl_value value = apfl_stack_must_get(ctx, -1); + if (value.type == VALUE_STRING) { + return value.string; + } + + assert(value.type == VALUE_CONST_STRING /* apfl_tostring results in either VALUE_STRING or VALUE_CONST_STRING */); + + apfl_push_string_view_copy(ctx, value.const_string); + value = apfl_stack_must_get(ctx, -1); + apfl_drop(ctx, -2); + + return value.string; +} + void apfl_join_strings(apfl_ctx ctx, apfl_stackidx glue, apfl_stackidx parts) { @@ -1888,6 +1907,14 @@ apfl_call_stack_depth(apfl_ctx ctx) return ctx->call_stack.len; } +static struct apfl_string_view +get_string_view_or_empty(struct apfl_string *s) +{ + return s == NULL + ? (struct apfl_string_view) { .len = 0, .bytes = NULL, } + : apfl_string_view_from(*s); +} + struct apfl_call_stack_entry_info apfl_call_stack_inspect(apfl_ctx ctx, size_t n) { @@ -1900,12 +1927,14 @@ apfl_call_stack_inspect(apfl_ctx ctx, size_t n) struct apfl_call_stack_entry_info info = { .type = cse->type, .name = (struct apfl_string_view) { .len = 0, .bytes = NULL, }, + .filename = (struct apfl_string_view) { .len = 0, .bytes = NULL, }, .toplevel = false, }; switch (cse->type) { case APFL_CSE_FUNCTION: info.line_current = cse->func.execution_line; + info.filename = get_string_view_or_empty(cse->func.instructions->filename); if (cse->func.function == NULL) { info.toplevel = true; } else { @@ -1919,9 +1948,7 @@ apfl_call_stack_inspect(apfl_ctx ctx, size_t n) } break; case APFL_CSE_CFUNCTION: - if (cse->cfunc.func->name) { - info.name = apfl_string_view_from(*cse->cfunc.func->name); - } + info.name = get_string_view_or_empty(cse->cfunc.func->name); break; case APFL_CSE_FUNCTION_DISPATCH: { struct apfl_string_view sv; @@ -1929,6 +1956,7 @@ apfl_call_stack_inspect(apfl_ctx ctx, size_t n) info.name = sv; } info.line_defined = cse->func_dispatch.function->line_defined; + info.filename = get_string_view_or_empty(cse->func_dispatch.function->filename); break; } case APFL_CSE_MATCHER: @@ -1938,9 +1966,19 @@ apfl_call_stack_inspect(apfl_ctx ctx, size_t n) return info; } -static bool format_defined_at(struct apfl_format_writer w, struct apfl_call_stack_entry_info info) -{ - FMT_TRY(apfl_format_put_string(w, "defined in line ")); +static bool +format_defined_at( + struct apfl_format_writer w, + struct apfl_call_stack_entry_info info, + struct apfl_string_view *filename +) { + FMT_TRY(apfl_format_put_string(w, "defined in ")); + if (filename != NULL && filename->len > 0) { + FMT_TRY(apfl_format_put_string(w, "file ")); + FMT_TRY(apfl_format_put_string(w, *filename)); + FMT_TRY(apfl_format_put_string(w, ", ")); + } + FMT_TRY(apfl_format_put_string(w, "line ")); FMT_TRY(apfl_format_put_int(w, info.line_defined)); return true; } @@ -1950,6 +1988,11 @@ apfl_call_stack_entry_info_format(struct apfl_format_writer w, struct apfl_call_ { switch (info.type) { case APFL_CSE_FUNCTION: + if (info.filename.len > 0) { + FMT_TRY(apfl_format_put_string(w, "File ")); + FMT_TRY(apfl_format_put_string(w, info.filename)); + FMT_TRY(apfl_format_put_string(w, ", ")); + } FMT_TRY(apfl_format_put_string(w, "Line ")); FMT_TRY(apfl_format_put_int(w, info.line_current)); FMT_TRY(apfl_format_put_string(w, ", ")); @@ -1960,7 +2003,7 @@ apfl_call_stack_entry_info_format(struct apfl_format_writer w, struct apfl_call_ FMT_TRY(apfl_format_put_string(w, "anonymous function (subfunction ")); FMT_TRY(apfl_format_put_int(w, (int)info.subfunction_index)); FMT_TRY(apfl_format_put_string(w, "; ")); - FMT_TRY(format_defined_at(w, info)); + FMT_TRY(format_defined_at(w, info, NULL)); FMT_TRY(apfl_format_put_string(w, ")")); } else { FMT_TRY(apfl_format_put_string(w, "function ")); @@ -1968,7 +2011,7 @@ apfl_call_stack_entry_info_format(struct apfl_format_writer w, struct apfl_call_ FMT_TRY(apfl_format_put_string(w, " (subfunction ")); FMT_TRY(apfl_format_put_int(w, (int)info.subfunction_index)); FMT_TRY(apfl_format_put_string(w, "; ")); - FMT_TRY(format_defined_at(w, info)); + FMT_TRY(format_defined_at(w, info, NULL)); FMT_TRY(apfl_format_put_string(w, ")")); } break; @@ -1989,7 +2032,7 @@ apfl_call_stack_entry_info_format(struct apfl_format_writer w, struct apfl_call_ FMT_TRY(apfl_format_put_string(w, info.name)); FMT_TRY(apfl_format_put_string(w, "(")); } - FMT_TRY(format_defined_at(w, info)); + FMT_TRY(format_defined_at(w, info, &info.filename)); FMT_TRY(apfl_format_put_string(w, ")")); break; case APFL_CSE_MATCHER: @@ -1999,3 +2042,131 @@ apfl_call_stack_entry_info_format(struct apfl_format_writer w, struct apfl_call_ return true; } + +static struct instruction_list * +setup_function_for_load_inner(apfl_ctx ctx, struct apfl_string *filename) +{ + struct apfl_value *func_value = apfl_stack_push_placeholder(ctx); + if (func_value == NULL) { + return NULL; + } + + if ((func_value->func = apfl_func_new(&ctx->gc, 1, NULL, 1, filename)) == NULL) { + apfl_drop(ctx, -1); + return NULL; + } + func_value->type = VALUE_FUNC; + + struct instruction_list *ilist = apfl_instructions_new(&ctx->gc, 1, filename); + if ( + ilist == NULL + || !apfl_gc_tmproot_add(&ctx->gc, GC_OBJECT_FROM(ilist, GC_TYPE_INSTRUCTIONS)) + ) { + 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)) { + apfl_drop(ctx, -1); + return NULL; + } + + return ilist; +} + +static struct instruction_list * +setup_function_for_load(apfl_ctx ctx, struct apfl_string *filename) +{ + size_t tmproots = apfl_gc_tmproots_begin(&ctx->gc); + struct instruction_list *out = setup_function_for_load_inner(ctx, filename); + apfl_gc_tmproots_restore(&ctx->gc, tmproots); + return out; +} + +void +apfl_load(apfl_ctx ctx, struct apfl_source_reader reader, apfl_stackidx name) +{ + 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); + } + + apfl_parser_ptr parser = apfl_parser_new( + ctx->gc.allocator, + apfl_tokenizer_as_token_source(tokenizer) + ); + if (parser == NULL) { + apfl_tokenizer_destroy(tokenizer); + apfl_raise_alloc_error(ctx); + } + + 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); + } + + for (;;) { + switch (apfl_parser_next(parser)) { + case APFL_PARSE_OK: { + struct apfl_error err; + if (!apfl_compile( + &ctx->gc, + apfl_parser_get_expr(parser), + &err, + ilist + )) { + apfl_drop(ctx, -1); + apfl_parser_destroy(parser); + apfl_tokenizer_destroy(tokenizer); + apfl_raise_error_object(ctx, err); + } + + break; + } + case APFL_PARSE_ERROR: + apfl_drop(ctx, -1); + apfl_parser_destroy(parser); + apfl_tokenizer_destroy(tokenizer); + apfl_raise_error_object(ctx, apfl_parser_get_error(parser)); + break; + case APFL_PARSE_EOF: + apfl_parser_destroy(parser); + apfl_tokenizer_destroy(tokenizer); + return; + } + } +} diff --git a/src/context.h b/src/context.h index a1c24f7..32c2b27 100644 --- a/src/context.h +++ b/src/context.h @@ -174,6 +174,9 @@ void apfl_stack_clear(apfl_ctx); struct apfl_value *apfl_stack_push_placeholder(apfl_ctx); bool apfl_move_string_onto_stack(apfl_ctx, struct apfl_string); +// Like apfl_tostring, but ensures it's a dynamically allocated string and returns the underlying string. +struct apfl_string *apfl_to_dynamic_string(apfl_ctx ctx, apfl_stackidx index); + /* Raise an error with a message formatted according to fmt. * * fmt allows placeholders in the form of `{:}`, where `:` diff --git a/src/eval.c b/src/eval.c index 5e1cb32..b06f89d 100644 --- a/src/eval.c +++ b/src/eval.c @@ -275,7 +275,13 @@ func_inner(apfl_ctx ctx, struct func_call_stack_entry *cse, size_t count) apfl_raise_alloc_error(ctx); } - if ((func_value->func = apfl_func_new(&ctx->gc, count, scope, cse->execution_line)) == NULL) { + if ((func_value->func = apfl_func_new( + &ctx->gc, + count, + scope, + cse->execution_line, + cse->instructions->filename + )) == NULL) { stack_must_drop(ctx, -1); apfl_raise_alloc_error(ctx); } @@ -1362,7 +1368,7 @@ iterative_runner_eval_expr_inner(apfl_iterative_runner runner, struct apfl_expr { apfl_ctx ctx = runner->ctx; - struct instruction_list *ilist = apfl_instructions_new(&ctx->gc, expr.position.line); + struct instruction_list *ilist = apfl_instructions_new(&ctx->gc, expr.position.line, NULL); if (ilist == NULL) { apfl_raise_alloc_error(ctx); } diff --git a/src/globals.c b/src/globals.c index 4f97201..878defe 100644 --- a/src/globals.c +++ b/src/globals.c @@ -376,16 +376,20 @@ impl_backtrace(apfl_ctx ctx) } } +static void +closefile(FILE **f) +{ + if (*f != NULL) { + fclose(*f); + *f = NULL; + } +} + static void file_onbeforecollect(void *opaque) { FILE **f = opaque; - if (*f == NULL) { - return; - } - - fclose(*f); - *f = NULL; + closefile(f); } static struct apfl_native_object_type file_object = { @@ -502,13 +506,41 @@ impl_fclose(apfl_ctx ctx) { ONE_ARG(ctx, "fclose"); FILE **fh = apfl_get_native_object(ctx, &file_object, -1); - if (*fh != NULL) { - fclose(*fh); - *fh = NULL; - } + closefile(fh); apfl_drop(ctx, -1); } +static void +loadfile(apfl_ctx ctx) +{ + ONE_ARG(ctx, "loadfile"); + apfl_copy(ctx, -1); + const char *filename = getcstring(ctx, -1); + + FILE **fh = apfl_push_native_object(ctx, &file_object); + *fh = fopen(filename, "rb"); + if (*fh == NULL) { + raise_errno(ctx); + } + apfl_drop(ctx, -2); // drop cstring + + apfl_load(ctx, apfl_stdio_source_reader(*fh), -2); + closefile(fh); + apfl_drop(ctx, -2); +} + +static void +loadstring(apfl_ctx ctx) +{ + ONE_ARG(ctx, "loadstring"); + apfl_tostring(ctx, -1); + apfl_push_const_string(ctx, "(loadstring)"); + + struct apfl_string_source_reader_data reader_data = apfl_string_source_reader_create(apfl_get_string(ctx, -2)); + apfl_load(ctx, apfl_string_source_reader(&reader_data), -1); + apfl_drop(ctx, -2); +} + static const struct global_def globals[] = { {"if", impl_if}, {"==", impl_eq}, @@ -536,6 +568,8 @@ static const struct global_def globals[] = { {"fread", impl_fread}, {"fwrite", impl_fwrite}, {"fclose", impl_fclose}, + {"loadfile", loadfile}, + {"loadstring", loadstring}, {NULL, NULL}, }; diff --git a/src/source_readers.c b/src/source_readers.c index 6adcef6..f380a79 100644 --- a/src/source_readers.c +++ b/src/source_readers.c @@ -4,7 +4,7 @@ #include "apfl.h" static bool -reader_callback(void *opaque, char *buf, size_t *len, bool need) +string_reader_callback(void *opaque, char *buf, size_t *len, bool need) { (void)need; @@ -32,7 +32,32 @@ apfl_string_source_reader_create(struct apfl_string_view sv) struct apfl_source_reader apfl_string_source_reader(struct apfl_string_source_reader_data *data) { return (struct apfl_source_reader) { - .callback = reader_callback, + .callback = string_reader_callback, .opaque = data, }; } + +static bool +stdio_reader_callback(void *opaque, char *buf, size_t *len, bool need) +{ + (void)need; + + FILE *f = opaque; + + size_t maxlen = *len; + *len = fread(buf, 1, maxlen, f); + if (*len == 0) { + return feof(f); + } + + return true; +} + +struct apfl_source_reader +apfl_stdio_source_reader(FILE *f) +{ + return (struct apfl_source_reader) { + .callback = stdio_reader_callback, + .opaque = f, + }; +} diff --git a/src/value.c b/src/value.c index 5f9edde..bcd8a58 100644 --- a/src/value.c +++ b/src/value.c @@ -224,7 +224,7 @@ apfl_type_name(enum apfl_value_type type) } struct function * -apfl_func_new(struct gc *gc, size_t cap, struct scope *scope, int line_defined) +apfl_func_new(struct gc *gc, size_t cap, struct scope *scope, int line_defined, struct apfl_string *filename) { struct subfunction *subfunctions = ALLOC_LIST(gc->allocator, struct subfunction, cap); if (subfunctions == NULL) { @@ -243,6 +243,7 @@ apfl_func_new(struct gc *gc, size_t cap, struct scope *scope, int line_defined) .scope = scope, .name = NULL, .line_defined = line_defined, + .filename = filename, }; return function; @@ -857,6 +858,9 @@ apfl_gc_func_traverse(struct function* function, gc_visitor cb, void *opaque) if (function->name != NULL) { cb(opaque, GC_OBJECT_FROM(function->name, GC_TYPE_STRING)); } + if (function->filename != NULL) { + cb(opaque, GC_OBJECT_FROM(function->filename, GC_TYPE_STRING)); + } } void diff --git a/src/value.h b/src/value.h index 771c691..70ff6d4 100644 --- a/src/value.h +++ b/src/value.h @@ -54,6 +54,7 @@ struct function { struct scope *scope; struct apfl_string *name; int line_defined; + struct apfl_string *filename; }; struct cfunction { @@ -144,7 +145,13 @@ bool apfl_dict_set_raw( size_t apfl_dict_len(struct dict_header *); void apfl_dict_deinit(struct dict_header *); -struct function *apfl_func_new(struct gc *, size_t cap, struct scope *, int line_defined); +struct function *apfl_func_new( + struct gc *, + size_t cap, + struct scope *, + int line_defined, + struct apfl_string *filename +); bool apfl_func_add_subfunc(struct function *, struct instruction_list *, struct matcher *); bool apfl_func_get_name(struct function *, struct apfl_string_view *name); void apfl_func_set_name(struct function *, struct apfl_string *name);