/* * Copyright 2025 Slendi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the “Software”), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "meta.h" #include "vendor/utf8proc.h" #include "vendor/vec.h" #ifdef DCFG_POSIX_SUPPORT # ifdef __sun // FIXME: Fix this stupid shit! # error "realpath() is dumb and stupid on sun. sorry not sorry." # endif # define _POSIX_C_SOURCE 200809L #else # ifdef _POSIX_C_SOURCE # undef _POSIX_C_SOURCE # endif # 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 #include #include #include int64_t dcfg_strtoll(const char *s, char **end, int base) { char const *p = s; while (isspace((unsigned char)*p)) p++; bool neg = false; if (*p == '+' || *p == '-') { neg = (*p == '-'); p++; } if (base == 0) { if (*p == '0') { if (p[1] == 'x' || p[1] == 'X') { base = 16; p += 2; } else { base = 8; p++; } } else { base = 10; } } else if (base == 16 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { p += 2; } int64_t val = 0; char const *q = p, *last = p; while (*q) { char c = *q; if (c == '_' || c == '\'') { q++; continue; } int d; if (c >= '0' && c <= '9') d = c - '0'; else if (c >= 'a' && c <= 'z') d = c - 'a' + 10; else if (c >= 'A' && c <= 'Z') d = c - 'A' + 10; else break; if (d >= base) break; val = val * base + d; last = q + 1; q++; } if (last == p) { if (end) *end = (char *)s; return 0; } if (end) *end = (char *)last; return neg ? -val : val; } double dcfg_strtod(char const *s, char **end) { char const *p = s; while (isspace((unsigned char)*p)) p++; bool neg = false; if (*p == '+' || *p == '-') { neg = (*p == '-'); p++; } char const *fd = strchr(p, '.'); char const *fc = strchr(p, ','); char dec = '.'; if (!fd && fc) dec = ','; else if (fd && fc && fc < fd) dec = ','; int64_t ip = 0; double frac = 0.0, div = 1.0; bool any = false, in_frac = false; bool in_exp = false, exp_any = false; int exp_sign = 1, exp_val = 0; char const *last = p; size_t i = p - s; for (;; ++i) { char c = s[i]; if (!c) break; if (!in_exp) { if (c == '_' || c == '\'') continue; if (!in_frac && c == dec) { in_frac = true; last = s + i + 1; continue; } if (!in_frac && (c == '.' || c == ',')) continue; if (c >= '0' && c <= '9') { any = true; int d = c - '0'; if (!in_frac) ip = ip * 10 + d; else { frac = frac * 10 + d; div *= 10.0; } last = s + i + 1; continue; } if ((c == 'e' || c == 'E') && any) { in_exp = true; last = s + i + 1; continue; } break; } else { if ((c == '+' || c == '-') && !exp_any) { if (c == '-') exp_sign = -1; last = s + i + 1; continue; } if (c >= '0' && c <= '9') { exp_any = true; exp_val = exp_val * 10 + (c - '0'); last = s + i + 1; continue; } break; } } if (!any) { if (end) *end = (char *)s; return 0.0; } double result = (double)ip + frac / div; if (in_exp && exp_any) result *= pow(10.0, exp_sign * exp_val); if (neg) result = -result; if (end) *end = (char *)last; return result; } typedef dcfg_Value Value; typedef dcfg_Instance Instance; typedef dcfg_StringView StringView; #define SV(cstr) ((StringView) { .data = cstr, .size = strlen(cstr) }) static inline bool sv_eq(StringView a, StringView b) { if (a.size != b.size) { return false; } return memcmp(a.data, b.data, a.size) == 0; } typedef struct { int row, col; } Location; typedef struct { Location begin, end; } LocationRange; typedef struct { StringView fp; LocationRange range; } SourceLocation; typedef struct { StringView k; Value *v; bool key_allocated; } ValueObjectEntry; typedef struct { ValueObjectEntry *entryv; } ValueObject; typedef struct { Value **valuev; } ValueArray; typedef struct { struct Environment *closure; StringView *argv; Value *body; } ValueFunctionF; typedef struct { bool is_builtin; union { dcfg_BuiltIn bi; ValueFunctionF f; } v; } ValueFunction; typedef struct { Value *function; Value **argv; } ValueFunctionCall; typedef struct { StringView *accessv; } ValueMemberAccess; struct dcfg_Value { Instance *instance; dcfg_ValueType type; SourceLocation location; union { int64_t i; double r; bool b; StringView s; StringView p; ValueObject o; ValueArray a; ValueFunction f; ValueMemberAccess ma; ValueFunctionCall c; } v; }; typedef struct Environment { struct Environment *parent; StringView *argv; Value **argvv; } Environment; static bool environment_create(Environment *out_env, Environment *parent) { out_env->argv = vector_create(); out_env->argvv = vector_create(); out_env->parent = parent; return true; } static bool environment_lookup(Environment *e, StringView name, Value **out) { 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; } } struct dcfg_Instance { pthread_mutex_t mtx; dcfg_AllocFn alloc; dcfg_FreeFn free; dcfg_RealpathFn realpath; dcfg_FopenFn fopen; dcfg_FseekFn fseek; dcfg_FtellFn ftell; char last_error[256]; StringView *sourcev; // Strings should be freed. StringView *source_pathv; // Strings should be freed. Environment *environmentv; int *environment_referencesv; }; #define ALLOC(sz) (instance->alloc((sz))) #define FREE(ptr) (instance->free((ptr))) static void *alloc(size_t size) { return calloc(1, size); } #ifdef DCFG_POSIX_SUPPORT static char *realpath_(char const *s) { return realpath(s, NULL); } void *fopen_(char const *f, char const *a) { return fopen(f, a); } int fseek_(void *f, size_t p, int o) { return fseek(f, p, o); } long ftell_(void *f) { return ftell(f); } #endif dcfg_Version dcfg_get_version(void) { return (((uint64_t)VERSION_MAJOR & 0xFFFFULL) << 48) | (((uint64_t)VERSION_MINOR & 0xFFFFULL) << 32) | ((uint64_t)VERSION_PATCH & 0xFFFFFFFFULL); } dcfg_Instance *dcfg_make_instance(dcfg_InstanceCreateInfo const *create_info) { assert(create_info); dcfg_Instance *instance = calloc(1, sizeof(*instance)); if (!instance) { return 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; if (!instance->alloc) { instance->alloc = alloc; } if (!instance->free) { instance->free = free; } #ifdef DCFG_POSIX_SUPPORT if (!instance->realpath) { instance->realpath = realpath_; } if (!instance->fopen) { instance->fopen = fopen_; } if (!instance->fseek) { instance->fseek = fseek_; } if (!instance->ftell) { instance->ftell = ftell_; } #endif assert(instance->alloc); assert(instance->free); assert(instance->realpath); assert(instance->fopen); assert(instance->fseek); assert(instance->ftell); instance->sourcev = vector_create(); instance->source_pathv = vector_create(); instance->environmentv = vector_create(); instance->environment_referencesv = vector_create(); return instance; } void dcfg_destroy_instance(dcfg_Instance *instance) { assert(instance); for (size_t i = 0; i < vector_size(instance->environment_referencesv); i++) { if (instance->environment_referencesv[i] > 0) { // environment_destroy(&instance->environmentv[i], true); } } vector_free(instance->environment_referencesv); for (size_t i = 0; i < vector_size(instance->environmentv); i++) { } vector_free(instance->environmentv); for (size_t i = 0; i < vector_size(instance->source_pathv); i++) { free((void *)instance->source_pathv[i].data); } vector_free(instance->source_pathv); for (size_t i = 0; i < vector_size(instance->sourcev); i++) { free((void *)instance->sourcev[i].data); } vector_free(instance->sourcev); pthread_mutex_lock(&instance->mtx); { // De-init other instance things } pthread_mutex_unlock(&instance->mtx); pthread_mutex_destroy(&instance->mtx); free(instance); } char const *dcfg_last_error(dcfg_Instance *instance) { assert(instance); char const *ret = NULL; pthread_mutex_lock(&instance->mtx); { if (!instance->last_error[0]) { pthread_mutex_unlock(&instance->mtx); return NULL; } ret = instance->last_error; } pthread_mutex_unlock(&instance->mtx); return ret; } typedef enum { TokenType_Identifier, TokenType_Integer, TokenType_Real, TokenType_String, TokenType_Path, TokenType_LBracket, TokenType_RBracket, TokenType_LParen, TokenType_RParen, TokenType_LSquirly, TokenType_RSquirly, TokenType_Set, TokenType_Dot, TokenType_End, TokenType_Error, } TokenType; typedef struct { TokenType type; SourceLocation location; union { StringView id; int64_t i; double r; StringView s; StringView p; } v; } Token; typedef struct { StringView source; StringView fp; int32_t ch, next; int ch_len, next_len; Location cursor; int offset; } Lexer; static inline int32_t decode_cp(StringView src, int pos, int *len) { if (pos >= (int)src.size) { *len = 0; return -1; } int32_t cp; int bytes = utf8proc_iterate( (uint8_t const *)src.data + pos, (int)(src.size - pos), &cp); if (bytes < 0) { *len = 0; return -1; } *len = bytes; return cp; } static inline bool is_space_cp(int32_t cp) { return (cp <= 0x7F && (cp == ' ' || cp == '\t' || cp == '\r' || cp == '\n')) || utf8proc_category(cp) == UTF8PROC_CATEGORY_ZS; } static inline bool is_alpha_cp(int32_t cp) { utf8proc_category_t cat = utf8proc_category(cp); return (cp <= 0x7F && isalpha(cp)) || (cat >= UTF8PROC_CATEGORY_LU && cat <= UTF8PROC_CATEGORY_LO) || (cat == UTF8PROC_CATEGORY_SO); } static inline bool is_digit_cp(int32_t cp) { return (cp <= 0x7F && isdigit(cp)) || utf8proc_category(cp) == UTF8PROC_CATEGORY_ND; } static inline bool is_alnum_cp(int32_t cp) { return is_alpha_cp(cp) || is_digit_cp(cp); } static inline bool is_path_ch_cp(int32_t cp) { return cp == '_' || cp == '.' || cp == '/' || cp == '-' || cp == ':' || is_alnum_cp(cp); } #define is_space is_space_cp #define is_path_ch is_path_ch_cp #define isalpha_cp is_alpha_cp #define isdigit_cp is_digit_cp #define isalnum_cp is_alnum_cp static void lex_advance(Lexer *lx) { if (lx->next == -1) { lx->ch = -1; return; } if (lx->ch == '\n') { ++lx->cursor.row; lx->cursor.col = 1; } else { ++lx->cursor.col; } lx->offset += lx->ch_len; lx->ch = lx->next; lx->ch_len = lx->next_len; lx->next = decode_cp(lx->source, lx->offset + lx->ch_len, &lx->next_len); } static StringView lex_slice(Lexer *lx, int start, int end) { StringView sv; sv.data = lx->source.data + start; sv.size = (size_t)(end - start); return sv; } static void skip_ws_and_comments(Lexer *lx) { for (;;) { while (is_space(lx->ch)) lex_advance(lx); if (lx->ch == '#') { while (lx->ch != -1 && lx->ch != '\n') lex_advance(lx); continue; } if (lx->ch == '/' && lx->next == '/') { lex_advance(lx); lex_advance(lx); while (lx->ch != -1 && lx->ch != '\n') lex_advance(lx); continue; } if (lx->ch == '/' && lx->next == '*') { lex_advance(lx); lex_advance(lx); while (lx->ch != -1) { if (lx->ch == '*' && lx->next == '/') { lex_advance(lx); lex_advance(lx); break; } lex_advance(lx); } continue; } break; } } static Token make_token(TokenType t, Lexer *lx, int start, int end) { Token tk = { 0 }; tk.type = t; tk.location.fp = lx->fp; tk.location.range.begin = lx->cursor; tk.location.range.end = lx->cursor; switch (t) { case TokenType_Identifier: tk.v.id = lex_slice(lx, start, end); break; case TokenType_Integer: tk.v.i = dcfg_strtoll(lex_slice(lx, start, end).data, NULL, 10); break; case TokenType_Real: tk.v.r = dcfg_strtod(lex_slice(lx, start, end).data, NULL); break; case TokenType_String: tk.v.s = lex_slice(lx, start, end); break; case TokenType_Path: tk.v.p = lex_slice(lx, start, end); break; default: break; } return tk; } static Token lex_number(Lexer *lx) { int start = lx->offset; bool real = false; while (isdigit_cp(lx->ch) || lx->ch == '_' || lx->ch == '\'') lex_advance(lx); if (lx->ch == '.' || lx->ch == ',') { real = true; lex_advance(lx); while (isdigit_cp(lx->ch) || lx->ch == '_' || lx->ch == '\'') lex_advance(lx); } return make_token( real ? TokenType_Real : TokenType_Integer, lx, start, lx->offset); } static Token lex_identifier(Lexer *lx) { int start = lx->offset; while (isalnum_cp(lx->ch) || lx->ch == '_') lex_advance(lx); return make_token(TokenType_Identifier, lx, start, lx->offset); } static Token lex_string(Lexer *lx) { int quote = lx->ch; lex_advance(lx); // skip opening quote int start = lx->offset; while (lx->ch != quote && lx->ch != -1) { if (lx->ch == '\\' && lx->next != -1) lex_advance(lx); // simple escapes lex_advance(lx); } int end = lx->offset; if (lx->ch == quote) lex_advance(lx); // skip closing quote return make_token(TokenType_String, lx, start, end); } static Token lex_path(Lexer *lx) { int start = lx->offset; for (;;) { if (lx->ch == '\\') { lex_advance(lx); if (lx->ch != -1) lex_advance(lx); } else if (is_path_ch(lx->ch)) { lex_advance(lx); } else break; } return make_token(TokenType_Path, lx, start, lx->offset); } bool Lexer_init(Lexer *lx, StringView src, StringView fp) { memset(lx, 0, sizeof *lx); lx->source = src; lx->fp = fp; lx->cursor = (Location) { 1, 1 }; lx->ch = decode_cp(src, 0, &lx->ch_len); lx->next = decode_cp(src, lx->ch_len, &lx->next_len); return true; } Token Lexer_next(Lexer *lx) { skip_ws_and_comments(lx); int start_off = lx->offset; if (lx->ch == -1) { Token tk = { .type = TokenType_End }; tk.location.fp = lx->fp; return tk; } if (lx->ch == '/' // "/foo" || (lx->ch == '.' && lx->next == '/') // "./foo" || (lx->ch == '.' && lx->offset + 2 < (int)lx->source.size && lx->source.data[lx->offset + 1] == '.' && lx->source.data[lx->offset + 2] == '/') // "../foo" || (isalpha_cp(lx->ch) && lx->next == ':')) { // "C:/foo" return lex_path(lx); } switch (lx->ch) { case '[': lex_advance(lx); return make_token(TokenType_LBracket, lx, start_off, lx->offset); case ']': lex_advance(lx); return make_token(TokenType_RBracket, lx, start_off, lx->offset); case '(': lex_advance(lx); return make_token(TokenType_LParen, lx, start_off, lx->offset); case ')': lex_advance(lx); return make_token(TokenType_RParen, lx, start_off, lx->offset); case '{': lex_advance(lx); return make_token(TokenType_LSquirly, lx, start_off, lx->offset); case '}': lex_advance(lx); return make_token(TokenType_RSquirly, lx, start_off, lx->offset); case '=': lex_advance(lx); return make_token(TokenType_Set, lx, start_off, lx->offset); case '.': lex_advance(lx); return make_token(TokenType_Dot, lx, start_off, lx->offset); case '"': case '\'': return lex_string(lx); default: break; } if (isalpha_cp(lx->ch) || lx->ch == '_') return lex_identifier(lx); if (isdigit_cp(lx->ch)) return lex_number(lx); Token err = { .type = TokenType_Error }; err.location.fp = lx->fp; lex_advance(lx); return err; } typedef enum { ASTKind_Key, // Identifier + string combo ASTKind_Path, ASTKind_Boolean, ASTKind_Integer, ASTKind_Real, ASTKind_Block, ASTKind_Array, ASTKind_Function, ASTKind_MemberAccess, ASTKind_FunctionCall, } ASTKind; typedef struct AST AST; typedef struct { StringView *argv; AST *body; } ASTFunction; typedef struct { AST **childv; } ASTArray; typedef struct { AST *k; // Either MemberAccess or Key AST *v; } ASTBlock_Entry; typedef struct { ASTBlock_Entry *entryv; } ASTBlock; typedef struct { StringView *accessv; } ASTMemberAccess; typedef struct { AST *function; AST **argv; } ASTFunctionCall; typedef struct { bool is_str; StringView s; } ASTKey; struct AST { ASTKind kind; SourceLocation location; union { int64_t i; double r; ASTKey s; StringView p; bool b; ASTFunction f; ASTArray a; ASTBlock bl; ASTMemberAccess m; ASTFunctionCall fc; } v; }; typedef struct { Lexer *lexer; Instance *instance; Token cur, next; } Parser; bool Parser_next(Parser *parser) { parser->cur = parser->next; parser->next = Lexer_next(parser->lexer); if (parser->next.type == TokenType_Error) { strcpy(parser->instance->last_error, "Failed to get parser token"); } return true; } bool Parser_init(Parser *out_parser, Lexer *lexer, Instance *instance) { memset(out_parser, 0, sizeof(*out_parser)); out_parser->lexer = lexer; out_parser->instance = instance; out_parser->next = Lexer_next(lexer); if (!Parser_next(out_parser)) { return false; } return true; } bool Parser_accept(Parser *parser, TokenType type, SourceLocation *out_location) { if (parser->cur.type == type) { if (out_location) { *out_location = parser->cur.location; } if (!Parser_next(parser)) { return false; } return true; } return false; } #undef ALLOC #undef FREE #define ALLOC(sz) (parser->instance->alloc((sz))) #define FREE(ptr) (parser->instance->free((ptr))) void AST_free_parser(AST *ast, Parser *parser) { assert(parser); if (!ast) { return; } switch (ast->kind) { case ASTKind_Function: AST_free_parser(ast->v.f.body, parser); vector_free(ast->v.f.argv); break; case ASTKind_Array: for (size_t i = 0; i < vector_size(ast->v.a.childv); i++) AST_free_parser(ast->v.a.childv[i], parser); vector_free(ast->v.a.childv); break; case ASTKind_Block: for (size_t i = 0; i < vector_size(ast->v.bl.entryv); i++) { AST_free_parser(ast->v.bl.entryv[i].k, parser); AST_free_parser(ast->v.bl.entryv[i].v, parser); } vector_free(ast->v.bl.entryv); break; case ASTKind_MemberAccess: vector_free(ast->v.m.accessv); break; case ASTKind_FunctionCall: AST_free_parser(ast->v.fc.function, parser); for (size_t i = 0; i < vector_size(ast->v.fc.argv); i++) AST_free_parser(ast->v.fc.argv[i], parser); vector_free(ast->v.fc.argv); break; default: break; } FREE(ast); } AST *parser_parse_dot_access(Parser *parser, StringView *current) { assert(parser); assert(parser->cur.type == TokenType_Identifier || parser->cur.type == TokenType_String); StringView *accessv = vector_create(); if (!accessv) { strcpy(parser->instance->last_error, "Failed to allocate vector"); return NULL; } if (current) { vector_add(&accessv, StringView, *current); } SourceLocation loc = parser->cur.location; SourceLocation last_loc = parser->cur.location; while (true) { if (parser->cur.type != TokenType_Identifier && parser->cur.type != TokenType_String) { break; } last_loc = parser->cur.location; vector_add(&accessv, StringView, parser->cur.v.s); if (!Parser_next(parser)) { strcpy(parser->instance->last_error, "Failed to get next token in dot access parsing"); vector_free(accessv); return NULL; } if (!Parser_accept(parser, TokenType_Dot, NULL)) { break; } } assert(vector_size(accessv) != 0); loc.range.end = last_loc.range.end; AST *ast = ALLOC(sizeof(*ast)); ast->kind = ASTKind_MemberAccess; ast->v.m.accessv = accessv; ast->location = loc; if (vector_size(accessv) == 1) { ast->kind = ASTKind_Key; ast->v.s.s = accessv[0]; vector_free(accessv); } return ast; } AST *parser_parse_value(Parser *parser) { AST *ast = ALLOC(sizeof(*ast)); ast->location = parser->cur.location; if (parser->cur.type == TokenType_Integer) { ast->kind = ASTKind_Integer; ast->v.i = parser->cur.v.i; } else if (parser->cur.type == TokenType_Real) { ast->kind = ASTKind_Real; ast->v.r = parser->cur.v.r; } else if (parser->cur.type == TokenType_String || parser->cur.type == TokenType_Identifier) { ast->kind = ASTKind_Key; ast->v.s.s = parser->cur.v.s; ast->v.s.is_str = parser->cur.type == TokenType_String; if (parser->next.type == TokenType_Dot) { FREE(ast); return parser_parse_dot_access(parser, NULL); } else if (parser->cur.type == TokenType_Identifier) { if (sv_eq(ast->v.s.s, SV("fn"))) { if (!Parser_next(parser)) { strcpy(parser->instance->last_error, "Failed to advance fn keyword"); FREE(ast); return NULL; } ast->kind = ASTKind_Function; ast->v.f.argv = vector_create(); if (!ast->v.f.argv) { strcpy(parser->instance->last_error, "Failed to allocate vector"); FREE(ast); return NULL; } while (true) { if (Parser_accept(parser, TokenType_Set, NULL)) { break; } if (parser->cur.type != TokenType_Identifier) { strcpy(parser->instance->last_error, "Expected identifier for function argument"); vector_free(ast->v.f.argv); FREE(ast); return NULL; } vector_add(&ast->v.f.argv, StringView, parser->cur.v.s); if (!Parser_next(parser)) { vector_free(ast->v.f.argv); FREE(ast); return NULL; } } ast->v.f.body = parser_parse_value(parser); if (!ast->v.f.body) { vector_free(ast->v.f.argv); FREE(ast); return NULL; } ast->location.range.end = ast->v.f.body->location.range.end; return ast; } else if (sv_eq(ast->v.s.s, SV("on")) || sv_eq(ast->v.s.s, SV("true"))) { ast->kind = ASTKind_Boolean; ast->v.b = true; } else if (sv_eq(ast->v.s.s, SV("off")) || sv_eq(ast->v.s.s, SV("false"))) { ast->kind = ASTKind_Boolean; ast->v.b = false; } } } else if (parser->cur.type == TokenType_Path) { ast->kind = ASTKind_Path; ast->v.p = parser->cur.v.p; } else if (Parser_accept(parser, TokenType_LBracket, NULL)) { ast->kind = ASTKind_Array; ast->v.a.childv = vector_create(); if (!ast->v.a.childv) { strcpy(parser->instance->last_error, "Failed to allocate vector"); FREE(ast); return NULL; } SourceLocation rbracket_loc; while (true) { if (Parser_accept(parser, TokenType_RBracket, &rbracket_loc)) { break; } AST *value = parser_parse_value(parser); if (!value) { for (size_t i = 0; i < vector_size(ast->v.a.childv); i++) { AST_free_parser(ast->v.a.childv[i], parser); } vector_free(ast->v.a.childv); FREE(ast); return NULL; } vector_add(&ast->v.a.childv, AST *, value); } ast->location.range.end = rbracket_loc.range.end; return ast; } else if (Parser_accept(parser, TokenType_LSquirly, NULL)) { ast->kind = ASTKind_Block; ast->v.bl.entryv = vector_create(); if (!ast->v.bl.entryv) { strcpy(parser->instance->last_error, "Failed to allocate vector"); FREE(ast); return NULL; } SourceLocation rsquirly_loc; while (true) { if (Parser_accept(parser, TokenType_RSquirly, &rsquirly_loc)) { break; } if (parser->cur.type != TokenType_Identifier && parser->cur.type != TokenType_String) { strcpy(parser->instance->last_error, "Expected identifier or string for object key"); for (size_t i = 0; i < vector_size(ast->v.bl.entryv); i++) { AST_free_parser(ast->v.bl.entryv[i].k, parser); AST_free_parser(ast->v.bl.entryv[i].v, parser); } vector_free(ast->v.bl.entryv); FREE(ast); return NULL; } AST *key = parser_parse_dot_access(parser, NULL); if (!Parser_accept(parser, TokenType_Set, NULL)) { strcpy(parser->instance->last_error, "Expected = after object key"); for (size_t i = 0; i < vector_size(ast->v.bl.entryv); i++) { AST_free_parser(ast->v.bl.entryv[i].k, parser); AST_free_parser(ast->v.bl.entryv[i].v, parser); } vector_free(ast->v.bl.entryv); FREE(ast); return NULL; } AST *value = parser_parse_value(parser); if (!value) { AST_free_parser(key, parser); vector_free(ast->v.bl.entryv); FREE(ast); return NULL; } assert( key->kind == ASTKind_MemberAccess || key->kind == ASTKind_Key); ASTBlock_Entry entry = { .k = key, .v = value, }; vector_add(&ast->v.bl.entryv, ASTBlock_Entry, entry); } return ast; } else if (Parser_accept(parser, TokenType_LParen, NULL)) { ast->kind = ASTKind_FunctionCall; ast->v.fc.function = parser_parse_value(parser); ast->v.fc.argv = vector_create(); if (!ast->v.fc.argv) { strcpy(parser->instance->last_error, "Failed to allocate vector"); FREE(ast); return NULL; } SourceLocation rparen_loc; while (true) { if (Parser_accept(parser, TokenType_RParen, &rparen_loc)) { break; } AST *value = parser_parse_value(parser); if (!value) { for (size_t i = 0; i < vector_size(ast->v.fc.argv); i++) { AST_free_parser(ast->v.fc.argv[i], parser); } vector_free(ast->v.fc.argv); FREE(ast); return NULL; } vector_add(&ast->v.fc.argv, AST *, value); } ast->location.range.end = rparen_loc.range.end; return ast; } else { if (parser->cur.type == TokenType_End) { strcpy(parser->instance->last_error, "Expected value, got end of file"); } else { strcpy(parser->instance->last_error, "Unexpected token for value"); } FREE(ast); ast = NULL; } if (!Parser_next(parser)) { if (ast) { FREE(ast); } return NULL; } return ast; } // TODO: Print SourceLocation in last error. AST *Parser_parse(Parser *parser) { return parser_parse_value(parser); } #undef ALLOC #undef FREE #define ALLOC(sz) (instance->alloc((sz))) #define FREE(ptr) (instance->free((ptr))) static Value *ensure_child_obj(Instance *inst, Value *parent, StringView key) { ValueObject *obj = &parent->v.o; for (size_t i = 0; i < vector_size(obj->entryv); ++i) { if (sv_eq(obj->entryv[i].k, key)) { return obj->entryv[i].v; } } Value *child = inst->alloc(sizeof *child); child->instance = inst; child->type = dcfg_ValueType_Object; child->v.o.entryv = vector_create(); ValueObjectEntry e = { .k = key, .v = child, .key_allocated = false }; vector_add(&obj->entryv, ValueObjectEntry, e); return child; } Value *ast_to_value(dcfg_Instance *instance, AST *root) { Value *value = ALLOC(sizeof(*value)); value->instance = instance; if (root->kind == ASTKind_Key) { if (root->v.s.is_str) { value->type = dcfg_ValueType_String; value->v.s = root->v.s.s; } else { value->type = dcfg_ValueType_MemberAccess; value->v.ma.accessv = vector_create(); vector_add(value->v.ma.accessv, StringView, root->v.s.s); } } else if (root->kind == ASTKind_Path) { value->type = dcfg_ValueType_Path; value->v.p = root->v.p; } else if (root->kind == ASTKind_Boolean) { value->type = dcfg_ValueType_Boolean; value->v.b = root->v.b; } else if (root->kind == ASTKind_Integer) { value->type = dcfg_ValueType_Integer; value->v.i = root->v.i; } else if (root->kind == ASTKind_Real) { value->type = dcfg_ValueType_Real; value->v.r = root->v.r; } else if (root->kind == ASTKind_Block) { value->type = dcfg_ValueType_Object; value->v.o.entryv = vector_create(); for (size_t i = 0; i < vector_size(root->v.bl.entryv); ++i) { ASTBlock_Entry *e = &root->v.bl.entryv[i]; Value *rhs = ast_to_value(instance, e->v); if (!rhs) { for (size_t j = 0; j < vector_size(value->v.o.entryv); j++) { dcfg_destroy(value->v.o.entryv[j].v); } vector_free(value->v.o.entryv); FREE(value); return NULL; } Value *target = value; StringView field; if (e->k->kind == ASTKind_Key) { field = e->k->v.s.s; } else { ASTMemberAccess *ma = &e->k->v.m; for (size_t j = 0; j + 1 < vector_size(ma->accessv); ++j) target = ensure_child_obj(instance, target, ma->accessv[j]); field = ma->accessv[vector_size(ma->accessv) - 1]; } if (target->type != dcfg_ValueType_Object) { strcpy(instance->last_error, "Previous declaration is not an object"); dcfg_destroy(rhs); for (size_t j = 0; j < vector_size(value->v.o.entryv); j++) dcfg_destroy(value->v.o.entryv[j].v); vector_free(value->v.o.entryv); FREE(value); return NULL; } 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, field)) { dcfg_destroy(obj->entryv[j].v); obj->entryv[j].v = rhs; replaced = true; break; } } if (!replaced) { ValueObjectEntry new_e = { .k = field, .v = rhs, .key_allocated = false }; vector_add(&obj->entryv, ValueObjectEntry, new_e); } } } else if (root->kind == ASTKind_Array) { value->type = dcfg_ValueType_Array; value->v.a.valuev = vector_create(); for (size_t i = 0; i < vector_size(root->v.a.childv); i++) { Value *v = ast_to_value(instance, root->v.a.childv[i]); if (!v) { for (size_t i = 0; i < vector_size(value->v.a.valuev); i++) { dcfg_destroy(value->v.a.valuev[i]); } vector_free(value->v.a.valuev); FREE(value); return NULL; } vector_add(&value->v.a.valuev, Value *, v); } } else if (root->kind == ASTKind_Function) { value->type = dcfg_ValueType_Function; value->v.f.is_builtin = false; value->v.f.v.f.argv = vector_create(); for (size_t i = 0; i < vector_size(root->v.f.argv); i++) { vector_add(&value->v.f.v.f.argv, StringView, root->v.f.argv[i]); } Value *v = ast_to_value(instance, root->v.f.body); if (!v) { vector_free(value->v.f.v.f.argv); FREE(value); return NULL; } value->v.f.v.f.body = v; } else if (root->kind == ASTKind_MemberAccess) { value->type = dcfg_ValueType_MemberAccess; 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); if (!function) { FREE(value); return NULL; } value->v.c.function = function; value->v.c.argv = vector_create(); for (size_t i = 0; i < vector_size(root->v.fc.argv); i++) { Value *arg = ast_to_value(instance, root->v.fc.argv[i]); if (!arg) { for (size_t i = 0; i < vector_size(value->v.c.argv); i++) dcfg_destroy(value->v.c.argv[i]); vector_free(value->v.c.argv); dcfg_destroy(value->v.c.function); FREE(value); return NULL; } vector_add(&value->v.c.argv, Value *, arg); } } return value; } dcfg_Value *dcfg_parse(dcfg_Instance *instance, dcfg_StringView const file_path) { char *path_buf = malloc(file_path.size + 1); memset(path_buf, 0, file_path.size + 1); memcpy(path_buf, file_path.data, file_path.size); char *abs = instance->realpath(path_buf); free(path_buf); if (!abs) { snprintf(instance->last_error, sizeof(instance->last_error) - 1, "realpath: %s", strerror(errno)); return NULL; } StringView abs_sv = SV(abs); FILE *fp = instance->fopen(abs, "r"); if (!fp) { FREE(abs); snprintf(instance->last_error, sizeof(instance->last_error) - 1, "fopen: %s", strerror(errno)); return NULL; } if (instance->fseek(fp, 0, SEEK_END)) { FREE(abs); fclose(fp); snprintf(instance->last_error, sizeof(instance->last_error) - 1, "fseek: %s", strerror(errno)); return NULL; } long const size = ftell(fp); if (instance->fseek(fp, 0, SEEK_SET)) { FREE(abs); fclose(fp); snprintf(instance->last_error, sizeof(instance->last_error) - 1, "fseek: %s", strerror(errno)); return NULL; } StringView str = { .size = size, .data = ALLOC(size + 1), }; if (!str.data) { FREE(abs); fclose(fp); snprintf(instance->last_error, sizeof(instance->last_error) - 1, "Failed to allocate source buffer: %s", strerror(errno)); return NULL; } if (!fread((void *)str.data, str.size, 1, fp)) { FREE(abs); FREE((void *)str.data); fclose(fp); snprintf(instance->last_error, sizeof(instance->last_error) - 1, "fread: %s", strerror(errno)); return NULL; } ((char *)str.data)[str.size] = '\0'; Lexer lexer; if (!Lexer_init(&lexer, str, abs_sv)) { FREE(abs); FREE((void *)str.data); fclose(fp); snprintf(instance->last_error, sizeof(instance->last_error) - 1, "Could not init lexer"); return NULL; } Parser parser; Parser_init(&parser, &lexer, instance); AST *ast = Parser_parse(&parser); if (!ast) { if (!*instance->last_error) { strcpy(instance->last_error, "Could not parse file"); } FREE(abs); FREE((void *)str.data); fclose(fp); return NULL; } instance->last_error[0] = '\0'; Value *v = ast_to_value(instance, ast); AST_free_parser(ast, &parser); if (!v) { if (!*instance->last_error) { strcpy(instance->last_error, "Could not get Value tree from AST"); } dcfg_destroy(v); FREE(abs); FREE((void *)str.data); fclose(fp); return NULL; } instance->last_error[0] = '\0'; vector_add(&instance->sourcev, StringView, str); vector_add(&instance->source_pathv, StringView, abs_sv); return v; } void dcfg_destroy(dcfg_Value *value) { if (!value) return; switch (value->type) { case dcfg_ValueType_Object: for (size_t i = 0; i < vector_size(value->v.o.entryv); i++) { dcfg_destroy(value->v.o.entryv[i].v); if (value->v.o.entryv[i].key_allocated) { value->instance->free((void *)value->v.o.entryv[i].k.data); } } vector_free(value->v.o.entryv); break; case dcfg_ValueType_Array: for (size_t i = 0; i < vector_size(value->v.a.valuev); i++) { dcfg_destroy(value->v.a.valuev[i]); } vector_free(value->v.a.valuev); break; case dcfg_ValueType_Function: if (!value->v.f.is_builtin) { dcfg_destroy(value->v.f.v.f.body); vector_free(value->v.f.v.f.argv); } break; case dcfg_ValueType_FunctionCall: dcfg_destroy(value->v.c.function); for (size_t i = 0; i < vector_size(value->v.c.argv); i++) { dcfg_destroy(value->v.c.argv[i]); } vector_free(value->v.c.argv); break; case dcfg_ValueType_MemberAccess: vector_free(value->v.ma.accessv); break; default: break; } 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) return dcfg_ValueType_Nil; (void)evaluate; return value->type; } bool dcfg_Value_get_object_field_ex(dcfg_Value *value, dcfg_StringView key, dcfg_Value **out_value, bool evaluate) { if (!value || value->type != dcfg_ValueType_Object) return false; ValueObject *obj = &value->v.o; for (size_t i = 0; i < vector_size(obj->entryv); ++i) { ValueObjectEntry *entry = &obj->entryv[i]; if (sv_eq(entry->k, key)) { *out_value = entry->v; if (evaluate) dcfg_Value_evaluate(*out_value, out_value); return true; } } return false; } bool dcfg_Value_get_array_item_ex( dcfg_Value *value, size_t index, dcfg_Value **out_value, bool evaluate) { if (!value || value->type != dcfg_ValueType_Array) return false; ValueArray *arr = &value->v.a; if (index >= vector_size(arr->valuev)) return false; *out_value = arr->valuev[index]; if (evaluate) dcfg_Value_evaluate(*out_value, out_value); return true; } bool dcfg_Value_get_function_body_ex( dcfg_Value *value, dcfg_Value **out_value, bool evaluate) { if (!value || value->type != dcfg_ValueType_Function || value->v.f.is_builtin) return false; if (out_value) *out_value = value->v.f.v.f.body; if (evaluate && out_value && *out_value) dcfg_Value_evaluate(*out_value, out_value); return true; } bool dcfg_Value_get_boolean(dcfg_Value *value, bool *out_value) { if (!value || value->type != dcfg_ValueType_Boolean) return false; if (out_value) *out_value = value->v.b; return true; } bool dcfg_Value_get_integer(dcfg_Value *value, int64_t *out_value) { if (!value || value->type != dcfg_ValueType_Integer) return false; if (out_value) *out_value = value->v.i; return true; } bool dcfg_Value_get_real(dcfg_Value *value, double *out_value) { if (!value || value->type != dcfg_ValueType_Real) return false; if (out_value) *out_value = value->v.r; return true; } bool dcfg_Value_get_string(dcfg_Value *value, dcfg_StringView *out_sv) { if (!value || value->type != dcfg_ValueType_String) return false; if (out_sv) *out_sv = value->v.s; return true; } bool dcfg_Value_get_path(dcfg_Value *value, dcfg_StringView *out_sv) { if (!value || value->type != dcfg_ValueType_Path) return false; if (out_sv) *out_sv = value->v.p; return true; } bool dcfg_Value_get_object_keys(dcfg_Value *value, size_t capacity, size_t *out_count, dcfg_StringView *out_keys) { if (!value || value->type != dcfg_ValueType_Object) return false; ValueObject *obj = &value->v.o; size_t count = vector_size(obj->entryv); if (out_count) *out_count = count; if (out_keys) { size_t n = capacity < count ? capacity : count; for (size_t i = 0; i < n; ++i) out_keys[i] = obj->entryv[i].k; } return true; } bool dcfg_Value_get_array_size(dcfg_Value *value, size_t *out_size) { if (!value || value->type != dcfg_ValueType_Array) return false; if (out_size) *out_size = vector_size(value->v.a.valuev); return true; } static bool eval_member(ValueMemberAccess *ma, Environment *env, Value **out) { 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_in_env( dcfg_Value *value, Environment *frame, dcfg_Value **out_value) { assert(value); assert(out_value); bool ret = true; 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; } else if (value->type == dcfg_ValueType_Boolean) { v->type = dcfg_ValueType_Boolean; v->v.b = value->v.b; } else if (value->type == dcfg_ValueType_Integer) { v->type = dcfg_ValueType_Integer; v->v.i = value->v.i; } else if (value->type == dcfg_ValueType_Real) { v->type = dcfg_ValueType_Real; v->v.r = value->v.r; } else if (value->type == dcfg_ValueType_String) { v->type = dcfg_ValueType_String; v->v.s = value->v.s; } else if (value->type == dcfg_ValueType_Path) { v->type = dcfg_ValueType_Path; v->v.p = value->v.p; } else if (value->type == dcfg_ValueType_Object) { v->type = dcfg_ValueType_Object; v->v.o.entryv = vector_create(); 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_in_env(e->v, frame, &new_v); if (!res) { ret = false; break; } 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); ((char *)ne.k.data)[ne.k.size] = '\0'; vector_add(&v->v.o.entryv, ValueObjectEntry, ne); } } else if (value->type == dcfg_ValueType_Array) { v->type = dcfg_ValueType_Array; 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_in_env(value->v.a.valuev[i], frame, &val); if (!res) { ret = false; } vector_add(&v->v.a.valuev, Value *, val); } } else if (value->type == dcfg_ValueType_Function) { Value *out_value_prev = *out_value; bool res = dcfg_call_function(value, NULL, 0, out_value); if (!res) { dcfg_destroy(out_value_prev); ret = false; } } else if (value->type == dcfg_ValueType_MemberAccess) { 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_in_env(value->v.c.function, frame, &function); if (!res || function->type != dcfg_ValueType_Function) { ret = false; } else { Value *out_value_prev = *out_value; bool res = dcfg_call_function(function, value->v.c.argv, vector_size(value->v.c.argv), out_value); if (!res) { ret = false; } else { dcfg_destroy(out_value_prev); } } } else { assert(0 && "Invalid value type"); } if (!ret) { dcfg_destroy(*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"