Skip to content

Commit d00b994

Browse files
committed
Merge branch 'main' into dotnet
2 parents 4a7282f + d2f4cf6 commit d00b994

File tree

22 files changed

+182
-114
lines changed

22 files changed

+182
-114
lines changed

.github/workflows/release-pypi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
with:
2323
enable-cache: true
2424
python-version: "3.14"
25-
version: "0.9.7"
25+
version: "0.9.8"
2626

2727
- name: Build package
2828
run: uv build

.github/workflows/test-go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
test-go:
2424
runs-on: ${{ matrix.os }}
2525
strategy:
26-
fail-fast: false
26+
fail-fast: true
2727
matrix:
2828
os:
2929
- ubuntu-latest

.github/workflows/stryker-javascript.yml renamed to .github/workflows/test-javascript-stryker.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
name: stryker-javascript
1+
name: test-javascript-stryker
22

33
on:
44
push:
5-
branches-ignore:
6-
- renovate/**
5+
branches:
6+
- main
7+
paths:
8+
- javascript/**
9+
- testdata/**
10+
- .github/**
711
pull_request:
812
branches:
913
- main
14+
paths:
15+
- javascript/**
16+
- testdata/**
17+
- .github/**
1018

1119
jobs:
1220
stryker-javascript:
@@ -22,7 +30,7 @@ jobs:
2230
working-directory: javascript
2331
- run: npm run stryker
2432
working-directory: javascript
25-
- uses: actions/upload-artifact@v4
33+
- uses: actions/upload-artifact@v5
2634
with:
2735
name: stryker-report
2836
path: |

.github/workflows/test-python.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
with:
3636
enable-cache: true
3737
python-version: "3.14"
38-
version: "0.9.7"
38+
version: "0.9.8"
3939

4040
- name: Linting
4141
run: uvx ruff check
@@ -67,7 +67,7 @@ jobs:
6767
with:
6868
enable-cache: true
6969
python-version: ${{ matrix.python-version }}
70-
version: "0.9.7"
70+
version: "0.9.8"
7171

7272
- name: Run tests
7373
run: uv run pytest

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
9+
### Fixed
10+
- Render the empty tag expression as an empty string ([#222](https://github.com/cucumber/tag-expressions/pull/222))
11+
- Improve error message for missing operands ([#221](https://github.com/cucumber/tag-expressions/pull/221))
912

1013
- [dotNET] Add a .NET implementation
1114

go/parser.go

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ func Parse(infix string) (Evaluatable, error) {
4242
isOp(operators.Peek()) &&
4343
((ASSOC[token] == "left" && PREC[token] <= PREC[operators.Peek()]) ||
4444
(ASSOC[token] == "right" && PREC[token] < PREC[operators.Peek()])) {
45-
pushExpr(operators.Pop(), expressions)
45+
if err := pushExpr(infix, operators.Pop(), expressions); err != nil {
46+
return nil, err
47+
}
4648
}
4749
operators.Push(token)
4850
expectedTokenType = OPERAND
@@ -57,7 +59,9 @@ func Parse(infix string) (Evaluatable, error) {
5759
return nil, err
5860
}
5961
for operators.Len() > 0 && operators.Peek() != "(" {
60-
pushExpr(operators.Pop(), expressions)
62+
if err := pushExpr(infix, operators.Pop(), expressions); err != nil {
63+
return nil, err
64+
}
6165
}
6266
if operators.Len() == 0 {
6367
return nil, fmt.Errorf("Tag expression \"%s\" could not be parsed because of syntax error: Unmatched ).", infix)
@@ -70,7 +74,9 @@ func Parse(infix string) (Evaluatable, error) {
7074
if err := check(infix, expectedTokenType, OPERAND); err != nil {
7175
return nil, err
7276
}
73-
pushExpr(token, expressions)
77+
if err := pushExpr(infix, token, expressions); err != nil {
78+
return nil, err
79+
}
7480
expectedTokenType = OPERATOR
7581
}
7682
}
@@ -79,7 +85,9 @@ func Parse(infix string) (Evaluatable, error) {
7985
if operators.Peek() == "(" {
8086
return nil, fmt.Errorf("Tag expression \"%s\" could not be parsed because of syntax error: Unmatched (.", infix)
8187
}
82-
pushExpr(operators.Pop(), expressions)
88+
if err := pushExpr(infix, operators.Pop(), expressions); err != nil {
89+
return nil, err
90+
}
8391
}
8492

8593
return expressions.Pop(), nil
@@ -153,24 +161,51 @@ func check(infix, expectedTokenType, tokenType string) error {
153161
return nil
154162
}
155163

156-
func pushExpr(token string, stack *EvaluatableStack) {
164+
func pushExpr(infix string, token string, stack *EvaluatableStack) error {
157165
if token == "and" {
158-
rightAndExpr := stack.Pop()
166+
rightAndExpr, err := popOperand(infix, stack)
167+
if err != nil {
168+
return err
169+
}
170+
leftAndExpr, err := popOperand(infix, stack)
171+
if err != nil {
172+
return err
173+
}
159174
stack.Push(&andExpr{
160-
leftExpr: stack.Pop(),
175+
leftExpr: leftAndExpr,
161176
rightExpr: rightAndExpr,
162177
})
163178
} else if token == "or" {
164-
rightOrExpr := stack.Pop()
179+
rightOrExpr, err := popOperand(infix, stack)
180+
if err != nil {
181+
return err
182+
}
183+
leftOrExpr, err := popOperand(infix, stack)
184+
if err != nil {
185+
return err
186+
}
165187
stack.Push(&orExpr{
166-
leftExpr: stack.Pop(),
188+
leftExpr: leftOrExpr,
167189
rightExpr: rightOrExpr,
168190
})
169191
} else if token == "not" {
170-
stack.Push(&notExpr{expr: stack.Pop()})
192+
expr, err := popOperand(infix, stack)
193+
if err != nil {
194+
return err
195+
}
196+
stack.Push(&notExpr{expr: expr})
171197
} else {
172198
stack.Push(&literalExpr{value: token})
173199
}
200+
201+
return nil
202+
}
203+
204+
func popOperand(infix string, stack *EvaluatableStack) (Evaluatable, error) {
205+
if stack.Len() > 0 {
206+
return stack.Pop(), nil
207+
}
208+
return nil, fmt.Errorf("Tag expression \"%s\" could not be parsed because of syntax error: Expected operand.", infix)
174209
}
175210

176211
type literalExpr struct {
@@ -249,5 +284,5 @@ func (t *trueExpr) Evaluate(variables []string) bool {
249284
}
250285

251286
func (t *trueExpr) ToString() string {
252-
return "true"
287+
return ""
253288
}

java/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<dependency>
3434
<groupId>org.junit</groupId>
3535
<artifactId>junit-bom</artifactId>
36-
<version>6.0.0</version>
36+
<version>6.0.1</version>
3737
<type>pom</type>
3838
<scope>import</scope>
3939
</dependency>

java/src/main/java/io/cucumber/tagexpressions/TagExpressionParser.java

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ private Expression parse() {
5252
||
5353
(ASSOC.get(token) == Assoc.RIGHT && PREC.get(token) < PREC.get(operators.peek())))
5454
) {
55-
pushExpr(pop(operators), expressions);
55+
pushExpr(operators.pop(), expressions);
5656
}
5757
operators.push(token);
5858
expectedTokenType = TokenType.OPERAND;
@@ -63,13 +63,13 @@ private Expression parse() {
6363
} else if (")".equals(token)) {
6464
check(expectedTokenType, TokenType.OPERATOR);
6565
while (operators.size() > 0 && !"(".equals(operators.peek())) {
66-
pushExpr(pop(operators), expressions);
66+
pushExpr(operators.pop(), expressions);
6767
}
6868
if (operators.size() == 0) {
6969
throw new TagExpressionException("Tag expression \"%s\" could not be parsed because of syntax error: Unmatched ).", this.infix);
7070
}
7171
if ("(".equals(operators.peek())) {
72-
pop(operators);
72+
operators.pop();
7373
}
7474
expectedTokenType = TokenType.OPERATOR;
7575
} else {
@@ -83,7 +83,7 @@ private Expression parse() {
8383
if ("(".equals(operators.peek())) {
8484
throw new TagExpressionException("Tag expression \"%s\" could not be parsed because of syntax error: Unmatched (.", infix);
8585
}
86-
pushExpr(pop(operators), expressions);
86+
pushExpr(operators.pop(), expressions);
8787
}
8888

8989
return expressions.pop();
@@ -128,31 +128,34 @@ private void check(TokenType expectedTokenType, TokenType tokenType) {
128128
}
129129
}
130130

131-
private <T> T pop(Deque<T> stack) {
132-
if (stack.isEmpty())
133-
throw new TagExpressionException("Tag expression \"%s\" could not be parsed because of an empty stack", infix);
134-
return stack.pop();
135-
}
136-
137-
private void pushExpr(String token, Deque<Expression> stack) {
131+
private void pushExpr(String token, Deque<Expression> expressions) {
138132
switch (token) {
139133
case "and":
140-
Expression rightAndExpr = pop(stack);
141-
stack.push(new And(pop(stack), rightAndExpr));
134+
Expression rightAndExpr = popOperand(expressions);
135+
Expression leftAndExpr = popOperand(expressions);
136+
expressions.push(new And(leftAndExpr, rightAndExpr));
142137
break;
143138
case "or":
144-
Expression rightOrExpr = pop(stack);
145-
stack.push(new Or(pop(stack), rightOrExpr));
139+
Expression rightOrExpr = popOperand(expressions);
140+
Expression leftOrExpr = popOperand(expressions);
141+
expressions.push(new Or(leftOrExpr, rightOrExpr));
146142
break;
147143
case "not":
148-
stack.push(new Not(pop(stack)));
144+
Expression expression = popOperand(expressions);
145+
expressions.push(new Not(expression));
149146
break;
150147
default:
151-
stack.push(new Literal(token));
148+
expressions.push(new Literal(token));
152149
break;
153150
}
154151
}
155152

153+
private <T> T popOperand(Deque<T> stack) {
154+
if (stack.isEmpty())
155+
throw new TagExpressionException("Tag expression \"%s\" could not be parsed because of syntax error: Expected operand.", infix);
156+
return stack.pop();
157+
}
158+
156159
private boolean isUnary(String token) {
157160
return "not".equals(token);
158161
}
@@ -267,7 +270,7 @@ public boolean evaluate(List<String> variables) {
267270

268271
@Override
269272
public String toString() {
270-
return "true";
273+
return "";
271274
}
272275
}
273276
}

javascript/package-lock.json

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

javascript/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@stryker-mutator/typescript-checker": "9.3.0",
5151
"@types/js-yaml": "^4.0.3",
5252
"@types/mocha": "10.0.10",
53-
"@types/node": "22.18.13",
53+
"@types/node": "22.19.0",
5454
"@typescript-eslint/eslint-plugin": "^8.46.0",
5555
"@typescript-eslint/parser": "^8.46.0",
5656
"eslint": "^9.21.0",
@@ -59,7 +59,7 @@
5959
"eslint-plugin-simple-import-sort": "^12.1.1",
6060
"globals": "^16.0.0",
6161
"js-yaml": "^4.1.0",
62-
"mocha": "11.7.4",
62+
"mocha": "11.7.5",
6363
"prettier": "^3.5.2",
6464
"pretty-quick": "4.2.2",
6565
"ts-node": "10.9.2",

0 commit comments

Comments
 (0)