diff options
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/befunge.c | 198 | ||||
| -rw-r--r-- | plugins/brainfuck.c | 44 | ||||
| -rw-r--r-- | plugins/forth.c | 636 | ||||
| -rw-r--r-- | plugins/lisp.c | 484 | ||||
| -rw-r--r-- | plugins/regex.c | 226 | ||||
| -rw-r--r-- | plugins/sql.c | 415 | ||||
| -rw-r--r-- | plugins/zprep_plugin.h | 30 |
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 |
