regexp_parser 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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