UNPKG

@metrichor/jmespath

Version:

Typescript implementation of the JMESPath spec (100% compliant)

308 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Lexer = exports.basicTokens = exports.Token = void 0; const index_1 = require("./utils/index"); var Token; (function (Token) { Token["TOK_EOF"] = "EOF"; Token["TOK_UNQUOTEDIDENTIFIER"] = "UnquotedIdentifier"; Token["TOK_QUOTEDIDENTIFIER"] = "QuotedIdentifier"; Token["TOK_RBRACKET"] = "Rbracket"; Token["TOK_RPAREN"] = "Rparen"; Token["TOK_COMMA"] = "Comma"; Token["TOK_COLON"] = "Colon"; Token["TOK_RBRACE"] = "Rbrace"; Token["TOK_NUMBER"] = "Number"; Token["TOK_CURRENT"] = "Current"; Token["TOK_ROOT"] = "Root"; Token["TOK_EXPREF"] = "Expref"; Token["TOK_PIPE"] = "Pipe"; Token["TOK_OR"] = "Or"; Token["TOK_AND"] = "And"; Token["TOK_EQ"] = "EQ"; Token["TOK_GT"] = "GT"; Token["TOK_LT"] = "LT"; Token["TOK_GTE"] = "GTE"; Token["TOK_LTE"] = "LTE"; Token["TOK_NE"] = "NE"; Token["TOK_FLATTEN"] = "Flatten"; Token["TOK_STAR"] = "Star"; Token["TOK_FILTER"] = "Filter"; Token["TOK_DOT"] = "Dot"; Token["TOK_NOT"] = "Not"; Token["TOK_LBRACE"] = "Lbrace"; Token["TOK_LBRACKET"] = "Lbracket"; Token["TOK_LPAREN"] = "Lparen"; Token["TOK_LITERAL"] = "Literal"; })(Token = exports.Token || (exports.Token = {})); exports.basicTokens = { '(': Token.TOK_LPAREN, ')': Token.TOK_RPAREN, '*': Token.TOK_STAR, ',': Token.TOK_COMMA, '.': Token.TOK_DOT, ':': Token.TOK_COLON, '@': Token.TOK_CURRENT, ['$']: Token.TOK_ROOT, ']': Token.TOK_RBRACKET, '{': Token.TOK_LBRACE, '}': Token.TOK_RBRACE, }; const operatorStartToken = { '!': true, '<': true, '=': true, '>': true, }; const skipChars = { '\t': true, '\n': true, '\r': true, ' ': true, }; class StreamLexer { constructor() { this._current = 0; } tokenize(stream) { const tokens = []; this._current = 0; let start; let identifier; let token; while (this._current < stream.length) { if (index_1.isAlpha(stream[this._current])) { start = this._current; identifier = this.consumeUnquotedIdentifier(stream); tokens.push({ start, type: Token.TOK_UNQUOTEDIDENTIFIER, value: identifier, }); } else if (exports.basicTokens[stream[this._current]] !== undefined) { tokens.push({ start: this._current, type: exports.basicTokens[stream[this._current]], value: stream[this._current], }); this._current += 1; } else if (index_1.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: Token.TOK_QUOTEDIDENTIFIER, value: identifier, }); } else if (stream[this._current] === `'`) { start = this._current; identifier = this.consumeRawStringLiteral(stream); tokens.push({ start, type: Token.TOK_LITERAL, value: identifier, }); } else if (stream[this._current] === '`') { start = this._current; const literal = this.consumeLiteral(stream); tokens.push({ start, type: Token.TOK_LITERAL, value: literal, }); } else if (operatorStartToken[stream[this._current]] !== undefined) { token = this.consumeOperator(stream); token && tokens.push(token); } else if (skipChars[stream[this._current]] !== undefined) { this._current += 1; } else if (stream[this._current] === '&') { start = this._current; this._current += 1; if (stream[this._current] === '&') { this._current += 1; tokens.push({ start, type: Token.TOK_AND, value: '&&' }); } else { tokens.push({ start, type: Token.TOK_EXPREF, value: '&' }); } } else if (stream[this._current] === '|') { start = this._current; this._current += 1; if (stream[this._current] === '|') { this._current += 1; tokens.push({ start, type: Token.TOK_OR, value: '||' }); } else { tokens.push({ start, type: Token.TOK_PIPE, value: '|' }); } } else { const error = new 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 && index_1.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; return JSON.parse(stream.slice(start, this._current)); } 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 literal.replace(`\\'`, `'`); } consumeNumber(stream) { const start = this._current; this._current += 1; const maxLength = stream.length; while (index_1.isNum(stream[this._current]) && this._current < maxLength) { this._current += 1; } const value = parseInt(stream.slice(start, this._current), 10); return { start, value, type: Token.TOK_NUMBER }; } consumeLBracket(stream) { const start = this._current; this._current += 1; if (stream[this._current] === '?') { this._current += 1; return { start, type: Token.TOK_FILTER, value: '[?' }; } if (stream[this._current] === ']') { this._current += 1; return { start, type: Token.TOK_FLATTEN, value: '[]' }; } return { start, type: Token.TOK_LBRACKET, value: '[' }; } consumeOperator(stream) { const start = this._current; const startingChar = stream[start]; this._current += 1; if (startingChar === '!') { if (stream[this._current] === '=') { this._current += 1; return { start, type: Token.TOK_NE, value: '!=' }; } return { start, type: Token.TOK_NOT, value: '!' }; } if (startingChar === '<') { if (stream[this._current] === '=') { this._current += 1; return { start, type: Token.TOK_LTE, value: '<=' }; } return { start, type: Token.TOK_LT, value: '<' }; } if (startingChar === '>') { if (stream[this._current] === '=') { this._current += 1; return { start, type: Token.TOK_GTE, value: '>=' }; } return { start, type: Token.TOK_GT, value: '>' }; } if (startingChar === '=' && stream[this._current] === '=') { this._current += 1; return { start, type: Token.TOK_EQ, value: '==' }; } } 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).trimLeft(); literalString = literalString.replace('\\`', '`'); const literal = this.looksLikeJSON(literalString) ? JSON.parse(literalString) : JSON.parse(`"${literalString}"`); 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])) { try { JSON.parse(literalString); return true; } catch (ex) { return false; } } return false; } } exports.Lexer = new StreamLexer(); exports.default = exports.Lexer; //# sourceMappingURL=Lexer.js.map