swh:1:snp:3af1a03cd091600016317ad7197cfc7dfd1319c7
Tip revision: 049e17de9ca7ad5b59102058d432aa1721232fdd authored by Andreas Stuhlmüller on 09 September 2015, 17:30:06 UTC
0.1.1
0.1.1
Tip revision: 049e17d
cps.js
'use strict';
var assert = require('assert');
var build = require('ast-types').builders;
var types = require('ast-types').namedTypes;
var Syntax = require('estraverse').Syntax;
var match = require('../syntax').match;
var clause = require('../syntax').clause;
var fail = require('../syntax').fail;
var inProgram = require('../syntax').inProgram;
var isPrimitive = require('../syntax').isPrimitive;
var makeGenvar = require('../syntax').makeGenvar;
var genvar = null;
function buildFunction(params, body, id) {
return build.functionExpression(id || null, params,
build.blockStatement([build.expressionStatement(body)]));
}
function buildCall(callee, args) {
return build.callExpression(callee, args);
}
function buildContinuation(param, body) {
return buildFunction([param], body);
}
function buildContinuationCall(callee, arg) {
return buildCall(callee, [arg]);
}
function cpsFunction(id, params, body) {
var k = genvar('k');
return buildFunction([k].concat(params), cpsFinalStatement(body, k, k), id);
}
function bindContinuation(k, metaK) {
if (types.Identifier.check(k)) {
return metaK(k);
}
else {
var k0 = genvar('k');
return buildCall(buildFunction([k0], metaK(k0)), [k]);
}
}
function atomize(node, metaK) {
return match(node, [
clause(Syntax.ArrayExpression, function(elements) {
return atomizeStar(elements, function(elements) {
return metaK(build.arrayExpression(elements));
});
}),
clause(Syntax.AssignmentExpression, function(left, right) {
assert(left.type === Syntax.MemberExpression &&
left.object.name === 'globalStore',
'Assignment is allowed only to fields of globalStore.');
return atomize(left, function(left) {
return atomize(right, function(right) {
return metaK(build.assignmentExpression(node.operator, left, right));
});
});
}),
clause(Syntax.BinaryExpression, function(left, right) {
return atomize(left, function(left) {
return atomize(right, function(right) {
return metaK(build.binaryExpression(node.operator, left, right));
});
});
}),
clause(Syntax.CallExpression, function(callee, args) {
if (isPrimitive(callee)) {
return atomize(callee, function(callee) {
return atomizeStar(args, function(args) {
return metaK(build.callExpression(callee, args));
});
});
}
else {
var x = genvar('result');
return cps(node, buildContinuation(x, metaK(x)));
}
}),
clause(Syntax.ConditionalExpression, function(test, consequent, alternate) {
return atomize(test, function(test) {
var x = genvar('result');
return bindContinuation(buildContinuation(x, metaK(x)), function(k) {
return build.conditionalExpression(test, cps(consequent, k), cps(alternate, k));
});
});
}),
clause(Syntax.FunctionExpression, function(id, params, body) {
return metaK(cpsFunction(id, params, body));
}),
clause(Syntax.Identifier, function() {
return metaK(node);
}),
clause(Syntax.Literal, function() {
return metaK(node);
}),
clause(Syntax.LogicalExpression, function(left, right) {
return atomize(left, function(left) {
var x = genvar('result');
return bindContinuation(buildContinuation(x, metaK(x)), function(k) {
if (node.operator === '||') {
return build.conditionalExpression(left, cps(left, k), cps(right, k));
}
else if (node.operator === '&&') {
return build.conditionalExpression(left, cps(right, k), cps(left, k));
}
else {
console.log(node.operator);
throw new Error('cps: unhandled logical operator ' + node.operator);
}
});
});
}),
clause(Syntax.MemberExpression, function(object, property) {
return atomize(object, function(object) {
return atomize(property, function(property) {
return metaK(build.memberExpression(object, property, node.computed));
});
});
}),
clause(Syntax.ObjectExpression, function(properties) {
return atomizeStar(properties, function(properties) {
return metaK(build.objectExpression(properties));
});
}),
clause(Syntax.Property, function(key, value) {
return atomize(value, function(value) {
return metaK(build.property('init', key, value));
});
}),
clause(Syntax.UnaryExpression, function(argument) {
return atomize(argument, function(argument) {
return metaK(build.unaryExpression(node.operator, argument));
});
}),
clause(Syntax.DebuggerStatement, function() {
var body = build.blockStatement([node]);
return metaK(buildCall(build.functionExpression(null, [], body), []));
})
], fail('atomize: unrecognized node', node));
}
function atomizeStar(es, metaK) {
es = es.concat();
function loop(i) {
if (i === es.length) {
return metaK(es);
}
else {
return atomize(es[i], function(e) {
es[i] = e;
return loop(i + 1);
});
}
}
return loop(0);
}
function cps(node, k) {
switch (node.type) {
case Syntax.ArrayExpression:
case Syntax.AssignmentExpression:
case Syntax.BinaryExpression:
case Syntax.FunctionExpression:
case Syntax.Identifier:
case Syntax.Literal:
case Syntax.MemberExpression:
case Syntax.ObjectExpression:
case Syntax.UnaryExpression:
case Syntax.DebuggerStatement:
return atomize(node, function(node) {
return buildContinuationCall(k, node);
});
default:
return match(node, [
clause(Syntax.CallExpression, function(callee, args) {
return atomize(callee, function(callee) {
return atomizeStar(args, function(args) {
if (isPrimitive(callee)) {
return buildContinuationCall(k, buildCall(callee, args));
}
else {
return buildCall(callee, [k].concat(args));
}
});
});
}),
clause(Syntax.ConditionalExpression, function(test, consequent, alternate) {
return atomize(test, function(test) {
return bindContinuation(k, function(k) {
return build.conditionalExpression(test, cps(consequent, k), cps(alternate, k));
});
});
}),
clause(Syntax.LogicalExpression, function(left, right) {
return atomize(left, function(left) {
return bindContinuation(k, function(k) {
if (node.operator === '||') {
return build.conditionalExpression(left, cps(left, k), cps(right, k));
}
else if (node.operator === '&&') {
return build.conditionalExpression(left, cps(right, k), cps(left, k));
}
else {
console.log(node.operator);
throw new Error('cps: unhandled logical operator ' + node.operator);
}
});
});
})], fail("can't cps", node));
}
}
function cpsDeclarations(declarations, i, metaK) {
return clause(Syntax.VariableDeclarator, function(id, init) {
if (types.FunctionExpression.check(init)) {
init.id = id;
}
if (i + 1 === declarations.length) {
return cps(init, metaK(id));
}
else {
return cps(init, buildContinuation(id, cpsDeclarations(declarations, i + 1, metaK)));
}
})(declarations[i], fail('expected declarator', declarations[i]));
}
function cpsSequence(nodes, i, k, fk) {
if (i === nodes.length) {
return cps(build.identifier('undefined'), k);
}
else if (i + 1 === nodes.length) {
return cpsFinalStatement(nodes[i], k, fk);
}
else {
return cpsInnerStatement(nodes[i], cpsSequence(nodes, i + 1, k, fk), fk);
}
}
function cpsInnerStatement(node, e, fk) {
return match(node, [
clause(Syntax.BlockStatement, function(body) {
return cpsSequence(body, 0, buildContinuation(genvar('dummy'), e), fk);
}),
clause(Syntax.EmptyStatement, function() {
return e;
}),
clause(Syntax.ExpressionStatement, function(expression) {
return cps(expression, buildContinuation(genvar('dummy'), e));
}),
clause(Syntax.IfStatement, function(test, consequent, alternate) {
if (!alternate) {
alternate = build.emptyStatement();
}
var k = buildContinuation(genvar('dummy'), e);
return bindContinuation(k, function(k) { // most likely unnecessary
return atomize(test, function(test) {
return build.conditionalExpression(test,
cpsFinalStatement(consequent, k, fk),
cpsFinalStatement(alternate, k, fk));
});
});
}),
clause(Syntax.ReturnStatement, function(argument) {
return cps(argument || build.identifier('undefined'), fk);
}),
clause(Syntax.VariableDeclaration, function(declarations) {
return cpsDeclarations(declarations, 0, function(id) {
return buildContinuation(id, e);
});
}),
clause(Syntax.DebuggerStatement, function() {
return cps(node, buildContinuation(genvar('debugger'), e));
})], fail('cpsInnerStatement', node));
}
function cpsFinalStatement(node, k, fk) {
return match(node, [
clause(Syntax.BlockStatement, function(body) {
return cpsSequence(body, 0, k, fk);
}),
clause(Syntax.EmptyStatement, function() {
return cps(build.identifier('undefined'), k);
}),
clause(Syntax.ExpressionStatement, function(expression) {
return cps(expression, k);
}),
clause(Syntax.IfStatement, function(test, consequent, alternate) {
if (!alternate) {
alternate = build.emptyStatement();
}
return bindContinuation(k, function(k) { // most likely unnecessary
return atomize(test, function(test) {
return build.conditionalExpression(test,
cpsFinalStatement(consequent, k, fk),
cpsFinalStatement(alternate, k, fk));
});
});
}),
clause(Syntax.ReturnStatement, function(argument) {
return cps(argument || build.identifier('undefined'), fk);
}),
clause(Syntax.VariableDeclaration, function(declarations) {
return cpsDeclarations(declarations, 0, function(id) {
return buildContinuation(id, cps(build.identifier('undefined'), k));
});
}),
clause(Syntax.DebuggerStatement, function() {
return cps(node, k);
})], fail('cpsFinalStatement', node));
}
function cpsMain(node) {
genvar = makeGenvar();
return inProgram(function(expression) {
return clause(Syntax.FunctionExpression, function(id, params, body) {
return cpsFunction(id, params, body);
})(expression, fail('cps: expected FunctionExpression', expression));
})(node, fail('cps: inProgram', node));
}
module.exports = {
cps: cpsMain
};