mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
e16a7d8f3b
`et` means `expandtab`. ```sh rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\) et\(.*\) :vi/vi: \1 xoet\2:vi/' rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\)noet\(.*\):vi/vi: \1et\2 :vi/' rg 'vi: .* :vi' -l -0 | \ xargs -0 sed -i '' 's/vi: \(.*\)xoet\(.*\):vi/vi: \1noet\2:vi/' ```
354 lines
8.3 KiB
C
354 lines
8.3 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/assert.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/struct/rlimit.h"
|
|
#include "libc/calls/struct/sigaction.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/locale.h"
|
|
#include "libc/sysv/consts/map.h"
|
|
#include "libc/sysv/consts/prot.h"
|
|
#include "libc/sysv/consts/rlimit.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "third_party/getopt/getopt.internal.h"
|
|
#include "tool/lambda/lib/blc.h"
|
|
|
|
#define USAGE \
|
|
" [-?hubBdsarvnNlS] <stdin >expr.txt\n\
|
|
Binary Lambda Calculus Virtual Machine\n\
|
|
\n\
|
|
FLAGS\n\
|
|
\n\
|
|
-h help\n\
|
|
-r rex log\n\
|
|
-b binary 8-bit i/o\n\
|
|
-B debug print binary\n\
|
|
-l print lambda notation\n\
|
|
-a action log [implies -r]\n\
|
|
-v variable log [implies -r]\n\
|
|
-s full machine state logging\n\
|
|
-n disables name rewriting rules\n\
|
|
-N disables most unicode symbolism\n\
|
|
-d dump terms on successful exit\n"
|
|
|
|
#define NIL 23
|
|
#define TRUE 27
|
|
#define FALSE 23
|
|
|
|
#define REF(c) (++(c)->refs, c)
|
|
|
|
static const char kRom[] = {
|
|
APP, 0, // 0 (λ 0 λ 0 (λ 0 wr0 wr1) put) (main gro)
|
|
ABS, // 2 λ 0 λ 0 (λ 0 wr0 wr1) put
|
|
APP, 0, // 3
|
|
VAR, 0, // 5
|
|
ABS, // 7
|
|
APP, // 8
|
|
ABS, // 9 λ 0 λ 0 wr0 wr1
|
|
APP, 2, // 10
|
|
VAR, // 12
|
|
IOP, // 13
|
|
ABS, // 14 λ 0 wr0 wr1
|
|
APP, 4, // 15
|
|
APP, 1, // 17
|
|
VAR, // 19
|
|
IOP, // 20 wr0
|
|
IOP, 0, // 21 wr1
|
|
ABS, // 23 (λλ 0) a.k.a. nil
|
|
ABS, // 24 exit
|
|
VAR, // 25
|
|
0, // 26 exit[0]
|
|
ABS, // 27 (λλ 1) a.k.a. true
|
|
ABS, // 28
|
|
VAR, 1, // 29
|
|
};
|
|
|
|
static int postdump;
|
|
static int kLazy[256];
|
|
|
|
void Quit(int sig) {
|
|
Dump(0, end, stderr);
|
|
exit(128 + sig);
|
|
}
|
|
|
|
void PrintUsage(const char *prog, int rc, FILE *f) {
|
|
fputs("Usage: ", f);
|
|
fputs(prog, f);
|
|
fputs(USAGE, f);
|
|
exit(rc);
|
|
}
|
|
|
|
int Backref(int x) {
|
|
return x - (end + 1);
|
|
}
|
|
|
|
static inline void Expand(int c) {
|
|
if (end >= TERMS) Error(5, "OUT OF TERMS");
|
|
mem[end++] = c;
|
|
}
|
|
|
|
void Gc(struct Closure *p) {
|
|
struct Closure *t;
|
|
while (p && p != &root) {
|
|
if (--p->refs) break;
|
|
Gc(p->next);
|
|
t = p->envp;
|
|
p->envp = 0;
|
|
p->next = frep;
|
|
frep = p;
|
|
p = t;
|
|
}
|
|
}
|
|
|
|
void Var(void) {
|
|
int i, x;
|
|
struct Closure *t, *e;
|
|
e = t = envp;
|
|
x = mem[ip + 1];
|
|
for (i = 0; i < x && e != &root; ++i) e = e->next;
|
|
if (e == &root) Error(10 + x, "UNDEFINED VARIABLE %d", x);
|
|
ip = e->term;
|
|
envp = REF(e->envp);
|
|
Gc(t);
|
|
}
|
|
|
|
void Gro(void) {
|
|
int c = fgetc(stdin);
|
|
if (c != -1) {
|
|
Expand(ABS);
|
|
Expand(APP);
|
|
Expand(4);
|
|
Expand(APP);
|
|
Expand(Backref(binary ? kLazy[c] : c & 1 ? FALSE : TRUE));
|
|
Expand(VAR);
|
|
Expand(0);
|
|
} else {
|
|
Expand(ABS);
|
|
Expand(ABS);
|
|
Expand(VAR);
|
|
Expand(0);
|
|
}
|
|
}
|
|
|
|
void Put(void) {
|
|
int bit;
|
|
long newip;
|
|
if (!binary) {
|
|
co = '0' + (ip & 1);
|
|
fputc(co, stdout);
|
|
newip = 2;
|
|
} else if (mem[ip + 1] & 1) { // ip ∈ {6,13}
|
|
fputc(co, stdout);
|
|
newip = 2;
|
|
} else { // ip ∈ {20,21}
|
|
newip = 9; // (λ 0 (λ 0 wr1 wr0))
|
|
bit = ip & 1;
|
|
co = (co * 2) | bit;
|
|
}
|
|
if (ferror(stdout)) {
|
|
exit(55);
|
|
}
|
|
ip = newip;
|
|
}
|
|
|
|
void Bye(void) {
|
|
int rc = mem[ip + 2]; // (λ 0) [exitcode]
|
|
if (rc) Error(rc, "CONTINUATIONS EXHAUSTED");
|
|
if (postdump && !rc) Dump(0, end, stderr);
|
|
exit(0);
|
|
}
|
|
|
|
// pops continuation and pushes it to environment
|
|
void Abs(void) {
|
|
if (!contp) Bye();
|
|
struct Closure *t = contp;
|
|
contp = t->next;
|
|
t->next = envp;
|
|
envp = t;
|
|
++ip;
|
|
}
|
|
|
|
struct Closure *Alloc(void) {
|
|
struct Closure *t;
|
|
if (!(t = frep)) {
|
|
if (!(t = Calloc(1, sizeof(struct Closure)))) {
|
|
Error(6, "OUT OF HEAP");
|
|
}
|
|
}
|
|
frep = t->next;
|
|
t->refs = 1;
|
|
++heap;
|
|
return t;
|
|
}
|
|
|
|
// pushes continuation for argument
|
|
void App(void) {
|
|
int x = mem[ip + 1];
|
|
struct Closure *t = Alloc();
|
|
t->term = ip + 2 + x;
|
|
t->envp = t->term > 21 && t->term != end ? REF(envp) : &root;
|
|
t->next = contp;
|
|
contp = t;
|
|
ip += 2;
|
|
}
|
|
|
|
int LoadByte(int c) {
|
|
int i, r = end;
|
|
for (i = 7; i >= 0; --i) {
|
|
Expand(ABS);
|
|
Expand(APP);
|
|
Expand(i ? +4 : Backref(NIL));
|
|
Expand(APP);
|
|
Expand(Backref(c & (1 << i) ? FALSE : TRUE));
|
|
Expand(VAR);
|
|
Expand(0);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void LoadRom(void) {
|
|
long i;
|
|
for (; end < sizeof(kRom) / sizeof(*kRom); ++end) {
|
|
mem[end] = kRom[end];
|
|
}
|
|
mem[4] = binary ? 2 : 9;
|
|
if (binary) {
|
|
for (i = 0; i < 256; ++i) {
|
|
kLazy[i] = LoadByte(i);
|
|
}
|
|
}
|
|
mem[1] = end - 2;
|
|
}
|
|
|
|
void Iop(void) {
|
|
if (ip == end) {
|
|
Gro();
|
|
} else {
|
|
Put(); // ip ∈ {6,13,20,21}
|
|
}
|
|
Gc(envp);
|
|
envp = &root;
|
|
}
|
|
|
|
static void Rex(void) {
|
|
if (slog) PrintMachineState(stderr);
|
|
if (rlog && (alog || mem[ip] != APP)) {
|
|
PrintExpressions(stderr, alog, vlog);
|
|
}
|
|
switch (mem[ip]) {
|
|
case VAR:
|
|
Var();
|
|
break;
|
|
case APP:
|
|
App();
|
|
break;
|
|
case ABS:
|
|
Abs();
|
|
break;
|
|
case IOP:
|
|
Iop();
|
|
break;
|
|
default:
|
|
Error(7, "CORRUPT TERM");
|
|
}
|
|
}
|
|
|
|
void Krivine(void) {
|
|
int main;
|
|
long gotoget;
|
|
LoadRom();
|
|
mem[end++] = APP;
|
|
gotoget = end++;
|
|
main = end;
|
|
mem[gotoget] = Parse(1, stdin).n;
|
|
if (rlog) {
|
|
Print(main, 1, 0, stderr);
|
|
fputs("\n", stderr);
|
|
if (alog) {
|
|
fputs("⟿ wrap[", stderr);
|
|
Print(0, 1, 0, stderr);
|
|
fputs("]\n", stderr);
|
|
}
|
|
}
|
|
for (;;) Rex();
|
|
}
|
|
|
|
void LoadFlags(int argc, char *argv[]) {
|
|
int i;
|
|
const char *prog;
|
|
prog = argc ? argv[0] : "cblc";
|
|
while ((i = getopt(argc, argv, "?hubBdsarvnNlS")) != -1) {
|
|
switch (i) {
|
|
case 'b':
|
|
binary = 1;
|
|
break;
|
|
case 'S':
|
|
safer = 1;
|
|
break;
|
|
case 'n':
|
|
noname = 1;
|
|
break;
|
|
case 'N':
|
|
asciiname = 1;
|
|
break;
|
|
case 'B':
|
|
style = 2;
|
|
break;
|
|
case 'l':
|
|
style = 1;
|
|
break;
|
|
case 's':
|
|
slog = 1;
|
|
break;
|
|
case 'r':
|
|
rlog = 1;
|
|
break;
|
|
case 'a':
|
|
rlog = 1;
|
|
alog = 1;
|
|
break;
|
|
case 'v':
|
|
rlog = 1;
|
|
vlog = 1;
|
|
break;
|
|
case 'd':
|
|
postdump = 1;
|
|
break;
|
|
case '?':
|
|
case 'h':
|
|
PrintUsage(prog, 0, stdout);
|
|
default:
|
|
PrintUsage(prog, 1, stderr);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
struct rlimit rlim = {512 * 1024 * 1024, 512 * 1024 * 1024};
|
|
setrlimit(RLIMIT_AS, &rlim);
|
|
signal(SIGQUIT, Quit);
|
|
signal(SIGPIPE, Quit);
|
|
LoadFlags(argc, argv);
|
|
setlocale(LC_ALL, "");
|
|
setvbuf(stdout, 0, _IOLBF, 0);
|
|
setvbuf(stderr, 0, _IOLBF, 0);
|
|
Krivine();
|
|
}
|