From 1d40c785f83c5e59107ca79d7eea8a173ea20ebc Mon Sep 17 00:00:00 2001 From: Slendi Date: Fri, 1 Aug 2025 07:38:13 +0300 Subject: [PATCH] Get something working Signed-off-by: Slendi --- CMakeLists.txt | 45 +++-- include/dcfg.h | 6 + programs/dcfg_dump.c | 24 ++- programs/dcfgq.c | 143 +++++++++++++++ src/dcfg.c | 421 +++++++++++++++++++++++++++++++++++++------ 5 files changed, 571 insertions(+), 68 deletions(-) create mode 100644 programs/dcfgq.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0732e7e..3f0fa37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/include/dcfg.h b/include/dcfg.h index 410354b..a29fe9a 100644 --- a/include/dcfg.h +++ b/include/dcfg.h @@ -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 } diff --git a/programs/dcfg_dump.c b/programs/dcfg_dump.c index 7f228a6..ae4c060 100644 --- a/programs/dcfg_dump.c +++ b/programs/dcfg_dump.c @@ -1,9 +1,11 @@ #include #include +#include #include #include #include +#include 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 \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] \n", argv[0]); + return 1; + } + } + if (optind >= argc) { + fprintf(stderr, "Usage: %s [-e] \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); diff --git a/programs/dcfgq.c b/programs/dcfgq.c new file mode 100644 index 0000000..27ca1c2 --- /dev/null +++ b/programs/dcfgq.c @@ -0,0 +1,143 @@ +#include + +#include +#include +#include + +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 \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; +} diff --git a/src/dcfg.c b/src/dcfg.c index 299fb19..321baf7 100644 --- a/src/dcfg.c +++ b/src/dcfg.c @@ -21,6 +21,8 @@ */ #include + +#include #include #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 +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 #include #include @@ -48,22 +77,6 @@ #include #include -#ifdef DCFG_PTHREAD_SUPPORT -# include -#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, "", 10); + case dcfg_ValueType_FunctionCall: + return sb_put(sb, "", 6); + case dcfg_ValueType_MemberAccess: + return sb_put(sb, "", 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"