author | Alan Dipert
<alan@dipert.org> 2019-09-04 03:56:43 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-09-04 03:56:43 UTC |
parent | 18b391a9888bec0e3da983f0c158ab235fed2d5b |
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);