0% found this document useful (0 votes)
33 views

Lisp C

The document describes the implementation of a Lisp interpreter written in C. It defines macros, functions, and data structures used to represent Lisp objects in memory and implement operations like evaluation, printing, garbage collection, and tracing. Key aspects include using a 64-bit integer to represent a pair as a tagged union, with the car in the lower 32 bits and cdr in the upper 32.

Uploaded by

Karol Drożak
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views

Lisp C

The document describes the implementation of a Lisp interpreter written in C. It defines macros, functions, and data structures used to represent Lisp objects in memory and implement operations like evaluation, printing, garbage collection, and tracing. Key aspects include using a 64-bit integer to represent a pair as a tagged union, with the car in the lower 32 bits and cdr in the upper 32.

Uploaded by

Karol Drożak
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

lisp.

c Strona 1

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│


│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2021 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/errno.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"

#define Null 0200000


#define Set(i, x) M[(Null + i) & 0xffffffff] = x
#define Get(i) M[(Null + i) & 0xffffffff]
#define Car(i) (int)Get(i)
#define Cdr(i) (int)(Get(i) >> 32)
#define Key(i) Car(i + 1)
#define Val(i) Cdr(i + 1)
#define Make(h, t) ((unsigned)(h) | (unsigned long long)(t) << 32)
#define Alloc(x) (Set(--cx, x), cx)
#define Puts(fd, s) write(fd, s, strlen(s))
#define Ord(x) x

char ob[512];
jmp_buf undefined;
unsigned long long M[Null * 2];
int (*funcall)(), ax, cx, dx, tp, op, depth, fail;
int kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine, kMacro;
char tape[512] = "NIL T EQ CAR CDR ATOM COND CONS QUOTE MACRO DEFINE ";
short kTpEnc[25] = {0140001, 0140001, 0140001, 0140001, 0160002,
0160002, 0160002, 0160002, 0160002, 0170003,
0170003, 0170003, 0170003, 0170003, 0174004,
0174004, 0174004, 0174004, 0174004, 0176005,
0176005, 0176005, 0176005, 0176005, 0176005};

static inline Head(x) {


if (x < 0) {
return Car(x);
} else if (!x) {
return +0;
} else {
Throw2(kCar, x);
}
}

static inline Tail(x) {


if (x < 0) {
return Cdr(x);
} else if (!x) {
return -0;
} else {
Throw2(kCdr, x);
}
}

static inline Eval(e, a) {


return Apply(e, a, 0);
}
lisp.c Strona 2

static inline Peel(x, a) {


return a && x == Key(a) ? Cdr(a) : a;
}

Cons(x, y) {
if (cx > -Null) {
return Alloc(Make(x, y));
} else {
Throw(kCons);
}
}

Throw2(x, y) {
Throw(List(x, y));
}

Throw(x) {
if (fail < 255) ++fail;
longjmp(undefined, ~x);
}

Alist(x, y, a) {
return Cons(Cons(x, y), a);
}

List(x, y) {
return Cons(x, Cons(y, -0));
}

Bsr(x, i) {
return x ? Bsr(x >> 1, i + 1) : i;
}

Probe(h, p) {
return (h + p * p) & (Null - 1);
}

Hash(h, x) {
return ((h + x) * 60611 + 20485) & (Null - 1);
}

Intern(x, y, h, p) {
if (x == Car(h) && y == Cdr(h)) return h;
if (Car(h)) return Intern(x, y, Probe(h, p), p + 1);
Set(h, Make(x, y));
return h;
}

ReadAtom() {
int x, y;
ax = y = 0;
do x = ReadChar();
while (x <= Ord(' '));
if (x > Ord(')') && dx > Ord(')')) y = ReadAtom();
return Intern(x, y, (ax = Hash(x, ax)), 1);
}

ReadList() {
int x;
if ((x = Read()) > 0) {
if (Car(x) == Ord(')')) return -0;
if (Car(x) == Ord('.') && !Cdr(x)) {
x = Read();
ReadList();
return x;
}
}
return Cons(x, ReadList());
lisp.c Strona 3

ReadChar() {
int b, a = dx;
dx = ReadByte();
if (a >= 0300) {
for (b = 0200; a & b; b >>= 1) {
a ^= b;
}
while ((dx & 0300) == 0200) {
a <<= 6;
a |= dx & 0177;
dx = ReadByte();
}
}
return towupper(a);
}

ReadByte() {
int n;
if (!tape[tp]) {
n = read(0, tape, sizeof(tape) - 1);
if (n <= 0) exit(fail);
tape[n] = 0;
tp = 0;
}
return tape[tp++] & 255;
}

Flush() {
int n, i = 0;
while (i < op) {
if ((n = write(1, ob + i, op - i)) > 0) {
i += n;
} else if (errno != EINTR) {
exit(errno);
}
}
op = 0;
}

PrintChar(c) {
int e, i, n;
if (op + 6 > sizeof(ob)) Flush();
if (c < 0200) {
ob[op++] = c;
if (c == Ord('\n')) Flush();
} else {
e = kTpEnc[Bsr(c, -1) - 7];
i = n = e & 255;
do ob[op + i--] = 0200 | (c & 077);
while (c >>= 6, i);
ob[op] = c | e >> 8;
op += n + 1;
}
}

PrintAtom(x) {
do PrintChar(Car(x));
while ((x = Cdr(x)));
}

PrintList(x) {
PrintChar(Ord('('));
if (x < 0) {
Print(Car(x));
while ((x = Cdr(x))) {
if (x < 0) {
PrintChar(Ord(' '));
lisp.c Strona 4

Print(Car(x));
} else {
PrintDot();
Print(x);
break;
}
}
}
PrintChar(Ord(')'));
}

Print(x) {
if (1. / x < 0) {
PrintList(x);
} else {
PrintAtom(x);
}
}

PrintDot() {
PrintChar(Ord(' '));
PrintChar(Ord('.'));
PrintChar(Ord(' '));
}

Newline() {
PrintChar(Ord('\r'));
PrintChar(Ord('\n'));
}

Read() {
int t = ReadAtom();
if (Car(t) != Ord('(')) return t;
return ReadList();
}

Gc(A, x) {
int C, B = cx;
x = Copy(x, A, A - B), C = cx;
while (C < B) Set(--A, Get(--B));
return cx = A, x;
}

Copy(x, m, k) {
int l, r;
if (x < m) {
l = Car(x);
r = Cdr(x);
r = Copy(r, m, k);
l = Copy(l, m, k);
return Cons(l, r) + k;
} else {
return x;
}
}

Evlis(x, a) {
if (x) {
return Cons(Eval(Head(x), a), Evlis(Tail(x), a));
} else {
return x;
}
}

Pairlis(x, y, a) {
if (x < 0) {
return Alist(Car(x), Head(y), Pairlis(Cdr(x), Tail(y), Peel(Car(x), a)));
} else if (!x) {
return a;
lisp.c Strona 5

} else {
return Alist(x, y, Peel(x, a));
}
}

Bind(x, y, a, u) {
if (x < 0) {
return Alist(Car(x), Eval(Head(y), u),
Bind(Cdr(x), Tail(y), Peel(Car(x), a), u));
} else if (!x) {
return a;
} else {
return Alist(x, Evlis(y, u), Peel(x, a));
}
}

Assoc(x, y) {
int k, v, t;
for (; y < 0; y = t) {
t = Cdr(y);
k = Key(y);
v = Val(y);
if (x == k) {
return v;
}
}
Throw(x);
}

Evcon(c, a, t) {
int x, d, y, e;
for (; c; c = d) {
if (c < 0) {
x = Car(c);
d = Cdr(c);
if (x < 0) {
y = Car(x);
e = Cdr(x);
if (Eval(y, a)) {
return Apply(Head(e), a, t);
}
} else {
Throw2(kCond, c);
}
} else {
Throw2(kCond, c);
}
}
return c;
}

Apply(e, a, t) {
int f, x, l, p, b, u, A;
if (!e) return e;
if (e > 0) return t ? e : Assoc(e, a);
x = Cdr(e);
f = Car(e);
if (f == kCond) return Evcon(x, a, t);
if (t) return e;
if (f > 0) {
u = Head(x);
t = Tail(x);
if (f == kQuote) return u;
if (f == kCons) return Cons(Eval(u, a), Eval(Head(t), a));
if (f == kEq) return Eval(u, a) == Eval(Head(t), a);
if (f == kAtom) return Eval(u, a) >= 0;
if (f == kCar) return Head(Eval(u, a));
if (f == kCdr) return Tail(Eval(u, a));
l = Assoc(f, a);
lisp.c Strona 6

} else {
l = f;
}
u = a;
A = cx;
p = Head(Tail(l));
b = Head(Tail(Tail(l)));
if (Head(l) == kMacro) {
x = funcall(b, Pairlis(p, x, a), 0, a, f, p);
} else {
for (;;) {
u = Bind(p, x, a, u);
x = funcall(b, u, f, a, f, p);
if (x < 0 && Car(x) == f) {
x = Gc(A, Cons(u, Cdr(x)));
u = Car(x);
x = Cdr(x);
} else {
break;
}
}
}
return Gc(A, x ? funcall(x, u, 0, a, f, p) : x);
}

Trace(b, u, t, a, f, p) {
int i, y;
if (f > 0) {
Indent(depth);
PrintChar(Ord('('));
Print(f);
for (i = u; p < 0; i = Cdr(i), p = Cdr(p)) {
PrintChar(Ord(' '));
Print(Car(p));
PrintChar(Ord('='));
Print(Cdr(Car(i)));
}
if (p > 0) {
PrintDot();
Print(p);
PrintChar(Ord('='));
Print(Car(i));
}
PrintChar(Ord(')'));
Newline();
depth += 2;
}
y = Apply(b, u, t);
if (f > 0) {
depth -= 2;
Indent(depth);
Print(f);
PrintChar(Ord(' '));
PrintChar(0x2192);
PrintChar(Ord(' '));
Print(y);
Newline();
}
return y;
}

Indent(i) {
if (i) {
PrintChar(Ord(' '));
Indent(i - 1);
}
}

Setup() {
lisp.c Strona 7

ReadAtom();
ReadAtom();
kEq = ReadAtom();
kCar = ReadAtom();
kCdr = ReadAtom();
kAtom = ReadAtom();
kCond = ReadAtom();
kCons = ReadAtom();
kQuote = ReadAtom();
kMacro = ReadAtom();
kDefine = ReadAtom();
}

Remove(x, y) {
if (!y) return y;
if (x == Key(y)) return Cdr(y);
return Cons(Car(y), Remove(x, Cdr(y)));
}

Define(x, a) {
return Cons(x, Remove(Car(x), a));
}

main(int argc, char *argv[]) {


int x, a, A;
funcall = Apply;
for (x = 1; x < argc; ++x) {
if (argv[x][0] == Ord('-') && argv[x][1] == Ord('t')) {
funcall = Trace;
} else {
Puts(2, "Usage: lisp.com [-t] <input.lisp >output.lisp\n");
exit(1);
}
}
Setup();
for (a = 0;;) {
A = cx;
if (!(x = setjmp(undefined))) {
x = Read();
if (x < 0 && Car(x) == kDefine) {
a = Gc(0, Define(Tail(x), a));
continue;
}
x = Eval(x, a);
} else {
x = ~x;
PrintChar(Ord('?'));
}
Print(x);
Newline();
Gc(A, 0);
}
}

You might also like