From 2a00af296b3e8aff8092652b0350451c4f0be316 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 24 Nov 2021 10:03:42 -0800 Subject: [PATCH 01/29] Create friendly version --- lisp.c | 30 ++++++++++++++++++-------- lisp.lisp | 12 +++++++++++ sectorlisp.S | 59 ++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/lisp.c b/lisp.c index 0f9ab1e..5ce6c63 100644 --- a/lisp.c +++ b/lisp.c @@ -25,6 +25,7 @@ #include #include #include +#include #endif /*───────────────────────────────────────────────────────────────────────────│─╗ @@ -46,6 +47,7 @@ int cx; /* stores negative memory use */ int dx; /* stores lookahead character */ int RAM[0100000]; /* your own ibm7090 */ +jmp_buf undefined; Intern() { int i, j, x; @@ -157,10 +159,12 @@ Print(e) { ╚────────────────────────────────────────────────────────────────────────────│*/ Car(x) { + if (x >= 0) longjmp(undefined, x); return M[x]; } Cdr(x) { + if (x >= 0) longjmp(undefined, x); return M[x + 1]; } @@ -186,7 +190,7 @@ Pairlis(x, y, a) { } Assoc(x, y) { - if (!y) return 0; + if (y >= 0) longjmp(undefined, x); if (x == Car(Car(y))) return Cdr(Car(y)); return Assoc(x, Cdr(y)); } @@ -207,14 +211,14 @@ Apply(f, x, a) { if (f == kAtom) return Car(x) < 0 ? 0 : kT; if (f == kCar) return Car(Car(x)); if (f == kCdr) return Cdr(Car(x)); + longjmp(undefined, f); } Eval(e, a) { int A, B, C; - if (e >= 0) - return Assoc(e, a); - if (Car(e) == kQuote) - return Car(Cdr(e)); + if (!e) return 0; + if (e > 0) return Assoc(e, a); + if (Car(e) == kQuote) return Car(Cdr(e)); A = cx; if (Car(e) == kCond) { e = Evcon(Cdr(e), a); @@ -235,12 +239,20 @@ Eval(e, a) { ╚────────────────────────────────────────────────────────────────────────────│*/ main() { - int i; + int x, a = 0; setlocale(LC_ALL, ""); bestlineSetXlatCallback(bestlineUppercase); - for(i = 0; i < sizeof(S); ++i) M[i] = S[i]; + for(x = 0; x < sizeof(S); ++x) M[x] = S[x]; for (;;) { - cx = 0; - Print(Eval(Read(), 0)); + if (!(x = setjmp(undefined))) { + x = Eval(Read(), a); + if (x < 0) { + a = Cons(x, a); + } + } else { + if (x == 1) x = 0; + PrintChar('?'); + } + Print(x); } } diff --git a/lisp.lisp b/lisp.lisp index 25a57ab..c7fc750 100644 --- a/lisp.lisp +++ b/lisp.lisp @@ -120,3 +120,15 @@ NIL ((EQ (CAR E) (QUOTE COND)) (EVCON (CDR E) A)) ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))) ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))))) + +(CONS (QUOTE NOT) + (QUOTE (LAMBDA (X) + (COND (X (QUOTE F)) + ((QUOTE T) (QUOTE T)))))) + +((LAMBDA (X E C) + (CONS (QUOTE LAMBDA) (CONS NIL (CONS (CAR (CDR C)) NIL)))) + (QUOTE T) + (QUOTE (LAMBDA (F) (F))) + (QUOTE (COND (X (QUOTE F)) + ((QUOTE T) (QUOTE T))))) diff --git a/sectorlisp.S b/sectorlisp.S index 9fe50a2..2965775 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -23,6 +23,8 @@ // Compatible with the original hardware .code16 + .set save,-10 + .set look,start+2 .globl _start _start: .asciz "NIL" # dec %si ; dec %cx ; dec %sp kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0 @@ -36,37 +38,53 @@ kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters kEq: .asciz "EQ" # needs to be last -begin: push %cs # that means ss = ds = es = cs +begin: mov $2,%bx + mov $0x8000,%cx +main: cli + push %cs # that means ss = ds = es = cs pop %ds # noting ljmp set cs to 0x7c00 push %cs # that's the bios load address pop %es # therefore NULL points to NUL push %cs # terminated NIL string above! pop %ss # errata exists but don't care xor %sp,%sp # use highest address as stack - mov $2,%bx -main: mov $0x8000,%cx # dl (g_look) is zero or cr + sti call GetToken call GetObject + mov %dx,save call Eval - xchg %ax,%si + test %ax,%ax + jns Print + push %ax + xchg %ax,%di + xchg %dx,%ax + call Cons + xchg %ax,%dx + pop %ax +Print: xchg %ax,%si call PrintObject mov $'\r',%al call PutChar jmp main -GetToken: # GetToken():al, dl is g_look +GetToken: # GetToken():al mov %cx,%di -1: mov %dl,%al +1: mov look,%al cmp $' ',%al jbe 2f stosb xchg %ax,%si 2: call GetChar # exchanges dx and ax + cmp $'\b',%al + jne 4f + dec %di + jmp 2b +4: xchg %ax,look cmp $' ',%al jbe 1b cmp $')',%al jbe 3f - cmp $')',%dl # dl = g_look + cmpb $')',look ja 1b 3: mov %bh,(%di) # bh is zero xchg %si,%ax @@ -123,14 +141,21 @@ Intern: push %cx # Intern(cx,di): ax jmp 1b 2: rep movsb # memcpy(di,si,cx) 9: pop %cx - ret +3: ret + +Undef: push %ax + mov $'?',%al + call PutChar + pop %ax + mov save,%dx + jmp Print GetChar:xor %ax,%ax # GetChar→al:dl int $0x16 # get keystroke PutChar:mov $0x0e,%ah # prints CP-437 int $0x10 # vidya service cmp $'\r',%al # don't clobber - jne 1f # look xchg ret + jne 3b # look xchg ret mov $'\n',%al jmp PutChar @@ -183,6 +208,15 @@ Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax add %dx,%ax ret +Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax +1: test %si,%si + jns Undef + mov (%si),%di + mov (%bx,%si),%si + scasw + jne 1b + jmp Car + GetList:call GetToken cmp $')',%al je .retF @@ -234,13 +268,6 @@ Cdr: scasw # increments our data index by 2 Car: mov (%di),%ax # contents of address register!! 2: ret -Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax -1: mov (%si),%di - mov (%bx,%si),%si - scasw - jne 1b - jmp Car - 1: mov (%bx,%di),%di # di = Cdr(c) Evcon: push %di # save c mov (%di),%si # di = Car(c) From 626f71b9a3cc9146ba8ef963206b1b2bb39ffa59 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 27 Nov 2021 20:38:00 -0800 Subject: [PATCH 02/29] Experiment with friendlier branch --- Makefile | 3 +- lisp.c | 255 +++++++++++++++++++++++++-------------------------- sectorlisp.S | 28 +++--- 3 files changed, 140 insertions(+), 146 deletions(-) diff --git a/Makefile b/Makefile index d53571a..500b434 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ -CFLAGS = -w -Os -LDFLAGS = -s +CFLAGS = -w -g CLEANFILES = \ lisp \ diff --git a/lisp.c b/lisp.c index 5ce6c63..f0e92cd 100644 --- a/lisp.c +++ b/lisp.c @@ -28,121 +28,78 @@ #include #endif -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § LISP Machine ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define kT 4 -#define kQuote 6 -#define kCond 12 -#define kAtom 17 -#define kCar 22 -#define kCdr 26 -#define kCons 30 -#define kEq 35 - -#define M (RAM + sizeof(RAM) / sizeof(RAM[0]) / 2) -#define S "NIL\0T\0QUOTE\0COND\0ATOM\0CAR\0CDR\0CONS\0EQ" - -int cx; /* stores negative memory use */ -int dx; /* stores lookahead character */ -int RAM[0100000]; /* your own ibm7090 */ jmp_buf undefined; +int cx, dx, M[0100000]; +int Null = sizeof(M) / sizeof(M[0]) / 2; +char *line = "NIL T CAR CDR ATOM COND CONS QUOTE EQ "; +int kT, kEq, kNil, kCar, kCdr, kCond, kAtom, kCons, kQuote; -Intern() { - int i, j, x; - for (i = 0; (x = M[i++]);) { - for (j = 0;; ++j) { - if (x != RAM[j]) break; - if (!x) return i - j - 1; - x = M[i++]; - } - while (x) - x = M[i++]; - } - j = 0; - x = --i; - while ((M[i++] = RAM[j++])); - return x; +Get(i) { + return M[Null + i]; } -GetChar() { - int c, t; - static char *l, *p; - if (l || (l = p = bestlineWithHistory("* ", "sectorlisp"))) { - if (*p) { - c = *p++ & 255; - } else { - free(l); - l = p = 0; - c = '\n'; - } - t = dx; - dx = c; - return t; - } else { - PrintChar('\n'); - exit(0); - } +Set(i, x) { + M[Null + i] = x; } -PrintChar(b) { - fputwc(b, stdout); +Read() { + return ReadObject(ReadAtom(0)); } -GetToken() { - int c, i = 0; - do if ((c = GetChar()) > ' ') RAM[i++] = c; - while (c <= ' ' || (c > ')' && dx > ')')); - RAM[i] = 0; - return c; +Intern(x, y, i) { + if (x == Get(i) && y == Get(i + 1)) return i; + if (Get(i)) return Intern(x, y, i + 2); + Set(i, x); + Set(i + 1, y); + return i; } -AddList(x) { - return Cons(x, GetList()); +ReadAtom(i) { + int c = ReadChar(); + if (c <= ' ') return ReadAtom(i); + return Intern(c, c > ')' && dx > ')' ? ReadAtom(0) : 0, i + c * 2); } -GetList() { - int c = GetToken(); - if (c == ')') return 0; - return AddList(GetObject(c)); +AddList(x) { + return Cons(x, ReadList()); } -GetObject(c) { - if (c == '(') return GetList(); - return Intern(); +ReadList() { + int t = ReadAtom(0); + if (Get(t) == ')') return kNil; + return AddList(ReadObject(t)); } -Read() { - return GetObject(GetToken()); +ReadObject(t) { + if (Get(t) != '(') return t; + return ReadList(); } PrintAtom(x) { - int c; - for (;;) { - if (!(c = M[x++])) break; - PrintChar(c); - } + do PrintChar(Get(x)); + while ((x = Get(x + 1))); } PrintList(x) { PrintChar('('); - PrintObject(Car(x)); - while ((x = Cdr(x))) { - if (x < 0) { - PrintChar(' '); - PrintObject(Car(x)); - } else { - PrintChar(L'∙'); - PrintObject(x); - break; + if (x < 0) { + PrintObject(Car(x)); + while ((x = Cdr(x)) != kNil) { + if (x < 0) { + PrintChar(' '); + PrintObject(Car(x)); + } else { + PrintChar(L'∙'); + PrintObject(x); + break; + } } } PrintChar(')'); } PrintObject(x) { - if (x < 0) { + if (1./x < 0) { PrintList(x); } else { PrintAtom(x); @@ -154,39 +111,49 @@ Print(e) { PrintChar('\n'); } -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - Car(x) { - if (x >= 0) longjmp(undefined, x); - return M[x]; + if (x < 0) { + return Get(x); + } else { + longjmp(undefined, x); + } } Cdr(x) { - if (x >= 0) longjmp(undefined, x); - return M[x + 1]; + if (x < 0) { + return Get(x + 1); + } else { + longjmp(undefined, x); + } } Cons(car, cdr) { - M[--cx] = cdr; - M[--cx] = car; + Set(--cx, cdr); + Set(--cx, car); return cx; } -Gc(x, m, k) { - return x < m ? Cons(Gc(Car(x), m, k), - Gc(Cdr(x), m, k)) + k : x; +Gc(A, x) { + int C, B = cx; + x = Copy(x, A, A - B), C = cx; + while (C < B) Set(--A, Get(--B)); + cx = A; + return x; +} + +Copy(x, m, k) { + return x < m ? Cons(Copy(Car(x), m, k), + Copy(Cdr(x), m, k)) + k : x; } Evlis(m, a) { - return m ? Cons(Eval(Car(m), a), - Evlis(Cdr(m), a)) : 0; + return m != kNil ? Cons(Eval(Car(m), a), + Evlis(Cdr(m), a)) : kNil; } Pairlis(x, y, a) { - return x ? Cons(Cons(Car(x), Car(y)), - Pairlis(Cdr(x), Cdr(y), a)) : a; + return x != kNil ? Cons(Cons(Car(x), Car(y)), + Pairlis(Cdr(x), Cdr(y), a)) : a; } Assoc(x, y) { @@ -196,63 +163,93 @@ Assoc(x, y) { } Evcon(c, a) { - if (Eval(Car(Car(c)), a)) { + if (Eval(Car(Car(c)), a) != kNil) { return Eval(Car(Cdr(Car(c))), a); - } else { + } else if (Cdr(c) != kNil) { return Evcon(Cdr(c), a); + } else { + longjmp(undefined, c); } } Apply(f, x, a) { if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a)); - if (f > kEq) return Apply(Eval(f, a), x, a); - if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0; + if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : kNil; if (f == kCons) return Cons(Car(x), Car(Cdr(x))); - if (f == kAtom) return Car(x) < 0 ? 0 : kT; + if (f == kAtom) return Car(x) < 0 ? kNil : kT; if (f == kCar) return Car(Car(x)); if (f == kCdr) return Cdr(Car(x)); - longjmp(undefined, f); + return Apply(Assoc(f, a), x, a); } Eval(e, a) { - int A, B, C; - if (!e) return 0; - if (e > 0) return Assoc(e, a); + int A = cx; + if (e == kNil) return kNil; + if (e >= 0) return Assoc(e, a); if (Car(e) == kQuote) return Car(Cdr(e)); - A = cx; if (Car(e) == kCond) { e = Evcon(Cdr(e), a); } else { e = Apply(Car(e), Evlis(Cdr(e), a), a); } - B = cx; - e = Gc(e, A, A - B); - C = cx; - while (C < B) - M[--A] = M[--B]; - cx = A; - return e; + return Gc(A, e); } -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § User Interface ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - main() { - int x, a = 0; + int x, a; setlocale(LC_ALL, ""); bestlineSetXlatCallback(bestlineUppercase); - for(x = 0; x < sizeof(S); ++x) M[x] = S[x]; - for (;;) { + kNil = ReadAtom(0); + kT = ReadAtom(0); + kCar = ReadAtom(0); + kCdr = ReadAtom(0); + kAtom = ReadAtom(0); + kCond = ReadAtom(0); + kCons = ReadAtom(0); + kQuote = ReadAtom(0); + kEq = ReadAtom(0); + for (a = kNil;;) { if (!(x = setjmp(undefined))) { - x = Eval(Read(), a); + x = Read(); + x = Eval(x, a); if (x < 0) { a = Cons(x, a); } } else { - if (x == 1) x = 0; PrintChar('?'); } Print(x); } } + +PrintChar(b) { + fputwc(b, stdout); +} + +ReadChar() { + int b, c, t; + static char *freeme; + if (line || (line = freeme = bestlineWithHistory("* ", "sectorlisp"))) { + if (*line) { + c = *line++ & 0377; + if (c >= 0300) { + for (b = 0200; c & b; b >>= 1) c ^= b; + while ((*line & 0300) == 0200) { + c <<= 6; + c |= *line++ & 0177; + } + } + } else { + free(freeme); + freeme = 0; + line = 0; + c = '\n'; + } + t = dx; + dx = c; + return t; + } else { + PrintChar('\n'); + exit(0); + } +} diff --git a/sectorlisp.S b/sectorlisp.S index 2965775..a604f19 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -23,12 +23,13 @@ // Compatible with the original hardware .code16 - .set save,-10 - .set look,start+2 + .set save,-2-2 + .set look,start+5-2 .globl _start _start: .asciz "NIL" # dec %si ; dec %cx ; dec %sp kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0 -start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address +start: mov $0x8000,%sp # this should be safe we hope + ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address .asciz "" kQuote: .asciz "QUOTE" kCond: .asciz "COND" @@ -38,20 +39,17 @@ kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters kEq: .asciz "EQ" # needs to be last -begin: mov $2,%bx - mov $0x8000,%cx -main: cli - push %cs # that means ss = ds = es = cs +begin: push %cs # that means ss = ds = es = cs pop %ds # noting ljmp set cs to 0x7c00 push %cs # that's the bios load address pop %es # therefore NULL points to NUL push %cs # terminated NIL string above! pop %ss # errata exists but don't care - xor %sp,%sp # use highest address as stack - sti - call GetToken + mov $2,%bx + mov %sp,%cx +main: call GetToken call GetObject - mov %dx,save + mov %dx,save(%bx) call Eval test %ax,%ax jns Print @@ -69,7 +67,7 @@ Print: xchg %ax,%si GetToken: # GetToken():al mov %cx,%di -1: mov look,%al +1: mov look(%bx),%al cmp $' ',%al jbe 2f stosb @@ -79,12 +77,12 @@ GetToken: # GetToken():al jne 4f dec %di jmp 2b -4: xchg %ax,look +4: xchg %ax,look(%bx) cmp $' ',%al jbe 1b cmp $')',%al jbe 3f - cmpb $')',look + cmpb $')',look(%bx) ja 1b 3: mov %bh,(%di) # bh is zero xchg %si,%ax @@ -147,7 +145,7 @@ Undef: push %ax mov $'?',%al call PutChar pop %ax - mov save,%dx + mov save(%bx),%dx jmp Print GetChar:xor %ax,%ax # GetChar→al:dl From 51d469be880a0b24db40488baf49fbf585023a6d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 29 Nov 2021 02:52:33 -0800 Subject: [PATCH 03/29] Make interning better --- lisp.c | 100 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/lisp.c b/lisp.c index f0e92cd..8fe1c11 100644 --- a/lisp.c +++ b/lisp.c @@ -28,25 +28,27 @@ #include #endif +#define Null 0100000 + jmp_buf undefined; -int cx, dx, M[0100000]; -int Null = sizeof(M) / sizeof(M[0]) / 2; +int cx, dx, M[Null * 2]; +int kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote; char *line = "NIL T CAR CDR ATOM COND CONS QUOTE EQ "; -int kT, kEq, kNil, kCar, kCdr, kCond, kAtom, kCons, kQuote; - -Get(i) { - return M[Null + i]; -} Set(i, x) { M[Null + i] = x; } -Read() { - return ReadObject(ReadAtom(0)); +Get(i) { + return M[Null + i]; +} + +Hash(h, c) { + return h + c * 2; } Intern(x, y, i) { + i &= Null - 1; if (x == Get(i) && y == Get(i + 1)) return i; if (Get(i)) return Intern(x, y, i + 2); Set(i, x); @@ -54,10 +56,17 @@ Intern(x, y, i) { return i; } -ReadAtom(i) { +ReadAtom(h) { int c = ReadChar(); - if (c <= ' ') return ReadAtom(i); - return Intern(c, c > ')' && dx > ')' ? ReadAtom(0) : 0, i + c * 2); + if (c <= 32) return ReadAtom(h); + return Intern(c, c > 41 && dx > 41 ? + ReadAtom(Hash(h, c)) : 0, + Hash(h, c) - Hash(0, 78)); +} + +PrintAtom(x) { + do PrintChar(Get(x)); + while ((x = Get(x + 1))); } AddList(x) { @@ -66,36 +75,31 @@ AddList(x) { ReadList() { int t = ReadAtom(0); - if (Get(t) == ')') return kNil; + if (Get(t) == 41) return 0; return AddList(ReadObject(t)); } ReadObject(t) { - if (Get(t) != '(') return t; + if (Get(t) != 40) return t; return ReadList(); } -PrintAtom(x) { - do PrintChar(Get(x)); - while ((x = Get(x + 1))); -} - PrintList(x) { - PrintChar('('); + PrintChar(40); if (x < 0) { PrintObject(Car(x)); - while ((x = Cdr(x)) != kNil) { + while ((x = Cdr(x))) { if (x < 0) { - PrintChar(' '); + PrintChar(32); PrintObject(Car(x)); } else { - PrintChar(L'∙'); + PrintChar(8729); PrintObject(x); break; } } } - PrintChar(')'); + PrintChar(41); } PrintObject(x) { @@ -108,7 +112,11 @@ PrintObject(x) { Print(e) { PrintObject(e); - PrintChar('\n'); + PrintChar(10); +} + +Read() { + return ReadObject(ReadAtom(0)); } Car(x) { @@ -142,18 +150,18 @@ Gc(A, x) { } Copy(x, m, k) { - return x < m ? Cons(Copy(Car(x), m, k), + return x < m ? Cons(Copy(Car(x), m, k), Copy(Cdr(x), m, k)) + k : x; } Evlis(m, a) { - return m != kNil ? Cons(Eval(Car(m), a), - Evlis(Cdr(m), a)) : kNil; + return m ? Cons(Eval(Car(m), a), + Evlis(Cdr(m), a)) : 0; } Pairlis(x, y, a) { - return x != kNil ? Cons(Cons(Car(x), Car(y)), - Pairlis(Cdr(x), Cdr(y), a)) : a; + return x ? Cons(Cons(Car(x), Car(y)), + Pairlis(Cdr(x), Cdr(y), a)) : a; } Assoc(x, y) { @@ -163,9 +171,9 @@ Assoc(x, y) { } Evcon(c, a) { - if (Eval(Car(Car(c)), a) != kNil) { + if (Eval(Car(Car(c)), a)) { return Eval(Car(Cdr(Car(c))), a); - } else if (Cdr(c) != kNil) { + } else if (Cdr(c)) { return Evcon(Cdr(c), a); } else { longjmp(undefined, c); @@ -174,9 +182,9 @@ Evcon(c, a) { Apply(f, x, a) { if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a)); - if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : kNil; + if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0; if (f == kCons) return Cons(Car(x), Car(Cdr(x))); - if (f == kAtom) return Car(x) < 0 ? kNil : kT; + if (f == kAtom) return Car(x) < 0 ? 0 : kT; if (f == kCar) return Car(Car(x)); if (f == kCdr) return Cdr(Car(x)); return Apply(Assoc(f, a), x, a); @@ -184,8 +192,8 @@ Apply(f, x, a) { Eval(e, a) { int A = cx; - if (e == kNil) return kNil; - if (e >= 0) return Assoc(e, a); + if (!e) return 0; + if (e > 0) return Assoc(e, a); if (Car(e) == kQuote) return Car(Cdr(e)); if (Car(e) == kCond) { e = Evcon(Cdr(e), a); @@ -195,11 +203,9 @@ Eval(e, a) { return Gc(A, e); } -main() { +Lisp() { int x, a; - setlocale(LC_ALL, ""); - bestlineSetXlatCallback(bestlineUppercase); - kNil = ReadAtom(0); + ReadAtom(0); kT = ReadAtom(0); kCar = ReadAtom(0); kCdr = ReadAtom(0); @@ -208,7 +214,7 @@ main() { kCons = ReadAtom(0); kQuote = ReadAtom(0); kEq = ReadAtom(0); - for (a = kNil;;) { + for (a = 0;;) { if (!(x = setjmp(undefined))) { x = Read(); x = Eval(x, a); @@ -216,7 +222,7 @@ main() { a = Cons(x, a); } } else { - PrintChar('?'); + PrintChar(63); } Print(x); } @@ -243,13 +249,19 @@ ReadChar() { free(freeme); freeme = 0; line = 0; - c = '\n'; + c = 10; } t = dx; dx = c; return t; } else { - PrintChar('\n'); + PrintChar(10); exit(0); } } + +main() { + setlocale(LC_ALL, ""); + bestlineSetXlatCallback(bestlineUppercase); + Lisp(); +} From 512b1a5b87a1ce77e1cfc8cfbca66bccedb950d0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 29 Nov 2021 07:17:57 -0800 Subject: [PATCH 04/29] Make bestline collapse one-liners --- bestline.c | 32 ++++++++++++++------ lisp.c | 85 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/bestline.c b/bestline.c index 23ffd64..a9a32bd 100644 --- a/bestline.c +++ b/bestline.c @@ -1799,7 +1799,6 @@ static int enableRawMode(int fd) { raw = orig_termios; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - raw.c_oflag &= ~OPOST; raw.c_iflag |= IUTF8; raw.c_cflag |= CS8; raw.c_cc[VMIN] = 1; @@ -2464,6 +2463,7 @@ static void bestlineRefreshLineForce(struct bestlineState *l) { static void bestlineEditInsert(struct bestlineState *l, const char *p, size_t n) { if (!bestlineGrow(l, l->len + n + 1)) return; + if (*p == ' ' && l->pos && l->buf[l->pos - 1] == ' ') return; memmove(l->buf + l->pos + n, l->buf + l->pos, l->len - l->pos); memcpy(l->buf + l->pos, p, n); l->pos += n; @@ -3010,6 +3010,16 @@ static void bestlineEditSlurp(struct bestlineState *l) { } static void bestlineEditRaise(struct bestlineState *l) { + (void)l; +} + +static char IsBalanced(struct bestlineState *l) { + int i, d; + for (d = i = 0; i < l->len; ++i) { + if (l->buf[i] == '(') ++d; + if (l->buf[i] == ')') --d; + } + return d <= 0; } /** @@ -3112,14 +3122,18 @@ static ssize_t bestlineEdit(int stdin_fd, int stdout_fd, const char *prompt, } break; case '\r': - l.final = 1; - free(history[--historylen]); - history[historylen] = 0; - bestlineEditEnd(&l); - bestlineRefreshLineForce(&l); - if ((p = (char *)realloc(l.buf, l.len + 1))) l.buf = p; - *obuf = l.buf; - return l.len; + if (IsBalanced(&l)) { + l.final = 1; + free(history[--historylen]); + history[historylen] = 0; + bestlineEditEnd(&l); + bestlineRefreshLineForce(&l); + if ((p = (char *)realloc(l.buf, l.len + 1))) l.buf = p; + *obuf = l.buf; + return l.len; + } else { + break; + } case 033: if (nread < 2) break; switch (seq[1]) { diff --git a/lisp.c b/lisp.c index 8fe1c11..bc85972 100644 --- a/lisp.c +++ b/lisp.c @@ -19,35 +19,33 @@ #include "bestline.h" #ifndef __COSMOPOLITAN__ -#include #include -#include -#include #include -#include #include #endif +#define var int +#define function #define Null 0100000 +var M[Null * 2]; jmp_buf undefined; -int cx, dx, M[Null * 2]; -int kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote; -char *line = "NIL T CAR CDR ATOM COND CONS QUOTE EQ "; -Set(i, x) { +var cx, dx, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote; + +function Set(i, x) { M[Null + i] = x; } -Get(i) { +function Get(i) { return M[Null + i]; } -Hash(h, c) { +function Hash(h, c) { return h + c * 2; } -Intern(x, y, i) { +function Intern(x, y, i) { i &= Null - 1; if (x == Get(i) && y == Get(i + 1)) return i; if (Get(i)) return Intern(x, y, i + 2); @@ -56,35 +54,35 @@ Intern(x, y, i) { return i; } -ReadAtom(h) { - int c = ReadChar(); +function ReadAtom(h) { + var c = ReadChar(); if (c <= 32) return ReadAtom(h); return Intern(c, c > 41 && dx > 41 ? ReadAtom(Hash(h, c)) : 0, Hash(h, c) - Hash(0, 78)); } -PrintAtom(x) { +function PrintAtom(x) { do PrintChar(Get(x)); while ((x = Get(x + 1))); } -AddList(x) { +function AddList(x) { return Cons(x, ReadList()); } -ReadList() { - int t = ReadAtom(0); +function ReadList() { + var t = ReadAtom(0); if (Get(t) == 41) return 0; return AddList(ReadObject(t)); } -ReadObject(t) { +function ReadObject(t) { if (Get(t) != 40) return t; return ReadList(); } -PrintList(x) { +function PrintList(x) { PrintChar(40); if (x < 0) { PrintObject(Car(x)); @@ -102,7 +100,7 @@ PrintList(x) { PrintChar(41); } -PrintObject(x) { +function PrintObject(x) { if (1./x < 0) { PrintList(x); } else { @@ -110,77 +108,77 @@ PrintObject(x) { } } -Print(e) { +function Print(e) { PrintObject(e); PrintChar(10); } -Read() { +function Read() { return ReadObject(ReadAtom(0)); } -Car(x) { +function Car(x) { if (x < 0) { return Get(x); } else { - longjmp(undefined, x); + Throw(x); } } -Cdr(x) { +function Cdr(x) { if (x < 0) { return Get(x + 1); } else { - longjmp(undefined, x); + Throw(x); } } -Cons(car, cdr) { +function Cons(car, cdr) { Set(--cx, cdr); Set(--cx, car); return cx; } -Gc(A, x) { - int C, B = cx; +function Gc(A, x) { + var C, B = cx; x = Copy(x, A, A - B), C = cx; while (C < B) Set(--A, Get(--B)); cx = A; return x; } -Copy(x, m, k) { +function Copy(x, m, k) { return x < m ? Cons(Copy(Car(x), m, k), Copy(Cdr(x), m, k)) + k : x; } -Evlis(m, a) { +function Evlis(m, a) { return m ? Cons(Eval(Car(m), a), Evlis(Cdr(m), a)) : 0; } -Pairlis(x, y, a) { +function Pairlis(x, y, a) { return x ? Cons(Cons(Car(x), Car(y)), Pairlis(Cdr(x), Cdr(y), a)) : a; } -Assoc(x, y) { - if (y >= 0) longjmp(undefined, x); +function Assoc(x, y) { + if (y >= 0) Throw(x); if (x == Car(Car(y))) return Cdr(Car(y)); return Assoc(x, Cdr(y)); } -Evcon(c, a) { +function Evcon(c, a) { if (Eval(Car(Car(c)), a)) { return Eval(Car(Cdr(Car(c))), a); } else if (Cdr(c)) { return Evcon(Cdr(c), a); } else { - longjmp(undefined, c); + Throw(c); } } -Apply(f, x, a) { +function Apply(f, x, a) { if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a)); if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0; if (f == kCons) return Cons(Car(x), Car(Cdr(x))); @@ -190,8 +188,8 @@ Apply(f, x, a) { return Apply(Assoc(f, a), x, a); } -Eval(e, a) { - int A = cx; +function Eval(e, a) { + var A = cx; if (!e) return 0; if (e > 0) return Assoc(e, a); if (Car(e) == kQuote) return Car(Cdr(e)); @@ -203,8 +201,8 @@ Eval(e, a) { return Gc(A, e); } -Lisp() { - int x, a; +function Lisp() { + var x, a; ReadAtom(0); kT = ReadAtom(0); kCar = ReadAtom(0); @@ -228,6 +226,10 @@ Lisp() { } } +Throw(x) { + longjmp(undefined, x); +} + PrintChar(b) { fputwc(b, stdout); } @@ -235,6 +237,7 @@ PrintChar(b) { ReadChar() { int b, c, t; static char *freeme; + static char *line = "NIL T CAR CDR ATOM COND CONS QUOTE EQ "; if (line || (line = freeme = bestlineWithHistory("* ", "sectorlisp"))) { if (*line) { c = *line++ & 0377; From 0f6b147099305cf716720a544d6a1bb3564e7256 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 29 Nov 2021 10:05:05 -0800 Subject: [PATCH 05/29] Make it friendlier --- lisp.c | 30 +++++++++++++++++------------- lisp.lisp | 32 ++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/lisp.c b/lisp.c index bc85972..3fbdd95 100644 --- a/lisp.c +++ b/lisp.c @@ -56,10 +56,10 @@ function Intern(x, y, i) { function ReadAtom(h) { var c = ReadChar(); - if (c <= 32) return ReadAtom(h); - return Intern(c, c > 41 && dx > 41 ? + if (c <= Ord(' ')) return ReadAtom(h); + return Intern(c, c > Ord(')') && dx > Ord(')') ? ReadAtom(Hash(h, c)) : 0, - Hash(h, c) - Hash(0, 78)); + Hash(h, c) - Hash(0, Ord('N'))); } function PrintAtom(x) { @@ -73,31 +73,31 @@ function AddList(x) { function ReadList() { var t = ReadAtom(0); - if (Get(t) == 41) return 0; + if (Get(t) == Ord(')')) return -0; return AddList(ReadObject(t)); } function ReadObject(t) { - if (Get(t) != 40) return t; + if (Get(t) != Ord('(')) return t; return ReadList(); } function PrintList(x) { - PrintChar(40); + PrintChar(Ord('(')); if (x < 0) { PrintObject(Car(x)); while ((x = Cdr(x))) { if (x < 0) { - PrintChar(32); + PrintChar(Ord(' ')); PrintObject(Car(x)); } else { - PrintChar(8729); + PrintChar(0x2219); PrintObject(x); break; } } } - PrintChar(41); + PrintChar(Ord(')')); } function PrintObject(x) { @@ -110,7 +110,7 @@ function PrintObject(x) { function Print(e) { PrintObject(e); - PrintChar(10); + PrintChar(Ord('\n')); } function Read() { @@ -154,7 +154,7 @@ function Copy(x, m, k) { function Evlis(m, a) { return m ? Cons(Eval(Car(m), a), - Evlis(Cdr(m), a)) : 0; + Evlis(Cdr(m), a)) : m; } function Pairlis(x, y, a) { @@ -226,6 +226,10 @@ function Lisp() { } } +Ord(c) { + return c; +} + Throw(x) { longjmp(undefined, x); } @@ -252,13 +256,13 @@ ReadChar() { free(freeme); freeme = 0; line = 0; - c = 10; + c = Ord('\n'); } t = dx; dx = c; return t; } else { - PrintChar(10); + PrintChar(Ord('\n')); exit(0); } } diff --git a/lisp.lisp b/lisp.lisp index c7fc750..2554631 100644 --- a/lisp.lisp +++ b/lisp.lisp @@ -61,7 +61,8 @@ NIL ;; FIND FIRST ATOM IN TREE ;; CORRECT RESULT OF EXPRESSION IS `A` ;; RECURSIVE CONDITIONAL FUNCTION BINDING -((LAMBDA (FF X) (FF X)) +((LAMBDA (FF X) + (FF X)) (QUOTE (LAMBDA (X) (COND ((ATOM X) X) ((QUOTE T) (FF (CAR X)))))) @@ -76,28 +77,27 @@ NIL ;; NOTE: ((EQ (CAR E) ()) (QUOTE *UNDEFINED)) CAN HELP ;; NOTE: ((EQ (CAR E) (QUOTE LAMBDA)) E) IS NICE ((LAMBDA (ASSOC EVCON PAIRLIS EVLIS APPLY EVAL) - (EVAL (QUOTE ((LAMBDA (FF X) (FF X)) - (QUOTE (LAMBDA (X) - (COND ((ATOM X) X) - ((QUOTE T) (FF (CAR X)))))) + (EVAL (QUOTE ((LAMBDA (FF X) + (FF X)) + (LAMBDA (X) + (COND ((ATOM X) X) + (T (FF (CAR X))))) (QUOTE ((A) B C)))) - ())) + NIL)) (QUOTE (LAMBDA (X Y) - (COND ((EQ Y ()) ()) - ((EQ X (CAR (CAR Y))) - (CDR (CAR Y))) - ((QUOTE T) - (ASSOC X (CDR Y)))))) + (COND ((EQ Y NIL) (QUOTE *UNDEFINED)) + ((EQ X (CAR (CAR Y))) (CDR (CAR Y))) + ((QUOTE T) (ASSOC X (CDR Y)))))) (QUOTE (LAMBDA (C A) (COND ((EVAL (CAR (CAR C)) A) (EVAL (CAR (CDR (CAR C))) A)) ((QUOTE T) (EVCON (CDR C) A))))) (QUOTE (LAMBDA (X Y A) - (COND ((EQ X ()) A) + (COND ((EQ X NIL) A) ((QUOTE T) (CONS (CONS (CAR X) (CAR Y)) (PAIRLIS (CDR X) (CDR Y) A)))))) (QUOTE (LAMBDA (M A) - (COND ((EQ M ()) ()) + (COND ((EQ M NIL) M) ((QUOTE T) (CONS (EVAL (CAR M) A) (EVLIS (CDR M) A)))))) (QUOTE (LAMBDA (FN X A) @@ -114,10 +114,14 @@ NIL (PAIRLIS (CAR (CDR FN)) X A)))))) (QUOTE (LAMBDA (E A) (COND - ((ATOM E) (ASSOC E A)) + ((ATOM E) + (COND ((EQ E NIL) E) + ((EQ E (QUOTE T)) (QUOTE T)) + ((QUOTE T) (ASSOC E A)))) ((ATOM (CAR E)) (COND ((EQ (CAR E) (QUOTE QUOTE)) (CAR (CDR E))) ((EQ (CAR E) (QUOTE COND)) (EVCON (CDR E) A)) + ((EQ (CAR E) (QUOTE LAMBDA)) E) ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))) ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))))) From 14873babc7d8f052628e3b1ca837d23d4251cd4a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 30 Nov 2021 13:29:54 -0800 Subject: [PATCH 06/29] Update code --- Makefile | 5 +- lisp.c | 274 --------------------------------------------------- lisp.lisp | 117 +++++++++++----------- sectorlisp.S | 114 +++++++++++---------- 4 files changed, 125 insertions(+), 385 deletions(-) delete mode 100644 lisp.c diff --git a/Makefile b/Makefile index 500b434..4080d03 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ all: lisp \ clean:; $(RM) lisp lisp.o bestline.o sectorlisp.o sectorlisp.bin sectorlisp.bin.dbg lisp: lisp.o bestline.o -lisp.o: lisp.c bestline.h +lisp.o: lisp.js bestline.h bestline.o: bestline.c bestline.h sectorlisp.o: sectorlisp.S @@ -28,3 +28,6 @@ sectorlisp.bin.dbg: sectorlisp.o sectorlisp.bin: sectorlisp.bin.dbg objcopy -S -O binary sectorlisp.bin.dbg sectorlisp.bin + +%.o: %.js + $(COMPILE.c) -xc $(OUTPUT_OPTION) $< diff --git a/lisp.c b/lisp.c deleted file mode 100644 index 3fbdd95..0000000 --- a/lisp.c +++ /dev/null @@ -1,274 +0,0 @@ -/*-*- 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 2020 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 "bestline.h" - -#ifndef __COSMOPOLITAN__ -#include -#include -#include -#endif - -#define var int -#define function -#define Null 0100000 - -var M[Null * 2]; -jmp_buf undefined; - -var cx, dx, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote; - -function Set(i, x) { - M[Null + i] = x; -} - -function Get(i) { - return M[Null + i]; -} - -function Hash(h, c) { - return h + c * 2; -} - -function Intern(x, y, i) { - i &= Null - 1; - if (x == Get(i) && y == Get(i + 1)) return i; - if (Get(i)) return Intern(x, y, i + 2); - Set(i, x); - Set(i + 1, y); - return i; -} - -function ReadAtom(h) { - var c = ReadChar(); - if (c <= Ord(' ')) return ReadAtom(h); - return Intern(c, c > Ord(')') && dx > Ord(')') ? - ReadAtom(Hash(h, c)) : 0, - Hash(h, c) - Hash(0, Ord('N'))); -} - -function PrintAtom(x) { - do PrintChar(Get(x)); - while ((x = Get(x + 1))); -} - -function AddList(x) { - return Cons(x, ReadList()); -} - -function ReadList() { - var t = ReadAtom(0); - if (Get(t) == Ord(')')) return -0; - return AddList(ReadObject(t)); -} - -function ReadObject(t) { - if (Get(t) != Ord('(')) return t; - return ReadList(); -} - -function PrintList(x) { - PrintChar(Ord('(')); - if (x < 0) { - PrintObject(Car(x)); - while ((x = Cdr(x))) { - if (x < 0) { - PrintChar(Ord(' ')); - PrintObject(Car(x)); - } else { - PrintChar(0x2219); - PrintObject(x); - break; - } - } - } - PrintChar(Ord(')')); -} - -function PrintObject(x) { - if (1./x < 0) { - PrintList(x); - } else { - PrintAtom(x); - } -} - -function Print(e) { - PrintObject(e); - PrintChar(Ord('\n')); -} - -function Read() { - return ReadObject(ReadAtom(0)); -} - -function Car(x) { - if (x < 0) { - return Get(x); - } else { - Throw(x); - } -} - -function Cdr(x) { - if (x < 0) { - return Get(x + 1); - } else { - Throw(x); - } -} - -function Cons(car, cdr) { - Set(--cx, cdr); - Set(--cx, car); - return cx; -} - -function Gc(A, x) { - var C, B = cx; - x = Copy(x, A, A - B), C = cx; - while (C < B) Set(--A, Get(--B)); - cx = A; - return x; -} - -function Copy(x, m, k) { - return x < m ? Cons(Copy(Car(x), m, k), - Copy(Cdr(x), m, k)) + k : x; -} - -function Evlis(m, a) { - return m ? Cons(Eval(Car(m), a), - Evlis(Cdr(m), a)) : m; -} - -function Pairlis(x, y, a) { - return x ? Cons(Cons(Car(x), Car(y)), - Pairlis(Cdr(x), Cdr(y), a)) : a; -} - -function Assoc(x, y) { - if (y >= 0) Throw(x); - if (x == Car(Car(y))) return Cdr(Car(y)); - return Assoc(x, Cdr(y)); -} - -function Evcon(c, a) { - if (Eval(Car(Car(c)), a)) { - return Eval(Car(Cdr(Car(c))), a); - } else if (Cdr(c)) { - return Evcon(Cdr(c), a); - } else { - Throw(c); - } -} - -function Apply(f, x, a) { - if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a)); - if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0; - if (f == kCons) return Cons(Car(x), Car(Cdr(x))); - if (f == kAtom) return Car(x) < 0 ? 0 : kT; - if (f == kCar) return Car(Car(x)); - if (f == kCdr) return Cdr(Car(x)); - return Apply(Assoc(f, a), x, a); -} - -function Eval(e, a) { - var A = cx; - if (!e) return 0; - if (e > 0) return Assoc(e, a); - if (Car(e) == kQuote) return Car(Cdr(e)); - if (Car(e) == kCond) { - e = Evcon(Cdr(e), a); - } else { - e = Apply(Car(e), Evlis(Cdr(e), a), a); - } - return Gc(A, e); -} - -function Lisp() { - var x, a; - ReadAtom(0); - kT = ReadAtom(0); - kCar = ReadAtom(0); - kCdr = ReadAtom(0); - kAtom = ReadAtom(0); - kCond = ReadAtom(0); - kCons = ReadAtom(0); - kQuote = ReadAtom(0); - kEq = ReadAtom(0); - for (a = 0;;) { - if (!(x = setjmp(undefined))) { - x = Read(); - x = Eval(x, a); - if (x < 0) { - a = Cons(x, a); - } - } else { - PrintChar(63); - } - Print(x); - } -} - -Ord(c) { - return c; -} - -Throw(x) { - longjmp(undefined, x); -} - -PrintChar(b) { - fputwc(b, stdout); -} - -ReadChar() { - int b, c, t; - static char *freeme; - static char *line = "NIL T CAR CDR ATOM COND CONS QUOTE EQ "; - if (line || (line = freeme = bestlineWithHistory("* ", "sectorlisp"))) { - if (*line) { - c = *line++ & 0377; - if (c >= 0300) { - for (b = 0200; c & b; b >>= 1) c ^= b; - while ((*line & 0300) == 0200) { - c <<= 6; - c |= *line++ & 0177; - } - } - } else { - free(freeme); - freeme = 0; - line = 0; - c = Ord('\n'); - } - t = dx; - dx = c; - return t; - } else { - PrintChar(Ord('\n')); - exit(0); - } -} - -main() { - setlocale(LC_ALL, ""); - bestlineSetXlatCallback(bestlineUppercase); - Lisp(); -} diff --git a/lisp.lisp b/lisp.lisp index 2554631..ebe8c72 100644 --- a/lisp.lisp +++ b/lisp.lisp @@ -69,70 +69,69 @@ NIL (QUOTE ((A) B C))) ;; LISP IMPLEMENTED IN LISP -;; WITHOUT ANY SUBJECTIVE SYNTACTIC SUGAR ;; RUNS "FIND FIRST ATOM IN TREE" PROGRAM ;; CORRECT RESULT OF EXPRESSION IS STILL `A` ;; REQUIRES CONS CAR CDR QUOTE ATOM EQ LAMBDA COND ;; SIMPLIFIED BUG FIXED VERSION OF JOHN MCCARTHY PAPER ;; NOTE: ((EQ (CAR E) ()) (QUOTE *UNDEFINED)) CAN HELP ;; NOTE: ((EQ (CAR E) (QUOTE LAMBDA)) E) IS NICE -((LAMBDA (ASSOC EVCON PAIRLIS EVLIS APPLY EVAL) - (EVAL (QUOTE ((LAMBDA (FF X) - (FF X)) - (LAMBDA (X) - (COND ((ATOM X) X) - (T (FF (CAR X))))) - (QUOTE ((A) B C)))) - NIL)) - (QUOTE (LAMBDA (X Y) - (COND ((EQ Y NIL) (QUOTE *UNDEFINED)) - ((EQ X (CAR (CAR Y))) (CDR (CAR Y))) - ((QUOTE T) (ASSOC X (CDR Y)))))) - (QUOTE (LAMBDA (C A) - (COND ((EVAL (CAR (CAR C)) A) - (EVAL (CAR (CDR (CAR C))) A)) - ((QUOTE T) (EVCON (CDR C) A))))) - (QUOTE (LAMBDA (X Y A) - (COND ((EQ X NIL) A) - ((QUOTE T) (CONS (CONS (CAR X) (CAR Y)) - (PAIRLIS (CDR X) (CDR Y) A)))))) - (QUOTE (LAMBDA (M A) - (COND ((EQ M NIL) M) - ((QUOTE T) (CONS (EVAL (CAR M) A) - (EVLIS (CDR M) A)))))) - (QUOTE (LAMBDA (FN X A) - (COND - ((ATOM FN) - (COND ((EQ FN (QUOTE CAR)) (CAR (CAR X))) - ((EQ FN (QUOTE CDR)) (CDR (CAR X))) - ((EQ FN (QUOTE ATOM)) (ATOM (CAR X))) - ((EQ FN (QUOTE CONS)) (CONS (CAR X) (CAR (CDR X)))) - ((EQ FN (QUOTE EQ)) (EQ (CAR X) (CAR (CDR X)))) - ((QUOTE T) (APPLY (EVAL FN A) X A)))) - ((EQ (CAR FN) (QUOTE LAMBDA)) - (EVAL (CAR (CDR (CDR FN))) - (PAIRLIS (CAR (CDR FN)) X A)))))) - (QUOTE (LAMBDA (E A) - (COND - ((ATOM E) - (COND ((EQ E NIL) E) - ((EQ E (QUOTE T)) (QUOTE T)) - ((QUOTE T) (ASSOC E A)))) - ((ATOM (CAR E)) - (COND ((EQ (CAR E) (QUOTE QUOTE)) (CAR (CDR E))) - ((EQ (CAR E) (QUOTE COND)) (EVCON (CDR E) A)) - ((EQ (CAR E) (QUOTE LAMBDA)) E) - ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))) - ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))))) -(CONS (QUOTE NOT) - (QUOTE (LAMBDA (X) - (COND (X (QUOTE F)) - ((QUOTE T) (QUOTE T)))))) +DEFINE ASSOC +(LAMBDA (X Y) + (COND ((EQ Y NIL) (QUOTE *UNDEFINED)) + ((EQ X (CAR (CAR Y))) (CDR (CAR Y))) + ((QUOTE T) (ASSOC X (CDR Y))))) -((LAMBDA (X E C) - (CONS (QUOTE LAMBDA) (CONS NIL (CONS (CAR (CDR C)) NIL)))) - (QUOTE T) - (QUOTE (LAMBDA (F) (F))) - (QUOTE (COND (X (QUOTE F)) - ((QUOTE T) (QUOTE T))))) +DEFINE EVCON +(LAMBDA (C A) + (COND ((EVAL (CAR (CAR C)) A) + (EVAL (CAR (CDR (CAR C))) A)) + ((QUOTE T) (EVCON (CDR C) A)))) + +DEFINE PAIRLIS +(LAMBDA (X Y A) + (COND ((EQ X NIL) A) + ((QUOTE T) (CONS (CONS (CAR X) (CAR Y)) + (PAIRLIS (CDR X) (CDR Y) A))))) + +DEFINE EVLIS +(LAMBDA (M A) + (COND ((EQ M NIL) M) + ((QUOTE T) (CONS (EVAL (CAR M) A) + (EVLIS (CDR M) A))))) + +DEFINE APPLY +(LAMBDA (FN X A) + (COND + ((ATOM FN) + (COND ((EQ FN (QUOTE CAR)) (CAR (CAR X))) + ((EQ FN (QUOTE CDR)) (CDR (CAR X))) + ((EQ FN (QUOTE ATOM)) (ATOM (CAR X))) + ((EQ FN (QUOTE CONS)) (CONS (CAR X) (CAR (CDR X)))) + ((EQ FN (QUOTE EQ)) (EQ (CAR X) (CAR (CDR X)))) + ((QUOTE T) (APPLY (EVAL FN A) X A)))) + ((EQ (CAR FN) (QUOTE LAMBDA)) + (EVAL (CAR (CDR (CDR FN))) + (PAIRLIS (CAR (CDR FN)) X A))))) + +DEFINE EVAL +(LAMBDA (E A) + (COND + ((ATOM E) + (COND ((EQ E NIL) E) + ((EQ E (QUOTE T)) (QUOTE T)) + ((QUOTE T) (ASSOC E A)))) + ((ATOM (CAR E)) + (COND ((EQ (CAR E) (QUOTE QUOTE)) (CAR (CDR E))) + ((EQ (CAR E) (QUOTE COND)) (EVCON (CDR E) A)) + ((EQ (CAR E) (QUOTE LAMBDA)) E) + ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))) + ((QUOTE T) (APPLY (CAR E) (EVLIS (CDR E) A) A)))) + +(EVAL (QUOTE ((LAMBDA (FF X) + (FF X)) + (LAMBDA (X) + (COND ((ATOM X) X) + (T (FF (CAR X))))) + (QUOTE ((A) B C)))) + NIL) diff --git a/sectorlisp.S b/sectorlisp.S index a604f19..ca497c6 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -23,14 +23,13 @@ // Compatible with the original hardware .code16 - .set save,-2-2 - .set look,start+5-2 - .globl _start + .set a,-2-2 + .globl _start # LISP: VERITAS NUMQUAM PERIT _start: .asciz "NIL" # dec %si ; dec %cx ; dec %sp kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0 -start: mov $0x8000,%sp # this should be safe we hope - ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address +start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address .asciz "" +kDefine:.asciz "DEFINE" kQuote: .asciz "QUOTE" kCond: .asciz "COND" kAtom: .asciz "ATOM" # ordering matters @@ -39,27 +38,38 @@ kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters kEq: .asciz "EQ" # needs to be last -begin: push %cs # that means ss = ds = es = cs - pop %ds # noting ljmp set cs to 0x7c00 - push %cs # that's the bios load address - pop %es # therefore NULL points to NUL - push %cs # terminated NIL string above! - pop %ss # errata exists but don't care - mov $2,%bx - mov %sp,%cx -main: call GetToken +Read: call GetToken call GetObject - mov %dx,save(%bx) - call Eval - test %ax,%ax - jns Print + ret + +Define: call Read push %ax + call Read + pop %di + call Cons xchg %ax,%di - xchg %dx,%ax + xchg %bp,%ax call Cons - xchg %ax,%dx - pop %ax -Print: xchg %ax,%si + xchg %ax,%bp + jmp main + +begin: mov $0x8000,%sp + push %cs + pop %ds + push %cs + pop %es + push %cs + pop %ss + mov $2,%bx + mov %sp,%cx + xor %bp,%bp +main: xor %dx,%dx + call Read + cmp $kDefine,%ax + je Define + mov %bp,%dx + call Eval +Catch: xchg %ax,%si call PrintObject mov $'\r',%al call PutChar @@ -67,26 +77,25 @@ Print: xchg %ax,%si GetToken: # GetToken():al mov %cx,%di -1: mov look(%bx),%al +1: mov %dl,%al cmp $' ',%al jbe 2f stosb xchg %ax,%si 2: call GetChar # exchanges dx and ax cmp $'\b',%al - jne 4f - dec %di - jmp 2b -4: xchg %ax,look(%bx) + je 4f cmp $' ',%al jbe 1b cmp $')',%al jbe 3f - cmpb $')',look(%bx) + cmp $')',%dl ja 1b 3: mov %bh,(%di) # bh is zero xchg %si,%ax ret +4: dec %di + jmp 2b .PrintList: mov $'(',%al @@ -106,7 +115,7 @@ GetToken: # GetToken():al .PutObject: # .PutObject(c:al,x:si) .PrintString: # nul-terminated in si call PutChar # preserves si -PrintObject: # PrintObject(x:si) +PrintObject: # PrintObject(x:si,a:di) test %si,%si # set sf=1 if cons js .PrintList # jump if not cons .PrintAtom: @@ -121,39 +130,42 @@ GetObject: # called just after GetToken # jmp Intern Intern: push %cx # Intern(cx,di): ax - mov %di,%bp - sub %cx,%bp - inc %bp + sub %cx,%di + inc %di + push %di xor %di,%di -1: pop %si +1: pop %cx + pop %si push %si - mov %bp,%cx + push %cx mov %di,%ax cmp %bh,(%di) - je 2f + je 8f rep cmpsb # memcmp(di,si,cx) je 9f - not %cx xor %ax,%ax - repne scasb # memchr(di,al,cx) +2: scasb + jne 2b jmp 1b -2: rep movsb # memcpy(di,si,cx) +8: rep movsb # memcpy(di,si,cx) 9: pop %cx -3: ret + pop %cx + ret Undef: push %ax mov $'?',%al call PutChar pop %ax - mov save(%bx),%dx - jmp Print + jmp Catch GetChar:xor %ax,%ax # GetChar→al:dl int $0x16 # get keystroke PutChar:mov $0x0e,%ah # prints CP-437 + push %bp # scroll up bug int $0x10 # vidya service + pop %bp # scroll up bug cmp $'\r',%al # don't clobber - jne 3b # look xchg ret + jne 1f # look xchg ret mov $'\n',%al jmp PutChar @@ -213,7 +225,12 @@ Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax mov (%bx,%si),%si scasw jne 1b - jmp Car + .byte 0xf6 +Cadr: mov (%bx,%di),%di # contents of decrement register + .byte 0x3C # cmp §scasw,%al (nop next byte) +Cdr: scasw # increments our data index by 2 +Car: mov (%di),%ax # contents of address register!! +2: ret GetList:call GetToken cmp $')',%al @@ -255,17 +272,11 @@ Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax mov (%bx,%si),%si # si = Cdr(x) lodsw # si = Cadr(x) je Cons -.isEq: cmp %di,%ax # we know for certain it's eq +.isEq: xor %di,%ax # we know for certain it's eq jne .retF -.retT: mov $kT,%ax +.retT: mov $kT,%al ret -Cadr: mov (%bx,%di),%di # contents of decrement register - .byte 0x3C # cmp §scasw,%al (nop next byte) -Cdr: scasw # increments our data index by 2 -Car: mov (%di),%ax # contents of address register!! -2: ret - 1: mov (%bx,%di),%di # di = Cdr(c) Evcon: push %di # save c mov (%di),%si # di = Car(c) @@ -309,6 +320,7 @@ Eval: test %ax,%ax # Eval(e:ax,a:dx):ax .sig: .fill 510 - (. - _start), 1, 0xce .word 0xAA55 .type .sig,@object + .type kDefine,@object .type kQuote,@object .type kCond,@object .type kAtom,@object From 59c904fb9589d6e9a8adfa3ea0a7a7be55051bd1 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 1 Dec 2021 07:25:22 -0800 Subject: [PATCH 07/29] Add JavaScript --- lisp.js | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++ sectorlisp.S | 2 +- 2 files changed, 404 insertions(+), 1 deletion(-) create mode 100755 lisp.js diff --git a/lisp.js b/lisp.js new file mode 100755 index 0000000..9eb026e --- /dev/null +++ b/lisp.js @@ -0,0 +1,403 @@ +/*bin/echo '#-*- indent-tabs-mode:nil;js-indent-level:2;coding:utf-8 -*- + + SectorLISP v2.o (ISC License) + Copyright 2021 Justine Tunney + + This file implements SectorLISP as a C / JavaScript polyglot and + includes friendly branch features such as the undefined behavior + exceptions handlers, optimized interning, and global definitions + +(aset standard-display-table #x2029 [?¶]) ;; emacs protip '>/dev/null +curl -so bestline.c -z bestline.c https://justine.lol/sectorlisp2/bestline.c +curl -so bestline.h -z bestline.h https://justine.lol/sectorlisp2/bestline.h +[ lisp.js -nt lisp ] && cc -w -xc lisp.js bestline.c -o lisp +exec ./lisp "$@" +exit +*/ + +//
` +#include "bestline.h" +#ifndef __COSMOPOLITAN__ +#include +#include +#include +#endif +#define var int +#define function +#define Null 0100000 +var M[Null * 2]; +jmp_buf undefined; +//` + +var cx, dx, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; + +function Set(i, x) { + M[Null + i] = x; +} + +function Get(i) { + return M[Null + i]; +} + +function Car(x) { + if (x < 0) { + return Get(x); + } else { + Throw(x); + } +} + +function Cdr(x) { + if (x < 0) { + return Get(x + 1); + } else { + Throw(x + 1); + } +} + +function Cons(car, cdr) { + Set(--cx, cdr); + Set(--cx, car); + return cx; +} + +function Hash(h, c) { + return h + c * 2; +} + +function Intern(x, y, i) { + i &= Null - 1; + if (x == Get(i) && y == Get(i + 1)) return i; + if (Get(i)) return Intern(x, y, i + 2); + Set(i, x); + Set(i + 1, y); + return i; +} + +function ReadAtom(h) { + var c = ReadChar(); + if (c <= Ord(' ')) return ReadAtom(h); + return Intern(c, c > Ord(')') && dx > Ord(')') ? + ReadAtom(Hash(h, c)) : 0, + Hash(h, c) - Hash(0, Ord('N'))); +} + +function PrintAtom(x) { + do PrintChar(Get(x)); + while ((x = Get(x + 1))); +} + +function AddList(x) { + return Cons(x, ReadList()); +} + +function ReadList() { + var t = ReadAtom(0); + if (Get(t) == Ord(')')) return -0; + return AddList(ReadObject(t)); +} + +function ReadObject(t) { + if (Get(t) != Ord('(')) return t; + return ReadList(); +} + +function PrintList(x) { + PrintChar(Ord('(')); + if (x < 0) { + PrintObject(Car(x)); + while ((x = Cdr(x))) { + if (x < 0) { + PrintChar(Ord(' ')); + PrintObject(Car(x)); + } else { + PrintChar(0x2219); + PrintObject(x); + break; + } + } + } + PrintChar(Ord(')')); +} + +function PrintObject(x) { + if (1./x < 0) { + PrintList(x); + } else { + PrintAtom(x); + } +} + +function Print(e) { + PrintObject(e); + PrintChar(Ord('\n')); +} + +function Read() { + return ReadObject(ReadAtom(0)); +} + +function Define(a) { + var x = Read(); + return Cons(Cons(x, Read()), a); +} + +function Gc(A, x) { + var C, B = cx; + x = Copy(x, A, A - B), C = cx; + while (C < B) Set(--A, Get(--B)); + cx = A; + return x; +} + +function Copy(x, m, k) { + return x < m ? Cons(Copy(Car(x), m, k), + Copy(Cdr(x), m, k)) + k : x; +} + +function Evlis(m, a) { + return m ? Cons(Eval(Car(m), a), + Evlis(Cdr(m), a)) : 0; +} + +function Pairlis(x, y, a) { + return x ? Cons(Cons(Car(x), Car(y)), + Pairlis(Cdr(x), Cdr(y), a)) : a; +} + +function Assoc(x, y) { + if (y >= 0) Throw(x); + if (x == Car(Car(y))) return Cdr(Car(y)); + return Assoc(x, Cdr(y)); +} + +function Evcon(c, a) { + if (Eval(Car(Car(c)), a)) { + return Eval(Car(Cdr(Car(c))), a); + } else if (Cdr(c)) { + return Evcon(Cdr(c), a); + } else { + Throw(c); + } +} + +function Apply(f, x, a) { + if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a)); + if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0; + if (f == kCons) return Cons(Car(x), Car(Cdr(x))); + if (f == kAtom) return Car(x) < 0 ? 0 : kT; + if (f == kCar) return Car(Car(x)); + if (f == kCdr) return Cdr(Car(x)); + return Apply(Assoc(f, a), x, a); +} + +function Eval(e, a) { + var A = cx; + if (!e) return e; + if (e > 0) return Assoc(e, a); + if (Car(e) == kQuote) return Car(Cdr(e)); + if (Car(e) == kCond) { + e = Evcon(Cdr(e), a); + } else { + e = Apply(Car(e), Evlis(Cdr(e), a), a); + } + return Gc(A, e); +} + +function LoadBuiltins() { + ReadAtom(0); + kT = ReadAtom(0); + kEq = ReadAtom(0); + kCar = ReadAtom(0); + kCdr = ReadAtom(0); + kAtom = ReadAtom(0); + kCond = ReadAtom(0); + kCons = ReadAtom(0); + kQuote = ReadAtom(0); + kDefine = ReadAtom(0); +} + +//
` +//////////////////////////////////////////////////////////////////////////////// +// ANSI POSIX C Specific Code + +Ord(c) { + return c; +} + +Throw(x) { + longjmp(undefined, ~x); +} + +PrintChar(b) { + fputwc(b, stdout); +} + +ReadChar() { + int b, c, t; + static char *freeme; + static char *line = "NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE "; + if (line || (line = freeme = bestlineWithHistory("* ", "sectorlisp"))) { + if (*line) { + c = *line++ & 0377; + if (c >= 0300) { + for (b = 0200; c & b; b >>= 1) c ^= b; + while ((*line & 0300) == 0200) { + c <<= 6; + c |= *line++ & 0177; + } + } + } else { + free(freeme); + freeme = 0; + line = 0; + c = '\n'; + } + t = dx; + dx = c; + return t; + } else { + PrintChar('\n'); + exit(0); + } +} + +main() { + var x, a, A; + setlocale(LC_ALL, ""); + bestlineSetXlatCallback(bestlineUppercase); + LoadBuiltins(); + for (a = 0;;) { + A = cx; + if (!(x = setjmp(undefined))) { + x = Read(); + if (x == kDefine) { + a = Gc(A, Define(a)); + continue; + } + x = Eval(x, a); + } else { + x = ~x; + PrintChar('?'); + } + Print(x); + Gc(A, 0); + } +} + +#if 0 +//` +//////////////////////////////////////////////////////////////////////////////// +// JavaScript Specific Code for https://justine.lol/ + +var a, code, index, M, Null; +var eInput, eOutput, eSubmit, eClear, eLoad, ePrograms; + +function Throw(x) { + throw x; +} + +function Ord(s) { + return s.charCodeAt(0); +} + +function PrintChar(c) { + eOutput.innerText += String.fromCharCode(c); + SaveOutput(); +} + +function ReadChar() { + var ax; + if (code.length) { + ax = dx; + if (index < code.length) { + dx = code.charCodeAt(index++); + } else { + code = ""; + dx = 0; + } + return ax; + } else { + Throw(0); + } +} + +function Lisp() { + var x, A; + while (dx) { + if (dx <= Ord(' ')) { + ReadChar(); + } else { + A = cx; + try { + x = Read(); + if (x == kDefine) { + a = Gc(A, Define(a)); + continue; + } + x = Eval(x, a); + } catch (x) { + PrintChar(Ord('?')); + } + Print(x); + Gc(A, 0); + } + } +} + +function Load(s) { + code = s + "\n"; + dx = Ord(s); + index = 1; +} + +function OnSubmit() { + Load(eInput.value); + Lisp(); +} + +function OnClear() { + eOutput.innerText = ""; + SaveOutput(); +} + +function OnLoad() { + ePrograms.classList.toggle("show"); +} + +function OnWindowClick(event) { + if (!event.target.matches('#load')) { + ePrograms.classList.remove("show"); + } +} + +function SaveOutput() { + if (typeof localStorage != 'undefined') { + localStorage.setItem('output', eOutput.innerText); + } +} + +function SetUp() { + a = 0; + cx = 0; + Null = 0100000; + M = new Array(Null * 2); + Load("NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE "); + LoadBuiltins() + eLoad = document.getElementById('load'); + eInput = document.getElementById('input'); + eClear = document.getElementById('clear'); + eOutput = document.getElementById('output'); + eSubmit = document.getElementById('submit'); + ePrograms = document.getElementById("programs"); + window.onclick = OnWindowClick; + eSubmit.onclick = OnSubmit; + eClear.onclick = OnClear; + eLoad.onclick = OnLoad; +} + +SetUp(); + +//
` +#endif +//` diff --git a/sectorlisp.S b/sectorlisp.S index ca497c6..c2b2373 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -24,7 +24,7 @@ .code16 .set a,-2-2 - .globl _start # LISP: VERITAS NUMQUAM PERIT + .globl _start _start: .asciz "NIL" # dec %si ; dec %cx ; dec %sp kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0 start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address From a29df7bee245948b61dc715e99f904060bfae198 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 1 Dec 2021 10:13:55 -0800 Subject: [PATCH 08/29] Fix JS error messages --- lisp.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp.js b/lisp.js index 9eb026e..961165f 100755 --- a/lisp.js +++ b/lisp.js @@ -336,8 +336,9 @@ function Lisp() { continue; } x = Eval(x, a); - } catch (x) { + } catch (z) { PrintChar(Ord('?')); + x = z; } Print(x); Gc(A, 0); From da7ef2f3e760062c30f9c8975da3014546ec6867 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 2 Dec 2021 12:58:20 -0800 Subject: [PATCH 09/29] Add brainfuck implementation --- Makefile | 18 +++++++++++-- brainfuck.S | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 brainfuck.S diff --git a/Makefile b/Makefile index 4080d03..b1614c7 100644 --- a/Makefile +++ b/Makefile @@ -6,12 +6,17 @@ CLEANFILES = \ bestline.o \ sectorlisp.o \ sectorlisp.bin \ - sectorlisp.bin.dbg + sectorlisp.bin.dbg \ + brainfuck.o \ + brainfuck.bin \ + brainfuck.bin.dbg .PHONY: all all: lisp \ sectorlisp.bin \ - sectorlisp.bin.dbg + sectorlisp.bin.dbg \ + brainfuck.bin \ + brainfuck.bin.dbg .PHONY: clean clean:; $(RM) lisp lisp.o bestline.o sectorlisp.o sectorlisp.bin sectorlisp.bin.dbg @@ -29,5 +34,14 @@ sectorlisp.bin.dbg: sectorlisp.o sectorlisp.bin: sectorlisp.bin.dbg objcopy -S -O binary sectorlisp.bin.dbg sectorlisp.bin +brainfuck.o: brainfuck.S + $(AS) -g -o $@ $< + +brainfuck.bin.dbg: brainfuck.o + $(LD) -oformat:binary -Ttext=0x7c00 -o $@ $< + +brainfuck.bin: brainfuck.bin.dbg + objcopy -S -O binary brainfuck.bin.dbg brainfuck.bin + %.o: %.js $(COMPILE.c) -xc $(OUTPUT_OPTION) $< diff --git a/brainfuck.S b/brainfuck.S new file mode 100644 index 0000000..b451371 --- /dev/null +++ b/brainfuck.S @@ -0,0 +1,75 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ + +// compliant brainfuck in 87 bytes +// boots from bios on pc w/ 128kb+ + + .code16 + .globl _start +_start: mov $0x10000>>4,%di + push %di + pop %ds + push %di + pop %es + mov %si,%dx +Brain: xor %ax,%ax + cmp %dx,%si + jb Fuck + int $0x16 + mov %al,(%si) + inc %dx +Fuck: lodsb + cmp $'>',%al + je Right + cmp $'<',%al + je Left + cmp $'+',%al + je Inc + cmp $'-',%al + je Dec + cmp $'.',%al + je Put + cmp $',',%al + je Get + cmp $']',%al + je Loop + cmp $'[',%al + jne Brain +Do: push %si +Loop: pop %ax + cmpb %ah,(%di) + jz Brain + push %ax + xchg %ax,%si + jmp Brain +Inc: incb (%di) + .byte 0x3C +Left: dec %di + .byte 0x80 +Dec: decb (%di) + .byte 0x3C +Right: inc %di + .byte 0x84 +Put: mov $0x0e,%ah + mov (%di),%al + int $0x10 + jmp Brain +Get: int $0x16 + mov %al,(%di) + jmp Brain From 23789127091e3625d69052d74456a8c4f70bb250 Mon Sep 17 00:00:00 2001 From: Peter Ferrie Date: Thu, 2 Dec 2021 15:23:46 -0800 Subject: [PATCH 10/29] shave somne bytes --- brainfuck.S | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/brainfuck.S b/brainfuck.S index b451371..159a102 100644 --- a/brainfuck.S +++ b/brainfuck.S @@ -2,6 +2,7 @@ │vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ Some size optimisations by Peter Ferrie │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -17,7 +18,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -// compliant brainfuck in 87 bytes +// compliant brainfuck in 83 bytes // boots from bios on pc w/ 128kb+ .code16 @@ -35,21 +36,22 @@ Brain: xor %ax,%ax mov %al,(%si) inc %dx Fuck: lodsb + cbw cmp $'>',%al je Right cmp $'<',%al je Left - cmp $'+',%al + sub $'+',%al je Inc - cmp $'-',%al + dec %ax + je Get + dec %ax je Dec - cmp $'.',%al + dec %ax je Put - cmp $',',%al - je Get - cmp $']',%al + cmp $']'-'.',%al je Loop - cmp $'[',%al + cmp $'['-'.',%al jne Brain Do: push %si Loop: pop %ax @@ -59,8 +61,6 @@ Loop: pop %ax xchg %ax,%si jmp Brain Inc: incb (%di) - .byte 0x3C -Left: dec %di .byte 0x80 Dec: decb (%di) .byte 0x3C @@ -71,5 +71,6 @@ Put: mov $0x0e,%ah int $0x10 jmp Brain Get: int $0x16 - mov %al,(%di) + stosb +Left: dec %di jmp Brain From aa43e770c3a951bf8b5713c40d24fbf2372d41da Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 2 Dec 2021 15:52:58 -0800 Subject: [PATCH 11/29] Reduce Brainfuck to 82 bytes --- brainfuck.S | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/brainfuck.S b/brainfuck.S index 159a102..5188eec 100644 --- a/brainfuck.S +++ b/brainfuck.S @@ -18,7 +18,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -// compliant brainfuck in 83 bytes +// compliant brainfuck in 82 bytes // boots from bios on pc w/ 128kb+ .code16 @@ -37,30 +37,34 @@ Brain: xor %ax,%ax inc %dx Fuck: lodsb cbw - cmp $'>',%al - je Right - cmp $'<',%al - je Left sub $'+',%al - je Inc + jz Inc # + 43 dec %ax - je Get + jz Get # , 44 dec %ax - je Dec + jz Dec # - 45 dec %ax - je Put + jz Put # . 46 + cmp $'>'-'.',%al + je Right # > 60 14 + cmp $'<'-'.',%al + je Left # < 62 16 cmp $']'-'.',%al - je Loop + je Loop # ] 91 45 cmp $'['-'.',%al - jne Brain + jne Brain # [ 93 47 Do: push %si Loop: pop %ax cmpb %ah,(%di) jz Brain push %ax xchg %ax,%si - jmp Brain + .byte 0x80 Inc: incb (%di) + jmp Brain +Get: int $0x16 + stosb +Left: dec %di .byte 0x80 Dec: decb (%di) .byte 0x3C @@ -70,7 +74,3 @@ Put: mov $0x0e,%ah mov (%di),%al int $0x10 jmp Brain -Get: int $0x16 - stosb -Left: dec %di - jmp Brain From d7f5c287ebdd8794eac70eb60f543d4ce980266a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 6 Dec 2021 08:36:30 -0800 Subject: [PATCH 12/29] Fix bugs --- lisp.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lisp.js b/lisp.js index 961165f..bbef7e9 100755 --- a/lisp.js +++ b/lisp.js @@ -51,7 +51,7 @@ function Cdr(x) { if (x < 0) { return Get(x + 1); } else { - Throw(x + 1); + Throw(x); } } @@ -157,7 +157,7 @@ function Copy(x, m, k) { function Evlis(m, a) { return m ? Cons(Eval(Car(m), a), - Evlis(Cdr(m), a)) : 0; + Evlis(Cdr(m), a)) : m; } function Pairlis(x, y, a) { @@ -257,7 +257,6 @@ ReadChar() { dx = c; return t; } else { - PrintChar('\n'); exit(0); } } From 3aa7a47e150b676f17a3be5477086bd2d8c719fd Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 8 Dec 2021 11:54:45 -0800 Subject: [PATCH 13/29] Fix off by one --- sectorlisp.S | 1 + 1 file changed, 1 insertion(+) diff --git a/sectorlisp.S b/sectorlisp.S index c2b2373..f6d4ae9 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -144,6 +144,7 @@ Intern: push %cx # Intern(cx,di): ax rep cmpsb # memcmp(di,si,cx) je 9f xor %ax,%ax + dec %di 2: scasb jne 2b jmp 1b From d21e0ab712718b44df3849650b20e29c3d92fe77 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 8 Dec 2021 11:55:45 -0800 Subject: [PATCH 14/29] Add latest improvements --- lisp.js | 145 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 37 deletions(-) diff --git a/lisp.js b/lisp.js index bbef7e9..c01fd67 100755 --- a/lisp.js +++ b/lisp.js @@ -24,12 +24,12 @@ exit #endif #define var int #define function -#define Null 0100000 +#define Null 16384 var M[Null * 2]; jmp_buf undefined; //` -var cx, dx, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; +var cx, dx, lo, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; function Set(i, x) { M[Null + i] = x; @@ -58,6 +58,7 @@ function Cdr(x) { function Cons(car, cdr) { Set(--cx, cdr); Set(--cx, car); + if (cx < lo) lo = cx; return cx; } @@ -137,17 +138,21 @@ function Read() { return ReadObject(ReadAtom(0)); } -function Define(a) { - var x = Read(); - return Cons(Cons(x, Read()), a); +function Remove(x, y) { + if (!y) return y; + if (x == Car(Car(y))) return Cdr(y); + return Cons(Car(y), Remove(x, Cdr(y))); +} + +function Define(x, y) { + return Cons(Cons(x, Read()), Remove(x, y)); } function Gc(A, x) { var C, B = cx; x = Copy(x, A, A - B), C = cx; while (C < B) Set(--A, Get(--B)); - cx = A; - return x; + return cx = A, x; } function Copy(x, m, k) { @@ -196,12 +201,8 @@ function Eval(e, a) { if (!e) return e; if (e > 0) return Assoc(e, a); if (Car(e) == kQuote) return Car(Cdr(e)); - if (Car(e) == kCond) { - e = Evcon(Cdr(e), a); - } else { - e = Apply(Car(e), Evlis(Cdr(e), a), a); - } - return Gc(A, e); + if (Car(e) == kCond) return Gc(A, Evcon(Cdr(e), a)); + return Gc(A, Apply(Car(e), Evlis(Cdr(e), a), a)); } function LoadBuiltins() { @@ -233,6 +234,9 @@ PrintChar(b) { fputwc(b, stdout); } +SaveMachine(a) { +} + ReadChar() { int b, c, t; static char *freeme; @@ -271,7 +275,8 @@ main() { if (!(x = setjmp(undefined))) { x = Read(); if (x == kDefine) { - a = Gc(A, Define(a)); + a = Gc(0, Define(Read(), a)); + SaveMachine(a); continue; } x = Eval(x, a); @@ -289,8 +294,8 @@ main() { //////////////////////////////////////////////////////////////////////////////// // JavaScript Specific Code for https://justine.lol/ -var a, code, index, M, Null; -var eInput, eOutput, eSubmit, eClear, eLoad, ePrograms; +var a, code, index, output, M, Null; +var eInput, eOutput, eSubmit, eReset, eLoad, ePrograms; function Throw(x) { throw x; @@ -300,9 +305,22 @@ function Ord(s) { return s.charCodeAt(0); } +function Reset() { + var i; + a = 0; + cx = 0; + lo = 0; + Null = 16384; + M = new Array(Null * 2); + for (i = 0; i < M.length; ++i) { + M[i] = 0; /* make json smaller */ + } + Load("NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE "); + LoadBuiltins() +} + function PrintChar(c) { - eOutput.innerText += String.fromCharCode(c); - SaveOutput(); + output += String.fromCharCode(c); } function ReadChar() { @@ -323,6 +341,8 @@ function ReadChar() { function Lisp() { var x, A; + lo = cx; + output = ''; while (dx) { if (dx <= Ord(' ')) { ReadChar(); @@ -331,7 +351,7 @@ function Lisp() { try { x = Read(); if (x == kDefine) { - a = Gc(A, Define(a)); + a = Gc(0, Define(Read(), a)); continue; } x = Eval(x, a); @@ -343,6 +363,10 @@ function Lisp() { Gc(A, 0); } } + eOutput.innerText = output; + SaveMachine(a); + SaveOutput(); + ReportUsage(); } function Load(s) { @@ -352,13 +376,28 @@ function Load(s) { } function OnSubmit() { - Load(eInput.value); + Load(eInput.value.toUpperCase()); Lisp(); } -function OnClear() { - eOutput.innerText = ""; +function Dump(a) { + if (!a) return; + Dump(Cdr(a)); + output += "DEFINE "; + PrintObject(Car(Car(a))); + output += " "; + PrintObject(Cdr(Car(a))); + output += "\n"; +} + +function OnReset() { + output = ""; + Dump(a); + eOutput.innerText = output; + Reset(); + localStorage.removeItem("sectorlisp.machine"); SaveOutput(); + ReportUsage(); } function OnLoad() { @@ -366,33 +405,65 @@ function OnLoad() { } function OnWindowClick(event) { - if (!event.target.matches('#load')) { + if (!event.target.matches("#load")) { ePrograms.classList.remove("show"); } } +function SaveMachine(a) { + var machine; + if (typeof localStorage != "undefined") { + machine = [M, a, cx]; + localStorage.setItem("sectorlisp.machine", JSON.stringify(machine)); + } +} + +function RestoreMachine() { + var machine; + if (typeof localStorage != "undefined" && + (machine = JSON.parse(localStorage.getItem("sectorlisp.machine")))) { + M = machine[0]; + a = machine[1]; + cx = machine[2]; + lo = cx; + } +} + function SaveOutput() { - if (typeof localStorage != 'undefined') { - localStorage.setItem('output', eOutput.innerText); + if (typeof localStorage != "undefined") { + localStorage.setItem("input", document.getElementById("input").value); + localStorage.setItem("output", eOutput.innerText); } } +function Number(i) { + return i.toLocaleString(); +} + +function ReportUsage() { + var i, c; + for (c = i = 0; i < Null; i += 2) { + if (Get(i)) ++c; + } + document.getElementById("usage").innerText = + Number((-cx >> 1) + c) + " / " + + Number((-lo >> 1) + c) + " / " + + Number(Null) + " doublewords"; +} + function SetUp() { - a = 0; - cx = 0; - Null = 0100000; - M = new Array(Null * 2); - Load("NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE "); - LoadBuiltins() - eLoad = document.getElementById('load'); - eInput = document.getElementById('input'); - eClear = document.getElementById('clear'); - eOutput = document.getElementById('output'); - eSubmit = document.getElementById('submit'); + Reset(); + RestoreMachine(); + ReportUsage(); + eLoad = document.getElementById("load"); + eInput = document.getElementById("input"); + eReset = document.getElementById("reset"); + eOutput = document.getElementById("output"); + eSubmit = document.getElementById("submit"); ePrograms = document.getElementById("programs"); window.onclick = OnWindowClick; eSubmit.onclick = OnSubmit; - eClear.onclick = OnClear; + eReset.onclick = OnReset; eLoad.onclick = OnLoad; } From 10bc29bcf08f62bb6c49e7f60b4e94efa90fb7cc Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 9 Dec 2021 07:49:33 -0800 Subject: [PATCH 15/29] Make improvements --- lisp.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lisp.js b/lisp.js index c01fd67..331bc53 100755 --- a/lisp.js +++ b/lisp.js @@ -392,9 +392,13 @@ function Dump(a) { function OnReset() { output = ""; - Dump(a); - eOutput.innerText = output; - Reset(); + try { + Dump(a); + eOutput.innerText = output; + Reset(); + } catch (e) { + /* ignored */ + } localStorage.removeItem("sectorlisp.machine"); SaveOutput(); ReportUsage(); @@ -452,9 +456,6 @@ function ReportUsage() { } function SetUp() { - Reset(); - RestoreMachine(); - ReportUsage(); eLoad = document.getElementById("load"); eInput = document.getElementById("input"); eReset = document.getElementById("reset"); @@ -465,6 +466,9 @@ function SetUp() { eSubmit.onclick = OnSubmit; eReset.onclick = OnReset; eLoad.onclick = OnLoad; + Reset(); + RestoreMachine(); + ReportUsage(); } SetUp(); From fea29b58a0640db8dc0517bbb59e87a337fedb27 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 9 Dec 2021 08:51:26 -0800 Subject: [PATCH 16/29] Inline Pairlis (436 bytes) --- sectorlisp.S | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/sectorlisp.S b/sectorlisp.S index f6d4ae9..c3e65b1 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -169,24 +169,11 @@ PutChar:mov $0x0e,%ah # prints CP-437 jne 1f # look xchg ret mov $'\n',%al jmp PutChar - -//////////////////////////////////////////////////////////////////////////////// - -Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):ax - jz 1f # jump if nil - push (%bx,%di) # save 1 Cdr(x) - lodsw - push (%si) # save 2 Cdr(y) - mov (%di),%di - call Cons # preserves dx - pop %si # restore 2 - pop %di # restore 1 - push %ax # save 3 - call Pairlis - jmp xCons # can be inlined here 1: xchg %dx,%ax ret +//////////////////////////////////////////////////////////////////////////////// + Evlis: test %di,%di # Evlis(m:di,a:dx):ax jz 1f # jump if nil push (%bx,%di) # save 1 Cdr(m) @@ -199,7 +186,7 @@ Evlis: test %di,%di # Evlis(m:di,a:dx):ax xCons: pop %di # restore 2 Cons: xchg %di,%cx # Cons(m:di,a:ax):ax - mov %cx,(%di) + mov %cx,(%di) # must preserve si mov %ax,(%bx,%di) lea 4(%di),%cx 1: xchg %di,%ax @@ -250,12 +237,21 @@ Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax jns .switch # jump if atom xchg %ax,%di # di = fn .lambda:mov (%bx,%di),%di # di = Cdr(fn) - push %di # save 1 + push %di # for .EvCadr mov (%di),%di # di = Cadr(fn) - call Pairlis - xchg %ax,%dx - pop %di # restore 1 - jmp .EvCadr +Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx + jz .EvCadr # return if x is nil + lodsw # ax = Car(y) + push (%bx,%di) # push Cdr(x) + mov (%di),%di # di = Car(x) + mov (%si),%si # si = Cdr(y) + call Cons # Cons(Car(x),Car(y)) + xchg %ax,%di + xchg %dx,%ax + call Cons # Cons(Cons(Car(x),Car(y)),a) + xchg %ax,%dx # a = new list + pop %di # grab Cdr(x) + jmp Pairlis .switch:cmp $kEq,%ax # eq is last builtin atom ja .dflt1 # ah is zero if not above mov (%si),%di # di = Car(x) @@ -286,8 +282,9 @@ Evcon: push %di # save c pop %di # restore c test %ax,%ax # nil test jz 1b - mov (%di),%di # di = Car(c) -.EvCadr:call Cadr # ax = Cadar(c) + push (%di) # push Car(c) +.EvCadr:pop %di + call Cadr # ax = Cadar(c) # jmp Eval Eval: test %ax,%ax # Eval(e:ax,a:dx):ax From 6b5fbf22f04f25624152c6d06dee796f4610ce0d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 9 Dec 2021 12:21:35 -0800 Subject: [PATCH 17/29] Reduce size (498 bytes) --- sectorlisp.S | 115 +++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/sectorlisp.S b/sectorlisp.S index c3e65b1..46e8306 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -23,7 +23,6 @@ // Compatible with the original hardware .code16 - .set a,-2-2 .globl _start _start: .asciz "NIL" # dec %si ; dec %cx ; dec %sp kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0 @@ -38,43 +37,6 @@ kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters kEq: .asciz "EQ" # needs to be last -Read: call GetToken - call GetObject - ret - -Define: call Read - push %ax - call Read - pop %di - call Cons - xchg %ax,%di - xchg %bp,%ax - call Cons - xchg %ax,%bp - jmp main - -begin: mov $0x8000,%sp - push %cs - pop %ds - push %cs - pop %es - push %cs - pop %ss - mov $2,%bx - mov %sp,%cx - xor %bp,%bp -main: xor %dx,%dx - call Read - cmp $kDefine,%ax - je Define - mov %bp,%dx - call Eval -Catch: xchg %ax,%si - call PrintObject - mov $'\r',%al - call PutChar - jmp main - GetToken: # GetToken():al mov %cx,%di 1: mov %dl,%al @@ -153,12 +115,6 @@ Intern: push %cx # Intern(cx,di): ax pop %cx ret -Undef: push %ax - mov $'?',%al - call PutChar - pop %ax - jmp Catch - GetChar:xor %ax,%ax # GetChar→al:dl int $0x16 # get keystroke PutChar:mov $0x0e,%ah # prints CP-437 @@ -206,20 +162,6 @@ Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax add %dx,%ax ret -Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax -1: test %si,%si - jns Undef - mov (%si),%di - mov (%bx,%si),%si - scasw - jne 1b - .byte 0xf6 -Cadr: mov (%bx,%di),%di # contents of decrement register - .byte 0x3C # cmp §scasw,%al (nop next byte) -Cdr: scasw # increments our data index by 2 -Car: mov (%di),%ax # contents of address register!! -2: ret - GetList:call GetToken cmp $')',%al je .retF @@ -274,6 +216,20 @@ Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx .retT: mov $kT,%al ret +Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax +1: test %si,%si + jns Undef + mov (%si),%di + mov (%bx,%si),%si + scasw + jne 1b + .byte 0xf6 +Cadr: mov (%bx,%di),%di # contents of decrement register + .byte 0x3C # cmp §scasw,%al (nop next byte) +Cdr: scasw # increments our data index by 2 +Car: mov (%di),%ax # contents of address register!! +2: ret + 1: mov (%bx,%di),%di # di = Cdr(c) Evcon: push %di # save c mov (%di),%si # di = Car(c) @@ -315,6 +271,49 @@ Eval: test %ax,%ax # Eval(e:ax,a:dx):ax pop %dx # restore a 1: ret +Undef: push %ax + mov $'?',%al + call PutChar + pop %ax + jmp Catch + +Read: call GetToken + call GetObject + ret + +Define: call Read + push %ax + call Read + pop %di + call Cons + xchg %ax,%di + xchg %bp,%ax + call Cons + xchg %ax,%bp + jmp main + +begin: mov $0x8000,%sp + push %cs + pop %ds + push %cs + pop %es + push %cs + pop %ss + mov $2,%bx + mov %sp,%cx + xor %bp,%bp +main: xor %dx,%dx + call Read + cmp $kDefine,%ax + je Define + mov %bp,%dx + call Eval +Catch: xchg %ax,%si + call PrintObject + mov $'\r',%al + call PutChar + jmp main + .sig: .fill 510 - (. - _start), 1, 0xce .word 0xAA55 .type .sig,@object From 8ad3d4822fffd9263a3b50fd3340073887a10aba Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 9 Dec 2021 17:34:04 -0800 Subject: [PATCH 18/29] Make JS improvements --- lisp.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lisp.js b/lisp.js index 331bc53..d6d8b8c 100755 --- a/lisp.js +++ b/lisp.js @@ -43,7 +43,7 @@ function Car(x) { if (x < 0) { return Get(x); } else { - Throw(x); + Throw(List(kCar, x)); } } @@ -51,7 +51,7 @@ function Cdr(x) { if (x < 0) { return Get(x + 1); } else { - Throw(x); + Throw(List(kCdr, x)); } } @@ -144,6 +144,10 @@ function Remove(x, y) { return Cons(Car(y), Remove(x, Cdr(y))); } +function List(x, y) { + return Cons(x, Cons(y, 0)); +} + function Define(x, y) { return Cons(Cons(x, Read()), Remove(x, y)); } @@ -166,6 +170,7 @@ function Evlis(m, a) { } function Pairlis(x, y, a) { + if (!!x ^ !!y) Throw(List(x, y)); return x ? Cons(Cons(Car(x), Car(y)), Pairlis(Cdr(x), Cdr(y), a)) : a; } From 3eb0db0a7a66eb486ca08842454d105f50d4516a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 10 Dec 2021 08:44:16 -0800 Subject: [PATCH 19/29] Fix things --- brainfuck.S | 54 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/brainfuck.S b/brainfuck.S index 5188eec..19bf532 100644 --- a/brainfuck.S +++ b/brainfuck.S @@ -18,25 +18,28 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -// compliant brainfuck in 82 bytes -// boots from bios on pc w/ 128kb+ +// compliant brainf*#k in 99 bytes +// ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++ +// ..+++.>>.<-.<.+++.------.--------.>>+.>++.[]$ .code16 .globl _start -_start: mov $0x10000>>4,%di +_start: mov $0x7e00,%di push %di - pop %ds - push %di - pop %es - mov %si,%dx -Brain: xor %ax,%ax - cmp %dx,%si - jb Fuck +Load: xor %ax,%ax int $0x16 - mov %al,(%si) - inc %dx -Fuck: lodsb + stosb + cmp $'[',%al + je Lsb + cmp $']',%al + je Rsb + cmp $'$',%al + jne Load + pop %si +Brain: lodsb cbw + mov $0x0e,%bh + mov (%di),%bl sub $'+',%al jz Inc # + 43 dec %ax @@ -53,12 +56,14 @@ Fuck: lodsb je Loop # ] 91 45 cmp $'['-'.',%al jne Brain # [ 93 47 -Do: push %si -Loop: pop %ax - cmpb %ah,(%di) +Do: cmp $1,%bl + sbb %bl,%bl +Loop: test %bl,%bl + lodsw jz Brain - push %ax xchg %ax,%si + inc %si + inc %si .byte 0x80 Inc: incb (%di) jmp Brain @@ -70,7 +75,18 @@ Dec: decb (%di) .byte 0x3C Right: inc %di .byte 0x84 -Put: mov $0x0e,%ah - mov (%di),%al +Put: xchg %bx,%ax int $0x10 jmp Brain +Rsb: pop %si + mov %di,(%si) + xchg %si,%ax + .byte 0x3c +Lsb: push %di + stosw + jmp Load + +Sig: .fill 510 - (. - _start), 1, 0xce + .word 0xAA55 + .type Sig,@object + From a25d58bddd285397dea259ecf7d7528f14707ff0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 11 Dec 2021 11:59:16 -0800 Subject: [PATCH 20/29] Make improvements --- Makefile | 3 +- lisp.js | 31 +++++++--------- sectorlisp.S | 103 +++++++++++++++++++++++++++------------------------ 3 files changed, 71 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index b1614c7..2a99698 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ CFLAGS = -w -g CLEANFILES = \ lisp \ lisp.o \ + lisp.o \ bestline.o \ sectorlisp.o \ sectorlisp.bin \ @@ -19,7 +20,7 @@ all: lisp \ brainfuck.bin.dbg .PHONY: clean -clean:; $(RM) lisp lisp.o bestline.o sectorlisp.o sectorlisp.bin sectorlisp.bin.dbg +clean:; $(RM) $(CLEANFILES) lisp: lisp.o bestline.o lisp.o: lisp.js bestline.h diff --git a/lisp.js b/lisp.js index d6d8b8c..22654b1 100755 --- a/lisp.js +++ b/lisp.js @@ -40,19 +40,13 @@ function Get(i) { } function Car(x) { - if (x < 0) { - return Get(x); - } else { - Throw(List(kCar, x)); - } + if (x > 0) Throw(List(kCar, x)); + return x ? Get(x) : +0; } function Cdr(x) { - if (x < 0) { - return Get(x + 1); - } else { - Throw(List(kCdr, x)); - } + if (x > 0) Throw(List(kCdr, x)); + return x ? Get(x + 1) : -0; } function Cons(car, cdr) { @@ -88,14 +82,17 @@ function PrintAtom(x) { while ((x = Get(x + 1))); } -function AddList(x) { - return Cons(x, ReadList()); -} - function ReadList() { - var t = ReadAtom(0); - if (Get(t) == Ord(')')) return -0; - return AddList(ReadObject(t)); + var x; + if ((x = Read()) > 0) { + if (Get(x) == Ord(')')) return -0; + if (Get(x) == Ord('.') && !Get(x + 1)) { + x = Read(); + ReadList(); + return x; + } + } + return Cons(x, ReadList()); } function ReadObject(t) { diff --git a/sectorlisp.S b/sectorlisp.S index 46e8306..e24b798 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -19,8 +19,13 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -// LISP meta-circular evaluator in a MBR -// Compatible with the original hardware +// LISP meta-circular evaluator in a MBR +// Compatible with the original hardware + +// This is the friendly extended version +// This adds (FOO . BAR) support to Read +// It print errors on undefined behavior +// It can also DEFINE persistent binding .code16 .globl _start @@ -31,11 +36,11 @@ start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address kDefine:.asciz "DEFINE" kQuote: .asciz "QUOTE" kCond: .asciz "COND" -kAtom: .asciz "ATOM" # ordering matters kCar: .asciz "CAR" # ordering matters kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters -kEq: .asciz "EQ" # needs to be last +kEq: .asciz "EQ" # ordering matters +kAtom: .asciz "ATOM" # needs to be last GetToken: # GetToken():al mov %cx,%di @@ -45,8 +50,6 @@ GetToken: # GetToken():al stosb xchg %ax,%si 2: call GetChar # exchanges dx and ax - cmp $'\b',%al - je 4f cmp $' ',%al jbe 1b cmp $')',%al @@ -56,8 +59,6 @@ GetToken: # GetToken():al 3: mov %bh,(%di) # bh is zero xchg %si,%ax ret -4: dec %di - jmp 2b .PrintList: mov $'(',%al @@ -122,16 +123,30 @@ PutChar:mov $0x0e,%ah # prints CP-437 int $0x10 # vidya service pop %bp # scroll up bug cmp $'\r',%al # don't clobber - jne 1f # look xchg ret + jne .RetDx # look xchg ret mov $'\n',%al jmp PutChar -1: xchg %dx,%ax +.RetDx: xchg %dx,%ax ret //////////////////////////////////////////////////////////////////////////////// +Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax + jb .RetDi # we assume immutable cells + push (%bx,%di) # mark prevents negative gc + mov (%di),%di + call Gc + pop %di + push %ax + call Gc + pop %di + call Cons + sub %si,%ax # ax -= C - B + add %dx,%ax + ret + Evlis: test %di,%di # Evlis(m:di,a:dx):ax - jz 1f # jump if nil + jz .RetDi # jump if nil push (%bx,%di) # save 1 Cdr(m) mov (%di),%ax call Eval @@ -145,36 +160,27 @@ Cons: xchg %di,%cx # Cons(m:di,a:ax):ax mov %cx,(%di) # must preserve si mov %ax,(%bx,%di) lea 4(%di),%cx -1: xchg %di,%ax - ret - -Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax - jb 1b # we assume immutable cells - push (%bx,%di) # mark prevents negative gc - mov (%di),%di - call Gc - pop %di - push %ax - call Gc - pop %di - call Cons - sub %si,%ax # ax -= C - B - add %dx,%ax +.RetDi: xchg %di,%ax ret GetList:call GetToken cmp $')',%al je .retF + cmp $'.',%al # FRIENDLY FEATURE + je 1f # CONS DOT LITERAL call GetObject push %ax # popped by xCons call GetList jmp xCons +1: call Read + push %ax + call GetList + pop %ax + ret -.dflt1: push %si # save x - call Eval - pop %si # restore x -# jmp Apply - +.resolv:push %si + call Eval # do (fn si) → ((λ ...) si) + pop %si Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax jns .switch # jump if atom xchg %ax,%di # di = fn @@ -194,31 +200,32 @@ Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx xchg %ax,%dx # a = new list pop %di # grab Cdr(x) jmp Pairlis -.switch:cmp $kEq,%ax # eq is last builtin atom - ja .dflt1 # ah is zero if not above +.switch:cmp $kAtom,%ax # eq is last builtin atom + ja .resolv # ah is zero if not above mov (%si),%di # di = Car(x) + je .ifAtom + cmp $kCons,%ax + jae .ifCons + test %di,%di # FRIENDLY FEATURE + jns .retF # CAR/CDR(NIL)→NIL .ifCar: cmp $kCar,%al je Car -.ifCdr: cmp $kCdr,%al - je Cdr -.ifAtom:cmp $kAtom,%al - jne .ifCons - test %di,%di # test if atom - jns .retT -.retF: xor %ax,%ax # ax = nil - ret -.ifCons:cmp $kCons,%al - mov (%bx,%si),%si # si = Cdr(x) +.ifCdr: jmp Cdr +.ifCons:mov (%bx,%si),%si # si = Cdr(x) lodsw # si = Cadr(x) je Cons .isEq: xor %di,%ax # we know for certain it's eq jne .retF .retT: mov $kT,%al ret +.ifAtom:test %di,%di # test if atom + jns .retT +.retF: xor %ax,%ax # ax = nil + ret Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax -1: test %si,%si - jns Undef +1: test %si,%si # FRIENDLY FEATURE + jns Undef # PRINT ?X IF X∉DX mov (%si),%di mov (%bx,%si),%si scasw @@ -228,7 +235,7 @@ Cadr: mov (%bx,%di),%di # contents of decrement register .byte 0x3C # cmp §scasw,%al (nop next byte) Cdr: scasw # increments our data index by 2 Car: mov (%di),%ax # contents of address register!! -2: ret + ret 1: mov (%bx,%di),%di # di = Cdr(c) Evcon: push %di # save c @@ -281,8 +288,8 @@ Read: call GetToken call GetObject ret -Define: call Read - push %ax +Define: call Read # FRIENDLY FEATURE + push %ax # DEFINE NAME SEXP call Read pop %di call Cons From f6e8f51307321dd1661a82cd35b96fe222e96769 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 12 Dec 2021 09:41:52 -0800 Subject: [PATCH 21/29] Make DEFINE more like Scheme --- lisp.js | 218 ++++++++++++++++++++++++++++++++++++--------------- sectorlisp.S | 45 +++++------ 2 files changed, 178 insertions(+), 85 deletions(-) diff --git a/lisp.js b/lisp.js index 22654b1..f5cd9c0 100755 --- a/lisp.js +++ b/lisp.js @@ -26,19 +26,24 @@ exit #define function #define Null 16384 var M[Null * 2]; +var (*funcall)(); jmp_buf undefined; //` -var cx, dx, lo, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; - -function Set(i, x) { - M[Null + i] = x; -} +var cx, dx, depth, panic; +var cHeap, cGets, cSets, cPrints; +var kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; function Get(i) { + ++cGets; return M[Null + i]; } +function Set(i, x) { + ++cSets; + M[Null + i] = x; +} + function Car(x) { if (x > 0) Throw(List(kCar, x)); return x ? Get(x) : +0; @@ -52,7 +57,7 @@ function Cdr(x) { function Cons(car, cdr) { Set(--cx, cdr); Set(--cx, car); - if (cx < lo) lo = cx; + if (cx < cHeap) cHeap = cx; return cx; } @@ -77,11 +82,6 @@ function ReadAtom(h) { Hash(h, c) - Hash(0, Ord('N'))); } -function PrintAtom(x) { - do PrintChar(Get(x)); - while ((x = Get(x + 1))); -} - function ReadList() { var x; if ((x = Read()) > 0) { @@ -100,17 +100,33 @@ function ReadObject(t) { return ReadList(); } +function Read() { + return ReadObject(ReadAtom(0)); +} + +function PrintAtom(x) { + do PrintChar(Get(x)); + while ((x = Get(x + 1))); +} + function PrintList(x) { PrintChar(Ord('(')); if (x < 0) { - PrintObject(Car(x)); + Print(Car(x)); while ((x = Cdr(x))) { + if (panic && cPrints > panic) { + PrintChar(Ord(' ')); + PrintChar(0x2026); + break; + } if (x < 0) { PrintChar(Ord(' ')); - PrintObject(Car(x)); + Print(Car(x)); } else { - PrintChar(0x2219); - PrintObject(x); + PrintChar(Ord(' ')); + PrintChar(Ord('.')); + PrintChar(Ord(' ')); + Print(x); break; } } @@ -118,7 +134,8 @@ function PrintList(x) { PrintChar(Ord(')')); } -function PrintObject(x) { +function Print(x) { + ++cPrints; if (1./x < 0) { PrintList(x); } else { @@ -126,13 +143,12 @@ function PrintObject(x) { } } -function Print(e) { - PrintObject(e); - PrintChar(Ord('\n')); +function List(x, y) { + return Cons(x, Cons(y, 0)); } -function Read() { - return ReadObject(ReadAtom(0)); +function Define(A, x, a) { + return Gc(A, Cons(x, Remove(Car(x), a))); } function Remove(x, y) { @@ -141,14 +157,6 @@ function Remove(x, y) { return Cons(Car(y), Remove(x, Cdr(y))); } -function List(x, y) { - return Cons(x, Cons(y, 0)); -} - -function Define(x, y) { - return Cons(Cons(x, Read()), Remove(x, y)); -} - function Gc(A, x) { var C, B = cx; x = Copy(x, A, A - B), C = cx; @@ -167,7 +175,6 @@ function Evlis(m, a) { } function Pairlis(x, y, a) { - if (!!x ^ !!y) Throw(List(x, y)); return x ? Cons(Cons(Car(x), Car(y)), Pairlis(Cdr(x), Cdr(y), a)) : a; } @@ -184,7 +191,7 @@ function Evcon(c, a) { } else if (Cdr(c)) { return Evcon(Cdr(c), a); } else { - Throw(c); + Throw(Cons(kCond, c)); } } @@ -195,16 +202,61 @@ function Apply(f, x, a) { if (f == kAtom) return Car(x) < 0 ? 0 : kT; if (f == kCar) return Car(Car(x)); if (f == kCdr) return Cdr(Car(x)); - return Apply(Assoc(f, a), x, a); + return funcall(f, Assoc(f, a), x, a); } function Eval(e, a) { - var A = cx; if (!e) return e; if (e > 0) return Assoc(e, a); if (Car(e) == kQuote) return Car(Cdr(e)); - if (Car(e) == kCond) return Gc(A, Evcon(Cdr(e), a)); - return Gc(A, Apply(Car(e), Evlis(Cdr(e), a), a)); + if (Car(e) == kCond) return Evcon(Cdr(e), a); + return Apply(Car(e), Evlis(Cdr(e), a), a); +} + +function Funcall(f, l, x, a) { + var A = cx; + return Gc(A, Apply(l, x, a)); +} + +function Funtrace(f, l, x, a) { + var y, i, A = cx; + Indent(depth); + Print(f); + Print(x); + PrintChar(Ord('\n')); + depth += 2; + y = Funcall(f, l, x, a); + depth -= 2; + Indent(depth); + Print(f); + Print(x); + PrintChar(Ord(' ')); + PrintChar(0x2192); + PrintChar(Ord(' ')); + Print(y); + PrintChar(Ord('\n')); + return y; +} + +function Indent(i) { + if (!i) return; + PrintChar(Ord(' ')); + Indent(i - 1); +} + +function Dump(a) { + if (!a) return; + Dump(Cdr(a)); + PrintChar(Ord('(')); + Print(kDefine); + PrintChar(Ord(' ')); + Print(Car(Car(a))); + PrintChar(Ord(' ')); + PrintChar(Ord('.')); + PrintChar(Ord(' ')); + Print(Cdr(Car(a))); + PrintChar(Ord(')')); + PrintChar(Ord('\n')); } function LoadBuiltins() { @@ -267,17 +319,24 @@ ReadChar() { } } -main() { +main(argc, argv) + char *argv[]; +{ var x, a, A; setlocale(LC_ALL, ""); bestlineSetXlatCallback(bestlineUppercase); + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 't') { + funcall = Funtrace; + } else { + funcall = Funcall; + } LoadBuiltins(); for (a = 0;;) { A = cx; if (!(x = setjmp(undefined))) { x = Read(); - if (x == kDefine) { - a = Gc(0, Define(Read(), a)); + if (x < 0 && Car(x) == kDefine) { + a = Define(0, Cdr(x), a); SaveMachine(a); continue; } @@ -287,6 +346,7 @@ main() { PrintChar('?'); } Print(x); + PrintChar('\n'); Gc(A, 0); } } @@ -296,8 +356,8 @@ main() { //////////////////////////////////////////////////////////////////////////////// // JavaScript Specific Code for https://justine.lol/ -var a, code, index, output, M, Null; -var eInput, eOutput, eSubmit, eReset, eLoad, ePrograms; +var a, code, index, output, funcall, M, Null; +var eInput, eOutput, eEval, eReset, eLoad, eTrace, ePrograms; function Throw(x) { throw x; @@ -311,7 +371,10 @@ function Reset() { var i; a = 0; cx = 0; - lo = 0; + cHeap = 0; + cGets = 0; + cSets = 0; + cPrints = 0; Null = 16384; M = new Array(Null * 2); for (i = 0; i < M.length; ++i) { @@ -343,8 +406,11 @@ function ReadChar() { function Lisp() { var x, A; - lo = cx; - output = ''; + cGets = 0; + cSets = 0; + cHeap = cx; + cPrints = 0; + output = ""; while (dx) { if (dx <= Ord(' ')) { ReadChar(); @@ -352,8 +418,8 @@ function Lisp() { A = cx; try { x = Read(); - if (x == kDefine) { - a = Gc(0, Define(Read(), a)); + if (x < 0 && Car(x) == kDefine) { + a = Define(0, Cdr(x), a); continue; } x = Eval(x, a); @@ -362,6 +428,7 @@ function Lisp() { x = z; } Print(x); + PrintChar(Ord('\n')); Gc(A, 0); } } @@ -377,21 +444,11 @@ function Load(s) { index = 1; } -function OnSubmit() { +function OnEval() { Load(eInput.value.toUpperCase()); Lisp(); } -function Dump(a) { - if (!a) return; - Dump(Cdr(a)); - output += "DEFINE "; - PrintObject(Car(Car(a))); - output += " "; - PrintObject(Cdr(Car(a))); - output += "\n"; -} - function OnReset() { output = ""; try { @@ -406,6 +463,18 @@ function OnReset() { ReportUsage(); } +function OnTrace() { + var t; + Load(eInput.value); + t = panic; + depth = 0; + panic = 10000; + funcall = Funtrace; + Lisp(); + funcall = Funcall; + panic = t; +} + function OnLoad() { ePrograms.classList.toggle("show"); } @@ -431,7 +500,7 @@ function RestoreMachine() { M = machine[0]; a = machine[1]; cx = machine[2]; - lo = cx; + cHeap = cx; } } @@ -449,25 +518,48 @@ function Number(i) { function ReportUsage() { var i, c; for (c = i = 0; i < Null; i += 2) { - if (Get(i)) ++c; + if (M[Null + i]) ++c; } - document.getElementById("usage").innerText = + document.getElementById("ops").innerText = + Number(cGets) + " gets / " + + Number(cSets) + " sets"; + document.getElementById("mem").innerText = Number((-cx >> 1) + c) + " / " + - Number((-lo >> 1) + c) + " / " + + Number((-cHeap >> 1) + c) + " / " + Number(Null) + " doublewords"; } +function Discount(f) { + return function() { + var x, g, h, s; + g = cGets; + s = cSets; + h = cHeap; + x = f.apply(this, arguments); + cHeap = h; + cSets = s; + cGets = g; + return x; + }; +} + function SetUp() { + funcall = Funcall; + Read = Discount(Read); + Print = Discount(Print); + Define = Discount(Define); eLoad = document.getElementById("load"); eInput = document.getElementById("input"); eReset = document.getElementById("reset"); + eTrace = document.getElementById("trace"); eOutput = document.getElementById("output"); - eSubmit = document.getElementById("submit"); + eEval = document.getElementById("eval"); ePrograms = document.getElementById("programs"); window.onclick = OnWindowClick; - eSubmit.onclick = OnSubmit; - eReset.onclick = OnReset; eLoad.onclick = OnLoad; + eReset.onclick = OnReset; + eTrace.onclick = OnTrace; + eEval.onclick = OnEval; Reset(); RestoreMachine(); ReportUsage(); diff --git a/sectorlisp.S b/sectorlisp.S index e24b798..8a264e9 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -34,8 +34,8 @@ kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0 start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address .asciz "" kDefine:.asciz "DEFINE" -kQuote: .asciz "QUOTE" kCond: .asciz "COND" +kQuote: .asciz "QUOTE" kCar: .asciz "CAR" # ordering matters kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters @@ -123,16 +123,14 @@ PutChar:mov $0x0e,%ah # prints CP-437 int $0x10 # vidya service pop %bp # scroll up bug cmp $'\r',%al # don't clobber - jne .RetDx # look xchg ret + jne .retDx # look xchg ret mov $'\n',%al jmp PutChar -.RetDx: xchg %dx,%ax - ret //////////////////////////////////////////////////////////////////////////////// Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax - jb .RetDi # we assume immutable cells + jb .retDi # we assume immutable cells push (%bx,%di) # mark prevents negative gc mov (%di),%di call Gc @@ -146,7 +144,7 @@ Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax ret Evlis: test %di,%di # Evlis(m:di,a:dx):ax - jz .RetDi # jump if nil + jz .retDi # jump if nil push (%bx,%di) # save 1 Cdr(m) mov (%di),%ax call Eval @@ -160,7 +158,7 @@ Cons: xchg %di,%cx # Cons(m:di,a:ax):ax mov %cx,(%di) # must preserve si mov %ax,(%bx,%di) lea 4(%di),%cx -.RetDi: xchg %di,%ax +.retDi: xchg %di,%ax ret GetList:call GetToken @@ -178,6 +176,9 @@ GetList:call GetToken pop %ax ret +.retDx: xchg %dx,%ax + ret + .resolv:push %si call Eval # do (fn si) → ((λ ...) si) pop %si @@ -189,11 +190,16 @@ Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax mov (%di),%di # di = Cadr(fn) Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx jz .EvCadr # return if x is nil + xor %ax,%ax # FRIENDLY FEATURE + test %si,%si # DEFAULT NIL ARGS + jz 1f lodsw # ax = Car(y) - push (%bx,%di) # push Cdr(x) +1: push (%bx,%di) # push Cdr(x) mov (%di),%di # di = Car(x) + test %si,%si + jz 1f mov (%si),%si # si = Cdr(y) - call Cons # Cons(Car(x),Car(y)) +1: call Cons # Cons(Car(x),Car(y)) xchg %ax,%di xchg %dx,%ax call Cons # Cons(Cons(Car(x),Car(y)),a) @@ -223,6 +229,10 @@ Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx .retF: xor %ax,%ax # ax = nil ret +Define: xchg %dx,%ax + call Cons + jmp .retDx + Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax 1: test %si,%si # FRIENDLY FEATURE jns Undef # PRINT ?X IF X∉DX @@ -260,6 +270,7 @@ Eval: test %ax,%ax # Eval(e:ax,a:dx):ax je Car cmp $kCond,%ax je Evcon # ABC Garbage Collector + jb Define push %dx # save a push %cx # save A push %ax @@ -288,17 +299,6 @@ Read: call GetToken call GetObject ret -Define: call Read # FRIENDLY FEATURE - push %ax # DEFINE NAME SEXP - call Read - pop %di - call Cons - xchg %ax,%di - xchg %bp,%ax - call Cons - xchg %ax,%bp - jmp main - begin: mov $0x8000,%sp push %cs pop %ds @@ -311,10 +311,11 @@ begin: mov $0x8000,%sp xor %bp,%bp main: xor %dx,%dx call Read - cmp $kDefine,%ax - je Define mov %bp,%dx call Eval + mov %dx,%bp + cmp $kDefine,%ax + je main Catch: xchg %ax,%si call PrintObject mov $'\r',%al From fe04b145b54055961bd0006ddd95ff0356ea919f Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 12 Dec 2021 14:48:00 -0800 Subject: [PATCH 22/29] Have Apply call Assoc --- sectorlisp.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sectorlisp.S b/sectorlisp.S index 8a264e9..237588d 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -180,7 +180,7 @@ GetList:call GetToken ret .resolv:push %si - call Eval # do (fn si) → ((λ ...) si) + call Assoc # do (fn si) → ((λ ...) si) pop %si Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax jns .switch # jump if atom From 334ec2121325089584c2035152b8519159a88631 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 13 Dec 2021 07:40:04 -0800 Subject: [PATCH 23/29] FIx clang issue --- lisp.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lisp.js b/lisp.js index f5cd9c0..cd1a4ea 100755 --- a/lisp.js +++ b/lisp.js @@ -239,24 +239,26 @@ function Funtrace(f, l, x, a) { } function Indent(i) { - if (!i) return; - PrintChar(Ord(' ')); - Indent(i - 1); + if (i) { + PrintChar(Ord(' ')); + Indent(i - 1); + } } function Dump(a) { - if (!a) return; - Dump(Cdr(a)); - PrintChar(Ord('(')); - Print(kDefine); - PrintChar(Ord(' ')); - Print(Car(Car(a))); - PrintChar(Ord(' ')); - PrintChar(Ord('.')); - PrintChar(Ord(' ')); - Print(Cdr(Car(a))); - PrintChar(Ord(')')); - PrintChar(Ord('\n')); + if (a) { + Dump(Cdr(a)); + PrintChar(Ord('(')); + Print(kDefine); + PrintChar(Ord(' ')); + Print(Car(Car(a))); + PrintChar(Ord(' ')); + PrintChar(Ord('.')); + PrintChar(Ord(' ')); + Print(Cdr(Car(a))); + PrintChar(Ord(')')); + PrintChar(Ord('\n')); + } } function LoadBuiltins() { From 0916d58550fd91d5490427fe462d7b9efc022a2f Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 13 Dec 2021 10:09:59 -0800 Subject: [PATCH 24/29] Add performance counter --- lisp.js | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/lisp.js b/lisp.js index cd1a4ea..fe278c8 100755 --- a/lisp.js +++ b/lisp.js @@ -406,8 +406,17 @@ function ReadChar() { } } +function GetMillis() { + if (typeof performance != "undefined") { + return performance.now(); + } else { + return 0; + } +} + function Lisp() { - var x, A; + var x, A, d, t; + d = 0; cGets = 0; cSets = 0; cHeap = cx; @@ -424,7 +433,9 @@ function Lisp() { a = Define(0, Cdr(x), a); continue; } + t = GetMillis(); x = Eval(x, a); + d += GetMillis() - t; } catch (z) { PrintChar(Ord('?')); x = z; @@ -437,7 +448,7 @@ function Lisp() { eOutput.innerText = output; SaveMachine(a); SaveOutput(); - ReportUsage(); + ReportUsage(d); } function Load(s) { @@ -452,7 +463,9 @@ function OnEval() { } function OnReset() { + var t; output = ""; + t = GetMillis(); try { Dump(a); eOutput.innerText = output; @@ -460,9 +473,10 @@ function OnReset() { } catch (e) { /* ignored */ } + t = GetMillis() - t; localStorage.removeItem("sectorlisp.machine"); SaveOutput(); - ReportUsage(); + ReportUsage(t); } function OnTrace() { @@ -513,22 +527,27 @@ function SaveOutput() { } } -function Number(i) { +function FormatInt(i) { return i.toLocaleString(); } -function ReportUsage() { - var i, c; +function FormatDuration(d) { + return d ? Math.round(d * 1000) / 1000 : 0; +} + +function ReportUsage(d) { + var i, c, s; for (c = i = 0; i < Null; i += 2) { if (M[Null + i]) ++c; } document.getElementById("ops").innerText = - Number(cGets) + " gets / " + - Number(cSets) + " sets"; + FormatInt(cGets) + " gets / " + + FormatInt(cSets) + " sets / " + + FormatDuration(d) + " ms"; document.getElementById("mem").innerText = - Number((-cx >> 1) + c) + " / " + - Number((-cHeap >> 1) + c) + " / " + - Number(Null) + " doublewords"; + FormatInt((-cx >> 1) + c) + " / " + + FormatInt((-cHeap >> 1) + c) + " / " + + FormatInt(Null) + " doublewords"; } function Discount(f) { From c9ce1c54a024ac4774a208a84c9083c3a611825d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 14 Dec 2021 09:08:27 -0800 Subject: [PATCH 25/29] Add -t flag --- lisp.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lisp.js b/lisp.js index fe278c8..c00d4d3 100755 --- a/lisp.js +++ b/lisp.js @@ -327,10 +327,16 @@ main(argc, argv) var x, a, A; setlocale(LC_ALL, ""); bestlineSetXlatCallback(bestlineUppercase); - if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 't') { - funcall = Funtrace; - } else { - funcall = Funcall; + funcall = Funcall; + for (x = 1; x < argc; ++x) { + if (argv[x][0] == '-' && argv[x][1] == 't') { + funcall = Funtrace; + } else { + fputs("Usage: ", stderr); + fputs(argv[0], stderr); + fputs(" [-t] errput.lisp\n", stderr); + exit(1); + } } LoadBuiltins(); for (a = 0;;) { From e54c840f49d17c3011f82d81643c63bf1ea385fe Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 18 Dec 2021 13:49:49 -0800 Subject: [PATCH 26/29] Make improvements --- .gitignore | 1 + Makefile | 13 +- checkjumps.sh | 8 + hash.c | 164 +++++++++++++++++ lisp.js | 474 ++++++++++++++++++++++++++++++++++---------------- 5 files changed, 507 insertions(+), 153 deletions(-) create mode 100755 checkjumps.sh create mode 100644 hash.c diff --git a/.gitignore b/.gitignore index e69d341..6979610 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /lisp +/hash /*.o /*.bin /*.bin.dbg diff --git a/Makefile b/Makefile index 2a99698..c48f981 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ -CFLAGS = -w -g +CFLAGS = -w -g -O2 CLEANFILES = \ lisp \ lisp.o \ - lisp.o \ + hash \ bestline.o \ sectorlisp.o \ sectorlisp.bin \ @@ -14,6 +14,7 @@ CLEANFILES = \ .PHONY: all all: lisp \ + hash \ sectorlisp.bin \ sectorlisp.bin.dbg \ brainfuck.bin \ @@ -44,5 +45,13 @@ brainfuck.bin.dbg: brainfuck.o brainfuck.bin: brainfuck.bin.dbg objcopy -S -O binary brainfuck.bin.dbg brainfuck.bin +.PHONY: check +check: + ./checkjumps.sh + gcc -w -c -o /dev/null -xc lisp.js + clang -w -c -o /dev/null -xc lisp.js + gcc -Wall -Werror -c -o /dev/null hash.c + clang -Wall -Werror -c -o /dev/null hash.c + %.o: %.js $(COMPILE.c) -xc $(OUTPUT_OPTION) $< diff --git a/checkjumps.sh b/checkjumps.sh new file mode 100755 index 0000000..fe888ee --- /dev/null +++ b/checkjumps.sh @@ -0,0 +1,8 @@ +#!/bin/sh +if objdump -Cwd -Mi8086 sectorlisp.o | cat -n | + grep '[[:xdigit:]][[:xdigit:]] [[:xdigit:]][[:xdigit:]] [[:xdigit:]][[:xdigit:]][[:space:]]*j'; then + echo need to shuffle code around >&2 + exit 1 +else + echo all jump encodings are tiny >&2 +fi diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..cba0a44 --- /dev/null +++ b/hash.c @@ -0,0 +1,164 @@ +#ifndef __COSMOPOLITAN__ +#include +#include +#endif + +#define word unsigned long +#define dubs unsigned __int128 + +struct Bar { + dubs r; + word n; + char k; +}; + +static word Null; + +static inline int IsTwoPow(word x) { + return !(x & (x - 1)); +} + +static inline int Bsf(word x) { +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + return __builtin_ctzll(x); +#else + uint32_t l, r; + x &= -x; + l = x | x >> 32; + r = !!(x >> 32), r <<= 1; + r += !!(l & 0xffff0000), r <<= 1; + r += !!(l & 0xff00ff00), r <<= 1; + r += !!(l & 0xf0f0f0f0), r <<= 1; + r += !!(l & 0xcccccccc), r <<= 1; + r += !!(l & 0xaaaaaaaa); + return r; +#endif +} + +static inline int Bsr(word x) { +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + return __builtin_clzll(x) ^ 63; +#else + static const char kDebruijn[64] = { + 0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61, + 54, 58, 35, 52, 50, 42, 21, 44, 38, 32, 29, 23, 17, 11, 4, 62, + 46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43, 31, 22, 10, 45, + 25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63, + }; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + return kDebruijn[(x * 0x03f79d71b4cb0a89) >> 58]; +#endif +} + +static inline word Log(word x) { + return --x ? Bsr(x) + 1 : 0; +} + +static struct Bar Bar(word n) { + struct Bar m; + m.r = 1; + m.n = n; + m.k = Log(n) << 1; + m.r = (m.r << m.k) / n; + return m; +} + +static word Mod(struct Bar m, dubs x) { + dubs t; + t = x - ((x * m.r) >> m.k) * m.n; + if (t >= m.n) t -= m.n; + return t; +} + +static word Mul(struct Bar m, word x, word y) { + dubs t = x; + return Mod(m, t * y); +} + +static word Pow(struct Bar m, word a, word n) { + word p, r; + for (p = a, r = 1; n; n >>= 1) { + if (n & 1) r = Mul(m, r, p); + p = Mul(m, p, p); + } + return r; +} + +static int W(struct Bar m, word a) { + word x, y, s; + s = Bsf(m.n >> 1) + 1; + x = Pow(m, a, m.n >> s); + for (y = 0; s; --s, x = y) { + y = Mul(m, x, x); + if (y == 1 && x != 1 && x != m.n - 1) { + return 0; + } + } + return y == 1; +} + +static int MillerTime(word n) { + struct Bar m; + if (n < 2) return 0; + if (n <= 3) return 1; + if (~n & 1) return 0; + if (n % 3 == 0) return 0; + m = Bar(n); + if (n < 1373653) return W(m,2) && W(m,3); + if (n < 9080191) return W(m,31) && W(m,73); + if (n < 4759123141) return W(m,2) && W(m,7) && W(m,61); + if (n < 1122004669633) return W(m,2) && W(m,13) && W(m,23) && W(m,1662803); + if (n < 2152302898747) return W(m,2) && W(m,3) && W(m,5) && W(m,7) && W(m,11); + if (n < 3474749660383) return W(m,2) && W(m,3) && W(m,5) && W(m,7) && W(m,11) && W(m,13); + return W(m,2) && W(m,3) && W(m,5) && W(m,7) && W(m,11) && W(m,13) && W(m,17); +} + +static word Hash(word h, word x, word a, word b, word c) { + return (((h + x) * a + b) >> c) & (Null / 2 - 1); +} + +static word Ok(word a, word b, word c) { + return Hash(Hash(Hash(0, 'L', a, b, c), 'I', a, b, c), 'N', a, b, c) == 0 && + Hash(0, 'T', a, b, c) == 1 && + Hash(0, 'T', a, b, c) != Hash(0, 'U', a, b, c); +} + +static int Usage(const char *prog) { + fprintf(stderr, "Usage: %s NULL\n", prog); + fprintf(stderr, "Finds magic numbers for SectorLISP Hash()\n"); + return 1; +} + +int main(int argc, char *argv[]) { + word a, b, c; + if (argc > 1) { + Null = strtoul(argv[1], 0, 0); + if (Null < 128) { + fprintf(stderr, "Error: Null is too small\n"); + return Usage(argv[0]); + } + if (!IsTwoPow(Null)) { + fprintf(stderr, "Error: Null must be two power\n"); + return Usage(argv[0]); + } + } else { + Null = 040000; + } + for (a = 2; a < Null; ++a) { + if (!MillerTime(a)) continue; + for (c = 0; c <= Bsr(Null / 2); ++c) { + for (b = 0; b < Null; ++b) { + if (Ok(a, b, c)) { + printf("return (((h + x) * %lu + %lu) >> %lu) & %#lo;\n", a, b, c, + Null / 2 - 1); + } + } + } + } + return 0; +} diff --git a/lisp.js b/lisp.js index c00d4d3..f28dc47 100755 --- a/lisp.js +++ b/lisp.js @@ -18,6 +18,7 @@ exit //
` #include "bestline.h" #ifndef __COSMOPOLITAN__ +#include #include #include #include @@ -30,9 +31,9 @@ var (*funcall)(); jmp_buf undefined; //` -var cx, dx, depth, panic; -var cHeap, cGets, cSets, cPrints; -var kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; +var ax, cx, dx, depth, panic, fail; +var cHeap, cGets, cSets, cReads, cPrints; +var kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; function Get(i) { ++cGets; @@ -61,52 +62,59 @@ function Cons(car, cdr) { return cx; } -function Hash(h, c) { - return h + c * 2; +function Probe(h, p) { + return (h + p * p) & (Null / 2 - 1); } -function Intern(x, y, i) { - i &= Null - 1; - if (x == Get(i) && y == Get(i + 1)) return i; - if (Get(i)) return Intern(x, y, i + 2); - Set(i, x); - Set(i + 1, y); - return i; +function Hash(h, x) { + return (((h + x) * 3083 + 3191) >> 4) & (Null / 2 - 1); } -function ReadAtom(h) { - var c = ReadChar(); - if (c <= Ord(' ')) return ReadAtom(h); - return Intern(c, c > Ord(')') && dx > Ord(')') ? - ReadAtom(Hash(h, c)) : 0, - Hash(h, c) - Hash(0, Ord('N'))); +function Intern(x, y, h, p) { + if (x == Get(h) && y == Get(h + Null / 2)) return h; + if (Get(h)) return Intern(x, y, Probe(h, p), p + 1); + Set(h, x); + Set(h + Null/2, y); + return h; +} + +function ReadAtom() { + var 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); } function ReadList() { - var x; + var x, y; if ((x = Read()) > 0) { if (Get(x) == Ord(')')) return -0; if (Get(x) == Ord('.') && !Get(x + 1)) { x = Read(); - ReadList(); - return x; + y = ReadList(); + if (!y) { + return x; + } else { + Throw(y); + } } } return Cons(x, ReadList()); } -function ReadObject(t) { +function Read() { + var t; + ++cReads; + t = ReadAtom(); if (Get(t) != Ord('(')) return t; return ReadList(); } -function Read() { - return ReadObject(ReadAtom(0)); -} - function PrintAtom(x) { do PrintChar(Get(x)); - while ((x = Get(x + 1))); + while ((x = Get(x + Null / 2))); } function PrintList(x) { @@ -144,17 +152,7 @@ function Print(x) { } function List(x, y) { - return Cons(x, Cons(y, 0)); -} - -function Define(A, x, a) { - return Gc(A, Cons(x, Remove(Car(x), a))); -} - -function Remove(x, y) { - if (!y) return y; - if (x == Car(Car(y))) return Cdr(y); - return Cons(Car(y), Remove(x, Cdr(y))); + return Cons(x, Cons(y, -0)); } function Gc(A, x) { @@ -164,6 +162,19 @@ function Gc(A, x) { return cx = A, x; } +function Evcon(c, a) { + if (c >= 0) Throw(kCond); + if (Eval(Car(Car(c)), a)) { + return Eval(Car(Cdr(Car(c))), a); + } else { + return Evcon(Cdr(c), a); + } +} + +function Peel(x, a) { + return a && x == Car(Car(a)) ? Cdr(a) : a; +} + function Copy(x, m, k) { return x < m ? Cons(Copy(Car(x), m, k), Copy(Cdr(x), m, k)) + k : x; @@ -176,33 +187,23 @@ function Evlis(m, a) { function Pairlis(x, y, a) { return x ? Cons(Cons(Car(x), Car(y)), - Pairlis(Cdr(x), Cdr(y), a)) : a; + Pairlis(Cdr(x), Cdr(y), + Peel(Car(x), a))) : a; } function Assoc(x, y) { - if (y >= 0) Throw(x); - if (x == Car(Car(y))) return Cdr(Car(y)); - return Assoc(x, Cdr(y)); -} - -function Evcon(c, a) { - if (Eval(Car(Car(c)), a)) { - return Eval(Car(Cdr(Car(c))), a); - } else if (Cdr(c)) { - return Evcon(Cdr(c), a); - } else { - Throw(Cons(kCond, c)); - } + if (!y) Throw(x); + return x == Car(Car(y)) ? Cdr(Car(y)) : Assoc(x, Cdr(y)); } function Apply(f, x, a) { if (f < 0) return Eval(Car(Cdr(Cdr(f))), Pairlis(Car(Cdr(f)), x, a)); - if (f == kEq) return Car(x) == Car(Cdr(x)) ? kT : 0; if (f == kCons) return Cons(Car(x), Car(Cdr(x))); - if (f == kAtom) return Car(x) < 0 ? 0 : kT; + if (f == kEq) return Car(x) == Car(Cdr(x)); + if (f == kAtom) return Car(x) >= 0; if (f == kCar) return Car(Car(x)); if (f == kCdr) return Cdr(Car(x)); - return funcall(f, Assoc(f, a), x, a); + return funcall(cx, f, Assoc(f, a), x, a); } function Eval(e, a) { @@ -213,19 +214,18 @@ function Eval(e, a) { return Apply(Car(e), Evlis(Cdr(e), a), a); } -function Funcall(f, l, x, a) { - var A = cx; +function Funcall(A, f, l, x, a) { return Gc(A, Apply(l, x, a)); } -function Funtrace(f, l, x, a) { - var y, i, A = cx; +function Funtrace(A, f, l, x, a) { + var y; Indent(depth); Print(f); Print(x); PrintChar(Ord('\n')); depth += 2; - y = Funcall(f, l, x, a); + y = Funcall(cx, f, l, x, a); depth -= 2; Indent(depth); Print(f); @@ -245,9 +245,25 @@ function Indent(i) { } } -function Dump(a) { +function DumpAlist(a) { + PrintChar(Ord('(')); + PrintChar(Ord('\n')); + for (;a ;a = Cdr(a)) { + PrintChar(Ord('(')); + Print(Car(Car(a))); + PrintChar(Ord(' ')); + PrintChar(Ord('.')); + PrintChar(Ord(' ')); + Print(Cdr(Car(a))); + PrintChar(Ord(')')); + PrintChar(Ord('\n')); + } + PrintChar(Ord(')')); +} + +function DumpDefines(a) { if (a) { - Dump(Cdr(a)); + DumpDefines(Cdr(a)); PrintChar(Ord('(')); Print(kDefine); PrintChar(Ord(' ')); @@ -262,16 +278,47 @@ function Dump(a) { } function LoadBuiltins() { - ReadAtom(0); - kT = ReadAtom(0); - kEq = ReadAtom(0); - kCar = ReadAtom(0); - kCdr = ReadAtom(0); - kAtom = ReadAtom(0); - kCond = ReadAtom(0); - kCons = ReadAtom(0); - kQuote = ReadAtom(0); - kDefine = ReadAtom(0); + Read(); + Read(); + kEq = Read(); + kCar = Read(); + kCdr = Read(); + kAtom = Read(); + kCond = Read(); + kCons = Read(); + kQuote = Read(); + kDefine = Read(); +} + +function Crunch(e, B) { + var x, y, i; + if (e >= 0) return e; + x = Crunch(Car(e), B); + y = Crunch(Cdr(e), B); + for (i = B - 2; i >= cx; i -= 2) { + if (x == Car(i) && + y == Cdr(i)) { + return i - B; + } + } + return Cons(x, y) - B; +} + +function Compact(x) { + var C, B = cx, A = 0; + x = Crunch(x, B), C = cx; + while (C < B) Set(--A, Get(--B)); + return cx = A, x; +} + +function Remove(x, y) { + if (!y) return y; + if (x == Car(Car(y))) return Cdr(y); + return Cons(Car(y), Remove(x, Cdr(y))); +} + +function Define(x, a) { + return Compact(Cons(x, Remove(Car(x), a))); } //
` @@ -283,6 +330,7 @@ Ord(c) { } Throw(x) { + if (fail < 255) ++fail; longjmp(undefined, ~x); } @@ -290,7 +338,7 @@ PrintChar(b) { fputwc(b, stdout); } -SaveMachine(a) { +SaveAlist(a) { } ReadChar() { @@ -317,7 +365,7 @@ ReadChar() { dx = c; return t; } else { - exit(0); + exit(fail); } } @@ -344,8 +392,8 @@ main(argc, argv) if (!(x = setjmp(undefined))) { x = Read(); if (x < 0 && Car(x) == kDefine) { - a = Define(0, Cdr(x), a); - SaveMachine(a); + a = Define(Cdr(x), a); + SaveAlist(a); continue; } x = Eval(x, a); @@ -365,16 +413,13 @@ main(argc, argv) // JavaScript Specific Code for https://justine.lol/ var a, code, index, output, funcall, M, Null; -var eInput, eOutput, eEval, eReset, eLoad, eTrace, ePrograms; +var eOutput, eEval, eReset, eLoad, eTrace, ePrograms, eDump; +var eGets, eSets, eMs, eAtoms, eCode, eHeap, eReads, eWrites, eClear; function Throw(x) { throw x; } -function Ord(s) { - return s.charCodeAt(0); -} - function Reset() { var i; a = 0; @@ -382,66 +427,116 @@ function Reset() { cHeap = 0; cGets = 0; cSets = 0; + cReads = 0; cPrints = 0; Null = 16384; M = new Array(Null * 2); - for (i = 0; i < M.length; ++i) { - M[i] = 0; /* make json smaller */ - } + // for (i = 0; i < M.length; ++i) { + // M[i] = 0; /* make json smaller */ + // } Load("NIL T EQ CAR CDR ATOM COND CONS QUOTE DEFINE "); LoadBuiltins() } -function PrintChar(c) { +function PrintU16(c) { output += String.fromCharCode(c); } +function IsHighSurrogate(c) { + return (0xfc00 & c) == 0xd800; +} + +function IsLowSurrogate(c) { + return (0xfc00 & c) == 0xdc00; +} + +function GetHighSurrogate(c) { + return ((c - 0x10000) >> 10) + 0xD800; +} + +function GetLowSurrogate(c) { + return ((c - 0x10000) & 1023) + 0xDC00; +} + +function ComposeUtf16(c, d) { + return ((c - 0xD800) << 10) + (d - 0xDC00) + 0x10000; +} + +function PrintChar(c) { + if (c < 0x10000) { + PrintU16(c); + } else if (c < 0x110000) { + PrintU16(GetHighSurrogate(c)); + PrintU16(GetLowSurrogate(c)); + } else { + PrintU16(0xFFFD); + } +} + +function Ord(s) { + var c, d; + c = s.charCodeAt(0); + if (IsHighSurrogate(c)) { + if (code.length > 1 && IsLowSurrogate((d = s.charCodeAt(1)))) { + c = ComposeUtf16(c, d); + } else { + c = 0xFFFD; + } + } else if (IsLowSurrogate(c)) { + c = 0xFFFD; + } + return c; +} + function ReadChar() { - var ax; + var c, d, t; if (code.length) { - ax = dx; if (index < code.length) { - dx = code.charCodeAt(index++); + c = code.charCodeAt(index++); + if (IsHighSurrogate(c)) { + if (index < code.length && + IsLowSurrogate((d = code.charCodeAt(index)))) { + c = ComposeUtf16(c, d), ++index; + } else { + c = 0xFFFD; + } + } else if (IsLowSurrogate(c)) { + c = 0xFFFD; + } } else { code = ""; - dx = 0; + c = 0; } - return ax; + t = dx; + dx = c; + return t; } else { Throw(0); } } -function GetMillis() { - if (typeof performance != "undefined") { - return performance.now(); - } else { - return 0; - } -} - function Lisp() { var x, A, d, t; d = 0; cGets = 0; cSets = 0; cHeap = cx; + cReads = 0; cPrints = 0; output = ""; while (dx) { if (dx <= Ord(' ')) { ReadChar(); } else { + t = GetMillis(); A = cx; try { x = Read(); if (x < 0 && Car(x) == kDefine) { - a = Define(0, Cdr(x), a); + a = Define(Cdr(x), a); continue; } - t = GetMillis(); x = Eval(x, a); - d += GetMillis() - t; } catch (z) { PrintChar(Ord('?')); x = z; @@ -449,45 +544,69 @@ function Lisp() { Print(x); PrintChar(Ord('\n')); Gc(A, 0); + d += GetMillis() - t; } } eOutput.innerText = output; - SaveMachine(a); + SaveAlist(a); SaveOutput(); ReportUsage(d); } function Load(s) { + index = 0; + dx = Ord(' '); code = s + "\n"; - dx = Ord(s); - index = 1; } function OnEval() { - Load(eInput.value.toUpperCase()); + Load(g_editor.getValue()); Lisp(); + SetStorage("input", g_editor.getValue()); +} + +function OnBeforeUnload() { + SetStorage("input", g_editor.getValue()); +} + +function OnDump() { + var t; + output = ""; + t = GetMillis(); + DumpDefines(a); + eOutput.innerText = output; + t = GetMillis() - t; + SaveOutput(); + ReportUsage(t); } -function OnReset() { +function OnReset(e) { var t; output = ""; t = GetMillis(); try { - Dump(a); + if (!e.shiftKey) DumpDefines(a); eOutput.innerText = output; Reset(); } catch (e) { /* ignored */ } t = GetMillis() - t; - localStorage.removeItem("sectorlisp.machine"); + RemoveStorage("alist"); SaveOutput(); ReportUsage(t); } +function OnClear() { + output = ""; + eOutput.innerText = output; + SaveOutput(); + ReportUsage(0); +} + function OnTrace() { var t; - Load(eInput.value); + Load(g_editor.getValue()); t = panic; depth = 0; panic = 10000; @@ -498,39 +617,54 @@ function OnTrace() { } function OnLoad() { - ePrograms.classList.toggle("show"); + if (ePrograms.className == "dropdown-content") { + ePrograms.className = "dropdown-content show"; + } else { + ePrograms.className = "dropdown-content"; + } } -function OnWindowClick(event) { - if (!event.target.matches("#load")) { - ePrograms.classList.remove("show"); +function OnWindowClick(e) { + if (e.target && !e.target.matches("#load")) { + ePrograms.className = "dropdown-content"; } } -function SaveMachine(a) { - var machine; - if (typeof localStorage != "undefined") { - machine = [M, a, cx]; - localStorage.setItem("sectorlisp.machine", JSON.stringify(machine)); +function OnWindowKeyDown(e) { + if (e.key == "Escape") { + ePrograms.className = "dropdown-content"; } } +function SaveAlist(a) { + output = ""; + DumpAlist(a); + SetStorage("alist", output); +} + function RestoreMachine() { - var machine; - if (typeof localStorage != "undefined" && - (machine = JSON.parse(localStorage.getItem("sectorlisp.machine")))) { - M = machine[0]; - a = machine[1]; - cx = machine[2]; + var v; + if ((v = GetStorage("output"))) { + eOutput.innerText = v; + } + if ((v = GetStorage("input"))) { + g_editor.setValue(v); + } + if ((v = GetStorage("alist"))) { + Reset(); + Load(v); + a = Compact(Read()); + } else if ((v = JSON.parse(GetStorage("machine")))) { + M = v[0]; + a = v[1]; + cx = v[2]; cHeap = cx; } } function SaveOutput() { - if (typeof localStorage != "undefined") { - localStorage.setItem("input", document.getElementById("input").value); - localStorage.setItem("output", eOutput.innerText); - } + SetStorage("input", g_editor.getValue()); + SetStorage("output", eOutput.innerText); } function FormatInt(i) { @@ -541,19 +675,21 @@ function FormatDuration(d) { return d ? Math.round(d * 1000) / 1000 : 0; } -function ReportUsage(d) { - var i, c, s; - for (c = i = 0; i < Null; i += 2) { - if (M[Null + i]) ++c; +function ReportUsage(ms) { + var i, atom, code, heap; + code = -cx >> 1; + heap = -cHeap >> 1; + for (atom = i = 0; i < Null / 2; ++i) { + if (M[Null + i]) ++atom; } - document.getElementById("ops").innerText = - FormatInt(cGets) + " gets / " + - FormatInt(cSets) + " sets / " + - FormatDuration(d) + " ms"; - document.getElementById("mem").innerText = - FormatInt((-cx >> 1) + c) + " / " + - FormatInt((-cHeap >> 1) + c) + " / " + - FormatInt(Null) + " doublewords"; + if (eGets) eGets.innerText = FormatInt(cGets); + if (eSets) eSets.innerText = FormatInt(cSets); + if (eMs) eMs.innerText = FormatInt(ms); + if (eAtoms) eAtoms.innerText = FormatInt(atom); + if (eCode) eCode.innerText = FormatInt(code); + if (eHeap) eHeap.innerText = FormatInt(heap - code); + if (eReads) eReads.innerText = FormatInt(cReads); + if (ePrints) ePrints.innerText = FormatInt(cPrints); } function Discount(f) { @@ -570,30 +706,66 @@ function Discount(f) { }; } +function GetMillis() { + if (typeof performance != "undefined") { + return performance.now(); + } else { + return 0; + } +} + +function GetStorage(k) { + if (typeof localStorage != "undefined") { + return localStorage.getItem(g_lisp + "." + k); + } else { + return null; + } +} + +function RemoveStorage(k) { + if (typeof localStorage != "undefined") { + localStorage.removeItem(g_lisp + "." + k); + } +} + +function SetStorage(k, v) { + if (typeof localStorage != "undefined") { + localStorage.setItem(g_lisp + "." + k, v); + } +} + function SetUp() { funcall = Funcall; Read = Discount(Read); Print = Discount(Print); Define = Discount(Define); eLoad = document.getElementById("load"); - eInput = document.getElementById("input"); eReset = document.getElementById("reset"); eTrace = document.getElementById("trace"); eOutput = document.getElementById("output"); eEval = document.getElementById("eval"); + eClear = document.getElementById("clear"); + eDump = document.getElementById("dump"); ePrograms = document.getElementById("programs"); - window.onclick = OnWindowClick; - eLoad.onclick = OnLoad; - eReset.onclick = OnReset; - eTrace.onclick = OnTrace; - eEval.onclick = OnEval; - Reset(); - RestoreMachine(); - ReportUsage(); + eGets = document.getElementById("cGets"); + eSets = document.getElementById("cSets"); + eMs = document.getElementById("cMs"); + eAtoms = document.getElementById("cAtoms"); + eCode = document.getElementById("cCode"); + eHeap = document.getElementById("cHeap"); + eReads = document.getElementById("cReads"); + ePrints = document.getElementById("cPrints"); + window.onkeydown = OnWindowKeyDown; + if (window.onbeforeunload) window.onbeforeunload = OnBeforeUnload; + if (ePrograms) window.onclick = OnWindowClick; + if (eLoad) eLoad.onclick = OnLoad; + if (eReset) eReset.onclick = OnReset; + if (eTrace) eTrace.onclick = OnTrace; + if (eEval) eEval.onclick = OnEval; + if (eDump) eDump.onclick = OnDump; + if (eClear) eClear.onclick = OnClear; } -SetUp(); - //
` #endif //` From 1b0a03013b4562e9c636eeddda5a14016771fb3e Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 22 Dec 2021 05:35:58 -0800 Subject: [PATCH 27/29] Shave a few bytes --- hash.c | 112 ++++++++------------------------------------------- lisp.js | 1 + sectorlisp.S | 6 +-- 3 files changed, 20 insertions(+), 99 deletions(-) diff --git a/hash.c b/hash.c index cba0a44..e41d393 100644 --- a/hash.c +++ b/hash.c @@ -4,13 +4,6 @@ #endif #define word unsigned long -#define dubs unsigned __int128 - -struct Bar { - dubs r; - word n; - char k; -}; static word Null; @@ -18,23 +11,6 @@ static inline int IsTwoPow(word x) { return !(x & (x - 1)); } -static inline int Bsf(word x) { -#if defined(__GNUC__) && !defined(__STRICT_ANSI__) - return __builtin_ctzll(x); -#else - uint32_t l, r; - x &= -x; - l = x | x >> 32; - r = !!(x >> 32), r <<= 1; - r += !!(l & 0xffff0000), r <<= 1; - r += !!(l & 0xff00ff00), r <<= 1; - r += !!(l & 0xf0f0f0f0), r <<= 1; - r += !!(l & 0xcccccccc), r <<= 1; - r += !!(l & 0xaaaaaaaa); - return r; -#endif -} - static inline int Bsr(word x) { #if defined(__GNUC__) && !defined(__STRICT_ANSI__) return __builtin_clzll(x) ^ 63; @@ -55,69 +31,6 @@ static inline int Bsr(word x) { #endif } -static inline word Log(word x) { - return --x ? Bsr(x) + 1 : 0; -} - -static struct Bar Bar(word n) { - struct Bar m; - m.r = 1; - m.n = n; - m.k = Log(n) << 1; - m.r = (m.r << m.k) / n; - return m; -} - -static word Mod(struct Bar m, dubs x) { - dubs t; - t = x - ((x * m.r) >> m.k) * m.n; - if (t >= m.n) t -= m.n; - return t; -} - -static word Mul(struct Bar m, word x, word y) { - dubs t = x; - return Mod(m, t * y); -} - -static word Pow(struct Bar m, word a, word n) { - word p, r; - for (p = a, r = 1; n; n >>= 1) { - if (n & 1) r = Mul(m, r, p); - p = Mul(m, p, p); - } - return r; -} - -static int W(struct Bar m, word a) { - word x, y, s; - s = Bsf(m.n >> 1) + 1; - x = Pow(m, a, m.n >> s); - for (y = 0; s; --s, x = y) { - y = Mul(m, x, x); - if (y == 1 && x != 1 && x != m.n - 1) { - return 0; - } - } - return y == 1; -} - -static int MillerTime(word n) { - struct Bar m; - if (n < 2) return 0; - if (n <= 3) return 1; - if (~n & 1) return 0; - if (n % 3 == 0) return 0; - m = Bar(n); - if (n < 1373653) return W(m,2) && W(m,3); - if (n < 9080191) return W(m,31) && W(m,73); - if (n < 4759123141) return W(m,2) && W(m,7) && W(m,61); - if (n < 1122004669633) return W(m,2) && W(m,13) && W(m,23) && W(m,1662803); - if (n < 2152302898747) return W(m,2) && W(m,3) && W(m,5) && W(m,7) && W(m,11); - if (n < 3474749660383) return W(m,2) && W(m,3) && W(m,5) && W(m,7) && W(m,11) && W(m,13); - return W(m,2) && W(m,3) && W(m,5) && W(m,7) && W(m,11) && W(m,13) && W(m,17); -} - static word Hash(word h, word x, word a, word b, word c) { return (((h + x) * a + b) >> c) & (Null / 2 - 1); } @@ -138,7 +51,7 @@ int main(int argc, char *argv[]) { word a, b, c; if (argc > 1) { Null = strtoul(argv[1], 0, 0); - if (Null < 128) { + if (Null < 64) { fprintf(stderr, "Error: Null is too small\n"); return Usage(argv[0]); } @@ -147,15 +60,24 @@ int main(int argc, char *argv[]) { return Usage(argv[0]); } } else { - Null = 040000; + /* Null = 040000; */ + Null = 64; } - for (a = 2; a < Null; ++a) { - if (!MillerTime(a)) continue; - for (c = 0; c <= Bsr(Null / 2); ++c) { - for (b = 0; b < Null; ++b) { + for (;; Null <<= 1) { + printf("\n"); + printf("#define Null %#lo\n", Null); + fflush(stdout); + for (a = 0; a < Null / 2; ++a) { + for (c = 0; c <= Bsr(Null / 2) / 2; ++c) { + /* for (b = 0; b < Null; ++b) { */ + /* solve 1 = ('T' * a + b) / 2^c for b */ + b = (((1<> %lu) & %#lo;\n", a, b, c, - Null / 2 - 1); + if (c) { + printf("return (((h + x) * %lu + %lu) >> %lu) & (Null/2-1);\n", a, b, c); + } else { + printf("return ((h + x) * %lu + %lu) & (Null/2-1);\n", a, b); + } } } } diff --git a/lisp.js b/lisp.js index f28dc47..afe28ce 100755 --- a/lisp.js +++ b/lisp.js @@ -56,6 +56,7 @@ function Cdr(x) { } function Cons(car, cdr) { + if (cx == -Null) Throw(kCons); Set(--cx, cdr); Set(--cx, car); if (cx < cHeap) cHeap = cx; diff --git a/sectorlisp.S b/sectorlisp.S index 237588d..03c5b4f 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -194,12 +194,10 @@ Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx test %si,%si # DEFAULT NIL ARGS jz 1f lodsw # ax = Car(y) + mov (%si),%si # si = Cdr(y) 1: push (%bx,%di) # push Cdr(x) mov (%di),%di # di = Car(x) - test %si,%si - jz 1f - mov (%si),%si # si = Cdr(y) -1: call Cons # Cons(Car(x),Car(y)) + call Cons # Cons(Car(x),Car(y)) xchg %ax,%di xchg %dx,%ax call Cons # Cons(Cons(Car(x),Car(y)),a) From 38acb277d0e7536e134a0ca9599521653c946475 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 4 Jan 2023 07:53:33 -0800 Subject: [PATCH 28/29] Add linker scripts --- Makefile | 9 +++++---- brainfuck.lds | 12 ++++++++++++ sectorlisp.lds | 12 ++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 brainfuck.lds create mode 100644 sectorlisp.lds diff --git a/Makefile b/Makefile index c48f981..5ef809f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ CFLAGS = -w -g -O2 +LDFLAGS = -z max-page-size=512 CLEANFILES = \ lisp \ @@ -30,8 +31,8 @@ bestline.o: bestline.c bestline.h sectorlisp.o: sectorlisp.S $(AS) -g -o $@ $< -sectorlisp.bin.dbg: sectorlisp.o - $(LD) -oformat:binary -Ttext=0x0000 -o $@ $< +sectorlisp.bin.dbg: sectorlisp.o sectorlisp.lds + $(LD) $(LDFLAGS) -T sectorlisp.lds -o $@ $< sectorlisp.bin: sectorlisp.bin.dbg objcopy -S -O binary sectorlisp.bin.dbg sectorlisp.bin @@ -39,8 +40,8 @@ sectorlisp.bin: sectorlisp.bin.dbg brainfuck.o: brainfuck.S $(AS) -g -o $@ $< -brainfuck.bin.dbg: brainfuck.o - $(LD) -oformat:binary -Ttext=0x7c00 -o $@ $< +brainfuck.bin.dbg: brainfuck.o brainfuck.lds + $(LD) $(LDFLAGS) -T brainfuck.lds -o $@ $< brainfuck.bin: brainfuck.bin.dbg objcopy -S -O binary brainfuck.bin.dbg brainfuck.bin diff --git a/brainfuck.lds b/brainfuck.lds new file mode 100644 index 0000000..08650ca --- /dev/null +++ b/brainfuck.lds @@ -0,0 +1,12 @@ +ENTRY(_start) + +SECTIONS { + . = 0x7c00; + .text : { + *(.text) + *(.rodata .rodata.*) + } + /DISCARD/ : { + *(.*) + } +} diff --git a/sectorlisp.lds b/sectorlisp.lds new file mode 100644 index 0000000..eb4ac07 --- /dev/null +++ b/sectorlisp.lds @@ -0,0 +1,12 @@ +ENTRY(_start) + +SECTIONS { + . = 0; + .text : { + *(.text) + *(.rodata .rodata.*) + } + /DISCARD/ : { + *(.*) + } +} From ec59e26728a6a895d6f7901bb3ab7bc69f0c7d1a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 23 Feb 2023 13:20:14 -0800 Subject: [PATCH 29/29] Fix build on RHEL7 Fixes #26 --- Makefile | 11 +++++------ bestline.c | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 5ef809f..c1d245f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,4 @@ -CFLAGS = -w -g -O2 -LDFLAGS = -z max-page-size=512 +CFLAGS ?= -w -O CLEANFILES = \ lisp \ @@ -29,19 +28,19 @@ lisp.o: lisp.js bestline.h bestline.o: bestline.c bestline.h sectorlisp.o: sectorlisp.S - $(AS) -g -o $@ $< + $(AS) $(ASFLAGS) -o $@ $< sectorlisp.bin.dbg: sectorlisp.o sectorlisp.lds - $(LD) $(LDFLAGS) -T sectorlisp.lds -o $@ $< + $(LD) -z max-page-size=512 $(LDFLAGS) -T sectorlisp.lds -o $@ $< sectorlisp.bin: sectorlisp.bin.dbg objcopy -S -O binary sectorlisp.bin.dbg sectorlisp.bin brainfuck.o: brainfuck.S - $(AS) -g -o $@ $< + $(AS) $(ASFLAGS) -o $@ $< brainfuck.bin.dbg: brainfuck.o brainfuck.lds - $(LD) $(LDFLAGS) -T brainfuck.lds -o $@ $< + $(LD) -z max-page-size=512 $(LDFLAGS) -T brainfuck.lds -o $@ $< brainfuck.bin: brainfuck.bin.dbg objcopy -S -O binary brainfuck.bin.dbg brainfuck.bin diff --git a/bestline.c b/bestline.c index a9a32bd..30ce69b 100644 --- a/bestline.c +++ b/bestline.c @@ -143,6 +143,7 @@ #include #include #include +#include #include #include #include