textmate_grammar 0.0.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 +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
|