@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.
- package/.github/ISSUE_TEMPLATE.md +5 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- package/.nycrc.json +16 -0
- package/.prettierrc +5 -0
- package/.rsync +3 -0
- package/.subrepo +1 -0
- package/CONTRIBUTING.md +8 -0
- package/LICENSE +21 -0
- package/Makefile +1 -0
- package/README.md +19 -0
- package/default.mk +69 -0
- package/dist/src/tag_expression_parser.d.ts +11 -0
- package/dist/src/tag_expression_parser.js +228 -0
- package/dist/src/tag_expression_parser.js.map +1 -0
- package/dist/test/tag_expression_parser_test.d.ts +1 -0
- package/dist/test/tag_expression_parser_test.js +90 -0
- package/dist/test/tag_expression_parser_test.js.map +1 -0
- package/package.json +44 -0
- package/scripts/npm-link +11 -0
- package/src/tag_expression_parser.ts +244 -0
- package/test/mocha.opts +5 -0
- package/test/tag_expression_parser_test.ts +92 -0
- package/tsconfig.json +29 -0
- package/tslint.json +23 -0
package/.nycrc.json
ADDED
package/.prettierrc
ADDED
package/.rsync
ADDED
package/.subrepo
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cucumber/tag-expressions-javascript
|
package/CONTRIBUTING.md
ADDED
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
|
+
[](https://greenkeeper.io/)
|
|
4
|
+
|
|
5
|
+
[](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
|
+
}
|
package/scripts/npm-link
ADDED
|
@@ -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
|
+
}
|
package/test/mocha.opts
ADDED
|
@@ -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
|
+
}
|