author | Alan Dipert
<alan@dipert.org> 2020-04-11 07:25:37 UTC |
committer | Alan Dipert
<alan@dipert.org> 2020-04-11 07:25:37 UTC |
parent | 90f9d4acde7c2ab213f4b990478148bf22cefe31 |
jacl-tests.lisp | +10 | -2 |
jacl.js | +40 | -2 |
diff --git a/jacl-tests.lisp b/jacl-tests.lisp index 59ffc72..e371a40 100644 --- a/jacl-tests.lisp +++ b/jacl-tests.lisp @@ -67,7 +67,15 @@ end) (assert= x 0) (assert= y 10))) -; -;(deftest "Dynamic TAGBODY") + +(defun countdown (from) + (tagbody + start + (when (> from 0) + (log @"from=" from) + (setq from (1- from)) + (go start)))) + +(countdown 3) (\. @|QUnit| (|start|)) diff --git a/jacl.js b/jacl.js index ccfdd17..1f12ed2 100644 --- a/jacl.js +++ b/jacl.js @@ -1567,6 +1567,36 @@ const analyze = (env, parent, form) => { } }; +const findNodesByName = (root, op) => { + let found = []; + if (root.op === op) found.push(root); + for (const child of root.children) { + found = [...found, ...findNodesByName(child, op)]; + } + return found; +}; + +const optimizeTagbody = node => { + gos: for (const go of findNodesByName(node, 'go')) { + let parent = go.parent; + while (parent) { + if (parent.op === 'tagbody' && go.tagbodyId === parent.id) { + go.op = 'go-continue'; + continue gos; + } + if (parent.op === 'lambda') { + continue gos; + } + parent = parent.parent; + } + } +}; + +const optimize = node => { + optimizeTagbody(node); + return node; +}; + const escapeSingle = name => name.replace(/'/g, "\\'"); const constantCode = val => { @@ -2010,6 +2040,14 @@ const emitNode = (print, node) => { } if (context !== 'expr') print(';\n'); break; + } case 'go-continue': { + if (context === 'return' || context === 'stmt') { + print(`tagbody_${node.tagbodyId}_to=${node.tagId};\n`); + print(`continue tagbody_${node.tagbodyId};\n`); + } else { + throw new Exception(`Unimplemented`); + } + break; } case 'progn': { if (context === 'expr' && !node.statements.length) { // If there's only a return expression, just emit that. @@ -2126,7 +2164,7 @@ let replInputStream = new BufferedStream(), const startRepl = async () => { try { for await(const obj of replReader) { - const node = analyze(emptyEnv, null, obj); + const node = optimize(analyze(emptyEnv, null, obj)); console.log(node) const sb = new StringBuffer(); emitNode(sb.append.bind(sb), node); @@ -2153,7 +2191,7 @@ const loadLispScripts = async () => { const rdr = new Reader(ss); for await(const obj of rdr) { if (obj === EOF) break; - const node = analyze(emptyEnv, null, obj); + const node = optimize(analyze(emptyEnv, null, obj)); const sb = new StringBuffer(); emitNode(sb.append.bind(sb), node); eval(sb.toString());