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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/Gemfile +1 -1
  4. data/README.md +9 -13
  5. data/lib/regexp_parser/expression.rb +33 -21
  6. data/lib/regexp_parser/expression/classes/backref.rb +18 -10
  7. data/lib/regexp_parser/expression/classes/conditional.rb +4 -0
  8. data/lib/regexp_parser/expression/classes/group.rb +4 -2
  9. data/lib/regexp_parser/expression/classes/keep.rb +1 -3
  10. data/lib/regexp_parser/expression/methods/match_length.rb +172 -0
  11. data/lib/regexp_parser/expression/quantifier.rb +2 -2
  12. data/lib/regexp_parser/expression/sequence.rb +0 -4
  13. data/lib/regexp_parser/expression/subexpression.rb +3 -5
  14. data/lib/regexp_parser/lexer.rb +31 -24
  15. data/lib/regexp_parser/parser.rb +25 -3
  16. data/lib/regexp_parser/syntax/tokens.rb +2 -10
  17. data/lib/regexp_parser/version.rb +1 -1
  18. data/regexp_parser.gemspec +2 -2
  19. data/spec/expression/base_spec.rb +80 -0
  20. data/spec/expression/clone_spec.rb +120 -0
  21. data/spec/expression/conditional_spec.rb +89 -0
  22. data/spec/expression/free_space_spec.rb +27 -0
  23. data/spec/expression/methods/match_length_spec.rb +141 -0
  24. data/spec/expression/methods/strfregexp_spec.rb +224 -0
  25. data/spec/expression/methods/tests_spec.rb +97 -0
  26. data/spec/expression/methods/traverse_spec.rb +140 -0
  27. data/spec/expression/subexpression_spec.rb +50 -0
  28. data/spec/expression/to_h_spec.rb +26 -0
  29. data/spec/expression/to_s_spec.rb +100 -0
  30. data/spec/lexer/all_spec.rb +22 -0
  31. data/{test/lexer/test_conditionals.rb → spec/lexer/conditionals_spec.rb} +31 -35
  32. data/spec/lexer/escapes_spec.rb +38 -0
  33. data/spec/lexer/keep_spec.rb +22 -0
  34. data/{test/lexer/test_literals.rb → spec/lexer/literals_spec.rb} +20 -24
  35. data/{test/lexer/test_nesting.rb → spec/lexer/nesting_spec.rb} +11 -13
  36. data/spec/lexer/refcalls_spec.rb +54 -0
  37. data/spec/parser/all_spec.rb +31 -0
  38. data/spec/parser/alternation_spec.rb +88 -0
  39. data/{test/parser/test_anchors.rb → spec/parser/anchors_spec.rb} +7 -10
  40. data/spec/parser/conditionals_spec.rb +179 -0
  41. data/spec/parser/errors_spec.rb +51 -0
  42. data/spec/parser/escapes_spec.rb +132 -0
  43. data/spec/parser/free_space_spec.rb +130 -0
  44. data/spec/parser/groups_spec.rb +267 -0
  45. data/spec/parser/keep_spec.rb +19 -0
  46. data/spec/parser/posix_classes_spec.rb +27 -0
  47. data/spec/parser/properties_spec.rb +127 -0
  48. data/spec/parser/quantifiers_spec.rb +293 -0
  49. data/spec/parser/refcalls_spec.rb +237 -0
  50. data/spec/parser/set/intersections_spec.rb +127 -0
  51. data/spec/parser/set/ranges_spec.rb +111 -0
  52. data/spec/parser/sets_spec.rb +178 -0
  53. data/{test/parser/test_types.rb → spec/parser/types_spec.rb} +13 -20
  54. data/spec/scanner/all_spec.rb +18 -0
  55. data/{test/scanner/test_anchors.rb → spec/scanner/anchors_spec.rb} +8 -10
  56. data/{test/scanner/test_conditionals.rb → spec/scanner/conditionals_spec.rb} +49 -53
  57. data/spec/scanner/errors_spec.rb +90 -0
  58. data/{test/scanner/test_escapes.rb → spec/scanner/escapes_spec.rb} +8 -10
  59. data/{test/scanner/test_free_space.rb → spec/scanner/free_space_spec.rb} +48 -52
  60. data/{test/scanner/test_groups.rb → spec/scanner/groups_spec.rb} +33 -41
  61. data/spec/scanner/keep_spec.rb +33 -0
  62. data/{test/scanner/test_literals.rb → spec/scanner/literals_spec.rb} +8 -12
  63. data/{test/scanner/test_meta.rb → spec/scanner/meta_spec.rb} +8 -10
  64. data/{test/scanner/test_properties.rb → spec/scanner/properties_spec.rb} +14 -19
  65. data/{test/scanner/test_quantifiers.rb → spec/scanner/quantifiers_spec.rb} +7 -9
  66. data/{test/scanner/test_refcalls.rb → spec/scanner/refcalls_spec.rb} +9 -9
  67. data/{test/scanner/test_scripts.rb → spec/scanner/scripts_spec.rb} +8 -12
  68. data/{test/scanner/test_sets.rb → spec/scanner/sets_spec.rb} +14 -17
  69. data/spec/scanner/types_spec.rb +29 -0
  70. data/spec/scanner/unicode_blocks_spec.rb +28 -0
  71. data/spec/spec_helper.rb +14 -0
  72. data/{test → spec}/support/runner.rb +9 -8
  73. data/{test → spec}/support/warning_extractor.rb +5 -7
  74. data/spec/syntax/syntax_spec.rb +44 -0
  75. data/spec/syntax/syntax_token_map_spec.rb +23 -0
  76. data/spec/syntax/versions/1.8.6_spec.rb +38 -0
  77. data/spec/syntax/versions/1.9.1_spec.rb +23 -0
  78. data/spec/syntax/versions/1.9.3_spec.rb +22 -0
  79. data/spec/syntax/versions/2.0.0_spec.rb +28 -0
  80. data/spec/syntax/versions/2.2.0_spec.rb +22 -0
  81. data/spec/syntax/versions/aliases_spec.rb +119 -0
  82. data/spec/token/token_spec.rb +85 -0
  83. metadata +131 -140
  84. data/test/expression/test_all.rb +0 -12
  85. data/test/expression/test_base.rb +0 -90
  86. data/test/expression/test_clone.rb +0 -89
  87. data/test/expression/test_conditionals.rb +0 -113
  88. data/test/expression/test_free_space.rb +0 -35
  89. data/test/expression/test_set.rb +0 -84
  90. data/test/expression/test_strfregexp.rb +0 -230
  91. data/test/expression/test_subexpression.rb +0 -58
  92. data/test/expression/test_tests.rb +0 -99
  93. data/test/expression/test_to_h.rb +0 -59
  94. data/test/expression/test_to_s.rb +0 -104
  95. data/test/expression/test_traverse.rb +0 -161
  96. data/test/helpers.rb +0 -10
  97. data/test/lexer/test_all.rb +0 -41
  98. data/test/lexer/test_keep.rb +0 -24
  99. data/test/lexer/test_refcalls.rb +0 -56
  100. data/test/parser/set/test_intersections.rb +0 -127
  101. data/test/parser/set/test_ranges.rb +0 -111
  102. data/test/parser/test_all.rb +0 -64
  103. data/test/parser/test_alternation.rb +0 -92
  104. data/test/parser/test_conditionals.rb +0 -187
  105. data/test/parser/test_errors.rb +0 -63
  106. data/test/parser/test_escapes.rb +0 -134
  107. data/test/parser/test_free_space.rb +0 -139
  108. data/test/parser/test_groups.rb +0 -289
  109. data/test/parser/test_keep.rb +0 -21
  110. data/test/parser/test_posix_classes.rb +0 -27
  111. data/test/parser/test_properties.rb +0 -134
  112. data/test/parser/test_quantifiers.rb +0 -301
  113. data/test/parser/test_refcalls.rb +0 -186
  114. data/test/parser/test_sets.rb +0 -179
  115. data/test/scanner/test_all.rb +0 -38
  116. data/test/scanner/test_errors.rb +0 -91
  117. data/test/scanner/test_keep.rb +0 -35
  118. data/test/scanner/test_types.rb +0 -35
  119. data/test/scanner/test_unicode_blocks.rb +0 -30
  120. data/test/support/disable_autotest.rb +0 -8
  121. data/test/syntax/test_all.rb +0 -6
  122. data/test/syntax/test_syntax.rb +0 -61
  123. data/test/syntax/test_syntax_token_map.rb +0 -25
  124. data/test/syntax/versions/test_1.8.rb +0 -55
  125. data/test/syntax/versions/test_1.9.1.rb +0 -36
  126. data/test/syntax/versions/test_1.9.3.rb +0 -32
  127. data/test/syntax/versions/test_2.0.0.rb +0 -37
  128. data/test/syntax/versions/test_2.2.0.rb +0 -32
  129. data/test/syntax/versions/test_aliases.rb +0 -129
  130. data/test/syntax/versions/test_all.rb +0 -5
  131. data/test/test_all.rb +0 -5
  132. data/test/token/test_all.rb +0 -2
  133. data/test/token/test_token.rb +0 -107
@@ -12,8 +12,8 @@ module Regexp::Expression
12
12
  @max = max
13
13
  end
14
14
 
15
- def initialize_clone(other)
16
- other.instance_variable_set(:@text, text.dup)
15
+ def initialize_clone(orig)
16
+ @text = orig.text.dup
17
17
  super
18
18
  end
19
19
 
@@ -44,10 +44,6 @@ module Regexp::Expression
44
44
  end
45
45
  end
46
46
 
47
- def text
48
- to_s
49
- end
50
-
51
47
  def starts_at
52
48
  expressions.first.starts_at
53
49
  end
@@ -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(other)
16
- other.expressions = expressions.map(&:clone)
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
- # Note: cant use #text accessor, b/c it is overriden as def text; to_s end
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
@@ -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
- break_literal(last) if type == :quantifier and
34
- last and last.type == :literal
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
- nesting, set_nesting, conditional_nesting)
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
- text = token.text
96
- if text.scan(/./mu).length > 1
97
- lead = text.sub(/.\z/mu, "")
98
- last = text[/.\z/mu] || ''
99
-
100
- if RUBY_VERSION >= '1.9'
101
- lead_length = lead.bytesize
102
- last_length = last.bytesize
103
- else
104
- lead_length = lead.length
105
- last_length = last.length
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
- tokens.pop
109
- tokens << Regexp::Token.new(:literal, :literal, lead, token.ts,
110
- (token.te - last_length), nesting, set_nesting, conditional_nesting)
110
+ def break_codepoint_list(token)
111
+ lead, _, tail = token.text.rpartition(' ')
112
+ return if lead.empty?
111
113
 
112
- tokens << Regexp::Token.new(:literal, :literal, last,
113
- (token.ts + lead_length),
114
- token.te, nesting, set_nesting, conditional_nesting)
115
- end
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
@@ -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
- if RUBY_VERSION >= '1.9'
43
- All = Map.map {|k,v| v}.flatten.uniq.sort
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
@@ -1,5 +1,5 @@
1
1
  class Regexp
2
2
  class Parser
3
- VERSION = '1.4.0'
3
+ VERSION = '1.5.0'
4
4
  end
5
5
  end
@@ -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,test}/**/*.rb') +
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('test/**/*.rb')
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