|
// 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; |
|
} |
Essentially, we generate the same for each. No special treatment:
and
Then let C compiler handle the rest. If the function does not exist, i.e was never declared before the template inclusion, then it will err at compile time. Hence no special treatment.
If it is between the {{}} and does not begin with c keywords, then generate the corresponding sb_append_fmt expresion. The compiler will catch the errors. Which leads to the need for line numbers. If an error occurs, we can see the line number within the template itself.
The key words like for and if are copied as is until we get to { then the content in between is treated to the same rules as ordinary tags.
Becomes: