raabro 0.9.0 → 1.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.
- data/CHANGELOG.txt +13 -0
- data/README.md +3 -0
- data/lib/raabro.rb +292 -74
- data/spec/all_spec.rb +54 -0
- data/spec/alt_spec.rb +92 -12
- data/spec/eseq_spec.rb +112 -0
- data/spec/jseq_spec.rb +67 -0
- data/spec/ren_spec.rb +43 -0
- data/spec/rep_spec.rb +28 -16
- data/spec/rex_spec.rb +22 -11
- data/spec/sample_xel_spec.rb +110 -0
- data/spec/seq_spec.rb +165 -187
- data/spec/spec_helper.rb +57 -0
- data/spec/str_spec.rb +20 -16
- data/spec/tree_spec.rb +47 -0
- metadata +9 -2
data/CHANGELOG.txt
ADDED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
|
2
2
|
# raabro
|
3
3
|
|
4
|
+
[](http://travis-ci.org/jmettraux/raabro)
|
5
|
+
[](http://badge.fury.io/rb/raabro)
|
6
|
+
|
4
7
|
A very dumb PEG parser library.
|
5
8
|
|
6
9
|
Son to [aabro](https://github.com/flon-io/aabro), grandson to [neg](https://github.com/jmettraux/neg).
|
data/lib/raabro.rb
CHANGED
@@ -26,25 +26,25 @@
|
|
26
26
|
|
27
27
|
module Raabro
|
28
28
|
|
29
|
-
VERSION = '0.
|
29
|
+
VERSION = '1.0.0'
|
30
30
|
|
31
31
|
class Input
|
32
32
|
|
33
33
|
attr_accessor :string, :offset
|
34
34
|
attr_reader :options
|
35
35
|
|
36
|
-
def initialize(string, options={})
|
36
|
+
def initialize(string, offset=0, options={})
|
37
37
|
|
38
38
|
@string = string
|
39
|
-
@offset = 0
|
40
|
-
@options = options
|
39
|
+
@offset = offset.is_a?(Hash) ? 0 : offset
|
40
|
+
@options = offset.is_a?(Hash) ? offset : options
|
41
41
|
end
|
42
42
|
|
43
43
|
def match(str_or_regex)
|
44
44
|
|
45
45
|
if str_or_regex.is_a?(Regexp)
|
46
46
|
m = @string[@offset..-1].match(str_or_regex)
|
47
|
-
m ? m[0].length : false
|
47
|
+
m && (m.offset(0).first == 0) ? m[0].length : false
|
48
48
|
else # String or whatever responds to #to_s
|
49
49
|
s = str_or_regex.to_s
|
50
50
|
l = s.length
|
@@ -71,124 +71,342 @@ module Raabro
|
|
71
71
|
@children = []
|
72
72
|
end
|
73
73
|
|
74
|
+
def successful_children
|
75
|
+
|
76
|
+
@children.select { |c| c.result == 1 }
|
77
|
+
end
|
78
|
+
|
79
|
+
def prune!
|
80
|
+
|
81
|
+
@children = successful_children
|
82
|
+
end
|
83
|
+
|
84
|
+
def shrink!
|
85
|
+
|
86
|
+
@children =
|
87
|
+
@children.inject([]) do |a, c|
|
88
|
+
a << c.shrink! if c.result == 1 && c.name
|
89
|
+
a
|
90
|
+
end
|
91
|
+
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def string
|
96
|
+
|
97
|
+
@input.string[@offset, @length]
|
98
|
+
end
|
99
|
+
|
100
|
+
def lookup(name)
|
101
|
+
|
102
|
+
name = name.to_s
|
103
|
+
|
104
|
+
return self if @name.to_s == name
|
105
|
+
@children.each { |c| if n = c.lookup(name); return n; end }
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def gather(name, acc=[])
|
110
|
+
|
111
|
+
name = name.to_s
|
112
|
+
|
113
|
+
if @name.to_s == name
|
114
|
+
acc << self
|
115
|
+
else
|
116
|
+
@children.each { |c| c.gather(name, acc) }
|
117
|
+
end
|
118
|
+
|
119
|
+
acc
|
120
|
+
end
|
121
|
+
|
74
122
|
def to_a(opts={})
|
75
123
|
|
124
|
+
opts = Array(opts).inject({}) { |h, e| h[e] = true; h } \
|
125
|
+
unless opts.is_a?(Hash)
|
126
|
+
|
76
127
|
cn =
|
77
128
|
opts[:leaves] && (@result == 1) && @children.empty? ?
|
78
|
-
|
129
|
+
string :
|
79
130
|
@children.collect { |e| e.to_a(opts) }
|
80
131
|
|
81
132
|
[ @name, @result, @offset, @length, @note, @parter, cn ]
|
82
133
|
end
|
83
|
-
end
|
84
134
|
|
85
|
-
|
135
|
+
def to_s(depth=0, io=StringIO.new)
|
86
136
|
|
87
|
-
|
137
|
+
io.print "\n" if depth > 0
|
138
|
+
io.print ' ' * depth
|
139
|
+
io.print "#{@result} #{@name.inspect} #{@offset},#{@length}"
|
140
|
+
io.print result == 1 && children.size == 0 ? ' ' + string.inspect : ''
|
88
141
|
|
89
|
-
|
90
|
-
r.result = 1
|
91
|
-
r.length = l
|
92
|
-
input.offset += l
|
93
|
-
end
|
142
|
+
@children.each { |c| c.to_s(depth + 1, io) }
|
94
143
|
|
95
|
-
|
144
|
+
depth == 0 ? io.string : nil
|
145
|
+
end
|
96
146
|
end
|
97
147
|
|
98
|
-
|
148
|
+
module ModuleMethods
|
99
149
|
|
100
|
-
|
101
|
-
end
|
150
|
+
def _match(name, input, parter, regex_or_string)
|
102
151
|
|
103
|
-
|
152
|
+
r = Raabro::Tree.new(name, parter, input)
|
104
153
|
|
105
|
-
|
106
|
-
|
154
|
+
if l = input.match(regex_or_string)
|
155
|
+
r.result = 1
|
156
|
+
r.length = l
|
157
|
+
input.offset += l
|
158
|
+
end
|
107
159
|
|
108
|
-
|
160
|
+
r
|
161
|
+
end
|
109
162
|
|
110
|
-
|
111
|
-
return method(parser) if parser.is_a?(Symbol)
|
163
|
+
def str(name, input, string)
|
112
164
|
|
113
|
-
|
114
|
-
|
165
|
+
_match(name, input, :str, string)
|
166
|
+
end
|
115
167
|
|
116
|
-
|
117
|
-
end
|
168
|
+
def rex(name, input, regex_or_string)
|
118
169
|
|
119
|
-
|
170
|
+
_match(name, input, :rex, Regexp.new(regex_or_string))
|
171
|
+
end
|
120
172
|
|
121
|
-
|
122
|
-
end
|
173
|
+
def _quantify(parser)
|
123
174
|
|
124
|
-
|
175
|
+
return nil if parser.is_a?(Symbol) && respond_to?(parser)
|
176
|
+
# so that :plus and co can be overriden
|
125
177
|
|
126
|
-
|
178
|
+
case parser
|
179
|
+
when '?', :q, :qmark then [ 0, 1 ]
|
180
|
+
when '*', :s, :star then [ 0, 0 ]
|
181
|
+
when '+', :p, :plus then [ 1, 0 ]
|
182
|
+
else nil
|
183
|
+
end
|
184
|
+
end
|
127
185
|
|
128
|
-
|
129
|
-
c = nil
|
186
|
+
def _narrow(parser)
|
130
187
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
188
|
+
raise ArgumentError.new("lone quantifier #{parser}") if _quantify(parser)
|
189
|
+
|
190
|
+
return parser if parser.is_a?(Method)
|
191
|
+
return method(parser) if parser.is_a?(Symbol)
|
192
|
+
|
193
|
+
k, m = parser.to_s.split('.')
|
194
|
+
k, m = [ Object, k ] unless m
|
195
|
+
|
196
|
+
Kernel.const_get(k).method(m)
|
135
197
|
end
|
136
198
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
else
|
141
|
-
input.offset = start
|
199
|
+
def _parse(parser, input)
|
200
|
+
|
201
|
+
_narrow(parser).call(input)
|
142
202
|
end
|
143
203
|
|
144
|
-
|
145
|
-
end
|
204
|
+
def seq(name, input, *parsers)
|
146
205
|
|
147
|
-
|
206
|
+
r = ::Raabro::Tree.new(name, :seq, input)
|
148
207
|
|
149
|
-
|
208
|
+
start = input.offset
|
209
|
+
c = nil
|
150
210
|
|
151
|
-
|
211
|
+
loop do
|
152
212
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
213
|
+
pa = parsers.shift
|
214
|
+
break unless pa
|
215
|
+
|
216
|
+
if q = _quantify(parsers.first)
|
217
|
+
parsers.shift
|
218
|
+
c = rep(nil, input, pa, *q)
|
219
|
+
r.children.concat(c.children)
|
220
|
+
else
|
221
|
+
c = _parse(pa, input)
|
222
|
+
r.children << c
|
223
|
+
end
|
224
|
+
|
225
|
+
break if c.result != 1
|
226
|
+
end
|
227
|
+
|
228
|
+
if c && c.result == 1
|
229
|
+
r.result = 1
|
230
|
+
r.length = input.offset - start
|
231
|
+
else
|
232
|
+
input.offset = start
|
233
|
+
end
|
234
|
+
|
235
|
+
r
|
157
236
|
end
|
158
237
|
|
159
|
-
|
160
|
-
|
161
|
-
|
238
|
+
def alt(name, input, *parsers)
|
239
|
+
|
240
|
+
greedy =
|
241
|
+
if parsers.last == true || parsers.last == false
|
242
|
+
parsers.pop
|
243
|
+
else
|
244
|
+
false
|
245
|
+
end
|
246
|
+
|
247
|
+
r = ::Raabro::Tree.new(name, greedy ? :altg : :alt, input)
|
248
|
+
|
249
|
+
start = input.offset
|
250
|
+
c = nil
|
251
|
+
|
252
|
+
parsers.each do |pa|
|
253
|
+
|
254
|
+
cc = _parse(pa, input)
|
255
|
+
r.children << cc
|
256
|
+
|
257
|
+
input.offset = start
|
258
|
+
|
259
|
+
if greedy
|
260
|
+
if cc.result == 1 && cc.length > (c ? c.length : -1)
|
261
|
+
c.result = 0 if c
|
262
|
+
c = cc
|
263
|
+
end
|
264
|
+
else
|
265
|
+
c = cc
|
266
|
+
break if c.result == 1
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
if c && c.result == 1
|
271
|
+
r.result = 1
|
272
|
+
r.length = c.length
|
273
|
+
input.offset = start + r.length
|
274
|
+
end
|
275
|
+
|
276
|
+
r.prune! if input.options[:prune]
|
277
|
+
|
278
|
+
r
|
162
279
|
end
|
163
280
|
|
164
|
-
|
165
|
-
|
281
|
+
def altg(name, input, *parsers)
|
282
|
+
|
283
|
+
alt(name, input, *parsers, true)
|
284
|
+
end
|
166
285
|
|
167
|
-
|
286
|
+
def rep(name, input, parser, min, max=0)
|
287
|
+
|
288
|
+
min = 0 if min == nil || min < 0
|
289
|
+
max = nil if max.nil? || max < 1
|
290
|
+
|
291
|
+
r = ::Raabro::Tree.new(name, :rep, input)
|
292
|
+
start = input.offset
|
293
|
+
count = 0
|
294
|
+
|
295
|
+
loop do
|
296
|
+
c = _parse(parser, input)
|
297
|
+
r.children << c
|
298
|
+
break if c.result != 1
|
299
|
+
count += 1
|
300
|
+
break if max && count == max
|
301
|
+
end
|
302
|
+
|
303
|
+
if count >= min && (max == nil || count <= max)
|
304
|
+
r.result = 1
|
305
|
+
r.length = input.offset - start
|
306
|
+
else
|
307
|
+
input.offset = start
|
308
|
+
end
|
309
|
+
|
310
|
+
r.prune! if input.options[:prune]
|
311
|
+
|
312
|
+
r
|
313
|
+
end
|
314
|
+
|
315
|
+
def ren(name, input, parser)
|
316
|
+
|
317
|
+
r = _parse(parser, input)
|
318
|
+
r.name = name
|
319
|
+
|
320
|
+
r
|
321
|
+
end
|
322
|
+
alias rename ren
|
168
323
|
|
169
|
-
|
170
|
-
max = nil if max.nil? || max < 1
|
324
|
+
def all(name, input, parser)
|
171
325
|
|
172
|
-
|
173
|
-
|
174
|
-
count = 0
|
326
|
+
start = input.offset
|
327
|
+
length = input.string.length - input.offset
|
175
328
|
|
176
|
-
|
177
|
-
c =
|
329
|
+
r = ::Raabro::Tree.new(name, :all, input)
|
330
|
+
c = _parse(parser, input)
|
178
331
|
r.children << c
|
179
|
-
|
180
|
-
|
181
|
-
|
332
|
+
|
333
|
+
if c.length < length
|
334
|
+
input.offset = start
|
335
|
+
else
|
336
|
+
r.result = 1
|
337
|
+
r.length = c.length
|
338
|
+
end
|
339
|
+
|
340
|
+
r
|
182
341
|
end
|
183
342
|
|
184
|
-
|
343
|
+
def eseq(name, input, startpa, eltpa, seppa=nil, endpa=nil)
|
344
|
+
|
345
|
+
jseq = false
|
346
|
+
|
347
|
+
if seppa.nil? && endpa.nil?
|
348
|
+
jseq = true
|
349
|
+
seppa = eltpa; eltpa = startpa; startpa = nil
|
350
|
+
end
|
351
|
+
|
352
|
+
start = input.offset
|
353
|
+
r = ::Raabro::Tree.new(name, jseq ? :jseq : :eseq, input)
|
185
354
|
r.result = 1
|
186
|
-
|
187
|
-
|
188
|
-
|
355
|
+
c = nil
|
356
|
+
|
357
|
+
if startpa
|
358
|
+
c = _parse(startpa, input)
|
359
|
+
r.children << c
|
360
|
+
r.result = 0 if c.result != 1
|
361
|
+
end
|
362
|
+
|
363
|
+
if r.result == 1
|
364
|
+
|
365
|
+
i = 1
|
366
|
+
count = 0
|
367
|
+
|
368
|
+
loop do
|
369
|
+
|
370
|
+
i = (i + 1) % 2
|
371
|
+
pa = i == 0 ? eltpa : seppa
|
372
|
+
|
373
|
+
c = _parse(pa, input)
|
374
|
+
r.children << c
|
375
|
+
|
376
|
+
break if c.result != 1
|
377
|
+
|
378
|
+
count += 1
|
379
|
+
end
|
380
|
+
|
381
|
+
r.result = 0 if jseq && count < 1
|
382
|
+
end
|
383
|
+
|
384
|
+
if r.result == 1 && endpa
|
385
|
+
c = _parse(endpa, input)
|
386
|
+
r.children << c
|
387
|
+
r.result = 0 if c.result != 1
|
388
|
+
end
|
389
|
+
|
390
|
+
if r.result == 1
|
391
|
+
r.length = input.offset - start
|
392
|
+
else
|
393
|
+
input.offset = start
|
394
|
+
end
|
395
|
+
|
396
|
+
r.prune! if input.options[:prune]
|
397
|
+
|
398
|
+
r
|
189
399
|
end
|
400
|
+
alias jseq eseq
|
401
|
+
end
|
402
|
+
extend ModuleMethods
|
403
|
+
|
404
|
+
def self.included(target)
|
190
405
|
|
191
|
-
|
406
|
+
target.instance_eval do
|
407
|
+
extend ::Raabro::ModuleMethods
|
408
|
+
extend self
|
409
|
+
end
|
192
410
|
end
|
193
411
|
end
|
194
412
|
|