author | Alan Dipert
<alan@dipert.org> 2019-10-22 03:37:04 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-10-22 03:37:04 UTC |
parent | bb3547746d32c657af0580b0fec3e56e102d0316 |
jacl.js | +28 | -20 |
diff --git a/jacl.js b/jacl.js index 108ea15..abc0ab4 100644 --- a/jacl.js +++ b/jacl.js @@ -717,20 +717,20 @@ class Reader { // Special forms const SPECIAL_FORMS = [ - '%CALL', + '%CALL', '%DOT', '%LAMBDA', '%SET', '%TAGBODY' ]; -for (const s of SPECIAL_FORMS) JACLPKG.intern(s); +for (const s of SPECIAL_FORMS) JACLPKG.intern(s); CLPKG.intern('QUOTE').setMacro().fvalue = function(env, form, x) { return Cons.listOf(JACLPKG.intern('%QUOTE'), x); }; -JACLPKG.intern('UNQUOTE').setMacro().fvalue = +JACLPKG.intern('UNQUOTE').setMacro().fvalue = JACLPKG.intern('UNQUOTE-SPLICING').setMacro().fvalue = function(env, form) { throw new Error(`Comma not inside backquote`); }; @@ -752,7 +752,7 @@ JACLPKG.intern('QUASIQUOTE').setMacro().fvalue = function(env, form) { const transformCompound = compound => { const rec = object => { - if (object instanceof Cons && + if (object instanceof Cons && (!(object.cdr instanceof Cons) || (object.cdr instanceof Cons && JACLPKG.intern('UNQUOTE') === object.cdr.car))) { return Cons.listOf(transform(object.car), transform(object.cdr, false)); @@ -898,19 +898,26 @@ const analyzeSpecials = new Map([ const [, target, val] = form; if (!(target instanceof LispSymbol)) throw new Error(`Can't assign to non-symbol`); - if (target.isConstant) - throw new Error(`Can't set constant`); - const targetExpr = analyze(env.withContext("lval"), null, target); const valExpr = analyze(env.withContext("sval"), null, val); - const node = makeNode("set", { env: env, parent: parent, form: form }); + let op; - targetExpr.parent = valExpr.parent = node; + if (target.packageName === PACKAGE.val().name + && env.hasLocal(target.name)) { + // Local + op = 'set-local'; + } else { + // Global + if (target.isConstant) throw new Error(`Can't set constant`); + op = 'set-global'; + } - node.target = targetExpr; + const node = makeNode(op, { env: env, parent: parent, form: form }); + node.targetSym = target; node.val = valExpr; - + node.targetSym.parent = node; + return node; }], [JACLPKG.intern("%TAGBODY"), (env, parent, form) => { @@ -922,7 +929,7 @@ const analyzeSpecials = new Map([ // after the tag // First pass: gather tags and statements, don't analyze - const newEnv = env.clone().withContext("statement"); + const newEnv = env.withContext("statement"); const tags = new Map(); let currentTag = null, currentStmts = []; for (const x of tagsStmts) { @@ -1029,6 +1036,9 @@ const analyzeSymbol = (env, parent, form) => { return node; }; +// TODO Use env strategy where we synthesize local names so that we can guarantee +// they are deconflicted with locals generated by compiler for impl. of e.g. +// TAGBODY. For interop, provide js::WINDOW instead of arbitrary JS name access class Env { constructor(init = true) { if (init) Env.init(this); @@ -1049,6 +1059,9 @@ class Env { newEnv.context = this.context; return newEnv; } + hasLocal(sym) { + return this.localVals.has(sym.name); + } withContext(context) { const newEnv = new Env(false); newEnv.localFuns = this.localFuns; @@ -1123,28 +1136,23 @@ const emitNode = (print, node) => { case "local": if (context === "return") print("return "); print(munge(form.name)); - if (context !== "sval" && context !== "lval") print(";\n"); + if (context !== "sval") print(";\n"); break; case "global": - // TODO revisit the sanity of the 'lval' concept for %SET if (context === "return") print("return "); if (form.packageName === 'COMMON-LISP' && form.name === 'NIL') { print("null"); } else { print(`Package.get('${escapeSingle(form.packageName)}', true).intern('${escapeSingle(form.name)}')`); if (node.slot === 'value') { - if (context === 'lval') { - print(".value"); - } else { - print(".val()"); - } + print(".val()"); } else if (node.slot === 'function') { print(".func()"); } else { throw new Error(`Unknown global slot: ${node.slot}`); } } - if (context !== "sval" && context !== "lval") print(";\n"); + if (context !== "sval") print(";\n"); break; case "set": if (context === "return") print("return ");