git » jacl.git » commit 6f2f680

Add .children to AST

author Alan Dipert
2020-04-04 05:52:51 UTC
committer Alan Dipert
2020-04-04 05:52:51 UTC
parent e50d74e341c6be8f3932d9dbf1cb4cab17aa6e12

Add .children to AST

jacl.js +59 -33

diff --git a/jacl.js b/jacl.js
index 41a778b..ae5a554 100644
--- a/jacl.js
+++ b/jacl.js
@@ -4,9 +4,10 @@ const UNDEFINED = new Object();
 const EOF = new Object();
 
 class TagEx extends Error {
-  constructor(name) {
+  constructor(name, tagbodyId, tagId) {
     super(`Unknown GO tag: ${name}`);
-    this.name = name;
+    this.tagbodyId = tagbodyId;
+    this.tagId = tagId;
   }
 }
 
@@ -991,7 +992,9 @@ const analyzeBlock = (env, parent, forms) => {
       forms.slice(forms.length-1)[0]
     )
   }
-  return { statements: stmts, ret: ret };
+  let children = [...stmts];
+  if (ret) children.push(ret);
+  return { statements: stmts, ret: ret, children: children };
 };
 
 const parseLambdaList = list => {
@@ -1210,7 +1213,12 @@ const asTagName = x => x instanceof LispSymbol ? x.name : x.valueOf();
 const analyzeSpecials = new Map([
   [JACLPKG.intern('%QUOTE'), (env, parent, form) => {
     const [, obj] = form;
-    return makeNode('constant', { env: env, parent: parent, form: obj });
+    return makeNode('constant', {
+      env: env,
+      parent: parent,
+      children: [],
+      form: obj
+    });
   }],
   [JACLPKG.intern('%DOT'), (env, parent, form) => {
     const [, target, field] = form;
@@ -1223,6 +1231,7 @@ const analyzeSpecials = new Map([
       field: field instanceof LispSymbol ? field.name : field.toString()
     });
     node.target = analyze(env.withContext('expr'), node, target);
+    node.children = [node.target];
     return node;
   }],
   [JACLPKG.intern('%CALL'), (env, parent, form) => {
@@ -1230,6 +1239,7 @@ const analyzeSpecials = new Map([
     const node = makeNode('call', { env: env, parent: parent, form: form });
     node.f = analyze(env.withContext('expr'), node, func);
     node.args = args.map(analyze.bind(null, env.withContext('expr'), node))
+    node.children = [node.f, ...node.args];
     return node;
   }],
   [JACLPKG.intern('%LAMBDA'), (env, parent, form) => {
@@ -1242,6 +1252,7 @@ const analyzeSpecials = new Map([
       body = body.slice(1);
     }
     node = makeNode('lambda', { env: env, parent: parent, form: form, declarations: declarations });
+    // TODO Lambda list initforms should be considered children.
     const { lambdaList, bodyEnv } = analyzeLambdaList(env.withContext('expr'), node, list);
     node.lambdaList = lambdaList;
     return merge(
@@ -1259,6 +1270,7 @@ const analyzeSpecials = new Map([
 
     const node = makeNode('js', { env: env, parent: parent, form: form });
     node.args = args.map(x => analyze(env.withContext('expr'), node, x));
+    node.children = node.args;
     node.template = templateStr;
     return node;
   }],
@@ -1267,12 +1279,14 @@ const analyzeSpecials = new Map([
     const node = makeNode('new', { env: env, parent: parent, form: form });
     node.ctor = analyze(env.withContext('expr'), node, ctor);
     node.args = args.map(x => analyze(env.withContext('expr'), node, x));
+    node.children = [ctor, ...args];
     return node;
   }],
   [JACLPKG.intern('%THROW'), (env, parent, form) => {
     const [, obj] = form;
     const node = makeNode('throw', { env: env, parent: parent, form: form });
     node.obj = analyze(env.withContext('expr'), node, obj);
+    node.children = [obj];
     return node;
   }],
   [JACLPKG.intern('%LET'), (env, parent, form) => {
@@ -1335,7 +1349,7 @@ const analyzeSpecials = new Map([
     node.targetSym = target;
     node.val = valExpr;
     node.val.parent = node;
-
+    node.children = [node.val];
     return node;
   }],
   [JACLPKG.intern('%TAGBODY'), (env, parent, form) => {
@@ -1367,16 +1381,25 @@ const analyzeSpecials = new Map([
       id: env.newId()
     });
 
+    node.prelude = tags.get(null);
+    tags.delete(null);
+
     const childEnv = env
       .withContext('stmt')
-      .withTags(node.id, Array.from(tags.keys()).filter(x => x !== null));
+      .withTags(node.id, Array.from(tags.keys()));
 
-    // Second pass: analyze statements
     const ana = analyze.bind(null, childEnv, node);
-    for (const [tag, stmts] of tags) tags.set(tag, stmts.map(ana));
-    node.prelude = tags.get(null);
-    tags.delete(null);
+
+    node.children = [];
+    
+    let tagId = 0;
+    for (const [tag, stmts] of tags) {
+      tags.set(tag, [tagId++, stmts.map(ana)]);
+      node.children = [...node.children, ...tags.get(tag)[1]];
+    }
     node.tags = tags;
+    node.prelude = node.prelude.map(ana);
+    node.children.unshift(node.prelude);
 
     return node;
   }],
@@ -1386,17 +1409,15 @@ const analyzeSpecials = new Map([
     const tagName = asTagName(tag);
     if (!env.hasTag(tagName))
       throw new Error(`Non-existent GO tag: ${tagName}`);
+    const [tagbodyId, tagId] = env.tags.get(tagName);
     return makeNode('go', {
       env: env,
       parent: parent,
       form: form,
       tagName: tagName,
-      // TODO If the %GO is closed over in a lambda/flet/labels then it needs
-      // to throw an exception. If a tagbody occurs in sval/return context then
-      // it needs to throw an exception also, to escape emitted IIFEs?
-      // Otherwise, it can set its enclosing tagbody's target and continue ot
-      // its loop.
-      tagbodyId: env.tags.get(tagName)
+      tagbodyId: tagbodyId,
+      tagId: tagId,
+      children: []
     });
   }],
   [JACLPKG.intern('%IF'), (env, parent, form) => {
@@ -1412,7 +1433,8 @@ const analyzeSpecials = new Map([
       form: form,
       testNode: testNode,
       thenNode: thenNode,
-      elseNode: elseNode
+      elseNode: elseNode,
+      children: [testNode, thenNode, elseNode]
     });
 
     testNode.parent = thenNode.parent = elseNode.parent = node;
@@ -1431,9 +1453,11 @@ const parseCall = (env, parent, form) => {
       env: env.withContext('expr'),
       parent: node,
       form: func,
-      slot: 'function'
+      slot: 'function',
+      children: []
     });
   }
+  node.children = [node.f, ...node.args];
   return node;
 };
 
@@ -1467,6 +1491,7 @@ const analyzeSymbol = (env, parent, form) => {
     node.op = 'global';
     node.slot = 'value';
   }
+  node.children = [];
   return node;
 };
 
@@ -1504,9 +1529,12 @@ class Env {
   newId() {
     return this.counter();
   }
-  withTags(id, tags) {
+  withTags(tagbodyId, tags) {
     const newEnv = this.clone();
-    newEnv.tags = new Map([...this.tags, ...tags.map(tag => [tag, id])]);
+    newEnv.tags = new Map([
+      ...this.tags,
+      ...tags.map((tag, tagId) => [tag, [tagbodyId, tagId]])
+    ]);
     return newEnv;
   }
   hasLocal(sym) {
@@ -1530,7 +1558,7 @@ const analyze = (env, parent, form) => {
   } else if (form instanceof Cons) {
     return analyzeList(env, parent, form);
   } else {
-    return makeNode('constant', { env: env, parent: parent, form: form });
+    return makeNode('constant', { env: env, parent: parent, children: [], form: form });
   }
 };
 
@@ -1919,9 +1947,7 @@ const emitNode = (print, node) => {
         print('{\n');
         if (node.tags.size) {
           print(`var tagbody_${node.id}_to;\n`);
-          print(`var tagbody_${node.id}_tags=[`);
-          print(Array.from(node.tags, ([tag]) => formatTag(tag)).join(','));
-          print(`];\n`);
+          print(`var tagbody_${node.id}_id = [];\n`);
         }
         if (node.prelude.length && node.tags.size) {
           print(`try{\n`);
@@ -1929,8 +1955,8 @@ const emitNode = (print, node) => {
               emitNode(print, stmt);
             }
           print(`}catch(e){\n`);
-            print(`if((e instanceof TagEx) && tagbody_${node.id}_tags.indexOf(e.name) >= 0){\n`);
-              print(`tagbody_${node.id}_to=e.name;\n`);
+            print(`if((e instanceof TagEx) && tagbody_${node.id}_id === e.tagbodyId){\n`);
+              print(`tagbody_${node.id}_to=e.tagId;\n`);
             print(`}else{\n`);
               print(`throw e;\n`);
             print(`}\n`);
@@ -1941,22 +1967,22 @@ const emitNode = (print, node) => {
           }
         }
         if (node.tags.size) {
-          const firstTag = formatTag(node.tags.entries().next().value[0]);
+          const firstTag = node.tags.values().next().value[0];
           print(`if (tagbody_${node.id}_to===undefined){\n`);
           print(`tagbody_${node.id}_to=${firstTag};\n`);
           print(`}\n`);
           print(`tagbody_${node.id}:while(true){\n`);
             print(`try{\n`);
               print(`switch(tagbody_${node.id}_to){\n`);
-                for (const [tag, stmts] of node.tags) {
-                  print(`case ${formatTag(tag)}:\n`);
+                for (const [tagId, stmts] of node.tags.values()) {
+                  print(`case ${tagId}:\n`);
                   for (const stmt of stmts) emitNode(print, stmt);
                 }
                 print(`default:\nbreak tagbody_${node.id};\n`);
               print('}\n');
             print(`}catch(e){\n`);
-              print(`if((e instanceof TagEx) && tagbody_${node.id}_tags.indexOf(e.name) >= 0){\n`);
-                print(`tagbody_${node.id}_to=e.name;\n`);
+              print(`if((e instanceof TagEx) && tagbody_${node.id}_id === e.tagbodyId){\n`);
+                print(`tagbody_${node.id}_to=e.tagId;\n`);
                 print(`continue tagbody_${node.id};\n`);
               print(`}else{\n`);
                 print(`throw e;\n`);
@@ -1973,9 +1999,9 @@ const emitNode = (print, node) => {
     } case 'go': {
       if (context === 'return') print('return ');
       if (context === 'return' || context == 'expr') {
-        print(`(function(){throw new TagEx(${formatTag(node.tagName)});})()`);
+        print(`(function(){throw new TagEx(${formatTag(node.tagName)}, tagbody_${node.tagbodyId}_id, ${node.tagId});})()`);
       } else {
-        print(`throw new TagEx(${formatTag(node.tagName)})`);
+        print(`throw new TagEx(${formatTag(node.tagName)}, tagbody_${node.tagbodyId}_id, ${node.tagId})`);
       }
       if (context !== 'expr') print(';\n');
       break;