rparsec2 1.1.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/lib/rparsec/context.rb +86 -0
- data/lib/rparsec/error.rb +28 -0
- data/lib/rparsec/expressions.rb +186 -0
- data/lib/rparsec/functors.rb +282 -0
- data/lib/rparsec/id_monad.rb +17 -0
- data/lib/rparsec/keywords.rb +114 -0
- data/lib/rparsec/locator.rb +40 -0
- data/lib/rparsec/misc.rb +106 -0
- data/lib/rparsec/monad.rb +63 -0
- data/lib/rparsec/operators.rb +106 -0
- data/lib/rparsec/parser.rb +893 -0
- data/lib/rparsec/parser_monad.rb +23 -0
- data/lib/rparsec/parsers.rb +624 -0
- data/lib/rparsec/token.rb +43 -0
- data/lib/rparsec.rb +7 -0
- metadata +59 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module RParsec
|
2
|
+
|
3
|
+
class ParserMonad
|
4
|
+
def fail msg
|
5
|
+
FailureParser.new(msg)
|
6
|
+
end
|
7
|
+
|
8
|
+
def value v
|
9
|
+
return Nil if v.nil?
|
10
|
+
ValueParser.new(v);
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind(v, &proc)
|
14
|
+
return v unless proc
|
15
|
+
BoundParser.new(v, proc);
|
16
|
+
end
|
17
|
+
|
18
|
+
def mplus(p1, p2)
|
19
|
+
PlusParser.new([p1, p2]);
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end # module
|
@@ -0,0 +1,624 @@
|
|
1
|
+
require 'rparsec/parser'
|
2
|
+
|
3
|
+
module RParsec
|
4
|
+
|
5
|
+
class FailureParser < Parser
|
6
|
+
init :msg
|
7
|
+
def _parse ctxt
|
8
|
+
return ctxt.failure(@msg)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ValueParser < Parser
|
13
|
+
init :value
|
14
|
+
def _parse ctxt
|
15
|
+
ctxt.retn @value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class LazyParser < Parser
|
20
|
+
init :block
|
21
|
+
def _parse ctxt
|
22
|
+
@block.call._parse ctxt
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Failures
|
27
|
+
def self.add_error(err, e)
|
28
|
+
return e if err.nil?
|
29
|
+
return err if e.nil?
|
30
|
+
cmp = compare_error(err, e)
|
31
|
+
return err if cmp > 0
|
32
|
+
return e if cmp < 0
|
33
|
+
err
|
34
|
+
# merge_error(err, e)
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
private
|
39
|
+
|
40
|
+
def get_first_element(err)
|
41
|
+
while err.kind_of?(Array)
|
42
|
+
err = err[0]
|
43
|
+
end
|
44
|
+
err
|
45
|
+
end
|
46
|
+
|
47
|
+
def compare_error(e1, e2)
|
48
|
+
e1, e2 = get_first_element(e1), get_first_element(e2)
|
49
|
+
return -1 if e1.index < e2.index
|
50
|
+
return 1 if e1.index > e2.index
|
51
|
+
0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
###############################################
|
57
|
+
#def merge_error(e1, e2)
|
58
|
+
# return e1 << e2 if e1.kind_of?(Array)
|
59
|
+
# [e1,e2]
|
60
|
+
#end
|
61
|
+
###############################################
|
62
|
+
class ThrowParser < Parser
|
63
|
+
init :symbol
|
64
|
+
def _parse _ctxt
|
65
|
+
throw @symbol
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class CatchParser < Parser
|
70
|
+
init :symbol, :parser
|
71
|
+
def _parse ctxt
|
72
|
+
interrupted = true
|
73
|
+
ok = false
|
74
|
+
catch @symbol do
|
75
|
+
ok = @parser._parse(ctxt)
|
76
|
+
interrupted = false
|
77
|
+
end
|
78
|
+
return ctxt.retn(@symbol) if interrupted
|
79
|
+
ok
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class PeekParser < Parser
|
84
|
+
init :parser
|
85
|
+
def _parse ctxt
|
86
|
+
ind = ctxt.index
|
87
|
+
return false unless @parser._parse ctxt
|
88
|
+
ctxt.index = ind
|
89
|
+
return true
|
90
|
+
end
|
91
|
+
def peek
|
92
|
+
self
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class AtomParser < Parser
|
97
|
+
init :parser
|
98
|
+
def _parse ctxt
|
99
|
+
ind = ctxt.index
|
100
|
+
return true if @parser._parse ctxt
|
101
|
+
ctxt.index = ind
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
def atomize
|
105
|
+
self
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class LookAheadSensitiveParser < Parser
|
110
|
+
def initialize(la = 1)
|
111
|
+
super()
|
112
|
+
@lookahead = la
|
113
|
+
end
|
114
|
+
def visible(ctxt, n)
|
115
|
+
ctxt.index - n < @lookahead
|
116
|
+
end
|
117
|
+
def lookahead(n)
|
118
|
+
raise ArgumentError, "lookahead number #{n} should be positive" unless n > 0
|
119
|
+
return self if n == @lookahead
|
120
|
+
withLookahead(n)
|
121
|
+
end
|
122
|
+
def not(msg = "#{self} unexpected")
|
123
|
+
NotParser.new(self, msg, @lookahead)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class NotParser < LookAheadSensitiveParser
|
128
|
+
def initialize(parser, msg, la = 1)
|
129
|
+
super(la)
|
130
|
+
@parser, @msg, @name = parser, msg, "~#{parser.name}"
|
131
|
+
end
|
132
|
+
def _parse ctxt
|
133
|
+
ind = ctxt.index
|
134
|
+
if @parser._parse ctxt
|
135
|
+
ctxt.index = ind
|
136
|
+
return ctxt.expecting(@msg)
|
137
|
+
end
|
138
|
+
return ctxt.retn(nil) if visible(ctxt, ind)
|
139
|
+
return false
|
140
|
+
end
|
141
|
+
def withLookahead(n)
|
142
|
+
NotParser.new(@parser, @msg, n)
|
143
|
+
end
|
144
|
+
def not()
|
145
|
+
@parser
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class ExpectParser < Parser
|
150
|
+
def initialize(parser, msg)
|
151
|
+
super()
|
152
|
+
@parser, @msg, @name = parser, msg, msg
|
153
|
+
end
|
154
|
+
def _parse ctxt
|
155
|
+
ind = ctxt.index
|
156
|
+
return true if @parser._parse ctxt
|
157
|
+
return false unless ind == ctxt.index
|
158
|
+
ctxt.expecting(@msg)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class PlusParser < LookAheadSensitiveParser
|
163
|
+
def initialize(alts, la = 1)
|
164
|
+
super(la)
|
165
|
+
@alts = alts
|
166
|
+
end
|
167
|
+
def _parse ctxt
|
168
|
+
ind, result, err = ctxt.index, ctxt.result, ctxt.error
|
169
|
+
for p in @alts
|
170
|
+
ctxt.reset_error
|
171
|
+
ctxt.index, ctxt.result = ind, result
|
172
|
+
return true if p._parse(ctxt)
|
173
|
+
return false unless visible(ctxt, ind)
|
174
|
+
err = Failures.add_error(err, ctxt.error)
|
175
|
+
end
|
176
|
+
ctxt.error = err
|
177
|
+
return false
|
178
|
+
end
|
179
|
+
def withLookahead(n)
|
180
|
+
PlusParser.new(@alts, n)
|
181
|
+
end
|
182
|
+
def plus other
|
183
|
+
PlusParser.new(@alts.dup << other, @lookahead).setName(name)
|
184
|
+
end
|
185
|
+
def_sig :plus, Parser
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
class AltParser < LookAheadSensitiveParser
|
190
|
+
def initialize(alts, la = 1)
|
191
|
+
super(la)
|
192
|
+
@alts, @lookahead = alts, la
|
193
|
+
end
|
194
|
+
def _parse ctxt
|
195
|
+
ind, result, err = ctxt.index, ctxt.result, ctxt.error
|
196
|
+
err_ind, err_pos = -1, -1
|
197
|
+
for p in @alts
|
198
|
+
ctxt.reset_error
|
199
|
+
ctxt.index, ctxt.result = ind, result
|
200
|
+
return true if p._parse(ctxt)
|
201
|
+
if ctxt.error.index > err_pos
|
202
|
+
err, err_ind, err_pos = ctxt.error, ctxt.index, ctxt.error.index
|
203
|
+
end
|
204
|
+
end
|
205
|
+
ctxt.index, ctxt.error = err_ind, err
|
206
|
+
return false
|
207
|
+
end
|
208
|
+
def withLookahead(n)
|
209
|
+
AltParser.new(@alts, n)
|
210
|
+
end
|
211
|
+
def | other
|
212
|
+
AltParser.new(@alts.dup << autobox_parser(other)).setName(name)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
class BestParser < Parser
|
218
|
+
init :alts, :longer
|
219
|
+
def _parse ctxt
|
220
|
+
best_result, best_ind = nil, -1
|
221
|
+
err_ind, err_pos = -1, -1
|
222
|
+
ind, result, err = ctxt.index, ctxt.result, ctxt.error
|
223
|
+
for p in @alts
|
224
|
+
ctxt.reset_error
|
225
|
+
ctxt.index, ctxt.result = ind, result
|
226
|
+
if p._parse(ctxt)
|
227
|
+
err, now_ind = nil, ctxt.index
|
228
|
+
if best_ind == -1 || (now_ind != best_ind && @longer == (now_ind > best_ind))
|
229
|
+
best_result, best_ind = ctxt.result, now_ind
|
230
|
+
end
|
231
|
+
elsif best_ind < 0 # no good match found yet.
|
232
|
+
if ctxt.error.index > err_pos
|
233
|
+
err_ind, err_pos = ctxt.index, ctxt.error.index
|
234
|
+
end
|
235
|
+
err = Failures.add_error(err, ctxt.error)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
if best_ind >= 0
|
239
|
+
ctxt.index = best_ind
|
240
|
+
return ctxt.retn(best_result)
|
241
|
+
else
|
242
|
+
ctxt.error, ctxt.index = err, err_ind
|
243
|
+
return false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class BoundParser < Parser
|
249
|
+
init :parser, :proc
|
250
|
+
def _parse ctxt
|
251
|
+
return false unless @parser._parse(ctxt)
|
252
|
+
@proc.call(ctxt.result)._parse ctxt
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class BoundnParser < Parser
|
257
|
+
init :parser, :proc
|
258
|
+
def _parse ctxt
|
259
|
+
return false unless @parser._parse(ctxt)
|
260
|
+
@proc.call(*ctxt.result)._parse ctxt
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class MapParser < Parser
|
265
|
+
init :parser, :proc
|
266
|
+
def _parse ctxt
|
267
|
+
return false unless @parser._parse(ctxt)
|
268
|
+
ctxt.result = @proc.call(ctxt.result)
|
269
|
+
true
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class MapnParser < Parser
|
274
|
+
init :parser, :proc
|
275
|
+
def _parse ctxt
|
276
|
+
return false unless @parser._parse(ctxt)
|
277
|
+
ctxt.result = @proc.call(*ctxt.result)
|
278
|
+
true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
class SequenceParser < Parser
|
283
|
+
init :parsers, :proc
|
284
|
+
def _parse ctxt
|
285
|
+
if @proc.nil?
|
286
|
+
for p in @parsers
|
287
|
+
return false unless p._parse(ctxt)
|
288
|
+
end
|
289
|
+
else
|
290
|
+
results = []
|
291
|
+
for p in @parsers
|
292
|
+
return false unless p._parse(ctxt)
|
293
|
+
results << ctxt.result
|
294
|
+
end
|
295
|
+
ctxt.retn(@proc.call(*results))
|
296
|
+
end
|
297
|
+
return true
|
298
|
+
end
|
299
|
+
def seq(other, &block)
|
300
|
+
# TypeChecker.check_arg_type Parser, other, :seq
|
301
|
+
SequenceParser.new(@parsers.dup << other, &block)
|
302
|
+
end
|
303
|
+
def_sig :seq, Parser
|
304
|
+
end
|
305
|
+
|
306
|
+
class FollowedParser < Parser
|
307
|
+
init :p1, :p2
|
308
|
+
def _parse ctxt
|
309
|
+
return false unless @p1._parse ctxt
|
310
|
+
result = ctxt.result
|
311
|
+
return false unless @p2._parse ctxt
|
312
|
+
ctxt.retn(result)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
class SatisfiesParser < Parser
|
317
|
+
init :pred, :expected
|
318
|
+
def _parse ctxt
|
319
|
+
elem = nil
|
320
|
+
if ctxt.eof || !@pred.call(elem = ctxt.current)
|
321
|
+
return ctxt.expecting(@expected)
|
322
|
+
end
|
323
|
+
ctxt.next
|
324
|
+
ctxt.retn elem
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
class AnyParser < Parser
|
329
|
+
def _parse ctxt
|
330
|
+
return ctxt.expecting if ctxt.eof
|
331
|
+
result = ctxt.current
|
332
|
+
ctxt.next
|
333
|
+
ctxt.retn result
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
class EofParser < Parser
|
338
|
+
init :msg
|
339
|
+
def _parse ctxt
|
340
|
+
return true if ctxt.eof
|
341
|
+
return ctxt.expecting(@msg)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
class RegexpParser < Parser
|
346
|
+
init :ptn, :msg
|
347
|
+
def _parse ctxt
|
348
|
+
scanner = ctxt.scanner
|
349
|
+
result = scanner.check @ptn
|
350
|
+
if result.nil?
|
351
|
+
ctxt.expecting(@msg)
|
352
|
+
else
|
353
|
+
ctxt.advance(scanner.matched_size)
|
354
|
+
ctxt.retn(result)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
class AreParser < Parser
|
360
|
+
init :vals, :msg
|
361
|
+
def _parse ctxt
|
362
|
+
if @vals.length > ctxt.available
|
363
|
+
return ctxt.expecting(@msg)
|
364
|
+
end
|
365
|
+
cur = 0
|
366
|
+
for cur in (0...@vals.length)
|
367
|
+
if @vals[cur] != ctxt.peek(cur)
|
368
|
+
return ctxt.expecting(@msg)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
ctxt.advance(@vals.length)
|
372
|
+
ctxt.retn @vals
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
class StringCaseInsensitiveParser < Parser
|
377
|
+
init :str, :msg
|
378
|
+
def _downcase c
|
379
|
+
case when c.ord >= ?A.ord && c.ord <= ?Z.ord then (c.ord + (?a.ord - ?A.ord)).chr else c end
|
380
|
+
end
|
381
|
+
private :_downcase
|
382
|
+
|
383
|
+
def _parse ctxt
|
384
|
+
if @str.length > ctxt.available
|
385
|
+
return ctxt.expecting(@msg)
|
386
|
+
end
|
387
|
+
cur = 0
|
388
|
+
for cur in (0...@str.length)
|
389
|
+
if _downcase(@str[cur]) != _downcase(ctxt.peek(cur))
|
390
|
+
return ctxt.expecting(@msg)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
result = ctxt.src[ctxt.index, @str.length]
|
394
|
+
ctxt.advance(@str.length)
|
395
|
+
ctxt.retn result
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
class FragmentParser < Parser
|
400
|
+
init :parser
|
401
|
+
def _parse ctxt
|
402
|
+
ind = ctxt.index
|
403
|
+
return false unless @parser._parse ctxt
|
404
|
+
ctxt.retn(ctxt.src[ind, ctxt.index - ind])
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
class TokenParser < Parser
|
409
|
+
init :symbol, :parser
|
410
|
+
def _parse ctxt
|
411
|
+
ind = ctxt.index
|
412
|
+
return false unless @parser._parse ctxt
|
413
|
+
raw = ctxt.result
|
414
|
+
raw = ctxt.src[ind, ctxt.index - ind] unless raw.kind_of? String
|
415
|
+
ctxt.retn(Token.new(@symbol, raw, ind))
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
class NestedParser < Parser
|
420
|
+
init :parser1, :parser2
|
421
|
+
def _parse ctxt
|
422
|
+
ind = ctxt.index
|
423
|
+
return false unless @parser1._parse ctxt
|
424
|
+
_run_nested(ind, ctxt, ctxt.result, @parser2)
|
425
|
+
end
|
426
|
+
private
|
427
|
+
def _run_nested(start, ctxt, src, parser)
|
428
|
+
ctxt.error = nil
|
429
|
+
new_ctxt = nil
|
430
|
+
if src.kind_of? String
|
431
|
+
new_ctxt = ParseContext.new(src)
|
432
|
+
return true if _run_parser parser, ctxt, new_ctxt
|
433
|
+
ctxt.index = start + new_ctxt.index
|
434
|
+
elsif src.kind_of? Array
|
435
|
+
new_ctxt = ParseContext.new(src)
|
436
|
+
return true if _run_parser parser, ctxt, new_ctxt
|
437
|
+
ctxt.index = start + _get_index(new_ctxt) unless new_ctxt.eof
|
438
|
+
else
|
439
|
+
new_ctxt = ParseContext.new([src])
|
440
|
+
return true if _run_parser parser, ctxt, new_ctxt
|
441
|
+
ctxt.index = ind unless new_ctxt.eof
|
442
|
+
end
|
443
|
+
ctxt.error.index = ctxt.index
|
444
|
+
false
|
445
|
+
end
|
446
|
+
def _get_index ctxt
|
447
|
+
cur = ctxt.current
|
448
|
+
return cur.index if cur.respond_to? :index
|
449
|
+
ctxt.index
|
450
|
+
end
|
451
|
+
def _run_parser parser, old_ctxt, new_ctxt
|
452
|
+
if parser._parse new_ctxt
|
453
|
+
old_ctxt.result = new_ctxt.result
|
454
|
+
true
|
455
|
+
else
|
456
|
+
old_ctxt.error = new_ctxt.error
|
457
|
+
false
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
class WatchParser < Parser
|
463
|
+
init :proc
|
464
|
+
def _parse ctxt
|
465
|
+
@proc.call(ctxt.result)
|
466
|
+
true
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
class WatchnParser < Parser
|
471
|
+
init :proc
|
472
|
+
def _parse ctxt
|
473
|
+
@proc.call(*ctxt.result)
|
474
|
+
true
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
class MapCurrentParser < Parser
|
479
|
+
init :proc
|
480
|
+
def _parse ctxt
|
481
|
+
ctxt.result = @proc.call(ctxt.result)
|
482
|
+
true
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
class MapnCurrentParser < Parser
|
487
|
+
init :proc
|
488
|
+
def _parse ctxt
|
489
|
+
ctxt.result = @proc.call(*ctxt.result)
|
490
|
+
true
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
class Repeat_Parser < Parser
|
495
|
+
init :parser, :times
|
496
|
+
def _parse ctxt
|
497
|
+
@times.times do
|
498
|
+
return false unless @parser._parse ctxt
|
499
|
+
end
|
500
|
+
return true
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
class RepeatParser < Parser
|
505
|
+
init :parser, :times
|
506
|
+
def _parse ctxt
|
507
|
+
result = []
|
508
|
+
@times.times do
|
509
|
+
return false unless @parser._parse ctxt
|
510
|
+
result << ctxt.result
|
511
|
+
end
|
512
|
+
return ctxt.retn(result)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
class Many_Parser < Parser
|
517
|
+
init :parser, :least
|
518
|
+
def _parse ctxt
|
519
|
+
@least.times do
|
520
|
+
return false unless @parser._parse ctxt
|
521
|
+
end
|
522
|
+
while true
|
523
|
+
ind = ctxt.index
|
524
|
+
if @parser._parse ctxt
|
525
|
+
return true if ind == ctxt.index # infinite loop
|
526
|
+
next
|
527
|
+
end
|
528
|
+
return ind == ctxt.index
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
class ManyParser < Parser
|
534
|
+
init :parser, :least
|
535
|
+
def _parse ctxt
|
536
|
+
result = []
|
537
|
+
@least.times do
|
538
|
+
return false unless @parser._parse ctxt
|
539
|
+
result << ctxt.result
|
540
|
+
end
|
541
|
+
while true
|
542
|
+
ind = ctxt.index
|
543
|
+
if @parser._parse ctxt
|
544
|
+
result << ctxt.result
|
545
|
+
return ctxt.retn(result) if ind == ctxt.index # infinite loop
|
546
|
+
next
|
547
|
+
end
|
548
|
+
if ind == ctxt.index
|
549
|
+
return ctxt.retn(result)
|
550
|
+
else
|
551
|
+
return false
|
552
|
+
end
|
553
|
+
end
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
class Some_Parser < Parser
|
558
|
+
init :parser, :least, :max
|
559
|
+
def _parse ctxt
|
560
|
+
@least.times { return false unless @parser._parse ctxt }
|
561
|
+
(@least...@max).each do
|
562
|
+
ind = ctxt.index
|
563
|
+
if @parser._parse ctxt
|
564
|
+
return true if ind == ctxt.index # infinite loop
|
565
|
+
next
|
566
|
+
end
|
567
|
+
return ind == ctxt.index
|
568
|
+
end
|
569
|
+
return true
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
class SomeParser < Parser
|
574
|
+
init :parser, :least, :max
|
575
|
+
def _parse ctxt
|
576
|
+
result = []
|
577
|
+
@least.times do
|
578
|
+
return false unless @parser._parse ctxt
|
579
|
+
result << ctxt.result
|
580
|
+
end
|
581
|
+
(@least...@max).each do
|
582
|
+
ind = ctxt.index
|
583
|
+
if @parser._parse ctxt
|
584
|
+
result << ctxt.result
|
585
|
+
return ctxt.retn(result) if ind == ctxt.index # infinite loop
|
586
|
+
next
|
587
|
+
end
|
588
|
+
if ind == ctxt.index
|
589
|
+
return ctxt.retn(result)
|
590
|
+
else
|
591
|
+
return false
|
592
|
+
end
|
593
|
+
end
|
594
|
+
return ctxt.retn(result)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
class OneParser < Parser
|
599
|
+
def _parse _ctxt
|
600
|
+
true
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
class ZeroParser < Parser
|
605
|
+
def _parse ctxt
|
606
|
+
return ctxt.failure
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
class GetIndexParser < Parser
|
611
|
+
def _parse ctxt
|
612
|
+
ctxt.retn(ctxt.index)
|
613
|
+
end
|
614
|
+
end
|
615
|
+
class SetIndexParser < Parser
|
616
|
+
init :index
|
617
|
+
def _parse ctxt
|
618
|
+
ctxt.index = @index
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
Nil = ValueParser.new(nil)
|
623
|
+
|
624
|
+
end # module
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rparsec/misc'
|
2
|
+
|
3
|
+
module RParsec
|
4
|
+
|
5
|
+
#
|
6
|
+
# Represents a token during lexical analysis.
|
7
|
+
#
|
8
|
+
class Token
|
9
|
+
extend DefHelper
|
10
|
+
|
11
|
+
def_ctor :kind, :text, :index
|
12
|
+
|
13
|
+
#
|
14
|
+
# The type of the token
|
15
|
+
#
|
16
|
+
attr_reader :kind
|
17
|
+
|
18
|
+
#
|
19
|
+
# The text of the matched range
|
20
|
+
#
|
21
|
+
attr_reader :text
|
22
|
+
|
23
|
+
#
|
24
|
+
# The starting index of the matched range
|
25
|
+
#
|
26
|
+
attr_reader :index
|
27
|
+
|
28
|
+
#
|
29
|
+
# The length of the token.
|
30
|
+
#
|
31
|
+
def length
|
32
|
+
@text.length
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# String representation of the token.
|
37
|
+
#
|
38
|
+
def to_s
|
39
|
+
"#{@kind}: #{@text}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end # module
|