Make DEFINE more like Scheme

This commit is contained in:
Justine Tunney 2021-12-12 09:41:52 -08:00
parent a25d58bddd
commit f6e8f51307
2 changed files with 178 additions and 85 deletions

218
lisp.js
View file

@ -26,19 +26,24 @@ exit
#define function #define function
#define Null 16384 #define Null 16384
var M[Null * 2]; var M[Null * 2];
var (*funcall)();
jmp_buf undefined; jmp_buf undefined;
//` //`
var cx, dx, lo, kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine; var cx, dx, depth, panic;
var cHeap, cGets, cSets, cPrints;
function Set(i, x) { var kT, kEq, kCar, kCdr, kCond, kAtom, kCons, kQuote, kDefine;
M[Null + i] = x;
}
function Get(i) { function Get(i) {
++cGets;
return M[Null + i]; return M[Null + i];
} }
function Set(i, x) {
++cSets;
M[Null + i] = x;
}
function Car(x) { function Car(x) {
if (x > 0) Throw(List(kCar, x)); if (x > 0) Throw(List(kCar, x));
return x ? Get(x) : +0; return x ? Get(x) : +0;
@ -52,7 +57,7 @@ function Cdr(x) {
function Cons(car, cdr) { function Cons(car, cdr) {
Set(--cx, cdr); Set(--cx, cdr);
Set(--cx, car); Set(--cx, car);
if (cx < lo) lo = cx; if (cx < cHeap) cHeap = cx;
return cx; return cx;
} }
@ -77,11 +82,6 @@ function ReadAtom(h) {
Hash(h, c) - Hash(0, Ord('N'))); Hash(h, c) - Hash(0, Ord('N')));
} }
function PrintAtom(x) {
do PrintChar(Get(x));
while ((x = Get(x + 1)));
}
function ReadList() { function ReadList() {
var x; var x;
if ((x = Read()) > 0) { if ((x = Read()) > 0) {
@ -100,17 +100,33 @@ function ReadObject(t) {
return ReadList(); return ReadList();
} }
function Read() {
return ReadObject(ReadAtom(0));
}
function PrintAtom(x) {
do PrintChar(Get(x));
while ((x = Get(x + 1)));
}
function PrintList(x) { function PrintList(x) {
PrintChar(Ord('(')); PrintChar(Ord('('));
if (x < 0) { if (x < 0) {
PrintObject(Car(x)); Print(Car(x));
while ((x = Cdr(x))) { while ((x = Cdr(x))) {
if (panic && cPrints > panic) {
PrintChar(Ord(' '));
PrintChar(0x2026);
break;
}
if (x < 0) { if (x < 0) {
PrintChar(Ord(' ')); PrintChar(Ord(' '));
PrintObject(Car(x)); Print(Car(x));
} else { } else {
PrintChar(0x2219); PrintChar(Ord(' '));
PrintObject(x); PrintChar(Ord('.'));
PrintChar(Ord(' '));
Print(x);
break; break;
} }
} }
@ -118,7 +134,8 @@ function PrintList(x) {
PrintChar(Ord(')')); PrintChar(Ord(')'));
} }
function PrintObject(x) { function Print(x) {
++cPrints;
if (1./x < 0) { if (1./x < 0) {
PrintList(x); PrintList(x);
} else { } else {
@ -126,13 +143,12 @@ function PrintObject(x) {
} }
} }
function Print(e) { function List(x, y) {
PrintObject(e); return Cons(x, Cons(y, 0));
PrintChar(Ord('\n'));
} }
function Read() { function Define(A, x, a) {
return ReadObject(ReadAtom(0)); return Gc(A, Cons(x, Remove(Car(x), a)));
} }
function Remove(x, y) { function Remove(x, y) {
@ -141,14 +157,6 @@ function Remove(x, y) {
return Cons(Car(y), Remove(x, Cdr(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) { function Gc(A, x) {
var C, B = cx; var C, B = cx;
x = Copy(x, A, A - B), C = cx; x = Copy(x, A, A - B), C = cx;
@ -167,7 +175,6 @@ function Evlis(m, a) {
} }
function Pairlis(x, y, a) { function Pairlis(x, y, a) {
if (!!x ^ !!y) Throw(List(x, y));
return x ? Cons(Cons(Car(x), Car(y)), return x ? Cons(Cons(Car(x), Car(y)),
Pairlis(Cdr(x), Cdr(y), a)) : a; Pairlis(Cdr(x), Cdr(y), a)) : a;
} }
@ -184,7 +191,7 @@ function Evcon(c, a) {
} else if (Cdr(c)) { } else if (Cdr(c)) {
return Evcon(Cdr(c), a); return Evcon(Cdr(c), a);
} else { } 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 == kAtom) return Car(x) < 0 ? 0 : kT;
if (f == kCar) return Car(Car(x)); if (f == kCar) return Car(Car(x));
if (f == kCdr) return Cdr(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) { function Eval(e, a) {
var A = cx;
if (!e) return e; if (!e) return e;
if (e > 0) return Assoc(e, a); if (e > 0) return Assoc(e, a);
if (Car(e) == kQuote) return Car(Cdr(e)); if (Car(e) == kQuote) return Car(Cdr(e));
if (Car(e) == kCond) return Gc(A, Evcon(Cdr(e), a)); if (Car(e) == kCond) return Evcon(Cdr(e), a);
return Gc(A, Apply(Car(e), Evlis(Cdr(e), a), 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() { function LoadBuiltins() {
@ -267,17 +319,24 @@ ReadChar() {
} }
} }
main() { main(argc, argv)
char *argv[];
{
var x, a, A; var x, a, A;
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
bestlineSetXlatCallback(bestlineUppercase); bestlineSetXlatCallback(bestlineUppercase);
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 't') {
funcall = Funtrace;
} else {
funcall = Funcall;
}
LoadBuiltins(); LoadBuiltins();
for (a = 0;;) { for (a = 0;;) {
A = cx; A = cx;
if (!(x = setjmp(undefined))) { if (!(x = setjmp(undefined))) {
x = Read(); x = Read();
if (x == kDefine) { if (x < 0 && Car(x) == kDefine) {
a = Gc(0, Define(Read(), a)); a = Define(0, Cdr(x), a);
SaveMachine(a); SaveMachine(a);
continue; continue;
} }
@ -287,6 +346,7 @@ main() {
PrintChar('?'); PrintChar('?');
} }
Print(x); Print(x);
PrintChar('\n');
Gc(A, 0); Gc(A, 0);
} }
} }
@ -296,8 +356,8 @@ main() {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// JavaScript Specific Code for https://justine.lol/ // JavaScript Specific Code for https://justine.lol/
var a, code, index, output, M, Null; var a, code, index, output, funcall, M, Null;
var eInput, eOutput, eSubmit, eReset, eLoad, ePrograms; var eInput, eOutput, eEval, eReset, eLoad, eTrace, ePrograms;
function Throw(x) { function Throw(x) {
throw x; throw x;
@ -311,7 +371,10 @@ function Reset() {
var i; var i;
a = 0; a = 0;
cx = 0; cx = 0;
lo = 0; cHeap = 0;
cGets = 0;
cSets = 0;
cPrints = 0;
Null = 16384; Null = 16384;
M = new Array(Null * 2); M = new Array(Null * 2);
for (i = 0; i < M.length; ++i) { for (i = 0; i < M.length; ++i) {
@ -343,8 +406,11 @@ function ReadChar() {
function Lisp() { function Lisp() {
var x, A; var x, A;
lo = cx; cGets = 0;
output = ''; cSets = 0;
cHeap = cx;
cPrints = 0;
output = "";
while (dx) { while (dx) {
if (dx <= Ord(' ')) { if (dx <= Ord(' ')) {
ReadChar(); ReadChar();
@ -352,8 +418,8 @@ function Lisp() {
A = cx; A = cx;
try { try {
x = Read(); x = Read();
if (x == kDefine) { if (x < 0 && Car(x) == kDefine) {
a = Gc(0, Define(Read(), a)); a = Define(0, Cdr(x), a);
continue; continue;
} }
x = Eval(x, a); x = Eval(x, a);
@ -362,6 +428,7 @@ function Lisp() {
x = z; x = z;
} }
Print(x); Print(x);
PrintChar(Ord('\n'));
Gc(A, 0); Gc(A, 0);
} }
} }
@ -377,21 +444,11 @@ function Load(s) {
index = 1; index = 1;
} }
function OnSubmit() { function OnEval() {
Load(eInput.value.toUpperCase()); Load(eInput.value.toUpperCase());
Lisp(); 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() { function OnReset() {
output = ""; output = "";
try { try {
@ -406,6 +463,18 @@ function OnReset() {
ReportUsage(); ReportUsage();
} }
function OnTrace() {
var t;
Load(eInput.value);
t = panic;
depth = 0;
panic = 10000;
funcall = Funtrace;
Lisp();
funcall = Funcall;
panic = t;
}
function OnLoad() { function OnLoad() {
ePrograms.classList.toggle("show"); ePrograms.classList.toggle("show");
} }
@ -431,7 +500,7 @@ function RestoreMachine() {
M = machine[0]; M = machine[0];
a = machine[1]; a = machine[1];
cx = machine[2]; cx = machine[2];
lo = cx; cHeap = cx;
} }
} }
@ -449,25 +518,48 @@ function Number(i) {
function ReportUsage() { function ReportUsage() {
var i, c; var i, c;
for (c = i = 0; i < Null; i += 2) { 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((-cx >> 1) + c) + " / " +
Number((-lo >> 1) + c) + " / " + Number((-cHeap >> 1) + c) + " / " +
Number(Null) + " doublewords"; 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() { function SetUp() {
funcall = Funcall;
Read = Discount(Read);
Print = Discount(Print);
Define = Discount(Define);
eLoad = document.getElementById("load"); eLoad = document.getElementById("load");
eInput = document.getElementById("input"); eInput = document.getElementById("input");
eReset = document.getElementById("reset"); eReset = document.getElementById("reset");
eTrace = document.getElementById("trace");
eOutput = document.getElementById("output"); eOutput = document.getElementById("output");
eSubmit = document.getElementById("submit"); eEval = document.getElementById("eval");
ePrograms = document.getElementById("programs"); ePrograms = document.getElementById("programs");
window.onclick = OnWindowClick; window.onclick = OnWindowClick;
eSubmit.onclick = OnSubmit;
eReset.onclick = OnReset;
eLoad.onclick = OnLoad; eLoad.onclick = OnLoad;
eReset.onclick = OnReset;
eTrace.onclick = OnTrace;
eEval.onclick = OnEval;
Reset(); Reset();
RestoreMachine(); RestoreMachine();
ReportUsage(); ReportUsage();

View file

@ -34,8 +34,8 @@ kT: .asciz "T" # add %dl,(%si) boot A:\ DL=0
start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address start: ljmp $0x7c00>>4,$begin # cs = 0x7c00 is boot address
.asciz "" .asciz ""
kDefine:.asciz "DEFINE" kDefine:.asciz "DEFINE"
kQuote: .asciz "QUOTE"
kCond: .asciz "COND" kCond: .asciz "COND"
kQuote: .asciz "QUOTE"
kCar: .asciz "CAR" # ordering matters kCar: .asciz "CAR" # ordering matters
kCdr: .asciz "CDR" # ordering matters kCdr: .asciz "CDR" # ordering matters
kCons: .asciz "CONS" # ordering matters kCons: .asciz "CONS" # ordering matters
@ -123,16 +123,14 @@ PutChar:mov $0x0e,%ah # prints CP-437
int $0x10 # vidya service int $0x10 # vidya service
pop %bp # scroll up bug pop %bp # scroll up bug
cmp $'\r',%al # don't clobber cmp $'\r',%al # don't clobber
jne .RetDx # look xchg ret jne .retDx # look xchg ret
mov $'\n',%al mov $'\n',%al
jmp PutChar jmp PutChar
.RetDx: xchg %dx,%ax
ret
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax 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 push (%bx,%di) # mark prevents negative gc
mov (%di),%di mov (%di),%di
call Gc call Gc
@ -146,7 +144,7 @@ Gc: cmp %dx,%di # Gc(x:di,A:dx,B:si):ax
ret ret
Evlis: test %di,%di # Evlis(m:di,a:dx):ax 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) push (%bx,%di) # save 1 Cdr(m)
mov (%di),%ax mov (%di),%ax
call Eval call Eval
@ -160,7 +158,7 @@ Cons: xchg %di,%cx # Cons(m:di,a:ax):ax
mov %cx,(%di) # must preserve si mov %cx,(%di) # must preserve si
mov %ax,(%bx,%di) mov %ax,(%bx,%di)
lea 4(%di),%cx lea 4(%di),%cx
.RetDi: xchg %di,%ax .retDi: xchg %di,%ax
ret ret
GetList:call GetToken GetList:call GetToken
@ -178,6 +176,9 @@ GetList:call GetToken
pop %ax pop %ax
ret ret
.retDx: xchg %dx,%ax
ret
.resolv:push %si .resolv:push %si
call Eval # do (fn si) ((λ ...) si) call Eval # do (fn si) ((λ ...) si)
pop %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) mov (%di),%di # di = Cadr(fn)
Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx Pairlis:test %di,%di # Pairlis(x:di,y:si,a:dx):dx
jz .EvCadr # return if x is nil jz .EvCadr # return if x is nil
xor %ax,%ax # FRIENDLY FEATURE
test %si,%si # DEFAULT NIL ARGS
jz 1f
lodsw # ax = Car(y) lodsw # ax = Car(y)
push (%bx,%di) # push Cdr(x) 1: push (%bx,%di) # push Cdr(x)
mov (%di),%di # di = Car(x) mov (%di),%di # di = Car(x)
test %si,%si
jz 1f
mov (%si),%si # si = Cdr(y) 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 %ax,%di
xchg %dx,%ax xchg %dx,%ax
call Cons # Cons(Cons(Car(x),Car(y)),a) 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 .retF: xor %ax,%ax # ax = nil
ret ret
Define: xchg %dx,%ax
call Cons
jmp .retDx
Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax Assoc: mov %dx,%si # Assoc(x:ax,y:dx):ax
1: test %si,%si # FRIENDLY FEATURE 1: test %si,%si # FRIENDLY FEATURE
jns Undef # PRINT ?X IF XDX jns Undef # PRINT ?X IF XDX
@ -260,6 +270,7 @@ Eval: test %ax,%ax # Eval(e:ax,a:dx):ax
je Car je Car
cmp $kCond,%ax cmp $kCond,%ax
je Evcon # ABC Garbage Collector je Evcon # ABC Garbage Collector
jb Define
push %dx # save a push %dx # save a
push %cx # save A push %cx # save A
push %ax push %ax
@ -288,17 +299,6 @@ Read: call GetToken
call GetObject call GetObject
ret 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 begin: mov $0x8000,%sp
push %cs push %cs
pop %ds pop %ds
@ -311,10 +311,11 @@ begin: mov $0x8000,%sp
xor %bp,%bp xor %bp,%bp
main: xor %dx,%dx main: xor %dx,%dx
call Read call Read
cmp $kDefine,%ax
je Define
mov %bp,%dx mov %bp,%dx
call Eval call Eval
mov %dx,%bp
cmp $kDefine,%ax
je main
Catch: xchg %ax,%si Catch: xchg %ax,%si
call PrintObject call PrintObject
mov $'\r',%al mov $'\r',%al