bexp
Version:
Boolean, Logical, And OR NOT Expression Parser
133 lines (119 loc) • 4.38 kB
JavaScript
/**
* Parse an infix notation in following formats
* * A + B - C || D
* * A AND B NOT C OR D
* * A && B !C || D
*/
class Parser{
/**
* @param {string} exp Infix notation or boolean expression
*/
constructor(exp, options){
this.options = Object.assign({ onEmpty: true, allowMathOperators: true}, options);
this._originalExp = exp;
if(typeof exp !== "undefined" ){
if(exp.trim() === "" && this.options.onEmpty) this.test = function(){ return this.options.onEmpty;}
else{
this._replaceOperators(exp);
}
}
}
/**
* Test if the set expression/notation returns true for given input
* @param {string | array} operands
*/
test(operands){
if(typeof this._originalExp === "undefined") throw new Error("The parser is set to test boolean expression only");
if(typeof operands === "string"){
operands = [operands];
}
const boolExpArr = this._convertToBooleanExpression(operands )
//console.log(boolExpArr)
return this._testBoolean(boolExpArr.join(" "));
}
_testBoolean(booleanExp){
try{
//return eval(this._exp.replace ("key", "'"+key+"'"));
//console.log(booleanExp)
return eval(booleanExp);
}catch(e){
throw new Error("Invalid Expression: " + (this._originalExp || booleanExp) );
}
}
/**
* Accepts boolean expression
* * True And False Or True But Not False
* * Yes And Yes Or Yes But Not No
* * Y + N -N
* *
* @param {string} booleanExp
*/
evaluate(booleanExp){
this._replaceOperators(booleanExp);
//console.log(this.tokens);
this._replaceBooleanOperands(this.tokens);
try{
//return eval(this._exp.replace ("key", "'"+key+"'"));
return eval(this.tokens.join(" "));
}catch(e){
throw new Error("Invalid Expression: " + booleanExp );
}
}
_replaceBooleanOperands(){
for(let i = 0; i < this.tokens.length; i++){
const token = this.tokens[i].toLowerCase();
if(token === "yes" || token === "y"){
this.tokens[i] = "true";
}else if(token === "no" || token === "n"){
this.tokens[i] = "false";
}else{
this.tokens[i] = token;
}
}
}
_convertToBooleanExpression(operands ){
const boolExpArr =Array.from(this.tokens);
//console.log(boolExpArr)
for(let i = 0; i < this.tokensIndexArr.length; i++){
const index = this.tokensIndexArr[i];
const operand = this.tokens[index];
boolExpArr[index] = operands.indexOf(operand) !== -1;
}
return boolExpArr;
}
_replaceOperators(exp){
exp = exp.replace("&&", " && ");
exp = exp.replace("||", " || ");
exp = exp.replace("(", " ( ");
exp = exp.replace(")", " ) ");
exp = exp.replace("!", " ! ");
if(this.options.allowMathOperators){
exp = exp.replace("+", " + ");
exp = exp.replace("-", " ! ");
}
exp = exp.trim();
this.tokens = exp.split(/[ \t]+/g);
this.tokensIndexArr = [];
for(let i = 0; i < this.tokens.length; i++){
const token = this.tokens[i].trim().toLowerCase();
if(token === "and" || token === "+" || token === "&&" || token === "but"){
this.tokens[i] = "&&";
}else if(token === "or" || token === "||" ){
this.tokens[i] = "||";
}else if(token === "not" || token === "-" || token === "!"){
const previouseToken = this.tokens[i-1];
if(previouseToken && previouseToken !== "&&" && previouseToken !== "||" && previouseToken !== "("){
this.tokens[i] = "&& !";
}else{
this.tokens[i] = "!";
}
}else if(token === "(" || token === ")"){
}else{
this.tokensIndexArr.push(i);
//this.tokens[i] = "key ==='" + this.tokens[i].trim() +"'";
this.tokens[i] = this.tokens[i].trim();
}
}
}
}
module.exports = Parser;