author | Alan Dipert
<alan@dipert.org> 2019-11-16 05:45:14 UTC |
committer | Alan Dipert
<alan@dipert.org> 2019-11-16 05:45:14 UTC |
parent | b16f9df808dc005edaecb82bc912ed783b2c32fd |
jacl.js | +77 | -21 |
diff --git a/jacl.js b/jacl.js index d73b76f..0546e8b 100644 --- a/jacl.js +++ b/jacl.js @@ -1,6 +1,13 @@ // Sentinel used in a few places to indicate absence of a user-provided value const UNDEFINED = new Object(); +class TagEx extends Error { + constructor(name) { + super(`Unknown GO tag: ${name}`); + this.name = name; + } +} + class Cons { constructor(car, cdr = null) { this.car = car; @@ -762,6 +769,8 @@ const SPECIAL_FORMS = [ '%CALL', '%DOT', '%LAMBDA', + '%LET', + '%PROG', '%SET', '%TAGBODY', '%GO' @@ -858,8 +867,6 @@ const merge = (...objs) => Object.assign(Object.create(null), ...objs); const makeNode = (op, ...objs) => merge({ op: op }, ...objs); -//(def specials '#{if def fn* do let* loop recur new set! ns}) - const analyzeBlock = (env, parent, forms) => { let stmts = forms.slice(0, forms.length-1) .map(x => analyze(env.withContext('stmt'), parent, x)), @@ -1152,6 +1159,15 @@ const analyzeSpecials = new Map([ analyzeBlock(env.withLocals(node.bindings.map(x => x[0])), node, body) ); }], + [JACLPKG.intern('%PROG'), (env, parent, form) => { + const [, ...body] = form; + const node = makeNode('prog', { + env: env, + parent: parent, + form: form + }); + return merge(node, analyzeBlock(env, node, body)); + }], [JACLPKG.intern('%SET'), (env, parent, form) => { const [, target, val] = form; if (!(target instanceof LispSymbol)) @@ -1693,38 +1709,78 @@ const emitNode = (print, node) => { break; case 'tagbody': { if (context === 'return') print('return '); - if (context === 'expr' || context === 'return') { - throw new Error('TODO tagbody IIFE'); - } else { - for (const stmt of node.prelude) { - emitNode(print, stmt); + if (context === 'return' || context === 'expr') { + print('(function()\n'); + } + 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`); + } + if (node.prelude.length && node.tags.size) { + print(`try{\n`); + for (const stmt of node.prelude) { + 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(`}else{\n`); + print(`throw e;\n`); + print(`}\n`); + print(`}\n`); + } else if (node.prelude.length) { + for (const stmt of node.prelude) { + emitNode(print, stmt); + } } if (node.tags.size) { const firstTag = formatTag(node.tags.entries().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(`switch(tagbody_${node.id}_to){\n`); - for (const [tag, stmts] of node.tags) { - print(`case ${formatTag(tag)}:\n`); - for (const stmt of stmts) emitNode(print, stmt); - } - print(`default:\nbreak tagbody_${node.id};\n`); - print('}\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 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(`continue tagbody_${node.id};\n`); + print(`}else{\n`); + print(`throw e;\n`); + print(`}\n`); + print(`}\n`); print('}\n'); } + if (context === 'return' || context === 'expr') { + print('return null;\n})()'); + } else { + print('}\n'); } - //if (context !== 'expr') print(';\n'); break; } case 'go': { if (context === 'return') print('return '); - if (context === 'expr' || context === 'return') { - throw new Error('TODO go IIFE'); + if (context === 'return' || context == 'expr') { + print(`(function(){throw new TagEx(${formatTag(node.tagName)});})()`); } else { - print('{\n'); - print(`tagbody_${node.tagbodyId}_to=${formatTag(node.tagName)};\n`); - print(`continue tagbody_${node.tagbodyId};\n`); - print('}\n'); + print(`throw new TagEx(${formatTag(node.tagName)})`); } + if (context !== 'expr') print(';\n'); + break; + } case 'prog': { + if (context === 'expr') print('(function()'); + print('{'); + emitBlock(print, node.statements, node.ret); + print('}'); + if (context === 'expr') print(')()'); break; } default: throw new Error(`Unknown op: ${op}`);