regexp_parser 1.4.0 → 1.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -1
- data/Gemfile +1 -1
- data/README.md +9 -13
- data/lib/regexp_parser/expression.rb +33 -21
- data/lib/regexp_parser/expression/classes/backref.rb +18 -10
- data/lib/regexp_parser/expression/classes/conditional.rb +4 -0
- data/lib/regexp_parser/expression/classes/group.rb +4 -2
- data/lib/regexp_parser/expression/classes/keep.rb +1 -3
- data/lib/regexp_parser/expression/methods/match_length.rb +172 -0
- data/lib/regexp_parser/expression/quantifier.rb +2 -2
- data/lib/regexp_parser/expression/sequence.rb +0 -4
- data/lib/regexp_parser/expression/subexpression.rb +3 -5
- data/lib/regexp_parser/lexer.rb +31 -24
- data/lib/regexp_parser/parser.rb +25 -3
- data/lib/regexp_parser/syntax/tokens.rb +2 -10
- data/lib/regexp_parser/version.rb +1 -1
- data/regexp_parser.gemspec +2 -2
- data/spec/expression/base_spec.rb +80 -0
- data/spec/expression/clone_spec.rb +120 -0
- data/spec/expression/conditional_spec.rb +89 -0
- data/spec/expression/free_space_spec.rb +27 -0
- data/spec/expression/methods/match_length_spec.rb +141 -0
- data/spec/expression/methods/strfregexp_spec.rb +224 -0
- data/spec/expression/methods/tests_spec.rb +97 -0
- data/spec/expression/methods/traverse_spec.rb +140 -0
- data/spec/expression/subexpression_spec.rb +50 -0
- data/spec/expression/to_h_spec.rb +26 -0
- data/spec/expression/to_s_spec.rb +100 -0
- data/spec/lexer/all_spec.rb +22 -0
- data/{test/lexer/test_conditionals.rb → spec/lexer/conditionals_spec.rb} +31 -35
- data/spec/lexer/escapes_spec.rb +38 -0
- data/spec/lexer/keep_spec.rb +22 -0
- data/{test/lexer/test_literals.rb → spec/lexer/literals_spec.rb} +20 -24
- data/{test/lexer/test_nesting.rb → spec/lexer/nesting_spec.rb} +11 -13
- data/spec/lexer/refcalls_spec.rb +54 -0
- data/spec/parser/all_spec.rb +31 -0
- data/spec/parser/alternation_spec.rb +88 -0
- data/{test/parser/test_anchors.rb → spec/parser/anchors_spec.rb} +7 -10
- data/spec/parser/conditionals_spec.rb +179 -0
- data/spec/parser/errors_spec.rb +51 -0
- data/spec/parser/escapes_spec.rb +132 -0
- data/spec/parser/free_space_spec.rb +130 -0
- data/spec/parser/groups_spec.rb +267 -0
- data/spec/parser/keep_spec.rb +19 -0
- data/spec/parser/posix_classes_spec.rb +27 -0
- data/spec/parser/properties_spec.rb +127 -0
- data/spec/parser/quantifiers_spec.rb +293 -0
- data/spec/parser/refcalls_spec.rb +237 -0
- data/spec/parser/set/intersections_spec.rb +127 -0
- data/spec/parser/set/ranges_spec.rb +111 -0
- data/spec/parser/sets_spec.rb +178 -0
- data/{test/parser/test_types.rb → spec/parser/types_spec.rb} +13 -20
- data/spec/scanner/all_spec.rb +18 -0
- data/{test/scanner/test_anchors.rb → spec/scanner/anchors_spec.rb} +8 -10
- data/{test/scanner/test_conditionals.rb → spec/scanner/conditionals_spec.rb} +49 -53
- data/spec/scanner/errors_spec.rb +90 -0
- data/{test/scanner/test_escapes.rb → spec/scanner/escapes_spec.rb} +8 -10
- data/{test/scanner/test_free_space.rb → spec/scanner/free_space_spec.rb} +48 -52
- data/{test/scanner/test_groups.rb → spec/scanner/groups_spec.rb} +33 -41
- data/spec/scanner/keep_spec.rb +33 -0
- data/{test/scanner/test_literals.rb → spec/scanner/literals_spec.rb} +8 -12
- data/{test/scanner/test_meta.rb → spec/scanner/meta_spec.rb} +8 -10
- data/{test/scanner/test_properties.rb → spec/scanner/properties_spec.rb} +14 -19
- data/{test/scanner/test_quantifiers.rb → spec/scanner/quantifiers_spec.rb} +7 -9
- data/{test/scanner/test_refcalls.rb → spec/scanner/refcalls_spec.rb} +9 -9
- data/{test/scanner/test_scripts.rb → spec/scanner/scripts_spec.rb} +8 -12
- data/{test/scanner/test_sets.rb → spec/scanner/sets_spec.rb} +14 -17
- data/spec/scanner/types_spec.rb +29 -0
- data/spec/scanner/unicode_blocks_spec.rb +28 -0
- data/spec/spec_helper.rb +14 -0
- data/{test → spec}/support/runner.rb +9 -8
- data/{test → spec}/support/warning_extractor.rb +5 -7
- data/spec/syntax/syntax_spec.rb +44 -0
- data/spec/syntax/syntax_token_map_spec.rb +23 -0
- data/spec/syntax/versions/1.8.6_spec.rb +38 -0
- data/spec/syntax/versions/1.9.1_spec.rb +23 -0
- data/spec/syntax/versions/1.9.3_spec.rb +22 -0
- data/spec/syntax/versions/2.0.0_spec.rb +28 -0
- data/spec/syntax/versions/2.2.0_spec.rb +22 -0
- data/spec/syntax/versions/aliases_spec.rb +119 -0
- data/spec/token/token_spec.rb +85 -0
- metadata +131 -140
- data/test/expression/test_all.rb +0 -12
- data/test/expression/test_base.rb +0 -90
- data/test/expression/test_clone.rb +0 -89
- data/test/expression/test_conditionals.rb +0 -113
- data/test/expression/test_free_space.rb +0 -35
- data/test/expression/test_set.rb +0 -84
- data/test/expression/test_strfregexp.rb +0 -230
- data/test/expression/test_subexpression.rb +0 -58
- data/test/expression/test_tests.rb +0 -99
- data/test/expression/test_to_h.rb +0 -59
- data/test/expression/test_to_s.rb +0 -104
- data/test/expression/test_traverse.rb +0 -161
- data/test/helpers.rb +0 -10
- data/test/lexer/test_all.rb +0 -41
- data/test/lexer/test_keep.rb +0 -24
- data/test/lexer/test_refcalls.rb +0 -56
- data/test/parser/set/test_intersections.rb +0 -127
- data/test/parser/set/test_ranges.rb +0 -111
- data/test/parser/test_all.rb +0 -64
- data/test/parser/test_alternation.rb +0 -92
- data/test/parser/test_conditionals.rb +0 -187
- data/test/parser/test_errors.rb +0 -63
- data/test/parser/test_escapes.rb +0 -134
- data/test/parser/test_free_space.rb +0 -139
- data/test/parser/test_groups.rb +0 -289
- data/test/parser/test_keep.rb +0 -21
- data/test/parser/test_posix_classes.rb +0 -27
- data/test/parser/test_properties.rb +0 -134
- data/test/parser/test_quantifiers.rb +0 -301
- data/test/parser/test_refcalls.rb +0 -186
- data/test/parser/test_sets.rb +0 -179
- data/test/scanner/test_all.rb +0 -38
- data/test/scanner/test_errors.rb +0 -91
- data/test/scanner/test_keep.rb +0 -35
- data/test/scanner/test_types.rb +0 -35
- data/test/scanner/test_unicode_blocks.rb +0 -30
- data/test/support/disable_autotest.rb +0 -8
- data/test/syntax/test_all.rb +0 -6
- data/test/syntax/test_syntax.rb +0 -61
- data/test/syntax/test_syntax_token_map.rb +0 -25
- data/test/syntax/versions/test_1.8.rb +0 -55
- data/test/syntax/versions/test_1.9.1.rb +0 -36
- data/test/syntax/versions/test_1.9.3.rb +0 -32
- data/test/syntax/versions/test_2.0.0.rb +0 -37
- data/test/syntax/versions/test_2.2.0.rb +0 -32
- data/test/syntax/versions/test_aliases.rb +0 -129
- data/test/syntax/versions/test_all.rb +0 -5
- data/test/test_all.rb +0 -5
- data/test/token/test_all.rb +0 -2
- data/test/token/test_token.rb +0 -107
@@ -12,8 +12,8 @@ module Regexp::Expression
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# Override base method to clone the expressions as well.
|
15
|
-
def initialize_clone(
|
16
|
-
|
15
|
+
def initialize_clone(orig)
|
16
|
+
self.expressions = orig.expressions.map(&:clone)
|
17
17
|
super
|
18
18
|
end
|
19
19
|
|
@@ -46,9 +46,7 @@ module Regexp::Expression
|
|
46
46
|
|
47
47
|
def to_s(format = :full)
|
48
48
|
# Note: the format does not get passed down to subexpressions.
|
49
|
-
#
|
50
|
-
# in Expression::Sequence, causing infinite recursion. Clean-up needed.
|
51
|
-
"#{@text}#{expressions.join}#{quantifier_affix(format)}"
|
49
|
+
"#{expressions.join}#{quantifier_affix(format)}"
|
52
50
|
end
|
53
51
|
|
54
52
|
def to_h
|
data/lib/regexp_parser/lexer.rb
CHANGED
@@ -22,6 +22,7 @@ class Regexp::Lexer
|
|
22
22
|
self.nesting = 0
|
23
23
|
self.set_nesting = 0
|
24
24
|
self.conditional_nesting = 0
|
25
|
+
self.shift = 0
|
25
26
|
|
26
27
|
last = nil
|
27
28
|
Regexp::Scanner.scan(input) do |type, token, text, ts, te|
|
@@ -30,11 +31,13 @@ class Regexp::Lexer
|
|
30
31
|
|
31
32
|
ascend(type, token)
|
32
33
|
|
33
|
-
|
34
|
-
last
|
34
|
+
if type == :quantifier and last
|
35
|
+
break_literal(last) if last.type == :literal
|
36
|
+
break_codepoint_list(last) if last.token == :codepoint_list
|
37
|
+
end
|
35
38
|
|
36
|
-
current = Regexp::Token.new(type, token, text, ts, te,
|
37
|
-
|
39
|
+
current = Regexp::Token.new(type, token, text, ts + shift, te + shift,
|
40
|
+
nesting, set_nesting, conditional_nesting)
|
38
41
|
|
39
42
|
current = merge_literal(current) if type == :literal and
|
40
43
|
set_nesting == 0 and
|
@@ -65,7 +68,7 @@ class Regexp::Lexer
|
|
65
68
|
|
66
69
|
private
|
67
70
|
|
68
|
-
attr_accessor :tokens, :nesting, :set_nesting, :conditional_nesting
|
71
|
+
attr_accessor :tokens, :nesting, :set_nesting, :conditional_nesting, :shift
|
69
72
|
|
70
73
|
def ascend(type, token)
|
71
74
|
case type
|
@@ -92,27 +95,31 @@ class Regexp::Lexer
|
|
92
95
|
# called by scan to break a literal run that is longer than one character
|
93
96
|
# into two separate tokens when it is followed by a quantifier
|
94
97
|
def break_literal(token)
|
95
|
-
|
96
|
-
if
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
98
|
+
lead, last, _ = token.text.partition(/.\z/mu)
|
99
|
+
return if lead.empty?
|
100
|
+
|
101
|
+
tokens.pop
|
102
|
+
tokens << Regexp::Token.new(:literal, :literal, lead,
|
103
|
+
token.ts, (token.te - last.bytesize),
|
104
|
+
nesting, set_nesting, conditional_nesting)
|
105
|
+
tokens << Regexp::Token.new(:literal, :literal, last,
|
106
|
+
(token.ts + lead.bytesize), token.te,
|
107
|
+
nesting, set_nesting, conditional_nesting)
|
108
|
+
end
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
-
|
110
|
+
def break_codepoint_list(token)
|
111
|
+
lead, _, tail = token.text.rpartition(' ')
|
112
|
+
return if lead.empty?
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
tokens.pop
|
115
|
+
tokens << Regexp::Token.new(:escape, :codepoint_list, lead + '}',
|
116
|
+
token.ts, (token.te - tail.length),
|
117
|
+
nesting, set_nesting, conditional_nesting)
|
118
|
+
tokens << Regexp::Token.new(:escape, :codepoint_list, '\u{' + tail,
|
119
|
+
(token.ts + lead.length + 1), (token.te + 3),
|
120
|
+
nesting, set_nesting, conditional_nesting)
|
121
|
+
|
122
|
+
self.shift = shift + 3 # one space less, but extra \, u, {, and }
|
116
123
|
end
|
117
124
|
|
118
125
|
# called by scan to merge two consecutive literals. this happens when tokens
|
data/lib/regexp_parser/parser.rb
CHANGED
@@ -39,6 +39,8 @@ class Regexp::Parser
|
|
39
39
|
parse_token(token)
|
40
40
|
end
|
41
41
|
|
42
|
+
assign_referenced_expressions
|
43
|
+
|
42
44
|
if block_given?
|
43
45
|
block.call(root)
|
44
46
|
else
|
@@ -163,14 +165,18 @@ class Regexp::Parser
|
|
163
165
|
node << Backreference::NameCall.new(token, active_opts)
|
164
166
|
when :number, :number_ref
|
165
167
|
node << Backreference::Number.new(token, active_opts)
|
166
|
-
when :number_rel_ref
|
167
|
-
node << Backreference::NumberRelative.new(token, active_opts)
|
168
168
|
when :number_recursion_ref
|
169
169
|
node << Backreference::NumberRecursionLevel.new(token, active_opts)
|
170
170
|
when :number_call
|
171
171
|
node << Backreference::NumberCall.new(token, active_opts)
|
172
|
+
when :number_rel_ref
|
173
|
+
node << Backreference::NumberRelative.new(token, active_opts).tap do |exp|
|
174
|
+
assign_effective_number(exp)
|
175
|
+
end
|
172
176
|
when :number_rel_call
|
173
|
-
node << Backreference::NumberCallRelative.new(token, active_opts)
|
177
|
+
node << Backreference::NumberCallRelative.new(token, active_opts).tap do |exp|
|
178
|
+
assign_effective_number(exp)
|
179
|
+
end
|
174
180
|
else
|
175
181
|
raise UnknownTokenError.new('Backreference', token)
|
176
182
|
end
|
@@ -627,4 +633,20 @@ class Regexp::Parser
|
|
627
633
|
def count_captured_group
|
628
634
|
captured_group_counts[node.level] += 1
|
629
635
|
end
|
636
|
+
|
637
|
+
def assign_effective_number(exp)
|
638
|
+
exp.effective_number =
|
639
|
+
exp.number + total_captured_group_count + (exp.number < 0 ? 1 : 0)
|
640
|
+
end
|
641
|
+
|
642
|
+
def assign_referenced_expressions
|
643
|
+
targets = {}
|
644
|
+
root.each_expression do |exp|
|
645
|
+
exp.is_a?(Group::Capture) && targets[exp.identifier] = exp
|
646
|
+
end
|
647
|
+
root.each_expression do |exp|
|
648
|
+
exp.respond_to?(:reference) &&
|
649
|
+
exp.referenced_expression = targets[exp.reference]
|
650
|
+
end
|
651
|
+
end
|
630
652
|
end # module Regexp::Parser
|
@@ -39,15 +39,7 @@ require 'regexp_parser/syntax/tokens/unicode_property'
|
|
39
39
|
# into the All and Types constants.
|
40
40
|
module Regexp::Syntax
|
41
41
|
module Token
|
42
|
-
|
43
|
-
|
44
|
-
else
|
45
|
-
All = Map.map {|k,v| v}.flatten.uniq
|
46
|
-
end
|
47
|
-
|
48
|
-
Types = Map.keys
|
49
|
-
|
50
|
-
All.freeze
|
51
|
-
Types.freeze
|
42
|
+
All = Map.values.flatten.uniq.sort.freeze
|
43
|
+
Types = Map.keys.freeze
|
52
44
|
end
|
53
45
|
end
|
data/regexp_parser.gemspec
CHANGED
@@ -21,12 +21,12 @@ Gem::Specification.new do |gem|
|
|
21
21
|
|
22
22
|
gem.require_paths = ['lib']
|
23
23
|
|
24
|
-
gem.files = Dir.glob('{lib,
|
24
|
+
gem.files = Dir.glob('{lib,spec}/**/*.rb') +
|
25
25
|
Dir.glob('lib/**/*.rl') +
|
26
26
|
Dir.glob('lib/**/*.yml') +
|
27
27
|
%w(Gemfile Rakefile LICENSE README.md CHANGELOG.md regexp_parser.gemspec)
|
28
28
|
|
29
|
-
gem.test_files = Dir.glob('
|
29
|
+
gem.test_files = Dir.glob('spec/**/*.rb')
|
30
30
|
|
31
31
|
gem.rdoc_options = ["--inline-source", "--charset=UTF-8"]
|
32
32
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe(Regexp::Expression::Base) do
|
4
|
+
specify('#to_re') do
|
5
|
+
re_text = '^a*(b([cde]+))+f?$'
|
6
|
+
|
7
|
+
re = RP.parse(re_text).to_re
|
8
|
+
|
9
|
+
expect(re).to be_a(::Regexp)
|
10
|
+
expect(re_text).to eq re.source
|
11
|
+
end
|
12
|
+
|
13
|
+
specify('#level') do
|
14
|
+
regexp = /^a(b(c(d)))e$/
|
15
|
+
root = RP.parse(regexp)
|
16
|
+
|
17
|
+
['^', 'a', '(b(c(d)))', 'e', '$'].each_with_index do |t, i|
|
18
|
+
expect(root[i].to_s).to eq t
|
19
|
+
expect(root[i].level).to eq 0
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(root[2][0].to_s).to eq 'b'
|
23
|
+
expect(root[2][0].level).to eq 1
|
24
|
+
|
25
|
+
expect(root[2][1][0].to_s).to eq 'c'
|
26
|
+
expect(root[2][1][0].level).to eq 2
|
27
|
+
|
28
|
+
expect(root[2][1][1][0].to_s).to eq 'd'
|
29
|
+
expect(root[2][1][1][0].level).to eq 3
|
30
|
+
end
|
31
|
+
|
32
|
+
specify('#terminal?') do
|
33
|
+
root = RP.parse('^a([b]+)c$')
|
34
|
+
|
35
|
+
expect(root).not_to be_terminal
|
36
|
+
|
37
|
+
expect(root[0]).to be_terminal
|
38
|
+
expect(root[1]).to be_terminal
|
39
|
+
expect(root[2]).not_to be_terminal
|
40
|
+
expect(root[2][0]).not_to be_terminal
|
41
|
+
expect(root[2][0][0]).to be_terminal
|
42
|
+
expect(root[3]).to be_terminal
|
43
|
+
expect(root[4]).to be_terminal
|
44
|
+
end
|
45
|
+
|
46
|
+
specify('alt #terminal?') do
|
47
|
+
root = RP.parse('^(ab|cd)$')
|
48
|
+
|
49
|
+
expect(root).not_to be_terminal
|
50
|
+
|
51
|
+
expect(root[0]).to be_terminal
|
52
|
+
expect(root[1]).not_to be_terminal
|
53
|
+
expect(root[1][0]).not_to be_terminal
|
54
|
+
expect(root[1][0][0]).not_to be_terminal
|
55
|
+
expect(root[1][0][0][0]).to be_terminal
|
56
|
+
expect(root[1][0][1]).not_to be_terminal
|
57
|
+
expect(root[1][0][1][0]).to be_terminal
|
58
|
+
end
|
59
|
+
|
60
|
+
specify('#coded_offset') do
|
61
|
+
root = RP.parse('^a*(b+(c?))$')
|
62
|
+
|
63
|
+
expect(root.coded_offset).to eq '@0+12'
|
64
|
+
|
65
|
+
[
|
66
|
+
['@0+1', '^'],
|
67
|
+
['@1+2', 'a*'],
|
68
|
+
['@3+8', '(b+(c?))'],
|
69
|
+
['@11+1', '$'],
|
70
|
+
].each_with_index do |check, i|
|
71
|
+
against = [root[i].coded_offset, root[i].to_s]
|
72
|
+
|
73
|
+
expect(against).to eq check
|
74
|
+
end
|
75
|
+
|
76
|
+
expect([root[2][0].coded_offset, root[2][0].to_s]).to eq ['@4+2', 'b+']
|
77
|
+
expect([root[2][1].coded_offset, root[2][1].to_s]).to eq ['@6+4', '(c?)']
|
78
|
+
expect([root[2][1][0].coded_offset, root[2][1][0].to_s]).to eq ['@7+2', 'c?']
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe('Expression#clone') do
|
4
|
+
specify('Base#clone') do
|
5
|
+
root = RP.parse(/^(?i:a)b+$/i)
|
6
|
+
copy = root.clone
|
7
|
+
|
8
|
+
expect(copy.to_s).to eq root.to_s
|
9
|
+
|
10
|
+
expect(root.object_id).not_to eq copy.object_id
|
11
|
+
expect(root.text).to eq copy.text
|
12
|
+
expect(root.text.object_id).not_to eq copy.text.object_id
|
13
|
+
|
14
|
+
root_1 = root[1]
|
15
|
+
copy_1 = copy[1]
|
16
|
+
|
17
|
+
expect(root_1.options).to eq copy_1.options
|
18
|
+
expect(root_1.options.object_id).not_to eq copy_1.options.object_id
|
19
|
+
|
20
|
+
root_2 = root[2]
|
21
|
+
copy_2 = copy[2]
|
22
|
+
|
23
|
+
expect(root_2).to be_quantified
|
24
|
+
expect(copy_2).to be_quantified
|
25
|
+
expect(root_2.quantifier.text).to eq copy_2.quantifier.text
|
26
|
+
expect(root_2.quantifier.text.object_id).not_to eq copy_2.quantifier.text.object_id
|
27
|
+
expect(root_2.quantifier.object_id).not_to eq copy_2.quantifier.object_id
|
28
|
+
|
29
|
+
# regression test
|
30
|
+
expect { root_2.clone }.not_to change { root_2.quantifier.object_id }
|
31
|
+
expect { root_2.clone }.not_to change { root_2.quantifier.text.object_id }
|
32
|
+
end
|
33
|
+
|
34
|
+
specify('Subexpression#clone') do
|
35
|
+
root = RP.parse(/^a(b([cde])f)g$/)
|
36
|
+
copy = root.clone
|
37
|
+
|
38
|
+
expect(copy.to_s).to eq root.to_s
|
39
|
+
|
40
|
+
expect(root).to respond_to(:expressions)
|
41
|
+
expect(copy).to respond_to(:expressions)
|
42
|
+
expect(root.expressions.object_id).not_to eq copy.expressions.object_id
|
43
|
+
copy.expressions.each_with_index do |exp, index|
|
44
|
+
expect(root[index].object_id).not_to eq exp.object_id
|
45
|
+
end
|
46
|
+
copy[2].each_with_index do |exp, index|
|
47
|
+
expect(root[2][index].object_id).not_to eq exp.object_id
|
48
|
+
end
|
49
|
+
|
50
|
+
# regression test
|
51
|
+
expect { root.clone }.not_to change { root.expressions.object_id }
|
52
|
+
end
|
53
|
+
|
54
|
+
specify('Group::Named#clone') do
|
55
|
+
root = RP.parse('^(?<somename>a)+bc$')
|
56
|
+
copy = root.clone
|
57
|
+
|
58
|
+
expect(copy.to_s).to eq root.to_s
|
59
|
+
|
60
|
+
root_1 = root[1]
|
61
|
+
copy_1 = copy[1]
|
62
|
+
|
63
|
+
expect(root_1.name).to eq copy_1.name
|
64
|
+
expect(root_1.name.object_id).not_to eq copy_1.name.object_id
|
65
|
+
expect(root_1.text).to eq copy_1.text
|
66
|
+
expect(root_1.expressions.object_id).not_to eq copy_1.expressions.object_id
|
67
|
+
copy_1.expressions.each_with_index do |exp, index|
|
68
|
+
expect(root_1[index].object_id).not_to eq exp.object_id
|
69
|
+
end
|
70
|
+
|
71
|
+
# regression test
|
72
|
+
expect { root_1.clone }.not_to change { root_1.name.object_id }
|
73
|
+
end
|
74
|
+
|
75
|
+
specify('Sequence#clone') do
|
76
|
+
root = RP.parse(/(a|b)/)
|
77
|
+
copy = root.clone
|
78
|
+
|
79
|
+
# regression test
|
80
|
+
expect(copy.to_s).to eq root.to_s
|
81
|
+
|
82
|
+
root_seq_op = root[0][0]
|
83
|
+
copy_seq_op = copy[0][0]
|
84
|
+
root_seq_1 = root[0][0][0]
|
85
|
+
copy_seq_1 = copy[0][0][0]
|
86
|
+
|
87
|
+
expect(root_seq_op.object_id).not_to eq copy_seq_op.object_id
|
88
|
+
expect(root_seq_1.object_id).not_to eq copy_seq_1.object_id
|
89
|
+
copy_seq_1.expressions.each_with_index do |exp, index|
|
90
|
+
expect(root_seq_1[index].object_id).not_to eq exp.object_id
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe('Base#unquantified_clone') do
|
95
|
+
it 'produces a clone' do
|
96
|
+
root = RP.parse(/^a(b([cde])f)g$/)
|
97
|
+
copy = root.unquantified_clone
|
98
|
+
|
99
|
+
expect(copy.to_s).to eq root.to_s
|
100
|
+
|
101
|
+
expect(copy.object_id).not_to eq root.object_id
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'does not carry over the callee quantifier' do
|
105
|
+
expect(RP.parse(/a{3}/)[0]).to be_quantified
|
106
|
+
expect(RP.parse(/a{3}/)[0].unquantified_clone).not_to be_quantified
|
107
|
+
|
108
|
+
expect(RP.parse(/[a]{3}/)[0]).to be_quantified
|
109
|
+
expect(RP.parse(/[a]{3}/)[0].unquantified_clone).not_to be_quantified
|
110
|
+
|
111
|
+
expect(RP.parse(/(a|b){3}/)[0]).to be_quantified
|
112
|
+
expect(RP.parse(/(a|b){3}/)[0].unquantified_clone).not_to be_quantified
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'keeps quantifiers of callee children' do
|
116
|
+
expect(RP.parse(/(a{3}){3}/)[0][0]).to be_quantified
|
117
|
+
expect(RP.parse(/(a{3}){3}/)[0].unquantified_clone[0]).to be_quantified
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe(Regexp::Expression::Conditional) do
|
4
|
+
let(:root) { RP.parse('^(a(b))(b(?(1)c|(?(2)d|(?(3)e|f)))g)$') }
|
5
|
+
let(:cond_1) { root[2][1] }
|
6
|
+
let(:cond_2) { root[2][1][2][0] }
|
7
|
+
let(:cond_3) { root[2][1][2][0][2][0] }
|
8
|
+
|
9
|
+
specify('root level') do
|
10
|
+
[
|
11
|
+
'^',
|
12
|
+
'(a(b))',
|
13
|
+
'(b(?(1)c|(?(2)d|(?(3)e|f)))g)',
|
14
|
+
'$'
|
15
|
+
].each_with_index do |t, i|
|
16
|
+
expect(root[i].conditional_level).to eq 0
|
17
|
+
expect(root[i].to_s).to eq t
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(root[2][0].to_s).to eq 'b'
|
21
|
+
expect(root[2][0].conditional_level).to eq 0
|
22
|
+
end
|
23
|
+
|
24
|
+
specify('level one') do
|
25
|
+
condition = cond_1.condition
|
26
|
+
branch_1 = cond_1.branches.first
|
27
|
+
|
28
|
+
expect(condition).to be_a Conditional::Condition
|
29
|
+
expect(condition.to_s).to eq '(1)'
|
30
|
+
expect(condition.conditional_level).to eq 1
|
31
|
+
|
32
|
+
expect(branch_1).to be_a Conditional::Branch
|
33
|
+
expect(branch_1.to_s).to eq 'c'
|
34
|
+
expect(branch_1.conditional_level).to eq 1
|
35
|
+
|
36
|
+
expect(branch_1.first.to_s).to eq 'c'
|
37
|
+
expect(branch_1.first.conditional_level).to eq 1
|
38
|
+
end
|
39
|
+
|
40
|
+
specify('level two') do
|
41
|
+
condition = cond_2.condition
|
42
|
+
branch_1 = cond_2.branches.first
|
43
|
+
branch_2 = cond_2.branches.last
|
44
|
+
|
45
|
+
expect(cond_2.to_s).to start_with '(?'
|
46
|
+
expect(cond_2.conditional_level).to eq 1
|
47
|
+
|
48
|
+
expect(condition).to be_a Conditional::Condition
|
49
|
+
expect(condition.to_s).to eq '(2)'
|
50
|
+
expect(condition.conditional_level).to eq 2
|
51
|
+
|
52
|
+
expect(branch_1).to be_a Conditional::Branch
|
53
|
+
expect(branch_1.to_s).to eq 'd'
|
54
|
+
expect(branch_1.conditional_level).to eq 2
|
55
|
+
|
56
|
+
expect(branch_1.first.to_s).to eq 'd'
|
57
|
+
expect(branch_1.first.conditional_level).to eq 2
|
58
|
+
|
59
|
+
expect(branch_2.first.to_s).to start_with '(?'
|
60
|
+
expect(branch_2.first.conditional_level).to eq 2
|
61
|
+
end
|
62
|
+
|
63
|
+
specify('level three') do
|
64
|
+
condition = cond_3.condition
|
65
|
+
branch_1 = cond_3.branches.first
|
66
|
+
branch_2 = cond_3.branches.last
|
67
|
+
|
68
|
+
expect(condition).to be_a Conditional::Condition
|
69
|
+
expect(condition.to_s).to eq '(3)'
|
70
|
+
expect(condition.conditional_level).to eq 3
|
71
|
+
|
72
|
+
expect(cond_3.to_s).to eq '(?(3)e|f)'
|
73
|
+
expect(cond_3.conditional_level).to eq 2
|
74
|
+
|
75
|
+
expect(branch_1).to be_a Conditional::Branch
|
76
|
+
expect(branch_1.to_s).to eq 'e'
|
77
|
+
expect(branch_1.conditional_level).to eq 3
|
78
|
+
|
79
|
+
expect(branch_1.first.to_s).to eq 'e'
|
80
|
+
expect(branch_1.first.conditional_level).to eq 3
|
81
|
+
|
82
|
+
expect(branch_2).to be_a Conditional::Branch
|
83
|
+
expect(branch_2.to_s).to eq 'f'
|
84
|
+
expect(branch_2.conditional_level).to eq 3
|
85
|
+
|
86
|
+
expect(branch_2.first.to_s).to eq 'f'
|
87
|
+
expect(branch_2.first.conditional_level).to eq 3
|
88
|
+
end
|
89
|
+
end
|