git » jacl.git » commit 9e30939

mv wip

author Alan Dipert
2021-04-07 01:39:16 UTC
committer Alan Dipert
2021-04-07 01:39:16 UTC
parent 4275223bd33b7e5820c7cb5c966d7fb0a33c9496

mv wip

jacl.js +65 -38

diff --git a/jacl.js b/jacl.js
index 01123d9..413449e 100644
--- a/jacl.js
+++ b/jacl.js
@@ -237,9 +237,9 @@ class LispSymbol {
     }
   }
   static intern(packageName, name) {
-    if (readInteger(packageName)[0])
+    if (readInteger(packageName))
       throw new Error(`Symbol package must not be number: '${packageName}'`);
-    if (readInteger(name)[0])
+    if (readInteger(name))
       throw new Error(`Symbol name must not be number: '${name}'`);
     return Package.get(packageName, true).intern(name);
   }
@@ -307,7 +307,7 @@ class LispSymbol {
       } else {
         // The symbol isn't qualified, so first attempt to resolve it using
         // information stored in the current package.
-        const [sym] = PACKAGE.val().findSymbol(name, false);
+        const sym = PACKAGE.val().findSymbol(name, false);
         // If the symbol wasn't found, intern in the current package.
         return sym ? sym : PACKAGE.val().intern(name);
       }
@@ -370,7 +370,7 @@ class Package {
   }
   findSymbol(name) {
     if (this.symbols.has(name)) {
-      return new Values(
+      return mValues(
         this.symbols.get(name),
         LispSymbol.kw(this.exports.has(name) ? 'external' : 'internal')
       );
@@ -378,14 +378,14 @@ class Package {
 
     for (const usedPkg of this.use) {
       if (usedPkg.isExported(name)) {
-        return new Values(
+        return mValues(
           usedPkg.symbols.get(name),
           LispSymbol.kw('inherited')
         );
       }
     }
 
-    return new Values(null, null);
+    return mValues(null, null);
   }
   static intern(packageName, name) {
     const pkg = Package.get(packageName, true);
@@ -490,6 +490,33 @@ PACKAGE.value = Package.get('JACL');
 
 JACLPKG.usePackage(CLPKG);
 
+// Multiple values
+
+const MV_EXPECTED = JACLPKG.intern("*MV-EXPECTED*")
+MV_EXPECTED.isSpecial = true;
+MV_EXPECTED.value = 1;
+let MV = [];
+
+const mValues = (...vals) => {
+  MV = vals.slice(0, Math.min(vals.length, MV_EXPECTED.value));
+  MV_EXPECTED.value = vals.length;
+  return MV.length ? MV[0] : null;
+};
+
+const mvArray = (thunk, n = Infinity) => {
+  try {
+    MV_EXPECTED.pushBinding(n);
+    const val = thunk();
+    return MV_EXPECTED.value === 1 ? [val] : MV.slice(0, MV_EXPECTED.value);
+  } finally {
+    MV_EXPECTED.popBinding();
+  }
+};
+
+mvArrayCall = (n, f, ...args) => {
+  return mvArray(() => f(...args), n);
+};
+
 class StringStream {
   constructor(str) {
     this.ptr = 0;
@@ -599,7 +626,7 @@ class ReadTable {
 const READTABLE = Package.intern('CL', '*READTABLE*');
 
 const interpretToken = (token, intern) => {
-  const [isInt, intVal] = readInteger(token.str);
+  const [isInt, intVal] = mvArray(() => readInteger(token.str), 2);
   if (isInt) return intVal;
   const sym = LispSymbol.createFromString(token, intern);
   if (eqClSym(sym, 'NIL')) {
@@ -626,7 +653,7 @@ const readList = async stream => {
     throw new Error(`Nothing before . in list`);
 
   if (x === listSentinels.get(')'))
-    return new Values(null);
+    return null;
 
   car = x;
 
@@ -639,21 +666,21 @@ const readList = async stream => {
     const cdr = x;
     x = await rdr.read(true, listSentinels);
     if (x === listSentinels.get(')'))
-      return new Values(new Cons(car, cdr));
+      return new Cons(car, cdr);
     throw new Error(`More than one object after . in list`);
   }
 
   if (x === listSentinels.get(')'))
-    return new Values(new Cons(car));
+    return new Cons(car);
 
-  return new Values(new Cons(car, new Cons(x, (await readList(stream))[0])));
+  return new Cons(car, new Cons(x, (await readList(stream))[0]));
 }
 
 const readString = async stream => {
   let str = new LispString();
   for await(const x of stream) {
     if (x === '"') {
-      return new Values(str);
+      return str;
     } else if(x === '\\') {
       str.push(await stream.read());
     } else {
@@ -665,62 +692,60 @@ const readString = async stream => {
 // TODO make this a real JS string reader
 const readJsString = async stream => {
   const [clStr] = await readString(stream);
-  return new Values(clStr.toString());
+  return clStr.toString();
 };
 
 const readGlobalReference = async stream => {
   const sym = await readMultiEscaped(stream, new Token().sawPipe(), false),
         parts = sym.name.split('.'),
         [topic, ...fields] = parts;
-  return new Values(
-    Cons.listOf(
-      JACLPKG.intern('.'),
-      Cons.listOf(JACLPKG.intern('%JS'), topic),
-      ...fields.map(x => new LispSymbol(x, null))
-    )
+  return Cons.listOf(
+    JACLPKG.intern('.'),
+    Cons.listOf(JACLPKG.intern('%JS'), topic),
+    ...fields.map(x => new LispSymbol(x, null))
   );
 };
 
 const readAwait = async stream => {
-  return new Values(Cons.listOf(
+  return Cons.listOf(
     JACLPKG.intern('AWAIT'),
     await (new Reader(stream)).read()
-  ));
+  );
 }
 
 READTABLE.value = new ReadTable()
   .setMacro(';', true, async stream => {
     for await(const ch of stream) {
-      if (ch === '\n') return new Values();
+      if (ch === '\n') return mValues();
     }
   })
   .setMacro('"', true, readString)
   .setMacro('(', true, readList)
   .setMacro("'", true, async stream => {
-    return new Values(Cons.listOf(
+    return Cons.listOf(
       Package.intern('CL', 'QUOTE'),
       await (new Reader(stream)).read()
-    ));
+    );
   })
   .setMacro("`", true, async stream => {
-    return new Values(Cons.listOf(
+    return Cons.listOf(
       JACLPKG.intern('QUASIQUOTE'),
       await new Reader(stream).read()
-    ));
+    );
   })
   .setMacro(',', true, async stream => {
     const ch = await stream.read();
     if (ch === '@') {
-      return new Values(Cons.listOf(
+      return Cons.listOf(
         JACLPKG.intern('UNQUOTE-SPLICING'),
         await new Reader(stream).read()
-      ));
+      );
     } else {
       stream.unread(ch);
-      return new Values(Cons.listOf(
+      return Cons.listOf(
         JACLPKG.intern('UNQUOTE'),
         await new Reader(stream).read()
-      ));
+      );
     }
   })
   .makeDispatchMacroChar('#', true)
@@ -728,7 +753,7 @@ READTABLE.value = new ReadTable()
     const rdr = new Reader(stream),
           sym = await rdr.read(false);
     if (sym instanceof LispSymbol && sym.packageName === null) {
-      return new Values(sym);
+      return sym;
     } else if (sym instanceof LispString && sym.packageName !== null) {
       throw new Error(`Object after #: contains a package marker`);
     } else {
@@ -736,16 +761,16 @@ READTABLE.value = new ReadTable()
     }
   })
   .setDispatchMacroChar('#', '\\', async stream => {
-    return new Values(await stream.read());
+    return await stream.read();
   })
   .setDispatchMacroChar('#', '<', async stream => {
     throw new Error(`Illegal # char: <`);
   })
   .setDispatchMacroChar('#', "'", async stream => {
-    return new Values(Cons.listOf(
+    return Cons.listOf(
       Package.intern('CL', 'FUNCTION'),
       await (new Reader(stream)).read()
-    ));
+    );
   });
 
 const isWhitespace = ch => ' \t\n\r\b'.indexOf(ch) > -1;
@@ -836,9 +861,9 @@ const readMultiEscaped = async function(stream, token, intern = true) {
 
 const readInteger = token => {
   if (/^[+-]?[0-9]+\.?$/.test(token)) {
-    return new Values(true, window.parseInt(token));
+    return mValues(true, window.parseInt(token));
   } else {
-    return new Values(false, null);
+    return mValues(false, null);
   }
 };
 
@@ -878,7 +903,9 @@ class Reader {
       } else if (isWhitespace(x)) {
         continue;
       } else if (macroFun = READTABLE.val().getMacro(x)) {
-        const vals = await macroFun(this.stream);
+        // const vals = await macroFun(this.stream);
+        const vals = mvArray(() => macroFun(this.stream));
+        console.log(vals);
         if (vals.length) {
           return vals[0];
         } else {
@@ -2370,8 +2397,8 @@ const startRepl = async () => {
   try {
     for await(const obj of replReader) {
       console.log("read", obj);
+      console.log(obj)
       const node = optimize(analyze(emptyEnv(), null, obj));
-      // console.log(node)
       const sb = new StringBuffer();
       emitNode(sb.append.bind(sb), node);
       const code = sb.toString();