git » jacl.git » commit c920199

back out lval nonsense for %SET

author Alan Dipert
2019-10-22 03:37:04 UTC
committer Alan Dipert
2019-10-22 03:37:04 UTC
parent bb3547746d32c657af0580b0fec3e56e102d0316

back out lval nonsense for %SET

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 ");