summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/befunge.c198
-rw-r--r--plugins/brainfuck.c44
-rw-r--r--plugins/forth.c636
-rw-r--r--plugins/lisp.c484
-rw-r--r--plugins/regex.c226
-rw-r--r--plugins/sql.c415
-rw-r--r--plugins/zprep_plugin.h30
7 files changed, 2033 insertions, 0 deletions
diff --git a/plugins/befunge.c b/plugins/befunge.c
new file mode 100644
index 0000000..844e27f
--- /dev/null
+++ b/plugins/befunge.c
@@ -0,0 +1,198 @@
+
+#include "zprep_plugin.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BF_W 80
+#define BF_H 25
+
+void befunge_transpile(const char *input_body, const ZApi *api)
+{
+ FILE *out = api->out;
+
+ char grid[BF_H][BF_W];
+ memset(grid, ' ', sizeof(grid));
+
+ int row = 0, col = 0;
+ const char *p = input_body;
+ while (*p && (*p == '\n' || *p == '\r'))
+ {
+ p++;
+ }
+
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ row++;
+ col = 0;
+ if (row >= BF_H)
+ {
+ break;
+ }
+ }
+ else if (col < BF_W)
+ {
+ grid[row][col] = *p;
+ col++;
+ }
+ p++;
+ }
+
+ fprintf(out, "{\n");
+ fprintf(out, " static long stack[1024]; int sp = 0;\n");
+ fprintf(out, " int x = 0, y = 0, dx = 1, dy = 0;\n");
+ fprintf(out, " int string_mode = 0;\n\n");
+
+ fprintf(out, " static void *dispatch[%d][%d] = {\n", BF_H, BF_W);
+ for (int r = 0; r < BF_H; r++)
+ {
+ fprintf(out, " { ");
+ for (int c = 0; c < BF_W; c++)
+ {
+ if (grid[r][c] == ' ')
+ {
+ fprintf(out, "&&space_handler, ");
+ }
+ else
+ {
+ fprintf(out, "&&cell_%d_%d, ", r, c);
+ }
+ }
+ fprintf(out, "},\n");
+ }
+ fprintf(out, " };\n\n");
+ fprintf(out, " goto *dispatch[0][0];\n\n");
+
+ for (int r = 0; r < BF_H; r++)
+ {
+ for (int c = 0; c < BF_W; c++)
+ {
+ char op = grid[r][c];
+ if (op == ' ')
+ {
+ continue;
+ }
+
+ fprintf(out, "cell_%d_%d:\n", r, c);
+
+ if (op >= '0' && op <= '9')
+ {
+ fprintf(out,
+ " if(string_mode) { stack[sp++] = '%c'; } else { stack[sp++] = %c; }\n",
+ op, op);
+ }
+ else if (op == '"')
+ {
+ fprintf(out, " string_mode = !string_mode;\n");
+ }
+ else
+ {
+ if (op == '>')
+ {
+ fprintf(out, " dx=1; dy=0;\n");
+ }
+ else if (op == '<')
+ {
+ fprintf(out, " dx=-1; dy=0;\n");
+ }
+ else if (op == '^')
+ {
+ fprintf(out, " dx=0; dy=-1;\n");
+ }
+ else if (op == 'v')
+ {
+ fprintf(out, " dx=0; dy=1;\n");
+ }
+ else if (op == '+')
+ {
+ fprintf(out, " { long a=stack[--sp]; stack[sp-1]+=a; }\n");
+ }
+ else if (op == '-')
+ {
+ fprintf(out, " { long a=stack[--sp]; stack[sp-1]-=a; }\n");
+ }
+ else if (op == '*')
+ {
+ fprintf(out, " { long a=stack[--sp]; stack[sp-1]*=a; }\n");
+ }
+ else if (op == '/')
+ {
+ fprintf(out,
+ " { long a=stack[--sp]; stack[sp-1]= (a!=0)?stack[sp-1]/a:0; }\n");
+ }
+ else if (op == '%')
+ {
+ fprintf(out,
+ " { long a=stack[--sp]; stack[sp-1]= (a!=0)?stack[sp-1]%%a:0; }\n");
+ }
+ else if (op == '!')
+ {
+ fprintf(out, " stack[sp-1] = !stack[sp-1];\n");
+ }
+ else if (op == '`')
+ {
+ fprintf(out, " { long a=stack[--sp]; stack[sp-1]=(stack[sp-1]>a); }\n");
+ }
+ else if (op == ':')
+ {
+ fprintf(out, " if(sp>0) { stack[sp]=stack[sp-1]; sp++; }\n");
+ }
+ else if (op == '\\')
+ {
+ fprintf(out, " if(sp>1) { long t=stack[sp-1]; stack[sp-1]=stack[sp-2]; "
+ "stack[sp-2]=t; }\n");
+ }
+ else if (op == '$')
+ {
+ fprintf(out, " if(sp>0) sp--;\n");
+ }
+ else if (op == '.')
+ {
+ fprintf(out, " printf(\"%%ld \", stack[--sp]);\n");
+ }
+ else if (op == ',')
+ {
+ fprintf(out, " printf(\"%%c\", (char)stack[--sp]);\n");
+ }
+ else if (op == '_')
+ {
+ fprintf(out, " { long a=stack[--sp]; dx=a?-1:1; dy=0; }\n");
+ }
+ else if (op == '|')
+ {
+ fprintf(out, " { long a=stack[--sp]; dx=0; dy=a?-1:1; }\n");
+ }
+ else if (op == '@')
+ {
+ fprintf(out, " goto end_befunge;\n");
+ }
+ else if (op == '#')
+ {
+ fprintf(out, " x+=dx; y+=dy;\n");
+ }
+ else
+ {
+ fprintf(out, " if(string_mode) stack[sp++] = '%c';\n", op);
+ }
+ }
+
+ fprintf(out, " x += dx; y += dy;\n");
+ fprintf(out, " if(x>=%d) x=0; else if(x<0) x=%d-1;\n", BF_W, BF_W);
+ fprintf(out, " if(y>=%d) y=0; else if(y<0) y=%d-1;\n", BF_H, BF_H);
+ fprintf(out, " goto *dispatch[y][x];\n\n");
+ }
+ }
+
+ fprintf(out, "space_handler:\n");
+ fprintf(out, " x += dx; y += dy;\n");
+ fprintf(out, " if(x>=%d) x=0; else if(x<0) x=%d-1;\n", BF_W, BF_W);
+ fprintf(out, " if(y>=%d) y=0; else if(y<0) y=%d-1;\n", BF_H, BF_H);
+ fprintf(out, " goto *dispatch[y][x];\n\n");
+
+ fprintf(out, "end_befunge:;\n");
+ fprintf(out, "}\n");
+}
+
+ZPlugin befunge_plugin = {.name = "befunge", .fn = befunge_transpile};
diff --git a/plugins/brainfuck.c b/plugins/brainfuck.c
new file mode 100644
index 0000000..e800da7
--- /dev/null
+++ b/plugins/brainfuck.c
@@ -0,0 +1,44 @@
+
+#include "zprep_plugin.h"
+
+void bf_transpile(const char *input_body, const ZApi *api)
+{
+ FILE *out = api->out;
+ fprintf(out,
+ "{\n static unsigned char tape[30000] = {0};\n unsigned char *ptr = tape;\n");
+ const char *c = input_body;
+ while (*c)
+ {
+ switch (*c)
+ {
+ case '>':
+ fprintf(out, " ++ptr;\n");
+ break;
+ case '<':
+ fprintf(out, " --ptr;\n");
+ break;
+ case '+':
+ fprintf(out, " ++*ptr;\n");
+ break;
+ case '-':
+ fprintf(out, " --*ptr;\n");
+ break;
+ case '.':
+ fprintf(out, " putchar(*ptr);\n");
+ break;
+ case ',':
+ fprintf(out, " *ptr = getchar();\n");
+ break;
+ case '[':
+ fprintf(out, " while (*ptr) {\n");
+ break;
+ case ']':
+ fprintf(out, " }\n");
+ break;
+ }
+ c++;
+ }
+ fprintf(out, "}\n");
+}
+
+ZPlugin brainfuck_plugin = {.name = "brainfuck", .fn = bf_transpile};
diff --git a/plugins/forth.c b/plugins/forth.c
new file mode 100644
index 0000000..9d39c6d
--- /dev/null
+++ b/plugins/forth.c
@@ -0,0 +1,636 @@
+
+#include "zprep_plugin.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+static long stack[256];
+static int sp = 0;
+static double fstack[256];
+static int fsp = 0;
+static long zmemory[4096];
+static int zhere = 0;
+static long loop_stack[32];
+static int lsp = 0;
+
+typedef struct
+{
+ char name[32];
+ char body[512];
+} ForthWord;
+static ForthWord forth_dict[128];
+static int forth_dict_count = 0;
+
+// Stack operations
+static void push(long v)
+{
+ if (sp < 256)
+ {
+ stack[sp++] = v;
+ }
+}
+static long pop()
+{
+ return sp > 0 ? stack[--sp] : 0;
+}
+static long peek()
+{
+ return sp > 0 ? stack[sp - 1] : 0;
+}
+static void fpush(double v)
+{
+ if (fsp < 256)
+ {
+ fstack[fsp++] = v;
+ }
+}
+static double fpop()
+{
+ return fsp > 0 ? fstack[--fsp] : 0.0;
+}
+static double fpeek()
+{
+ return fsp > 0 ? fstack[fsp - 1] : 0.0;
+}
+
+// String utilities
+static char *xstrdup(const char *s)
+{
+ return s ? strdup(s) : NULL;
+}
+static void *xmalloc(size_t n)
+{
+ void *p = malloc(n);
+ if (!p)
+ {
+ exit(1);
+ }
+ return p;
+}
+
+// Tokenizer
+static char *next_token(char **ptr)
+{
+ if (!ptr || !*ptr)
+ {
+ return NULL;
+ }
+ while (**ptr && (**ptr == ' ' || **ptr == '\t' || **ptr == '\n'))
+ {
+ (*ptr)++;
+ }
+ if (**ptr == '\0')
+ {
+ return NULL;
+ }
+ char *start = *ptr;
+ while (**ptr && **ptr != ' ' && **ptr != '\t' && **ptr != '\n')
+ {
+ (*ptr)++;
+ }
+ if (**ptr)
+ {
+ **ptr = '\0';
+ (*ptr)++;
+ }
+ return start;
+}
+
+static void skip_tokens_until(char **ptr, const char *target, const char *alt)
+{
+ char *t;
+ while ((t = next_token(ptr)))
+ {
+ if (strcmp(t, target) == 0)
+ {
+ return;
+ }
+ if (alt && strcmp(t, alt) == 0)
+ {
+ return;
+ }
+ }
+}
+
+void eval_forth_token(char *token, char **scan_ptr, char **out_ptr)
+{
+ if (0 == strcmp(token, "+"))
+ {
+ push(pop() + pop());
+ }
+ else if (0 == strcmp(token, "-"))
+ {
+ long b = pop();
+ push(pop() - b);
+ }
+ else if (0 == strcmp(token, "*"))
+ {
+ push(pop() * pop());
+ }
+ else if (0 == strcmp(token, "/"))
+ {
+ long b = pop();
+ push(b ? pop() / b : 0);
+ }
+ else if (0 == strcmp(token, "%"))
+ {
+ long b = pop();
+ push(b ? pop() % b : 0);
+ }
+ else if (0 == strcmp(token, "<<"))
+ {
+ long b = pop();
+ push(pop() << b);
+ }
+ else if (0 == strcmp(token, ">>"))
+ {
+ long b = pop();
+ push(pop() >> b);
+ }
+ else if (0 == strcmp(token, "&"))
+ {
+ push(pop() & pop());
+ }
+ else if (0 == strcmp(token, "|"))
+ {
+ push(pop() | pop());
+ }
+ else if (0 == strcmp(token, "^"))
+ {
+ push(pop() ^ pop());
+ }
+ else if (0 == strcmp(token, "~"))
+ {
+ push(~pop());
+ }
+ else if (0 == strcmp(token, "="))
+ {
+ push(pop() == pop());
+ }
+ else if (0 == strcmp(token, ">"))
+ {
+ long b = pop();
+ push(pop() > b);
+ }
+ else if (0 == strcmp(token, "<"))
+ {
+ long b = pop();
+ push(pop() < b);
+ }
+ else if (0 == strcmp(token, "choose"))
+ {
+ long f = pop();
+ long t = pop();
+ long c = pop();
+ push(c ? t : f);
+ }
+ else if (0 == strcmp(token, "if"))
+ {
+ if (pop() == 0)
+ {
+ skip_tokens_until(scan_ptr, "then", "else");
+ }
+ }
+ else if (0 == strcmp(token, "else"))
+ {
+ skip_tokens_until(scan_ptr, "then", NULL);
+ }
+ else if (0 == strcmp(token, "then"))
+ {
+ /* Marker. */
+ }
+ else if (0 == strcmp(token, "("))
+ {
+ char *t;
+ while (NULL != (t = next_token(scan_ptr)))
+ {
+ if (strchr(t, ')'))
+ {
+ break;
+ }
+ }
+ }
+ else if (0 == strcmp(token, "do"))
+ {
+ long start = pop();
+ long limit = pop();
+ char *rest = *scan_ptr;
+ char *temp = xstrdup(rest);
+ char *curs = temp;
+ char *t;
+ int depth = 0;
+ char *end = NULL;
+ while (NULL != (t = next_token(&curs)))
+ {
+ if (0 == strcmp(t, "do"))
+ {
+ depth++;
+ }
+ if (0 == strcmp(t, "loop"))
+ {
+ if (depth == 0)
+ {
+ end = curs;
+ break;
+ }
+ depth--;
+ }
+ }
+ if (!end)
+ {
+ fprintf(stderr, "[Forth Error] 'do' without matching 'loop'\n");
+ exit(1);
+ }
+ long len = end - temp;
+ *scan_ptr += len;
+ char *body = xmalloc(len + 1);
+ strncpy(body, rest, len);
+ body[len] = '\0';
+ for (long i = start; i < limit; i++)
+ {
+ if (lsp < 32)
+ {
+ loop_stack[lsp++] = i;
+ }
+ else
+ {
+ fprintf(stderr, "[Error] Loop Stack Overflow\n");
+ exit(1);
+ }
+ char *runnable = xstrdup(body);
+ char *rc = runnable;
+ char *rt;
+ while (NULL != (rt = next_token(&rc)))
+ {
+ eval_forth_token(rt, &rc, out_ptr);
+ }
+ free(runnable);
+ lsp--;
+ }
+ free(body);
+ free(temp);
+ }
+ else if (0 == strcmp(token, "loop"))
+ {
+ /* Marker. */
+ }
+ else if (0 == strcmp(token, "i"))
+ {
+ if (lsp > 0)
+ {
+ push(loop_stack[lsp - 1]);
+ }
+ else
+ {
+ fprintf(stderr, "[Error] 'i' used outside of loop\n");
+ exit(1);
+ }
+ }
+ else if (0 == strcmp(token, "f+"))
+ {
+ fpush(fpop() + fpop());
+ }
+ else if (0 == strcmp(token, "f-"))
+ {
+ double b = fpop();
+ fpush(fpop() - b);
+ }
+ else if (0 == strcmp(token, "f*"))
+ {
+ fpush(fpop() * fpop());
+ }
+ else if (0 == strcmp(token, "f/"))
+ {
+ double b = fpop();
+ fpush(b != 0.0 ? fpop() / b : 0.0);
+ }
+ else if (0 == strcmp(token, "fsqrt"))
+ {
+ fpush(sqrt(fpop()));
+ }
+ else if (0 == strcmp(token, "dup"))
+ {
+ push(peek());
+ }
+ else if (0 == strcmp(token, "drop"))
+ {
+ pop();
+ }
+ else if (0 == strcmp(token, "swap"))
+ {
+ long a = pop();
+ long b = pop();
+ push(a);
+ push(b);
+ }
+ else if (0 == strcmp(token, "rot"))
+ {
+ long a = pop();
+ long b = pop();
+ long c = pop();
+ push(b);
+ push(a);
+ push(c);
+ }
+ else if (0 == strcmp(token, "-rot"))
+ {
+ long a = pop();
+ long b = pop();
+ long c = pop();
+ push(a);
+ push(c);
+ push(b);
+ }
+ else if (0 == strcmp(token, "fdup"))
+ {
+ fpush(fpeek());
+ }
+ else if (0 == strcmp(token, "fdrop"))
+ {
+ fpop();
+ }
+ else if (0 == strcmp(token, "fswap"))
+ {
+ double a = fpop();
+ double b = fpop();
+ fpush(a);
+ fpush(b);
+ }
+ else if (0 == strcmp(token, "i>f"))
+ {
+ fpush((double)pop());
+ }
+ else if (0 == strcmp(token, "f>i"))
+ {
+ push((long)fpop());
+ }
+ else if (0 == strcmp(token, "emit"))
+ {
+ *out_ptr += sprintf(*out_ptr, "%ld", pop());
+ }
+ else if (0 == strcmp(token, "femit"))
+ {
+ *out_ptr += sprintf(*out_ptr, "%f", fpop());
+ }
+ else if (0 == strcmp(token, ","))
+ {
+ *out_ptr += sprintf(*out_ptr, ", ");
+ }
+ else if (0 == strcmp(token, "."))
+ {
+ fprintf(stderr, "[Debug] Int: %ld\n", peek());
+ }
+ else if (0 == strcmp(token, "f."))
+ {
+ fprintf(stderr, "[Debug] Float: %f\n", fpeek());
+ }
+ else if (0 == strcmp(token, ".\""))
+ {
+ char *p = *scan_ptr;
+ if (' ' == *p)
+ {
+ p++;
+ }
+ char *end = strchr(p, '"');
+ if (end)
+ {
+ int len = end - p;
+ strncpy(*out_ptr, p, len);
+ *out_ptr += len;
+ **out_ptr = '\0';
+ *scan_ptr = end + 1;
+ }
+ else
+ {
+ fprintf(stderr, "[Forth Error] Unterminated string literal\n");
+ exit(1);
+ }
+ }
+ else if (0 == strcmp(token, "s\""))
+ {
+ char *p = *scan_ptr;
+ if (' ' == *p)
+ {
+ p++;
+ }
+ char *end = strchr(p, '"');
+ if (end)
+ {
+ int len = end - p;
+ long start_addr = zhere;
+ for (int i = 0; i < len; i++)
+ {
+ zmemory[zhere++] = (long)p[i];
+ }
+ push(start_addr);
+ push(len);
+ *scan_ptr = end + 1;
+ }
+ else
+ {
+ fprintf(stderr, "[Forth Error] Unterminated string literal (s\")\n");
+ exit(1);
+ }
+ }
+ else if (0 == strcmp(token, "c@"))
+ {
+ long addr = pop();
+ push(zmemory[addr]);
+ }
+ else if (0 == strcmp(token, "!"))
+ {
+ long addr = pop();
+ long val = pop();
+ zmemory[addr] = val;
+ }
+ else if (0 == strcmp(token, "@"))
+ {
+ long addr = pop();
+ push(zmemory[addr]);
+ }
+ else if (0 == strcmp(token, "?"))
+ {
+ long addr = pop();
+ fprintf(stderr, "[Debug] @%ld = %ld\n", addr, zmemory[addr]);
+ }
+ else if (0 == strcmp(token, "allot"))
+ {
+ zhere += pop();
+ }
+ else if (0 == strcmp(token, "variable"))
+ {
+ char *name = next_token(scan_ptr);
+ if (!name)
+ {
+ fprintf(stderr, "[Error] 'variable' needs a name\n");
+ exit(1);
+ }
+ if (forth_dict_count < 128)
+ {
+ strcpy(forth_dict[forth_dict_count].name, name);
+ sprintf(forth_dict[forth_dict_count].body, "%d", zhere);
+ forth_dict_count++;
+ zhere++;
+ }
+ }
+ else if (0 == strcmp(token, "constant"))
+ {
+ long val = pop();
+ char *name = next_token(scan_ptr);
+ if (!name)
+ {
+ fprintf(stderr, "[Error] 'constant' needs a name\n");
+ exit(1);
+ }
+ if (forth_dict_count < 128)
+ {
+ strcpy(forth_dict[forth_dict_count].name, name);
+ sprintf(forth_dict[forth_dict_count].body, "%ld", val);
+ forth_dict_count++;
+ }
+ }
+ else
+ {
+ if (strchr(token, '.'))
+ {
+ char *end;
+ double fval = strtod(token, &end);
+ if ('\0' == *end)
+ {
+ fpush(fval);
+ return;
+ }
+ }
+ char *end;
+ long val = strtol(token, &end, 0);
+ if ('\0' == *end)
+ {
+ push(val);
+ }
+ else
+ {
+ int found = 0;
+ for (int i = forth_dict_count - 1; i >= 0; i--)
+ {
+ if (0 == strcmp(forth_dict[i].name, token))
+ {
+ char body_copy[512];
+ strcpy(body_copy, forth_dict[i].body);
+ char *sub_scan = body_copy;
+ char *sub_token;
+ while (NULL != (sub_token = next_token(&sub_scan)))
+ {
+ eval_forth_token(sub_token, &sub_scan, out_ptr);
+ }
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ *out_ptr += sprintf(*out_ptr, "%s ", token);
+ }
+ }
+ }
+}
+
+void process_forth_source(char *src, char **out_ptr)
+{
+ char *cursor = src;
+ char *token;
+ int state = 0;
+ char name[32];
+ char body[512];
+ body[0] = '\0';
+
+ while (NULL != (token = next_token(&cursor)))
+ {
+ if (0 == strcmp(token, "#end"))
+ {
+ break;
+ }
+
+ if (0 == state)
+ {
+ if (0 == strcmp(token, ":"))
+ {
+ state = 1;
+ }
+ else
+ {
+ eval_forth_token(token, &cursor, out_ptr);
+ }
+ }
+ else if (1 == state)
+ {
+ strncpy(name, token, 31);
+ name[31] = '\0';
+ body[0] = '\0';
+ state = 2;
+ }
+ else if (2 == state)
+ {
+ if (0 == strcmp(token, ";"))
+ {
+ if (forth_dict_count < 128)
+ {
+ strcpy(forth_dict[forth_dict_count].name, name);
+ strcpy(forth_dict[forth_dict_count].body, body);
+ forth_dict_count++;
+ }
+ state = 0;
+ }
+ else
+ {
+ if (strlen(body) > 0)
+ {
+ strcat(body, " ");
+ }
+ if (strlen(body) + strlen(token) < 512)
+ {
+ strcat(body, token);
+ }
+ }
+ }
+ }
+}
+
+void load_prelude()
+{
+ char prelude[] = ": squared fdup f* ; : hypot squared fswap squared f+ fsqrt ; : over swap dup "
+ "rot rot ; : panic 0 1 - exit ; ";
+ char dummy_buf[1024];
+ dummy_buf[0] = '\0';
+ char *ptr = dummy_buf;
+ process_forth_source(prelude, &ptr);
+}
+
+void zprep_forth_plugin_fn(const char *input_body, const ZApi *api)
+{
+ size_t cap = 8192;
+ char *output_buffer = malloc(cap);
+ if (!output_buffer)
+ {
+ return;
+ }
+ output_buffer[0] = '\0';
+
+ char *write_ptr = output_buffer;
+ char *input_copy = xstrdup(input_body);
+
+ process_forth_source(input_copy, &write_ptr);
+
+ free(input_copy);
+
+ fprintf(api->out, "%s", output_buffer);
+
+ free(output_buffer);
+}
+
+ZPlugin forth_plugin = {.name = "forth", .fn = zprep_forth_plugin_fn};
+
+ZPlugin *zprep_plugin_init()
+{
+ return &forth_plugin;
+}
diff --git a/plugins/lisp.c b/plugins/lisp.c
new file mode 100644
index 0000000..7147928
--- /dev/null
+++ b/plugins/lisp.c
@@ -0,0 +1,484 @@
+
+#include "zprep_plugin.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+static void skip_whitespace(const char **p)
+{
+ while (**p && isspace(**p))
+ {
+ (*p)++;
+ }
+}
+
+static void parse_lisp_expr(const char **p, const ZApi *api, int depth);
+
+static void parse_atom(const char **p, const ZApi *api)
+{
+ FILE *out = api->out;
+ const char *start = *p;
+
+ // Check for unexpected closer
+ if (**p == ')')
+ {
+ fprintf(stderr, "Error: Unexpected ')' at %s:%d\n", api->filename, api->current_line);
+ exit(1);
+ }
+
+ // Number
+ if (isdigit(**p) || (**p == '-' && isdigit((*p)[1])))
+ {
+ if (**p == '-')
+ {
+ (*p)++;
+ }
+ while (**p && isdigit(**p))
+ {
+ (*p)++;
+ }
+ fprintf(out, "l_num(");
+ fwrite(start, 1, *p - start, out);
+ fprintf(out, ")");
+ return;
+ }
+
+ if (**p == '"')
+ {
+ (*p)++;
+ while (**p && **p != '"')
+ {
+ if (**p == '\\')
+ {
+ (*p)++;
+ }
+ (*p)++;
+ }
+ if (**p == '"')
+ {
+ (*p)++;
+ }
+ fprintf(out, "l_nil()");
+ return;
+ }
+
+ // Symbol
+ while (**p && !isspace(**p) && **p != ')' && **p != '(')
+ {
+ (*p)++;
+ }
+ fwrite(start, 1, *p - start, out);
+}
+
+static void parse_lisp_list(const char **p, const ZApi *api, int depth)
+{
+ FILE *out = api->out;
+ (*p)++; // consume (
+ skip_whitespace(p);
+
+ if (!**p)
+ {
+ fprintf(stderr, "Error: Unclosed parenthesis (unexpected EOF) at %s:%d\n", api->filename,
+ api->current_line);
+ exit(1);
+ }
+
+ const char *op_start = *p;
+ while (**p && !isspace(**p) && **p != ')' && **p != '(')
+ {
+ (*p)++;
+ }
+ int op_len = *p - op_start;
+
+ // Arithmetic
+ if (op_len == 1 && strchr("+-*/", *op_start))
+ {
+ char op_char = *op_start;
+ char *func = "l_add";
+ if (op_char == '-')
+ {
+ func = "l_sub";
+ }
+ if (op_char == '*')
+ {
+ func = "l_mul";
+ }
+ if (op_char == '/')
+ {
+ func = "l_div";
+ }
+
+ fprintf(out, "%s(", func);
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ", ");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ")");
+ skip_whitespace(p);
+ while (**p && **p != ')')
+ {
+ if (**p == '(')
+ {
+ int d = 1;
+ (*p)++;
+ while (d > 0 && **p)
+ {
+ if (**p == '(')
+ {
+ d++;
+ }
+ if (**p == ')')
+ {
+ d--;
+ }
+ (*p)++;
+ }
+ }
+ else
+ {
+ while (**p && !isspace(**p) && **p != ')')
+ {
+ (*p)++;
+ }
+ }
+ skip_whitespace(p);
+ }
+ }
+ else if ((op_len == 1 && strchr("<>", *op_start)) || (op_len == 2 && strchr("=!", *op_start)))
+ {
+ char func[32] = "l_eq";
+ if (op_len == 1 && *op_start == '<')
+ {
+ strcpy(func, "l_lt");
+ }
+ else if (op_len == 1 && *op_start == '>')
+ {
+ strcpy(func, "l_gt");
+ }
+
+ fprintf(out, "%s(", func);
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ", ");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ")");
+ }
+ // Cons/Car/Cdr
+ else if (op_len == 4 && strncmp(op_start, "cons", 4) == 0)
+ {
+ fprintf(out, "l_cons(");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ", ");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ")");
+ }
+ else if (op_len == 3 && strncmp(op_start, "car", 3) == 0)
+ {
+ fprintf(out, "l_car(");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ")");
+ }
+ else if (op_len == 3 && strncmp(op_start, "cdr", 3) == 0)
+ {
+ fprintf(out, "l_cdr(");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ")");
+ }
+ // List - stub
+ else if (op_len == 4 && strncmp(op_start, "list", 4) == 0)
+ {
+ fprintf(out, "l_nil()");
+ }
+ // print/println
+ else if (strncmp(op_start, "print", 5) == 0)
+ {
+ skip_whitespace(p);
+ if (**p == '"')
+ {
+ // Raw string print
+ fprintf(out, "printf(\"");
+ (*p)++;
+ while (**p && **p != '"')
+ {
+ if (**p == '\\')
+ {
+ fprintf(out, "\\");
+ (*p)++;
+ if (**p)
+ {
+ fprintf(out, "%c", *(*p)++);
+ }
+ }
+ else
+ {
+ fprintf(out, "%c", *(*p)++);
+ }
+ }
+ fprintf(out, "%s\")", op_start[5] == 'l' ? "\\n" : "");
+ if (**p == '"')
+ {
+ (*p)++;
+ }
+ }
+ else
+ {
+ // LVal print
+ fprintf(out, "l_print(");
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ");%s", op_start[5] == 'l' ? " printf(\"\\n\");" : "");
+ }
+ }
+ // if
+ else if (op_len == 2 && strncmp(op_start, "if", 2) == 0)
+ {
+ fprintf(out, "(l_truthy(");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ") ? ");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, " : ");
+ skip_whitespace(p);
+ if (**p != ')')
+ {
+ parse_lisp_expr(p, api, depth + 1);
+ }
+ else
+ {
+ fprintf(out, "l_nil()");
+ }
+ fprintf(out, ")");
+ }
+ // let
+ else if (op_len == 3 && strncmp(op_start, "let", 3) == 0)
+ {
+ fprintf(out, "({\n");
+ skip_whitespace(p);
+ // Bindings...
+ if (**p == '(')
+ {
+ (*p)++;
+ skip_whitespace(p);
+ while (**p && **p != ')')
+ {
+ if (**p == '(')
+ {
+ (*p)++;
+ skip_whitespace(p);
+ const char *vstart = *p;
+ while (**p && !isspace(**p) && **p != ')')
+ {
+ (*p)++;
+ }
+ fprintf(out, "LVal ");
+ fwrite(vstart, 1, *p - vstart, out);
+ fprintf(out, " = ");
+ skip_whitespace(p);
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ";\n");
+ skip_whitespace(p);
+ if (**p == ')')
+ {
+ (*p)++;
+ }
+ }
+ skip_whitespace(p);
+ }
+ if (!**p)
+ {
+ fprintf(stderr, "Error: Unclosed let bindings at %s:%d\n", api->filename,
+ api->current_line);
+ exit(1);
+ }
+ if (**p == ')')
+ {
+ (*p)++;
+ }
+ }
+ // Body...
+ skip_whitespace(p);
+ while (**p && **p != ')')
+ {
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ";\n");
+ skip_whitespace(p);
+ }
+ fprintf(out, "})");
+ }
+ // defun
+ else if (op_len == 5 && strncmp(op_start, "defun", 5) == 0)
+ {
+ skip_whitespace(p);
+ const char *nstart = *p;
+ while (**p && !isspace(**p) && **p != '(')
+ {
+ (*p)++;
+ }
+ fprintf(out, "auto LVal ");
+ fwrite(nstart, 1, *p - nstart, out);
+ fprintf(out, "(");
+ skip_whitespace(p);
+ (*p)++; // (
+ skip_whitespace(p);
+ int first = 1;
+ while (**p && **p != ')')
+ {
+ if (!first)
+ {
+ fprintf(out, ", ");
+ }
+ first = 0;
+ fprintf(out, "LVal ");
+ const char *astart = *p;
+ while (**p && !isspace(**p) && **p != ')')
+ {
+ (*p)++;
+ }
+ fwrite(astart, 1, *p - astart, out);
+ skip_whitespace(p);
+ }
+ (*p)++; // )
+ fprintf(out, ") {\n return ({\n");
+ skip_whitespace(p);
+ while (**p && **p != ')')
+ {
+ parse_lisp_expr(p, api, depth + 1);
+ fprintf(out, ";\n");
+ skip_whitespace(p);
+ }
+ fprintf(out, "});\n}");
+ }
+ // Function call
+ else
+ {
+ fwrite(op_start, 1, op_len, out);
+ fprintf(out, "(");
+ skip_whitespace(p);
+ int first = 1;
+ while (**p && **p != ')')
+ {
+ if (!first)
+ {
+ fprintf(out, ", ");
+ }
+ first = 0;
+ parse_lisp_expr(p, api, depth + 1);
+ skip_whitespace(p);
+ }
+ fprintf(out, ")");
+ }
+
+ // consume remaining args or check for close
+ while (**p && **p != ')')
+ {
+ (*p)++;
+ }
+
+ if (!**p)
+ {
+ fprintf(stderr, "Error: Unclosed parenthesis (end of list) at %s:%d\n", api->filename,
+ api->current_line);
+ exit(1);
+ }
+ if (**p == ')')
+ {
+ (*p)++;
+ }
+}
+
+static void parse_lisp_expr(const char **p, const ZApi *api, int depth)
+{
+ skip_whitespace(p);
+ if (!**p)
+ {
+ return; // Should not happen if called correctly
+ }
+ if (**p == '(')
+ {
+ parse_lisp_list(p, api, depth);
+ }
+ else
+ {
+ parse_atom(p, api);
+ }
+}
+
+void lisp_transpile(const char *input_body, const ZApi *api)
+{
+ FILE *out = api->out;
+ const char *p = input_body;
+
+ static int runtime_emitted = 0;
+ if (!runtime_emitted && api->hoist_out)
+ {
+ FILE *h = api->hoist_out;
+ fprintf(h, "/* Lisp Runtime */\n");
+ fprintf(h, "typedef enum { L_NUM, L_PAIR, L_NIL } LType;\n");
+ fprintf(h, "typedef struct LVal { LType type; union { long num; struct { struct LVal *car; "
+ "struct LVal *cdr; } pair; }; } *LVal;\n");
+ fprintf(h, "static struct LVal _nil = { L_NIL }; static LVal LNIL = &_nil;\n");
+ fprintf(h, "static LVal nil = &_nil;\n"); // Use static for file scope
+ fprintf(h, "static LVal l_num(long n) { LVal v = malloc(sizeof(struct LVal)); "
+ "v->type=L_NUM; v->num=n; return v; }\n");
+ fprintf(h, "static LVal l_nil() { return LNIL; }\n");
+ fprintf(h, "static LVal l_cons(LVal a, LVal b) { LVal v = malloc(sizeof(struct LVal)); "
+ "v->type=L_PAIR; v->pair.car=a; v->pair.cdr=b; return v; }\n");
+ fprintf(
+ h,
+ "static LVal l_car(LVal v) { return (v && v->type==L_PAIR) ? v->pair.car : LNIL; }\n");
+ fprintf(
+ h,
+ "static LVal l_cdr(LVal v) { return (v && v->type==L_PAIR) ? v->pair.cdr : LNIL; }\n");
+ fprintf(h, "static int l_truthy(LVal v) { return (v && v->type!=L_NIL); }\n");
+ fprintf(h, "static LVal l_add(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long "
+ "y=(b&&b->type==L_NUM)?b->num:0; return l_num(x+y); }\n");
+ fprintf(h, "static LVal l_sub(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long "
+ "y=(b&&b->type==L_NUM)?b->num:0; return l_num(x-y); }\n");
+ fprintf(h, "static LVal l_mul(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long "
+ "y=(b&&b->type==L_NUM)?b->num:0; return l_num(x*y); }\n");
+ fprintf(h, "static LVal l_div(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long "
+ "y=(b&&b->type==L_NUM)?b->num:0; return l_num(y?x/y:0); }\n");
+ fprintf(h, "static LVal l_lt(LVal a, LVal b) { long x=(a&&a->type==L_NUM)?a->num:0; long "
+ "y=(b&&b->type==L_NUM)?b->num:0; return (x<y)?l_num(1):LNIL; }\n");
+ fprintf(h, "static void l_print(LVal v) { \n");
+ fprintf(h, " if(!v || v->type==L_NIL) printf(\"nil\");\n");
+ fprintf(h, " else if(v->type==L_NUM) printf(\"%%ld\", v->num);\n");
+ fprintf(h, " else if(v->type==L_PAIR) { printf(\"(\"); l_print(v->pair.car); printf(\" . "
+ "\"); l_print(v->pair.cdr); printf(\")\"); }\n");
+ fprintf(h, "}\n");
+
+ runtime_emitted = 1;
+ }
+
+ fprintf(out, "({\n");
+
+ while (*p)
+ {
+ skip_whitespace(&p);
+ if (!*p)
+ {
+ break;
+ }
+
+ if (*p == ')')
+ {
+ fprintf(stderr, "Error: Unexpected ')' at top level at %s:%d\n", api->filename,
+ api->current_line);
+ exit(1);
+ }
+
+ fprintf(out, " ");
+ parse_lisp_expr(&p, api, 1);
+ fprintf(out, ";\n");
+ }
+ fprintf(out, "})\n");
+}
+
+ZPlugin lisp_plugin = {.name = "lisp", .fn = lisp_transpile};
diff --git a/plugins/regex.c b/plugins/regex.c
new file mode 100644
index 0000000..d7a8a97
--- /dev/null
+++ b/plugins/regex.c
@@ -0,0 +1,226 @@
+
+#include "zprep_plugin.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+static void emit_match_logic(const char *pattern, FILE *out, int *label_counter);
+
+void regex_transpile(const char *input_body, const ZApi *api)
+{
+ FILE *out = api->out;
+ const char *p = input_body;
+ static int regex_counter = 0;
+
+ // Trim whitespace from start/end of pattern block
+ while (*p && isspace(*p))
+ {
+ p++;
+ }
+ char *pattern = strdup(p);
+ int len = strlen(pattern);
+ while (len > 0 && isspace(pattern[len - 1]))
+ {
+ pattern[--len] = 0;
+ }
+
+ // Generate unique function name
+ int fn_id = regex_counter++;
+ char fn_name[64];
+ snprintf(fn_name, sizeof(fn_name), "_regex_match_%d", fn_id);
+
+ FILE *target = api->hoist_out ? api->hoist_out : out;
+
+ fprintf(target, "static int %s(const char *text) {\n", fn_name);
+ fprintf(target, " if (!text) return 0;\n");
+ fprintf(target, " const char *c = text;\n");
+
+ int label_id = 0;
+ emit_match_logic(pattern, target, &label_id);
+
+ fprintf(target, " return 1;\n");
+ fprintf(target, "}\n");
+
+ fprintf(out, "%s", fn_name);
+
+ free(pattern);
+}
+
+static void emit_match_logic(const char *pattern, FILE *out, int * /*label_counter*/)
+{
+ const char *c = pattern;
+
+ while (*c)
+ {
+ if (isspace(*c))
+ {
+ c++;
+ continue;
+ }
+
+ // Handle Anchors
+ if (*c == '^')
+ {
+ c++;
+ continue;
+ }
+
+ if (*c == '$')
+ {
+ fprintf(out, " if (*c != '\\0') return 0;\n");
+ c++;
+ continue;
+ }
+
+ // Character Class [a-z]
+ if (*c == '[')
+ {
+ const char *scanner = c + 1;
+ while (*scanner && *scanner != ']')
+ {
+ scanner++;
+ }
+ if (!*scanner)
+ {
+ return;
+ }
+ const char *class_end = scanner;
+
+ const char *q = class_end + 1;
+ while (*q && isspace(*q))
+ {
+ q++;
+ }
+ char quantifier = 0;
+ if (*q == '+' || *q == '*')
+ {
+ quantifier = *q;
+ }
+
+ fprintf(out, " {\n");
+ fprintf(out, " int _count = 0;\n");
+ if (quantifier)
+ {
+ fprintf(out, " while (1) {\n");
+ }
+
+ if (quantifier)
+ {
+ fprintf(out, " if (*c == 0) break;\n"); // End of input for loop
+ }
+ else
+ {
+ fprintf(out, " if (*c == 0) return 0;\n"); // End of input for single char
+ }
+
+ fprintf(out, " int match = 0;\n");
+
+ const char *p = c + 1;
+ int invert = 0;
+ while (p < class_end && isspace(*p))
+ {
+ p++;
+ }
+ if (p < class_end && *p == '^')
+ {
+ invert = 1;
+ p++;
+ }
+ while (p < class_end && isspace(*p))
+ {
+ p++;
+ }
+
+ while (p < class_end)
+ {
+ if (isspace(*p))
+ {
+ p++;
+ continue;
+ }
+
+ // Range check
+ const char *next = p + 1;
+ while (next < class_end && isspace(*next))
+ {
+ next++;
+ }
+
+ if (next < class_end && *next == '-' && next + 1 < class_end)
+ {
+ const char *range_end = next + 1;
+ while (range_end < class_end && isspace(*range_end))
+ {
+ range_end++;
+ }
+
+ if (range_end < class_end)
+ {
+ fprintf(out, " if (*c >= '%c' && *c <= '%c') match = 1;\n", *p,
+ *range_end);
+ p = range_end + 1;
+ continue;
+ }
+ }
+
+ fprintf(out, " if (*c == '%c') match = 1;\n", *p);
+ p++;
+ }
+
+ if (invert)
+ {
+ fprintf(out, " if (match) { match = 0; } else { match = 1; }\n");
+ }
+
+ if (quantifier)
+ {
+ fprintf(out, " if (!match) break;\n");
+ fprintf(out, " c++; _count++;\n");
+ fprintf(out, " }\n"); // End while
+ if (quantifier == '+')
+ {
+ fprintf(out, " if (_count == 0) return 0;\n");
+ }
+ }
+ else
+ {
+ fprintf(out, " if (!match) return 0;\n");
+ fprintf(out, " c++;\n");
+ }
+
+ fprintf(out, " }\n"); // End Block
+
+ // Advance main pointer c
+ c = class_end + 1;
+ if (quantifier)
+ {
+ // Skip the quantifier token we processed
+ while (*c && isspace(*c))
+ {
+ c++;
+ }
+ if (*c == quantifier)
+ {
+ c++;
+ }
+ }
+ continue;
+ }
+
+ // Dot
+ if (*c == '.')
+ {
+ fprintf(out, " if (*c == 0) return 0; c++;\n");
+ c++;
+ continue;
+ }
+
+ // Literal
+ char lit = *c;
+ fprintf(out, " if (*c != '%c') return 0; c++;\n", lit);
+ c++;
+ }
+}
+
+ZPlugin regex_plugin = {.name = "regex", .fn = regex_transpile};
diff --git a/plugins/sql.c b/plugins/sql.c
new file mode 100644
index 0000000..0c563e2
--- /dev/null
+++ b/plugins/sql.c
@@ -0,0 +1,415 @@
+
+#include "zprep_plugin.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct Col
+{
+ char name[32];
+ char type[32];
+ struct Col *next;
+} Col;
+
+typedef struct Table
+{
+ char name[64];
+ Col *cols;
+ struct Table *next;
+} Table;
+
+static Table *g_tables = NULL;
+
+static void register_table(const char *name)
+{
+ Table *t = malloc(sizeof(Table));
+ strcpy(t->name, name);
+ t->cols = NULL;
+ t->next = g_tables;
+ g_tables = t;
+}
+
+static void add_column(const char *table_name, const char *col_name, const char *col_type)
+{
+ Table *t = g_tables;
+ while (t && strcmp(t->name, table_name) != 0)
+ {
+ t = t->next;
+ }
+ if (t)
+ {
+ Col *c = malloc(sizeof(Col));
+ strcpy(c->name, col_name);
+ strcpy(c->type, col_type);
+ c->next = NULL;
+
+ if (!t->cols)
+ {
+ t->cols = c;
+ }
+ else
+ {
+ Col *last = t->cols;
+ while (last->next)
+ {
+ last = last->next;
+ }
+ last->next = c;
+ }
+ }
+}
+
+static Table *find_table(const char *name)
+{
+ Table *t = g_tables;
+ while (t)
+ {
+ if (strcmp(t->name, name) == 0)
+ {
+ return t;
+ }
+ t = t->next;
+ }
+ return NULL;
+}
+
+static void skip_whitespace(const char **p)
+{
+ while (1)
+ {
+ while (**p && isspace(**p))
+ {
+ (*p)++;
+ }
+
+ if (**p == '/' && *(*p + 1) == '/')
+ {
+ // Single line comment
+ (*p) += 2;
+ while (**p && **p != '\n')
+ {
+ (*p)++;
+ }
+ }
+ else if (**p == '/' && *(*p + 1) == '*')
+ {
+ // Multi line comment
+ (*p) += 2;
+ while (**p && !(**p == '*' && *(*p + 1) == '/'))
+ {
+ (*p)++;
+ }
+ if (**p)
+ {
+ (*p) += 2; // Skip */
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+static int match_kw(const char **p, const char *kw)
+{
+ size_t len = strlen(kw);
+ if (strncasecmp(*p, kw, len) == 0 && !isalnum((*p)[len]) && (*p)[len] != '_')
+ {
+ *p += len;
+ return 1;
+ }
+ return 0;
+}
+
+static void parse_create_table(const char **p, const ZApi *api)
+{
+ FILE *out = api->out;
+ skip_whitespace(p);
+
+ // table_name
+ const char *name_start = *p;
+ while (**p && (isalnum(**p) || **p == '_'))
+ {
+ (*p)++;
+ }
+ int name_len = *p - name_start;
+ char table_name[64];
+ snprintf(table_name, name_len + 1, "%s", name_start);
+
+ register_table(table_name);
+
+ skip_whitespace(p);
+ if (**p == '(')
+ {
+ (*p)++;
+ }
+ else
+ {
+ fprintf(stderr, "Error: Expected '(' after table name\n");
+ exit(1);
+ }
+ skip_whitespace(p);
+
+ fprintf(out, "typedef struct {\n");
+
+ // Columns
+ while (**p && **p != ')')
+ {
+ skip_whitespace(p);
+ const char *col_start = *p;
+ while (**p && (isalnum(**p) || **p == '_'))
+ {
+ (*p)++;
+ }
+ int col_len = *p - col_start;
+ if (col_len == 0 && **p != ',')
+ {
+ // Unexpected token or end
+ break;
+ }
+
+ char col_name[64];
+ snprintf(col_name, col_len + 1, "%s", col_start);
+
+ skip_whitespace(p);
+ const char *type_start = *p;
+ while (**p && (isalnum(**p) || **p == '_'))
+ {
+ (*p)++;
+ }
+ int type_len = *p - type_start;
+
+ char ctype[32] = "int";
+ char store_type[32] = "int";
+
+ if (type_len > 0 && (strncasecmp(type_start, "TEXT", type_len) == 0 ||
+ strncasecmp(type_start, "STRING", type_len) == 0))
+ {
+ strcpy(ctype, "const char*");
+ strcpy(store_type, "char*");
+ }
+
+ add_column(table_name, col_name, store_type);
+
+ fprintf(out, " %s %s;\n", ctype, col_name);
+
+ skip_whitespace(p);
+ if (**p == ',')
+ {
+ (*p)++;
+ }
+ }
+ if (**p == ')')
+ {
+ (*p)++;
+ }
+
+ skip_whitespace(p);
+ if (**p == ';')
+ {
+ (*p)++;
+ }
+
+ fprintf(out, "} Row_%s;\n", table_name);
+ fprintf(out, "static Row_%s table_%s[128];\n", table_name, table_name);
+ fprintf(out, "static int count_%s = 0;\n", table_name);
+}
+
+static void parse_insert(const char **p, const ZApi *api)
+{
+ FILE *out = api->out;
+ skip_whitespace(p);
+ if (!match_kw(p, "INTO"))
+ {
+ // Well, it is just a toy plugin rn, what did you expect?
+ }
+ skip_whitespace(p);
+
+ const char *name_start = *p;
+ while (**p && (isalnum(**p) || **p == '_'))
+ {
+ (*p)++;
+ }
+ int name_len = *p - name_start;
+ char table_name[64];
+ snprintf(table_name, name_len + 1, "%s", name_start);
+
+ skip_whitespace(p);
+ if (!match_kw(p, "VALUES"))
+ {
+ fprintf(stderr, "Error: Expected VALUES in INSERT\n");
+ exit(1);
+ }
+ skip_whitespace(p);
+ if (**p == '(')
+ {
+ (*p)++;
+ }
+
+ fprintf(out, "if (count_%s < 128) {\n", table_name);
+ fprintf(out, " table_%s[count_%s] = (Row_%s){ ", table_name, table_name, table_name);
+
+ while (**p && **p != ')')
+ {
+ skip_whitespace(p);
+ // Value expression
+ const char *val_start = *p;
+ if (**p == '"')
+ {
+ (*p)++;
+ while (**p && **p != '"')
+ {
+ if (**p == '\\')
+ {
+ (*p)++;
+ }
+ (*p)++;
+ }
+ if (**p == '"')
+ {
+ (*p)++;
+ }
+ }
+ else
+ {
+ while (**p && **p != ',' && **p != ')')
+ {
+ (*p)++;
+ }
+ }
+ fwrite(val_start, 1, *p - val_start, out);
+
+ skip_whitespace(p);
+ if (**p == ',')
+ {
+ fprintf(out, ", ");
+ (*p)++;
+ }
+ }
+ if (**p == ')')
+ {
+ (*p)++;
+ }
+ skip_whitespace(p);
+ if (**p == ';')
+ {
+ (*p)++;
+ }
+
+ fprintf(out, " };\n");
+ fprintf(out, " count_%s++;\n", table_name);
+ fprintf(out, "}\n");
+}
+
+static void parse_select(const char **p, const ZApi *api)
+{
+ FILE *out = api->out;
+ skip_whitespace(p);
+ match_kw(p, "*"); // MVP only supports *
+ skip_whitespace(p);
+ if (!match_kw(p, "FROM"))
+ {
+ fprintf(stderr, "Error: Expected FROM in SELECT\n");
+ exit(1);
+ }
+ skip_whitespace(p);
+
+ const char *name_start = *p;
+ while (**p && (isalnum(**p) || **p == '_'))
+ {
+ (*p)++;
+ }
+ int name_len = *p - name_start;
+ char table_name[64];
+ snprintf(table_name, name_len + 1, "%s", name_start);
+
+ skip_whitespace(p);
+ if (**p == ';')
+ {
+ (*p)++;
+ }
+
+ Table *t = find_table(table_name);
+ if (!t)
+ {
+ fprintf(stderr, "Error: Unknown table '%s' in SELECT\n", table_name);
+ exit(1);
+ }
+
+ fprintf(out, "for(int _i=0; _i<count_%s; _i++) {\n", table_name);
+ fprintf(out, " printf(\"");
+
+ // Format string
+ Col *c = t->cols;
+ while (c)
+ {
+ if (strcmp(c->type, "int") == 0)
+ {
+ fprintf(out, "%%d");
+ }
+ else
+ {
+ fprintf(out, "%%s");
+ }
+
+ if (c->next)
+ {
+ fprintf(out, " ");
+ }
+ c = c->next;
+ }
+ fprintf(out, "\\n\"");
+
+ // Args
+ c = t->cols;
+ while (c)
+ {
+ fprintf(out, ", table_%s[_i].%s", table_name, c->name);
+ c = c->next;
+ }
+
+ fprintf(out, ");\n");
+ fprintf(out, "}\n");
+}
+
+void sql_transpile(const char *input_body, const ZApi *api)
+{
+ FILE *out = api->out;
+ const char *p = input_body;
+
+ fprintf(out, "({\n");
+
+ while (*p)
+ {
+ skip_whitespace(&p);
+ if (!*p)
+ {
+ break;
+ }
+
+ if (match_kw(&p, "CREATE"))
+ {
+ skip_whitespace(&p);
+ match_kw(&p, "TABLE"); // consume optional TABLE
+ parse_create_table(&p, api);
+ }
+ else if (match_kw(&p, "INSERT"))
+ {
+ parse_insert(&p, api);
+ }
+ else if (match_kw(&p, "SELECT"))
+ {
+ parse_select(&p, api);
+ }
+ else
+ {
+ p++; // Skip unknown or error
+ }
+ }
+
+ fprintf(out, "0; })"); // Return 0
+}
+
+ZPlugin sql_plugin = {.name = "sql", .fn = sql_transpile};
diff --git a/plugins/zprep_plugin.h b/plugins/zprep_plugin.h
new file mode 100644
index 0000000..b51cffd
--- /dev/null
+++ b/plugins/zprep_plugin.h
@@ -0,0 +1,30 @@
+
+#ifndef ZPREP_PLUGIN_H
+#define ZPREP_PLUGIN_H
+
+#include <stddef.h>
+#include <stdio.h>
+
+// The Host provides this API to the Plugin.
+typedef struct
+{
+ // Context Information (Where are we?).
+ const char *filename;
+ int current_line;
+ FILE *out; // Inline output (expression context)
+ FILE *hoist_out; // Hoisted output (file scope / top level)
+} ZApi;
+
+// The Plugin Function Signature.
+// Returns void. All output is done via 'api'.
+typedef void (*ZPluginTranspileFn)(const char *input_body, const ZApi *api);
+
+typedef struct
+{
+ char name[32];
+ ZPluginTranspileFn fn;
+} ZPlugin;
+
+typedef ZPlugin *(*ZPluginInitFn)(void);
+
+#endif