UNPKG

@metrichor/jmespath

Version:

Typescript implementation of the JMESPath spec (100% compliant)

385 lines 16.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = void 0; const Lexer_1 = __importStar(require("./Lexer")); const bindingPower = { [Lexer_1.Token.TOK_EOF]: 0, [Lexer_1.Token.TOK_UNQUOTEDIDENTIFIER]: 0, [Lexer_1.Token.TOK_QUOTEDIDENTIFIER]: 0, [Lexer_1.Token.TOK_RBRACKET]: 0, [Lexer_1.Token.TOK_RPAREN]: 0, [Lexer_1.Token.TOK_COMMA]: 0, [Lexer_1.Token.TOK_RBRACE]: 0, [Lexer_1.Token.TOK_NUMBER]: 0, [Lexer_1.Token.TOK_CURRENT]: 0, [Lexer_1.Token.TOK_EXPREF]: 0, [Lexer_1.Token.TOK_ROOT]: 0, [Lexer_1.Token.TOK_PIPE]: 1, [Lexer_1.Token.TOK_OR]: 2, [Lexer_1.Token.TOK_AND]: 3, [Lexer_1.Token.TOK_EQ]: 5, [Lexer_1.Token.TOK_GT]: 5, [Lexer_1.Token.TOK_LT]: 5, [Lexer_1.Token.TOK_GTE]: 5, [Lexer_1.Token.TOK_LTE]: 5, [Lexer_1.Token.TOK_NE]: 5, [Lexer_1.Token.TOK_FLATTEN]: 9, [Lexer_1.Token.TOK_STAR]: 20, [Lexer_1.Token.TOK_FILTER]: 21, [Lexer_1.Token.TOK_DOT]: 40, [Lexer_1.Token.TOK_NOT]: 45, [Lexer_1.Token.TOK_LBRACE]: 50, [Lexer_1.Token.TOK_LBRACKET]: 55, [Lexer_1.Token.TOK_LPAREN]: 60, }; class TokenParser { constructor() { this.index = 0; this.tokens = []; } parse(expression) { this.loadTokens(expression); this.index = 0; const ast = this.expression(0); if (this.lookahead(0) !== Lexer_1.Token.TOK_EOF) { const token = this.lookaheadToken(0); this.errorToken(token, `Unexpected token type: ${token.type}, value: ${token.value}`); } return ast; } loadTokens(expression) { this.tokens = [...Lexer_1.default.tokenize(expression), { type: Lexer_1.Token.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) { let left; let right; let expression; switch (token.type) { case Lexer_1.Token.TOK_LITERAL: return { type: 'Literal', value: token.value }; case Lexer_1.Token.TOK_UNQUOTEDIDENTIFIER: return { type: 'Field', name: token.value }; case Lexer_1.Token.TOK_QUOTEDIDENTIFIER: const node = { type: 'Field', name: token.value }; if (this.lookahead(0) === Lexer_1.Token.TOK_LPAREN) { throw new Error('Quoted identifier not allowed for function names.'); } else { return node; } case Lexer_1.Token.TOK_NOT: right = this.expression(bindingPower.Not); return { type: 'NotExpression', children: [right] }; case Lexer_1.Token.TOK_STAR: left = { type: 'Identity' }; right = (this.lookahead(0) === Lexer_1.Token.TOK_RBRACKET && { type: 'Identity' }) || this.parseProjectionRHS(bindingPower.Star); return { type: 'ValueProjection', children: [left, right] }; case Lexer_1.Token.TOK_FILTER: return this.led(token.type, { type: 'Identity' }); case Lexer_1.Token.TOK_LBRACE: return this.parseMultiselectHash(); case Lexer_1.Token.TOK_FLATTEN: left = { type: Lexer_1.Token.TOK_FLATTEN, children: [{ type: 'Identity' }] }; right = this.parseProjectionRHS(bindingPower.Flatten); return { type: 'Projection', children: [left, right] }; case Lexer_1.Token.TOK_LBRACKET: if (this.lookahead(0) === Lexer_1.Token.TOK_NUMBER || this.lookahead(0) === Lexer_1.Token.TOK_COLON) { right = this.parseIndexExpression(); return this.projectIfSlice({ type: 'Identity' }, right); } if (this.lookahead(0) === Lexer_1.Token.TOK_STAR && this.lookahead(1) === Lexer_1.Token.TOK_RBRACKET) { this.advance(); this.advance(); right = this.parseProjectionRHS(bindingPower.Star); return { children: [{ type: 'Identity' }, right], type: 'Projection', }; } return this.parseMultiselectList(); case Lexer_1.Token.TOK_CURRENT: return { type: Lexer_1.Token.TOK_CURRENT }; case Lexer_1.Token.TOK_ROOT: return { type: Lexer_1.Token.TOK_ROOT }; case Lexer_1.Token.TOK_EXPREF: expression = this.expression(bindingPower.Expref); return { type: 'ExpressionReference', children: [expression] }; case Lexer_1.Token.TOK_LPAREN: const args = []; while (this.lookahead(0) !== Lexer_1.Token.TOK_RPAREN) { if (this.lookahead(0) === Lexer_1.Token.TOK_CURRENT) { expression = { type: Lexer_1.Token.TOK_CURRENT }; this.advance(); } else { expression = this.expression(0); } args.push(expression); } this.match(Lexer_1.Token.TOK_RPAREN); return args[0]; default: this.errorToken(token); } } led(tokenName, left) { let right; switch (tokenName) { case Lexer_1.Token.TOK_DOT: const rbp = bindingPower.Dot; if (this.lookahead(0) !== Lexer_1.Token.TOK_STAR) { right = this.parseDotRHS(rbp); return { type: 'Subexpression', children: [left, right] }; } this.advance(); right = this.parseProjectionRHS(rbp); return { type: 'ValueProjection', children: [left, right] }; case Lexer_1.Token.TOK_PIPE: right = this.expression(bindingPower.Pipe); return { type: Lexer_1.Token.TOK_PIPE, children: [left, right] }; case Lexer_1.Token.TOK_OR: right = this.expression(bindingPower.Or); return { type: 'OrExpression', children: [left, right] }; case Lexer_1.Token.TOK_AND: right = this.expression(bindingPower.And); return { type: 'AndExpression', children: [left, right] }; case Lexer_1.Token.TOK_LPAREN: const name = left.name; const args = []; let expression; while (this.lookahead(0) !== Lexer_1.Token.TOK_RPAREN) { if (this.lookahead(0) === Lexer_1.Token.TOK_CURRENT) { expression = { type: Lexer_1.Token.TOK_CURRENT }; this.advance(); } else { expression = this.expression(0); } if (this.lookahead(0) === Lexer_1.Token.TOK_COMMA) { this.match(Lexer_1.Token.TOK_COMMA); } args.push(expression); } this.match(Lexer_1.Token.TOK_RPAREN); const node = { name, type: 'Function', children: args }; return node; case Lexer_1.Token.TOK_FILTER: const condition = this.expression(0); this.match(Lexer_1.Token.TOK_RBRACKET); right = (this.lookahead(0) === Lexer_1.Token.TOK_FLATTEN && { type: 'Identity' }) || this.parseProjectionRHS(bindingPower.Filter); return { type: 'FilterProjection', children: [left, right, condition] }; case Lexer_1.Token.TOK_FLATTEN: const leftNode = { type: Lexer_1.Token.TOK_FLATTEN, children: [left] }; const rightNode = this.parseProjectionRHS(bindingPower.Flatten); return { type: 'Projection', children: [leftNode, rightNode] }; case Lexer_1.Token.TOK_EQ: case Lexer_1.Token.TOK_NE: case Lexer_1.Token.TOK_GT: case Lexer_1.Token.TOK_GTE: case Lexer_1.Token.TOK_LT: case Lexer_1.Token.TOK_LTE: return this.parseComparator(left, tokenName); case Lexer_1.Token.TOK_LBRACKET: const token = this.lookaheadToken(0); if (token.type === Lexer_1.Token.TOK_NUMBER || token.type === Lexer_1.Token.TOK_COLON) { right = this.parseIndexExpression(); return this.projectIfSlice(left, right); } this.match(Lexer_1.Token.TOK_STAR); this.match(Lexer_1.Token.TOK_RBRACKET); right = this.parseProjectionRHS(bindingPower.Star); return { type: 'Projection', children: [left, right] }; default: return this.errorToken(this.lookaheadToken(0)); } } match(tokenType) { if (this.lookahead(0) === tokenType) { this.advance(); return; } else { const token = this.lookaheadToken(0); this.errorToken(token, `Expected ${tokenType}, got: ${token.type}`); } } errorToken(token, message = '') { const error = new Error(message || `Invalid token (${token.type}): "${token.value}"`); error.name = 'ParserError'; throw error; } parseIndexExpression() { if (this.lookahead(0) === Lexer_1.Token.TOK_COLON || this.lookahead(1) === Lexer_1.Token.TOK_COLON) { return this.parseSliceExpression(); } const node = { type: 'Index', value: this.lookaheadToken(0).value, }; this.advance(); this.match(Lexer_1.Token.TOK_RBRACKET); return node; } projectIfSlice(left, right) { const indexExpr = { type: 'IndexExpression', children: [left, right] }; if (right.type === 'Slice') { return { children: [indexExpr, this.parseProjectionRHS(bindingPower.Star)], type: 'Projection', }; } return indexExpr; } parseSliceExpression() { const parts = [null, null, null]; let index = 0; let currentTokenType = this.lookahead(0); while (currentTokenType !== Lexer_1.Token.TOK_RBRACKET && index < 3) { if (currentTokenType === Lexer_1.Token.TOK_COLON) { index += 1; this.advance(); } else if (currentTokenType === Lexer_1.Token.TOK_NUMBER) { parts[index] = this.lookaheadToken(0).value; this.advance(); } else { const token = this.lookaheadToken(0); this.errorToken(token, `Syntax error, unexpected token: ${token.value}(${token.type})`); } currentTokenType = this.lookahead(0); } this.match(Lexer_1.Token.TOK_RBRACKET); return { children: parts, type: 'Slice', }; } parseComparator(left, comparator) { const right = this.expression(bindingPower[comparator]); return { type: 'Comparator', name: comparator, children: [left, right] }; } parseDotRHS(rbp) { const lookahead = this.lookahead(0); const exprTokens = [Lexer_1.Token.TOK_UNQUOTEDIDENTIFIER, Lexer_1.Token.TOK_QUOTEDIDENTIFIER, Lexer_1.Token.TOK_STAR]; if (exprTokens.includes(lookahead)) { return this.expression(rbp); } if (lookahead === Lexer_1.Token.TOK_LBRACKET) { this.match(Lexer_1.Token.TOK_LBRACKET); return this.parseMultiselectList(); } if (lookahead === Lexer_1.Token.TOK_LBRACE) { this.match(Lexer_1.Token.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) === Lexer_1.Token.TOK_LBRACKET) { return this.expression(rbp); } if (this.lookahead(0) === Lexer_1.Token.TOK_FILTER) { return this.expression(rbp); } if (this.lookahead(0) === Lexer_1.Token.TOK_DOT) { this.match(Lexer_1.Token.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) !== Lexer_1.Token.TOK_RBRACKET) { const expression = this.expression(0); expressions.push(expression); if (this.lookahead(0) === Lexer_1.Token.TOK_COMMA) { this.match(Lexer_1.Token.TOK_COMMA); if (this.lookahead(0) === Lexer_1.Token.TOK_RBRACKET) { throw new Error('Unexpected token Rbracket'); } } } this.match(Lexer_1.Token.TOK_RBRACKET); return { type: 'MultiSelectList', children: expressions }; } parseMultiselectHash() { const pairs = []; const identifierTypes = [Lexer_1.Token.TOK_UNQUOTEDIDENTIFIER, Lexer_1.Token.TOK_QUOTEDIDENTIFIER]; let keyToken; let keyName; let value; // tslint:disable-next-line: prettier for (;;) { keyToken = this.lookaheadToken(0); if (!identifierTypes.includes(keyToken.type)) { throw new Error(`Expecting an identifier token, got: ${keyToken.type}`); } keyName = keyToken.value; this.advance(); this.match(Lexer_1.Token.TOK_COLON); value = this.expression(0); pairs.push({ value, type: 'KeyValuePair', name: keyName }); if (this.lookahead(0) === Lexer_1.Token.TOK_COMMA) { this.match(Lexer_1.Token.TOK_COMMA); } else if (this.lookahead(0) === Lexer_1.Token.TOK_RBRACE) { this.match(Lexer_1.Token.TOK_RBRACE); break; } } return { type: 'MultiSelectHash', children: pairs }; } } exports.Parser = new TokenParser(); exports.default = exports.Parser; //# sourceMappingURL=Parser.js.map