grammar 0.5 → 0.8
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/benchmark/json.benchmark.rb +355 -0
- data/benchmark/json.grammar.rb +56 -0
- data/benchmark/json.grammar0_5.rb +57 -0
- data/benchmark/json.ll1.rb +155 -0
- data/benchmark/json.peggy.rb +174 -0
- data/benchmark/json.re.rb +81 -0
- data/lib/grammar.rb +212 -639
- data/lib/grammar/ruby.rb +606 -0
- data/lib/grammar/ruby/code.rb +1030 -0
- data/lib/grammar/ruby0.rb +521 -0
- data/lib/grammar/ruby2cext.rb +19 -0
- data/lib/grammar/rubycall.rb +21 -0
- data/test/advanced.rb +105 -0
- data/test/atoms.rb +77 -0
- data/test/basic.rb +32 -0
- data/test/composite.rb +147 -0
- data/test/molecules.rb +125 -0
- data/test/test_demo.rb +200 -0
- data/test/test_ruby.rb +30 -0
- data/test/test_ruby0.rb +30 -0
- data/test/test_ruby2cext.rb +30 -0
- data/test/test_rubycall.rb +30 -0
- metadata +45 -28
- data/samples/fact.tcl +0 -12
- data/samples/infix2postfix.rb +0 -114
- data/samples/tcl.rb +0 -163
- data/samples/test.infix +0 -4
- data/test/test_grammar.rb +0 -274
@@ -0,0 +1,521 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class Grammar
|
4
|
+
class Ruby0
|
5
|
+
|
6
|
+
def self.compile(gram)
|
7
|
+
lambda { |output, input| gram[new(output, &input)] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](object, clone=false)
|
11
|
+
clone ? object.clone : object
|
12
|
+
rescue
|
13
|
+
object
|
14
|
+
end
|
15
|
+
def steps(*steps)
|
16
|
+
steps[-1]
|
17
|
+
end
|
18
|
+
def or(first) # :yield:
|
19
|
+
first || yield
|
20
|
+
end
|
21
|
+
def and(first) # :yield:
|
22
|
+
first && yield
|
23
|
+
end
|
24
|
+
def not(operand)
|
25
|
+
!operand
|
26
|
+
end
|
27
|
+
def if(condition, yes, &no)
|
28
|
+
condition ? yes[] : no[]
|
29
|
+
end
|
30
|
+
def send(obj, method, *args, &block)
|
31
|
+
obj.__send__(method, *args, &block)
|
32
|
+
end
|
33
|
+
def send_splat(obj, method, *args, &block)
|
34
|
+
args.concat(args.pop)
|
35
|
+
obj.__send__(method, *args, &block)
|
36
|
+
end
|
37
|
+
def <<(item)
|
38
|
+
@output << item
|
39
|
+
end
|
40
|
+
def output # :yield: buf
|
41
|
+
block_given? ? (@output = yield(@output)) : @output
|
42
|
+
end
|
43
|
+
|
44
|
+
# these are mainly for internal usage
|
45
|
+
attr_reader(:input, :lookahead, :error)
|
46
|
+
|
47
|
+
class Void
|
48
|
+
def concat(other);self;end
|
49
|
+
def <<(other);self;end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Error < Exception
|
53
|
+
attr_reader(:consumed)
|
54
|
+
attr_reader(:expected)
|
55
|
+
attr_reader(:list)
|
56
|
+
attr_accessor(:raise)
|
57
|
+
def initialize(raise=true)
|
58
|
+
@list = []
|
59
|
+
@expected = []
|
60
|
+
@consumed = 0
|
61
|
+
@raise = raise
|
62
|
+
end
|
63
|
+
def [](expected, found)
|
64
|
+
@expected << expected if expected
|
65
|
+
if @raise
|
66
|
+
@list << [@expected, found]
|
67
|
+
@expected = []
|
68
|
+
Kernel.raise(self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def noraise
|
72
|
+
@raise
|
73
|
+
ensure
|
74
|
+
@raise = false
|
75
|
+
end
|
76
|
+
def concat(other)
|
77
|
+
@expected.concat(other.expected)
|
78
|
+
@list.concat(other.list)
|
79
|
+
end
|
80
|
+
def clear
|
81
|
+
@consumed += 1
|
82
|
+
@raise = true
|
83
|
+
@expected.clear
|
84
|
+
@list.clear
|
85
|
+
end
|
86
|
+
def each # :yield: expected, found
|
87
|
+
@list.each { |err|
|
88
|
+
yield(*err)
|
89
|
+
}
|
90
|
+
end
|
91
|
+
def to_str
|
92
|
+
s = ""
|
93
|
+
first = true
|
94
|
+
@list.each { |err|
|
95
|
+
s.concat(first ? "expected: " : "\nor: ")
|
96
|
+
first = true
|
97
|
+
err[0].each { |e|
|
98
|
+
s << ?| unless first
|
99
|
+
s.concat(e.to_s)
|
100
|
+
first = false
|
101
|
+
}
|
102
|
+
s.concat(", found: ").concat(err[1].to_s)
|
103
|
+
first = false
|
104
|
+
}
|
105
|
+
s
|
106
|
+
end
|
107
|
+
def to_s
|
108
|
+
to_str
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def initialize(output=nil, lookahead=nil, input_buffer=[],
|
113
|
+
error=self.class::Error.new, followed = 0, hold = false,
|
114
|
+
&input)
|
115
|
+
@output = output
|
116
|
+
@input = input
|
117
|
+
@lookahead = lookahead || (input && input[])
|
118
|
+
@input_buffer = input_buffer
|
119
|
+
@hold = hold
|
120
|
+
@followed = followed
|
121
|
+
@error = error
|
122
|
+
end
|
123
|
+
|
124
|
+
def fork(buf)
|
125
|
+
self.class.new(buf, @lookahead, @input_buffer,
|
126
|
+
@error.clone, @followed, @hold, &@input)
|
127
|
+
end
|
128
|
+
|
129
|
+
def join(engine)
|
130
|
+
@input = engine.input
|
131
|
+
@lookahead = engine.lookahead
|
132
|
+
@error = engine.error
|
133
|
+
end
|
134
|
+
|
135
|
+
def alternation(gram1) # :yield: engine
|
136
|
+
raise = @error.noraise
|
137
|
+
@followed += 1
|
138
|
+
begin
|
139
|
+
gram1[self]
|
140
|
+
ensure
|
141
|
+
@followed -= 1
|
142
|
+
raise1 = @error.raise
|
143
|
+
@error.raise = raise
|
144
|
+
end or yield(self)
|
145
|
+
ensure
|
146
|
+
@error.raise ||= raise1
|
147
|
+
end
|
148
|
+
|
149
|
+
def sequence(gram1) # :yield: engine
|
150
|
+
@followed += 1
|
151
|
+
begin
|
152
|
+
gram1[self]
|
153
|
+
ensure
|
154
|
+
@followed -= 1
|
155
|
+
end and begin
|
156
|
+
# TODO: keep going with hold if nothing expected yet
|
157
|
+
@hold ? true : yield(self)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def positive # :yield: engine
|
162
|
+
hold = @hold
|
163
|
+
begin
|
164
|
+
@hold = true
|
165
|
+
yield(self)
|
166
|
+
ensure
|
167
|
+
@hold = hold
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def negative # :yield: engine
|
172
|
+
hold = @hold
|
173
|
+
raise = @error.noraise
|
174
|
+
begin
|
175
|
+
@hold = true
|
176
|
+
not yield(self)
|
177
|
+
ensure
|
178
|
+
@error.raise = raise
|
179
|
+
@hold = hold
|
180
|
+
end or failure("something else")
|
181
|
+
end
|
182
|
+
|
183
|
+
# atomic parsing
|
184
|
+
|
185
|
+
def failure(expected=nil)
|
186
|
+
@error[expected, @lookahead]
|
187
|
+
end
|
188
|
+
def consume
|
189
|
+
@hold or (
|
190
|
+
@error.clear
|
191
|
+
@output << @lookahead
|
192
|
+
@lookahead = @input[]
|
193
|
+
true
|
194
|
+
)
|
195
|
+
end
|
196
|
+
def eof
|
197
|
+
!@lookahead or failure("EOF")
|
198
|
+
end
|
199
|
+
def any
|
200
|
+
@lookahead ? consume : failure("ANY")
|
201
|
+
end
|
202
|
+
def match(pattern)
|
203
|
+
(pattern===@lookahead) ? consume : failure(pattern)
|
204
|
+
end
|
205
|
+
def always # :yield: engine
|
206
|
+
yield(self)
|
207
|
+
end
|
208
|
+
|
209
|
+
# variables and objects
|
210
|
+
|
211
|
+
class Var
|
212
|
+
def initialize(val=nil)
|
213
|
+
@var = val
|
214
|
+
end
|
215
|
+
def []
|
216
|
+
@var
|
217
|
+
end
|
218
|
+
def <<(val)
|
219
|
+
@var = val
|
220
|
+
end
|
221
|
+
end
|
222
|
+
def variables(n=nil, &block) # :yield: *vars
|
223
|
+
vars = (0...(n||block.arity)).map { Var.new }
|
224
|
+
yield(*vars)[self]
|
225
|
+
end
|
226
|
+
|
227
|
+
def recurse(inner, &outer) # :yield: engine
|
228
|
+
inner0 = inner.to_proc
|
229
|
+
left = false
|
230
|
+
right = false
|
231
|
+
lloop = false
|
232
|
+
followed = @followed
|
233
|
+
consumed = @error.consumed
|
234
|
+
inner << Proc.new {
|
235
|
+
if @error.consumed==consumed
|
236
|
+
@followed!=followed or raise("something must follow left recursion")
|
237
|
+
left = true
|
238
|
+
lloop ? (@error.raise = true) : failure(nil)
|
239
|
+
elsif @followed==followed
|
240
|
+
right = true
|
241
|
+
true
|
242
|
+
else
|
243
|
+
recurse(inner, &outer)
|
244
|
+
end
|
245
|
+
}
|
246
|
+
yield(self) or return
|
247
|
+
@hold and return(true)
|
248
|
+
while true
|
249
|
+
while right
|
250
|
+
consumed = @error.consumed
|
251
|
+
right = false
|
252
|
+
@error.raise = true
|
253
|
+
yield(self) or raise("should have raised")
|
254
|
+
end
|
255
|
+
left or begin
|
256
|
+
!lloop or raise("left recursion not first")
|
257
|
+
return(true)
|
258
|
+
end
|
259
|
+
lloop = true
|
260
|
+
consumed = @error.consumed
|
261
|
+
begin
|
262
|
+
yield(self) or raise("should have raised")
|
263
|
+
rescue self.class::Error => err
|
264
|
+
err.equal?(@error) && err.consumed==consumed or raise(err)
|
265
|
+
return(true)
|
266
|
+
end
|
267
|
+
@error.consumed!=consumed or raise("infinite loop detected")
|
268
|
+
end
|
269
|
+
ensure
|
270
|
+
inner << inner0
|
271
|
+
end
|
272
|
+
|
273
|
+
def redirect(gram, buf0, &block) # :yield: buf[, engine]
|
274
|
+
buf = buf0.clone
|
275
|
+
output = @output
|
276
|
+
@followed += 1 if block_given?
|
277
|
+
begin
|
278
|
+
@output = buf
|
279
|
+
gram[self]
|
280
|
+
ensure
|
281
|
+
buf = @output
|
282
|
+
@followed -= 1 if block_given?
|
283
|
+
@output = output
|
284
|
+
end or return
|
285
|
+
if !@hold && block_given?
|
286
|
+
block.arity==1 ? yield(buf) : yield(buf, self)
|
287
|
+
end
|
288
|
+
true
|
289
|
+
end
|
290
|
+
|
291
|
+
def discard(gram) # :yield: engine
|
292
|
+
buf = Void.new
|
293
|
+
output = @output
|
294
|
+
@followed += 1 if block_given?
|
295
|
+
begin
|
296
|
+
@output = buf
|
297
|
+
gram[self]
|
298
|
+
ensure
|
299
|
+
@followed -= 1 if block_given?
|
300
|
+
@output = output
|
301
|
+
end or return
|
302
|
+
if !@hold && block_given?
|
303
|
+
@output << yield(self)
|
304
|
+
end
|
305
|
+
true
|
306
|
+
end
|
307
|
+
|
308
|
+
def backref(gram, &block) # :yield: n[, engine]
|
309
|
+
tainted = @output.tainted?
|
310
|
+
size0 = @output.taint.size
|
311
|
+
@followed += 1 if block_given?
|
312
|
+
begin
|
313
|
+
ret = gram[self]
|
314
|
+
ensure
|
315
|
+
@followed -= 1 if block_given?
|
316
|
+
n = @output.size-size0
|
317
|
+
if @hold || !ret
|
318
|
+
@output.slice!(size0, n)
|
319
|
+
end
|
320
|
+
end or return
|
321
|
+
if !@hold && block_given?
|
322
|
+
block.arity==1 ? yield(n) : yield(n, self)
|
323
|
+
end
|
324
|
+
true
|
325
|
+
ensure
|
326
|
+
@output.untaint unless tainted
|
327
|
+
end
|
328
|
+
|
329
|
+
def backtrack(gram, len=nil) # :yield: n[, engine]
|
330
|
+
tainted = @output.tainted?
|
331
|
+
return gram[self] if @error.raise
|
332
|
+
len ||= -1
|
333
|
+
hold = @hold
|
334
|
+
lookahead = @lookahead
|
335
|
+
error = @error.clone
|
336
|
+
buffer = @input_buffer.clone
|
337
|
+
size0 = @output.taint.size
|
338
|
+
@followed += 1 if block_given?
|
339
|
+
begin
|
340
|
+
len = -1 if hold
|
341
|
+
@hold = false
|
342
|
+
input = @input
|
343
|
+
len += 1
|
344
|
+
@input = lambda {
|
345
|
+
begin
|
346
|
+
c = input[]
|
347
|
+
ensure
|
348
|
+
if (len-=1).zero?
|
349
|
+
@input = input
|
350
|
+
buffer = nil
|
351
|
+
else
|
352
|
+
buffer << c
|
353
|
+
end
|
354
|
+
end
|
355
|
+
}
|
356
|
+
ret = gram[self]
|
357
|
+
rescue self.class::Error => err
|
358
|
+
@error = err
|
359
|
+
raise(err) if !buffer
|
360
|
+
ensure
|
361
|
+
@followed -= 1 if block_given?
|
362
|
+
@hold = hold
|
363
|
+
n = @output.size-size0
|
364
|
+
if hold || !ret
|
365
|
+
if buffer
|
366
|
+
error.concat(@error) if !ret
|
367
|
+
@lookahead = lookahead
|
368
|
+
@error = error
|
369
|
+
index = -1
|
370
|
+
@input = lambda { buffer[index+=1] || (@input=input)[] }
|
371
|
+
end
|
372
|
+
@output.slice!(size0, n)
|
373
|
+
return ret
|
374
|
+
end
|
375
|
+
end or return
|
376
|
+
if !@hold && block_given?
|
377
|
+
block.arity==1 ? yield(n) : yield(n, self)
|
378
|
+
end
|
379
|
+
true
|
380
|
+
ensure
|
381
|
+
@output.untaint unless tainted
|
382
|
+
end
|
383
|
+
|
384
|
+
def supply(tokenizer, parser, buf0, &block) # :yield: buf[, engine]
|
385
|
+
!@hold or raise("can't hold supply")
|
386
|
+
buf = buf0.clone
|
387
|
+
engine = fork(buf)
|
388
|
+
@followed += 1 if block_given?
|
389
|
+
begin
|
390
|
+
index = -1
|
391
|
+
@input = lambda {
|
392
|
+
buf[index+=1] or begin
|
393
|
+
buf.slice!(0, buf.size)
|
394
|
+
tokenizer[engine]
|
395
|
+
buf[index = 0]
|
396
|
+
end
|
397
|
+
}
|
398
|
+
@lookahead = input[]
|
399
|
+
parser[self]
|
400
|
+
ensure
|
401
|
+
@followed -= 1 if block_given?
|
402
|
+
lookahead = @lookahead
|
403
|
+
join(engine)
|
404
|
+
end or return
|
405
|
+
if block_given?
|
406
|
+
buf.slice!(0, index+1)
|
407
|
+
buf << lookahead
|
408
|
+
block.arity==1 ? yield(buf) : yield(buf, self)
|
409
|
+
end
|
410
|
+
true
|
411
|
+
end
|
412
|
+
|
413
|
+
class PipeBuffer
|
414
|
+
attr_accessor(:max_size)
|
415
|
+
def initialize(buf, mutex, cvar, max_size)
|
416
|
+
@buf = buf
|
417
|
+
@tmp_buf = nil
|
418
|
+
@mutex = mutex
|
419
|
+
@cvar = cvar
|
420
|
+
@max_size = max_size || -1
|
421
|
+
end
|
422
|
+
def _produce(arg)
|
423
|
+
@mutex.lock
|
424
|
+
begin
|
425
|
+
@buf << arg
|
426
|
+
@cvar.signal
|
427
|
+
ensure
|
428
|
+
@cvar.wait(@mutex) if @buf.size==@max_size
|
429
|
+
@mutex.unlock
|
430
|
+
end
|
431
|
+
self
|
432
|
+
end
|
433
|
+
alias_method(:<<, :_produce)
|
434
|
+
def taint
|
435
|
+
@tmp_buf ||= @buf.class.new
|
436
|
+
class << self
|
437
|
+
remove_method(:<<)
|
438
|
+
end
|
439
|
+
super
|
440
|
+
end
|
441
|
+
def untaint
|
442
|
+
class << self
|
443
|
+
alias_method(:<<, :_produce)
|
444
|
+
end
|
445
|
+
@tmp_buf.size.times { |i| self << @tmp_buf[i] }
|
446
|
+
@tmp_buf.slice!(0, @tmp_buf.size)
|
447
|
+
super
|
448
|
+
end
|
449
|
+
def method_missing(meth, *args, &block)
|
450
|
+
@tmp_buf.send(meth, *args, &block)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def pipe(lexer, parser, buf0, max_size=nil) # :yield: buf[, engine]
|
455
|
+
!@hold or raise("can't hold pipe")
|
456
|
+
buf = buf0.clone
|
457
|
+
mutex = Mutex.new
|
458
|
+
cvar = ConditionVariable.new
|
459
|
+
pbuf = PipeBuffer.new(buf, mutex, cvar, max_size)
|
460
|
+
engine = fork(pbuf)
|
461
|
+
producer = Thread.new {
|
462
|
+
begin
|
463
|
+
lexer[engine]
|
464
|
+
ensure
|
465
|
+
mutex.lock
|
466
|
+
cvar.signal
|
467
|
+
cvar = nil
|
468
|
+
mutex.unlock
|
469
|
+
end
|
470
|
+
}
|
471
|
+
@followed += 1 if block_given?
|
472
|
+
begin
|
473
|
+
index = 0
|
474
|
+
@input = lambda {
|
475
|
+
mutex.lock
|
476
|
+
begin
|
477
|
+
buf[index]
|
478
|
+
ensure
|
479
|
+
begin
|
480
|
+
if 2*(index+=1)>buf.size
|
481
|
+
buf.slice!(0, index)
|
482
|
+
index = 0
|
483
|
+
end
|
484
|
+
if cvar
|
485
|
+
cvar.signal
|
486
|
+
cvar.wait(mutex) if buf.empty?
|
487
|
+
end
|
488
|
+
ensure
|
489
|
+
mutex.unlock
|
490
|
+
end
|
491
|
+
end
|
492
|
+
}
|
493
|
+
mutex.lock
|
494
|
+
#mutex.sleep if buf.empty?
|
495
|
+
cvar.wait(mutex) if cvar && buf.empty?
|
496
|
+
mutex.unlock
|
497
|
+
@lookahead = input[]
|
498
|
+
parser[self]
|
499
|
+
ensure
|
500
|
+
@followed -= 1 if block_given?
|
501
|
+
mutex.lock
|
502
|
+
if cvar
|
503
|
+
pbuf.max_size = 1
|
504
|
+
cvar.wait(mutex) unless producer.stop?
|
505
|
+
producer.kill
|
506
|
+
end
|
507
|
+
mutex.unlock
|
508
|
+
lookahead = @lookahead
|
509
|
+
join(engine)
|
510
|
+
end or return
|
511
|
+
if block_given?
|
512
|
+
buf[0, index] = (buf.class.new << lookahead)
|
513
|
+
block.arity==1 ? yield(buf) : yield(buf, self)
|
514
|
+
end
|
515
|
+
true
|
516
|
+
end
|
517
|
+
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
|