UNPKG

cucumber-tag-expressions

Version:
228 lines 6.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var OPERAND = 'operand'; var OPERATOR = 'operator'; var PREC = { '(': -2, ')': -1, or: 0, and: 1, not: 2, }; var ASSOC = { or: 'left', and: 'left', not: 'right', }; /** * Parses infix boolean expression (using Dijkstra's Shunting Yard algorithm) * and builds a tree of expressions. The root node of the expression is returned. * * This expression can be evaluated by passing in an array of literals that resolve to true */ function parse(infix) { var tokens = tokenize(infix); if (tokens.length === 0) { return new True(); } var expressions = []; var operators = []; var expectedTokenType = OPERAND; tokens.forEach(function (token) { if (isUnary(token)) { check(expectedTokenType, OPERAND); operators.push(token); expectedTokenType = OPERAND; } else if (isBinary(token)) { check(expectedTokenType, OPERATOR); while (operators.length > 0 && isOp(peek(operators)) && ((ASSOC[token] === 'left' && PREC[token] <= PREC[peek(operators)]) || (ASSOC[token] === 'right' && PREC[token] < PREC[peek(operators)]))) { pushExpr(pop(operators), expressions); } operators.push(token); expectedTokenType = OPERAND; } else if ('(' === token) { check(expectedTokenType, OPERAND); operators.push(token); expectedTokenType = OPERAND; } else if (')' === token) { check(expectedTokenType, OPERATOR); while (operators.length > 0 && peek(operators) !== '(') { pushExpr(pop(operators), expressions); } if (operators.length === 0) { throw Error('Syntax error. Unmatched )'); } if (peek(operators) === '(') { pop(operators); } expectedTokenType = OPERATOR; } else { check(expectedTokenType, OPERAND); pushExpr(token, expressions); expectedTokenType = OPERATOR; } }); while (operators.length > 0) { if (peek(operators) === '(') { throw Error('Syntax error. Unmatched ('); } pushExpr(pop(operators), expressions); } return pop(expressions); } exports.default = parse; function tokenize(expr) { var tokens = []; var isEscaped = false; var token; for (var i = 0; i < expr.length; i++) { var c = expr.charAt(i); if ('\\' === c) { isEscaped = true; } else { if (/\s/.test(c)) { // skip if (token) { // end of token tokens.push(token.join('')); token = undefined; } } else { if ((c === '(' || c === ')') && !isEscaped) { if (token) { // end of token tokens.push(token.join('')); token = undefined; } tokens.push(c); continue; } token = token ? token : []; // start of token token.push(c); } isEscaped = false; } } if (token) { tokens.push(token.join('')); } return tokens; } function isUnary(token) { return 'not' === token; } function isBinary(token) { return 'or' === token || 'and' === token; } function isOp(token) { return ASSOC[token] !== undefined; } function check(expectedTokenType, tokenType) { if (expectedTokenType !== tokenType) { throw new Error('Syntax error. Expected ' + expectedTokenType); } } function peek(stack) { return stack[stack.length - 1]; } function pop(stack) { if (stack.length === 0) { throw new Error('empty stack'); } return stack.pop(); } function pushExpr(token, stack) { if (token === 'and') { var rightAndExpr = pop(stack); stack.push(new And(pop(stack), rightAndExpr)); } else if (token === 'or') { var rightOrExpr = pop(stack); stack.push(new Or(pop(stack), rightOrExpr)); } else if (token === 'not') { stack.push(new Not(pop(stack))); } else { stack.push(new Literal(token)); } } var Literal = /** @class */ (function () { function Literal(value) { this.value = value; } Literal.prototype.evaluate = function (variables) { return variables.indexOf(this.value) !== -1; }; Literal.prototype.toString = function () { return this.value.replace(/\(/g, '\\(').replace(/\)/g, '\\)'); }; return Literal; }()); var Or = /** @class */ (function () { function Or(leftExpr, rightExpr) { this.leftExpr = leftExpr; this.rightExpr = rightExpr; } Or.prototype.evaluate = function (variables) { return (this.leftExpr.evaluate(variables) || this.rightExpr.evaluate(variables)); }; Or.prototype.toString = function () { return ('( ' + this.leftExpr.toString() + ' or ' + this.rightExpr.toString() + ' )'); }; return Or; }()); var And = /** @class */ (function () { function And(leftExpr, rightExpr) { this.leftExpr = leftExpr; this.rightExpr = rightExpr; } And.prototype.evaluate = function (variables) { return (this.leftExpr.evaluate(variables) && this.rightExpr.evaluate(variables)); }; And.prototype.toString = function () { return ('( ' + this.leftExpr.toString() + ' and ' + this.rightExpr.toString() + ' )'); }; return And; }()); var Not = /** @class */ (function () { function Not(expr) { this.expr = expr; } Not.prototype.evaluate = function (variables) { return !this.expr.evaluate(variables); }; Not.prototype.toString = function () { return 'not ( ' + this.expr.toString() + ' )'; }; return Not; }()); var True = /** @class */ (function () { function True() { } True.prototype.evaluate = function (variables) { return true; }; True.prototype.toString = function () { return 'true'; }; return True; }()); //# sourceMappingURL=tag_expression_parser.js.map