From a5f1cb5ce34f31b9c46de94e55b5f85d571b792f Mon Sep 17 00:00:00 2001 From: justanothercatgirl Date: Tue, 1 Apr 2025 01:44:47 +0300 Subject: Added stringbuilder and STE --- ste/.gitignore | 3 +++ ste/README.md | 48 ++++++++++++++++++++++++++++++++++++++++++ ste/example/Makefile | 14 +++++++++++++ ste/example/main.c | 15 +++++++++++++ ste/example/tmpl.c.ste | 11 ++++++++++ ste/ste.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 ste/.gitignore create mode 100644 ste/README.md create mode 100644 ste/example/Makefile create mode 100644 ste/example/main.c create mode 100644 ste/example/tmpl.c.ste create mode 100644 ste/ste.c (limited to 'ste') diff --git a/ste/.gitignore b/ste/.gitignore new file mode 100644 index 0000000..953abb8 --- /dev/null +++ b/ste/.gitignore @@ -0,0 +1,3 @@ +tmpl.c +main +ste diff --git a/ste/README.md b/ste/README.md new file mode 100644 index 0000000..680f9a4 --- /dev/null +++ b/ste/README.md @@ -0,0 +1,48 @@ +# Simple Template Engine for C +The idea is stolen from [Tsoding](https://github.com/tsoding). + +This is a template engine for the C programming language. +A really simple one, to be honset. To see how it works, it's best to just check +out examples, but in a nutshell: initially, everything is a string. After `%` +sign, it becomes C source code. basically, `%` sign acts as a flip-flop switch +between C and text. All of the text is wrapped inside the `OUT` macro, which +must be defined in the source code (name may be customized via command-line +arguments). To use the code, `#include generated.c` in the middle of function +where you need this template. + +# Example +`main.c`: +```c +#define STRINGBUILDER_IMPLEMENTATION +#include "stringbuilder.h" /* refer to [c_headers](https://git.twistea.su/c_headers) */ +#include + +int main(void) { + jac_sb sb = jac_sb_empty(); + #define OUT(str) jac_sb_append_buf(&sb, str) + #define INT(i) jac_sb_snprintf(&sb, "%i", i); /* Note the semicolon */ + #include "tmpl.c" + #undef OUT + printf("%s\n", sb.data); + jac_sb_free(sb); + return 727; +} +``` + +template file (`tmpl.c.ste`): +```html + + + + % + for (int i = 0; i < 10; ++i) { + %

This is paragraph number % INT(i) %

% + } + % + + +``` + +Build: `cc ste.c -o ste && ste tmpl.c.ste && cc main.c -o main` + + diff --git a/ste/example/Makefile b/ste/example/Makefile new file mode 100644 index 0000000..f8a6ef4 --- /dev/null +++ b/ste/example/Makefile @@ -0,0 +1,14 @@ + +.SUFFIXES: + +all: main + +ste: ../ste.c + $(CC) -o $@ $< + +%.c: %.c.ste ste + ./ste -m OUTPUT $< + +main: main.c tmpl.c + $(CC) -o $@ $< + diff --git a/ste/example/main.c b/ste/example/main.c new file mode 100644 index 0000000..18850d9 --- /dev/null +++ b/ste/example/main.c @@ -0,0 +1,15 @@ +#define STRINGBUILDER_IMPLEMENTATION +#include "../../include/stringbuilder.h" /* refer to [c_headers](https://git.twistea.su/c_headers) */ +#include + +int main(void) { + jac_sb sb = jac_sb_empty(); + #define OUTPUT(str) jac_sb_append_buf(&sb, str) + #define INTEGER(i) jac_sb_snprintf(&sb, "%i", i); /* Note the semicolon */ + # include "tmpl.c" + #undef OUTPUT + #undef INTEGER + printf("%s\n", sb.data); + jac_sb_free(sb); + return 727; +} diff --git a/ste/example/tmpl.c.ste b/ste/example/tmpl.c.ste new file mode 100644 index 0000000..7f486cd --- /dev/null +++ b/ste/example/tmpl.c.ste @@ -0,0 +1,11 @@ + + + + % + for (int i = 0; i < 10; ++i) { + %

This is paragraph number % INTEGER(i) %

% + } + % + + + diff --git a/ste/ste.c b/ste/ste.c new file mode 100644 index 0000000..43252b9 --- /dev/null +++ b/ste/ste.c @@ -0,0 +1,57 @@ +#include +#include + +void process_files(FILE* in, FILE* out, const char *macro) { + enum {MODESTR = 0, MODESRC = 1} a = MODESTR; + fprintf(out, "%s(\"", macro); + for (;;) { + int c = fgetc(in); + if (feof(in)) break; + if (c == '%') { + a = !a; + if (a == MODESTR) fprintf(out, " %s(\"", macro); + else fputs("\");", out); + continue; + } + if (a == MODESTR) fprintf(out, "\\x%02X", c); + else (fputc(c, out)); + } + if (a == MODESTR) fputs("\");", out); +} + +int main(int argc, char **argv) { + const char *macro = "OUT"; + char *filename = NULL; + FILE *in, *out; + + if (argc < 2) { + usage: + fprintf(stderr, "Usage: %s [-m name] \n", *argv); + return 1; + } + + for (int i = 0; i < argc; ++i) { + if (!strcmp("-m", argv[i])) macro = argv[++i]; + else filename = argv[i]; + } + if (!filename || strlen(filename) <= 4) goto usage; + + in = fopen(filename, "r"); + for (char *fn = filename; fn[3]; ++fn) { + if (!strncmp(".ste", fn, 4)) { + *fn = '\0'; + break; + } + } + out = fopen(filename, "w"); + if (!in || !out) { + perror("fopen"); + goto usage; + } + + process_files(in, out, macro); + + fclose(in); + fclose(out); + return 0; +} -- cgit v1.2.3-70-g09d2