Files
dcfg/src/dcfg.c

1158 lines
25 KiB
C
Raw Normal View History

#include <dcfg.h>
#include <stdio.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 _XOPEN_SOURCE 200809L
#else
# define _POSIX_C_SOURCE 0L
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef DCFG_PTHREAD_SUPPORT
# include <pthread.h>
#else
typedef struct {
int unused;
} pthread_mutex_t;
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)
{
size_t n = strlen(s);
char *clean = malloc(n + 1);
if (!clean) {
errno = ENOMEM;
if (end)
*end = (char *)s;
return 0;
}
size_t w = 0;
for (size_t r = 0; r < n && s[r]; ++r)
if (s[r] != '_')
clean[w++] = s[r];
clean[w] = '\0';
char *tmp_end;
int64_t v = strtoll(clean, &tmp_end, base);
if (end) {
size_t consumed = (size_t)(tmp_end - clean);
size_t i = 0, c = 0;
while (i < n && c < consumed) {
if (s[i] != '_')
++c;
++i;
}
*end = (char *)s + i;
}
free(clean);
return v;
}
double dcfg_strtod(char const *s, char **end)
{
size_t n = strlen(s);
char *clean = malloc(n + 1);
if (!clean) {
errno = ENOMEM;
if (end)
*end = (char *)s;
return 0.0;
}
size_t w = 0;
for (size_t r = 0; r < n && s[r]; ++r)
if (s[r] != '_')
clean[w++] = s[r];
clean[w] = '\0';
char *tmp_end;
double v = strtod(clean, &tmp_end);
if (end) {
size_t consumed = (size_t)(tmp_end - clean);
size_t i = 0, c = 0;
while (i < n && c < consumed) {
if (s[i] != '_')
++c;
++i;
}
*end = (char *)s + i;
}
free(clean);
return v;
}
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;
} ValueObjectEntry;
typedef struct {
ValueObjectEntry *entryv;
} ValueObject;
typedef struct {
Value **valuev;
} ValueArray;
typedef struct {
StringView *argv;
Value *body;
} ValueFunctionF;
typedef struct {
bool is_builtin;
union {
Value *(*bi)(Value **argv, size_t argc);
ValueFunctionF f;
} v;
} ValueFunction;
typedef struct {
Value *function;
Value **argv;
} ValueFunctionCall;
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;
ValueFunctionCall c;
} v;
};
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];
};
#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_Instance *dcfg_make_instance(dcfg_InstanceCreateInfo const *create_info)
{
assert(create_info);
dcfg_Instance *instance = calloc(1, sizeof(*instance));
if (!instance) {
return NULL;
}
pthread_mutex_init(&instance->mtx, NULL);
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);
return instance;
}
void dcfg_destroy_instance(dcfg_Instance *instance)
{
assert(instance);
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;
}
return (int32_t)utf8proc_iterate(
(uint8_t const *)src.data + pos, (int)(src.size - pos), len);
}
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)
{
return (cp <= 0x7F && isalpha(cp))
|| (utf8proc_category(cp) >= UTF8PROC_CATEGORY_LU
&& utf8proc_category(cp) <= UTF8PROC_CATEGORY_LO);
}
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;
}
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 == '_')
lex_advance(lx);
if (lx->ch == '.') {
real = true;
lex_advance(lx);
while (isdigit_cp(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;
struct AST {
ASTKind kind;
SourceLocation location;
union {
int64_t i;
double r;
StringView 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);
bool ret = parser->next.type != TokenType_Error;
if (!ret) {
strcpy(parser->instance->last_error, "Failed to get parser token");
}
return ret;
}
bool Parser_init(Parser *out_parser, Lexer *lexer, Instance *instance)
{
out_parser->lexer = lexer;
out_parser->instance = instance;
if (!Parser_next(out_parser)) {
return false;
}
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, *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, 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 = 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 = parser->cur.v.s;
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, 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, parser->cur.v.s);
}
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, SV("on"))
|| sv_eq(ast->v.s, SV("true"))) {
ast->kind = ASTKind_Boolean;
ast->v.b = true;
} else if (sv_eq(ast->v.s, SV("off"))
|| sv_eq(ast->v.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, 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");
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");
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, 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, 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)))
Value *ast_to_value(dcfg_Instance *instance, AST *root)
{
Value *value = ALLOC(sizeof(*value));
value->instance = instance;
if (root->kind == ASTKind_Key) {
// FIXME: Implement
FREE(value);
return NULL;
} 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) {
// FIXME: Implement
FREE(value);
return NULL;
} 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, 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, 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) {
// FIXME: Implement
FREE(value);
return NULL;
} 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) {
// TODO: Free argv values
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);
FREE(value);
return NULL;
}
vector_add(&value->v.c.argv, arg);
}
}
return value;
}
dcfg_Value *dcfg_parse(dcfg_Instance *instance, dcfg_StringView const file_path)
{
char path_buf[file_path.size + 1] = {};
memcpy(path_buf, file_path.data, file_path.size);
char *abs = instance->realpath(path_buf);
if (!abs) {
snprintf(instance->last_error, sizeof(instance->last_error) - 1,
"realpath: %s", strerror(errno));
}
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,
};
str.data = ALLOC(size);
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;
}
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) {
FREE(abs);
FREE((void *)str.data);
fclose(fp);
return NULL;
}
Value *v = ast_to_value(instance, ast);
if (!v) {
dcfg_destroy(v);
FREE(abs);
FREE((void *)str.data);
fclose(fp);
return NULL;
}
return v;
}
void dcfg_destroy(dcfg_Value *value)
{
(void)value;
// FIXME: Implement
}
// Libraries
#include "vendor/utf8proc.c"
#include "vendor/vec.c"