author | Alan Dipert
<alan@dipert.org> 2020-04-04 05:52:51 UTC |
committer | Alan Dipert
<alan@dipert.org> 2020-04-04 05:52:51 UTC |
parent | e50d74e341c6be8f3932d9dbf1cb4cab17aa6e12 |
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;