209 lines
5.1 KiB
JavaScript
209 lines
5.1 KiB
JavaScript
var uglify = require('uglify-js');
|
|
var parser = uglify.parser;
|
|
var parse = function (expr) {
|
|
if (typeof expr !== 'string') throw 'expression should be a string';
|
|
|
|
try {
|
|
var ast = parser.parse.apply(null, arguments);
|
|
}
|
|
catch (err) {
|
|
if (err.message === undefined
|
|
|| err.line === undefined
|
|
|| err.col === undefined
|
|
|| err.pos === undefined
|
|
) { throw err }
|
|
|
|
var e = new SyntaxError(
|
|
err.message
|
|
+ '\n at line ' + err.line + ':' + err.col + ' in expression:\n\n'
|
|
+ ' ' + expr.split(/\r?\n/)[err.line]
|
|
);
|
|
|
|
e.original = err;
|
|
e.line = err.line;
|
|
e.col = err.col;
|
|
e.pos = err.pos;
|
|
throw e;
|
|
}
|
|
return ast;
|
|
};
|
|
|
|
var deparse = function (ast, b) {
|
|
return uglify.uglify.gen_code(ast, { beautify : b });
|
|
};
|
|
|
|
var traverse = require('traverse');
|
|
var vm = require('vm');
|
|
|
|
var burrito = module.exports = function (code, cb) {
|
|
var ast = Array_isArray(code)
|
|
? code // already an ast
|
|
: parse(code.toString(), false, true)
|
|
;
|
|
|
|
var ast_ = traverse(ast).map(function mapper () {
|
|
wrapNode(this, cb);
|
|
});
|
|
|
|
return deparse(parse(deparse(ast_)), true);
|
|
};
|
|
|
|
var wrapNode = burrito.wrapNode = function (state, cb) {
|
|
var node = state.node;
|
|
|
|
var ann = Array_isArray(node) && node[0]
|
|
&& typeof node[0] === 'object' && node[0].name
|
|
? node[0]
|
|
: null
|
|
;
|
|
|
|
if (!ann) return undefined;
|
|
|
|
var self = {
|
|
name : ann.name,
|
|
node : node,
|
|
start : node[0].start,
|
|
end : node[0].end,
|
|
value : node.slice(1),
|
|
state : state
|
|
};
|
|
|
|
self.wrap = function (s) {
|
|
var subsrc = deparse(
|
|
traverse(node).map(function (x) {
|
|
if (!this.isRoot) wrapNode(this, cb)
|
|
})
|
|
);
|
|
|
|
if (self.name === 'binary') {
|
|
var a = deparse(traverse(node[2]).map(function (x) {
|
|
if (!this.isRoot) wrapNode(this, cb)
|
|
}));
|
|
var b = deparse(traverse(node[3]).map(function (x) {
|
|
if (!this.isRoot) wrapNode(this, cb)
|
|
}));
|
|
}
|
|
|
|
var src = '';
|
|
|
|
if (typeof s === 'function') {
|
|
if (self.name === 'binary') {
|
|
src = s(subsrc, a, b);
|
|
}
|
|
else {
|
|
src = s(subsrc);
|
|
}
|
|
}
|
|
else {
|
|
src = s.toString()
|
|
.replace(/%s/g, function () {
|
|
return subsrc
|
|
})
|
|
;
|
|
|
|
if (self.name === 'binary') {
|
|
src = src
|
|
.replace(/%a/g, function () { return a })
|
|
.replace(/%b/g, function () { return b })
|
|
;
|
|
}
|
|
}
|
|
|
|
var expr = parse(src);
|
|
state.update(expr, true);
|
|
};
|
|
|
|
var cache = {};
|
|
|
|
self.parent = state.isRoot ? null : function () {
|
|
if (!cache.parent) {
|
|
var s = state;
|
|
var x;
|
|
do {
|
|
s = s.parent;
|
|
if (s) x = wrapNode(s);
|
|
} while (s && !x);
|
|
|
|
cache.parent = x;
|
|
}
|
|
|
|
return cache.parent;
|
|
};
|
|
|
|
self.source = function () {
|
|
if (!cache.source) cache.source = deparse(node);
|
|
return cache.source;
|
|
};
|
|
|
|
self.label = function () {
|
|
return burrito.label(self);
|
|
};
|
|
|
|
if (cb) cb.call(state, self);
|
|
|
|
if (self.node[0].name === 'conditional') {
|
|
self.wrap('[%s][0]');
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
burrito.microwave = function (code, context, cb) {
|
|
if (!cb) { cb = context; context = {} };
|
|
if (!context) context = {};
|
|
|
|
var src = burrito(code, cb);
|
|
return vm.runInNewContext(src, context);
|
|
};
|
|
|
|
burrito.generateName = function (len) {
|
|
var name = '';
|
|
var lower = '$'.charCodeAt(0);
|
|
var upper = 'z'.charCodeAt(0);
|
|
|
|
while (name.length < len) {
|
|
var c = String.fromCharCode(Math.floor(
|
|
Math.random() * (upper - lower + 1) + lower
|
|
));
|
|
if ((name + c).match(/^[A-Za-z_$][A-Za-z0-9_$]*$/)) name += c;
|
|
}
|
|
|
|
return name;
|
|
};
|
|
|
|
burrito.parse = parse;
|
|
burrito.deparse = deparse;
|
|
|
|
burrito.label = function (node) {
|
|
if (node.name === 'call') {
|
|
if (typeof node.value[0] === 'string') {
|
|
return node.value[0];
|
|
}
|
|
else if (node.value[0] && typeof node.value[0][1] === 'string') {
|
|
return node.value[0][1];
|
|
}
|
|
else if (node.value[0][0] === 'dot') {
|
|
return node.value[0][node.value[0].length - 1];
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
else if (node.name === 'var') {
|
|
return node.value[0].map(function (x) { return x[0] });
|
|
}
|
|
else if (node.name === 'defun') {
|
|
return node.value[0];
|
|
}
|
|
else if (node.name === 'function') {
|
|
return node.value[0];
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
var Array_isArray = Array.isArray || function isArray (xs) {
|
|
return Object.prototype.toString.call(xs) === '[object Array]';
|
|
};
|