author | Alan Dipert
<alan@dipert.org> 2019-09-28 21:33:27 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-09-28 21:33:27 UTC |
parent | a4eaac2085c029a5558a5099b7e56e50579f657e |
jacl.js | +56 | -34 |
diff --git a/jacl.js b/jacl.js index 90e9c30..3c15a53 100644 --- a/jacl.js +++ b/jacl.js @@ -234,26 +234,40 @@ class Package { const JACLPKG = Package.makePackage('JACL'); const JSPKG = Package.makePackage('JS'); -Package.makePackage('COMMON-LISP', 'CL'); +const CLPKG = Package.makePackage('COMMON-LISP', 'CL'); Package.makePackage('COMMON-LISP-USER', 'CL-USER'); Package.makePackage('KEYWORD'); -const JSVALS = new Map([ - ['+FALSE+', 'false'], - ['+NAN+', 'NaN'], - ['+NULL+', 'null'], - ['+TRUE+', 'true'], - ['+UNDEFINED+', 'undefined'] +// CL package constants +const CLCONSTS = new Map([ + ['T', true], + ['NIL', null] ]); -for (const [k,v] of JSVALS) { - JSPKG.intern(k); +for (const [k,v] of CLCONSTS) { + CLPKG.intern(k).value = v; + CLPKG.exportSymbol(k); +} + +// JS package constants +const JSCONSTS = new Map([ + ['+FALSE+', false], + ['+NAN+', NaN], + ['+NULL+', null], + ['+TRUE+', true], + ['+UNDEFINED+', undefined] +]); + +for (const [k,v] of JSCONSTS) { + JSPKG.intern(k).value = v; JSPKG.exportSymbol(k); } const PACKAGE = Package.intern('CL', '*PACKAGE*'); PACKAGE.value = Package.get('JACL'); +JACLPKG.usePackage(CLPKG); + class BufferedStream { constructor() { this.buf = []; @@ -575,9 +589,8 @@ class Reader { } // Special forms related to interop -for (const s of ['JS', 'DOT']) { +for (const s of ['%DOT', '%CALL']) { JACLPKG.intern(s); - JACLPKG.exportSymbol(s); } // Primitive functions related to interop @@ -594,10 +607,14 @@ JACLPKG.intern('.').setMacro().fvalue = function(topic, ...ops) { if (arguments.length < 2) throw new Error(`\. requires at least two arguments`); return ops.reduce((prev, op) => { if (op instanceof LispSymbol) { - return Cons.listOf(JACLPKG.findSymbol('DOT')[0], prev, op); + return Cons.listOf(JACLPKG.intern('%DOT'), prev, op); } else if (op instanceof Cons) { const [method, ...args] = op; - return Cons.listOf(Cons.listOf(JACLPKG.findSymbol('DOT')[0], prev, method), ...args); + return Cons.listOf( + JACLPKG.intern('%CALL'), + Cons.listOf(JACLPKG.intern('%DOT'), prev, method), + ...args + ); } else { throw new Error(`Invalid \. syntax: ${op}`); } @@ -616,12 +633,6 @@ JACLPKG.intern('ENABLE-JS-SYNTAX').fvalue = () => { .val() .makeDispatchMacroChar('@', true) .setDispatchMacroChar('@', '"', readJsString) - .setDispatchMacroChar('@', '|', async stream => { - return new Values(Cons.listOf( - JACLPKG.intern('JS'), - await readMultiEscaped(stream, new Token().sawPipe(), true) - )); - }) return null; }; JACLPKG.exportSymbol('ENABLE-JS-SYNTAX'); @@ -633,6 +644,14 @@ const isMacroForm = form => { && form.car.isMacro; }; +const isLambdaForm = form => { + return form instanceof Cons + && form.car + && form.car instanceof LispSymbol + && form.car.packageName === 'COMMON-LISP' + && form.car.name === 'LAMBDA'; +} + const compile = (form, env) => { if (form instanceof Number || typeof form === 'number') { return form.toString(); @@ -640,9 +659,13 @@ const compile = (form, env) => { return JSON.stringify(form); } else if (form instanceof LispString) { return `(LispString.fromString("${form}"))`; - } else if (form instanceof LispSymbol) { - if (form.packageName === 'JS' && JSVALS.has(form.name)) { - return JSVALS.get(form.name); + } else if (form instanceof LispSymbol && form.packageName !== null) { + // TODO handle JS: and COMMON-LISP:{NIL,T} like constants here + // (emit directly, no symbol junk) + if (form.packageName === 'JS' && !JSCONSTS.has(form.name)) { + return form.name; + } else { + return `LispSymbol.intern(${JSON.stringify(form.packageName)}, ${JSON.stringify(form.name)}).val()`; } } else if (isMacroForm(form)) { while (isMacroForm(form)) { @@ -651,24 +674,23 @@ const compile = (form, env) => { } return compile(form, env); } else if (form instanceof Cons) { - const [op, ...args] = form, - [arg1, arg2] = args; - if (JACLPKG.intern('JS') === op) { - if (arg1 instanceof LispSymbol) { - return arg1.name; - } else { - return arg1.toString(); - } - } else if (JACLPKG.intern('DOT') === op) { + let [op, ...args] = form, + [arg1, arg2] = args; + if (JACLPKG.intern('%DOT') === op) { return `${compile(arg1, env)}.${arg2.name}` + } else if (JACLPKG.intern('%CALL') === op) { + let [, func, ...args] = form; + return `${compile(func, env)}(` + + args.map(x => compile(x, env)).join(',') + + `)`; } else if (op instanceof LispSymbol) { return `LispSymbol.intern('${op.packageName}', '${op.name}').func()(` + args.map(x => compile(x, env)).join(',') + `)`; + } else if (isLambdaForm(op)) { + return `${compile(op, env)}(${args.map(x => compile(x, env)).join(',')})` } else { - return `${compile(op, env)}(`+ - args.map(x => compile(x, env)).join(',') + - `)` + throw new Error(`Illegal function call`); } } else if (form === null) { return 'null';