feat(hmpl): render simple placeholder with memory leak of course

This commit is contained in:
2025-03-21 04:00:23 +00:00
parent 9438280750
commit c4d14e5aa2
8 changed files with 125 additions and 37 deletions

View File

@@ -85,9 +85,9 @@
pg-from = pkgs.callPackage ./package/postgres/pg-from/default.nix rust.commonArgs; 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-schema = pkgs.callPackage ./package/postgres/pg-schema/default.nix rust.commonArgs;
pg-migration = pkgs.callPackage ./package/postgres/pg-migration/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 { hmpl = pkgs.callPackage ./package/c/hmpl/default.nix {
libhectic = self.packages.${system}.libhectic; chectic = self.packages.${system}.chectic;
}; };
}; };

View File

@@ -1,4 +1,4 @@
#include "libhectic.h" #include "chectic.h"
void set_output_color_mode(ColorMode mode) { void set_output_color_mode(ColorMode mode) {
color_mode = mode; color_mode = mode;

View File

@@ -1,7 +1,7 @@
{ stdenv, gcc, lib }: { stdenv, gcc, lib }:
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "libhectic"; pname = "chectic";
version = "1.0"; version = "1.0";
src = ./.; src = ./.;
doCheck = true; doCheck = true;
@@ -11,23 +11,23 @@ stdenv.mkDerivation {
${gcc}/bin/cc -Wall -Wextra -g \ ${gcc}/bin/cc -Wall -Wextra -g \
-std=c99 \ -std=c99 \
-pedantic -fsanitize=address \ -pedantic -fsanitize=address \
-c libhectic.c -o target/libhectic.o -c chectic.c -o target/chectic.o
${gcc}/bin/ar rcs target/libhectic.a target/libhectic.o ${gcc}/bin/ar rcs target/libchectic.a target/chectic.o
''; '';
checkPhase = '' checkPhase = ''
mkdir -p target/test mkdir -p target/test
for test_file in test/*.c; do for test_file in test/*.c; do
exe="target/test/$(basename ''${test_file%.c})" 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" "$exe"
done done
''; '';
installPhase = '' installPhase = ''
mkdir -p $out/lib $out/include mkdir -p $out/lib $out/include
cp target/libhectic.a $out/lib/ cp target/libchectic.a $out/lib/
cp libhectic.h $out/include/ cp chectic.h $out/include/
''; '';
meta = { meta = {

View File

@@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "libhectic.h" #include "chectic.h"
void test_arena_init() { void test_arena_init() {
Arena arena = arena_init(128); Arena arena = arena_init(128);

View File

@@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "libhectic.h" #include "chectic.h"
#define TEST_RAISE_GENERIC(LOG_MACRO, LEVEL, LEVEL_STR) do { \ #define TEST_RAISE_GENERIC(LOG_MACRO, LEVEL, LEVEL_STR) do { \
FILE *orig_stderr = stderr; \ FILE *orig_stderr = stderr; \

View File

@@ -1,4 +1,4 @@
{ stdenv, gcc, lib, libhectic, cjson }: { stdenv, gcc, lib, chectic, cjson }:
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "hmpl"; pname = "hmpl";
@@ -6,13 +6,13 @@ stdenv.mkDerivation {
src = ./.; src = ./.;
doCheck = true; doCheck = true;
buildInputs = [ libhectic cjson ]; buildInputs = [ chectic cjson ];
buildPhase = '' buildPhase = ''
mkdir -p target mkdir -p target
${gcc}/bin/cc -Wall -Wextra -g \ ${gcc}/bin/cc -Wall -Wextra -g \
-pedantic -fsanitize=address hmpl.c \ -pedantic -fsanitize=address hmpl.c \
-l:libhectic.a -l:cjson -o target/hmpl -lchectic -lcjson -o target/hmpl
''; '';
checkPhase = '' ''; checkPhase = '' '';
@@ -23,7 +23,7 @@ stdenv.mkDerivation {
''; '';
meta = { meta = {
description = "libhectic"; description = "chectic";
license = lib.licenses.mit; license = lib.licenses.mit;
}; };
} }

View File

@@ -3,7 +3,8 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
//#include "libhmpl.h" //#include "libhmpl.h"
#include "libhectic.h" #include "chectic.h"
#include "cjson/cJSON.h"
#define KB128 131072 #define KB128 131072
@@ -47,7 +48,7 @@
// _offset := simple_start + start_pattern_length; // _offset := simple_start + start_pattern_length;
// RAISE LOG '% := % + %', _offset, simple_start, start_pattern_length; // RAISE LOG '% := % + %', _offset, simple_start, start_pattern_length;
// IF _offset = 0 THEN // IF _offset = 0 THEN
// RAISE EXCEPTION 'Malformed template: offcet cannot be 0'; // RAISE EXCEPTION 'Malformed template: offset cannot be 0';
// END IF; // END IF;
// CONTINUE; // CONTINUE;
// END IF; // END IF;
@@ -59,8 +60,62 @@
// //
// RETURN result; // RETURN result;
// END $$; // 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 // start
char start_pattern[4]; char start_pattern[4];
sprintf(&start_pattern[0], "{{%s", prefix); sprintf(&start_pattern[0], "{{%s", prefix);
@@ -71,35 +126,68 @@ void render_template_placeholders(char *text, char *context, char prefix[1]) {
while (1) { while (1) {
// find tag start // find tag start
char *placeholder_start = strstr(text + offset, start_pattern); char *placeholder_start = strstr(text + offset, start_pattern);
if (!placeholder_start) { if (!placeholder_start) { break; }
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, ""); render_template_placeholders(text, context, "");
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
char *text = NULL; init_logger();
char *context = strdup(argc > 1 ? argv[1] : "{}"); raise_info("start");
if (argc > 2) { char *text = NULL;
text = strdup(argv[2]); cJSON *context = cJSON_Parse(strdup(argc > 1 ? argv[1] : "{}"));
} 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 (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); render_template(text, context);
}
free(text); printf("%s", text);
free(context);
return 0; free(text);
free(context);
return 0;
} }