regexp_parser 2.7.0 → 2.12.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/Gemfile +7 -5
- data/LICENSE +1 -1
- data/Rakefile +5 -3
- data/lib/regexp_parser/error.rb +2 -0
- data/lib/regexp_parser/expression/base.rb +2 -7
- data/lib/regexp_parser/expression/classes/alternation.rb +3 -1
- data/lib/regexp_parser/expression/classes/anchor.rb +2 -0
- data/lib/regexp_parser/expression/classes/backreference.rb +7 -26
- data/lib/regexp_parser/expression/classes/character_set/intersection.rb +2 -0
- data/lib/regexp_parser/expression/classes/character_set/range.rb +4 -7
- data/lib/regexp_parser/expression/classes/character_set.rb +6 -8
- data/lib/regexp_parser/expression/classes/character_type.rb +2 -0
- data/lib/regexp_parser/expression/classes/conditional.rb +4 -28
- data/lib/regexp_parser/expression/classes/escape_sequence.rb +29 -96
- data/lib/regexp_parser/expression/classes/free_space.rb +5 -1
- data/lib/regexp_parser/expression/classes/group.rb +2 -22
- data/lib/regexp_parser/expression/classes/keep.rb +3 -1
- data/lib/regexp_parser/expression/classes/literal.rb +2 -0
- data/lib/regexp_parser/expression/classes/posix_class.rb +7 -5
- data/lib/regexp_parser/expression/classes/root.rb +2 -0
- data/lib/regexp_parser/expression/classes/unicode_property.rb +13 -11
- data/lib/regexp_parser/expression/methods/construct.rb +4 -4
- data/lib/regexp_parser/expression/methods/escape_sequence_char.rb +7 -0
- data/lib/regexp_parser/expression/methods/escape_sequence_codepoint.rb +76 -0
- data/lib/regexp_parser/expression/methods/human_name.rb +2 -0
- data/lib/regexp_parser/expression/methods/match.rb +2 -0
- data/lib/regexp_parser/expression/methods/match_length.rb +2 -0
- data/lib/regexp_parser/expression/methods/negative.rb +22 -0
- data/lib/regexp_parser/expression/methods/options.rb +2 -0
- data/lib/regexp_parser/expression/methods/parts.rb +25 -0
- data/lib/regexp_parser/expression/methods/printing.rb +28 -0
- data/lib/regexp_parser/expression/methods/referenced_expressions.rb +30 -0
- data/lib/regexp_parser/expression/methods/strfregexp.rb +2 -0
- data/lib/regexp_parser/expression/methods/tests.rb +42 -3
- data/lib/regexp_parser/expression/methods/traverse.rb +35 -20
- data/lib/regexp_parser/expression/quantifier.rb +33 -18
- data/lib/regexp_parser/expression/sequence.rb +7 -9
- data/lib/regexp_parser/expression/sequence_operation.rb +6 -9
- data/lib/regexp_parser/expression/shared.rb +40 -24
- data/lib/regexp_parser/expression/subexpression.rb +22 -18
- data/lib/regexp_parser/expression.rb +39 -31
- data/lib/regexp_parser/lexer.rb +17 -7
- data/lib/regexp_parser/parser.rb +101 -95
- data/lib/regexp_parser/scanner/errors/premature_end_error.rb +10 -0
- data/lib/regexp_parser/scanner/errors/scanner_error.rb +8 -0
- data/lib/regexp_parser/scanner/errors/validation_error.rb +65 -0
- data/lib/regexp_parser/scanner/properties/long.csv +61 -0
- data/lib/regexp_parser/scanner/properties/short.csv +15 -0
- data/lib/regexp_parser/scanner/property.rl +1 -1
- data/lib/regexp_parser/scanner/scanner.rl +90 -140
- data/lib/regexp_parser/scanner.rb +1336 -1450
- data/lib/regexp_parser/syntax/any.rb +2 -0
- data/lib/regexp_parser/syntax/base.rb +2 -0
- data/lib/regexp_parser/syntax/token/anchor.rb +5 -3
- data/lib/regexp_parser/syntax/token/assertion.rb +4 -2
- data/lib/regexp_parser/syntax/token/backreference.rb +11 -6
- data/lib/regexp_parser/syntax/token/character_set.rb +6 -1
- data/lib/regexp_parser/syntax/token/character_type.rb +6 -4
- data/lib/regexp_parser/syntax/token/conditional.rb +5 -3
- data/lib/regexp_parser/syntax/token/escape.rb +12 -8
- data/lib/regexp_parser/syntax/token/group.rb +8 -6
- data/lib/regexp_parser/syntax/token/keep.rb +3 -1
- data/lib/regexp_parser/syntax/token/meta.rb +11 -2
- data/lib/regexp_parser/syntax/token/posix_class.rb +4 -2
- data/lib/regexp_parser/syntax/token/quantifier.rb +8 -6
- data/lib/regexp_parser/syntax/token/unicode_property.rb +164 -70
- data/lib/regexp_parser/syntax/token/virtual.rb +13 -0
- data/lib/regexp_parser/syntax/token.rb +18 -16
- data/lib/regexp_parser/syntax/version_lookup.rb +4 -2
- data/lib/regexp_parser/syntax/versions/1.8.6.rb +2 -0
- data/lib/regexp_parser/syntax/versions/1.9.1.rb +2 -0
- data/lib/regexp_parser/syntax/versions/1.9.3.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.0.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.2.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.3.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.4.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.4.1.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.5.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.6.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.6.2.rb +2 -0
- data/lib/regexp_parser/syntax/versions/2.6.3.rb +2 -0
- data/lib/regexp_parser/syntax/versions/3.1.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/3.2.0.rb +2 -0
- data/lib/regexp_parser/syntax/versions/3.5.0.rb +4 -0
- data/lib/regexp_parser/syntax/versions/4.0.0.rb +4 -0
- data/lib/regexp_parser/syntax/versions.rb +3 -1
- data/lib/regexp_parser/syntax.rb +3 -1
- data/lib/regexp_parser/token.rb +2 -0
- data/lib/regexp_parser/version.rb +3 -1
- data/lib/regexp_parser.rb +8 -6
- data/regexp_parser.gemspec +7 -5
- metadata +19 -11
- data/CHANGELOG.md +0 -632
- data/README.md +0 -503
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Regexp::Expression::EscapeSequence
|
|
4
|
+
AsciiEscape.class_eval { def codepoint; 0x1B end }
|
|
5
|
+
Backspace.class_eval { def codepoint; 0x8 end }
|
|
6
|
+
Bell.class_eval { def codepoint; 0x7 end }
|
|
7
|
+
FormFeed.class_eval { def codepoint; 0xC end }
|
|
8
|
+
Newline.class_eval { def codepoint; 0xA end }
|
|
9
|
+
Return.class_eval { def codepoint; 0xD end }
|
|
10
|
+
Tab.class_eval { def codepoint; 0x9 end }
|
|
11
|
+
VerticalTab.class_eval { def codepoint; 0xB end }
|
|
12
|
+
|
|
13
|
+
Literal.class_eval { def codepoint; text[1].ord end }
|
|
14
|
+
|
|
15
|
+
Octal.class_eval { def codepoint; text[/\d+/].to_i(8) end }
|
|
16
|
+
|
|
17
|
+
Hex.class_eval { def codepoint; text[/\h+/].hex end }
|
|
18
|
+
Codepoint.class_eval { def codepoint; text[/\h+/].hex end }
|
|
19
|
+
|
|
20
|
+
UTF8Hex.class_eval do
|
|
21
|
+
def codepoint
|
|
22
|
+
text.scan(/\h+/).map(&:hex).pack('C*').force_encoding('utf-8').ord
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
CodepointList.class_eval do
|
|
27
|
+
# Maybe this should be a unique top-level expression class?
|
|
28
|
+
def char
|
|
29
|
+
raise NoMethodError, 'CodepointList responds only to #chars'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def codepoint
|
|
33
|
+
raise NoMethodError, 'CodepointList responds only to #codepoints'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def chars
|
|
37
|
+
codepoints.map { |cp| cp.chr('utf-8') }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def codepoints
|
|
41
|
+
text.scan(/\h+/).map(&:hex)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
AbstractMetaControlSequence.class_eval do
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def control_sequence_to_s(control_sequence)
|
|
49
|
+
five_lsb = control_sequence.unpack('B*').first[-5..-1]
|
|
50
|
+
["000#{five_lsb}"].pack('B*')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def meta_char_to_codepoint(meta_char)
|
|
54
|
+
byte_value = meta_char.ord
|
|
55
|
+
byte_value < 128 ? byte_value + 128 : byte_value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Control.class_eval do
|
|
60
|
+
def codepoint
|
|
61
|
+
control_sequence_to_s(text).ord
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Meta.class_eval do
|
|
66
|
+
def codepoint
|
|
67
|
+
meta_char_to_codepoint(text[-1])
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
MetaControl.class_eval do
|
|
72
|
+
def codepoint
|
|
73
|
+
meta_char_to_codepoint(control_sequence_to_s(text))
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Regexp::Expression
|
|
4
|
+
module Shared
|
|
5
|
+
def negative?
|
|
6
|
+
false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# not an alias so as to respect overrides of #negative?
|
|
10
|
+
def negated?
|
|
11
|
+
negative?
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Anchor::NonWordBoundary.class_eval { def negative?; true end }
|
|
16
|
+
Assertion::NegativeLookahead.class_eval { def negative?; true end }
|
|
17
|
+
Assertion::NegativeLookbehind.class_eval { def negative?; true end }
|
|
18
|
+
CharacterSet.class_eval { def negative?; negative end }
|
|
19
|
+
CharacterType::Base.class_eval { def negative?; token.to_s.start_with?('non') end }
|
|
20
|
+
PosixClass.class_eval { def negative?; type == :nonposixclass end }
|
|
21
|
+
UnicodeProperty::Base.class_eval { def negative?; type == :nonproperty end }
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Regexp::Expression
|
|
4
|
+
module Shared
|
|
5
|
+
# default implementation
|
|
6
|
+
def parts
|
|
7
|
+
[text.dup]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def intersperse(expressions, separator)
|
|
13
|
+
expressions.flat_map { |exp| [exp, separator] }.slice(0...-1)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
CharacterSet.class_eval { def parts; ["#{text}#{'^' if negated?}", *expressions, ']'] end }
|
|
18
|
+
CharacterSet::Range.class_eval { def parts; intersperse(expressions, text.dup) end }
|
|
19
|
+
Conditional::Expression.class_eval { def parts; [text.dup, condition, *intersperse(branches, '|'), ')'] end }
|
|
20
|
+
Group::Base.class_eval { def parts; [text.dup, *expressions, ')'] end }
|
|
21
|
+
Group::Passive.class_eval { def parts; implicit? ? expressions : super end }
|
|
22
|
+
Group::Comment.class_eval { def parts; [text.dup] end }
|
|
23
|
+
Subexpression.class_eval { def parts; expressions end }
|
|
24
|
+
SequenceOperation.class_eval { def parts; intersperse(expressions, text.dup) end }
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Regexp::Expression
|
|
4
|
+
module Shared
|
|
5
|
+
def inspect
|
|
6
|
+
[
|
|
7
|
+
"#<#{self.class}",
|
|
8
|
+
pretty_print_instance_variables.map { |v| " #{v}=#{instance_variable_get(v).inspect}" },
|
|
9
|
+
">"
|
|
10
|
+
].join
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Make pretty-print work despite #inspect implementation.
|
|
14
|
+
def pretty_print(q)
|
|
15
|
+
q.pp_object(self)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Called by pretty_print (ruby/pp) and #inspect.
|
|
19
|
+
def pretty_print_instance_variables
|
|
20
|
+
[
|
|
21
|
+
(:@text unless text.to_s.empty?),
|
|
22
|
+
(:@quantifier if quantified?),
|
|
23
|
+
(:@options unless options.empty?),
|
|
24
|
+
(:@expressions unless terminal?),
|
|
25
|
+
].compact
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Regexp::Expression
|
|
4
|
+
module ReferencedExpressions
|
|
5
|
+
attr_accessor :referenced_expressions
|
|
6
|
+
|
|
7
|
+
def referenced_expression
|
|
8
|
+
referenced_expressions && referenced_expressions.first
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize_copy(orig)
|
|
12
|
+
exp_id = [self.class, self.starts_at]
|
|
13
|
+
|
|
14
|
+
# prevent infinite recursion for recursive subexp calls
|
|
15
|
+
copied = self.class.instance_eval { @copied_ref_exps ||= {} }
|
|
16
|
+
self.referenced_expressions =
|
|
17
|
+
if copied[exp_id]
|
|
18
|
+
orig.referenced_expressions
|
|
19
|
+
else
|
|
20
|
+
copied[exp_id] = true
|
|
21
|
+
orig.referenced_expressions && orig.referenced_expressions.map(&:dup)
|
|
22
|
+
end
|
|
23
|
+
copied.clear
|
|
24
|
+
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
Base.include ReferencedExpressions
|
|
30
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Regexp::Expression
|
|
2
4
|
module Shared
|
|
3
5
|
|
|
@@ -95,12 +97,49 @@ module Regexp::Expression
|
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
# Deep-compare two expressions for equality.
|
|
100
|
+
#
|
|
101
|
+
# When changing the conditions, please make sure to update
|
|
102
|
+
# #pretty_print_instance_variables so that it includes all relevant values.
|
|
98
103
|
def ==(other)
|
|
99
|
-
|
|
100
|
-
other.
|
|
101
|
-
other.
|
|
104
|
+
self.class == other.class &&
|
|
105
|
+
text == other.text &&
|
|
106
|
+
quantifier == other.quantifier &&
|
|
107
|
+
options == other.options &&
|
|
108
|
+
(terminal? || expressions == other.expressions)
|
|
102
109
|
end
|
|
103
110
|
alias :=== :==
|
|
104
111
|
alias :eql? :==
|
|
112
|
+
|
|
113
|
+
def optional?
|
|
114
|
+
quantified? && quantifier.min == 0
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def quantified?
|
|
118
|
+
!quantifier.nil?
|
|
119
|
+
end
|
|
105
120
|
end
|
|
121
|
+
|
|
122
|
+
Shared.class_eval { def terminal?; self.class.terminal? end }
|
|
123
|
+
Shared::ClassMethods.class_eval { def terminal?; true end }
|
|
124
|
+
Subexpression.instance_eval { def terminal?; false end }
|
|
125
|
+
|
|
126
|
+
Shared.class_eval { def capturing?; self.class.capturing? end }
|
|
127
|
+
Shared::ClassMethods.class_eval { def capturing?; false end }
|
|
128
|
+
Group::Capture.instance_eval { def capturing?; true end }
|
|
129
|
+
|
|
130
|
+
Shared.class_eval { def comment?; self.class.comment? end }
|
|
131
|
+
Shared::ClassMethods.class_eval { def comment?; false end }
|
|
132
|
+
Comment.instance_eval { def comment?; true end }
|
|
133
|
+
Group::Comment.instance_eval { def comment?; true end }
|
|
134
|
+
|
|
135
|
+
Shared.class_eval { def decorative?; self.class.decorative? end }
|
|
136
|
+
Shared::ClassMethods.class_eval { def decorative?; false end }
|
|
137
|
+
FreeSpace.instance_eval { def decorative?; true end }
|
|
138
|
+
Group::Comment.instance_eval { def decorative?; true end }
|
|
139
|
+
|
|
140
|
+
Shared.class_eval { def referential?; self.class.referential? end }
|
|
141
|
+
Shared::ClassMethods.class_eval { def referential?; false end }
|
|
142
|
+
Backreference::Base.instance_eval { def referential?; true end }
|
|
143
|
+
Conditional::Condition.instance_eval { def referential?; true end }
|
|
144
|
+
Conditional::Expression.instance_eval { def referential?; true end }
|
|
106
145
|
end
|
|
@@ -1,6 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Regexp::Expression
|
|
2
4
|
class Subexpression < Regexp::Expression::Base
|
|
3
5
|
|
|
6
|
+
# Traverses the expression, passing each recursive child to the
|
|
7
|
+
# given block.
|
|
8
|
+
# If the block takes two arguments, the indices of the children within
|
|
9
|
+
# their parents are also passed to it.
|
|
10
|
+
def each_expression(include_self = false, &block)
|
|
11
|
+
return enum_for(__method__, include_self) unless block
|
|
12
|
+
|
|
13
|
+
if block.arity == 1
|
|
14
|
+
block.call(self) if include_self
|
|
15
|
+
each_expression_without_index(&block)
|
|
16
|
+
else
|
|
17
|
+
block.call(self, 0) if include_self
|
|
18
|
+
each_expression_with_index(&block)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
4
22
|
# Traverses the subexpression (depth-first, pre-order) and calls the given
|
|
5
23
|
# block for each expression with three arguments; the traversal event,
|
|
6
24
|
# the expression, and the index of the expression within its parent.
|
|
@@ -34,34 +52,31 @@ module Regexp::Expression
|
|
|
34
52
|
end
|
|
35
53
|
alias :walk :traverse
|
|
36
54
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
# Returns a new array with the results of calling the given block once
|
|
56
|
+
# for every expression. If a block is not given, returns an array with
|
|
57
|
+
# each expression and its level index as an array.
|
|
58
|
+
def flat_map(include_self = false, &block)
|
|
59
|
+
case block && block.arity
|
|
60
|
+
when nil then each_expression(include_self).to_a
|
|
61
|
+
when 2 then each_expression(include_self).map(&block)
|
|
62
|
+
else each_expression(include_self).map { |exp| block.call(exp) }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
41
65
|
|
|
42
|
-
|
|
66
|
+
protected
|
|
43
67
|
|
|
68
|
+
def each_expression_with_index(&block)
|
|
44
69
|
each_with_index do |exp, index|
|
|
45
70
|
block.call(exp, index)
|
|
46
|
-
exp.
|
|
71
|
+
exp.each_expression_with_index(&block) unless exp.terminal?
|
|
47
72
|
end
|
|
48
73
|
end
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
result = []
|
|
55
|
-
|
|
56
|
-
each_expression(include_self) do |exp, index|
|
|
57
|
-
if block_given?
|
|
58
|
-
result << yield(exp, index)
|
|
59
|
-
else
|
|
60
|
-
result << [exp, index]
|
|
61
|
-
end
|
|
75
|
+
def each_expression_without_index(&block)
|
|
76
|
+
each do |exp|
|
|
77
|
+
block.call(exp)
|
|
78
|
+
exp.each_expression_without_index(&block) unless exp.terminal?
|
|
62
79
|
end
|
|
63
|
-
|
|
64
|
-
result
|
|
65
80
|
end
|
|
66
81
|
end
|
|
67
82
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Regexp::Expression
|
|
2
4
|
# TODO: in v3.0.0, maybe put Shared back into Base, and inherit from Base and
|
|
3
5
|
# call super in #initialize, but raise in #quantifier= and #quantify,
|
|
@@ -6,16 +8,12 @@ module Regexp::Expression
|
|
|
6
8
|
class Quantifier
|
|
7
9
|
include Regexp::Expression::Shared
|
|
8
10
|
|
|
9
|
-
MODES = %i[greedy possessive reluctant]
|
|
10
|
-
|
|
11
|
-
attr_reader :min, :max, :mode
|
|
11
|
+
MODES = %i[greedy possessive reluctant].freeze
|
|
12
12
|
|
|
13
13
|
def initialize(*args)
|
|
14
14
|
deprecated_old_init(*args) and return if args.count == 4 || args.count == 5
|
|
15
15
|
|
|
16
16
|
init_from_token_and_options(*args)
|
|
17
|
-
@mode = (token.to_s[/greedy|reluctant|possessive/] || :greedy).to_sym
|
|
18
|
-
@min, @max = minmax
|
|
19
17
|
# TODO: remove in v3.0.0, stop removing parts of #token (?)
|
|
20
18
|
self.token = token.to_s.sub(/_(greedy|possessive|reluctant)/, '').to_sym
|
|
21
19
|
end
|
|
@@ -39,9 +37,21 @@ module Regexp::Expression
|
|
|
39
37
|
end
|
|
40
38
|
alias :lazy? :reluctant?
|
|
41
39
|
|
|
40
|
+
def min
|
|
41
|
+
derived_data[:min]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def max
|
|
45
|
+
derived_data[:max]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def mode
|
|
49
|
+
derived_data[:mode]
|
|
50
|
+
end
|
|
51
|
+
|
|
42
52
|
private
|
|
43
53
|
|
|
44
|
-
def deprecated_old_init(token, text,
|
|
54
|
+
def deprecated_old_init(token, text, _min, _max, _mode = :greedy)
|
|
45
55
|
warn "Calling `Expression::Base#quantify` or `#{self.class}.new` with 4+ arguments "\
|
|
46
56
|
"is deprecated.\nIt will no longer be supported in regexp_parser v3.0.0.\n"\
|
|
47
57
|
"Please pass a Regexp::Token instead, e.g. replace `token, text, min, max, mode` "\
|
|
@@ -51,20 +61,25 @@ module Regexp::Expression
|
|
|
51
61
|
"This is consistent with how Expression::Base instances are created. "
|
|
52
62
|
@token = token
|
|
53
63
|
@text = text
|
|
54
|
-
@min = min
|
|
55
|
-
@max = max
|
|
56
|
-
@mode = mode
|
|
57
64
|
end
|
|
58
65
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
def derived_data
|
|
67
|
+
@derived_data ||= begin
|
|
68
|
+
min, max =
|
|
69
|
+
case text[0]
|
|
70
|
+
when '?'; [0, 1]
|
|
71
|
+
when '*'; [0, -1]
|
|
72
|
+
when '+'; [1, -1]
|
|
73
|
+
else
|
|
74
|
+
int_min = text[/\{(\d*)/, 1]
|
|
75
|
+
int_max = text[/,?(\d*)\}/, 1]
|
|
76
|
+
[int_min.to_i, (int_max.empty? ? -1 : int_max.to_i)]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
mod = text[/.([?+])/, 1]
|
|
80
|
+
mode = (mod == '?' && :reluctant) || (mod == '+' && :possessive) || :greedy
|
|
81
|
+
|
|
82
|
+
{ min: min, max: max, mode: mode }
|
|
68
83
|
end
|
|
69
84
|
end
|
|
70
85
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Regexp::Expression
|
|
2
4
|
# A sequence of expressions. Differs from a Subexpressions by how it handles
|
|
3
5
|
# quantifiers, as it applies them to its last element instead of itself as
|
|
@@ -12,6 +14,7 @@ module Regexp::Expression
|
|
|
12
14
|
level: exp.level,
|
|
13
15
|
set_level: exp.set_level,
|
|
14
16
|
conditional_level: params[:conditional_level] || exp.conditional_level,
|
|
17
|
+
ts: params[:ts],
|
|
15
18
|
)
|
|
16
19
|
sequence.options = active_opts
|
|
17
20
|
exp.expressions << sequence
|
|
@@ -19,17 +22,12 @@ module Regexp::Expression
|
|
|
19
22
|
end
|
|
20
23
|
end
|
|
21
24
|
|
|
22
|
-
def
|
|
23
|
-
expressions.first.
|
|
25
|
+
def ts
|
|
26
|
+
(head = expressions.first) ? head.ts : @ts
|
|
24
27
|
end
|
|
25
|
-
alias :ts :starts_at
|
|
26
|
-
|
|
27
|
-
def quantify(*args)
|
|
28
|
-
target = expressions.reverse.find { |exp| !exp.is_a?(FreeSpace) }
|
|
29
|
-
target or raise Regexp::Parser::Error,
|
|
30
|
-
"No valid target found for '#{text}' quantifier"
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
def quantify(token, *args)
|
|
30
|
+
extract_quantifier_target(token.text).quantify(token, *args)
|
|
33
31
|
end
|
|
34
32
|
end
|
|
35
33
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Regexp::Expression
|
|
2
4
|
# abstract class
|
|
3
5
|
class SequenceOperation < Regexp::Expression::Subexpression
|
|
@@ -5,21 +7,16 @@ module Regexp::Expression
|
|
|
5
7
|
alias :operands :expressions
|
|
6
8
|
alias :operator :text
|
|
7
9
|
|
|
8
|
-
def
|
|
9
|
-
expressions.first.
|
|
10
|
+
def ts
|
|
11
|
+
(head = expressions.first) ? head.ts : @ts
|
|
10
12
|
end
|
|
11
|
-
alias :ts :starts_at
|
|
12
13
|
|
|
13
14
|
def <<(exp)
|
|
14
15
|
expressions.last << exp
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
def add_sequence(active_opts = {})
|
|
18
|
-
self.class::OPERAND.add_to(self,
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def parts
|
|
22
|
-
intersperse(expressions, text.dup)
|
|
18
|
+
def add_sequence(active_opts = {}, params = { ts: 0 })
|
|
19
|
+
self.class::OPERAND.add_to(self, params, active_opts)
|
|
23
20
|
end
|
|
24
21
|
end
|
|
25
22
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Regexp::Expression
|
|
2
4
|
module Shared
|
|
3
5
|
module ClassMethods; end # filled in ./methods/*.rb
|
|
@@ -8,7 +10,8 @@ module Regexp::Expression
|
|
|
8
10
|
|
|
9
11
|
attr_accessor :type, :token, :text, :ts, :te,
|
|
10
12
|
:level, :set_level, :conditional_level,
|
|
11
|
-
:options
|
|
13
|
+
:options, :parent,
|
|
14
|
+
:custom_to_s_handling, :pre_quantifier_decorations
|
|
12
15
|
|
|
13
16
|
attr_reader :nesting_level, :quantifier
|
|
14
17
|
end
|
|
@@ -32,6 +35,10 @@ module Regexp::Expression
|
|
|
32
35
|
self.text = orig.text.dup if orig.text
|
|
33
36
|
self.options = orig.options.dup if orig.options
|
|
34
37
|
self.quantifier = orig.quantifier.clone if orig.quantifier
|
|
38
|
+
self.parent = nil # updated by Subexpression#initialize_copy
|
|
39
|
+
if orig.pre_quantifier_decorations
|
|
40
|
+
self.pre_quantifier_decorations = orig.pre_quantifier_decorations.map(&:dup)
|
|
41
|
+
end
|
|
35
42
|
super
|
|
36
43
|
end
|
|
37
44
|
|
|
@@ -39,35 +46,52 @@ module Regexp::Expression
|
|
|
39
46
|
ts
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
def ends_at(include_quantifier = true)
|
|
50
|
+
ts + (include_quantifier ? full_length : base_length)
|
|
51
|
+
end
|
|
52
|
+
|
|
42
53
|
def base_length
|
|
43
54
|
to_s(:base).length
|
|
44
55
|
end
|
|
45
56
|
|
|
46
57
|
def full_length
|
|
47
|
-
to_s.length
|
|
48
|
-
end
|
|
49
|
-
|
|
58
|
+
to_s(:original).length
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# #to_s reproduces the original source, as an unparser would.
|
|
62
|
+
#
|
|
63
|
+
# It takes an optional format argument.
|
|
64
|
+
#
|
|
65
|
+
# Example:
|
|
66
|
+
#
|
|
67
|
+
# lit = Regexp::Parser.parse(/a +/x)[0]
|
|
68
|
+
#
|
|
69
|
+
# lit.to_s # => 'a+' # default; with quantifier
|
|
70
|
+
# lit.to_s(:full) # => 'a+' # default; with quantifier
|
|
71
|
+
# lit.to_s(:base) # => 'a' # without quantifier
|
|
72
|
+
# lit.to_s(:original) # => 'a +' # with quantifier AND intermittent decorations
|
|
73
|
+
#
|
|
50
74
|
def to_s(format = :full)
|
|
51
|
-
|
|
75
|
+
base = ''.dup
|
|
76
|
+
parts.each do |part|
|
|
77
|
+
if part.instance_of?(String)
|
|
78
|
+
base << part
|
|
79
|
+
elsif !part.custom_to_s_handling
|
|
80
|
+
base << part.to_s(:original)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
"#{base}#{pre_quantifier_decoration(format)}#{quantifier_affix(format)}"
|
|
52
84
|
end
|
|
53
85
|
alias :to_str :to_s
|
|
54
86
|
|
|
55
|
-
def
|
|
56
|
-
|
|
87
|
+
def pre_quantifier_decoration(expression_format = :original)
|
|
88
|
+
pre_quantifier_decorations.to_a.join if expression_format == :original
|
|
57
89
|
end
|
|
58
90
|
|
|
59
|
-
def quantifier_affix(expression_format)
|
|
91
|
+
def quantifier_affix(expression_format = :full)
|
|
60
92
|
quantifier.to_s if quantified? && expression_format != :base
|
|
61
93
|
end
|
|
62
94
|
|
|
63
|
-
def quantified?
|
|
64
|
-
!quantifier.nil?
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def optional?
|
|
68
|
-
quantified? && quantifier.min == 0
|
|
69
|
-
end
|
|
70
|
-
|
|
71
95
|
def offset
|
|
72
96
|
[starts_at, full_length]
|
|
73
97
|
end
|
|
@@ -76,14 +100,6 @@ module Regexp::Expression
|
|
|
76
100
|
'@%d+%d' % offset
|
|
77
101
|
end
|
|
78
102
|
|
|
79
|
-
def terminal?
|
|
80
|
-
true # overridden to be false in Expression::Subexpression
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def referential?
|
|
84
|
-
false # overridden to be true e.g. in Expression::Backreference::Base
|
|
85
|
-
end
|
|
86
|
-
|
|
87
103
|
def nesting_level=(lvl)
|
|
88
104
|
@nesting_level = lvl
|
|
89
105
|
quantifier && quantifier.nesting_level = lvl
|