From e49654efb173ecfcc64f49620c2d332c03c706d9 Mon Sep 17 00:00:00 2001 From: Slendi Date: Sat, 26 Jul 2025 23:41:34 +0300 Subject: [PATCH] Add dump Signed-off-by: Slendi --- CMakeLists.txt | 22 +++++ build.sh | 22 ++++- include/dcfg.h | 8 ++ programs/dcfg_dump.c | 159 +++++++++++++++++++++++++++++++ samples/testing.dcfg | 2 + src/dcfg.c | 222 +++++++++++++++++++++++++++++++++++++++---- 6 files changed, 411 insertions(+), 24 deletions(-) create mode 100644 programs/dcfg_dump.c create mode 100644 samples/testing.dcfg diff --git a/CMakeLists.txt b/CMakeLists.txt index 638b572..7d9daf3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_C_EXTENSIONS ON) # Would've done OFF but I need typeof (for now) option(DCFG_BUILD_SHARED "Build DCFG as a shared library" ON) 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) find_package(Threads) @@ -54,3 +55,24 @@ if(MSVC) else() target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -pedantic -Werror -Wno-newline-eof -Wno-language-extension-token) endif() + +if(DCFG_BUILD_PROGRAMS) + set(PROGRAMS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/programs) + file(GLOB PROGRAM_SOURCES "${PROGRAMS_DIR}/*.c") + + foreach(PROG_SRC ${PROGRAM_SOURCES}) + get_filename_component(PROG_NAME ${PROG_SRC} NAME_WE) + add_executable(${PROG_NAME} ${PROG_SRC}) + target_include_directories(${PROG_NAME} PRIVATE ${INCLUDE_DIR}) + target_link_libraries(${PROG_NAME} PRIVATE ${PROJECT_NAME}) + 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() + install(TARGETS ${PROG_NAME} DESTINATION bin) + endforeach() +endif() diff --git a/build.sh b/build.sh index 3cc12e6..3da8546 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,5 @@ #!/bin/sh + set -e BUILD_DIR="BUILD" @@ -7,8 +8,10 @@ CFLAGS="-std=gnu99 -Wall -Wextra -pedantic -Werror -Wno-newline-eof -Wno-languag BUILD_SHARED=1 PTHREAD_SUPPORT=1 POSIX_SUPPORT=1 +BUILD_PROGRAMS=0 SRC_DIR="src" INCLUDE_DIR="include" +PROGRAMS_DIR="programs" OUTPUT_NAME="libdcfg" for arg in "$@"; do @@ -17,12 +20,13 @@ for arg in "$@"; do --release) CFLAGS="$CFLAGS -O2" ;; --no-pthread) PTHREAD_SUPPORT=0 ;; --no-posix) POSIX_SUPPORT=0 ;; + --with-programs) BUILD_PROGRAMS=1 ;; --clean) rm -rf "$BUILD_DIR" "$INSTALL_DIR"; echo "Cleaned."; exit 0 ;; esac done # Setup directories -mkdir -p "$BUILD_DIR" "$INSTALL_DIR/lib" "$INSTALL_DIR/include" +mkdir -p "$BUILD_DIR" "$INSTALL_DIR/lib" "$INSTALL_DIR/include" "$INSTALL_DIR/bin" # Compiler and linker CC=${CC:-cc} @@ -39,16 +43,28 @@ echo "Building DCFG..." cd "$BUILD_DIR" set -x +# Shared library $CC $CFLAGS -fPIC -shared "../$SRC_DIR/dcfg.c" -I"../$INCLUDE_DIR" -o "$OUTPUT_NAME.so" $LIBS +# Static library $CC $CFLAGS -c "../$SRC_DIR/dcfg.c" -I"../$INCLUDE_DIR" ar rcs "$OUTPUT_NAME.a" dcfg.o - set +x -echo "Installing..." +echo "Installing library..." cp -r "../$INCLUDE_DIR/"* "$INSTALL_DIR/include/" cp "$OUTPUT_NAME.so" "$INSTALL_DIR/lib/" cp "$OUTPUT_NAME.a" "$INSTALL_DIR/lib/" +# Build programs if requested +if [ "$BUILD_PROGRAMS" -eq 1 ]; then + echo "Building example programs..." + for src in "../$PROGRAMS_DIR"/*.c; do + prog=$(basename "$src" .c) + echo "Building $prog..." + $CC $CFLAGS "$src" -I"../$INCLUDE_DIR" "$OUTPUT_NAME.a" $LIBS -o "$prog" + cp "$prog" "$INSTALL_DIR/bin/" + done +fi + echo "Done. Installed to $INSTALL_DIR" diff --git a/include/dcfg.h b/include/dcfg.h index 11ef073..3263842 100644 --- a/include/dcfg.h +++ b/include/dcfg.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -17,6 +18,7 @@ typedef struct dcfg_StringView { char const *data; size_t size; } dcfg_StringView; +#define dcfg_SV(cstr) ((dcfg_StringView) { .data = cstr, .size = strlen(cstr) }) typedef void *(*dcfg_AllocFn)(size_t); // This should automatically zero memory. typedef void (*dcfg_FreeFn)(void *); @@ -75,6 +77,8 @@ bool dcfg_Value_get_object_field_ex(dcfg_Value *value, dcfg_StringView const key, dcfg_Value **out_value, bool const evaluate); bool dcfg_Value_get_array_item_ex(dcfg_Value *value, size_t const index, dcfg_Value **out_value, bool const evaluate); +bool dcfg_Value_get_function_body_ex( + dcfg_Value *value, dcfg_Value **out_value, bool evaluate); bool dcfg_Value_get_boolean(dcfg_Value *value, bool *out_value); bool dcfg_Value_get_integer(dcfg_Value *value, int64_t *out_value); @@ -95,6 +99,10 @@ static inline bool dcfg_Value_get_array_item( { return dcfg_Value_get_array_item_ex(value, index, out_value, true); } +bool dcfg_Value_get_function_body(dcfg_Value *value, dcfg_Value **out_value) +{ + return dcfg_Value_get_function_body_ex(value, out_value, true); +} bool dcfg_call_function(dcfg_Value *function, dcfg_Value **args, size_t arg_count, dcfg_Value **out_value); diff --git a/programs/dcfg_dump.c b/programs/dcfg_dump.c new file mode 100644 index 0000000..9ecf69a --- /dev/null +++ b/programs/dcfg_dump.c @@ -0,0 +1,159 @@ +#include + +#include +#include +#include + +void walk_value(dcfg_Value *value, bool evaluate, int indent); + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + dcfg_InstanceCreateInfo ci = { 0 }; + dcfg_Instance *instance = dcfg_make_instance(&ci); + if (!instance) { + printf("Failed to create DCFG instance. Error: %s\n", + dcfg_last_error(instance)); + return 1; + } + + dcfg_Value *value = dcfg_parse(instance, dcfg_SV(argv[1])); + if (!value) { + printf("Failed to parse DCFG file. Error: %s\n", + dcfg_last_error(instance)); + return 1; + } + + walk_value(value, false, 0); + + dcfg_destroy(value); + dcfg_destroy_instance(instance); +} + +static void print_indent(int indent) +{ + for (int i = 0; i < indent; ++i) + putchar('\t'); +} + +void walk_value(dcfg_Value *value, bool evaluate, int indent) +{ + print_indent(indent); + + dcfg_ValueType type = dcfg_Value_type_ex(value, evaluate); + + switch (type) { + case dcfg_ValueType_Nil: + printf("null"); + break; + + case dcfg_ValueType_Boolean: { + bool b; + dcfg_Value_get_boolean(value, &b); + printf("%s", b ? "true" : "false"); + } break; + + case dcfg_ValueType_Integer: { + int64_t v; + dcfg_Value_get_integer(value, &v); + printf("%" PRId64, v); + } break; + + case dcfg_ValueType_Real: { + double v; + dcfg_Value_get_real(value, &v); + printf("%g", v); + } break; + + case dcfg_ValueType_String: { + dcfg_StringView sv; + dcfg_Value_get_string(value, &sv); + printf("\"%.*s\"", (int)sv.size, sv.data); + } break; + + case dcfg_ValueType_Path: { + dcfg_StringView sv; + dcfg_Value_get_path(value, &sv); + printf("", (int)sv.size, sv.data); + } break; + + case dcfg_ValueType_Object: { + size_t n = 0; + dcfg_Value_get_object_keys(value, 0, &n, NULL); + dcfg_StringView *keys + = n ? (dcfg_StringView *)alloca(n * sizeof(*keys)) : NULL; + dcfg_Value_get_object_keys(value, n, &n, keys); + + printf("{\n"); + for (size_t i = 0; i < n; ++i) { + dcfg_Value *child; + if (!dcfg_Value_get_object_field_ex( + value, keys[i], &child, evaluate)) + continue; + + print_indent(indent + 1); + printf("\"%.*s\": ", (int)keys[i].size, keys[i].data); + walk_value(child, evaluate, indent + 1); + if (i + 1 < n) + printf(","); + printf("\n"); + } + print_indent(indent); + printf("}"); + } break; + + case dcfg_ValueType_Array: { + size_t sz = 0; + dcfg_Value_get_array_size(value, &sz); + + printf("[\n"); + for (size_t i = 0; i < sz; ++i) { + dcfg_Value *item; + if (!dcfg_Value_get_array_item_ex(value, i, &item, evaluate)) + continue; + + walk_value(item, evaluate, indent + 1); + if (i + 1 < sz) + printf(","); + printf("\n"); + } + print_indent(indent); + printf("]"); + } break; + + case dcfg_ValueType_Function: { + dcfg_Value *body = NULL; + + if (dcfg_Value_get_function_body_ex(value, &body, evaluate) && body) { + printf(" "); + walk_value(body, evaluate, indent + 1); + } else { + printf(""); + } + } break; + + case dcfg_ValueType_FunctionCall: + if (evaluate) { + dcfg_Value *res; + if (dcfg_Value_evaluate(value, &res)) + walk_value(res, evaluate, indent); + else + printf(""); + } else { + printf(""); + } + break; + + default: + printf(""); + break; + } + + if (indent == 0) { + putchar('\n'); + } +} diff --git a/samples/testing.dcfg b/samples/testing.dcfg new file mode 100644 index 0000000..24fd069 --- /dev/null +++ b/samples/testing.dcfg @@ -0,0 +1,2 @@ +fn lib = [ 123 "string" ./path 80085.3 ] + diff --git a/src/dcfg.c b/src/dcfg.c index cc76219..9b4dec1 100644 --- a/src/dcfg.c +++ b/src/dcfg.c @@ -343,8 +343,18 @@ static inline int32_t decode_cp(StringView src, int pos, int *len) *len = 0; return -1; } - return (int32_t)utf8proc_iterate( - (uint8_t const *)src.data + pos, (int)(src.size - pos), len); + + 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) @@ -625,13 +635,18 @@ typedef struct { AST **argv; } ASTFunctionCall; +typedef struct { + bool is_str; + StringView s; +} ASTKey; + struct AST { ASTKind kind; SourceLocation location; union { int64_t i; double r; - StringView s; + ASTKey s; StringView p; bool b; ASTFunction f; @@ -653,20 +668,18 @@ bool Parser_next(Parser *parser) { parser->cur = parser->next; parser->next = Lexer_next(parser->lexer); - bool ret = parser->next.type != TokenType_Error; - if (!ret) { + if (parser->next.type == TokenType_Error) { strcpy(parser->instance->last_error, "Failed to get parser token"); } - return ret; + 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; - if (!Parser_next(out_parser)) { - return false; - } + out_parser->next = Lexer_next(lexer); if (!Parser_next(out_parser)) { return false; } @@ -781,7 +794,7 @@ AST *parser_parse_dot_access(Parser *parser, StringView *current) if (vector_size(accessv) == 1) { ast->kind = ASTKind_Key; - ast->v.s = accessv[0]; + ast->v.s.s = accessv[0]; vector_free(accessv); } return ast; @@ -800,13 +813,14 @@ AST *parser_parse_value(Parser *parser) } else if (parser->cur.type == TokenType_String || parser->cur.type == TokenType_Identifier) { ast->kind = ASTKind_Key; - ast->v.s = parser->cur.v.s; + 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, SV("fn"))) { + if (sv_eq(ast->v.s.s, SV("fn"))) { if (!Parser_next(parser)) { strcpy(parser->instance->last_error, "Failed to advance fn keyword"); @@ -837,6 +851,11 @@ AST *parser_parse_value(Parser *parser) } vector_add(&ast->v.f.argv, 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); @@ -847,12 +866,12 @@ AST *parser_parse_value(Parser *parser) } 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"))) { + } 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, SV("off")) - || sv_eq(ast->v.s, SV("false"))) { + } 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; } @@ -1000,9 +1019,14 @@ 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; + if (root->v.s.is_str) { + value->type = dcfg_ValueType_String; + value->v.s = root->v.s.s; + } else { + // FIXME: Implement + FREE(value); + return NULL; + } } else if (root->kind == ASTKind_Path) { value->type = dcfg_ValueType_Path; value->v.p = root->v.p; @@ -1089,6 +1113,7 @@ dcfg_Value *dcfg_parse(dcfg_Instance *instance, dcfg_StringView const file_path) if (!abs) { snprintf(instance->last_error, sizeof(instance->last_error) - 1, "realpath: %s", strerror(errno)); + return NULL; } StringView abs_sv = SV(abs); @@ -1146,25 +1171,33 @@ dcfg_Value *dcfg_parse(dcfg_Instance *instance, dcfg_StringView const file_path) 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); 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, str); - vector_add(&instance->source_pathv, abs_sv); - v->i_sourcev_idx = vector_size(instance->sourcev) - 1; + + vector_add(&instance->source_pathv, abs_sv); v->i_source_pathv_idx = vector_size(instance->source_pathv) - 1; return v; @@ -1176,6 +1209,153 @@ void dcfg_destroy(dcfg_Value *value) // FIXME: Implement } +dcfg_ValueType dcfg_Value_type_ex(dcfg_Value *value, bool evaluate) +{ + if (!value) + return dcfg_ValueType_Nil; + (void)evaluate; /* eval ignored for now */ + 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; +} + +bool dcfg_call_function(dcfg_Value *function, dcfg_Value **args, + size_t arg_count, dcfg_Value **out_value) +{ + (void)function, (void)args, (void)arg_count, (void)out_value; + // FIXME: Implement + return false; +} + +bool dcfg_Value_evaluate(dcfg_Value *value, dcfg_Value **out_value) +{ + (void)value, (void)out_value; + // FIXME: Implement + return false; +} + // Libraries #include "vendor/utf8proc.c" #include "vendor/vec.c"