author | Alan Dipert
<alan@dipert.org> 2019-11-11 14:10:10 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-11-11 14:10:10 UTC |
parent | b04a1d962af3f66c8c0fcb256d7d5cccd8f9f3e1 |
jacl.js | +49 | -15 |
diff --git a/jacl.js b/jacl.js index c058183..f595457 100644 --- a/jacl.js +++ b/jacl.js @@ -1192,11 +1192,16 @@ const analyzeSpecials = new Map([ } tags.set(currentTag, currentStmts); - const node = makeNode('tagbody', { env: env, parent: parent, form: form }); + const node = makeNode('tagbody', { + env: env, + parent: parent, + form: form, + id: env.newId() + }); const childEnv = env .withContext('statement') - .withTags(Array.from(tags.keys()).filter(x => x !== null)); + .withTags(node.id, Array.from(tags.keys()).filter(x => x !== null)); // Second pass: analyze statements const ana = analyze.bind(null, childEnv, node); @@ -1217,7 +1222,13 @@ const analyzeSpecials = new Map([ env: env, parent: parent, form: form, - tagName: tagName + 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) }); }], [JACLPKG.intern('%IF'), (env, parent, form) => { @@ -1290,6 +1301,11 @@ const analyzeSymbol = (env, parent, form) => { return node; }; +const makeCounter = () => { + let i = 0; + return () => i++; +}; + class Env { constructor(init = true) { if (init) Env.init(this); @@ -1297,15 +1313,18 @@ class Env { } static init(env) { env.locals = new Set(); - env.tags = new Set(); + // sym => tagbodyName + env.tags = new Map(); env.context = 'sval'; + env.counter = makeCounter(); return env; } clone() { const newEnv = new Env(false); newEnv.locals = new Set(this.locals); - newEnv.tags = new Set(this.tags); + newEnv.tags = new Map(this.tags); newEnv.context = this.context; + newEnv.counter = this.counter; return newEnv; } withLocals(syms) { @@ -1313,16 +1332,19 @@ class Env { newEnv.locals = new Set([...this.locals, ...syms]); return newEnv; } - withTags(syms) { + newId() { + return this.counter(); + } + withTags(id, tags) { const newEnv = this.clone(); - newEnv.tags = new Set([...this.tags, ...syms]); + newEnv.tags = new Map([...this.tags, ...tags.map(tag => [tag, id])]); return newEnv; } hasLocal(sym) { return this.locals.has(sym); } - hasTag(sym) { - return this.tags.has(sym); + hasTag(tag) { + return this.tags.has(tag); } withContext(context) { const newEnv = this.clone(); @@ -1658,16 +1680,28 @@ const emitNode = (print, node) => { print('\n'); } break; - case 'tagbody': + case 'tagbody': { if (context === 'return') print('return '); - if (context === 'statement') { - // TODO + if (context === 'sval' || context === 'return') { + throw new Error('TODO tagbody IIFE'); } else { + for (const stmt of node.prelude) { + emitNode(print, stmt); + } + if (node.tags.size) { + const firstTag = () => { + const name = node.tags.entries().next().value[0]; + return typeof name === 'number' ? name : `'${escapeSingle(name)}'`; + }; + print(`tagbody_${node.id}_to=${firstTag()};\n`); + print(`tagbody_${node.id}:while(true){\n`); + print(`break;`); + print('}\n'); + } } - if (context !== 'sval') print(';\n'); + //if (context !== 'sval') print(';\n'); break; - // TODO lambda - default: + } default: throw new Error(`Unknown op: ${op}`); } };