Get something working
Some checks failed
CMake / ubuntu-latest - shared=OFF, pthread=OFF, posix=OFF (push) Failing after 13s
CMake / ubuntu-latest - shared=ON, pthread=OFF, posix=OFF (push) Failing after 16s
CMake / ubuntu-latest - shared=OFF, pthread=OFF, posix=ON (push) Failing after 12s
CMake / ubuntu-latest - shared=ON, pthread=OFF, posix=ON (push) Failing after 14s
CMake / ubuntu-latest - shared=OFF, pthread=ON, posix=ON (push) Failing after 16s
CMake / ubuntu-latest - shared=ON, pthread=ON, posix=ON (push) Failing after 13s
Some checks failed
CMake / ubuntu-latest - shared=OFF, pthread=OFF, posix=OFF (push) Failing after 13s
CMake / ubuntu-latest - shared=ON, pthread=OFF, posix=OFF (push) Failing after 16s
CMake / ubuntu-latest - shared=OFF, pthread=OFF, posix=ON (push) Failing after 12s
CMake / ubuntu-latest - shared=ON, pthread=OFF, posix=ON (push) Failing after 14s
CMake / ubuntu-latest - shared=OFF, pthread=ON, posix=ON (push) Failing after 16s
CMake / ubuntu-latest - shared=ON, pthread=ON, posix=ON (push) Failing after 13s
Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
@@ -2,36 +2,47 @@ cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(DCFG C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
option(DCFG_PTHREAD_SUPPORT "Enable pthreads support" ON)
|
||||
option(DCFG_POSIX_SUPPORT "Enable POSIX support" ON)
|
||||
option(DCFG_BUILD_PROGRAMS "Build DCFG example programs" ON)
|
||||
option(DCFG_ASAN "Enable AddressSanitizer" OFF)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
if(DCFG_ASAN)
|
||||
if(MSVC)
|
||||
set(ASAN_COMPILE_FLAGS /fsanitize=address)
|
||||
set(ASAN_LINK_FLAGS /fsanitize=address)
|
||||
else()
|
||||
set(ASAN_COMPILE_FLAGS -fsanitize=address -fno-omit-frame-pointer)
|
||||
set(ASAN_LINK_FLAGS -fsanitize=address)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Shared library
|
||||
add_library(${PROJECT_NAME}_shared SHARED ${SRC_DIR}/dcfg.c)
|
||||
target_include_directories(${PROJECT_NAME}_shared PUBLIC ${INCLUDE_DIR})
|
||||
set_target_properties(${PROJECT_NAME}_shared PROPERTIES OUTPUT_NAME "dcfg")
|
||||
target_include_directories(${PROJECT_NAME}_shared PUBLIC ${INCLUDE_DIR})
|
||||
set_target_properties(${PROJECT_NAME}_shared PROPERTIES OUTPUT_NAME "dcfg")
|
||||
|
||||
# Static library
|
||||
add_library(${PROJECT_NAME}_static STATIC ${SRC_DIR}/dcfg.c)
|
||||
target_include_directories(${PROJECT_NAME}_static PUBLIC ${INCLUDE_DIR})
|
||||
set_target_properties(${PROJECT_NAME}_static PROPERTIES OUTPUT_NAME "dcfg")
|
||||
target_include_directories(${PROJECT_NAME}_static PUBLIC ${INCLUDE_DIR})
|
||||
set_target_properties(${PROJECT_NAME}_static PROPERTIES OUTPUT_NAME "dcfg")
|
||||
|
||||
# Common settings for both libraries
|
||||
foreach(TARGET ${PROJECT_NAME}_shared ${PROJECT_NAME}_static)
|
||||
if(DCFG_PTHREAD_SUPPORT)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(${TARGET} PRIVATE Threads::Threads)
|
||||
target_compile_definitions(${TARGET} PRIVATE DCFG_PTHREAD_SUPPORT)
|
||||
endif()
|
||||
|
||||
if(DCFG_POSIX_SUPPORT)
|
||||
target_compile_definitions(${TARGET} PRIVATE DCFG_POSIX_SUPPORT)
|
||||
endif()
|
||||
@@ -45,13 +56,17 @@ foreach(TARGET ${PROJECT_NAME}_shared ${PROJECT_NAME}_static)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(${TARGET} PRIVATE m)
|
||||
endif()
|
||||
|
||||
# AddressSanitizer
|
||||
if(DCFG_ASAN)
|
||||
target_compile_options(${TARGET} PRIVATE ${ASAN_COMPILE_FLAGS})
|
||||
target_link_libraries(${TARGET} PRIVATE ${ASAN_LINK_FLAGS})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Install libraries
|
||||
install(TARGETS ${PROJECT_NAME}_shared ${PROJECT_NAME}_static DESTINATION lib)
|
||||
install(DIRECTORY ${INCLUDE_DIR}/ DESTINATION include)
|
||||
|
||||
# Build example programs
|
||||
if(DCFG_BUILD_PROGRAMS)
|
||||
set(PROGRAMS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/programs)
|
||||
file(GLOB PROGRAM_SOURCES "${PROGRAMS_DIR}/*.c")
|
||||
@@ -61,14 +76,22 @@ if(DCFG_BUILD_PROGRAMS)
|
||||
add_executable(${PROG_NAME} ${PROG_SRC})
|
||||
target_include_directories(${PROG_NAME} PRIVATE ${INCLUDE_DIR})
|
||||
target_link_libraries(${PROG_NAME} PRIVATE ${PROJECT_NAME}_shared)
|
||||
|
||||
if(DCFG_PTHREAD_SUPPORT)
|
||||
target_link_libraries(${PROG_NAME} PRIVATE Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(${PROG_NAME} PRIVATE /W4 /permissive-)
|
||||
else()
|
||||
target_compile_options(${PROG_NAME} PRIVATE -Wall -Wextra -pedantic -Werror -Wno-newline-eof -Wno-language-extension-token)
|
||||
endif()
|
||||
|
||||
if(DCFG_ASAN)
|
||||
target_compile_options(${PROG_NAME} PRIVATE ${ASAN_COMPILE_FLAGS})
|
||||
target_link_libraries(${PROG_NAME} PRIVATE ${ASAN_LINK_FLAGS})
|
||||
endif()
|
||||
|
||||
install(TARGETS ${PROG_NAME} DESTINATION bin)
|
||||
endforeach()
|
||||
endif()
|
||||
|
@@ -75,6 +75,8 @@ typedef enum dcfg_ValueKind {
|
||||
dcfg_ValueType_FunctionCall,
|
||||
} dcfg_ValueType;
|
||||
|
||||
typedef dcfg_Value *(*dcfg_BuiltIn)(dcfg_Value **argv, size_t argc);
|
||||
|
||||
typedef uint64_t dcfg_Version;
|
||||
#define DCFG_GET_MAJOR(v) (((v) >> 48) & 0xFFFF)
|
||||
#define DCFG_GET_MINOR(v) (((v) >> 32) & 0xFFFF)
|
||||
@@ -140,6 +142,10 @@ bool dcfg_call_function(dcfg_Value *function, dcfg_Value **args,
|
||||
size_t arg_count, dcfg_Value **out_value);
|
||||
// Allocates new values
|
||||
bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value);
|
||||
// Allocates new values
|
||||
bool dcfg_Value_evaluate_toplevel(dcfg_Value *top, dcfg_Value **out_value,
|
||||
dcfg_StringView *function_names, dcfg_BuiltIn *functions,
|
||||
size_t function_count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
#include <dcfg.h>
|
||||
|
||||
#include <alloca.h>
|
||||
#include <getopt.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void walk_value(dcfg_Value *value, bool evaluate, int indent, bool first);
|
||||
#define WALK(v, e, i) walk_value((v), (e), (i), true)
|
||||
@@ -16,11 +18,25 @@ static void print_indent(int indent)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s <dcfg file>\n", argv[0]);
|
||||
bool evaluate = false;
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "e")) != -1) {
|
||||
switch (opt) {
|
||||
case 'e':
|
||||
evaluate = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: %s [-e] <dcfg file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "Usage: %s [-e] <dcfg file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char const *path = argv[optind];
|
||||
|
||||
dcfg_InstanceCreateInfo ci = { 0 };
|
||||
dcfg_Instance *instance = dcfg_make_instance(&ci);
|
||||
if (!instance) {
|
||||
@@ -29,7 +45,7 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
dcfg_Value *value = dcfg_parse(instance, dcfg_SV(argv[1]));
|
||||
dcfg_Value *value = dcfg_parse(instance, dcfg_SV(path));
|
||||
if (!value) {
|
||||
printf("Failed to parse DCFG file. Error: %s\n",
|
||||
dcfg_last_error(instance));
|
||||
@@ -37,7 +53,7 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
WALK(value, false, 0);
|
||||
WALK(value, evaluate, 0);
|
||||
|
||||
dcfg_destroy(value);
|
||||
dcfg_destroy_instance(instance);
|
||||
|
143
programs/dcfgq.c
Normal file
143
programs/dcfgq.c
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <dcfg.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
T_FIELD,
|
||||
T_INDEX,
|
||||
} TokenType;
|
||||
|
||||
typedef struct {
|
||||
TokenType type;
|
||||
union {
|
||||
dcfg_StringView field;
|
||||
size_t index;
|
||||
} v;
|
||||
} Token;
|
||||
|
||||
#define MAX_TOKENS 256
|
||||
|
||||
static size_t tokenize(char const *query, Token *out)
|
||||
{
|
||||
size_t count = 0;
|
||||
size_t i = 0;
|
||||
if (query[i] == '.')
|
||||
i++;
|
||||
while (query[i] && count < MAX_TOKENS) {
|
||||
if (query[i] == '.') {
|
||||
i++;
|
||||
size_t start = i;
|
||||
while (query[i] && query[i] != '.' && query[i] != '[')
|
||||
i++;
|
||||
out[count].type = T_FIELD;
|
||||
out[count].v.field.data = query + start;
|
||||
out[count].v.field.size = i - start;
|
||||
count++;
|
||||
} else if (query[i] == '[') {
|
||||
i++;
|
||||
char *end;
|
||||
size_t idx = strtoul(query + i, &end, 10);
|
||||
if (*end != ']') {
|
||||
fprintf(stderr, "invalid index\n");
|
||||
exit(1);
|
||||
}
|
||||
i = (size_t)(end - query) + 1;
|
||||
out[count].type = T_INDEX;
|
||||
out[count].v.index = idx;
|
||||
count++;
|
||||
} else {
|
||||
size_t start = i;
|
||||
while (query[i] && query[i] != '.' && query[i] != '[')
|
||||
i++;
|
||||
out[count].type = T_FIELD;
|
||||
out[count].v.field.data = query + start;
|
||||
out[count].v.field.size = i - start;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void print_value(dcfg_Value *v)
|
||||
{
|
||||
dcfg_StringView sv;
|
||||
if (dcfg_serialize_value(v, &sv)) {
|
||||
fwrite(sv.data, 1, sv.size, stdout);
|
||||
free((void *)sv.data);
|
||||
putchar('\n');
|
||||
} else {
|
||||
puts("null");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "usage: %s <query> <file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
char const *query = argv[1];
|
||||
char const *file = argv[2];
|
||||
|
||||
dcfg_Instance *inst = dcfg_make_instance(&(dcfg_InstanceCreateInfo) { 0 });
|
||||
if (!inst) {
|
||||
fprintf(stderr, "failed to create instance\n");
|
||||
return 1;
|
||||
}
|
||||
dcfg_Value *root = dcfg_parse(inst, dcfg_SV(file));
|
||||
if (!root) {
|
||||
fprintf(stderr, "parse error: %s\n", dcfg_last_error(inst));
|
||||
dcfg_destroy_instance(inst);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Token toks[MAX_TOKENS];
|
||||
size_t n = tokenize(query, toks);
|
||||
|
||||
dcfg_Value *evaled;
|
||||
if (!dcfg_Value_evaluate_toplevel(root, &evaled, NULL, NULL, 0)) {
|
||||
fprintf(stderr, "evaluation error: %s\n", dcfg_last_error(inst));
|
||||
dcfg_destroy(root);
|
||||
dcfg_destroy_instance(inst);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dcfg_Value *cur = evaled;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
Token *t = &toks[i];
|
||||
dcfg_Value *next = NULL;
|
||||
|
||||
if (t->type == T_FIELD) {
|
||||
if (!dcfg_Value_get_object_field_ex(cur, t->v.field, &next, true)) {
|
||||
fprintf(stderr, "no such field\n");
|
||||
dcfg_destroy(evaled);
|
||||
dcfg_destroy(root);
|
||||
dcfg_destroy_instance(inst);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!dcfg_Value_get_array_item_ex(cur, t->v.index, &next, true)) {
|
||||
fprintf(stderr, "index out of bounds\n");
|
||||
dcfg_destroy(evaled);
|
||||
dcfg_destroy(root);
|
||||
dcfg_destroy_instance(inst);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur != evaled)
|
||||
dcfg_destroy(cur);
|
||||
|
||||
cur = next;
|
||||
}
|
||||
print_value(cur);
|
||||
if (cur != evaled)
|
||||
dcfg_destroy(cur);
|
||||
|
||||
dcfg_destroy(evaled);
|
||||
dcfg_destroy(root);
|
||||
dcfg_destroy_instance(inst);
|
||||
return 0;
|
||||
}
|
421
src/dcfg.c
421
src/dcfg.c
@@ -21,6 +21,8 @@
|
||||
*/
|
||||
|
||||
#include <dcfg.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "meta.h"
|
||||
@@ -33,7 +35,7 @@
|
||||
// FIXME: Fix this stupid shit!
|
||||
# error "realpath() is dumb and stupid on sun. sorry not sorry."
|
||||
# endif
|
||||
# define _XOPEN_SOURCE 200809L
|
||||
# define _POSIX_C_SOURCE 200809L
|
||||
#else
|
||||
# ifdef _POSIX_C_SOURCE
|
||||
# undef _POSIX_C_SOURCE
|
||||
@@ -41,6 +43,33 @@
|
||||
# define _POSIX_C_SOURCE 0L
|
||||
#endif
|
||||
|
||||
#ifdef DCFG_PTHREAD_SUPPORT
|
||||
# ifdef _POSIX_C_SOURCE
|
||||
# undef _POSIX_C_SOURCE
|
||||
# endif
|
||||
# define _POSIX_C_SOURCE 200809L
|
||||
# include <pthread.h>
|
||||
extern int pthread_mutexattr_settype(pthread_mutexattr_t *, int);
|
||||
#else
|
||||
# if defined __USE_POSIX199506 || defined __USE_UNIX98
|
||||
# else
|
||||
typedef struct {
|
||||
int unused;
|
||||
} pthread_mutex_t;
|
||||
typedef struct {
|
||||
int unused;
|
||||
} pthread_mutexattr_t;
|
||||
# define PTHREAD_MUTEX_RECURSIVE_NP 0
|
||||
# endif
|
||||
|
||||
static void pthread_mutex_init(pthread_mutex_t *, void *) { }
|
||||
static void pthread_mutex_destroy(pthread_mutex_t *) { }
|
||||
static void pthread_mutex_lock(pthread_mutex_t *) { }
|
||||
static void pthread_mutex_unlock(pthread_mutex_t *) { }
|
||||
pthread_mutexattr_init(pthread_mutexattr_t *);
|
||||
static void pthread_mutexattr_settype(pthread_mutexattr_t *, int) { }
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -48,22 +77,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef DCFG_PTHREAD_SUPPORT
|
||||
# include <pthread.h>
|
||||
#else
|
||||
# if defined __USE_POSIX199506 || defined __USE_UNIX98
|
||||
# else
|
||||
typedef struct {
|
||||
int unused;
|
||||
} pthread_mutex_t;
|
||||
# endif
|
||||
|
||||
void pthread_mutex_init(pthread_mutex_t *, void *);
|
||||
void pthread_mutex_destroy(pthread_mutex_t *);
|
||||
void pthread_mutex_lock(pthread_mutex_t *);
|
||||
void pthread_mutex_unlock(pthread_mutex_t *);
|
||||
#endif
|
||||
|
||||
int64_t dcfg_strtoll(const char *s, char **end, int base)
|
||||
{
|
||||
char const *p = s;
|
||||
@@ -258,6 +271,7 @@ typedef struct {
|
||||
} ValueArray;
|
||||
|
||||
typedef struct {
|
||||
struct Environment *closure;
|
||||
StringView *argv;
|
||||
Value *body;
|
||||
} ValueFunctionF;
|
||||
@@ -265,7 +279,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
bool is_builtin;
|
||||
union {
|
||||
Value *(*bi)(Value **argv, size_t argc);
|
||||
dcfg_BuiltIn bi;
|
||||
ValueFunctionF f;
|
||||
} v;
|
||||
} ValueFunction;
|
||||
@@ -276,16 +290,13 @@ typedef struct {
|
||||
} ValueFunctionCall;
|
||||
|
||||
typedef struct {
|
||||
StringView **accessv;
|
||||
StringView *accessv;
|
||||
} ValueMemberAccess;
|
||||
|
||||
struct dcfg_Value {
|
||||
Instance *instance;
|
||||
dcfg_ValueType type;
|
||||
SourceLocation location;
|
||||
int i_sourcev_idx;
|
||||
int i_source_pathv_idx;
|
||||
int i_environment_idx;
|
||||
|
||||
union {
|
||||
int64_t i;
|
||||
@@ -304,10 +315,10 @@ struct dcfg_Value {
|
||||
typedef struct Environment {
|
||||
struct Environment *parent;
|
||||
StringView *argv;
|
||||
Value *argvv;
|
||||
Value **argvv;
|
||||
} Environment;
|
||||
|
||||
bool environment_create(Environment *out_env, Environment *parent)
|
||||
static bool environment_create(Environment *out_env, Environment *parent)
|
||||
{
|
||||
out_env->argv = vector_create();
|
||||
out_env->argvv = vector_create();
|
||||
@@ -315,13 +326,35 @@ bool environment_create(Environment *out_env, Environment *parent)
|
||||
return true;
|
||||
}
|
||||
|
||||
void environment_destroy(Environment *env)
|
||||
static bool environment_lookup(Environment *e, StringView name, Value **out)
|
||||
{
|
||||
vector_free(&env->argv);
|
||||
for (size_t i = 0; i < vector_size(env->argvv); i++) {
|
||||
dcfg_destroy(&env->argvv[i]);
|
||||
for (; e; e = e->parent) {
|
||||
for (size_t i = 0; i < vector_size(e->argv); i++) {
|
||||
if (sv_eq(e->argv[i], name)) {
|
||||
*out = e->argvv[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void environment_destroy(Environment *env, bool destroy_values)
|
||||
{
|
||||
assert(env);
|
||||
if (env->argv) {
|
||||
vector_free(env->argv);
|
||||
env->argv = NULL;
|
||||
}
|
||||
if (env->argvv) {
|
||||
if (destroy_values) {
|
||||
for (size_t i = 0; i < vector_size(env->argvv); i++) {
|
||||
dcfg_destroy(env->argvv[i]);
|
||||
}
|
||||
}
|
||||
vector_free(env->argvv);
|
||||
env->argvv = NULL;
|
||||
}
|
||||
vector_free(&env->argvv);
|
||||
}
|
||||
|
||||
struct dcfg_Instance {
|
||||
@@ -369,7 +402,11 @@ dcfg_Instance *dcfg_make_instance(dcfg_InstanceCreateInfo const *create_info)
|
||||
if (!instance) {
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_init(&instance->mtx, NULL);
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
|
||||
pthread_mutex_init(&instance->mtx, &attr);
|
||||
|
||||
instance->alloc = create_info->alloc;
|
||||
instance->free = create_info->free;
|
||||
@@ -416,7 +453,7 @@ void dcfg_destroy_instance(dcfg_Instance *instance)
|
||||
for (size_t i = 0; i < vector_size(instance->environment_referencesv);
|
||||
i++) {
|
||||
if (instance->environment_referencesv[i] > 0) {
|
||||
environment_destroy(&instance->environmentv[i]);
|
||||
// environment_destroy(&instance->environmentv[i], true);
|
||||
}
|
||||
}
|
||||
vector_free(instance->environment_referencesv);
|
||||
@@ -1346,9 +1383,9 @@ Value *ast_to_value(dcfg_Instance *instance, AST *root)
|
||||
value->v.f.v.f.body = v;
|
||||
} else if (root->kind == ASTKind_MemberAccess) {
|
||||
value->type = dcfg_ValueType_MemberAccess;
|
||||
// FIXME: Implement
|
||||
FREE(value);
|
||||
return NULL;
|
||||
value->v.ma.accessv = vector_create();
|
||||
for (size_t i = 0; i < vector_size(root->v.m.accessv); i++)
|
||||
vector_add(&value->v.ma.accessv, StringView, root->v.m.accessv[i]);
|
||||
} else if (root->kind == ASTKind_FunctionCall) {
|
||||
value->type = dcfg_ValueType_FunctionCall;
|
||||
Value *function = ast_to_value(instance, root->v.fc.function);
|
||||
@@ -1477,10 +1514,7 @@ dcfg_Value *dcfg_parse(dcfg_Instance *instance, dcfg_StringView const file_path)
|
||||
instance->last_error[0] = '\0';
|
||||
|
||||
vector_add(&instance->sourcev, StringView, str);
|
||||
v->i_sourcev_idx = vector_size(instance->sourcev) - 1;
|
||||
|
||||
vector_add(&instance->source_pathv, StringView, abs_sv);
|
||||
v->i_source_pathv_idx = vector_size(instance->source_pathv) - 1;
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -1523,6 +1557,10 @@ void dcfg_destroy(dcfg_Value *value)
|
||||
vector_free(value->v.c.argv);
|
||||
break;
|
||||
|
||||
case dcfg_ValueType_MemberAccess:
|
||||
vector_free(value->v.ma.accessv);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1530,6 +1568,142 @@ void dcfg_destroy(dcfg_Value *value)
|
||||
value->instance->free(value); // https://youtu.be/RgFaK6ZQifE
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
dcfg_Instance *inst;
|
||||
char *buf;
|
||||
size_t len, cap;
|
||||
} StrBld;
|
||||
|
||||
static bool sb_reserve(StrBld *sb, size_t more)
|
||||
{
|
||||
if (sb->len + more <= sb->cap)
|
||||
return true;
|
||||
size_t ncap = sb->cap ? sb->cap * 2 : 128;
|
||||
while (ncap < sb->len + more)
|
||||
ncap *= 2;
|
||||
char *nbuf = sb->inst->alloc(ncap);
|
||||
if (!nbuf)
|
||||
return false;
|
||||
if (sb->buf) {
|
||||
memcpy(nbuf, sb->buf, sb->len);
|
||||
sb->inst->free(sb->buf);
|
||||
}
|
||||
sb->buf = nbuf;
|
||||
sb->cap = ncap;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sb_put(StrBld *sb, char const *s, size_t n)
|
||||
{
|
||||
if (!sb_reserve(sb, n))
|
||||
return false;
|
||||
memcpy(sb->buf + sb->len, s, n);
|
||||
sb->len += n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sb_put_sv(StrBld *sb, StringView sv)
|
||||
{
|
||||
return sb_put(sb, sv.data, sv.size);
|
||||
}
|
||||
|
||||
static bool sb_put_char(StrBld *sb, char c) { return sb_put(sb, &c, 1); }
|
||||
|
||||
static bool ser_value(dcfg_Value *v, StrBld *sb)
|
||||
{
|
||||
switch (v->type) {
|
||||
case dcfg_ValueType_Nil:
|
||||
return sb_put(sb, "nil", 3);
|
||||
case dcfg_ValueType_Boolean:
|
||||
return sb_put(sb, v->v.b ? "true" : "false", v->v.b ? 4 : 5);
|
||||
case dcfg_ValueType_Integer: {
|
||||
char tmp[64];
|
||||
int n = snprintf(tmp, sizeof tmp, "%" PRId64, v->v.i);
|
||||
return sb_put(sb, tmp, (size_t)n);
|
||||
}
|
||||
case dcfg_ValueType_Real: {
|
||||
char tmp[64];
|
||||
int n = snprintf(tmp, sizeof tmp, "%.17g", v->v.r);
|
||||
return sb_put(sb, tmp, (size_t)n);
|
||||
}
|
||||
case dcfg_ValueType_String: {
|
||||
if (!sb_put_char(sb, '"'))
|
||||
return false;
|
||||
for (size_t i = 0; i < v->v.s.size; ++i) {
|
||||
char c = v->v.s.data[i];
|
||||
if (c == '"' || c == '\\') {
|
||||
if (!sb_put_char(sb, '\\'))
|
||||
return false;
|
||||
}
|
||||
if (!sb_put_char(sb, c))
|
||||
return false;
|
||||
}
|
||||
return sb_put_char(sb, '"');
|
||||
}
|
||||
case dcfg_ValueType_Path:
|
||||
return sb_put_sv(sb, v->v.p);
|
||||
case dcfg_ValueType_Array: {
|
||||
if (!sb_put_char(sb, '['))
|
||||
return false;
|
||||
for (size_t i = 0; i < vector_size(v->v.a.valuev); ++i) {
|
||||
if (i && !sb_put(sb, ", ", 2))
|
||||
return false;
|
||||
if (!ser_value(v->v.a.valuev[i], sb))
|
||||
return false;
|
||||
}
|
||||
return sb_put_char(sb, ']');
|
||||
}
|
||||
case dcfg_ValueType_Object: {
|
||||
if (!sb_put_char(sb, '{'))
|
||||
return false;
|
||||
for (size_t i = 0; i < vector_size(v->v.o.entryv); ++i) {
|
||||
ValueObjectEntry *e = &v->v.o.entryv[i];
|
||||
if (i && !sb_put(sb, ", ", 2))
|
||||
return false;
|
||||
if (!sb_put_sv(sb, e->k) || !sb_put(sb, " = ", 3))
|
||||
return false;
|
||||
if (!ser_value(e->v, sb))
|
||||
return false;
|
||||
}
|
||||
return sb_put_char(sb, '}');
|
||||
}
|
||||
case dcfg_ValueType_Function:
|
||||
return sb_put(sb, "<function>", 10);
|
||||
case dcfg_ValueType_FunctionCall:
|
||||
return sb_put(sb, "<call>", 6);
|
||||
case dcfg_ValueType_MemberAccess:
|
||||
return sb_put(sb, "<member>", 8);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool dcfg_serialize_value(dcfg_Value *value, dcfg_StringView *out_sv)
|
||||
{
|
||||
if (!value || !out_sv)
|
||||
return false;
|
||||
|
||||
StrBld sb = { .inst = value->instance };
|
||||
if (!ser_value(value, &sb)) {
|
||||
if (sb.buf)
|
||||
value->instance->free(sb.buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
char *final = value->instance->alloc(sb.len + 1);
|
||||
if (!final) {
|
||||
value->instance->free(sb.buf);
|
||||
return false;
|
||||
}
|
||||
memcpy(final, sb.buf, sb.len);
|
||||
final[sb.len] = '\0';
|
||||
value->instance->free(sb.buf);
|
||||
|
||||
out_sv->data = final;
|
||||
out_sv->size = sb.len;
|
||||
return true;
|
||||
}
|
||||
|
||||
dcfg_ValueType dcfg_Value_type_ex(dcfg_Value *value, bool evaluate)
|
||||
{
|
||||
if (!value)
|
||||
@@ -1662,22 +1836,22 @@ bool dcfg_Value_get_array_size(dcfg_Value *value, size_t *out_size)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcfg_call_function(dcfg_Value *function, dcfg_Value **args,
|
||||
size_t arg_count, dcfg_Value **out_value)
|
||||
static bool eval_member(ValueMemberAccess *ma, Environment *env, Value **out)
|
||||
{
|
||||
if (function->v.f.is_builtin) {
|
||||
*out_value = function->v.f.v.bi(args, arg_count);
|
||||
if (!*out_value) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// FIXME: Implement
|
||||
Value *root;
|
||||
if (!environment_lookup(env, ma->accessv[0], &root))
|
||||
return false;
|
||||
|
||||
for (size_t i = 1; i < vector_size(ma->accessv); ++i) {
|
||||
if (!dcfg_Value_get_object_field_ex(root, ma->accessv[i], &root, true))
|
||||
return false;
|
||||
}
|
||||
*out = root;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
bool dcfg_Value_evaluate_in_env(
|
||||
dcfg_Value *value, Environment *frame, dcfg_Value **out_value)
|
||||
{
|
||||
assert(value);
|
||||
assert(out_value);
|
||||
@@ -1686,6 +1860,7 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
value->instance->last_error[0] = '\0';
|
||||
*out_value = value->instance->alloc(sizeof(**out_value));
|
||||
Value *v = *out_value;
|
||||
(*out_value)->instance = value->instance;
|
||||
|
||||
if (value->type == dcfg_ValueType_Nil) {
|
||||
v->type = dcfg_ValueType_Nil;
|
||||
@@ -1710,7 +1885,7 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
for (size_t i = 0; i < vector_size(value->v.o.entryv); i++) {
|
||||
ValueObjectEntry *e = &value->v.o.entryv[i];
|
||||
Value *new_v;
|
||||
bool res = dcfg_Value_evaluate(e->v, &new_v);
|
||||
bool res = dcfg_Value_evaluate_in_env(e->v, frame, &new_v);
|
||||
if (!res) {
|
||||
ret = false;
|
||||
break;
|
||||
@@ -1718,6 +1893,7 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
ValueObjectEntry ne = {
|
||||
.k = e->k,
|
||||
.v = new_v,
|
||||
.key_allocated = true,
|
||||
};
|
||||
ne.k.data = value->instance->alloc(ne.k.size + 1);
|
||||
memcpy((void *)ne.k.data, e->k.data, ne.k.size);
|
||||
@@ -1729,7 +1905,8 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
v->v.a.valuev = vector_create();
|
||||
for (size_t i = 0; i < vector_size(value->v.a.valuev); i++) {
|
||||
Value *val = NULL;
|
||||
bool res = dcfg_Value_evaluate(value->v.a.valuev[i], &val);
|
||||
bool res
|
||||
= dcfg_Value_evaluate_in_env(value->v.a.valuev[i], frame, &val);
|
||||
if (!res) {
|
||||
ret = false;
|
||||
}
|
||||
@@ -1743,11 +1920,23 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
ret = false;
|
||||
}
|
||||
} else if (value->type == dcfg_ValueType_MemberAccess) {
|
||||
// FIXME: Implement
|
||||
ret = false;
|
||||
if (!frame) {
|
||||
ret = false;
|
||||
strcpy(value->instance->last_error,
|
||||
"Cannot use member access outside of function");
|
||||
} else {
|
||||
Value *out_value_prev = *out_value;
|
||||
bool ok = eval_member(&value->v.ma, frame, out_value);
|
||||
if (!ok) {
|
||||
ret = false;
|
||||
} else {
|
||||
dcfg_destroy(out_value_prev);
|
||||
}
|
||||
}
|
||||
} else if (value->type == dcfg_ValueType_FunctionCall) {
|
||||
Value *function;
|
||||
bool res = dcfg_Value_evaluate(value->v.c.function, &function);
|
||||
bool res
|
||||
= dcfg_Value_evaluate_in_env(value->v.c.function, frame, &function);
|
||||
if (!res || function->type != dcfg_ValueType_Function) {
|
||||
ret = false;
|
||||
} else {
|
||||
@@ -1755,8 +1944,9 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
bool res = dcfg_call_function(function, value->v.c.argv,
|
||||
vector_size(value->v.c.argv), out_value);
|
||||
if (!res) {
|
||||
dcfg_destroy(out_value_prev);
|
||||
ret = false;
|
||||
} else {
|
||||
dcfg_destroy(out_value_prev);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1768,6 +1958,131 @@ bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dcfg_call_function(
|
||||
dcfg_Value *fn, dcfg_Value **args, size_t argc, dcfg_Value **out_value)
|
||||
{
|
||||
pthread_mutex_lock(&fn->instance->mtx);
|
||||
if (fn->v.f.is_builtin) {
|
||||
*out_value = fn->v.f.v.bi(args, argc);
|
||||
pthread_mutex_unlock(&fn->instance->mtx);
|
||||
return *out_value != NULL;
|
||||
}
|
||||
|
||||
Environment frame;
|
||||
if (!environment_create(&frame, fn->v.f.v.f.closure)) {
|
||||
pthread_mutex_unlock(&fn->instance->mtx);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t nform = vector_size(fn->v.f.v.f.argv);
|
||||
if (argc != nform) {
|
||||
strcpy(fn->instance->last_error, "Invalid argument count");
|
||||
environment_destroy(&frame, false);
|
||||
pthread_mutex_unlock(&fn->instance->mtx);
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < nform; i++) {
|
||||
vector_add(&frame.argv, StringView, fn->v.f.v.f.argv[i]);
|
||||
vector_add(&frame.argvv, Value *, args[i]);
|
||||
}
|
||||
|
||||
bool ok = dcfg_Value_evaluate_in_env(fn->v.f.v.f.body, &frame, out_value);
|
||||
environment_destroy(&frame, false);
|
||||
pthread_mutex_unlock(&fn->instance->mtx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value)
|
||||
{
|
||||
bool ret;
|
||||
pthread_mutex_lock(&value->instance->mtx);
|
||||
{
|
||||
ret = dcfg_Value_evaluate_in_env(value, NULL, out_value);
|
||||
}
|
||||
pthread_mutex_unlock(&value->instance->mtx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dcfg_Value_evaluate_toplevel(dcfg_Value *top, dcfg_Value **out_value,
|
||||
dcfg_StringView *function_names, dcfg_BuiltIn *functions,
|
||||
size_t function_count)
|
||||
{
|
||||
if (!top)
|
||||
return false;
|
||||
if (top->type != dcfg_ValueType_Function)
|
||||
return dcfg_Value_evaluate(top, out_value);
|
||||
|
||||
Instance *inst = top->instance;
|
||||
|
||||
Value *lib = inst->alloc(sizeof *lib);
|
||||
if (!lib)
|
||||
return false;
|
||||
lib->instance = inst;
|
||||
lib->type = dcfg_ValueType_Object;
|
||||
lib->v.o.entryv = vector_create();
|
||||
if (!lib->v.o.entryv) {
|
||||
inst->free(lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < function_count; ++i) {
|
||||
Value *fn = inst->alloc(sizeof *fn);
|
||||
if (!fn) {
|
||||
dcfg_destroy(lib);
|
||||
return false;
|
||||
}
|
||||
fn->instance = inst;
|
||||
fn->type = dcfg_ValueType_Function;
|
||||
fn->v.f.is_builtin = true;
|
||||
fn->v.f.v.bi = functions[i];
|
||||
|
||||
StringView name = function_names[i];
|
||||
Value *target = lib;
|
||||
size_t start = 0;
|
||||
for (size_t pos = 0; pos <= name.size; ++pos) {
|
||||
bool end = (pos == name.size);
|
||||
if (end || name.data[pos] == '.') {
|
||||
StringView seg
|
||||
= { .data = name.data + start, .size = pos - start };
|
||||
|
||||
if (end) {
|
||||
ValueObject *obj = &target->v.o;
|
||||
bool replaced = false;
|
||||
for (size_t j = 0; j < vector_size(obj->entryv); ++j) {
|
||||
if (sv_eq(obj->entryv[j].k, seg)) {
|
||||
dcfg_destroy(obj->entryv[j].v);
|
||||
obj->entryv[j].v = fn;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!replaced) {
|
||||
ValueObjectEntry e
|
||||
= { .k = seg, .v = fn, .key_allocated = false };
|
||||
vector_add(&obj->entryv, ValueObjectEntry, e);
|
||||
}
|
||||
} else {
|
||||
target = ensure_child_obj(inst, target, seg);
|
||||
if (target->type != dcfg_ValueType_Object) {
|
||||
dcfg_destroy(fn);
|
||||
dcfg_destroy(lib);
|
||||
strcpy(inst->last_error,
|
||||
"Function name clashes with non-object field");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
start = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value *argv[1] = { lib };
|
||||
bool ok = dcfg_call_function(top, argv, 1, out_value);
|
||||
vector_free(lib->v.o.entryv);
|
||||
inst->free(lib);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Libraries
|
||||
#include "vendor/utf8proc.c"
|
||||
#include "vendor/vec.c"
|
||||
|
Reference in New Issue
Block a user