|
// initially generated with https://aistudio.google.com/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdbool.h> |
|
#include <sys/stat.h> |
|
|
|
char const* filename = NULL; |
|
|
|
void print(char c) { |
|
if (c == '\"') printf("\\\""); |
|
else if (c == '\r') printf("\\r"); |
|
else if (c == '\n') printf("\\n"); |
|
else if (c == '\\') printf("\\\\"); |
|
else printf("%c", c); |
|
} |
|
|
|
void process(char const* text) { |
|
char const* cursor = text; |
|
char const* start_tag; |
|
|
|
while ((start_tag = strstr(cursor, "{{")) != NULL) { |
|
// Print text before tag |
|
if (start_tag != cursor) { |
|
printf(" sb_append(sb, \""); |
|
while (cursor < start_tag) { |
|
print(*cursor++); |
|
} |
|
printf("\");\n"); |
|
} |
|
|
|
// Find end of tag |
|
const char* end_tag = strstr(start_tag, "}}"); |
|
if (!end_tag) break; |
|
|
|
// Extract and print code |
|
printf(" "); |
|
const char* code_ptr = start_tag + 2; |
|
while (code_ptr < end_tag) { |
|
printf("%c", *code_ptr++); |
|
} |
|
|
|
cursor = end_tag + 2; |
|
} |
|
|
|
// Print remaining line content |
|
if (*cursor != '\0') { |
|
printf(" sb_append(sb, \""); |
|
while (*cursor) { |
|
print(*cursor++); |
|
} |
|
printf("\");\n"); |
|
} |
|
} |
|
|
|
int main(int argc, char** argv) { |
|
FILE* in; |
|
struct stat st; |
|
char *buffer; |
|
|
|
if (argc == 2) { |
|
filename = argv[1]; |
|
in = fopen(filename, "r"); |
|
if (!in) { |
|
perror("Can't open specified file"); |
|
return EXIT_FAILURE; |
|
} |
|
if (fstat(fileno(in), &st)) { |
|
perror("Can't get file stat"); |
|
return EXIT_FAILURE; |
|
} |
|
buffer = malloc(st.st_size + 1); |
|
if (fread(buffer, 1, st.st_size, in) == 0) { |
|
perror("Can't read specified file"); |
|
return EXIT_FAILURE; |
|
}; |
|
buffer[st.st_size] = '\0'; |
|
} |
|
|
|
printf("#include <string-builder.h>\n\n"); |
|
printf("struct Context;\n\n"); |
|
printf("void render_template(struct Context* ctx, struct StringBuilder* sb);\n\n"); |
|
printf("#ifdef IMPLEMENTATION\n\n"); |
|
printf("void render_template(struct Context* ctx, struct StringBuilder* sb) {\n"); |
|
|
|
process(buffer); |
|
|
|
printf("}\n\n"); |
|
printf("#endif // IMPLEMENTATION\n"); |
|
|
|
free(buffer); |
|
return EXIT_SUCCESS; |
|
} |
@oduortoni
#define $ (*ctx)is not a complex macro. It is a macro that enforces specificsyntax. Difference between
$user.nameand$.user.nameis barely noticeable,yet later is completely valid C code that simplifies the logic of translation
tool.
{{ for (int i = 0; i < $.user.friends.len; ++i) { }} {{ char *name = get_friend_name($.user.friends.ids[i]); }} <li>{{ "%s", name }}</li> {{ } }}In situation like this, you can't use
Stringmode because it doens't startwith the
$.so you have to useFormatmode.