Define globals in apfl itself
We're now first building a standalone bytecode compiler `apflc` that will compile `globals.apfl` into bytecode and write it out as a C source file. When initializing a new context, that embedded bytecode will then get evaluated and the global scope will be populated from the dictionary returned by that bytecode.
This commit is contained in:
parent
baef5914cd
commit
25c872f4ee
17 changed files with 665 additions and 160 deletions
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(apfl VERSION 0.0.1 LANGUAGES C)
|
||||
enable_testing()
|
||||
add_subdirectory(src)
|
||||
|
|
|
|||
|
|
@ -4,37 +4,56 @@ set(CMAKE_C_EXTENSIONS OFF)
|
|||
option(BUILD_SHARED_LIBS "Build dynamic / shared libraries" ON)
|
||||
option(TEST_WITH_VALGRIND_MEMCHECK "Also run tests with valgrind / memcheck" ON)
|
||||
|
||||
add_library(apfl
|
||||
set(commonfiles
|
||||
alloc.c
|
||||
bytecode.c
|
||||
compile.c
|
||||
encode.c
|
||||
context.c
|
||||
error.c
|
||||
eval.c
|
||||
expr.c
|
||||
format.c
|
||||
gc.c
|
||||
hashmap.c
|
||||
io.c
|
||||
globals.c
|
||||
messages.c
|
||||
parser.c
|
||||
position.c
|
||||
resizable.c
|
||||
source_readers.c
|
||||
scope.c
|
||||
strings.c
|
||||
token.c
|
||||
tokenizer.c
|
||||
value.c
|
||||
)
|
||||
|
||||
add_library(apfl
|
||||
${commonfiles}
|
||||
builtins.c
|
||||
context.c
|
||||
eval.c
|
||||
scope.c
|
||||
|
||||
mod_globals.c
|
||||
)
|
||||
target_link_libraries(apfl PUBLIC m)
|
||||
|
||||
add_executable(apfl-bin main.c)
|
||||
target_link_libraries(apfl-bin PUBLIC apfl)
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
find_package(ApflApflcNative)
|
||||
else()
|
||||
add_executable(apflc apflc.c ${commonfiles} stubs_for_apflc.c)
|
||||
target_link_libraries(apflc PUBLIC m)
|
||||
export(TARGETS apflc FILE "${CMAKE_BINARY_DIR}/ApflApflcNativeConfig.cmake")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mod_globals.c"
|
||||
COMMAND apflc -c apfl_mod_globals "${CMAKE_CURRENT_SOURCE_DIR}/globals.apfl" "${CMAKE_CURRENT_BINARY_DIR}/mod_globals.c"
|
||||
DEPENDS apflc "${CMAKE_CURRENT_SOURCE_DIR}/globals.apfl"
|
||||
)
|
||||
|
||||
add_executable(functional-test-runner functional-test-runner.c)
|
||||
target_link_libraries(functional-test-runner PUBLIC apfl)
|
||||
|
||||
|
|
|
|||
|
|
@ -732,6 +732,9 @@ int apfl_cmp(apfl_ctx, apfl_stackidx a, apfl_stackidx b);
|
|||
// Push a C function onto the stack with nslots slots (initialized to nil)
|
||||
void apfl_push_cfunc(apfl_ctx, apfl_cfunc, size_t nslots);
|
||||
|
||||
// Set the name of a function. name is dropped, func will be on the top of the stack afterwards
|
||||
void apfl_set_func_name(apfl_ctx, apfl_stackidx func, apfl_stackidx name);
|
||||
|
||||
void apfl_cfunc_getslot(apfl_ctx, apfl_stackidx cfunc, apfl_slotidx);
|
||||
void apfl_cfunc_self_getslot(apfl_ctx, apfl_slotidx);
|
||||
void apfl_cfunc_setslot(apfl_ctx, apfl_stackidx cfunc, apfl_slotidx, apfl_stackidx value);
|
||||
|
|
|
|||
276
src/apflc.c
Normal file
276
src/apflc.c
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "apfl.h"
|
||||
#include "bytecode.h"
|
||||
#include "compile.h"
|
||||
#include "format.h"
|
||||
#include "gc.h"
|
||||
#include "strings.h"
|
||||
|
||||
struct c_writer_data {
|
||||
struct apfl_io_writer w;
|
||||
size_t written_len;
|
||||
};
|
||||
|
||||
static const unsigned char hex[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
};
|
||||
|
||||
static bool
|
||||
write_as_c_source(void *opaque, const unsigned char *buf, size_t len)
|
||||
{
|
||||
struct c_writer_data *data = opaque;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (data->written_len % 16 == 0) {
|
||||
if (!apfl_io_write_byte(data->w, '\n')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char out[] = {'0', 'x', hex[(buf[i] >> 4) & 0x0F], hex[buf[i] & 0x0F], ','};
|
||||
|
||||
if (!apfl_io_write_string_view(
|
||||
data->w,
|
||||
(struct apfl_string_view) { .bytes = out, .len = sizeof(out), }
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(data->written_len)++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
c_source_prelude(struct apfl_io_writer w)
|
||||
{
|
||||
return apfl_io_write_string(
|
||||
w,
|
||||
"#include <stddef.h>\n"
|
||||
// TODO: This is a bit hackish, the generated C code should include
|
||||
// apfl.h instead of declaring the string view struct itself. However,
|
||||
// since the code is generated, it does not live in the same directory
|
||||
// as apfl.h, so our usual `#include "apfl.h"` doesn't work.
|
||||
"struct apfl_string_view {\n"
|
||||
" const unsigned char *bytes;\n"
|
||||
" size_t len;\n"
|
||||
"};\n"
|
||||
"static const unsigned char bytecode[] = {"
|
||||
);
|
||||
}
|
||||
|
||||
static bool
|
||||
c_source_epilogue(struct apfl_io_writer w, const char *function_name)
|
||||
{
|
||||
return apfl_io_write_string(w,
|
||||
"\n};\n"
|
||||
"struct apfl_string_view\n"
|
||||
)
|
||||
&& apfl_io_write_string(w, function_name)
|
||||
&& apfl_io_write_string(w,
|
||||
"(void)\n"
|
||||
"{\n"
|
||||
" return (struct apfl_string_view) {\n"
|
||||
" .bytes = bytecode,\n"
|
||||
" .len = sizeof(bytecode),\n"
|
||||
" };\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
|
||||
struct roots {
|
||||
struct instruction_list *ilist;
|
||||
struct apfl_string *name;
|
||||
};
|
||||
|
||||
static void
|
||||
getroots(void *own_opaque, gc_visitor visitor, void *visitor_opaque)
|
||||
{
|
||||
struct roots *roots = own_opaque;
|
||||
if (roots->ilist != NULL) {
|
||||
visitor(visitor_opaque, GC_OBJECT_FROM(roots->ilist, GC_TYPE_INSTRUCTIONS));
|
||||
}
|
||||
if (roots->name != NULL) {
|
||||
visitor(visitor_opaque, GC_OBJECT_FROM(roots->name, GC_TYPE_STRING));
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_next_string_from_arg(const char *progname, const char ***argpp)
|
||||
{
|
||||
if (**argpp == NULL) {
|
||||
fprintf(stderr, "Usage: %s [-c function_name] input output\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *filename = **argpp;
|
||||
(*argpp)++;
|
||||
return filename;
|
||||
}
|
||||
|
||||
static FILE *
|
||||
openfile(const char *progname, const char *filename, const char *mode)
|
||||
{
|
||||
FILE *f = fopen(filename, mode);
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "%s: Could not open %s: %s\n", progname, filename, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static void
|
||||
closefile(FILE *f)
|
||||
{
|
||||
if (f != NULL) {
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
compile(
|
||||
const char *progname,
|
||||
struct gc *gc,
|
||||
struct apfl_io_reader r,
|
||||
struct instruction_list *ilist
|
||||
) {
|
||||
apfl_tokenizer_ptr tokenizer = apfl_tokenizer_new(gc->allocator, apfl_io_reader_as_source_reader(&r));
|
||||
if (tokenizer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
apfl_parser_ptr parser = apfl_parser_new(
|
||||
gc->allocator,
|
||||
apfl_tokenizer_as_token_source(tokenizer)
|
||||
);
|
||||
if (parser == NULL) {
|
||||
apfl_tokenizer_destroy(tokenizer);
|
||||
fprintf(stderr, "%s: Failed to init parser\n", progname);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct apfl_error error;
|
||||
if (!apfl_compile_whole_file(
|
||||
gc,
|
||||
parser,
|
||||
&error,
|
||||
ilist
|
||||
)) {
|
||||
fprintf(stderr, "%s: Could not compile file:\n", progname);
|
||||
apfl_error_print(error, stderr);
|
||||
|
||||
apfl_parser_destroy(parser);
|
||||
apfl_tokenizer_destroy(tokenizer);
|
||||
return false;
|
||||
}
|
||||
|
||||
apfl_parser_destroy(parser);
|
||||
apfl_tokenizer_destroy(tokenizer);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
(void)argc;
|
||||
const char **argp = argv+1;
|
||||
|
||||
FILE *in = NULL;
|
||||
FILE *out = NULL;
|
||||
|
||||
const char *c_function_name = NULL;
|
||||
if (*argp != NULL && apfl_string_eq(*argp, "-c")) {
|
||||
argp++;
|
||||
if ((c_function_name = get_next_string_from_arg(argv[0], &argp)) == NULL) {
|
||||
goto error_before_gc;
|
||||
}
|
||||
}
|
||||
|
||||
const char *name_in = NULL;
|
||||
const char *name_out = NULL;
|
||||
if (
|
||||
(name_in = get_next_string_from_arg(argv[0], &argp)) == NULL
|
||||
|| (name_out = get_next_string_from_arg(argv[0], &argp)) == NULL
|
||||
) {
|
||||
goto error_before_gc;
|
||||
}
|
||||
|
||||
int rv = 1;
|
||||
|
||||
if (
|
||||
(in = openfile(argv[0], name_in, "rb")) == NULL
|
||||
|| (out = openfile(argv[0], name_out, "wb")) == NULL
|
||||
) {
|
||||
goto error_before_gc;
|
||||
}
|
||||
|
||||
struct apfl_io_reader r = apfl_io_file_reader(in);
|
||||
struct apfl_io_writer w_raw = apfl_io_file_writer(out);
|
||||
|
||||
struct roots roots = {
|
||||
.ilist = NULL,
|
||||
.name = NULL,
|
||||
};
|
||||
|
||||
struct gc gc;
|
||||
apfl_gc_init(&gc, apfl_allocator_default(), getroots, &roots);
|
||||
|
||||
if ((roots.name = apfl_string_copy_to_new_gc_string(
|
||||
&gc,
|
||||
apfl_string_view_from(name_in)
|
||||
)) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((roots.ilist = apfl_instructions_new(&gc, 1, roots.name)) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!compile(argv[0], &gc, r, roots.ilist)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (c_function_name != NULL && !c_source_prelude(w_raw)) {
|
||||
fprintf(stderr, "%s: IO error\n", argv[0]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct apfl_io_writer bytecode_writer = w_raw;
|
||||
struct c_writer_data c_writer_data = {
|
||||
.w = w_raw,
|
||||
.written_len = 0,
|
||||
};
|
||||
if (c_function_name != NULL) {
|
||||
bytecode_writer = (struct apfl_io_writer) {
|
||||
.opaque = &c_writer_data,
|
||||
.write = write_as_c_source,
|
||||
};
|
||||
}
|
||||
|
||||
if (!apfl_bytecode_serialize(
|
||||
gc.allocator,
|
||||
bytecode_writer,
|
||||
roots.ilist
|
||||
)) {
|
||||
fprintf(stderr, "%s: Could not serialize bytecode\n", argv[0]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (c_function_name != NULL && !c_source_epilogue(w_raw, c_function_name)) {
|
||||
fprintf(stderr, "%s: IO error\n", argv[0]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
error:
|
||||
apfl_gc_deinit(&gc);
|
||||
error_before_gc:
|
||||
closefile(in);
|
||||
closefile(out);
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -6,9 +6,10 @@
|
|||
#include "apfl.h"
|
||||
|
||||
#include "alloc.h"
|
||||
#include "builtins.h"
|
||||
#include "bytecode.h"
|
||||
#include "context.h"
|
||||
#include "globals.h"
|
||||
#include "modules.h"
|
||||
#include "scope.h"
|
||||
|
||||
#define TRY_FORMAT(ctx, x) \
|
||||
|
|
@ -583,42 +584,62 @@ unserialize_bytecode(apfl_ctx ctx)
|
|||
apfl_load_bytecode(ctx, r);
|
||||
}
|
||||
|
||||
static const struct global_def globals[] = {
|
||||
{"if", impl_if},
|
||||
{"==", impl_eq},
|
||||
{">", impl_gt},
|
||||
{"<", impl_lt},
|
||||
{">=", impl_ge},
|
||||
{"<=", impl_le},
|
||||
{"+", impl_plus},
|
||||
{"-", impl_minus},
|
||||
{"*", impl_mult},
|
||||
{"/", impl_div},
|
||||
{"&", impl_concat},
|
||||
{"join", impl_join},
|
||||
{"print", print},
|
||||
{"dump", dump},
|
||||
{"disasm", disasm},
|
||||
{"tostring", tostring},
|
||||
{"not", not},
|
||||
{"len", len},
|
||||
{"type", type},
|
||||
{"while", impl_while},
|
||||
{"gc", impl_gc},
|
||||
{"backtrace", impl_backtrace},
|
||||
{"fopen", impl_fopen},
|
||||
{"fread", impl_fread},
|
||||
{"fwrite", impl_fwrite},
|
||||
{"fclose", impl_fclose},
|
||||
{"loadfile", loadfile},
|
||||
{"loadstring", loadstring},
|
||||
{"-serialize-bytecode", serialize_bytecode},
|
||||
{"-unserialize-bytecode", unserialize_bytecode},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
const struct global_def *
|
||||
apfl_globals(void)
|
||||
static void
|
||||
set_func_name(apfl_ctx ctx)
|
||||
{
|
||||
return globals;
|
||||
size_t argc = apfl_len(ctx, 0);
|
||||
if (argc != 2) {
|
||||
apfl_raise_const_error(ctx, "set-func-name needs exactly 2 arguments");
|
||||
}
|
||||
apfl_get_list_member_by_index(ctx, 0, 0);
|
||||
apfl_get_list_member_by_index(ctx, 0, 1);
|
||||
apfl_set_func_name(ctx, -2, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
add_builtin(apfl_ctx ctx, const char *name, apfl_cfunc func)
|
||||
{
|
||||
apfl_push_const_string(ctx, name);
|
||||
apfl_push_cfunc(ctx, func, 0);
|
||||
apfl_push_const_string(ctx, name);
|
||||
apfl_set_func_name(ctx, -2, -1);
|
||||
apfl_dict_set(ctx, -3, -2, -1);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_builtins(apfl_ctx ctx)
|
||||
{
|
||||
apfl_dict_create(ctx);
|
||||
|
||||
add_builtin(ctx, "if", impl_if);
|
||||
add_builtin(ctx, "==", impl_eq);
|
||||
add_builtin(ctx, ">", impl_gt);
|
||||
add_builtin(ctx, "<", impl_lt);
|
||||
add_builtin(ctx, ">=", impl_ge);
|
||||
add_builtin(ctx, "<=", impl_le);
|
||||
add_builtin(ctx, "+", impl_plus);
|
||||
add_builtin(ctx, "-", impl_minus);
|
||||
add_builtin(ctx, "*", impl_mult);
|
||||
add_builtin(ctx, "/", impl_div);
|
||||
add_builtin(ctx, "&", impl_concat);
|
||||
add_builtin(ctx, "join", impl_join);
|
||||
add_builtin(ctx, "print", print);
|
||||
add_builtin(ctx, "dump", dump);
|
||||
add_builtin(ctx, "disasm", disasm);
|
||||
add_builtin(ctx, "tostring", tostring);
|
||||
add_builtin(ctx, "not", not);
|
||||
add_builtin(ctx, "len", len);
|
||||
add_builtin(ctx, "type", type);
|
||||
add_builtin(ctx, "while", impl_while);
|
||||
add_builtin(ctx, "gc", impl_gc);
|
||||
add_builtin(ctx, "backtrace", impl_backtrace);
|
||||
add_builtin(ctx, "fopen", impl_fopen);
|
||||
add_builtin(ctx, "fread", impl_fread);
|
||||
add_builtin(ctx, "fwrite", impl_fwrite);
|
||||
add_builtin(ctx, "fclose", impl_fclose);
|
||||
add_builtin(ctx, "loadfile", loadfile);
|
||||
add_builtin(ctx, "loadstring", loadstring);
|
||||
add_builtin(ctx, "-serialize-bytecode", serialize_bytecode);
|
||||
add_builtin(ctx, "-unserialize-bytecode", unserialize_bytecode);
|
||||
add_builtin(ctx, "set-func-name", set_func_name);
|
||||
}
|
||||
16
src/builtins.h
Normal file
16
src/builtins.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef APFL_BUILTINS_H
|
||||
#define APFL_BUILTINS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
void apfl_builtins(apfl_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -952,3 +952,33 @@ apfl_compile(struct gc *gc, struct apfl_expr expr, struct apfl_error *error_out,
|
|||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
apfl_compile_whole_file(
|
||||
struct gc *gc,
|
||||
struct apfl_parser *parser,
|
||||
struct apfl_error *error,
|
||||
struct instruction_list *out
|
||||
) {
|
||||
for (;;) {
|
||||
switch (apfl_parser_next(parser)) {
|
||||
case APFL_PARSE_OK: {
|
||||
if (!apfl_compile(
|
||||
gc,
|
||||
apfl_parser_get_expr(parser),
|
||||
error,
|
||||
out
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case APFL_PARSE_ERROR:
|
||||
*error = apfl_parser_get_error(parser);
|
||||
return false;
|
||||
case APFL_PARSE_EOF:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ extern "C" {
|
|||
#include "gc.h"
|
||||
|
||||
bool apfl_compile(struct gc *, struct apfl_expr, struct apfl_error *, struct instruction_list *);
|
||||
bool apfl_compile_whole_file(
|
||||
struct gc *gc,
|
||||
struct apfl_parser *parser,
|
||||
struct apfl_error *error,
|
||||
struct instruction_list *out
|
||||
);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
167
src/context.c
167
src/context.c
|
|
@ -6,18 +6,18 @@
|
|||
|
||||
#include "apfl.h"
|
||||
#include "alloc.h"
|
||||
#include "builtins.h"
|
||||
#include "compile.h"
|
||||
#include "context.h"
|
||||
#include "gc.h"
|
||||
#include "globals.h"
|
||||
#include "hashmap.h"
|
||||
#include "modules.h"
|
||||
#include "resizable.h"
|
||||
#include "strings.h"
|
||||
#include "value.h"
|
||||
|
||||
static bool try_push_const_string(apfl_ctx ctx, const char *string);
|
||||
static bool current_stack_move_to_top(apfl_ctx, apfl_stackidx);
|
||||
static struct apfl_string *new_copied_string(struct gc *, struct apfl_string_view);
|
||||
|
||||
noreturn static void
|
||||
panic(apfl_ctx ctx, enum apfl_result result)
|
||||
|
|
@ -809,54 +809,45 @@ iterative_runners_list_new(void)
|
|||
};
|
||||
}
|
||||
|
||||
static void
|
||||
init_globals_protected(apfl_ctx ctx, void *opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
struct apfl_io_string_reader_data reader = apfl_io_string_reader_create(apfl_mod_globals());
|
||||
struct apfl_io_reader r = apfl_io_string_reader(&reader);
|
||||
apfl_load_bytecode(ctx, r);
|
||||
apfl_list_create(ctx, 0);
|
||||
apfl_call(ctx, -2, -1);
|
||||
apfl_list_create(ctx, 1);
|
||||
apfl_builtins(ctx);
|
||||
apfl_list_append(ctx, -2, -1);
|
||||
apfl_call(ctx, -2, -1);
|
||||
|
||||
struct apfl_value val = apfl_stack_must_get(ctx, -1);
|
||||
if (val.type != VALUE_DICT) {
|
||||
apfl_raise_const_error(ctx, "Globals didn't return a dict");
|
||||
}
|
||||
|
||||
HASHMAP_EACH(&val.dict->map, cur) {
|
||||
struct apfl_value *k = apfl_hashmap_cursor_peek_key(cur);
|
||||
apfl_stack_must_push(ctx, *k);
|
||||
struct apfl_string *name = apfl_to_dynamic_string(ctx, -1);
|
||||
|
||||
struct apfl_value *v = apfl_hashmap_cursor_peek_value(cur);
|
||||
|
||||
if (!apfl_scope_set(&ctx->gc, ctx->globals, name, *v)) {
|
||||
apfl_raise_alloc_error(ctx);
|
||||
}
|
||||
|
||||
apfl_drop(ctx, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
init_globals(apfl_ctx ctx)
|
||||
{
|
||||
struct gc *gc = &ctx->gc;
|
||||
|
||||
size_t tmproots = apfl_gc_tmproots_begin(gc);
|
||||
|
||||
for (
|
||||
const struct global_def *global = apfl_globals();
|
||||
global->name != NULL && global->func != NULL;
|
||||
global++
|
||||
) {
|
||||
struct cfunction *cfunc = apfl_cfunc_new(gc, global->func, 0);
|
||||
if (cfunc == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!apfl_gc_tmproot_add(gc, GC_OBJECT_FROM(cfunc, GC_TYPE_CFUNC))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct apfl_string *name = new_copied_string(gc, apfl_string_view_from(global->name));
|
||||
if (name == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!apfl_gc_tmproot_add(gc, GC_OBJECT_FROM(name, GC_TYPE_STRING))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// TODO: There should also be an API for setting the name of a cfunc
|
||||
cfunc->name = name;
|
||||
|
||||
if (!apfl_scope_set(gc, ctx->globals, name, (struct apfl_value) {
|
||||
.type = VALUE_CFUNC,
|
||||
.cfunc = cfunc,
|
||||
})) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
apfl_gc_tmproots_restore(gc, tmproots);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
apfl_gc_tmproots_restore(gc, tmproots);
|
||||
return false;
|
||||
return apfl_do_protected(ctx, init_globals_protected, NULL, NULL) == APFL_RESULT_OK;
|
||||
}
|
||||
|
||||
apfl_ctx
|
||||
|
|
@ -1061,23 +1052,6 @@ apfl_push_number(apfl_ctx ctx, apfl_number num)
|
|||
});
|
||||
}
|
||||
|
||||
static struct apfl_string *
|
||||
new_copied_string(struct gc *gc, struct apfl_string_view sv)
|
||||
{
|
||||
struct apfl_string s = apfl_string_blank();
|
||||
if (!apfl_string_copy(gc->allocator, &s, sv)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct apfl_string *out = apfl_string_move_into_new_gc_string(gc, &s);
|
||||
if (out == NULL) {
|
||||
apfl_string_deinit(gc->allocator, &s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_push_string_view_copy(apfl_ctx ctx, struct apfl_string_view sv)
|
||||
{
|
||||
|
|
@ -1085,7 +1059,7 @@ apfl_push_string_view_copy(apfl_ctx ctx, struct apfl_string_view sv)
|
|||
ctx,
|
||||
VALUE_STRING,
|
||||
string,
|
||||
new_copied_string(&ctx->gc, sv)
|
||||
apfl_string_copy_to_new_gc_string(&ctx->gc, sv)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1697,6 +1671,29 @@ apfl_push_cfunc(apfl_ctx ctx, apfl_cfunc cfunc, size_t nslots)
|
|||
)
|
||||
}
|
||||
|
||||
void
|
||||
apfl_set_func_name(apfl_ctx ctx, apfl_stackidx func_index, apfl_stackidx name_index)
|
||||
{
|
||||
apfl_multi_move_to_top_of_stack(ctx, 2, (apfl_stackidx []) {func_index, name_index});
|
||||
if (apfl_get_type(ctx, -1) != APFL_VALUE_STRING) {
|
||||
apfl_raise_errorfmt(ctx, "Expected string, got {stack:type}", -1);
|
||||
}
|
||||
struct apfl_value func = apfl_stack_must_get(ctx, -2);
|
||||
struct apfl_string *name = apfl_to_dynamic_string(ctx, -1);
|
||||
switch (func.type) {
|
||||
case VALUE_FUNC:
|
||||
func.func->name = name;
|
||||
break;
|
||||
case VALUE_CFUNC:
|
||||
func.cfunc->name = name;
|
||||
break;
|
||||
default:
|
||||
apfl_raise_errorfmt(ctx, "Expected function, got {value:type}", func);
|
||||
}
|
||||
|
||||
apfl_drop(ctx, -1);
|
||||
}
|
||||
|
||||
static noreturn void
|
||||
raise_no_cfunc(apfl_ctx ctx)
|
||||
{
|
||||
|
|
@ -2136,35 +2133,17 @@ apfl_load(apfl_ctx ctx, struct apfl_source_reader reader, apfl_stackidx name)
|
|||
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;
|
||||
}
|
||||
struct apfl_error err;
|
||||
if (!apfl_compile_whole_file(
|
||||
&ctx->gc,
|
||||
parser,
|
||||
&err,
|
||||
ilist
|
||||
)) {
|
||||
apfl_drop(ctx, -1);
|
||||
apfl_parser_destroy(parser);
|
||||
apfl_tokenizer_destroy(tokenizer);
|
||||
apfl_raise_error_object(ctx, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
101
src/globals.apfl
Normal file
101
src/globals.apfl
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
{builtins ->
|
||||
if := builtins.if
|
||||
== := builtins.==
|
||||
> := builtins.>
|
||||
< := builtins.<
|
||||
>= := builtins.>=
|
||||
<= := builtins.<=
|
||||
+ := builtins.+
|
||||
- := builtins.-
|
||||
* := builtins.*
|
||||
/ := builtins./
|
||||
join := builtins.join
|
||||
print := builtins.print
|
||||
dump := builtins.dump
|
||||
disasm := builtins.disasm
|
||||
tostring := builtins.tostring
|
||||
not := builtins.not
|
||||
len := builtins.len
|
||||
type := builtins.type
|
||||
while := builtins.while
|
||||
gc := builtins.gc
|
||||
backtrace := builtins.backtrace
|
||||
fopen := builtins.fopen
|
||||
fread := builtins.fread
|
||||
fwrite := builtins.fwrite
|
||||
fclose := builtins.fclose
|
||||
loadfile := builtins.loadfile
|
||||
loadstring := builtins.loadstring
|
||||
-serialize-bytecode := builtins.-serialize-bytecode
|
||||
-unserialize-bytecode := builtins.-unserialize-bytecode
|
||||
|
||||
-named := { name f ->
|
||||
builtins.set-func-name f name
|
||||
}
|
||||
|
||||
& := { ~strings ->
|
||||
join "" strings
|
||||
}
|
||||
|
||||
partial := { f ~a1 ->
|
||||
{ ~a2 ->
|
||||
f ~a1 ~a2
|
||||
}
|
||||
}
|
||||
|
||||
compose := {
|
||||
f -> f
|
||||
f ~fs ->
|
||||
fs-composed := compose ~fs
|
||||
{ ~args ->
|
||||
f (fs-composed ~args)
|
||||
}
|
||||
}
|
||||
|
||||
!= := -named '!= (compose not ==)
|
||||
!> := -named '!> (compose not >)
|
||||
!< := -named '!< (compose not <)
|
||||
!>= := -named '!>= (compose not >=)
|
||||
!<= := -named '!<= (compose not <=)
|
||||
|
||||
# Dictionary of exported functions
|
||||
[
|
||||
'if -> if
|
||||
'== -> ==
|
||||
'> -> >
|
||||
'< -> <
|
||||
'>= -> >=
|
||||
'<= -> <=
|
||||
'+ -> +
|
||||
'- -> -
|
||||
'* -> *
|
||||
'/ -> /
|
||||
'join -> join
|
||||
'print -> print
|
||||
'dump -> dump
|
||||
'disasm -> disasm
|
||||
'tostring -> tostring
|
||||
'not -> not
|
||||
'len -> len
|
||||
'type -> type
|
||||
'while -> while
|
||||
'gc -> gc
|
||||
'backtrace -> backtrace
|
||||
'fopen -> fopen
|
||||
'fread -> fread
|
||||
'fwrite -> fwrite
|
||||
'fclose -> fclose
|
||||
'loadfile -> loadfile
|
||||
'loadstring -> loadstring
|
||||
'-serialize-bytecode -> -serialize-bytecode
|
||||
'-unserialize-bytecode -> -unserialize-bytecode
|
||||
'& -> &
|
||||
'partial -> partial
|
||||
'compose -> compose
|
||||
'!= -> !=
|
||||
'!> -> !>
|
||||
'!< -> !<
|
||||
'!>= -> !>=
|
||||
'!<= -> !<=
|
||||
]
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef APFL_GLOBALS_H
|
||||
#define APFL_GLOBALS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
struct global_def {
|
||||
const char *name;
|
||||
apfl_cfunc func;
|
||||
};
|
||||
|
||||
const struct global_def *apfl_globals(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
16
src/modules.h
Normal file
16
src/modules.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef APFL_MODULES_H
|
||||
#define APFL_MODULES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "apfl.h"
|
||||
|
||||
struct apfl_string_view apfl_mod_globals(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -183,6 +183,24 @@ apfl_string_move_into_new_gc_string(struct gc *gc, struct apfl_string *in)
|
|||
return str;
|
||||
}
|
||||
|
||||
struct apfl_string *
|
||||
apfl_string_copy_to_new_gc_string(struct gc *gc, struct apfl_string_view sv)
|
||||
{
|
||||
struct apfl_string s = apfl_string_blank();
|
||||
if (!apfl_string_copy(gc->allocator, &s, sv)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct apfl_string *out = apfl_string_move_into_new_gc_string(gc, &s);
|
||||
if (out == NULL) {
|
||||
apfl_string_deinit(gc->allocator, &s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
struct apfl_string_view
|
||||
apfl_string_view_offset(struct apfl_string_view sv, size_t off)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ extern "C" {
|
|||
#include "gc.h"
|
||||
|
||||
struct apfl_string *apfl_string_move_into_new_gc_string(struct gc *gc, struct apfl_string *in);
|
||||
struct apfl_string *apfl_string_copy_to_new_gc_string(struct gc *gc, struct apfl_string_view sv);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
32
src/stubs_for_apflc.c
Normal file
32
src/stubs_for_apflc.c
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "context.h"
|
||||
#include "scope.h"
|
||||
|
||||
#define GC_TRAVERSE_STUB(name, T) \
|
||||
void \
|
||||
name(T arg, gc_visitor visitor, void *opaque) \
|
||||
{ \
|
||||
(void)arg; \
|
||||
(void)visitor; \
|
||||
(void)opaque; \
|
||||
\
|
||||
assert(false && "Stub " #name " called"); \
|
||||
}
|
||||
|
||||
#define GC_DEINIT_STUB(name, T) \
|
||||
void name(struct apfl_allocator allocator, T arg) \
|
||||
{ \
|
||||
(void)allocator; \
|
||||
(void)arg; \
|
||||
\
|
||||
assert(false && "Stub " #name " called"); \
|
||||
}
|
||||
|
||||
GC_TRAVERSE_STUB(apfl_gc_var_traverse, struct apfl_value *)
|
||||
GC_TRAVERSE_STUB(apfl_gc_scope_traverse, struct scope *)
|
||||
GC_TRAVERSE_STUB(apfl_gc_matcher_traverse, struct matcher *)
|
||||
|
||||
GC_DEINIT_STUB(apfl_scope_deinit, struct scope *)
|
||||
GC_DEINIT_STUB(apfl_matcher_deinit, struct matcher *)
|
||||
2
webpage/.gitignore
vendored
Normal file
2
webpage/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
build/
|
||||
build-native/
|
||||
|
|
@ -1,10 +1,16 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
cd playground
|
||||
rm -rf build-native
|
||||
mkdir build-native
|
||||
cd build-native
|
||||
cmake ../../../CMakeLists.txt
|
||||
make -j"$(nproc)" apflc
|
||||
cd ..
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
emcmake cmake -DCMAKE_C_FLAGS="-O2" ../../../CMakeLists.txt
|
||||
emcmake cmake -DCMAKE_C_FLAGS="-O2" -DBUILD_SHARED_LIBS=NO -DApflApflcNative_DIR="$(pwd)/../build-native/" ../../../CMakeLists.txt
|
||||
emmake make -j"$(nproc)" apfl
|
||||
cd ..
|
||||
emcc -sASYNCIFY -O3 -oplayground.js playground.c build/src/libapfl.a
|
||||
|
|
|
|||
Loading…
Reference in a new issue