Download products/unlambda-seal/unlambda.c from Snapkitty/seit: direct link, hf CLI and curl.
- Browser
- Download file 8.23 kB
-
https://huggingface.co/Snapkitty/seit/resolve/main/products/unlambda-seal/unlambda.c
- Command line
-
hf download hf://Snapkitty/seit/products/unlambda-seal/unlambda.c
-
curl -L -o unlambda.c https://huggingface.co/Snapkitty/seit/resolve/main/products/unlambda-seal/unlambda.c
8.23 kB
| /* | |
| * unlambda.c — Unlambda Seal (SEIT product recreation) | |
| * Copyright (C) 2026 Ahmad Ali Parr / SnapKitty Research Lab | |
| * SPDX-License-Identifier: LicenseRef-SEIT-FSL-Donation-1.0 | |
| * | |
| * Functional source, donation-gated runtime: you may read, share, and | |
| * modify this source freely; running it requires a qualifying donation | |
| * to the licensor. See LICENSE.txt for the full terms. No warranty. | |
| */ | |
| /* | |
| * unlambda.c — A small Unlambda (v2) interpreter in C11. | |
| * | |
| * Unlambda is a minimalist esoteric functional language: the entire language | |
| * is function application (written `) plus a handful of combinators and | |
| * I/O primitives. This interpreter implements the standard strict semantics: | |
| * | |
| * s k i standard combinators `sxyz = (xz)(yz), `kxy = x, `ix = x | |
| * v void: `vx = v | |
| * c call/cc (one-shot, via setjmp/longjmp) | |
| * d1 `d1x = promise of x (x not evaluated until forced) | |
| * d2 ``d2xy = promise of `xy | |
| * e `ex terminates the program | |
| * @ `@x: read a char c; then `x.c — on EOF, `xv | |
| * (a "character value" is its own print primitive) | |
| * | like @ but echoes the character | |
| * .x `.xy: print x, return i | |
| * r `rx: print newline, return i | |
| * | |
| * Notes / honest limits: | |
| * - `d` followed by 1/2 selects d1/d2; a bare `d` means d1 (original). | |
| * - Whitespace and `#` comments are skipped between tokens (convenience; | |
| * `.` still takes the literal next character, even a space). | |
| * - call/cc continuations are ONE-SHOT: invoking a captured continuation | |
| * twice is undefined (the jmp_buf is spent). | |
| * - Nodes are never freed (fine for small programs). | |
| * | |
| * The classic print idiom is reversed nesting, because evaluation is | |
| * strict: "`.o`.l`.l`.e`.H`i" prints "Hello". | |
| * | |
| * Build: make | |
| * Run: ./unlambda seal.unl | |
| * ./unlambda < prog.unl (stdin; avoid if the program uses @) | |
| */ | |
| typedef enum { | |
| N_S, N_K, N_I, N_V, N_C, N_D1, N_D2, N_E, | |
| N_AT, N_PIPE, N_DOT, N_R, | |
| N_APP, N_PROMISE, N_CONT | |
| } NType; | |
| typedef struct Node { | |
| NType t; | |
| char c; /* N_DOT: character to print */ | |
| struct Node *l, *r; /* N_APP: function, argument; N_PROMISE: expr */ | |
| jmp_buf jb; /* N_CONT: captured continuation */ | |
| } Node; | |
| static Node *new_node(NType t) { | |
| Node *n = calloc(1, sizeof *n); | |
| if (!n) { fprintf(stderr, "unlambda: out of memory\n"); exit(1); } | |
| n->t = t; | |
| return n; | |
| } | |
| /* ---------------- parser ---------------- */ | |
| static const char *src; | |
| static int parse_error; | |
| static void skip_ws(void) { | |
| for (;;) { | |
| char ch = *src; | |
| if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') { src++; continue; } | |
| if (ch == '#') { while (*src && *src != '\n') src++; continue; } | |
| break; | |
| } | |
| } | |
| static Node *parse_expr(void) { | |
| skip_ws(); | |
| char ch = *src; | |
| if (ch == '\0') { parse_error = 1; return NULL; } | |
| src++; | |
| switch (ch) { | |
| case '`': { | |
| Node *a = parse_expr(); | |
| Node *b = parse_expr(); | |
| if (!a || !b) return NULL; | |
| Node *n = new_node(N_APP); n->l = a; n->r = b; return n; | |
| } | |
| case 's': return new_node(N_S); | |
| case 'k': return new_node(N_K); | |
| case 'i': return new_node(N_I); | |
| case 'v': return new_node(N_V); | |
| case 'c': return new_node(N_C); | |
| case 'd': | |
| if (*src == '1') { src++; return new_node(N_D1); } | |
| if (*src == '2') { src++; return new_node(N_D2); } | |
| return new_node(N_D1); | |
| case 'e': return new_node(N_E); | |
| case '@': return new_node(N_AT); | |
| case '|': return new_node(N_PIPE); | |
| case '.': { | |
| if (*src == '\0') { parse_error = 1; return NULL; } | |
| Node *n = new_node(N_DOT); n->c = *src; src++; return n; | |
| } | |
| case 'r': return new_node(N_R); | |
| default: | |
| fprintf(stderr, "unlambda: parse error at '%c'\n", ch); | |
| parse_error = 1; | |
| return NULL; | |
| } | |
| } | |
| /* ---------------- evaluator ---------------- */ | |
| static Node *cc_val; /* value delivered when a continuation is invoked */ | |
| static Node *eval(Node *n); | |
| static Node *apply(Node *f, Node *x); | |
| /* Force promises in function position. */ | |
| static Node *force(Node *n) { | |
| while (n->t == N_PROMISE) | |
| n = eval(n->l); | |
| return n; | |
| } | |
| static Node *eval(Node *n) { | |
| switch (n->t) { | |
| case N_PROMISE: | |
| return eval(n->l); | |
| case N_APP: { | |
| Node *f = force(eval(n->l)); | |
| if (f->t == N_D1) { /* d1: delay the argument */ | |
| Node *p = new_node(N_PROMISE); p->l = n->r; return p; | |
| } | |
| Node *x = eval(n->r); | |
| return apply(f, x); | |
| } | |
| default: | |
| return n; | |
| } | |
| } | |
| static Node *apply(Node *f, Node *x) { | |
| /* f is a forced value here */ | |
| if (f->t == N_APP) { | |
| /* partial application in function position */ | |
| Node *fl = f->l; | |
| if (fl->t == N_K) | |
| return f->r; /* `kxy = x */ | |
| if (fl->t == N_S) { /* `sx -> partial */ | |
| Node *n = new_node(N_APP); n->l = f; n->r = x; return n; | |
| } | |
| if (fl->t == N_D2) { /* ``d2xy = promise */ | |
| Node *a = new_node(N_APP); a->l = f->r; a->r = x; | |
| Node *p = new_node(N_PROMISE); p->l = a; return p; | |
| } | |
| if (fl->t == N_APP && fl->l->t == N_S) { /* ```sxyz=(xz)(yz) */ | |
| Node *a = fl->r, *b = f->r; | |
| Node *left = new_node(N_APP); left->l = a; left->r = x; | |
| Node *right = new_node(N_APP); right->l = b; right->r = x; | |
| Node *app = new_node(N_APP); app->l = left; app->r = right; | |
| return eval(app); | |
| } | |
| fprintf(stderr, "unlambda: runtime error (bad application)\n"); | |
| exit(1); | |
| } | |
| switch (f->t) { | |
| case N_S: | |
| case N_K: | |
| case N_D2: { | |
| Node *n = new_node(N_APP); n->l = f; n->r = x; return n; | |
| } | |
| case N_I: | |
| return x; | |
| case N_V: | |
| return f; /* `vx = v */ | |
| case N_C: { /* call/cc, one-shot */ | |
| Node *cont = new_node(N_CONT); | |
| if (setjmp(cont->jb) == 0) | |
| return eval(apply(x, cont)); | |
| return cc_val; /* invoked: abandon */ | |
| } | |
| case N_CONT: | |
| cc_val = x; | |
| longjmp(f->jb, 1); | |
| return x; /* unreachable */ | |
| case N_D1: { /* belt and braces */ | |
| Node *p = new_node(N_PROMISE); p->l = x; return p; | |
| } | |
| case N_E: | |
| exit(0); | |
| case N_AT: | |
| case N_PIPE: { | |
| int ch = getchar(); | |
| Node *arg; | |
| if (ch == EOF) { | |
| arg = new_node(N_V); | |
| } else { | |
| if (f->t == N_PIPE) | |
| putchar(ch); | |
| arg = new_node(N_DOT); arg->c = (char)ch; | |
| } | |
| return eval(apply(x, arg)); | |
| } | |
| case N_DOT: | |
| putchar(f->c); | |
| return new_node(N_I); | |
| case N_R: | |
| putchar('\n'); | |
| return new_node(N_I); | |
| default: | |
| fprintf(stderr, "unlambda: runtime error (bad function)\n"); | |
| exit(1); | |
| } | |
| return NULL; /* unreachable */ | |
| } | |
| /* ---------------- driver ---------------- */ | |
| static char *read_all(FILE *fp) { | |
| size_t cap = 1 << 16, len = 0; | |
| char *buf = malloc(cap); | |
| if (!buf) { fprintf(stderr, "unlambda: out of memory\n"); exit(1); } | |
| int ch; | |
| while ((ch = fgetc(fp)) != EOF) { | |
| if (len + 1 >= cap) { cap *= 2; buf = realloc(buf, cap); } | |
| buf[len++] = (char)ch; | |
| } | |
| buf[len] = '\0'; | |
| return buf; | |
| } | |
| int main(int argc, char **argv) { | |
| FILE *fp = stdin; | |
| if (argc > 1) { | |
| fp = fopen(argv[1], "r"); | |
| if (!fp) { perror("unlambda: open"); return 1; } | |
| } | |
| char *text = read_all(fp); | |
| if (fp != stdin) | |
| fclose(fp); | |
| src = text; | |
| Node *prog = parse_expr(); | |
| if (parse_error || !prog) { fprintf(stderr, "unlambda: parse error\n"); return 1; } | |
| skip_ws(); | |
| if (*src != '\0') { fprintf(stderr, "unlambda: parse error (trailing input)\n"); return 1; } | |
| eval(prog); | |
| return 0; | |
| } | |