UNPKG

@jmespath-community/jmespath

Version:

Typescript implementation of the JMESPath Community specification

1,446 lines (1,440 loc) 72 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); // src/utils/index.ts var isObject = (obj) => { return obj !== null && Object.prototype.toString.call(obj) === "[object Object]"; }; var strictDeepEqual = (first, second) => { if (first === second) { return true; } if (typeof first !== typeof second) { return false; } if (Array.isArray(first) && Array.isArray(second)) { if (first.length !== second.length) { return false; } for (let i = 0; i < first.length; i += 1) { if (!strictDeepEqual(first[i], second[i])) { return false; } } return true; } if (isObject(first) && isObject(second)) { const firstEntries = Object.entries(first); const secondKeys = new Set(Object.keys(second)); if (firstEntries.length !== secondKeys.size) { return false; } for (const [key, value] of firstEntries) { if (!strictDeepEqual(value, second[key])) { return false; } secondKeys.delete(key); } return secondKeys.size === 0; } return false; }; var isFalse = (obj) => { if (obj === null || obj === void 0 || obj === false) { return true; } if (typeof obj === "string") { return obj === ""; } if (typeof obj === "object") { if (Array.isArray(obj)) { return obj.length === 0; } if (obj === null) { return true; } return Object.keys(obj).length === 0; } return false; }; var isAlpha = (ch) => { return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_"; }; var isNum = (ch) => { return ch >= "0" && ch <= "9" || ch === "-"; }; var isAlphaNum = (ch) => { return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_"; }; var ensureInteger = (value) => { if (!(typeof value === "number") || Math.floor(value) !== value) { throw new Error("invalid-value: expecting an integer."); } return value; }; var ensurePositiveInteger = (value) => { if (!(typeof value === "number") || value < 0 || Math.floor(value) !== value) { throw new Error("invalid-value: expecting a non-negative integer."); } return value; }; var ensureNumbers = (...operands) => { for (let i = 0; i < operands.length; i++) { if (operands[i] === null || operands[i] === void 0) { throw new Error("not-a-number: undefined"); } if (typeof operands[i] !== "number") { throw new Error("not-a-number"); } } }; var notZero = (n) => { n = +n; if (!n) { throw new Error("not-a-number: divide by zero"); } return n; }; var add = (left, right) => { ensureNumbers(left, right); const result = left + right; return result; }; var sub = (left, right) => { ensureNumbers(left, right); const result = left - right; return result; }; var mul = (left, right) => { ensureNumbers(left, right); const result = left * right; return result; }; var divide = (left, right) => { ensureNumbers(left, right); const result = left / notZero(right); return result; }; var div = (left, right) => { ensureNumbers(left, right); const result = Math.floor(left / notZero(right)); return result; }; var mod = (left, right) => { ensureNumbers(left, right); const result = left % right; return result; }; // src/utils/strings.ts var findFirst = (subject, sub2, start, end) => { if (!subject || !sub2) { return null; } start = Math.max(ensureInteger(start = start || 0), 0); end = Math.min(ensureInteger(end = end || subject.length), subject.length); const offset = subject.slice(start, end).indexOf(sub2); return offset === -1 ? null : offset + start; }; var findLast = (subject, sub2, start, end) => { if (!subject || !sub2) { return null; } start = Math.max(ensureInteger(start = start || 0), 0); end = Math.min(ensureInteger(end = end || subject.length), subject.length); const offset = subject.slice(start, end).lastIndexOf(sub2); const result = offset === -1 ? null : offset + start; return result; }; var lower = (subject) => subject.toLowerCase(); var ensurePadFuncParams = (name, width, padding) => { padding = padding || " "; if (padding.length > 1) { throw new Error(`invalid value, ${name} expects its 'pad' parameter to be a valid string with a single codepoint`); } ensurePositiveInteger(width); return padding; }; var padLeft = (subject, width, padding) => { padding = ensurePadFuncParams("pad_left", width, padding); return subject && subject.padStart(width, padding) || ""; }; var padRight = (subject, width, padding) => { padding = ensurePadFuncParams("pad_right", width, padding); return subject && subject.padEnd(width, padding) || ""; }; var replace = (subject, string, by, count) => { if (count === 0) { return subject; } if (!count) { return subject.split(string).join(by); } ensurePositiveInteger(count); [...Array(count).keys()].map(() => subject = subject.replace(string, by)); return subject; }; var split = (subject, search2, count) => { if (subject.length == 0 && search2.length === 0) { return []; } if (count === null || count === void 0) { return subject.split(search2); } ensurePositiveInteger(count); if (count === 0) { return [subject]; } const split2 = subject.split(search2); return [...split2.slice(0, count), split2.slice(count).join(search2)]; }; var trim = (subject, chars) => { return trimLeft(trimRight(subject, chars), chars); }; var trimLeft = (subject, chars) => { return trimImpl(subject, (list) => new RegExp(`^[${list}]*(.*?)`), chars); }; var trimRight = (subject, chars) => { return trimImpl(subject, (list) => new RegExp(`(.*?)[${list}]*$`), chars); }; var trimImpl = (subject, regExper, chars) => { const pattern = chars ? chars.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&") : "\\s\x85"; return subject.replace(regExper(pattern), "$1"); }; var upper = (subject) => subject.toUpperCase(); // src/Lexer.ts var basicTokens = { "(": "Lparen" /* TOK_LPAREN */, ")": "Rparen" /* TOK_RPAREN */, "*": "Star" /* TOK_STAR */, ",": "Comma" /* TOK_COMMA */, ".": "Dot" /* TOK_DOT */, ":": "Colon" /* TOK_COLON */, "@": "Current" /* TOK_CURRENT */, "]": "Rbracket" /* TOK_RBRACKET */, "{": "Lbrace" /* TOK_LBRACE */, "}": "Rbrace" /* TOK_RBRACE */, "+": "Plus" /* TOK_PLUS */, "%": "Modulo" /* TOK_MODULO */, "?": "Question" /* TOK_QUESTION */, "\u2212": "Minus" /* TOK_MINUS */, "\xD7": "Multiply" /* TOK_MULTIPLY */, "\xF7": "Divide" /* TOK_DIVIDE */ }; var operatorStartToken = { "!": true, "<": true, "=": true, ">": true, "&": true, "|": true, "/": true }; var skipChars = { " ": true, "\n": true, "\r": true, " ": true }; var StreamLexer = class { constructor() { __publicField(this, "_current", 0); __publicField(this, "_enable_legacy_literals", false); } tokenize(stream, options) { const tokens = []; this._current = 0; this._enable_legacy_literals = options?.enable_legacy_literals || false; let start; let identifier; let token; while (this._current < stream.length) { if (isAlpha(stream[this._current])) { start = this._current; identifier = this.consumeUnquotedIdentifier(stream); tokens.push({ start, type: "UnquotedIdentifier" /* TOK_UNQUOTEDIDENTIFIER */, value: identifier }); } else if (basicTokens[stream[this._current]] !== void 0) { tokens.push({ start: this._current, type: basicTokens[stream[this._current]], value: stream[this._current] }); this._current += 1; } else if (stream[this._current] === "$") { start = this._current; if (this._current + 1 < stream.length && isAlpha(stream[this._current + 1])) { this._current += 1; identifier = this.consumeUnquotedIdentifier(stream); tokens.push({ start, type: "Variable" /* TOK_VARIABLE */, value: identifier }); } else { tokens.push({ start, type: "Root" /* TOK_ROOT */, value: stream[this._current] }); this._current += 1; } } else if (stream[this._current] === "-") { if (this._current + 1 < stream.length && isNum(stream[this._current + 1])) { const token2 = this.consumeNumber(stream); token2 && tokens.push(token2); } else { const token2 = { start: this._current, type: "Minus" /* TOK_MINUS */, value: "-" }; tokens.push(token2); this._current += 1; } } else if (isNum(stream[this._current])) { token = this.consumeNumber(stream); tokens.push(token); } else if (stream[this._current] === "[") { token = this.consumeLBracket(stream); tokens.push(token); } else if (stream[this._current] === '"') { start = this._current; identifier = this.consumeQuotedIdentifier(stream); tokens.push({ start, type: "QuotedIdentifier" /* TOK_QUOTEDIDENTIFIER */, value: identifier }); } else if (stream[this._current] === `'`) { start = this._current; identifier = this.consumeRawStringLiteral(stream); tokens.push({ start, type: "Literal" /* TOK_LITERAL */, value: identifier }); } else if (stream[this._current] === "`") { start = this._current; const literal = this.consumeLiteral(stream); tokens.push({ start, type: "Literal" /* TOK_LITERAL */, value: literal }); } else if (operatorStartToken[stream[this._current]] !== void 0) { token = this.consumeOperator(stream); token && tokens.push(token); } else if (skipChars[stream[this._current]] !== void 0) { this._current += 1; } else { const error = new Error(`Syntax error: unknown character: ${stream[this._current]}`); error.name = "LexerError"; throw error; } } return tokens; } consumeUnquotedIdentifier(stream) { const start = this._current; this._current += 1; while (this._current < stream.length && isAlphaNum(stream[this._current])) { this._current += 1; } return stream.slice(start, this._current); } consumeQuotedIdentifier(stream) { const start = this._current; this._current += 1; const maxLength = stream.length; while (stream[this._current] !== '"' && this._current < maxLength) { let current = this._current; if (stream[current] === "\\" && (stream[current + 1] === "\\" || stream[current + 1] === '"')) { current += 2; } else { current += 1; } this._current = current; } this._current += 1; const [value, ok] = this.parseJSON(stream.slice(start, this._current)); if (!ok) { const error = new Error(`syntax: unexpected end of JSON input`); error.name = "LexerError"; throw error; } return value; } consumeRawStringLiteral(stream) { const start = this._current; this._current += 1; const maxLength = stream.length; while (stream[this._current] !== `'` && this._current < maxLength) { let current = this._current; if (stream[current] === "\\" && (stream[current + 1] === "\\" || stream[current + 1] === `'`)) { current += 2; } else { current += 1; } this._current = current; } this._current += 1; const literal = stream.slice(start + 1, this._current - 1); return replace(replace(literal, `\\\\`, `\\`), `\\'`, `'`); } consumeNumber(stream) { const start = this._current; this._current += 1; const maxLength = stream.length; while (isNum(stream[this._current]) && this._current < maxLength) { this._current += 1; } const value = parseInt(stream.slice(start, this._current), 10); return { start, value, type: "Number" /* TOK_NUMBER */ }; } consumeLBracket(stream) { const start = this._current; this._current += 1; if (stream[this._current] === "?") { this._current += 1; return { start, type: "Filter" /* TOK_FILTER */, value: "[?" }; } if (stream[this._current] === "]") { this._current += 1; return { start, type: "Flatten" /* TOK_FLATTEN */, value: "[]" }; } return { start, type: "Lbracket" /* TOK_LBRACKET */, value: "[" }; } consumeOrElse(stream, peek, token, orElse) { const start = this._current; this._current += 1; if (this._current < stream.length && stream[this._current] === peek) { this._current += 1; return { start, type: orElse, value: stream.slice(start, this._current) }; } return { start, type: token, value: stream[start] }; } consumeOperator(stream) { const start = this._current; const startingChar = stream[start]; switch (startingChar) { case "!": return this.consumeOrElse(stream, "=", "Not" /* TOK_NOT */, "NE" /* TOK_NE */); case "<": return this.consumeOrElse(stream, "=", "LT" /* TOK_LT */, "LTE" /* TOK_LTE */); case ">": return this.consumeOrElse(stream, "=", "GT" /* TOK_GT */, "GTE" /* TOK_GTE */); case "=": return this.consumeOrElse(stream, "=", "Assign" /* TOK_ASSIGN */, "EQ" /* TOK_EQ */); case "&": return this.consumeOrElse(stream, "&", "Expref" /* TOK_EXPREF */, "And" /* TOK_AND */); case "|": return this.consumeOrElse(stream, "|", "Pipe" /* TOK_PIPE */, "Or" /* TOK_OR */); case "/": return this.consumeOrElse(stream, "/", "Divide" /* TOK_DIVIDE */, "Div" /* TOK_DIV */); } } consumeLiteral(stream) { this._current += 1; const start = this._current; const maxLength = stream.length; while (stream[this._current] !== "`" && this._current < maxLength) { let current = this._current; if (stream[current] === "\\" && (stream[current + 1] === "\\" || stream[current + 1] === "`")) { current += 2; } else { current += 1; } this._current = current; } let literalString = stream.slice(start, this._current).trimStart(); literalString = literalString.replace("\\`", "`"); let literal = null; let ok = false; if (this.looksLikeJSON(literalString)) { [literal, ok] = this.parseJSON(literalString); } if (!ok && this._enable_legacy_literals) { [literal, ok] = this.parseJSON(`"${literalString}"`); } if (!ok) { const error = new Error( `Syntax error: unexpected end of JSON input or invalid format for a JSON literal: ${stream[this._current]}` ); error.name = "LexerError"; throw error; } this._current += 1; return literal; } looksLikeJSON(literalString) { const startingChars = '[{"'; const jsonLiterals = ["true", "false", "null"]; const numberLooking = "-0123456789"; if (literalString === "") { return false; } if (startingChars.includes(literalString[0])) { return true; } if (jsonLiterals.includes(literalString)) { return true; } if (numberLooking.includes(literalString[0])) { const [_, ok] = this.parseJSON(literalString); return ok; } return false; } parseJSON(text) { try { const json = JSON.parse(text); return [json, true]; } catch { return [null, false]; } } }; var Lexer = new StreamLexer(); var Lexer_default = Lexer; // src/Parser.ts var bindingPower = { ["EOF" /* TOK_EOF */]: 0, ["Variable" /* TOK_VARIABLE */]: 0, ["UnquotedIdentifier" /* TOK_UNQUOTEDIDENTIFIER */]: 0, ["QuotedIdentifier" /* TOK_QUOTEDIDENTIFIER */]: 0, ["Rbracket" /* TOK_RBRACKET */]: 0, ["Rparen" /* TOK_RPAREN */]: 0, ["Comma" /* TOK_COMMA */]: 0, ["Rbrace" /* TOK_RBRACE */]: 0, ["Number" /* TOK_NUMBER */]: 0, ["Current" /* TOK_CURRENT */]: 0, ["Expref" /* TOK_EXPREF */]: 0, ["Root" /* TOK_ROOT */]: 0, ["Assign" /* TOK_ASSIGN */]: 1, ["Pipe" /* TOK_PIPE */]: 1, ["Question" /* TOK_QUESTION */]: 2, ["Or" /* TOK_OR */]: 3, ["And" /* TOK_AND */]: 4, ["EQ" /* TOK_EQ */]: 5, ["GT" /* TOK_GT */]: 5, ["LT" /* TOK_LT */]: 5, ["GTE" /* TOK_GTE */]: 5, ["LTE" /* TOK_LTE */]: 5, ["NE" /* TOK_NE */]: 5, ["Minus" /* TOK_MINUS */]: 6, ["Plus" /* TOK_PLUS */]: 6, ["Div" /* TOK_DIV */]: 7, ["Divide" /* TOK_DIVIDE */]: 7, ["Modulo" /* TOK_MODULO */]: 7, ["Multiply" /* TOK_MULTIPLY */]: 7, ["Flatten" /* TOK_FLATTEN */]: 9, ["Star" /* TOK_STAR */]: 20, ["Filter" /* TOK_FILTER */]: 21, ["Dot" /* TOK_DOT */]: 40, ["Not" /* TOK_NOT */]: 45, ["Lbrace" /* TOK_LBRACE */]: 50, ["Lbracket" /* TOK_LBRACKET */]: 55, ["Lparen" /* TOK_LPAREN */]: 60 }; var TokenParser = class _TokenParser { constructor() { __publicField(this, "index", 0); __publicField(this, "tokens", []); } parse(expression, options) { this.loadTokens(expression, options || { enable_legacy_literals: false }); this.index = 0; const ast = this.expression(0); if (this.lookahead(0) !== "EOF" /* TOK_EOF */) { const token = this.lookaheadToken(0); this.errorToken(token, `Syntax error: unexpected token type: ${token.type}, value: ${token.value}`); } return ast; } loadTokens(expression, options) { this.tokens = Lexer_default.tokenize(expression, options); this.tokens.push({ type: "EOF" /* TOK_EOF */, value: "", start: expression.length }); } expression(rbp) { const leftToken = this.lookaheadToken(0); this.advance(); let left = this.nud(leftToken); let currentTokenType = this.lookahead(0); while (rbp < bindingPower[currentTokenType]) { this.advance(); left = this.led(currentTokenType, left); currentTokenType = this.lookahead(0); } return left; } lookahead(offset) { return this.tokens[this.index + offset].type; } lookaheadToken(offset) { return this.tokens[this.index + offset]; } advance() { this.index += 1; } nud(token) { switch (token.type) { case "Variable" /* TOK_VARIABLE */: return { type: "Variable", name: token.value }; case "Literal" /* TOK_LITERAL */: return { type: "Literal", value: token.value }; case "UnquotedIdentifier" /* TOK_UNQUOTEDIDENTIFIER */: { if (_TokenParser.isKeyword(token, "let") && this.lookahead(0) === "Variable" /* TOK_VARIABLE */) { return this.parseLetExpression(); } else { return { type: "Field", name: token.value }; } } case "QuotedIdentifier" /* TOK_QUOTEDIDENTIFIER */: if (this.lookahead(0) === "Lparen" /* TOK_LPAREN */) { throw new Error("Syntax error: quoted identifier not allowed for function names."); } else { return { type: "Field", name: token.value }; } case "Not" /* TOK_NOT */: { const child = this.expression(bindingPower.Not); return { type: "NotExpression", child }; } case "Minus" /* TOK_MINUS */: { const child = this.expression(bindingPower.Minus); return { type: "Unary", operator: token.type, operand: child }; } case "Plus" /* TOK_PLUS */: { const child = this.expression(bindingPower.Plus); return { type: "Unary", operator: token.type, operand: child }; } case "Star" /* TOK_STAR */: { const left = { type: "Identity" }; return { type: "ValueProjection", left, right: this.parseProjectionRHS(bindingPower.Star) }; } case "Filter" /* TOK_FILTER */: return this.led(token.type, { type: "Identity" }); case "Lbrace" /* TOK_LBRACE */: return this.parseMultiselectHash(); case "Flatten" /* TOK_FLATTEN */: { const left = { type: "Flatten", child: { type: "Identity" } }; const right = this.parseProjectionRHS(bindingPower.Flatten); return { type: "Projection", left, right }; } case "Lbracket" /* TOK_LBRACKET */: { if (this.lookahead(0) === "Number" /* TOK_NUMBER */ || this.lookahead(0) === "Colon" /* TOK_COLON */) { const right = this.parseIndexExpression(); return this.projectIfSlice({ type: "Identity" }, right); } if (this.lookahead(0) === "Star" /* TOK_STAR */ && this.lookahead(1) === "Rbracket" /* TOK_RBRACKET */) { this.advance(); this.advance(); const right = this.parseProjectionRHS(bindingPower.Star); return { left: { type: "Identity" }, right, type: "Projection" }; } return this.parseMultiselectList(); } case "Current" /* TOK_CURRENT */: return { type: "Current" /* TOK_CURRENT */ }; case "Root" /* TOK_ROOT */: return { type: "Root" /* TOK_ROOT */ }; case "Expref" /* TOK_EXPREF */: { const child = this.expression(bindingPower.Expref); return { type: "ExpressionReference", child }; } case "Lparen" /* TOK_LPAREN */: { const expression = this.expression(0); this.match("Rparen" /* TOK_RPAREN */); return expression; } default: this.errorToken(token); } } led(tokenName, left) { switch (tokenName) { case "Question" /* TOK_QUESTION */: { const trueExpr = this.expression(0); this.match("Colon" /* TOK_COLON */); const falseExpr = this.expression(0); return { type: "Ternary", condition: left, trueExpr, falseExpr }; } case "Dot" /* TOK_DOT */: { const rbp = bindingPower.Dot; if (this.lookahead(0) !== "Star" /* TOK_STAR */) { const right2 = this.parseDotRHS(rbp); return { type: "Subexpression", left, right: right2 }; } this.advance(); const right = this.parseProjectionRHS(rbp); return { type: "ValueProjection", left, right }; } case "Pipe" /* TOK_PIPE */: { const right = this.expression(bindingPower.Pipe); return { type: "Pipe", left, right }; } case "Or" /* TOK_OR */: { const right = this.expression(bindingPower.Or); return { type: "OrExpression", left, right }; } case "And" /* TOK_AND */: { const right = this.expression(bindingPower.And); return { type: "AndExpression", left, right }; } case "Lparen" /* TOK_LPAREN */: { if (left.type !== "Field") { throw new Error("Syntax error: expected a Field node"); } const name = left.name; const args = this.parseCommaSeparatedExpressionsUntilToken("Rparen" /* TOK_RPAREN */); const node = { name, type: "Function", children: args }; return node; } case "Filter" /* TOK_FILTER */: { const condition = this.expression(0); this.match("Rbracket" /* TOK_RBRACKET */); const right = this.lookahead(0) === "Flatten" /* TOK_FLATTEN */ ? { type: "Identity" } : this.parseProjectionRHS(bindingPower.Filter); return { type: "FilterProjection", left, right, condition }; } case "Flatten" /* TOK_FLATTEN */: { const leftNode = { type: "Flatten", child: left }; const right = this.parseProjectionRHS(bindingPower.Flatten); return { type: "Projection", left: leftNode, right }; } case "Assign" /* TOK_ASSIGN */: { const leftNode = left; const right = this.expression(0); return { type: "Binding", variable: leftNode.name, reference: right }; } case "EQ" /* TOK_EQ */: case "NE" /* TOK_NE */: case "GT" /* TOK_GT */: case "GTE" /* TOK_GTE */: case "LT" /* TOK_LT */: case "LTE" /* TOK_LTE */: return this.parseComparator(left, tokenName); case "Plus" /* TOK_PLUS */: case "Minus" /* TOK_MINUS */: case "Multiply" /* TOK_MULTIPLY */: case "Star" /* TOK_STAR */: case "Divide" /* TOK_DIVIDE */: case "Modulo" /* TOK_MODULO */: case "Div" /* TOK_DIV */: return this.parseArithmetic(left, tokenName); case "Lbracket" /* TOK_LBRACKET */: { const token = this.lookaheadToken(0); if (token.type === "Number" /* TOK_NUMBER */ || token.type === "Colon" /* TOK_COLON */) { const right2 = this.parseIndexExpression(); return this.projectIfSlice(left, right2); } this.match("Star" /* TOK_STAR */); this.match("Rbracket" /* TOK_RBRACKET */); const right = this.parseProjectionRHS(bindingPower.Star); return { type: "Projection", left, right }; } default: return this.errorToken(this.lookaheadToken(0)); } } static isKeyword(token, keyword) { return token.type === "UnquotedIdentifier" /* TOK_UNQUOTEDIDENTIFIER */ && token.value === keyword; } match(tokenType) { if (this.lookahead(0) === tokenType) { this.advance(); return; } else { const token = this.lookaheadToken(0); this.errorToken(token, `Syntax error: expected ${tokenType}, got: ${token.type}`); } } errorToken(token, message = "") { const error = new Error(message || `Syntax error: invalid token (${token.type}): "${token.value}"`); error.name = "ParserError"; throw error; } parseIndexExpression() { if (this.lookahead(0) === "Colon" /* TOK_COLON */ || this.lookahead(1) === "Colon" /* TOK_COLON */) { return this.parseSliceExpression(); } const value = Number(this.lookaheadToken(0).value); this.advance(); this.match("Rbracket" /* TOK_RBRACKET */); return { type: "Index", value }; } projectIfSlice(left, right) { const indexExpr = { type: "IndexExpression", left, right }; if (right.type === "Slice") { return { left: indexExpr, right: this.parseProjectionRHS(bindingPower.Star), type: "Projection" }; } return indexExpr; } parseSliceExpression() { const parts = [null, null, null]; let index = 0; let current = this.lookaheadToken(0); while (current.type != "Rbracket" /* TOK_RBRACKET */ && index < 3) { if (current.type === "Colon" /* TOK_COLON */) { index++; if (index === 3) { this.errorToken(this.lookaheadToken(0), "Syntax error, too many colons in slice expression"); } this.advance(); } else if (current.type === "Number" /* TOK_NUMBER */) { const part = this.lookaheadToken(0).value; parts[index] = part; this.advance(); } else { const next = this.lookaheadToken(0); this.errorToken(next, `Syntax error, unexpected token: ${next.value}(${next.type})`); } current = this.lookaheadToken(0); } this.match("Rbracket" /* TOK_RBRACKET */); const [start, stop, step] = parts; return { type: "Slice", start, stop, step }; } parseLetExpression() { const separated = this.parseCommaSeparatedExpressionsUntilKeyword("in"); const expression = this.expression(0); const bindings = separated.map((binding) => binding); return { type: "LetExpression", bindings, expression }; } parseCommaSeparatedExpressionsUntilKeyword(keyword) { return this.parseCommaSeparatedExpressionsUntil( () => { return _TokenParser.isKeyword(this.lookaheadToken(0), keyword); }, () => { this.advance(); } ); } parseCommaSeparatedExpressionsUntilToken(token) { return this.parseCommaSeparatedExpressionsUntil( () => { return this.lookahead(0) === token; }, () => { return this.match(token); } ); } parseCommaSeparatedExpressionsUntil(isEndToken, matchEndToken) { const args = []; let expression; while (!isEndToken()) { expression = this.expression(0); if (this.lookahead(0) === "Comma" /* TOK_COMMA */) { this.match("Comma" /* TOK_COMMA */); } args.push(expression); } matchEndToken(); return args; } parseComparator(left, comparator) { const right = this.expression(bindingPower[comparator]); return { type: "Comparator", name: comparator, left, right }; } parseArithmetic(left, operator) { const right = this.expression(bindingPower[operator]); return { type: "Arithmetic", operator, left, right }; } parseDotRHS(rbp) { const lookahead = this.lookahead(0); const exprTokens = ["UnquotedIdentifier" /* TOK_UNQUOTEDIDENTIFIER */, "QuotedIdentifier" /* TOK_QUOTEDIDENTIFIER */, "Star" /* TOK_STAR */]; if (exprTokens.includes(lookahead)) { return this.expression(rbp); } if (lookahead === "Lbracket" /* TOK_LBRACKET */) { this.match("Lbracket" /* TOK_LBRACKET */); return this.parseMultiselectList(); } if (lookahead === "Lbrace" /* TOK_LBRACE */) { this.match("Lbrace" /* TOK_LBRACE */); return this.parseMultiselectHash(); } const token = this.lookaheadToken(0); this.errorToken(token, `Syntax error, unexpected token: ${token.value}(${token.type})`); } parseProjectionRHS(rbp) { if (bindingPower[this.lookahead(0)] < 10) { return { type: "Identity" }; } if (this.lookahead(0) === "Lbracket" /* TOK_LBRACKET */) { return this.expression(rbp); } if (this.lookahead(0) === "Filter" /* TOK_FILTER */) { return this.expression(rbp); } if (this.lookahead(0) === "Dot" /* TOK_DOT */) { this.match("Dot" /* TOK_DOT */); return this.parseDotRHS(rbp); } const token = this.lookaheadToken(0); this.errorToken(token, `Syntax error, unexpected token: ${token.value}(${token.type})`); } parseMultiselectList() { const expressions = []; while (this.lookahead(0) !== "Rbracket" /* TOK_RBRACKET */) { const expression = this.expression(0); expressions.push(expression); if (this.lookahead(0) === "Comma" /* TOK_COMMA */) { this.match("Comma" /* TOK_COMMA */); if (this.lookahead(0) === "Rbracket" /* TOK_RBRACKET */) { throw new Error("Syntax error: unexpected token Rbracket"); } } } this.match("Rbracket" /* TOK_RBRACKET */); return { type: "MultiSelectList", children: expressions }; } parseMultiselectHash() { const pairs = []; const identifierTypes = ["UnquotedIdentifier" /* TOK_UNQUOTEDIDENTIFIER */, "QuotedIdentifier" /* TOK_QUOTEDIDENTIFIER */]; let keyToken; let keyName; let value; for (; ; ) { keyToken = this.lookaheadToken(0); if (!identifierTypes.includes(keyToken.type)) { throw new Error(`Syntax error: expecting an identifier token, got: ${keyToken.type}`); } keyName = keyToken.value; this.advance(); this.match("Colon" /* TOK_COLON */); value = this.expression(0); pairs.push({ value, type: "KeyValuePair", name: keyName }); if (this.lookahead(0) === "Comma" /* TOK_COMMA */) { this.match("Comma" /* TOK_COMMA */); } else if (this.lookahead(0) === "Rbrace" /* TOK_RBRACE */) { this.match("Rbrace" /* TOK_RBRACE */); break; } } return { type: "MultiSelectHash", children: pairs }; } }; var Parser = new TokenParser(); var Parser_default = Parser; // src/utils/text.ts var Text = class _Text { constructor(text) { __publicField(this, "_text"); this._text = text; } get string() { return this._text; } get length() { return this.codePoints.length; } compareTo(other) { return _Text.compare(this, new _Text(other)); } static get comparer() { const stringComparer = (left, right) => { return new _Text(left).compareTo(right); }; return stringComparer; } static compare(left, right) { const leftCp = left.codePoints; const rightCp = right.codePoints; for (let index = 0; index < Math.min(leftCp.length, rightCp.length); index++) { if (leftCp[index] === rightCp[index]) { continue; } return leftCp[index] - rightCp[index] > 0 ? 1 : -1; } return leftCp.length - rightCp.length > 0 ? 1 : -1; } reverse() { return String.fromCodePoint(...this.codePoints.reverse()); } get codePoints() { const array = [...this._text].map((s) => s.codePointAt(0)); return array; } }; // src/Runtime.ts var createMathFunction = (mathFn) => ([value]) => mathFn(value); var createStringFunction = (stringFn) => ([subject]) => stringFn(subject); var createObjectFunction = (objFn) => ([obj]) => objFn(obj); var Runtime = class { constructor(interpreter) { __publicField(this, "_interpreter"); __publicField(this, "_functionTable"); __publicField(this, "_customFunctions", /* @__PURE__ */ new Set()); __publicField(this, "TYPE_NAME_TABLE", Object.freeze({ [0 /* TYPE_NUMBER */]: "number", [1 /* TYPE_ANY */]: "any", [2 /* TYPE_STRING */]: "string", [3 /* TYPE_ARRAY */]: "array", [4 /* TYPE_OBJECT */]: "object", [5 /* TYPE_BOOLEAN */]: "boolean", [6 /* TYPE_EXPREF */]: "expression", [7 /* TYPE_NULL */]: "null", [8 /* TYPE_ARRAY_NUMBER */]: "Array<number>", [10 /* TYPE_ARRAY_OBJECT */]: "Array<object>", [9 /* TYPE_ARRAY_STRING */]: "Array<string>", [11 /* TYPE_ARRAY_ARRAY */]: "Array<Array<any>>" })); __publicField(this, "functionAvg", ([inputArray]) => { if (!inputArray || inputArray.length == 0) { return null; } let sum = 0; for (let i = 0; i < inputArray.length; i += 1) { sum += inputArray[i]; } return sum / inputArray.length; }); __publicField(this, "functionContains", ([ searchable, searchValue ]) => { if (Array.isArray(searchable)) { const array = searchable; return array.includes(searchValue); } if (typeof searchable === "string") { const text = searchable; if (typeof searchValue === "string") { return text.includes(searchValue); } } return null; }); __publicField(this, "functionEndsWith", (resolvedArgs) => { const [searchStr, suffix] = resolvedArgs; return searchStr.includes(suffix, searchStr.length - suffix.length); }); __publicField(this, "functionFindFirst", this.createFindFunction(findFirst)); __publicField(this, "functionFindLast", this.createFindFunction(findLast)); __publicField(this, "functionFromItems", ([array]) => { array.map((pair) => { if (pair.length != 2 || typeof pair[0] !== "string") { throw new Error("invalid value, each array must contain two elements, a pair of string and value"); } }); return Object.fromEntries(array); }); __publicField(this, "functionGroupBy", ([array, exprefNode]) => { const keyFunction = this.createKeyFunction(exprefNode, [2 /* TYPE_STRING */]); return array.reduce((acc, cur) => { const k = keyFunction(cur ?? {}); const target = acc[k] = acc[k] || []; target.push(cur); return acc; }, {}); }); __publicField(this, "functionItems", ([inputValue]) => { return Object.entries(inputValue); }); __publicField(this, "functionJoin", (resolvedArgs) => { const [joinChar, listJoin] = resolvedArgs; return listJoin.join(joinChar); }); __publicField(this, "functionLength", ([inputValue]) => { if (typeof inputValue === "string") { return new Text(inputValue).length; } if (Array.isArray(inputValue)) { return inputValue.length; } return Object.keys(inputValue).length; }); __publicField(this, "functionMap", ([exprefNode, elements]) => { if (!this._interpreter) { return []; } const mapped = []; const interpreter = this._interpreter; for (let i = 0; i < elements.length; i += 1) { mapped.push(interpreter.visit(exprefNode, elements[i])); } return mapped; }); __publicField(this, "functionMax", ([inputValue]) => { if (!inputValue.length) { return null; } const typeName = this.getTypeName(inputValue[0]); if (typeName === 0 /* TYPE_NUMBER */) { return Math.max(...inputValue); } const elements = inputValue; let maxElement = elements[0]; for (let i = 1; i < elements.length; i += 1) { if (maxElement.localeCompare(elements[i]) < 0) { maxElement = elements[i]; } } return maxElement; }); __publicField(this, "functionMaxBy", (resolvedArgs) => { const exprefNode = resolvedArgs[1]; const resolvedArray = resolvedArgs[0]; const keyFunction = this.createKeyFunction(exprefNode, [0 /* TYPE_NUMBER */, 2 /* TYPE_STRING */]); let maxNumber = -Infinity; let maxRecord; let current; for (let i = 0; i < resolvedArray.length; i += 1) { current = keyFunction && keyFunction(resolvedArray[i]); if (current !== void 0 && current > maxNumber) { maxNumber = current; maxRecord = resolvedArray[i]; } } return maxRecord || null; }); __publicField(this, "functionMerge", (resolvedArgs) => { let merged = {}; for (let i = 0; i < resolvedArgs.length; i += 1) { const current = resolvedArgs[i]; merged = Object.assign(merged, current); } return merged; }); __publicField(this, "functionMin", ([inputValue]) => { if (!inputValue.length) { return null; } const typeName = this.getTypeName(inputValue[0]); if (typeName === 0 /* TYPE_NUMBER */) { return Math.min(...inputValue); } const elements = inputValue; let minElement = elements[0]; for (let i = 1; i < elements.length; i += 1) { if (elements[i].localeCompare(minElement) < 0) { minElement = elements[i]; } } return minElement; }); __publicField(this, "functionMinBy", (resolvedArgs) => { const exprefNode = resolvedArgs[1]; const resolvedArray = resolvedArgs[0]; const keyFunction = this.createKeyFunction(exprefNode, [0 /* TYPE_NUMBER */, 2 /* TYPE_STRING */]); let minNumber = Infinity; let minRecord; let current; for (let i = 0; i < resolvedArray.length; i += 1) { current = keyFunction && keyFunction(resolvedArray[i]); if (current !== void 0 && current < minNumber) { minNumber = current; minRecord = resolvedArray[i]; } } return minRecord || null; }); __publicField(this, "functionNotNull", (resolvedArgs) => { for (let i = 0; i < resolvedArgs.length; i += 1) { if (this.getTypeName(resolvedArgs[i]) !== 7 /* TYPE_NULL */) { return resolvedArgs[i]; } } return null; }); __publicField(this, "functionPadLeft", this.createPadFunction(padLeft)); __publicField(this, "functionPadRight", this.createPadFunction(padRight)); __publicField(this, "functionReplace", (resolvedArgs) => { const subject = resolvedArgs[0]; const string = resolvedArgs[1]; const by = resolvedArgs[2]; return replace(subject, string, by, resolvedArgs.length > 3 ? resolvedArgs[3] : void 0); }); __publicField(this, "functionSplit", (resolvedArgs) => { const subject = resolvedArgs[0]; const search2 = resolvedArgs[1]; return split(subject, search2, resolvedArgs.length > 2 ? resolvedArgs[2] : void 0); }); __publicField(this, "functionReverse", ([inputValue]) => { const typeName = this.getTypeName(inputValue); if (typeName === 2 /* TYPE_STRING */) { return new Text(inputValue).reverse(); } const reversedArray = inputValue.slice(0); reversedArray.reverse(); return reversedArray; }); __publicField(this, "functionSort", ([inputValue]) => { if (inputValue.length == 0) { return inputValue; } if (typeof inputValue[0] === "string") { return [...inputValue].sort(Text.comparer); } return [...inputValue].sort(); }); __publicField(this, "functionSortBy", (resolvedArgs) => { const sortedArray = resolvedArgs[0].slice(0); if (sortedArray.length === 0) { return sortedArray; } const interpreter = this._interpreter; const exprefNode = resolvedArgs[1]; const requiredType = this.getTypeName(interpreter.visit(exprefNode, sortedArray[0])); if (requiredType !== void 0 && ![0 /* TYPE_NUMBER */, 2 /* TYPE_STRING */].includes(requiredType)) { throw new Error(`Invalid type: unexpected type (${this.TYPE_NAME_TABLE[requiredType]})`); } function throwInvalidTypeError(rt, item) { throw new Error( `Invalid type: expected (${rt.TYPE_NAME_TABLE[requiredType]}), received ${rt.TYPE_NAME_TABLE[rt.getTypeName(item)]}` ); } return sortedArray.sort((a, b) => { const exprA = interpreter.visit(exprefNode, a); const exprB = interpreter.visit(exprefNode, b); if (this.getTypeName(exprA) !== requiredType) { throwInvalidTypeError(this, exprA); } else if (this.getTypeName(exprB) !== requiredType) { throwInvalidTypeError(this, exprB); } if (requiredType === 2 /* TYPE_STRING */) { return Text.comparer(exprA, exprB); } return exprA - exprB; }); }); __publicField(this, "functionStartsWith", ([searchable, searchStr]) => { return searchable.startsWith(searchStr); }); __publicField(this, "functionSum", ([inputValue]) => { return inputValue.reduce((x, y) => x + y, 0); }); __publicField(this, "functionToArray", ([inputValue]) => { if (this.getTypeName(inputValue) === 3 /* TYPE_ARRAY */) { return inputValue; } return [inputValue]; }); __publicField(this, "functionToNumber", ([inputValue]) => { const typeName = this.getTypeName(inputValue); let convertedValue; if (typeName === 0 /* TYPE_NUMBER */) { return inputValue; } if (typeName === 2 /* TYPE_STRING */) { convertedValue = +inputValue; if (!isNaN(convertedValue)) { return convertedValue; } } return null; }); __publicField(this, "functionToString", ([inputValue]) => { if (this.getTypeName(inputValue) === 2 /* TYPE_STRING */) { return inputValue; } return JSON.stringify(inputValue); }); __publicField(this, "functionTrim", this.createTrimFunction(trim)); __publicField(this, "functionTrimLeft", this.createTrimFunction(trimLeft)); __publicField(this, "functionTrimRight", this.createTrimFunction(trimRight)); __publicField(this, "functionType", ([inputValue]) => { switch (this.getTypeName(inputValue)) { case 0 /* TYPE_NUMBER */: return "number"; case 2 /* TYPE_STRING */: return "string"; case 3 /* TYPE_ARRAY */: return "array"; case 4 /* TYPE_OBJECT */: return "object"; case 5 /* TYPE_BOOLEAN */: return "boolean"; case 7 /* TYPE_NULL */: return "null"; default: throw new Error("invalid-type"); } }); __publicField(this, "functionZip", (array) => { const length = Math.min(...array.map((arr) => arr.length)); const result = Array(length).fill(null).map((_, index) => array.map((arr) => arr[index])); return result; }); this._interpreter = interpreter; this._functionTable = this.buildFunctionTable(); } buildFunctionTable() { return { // Math functions abs: { _func: createMathFunction(Math.abs), _signature: [{ types: [0 /* TYPE_NUMBER */] }] }, ceil: { _func: createMathFunction(Math.ceil), _signature: [{ types: [0 /* TYPE_NUMBER */] }] }, floor: { _func: createMathFunction(Math.floor), _signature: [{ types: [0 /* TYPE_NUMBER */] }] }, // String functions lower: { _func: createStringFunction(lower), _signature: [{ types: [2 /* TYPE_STRING */] }] }, upper: { _func: createStringFunction(upper), _signature: [{ types: [2 /* TYPE_STRING */] }] }, // Object functions keys: { _func: createObjectFunction(Object.keys), _signature: [{ types: [4 /* TYPE_OBJECT */] }] }, values: { _func: createObjectFunction(Object.values), _signature: [{ types: [4 /* TYPE_OBJECT */] }] }, // Complex functions that need custom implementations avg: { _func: this.functionAvg, _signature: [{ types: [8 /* TYPE_ARRAY_NUMBER */] }] }, contains: { _func: this.functionContains, _signature: [ { types: [2 /* TYPE_STRING */, 3 /* TYPE_ARRAY */] }, { types: [1 /* TYPE_ANY */] } ] }, ends_with: { _func: this.functionEndsWith, _signature: [{ types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }] }, find_first: { _func: this.functionFindFirst, _signature: [ { types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }, { types: [0 /* TYPE_NUMBER */], optional: true }, { types: [0 /* TYPE_NUMBER */], optional: true } ] }, find_last: { _func: this.functionFindLast, _signature: [ { types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }, { types: [0 /* TYPE_NUMBER */], optional: true }, { types: [0 /* TYPE_NUMBER */], optional: true } ] }, from_items: { _func: this.functionFromItems, _signature: [{ types: [11 /* TYPE_ARRAY_ARRAY */] }] }, group_by: { _func: this.functionGroupBy, _signature: [{ types: [3 /* TYPE_ARRAY */] }, { types: [6 /* TYPE_EXPREF */] }] }, items: { _func: this.functionItems, _signature: [{ types: [4 /* TYPE_OBJECT */] }] }, join: { _func: this.functionJoin, _signature: [{ types: [2 /* TYPE_STRING */] }, { types: [9 /* TYPE_ARRAY_STRING */] }] }, length: { _func: this.functionLength, _signature: [{ types: [2 /* TYPE_STRING */, 3 /* TYPE_ARRAY */, 4 /* TYPE_OBJECT */] }] }, map: { _func: this.functionMap, _signature: [{ types: [6 /* TYPE_EXPREF */] }, { types: [3 /* TYPE_ARRAY */] }] }, max: { _func: this.functionMax, _signature: [{ types: [8 /* TYPE_ARRAY_NUMBER */, 9 /* TYPE_ARRAY_STRING */] }] }, max_by: { _func: this.functionMaxBy, _signature: [{ types: [3 /* TYPE_ARRAY */] }, { types: [6 /* TYPE_EXPREF */] }] }, merge: { _func: this.functionMerge, _signature: [{ types: [4 /* TYPE_OBJECT */], variadic: true }] }, min: { _func: this.functionMin, _signature: [{ types: [8 /* TYPE_ARRAY_NUMBER */, 9 /* TYPE_ARRAY_STRING */] }] }, min_by: { _func: this.functionMinBy, _signature: [{ types: [3 /* TYPE_ARRAY */] }, { types: [6 /* TYPE_EXPREF */] }] }, not_null: { _func: this.functionNotNull, _signature: [{ types: [1 /* TYPE_ANY */], variadic: true }] }, pad_left: { _func: this.functionPadLeft, _signature: [ { types: [2 /* TYPE_STRING */] }, { types: [0 /* TYPE_NUMBER */] }, { types: [2 /* TYPE_STRING */], optional: true } ] }, pad_right: { _func: this.functionPadRight, _signature: [ { types: [2 /* TYPE_STRING */] }, { types: [0 /* TYPE_NUMBER */] }, { types: [2 /* TYPE_STRING */], optional: true } ] }, replace: { _func: this.functionReplace, _signature: [ { types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }, { types: [0 /* TYPE_NUMBER */], optional: true } ] }, reverse: { _func: this.functionReverse, _signature: [{ types: [2 /* TYPE_STRING */, 3 /* TYPE_ARRAY */] }] }, sort: { _func: this.functionSort, _signature: [{ types: [9 /* TYPE_ARRAY_STRING */, 8 /* TYPE_ARRAY_NUMBER */] }] }, sort_by: { _func: this.functionSortBy, _signature: [{ types: [3 /* TYPE_ARRAY */] }, { types: [6 /* TYPE_EXPREF */] }] }, split: { _func: this.functionSplit, _signature: [ { types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }, { types: [0 /* TYPE_NUMBER */], optional: true } ] }, starts_with: { _func: this.functionStartsWith, _signature: [{ types: [2 /* TYPE_STRING */] }, { types: [2 /* TYPE_STRING */] }] }, sum: { _func: this.functionSum, _signature: [{ types: [8 /* TYPE_ARRAY_NUMBER */] }] }, to_array: { _func: this.functionToArray, _signature: [{ types: [1 /* TYPE_ANY */] }] }, to_number: { _func: this.functionToNumber, _signature: [{ types: [1 /* TYPE_ANY */] }] }, to_string: { _func: this.functionToString, _signature: [{ types: [1 /* TYPE_ANY */] }] }, trim: { _func: this.functionTrim, _signature: [{ types: [2 /* TYPE_STRING