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:
Laria 2023-03-05 22:55:20 +01:00
parent baef5914cd
commit 25c872f4ee
17 changed files with 665 additions and 160 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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
'!= -> !=
'!> -> !>
'!< -> !<
'!>= -> !>=
'!<= -> !<=
]
}

View file

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

View file

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

View file

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

@ -0,0 +1,2 @@
build/
build-native/

View file

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