| #!/usr/bin/env node |
| |
| // This script converts for and do-while loops into equivalent while loops. |
| // Note that for-in statements are left unmodified, as they do not have a |
| // simple analogy to while loops. Also note that labeled continue statements |
| // are not correctly handled at this point, and will trigger an assertion |
| // failure if encountered. |
| |
| var assert = require("assert"); |
| var recast = require("recast"); |
| var types = recast.types; |
| var n = types.namedTypes; |
| var b = types.builders; |
| |
| recast.run(function(ast, callback) { |
| recast.visit(ast, { |
| visitForStatement: function(path) { |
| var fst = path.node; |
| |
| path.replace( |
| fst.init, |
| b.whileStatement( |
| fst.test, |
| insertBeforeLoopback(fst, fst.update) |
| ) |
| ); |
| |
| this.traverse(path); |
| }, |
| |
| visitDoWhileStatement: function(path) { |
| var dwst = path.node; |
| return b.whileStatement( |
| b.literal(true), |
| insertBeforeLoopback( |
| dwst, |
| b.ifStatement( |
| dwst.test, |
| b.breakStatement() |
| ) |
| ) |
| ); |
| } |
| }); |
| |
| callback(ast); |
| }); |
| |
| function insertBeforeLoopback(loop, toInsert) { |
| var body = loop.body; |
| |
| if (!n.Statement.check(toInsert)) { |
| toInsert = b.expressionStatement(toInsert); |
| } |
| |
| if (n.BlockStatement.check(body)) { |
| body.body.push(toInsert); |
| } else { |
| body = b.blockStatement([body, toInsert]); |
| loop.body = body; |
| } |
| |
| recast.visit(body, { |
| visitContinueStatement: function(path) { |
| var cst = path.node; |
| |
| assert.equal( |
| cst.label, null, |
| "Labeled continue statements are not yet supported." |
| ); |
| |
| path.replace(toInsert, path.node); |
| return false; |
| }, |
| |
| // Do not descend into nested loops. |
| visitWhileStatement: function() {}, |
| visitForStatement: function() {}, |
| visitForInStatement: function() {}, |
| visitDoWhileStatement: function() {} |
| }); |
| |
| return body; |
| } |