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

}