UNPKG

@firehammer/jexl

Version:

Javascript Expression Language: Powerful context-based expression parser and evaluator

101 lines (100 loc) 5.24 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); function escapeKeyOfExpressionIdentifier(identifier) { for (var _len = arguments.length, keys = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { keys[_key - 1] = arguments[_key]; } if (keys.length === 0) { return identifier; } var key = keys[0]; return escapeKeyOfExpressionIdentifier.apply(void 0, [key.match(/^[A-Za-z_]\w*$/) ? "".concat(identifier, ".").concat(key) : "".concat(identifier, "[\"").concat(key.replace(/"/g, '\\"'), "\"]")].concat((0, _toConsumableArray2.default)(keys.slice(1)))); } function stringify(grammar, ast) { var ancestors = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; if (!ast) { return ""; } var recur = function recur(childAst) { return stringify(grammar, childAst, [].concat((0, _toConsumableArray2.default)(ancestors), [ast])); }; var recurWithBracketsIfRequired = function recurWithBracketsIfRequired(parentElement, childAst) { var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref$isRightHandSide = _ref.isRightHandSide, isRightHandSide = _ref$isRightHandSide === void 0 ? false : _ref$isRightHandSide; var parentPrecedence = parentElement && parentElement.type === "binaryOp" ? parentElement.precedence : 0; var childBinaryExpressionElement = childAst.type === "BinaryExpression" ? grammar.elements[childAst.operator] : null; var childPrecedence = childAst.type === "ConditionalExpression" ? 0 : childBinaryExpressionElement && childBinaryExpressionElement.type === "binaryOp" ? childBinaryExpressionElement.precedence : Infinity; var rhsOfBinaryOpInTernaryConditionWorkaround = isRightHandSide && childAst.type === "FunctionCall" && childAst.pool === "transforms" && parentElement && parentElement.type === "binaryOp" && ancestors[ancestors.length - 1] && ancestors[ancestors.length - 1].type === "ConditionalExpression"; var childExpressionString = recur(childAst); if (isRightHandSide ? parentPrecedence >= childPrecedence || rhsOfBinaryOpInTernaryConditionWorkaround : parentPrecedence > childPrecedence) { return "(".concat(childExpressionString, ")"); } return childExpressionString; }; switch (ast.type) { case "Literal": if (typeof ast.value === "number" && ast.value.toString().includes("e")) { var prefix = ast.value < 0 ? "-" : ""; return prefix + Math.abs(ast.value).toLocaleString("en-US", { useGrouping: false }); } return JSON.stringify(ast.value); case "Identifier": // TODO: if identifierAst can generate FilterExpressions when required then can we ditch `escapeKeyOfExpressionIdentifier`? if (ast.from) { return "".concat(recur(ast.from)).concat(escapeKeyOfExpressionIdentifier("", ast.value)); } else { return escapeKeyOfExpressionIdentifier(ast.value); } case "UnaryExpression": { var right = recur(ast.right); if (ast.right.type === "BinaryExpression" || ast.right.type === "ConditionalExpression") { right = "(".concat(right, ")"); } return "".concat(ast.operator).concat(right); } case "BinaryExpression": { var left = recurWithBracketsIfRequired(grammar.elements[ast.operator], ast.left); var _right = recurWithBracketsIfRequired(grammar.elements[ast.operator], ast.right, { isRightHandSide: true }); return "".concat(left, " ").concat(ast.operator, " ").concat(_right); } case "ConditionalExpression": { var test = recurWithBracketsIfRequired(null, ast.test); var consequent = recurWithBracketsIfRequired(null, ast.consequent); var alternate = recurWithBracketsIfRequired(null, ast.alternate); return "".concat(test, " ? ").concat(consequent, " : ").concat(alternate); } case "ArrayLiteral": return "[".concat(ast.value.map(recur).join(", "), "]"); case "ObjectLiteral": return "{ ".concat(Object.entries(ast.value).map(function (_ref2) { var _ref3 = (0, _slicedToArray2.default)(_ref2, 2), key = _ref3[0], value = _ref3[1]; return "\"".concat(key, "\": ").concat(recur(value)); }).join(", "), " }"); case "FilterExpression": return "".concat(recur(ast.subject), "[").concat(ast.relative ? "." : "").concat(recur(ast.expr), "]"); case "FunctionCall": switch (ast.pool) { case "functions": return "".concat(ast.name, "(").concat(ast.args.map(recur).join(", "), ")"); case "transforms": // Note that transforms always have at least one argument // i.e. `a | b` is `b` with one argument of `a` return "".concat(recur(ast.args[0]), " | ").concat(ast.name).concat(ast.args.length > 1 ? "(".concat(ast.args.slice(1).map(recur).join(", "), ")") : ""); } } } module.exports = { stringify: stringify };