git » jacl.git » commit ce8c130

wip

author Alan Dipert
2019-08-14 16:45:53 UTC
committer Alan Dipert
2019-08-14 16:45:53 UTC
parent 61fdc40193cc88c8e0cb7514bf10873b1296d6e6

wip

jacl.js +45 -33

diff --git a/jacl.js b/jacl.js
index dccb9cd..7eb4eda 100644
--- a/jacl.js
+++ b/jacl.js
@@ -1,6 +1,4 @@
-const KEYS = {
-  UNBOUND: Symbol()
-};
+const UNDEFINED = new Object();
 
 class Cons {
   constructor(car, cdr = null) {
@@ -36,17 +34,17 @@ class LispSymbol {
   constructor(name, packageName) {
     this.name = name;
     this.package = packageName;
-    this.value = KEYS.UNBOUND;
-    this.fvalue = KEYS.UNBOUND;
+    this.value = UNDEFINED;
+    this.fvalue = UNDEFINED;
     this.stack = [];
   }
   val() {
-    if (this.value === KEYS.UNBOUND)
+    if (this.value === UNDEFINED)
       throw new Error(`Variable '${this.name}' unbound`);
     return this.value;
   }
   func() {
-    if (this.fvalue === KEYS.UNBOUND)
+    if (this.fvalue === UNDEFINED)
       throw new Error(`Function '${this.name}' unbound`);
     return this.fvalue;
   }
@@ -151,7 +149,9 @@ class BufferedStream {
     for (const x of xs) this.write(x);
   }
   unreadEach(xs) {
-    for (const x of xs) this.unread(x);
+    for (let i = xs.length; i--; i >= 0) {
+      this.unread(xs[i]);
+    }
   }
   unread(obj) {
     if (this.resolveQueue.length) {
@@ -201,32 +201,38 @@ class Token extends String {
   interpret() {
     return readInteger(this) || LispSymbol.fromString(this)
   }
+  static is(x, y) {
+    return (x instanceof Token) && x.valueOf() === y;
+  }
 }
 
-const CLOSE_PAREN = new Object();
+const LIST_CLOSE_PAREN = new Object();
 
+// TODO broken
 const readList = async stream => {
-  let x = await (new Tokenizer(stream)).nextToken()
-  if (x === CLOSE_PAREN) {
+  let tok = Tokenizer.of(stream),
+    rdr = Reader.of(tok),
+    x = await tok.read();
+  if (x === LIST_CLOSE_PAREN) {
     return new Values(null);
+  } else if (Token.is(x, '.')) {
+    throw new Error(`Nothing before . in list`);
   } else {
-    if (x instanceof Token) {
-      stream.unreadEach(x);
-      x = await (new Reader(new Tokenizer(stream))).read();
+    tok.unread(x);
+    let car = await rdr.read(),
+      maybeDot = await tok.read();
+    if (Token.is(maybeDot, '.')) {
+      let cons = new Values(new Cons(car, await rdr.read())),
+        maybeClose = await tok.read();
+      if (maybeClose === LIST_CLOSE_PAREN) {
+        return cons;
+      } else {
+        throw new Error(`More than one object after . in list`);
+      }
+    } else {
+      return new Values(new Cons(car, (await readList(stream))[0]));
     }
-    return new Values(new Cons(x, (await readList(stream))[0]));
-  }
-  //while (true) {
-  //  const x = await rdr.read();
-  //  if (x === CLOSE_PAREN) {
-  //    return new Values(head);
-  //  } else if (cons !== null) {
-  //    cons.cdr = new Cons(x);
-  //    cons = cons.cdr;
-  //  } else {
-  //    cons = head = new Cons(x);
-  //  }
-  //}
+  }
 }
 
 READTABLE.value = new ReadTable()
@@ -247,7 +253,7 @@ READTABLE.value = new ReadTable()
       }
     }
   })
-  .setMacro(')', true, async stream => new Values(CLOSE_PAREN))
+  .setMacro(')', true, async stream => new Values(LIST_CLOSE_PAREN))
   .setMacro('(', true, readList);
 
 const isWhitespace = ch => ' \t\n\r\b'.indexOf(ch) > -1;
@@ -315,10 +321,13 @@ class Tokenizer {
   constructor(stream) {
     this.stream = stream;
   }
+  static of(stream) {
+    return new Tokenizer(stream);
+  }
   unread(token) {
-    this.stream.writeEach(token);
+    this.stream.unreadEach(token);
   }
-  async nextToken() {
+  async read() {
     let macroFun;
     for await(const x of this.stream) {
       if (isWhitespace(x)) {
@@ -344,7 +353,7 @@ class Tokenizer {
   }
   [Symbol.asyncIterator]() {
     return {
-      next: () => this.nextToken().then(obj => {
+      next: () => this.read().then(obj => {
         return { value: obj, done: false };
       })
     }
@@ -355,9 +364,12 @@ class Reader {
   constructor(tokenizer) {
     this.tokenizer = tokenizer;
   }
+  static of(tokenizer) {
+    return new Reader(tokenizer);
+  }
   async read() {
-    const obj = await this.tokenizer.nextToken();
-    if (obj === CLOSE_PAREN) {
+    const obj = await this.tokenizer.read();
+    if (obj === LIST_CLOSE_PAREN) {
       throw new Error(`Unmatched ')'`);
     } else if (obj instanceof Token) {
       return obj.interpret();