git » jacl.git » commit 46b3e04

#: to read uninterned symbols

author Alan Dipert
2019-09-04 03:56:43 UTC
committer Alan Dipert
2019-09-04 03:56:43 UTC
parent 18b391a9888bec0e3da983f0c158ab235fed2d5b

#: to read uninterned symbols

jacl-tests.js +7 -3
jacl.js +36 -40

diff --git a/jacl-tests.js b/jacl-tests.js
index 477c0d3..67e809e 100644
--- a/jacl-tests.js
+++ b/jacl-tests.js
@@ -55,11 +55,15 @@ QUnit.test('Symbols', async is => {
   sym = await read1(':some-lil-kw ');
   is.strictEqual(sym.name, 'SOME-LIL-KW', 'kw name');
   is.strictEqual(sym.packageName, 'KEYWORD', 'kw package');
+});
 
-  //sym = await read1('#:foop ');
-  //is.strictEqual(sym.name, 'FOOP', 'uninterned sym has a name');
-  //is.strictEqual(sym.packageName, null, 'uninterned packageName is null');
+QUnit.test('Sharpsign', async is => {
+  var [rdr, read1] = freshRdr(is);
+  var sym;
 
+  sym = await read1('#:foop ');
+  is.strictEqual(sym.name, 'FOOP', 'uninterned sym has a name');
+  is.strictEqual(sym.packageName, null, 'uninterned packageName is null');
 });
 
 QUnit.test('Conses', async is => {
diff --git a/jacl.js b/jacl.js
index 4744305..30a22cf 100644
--- a/jacl.js
+++ b/jacl.js
@@ -87,7 +87,7 @@ class LispSymbol {
 
     throw new Error(`Unknown symbol syntax: '${token}'`);
   }
-  static internFromString(str) {
+  static createFromString(str, intern) {
     if (typeof str !== 'string') throw new Error(`Can only intern strings`);
 
     if (LispSymbol.isKeyword(str)) {
@@ -96,9 +96,12 @@ class LispSymbol {
 
     let [pkgName, name] = LispSymbol.getPackageAndName(str);
 
-    if (!pkgName) pkgName = PACKAGE.val().name;
-
-    return LispSymbol.intern(pkgName, name);
+    if (intern) {
+      if (!pkgName) pkgName = PACKAGE.val().name;
+      return LispSymbol.intern(pkgName, name);
+    } else {
+      return new LispSymbol(name, null);
+    }
   }
 }
 
@@ -223,11 +226,11 @@ class ReadTable {
 
 const READTABLE = Package.intern('CL', '*READTABLE*');
 
-const interpretToken = str => {
+const interpretToken = (str, intern) => {
   const [isInt, intVal] = readInteger(str);
   if (isInt) return intVal;
 
-  return LispSymbol.internFromString(str);
+  return LispSymbol.createFromString(str, intern);
 };
 
 const skipWhitespace = async stream => {
@@ -293,6 +296,24 @@ const readList = async stream => {
   return new Values(new Cons(car, (await readList(stream))[0]));
 }
 
+const readSharpsign = async stream => {
+  const ch = await stream.read();
+  switch(ch) {
+    case ':':
+      const rdr = new Reader(stream),
+            sym = await rdr.read(false);
+      if (sym instanceof LispSymbol && sym.packageName === null) {
+        return new Values(sym);
+      } else if (sym instanceof LispString && sym.packageName !== null) {
+        throw new Error(`Object after #: contains a package marker`);
+      } else {
+        throw new Error(`Object after #: not a symbol`);
+      }
+    default:
+      throw new Error(`Unknown # char: ${ch}`);
+  }
+};
+
 READTABLE.value = new ReadTable()
   .setMacro(';', true, async stream => {
     for await(const ch of stream) {
@@ -317,7 +338,8 @@ READTABLE.value = new ReadTable()
       Package.intern('CL', 'QUOTE'),
       await (new Reader(stream)).read()
     ));
-  });
+  })
+  .setMacro('#', false, readSharpsign);
 
 const isWhitespace = ch => ' \t\n\r\b'.indexOf(ch) > -1;
 
@@ -327,7 +349,7 @@ const isConstituent = ch => {
     || /[0-9]/.test(ch);
 }
 
-const readMultiEscaped = async function(stream, token) {
+const readMultiEscaped = async function(stream, token, intern = true) {
   for await(const y of stream) {
     if (isConstituent(y) || READTABLE.val().isTerminating(y) || isWhitespace(y)) {
       token += y;
@@ -351,7 +373,7 @@ const readInteger = token => {
   }
 };
 
-const readSingleEscaped = async function(stream, token) {
+const readSingleEscaped = async function(stream, token, intern = true) {
   for await(const y of stream) {
     if (isConstituent(y)) {
       token += y.toUpperCase();
@@ -363,7 +385,7 @@ const readSingleEscaped = async function(stream, token) {
       return readMultiEscaped(stream, token);
     } else if (READTABLE.val().isTerminating(y) || isWhitespace(y) || y === ')') {
       stream.unread(y);
-      return interpretToken(token);
+      return interpretToken(token, intern);
     } else {
       throw new Error(`Illegal character: '${y}'`);
     }
@@ -377,7 +399,7 @@ class Reader {
   static of(stream) {
     return new Reader(stream);
   }
-  async read() {
+  async read(internSymbols = true) {
     let macroFun;
     for await(const x of this.stream) {
       if (isWhitespace(x)) {
@@ -391,11 +413,11 @@ class Reader {
         }
       } else if (x === '\\') {
         let y = await this.stream.read();
-        return readSingleEscaped(this.stream, y);
+        return readSingleEscaped(this.stream, y, internSymbols);
       } else if (x === '|') {
-        return readMultiEscaped(this.stream, '');
+        return readMultiEscaped(this.stream, '', internSymbols);
       } else if (isConstituent(x)) {
-        return readSingleEscaped(this.stream, x.toUpperCase());
+        return readSingleEscaped(this.stream, x.toUpperCase(), internSymbols);
       } else {
         throw new Error(`Illegal character: '${x}'`);
       }
@@ -410,32 +432,6 @@ class Reader {
   }
 }
 
-//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(),
     rdr = new Reader(buf);