UNPKG

@abdullah2993/expression-parser

Version:

An expression evaluator written in typescript with the goal to support SQL like WHERE clauses.

205 lines (204 loc) 9.13 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.parse = exports.Parser = void 0; var ast_1 = require("./ast"); var lexer_1 = require("./lexer"); var token_1 = require("./token"); var precedences = (_a = {}, _a[token_1.TokenType.And] = 1, _a[token_1.TokenType.Or] = 2, _a[token_1.TokenType.Not] = 3, _a[token_1.TokenType.Eq] = 4, _a[token_1.TokenType.Neq] = 4, _a[token_1.TokenType.Lt] = 5, _a[token_1.TokenType.Lte] = 5, _a[token_1.TokenType.Gt] = 5, _a[token_1.TokenType.Gte] = 5, _a[token_1.TokenType.Between] = 6, _a[token_1.TokenType.Is] = 7, _a[token_1.TokenType.Plus] = 8, _a[token_1.TokenType.Minus] = 8, _a[token_1.TokenType.Mul] = 9, _a[token_1.TokenType.Div] = 9, _a[token_1.TokenType.Lparn] = 10, _a); var Parser = /** @class */ (function () { function Parser(lexer, precedenceMap) { var _a, _b; if (precedenceMap === void 0) { precedenceMap = precedences; } this.lexer = lexer; this.precedenceMap = precedenceMap; this.currentToken = this.lexer.next(); this.peekToken = this.lexer.next(); this.prefixParsers = (_a = {}, _a[token_1.TokenType.Identifier] = this.parseIdentifier.bind(this), _a[token_1.TokenType.String] = this.parseString.bind(this), _a[token_1.TokenType.Numeric] = this.parseNumber.bind(this), _a[token_1.TokenType.Lparn] = this.parseGroupedExpression.bind(this), _a[token_1.TokenType.True] = this.parseBoolean.bind(this), _a[token_1.TokenType.False] = this.parseBoolean.bind(this), _a[token_1.TokenType.Case] = this.parseCaseExpression.bind(this), _a); this.infixParsers = (_b = {}, _b[token_1.TokenType.Plus] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Minus] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Mul] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Div] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Eq] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Neq] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Gt] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Lt] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Gte] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Lte] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.And] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Or] = this.parseInfixExpression.bind(this), _b[token_1.TokenType.Is] = this.parseIsExpression.bind(this), _b[token_1.TokenType.Between] = this.parseBetweenExpression.bind(this), _b[token_1.TokenType.Lparn] = this.parseCallExpression.bind(this), _b); } Object.defineProperty(Parser.prototype, "currentPrecedence", { get: function () { var _a; return (_a = this.precedenceMap[this.currentToken.type]) !== null && _a !== void 0 ? _a : 0; }, enumerable: false, configurable: true }); Object.defineProperty(Parser.prototype, "peekPrecedence", { get: function () { var _a; return (_a = this.precedenceMap[this.peekToken.type]) !== null && _a !== void 0 ? _a : 0; }, enumerable: false, configurable: true }); Parser.prototype.parse = function () { return this.parseExpression(); }; Parser.prototype.parseExpression = function (precedence) { if (precedence === void 0) { precedence = 0; } var prefixParser = this.prefixParsers[this.currentToken.type]; if (!prefixParser) { throw new Error("Unexpected start of expression: " + this.currentToken); } var leftExpression = prefixParser(); while (precedence < this.peekPrecedence && !this.currentTokenIs(token_1.TokenType.EOF)) { var infixParser = this.infixParsers[this.peekToken.type]; if (!infixParser) { return leftExpression; } this.nextToken(); leftExpression = infixParser(leftExpression); } return leftExpression; }; Parser.prototype.nextToken = function () { this.currentToken = this.peekToken; this.peekToken = this.lexer.next(); if (this.currentTokenIs(token_1.TokenType.Illegal)) { throw new Error("Invalid input: " + this.currentToken); } return this.currentToken; }; Parser.prototype.currentTokenIs = function (type) { return this.currentToken.type === type; }; Parser.prototype.peekTokenIs = function (type) { return this.peekToken.type === type; }; Parser.prototype.expectPeekToken = function (type) { if (this.peekTokenIs(type)) { this.nextToken(); return this.currentToken; } throw new Error("Expected " + type + " but got " + this.peekToken); }; Parser.prototype.parseIdentifier = function () { return new ast_1.IdentifierExpression(this.currentToken.literal); }; Parser.prototype.parseNumber = function () { return new ast_1.ValueExpression(Number(this.currentToken.literal)); }; Parser.prototype.parseString = function () { return new ast_1.ValueExpression(this.currentToken.literal); }; Parser.prototype.parseBoolean = function () { return new ast_1.ValueExpression(this.currentToken.type === token_1.TokenType.True); }; Parser.prototype.parseGroupedExpression = function () { this.nextToken(); var expression = this.parseExpression(); if (!this.expectPeekToken(token_1.TokenType.Rparn)) { throw new Error("Expected ) but got " + this.peekToken); } return expression; }; Parser.prototype.parseInfixExpression = function (left) { var currentPrecedence = this.currentPrecedence; var op = this.currentToken.type; this.nextToken(); return new ast_1.BinaryExpression(op, left, this.parseExpression(currentPrecedence)); }; Parser.prototype.parseIsExpression = function (left) { var op = token_1.TokenType.Eq; if (this.peekTokenIs(token_1.TokenType.Not)) { op = token_1.TokenType.Neq; this.nextToken(); } this.expectPeekToken(token_1.TokenType.Null); return new ast_1.BinaryExpression(op, left, new ast_1.ValueExpression(null)); }; Parser.prototype.parseBetweenExpression = function (left) { this.expectPeekToken(token_1.TokenType.Numeric); var min = this.parseNumber(); this.expectPeekToken(token_1.TokenType.And); this.expectPeekToken(token_1.TokenType.Numeric); var max = this.parseNumber(); return new ast_1.BinaryExpression(token_1.TokenType.And, new ast_1.BinaryExpression(token_1.TokenType.Gte, left, min), new ast_1.BinaryExpression(token_1.TokenType.Lte, left, max)); }; Parser.prototype.parseCallExpression = function (fn) { var args = []; this.nextToken(); while (!this.currentTokenIs(token_1.TokenType.Rparn)) { args.push(this.parseExpression()); this.nextToken(); if (!this.currentTokenIs(token_1.TokenType.Comma) && !this.currentTokenIs(token_1.TokenType.Rparn)) { throw new Error("Expected , or ) got " + this.currentToken); } } return new ast_1.FunctionCallExpression(fn.name, args); }; Parser.prototype.parseCaseExpression = function () { var conditions = []; var last; if (!this.peekTokenIs(token_1.TokenType.When)) { throw new Error("Expected when got " + this.currentToken); } while (!this.peekTokenIs(token_1.TokenType.End) && !this.peekTokenIs(token_1.TokenType.Else)) { this.expectPeekToken(token_1.TokenType.When); this.nextToken(); var when = this.parseExpression(); this.expectPeekToken(token_1.TokenType.Then); this.nextToken(); var then = this.parseExpression(); conditions.push({ when: when, then: then }); } if (this.peekTokenIs(token_1.TokenType.Else)) { this.nextToken(); this.nextToken(); last = this.parseExpression(); } this.expectPeekToken(token_1.TokenType.End); return new ast_1.CaseExpression(conditions, last); }; return Parser; }()); exports.Parser = Parser; function parse(expression) { var lexer = new lexer_1.Lexer(expression); var parser = new Parser(lexer); return parser.parse(); } exports.parse = parse;