diff --git a/Makefile b/Makefile index 4944128..d53571a 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +CFLAGS = -w -Os +LDFLAGS = -s + CLEANFILES = \ lisp \ lisp.o \ diff --git a/lisp.c b/lisp.c index f9b7477..07bc23c 100644 --- a/lisp.c +++ b/lisp.c @@ -20,232 +20,141 @@ #ifndef __COSMOPOLITAN__ #include +#include #include #include -#include +#include +#include #endif -#define QUOTES 1 /* allow 'X shorthand for (QUOTE X) */ -#define FUNDEF 1 /* be friendly w/undefined behavior */ -#define TRACE 0 /* prints Eval() arguments / result */ - /*───────────────────────────────────────────────────────────────────────────│─╗ │ The LISP Challenge § LISP Machine ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -#define ATOM 1 -#define CONS 0 +#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 ISATOM(x) ((x)&1) -#define VALUE(x) ((x)>>1) -#define OBJECT(t,v) ((v)<<1|(t)) +#define M (RAM + sizeof(RAM) / sizeof(RAM[0]) / 2) +#define S "NIL\0T\0QUOTE\0COND\0ATOM\0CAR\0CDR\0CONS\0EQ" -#define NIL OBJECT(ATOM,0) -#define ATOM_T OBJECT(ATOM,4) -#define ATOM_QUOTE OBJECT(ATOM,6) -#define ATOM_COND OBJECT(ATOM,12) -#define ATOM_ATOM OBJECT(ATOM,17) -#define ATOM_CAR OBJECT(ATOM,22) -#define ATOM_CDR OBJECT(ATOM,26) -#define ATOM_CONS OBJECT(ATOM,30) -#define ATOM_EQ OBJECT(ATOM,35) -#define ATOM_LAMBDA OBJECT(ATOM,38) -#define UNDEFINED OBJECT(ATOM,45) +int cx; /* stores negative memory use */ +int dx; /* stores lookahead character */ +int RAM[0100000]; /* your own ibm7090 */ -const char kSymbols[] = - "NIL\0" - "T\0" - "QUOTE\0" - "COND\0" - "ATOM\0" - "CAR\0" - "CDR\0" - "CONS\0" - "EQ\0" - "LAMBDA\0" -#if FUNDEF - "*UNDEFINED" -#endif -; - -int g_look; -int g_index; -char g_token[128]; -int g_mem[8192]; -char g_str[8192]; - -int GetList(void); -int GetObject(void); -void PrintObject(int); -int Eval(int, int); - -void SetupBuiltins(void) { - memmove(g_str, kSymbols, sizeof(kSymbols)); +Car(x) { + return M[x]; } -int Car(int x) { - return g_mem[VALUE(x) + 0]; +Cdr(x) { + return M[x + 1]; } -int Cdr(int x) { - return g_mem[VALUE(x) + 1]; +Cons(car, cdr) { + M[--cx] = cdr; + M[--cx] = car; + return cx; } -int Cons(int car, int cdr) { - int i, cell; - i = g_index; - g_mem[i + 0] = car; - g_mem[i + 1] = cdr; - g_index = i + 2; - cell = OBJECT(CONS, i); - return cell; +Gc(x, m, k) { + return x < m ? Cons(Gc(Car(x), m, k), + Gc(Cdr(x), m, k)) + k : x; } -char *StpCpy(char *d, char *s) { - char c; - do { - c = *s++; - *d++ = c; - } while (c); - return d; -} - -int Intern(char *s) { - int j, cx; - char c, *z, *t; - z = g_str; - c = *z++; - while (c) { +Intern() { + int i, j, x; + for (i = 0; (x = M[i++]);) { for (j = 0;; ++j) { - if (c != s[j]) { - break; - } - if (!c) { - return OBJECT(ATOM, z - g_str - j - 1); - } - c = *z++; + if (x != RAM[j]) break; + if (!x) return i - j - 1; + x = M[i++]; } - while (c) c = *z++; - c = *z++; + while (x) + x = M[i++]; } - --z; - StpCpy(z, s); - return OBJECT(ATOM, z - g_str); + j = 0; + x = --i; + while ((M[i++] = RAM[j++])); + return x; } -void PrintChar(unsigned char b) { - if (write(1, &b, 1) == -1) exit(1); -} - -void PrintString(const char *s) { - char c; - for (;;) { - if (!(c = s[0])) break; - PrintChar(c); - ++s; - } -} - -int GetChar(void) { - int b; +GetChar() { + int c, t; static char *l, *p; if (l || (l = p = bestlineWithHistory("* ", "sectorlisp"))) { if (*p) { - b = *p++ & 255; + c = *p++ & 255; } else { free(l); l = p = 0; - b = '\n'; + c = '\n'; } - return b; + t = dx; + dx = c; + return t; } else { - PrintString("\n"); + PrintChar('\n'); exit(0); } } -void GetToken(void) { - int al; - char *di; - di = g_token; - do { - if (g_look > ' ') { - *di++ = g_look; - } - al = g_look; - g_look = GetChar(); - } while (al <= ' ' || (al > ')' && g_look > ')')); - *di++ = 0; +PrintChar(b) { + fputwc(b, stdout); } -int ConsumeObject(void) { - GetToken(); - return GetObject(); +GetToken() { + int c, i = 0; + do if ((c = GetChar()) > ' ') RAM[i++] = c; + while (c <= ' ' || (c > ')' && dx > ')')); + RAM[i] = 0; + return c; } -int List(int x, int y) { - return Cons(x, Cons(y, NIL)); -} - -int Quote(int x) { - return List(ATOM_QUOTE, x); -} - -int GetQuote(void) { - return Quote(ConsumeObject()); -} - -int AddList(int x) { +AddList(x) { return Cons(x, GetList()); } -int GetList(void) { - GetToken(); -#if QUOTES - if (*g_token == '\'') return AddList(GetQuote()); -#endif - if (*g_token == ')') return NIL; - return AddList(GetObject()); +GetList() { + int c = GetToken(); + if (c == ')') return 0; + return AddList(GetObject(c)); } -int GetObject(void) { -#if QUOTES - if (*g_token == '\'') return GetQuote(); -#endif - if (*g_token == '(') return GetList(); - return Intern(g_token); +GetObject(c) { + if (c == '(') return GetList(); + return Intern(); } -int ReadObject(void) { - g_look = GetChar(); - GetToken(); - return GetObject(); +ReadObject() { + return GetObject(GetToken()); } -int Read(void) { +Read() { return ReadObject(); } -void PrintAtom(int x) { - PrintString(g_str + VALUE(x)); +PrintAtom(x) { + int c; + for (;;) { + if (!(c = M[x++])) break; + PrintChar(c); + } } -void PrintList(int x) { -#if QUOTES - if (Car(x) == ATOM_QUOTE) { - PrintChar('\''); - PrintObject(Car(Cdr(x))); - return; - } -#endif +PrintList(x) { PrintChar('('); PrintObject(Car(x)); - while ((x = Cdr(x)) != NIL) { - if (!ISATOM(x)) { + while ((x = Cdr(x)) != 0) { + if (x < 0) { PrintChar(' '); PrintObject(Car(x)); } else { - PrintString("∙"); + PrintChar(L'∙'); PrintObject(x); break; } @@ -253,130 +162,92 @@ void PrintList(int x) { PrintChar(')'); } -void PrintObject(int x) { - if (ISATOM(x)) { - PrintAtom(x); - } else { +PrintObject(x) { + if (x < 0) { PrintList(x); + } else { + PrintAtom(x); } } -void Print(int i) { - PrintObject(i); - PrintString("\n"); +Print(e) { + PrintObject(e); + PrintChar('\n'); } /*───────────────────────────────────────────────────────────────────────────│─╗ │ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -int Assoc(int x, int y) { - if (y == NIL) return NIL; - if (x == Car(Car(y))) return Cdr(Car(y)); - return Assoc(x, Cdr(y)); +Pairlis(x, y, a) { + if (!x) return a; + return Cons(Cons(Car(x), Car(y)), + Pairlis(Cdr(x), Cdr(y), a)); } -int Evcon(int c, int a) { - if (Eval(Car(Car(c)), a) != NIL) { +Evlis(m, a) { + if (!m) return 0; + return Cons(Eval(Car(m), a), + Evlis(Cdr(m), a)); +} + +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 == 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)); +} + +Evaluate(e, a) { + if (e < 0) { + if (Car(e) == kQuote) return Car(Cdr(e)); + if (Car(e) == kCond) return Evcon(Cdr(e), a); + return Apply(Car(e), Evlis(Cdr(e), a), a); + } + return Assoc(e, a); +} + +Evcon(c, a) { + if (Eval(Car(Car(c)), a)) { return Eval(Car(Cdr(Car(c))), a); } else { return Evcon(Cdr(c), a); } } -int Pairlis(int x, int y, int a) { - int di, si; /* it's zip() basically */ - if (x == NIL) return a; - di = Cons(Car(x), Car(y)); - si = Pairlis(Cdr(x), Cdr(y), a); - return Cons(di, si); /* Tail-Modulo-Cons */ +Assoc(x, y) { + if (!y) return 0; + if (x == Car(Car(y))) return Cdr(Car(y)); + return Assoc(x, Cdr(y)); } -int Evlis(int m, int a) { - int di, si; - if (m == NIL) return NIL; - di = Eval(Car(m), a); - si = Evlis(Cdr(m), a); - return Cons(di, si); -} - -int Apply(int fn, int x, int a) { - int t1, si, ax; - if (ISATOM(fn)) { - switch (fn) { -#if FUNDEF - case NIL: - return UNDEFINED; -#endif - case ATOM_CAR: - return Car(Car(x)); - case ATOM_CDR: - return Cdr(Car(x)); - case ATOM_ATOM: - return ISATOM(Car(x)) ? ATOM_T : NIL; - case ATOM_CONS: - return Cons(Car(x), Car(Cdr(x))); - case ATOM_EQ: - return Car(x) == Car(Cdr(x)) ? ATOM_T : NIL; - default: - return Apply(Eval(fn, a), x, a); - } - } - if (Car(fn) == ATOM_LAMBDA) { - t1 = Cdr(fn); - si = Pairlis(Car(t1), x, a); - ax = Car(Cdr(t1)); - return Eval(ax, si); - } - return UNDEFINED; -} - -int Evaluate(int e, int a) { - int ax; - if (ISATOM(e)) - return Assoc(e, a); - ax = Car(e); - if (ISATOM(ax)) { - if (ax == ATOM_QUOTE) - return Car(Cdr(e)); - if (ax == ATOM_COND) - return Evcon(Cdr(e), a); - } - return Apply(ax, Evlis(Cdr(e), a), a); -} - -int Eval(int e, int a) { - int ax; -#if TRACE - PrintString("> "); - PrintObject(e); - PrintString("\r\n "); - PrintObject(a); - PrintString("\r\n"); -#endif - ax = Evaluate(e, a); -#if TRACE - PrintString("< "); - PrintObject(ax); - PrintString("\r\n"); -#endif - return ax; +Eval(e, a) { + int A, B, C; + A = cx; + e = Evaluate(e, a); + B = cx; + e = Gc(e, A, A - B); + C = cx; + while (C < B) + M[--A] = M[--B]; + cx = A; + return e; } /*───────────────────────────────────────────────────────────────────────────│─╗ │ The LISP Challenge § User Interface ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -void Repl(void) { +main() { + int i; + setlocale(LC_ALL, ""); + bestlineSetXlatCallback(bestlineUppercase); + for(i = 0; i < sizeof(S); ++i) M[i] = S[i]; for (;;) { - Print(Eval(Read(), NIL)); + cx = 0; + Print(Eval(Read(), 0)); } } - -int main(int argc, char *argv[]) { - SetupBuiltins(); - bestlineSetXlatCallback(bestlineUppercase); - PrintString("THE LISP CHALLENGE V1\r\n" - "VISIT GITHUB.COM/JART\r\n"); - Repl(); -} diff --git a/sectorlisp.S b/sectorlisp.S index fab64ec..4d8e986 100644 --- a/sectorlisp.S +++ b/sectorlisp.S @@ -22,28 +22,9 @@ // LISP meta-circular evaluator in a MBR // Compatible with the original hardware -.set g_mem, %cx -.set g_token, %cx -.set ZERO, %bh -.set TWO, %bx - -.section .text,"ax",@progbits -.type kNil,@object -.type kT,@object -.type kQuote,@object -.type kCond,@object -.type kAtom,@object -.type kCar,@object -.type kCdr,@object -.type kCons,@object -.type kEq,@object -.type start,@function -.type begin,@function -.globl _start -.code16 - -_start: -kNil: .asciz "NIL" # dec %si ; dec %cx ; dec %sp + .code16 + .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 .asciz "" @@ -55,20 +36,20 @@ kCdr: .asciz "CDR" # ordering matters kCons: .asciz "CONS" # ordering matters kEq: .asciz "EQ" # needs to be last -begin: xor TWO,TWO - push %cs # memory model ds=es=ss=cs - pop %ds - push %cs - pop %es - push %cs - cli # disable interrupts - pop %ss # disable nonmaskable interrupts - mov TWO,%sp # use null pointer as our stack - sti # enable interrupts - cld # direction forward - inc TWO - inc TWO -main: mov $0x8000,g_mem # dl (g_look) is zero or cr +begin: xor %bx,%bx # we use the tiny memory model + 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! + cli # disables hardware interrupts + pop %ss # disable nonmaskable ones too + mov %bx,%sp # use highest address as stack + sti # reenable hardware interrupts + cld # normalize the direction flag + inc %bx + inc %bx +main: mov $0x8000,%cx # dl (g_look) is zero or cr call GetToken call GetObject xor %dx,%dx @@ -80,7 +61,7 @@ main: mov $0x8000,g_mem # dl (g_look) is zero or cr jmp main GetToken: # GetToken():al, dl is g_look - mov g_token,%di + mov %cx,%di 1: mov %dl,%al cmp $' ',%al jbe 2f @@ -93,7 +74,7 @@ GetToken: # GetToken():al, dl is g_look jbe 3f cmp $')',%dl # dl = g_look ja 1b -3: movb ZERO,(%di) +3: movb %bh,(%di) # bh is zero xchg %si,%ax ret @@ -104,7 +85,7 @@ PrintObject: # PrintObject(x:si) jns .PrintAtom # jump if cons .PrintList: mov $'(',%al -2: push (TWO,%si) +2: push (%bx,%si) mov (%si),%si call .PutObject mov $' ',%al @@ -138,7 +119,7 @@ GetObject: # called just after GetToken jne 1b jmp 5f 2: pop %si # drop 1 - mov g_token,%si + mov %cx,%si 3: scasb jne 3b cmp (%di),%al @@ -151,15 +132,12 @@ GetObject: # called just after GetToken 5: pop %ax # restore 1 .ret: ret -GetChar: # GetChar→al:dl - xor %ax,%ax # get keystroke - int $0x16 # keyboard service - # ah is bios scancode - # al is ascii character -PutChar:mov $0x0e,%ah # teletype output al cp437 +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 # xchg dx,ax and ret + jne 1f # look xchg ret mov $'\n',%al jmp PutChar @@ -167,7 +145,7 @@ PutChar:mov $0x0e,%ah # teletype output al cp437 Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):ax jz 1f # jump if nil - push (TWO,%di) # save 1 Cdr(x) + push (%bx,%di) # save 1 Cdr(x) lodsw push (%si) # save 2 Cdr(y) mov (%di),%di @@ -182,7 +160,7 @@ Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):ax Evlis: test %di,%di # Evlis(m:di,a:dx):ax jz 1f # jump if nil - push (TWO,%di) # save 1 Cdr(m) + push (%bx,%di) # save 1 Cdr(m) mov (%di),%ax call Eval pop %di # restore 1 @@ -193,17 +171,17 @@ Evlis: test %di,%di # Evlis(m:di,a:dx):ax xCons: pop %di # restore 2 Cons: xchg %ax,%si # Cons(m:di,a:ax):ax xchg %di,%ax - mov g_mem,%di + mov %cx,%di stosw xchg %si,%ax stosw - xchg %di,g_mem + xchg %di,%cx 1: xchg %di,%ax ret Gc: cmp %dx,%di # Gc(x:di,mark:dx,aj:bp):ax jb 1b # we assume immutable cells - push (TWO,%di) # mark prevents negative gc + push (%bx,%di) # mark prevents negative gc mov (%di),%di call Gc pop %di @@ -218,11 +196,12 @@ GetList:call GetToken cmpb $')',%al je .retF call GetObject - push %ax # save 1 + push %ax # popped by xCons call GetList jmp xCons -.Eval: test %ax,%ax # Eval(e:ax,a:dx):ax w/o gc +Evaluate: # Evaluate(e:ax,a:dx):ax + test %ax,%ax # Implementation of Eval jns Assoc # lookup val if atom xchg %ax,%si # di = e lodsw # ax = Car(e) @@ -238,23 +217,7 @@ GetList:call GetToken # jmp Apply Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax - jns .switch # jump if atom - xchg %ax,%di # di = fn -.lambda:mov (TWO,%di),%di # di = Cdr(fn) - push %di # save 1 - mov (%di),%di # di = Cadr(fn) - call Pairlis - xchg %ax,%dx - pop %di # restore 1 - jmp .EvCadr -.ifCons:cmp $kCons,%al - mov (TWO,%si),%si # si = Cdr(x) - lodsw # si = Cadr(x) - je Cons -.isEq: cmp %di,%ax # we know for certain it's eq - jne .retF -.retT: mov $kT,%ax - ret + js .lamb # jump if atom .switch:cmp $kEq,%ax # eq is last builtin atom ja .dflt1 # ah is zero if not above mov (%si),%di # di = Car(x) @@ -268,12 +231,28 @@ Apply: test %ax,%ax # Apply(fn:ax,x:si:a:dx):ax jns .retT .retF: xor %ax,%ax # ax = nil ret +.lamb: xchg %ax,%di # di = fn +.lambda:mov (%bx,%di),%di # di = Cdr(fn) + push %di # save 1 + mov (%di),%di # di = Cadr(fn) + call Pairlis + xchg %ax,%dx + pop %di # restore 1 + jmp .EvCadr +.ifCons:cmp $kCons,%al + mov (%bx,%si),%si # si = Cdr(x) + lodsw # si = Cadr(x) + je Cons +.isEq: cmp %di,%ax # we know for certain it's eq + jne .retF +.retT: mov $kT,%ax + ret .dflt1: push %si # save x call Eval pop %si # restore x jmp Apply -Cadr: mov (TWO,%di),%di # contents of decrement register +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!! @@ -282,13 +261,13 @@ Car: mov (%di),%ax # contents of address register!! Assoc: mov %dx,%di # Assoc(x:ax,y:dx):ax test %dx,%dx # nil test jz .retF # return nil if end of list - mov (TWO,%di),%dx # we assume Eval() saved dx + mov (%bx,%di),%dx # we assume Eval() saved dx mov (%di),%di scasw jne Assoc jmp Car -1: mov (TWO,%di),%di # di = Cdr(c) +1: mov (%bx,%di),%di # di = Cdr(c) Evcon: push %di # save c mov (%di),%si # di = Car(c) lodsw # ax = Caar(c) @@ -301,23 +280,29 @@ Evcon: push %di # save c # jmp Eval Eval: push %dx # Eval(e:ax,a:dx):ax w/ gc - push g_mem # with garbage collections - call .Eval # discards non-result cons + push %cx # w/ ABC garbage collector + call Evaluate # discards non-result cons pop %dx - push g_mem - mov g_mem,%bp + push %cx + mov %cx,%bp sub %dx,%bp xchg %ax,%di call Gc pop %si mov %dx,%di - sub %si,%cx # cx = g_mem - si + sub %si,%cx rep movsb - mov %di,g_mem + mov %di,%cx pop %dx ret -.type .sig,@object -.sig: -.fill 510 - (. - _start), 1, 0xce -.word 0xAA55 +.sig: .fill 510 - (. - _start), 1, 0xce + .word 0xAA55 + .type .sig,@object + .type kQuote,@object + .type kCond,@object + .type kAtom,@object + .type kCar,@object + .type kCdr,@object + .type kCons,@object + .type kEq,@object