git » jacl.git » commit 8c41c77

tests passing bitches

author Alan Dipert
2019-08-28 05:40:14 UTC
committer Alan Dipert
2019-08-28 05:40:14 UTC
parent a5a0917ae0abaf307e3bc0fe36aee2a7222b25a1

tests passing bitches

jacl-tests.js +6 -7
jacl.js +71 -54

diff --git a/jacl-tests.js b/jacl-tests.js
index 022845d..56bee98 100644
--- a/jacl-tests.js
+++ b/jacl-tests.js
@@ -5,7 +5,7 @@ QUnit.module('Reader');
 const makeRead1 = (rdr, it) => {
   return async (str, timeout = true) => {
     const done = timeout ? it.async() : null;
-    rdr.tokenizer.stream.writeEach(str);
+    rdr.stream.writeEach(str);
     const obj = await rdr.read();
     if (done) done();
     return obj;
@@ -13,7 +13,7 @@ const makeRead1 = (rdr, it) => {
 };
 
 const freshRdr = (is) => {
-  const rdr = Reader.of(Tokenizer.of(new BufferedStream()));
+  const rdr = new Reader(new BufferedStream());
   return [rdr, makeRead1(rdr, is)];
 }
 
@@ -58,7 +58,7 @@ QUnit.test('Symbols', async is => {
 
 });
 
-QUnit.skip('Conses', async is => {
+QUnit.test('Conses', async is => {
   var [rdr, read1] = freshRdr(is);
   var cons;
 
@@ -101,9 +101,8 @@ QUnit.skip('Conses', async is => {
   is.deepEqual(Array.from(cons), [1,2], 'convert to pair');
 
   cons = await read1('((1 . 2))');
-  console.log(cons);
-  //is.deepEqual(Array.from(cons), [[1,2]], 'convert to nested pair');
+  is.deepEqual(Array.from(cons), [[1,2]], 'convert to nested pair');
 
-  //cons = await read1('(1 (2 . 3) 4)');
-  //is.deepEqual(Array.from(cons), [1,[2,3],4], 'convert to nested array');
+  cons = await read1('(1 (2 . 3) 4)');
+  is.deepEqual(Array.from(cons), [1,[2,3],4], 'convert to nested array');
 });
diff --git a/jacl.js b/jacl.js
index 2822796..6dc1a78 100644
--- a/jacl.js
+++ b/jacl.js
@@ -225,46 +225,55 @@ const READTABLE = Package.intern('CL', '*READTABLE*');
 
 class Token extends String {
   interpret() {
-    const [isInt, intVal] = readInteger(this);
+    const prim = this.valueOf();
+
+    const [isInt, intVal] = readInteger(prim);
     if (isInt) return intVal;
 
-    return LispSymbol.internFromString(this.valueOf());
+    return LispSymbol.internFromString(prim);
   }
   static is(x, y) {
     return (x instanceof Token) && x.valueOf() === y;
   }
 }
 
-const LIST_CLOSE_PAREN = new Object();
-
 const readList = async stream => {
-  const tok = Tokenizer.of(stream),
-        rdr = Reader.of(tok);
+  const rdr = new Reader(stream),
+        x   = await rdr.safeRead();
 
-  const t = await tok.read();
+  if (x instanceof DotError)
+    throw new Error(`Nothing before . in list`);
 
-  if (t === LIST_CLOSE_PAREN) return new Values(null);
-  if (Token.is(t, '.')) throw new Error(`Nothing before . in list`);
+  if (x instanceof CloseParenError)
+    return new Values(null);
 
-  tok.unread(t);
+  const car   = x,
+        after = await rdr.safeRead();
 
-  const car   = await rdr.read(),
-        after = await tok.read();
-
-  if (Token.is(after, '.')) {
-    const cons = new Cons(car, await rdr.read());
-    if (await tok.read() === LIST_CLOSE_PAREN) {
+  if (after instanceof DotError) {
+    const y = await rdr.safeRead();
+    if (y instanceof Error)
+      throw new Error(`Nothing after . in list`);
+    const cons = new Cons(car, y);
+    if ((await rdr.safeRead()) instanceof CloseParenError) {
       return new Values(cons);
     } else {
       throw new Error(`More than one object after . in list`);
     }
   }
 
-  if (after === LIST_CLOSE_PAREN) return new Values(new Cons(car));
+  if (after instanceof CloseParenError)
+    return new Values(new Cons(car));
 
-  tok.unread(after);
+  return new Values(new Cons(car, new Cons(after, (await readList(stream))[0])));
+}
 
-  return new Values(new Cons(car, (await readList(stream))[0]));
+class DotError extends Error {
+  constructor() { super('. context error'); }
+}
+
+class CloseParenError extends Error {
+  constructor() { super('Unmatched )'); }
 }
 
 READTABLE.value = new ReadTable()
@@ -285,7 +294,8 @@ READTABLE.value = new ReadTable()
       }
     }
   })
-  .setMacro(')', true, async stream => new Values(LIST_CLOSE_PAREN))
+  .setMacro('.', false, async stream => { throw new DotError(); })
+  .setMacro(')', true, async stream => { throw new CloseParenError(); })
   .setMacro('(', true, readList)
   .setMacro("'", true, async stream => {
     return new Values(Cons.listOf(
@@ -338,23 +348,31 @@ const readSingleEscaped = async function(stream, token) {
       return readMultiEscaped(stream, token);
     } else if (READTABLE.val().isTerminating(y) || isWhitespace(y)) {
       stream.unread(y);
-      return new Token(token);
+      return new Token(token).interpret();
     } else {
       throw new Error(`Illegal character: '${y}'`);
     }
   }
 };
 
-
-class Tokenizer {
+class Reader {
   constructor(stream) {
     this.stream = stream;
   }
   static of(stream) {
-    return new Tokenizer(stream);
-  }
-  unread(token) {
-    this.stream.unreadEach(token);
+    return new Reader(stream);
+  }
+  async safeRead() {
+    var x;
+    try {
+      x = await this.read();
+    } catch (e) {
+      if (e instanceof DotError || e instanceof CloseParenError) {
+        return e;
+      }
+      throw e;
+    }
+    return x;
   }
   async read() {
     let macroFun;
@@ -389,35 +407,34 @@ class Tokenizer {
   }
 }
 
-class Reader {
-  constructor(tokenizer) {
-    this.tokenizer = tokenizer;
-  }
-  static of(tokenizer) {
-    return new Reader(tokenizer);
-  }
-  async read() {
-    const obj = await this.tokenizer.read();
-    if (obj === LIST_CLOSE_PAREN) {
-      throw new Error(`Unmatched ')'`);
-    } else if (obj instanceof Token) {
-      return obj.interpret();
-    } else {
-      return obj;
-    }
-  }
-  [Symbol.asyncIterator]() {
-    return {
-      next: () => this.read().then(obj => {
-        return { value: obj, done: false };
-      })
-    }
-  }
-}
+//class Reader {
+//  constructor(tokenizer) {
+//    this.tokenizer = tokenizer;
+//  }
+//  static of(tokenizer) {
+//    return new Reader(tokenizer);
+//  }
+//  async read() {
+//    const obj = await this.tokenizer.read();
+//    if (obj === LIST_CLOSE_PAREN) {
+//      throw new Error(`Unmatched ')'`);
+//    } else if (obj instanceof Token) {
+//      return obj.interpret();
+//    } else {
+//      return obj;
+//    }
+//  }
+//  [Symbol.asyncIterator]() {
+//    return {
+//      next: () => this.read().then(obj => {
+//        return { value: obj, done: false };
+//      })
+//    }
+//  }
+//}
 
 var buf = new BufferedStream(),
-    tok = new Tokenizer(buf),
-    rdr = new Reader(tok);
+    rdr = new Reader(buf);
 
 (async function() {
   for await(const obj of rdr) {