Skip to content

Commit e730409

Browse files
committed
Add referenced_expressions
1 parent 79a351d commit e730409

File tree

8 files changed

+53
-40
lines changed

8 files changed

+53
-40
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- `#referenced_expressions`
13+
- like `#referenced_expression`, but for multiplexing backrefs
14+
- returns the `Group` expressions that are being referenced
15+
1016
### Fixed
1117

1218
- fixed `#char` & `#codepoint` errors for single-digit hex escapes

lib/regexp_parser/expression.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
require_relative 'expression/methods/options'
3535
require_relative 'expression/methods/parts'
3636
require_relative 'expression/methods/printing'
37+
require_relative 'expression/methods/referenced_expressions'
3738
require_relative 'expression/methods/strfregexp'
3839
require_relative 'expression/methods/tests'
3940
require_relative 'expression/methods/traverse'

lib/regexp_parser/expression/classes/backreference.rb

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,6 @@
11
module Regexp::Expression
22
module Backreference
3-
class Base < Regexp::Expression::Base
4-
attr_accessor :referenced_expression
5-
6-
def initialize_copy(orig)
7-
exp_id = [self.class, self.starts_at]
8-
9-
# prevent infinite recursion for recursive subexp calls
10-
copied = @@copied ||= {}
11-
self.referenced_expression =
12-
if copied[exp_id]
13-
orig.referenced_expression
14-
else
15-
copied[exp_id] = true
16-
orig.referenced_expression.dup
17-
end
18-
copied.clear
19-
20-
super
21-
end
22-
end
3+
class Base < Regexp::Expression::Base; end
234

245
class Number < Backreference::Base
256
attr_reader :number

lib/regexp_parser/expression/classes/conditional.rb

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,17 @@ def initialize
77
end
88

99
class Condition < Regexp::Expression::Base
10-
attr_accessor :referenced_expression
11-
1210
# Name or number of the referenced capturing group that determines state.
1311
# Returns a String if reference is by name, Integer if by number.
1412
def reference
1513
ref = text.tr("'<>()", "")
1614
ref =~ /\D/ ? ref : Integer(ref)
1715
end
18-
19-
def initialize_copy(orig)
20-
self.referenced_expression = orig.referenced_expression.dup
21-
super
22-
end
2316
end
2417

2518
class Branch < Regexp::Expression::Sequence; end
2619

2720
class Expression < Regexp::Expression::Subexpression
28-
attr_accessor :referenced_expression
29-
3021
def <<(exp)
3122
expressions.last << exp
3223
end
@@ -54,11 +45,6 @@ def branches
5445
def reference
5546
condition.reference
5647
end
57-
58-
def initialize_copy(orig)
59-
self.referenced_expression = orig.referenced_expression.dup
60-
super
61-
end
6248
end
6349
end
6450
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Regexp::Expression
2+
module ReferencedExpressions
3+
attr_accessor :referenced_expressions
4+
5+
def referenced_expression
6+
referenced_expressions && referenced_expressions.first
7+
end
8+
9+
def initialize_copy(orig)
10+
exp_id = [self.class, self.starts_at]
11+
12+
# prevent infinite recursion for recursive subexp calls
13+
copied = self.class.instance_eval { @copied_ref_exps ||= {} }
14+
self.referenced_expressions =
15+
if copied[exp_id]
16+
orig.referenced_expressions
17+
else
18+
copied[exp_id] = true
19+
orig.referenced_expressions && orig.referenced_expressions.map(&:dup)
20+
end
21+
copied.clear
22+
23+
super
24+
end
25+
end
26+
27+
Base.include ReferencedExpressions
28+
end

lib/regexp_parser/parser.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,16 +580,19 @@ def active_opts
580580
# the instance of Group::Capture that it refers to via its number.
581581
def assign_referenced_expressions
582582
# find all referenceable and referring expressions
583-
targets = { 0 => root }
583+
targets = { 0 => [root] }
584584
referrers = []
585585
root.each_expression do |exp|
586-
exp.is_a?(Group::Capture) && targets[exp.identifier] = exp
587-
referrers << exp if exp.referential?
586+
if exp.referential?
587+
referrers << exp
588+
elsif exp.is_a?(Group::Capture)
589+
(targets[exp.identifier] ||= []) << exp
590+
end
588591
end
589-
# assign reference expression to referring expressions
592+
# assign referenced expressions to referring expressions
590593
# (in a second iteration because there might be forward references)
591594
referrers.each do |exp|
592-
exp.referenced_expression = targets[exp.reference] ||
595+
exp.referenced_expressions = targets[exp.reference] ||
593596
raise(ParserError, "Invalid reference #{exp.reference} at pos #{exp.ts}")
594597
end
595598
end

spec/expression/methods/match_length_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
specify('raises for missing references') do
3434
exp = RP.parse(/(a)\1/).last
35-
exp.referenced_expression = nil
35+
exp.referenced_expressions = nil
3636
expect { exp.match_length }.to raise_error(ArgumentError)
3737
end
3838

spec/parser/refcalls_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@
5959
expect(exp3.referenced_expression.to_s).to eq '(ghi)'
6060
end
6161

62+
specify('parse backref referenced_expressions (multiplex)') do
63+
root = RP.parse('(?<a>A)(?<a>B)\\k<a>')
64+
exp = root.last
65+
66+
expect(exp.referenced_expressions).to eq [root[0], root[1]]
67+
expect(exp.referenced_expressions.map(&:to_s)).to eq ['(?<a>A)', '(?<a>B)']
68+
end
69+
6270
specify('parse backref call referenced_expression') do
6371
root = RP.parse('\\g<+1>(abc)\\g<+2>(def)(ghi)\\g<-2>')
6472
exp1 = root[0]

0 commit comments

Comments
 (0)