UNPKG

odatafy-parser

Version:

generate odatafy ast from odata query parameters

155 lines (154 loc) 6.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.reverseExprLogic = exports.resolveExpression = exports.reverseEscaped = exports.escapeExpression = exports.hasOnlyMatchingParentheses = exports.escapeFunctions = exports.escapeStrings = exports.getIdentifier = exports.Prefixes = void 0; // this is by far the most amazing algorithm ever created in 6 hours //God intends that one shall never touch any of this code const find_matching_bracket_1 = __importDefault(require("find-matching-bracket")); const crypto_1 = __importDefault(require("crypto")); var Prefixes; (function (Prefixes) { Prefixes["Str_Escape"] = "$"; Prefixes["Func_Escape"] = "%"; Prefixes["Expr_Escape"] = "\u00A7"; Prefixes["Multiexpr_Escape"] = "&"; })(Prefixes = exports.Prefixes || (exports.Prefixes = {})); function getIdentifier(prefix) { return `${prefix}${crypto_1.default.randomBytes(20).toString('hex')}${prefix}`; } exports.getIdentifier = getIdentifier; function escapeStrings(expr) { let escaped = expr; const stringResolve = {}; while (escaped.includes("'")) { const regex = /'.*?'/; const identifier = getIdentifier(Prefixes.Str_Escape); const toEscape = escaped.match(regex); if (toEscape == null) { break; } stringResolve[identifier] = toEscape[0].toString(); escaped = escaped.replace(regex, identifier); } return [escaped, stringResolve]; } exports.escapeStrings = escapeStrings; function escapeFunctions(expr) { let escaped = expr; const functionResolve = {}; const regex = /(concat|contains|endswith|indexof|matchesPattern|startswith|geo.distance|geo.intersects|hassubset|hassubsequence|length|tolower|toupper|trim|year|month|day|hour|minute|second|fractionalseconds|totalseconds|date|time|totaloffsetminutes|round|floor|ceiling|geo.length|mindatetime|maxdatetime|now)\(/g; while ([...escaped.matchAll(regex)].length > 0) { const matches = [...escaped.matchAll(regex)]; const end = (0, find_matching_bracket_1.default)(escaped, matches[0].index + (matches[0][0].length - 1)); const identifier = getIdentifier(Prefixes.Func_Escape); const toEscape = escaped.substring(matches[0].index, end + 1); functionResolve[identifier] = toEscape; escaped = escaped.substring(0, matches[0].index) + identifier + escaped.substring(end + 1); } return [escaped, functionResolve]; } exports.escapeFunctions = escapeFunctions; function hasOnlyMatchingParentheses(expr) { return [...expr.matchAll(/\(/g)].length == [...expr.matchAll(/\)/g)].length; } exports.hasOnlyMatchingParentheses = hasOnlyMatchingParentheses; const expressionResolver = {}; const multiExpressionResolve = {}; function escapeExpression(expr, depth = 0) { let result = ''; const andOrRegex = /( and | or )/; const expressionsElements = expr.split(andOrRegex); for (let i = 0; i < expressionsElements.length; i++) { if (expressionsElements[i].match(andOrRegex)) { result += expressionsElements[i]; continue; } if (!(expressionsElements[i].includes('(') || expressionsElements[i].includes(')')) || hasOnlyMatchingParentheses(expressionsElements[i])) { const identifier = getIdentifier(Prefixes.Expr_Escape); result += identifier; expressionResolver[identifier] = expressionsElements[i]; continue; } let subExpr = expressionsElements[i]; let not = false; if (subExpr.startsWith('not ')) { subExpr = subExpr.replace('not ', ''); not = true; } //cannot be out of bounds while (!hasOnlyMatchingParentheses(subExpr)) { subExpr += expressionsElements[i + 1] + expressionsElements[i + 2]; i += 2; } subExpr = subExpr.trim(); subExpr = subExpr.substring(1, subExpr.length - 1); const identifier = getIdentifier(Prefixes.Multiexpr_Escape); const toEscape = escapeExpression(subExpr, depth + 1); result += identifier; multiExpressionResolve[identifier] = { toEscape: toEscape, depth: depth, not: not }; } return result; } exports.escapeExpression = escapeExpression; function reverseEscaped(expr, resolvers) { const andOrRegex = /( and | or )/; const expressionElements = expr.split(andOrRegex).reverse(); expr = expressionElements.join(''); const transformedResolvers = Object.keys(resolvers).map((resolverKey) => { return { toEscape: resolvers[resolverKey].toEscape, depth: resolvers[resolverKey].depth, not: resolvers[resolverKey].not, key: resolverKey }; }); const maxDepth = Math.max(...transformedResolvers.map((o) => o.depth)); for (let i = 0; i <= maxDepth; i++) { const transformedResolversOnDepth = transformedResolvers.filter((transRes) => transRes.depth == i); transformedResolversOnDepth.forEach((tRoD) => { const tRoDElements = tRoD.toEscape.split(andOrRegex).reverse(); tRoD.toEscape = tRoDElements.join(''); expr = expr.replace(tRoD.key, `${tRoD.not ? 'not ' : ''}(${tRoD.toEscape})`); }); } return expr; } exports.reverseEscaped = reverseEscaped; function resolveExpression(expr, exprResolver, funcResolver, strResolver) { Object.keys(exprResolver).forEach((key) => { expr = expr.replace(key, exprResolver[key]); }); Object.keys(funcResolver).forEach((key) => { expr = expr.replace(key, funcResolver[key]); }); Object.keys(strResolver).forEach((key) => { expr = expr.replace(key, strResolver[key]); }); return expr; } exports.resolveExpression = resolveExpression; function reverseExprLogic(expr) { const [strResult, stringResolver] = escapeStrings(expr); const [funcResult, functionResolver] = escapeFunctions(strResult); const exprResult = escapeExpression(funcResult); const reverseResult = reverseEscaped(exprResult, multiExpressionResolve); const result = resolveExpression(reverseResult, expressionResolver, functionResolver, stringResolver); return result; } exports.reverseExprLogic = reverseExprLogic; function filterExpressionPreProc(expr) { return reverseExprLogic(expr); } exports.default = filterExpressionPreProc; //console.log(reverseExprLogic("Alter eq 5 and contains(XYZ, 'B') or NOT ((Name eq 'Fynn') or NOT (Name eq 'Robbyn' and Name lte 3 and Lol gt 5 ) ) or Mem gt 6"));