From c4d14e5aa273beeef18c4236bf0bd98072e11a20 Mon Sep 17 00:00:00 2001 From: yukkop Date: Fri, 21 Mar 2025 04:00:23 +0000 Subject: [PATCH] feat(hmpl): render simple placeholder with memory leak of course --- flake.nix | 4 +- .../libhectic.c => chectic/chectic.c} | 2 +- .../libhectic.h => chectic/chectic.h} | 0 package/c/{libhectic => chectic}/default.nix | 12 +- package/c/{libhectic => chectic}/test/arena.c | 2 +- package/c/{libhectic => chectic}/test/test.c | 2 +- package/c/hmpl/default.nix | 8 +- package/c/hmpl/hmpl.c | 132 +++++++++++++++--- 8 files changed, 125 insertions(+), 37 deletions(-) rename package/c/{libhectic/libhectic.c => chectic/chectic.c} (99%) rename package/c/{libhectic/libhectic.h => chectic/chectic.h} (100%) rename package/c/{libhectic => chectic}/default.nix (70%) rename package/c/{libhectic => chectic}/test/arena.c (98%) rename package/c/{libhectic => chectic}/test/test.c (98%) diff --git a/flake.nix b/flake.nix index e6a239b..e428952 100644 --- a/flake.nix +++ b/flake.nix @@ -85,9 +85,9 @@ pg-from = pkgs.callPackage ./package/postgres/pg-from/default.nix rust.commonArgs; pg-schema = pkgs.callPackage ./package/postgres/pg-schema/default.nix rust.commonArgs; pg-migration = pkgs.callPackage ./package/postgres/pg-migration/default.nix rust.commonArgs; - libhectic = pkgs.callPackage ./package/c/libhectic/default.nix {}; + chectic = pkgs.callPackage ./package/c/chectic/default.nix {}; hmpl = pkgs.callPackage ./package/c/hmpl/default.nix { - libhectic = self.packages.${system}.libhectic; + chectic = self.packages.${system}.chectic; }; }; diff --git a/package/c/libhectic/libhectic.c b/package/c/chectic/chectic.c similarity index 99% rename from package/c/libhectic/libhectic.c rename to package/c/chectic/chectic.c index 62c029e..d69e3f3 100644 --- a/package/c/libhectic/libhectic.c +++ b/package/c/chectic/chectic.c @@ -1,4 +1,4 @@ -#include "libhectic.h" +#include "chectic.h" void set_output_color_mode(ColorMode mode) { color_mode = mode; diff --git a/package/c/libhectic/libhectic.h b/package/c/chectic/chectic.h similarity index 100% rename from package/c/libhectic/libhectic.h rename to package/c/chectic/chectic.h diff --git a/package/c/libhectic/default.nix b/package/c/chectic/default.nix similarity index 70% rename from package/c/libhectic/default.nix rename to package/c/chectic/default.nix index ba3cb31..e871ab8 100644 --- a/package/c/libhectic/default.nix +++ b/package/c/chectic/default.nix @@ -1,7 +1,7 @@ { stdenv, gcc, lib }: stdenv.mkDerivation { - pname = "libhectic"; + pname = "chectic"; version = "1.0"; src = ./.; doCheck = true; @@ -11,23 +11,23 @@ stdenv.mkDerivation { ${gcc}/bin/cc -Wall -Wextra -g \ -std=c99 \ -pedantic -fsanitize=address \ - -c libhectic.c -o target/libhectic.o - ${gcc}/bin/ar rcs target/libhectic.a target/libhectic.o + -c chectic.c -o target/chectic.o + ${gcc}/bin/ar rcs target/libchectic.a target/chectic.o ''; checkPhase = '' mkdir -p target/test for test_file in test/*.c; do exe="target/test/$(basename ''${test_file%.c})" - ${gcc}/bin/cc -Wall -Wextra -g -pedantic -fsanitize=address -I. "$test_file" -Ltarget -l:libhectic.a -o "$exe" + ${gcc}/bin/cc -Wall -Wextra -g -pedantic -fsanitize=address -I. "$test_file" -Ltarget -lchectic -o "$exe" "$exe" done ''; installPhase = '' mkdir -p $out/lib $out/include - cp target/libhectic.a $out/lib/ - cp libhectic.h $out/include/ + cp target/libchectic.a $out/lib/ + cp chectic.h $out/include/ ''; meta = { diff --git a/package/c/libhectic/test/arena.c b/package/c/chectic/test/arena.c similarity index 98% rename from package/c/libhectic/test/arena.c rename to package/c/chectic/test/arena.c index dd41cbd..5f275ac 100644 --- a/package/c/libhectic/test/arena.c +++ b/package/c/chectic/test/arena.c @@ -2,7 +2,7 @@ #include #include #include -#include "libhectic.h" +#include "chectic.h" void test_arena_init() { Arena arena = arena_init(128); diff --git a/package/c/libhectic/test/test.c b/package/c/chectic/test/test.c similarity index 98% rename from package/c/libhectic/test/test.c rename to package/c/chectic/test/test.c index c0d1e7b..43eddef 100644 --- a/package/c/libhectic/test/test.c +++ b/package/c/chectic/test/test.c @@ -2,7 +2,7 @@ #include #include #include -#include "libhectic.h" +#include "chectic.h" #define TEST_RAISE_GENERIC(LOG_MACRO, LEVEL, LEVEL_STR) do { \ FILE *orig_stderr = stderr; \ diff --git a/package/c/hmpl/default.nix b/package/c/hmpl/default.nix index 052ef8b..62b5ce9 100644 --- a/package/c/hmpl/default.nix +++ b/package/c/hmpl/default.nix @@ -1,4 +1,4 @@ -{ stdenv, gcc, lib, libhectic, cjson }: +{ stdenv, gcc, lib, chectic, cjson }: stdenv.mkDerivation { pname = "hmpl"; @@ -6,13 +6,13 @@ stdenv.mkDerivation { src = ./.; doCheck = true; - buildInputs = [ libhectic cjson ]; + buildInputs = [ chectic cjson ]; buildPhase = '' mkdir -p target ${gcc}/bin/cc -Wall -Wextra -g \ -pedantic -fsanitize=address hmpl.c \ - -l:libhectic.a -l:cjson -o target/hmpl + -lchectic -lcjson -o target/hmpl ''; checkPhase = '' ''; @@ -23,7 +23,7 @@ stdenv.mkDerivation { ''; meta = { - description = "libhectic"; + description = "chectic"; license = lib.licenses.mit; }; } diff --git a/package/c/hmpl/hmpl.c b/package/c/hmpl/hmpl.c index fb17c45..8f35efb 100644 --- a/package/c/hmpl/hmpl.c +++ b/package/c/hmpl/hmpl.c @@ -3,7 +3,8 @@ #include #include //#include "libhmpl.h" -#include "libhectic.h" +#include "chectic.h" +#include "cjson/cJSON.h" #define KB128 131072 @@ -47,7 +48,7 @@ // _offset := simple_start + start_pattern_length; // RAISE LOG '% := % + %', _offset, simple_start, start_pattern_length; // IF _offset = 0 THEN -// RAISE EXCEPTION 'Malformed template: offcet cannot be 0'; +// RAISE EXCEPTION 'Malformed template: offset cannot be 0'; // END IF; // CONTINUE; // END IF; @@ -59,8 +60,62 @@ // // RETURN result; // END $$; +char *eval(cJSON *context, const char *key) { + if (!context || !key) return NULL; + char *key_copy = strdup(key); + char *token, *rest = key_copy; + cJSON *res = context; -void render_template_placeholders(char *text, char *context, char prefix[1]) { + while ((token = strtok_r(rest, ".", &rest))) { + res = cJSON_GetObjectItemCaseSensitive(res, token); + if (!res) { + free(key_copy); + return NULL; + } + } + free(key_copy); + + if (cJSON_IsString(res) && res->valuestring) + return strdup(res->valuestring); + else if (cJSON_IsNumber(res)) { + char buf[64]; + snprintf(buf, sizeof(buf), "%g", res->valuedouble); + return strdup(buf); + } + return cJSON_PrintUnformatted(res); +} + +void substring(const char *src, char *dest, size_t start, size_t len) { + raise_debug("substring %s from %d to %d", src, start, len); + size_t srclen = strlen(src); + if (start >= srclen) { + dest[0] = '\0'; + return; + } + if (start + len > srclen) + len = srclen - start; + strncpy(dest, src + start, len); + dest[len] = '\0'; +} + +char* replace_substring(const char* src, int start, int end, const char* replacement) { + raise_debug("replace_substring"); + int src_len = strlen(src); + int rep_len = strlen(replacement); + int new_len = src_len - (end - start + 1) + rep_len; + + char* new_str = malloc(new_len + 1); + if (!new_str) return NULL; + + memcpy(new_str, src, start); // copy before + memcpy(new_str + start, replacement, rep_len); // insert replacement + strcpy(new_str + start + rep_len, src + end + 1); // copy after + + return new_str; +} + +void render_template_placeholders(char *text, cJSON *context, char prefix[1]) { + raise_debug("render_template_placeholders"); // start char start_pattern[4]; sprintf(&start_pattern[0], "{{%s", prefix); @@ -71,35 +126,68 @@ void render_template_placeholders(char *text, char *context, char prefix[1]) { while (1) { // find tag start char *placeholder_start = strstr(text + offset, start_pattern); - if (!placeholder_start) { - break; + if (!placeholder_start) { break; } + char *releative_start = (size_t)placeholder_start - (size_t)text + start_pattern_length; + raise_debug("start: %d", releative_start); + + if (offset != 0) { + placeholder_start += offset - 1; } + + char* placeholder_end = strstr(placeholder_start, "}}"); + // TODO: user error instead exaption + if (!placeholder_end) { raise_exception("Malformed template: missing closing braces for placeholder start"); }; + raise_debug("end: %d", (size_t)placeholder_end - (size_t)text); + + int len = (size_t)placeholder_end - (size_t)placeholder_start - start_pattern_length; + char* placeholder_key = malloc(len + 1);; + substring(text, placeholder_key, releative_start, len); + raise_debug("key: %s", placeholder_key); + char* replacement = eval(context, placeholder_key); + raise_debug("%s = eval(%s, %s)", replacement, context, placeholder_key); + if (!replacement) { + offset = placeholder_start + start_pattern_length; + raise_log("offset is %s = %s + %s", offset, placeholder_start, start_pattern_length); + if (offset = 0) { + raise_exception("offset cannot be 0 here"); + }; + continue; + } + text = replace_substring(text, releative_start - start_pattern_length, releative_start + len + 2 - 1, replacement); + raise_info(text); }; } -void render_template(char *text, char *context) { +void render_template(char *text, cJSON *context) { render_template_placeholders(text, context, ""); } int main(int argc, char *argv[]) { - char *text = NULL; - char *context = strdup(argc > 1 ? argv[1] : "{}"); + init_logger(); + raise_info("start"); - if (argc > 2) { - text = strdup(argv[2]); - } else if (!isatty(fileno(stdin))) { - size_t size = 0; - ssize_t len = getdelim(&text, &size, '\0', stdin); - if (len < 0) { - perror("read stdin"); - free(context); - return 1; - } - } + char *text = NULL; + cJSON *context = cJSON_Parse(strdup(argc > 1 ? argv[1] : "{}")); + if (argc > 2) { + text = strdup(argv[2]); + } else if (!isatty(fileno(stdin))) { + size_t size = 0; + ssize_t len = getdelim(&text, &size, '\0', stdin); + if (len < 0) { + perror("read stdin"); + free(context); + return 1; + } + } + + if (text) { render_template(text, context); + } - free(text); - free(context); - return 0; + printf("%s", text); + + free(text); + free(context); + return 0; }