cucumber-cucumber-expressions 16.1.2 → 17.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +27 -0
- data/.rubocop_todo.yml +524 -0
- data/Gemfile +2 -1
- data/Rakefile +4 -17
- data/VERSION +1 -1
- data/cucumber-cucumber-expressions.gemspec +11 -9
- data/lib/cucumber/cucumber_expressions/argument.rb +6 -3
- data/lib/cucumber/cucumber_expressions/ast.rb +24 -59
- data/lib/cucumber/cucumber_expressions/combinatorial_generated_expression_factory.rb +6 -13
- data/lib/cucumber/cucumber_expressions/cucumber_expression.rb +9 -11
- data/lib/cucumber/cucumber_expressions/cucumber_expression_generator.rb +7 -11
- data/lib/cucumber/cucumber_expressions/cucumber_expression_parser.rb +37 -53
- data/lib/cucumber/cucumber_expressions/cucumber_expression_tokenizer.rb +13 -18
- data/lib/cucumber/cucumber_expressions/errors.rb +119 -101
- data/lib/cucumber/cucumber_expressions/expression_factory.rb +2 -0
- data/lib/cucumber/cucumber_expressions/generated_expression.rb +2 -0
- data/lib/cucumber/cucumber_expressions/group.rb +2 -0
- data/lib/cucumber/cucumber_expressions/group_builder.rb +2 -0
- data/lib/cucumber/cucumber_expressions/parameter_type.rb +13 -23
- data/lib/cucumber/cucumber_expressions/parameter_type_matcher.rb +8 -6
- data/lib/cucumber/cucumber_expressions/parameter_type_registry.rb +23 -20
- data/lib/cucumber/cucumber_expressions/regular_expression.rb +3 -2
- data/lib/cucumber/cucumber_expressions/tree_regexp.rb +5 -4
- data/spec/cucumber/cucumber_expressions/argument_spec.rb +4 -2
- data/spec/cucumber/cucumber_expressions/combinatorial_generated_expression_factory_test.rb +7 -6
- data/spec/cucumber/cucumber_expressions/cucumber_expression_generator_spec.rb +104 -101
- data/spec/cucumber/cucumber_expressions/cucumber_expression_parser_spec.rb +2 -0
- data/spec/cucumber/cucumber_expressions/cucumber_expression_spec.rb +84 -87
- data/spec/cucumber/cucumber_expressions/cucumber_expression_tokenizer_spec.rb +3 -1
- data/spec/cucumber/cucumber_expressions/cucumber_expression_transformation_spec.rb +2 -0
- data/spec/cucumber/cucumber_expressions/custom_parameter_type_spec.rb +79 -60
- data/spec/cucumber/cucumber_expressions/expression_factory_spec.rb +2 -0
- data/spec/cucumber/cucumber_expressions/parameter_type_registry_spec.rb +43 -48
- data/spec/cucumber/cucumber_expressions/parameter_type_spec.rb +3 -1
- data/spec/cucumber/cucumber_expressions/regular_expression_spec.rb +37 -28
- data/spec/cucumber/cucumber_expressions/tree_regexp_spec.rb +23 -22
- metadata +21 -9
- data/.rspec +0 -1
- data/scripts/update-gemspec +0 -32
- data/spec/capture_warnings.rb +0 -74
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Cucumber
|
2
4
|
module CucumberExpressions
|
3
5
|
ESCAPE_CHARACTER = '\\'
|
@@ -8,55 +10,31 @@ module Cucumber
|
|
8
10
|
END_OPTIONAL_CHARACTER = ')'
|
9
11
|
|
10
12
|
class Node
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
attr_reader :type, :nodes, :token, :start, :end
|
14
|
+
|
15
|
+
def initialize(type, nodes, token, start, ending)
|
16
|
+
raise 'Either nodes or token must be defined' if nodes.nil? && token.nil?
|
17
|
+
|
15
18
|
@type = type
|
16
19
|
@nodes = nodes
|
17
20
|
@token = token
|
18
21
|
@start = start
|
19
|
-
@end =
|
20
|
-
end
|
21
|
-
|
22
|
-
def type
|
23
|
-
@type
|
24
|
-
end
|
25
|
-
|
26
|
-
def nodes
|
27
|
-
@nodes
|
28
|
-
end
|
29
|
-
|
30
|
-
def token
|
31
|
-
@token
|
32
|
-
end
|
33
|
-
|
34
|
-
def start
|
35
|
-
@start
|
36
|
-
end
|
37
|
-
|
38
|
-
def end
|
39
|
-
@end
|
22
|
+
@end = ending
|
40
23
|
end
|
41
24
|
|
42
25
|
def text
|
43
|
-
if @token.nil?
|
44
|
-
|
45
|
-
end
|
26
|
+
return @nodes.map { |value| value.text }.join('') if @token.nil?
|
27
|
+
|
46
28
|
@token
|
47
29
|
end
|
48
30
|
|
49
31
|
def to_hash
|
50
32
|
hash = Hash.new
|
51
|
-
hash[
|
52
|
-
unless @nodes.nil?
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
hash["token"] = @token
|
57
|
-
end
|
58
|
-
hash["start"] = @start
|
59
|
-
hash["end"] = @end
|
33
|
+
hash['type'] = @type
|
34
|
+
hash['nodes'] = @nodes.map { |node| node.to_hash } unless @nodes.nil?
|
35
|
+
hash['token'] = @token unless @token.nil?
|
36
|
+
hash['start'] = @start
|
37
|
+
hash['end'] = @end
|
60
38
|
hash
|
61
39
|
end
|
62
40
|
end
|
@@ -70,26 +48,11 @@ module Cucumber
|
|
70
48
|
EXPRESSION = 'EXPRESSION_NODE'
|
71
49
|
end
|
72
50
|
|
73
|
-
|
74
51
|
class Token
|
75
|
-
|
76
|
-
@type, @text, @start, @end = type, text, start, _end
|
77
|
-
end
|
52
|
+
attr_reader :type, :text, :start, :end
|
78
53
|
|
79
|
-
def type
|
80
|
-
@type
|
81
|
-
end
|
82
|
-
|
83
|
-
def text
|
84
|
-
@text
|
85
|
-
end
|
86
|
-
|
87
|
-
def start
|
88
|
-
@start
|
89
|
-
end
|
90
|
-
|
91
|
-
def end
|
92
|
-
@end
|
54
|
+
def initialize(type, text, start, ending)
|
55
|
+
@type, @text, @start, @end = type, text, start, ending
|
93
56
|
end
|
94
57
|
|
95
58
|
def self.is_escape_character(codepoint)
|
@@ -102,6 +65,7 @@ module Cucumber
|
|
102
65
|
# TODO: Unicode whitespace?
|
103
66
|
return true
|
104
67
|
end
|
68
|
+
|
105
69
|
case c
|
106
70
|
when ESCAPE_CHARACTER
|
107
71
|
true
|
@@ -126,6 +90,7 @@ module Cucumber
|
|
126
90
|
# TODO: Unicode whitespace?
|
127
91
|
return TokenType::WHITE_SPACE
|
128
92
|
end
|
93
|
+
|
129
94
|
case c
|
130
95
|
when ALTERNATION_CHARACTER
|
131
96
|
TokenType::ALTERNATION
|
@@ -178,10 +143,10 @@ module Cucumber
|
|
178
143
|
|
179
144
|
def to_hash
|
180
145
|
{
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
146
|
+
'type' => @type,
|
147
|
+
'text' => @text,
|
148
|
+
'start' => @start,
|
149
|
+
'end' => @end
|
185
150
|
}
|
186
151
|
end
|
187
152
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require('cucumber/cucumber_expressions/generated_expression')
|
2
4
|
|
3
5
|
module Cucumber
|
4
6
|
module CucumberExpressions
|
5
|
-
|
6
7
|
class CombinatorialGeneratedExpressionFactory
|
7
8
|
def initialize(expression_template, parameter_type_combinations)
|
8
9
|
@expression_template = expression_template
|
@@ -19,9 +20,7 @@ module Cucumber
|
|
19
20
|
MAX_EXPRESSIONS = 256
|
20
21
|
|
21
22
|
def generate_permutations(generated_expressions, depth, current_parameter_types)
|
22
|
-
if generated_expressions.length >= MAX_EXPRESSIONS
|
23
|
-
return
|
24
|
-
end
|
23
|
+
return if generated_expressions.length >= MAX_EXPRESSIONS
|
25
24
|
|
26
25
|
if depth == @parameter_type_combinations.length
|
27
26
|
generated_expression = GeneratedExpression.new(@expression_template, current_parameter_types)
|
@@ -31,19 +30,13 @@ module Cucumber
|
|
31
30
|
|
32
31
|
(0...@parameter_type_combinations[depth].length).each do |i|
|
33
32
|
# Avoid recursion if no elements can be added.
|
34
|
-
if generated_expressions.length >= MAX_EXPRESSIONS
|
35
|
-
|
36
|
-
end
|
33
|
+
return if generated_expressions.length >= MAX_EXPRESSIONS
|
34
|
+
|
37
35
|
new_current_parameter_types = current_parameter_types.dup # clone
|
38
36
|
new_current_parameter_types.push(@parameter_type_combinations[depth][i])
|
39
|
-
generate_permutations(
|
40
|
-
generated_expressions,
|
41
|
-
depth + 1,
|
42
|
-
new_current_parameter_types
|
43
|
-
)
|
37
|
+
generate_permutations(generated_expressions, depth + 1, new_current_parameter_types)
|
44
38
|
end
|
45
39
|
end
|
46
40
|
end
|
47
|
-
|
48
41
|
end
|
49
42
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cucumber/cucumber_expressions/argument'
|
2
4
|
require 'cucumber/cucumber_expressions/tree_regexp'
|
3
5
|
require 'cucumber/cucumber_expressions/errors'
|
@@ -6,8 +8,7 @@ require 'cucumber/cucumber_expressions/cucumber_expression_parser'
|
|
6
8
|
module Cucumber
|
7
9
|
module CucumberExpressions
|
8
10
|
class CucumberExpression
|
9
|
-
|
10
|
-
ESCAPE_PATTERN = /([\\^\[({$.|?*+})\]])/
|
11
|
+
ESCAPE_PATTERN = /([\\^\[({$.|?*+})\]])/.freeze
|
11
12
|
|
12
13
|
def initialize(expression, parameter_type_registry)
|
13
14
|
@expression = expression
|
@@ -72,9 +73,8 @@ module Cucumber
|
|
72
73
|
def rewrite_alternation(node)
|
73
74
|
# Make sure the alternative parts aren't empty and don't contain parameter types
|
74
75
|
node.nodes.each { |alternative|
|
75
|
-
if alternative.nodes.length == 0
|
76
|
-
|
77
|
-
end
|
76
|
+
raise AlternativeMayNotBeEmpty.new(alternative, @expression) if alternative.nodes.length == 0
|
77
|
+
|
78
78
|
assert_not_empty(alternative) { |astNode| raise AlternativeMayNotExclusivelyContainOptionals.new(astNode, @expression) }
|
79
79
|
}
|
80
80
|
regex = node.nodes.map { |n| rewrite_to_regex(n) }.join('|')
|
@@ -88,14 +88,12 @@ module Cucumber
|
|
88
88
|
def rewrite_parameter(node)
|
89
89
|
name = node.text
|
90
90
|
parameter_type = @parameter_type_registry.lookup_by_type_name(name)
|
91
|
-
if parameter_type.nil?
|
92
|
-
|
93
|
-
end
|
91
|
+
raise UndefinedParameterTypeError.new(node, @expression, name) if parameter_type.nil?
|
92
|
+
|
94
93
|
@parameter_types.push(parameter_type)
|
95
94
|
regexps = parameter_type.regexps
|
96
|
-
if regexps.length == 1
|
97
|
-
|
98
|
-
end
|
95
|
+
return "(#{regexps[0]})" if regexps.length == 1
|
96
|
+
|
99
97
|
"((?:#{regexps.join(')|(?:')}))"
|
100
98
|
end
|
101
99
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cucumber/cucumber_expressions/parameter_type_matcher'
|
2
4
|
require 'cucumber/cucumber_expressions/generated_expression'
|
3
5
|
require 'cucumber/cucumber_expressions/combinatorial_generated_expression_factory'
|
@@ -12,16 +14,14 @@ module Cucumber
|
|
12
14
|
def generate_expressions(text)
|
13
15
|
parameter_type_combinations = []
|
14
16
|
parameter_type_matchers = create_parameter_type_matchers(text)
|
15
|
-
expression_template =
|
17
|
+
expression_template = +''
|
16
18
|
pos = 0
|
17
19
|
|
18
20
|
loop do
|
19
21
|
matching_parameter_type_matchers = []
|
20
22
|
parameter_type_matchers.each do |parameter_type_matcher|
|
21
23
|
advanced_parameter_type_matcher = parameter_type_matcher.advance_to(pos)
|
22
|
-
if advanced_parameter_type_matcher.find
|
23
|
-
matching_parameter_type_matchers.push(advanced_parameter_type_matcher)
|
24
|
-
end
|
24
|
+
matching_parameter_type_matchers.push(advanced_parameter_type_matcher) if advanced_parameter_type_matcher.find
|
25
25
|
end
|
26
26
|
|
27
27
|
if matching_parameter_type_matchers.any?
|
@@ -39,16 +39,14 @@ module Cucumber
|
|
39
39
|
# Users are most likely to want these, so they should be listed at the top.
|
40
40
|
parameter_types = []
|
41
41
|
best_parameter_type_matchers.each do |parameter_type_matcher|
|
42
|
-
unless parameter_types.include?(parameter_type_matcher.parameter_type)
|
43
|
-
parameter_types.push(parameter_type_matcher.parameter_type)
|
44
|
-
end
|
42
|
+
parameter_types.push(parameter_type_matcher.parameter_type) unless parameter_types.include?(parameter_type_matcher.parameter_type)
|
45
43
|
end
|
46
44
|
parameter_types.sort!
|
47
45
|
|
48
46
|
parameter_type_combinations.push(parameter_types)
|
49
47
|
|
50
48
|
expression_template += escape(text.slice(pos...best_parameter_type_matcher.start))
|
51
|
-
expression_template +=
|
49
|
+
expression_template += '{%s}'
|
52
50
|
|
53
51
|
pos = best_parameter_type_matcher.start + best_parameter_type_matcher.group.length
|
54
52
|
else
|
@@ -71,9 +69,7 @@ module Cucumber
|
|
71
69
|
def create_parameter_type_matchers(text)
|
72
70
|
parameter_matchers = []
|
73
71
|
@parameter_type_registry.parameter_types.each do |parameter_type|
|
74
|
-
if parameter_type.use_for_snippets
|
75
|
-
parameter_matchers += create_parameter_type_matchers2(parameter_type, text)
|
76
|
-
end
|
72
|
+
parameter_matchers += create_parameter_type_matchers2(parameter_type, text) if parameter_type.use_for_snippets
|
77
73
|
end
|
78
74
|
parameter_matchers
|
79
75
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cucumber/cucumber_expressions/ast'
|
2
4
|
require 'cucumber/cucumber_expressions/errors'
|
3
5
|
require 'cucumber/cucumber_expressions/cucumber_expression_tokenizer'
|
@@ -11,16 +13,16 @@ module Cucumber
|
|
11
13
|
token = tokens[current]
|
12
14
|
case token.type
|
13
15
|
when TokenType::WHITE_SPACE, TokenType::TEXT, TokenType::END_PARAMETER, TokenType::END_OPTIONAL
|
14
|
-
return 1, [Node.new(NodeType::TEXT, nil, token.text, token.start, token.end)]
|
16
|
+
return [1, [Node.new(NodeType::TEXT, nil, token.text, token.start, token.end)]]
|
15
17
|
when TokenType::ALTERNATION
|
16
18
|
raise AlternationNotAllowedInOptional.new(expression, token)
|
17
19
|
when TokenType::BEGIN_PARAMETER, TokenType::START_OF_LINE, TokenType::END_OF_LINE, TokenType::BEGIN_OPTIONAL
|
18
20
|
else
|
19
21
|
# If configured correctly this will never happen
|
20
|
-
return 0, nil
|
22
|
+
return [0, nil]
|
21
23
|
end
|
22
24
|
# If configured correctly this will never happen
|
23
|
-
return 0, nil
|
25
|
+
return [0, nil]
|
24
26
|
end
|
25
27
|
|
26
28
|
# name := whitespace | .
|
@@ -28,51 +30,40 @@ module Cucumber
|
|
28
30
|
token = tokens[current]
|
29
31
|
case token.type
|
30
32
|
when TokenType::WHITE_SPACE, TokenType::TEXT
|
31
|
-
return 1, [Node.new(NodeType::TEXT, nil, token.text, token.start, token.end)]
|
33
|
+
return [1, [Node.new(NodeType::TEXT, nil, token.text, token.start, token.end)]]
|
32
34
|
when TokenType::BEGIN_PARAMETER, TokenType::END_PARAMETER, TokenType::BEGIN_OPTIONAL, TokenType::END_OPTIONAL, TokenType::ALTERNATION
|
33
35
|
raise InvalidParameterTypeNameInNode.new(expression, token)
|
34
36
|
when TokenType::START_OF_LINE, TokenType::END_OF_LINE
|
35
37
|
# If configured correctly this will never happen
|
36
|
-
return 0, nil
|
38
|
+
return [0, nil]
|
37
39
|
else
|
38
40
|
# If configured correctly this will never happen
|
39
|
-
return 0, nil
|
41
|
+
return [0, nil]
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
45
|
# parameter := '{' + name* + '}'
|
44
|
-
parse_parameter = parse_between(
|
45
|
-
NodeType::PARAMETER,
|
46
|
-
TokenType::BEGIN_PARAMETER,
|
47
|
-
TokenType::END_PARAMETER,
|
48
|
-
[parse_name]
|
49
|
-
)
|
46
|
+
parse_parameter = parse_between(NodeType::PARAMETER, TokenType::BEGIN_PARAMETER, TokenType::END_PARAMETER, [parse_name])
|
50
47
|
|
51
48
|
# optional := '(' + option* + ')'
|
52
49
|
# option := optional | parameter | text
|
53
50
|
optional_sub_parsers = []
|
54
|
-
parse_optional = parse_between(
|
55
|
-
NodeType::OPTIONAL,
|
56
|
-
TokenType::BEGIN_OPTIONAL,
|
57
|
-
TokenType::END_OPTIONAL,
|
58
|
-
optional_sub_parsers
|
59
|
-
)
|
51
|
+
parse_optional = parse_between(NodeType::OPTIONAL, TokenType::BEGIN_OPTIONAL, TokenType::END_OPTIONAL, optional_sub_parsers)
|
60
52
|
optional_sub_parsers << parse_optional << parse_parameter << parse_text
|
61
53
|
|
62
54
|
# alternation := alternative* + ( '/' + alternative* )+
|
63
55
|
parse_alternative_separator = lambda do |_, tokens, current|
|
64
|
-
unless looking_at(tokens, current, TokenType::ALTERNATION)
|
65
|
-
|
66
|
-
end
|
56
|
+
return [0, nil] unless looking_at(tokens, current, TokenType::ALTERNATION)
|
57
|
+
|
67
58
|
token = tokens[current]
|
68
|
-
return 1, [Node.new(NodeType::ALTERNATIVE, nil, token.text, token.start, token.end)]
|
59
|
+
return [1, [Node.new(NodeType::ALTERNATIVE, nil, token.text, token.start, token.end)]]
|
69
60
|
end
|
70
61
|
|
71
62
|
alternative_parsers = [
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
63
|
+
parse_alternative_separator,
|
64
|
+
parse_optional,
|
65
|
+
parse_parameter,
|
66
|
+
parse_text,
|
76
67
|
]
|
77
68
|
|
78
69
|
# alternation := (?<=left-boundary) + alternative* + ( '/' + alternative* )+ + (?=right-boundary)
|
@@ -81,30 +72,26 @@ module Cucumber
|
|
81
72
|
# alternative: = optional | parameter | text
|
82
73
|
parse_alternation = lambda do |expr, tokens, current|
|
83
74
|
previous = current - 1
|
84
|
-
unless looking_at_any(tokens, previous, [TokenType::START_OF_LINE, TokenType::WHITE_SPACE, TokenType::END_PARAMETER])
|
85
|
-
return 0, nil
|
86
|
-
end
|
75
|
+
return [0, nil] unless looking_at_any(tokens, previous, [TokenType::START_OF_LINE, TokenType::WHITE_SPACE, TokenType::END_PARAMETER])
|
87
76
|
|
88
77
|
consumed, ast = parse_tokens_until(expr, alternative_parsers, tokens, current, [TokenType::WHITE_SPACE, TokenType::END_OF_LINE, TokenType::BEGIN_PARAMETER])
|
89
78
|
sub_current = current + consumed
|
90
|
-
unless ast.map { |astNode| astNode.type }.include? NodeType::ALTERNATIVE
|
91
|
-
return 0, nil
|
92
|
-
end
|
79
|
+
return [0, nil] unless ast.map { |astNode| astNode.type }.include? NodeType::ALTERNATIVE
|
93
80
|
|
94
81
|
start = tokens[current].start
|
95
82
|
_end = tokens[sub_current].start
|
96
83
|
# Does not consume right hand boundary token
|
97
|
-
return consumed, [Node.new(NodeType::ALTERNATION, split_alternatives(start, _end, ast), nil, start, _end)]
|
84
|
+
return [consumed, [Node.new(NodeType::ALTERNATION, split_alternatives(start, _end, ast), nil, start, _end)]]
|
98
85
|
end
|
99
86
|
|
100
87
|
#
|
101
88
|
# cucumber-expression := ( alternation | optional | parameter | text )*
|
102
89
|
#
|
103
90
|
parse_cucumber_expression = parse_between(
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
91
|
+
NodeType::EXPRESSION,
|
92
|
+
TokenType::START_OF_LINE,
|
93
|
+
TokenType::END_OF_LINE,
|
94
|
+
[parse_alternation, parse_optional, parse_parameter, parse_text]
|
108
95
|
)
|
109
96
|
|
110
97
|
tokenizer = CucumberExpressionTokenizer.new
|
@@ -117,30 +104,28 @@ module Cucumber
|
|
117
104
|
|
118
105
|
def parse_between(type, begin_token, end_token, parsers)
|
119
106
|
lambda do |expression, tokens, current|
|
120
|
-
unless looking_at(tokens, current, begin_token)
|
121
|
-
|
122
|
-
end
|
107
|
+
return [0, nil] unless looking_at(tokens, current, begin_token)
|
108
|
+
|
123
109
|
sub_current = current + 1
|
124
110
|
consumed, ast = parse_tokens_until(expression, parsers, tokens, sub_current, [end_token, TokenType::END_OF_LINE])
|
125
111
|
sub_current += consumed
|
126
112
|
|
127
113
|
# endToken not found
|
128
|
-
unless looking_at(tokens, sub_current, end_token)
|
129
|
-
|
130
|
-
end
|
114
|
+
raise MissingEndToken.new(expression, begin_token, end_token, tokens[current]) unless looking_at(tokens, sub_current, end_token)
|
115
|
+
|
131
116
|
# consumes endToken
|
132
117
|
start = tokens[current].start
|
133
118
|
_end = tokens[sub_current].end
|
134
119
|
consumed = sub_current + 1 - current
|
135
120
|
ast = [Node.new(type, ast, nil, start, _end)]
|
136
|
-
return consumed, ast
|
121
|
+
return [consumed, ast]
|
137
122
|
end
|
138
123
|
end
|
139
124
|
|
140
125
|
def parse_token(expression, parsers, tokens, start_at)
|
141
126
|
parsers.each do |parser|
|
142
127
|
consumed, ast = parser.call(expression, tokens, start_at)
|
143
|
-
return consumed, ast unless consumed == 0
|
128
|
+
return [consumed, ast] unless consumed == 0
|
144
129
|
end
|
145
130
|
# If configured correctly this will never happen
|
146
131
|
raise 'No eligible parsers for ' + tokens
|
@@ -151,15 +136,15 @@ module Cucumber
|
|
151
136
|
size = tokens.length
|
152
137
|
ast = []
|
153
138
|
while current < size do
|
154
|
-
if looking_at_any(tokens, current, end_tokens)
|
155
|
-
|
156
|
-
end
|
139
|
+
break if looking_at_any(tokens, current, end_tokens)
|
140
|
+
|
157
141
|
consumed, sub_ast = parse_token(expression, parsers, tokens, current)
|
158
142
|
if consumed == 0
|
159
143
|
# If configured correctly this will never happen
|
160
144
|
# Keep to avoid infinite loops
|
161
145
|
raise 'No eligible parsers for ' + tokens
|
162
146
|
end
|
147
|
+
|
163
148
|
current += consumed
|
164
149
|
ast += sub_ast
|
165
150
|
end
|
@@ -176,9 +161,8 @@ module Cucumber
|
|
176
161
|
# Keep for completeness
|
177
162
|
return token == TokenType::START_OF_LINE
|
178
163
|
end
|
179
|
-
if at >= tokens.length
|
180
|
-
|
181
|
-
end
|
164
|
+
return token == TokenType::END_OF_LINE if at >= tokens.length
|
165
|
+
|
182
166
|
tokens[at].type == token
|
183
167
|
end
|
184
168
|
|
@@ -186,7 +170,7 @@ module Cucumber
|
|
186
170
|
separators = []
|
187
171
|
alternatives = []
|
188
172
|
alternative = []
|
189
|
-
alternation.each
|
173
|
+
alternation.each do |n|
|
190
174
|
if NodeType::ALTERNATIVE == n.type
|
191
175
|
separators.push(n)
|
192
176
|
alternatives.push(alternative)
|
@@ -194,7 +178,7 @@ module Cucumber
|
|
194
178
|
else
|
195
179
|
alternative.push(n)
|
196
180
|
end
|
197
|
-
|
181
|
+
end
|
198
182
|
alternatives.push(alternative)
|
199
183
|
create_alternative_nodes(start, _end, separators, alternatives)
|
200
184
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cucumber/cucumber_expressions/ast'
|
2
4
|
require 'cucumber/cucumber_expressions/errors'
|
3
5
|
|
@@ -15,9 +17,7 @@ module Cucumber
|
|
15
17
|
|
16
18
|
codepoints = expression.codepoints
|
17
19
|
|
18
|
-
if codepoints.empty?
|
19
|
-
tokens.push(Token.new(TokenType::START_OF_LINE, '', 0, 0))
|
20
|
-
end
|
20
|
+
tokens.push(Token.new(TokenType::START_OF_LINE, '', 0, 0)) if codepoints.empty?
|
21
21
|
|
22
22
|
codepoints.each do |codepoint|
|
23
23
|
if !treat_as_text && Token.is_escape_character(codepoint)
|
@@ -63,10 +63,10 @@ module Cucumber
|
|
63
63
|
|
64
64
|
consumed_index = @buffer_start_index + @buffer.length + escape_tokens
|
65
65
|
t = Token.new(
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
token_type,
|
67
|
+
@buffer.map { |codepoint| codepoint.chr(Encoding::UTF_8) }.join(''),
|
68
|
+
@buffer_start_index,
|
69
|
+
consumed_index
|
70
70
|
)
|
71
71
|
@buffer = []
|
72
72
|
@buffer_start_index = consumed_index
|
@@ -74,21 +74,16 @@ module Cucumber
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def token_type_of(codepoint, treat_as_text)
|
77
|
-
unless treat_as_text
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
raise CantEscape.new(
|
84
|
-
@expression,
|
85
|
-
@buffer_start_index + @buffer.length + @escaped
|
86
|
-
)
|
77
|
+
return Token.type_of(codepoint) unless treat_as_text
|
78
|
+
|
79
|
+
return TokenType::TEXT if Token.can_escape(codepoint)
|
80
|
+
|
81
|
+
raise CantEscape.new(@expression, @buffer_start_index + @buffer.length + @escaped)
|
87
82
|
end
|
88
83
|
|
89
84
|
def should_create_new_token?(previous_token_type, current_token_type)
|
90
85
|
current_token_type != previous_token_type ||
|
91
|
-
|
86
|
+
(current_token_type != TokenType::WHITE_SPACE && current_token_type != TokenType::TEXT)
|
92
87
|
end
|
93
88
|
end
|
94
89
|
end
|