ut-tools
Version:
Build and Release management automation package.
114 lines (97 loc) • 2.9 kB
JavaScript
export default function reduce(node, ctx) {
switch (node.type) {
case 'Program':
return reduceProgram(node, ctx);
case 'ExpressionStatement':
return reduce(node.expression, ctx);
case 'MemberExpression':
return reduceMemExpr(node, ctx);
case 'LogicalExpression':
return reduceLogExpr(node, ctx);
case 'ConditionalExpression':
return reduceConExpr(node, ctx);
case 'BinaryExpression':
return reduceBinExpr(node, ctx);
case 'UnaryExpression':
return reduceUnExpr(node, ctx);
case 'CallExpression':
return reduceCallExpr(node, ctx);
case 'NewExpression':
return reduceNewExpr(node, ctx);
case 'ArrayExpression':
return reduceArrExpr(node, ctx);
case 'ThisExpression':
return ctx;
case 'Identifier':
return resolveIdentifier(node.name, ctx);
case 'Literal':
return node.value;
default:
throw SyntaxError('Unexpected node');
}
}
function reduceProgram(node, ctx) {
if (node.body.length !== 1) {
throw SyntaxError('Too complex expression');
}
return reduce(node.body[0], ctx);
}
function reduceMemExpr(node, ctx) {
const value = reduce(node.object, ctx);
const key =
node.property.type === 'Identifier'
? node.property.name
: reduce(node.property, ctx);
if (typeof value[key] === 'function') {
return value[key].bind(value);
}
return value[key];
}
function reduceCallExpr(node, ctx) {
return Reflect.apply(
reduce(node.callee, ctx),
null,
node.arguments.map((arg) => reduce(arg, ctx)),
);
}
function reduceNewExpr(node, ctx) {
return Reflect.construct(
reduce(node.callee, ctx),
node.arguments.map((arg) => reduce(arg, ctx)),
);
}
function reduceUnExpr(node, ctx) {
if (!node.prefix || node.argument.type === 'UnaryExpression') {
// node.argument.type === 'UnaryExpression' condition is jsep specific, as it doesn't support UpdateExpression(s) and produce double UnaryExpression(s)
throw SyntaxError('Unexpected operator');
}
return Function('v', `return ${node.operator}v`)(reduce(node.argument, ctx));
}
function reduceConExpr(node, ctx) {
return Function('t, c, a', `return t ? c : a`)(
reduce(node.test, ctx),
reduce(node.consequent, ctx),
reduce(node.alternate, ctx),
);
}
function reduceBinExpr(node, ctx) {
return evalLhsRhs(node, ctx);
}
function reduceLogExpr(node, ctx) {
return evalLhsRhs(node, ctx);
}
function reduceArrExpr(node, ctx) {
return node.elements.map((el) => reduce(el, ctx));
}
function evalLhsRhs(node, ctx) {
return Function('lhs, rhs', `return lhs ${node.operator} rhs`)(
reduce(node.left, ctx),
reduce(node.right, ctx),
);
}
function resolveIdentifier(name, ctx) {
if (ctx === void 0 || !(name in ctx)) {
throw ReferenceError(`${name} is not defined`);
}
return Reflect.get(ctx, name, ctx);
}