@cucumber/tag-expressions 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ PLEASE DO NOT CREATE ISSUES IN THIS REPO.
2
+ THIS REPO IS A READ-ONLY MIRROR.
3
+
4
+ Create your issue in the Cucumber monorepo instead:
5
+ https://github.com/cucumber/cucumber/issues
@@ -0,0 +1,5 @@
1
+ PLEASE DO NOT CREATE PULL REAUESTS IN THIS REPO.
2
+ THIS REPO IS A READ-ONLY MIRROR.
3
+
4
+ Create your pull request in the Cucumber monorepo instead:
5
+ https://github.com/cucumber/cucumber/pulls
package/.nycrc.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extension": [
3
+ ".ts",
4
+ ".tsx"
5
+ ],
6
+ "exclude": [
7
+ "coverage",
8
+ "dist",
9
+ "src/**/*.d.ts"
10
+ ],
11
+ "reporter": [
12
+ "html",
13
+ "text"
14
+ ],
15
+ "all": true
16
+ }
package/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "trailingComma": "es5",
3
+ "semi": false,
4
+ "singleQuote": true
5
+ }
package/.rsync ADDED
@@ -0,0 +1,3 @@
1
+ ../../LICENSE LICENSE
2
+ ../../.templates/github/ .github/
3
+ ../../.templates/javascript/ .
package/.subrepo ADDED
@@ -0,0 +1 @@
1
+ cucumber/tag-expressions-javascript
@@ -0,0 +1,8 @@
1
+ ## Code coverage
2
+
3
+ Just run the tests:
4
+
5
+ npm test
6
+
7
+ The build will fail if coverage drops below 100%.
8
+ The report is in `coverage/lcov-report/index.html`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Cucumber Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Makefile ADDED
@@ -0,0 +1 @@
1
+ include default.mk
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Cucumber Tag Expressions for JavaScript
2
+
3
+ [![Greenkeeper badge](https://badges.greenkeeper.io/cucumber/tag-expressions-javascript.svg)](https://greenkeeper.io/)
4
+
5
+ [![Build Status](https://travis-ci.org/cucumber/tag-expressions-javascript.svg?branch=master)](https://travis-ci.org/cucumber/tag-expressions-javascript)
6
+
7
+ [The docs are here](https://cucumber.io/docs/cucumber/api/#tag-expressions).
8
+
9
+ ## Example
10
+
11
+ ```js
12
+ import {TagExpressionParser} from '@cucumber/tag-expressions'
13
+ const parser = new TagExpressionParser()
14
+
15
+ const expressionNode = parser.parse('@tagA and @tagB')
16
+
17
+ expressionNode.evaluate(["@tagA", "@tagB"]) // => true
18
+ expressionNode.evaluate(["@tagA", "@tagC"]) // => false
19
+ ```
package/default.mk ADDED
@@ -0,0 +1,69 @@
1
+ SHELL := /usr/bin/env bash
2
+ TYPESCRIPT_SOURCE_FILES = $(shell find src test -type f -name "*.ts" -o -name "*.tsx")
3
+ PRIVATE = $(shell node -e "console.log(require('./package.json').private)")
4
+
5
+ default: .linted .tested .built
6
+ .PHONY: default
7
+
8
+ .deps: package-lock.json
9
+ touch $@
10
+
11
+ .codegen: .deps
12
+ touch $@
13
+
14
+ .built: .codegen $(TYPESCRIPT_SOURCE_FILES)
15
+ npm run build
16
+ touch $@
17
+
18
+ .tested: .built $(TYPESCRIPT_SOURCE_FILES)
19
+ TS_NODE_TRANSPILE_ONLY=1 npm run test
20
+ touch $@
21
+
22
+ .linted: $(TYPESCRIPT_SOURCE_FILES) package-lock.json
23
+ npm run lint-fix
24
+ touch $@
25
+
26
+ package-lock.json: package.json
27
+ npm install
28
+ touch $@
29
+
30
+ update-dependencies:
31
+ npx npm-check-updates --upgrade
32
+ .PHONY: update-dependencies
33
+
34
+ remove-local-dependencies:
35
+ cat package.json | sed 's/"@cucumber\/\(.*\)": "file:..\/..\/.*"/"@cucumber\/\1": "0.0.0"/' > package.json.tmp
36
+ mv package.json.tmp package.json
37
+ .PHONY: remove-local-dependencies
38
+
39
+ pre-release: remove-local-dependencies update-version update-dependencies clean default
40
+ .PHONY: pre-release
41
+
42
+ update-version:
43
+ ifdef NEW_VERSION
44
+ npm --no-git-tag-version --allow-same-version version "$(NEW_VERSION)"
45
+ else
46
+ @echo -e "\033[0;31mNEW_VERSION is not defined. Can't update version :-(\033[0m"
47
+ exit 1
48
+ endif
49
+ .PHONY: update-version
50
+
51
+ publish: .codegen
52
+ ifneq (true,$(PRIVATE))
53
+ npm publish --access public
54
+ else
55
+ @echo "Not publishing private npm module"
56
+ endif
57
+ .PHONY: publish
58
+
59
+ post-release:
60
+ cat package.json | sed 's/"@cucumber\/\(.*\)": .*"/"@cucumber\/\1": "file:..\/..\/\1\/javascript"/' > package.json.tmp
61
+ mv package.json.tmp package.json
62
+ .PHONY: post-release
63
+
64
+ clean: clean-javascript
65
+ .PHONY: clean
66
+
67
+ clean-javascript:
68
+ rm -rf .deps .codegen .built .tested* .linted package-lock.json node_modules coverage dist acceptance
69
+ .PHONY: clean-javascript
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Parses infix boolean expression (using Dijkstra's Shunting Yard algorithm)
3
+ * and builds a tree of expressions. The root node of the expression is returned.
4
+ *
5
+ * This expression can be evaluated by passing in an array of literals that resolve to true
6
+ */
7
+ export default function parse(infix: string): Node;
8
+ interface Node {
9
+ evaluate(variables: string[]): boolean;
10
+ }
11
+ export {};
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var OPERAND = 'operand';
4
+ var OPERATOR = 'operator';
5
+ var PREC = {
6
+ '(': -2,
7
+ ')': -1,
8
+ or: 0,
9
+ and: 1,
10
+ not: 2,
11
+ };
12
+ var ASSOC = {
13
+ or: 'left',
14
+ and: 'left',
15
+ not: 'right',
16
+ };
17
+ /**
18
+ * Parses infix boolean expression (using Dijkstra's Shunting Yard algorithm)
19
+ * and builds a tree of expressions. The root node of the expression is returned.
20
+ *
21
+ * This expression can be evaluated by passing in an array of literals that resolve to true
22
+ */
23
+ function parse(infix) {
24
+ var tokens = tokenize(infix);
25
+ if (tokens.length === 0) {
26
+ return new True();
27
+ }
28
+ var expressions = [];
29
+ var operators = [];
30
+ var expectedTokenType = OPERAND;
31
+ tokens.forEach(function (token) {
32
+ if (isUnary(token)) {
33
+ check(expectedTokenType, OPERAND);
34
+ operators.push(token);
35
+ expectedTokenType = OPERAND;
36
+ }
37
+ else if (isBinary(token)) {
38
+ check(expectedTokenType, OPERATOR);
39
+ while (operators.length > 0 &&
40
+ isOp(peek(operators)) &&
41
+ ((ASSOC[token] === 'left' && PREC[token] <= PREC[peek(operators)]) ||
42
+ (ASSOC[token] === 'right' && PREC[token] < PREC[peek(operators)]))) {
43
+ pushExpr(pop(operators), expressions);
44
+ }
45
+ operators.push(token);
46
+ expectedTokenType = OPERAND;
47
+ }
48
+ else if ('(' === token) {
49
+ check(expectedTokenType, OPERAND);
50
+ operators.push(token);
51
+ expectedTokenType = OPERAND;
52
+ }
53
+ else if (')' === token) {
54
+ check(expectedTokenType, OPERATOR);
55
+ while (operators.length > 0 && peek(operators) !== '(') {
56
+ pushExpr(pop(operators), expressions);
57
+ }
58
+ if (operators.length === 0) {
59
+ throw Error('Syntax error. Unmatched )');
60
+ }
61
+ if (peek(operators) === '(') {
62
+ pop(operators);
63
+ }
64
+ expectedTokenType = OPERATOR;
65
+ }
66
+ else {
67
+ check(expectedTokenType, OPERAND);
68
+ pushExpr(token, expressions);
69
+ expectedTokenType = OPERATOR;
70
+ }
71
+ });
72
+ while (operators.length > 0) {
73
+ if (peek(operators) === '(') {
74
+ throw Error('Syntax error. Unmatched (');
75
+ }
76
+ pushExpr(pop(operators), expressions);
77
+ }
78
+ return pop(expressions);
79
+ }
80
+ exports.default = parse;
81
+ function tokenize(expr) {
82
+ var tokens = [];
83
+ var isEscaped = false;
84
+ var token;
85
+ for (var i = 0; i < expr.length; i++) {
86
+ var c = expr.charAt(i);
87
+ if ('\\' === c) {
88
+ isEscaped = true;
89
+ }
90
+ else {
91
+ if (/\s/.test(c)) {
92
+ // skip
93
+ if (token) {
94
+ // end of token
95
+ tokens.push(token.join(''));
96
+ token = undefined;
97
+ }
98
+ }
99
+ else {
100
+ if ((c === '(' || c === ')') && !isEscaped) {
101
+ if (token) {
102
+ // end of token
103
+ tokens.push(token.join(''));
104
+ token = undefined;
105
+ }
106
+ tokens.push(c);
107
+ continue;
108
+ }
109
+ token = token ? token : []; // start of token
110
+ token.push(c);
111
+ }
112
+ isEscaped = false;
113
+ }
114
+ }
115
+ if (token) {
116
+ tokens.push(token.join(''));
117
+ }
118
+ return tokens;
119
+ }
120
+ function isUnary(token) {
121
+ return 'not' === token;
122
+ }
123
+ function isBinary(token) {
124
+ return 'or' === token || 'and' === token;
125
+ }
126
+ function isOp(token) {
127
+ return ASSOC[token] !== undefined;
128
+ }
129
+ function check(expectedTokenType, tokenType) {
130
+ if (expectedTokenType !== tokenType) {
131
+ throw new Error('Syntax error. Expected ' + expectedTokenType);
132
+ }
133
+ }
134
+ function peek(stack) {
135
+ return stack[stack.length - 1];
136
+ }
137
+ function pop(stack) {
138
+ if (stack.length === 0) {
139
+ throw new Error('empty stack');
140
+ }
141
+ return stack.pop();
142
+ }
143
+ function pushExpr(token, stack) {
144
+ if (token === 'and') {
145
+ var rightAndExpr = pop(stack);
146
+ stack.push(new And(pop(stack), rightAndExpr));
147
+ }
148
+ else if (token === 'or') {
149
+ var rightOrExpr = pop(stack);
150
+ stack.push(new Or(pop(stack), rightOrExpr));
151
+ }
152
+ else if (token === 'not') {
153
+ stack.push(new Not(pop(stack)));
154
+ }
155
+ else {
156
+ stack.push(new Literal(token));
157
+ }
158
+ }
159
+ var Literal = /** @class */ (function () {
160
+ function Literal(value) {
161
+ this.value = value;
162
+ }
163
+ Literal.prototype.evaluate = function (variables) {
164
+ return variables.indexOf(this.value) !== -1;
165
+ };
166
+ Literal.prototype.toString = function () {
167
+ return this.value.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
168
+ };
169
+ return Literal;
170
+ }());
171
+ var Or = /** @class */ (function () {
172
+ function Or(leftExpr, rightExpr) {
173
+ this.leftExpr = leftExpr;
174
+ this.rightExpr = rightExpr;
175
+ }
176
+ Or.prototype.evaluate = function (variables) {
177
+ return (this.leftExpr.evaluate(variables) || this.rightExpr.evaluate(variables));
178
+ };
179
+ Or.prototype.toString = function () {
180
+ return ('( ' +
181
+ this.leftExpr.toString() +
182
+ ' or ' +
183
+ this.rightExpr.toString() +
184
+ ' )');
185
+ };
186
+ return Or;
187
+ }());
188
+ var And = /** @class */ (function () {
189
+ function And(leftExpr, rightExpr) {
190
+ this.leftExpr = leftExpr;
191
+ this.rightExpr = rightExpr;
192
+ }
193
+ And.prototype.evaluate = function (variables) {
194
+ return (this.leftExpr.evaluate(variables) && this.rightExpr.evaluate(variables));
195
+ };
196
+ And.prototype.toString = function () {
197
+ return ('( ' +
198
+ this.leftExpr.toString() +
199
+ ' and ' +
200
+ this.rightExpr.toString() +
201
+ ' )');
202
+ };
203
+ return And;
204
+ }());
205
+ var Not = /** @class */ (function () {
206
+ function Not(expr) {
207
+ this.expr = expr;
208
+ }
209
+ Not.prototype.evaluate = function (variables) {
210
+ return !this.expr.evaluate(variables);
211
+ };
212
+ Not.prototype.toString = function () {
213
+ return 'not ( ' + this.expr.toString() + ' )';
214
+ };
215
+ return Not;
216
+ }());
217
+ var True = /** @class */ (function () {
218
+ function True() {
219
+ }
220
+ True.prototype.evaluate = function (variables) {
221
+ return true;
222
+ };
223
+ True.prototype.toString = function () {
224
+ return 'true';
225
+ };
226
+ return True;
227
+ }());
228
+ //# sourceMappingURL=tag_expression_parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag_expression_parser.js","sourceRoot":"","sources":["../../src/tag_expression_parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ var assert_1 = __importDefault(require("assert"));
7
+ var tag_expression_parser_1 = __importDefault(require("../src/tag_expression_parser"));
8
+ describe('TagExpressionParser', function () {
9
+ describe('#parse', function () {
10
+ ;
11
+ [
12
+ ['', 'true'],
13
+ ['a and b', '( a and b )'],
14
+ ['a or b', '( a or b )'],
15
+ ['not a', 'not ( a )'],
16
+ ['( a and b ) or ( c and d )', '( ( a and b ) or ( c and d ) )'],
17
+ [
18
+ 'not a or b and not c or not d or e and f',
19
+ '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )',
20
+ ],
21
+ [
22
+ 'not a\\(\\) or b and not c or not d or e and f',
23
+ '( ( ( not ( a\\(\\) ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )',
24
+ ],
25
+ ].forEach(function (inOut) {
26
+ it(inOut[0], function () {
27
+ var infix = inOut[0];
28
+ var expr = tag_expression_parser_1.default(infix);
29
+ assert_1.default.strictEqual(expr.toString(), inOut[1]);
30
+ var roundtripTokens = expr.toString();
31
+ var roundtripExpr = tag_expression_parser_1.default(roundtripTokens);
32
+ assert_1.default.strictEqual(roundtripExpr.toString(), inOut[1]);
33
+ });
34
+ });
35
+ [
36
+ ['@a @b or', 'Syntax error. Expected operator'],
37
+ ['@a and (@b not)', 'Syntax error. Expected operator'],
38
+ ['@a and (@b @c) or', 'Syntax error. Expected operator'],
39
+ ['@a and or', 'Syntax error. Expected operand'],
40
+ ['or or', 'Syntax error. Expected operand'],
41
+ ['a b', 'Syntax error. Expected operator'],
42
+ ['( a and b ) )', 'Syntax error. Unmatched )'],
43
+ ['( ( a and b )', 'Syntax error. Unmatched ('],
44
+ ].forEach(function (inOut) {
45
+ it(inOut[0] + ' fails', function () {
46
+ var infix = inOut[0];
47
+ try {
48
+ tag_expression_parser_1.default(infix);
49
+ throw new Error('Expected syntax error');
50
+ }
51
+ catch (expected) {
52
+ assert_1.default.strictEqual(expected.message, inOut[1]);
53
+ }
54
+ });
55
+ });
56
+ // evaluation
57
+ it('evaluates not', function () {
58
+ var expr = tag_expression_parser_1.default('not x');
59
+ assert_1.default.strictEqual(expr.evaluate(['x']), false);
60
+ assert_1.default.strictEqual(expr.evaluate(['y']), true);
61
+ });
62
+ it('evaluates and', function () {
63
+ var expr = tag_expression_parser_1.default('x and y');
64
+ assert_1.default.strictEqual(expr.evaluate(['x', 'y']), true);
65
+ assert_1.default.strictEqual(expr.evaluate(['y']), false);
66
+ assert_1.default.strictEqual(expr.evaluate(['x']), false);
67
+ });
68
+ it('evaluates or', function () {
69
+ var expr = tag_expression_parser_1.default(' x or(y) ');
70
+ assert_1.default.strictEqual(expr.evaluate([]), false);
71
+ assert_1.default.strictEqual(expr.evaluate(['y']), true);
72
+ assert_1.default.strictEqual(expr.evaluate(['x']), true);
73
+ });
74
+ it('evaluates expressions with escaped chars', function () {
75
+ var expr = tag_expression_parser_1.default(' x\\(1\\) or(y\\(2\\)) ');
76
+ assert_1.default.strictEqual(expr.evaluate([]), false);
77
+ assert_1.default.strictEqual(expr.evaluate(['y(2)']), true);
78
+ assert_1.default.strictEqual(expr.evaluate(['x(1)']), true);
79
+ assert_1.default.strictEqual(expr.evaluate(['y']), false);
80
+ assert_1.default.strictEqual(expr.evaluate(['x']), false);
81
+ });
82
+ it('evaluates empty expressions to true', function () {
83
+ var expr = tag_expression_parser_1.default('');
84
+ assert_1.default.strictEqual(expr.evaluate([]), true);
85
+ assert_1.default.strictEqual(expr.evaluate(['y']), true);
86
+ assert_1.default.strictEqual(expr.evaluate(['x']), true);
87
+ });
88
+ });
89
+ });
90
+ //# sourceMappingURL=tag_expression_parser_test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag_expression_parser_test.js","sourceRoot":"","sources":["../../test/tag_expression_parser_test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@cucumber/tag-expressions",
3
+ "version": "2.0.4",
4
+ "description": "Cucumber Tag Expression parser",
5
+ "main": "dist/src/tag_expression_parser.js",
6
+ "types": "dist/src/tag_expression_parser.d.ts",
7
+ "scripts": {
8
+ "test": "mocha",
9
+ "lint": "tslint src/**/*.ts test/**/*.ts",
10
+ "lint-fix": "tslint --fix src/**/*.ts test/**/*.ts",
11
+ "coverage": "nyc --reporter=html --reporter=text mocha",
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git://github.com/cucumber/tag-expressions-javascript.git"
18
+ },
19
+ "keywords": [
20
+ "cucumber"
21
+ ],
22
+ "author": "Cucumber Limited <cukes@googlegroups.com>",
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/cucumber/tag-expressions-javascript/issues"
26
+ },
27
+ "homepage": "https://github.com/cucumber/tag-expressions-javascript",
28
+ "devDependencies": {
29
+ "@types/mocha": "^5.2.7",
30
+ "@types/node": "^13.1.6",
31
+ "mocha": "^7.0.0",
32
+ "nyc": "^15.0.0",
33
+ "prettier": "^1.19.1",
34
+ "ts-node": "^8.6.0",
35
+ "tslint": "^5.20.1",
36
+ "tslint-config-prettier": "^1.18.0",
37
+ "tslint-plugin-prettier": "^2.1.0",
38
+ "typescript": "^3.7.4"
39
+ },
40
+ "directories": {
41
+ "test": "test"
42
+ },
43
+ "dependencies": {}
44
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Links an npm module
4
+ #
5
+ set -euf -o pipefail
6
+
7
+ # `npm link` does not work on CircleCI, so we're doing manual symlinking instead
8
+ rm -rf "node_modules/$1"
9
+ # Some modules have no dependencies in package.json, so we'll create it just in case
10
+ mkdir -p "node_modules"
11
+ ln -s "$(pwd)/../../$1/javascript" node_modules/$1
@@ -0,0 +1,244 @@
1
+ const OPERAND = 'operand'
2
+ const OPERATOR = 'operator'
3
+ const PREC: { [key: string]: number } = {
4
+ '(': -2,
5
+ ')': -1,
6
+ or: 0,
7
+ and: 1,
8
+ not: 2,
9
+ }
10
+ const ASSOC: { [key: string]: string } = {
11
+ or: 'left',
12
+ and: 'left',
13
+ not: 'right',
14
+ }
15
+
16
+ /**
17
+ * Parses infix boolean expression (using Dijkstra's Shunting Yard algorithm)
18
+ * and builds a tree of expressions. The root node of the expression is returned.
19
+ *
20
+ * This expression can be evaluated by passing in an array of literals that resolve to true
21
+ */
22
+ export default function parse(infix: string): Node {
23
+ const tokens = tokenize(infix)
24
+ if (tokens.length === 0) {
25
+ return new True()
26
+ }
27
+ const expressions: Node[] = []
28
+ const operators: string[] = []
29
+ let expectedTokenType = OPERAND
30
+
31
+ tokens.forEach(function(token) {
32
+ if (isUnary(token)) {
33
+ check(expectedTokenType, OPERAND)
34
+ operators.push(token)
35
+ expectedTokenType = OPERAND
36
+ } else if (isBinary(token)) {
37
+ check(expectedTokenType, OPERATOR)
38
+ while (
39
+ operators.length > 0 &&
40
+ isOp(peek(operators)) &&
41
+ ((ASSOC[token] === 'left' && PREC[token] <= PREC[peek(operators)]) ||
42
+ (ASSOC[token] === 'right' && PREC[token] < PREC[peek(operators)]))
43
+ ) {
44
+ pushExpr(pop(operators), expressions)
45
+ }
46
+ operators.push(token)
47
+ expectedTokenType = OPERAND
48
+ } else if ('(' === token) {
49
+ check(expectedTokenType, OPERAND)
50
+ operators.push(token)
51
+ expectedTokenType = OPERAND
52
+ } else if (')' === token) {
53
+ check(expectedTokenType, OPERATOR)
54
+ while (operators.length > 0 && peek(operators) !== '(') {
55
+ pushExpr(pop(operators), expressions)
56
+ }
57
+ if (operators.length === 0) {
58
+ throw Error('Syntax error. Unmatched )')
59
+ }
60
+ if (peek(operators) === '(') {
61
+ pop(operators)
62
+ }
63
+ expectedTokenType = OPERATOR
64
+ } else {
65
+ check(expectedTokenType, OPERAND)
66
+ pushExpr(token, expressions)
67
+ expectedTokenType = OPERATOR
68
+ }
69
+ })
70
+
71
+ while (operators.length > 0) {
72
+ if (peek(operators) === '(') {
73
+ throw Error('Syntax error. Unmatched (')
74
+ }
75
+ pushExpr(pop(operators), expressions)
76
+ }
77
+
78
+ return pop(expressions)
79
+ }
80
+
81
+ function tokenize(expr: string): string[] {
82
+ const tokens = []
83
+ let isEscaped = false
84
+ let token
85
+ for (let i = 0; i < expr.length; i++) {
86
+ const c = expr.charAt(i)
87
+ if ('\\' === c) {
88
+ isEscaped = true
89
+ } else {
90
+ if (/\s/.test(c)) {
91
+ // skip
92
+ if (token) {
93
+ // end of token
94
+ tokens.push(token.join(''))
95
+ token = undefined
96
+ }
97
+ } else {
98
+ if ((c === '(' || c === ')') && !isEscaped) {
99
+ if (token) {
100
+ // end of token
101
+ tokens.push(token.join(''))
102
+ token = undefined
103
+ }
104
+ tokens.push(c)
105
+ continue
106
+ }
107
+ token = token ? token : [] // start of token
108
+ token.push(c)
109
+ }
110
+ isEscaped = false
111
+ }
112
+ }
113
+ if (token) {
114
+ tokens.push(token.join(''))
115
+ }
116
+ return tokens
117
+ }
118
+
119
+ function isUnary(token: string) {
120
+ return 'not' === token
121
+ }
122
+
123
+ function isBinary(token: string) {
124
+ return 'or' === token || 'and' === token
125
+ }
126
+
127
+ function isOp(token: string) {
128
+ return ASSOC[token] !== undefined
129
+ }
130
+
131
+ function check(expectedTokenType: string, tokenType: string) {
132
+ if (expectedTokenType !== tokenType) {
133
+ throw new Error('Syntax error. Expected ' + expectedTokenType)
134
+ }
135
+ }
136
+
137
+ function peek(stack: string[]) {
138
+ return stack[stack.length - 1]
139
+ }
140
+
141
+ function pop<T>(stack: T[]): T {
142
+ if (stack.length === 0) {
143
+ throw new Error('empty stack')
144
+ }
145
+ return stack.pop()
146
+ }
147
+
148
+ function pushExpr(token: string, stack: Node[]) {
149
+ if (token === 'and') {
150
+ const rightAndExpr = pop(stack)
151
+ stack.push(new And(pop(stack), rightAndExpr))
152
+ } else if (token === 'or') {
153
+ const rightOrExpr = pop(stack)
154
+ stack.push(new Or(pop(stack), rightOrExpr))
155
+ } else if (token === 'not') {
156
+ stack.push(new Not(pop(stack)))
157
+ } else {
158
+ stack.push(new Literal(token))
159
+ }
160
+ }
161
+
162
+ interface Node {
163
+ evaluate(variables: string[]): boolean
164
+ }
165
+
166
+ class Literal implements Node {
167
+ constructor(private readonly value: string) {}
168
+
169
+ public evaluate(variables: string[]) {
170
+ return variables.indexOf(this.value) !== -1
171
+ }
172
+
173
+ public toString() {
174
+ return this.value.replace(/\(/g, '\\(').replace(/\)/g, '\\)')
175
+ }
176
+ }
177
+
178
+ class Or implements Node {
179
+ constructor(
180
+ private readonly leftExpr: Node,
181
+ private readonly rightExpr: Node
182
+ ) {}
183
+
184
+ public evaluate(variables: string[]) {
185
+ return (
186
+ this.leftExpr.evaluate(variables) || this.rightExpr.evaluate(variables)
187
+ )
188
+ }
189
+
190
+ public toString() {
191
+ return (
192
+ '( ' +
193
+ this.leftExpr.toString() +
194
+ ' or ' +
195
+ this.rightExpr.toString() +
196
+ ' )'
197
+ )
198
+ }
199
+ }
200
+
201
+ class And implements Node {
202
+ constructor(
203
+ private readonly leftExpr: Node,
204
+ private readonly rightExpr: Node
205
+ ) {}
206
+
207
+ public evaluate(variables: string[]) {
208
+ return (
209
+ this.leftExpr.evaluate(variables) && this.rightExpr.evaluate(variables)
210
+ )
211
+ }
212
+
213
+ public toString() {
214
+ return (
215
+ '( ' +
216
+ this.leftExpr.toString() +
217
+ ' and ' +
218
+ this.rightExpr.toString() +
219
+ ' )'
220
+ )
221
+ }
222
+ }
223
+
224
+ class Not implements Node {
225
+ constructor(private readonly expr: Node) {}
226
+
227
+ public evaluate(variables: string[]) {
228
+ return !this.expr.evaluate(variables)
229
+ }
230
+
231
+ public toString() {
232
+ return 'not ( ' + this.expr.toString() + ' )'
233
+ }
234
+ }
235
+
236
+ class True implements Node {
237
+ public evaluate(variables: string[]) {
238
+ return true
239
+ }
240
+
241
+ public toString() {
242
+ return 'true'
243
+ }
244
+ }
@@ -0,0 +1,5 @@
1
+ --require ts-node/register
2
+ --require source-map-support/register
3
+ --extension ts,tsx
4
+ --recursive
5
+ --timeout 60000
@@ -0,0 +1,92 @@
1
+ import assert from 'assert'
2
+ import parse from '../src/tag_expression_parser'
3
+
4
+ describe('TagExpressionParser', () => {
5
+ describe('#parse', () => {
6
+ ;[
7
+ ['', 'true'],
8
+ ['a and b', '( a and b )'],
9
+ ['a or b', '( a or b )'],
10
+ ['not a', 'not ( a )'],
11
+ ['( a and b ) or ( c and d )', '( ( a and b ) or ( c and d ) )'],
12
+ [
13
+ 'not a or b and not c or not d or e and f',
14
+ '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )',
15
+ ],
16
+ [
17
+ 'not a\\(\\) or b and not c or not d or e and f',
18
+ '( ( ( not ( a\\(\\) ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )',
19
+ ],
20
+ // a or not b
21
+ ].forEach(function(inOut) {
22
+ it(inOut[0], function() {
23
+ const infix = inOut[0]
24
+ const expr = parse(infix)
25
+ assert.strictEqual(expr.toString(), inOut[1])
26
+
27
+ const roundtripTokens = expr.toString()
28
+ const roundtripExpr = parse(roundtripTokens)
29
+ assert.strictEqual(roundtripExpr.toString(), inOut[1])
30
+ })
31
+ })
32
+ ;[
33
+ ['@a @b or', 'Syntax error. Expected operator'],
34
+ ['@a and (@b not)', 'Syntax error. Expected operator'],
35
+ ['@a and (@b @c) or', 'Syntax error. Expected operator'],
36
+ ['@a and or', 'Syntax error. Expected operand'],
37
+ ['or or', 'Syntax error. Expected operand'],
38
+ ['a b', 'Syntax error. Expected operator'],
39
+ ['( a and b ) )', 'Syntax error. Unmatched )'],
40
+ ['( ( a and b )', 'Syntax error. Unmatched ('],
41
+ // a or not b
42
+ ].forEach(function(inOut) {
43
+ it(inOut[0] + ' fails', function() {
44
+ const infix = inOut[0]
45
+ try {
46
+ parse(infix)
47
+ throw new Error('Expected syntax error')
48
+ } catch (expected) {
49
+ assert.strictEqual(expected.message, inOut[1])
50
+ }
51
+ })
52
+ })
53
+
54
+ // evaluation
55
+
56
+ it('evaluates not', function() {
57
+ const expr = parse('not x')
58
+ assert.strictEqual(expr.evaluate(['x']), false)
59
+ assert.strictEqual(expr.evaluate(['y']), true)
60
+ })
61
+
62
+ it('evaluates and', function() {
63
+ const expr = parse('x and y')
64
+ assert.strictEqual(expr.evaluate(['x', 'y']), true)
65
+ assert.strictEqual(expr.evaluate(['y']), false)
66
+ assert.strictEqual(expr.evaluate(['x']), false)
67
+ })
68
+
69
+ it('evaluates or', function() {
70
+ const expr = parse(' x or(y) ')
71
+ assert.strictEqual(expr.evaluate([]), false)
72
+ assert.strictEqual(expr.evaluate(['y']), true)
73
+ assert.strictEqual(expr.evaluate(['x']), true)
74
+ })
75
+
76
+ it('evaluates expressions with escaped chars', function() {
77
+ const expr = parse(' x\\(1\\) or(y\\(2\\)) ')
78
+ assert.strictEqual(expr.evaluate([]), false)
79
+ assert.strictEqual(expr.evaluate(['y(2)']), true)
80
+ assert.strictEqual(expr.evaluate(['x(1)']), true)
81
+ assert.strictEqual(expr.evaluate(['y']), false)
82
+ assert.strictEqual(expr.evaluate(['x']), false)
83
+ })
84
+
85
+ it('evaluates empty expressions to true', function() {
86
+ const expr = parse('')
87
+ assert.strictEqual(expr.evaluate([]), true)
88
+ assert.strictEqual(expr.evaluate(['y']), true)
89
+ assert.strictEqual(expr.evaluate(['x']), true)
90
+ })
91
+ })
92
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "declaration": true,
4
+ "target": "es5",
5
+ "lib": [
6
+ "es5",
7
+ "es6",
8
+ "es7",
9
+ "es2015",
10
+ "es2017",
11
+ "dom"
12
+ ],
13
+ "sourceMap": true,
14
+ "allowJs": false,
15
+ "jsx": "react",
16
+ "resolveJsonModule": true,
17
+ "module": "commonjs",
18
+ "esModuleInterop": true,
19
+ "noImplicitAny": true,
20
+ "moduleResolution": "node",
21
+ "outDir": "dist",
22
+ "downlevelIteration": true,
23
+ "skipLibCheck": true
24
+ },
25
+ "include": [
26
+ "src/**/*",
27
+ "test/**/*"
28
+ ]
29
+ }
package/tslint.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "defaultSeverity": "error",
3
+ "extends": [
4
+ "tslint:latest",
5
+ "tslint-plugin-prettier",
6
+ "tslint-config-prettier"
7
+ ],
8
+ "jsRules": {},
9
+ "rules": {
10
+ "object-literal-sort-keys": false,
11
+ "ordered-imports": false,
12
+ "no-submodule-imports": false,
13
+ "member-ordering": false,
14
+ "max-classes-per-file": false,
15
+ "interface-name": false,
16
+ "no-empty-interface": false,
17
+ "no-namespace": false,
18
+ "no-implicit-dependencies": [true, "dev"],
19
+ "only-arrow-functions": false,
20
+ "prettier": true
21
+ },
22
+ "rulesDirectory": []
23
+ }