@metrichor/jmespath
Version:
Typescript implementation of the JMESPath spec (100% compliant)
385 lines • 16.2 kB
JavaScript
"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