author | Alan Dipert
<alan@dipert.org> 2021-04-07 01:39:16 UTC |
committer | Alan Dipert
<alan@dipert.org> 2021-04-07 01:39:16 UTC |
parent | 4275223bd33b7e5820c7cb5c966d7fb0a33c9496 |
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();