mustermann 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -1
  3. data/.travis.yml +4 -3
  4. data/.yardopts +1 -0
  5. data/README.md +53 -10
  6. data/Rakefile +4 -1
  7. data/bench/capturing.rb +42 -9
  8. data/bench/template_vs_addressable.rb +3 -0
  9. data/internals.md +64 -0
  10. data/lib/mustermann.rb +14 -5
  11. data/lib/mustermann/ast/compiler.rb +150 -0
  12. data/lib/mustermann/ast/expander.rb +112 -0
  13. data/lib/mustermann/ast/node.rb +155 -0
  14. data/lib/mustermann/ast/parser.rb +136 -0
  15. data/lib/mustermann/ast/pattern.rb +89 -0
  16. data/lib/mustermann/ast/transformer.rb +121 -0
  17. data/lib/mustermann/ast/translator.rb +111 -0
  18. data/lib/mustermann/ast/tree_renderer.rb +29 -0
  19. data/lib/mustermann/ast/validation.rb +40 -0
  20. data/lib/mustermann/error.rb +4 -12
  21. data/lib/mustermann/extension.rb +3 -6
  22. data/lib/mustermann/identity.rb +4 -4
  23. data/lib/mustermann/pattern.rb +34 -5
  24. data/lib/mustermann/rails.rb +7 -16
  25. data/lib/mustermann/regexp_based.rb +4 -4
  26. data/lib/mustermann/shell.rb +4 -4
  27. data/lib/mustermann/simple.rb +1 -1
  28. data/lib/mustermann/simple_match.rb +2 -2
  29. data/lib/mustermann/sinatra.rb +10 -20
  30. data/lib/mustermann/template.rb +11 -104
  31. data/lib/mustermann/version.rb +1 -1
  32. data/mustermann.gemspec +1 -1
  33. data/spec/extension_spec.rb +143 -0
  34. data/spec/mustermann_spec.rb +41 -0
  35. data/spec/pattern_spec.rb +16 -6
  36. data/spec/rails_spec.rb +77 -9
  37. data/spec/sinatra_spec.rb +6 -0
  38. data/spec/support.rb +5 -78
  39. data/spec/support/coverage.rb +18 -0
  40. data/spec/support/env.rb +6 -0
  41. data/spec/support/expand_matcher.rb +27 -0
  42. data/spec/support/match_matcher.rb +39 -0
  43. data/spec/support/pattern.rb +28 -0
  44. metadata +43 -43
  45. data/.test_queue_stats +0 -0
  46. data/lib/mustermann/ast.rb +0 -403
  47. data/spec/ast_spec.rb +0 -8
@@ -0,0 +1,28 @@
1
+ module Support
2
+ module Pattern
3
+ def pattern(pattern, options = nil, &block)
4
+ description = "pattern %p" % pattern
5
+
6
+ if options
7
+ description << " with options %p" % [options]
8
+ instance = described_class.new(pattern, options)
9
+ else
10
+ instance = described_class.new(pattern)
11
+ end
12
+
13
+ context description do
14
+ subject(:pattern) { instance }
15
+ its(:to_s) { should be == pattern }
16
+ its(:inspect) { should be == "#<#{described_class}:#{pattern.inspect}>" }
17
+ its(:names) { should be_an(Array) }
18
+
19
+ example 'string should be immune to external change' do
20
+ subject.to_s.replace "NOT THE PATTERN"
21
+ subject.to_s.should be == pattern
22
+ end
23
+
24
+ instance_eval(&block)
25
+ end
26
+ end
27
+ end
28
+ end
metadata CHANGED
@@ -1,36 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mustermann
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Konstantin Haase
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-27 00:00:00.000000000 Z
11
+ date: 2013-05-12 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rspec
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: sinatra
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,97 +41,85 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rack-test
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rake
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: yard
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: redcarpet
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: simplecov
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - '>='
124
109
  - !ruby/object:Gem::Version
125
110
  version: '0'
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: coveralls
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
- - - ! '>='
115
+ - - '>='
132
116
  - !ruby/object:Gem::Version
133
117
  version: '0'
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
- - - ! '>='
122
+ - - '>='
140
123
  - !ruby/object:Gem::Version
141
124
  version: '0'
142
125
  description: library implementing patterns that behave like regular expressions
@@ -145,11 +128,12 @@ executables: []
145
128
  extensions: []
146
129
  extra_rdoc_files:
147
130
  - README.md
131
+ - internals.md
148
132
  files:
149
133
  - .gitignore
150
134
  - .rspec
151
- - .test_queue_stats
152
135
  - .travis.yml
136
+ - .yardopts
153
137
  - Gemfile
154
138
  - LICENSE
155
139
  - README.md
@@ -157,8 +141,17 @@ files:
157
141
  - bench/capturing.rb
158
142
  - bench/simple_vs_sinatra.rb
159
143
  - bench/template_vs_addressable.rb
144
+ - internals.md
160
145
  - lib/mustermann.rb
161
- - lib/mustermann/ast.rb
146
+ - lib/mustermann/ast/compiler.rb
147
+ - lib/mustermann/ast/expander.rb
148
+ - lib/mustermann/ast/node.rb
149
+ - lib/mustermann/ast/parser.rb
150
+ - lib/mustermann/ast/pattern.rb
151
+ - lib/mustermann/ast/transformer.rb
152
+ - lib/mustermann/ast/translator.rb
153
+ - lib/mustermann/ast/tree_renderer.rb
154
+ - lib/mustermann/ast/validation.rb
162
155
  - lib/mustermann/error.rb
163
156
  - lib/mustermann/extension.rb
164
157
  - lib/mustermann/identity.rb
@@ -172,7 +165,6 @@ files:
172
165
  - lib/mustermann/template.rb
173
166
  - lib/mustermann/version.rb
174
167
  - mustermann.gemspec
175
- - spec/ast_spec.rb
176
168
  - spec/extension_spec.rb
177
169
  - spec/identity_spec.rb
178
170
  - spec/mustermann_spec.rb
@@ -184,34 +176,37 @@ files:
184
176
  - spec/simple_spec.rb
185
177
  - spec/sinatra_spec.rb
186
178
  - spec/support.rb
179
+ - spec/support/coverage.rb
180
+ - spec/support/env.rb
181
+ - spec/support/expand_matcher.rb
182
+ - spec/support/match_matcher.rb
183
+ - spec/support/pattern.rb
187
184
  - spec/template_spec.rb
188
185
  homepage: https://github.com/rkh/mustermann
189
186
  licenses:
190
187
  - MIT
188
+ metadata: {}
191
189
  post_install_message:
192
190
  rdoc_options: []
193
191
  require_paths:
194
192
  - lib
195
193
  required_ruby_version: !ruby/object:Gem::Requirement
196
- none: false
197
194
  requirements:
198
- - - ! '>='
195
+ - - '>='
199
196
  - !ruby/object:Gem::Version
200
197
  version: 2.0.0
201
198
  required_rubygems_version: !ruby/object:Gem::Requirement
202
- none: false
203
199
  requirements:
204
- - - ! '>='
200
+ - - '>='
205
201
  - !ruby/object:Gem::Version
206
202
  version: '0'
207
203
  requirements: []
208
204
  rubyforge_project:
209
- rubygems_version: 1.8.23
205
+ rubygems_version: 2.0.0
210
206
  signing_key:
211
- specification_version: 3
207
+ specification_version: 4
212
208
  summary: use patterns like regular expressions
213
209
  test_files:
214
- - spec/ast_spec.rb
215
210
  - spec/extension_spec.rb
216
211
  - spec/identity_spec.rb
217
212
  - spec/mustermann_spec.rb
@@ -223,5 +218,10 @@ test_files:
223
218
  - spec/simple_spec.rb
224
219
  - spec/sinatra_spec.rb
225
220
  - spec/support.rb
221
+ - spec/support/coverage.rb
222
+ - spec/support/env.rb
223
+ - spec/support/expand_matcher.rb
224
+ - spec/support/match_matcher.rb
225
+ - spec/support/pattern.rb
226
226
  - spec/template_spec.rb
227
227
  has_rdoc:
Binary file
@@ -1,403 +0,0 @@
1
- require 'mustermann/regexp_based'
2
- require 'strscan'
3
-
4
- module Mustermann
5
- # Superclass for pattern styles that parse an AST from the string pattern.
6
- # @abstract
7
- class AST < RegexpBased
8
- supported_options :capture, :except, :greedy, :space_matches_plus
9
-
10
- # @!visibility private
11
- class Node
12
- # @!visibility private
13
- attr_accessor :payload
14
-
15
- # Helper for creating a new instance and calling #parse on it.
16
- # @return [Node]
17
- # @!visibility private
18
- def self.parse(element = new, &block)
19
- element.tap { |n| n.parse(&block) }
20
- end
21
-
22
- # @!visibility private
23
- def initialize(payload = nil, **options)
24
- options.each { |key, value| public_send("#{key}=", value) }
25
- self.payload = payload
26
- end
27
-
28
- # Double dispatch helper for reading from the buffer into the payload.
29
- # @!visibility private
30
- def parse
31
- self.payload ||= []
32
- while element = yield
33
- payload << element
34
- end
35
- end
36
-
37
- # @return [String] regular expression corresponding to node
38
- # @!visibility private
39
- def compile(options)
40
- Array(payload).map { |e| e.compile(options) }.join
41
- end
42
-
43
- # @return [Node] This node after tree transformation. Might be self.
44
- # @!visibility private
45
- def transform
46
- self.payload = payload.transform if payload.respond_to? :transform
47
- return self unless Array === payload
48
-
49
- new_payload = []
50
- with_lookahead = []
51
-
52
- payload.each do |element|
53
- element = element.transform
54
- if with_lookahead.empty?
55
- list = element.expect_lookahead? ? with_lookahead : new_payload
56
- list << element
57
- elsif element.lookahead?
58
- with_lookahead << element
59
- else
60
- with_lookahead = [WithLookAhead.new(with_lookahead, false)] if element.separator? and with_lookahead.size > 1
61
- new_payload.concat(with_lookahead)
62
- new_payload << element
63
- with_lookahead.clear
64
- end
65
- end
66
-
67
- with_lookahead = [WithLookAhead.new(with_lookahead, true)] if with_lookahead.size > 1
68
- new_payload.concat(with_lookahead)
69
- @payload = new_payload
70
- self
71
- end
72
-
73
- # @!visibility private
74
- def separator?
75
- false
76
- end
77
-
78
- # @!visibility private
79
- def lookahead?(in_lookahead = false)
80
- false
81
- end
82
-
83
- # @!visibility private
84
- def expect_lookahead?
85
- false
86
- end
87
-
88
- # @return [String] Regular expression for matching the given character in all representations
89
- # @!visibility private
90
- def encoded(char, uri_decode, space_matches_plus)
91
- return Regexp.escape(char) unless uri_decode
92
- uri_parser = URI::Parser.new
93
- encoded = uri_parser.escape(char, /./)
94
- list = [uri_parser.escape(char), encoded.downcase, encoded.upcase].uniq.map { |c| Regexp.escape(c) }
95
- list << encoded('+', uri_decode, space_matches_plus) if space_matches_plus and char == " "
96
- "(?:%s)" % list.join("|")
97
- end
98
-
99
- # @return [Array<String>]
100
- # @!visibility private
101
- def capture_names
102
- return payload.capture_names if payload.respond_to? :capture_names
103
- return [] unless payload.respond_to? :map
104
- payload.map { |e| e.capture_names if e.respond_to? :capture_names }
105
- end
106
- end
107
-
108
- # @!visibility private
109
- class Char < Node
110
- # @!visibility private
111
- def compile(uri_decode: true, space_matches_plus: true, **options)
112
- encoded(payload, uri_decode, space_matches_plus)
113
- end
114
-
115
- # @!visibility private
116
- def lookahead?(in_lookahead = false)
117
- in_lookahead
118
- end
119
-
120
- # @return [String] regexp to be used in lookahead for semi-greedy capturing
121
- # @!visibility private
122
- def lookahead(ahead, options)
123
- ahead + compile(options)
124
- end
125
- end
126
-
127
- # @!visibility private
128
- class Separator < Node
129
- # @!visibility private
130
- def compile(options)
131
- Regexp.escape(payload)
132
- end
133
-
134
- # @!visibility private
135
- def separator?
136
- true
137
- end
138
- end
139
-
140
- # @!visibility private
141
- class Optional < Node
142
- # @return [String] regexp to be used in lookahead for semi-greedy capturing
143
- # @!visibility private
144
- def lookahead(ahead, options)
145
- payload.lookahead(ahead, options)
146
- end
147
-
148
- # @!visibility private
149
- def compile(options)
150
- "(?:%s)?" % payload.compile(options)
151
- end
152
-
153
- # @!visibility private
154
- def lookahead?(in_lookahead = false)
155
- payload.lookahead? true or payload.expect_lookahead?
156
- end
157
- end
158
-
159
- # @!visibility private
160
- class Group < Node
161
- # @!visibility private
162
- def initialize(payload = nil, **options)
163
- super(Array(payload), **options)
164
- end
165
-
166
- # @!visibility private
167
- def lookahead?(in_lookahead = false)
168
- return false unless payload[0..-2].all? { |e| e.lookahead? in_lookahead }
169
- payload.last.expect_lookahead? or payload.last.lookahead? in_lookahead
170
- end
171
-
172
- # @!visibility private
173
- def transform
174
- payload.size == 1 ? payload.first.transform : super
175
- end
176
-
177
- # @!visibility private
178
- def parse
179
- super
180
- rescue UnexpectedClosingGroup
181
- end
182
-
183
- # @return [String] regexp to be used in lookahead for semi-greedy capturing
184
- # @!visibility private
185
- def lookahead(ahead, options)
186
- payload.inject(ahead) { |a,e| e.lookahead(a, options) }
187
- end
188
- end
189
-
190
- # @!visibility private
191
- class Capture < Node
192
- # @!visibility private
193
- def expect_lookahead?
194
- true
195
- end
196
-
197
- # @!visibility private
198
- def parse
199
- self.payload ||= ""
200
- super
201
- end
202
-
203
- # @!visibility private
204
- def capture_names
205
- [name]
206
- end
207
-
208
- # @!visibility private
209
- def name
210
- raise CompileError, "capture name can't be empty" if payload.nil? or payload.empty?
211
- raise CompileError, "capture name must start with underscore or lower case letter" unless payload =~ /^[a-z_]/
212
- raise CompileError, "capture name can't be #{payload}" if payload == "splat" or payload == "captures"
213
- payload
214
- end
215
-
216
- # @return [String] regexp without the named capture
217
- # @!visibility private
218
- def pattern(capture: nil, **options)
219
- case capture
220
- when Symbol then from_symbol(capture, **options)
221
- when Array then from_array(capture, **options)
222
- when Hash then from_hash(capture, **options)
223
- when String then from_string(capture, **options)
224
- when nil then from_nil(**options)
225
- else capture
226
- end
227
- end
228
-
229
- # @return [String] regexp to be used in lookahead for semi-greedy capturing
230
- # @!visibility private
231
- def lookahead(ahead, options)
232
- ahead + pattern(lookahead: ahead, greedy: false, **options).to_s
233
- end
234
-
235
- # @!visibility private
236
- def compile(options)
237
- return pattern(options) if options[:no_captures]
238
- "(?<#{name}>#{compile(no_captures: true, **options)})"
239
- end
240
-
241
- private
242
-
243
- def qualified(string, greedy: true, **options)
244
- "#{string}+#{?? unless greedy}"
245
- end
246
-
247
- def default(**options)
248
- "[^/\\?#]"
249
- end
250
-
251
- def from_nil(**options)
252
- qualified(with_lookahead(default(**options), **options), **options)
253
- end
254
-
255
- def from_hash(hash, **options)
256
- entry = hash[name.to_sym]
257
- pattern(capture: entry, **options)
258
- end
259
-
260
- def from_array(array, **options)
261
- array = array.map { |e| pattern(capture: e, **options) }
262
- Regexp.union(*array)
263
- end
264
-
265
- def from_symbol(symbol, **options)
266
- qualified(with_lookahead("[[:#{symbol}:]]", **options), **options)
267
- end
268
-
269
- def from_string(string, uri_decode: true, space_matches_plus: true, **options)
270
- Regexp.new(string.chars.map { |c| encoded(c, uri_decode, space_matches_plus) }.join)
271
- end
272
-
273
- def with_lookahead(string, lookahead: nil, **options)
274
- return string unless lookahead
275
- "(?:(?!#{lookahead})#{string})"
276
- end
277
- end
278
-
279
- # @!visibility private
280
- class Splat < Capture
281
- # @!visibility private
282
- def expect_lookahead?
283
- false
284
- end
285
-
286
- # @!visibility private
287
- def name
288
- "splat"
289
- end
290
-
291
- # @!visibility private
292
- def pattern(options)
293
- ".*?"
294
- end
295
- end
296
-
297
- # @!visibility private
298
- class NamedSplat < Splat
299
- alias_method :name, :payload
300
- end
301
-
302
- # @!visibility private
303
- class WithLookAhead < Node
304
- # @!visibility private
305
- attr_accessor :head, :at_end
306
-
307
- # @!visibility private
308
- def initialize(payload, at_end)
309
- self.head, *self.payload = payload
310
- self.at_end = at_end
311
- end
312
-
313
- # @!visibility private
314
- def compile(options)
315
- lookahead = payload.inject('') { |l,e| e.lookahead(l, options) }
316
- lookahead << (at_end ? '$' : '/')
317
- head.compile(lookahead: lookahead, **options) + super
318
- end
319
- end
320
-
321
- # @!visibility private
322
- class Root < Node
323
- # @!visibility private
324
- attr_accessor :pattern
325
-
326
- # @!visibility private
327
- def self.parse(string, &block)
328
- root = new
329
- root.pattern = string
330
- super(root, &block).transform
331
- end
332
-
333
- # @!visibility private
334
- def capture_names
335
- super.flatten
336
- end
337
-
338
- # @!visibility private
339
- def check_captures
340
- names = capture_names
341
- names.delete("splat")
342
- raise CompileError, "can't use the same capture name twice" if names.uniq != names
343
- end
344
-
345
- # @!visibility private
346
- def compile(except: nil, **options)
347
- check_captures
348
- except &&= "(?!#{except}\\Z)"
349
- Regexp.new("\\A#{except}#{super(options)}\\Z")
350
- rescue CompileError => e
351
- e.message << ": #{pattern.inspect}"
352
- raise e
353
- end
354
- end
355
-
356
- # @!visibility private
357
- def parse(string)
358
- buffer = StringScanner.new(string)
359
- Root.parse(string) { parse_buffer(buffer) unless buffer.eos? }
360
- rescue ParseError => e
361
- e.message << " while parsing #{string.inspect}"
362
- raise e
363
- end
364
-
365
- # @!visibility private
366
- def compile(string, except: nil, **options)
367
- options[:except] = compile(except, no_captures: true, **options) if except
368
- parse(string).compile(options)
369
- end
370
-
371
- # @!visibility private
372
- def parse_buffer(buffer)
373
- parse_suffix(parse_element(buffer), buffer)
374
- end
375
-
376
- # @!visibility private
377
- def parse_element(buffer)
378
- raise NotImplementedError, 'subclass responsibility'
379
- end
380
-
381
- # @!visibility private
382
- def parse_suffix(element, buffer)
383
- element
384
- end
385
-
386
- # @!visibility private
387
- def unexpected(char, exception: ParseError)
388
- char = char.getch if char.respond_to? :getch
389
- char = "space" if char == " "
390
- raise exception, "unexpected #{char || "end of string"}"
391
- end
392
-
393
- # @!visibility private
394
- def expect(buffer, regexp, **options)
395
- regexp = Regexp.new Regexp.escape(regexp.to_str) unless regexp.is_a? Regexp
396
- string = buffer.scan(regexp) || unexpected(buffer, **options)
397
- regexp.names.any? ? regexp.match(string) : string
398
- end
399
-
400
- private :parse, :compile, :parse_buffer, :parse_element, :parse_suffix, :unexpected, :expect
401
- private_constant :Node, :Char, :Separator, :Optional, :Group, :Capture, :Splat, :NamedSplat, :WithLookAhead, :Root
402
- end
403
- end