textmate_grammar 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/lib/textmate_grammar/generated/grammar.rb +32 -0
- data/lib/textmate_grammar/generated/rule.rb +144 -0
- data/lib/textmate_grammar/grammar.rb +670 -0
- data/lib/textmate_grammar/grammar_plugin.rb +189 -0
- data/lib/textmate_grammar/import_patterns.rb +14 -0
- data/lib/textmate_grammar/linters/flat_includes.rb +32 -0
- data/lib/textmate_grammar/linters/includes_then_tag_as.rb +48 -0
- data/lib/textmate_grammar/linters/standard_naming.rb +226 -0
- data/lib/textmate_grammar/linters/start_match_empty.rb +49 -0
- data/lib/textmate_grammar/linters/tests.rb +19 -0
- data/lib/textmate_grammar/linters/unused_unresolved.rb +9 -0
- data/lib/textmate_grammar/pattern_extensions/look_ahead_for.rb +32 -0
- data/lib/textmate_grammar/pattern_extensions/look_ahead_to_avoid.rb +31 -0
- data/lib/textmate_grammar/pattern_extensions/look_behind_for.rb +31 -0
- data/lib/textmate_grammar/pattern_extensions/look_behind_to_avoid.rb +31 -0
- data/lib/textmate_grammar/pattern_extensions/lookaround_pattern.rb +169 -0
- data/lib/textmate_grammar/pattern_extensions/match_result_of.rb +67 -0
- data/lib/textmate_grammar/pattern_extensions/maybe.rb +50 -0
- data/lib/textmate_grammar/pattern_extensions/one_of.rb +107 -0
- data/lib/textmate_grammar/pattern_extensions/one_or_more_of.rb +42 -0
- data/lib/textmate_grammar/pattern_extensions/or_pattern.rb +55 -0
- data/lib/textmate_grammar/pattern_extensions/placeholder.rb +102 -0
- data/lib/textmate_grammar/pattern_extensions/recursively_match.rb +76 -0
- data/lib/textmate_grammar/pattern_extensions/zero_or_more_of.rb +50 -0
- data/lib/textmate_grammar/pattern_variations/base_pattern.rb +870 -0
- data/lib/textmate_grammar/pattern_variations/legacy_pattern.rb +61 -0
- data/lib/textmate_grammar/pattern_variations/pattern.rb +9 -0
- data/lib/textmate_grammar/pattern_variations/pattern_range.rb +233 -0
- data/lib/textmate_grammar/pattern_variations/repeatable_pattern.rb +204 -0
- data/lib/textmate_grammar/regex_operator.rb +182 -0
- data/lib/textmate_grammar/regex_operators/alternation.rb +24 -0
- data/lib/textmate_grammar/regex_operators/concat.rb +23 -0
- data/lib/textmate_grammar/stdlib/common.rb +20 -0
- data/lib/textmate_grammar/tokens.rb +110 -0
- data/lib/textmate_grammar/transforms/add_ending.rb +25 -0
- data/lib/textmate_grammar/transforms/bailout.rb +92 -0
- data/lib/textmate_grammar/transforms/fix_repeated_tag_as.rb +75 -0
- data/lib/textmate_grammar/transforms/resolve_placeholders.rb +121 -0
- data/lib/textmate_grammar/util.rb +198 -0
- data/lib/textmate_grammar.rb +4 -0
- metadata +85 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base_pattern.rb"
|
4
|
+
|
5
|
+
#
|
6
|
+
# LegacyPattern allows for a hash to be treated as a Pattern
|
7
|
+
# It implements the minimum required to be sucessfully generated by the grammar
|
8
|
+
#
|
9
|
+
class LegacyPattern < PatternBase
|
10
|
+
def initialize(hash)
|
11
|
+
super("placeholder")
|
12
|
+
@hash = hash.transform_keys(&:to_sym)
|
13
|
+
end
|
14
|
+
|
15
|
+
# LegacyPattern cannot be evaluated
|
16
|
+
def evaluate(*_ignored)
|
17
|
+
raise "LegacyPattern cannot be used as a part of a Pattern"
|
18
|
+
end
|
19
|
+
|
20
|
+
# LegacyPattern cannot be chained
|
21
|
+
def insert!(_pattern)
|
22
|
+
raise "LegacyPattern cannot be used as a part of a Pattern"
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# (see PatternBase#to_tag)
|
27
|
+
#
|
28
|
+
# @return [Hash] The hash it was constructed with
|
29
|
+
#
|
30
|
+
def to_tag
|
31
|
+
@hash
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# (see PatternBase#run_tests)
|
36
|
+
#
|
37
|
+
def run_tests
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# (see PatternBase#map!)
|
43
|
+
#
|
44
|
+
def map!(*)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# (see PatternBase#start_pattern)
|
50
|
+
#
|
51
|
+
def start_pattern
|
52
|
+
""
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# (see PatternBase#__deep_clone__)
|
57
|
+
#
|
58
|
+
def __deep_clone__
|
59
|
+
self.class.new(@hash)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base_pattern.rb"
|
4
|
+
|
5
|
+
#
|
6
|
+
# Provides the ability to create begin/end and begin/while rules
|
7
|
+
#
|
8
|
+
class PatternRange < PatternBase
|
9
|
+
attr_reader :start_pattern
|
10
|
+
|
11
|
+
#
|
12
|
+
# Creates a new PatternRange
|
13
|
+
#
|
14
|
+
# @param [Hash] arguments options
|
15
|
+
# @option arguments [PatternBase,Regexp,String] :start_pattern the start pattern
|
16
|
+
# @option arguments [PatternBase,Regexp,String] :end_pattern the end pattern
|
17
|
+
# @option arguments [PatternBase,Regexp,String] :while_pattern the while pattern
|
18
|
+
# @option arguments [String] :tag_as the tag for this pattern
|
19
|
+
# @option arguments [String] :tag_contents_as the tag for contents of this pattern
|
20
|
+
# @option arguments [String] :tag_start_as the tag for the start pattern
|
21
|
+
# @option arguments [String] :tag_end_as the tag for the end pattern
|
22
|
+
# @option arguments [String] :tag_while_as the tag for the continuation pattern
|
23
|
+
#
|
24
|
+
# Plugins may add additional options
|
25
|
+
# @note exactly one of :end_pattern or :while_pattern is required
|
26
|
+
#
|
27
|
+
def initialize(arguments)
|
28
|
+
@original_arguments = arguments
|
29
|
+
@match = nil
|
30
|
+
@next_pattern = nil
|
31
|
+
|
32
|
+
raise "PatternRange.new() expects a hash" unless arguments.is_a? Hash
|
33
|
+
|
34
|
+
# ensure end_pattern: XOR while_pattern: is provided
|
35
|
+
if arguments[:end_pattern].nil? && arguments[:while_pattern].nil?
|
36
|
+
raise "one of `while_pattern:` or `end_pattern` must be supplied"
|
37
|
+
end
|
38
|
+
|
39
|
+
if !arguments[:end_pattern].nil? && !arguments[:while_pattern].nil?
|
40
|
+
raise "only one of `while_pattern:` or `end_pattern` must be supplied"
|
41
|
+
end
|
42
|
+
|
43
|
+
@start_pattern = arguments[:start_pattern]
|
44
|
+
@stop_pattern = arguments[:end_pattern] || arguments[:while_pattern]
|
45
|
+
|
46
|
+
# convert to patterns if needed
|
47
|
+
@start_pattern = PatternBase.new(@start_pattern) unless @start_pattern.is_a? PatternBase
|
48
|
+
@stop_pattern = PatternBase.new(@stop_pattern) unless @stop_pattern.is_a? PatternBase
|
49
|
+
|
50
|
+
# store originals for to_s
|
51
|
+
@original_start_pattern = @start_pattern
|
52
|
+
@original_stop_pattern = @stop_pattern
|
53
|
+
|
54
|
+
if arguments[:tag_start_as]
|
55
|
+
@start_pattern = PatternBase.new(
|
56
|
+
match: @start_pattern,
|
57
|
+
tag_as: arguments[:tag_start_as],
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
tag_stop_as = arguments[:tag_end_as] || arguments[:tag_while_as]
|
62
|
+
|
63
|
+
if tag_stop_as
|
64
|
+
@stop_pattern = PatternBase.new(
|
65
|
+
match: @stop_pattern,
|
66
|
+
tag_as: tag_stop_as,
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
raise "match: is not supported in a PatternRange" if arguments[:match]
|
71
|
+
|
72
|
+
@stop_type = arguments[:end_pattern] ? :end_pattern : :while_pattern
|
73
|
+
|
74
|
+
arguments.delete(:start_pattern)
|
75
|
+
arguments.delete(:end_pattern)
|
76
|
+
arguments.delete(:while_pattern)
|
77
|
+
|
78
|
+
# ensure that includes is either nil or a flat array
|
79
|
+
if arguments[:includes]
|
80
|
+
arguments[:includes] = [arguments[:includes]] unless arguments[:includes].is_a? Array
|
81
|
+
arguments[:includes] = arguments[:includes].flatten
|
82
|
+
end
|
83
|
+
|
84
|
+
#canonize end_pattern_last
|
85
|
+
arguments[:end_pattern_last] = arguments[:apply_end_pattern_last] if arguments[:apply_end_pattern_last]
|
86
|
+
arguments[:end_pattern_last] = arguments[:applyEndPatternLast] if arguments[:applyEndPatternLast]
|
87
|
+
|
88
|
+
arguments.delete(:apply_end_pattern_last)
|
89
|
+
arguments.delete(:applyEndPatternLast)
|
90
|
+
|
91
|
+
@arguments = arguments
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# (see PatternBase#__deep_clone__)
|
96
|
+
#
|
97
|
+
def __deep_clone__
|
98
|
+
options = @arguments.__deep_clone__
|
99
|
+
options[:start_pattern] = @original_start_pattern.__deep_clone__
|
100
|
+
if @stop_type == :end_pattern
|
101
|
+
options[:end_pattern] = @original_stop_pattern.__deep_clone__
|
102
|
+
else
|
103
|
+
options[:while_pattern] = @original_stop_pattern.__deep_clone__
|
104
|
+
end
|
105
|
+
self.class.new(options)
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Raises an error to prevent use inside a pattern list
|
110
|
+
#
|
111
|
+
# @param _ignored ignored
|
112
|
+
#
|
113
|
+
# @return [void]
|
114
|
+
#
|
115
|
+
def evaluate(*_ignored)
|
116
|
+
raise "PatternRange cannot be used as a part of a Pattern"
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Raises an error to prevent use inside a pattern list
|
121
|
+
#
|
122
|
+
# @param _ignored ignored
|
123
|
+
#
|
124
|
+
# @return [void]
|
125
|
+
#
|
126
|
+
def do_evaluate_self(*_ignored)
|
127
|
+
raise "PatternRange cannot be used as a part of a Pattern"
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# Generate a Textmate rule from the PatternRange
|
132
|
+
#
|
133
|
+
# @return [Hash] The Textmate rule
|
134
|
+
#
|
135
|
+
def to_tag
|
136
|
+
match_key = { end_pattern: "end", while_pattern: "while" }[@stop_type]
|
137
|
+
capture_key = { end_pattern: "endCaptures", while_pattern: "whileCaptures" }[@stop_type]
|
138
|
+
|
139
|
+
start_groups = @start_pattern.collect_group_attributes
|
140
|
+
stop_groups = @stop_pattern.collect_group_attributes
|
141
|
+
|
142
|
+
output = {
|
143
|
+
"begin" => @start_pattern.evaluate,
|
144
|
+
# this is supposed to be start_groups as back references in end and while
|
145
|
+
# refer to the start pattern
|
146
|
+
match_key => @stop_pattern.evaluate(start_groups, fixup_refereces: true),
|
147
|
+
"beginCaptures" => convert_group_attributes_to_captures(start_groups),
|
148
|
+
capture_key => convert_group_attributes_to_captures(stop_groups),
|
149
|
+
}
|
150
|
+
|
151
|
+
output[:name] = @arguments[:tag_as] unless @arguments[:tag_as].nil?
|
152
|
+
output[:contentName] = @arguments[:tag_content_as] unless @arguments[:tag_content_as].nil?
|
153
|
+
|
154
|
+
output["begin"] = output["begin"][1..-2] if @start_pattern.optimize_outer_group?
|
155
|
+
output[match_key] = output[match_key][1..-2] if @stop_pattern.optimize_outer_group?
|
156
|
+
|
157
|
+
if @arguments[:includes].is_a? Array
|
158
|
+
output[:patterns] = convert_includes_to_patterns(@arguments[:includes])
|
159
|
+
elsif !@arguments[:includes].nil?
|
160
|
+
output[:patterns] = convert_includes_to_patterns([@arguments[:includes]])
|
161
|
+
end
|
162
|
+
|
163
|
+
# end_pattern_last
|
164
|
+
output["applyEndPatternLast"] = 1 if @arguments[:end_pattern_last]
|
165
|
+
|
166
|
+
output
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Displays this pattern range as source code that would generate it
|
171
|
+
#
|
172
|
+
# @return [String] The PatternRange as source code
|
173
|
+
#
|
174
|
+
def to_s
|
175
|
+
start_pattern = @original_start_pattern.to_s(2, true)
|
176
|
+
stop_pattern = @original_stop_pattern.to_s(2, true)
|
177
|
+
|
178
|
+
output = "PatternRange.new("
|
179
|
+
output += "\n start_pattern: " + start_pattern.lstrip
|
180
|
+
output += ",\n #{@stop_type}: " + stop_pattern.lstrip
|
181
|
+
[:tag_as, :tag_content_as, :tag_start_as, :tag_end_as, :tag_while_as].each do |tag|
|
182
|
+
next if @arguments[tag].nil?
|
183
|
+
|
184
|
+
output += ",\n #{tag}: \"" + @arguments[tag] + "\"" if @arguments[tag].is_a? String
|
185
|
+
end
|
186
|
+
output += ",\n includes: " + @arguments[:includes].to_s if @arguments[:includes]
|
187
|
+
output += ",\n end_pattern_last: #{@arguments[:end_pattern_last]}" if @arguments[:end_pattern_last]
|
188
|
+
output += ",\n)"
|
189
|
+
|
190
|
+
output
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# (see PatternBase#map!)
|
195
|
+
#
|
196
|
+
def map!(map_includes = false, &block)
|
197
|
+
yield self
|
198
|
+
|
199
|
+
@start_pattern.map!(map_includes, &block)
|
200
|
+
@stop_pattern.map!(map_includes, &block)
|
201
|
+
map_includes!(&block) if map_includes
|
202
|
+
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# (see PatternBase#transform_includes)
|
208
|
+
#
|
209
|
+
def transform_includes(&block)
|
210
|
+
copy = __deep_clone__
|
211
|
+
copy.arguments[:includes].map!(&block) if copy.arguments[:includes].is_a? Array
|
212
|
+
|
213
|
+
copy.map!(true) do |s|
|
214
|
+
s.arguments[:includes].map!(&block) if s.arguments[:includes].is_a? Array
|
215
|
+
end.freeze
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# (see PatternBase#run_tests)
|
220
|
+
#
|
221
|
+
def run_tests
|
222
|
+
s = @start_pattern.run_tests
|
223
|
+
e = @stop_pattern.run_tests
|
224
|
+
s && e
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# (see PatternBase#inspect)
|
229
|
+
#
|
230
|
+
def inspect
|
231
|
+
super.split(" ")[0] + " start_pattern:" + @start_pattern.inspect + ">"
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./base_pattern.rb"
|
4
|
+
|
5
|
+
#
|
6
|
+
# RepeatablePattern provides quantifiers for patterns
|
7
|
+
#
|
8
|
+
class RepeatablePattern < PatternBase
|
9
|
+
# @return [Integer,nil] the minimum amount that can be matched
|
10
|
+
attr_accessor :at_least
|
11
|
+
# @return [Integer,nil] the maximum amount that can be matched
|
12
|
+
attr_accessor :at_most
|
13
|
+
# (see PatternBase#initialize)
|
14
|
+
def initialize(*arguments)
|
15
|
+
super(*arguments)
|
16
|
+
|
17
|
+
@at_least = nil
|
18
|
+
@at_most = nil
|
19
|
+
process_quantifiers_from_arguments
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# sets @at_least and @at_most based on arguments
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
#
|
29
|
+
def process_quantifiers_from_arguments
|
30
|
+
# this sets the @at_most and @at_least value based on the arguments
|
31
|
+
|
32
|
+
#
|
33
|
+
# Simplify the quantity down to just :at_least and :at_most
|
34
|
+
#
|
35
|
+
attributes_clone = @arguments.clone
|
36
|
+
# convert Enumerators to numbers
|
37
|
+
[:at_least, :at_most, :how_many_times?].each do |each|
|
38
|
+
if attributes_clone[each].is_a?(Enumerator)
|
39
|
+
attributes_clone[each] = attributes_clone[each].size
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# canonize dont_back_track? and as_few_as_possible?
|
44
|
+
@arguments[:dont_back_track?] ||= @arguments[:possessive?]
|
45
|
+
@arguments[:as_few_as_possible?] ||= @arguments[:lazy?]
|
46
|
+
if @arguments[:greedy?]
|
47
|
+
@arguments[:dont_back_track?] = false
|
48
|
+
@arguments[:as_few_as_possible?] = false
|
49
|
+
end
|
50
|
+
# extract the data
|
51
|
+
at_least = attributes_clone[:at_least]
|
52
|
+
at_most = attributes_clone[:at_most]
|
53
|
+
how_many_times = attributes_clone[:how_many_times?]
|
54
|
+
# simplify to at_least and at_most
|
55
|
+
at_least = at_most = how_many_times if how_many_times.is_a?(Integer)
|
56
|
+
|
57
|
+
# check if quantifying is allowed
|
58
|
+
# check after everything else in case additional quantifying options
|
59
|
+
# are created in the future
|
60
|
+
if quantifying_allowed?
|
61
|
+
@at_least = at_least
|
62
|
+
@at_most = at_most
|
63
|
+
# if a quantifying value was set and quantifying is not allowed, raise an error
|
64
|
+
# telling the user that its not allowed
|
65
|
+
elsif !(at_most.nil? && at_least.nil?)
|
66
|
+
raise <<-HEREDOC.remove_indent
|
67
|
+
|
68
|
+
Inside of the #{name} pattern, there are some quantity arguments like:
|
69
|
+
:at_least
|
70
|
+
:at_most
|
71
|
+
or :how_many_times?
|
72
|
+
These are not allowed in this kind of #{do_get_to_s_name}) pattern
|
73
|
+
If you did this intentionally please wrap it inside of a Pattern.new()
|
74
|
+
ex: #{do_get_to_s_name} Pattern.new( *your_arguments* ) )
|
75
|
+
HEREDOC
|
76
|
+
end
|
77
|
+
|
78
|
+
return unless @arguments[:dont_back_track?] && @arguments[:as_few_as_possible?]
|
79
|
+
|
80
|
+
raise ":dont_back_track? and :as_few_as_possible? cannot both be provided"
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# converts @at_least and @at_most into the appropriate quantifier
|
85
|
+
# this is a simple_quantifier because it does not include atomic-ness
|
86
|
+
#
|
87
|
+
# @return [String] the quantifier
|
88
|
+
#
|
89
|
+
def simple_quantifier
|
90
|
+
# Generate the ending based on :at_least and :at_most
|
91
|
+
|
92
|
+
# by default assume no quantifiers
|
93
|
+
quantifier = ""
|
94
|
+
# if there is no at_least, at_most, or how_many_times, then theres no quantifier
|
95
|
+
if @at_least.nil? and @at_most.nil?
|
96
|
+
quantifier = ""
|
97
|
+
# if there is a quantifier
|
98
|
+
else
|
99
|
+
# if there's no at_least, then assume at_least = 1
|
100
|
+
@at_least = 1 if @at_least.nil?
|
101
|
+
|
102
|
+
quantifier =
|
103
|
+
if @at_least == 1 and @at_most == 1
|
104
|
+
# no qualifier
|
105
|
+
""
|
106
|
+
elsif @at_least == 0 and @at_most == 1
|
107
|
+
# this is just a different way of "maybe"
|
108
|
+
"?"
|
109
|
+
elsif @at_least == 0 and @at_most.nil?
|
110
|
+
# this is just a different way of "zeroOrMoreOf"
|
111
|
+
"*"
|
112
|
+
elsif @at_least == 1 and @at_most.nil?
|
113
|
+
# this is just a different way of "oneOrMoreOf"
|
114
|
+
"+"
|
115
|
+
elsif @at_least == @at_most
|
116
|
+
# exactly N times
|
117
|
+
"{#{@at_least}}"
|
118
|
+
else
|
119
|
+
# if it is more complicated than that, just use a range
|
120
|
+
"{#{@at_least},#{@at_most}}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
# quantifiers can be made possessive without requiring atomic groups
|
124
|
+
quantifier += "+" if quantifier != "" && @arguments[:dont_back_track?] == true
|
125
|
+
quantifier += "?" if quantifier != "" && @arguments[:as_few_as_possible?] == true
|
126
|
+
quantifier
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Adds quantifiers to match
|
131
|
+
#
|
132
|
+
# @param [String, PatternBase] match the pattern to add a quantifier to
|
133
|
+
# @param [Array] groups group information, used for evaluating match
|
134
|
+
#
|
135
|
+
# @return [String] match with quantifiers applied
|
136
|
+
#
|
137
|
+
def add_quantifier_options_to(match, groups)
|
138
|
+
match = match.evaluate(groups) if match.is_a? PatternBase
|
139
|
+
quantifier = simple_quantifier
|
140
|
+
# check if there are quantifiers
|
141
|
+
if quantifier != ""
|
142
|
+
# if the match is not a single entity, then it needs to be wrapped
|
143
|
+
match = "(?:#{match})" unless string_single_entity?(match)
|
144
|
+
# add the quantified ending
|
145
|
+
match += quantifier
|
146
|
+
elsif @arguments[:dont_back_track?] == true
|
147
|
+
# make atomic, which allows arbitrary expression to be prevented from backtracking
|
148
|
+
match = "(?>#{match})"
|
149
|
+
end
|
150
|
+
if @arguments[:word_cannot_be_any_of]
|
151
|
+
word_pattern = @arguments[:word_cannot_be_any_of].map { |w| Regexp.escape w }.join "|"
|
152
|
+
match = "(?!\\b(?:#{word_pattern})\\b)#{match}"
|
153
|
+
end
|
154
|
+
match
|
155
|
+
end
|
156
|
+
|
157
|
+
# (see PatternBase#do_evaluate_self)
|
158
|
+
def do_evaluate_self(groups)
|
159
|
+
add_capture_group_if_needed(add_quantifier_options_to(@match, groups))
|
160
|
+
end
|
161
|
+
|
162
|
+
# controls weather @arguments[:at_most] et. al. set @at_most et. al.
|
163
|
+
# @note override when inheriting. Return false unless the subclass allow quantifying
|
164
|
+
# @return [Boolean] if quantifying is allowed
|
165
|
+
# @note the default implementation returns True
|
166
|
+
def quantifying_allowed?
|
167
|
+
true
|
168
|
+
end
|
169
|
+
|
170
|
+
#
|
171
|
+
# (see PatternBase#do_add_attributes)
|
172
|
+
#
|
173
|
+
def do_add_attributes(indent)
|
174
|
+
# rubocop:disable Metrics/LineLength
|
175
|
+
output = ""
|
176
|
+
# special #then arguments
|
177
|
+
if quantifying_allowed?
|
178
|
+
output += ",\n#{indent} at_least: " + @arguments[:at_least].to_s if @arguments[:at_least]
|
179
|
+
output += ",\n#{indent} at_most: " + @arguments[:at_most].to_s if @arguments[:at_most]
|
180
|
+
output += ",\n#{indent} how_many_times: " + @arguments[:how_many_times].to_s if @arguments[:how_many_times]
|
181
|
+
output += ",\n#{indent} word_cannot_be_any_of: " + @arguments[:word_cannot_be_any_of].to_s if @arguments[:word_cannot_be_any_of]
|
182
|
+
end
|
183
|
+
output += ",\n#{indent} dont_back_track?: " + @arguments[:dont_back_track?].to_s if @arguments[:dont_back_track?]
|
184
|
+
output
|
185
|
+
# rubocop:enable Metrics/LineLength
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Does this pattern potentially rematch any capture groups
|
190
|
+
#
|
191
|
+
# @note this is used by FixRepeatedTagAs to modify patterns
|
192
|
+
# The answer of true is a safe, but expensive to runtime, default
|
193
|
+
#
|
194
|
+
# @return [Boolean] True if this pattern potentially rematches capture groups
|
195
|
+
#
|
196
|
+
def self_capture_group_rematch
|
197
|
+
# N or more
|
198
|
+
return true if @at_most.nil? && !@at_least.nil?
|
199
|
+
# up to N
|
200
|
+
return true if !@at_most.nil? && @at_most > 1
|
201
|
+
|
202
|
+
false
|
203
|
+
end
|
204
|
+
end
|