diff --git a/go/parser.go b/go/parser.go index 4ff46382..7f358bc7 100644 --- a/go/parser.go +++ b/go/parser.go @@ -5,10 +5,12 @@ import ( "fmt" "strings" "unicode" + "regexp" ) const OPERAND = "operand" const OPERATOR = "operator" +var VALID_TOKEN = regexp.MustCompile(`^(?:@[^@]*|and|or|not|\(|\))$`) type Evaluatable interface { Evaluate(variables []string) bool @@ -116,6 +118,10 @@ func tokenize(expr string) ([]string, error) { escaped = true } else if c == '(' || c == ')' || unicode.IsSpace(c) { if token.Len() > 0 { + err := isTokenValid(token.String(), expr) + if err != nil { + return nil, err + } tokens = append(tokens, token.String()) token.Reset() } @@ -127,6 +133,10 @@ func tokenize(expr string) ([]string, error) { } } if token.Len() > 0 { + err := isTokenValid(token.String(), expr) + if err != nil { + return nil, err + } tokens = append(tokens, token.String()) } @@ -152,6 +162,12 @@ func check(infix, expectedTokenType, tokenType string) error { } return nil } +func isTokenValid(token string, expr string) error { + if !VALID_TOKEN.MatchString(token) { + return fmt.Errorf("Tag expression \"%s\" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like \"@tag1\" and avoiding more than one \"@\" in the tag name.", expr) + } + return nil +} func pushExpr(token string, stack *EvaluatableStack) { if token == "and" { diff --git a/java/src/main/java/io/cucumber/tagexpressions/TagExpressionParser.java b/java/src/main/java/io/cucumber/tagexpressions/TagExpressionParser.java index e71d74f3..e94da9f8 100644 --- a/java/src/main/java/io/cucumber/tagexpressions/TagExpressionParser.java +++ b/java/src/main/java/io/cucumber/tagexpressions/TagExpressionParser.java @@ -10,6 +10,8 @@ import java.util.regex.Pattern; public final class TagExpressionParser { + //regex for token to ensure no token has ',' in them later can be customized further + private final static String VALID_TOKEN = "^(?:@[^@]*|and|or|not|\\(|\\))$"; private static final Map ASSOC = new HashMap() {{ put("or", Assoc.LEFT); put("and", Assoc.LEFT); @@ -106,6 +108,7 @@ private static List tokenize(String expr) { isEscaped = true; } else if (c == '(' || c == ')' || Character.isWhitespace(c)) { if (token.length() > 0) { + isTokenValid(token,expr); tokens.add(token.toString()); token = new StringBuilder(); } @@ -116,12 +119,28 @@ private static List tokenize(String expr) { token.append(c); } } - if (token.length() > 0) { + if (token.length() > 0) { + isTokenValid(token,expr); tokens.add(token.toString()); } return tokens; } + /** + * this method checks if the token comply with the req + * regex if not throws exception + * @param token supposed tag or operator of the expresiion + * @param expr entire expression + */ + private static void isTokenValid(StringBuilder token,String expr){ + + if(token.length()>0&&!String.valueOf(token).matches(VALID_TOKEN)){ + throw new TagExpressionException("Tag expression \"%s\" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like \"@tag1\" and avoiding more than one \"@\" in the tag name.", + expr); + } + + } + private void check(TokenType expectedTokenType, TokenType tokenType) { if (expectedTokenType != tokenType) { throw new TagExpressionException("Tag expression \"%s\" could not be parsed because of syntax error: Expected %s.", infix, expectedTokenType.toString().toLowerCase()); diff --git a/javascript/src/index.ts b/javascript/src/index.ts index 00f3636c..58336ad1 100644 --- a/javascript/src/index.ts +++ b/javascript/src/index.ts @@ -12,6 +12,7 @@ const ASSOC: { [key: string]: string } = { and: 'left', not: 'right', } +const VALID_TOKEN = /^(?:@[^@]*|and|or|not|\(|\))$/; /** * Parses infix boolean expression (using Dijkstra's Shunting Yard algorithm) @@ -109,6 +110,7 @@ function tokenize(expr: string): string[] { isEscaped = true } else if (c === '(' || c === ')' || /\s/.test(c)) { if (token.length > 0) { + isTokenValid(token.join(''),expr); tokens.push(token.join('')) token = [] } @@ -120,11 +122,20 @@ function tokenize(expr: string): string[] { } } if (token.length > 0) { + isTokenValid(token.join(''),expr); tokens.push(token.join('')) } return tokens } +function isTokenValid(token: string, expr: string): void { + if (!token.match(VALID_TOKEN)) { + throw new Error( + `Tag expression "${expr}" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like "@tag1" and avoiding more than one "@" in the tag name.` + ); + } +} + function isUnary(token: string) { return 'not' === token } diff --git a/perl/lib/Cucumber/TagExpressions.pm b/perl/lib/Cucumber/TagExpressions.pm index 936f1cd6..d2d4a823 100644 --- a/perl/lib/Cucumber/TagExpressions.pm +++ b/perl/lib/Cucumber/TagExpressions.pm @@ -30,7 +30,7 @@ use strict; use warnings; use Cucumber::TagExpressions::Node; - +our $VALID_TOKEN = qr/^(?:@[^@]*|and|or|not|\(|\))$/; sub _expect_token { my ( $state, $token ) = @_; @@ -57,11 +57,19 @@ sub _get_token { my $token = ''; while (1) { my $char = _consume_char( $state, 1 ); - return ($token ? $token : undef) - if not defined $char; + if (!defined $char) { + if ($token){ + _is_token_valid( $state, $token); + return $token; + } + else{ + return undef; + } + } if ( $char =~ m/\s/ ) { if ( $token ) { + _is_token_valid( $state, $token); return $token; } else { @@ -70,6 +78,7 @@ sub _get_token { } elsif ( $char eq '(' or $char eq ')' ) { if ( $token ) { + _is_token_valid( $state, $token); _save_token( $state, $char ); return $token; } @@ -93,6 +102,13 @@ sub _get_token { } } +sub _is_token_valid { + my ($state, $token) = @_; + if ($token !~ $VALID_TOKEN) { + die qq{Tag expression "$state->{text}" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like "\@tag1" and avoiding more than one "\@" in the tag name.} + } +} + sub _save_token { my ( $state, $token ) = @_; diff --git a/python/cucumber_tag_expressions/parser.py b/python/cucumber_tag_expressions/parser.py index 792c065a..835555da 100644 --- a/python/cucumber_tag_expressions/parser.py +++ b/python/cucumber_tag_expressions/parser.py @@ -17,6 +17,8 @@ from __future__ import absolute_import from enum import Enum from cucumber_tag_expressions.model import Literal, And, Or, Not, True_ +import re + # ----------------------------------------------------------------------------- @@ -156,6 +158,9 @@ class TagExpressionParser(object): TOKEN_MAP = {token.keyword: token for token in Token.__members__.values()} + valid_token_pattern = r'^(?:@[^@]*|and|or|not|\(|\))$' + valid_token_regex = re.compile(valid_token_pattern) + @classmethod def select_token(cls, text): """Select the token that matches the text or return None. @@ -165,6 +170,13 @@ def select_token(cls, text): """ return cls.TOKEN_MAP.get(text, None) + @classmethod + def check_valid_token(cls,part,expr): + if not cls.valid_token_regex.match(part): + message = 'Tag expression "%s" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like "@tag1" and avoiding more than one "@" in the tag name.' + raise TagExpressionError(message % (expr)) + + @classmethod def make_operand(cls, text): """Creates operand-object from parsed text.""" @@ -201,6 +213,7 @@ def ensure_expected_token_type(token_type): for index, part in enumerate(parts): token = cls.select_token(part) + cls.check_valid_token(part,text) if token is None: # -- CASE OPERAND: Literal or ... ensure_expected_token_type(TokenType.OPERAND) diff --git a/python/tests/functional/test_tag_expression.py b/python/tests/functional/test_tag_expression.py index 1f505723..e378b2b5 100644 --- a/python/tests/functional/test_tag_expression.py +++ b/python/tests/functional/test_tag_expression.py @@ -34,8 +34,8 @@ class TestTagExpression(object): @pytest.mark.parametrize("tag_expression_text, expected, tags, case", [ ("", True, [], "no_tags"), - ("", True, ["a"], "one tag: a"), - ("", True, ["other"], "one tag: other"), + ("", True, ["@a"], "one tag: @a"), + ("", True, ["@other"], "one tag: @other"), ]) def test_empty_expression_is_true(self, tag_expression_text, expected, tags, case): tag_expression = TagExpressionParser.parse(tag_expression_text) @@ -43,10 +43,10 @@ def test_empty_expression_is_true(self, tag_expression_text, expected, tags, cas @pytest.mark.parametrize("tag_expression_text, expected, tags, case", [ - ("not a", False, ["a", "other"], "two tags: a, other"), - ("not a", False, ["a"], "one tag: a"), - ("not a", True, ["other"], "one tag: other"), - ("not a", True, [], "no_tags"), + ("not @a", False, ["@a", "@other"], "two tags: @a, @other"), + ("not @a", False, ["@a"], "one tag: @a"), + ("not @a", True, ["@other"], "one tag: @other"), + ("not @a", True, [], "no_tags"), ]) def test_not_operation(self, tag_expression_text, expected, tags, case): tag_expression = TagExpressionParser.parse(tag_expression_text) @@ -75,34 +75,34 @@ def test_fails_when_only_operators_are_used(self, tag_part): @pytest.mark.parametrize("tag_expression_text, expected, tags, case", [ - ("a and b", True, ["a", "b"], "both tags"), - ("a and b", True, ["a", "b", "other"], "both tags and more"), - ("a and b", False, ["a"], "one tag: a"), - ("a and b", False, ["b"], "one tag: b"), - ("a and b", False, ["other"], "one tag: other"), - ("a and b", False, [], "no_tags"), + ("@a and @b", True, ["@a", "@b"], "both tags"), + ("@a and @b", True, ["@a", "@b", "@other"], "both tags and more"), + ("@a and @b", False, ["@a"], "one tag: @a"), + ("@a and @b", False, ["@b"], "one tag: @b"), + ("@a and @b", False, ["@other"], "one tag: @other"), + ("@a and @b", False, [], "no_tags"), ]) def test_and_operation(self, tag_expression_text, expected, tags, case): tag_expression = TagExpressionParser.parse(tag_expression_text) assert expected == tag_expression.evaluate(tags) @pytest.mark.parametrize("tag_expression_text, expected, tags, case", [ - ("a or b", True, ["a", "b"], "both tags"), - ("a or b", True, ["a", "b", "other"], "both tags and more"), - ("a or b", True, ["a"], "one tag: a"), - ("a or b", True, ["b"], "one tag: b"), - ("a or b", False, ["other"], "one tag: other"), - ("a or b", False, [], "no_tags"), + ("@a or @b", True, ["@a", "@b"], "both tags"), + ("@a or @b", True, ["@a", "@b", "@other"], "both tags and more"), + ("@a or @b", True, ["@a"], "one tag: @a"), + ("@a or @b", True, ["@b"], "one tag: @b"), + ("@a or @b", False, ["@other"], "one tag: @other"), + ("@a or @b", False, [], "no_tags"), ]) def test_or_operation(self, tag_expression_text, expected, tags, case): tag_expression = TagExpressionParser.parse(tag_expression_text) assert expected == tag_expression.evaluate(tags) @pytest.mark.parametrize("tag_expression_text, expected, tags, case", [ - ("a", True, ["a", "other"], "two tags: a, other"), - ("a", True, ["a"], "one tag: a"), - ("a", False, ["other"], "one tag: other"), - ("a", False, [], "no_tags"), + ("@a", True, ["@a", "@other"], "two tags: @a, @other"), + ("@a", True, ["@a"], "one tag: @a"), + ("@a", False, ["@other"], "one tag: @other"), + ("@a", False, [], "no_tags"), ]) def test_literal(self, tag_expression_text, expected, tags, case): tag_expression = TagExpressionParser.parse(tag_expression_text) @@ -110,20 +110,20 @@ def test_literal(self, tag_expression_text, expected, tags, case): # NOTE: CANDIDATE for property-based testing @pytest.mark.parametrize("tag_expression_text, expected, tags, case", [ - ("a and b", True, ["a", "b"], "two tags: a, b"), - ("a and b", False, ["a"], "one tag: a"), - ("a and b", False, [], "no_tags"), - ("a or b", True, ["a", "b"], "two tags: a, b"), - ("a or b", True, ["b"], "one tag: b"), - ("a or b", False, [], "no_tags"), - ("a and b or c", True, ["a", "b", "c"], "three tags: a, b, c"), - ("a and b or c", True, ["a", "other", "c"], "three tags: a, other, c"), - ("a and b or c", True, ["a", "b", "other"], "three tags: a, b, other"), - ("a and b or c", True, ["a", "b"], "two tags: a, b"), - ("a and b or c", True, ["a", "c"], "two tags: a, c"), - ("a and b or c", False, ["a"], "one tag: a"), - ("a and b or c", True, ["c"], "one tag: c"), - ("a and b or c", False, [], "not tags"), + ("@a and @b", True, ["@a", "@b"], "two tags: @a, @b"), + ("@a and @b", False, ["@a"], "one tag: @a"), + ("@a and @b", False, [], "no_tags"), + ("@a or @b", True, ["@a", "@b"], "two tags: @a, @b"), + ("@a or @b", True, ["@b"], "one tag: @b"), + ("@a or @b", False, [], "no_tags"), + ("@a and @b or @c", True, ["@a", "@b", "@c"], "three tags: @a, @b, @c"), + ("@a and @b or @c", True, ["@a", "@other", "@c"], "three tags: @a, @other, @c"), + ("@a and @b or @c", True, ["@a", "@b", "@other"], "three tags: @a, @b, @other"), + ("@a and @b or @c", True, ["@a", "@b"], "two tags: @a, @b"), + ("@a and @b or @c", True, ["@a", "@c"], "two tags: @a, @c"), + ("@a and @b or @c", False, ["@a"], "one tag: @a"), + ("@a and @b or @c", True, ["@c"], "one tag: @c"), + ("@a and @b or @c", False, [], "not tags"), ]) def test_not_not_expression_sameas_expression(self, tag_expression_text, expected, tags, case): not2_tag_expression_text = "not not "+ tag_expression_text diff --git a/python/tests/unit/test_parser.py b/python/tests/unit/test_parser.py index 27204264..db9634bc 100644 --- a/python/tests/unit/test_parser.py +++ b/python/tests/unit/test_parser.py @@ -126,12 +126,12 @@ def assert_parse_with_error_contains_message(text, error_message): # -- TESTS FOR: TagExpressionParser.parse() correct_test_data = [ - ("a and b", "( a and b )"), - ("a or (b)", "( a or b )"), - ("not a", "not ( a )"), - ("( a and b ) or ( c and d )", "( ( a and b ) or ( c and d ) )"), - ("not a or b and not c or not d or e and f", - "( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )"), + ("@a and @b", "( @a and @b )"), + ("@a or (@b)", "( @a or @b )"), + ("not @a", "not ( @a )"), + ("( @a and @b ) or ( @c and @d )", "( ( @a and @b ) or ( @c and @d ) )"), + ("not @a or @b and not @c or not @d or @e and @f", + "( ( ( not ( @a ) or ( @b and not ( @c ) ) ) or not ( @d ) ) or ( @e and @f ) )"), ] @pytest.mark.parametrize("text, expected", correct_test_data) @@ -139,9 +139,9 @@ def test_parse(self, text, expected): self.assert_parse_expression_equals_expression_string(text, expected) @pytest.mark.parametrize("text, expected", [ - ("(a)", "a"), - ("b", "b"), - ("(((((c)))))", "c"), + ("(@a)", "@a"), + ("@b", "@b"), + ("(((((@c)))))", "@c"), ]) def test_parse__with_one_literal(self, text, expected): self.assert_parse_expression_equals_expression_string(text, expected) @@ -151,23 +151,23 @@ def test_parse__empty_is_always_true(self, text): self.assert_parse_expression_equals_expression_repr(text, "True_()") @pytest.mark.parametrize("text, expected", [ - ("a and b or c", "( ( a and b ) or c )"), - ("a or b and c", "( a or ( b and c ) )"), - ("a and b and c", "( ( a and b ) and c )"), - ("a or b or c", "( ( a or b ) or c )"), - ("a and not b", "( a and not ( b ) )"), - ("a or not b", "( a or not ( b ) )"), - ("not a and b", "( not ( a ) and b )"), - ("not a or b", "( not ( a ) or b )"), - ("not (a and b) or c", "( not ( a and b ) or c )"), + ("@a and @b or @c", "( ( @a and @b ) or @c )"), + ("@a or @b and @c", "( @a or ( @b and @c ) )"), + ("@a and @b and @c", "( ( @a and @b ) and @c )"), + ("@a or @b or @c", "( ( @a or @b ) or @c )"), + ("@a and not @b", "( @a and not ( @b ) )"), + ("@a or not @b", "( @a or not ( @b ) )"), + ("not @a and @b", "( not ( @a ) and @b )"), + ("not @a or @b", "( not ( @a ) or @b )"), + ("not (@a and @b) or @c", "( not ( @a and @b ) or @c )"), ]) def test_parse__ensure_precedence(self, text, expected): """Ensures that the operation precedence is parsed correctly.""" self.assert_parse_expression_equals_expression_string(text, expected) @pytest.mark.parametrize("text, expected", [ - ("not not a", "not ( not ( a ) )"), - ("not not a and b", "( not ( not ( a ) ) and b )"), + ("not not @a", "not ( not ( @a ) )"), + ("not not @a and @b", "( not ( not ( @a ) ) and @b )"), ]) def test_parse__with_not_not(self, text, expected): self.assert_parse_expression_equals_expression_string(text, expected) @@ -175,14 +175,14 @@ def test_parse__with_not_not(self, text, expected): # -- BAD CASES: @pytest.mark.parametrize("text, error_message", [ - ("( a and b ))", "Missing '(': Too few open-parens"), - ("( ( a and b )", "Unclosed '(': Too many open-parens"), + ("( @a and @b ))", "Missing '(': Too few open-parens"), + ("( ( @a and @b )", "Unclosed '(': Too many open-parens"), ]) def test_parse__fails_with_unbalanced_parens(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @pytest.mark.parametrize("text, error_message", [ - ("a not ( and )", "Syntax error. Expected operator after a"), + ("@a not ( and )", "Syntax error. Expected operator after @a"), ]) def test_parse__fails_with_missing_operation_args(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @@ -194,7 +194,7 @@ def test_parse__fails_with_only_operations(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @pytest.mark.parametrize("text, error_message", [ - ("a b", "Syntax error. Expected operator after a"), + ("@a @b", "Syntax error. Expected operator after @a"), ]) def test_parse__fails_for_args_without_operation(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @@ -207,9 +207,9 @@ def test_parse__fails_for_empty_parens_groups(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @pytest.mark.parametrize("text, expected", [ - ("a b or", "Syntax error. Expected operator after a"), - ("a and (b not)", "Syntax error. Expected operator after b"), - ("a and (b c) or", "Syntax error. Expected operator after b"), + ("@a @b or", "Syntax error. Expected operator after @a"), + ("@a and (@b not)", "Syntax error. Expected operator after @b"), + ("@a and (@b @c) or", "Syntax error. Expected operator after @b"), ]) def test_parse__fails_with_rpn_notation(self, text, expected): # -- NOTE: RPN parsebility due to Shunting-yard algorithm (stack-based). @@ -233,16 +233,16 @@ def test_parse__fails_with_rpn_notation(self, text, expected): # -- BAD CASES: Too few operands @pytest.mark.parametrize("text, error_message", [ - ("a and ", "and: Too few operands"), - (" and b", "Syntax error. Expected operand after BEGIN"), + ("@a and ", "and: Too few operands"), + (" and @b", "Syntax error. Expected operand after BEGIN"), ]) def test_parse__fails_and_operation_with_too_few_args(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @pytest.mark.parametrize("text, error_message", [ - ("a or ", "or: Too few operands"), - (" or b", "Syntax error. Expected operand after BEGIN"), - ("a and b or ", "or: Too few operands"), + ("@a or ", "or: Too few operands"), + (" or @b", "Syntax error. Expected operand after BEGIN"), + ("@a and @b or ", "or: Too few operands"), ]) def test_parse__fails_or_operation_with_too_few_args(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) @@ -250,8 +250,8 @@ def test_parse__fails_or_operation_with_too_few_args(self, text, error_message): @pytest.mark.parametrize("text, error_message", [ ("not ", "not: Too few operands"), ("not ()", "Syntax error. Expected operand after ("), - ("not () and b", "Syntax error. Expected operand after ("), - ("not () or b", "Syntax error. Expected operand after ("), + ("not () and @b", "Syntax error. Expected operand after ("), + ("not () or @b", "Syntax error. Expected operand after ("), ]) def test_parse__fails_not_operation_with_too_few_args(self, text, error_message): self.assert_parse_with_error_contains_message(text, error_message) diff --git a/ruby/lib/cucumber/tag_expressions/parser.rb b/ruby/lib/cucumber/tag_expressions/parser.rb index 1c09450b..91491b8a 100644 --- a/ruby/lib/cucumber/tag_expressions/parser.rb +++ b/ruby/lib/cucumber/tag_expressions/parser.rb @@ -7,6 +7,7 @@ class Parser def initialize @expressions = [] @operators = [] + @valid_token = /^(?:@[^@]*|and|or|not|\(|\))$/ @operator_types = { 'or' => { type: :binary_operator, precedence: 0, assoc: :left }, @@ -80,6 +81,7 @@ def tokenize(infix_expression) escaped = true elsif ch == '(' || ch == ')' || ch.match(/\s/) if token.length > 0 + is_token_valid(token,infix_expression) tokens.push(token) token = "" end @@ -91,11 +93,18 @@ def tokenize(infix_expression) end end if token.length > 0 + is_token_valid(token,infix_expression) tokens.push(token) end tokens end + def is_token_valid(token, expr) + unless token.to_s.match?(@valid_token) + raise %Q{Tag expression "#{expr}" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like \"@tag1\" and avoiding more than one \"@\" in the tag name.} + end + end + def push_expression(token) case token when 'and' diff --git a/testdata/errors.yml b/testdata/errors.yml index 389e782d..6c22c2b7 100644 --- a/testdata/errors.yml +++ b/testdata/errors.yml @@ -6,17 +6,21 @@ error: 'Tag expression "@a and (@b @c) or" could not be parsed because of syntax error: Expected operator.' - expression: '@a and or' error: 'Tag expression "@a and or" could not be parsed because of syntax error: Expected operand.' +- expression: '@a,@b' + error: 'Tag expression "@a,@b" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like "@tag1" and avoiding more than one "@" in the tag name.' +- expression: '(@a ,@b)' + error: 'Tag expression "(@a ,@b)" could not be parsed because of syntax error: Please adhere to the Gherkin tag naming convention, using tags like "@tag1" and avoiding more than one "@" in the tag name.' - expression: 'or or' error: 'Tag expression "or or" could not be parsed because of syntax error: Expected operand.' -- expression: 'a and or' - error: 'Tag expression "a and or" could not be parsed because of syntax error: Expected operand.' -- expression: 'a b' - error: 'Tag expression "a b" could not be parsed because of syntax error: Expected operator.' -- expression: '( a and b ) )' - error: 'Tag expression "( a and b ) )" could not be parsed because of syntax error: Unmatched ).' -- expression: '( ( a and b )' - error: 'Tag expression "( ( a and b )" could not be parsed because of syntax error: Unmatched (.' -- expression: 'x or \y or z' - error: 'Tag expression "x or \y or z" could not be parsed because of syntax error: Illegal escape before "y".' -- expression: 'x\ or y' - error: 'Tag expression "x\ or y" could not be parsed because of syntax error: Expected operator.' +- expression: '@a and or' + error: 'Tag expression "@a and or" could not be parsed because of syntax error: Expected operand.' +- expression: '@a @b' + error: 'Tag expression "@a @b" could not be parsed because of syntax error: Expected operator.' +- expression: '( @a and @b ) )' + error: 'Tag expression "( @a and @b ) )" could not be parsed because of syntax error: Unmatched ).' +- expression: '( ( @a and @b )' + error: 'Tag expression "( ( @a and @b )" could not be parsed because of syntax error: Unmatched (.' +- expression: '@x or @\y or @z' + error: 'Tag expression "@x or @\y or @z" could not be parsed because of syntax error: Illegal escape before "y".' +- expression: '@x\ or @y' + error: 'Tag expression "@x\ or @y" could not be parsed because of syntax error: Expected operator.' diff --git a/testdata/evaluations.yml b/testdata/evaluations.yml index a84fb3ca..821a5c06 100644 --- a/testdata/evaluations.yml +++ b/testdata/evaluations.yml @@ -1,59 +1,59 @@ -- expression: 'not x' +- expression: 'not @x' tests: - - variables: ['x'] + - variables: ['@x'] result: false - - variables: ['y'] + - variables: ['@y'] result: true -- expression: 'x and y' +- expression: '@x and @y' tests: - - variables: ['x', 'y'] + - variables: ['@x', '@y'] result: true - - variables: ['x'] + - variables: ['@x'] result: false - - variables: ['y'] + - variables: ['@y'] result: false -- expression: 'x or y' +- expression: '@x or @y' tests: - variables: [] result: false - - variables: ['x', 'y'] + - variables: ['@x', '@y'] result: true - - variables: ['x'] + - variables: ['@x'] result: true - - variables: ['y'] + - variables: ['@y'] result: true -- expression: 'x\(1\) or y\(2\)' +- expression: '@x\(1\) or @y\(2\)' tests: - - variables: ['x(1)'] + - variables: ['@x(1)'] result: true - - variables: ['y(2)'] + - variables: ['@y(2)'] result: true -- expression: 'x\\ or y\\\) or z\\' +- expression: '@x\\ or @y\\\) or @z\\' tests: - - variables: ['x\'] + - variables: ['@x\'] result: true - - variables: ['y\)'] + - variables: ['@y\)'] result: true - - variables: ['z\'] + - variables: ['@z\'] result: true - - variables: ['x'] + - variables: ['@x'] result: false - - variables: ['y)'] + - variables: ['@y)'] result: false - - variables: ['z'] + - variables: ['@z'] result: false -- expression: '\\x or y\\ or z\\' +- expression: '@\\x or @y\\ or @z\\' tests: - - variables: ['\x'] + - variables: ['@\x'] result: true - - variables: ['y\'] + - variables: ['@y\'] result: true - - variables: ['z\'] + - variables: ['@z\'] result: true - - variables: ['x'] + - variables: ['@x'] result: false - - variables: ['y'] + - variables: ['@y'] result: false - - variables: ['z'] + - variables: ['@z'] result: false diff --git a/testdata/parsing.yml b/testdata/parsing.yml index c12f1c54..c7c5a91f 100644 --- a/testdata/parsing.yml +++ b/testdata/parsing.yml @@ -1,48 +1,46 @@ -- expression: '' - formatted: 'true' -- expression: 'a and b' - formatted: '( a and b )' -- expression: 'a or b' - formatted: '( a or b )' -- expression: 'not a' - formatted: 'not ( a )' -- expression: 'a and b and c' - formatted: '( ( a and b ) and c )' -- expression: '( a and b ) or ( c and d )' - formatted: '( ( a and b ) or ( c and d ) )' -- expression: 'not a or b and not c or not d or e and f' - formatted: '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )' -- expression: 'not a\(\) or b and not c or not d or e and f' - formatted: '( ( ( not ( a\(\) ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )' +- expression: '@a and @b' + formatted: '( @a and @b )' +- expression: '@a or @b' + formatted: '( @a or @b )' +- expression: 'not @a' + formatted: 'not ( @a )' +- expression: '@a and @b and @c' + formatted: '( ( @a and @b ) and @c )' +- expression: '( @a and @b ) or ( @c and @d )' + formatted: '( ( @a and @b ) or ( @c and @d ) )' +- expression: 'not @a or @b and not @c or not @d or @e and @f' + formatted: '( ( ( not ( @a ) or ( @b and not ( @c ) ) ) or not ( @d ) ) or ( @e and @f ) )' +- expression: 'not @a\(\) or @b and not @c or not @d or @e and @f' + formatted: '( ( ( not ( @a\(\) ) or ( @b and not ( @c ) ) ) or not ( @d ) ) or ( @e and @f ) )' -- expression: 'not (a and b)' - formatted: 'not ( a and b )' -- expression: 'not (a or b)' - formatted: 'not ( a or b )' -- expression: 'not (a and b) and c or not (d or f)' - formatted: '( ( not ( a and b ) and c ) or not ( d or f ) )' +- expression: 'not (@a and @b)' + formatted: 'not ( @a and @b )' +- expression: 'not (@a or @b)' + formatted: 'not ( @a or @b )' +- expression: 'not (@a and @b) and @c or not (@d or @f)' + formatted: '( ( not ( @a and @b ) and @c ) or not ( @d or @f ) )' -- expression: 'a\\ and b' - formatted: '( a\\ and b )' -- expression: '\\a and b' - formatted: '( \\a and b )' -- expression: 'a\\ and b' - formatted: '( a\\ and b )' -- expression: 'a and b\\' - formatted: '( a and b\\ )' -- expression: '( a and b\\\\)' - formatted: '( a and b\\\\ )' -- expression: 'a\\\( and b\\\)' - formatted: '( a\\\( and b\\\) )' -- expression: '(a and \\b)' - formatted: '( a and \\b )' -- expression: 'x or(y) ' - formatted: '( x or y )' -- expression: 'x\(1\) or(y\(2\))' - formatted: '( x\(1\) or y\(2\) )' -- expression: '\\x or y\\ or z\\' - formatted: '( ( \\x or y\\ ) or z\\ )' -- expression: 'x\\ or(y\\\)) or(z\\)' - formatted: '( ( x\\ or y\\\) ) or z\\ )' -- expression: 'x\ or y' - formatted: '( x\ or y )' +- expression: '@a\\ and @b' + formatted: '( @a\\ and @b )' +- expression: '@\\a and @b' + formatted: '( @\\a and @b )' +- expression: '@a\\ and @b' + formatted: '( @a\\ and @b )' +- expression: '@a and @b\\' + formatted: '( @a and @b\\ )' +- expression: '( @a and @b\\\\)' + formatted: '( @a and @b\\\\ )' +- expression: '@a\\\( and @b\\\)' + formatted: '( @a\\\( and @b\\\) )' +- expression: '(@a and @\\b)' + formatted: '( @a and @\\b )' +- expression: '@x or(@y) ' + formatted: '( @x or @y )' +- expression: '@x\(1\) or(@y\(2\))' + formatted: '( @x\(1\) or @y\(2\) )' +- expression: '@\\x or @y\\ or @z\\' + formatted: '( ( @\\x or @y\\ ) or @z\\ )' +- expression: '@x\\ or(@y\\\)) or(@z\\)' + formatted: '( ( @x\\ or @y\\\) ) or @z\\ )' +- expression: '@x\ or @y' + formatted: '( @x\ or @y )'