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

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-08-01 07:38:13 +03:00
parent 80af330d1b
commit 1d40c785f8
5 changed files with 571 additions and 68 deletions

View File

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

View File

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

View File

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

View File

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