#!/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;
|
|
}
|