@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
JavaScript
"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;