summaryrefslogtreecommitdiff
path: root/plugins/lisp.c
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 15:51:21 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-01-11 15:51:21 +0000
commit348c64f97b133a7f346d242bb4658df3b5a99719 (patch)
treeba6a3fe8c8c4973e035d253b8fb50619286a47fc /plugins/lisp.c
parenta524ba47371503e0a42d395d015dcd83c15c0199 (diff)
Plugins time.
Diffstat (limited to 'plugins/lisp.c')
-rw-r--r--plugins/lisp.c484
1 files changed, 484 insertions, 0 deletions
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};