reg 0.4.8 → 0.5.0a0

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.
Files changed (64) hide show
  1. checksums.yaml +4 -0
  2. data/COPYING +0 -0
  3. data/History.txt +14 -0
  4. data/Makefile +59 -0
  5. data/README +87 -40
  6. data/article.txt +838 -0
  7. data/{assert.rb → lib/assert.rb} +3 -3
  8. data/{reg.rb → lib/reg.rb} +11 -4
  9. data/lib/reg/version.rb +21 -0
  10. data/lib/regarray.rb +455 -0
  11. data/{regarrayold.rb → lib/regarrayold.rb} +33 -7
  12. data/lib/regbackref.rb +73 -0
  13. data/lib/regbind.rb +230 -0
  14. data/{regcase.rb → lib/regcase.rb} +15 -5
  15. data/lib/regcompiler.rb +2341 -0
  16. data/{regcore.rb → lib/regcore.rb} +196 -85
  17. data/{regdeferred.rb → lib/regdeferred.rb} +35 -4
  18. data/{regposition.rb → lib/regevent.rb} +36 -38
  19. data/lib/reggraphpoint.rb +28 -0
  20. data/lib/reghash.rb +631 -0
  21. data/lib/reginstrumentation.rb +36 -0
  22. data/{regitem_that.rb → lib/regitem_that.rb} +32 -11
  23. data/{regknows.rb → lib/regknows.rb} +4 -2
  24. data/{reglogic.rb → lib/reglogic.rb} +76 -59
  25. data/{reglookab.rb → lib/reglookab.rb} +31 -21
  26. data/lib/regmatchset.rb +323 -0
  27. data/{regold.rb → lib/regold.rb} +27 -27
  28. data/{regpath.rb → lib/regpath.rb} +91 -1
  29. data/lib/regposition.rb +79 -0
  30. data/lib/regprogress.rb +1522 -0
  31. data/lib/regrepeat.rb +307 -0
  32. data/lib/regreplace.rb +254 -0
  33. data/lib/regslicing.rb +581 -0
  34. data/lib/regsubseq.rb +72 -0
  35. data/lib/regsugar.rb +361 -0
  36. data/lib/regvar.rb +180 -0
  37. data/lib/regxform.rb +212 -0
  38. data/{trace.rb → lib/trace_during.rb} +6 -4
  39. data/lib/warning.rb +37 -0
  40. data/parser.txt +26 -8
  41. data/philosophy.txt +18 -0
  42. data/reg.gemspec +58 -25
  43. data/regguide.txt +18 -0
  44. data/test/andtest.rb +46 -0
  45. data/test/regcompiler_test.rb +346 -0
  46. data/test/regdemo.rb +20 -0
  47. data/{item_thattest.rb → test/regitem_thattest.rb} +2 -2
  48. data/test/regtest.rb +2125 -0
  49. data/test/test_all.rb +32 -0
  50. data/test/test_reg.rb +19 -0
  51. metadata +108 -73
  52. data/calc.reg +0 -73
  53. data/forward_to.rb +0 -49
  54. data/numberset.rb +0 -200
  55. data/regarray.rb +0 -675
  56. data/regbackref.rb +0 -126
  57. data/regbind.rb +0 -74
  58. data/reggrid.csv +1 -2
  59. data/reghash.rb +0 -318
  60. data/regprogress.rb +0 -1054
  61. data/regreplace.rb +0 -114
  62. data/regsugar.rb +0 -230
  63. data/regtest.rb +0 -1078
  64. data/regvar.rb +0 -76
@@ -0,0 +1,2341 @@
1
+ =begin copyright
2
+ reg - the ruby extended grammar
3
+ Copyright (C) 2016 Caleb Clausen
4
+
5
+ This library is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU Lesser General Public
7
+ License as published by the Free Software Foundation; either
8
+ version 2.1 of the License, or (at your option) any later version.
9
+
10
+ This library is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public
16
+ License along with this library; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ =end
19
+ require 'sequence/singleitem'
20
+ require 'thread'
21
+ require 'warning'
22
+
23
+ $EnableSlicings=nil
24
+
25
+ $bt_catch_method=:bt_stop
26
+ #$bt_catch_method=:catch
27
+ warning "not sure what $bt_catch_method should be set to"
28
+
29
+
30
+ module Reg
31
+ #--------------------------------------------------------------
32
+ module Reg
33
+ def cmatch_jit_compiler progress
34
+ gen_cmatch
35
+ cmatch(progress) {yield}
36
+ end
37
+ alias cmatch cmatch_jit_compiler
38
+
39
+ def bmatch_jit_compiler progress
40
+ gen_bmatch
41
+ bmatch progress
42
+ end
43
+ alias bmatch bmatch_jit_compiler
44
+
45
+ @@injects=0
46
+ def gen_cmatch
47
+ inject_code [
48
+ "undef cmatch ##{@@injects+=1}\n",
49
+ "def cmatch(progress) ##{self.inspect}\n",
50
+ make_new_cursor && [
51
+ $EnableSlicings && [
52
+ "outer_attempt=progress.match_attempt_starting(self)\n",
53
+ "progress.on_throw(:RegMatchFail, [:match_attempt_fail, outer_attempt])\n"
54
+ ].join,
55
+ "progress.startcontext(self,(#{make_new_cursor} rescue progress.throw))\n",
56
+ ].join,
57
+ "cu=progress.cursor\n",
58
+ $EnableSlicings && ["attempt=progress.match_attempt_starting(self)\n",
59
+ "progress.on_throw(:RegMatchFail, [:match_attempt_fail, attempt])\n"
60
+ ].join,
61
+ generate_cmatch.gsub(
62
+ /\byield\b/, [
63
+ $EnableSlicings && "progress.match_attempt_success(attempt,:subitemrange)\n",
64
+ make_new_cursor && "progress.endcontext\n",
65
+ $EnableSlicings && make_new_cursor && "progress.match_attempt_success(outer_attempt)\n",
66
+ "yield\n"
67
+ ].join
68
+ ),
69
+ "end\n"
70
+ ].join.gsub(/^\s*\n/m, '').gsub(/^\s*/, " ").gsub(/^\s*(un)?def\s/, " \\1def ")
71
+ end
72
+
73
+ def gen_bmatch
74
+ inject_code <<-"END".gsub(/^\s*\n/m, '').gsub(/^\s*/, " ")
75
+ undef bmatch ##{@@injects+=1}
76
+ def bmatch(progress) ##{self.inspect}
77
+ #{"outer_attempt=progress.match_attempt_starting(self)" if make_new_cursor and $EnableSlicings}
78
+ #{"if (progress.newcontext(self,#{make_new_cursor}) rescue false)" if make_new_cursor}
79
+ cu=progress.cursor
80
+ #{"attempt=progress.match_attempt_starting(self)" if $EnableSlicings}
81
+ result= begin #hmm.. maybe rename that var
82
+ #{generate_bmatch}
83
+ end
84
+ #{post_match}
85
+ #{"result ? progress.match_attempt_success(attempt,:subitemrange) : progress.match_attempt_fail(attempt)" if $EnableSlicings}
86
+ #{"end" if make_new_cursor}
87
+ #{"
88
+ if result : progress.match_attempt_success(outer_attempt)
89
+ else progress.move(-1); progress.match_attempt_fail(outer_attempt)
90
+ end
91
+ " if make_new_cursor and $EnableSlicings}
92
+
93
+ result
94
+ end
95
+ END
96
+ end
97
+
98
+ def generate_bmatch
99
+ " cu.skip(self)\n"
100
+ end
101
+
102
+ def generate_cmatch
103
+ " cu.skip(self) or progress.throw\n"+
104
+ " yield\n"
105
+ end
106
+
107
+ def make_new_cursor; end
108
+ def throw_guard; end
109
+ def post_match; end
110
+
111
+ if (defined? DEBUGGER__) || (defined? Debugger) #and $Debug
112
+ require 'tempfile'
113
+ warning "using weird hack to enable stepping in bmatch/cmatch"
114
+
115
+ def inject_code(str)
116
+ tf=Tempfile.new("regmatchcode.#{Thread.__id__}.#{@@injects}_")
117
+ Thread.current[:$reg_matcher]=self
118
+ tf.write "class <<Thread.current[:$reg_matcher]\n"
119
+ tf.write str
120
+ tf.write "end\n"
121
+ tf.flush
122
+ tf.rewind
123
+ load tf.path
124
+ tf.close
125
+ end
126
+ else
127
+ def inject_code(code)
128
+ instance_eval code
129
+ rescue SyntaxError
130
+ print code
131
+ raise
132
+ end
133
+ end
134
+
135
+ $RegSlicingEnable=nil #leave this off for now
136
+
137
+ def gen_start_slicing_code i,andword=''
138
+ return '' unless $Debug and $RegSlicingEnable
139
+ sltype=Slicing.for(@regs[i])
140
+ case sltype
141
+ when Slicing::Subseq; pre="sl="
142
+ when nil; sltype="nil"
143
+ else sltype=sltype.name.sub(/^Reg::/,'')
144
+ end
145
+ construct=".new(@regs_#{i})" unless sltype=="nil"
146
+ return ["progress.start_slicing(",pre,sltype,construct,",",i,") ",andword," \n"].to_s
147
+ end
148
+
149
+ def gen_finish_slicing_code i,andword=''
150
+ return '' unless $Debug and $RegSlicingEnable
151
+ sltype=Slicing.for(@regs[i])
152
+ Slicing::Subseq===sltype or return ''
153
+ "progress.finish_slicing(sl) #{andword} \n"
154
+ end
155
+
156
+ def subitemrange; itemrange end
157
+
158
+ #provide a default version of === that calls cmatch?
159
+
160
+ def match_method reg
161
+ case reg
162
+ when HasBmatch; "b"
163
+ when HasCmatch; "c"
164
+ else ""
165
+ end
166
+ end
167
+
168
+ def bp(condition=OB)
169
+ BP.new self,condition
170
+ end
171
+
172
+ def match(other)
173
+ # return super if is_a? Composite #yeccch. ruby includes modules in the wrong order
174
+ self===other and Progress.new(huh( 'empty')).huh #return new empty progress if ===(other)
175
+ end
176
+ end
177
+
178
+ class Progress
179
+
180
+ #---------------------------------------------
181
+ #remove_method :initialize
182
+ def initialize(matcher,cursor)
183
+ # @parent=nil #eliminate
184
+ # @matcher=matcher #move into Context
185
+ # @regsidx=0 #move into Context
186
+ # @cursor=cursor #move into Context
187
+ # @context_stack=[]
188
+
189
+ #context_stack is declasse. need @path and @oldpaths instead
190
+ @oldpaths=[]
191
+ @path=Path.new
192
+ assert !cursor.is_a?( ::Sequence::Position )
193
+ newcontext matcher, cursor
194
+ # @matchset_stack=[]
195
+ @matchfail_todo=[MatchFailRec.new] #list of things to do when match fails....
196
+ #undo(&adjust variables), matchsucceed, position, (matchset)
197
+ # @position_stack=[@cursor.pos] #r-list? of positions
198
+ @variables={}
199
+ @undos_stack=[] # list of undo procs and vars defined in this entire match
200
+ @matchsucceed_stack=[] #things to do when entire match succeeds... subst and deferreds
201
+
202
+ @child_threads=[]
203
+ warning "need to store root slicing and slicing path somewhere (if slicing enabled)"
204
+
205
+ # assert check_result
206
+ end
207
+
208
+ # def cursor; @path.last end
209
+ attr :cursor
210
+
211
+ def startcontext(matcher,data=@cursor)
212
+ assert !data.is_a?(::Sequence::Position)
213
+
214
+ @cursor=data
215
+ assert !@cursor.is_a?( ::Sequence::Position )
216
+ @path.push( data, matcher ) rescue (move(-1);throw)
217
+ on_throw(:RegMatchFail, [:move, -1],:endcontext)
218
+ end
219
+
220
+ undef newcontext
221
+ def newcontext(matcher,data=@cursor)
222
+ assert !data.is_a?(::Sequence::Position)
223
+ @cursor=data
224
+ assert !@cursor.is_a?( ::Sequence::Position )
225
+ @path.push data, matcher
226
+ end
227
+
228
+ undef endcontext
229
+ def endcontext
230
+ @path.pop
231
+ @cursor=@path.get_last_cursor
232
+ assert !@cursor.is_a?( ::Sequence::Position )
233
+ end
234
+
235
+ undef backup_stacks
236
+ def backup_stacks
237
+ assert(matchfail_todo.size >= 1)
238
+
239
+ discarding=matchfail_todo.pop
240
+
241
+ #backup undo stack and execute undos
242
+ discarding_undos=discarding.undos_inc
243
+ assert @undos_stack.size>=discarding_undos
244
+ process_undos @undos_stack.slice!(-discarding_undos..-1) if discarding_undos>0
245
+
246
+ #backup matchsucceed stack
247
+ discarding_succ=discarding.matchsucceed_inc
248
+ assert @matchsucceed_stack.size>=discarding_succ
249
+ @matchsucceed_stack.slice!(-discarding_succ..-1) if discarding_succ>0
250
+
251
+ end
252
+
253
+ #---------------------------------------------
254
+ def bt_stop
255
+ # push_match #this may have to move out into generated code...
256
+ nowpath=@path.dup
257
+ #does Sequence#dup actually return a Position? that would be bad.
258
+ assert !nowpath.is_a?( ::Sequence::Position )
259
+ assert( (oldsize=@oldpaths.size)>=0 )
260
+ @oldpaths.push nowpath.hibernate!
261
+ matchfail_todo.push MatchFailRec.new
262
+
263
+ #assert @posstack||=[]
264
+ #assert @posstack.push cursor,cursor.pos
265
+ #assert size=@posstack.size
266
+
267
+ result=catch{
268
+ yield
269
+ }
270
+
271
+ #...match failure in yield (or subsequently)
272
+ bt_backup
273
+ assert @oldpaths.size==oldsize
274
+
275
+ #assert size<=@posstack.size
276
+ #assert @posstack.slice!(size..-1)
277
+ #assert size==@posstack.size
278
+ #assert cursor.pos==@posstack.pop
279
+ #assert cursor.equal? @posstack.pop
280
+ result
281
+ end
282
+
283
+ #---------------------------------------------
284
+ def bt_backup
285
+ backup_stacks
286
+
287
+ #revert current slicings path to previous path
288
+ $EnableSlicings and huh
289
+
290
+ #empty @oldpaths implies there are no backtracking stops...
291
+ @path=@oldpaths.pop or return
292
+ @path.reawaken!
293
+ @cursor=@path.get_last_cursor
294
+ assert !@cursor.is_a?( ::Sequence::Position )
295
+ assert @path.ok
296
+ end
297
+ private :bt_backup
298
+
299
+ #---------------------------------------------
300
+ def match_attempt_starting mtr;
301
+ assert @match_attempts||=[]
302
+ assert @match_attempts<<[cursor,cursor.pos,mtr]
303
+ return( assert @match_attempts.size-1 )
304
+ end
305
+
306
+ #---------------------------------------------
307
+ def match_attempt_success attempt, itemrange_method=:itemrange;
308
+ assert attempt<@match_attempts.size
309
+ assert cursor.equal?( @match_attempts[attempt][0] )
310
+ assert @match_attempts[attempt].last.send(itemrange_method)===(cursor.pos - @match_attempts[attempt][1])
311
+ #assert @match_attempts.pop
312
+ end
313
+
314
+ #---------------------------------------------
315
+ def match_attempt_fail attempt;
316
+ assert attempt<@match_attempts.size
317
+ assert cursor.equal?( @match_attempts[attempt][0] )
318
+ assert cursor.pos == @match_attempts[attempt][1]
319
+ assert @match_attempts.slice!(attempt..-1)
320
+ end
321
+
322
+ #---------------------------------------------
323
+ def throw(event=:RegMatchFail,result=nil)
324
+ deleting=nil
325
+ assert @catchers.all?{|ctr| !ctr.nil?}
326
+ @catchers.size.-(1).downto(0){|i|
327
+ @catchers[i].first==event and break deleting=@catchers.slice!(i..-1)
328
+ }
329
+ deleting or raise "uncaught throw event: #{event}"
330
+
331
+ deleting.first.last.reverse_each{|m| send(*m) } #execute methods deferred by on_throw
332
+ deleting.reverse_each{|(aborted_event,onfail,*)|
333
+ # aborted_event==:RegMatchFail and event!=:RegMatchSucceed and bt_backup
334
+ onfail.each{|m| send(*m) }
335
+ }
336
+ if event==:RegMatchSucceed
337
+ kill_child_threads
338
+ process_laters
339
+ end
340
+ super(event,result)
341
+ end
342
+
343
+ #---------------------------------------------
344
+ def catch(event=:RegMatchFail,*onfail,&block)
345
+ @catchers||=[]
346
+ @catchers.push [event,onfail,block,[]]
347
+ assert @catchers.last
348
+ super(event,&block)
349
+ end
350
+
351
+ #---------------------------------------------
352
+ def on_throw(event,*onfail)
353
+ @catchers.reverse_each{|catcher|
354
+ if catcher.first==event
355
+ catcher.last.push(*onfail)
356
+ break
357
+ end
358
+ } and raise ArgumentError
359
+ end
360
+
361
+ #---------------------------------------------
362
+ attr :child_threads
363
+
364
+ def kill_child_threads
365
+ @child_threads.each{|thr| thr.kill}
366
+ @child_threads=nil
367
+ end
368
+
369
+
370
+
371
+ class Path
372
+ #a path is basically a stack of ::Sequence::Position
373
+ def initialize(*elems)
374
+ @list=elems.map!{|seq| [seq, seq.pos]}.flatten
375
+ @list[-1]=nil unless @list.empty?
376
+ assert ok
377
+ end
378
+
379
+ def ok
380
+ return true if @list.empty?
381
+ 0.step(@list.size-4,2){|i|
382
+ assert @list[i].is_a?( ::Sequence )
383
+ assert @list[i].position?( @list[i+1] )
384
+ }
385
+ assert @list[-2].is_a?( ::Sequence )
386
+ assert !hibernating?||@list[-2].position?( @list[-1] )
387
+ end
388
+
389
+ def push(datum,matcher=nil)
390
+ assert ok
391
+ assert !datum.kind_of?( ::Sequence::Position )
392
+ @list[-1]=@list[-2].pos unless @list.empty?
393
+ @list.push datum, nil
394
+ assert ok
395
+ #assert datum.pos==@list.last.pos
396
+ return self
397
+ end
398
+
399
+ def pop
400
+ assert ok
401
+ result,pos=@list.slice!(-2,2)
402
+ assert pos.nil? #this fails, but extremely rarely... why?
403
+ return result if @list.empty?
404
+ @list[-2].pos=@list[-1]
405
+ @list[-1]=nil
406
+ assert ok
407
+ return result
408
+ end
409
+
410
+ def dup
411
+ assert ok
412
+ result=super
413
+ result.instance_variable_set(:@list,@list.dup)
414
+ assert ok
415
+ assert result.ok
416
+ return result
417
+ end
418
+
419
+ extend Forwardable
420
+ def size; @list.size>>1 end
421
+
422
+ def get_last_cursor
423
+ result=@list[-2]
424
+ result
425
+ end
426
+
427
+ def hibernate!
428
+ assert !hibernating?
429
+ assert ok
430
+ @list[-1]=@list[-2].pos unless @list.empty?
431
+ assert hibernating?
432
+ assert ok
433
+ self
434
+ end
435
+
436
+ def reawaken!
437
+ assert hibernating?
438
+ assert ok
439
+ return self if @list.empty?
440
+ @list[-2].pos=@list[-1]
441
+ @list[-1]=nil
442
+ assert !hibernating?
443
+ assert ok
444
+ self
445
+ end
446
+
447
+ def hibernating?
448
+ !@list.empty? and
449
+ !@list.last.nil?
450
+ end
451
+
452
+ =begin
453
+ def revert_cursors_from(otherpath)
454
+ i=nil
455
+ 0.upto(@list.size){|i|
456
+ huh #not sure if equality defined correctly for xcuror::position
457
+ @list[i]!=otherpath[i] and break
458
+ }
459
+
460
+ i.upto(@list.size){|j|
461
+ @list[j].data.pos=@list[j].pos
462
+ }
463
+
464
+ huh
465
+ end
466
+ =end
467
+ #attr :list
468
+
469
+
470
+ end
471
+
472
+ end
473
+
474
+ #--------------------------------------------------------------
475
+ module Multiple
476
+ undef ===
477
+ def ===(other)
478
+ itemrange===1 or return
479
+ pr=Progress.new self, ::Sequence::SingleItem[other]
480
+ pr.catch( :RegMatchSucceed ){pr.send($bt_catch_method){
481
+ cmatch(pr) {pr.throw(:RegMatchSucceed, true)}
482
+ }}
483
+ end
484
+ # undef maybe_multiple
485
+ end
486
+
487
+
488
+ #--------------------------------------------------------------
489
+ module Composite
490
+ undef at_construct_time
491
+ def at_construct_time(*args)
492
+ multiple_infection(*args)
493
+ undoable_infection
494
+ b_c_match_infection
495
+ cmatch_and_bound_infection
496
+ end
497
+
498
+ undef multiple_infection
499
+ def multiple_infection(*regs)
500
+ regs.empty? and regs=subregs
501
+ unless regs.grep(Undoable).empty? or ::Reg::Hash===self or ::Reg::Object===self
502
+ extend Multiple
503
+ end
504
+ #Reg::Array overrides this to do nothing
505
+ #Multiples in the #subregs of Hash,Object,RestrictHash,Case are prohibited
506
+ end
507
+
508
+ undef undoable_infection
509
+ def undoable_infection
510
+ unless subregs.grep(Undoable).empty? or ::Reg::Hash===self or ::Reg::Object===self
511
+ extend Undoable
512
+ end
513
+ end
514
+
515
+ def b_c_match_infection
516
+ (is_a?(HasCmatch) || subregs.find{|reg|HasCmatch===reg}) && extend(
517
+ # unless is_a?(Multiple)
518
+ # class<<self; alias generate_bmatch default_generate_bmatch end
519
+ # HasBmatch
520
+ # else
521
+ HasCmatch
522
+ # end
523
+ ) or
524
+ subregs.find{|reg|HasBmatch===reg} && extend(HasBmatch)
525
+ assert((not( (HasCmatch===self)&(HasBmatch===self) ))) #can't be both at once
526
+ end
527
+
528
+ def cmatch_and_bound_infection
529
+ unless subregs.grep(HasCmatch_And_Bound).empty?
530
+ extend HasCmatch_And_Bound, HasCmatch
531
+ end
532
+ end
533
+
534
+ def match(other)
535
+ itemrange===1 or return
536
+ #create a new progress with other as toplevel context
537
+ pr=Progress.new self, ::Sequence::SingleItem[other]
538
+ #cmatch and return progress
539
+ pr.catch( :RegMatchSucceed ){pr.send($bt_catch_method){
540
+ cmatch(pr) {pr.throw(:RegMatchSucceed, true)}
541
+ }} and pr
542
+ end
543
+ end
544
+
545
+
546
+
547
+ #--------------------------------------------------------------
548
+ module CompileUtils
549
+
550
+ #explode @regs into @regs_0..@regs_#{@regs.size-1}
551
+ def explode_regs(regs=@regs)
552
+ instance_eval((0...regs.size).map{|i|
553
+ "@regs_#{i}"
554
+ }.join(',')+",* = *regs\n") unless regs.empty?
555
+ end
556
+ end
557
+ #--------------------------------------------------------------
558
+ #these are the default forwarding definitions of bmatch and cmatch
559
+ #they forward to each other, so at least one of these methods must be overridden!
560
+ WrapBmatch=proc do
561
+ define_method :generate_bmatch do#forward to cmatch
562
+ #ensure that we aren't using the default version of both match methods,
563
+ #which results in disastrous infinite mutual recursion
564
+ @_generated_default_match||="b"
565
+ @_generated_default_match!="b" and raise NoMethodError
566
+ <<-END
567
+ progress.#{$bt_catch_method} do
568
+ cmatch(progress){break true}
569
+ end
570
+ END
571
+ end
572
+
573
+ alias_method :default_generate_bmatch, :generate_bmatch
574
+ end
575
+
576
+ #--------------------------------------------------------------
577
+ WrapCmatch=proc do
578
+ define_method :generate_cmatch do#forward to bmatch
579
+ #ensure that we aren't using the default version of both match methods,
580
+ #which results in disastrous infinite mutual recursion
581
+ @_generated_default_match||="c"
582
+ @_generated_default_match!="c" and raise NoMethodError
583
+ <<-END
584
+ if bmatch progress
585
+ yield
586
+ else
587
+ progress.throw
588
+ end
589
+ END
590
+ end
591
+
592
+ alias_method :default_generate_cmatch, :generate_cmatch
593
+ end
594
+
595
+
596
+ #--------------------------------------------------------------
597
+ module HasCmatch
598
+ include CompileUtils
599
+
600
+ end
601
+
602
+ #--------------------------------------------------------------
603
+ module HasBmatch
604
+ #include HasCmatch #we have one, but we don't like to talk about it...
605
+ include CompileUtils
606
+ end
607
+
608
+ #--------------------------------------------------------------
609
+ module Composite
610
+ include CompileUtils #is it really so simple? idunno.....
611
+ end
612
+
613
+ #--------------------------------------------------------------
614
+ warning "need to extend all Composite patterns with Has[BC]match in initialize()"
615
+ warning "need to call gen_cmatch and gen_bmatch at right times"
616
+
617
+ #--------------------------------------------------------------
618
+ class BP
619
+ include Reg,Composite,CompileUtils
620
+ def initialize(reg,condition)
621
+ @reg=reg
622
+ @condition=condition
623
+ super
624
+ end
625
+
626
+ def generate_cmatch
627
+ ((defined? DEBUGGER__ or defined? Debugger)&&" Process.kill('INT',0) if @condition===other\n").to_s+
628
+ " @reg.cmatch(progress) {yield}\n"
629
+ end
630
+
631
+ def generate_bmatch
632
+ ((defined? DEBUGGER__ or defined? Debugger)&&" Process.kill('INT',0) if @condition===other\n").to_s+
633
+ " @reg.bmatch(progress)\n"
634
+ end
635
+
636
+ if defined? DEBUGGER__ or defined? Debugger
637
+ def ===(other)
638
+ Process.kill('INT',0) if @condition===other
639
+ @reg===other
640
+ end
641
+ else
642
+ def ===(other)
643
+ @reg===other
644
+ end
645
+ end
646
+ end
647
+
648
+ #--------------------------------------------------------------
649
+ class Array
650
+ include Composite
651
+ include CompileUtils
652
+ instance_eval(&WrapBmatch)
653
+
654
+
655
+ #on_throw(...,[move,-1]) in gen_cmatch undoes that read1 call
656
+ def make_new_cursor; "progress.cursor.read1.to_sequence" end
657
+ def throw_guard; "progress.on_throw(:RegMatchFail, :endcontext)\n" end
658
+ def post_match; "progress.endcontext\n" end
659
+
660
+ def generate_cmatch
661
+ # self.is_a? HasBmatch and return super #cant use return here
662
+ generate_cmatch_simple(@regs,"cu.eof? or progress.throw;\n")
663
+ end
664
+
665
+ def generate_bmatch
666
+ generate_bmatch_simple(@regs,"cu.eof?\n")
667
+ end
668
+
669
+ def generate_cmatch_simple(regs=@regs,preyield="")
670
+ begin
671
+ explode_regs(regs)
672
+
673
+ braces=0
674
+ result=generate_matchlines(regs,"or progress.throw") {
675
+ braces+=1
676
+ } + [block_given??yield : nil,
677
+ #"p :arr_subseq_preyield\n",
678
+ " #{preyield} yield\n",
679
+ " #{'}'*braces}\n",
680
+ ]
681
+ result.join
682
+ end
683
+ end
684
+
685
+ def generate_bmatch_simple(regs=@regs,presucceed='')
686
+ begin
687
+ explode_regs(regs)
688
+
689
+ "origpos=cu.pos\nbegin\n"+
690
+ generate_matchlines(regs){
691
+ raise "no cmatches here!"
692
+ }.join.sub(/ and *\n$/m, "\n")+
693
+ presucceed+
694
+ "end or (cu.pos=origpos;nil)\n"
695
+ end
696
+ end
697
+
698
+
699
+ def generate_matchlines(regs=@regs,andword="and")
700
+ regs.empty? and return ["true\n"]
701
+ #["p :arr_subseq_begin\n"]+
702
+ (0...regs.size).map{|i|
703
+ gen_start_slicing_code(i,andword) +
704
+ case match_method regs[i]
705
+ when "c"
706
+ yield
707
+ " @regs_#{i}.cmatch(progress) {\n"
708
+ when "b"
709
+ " @regs_#{i}.bmatch progress #{andword} \n"
710
+ else
711
+ " cu.skip @regs_#{i} #{andword} \n"
712
+ end
713
+ }
714
+ end
715
+
716
+ undef ===
717
+ def ===(other)
718
+ pr=Progress.new self, ::Sequence::SingleItem[other]
719
+ pr.catch( :RegMatchSucceed ){
720
+ pr.send($bt_catch_method){
721
+ cmatch(pr) {
722
+ pr.throw(:RegMatchSucceed, true)
723
+ }}}
724
+ end
725
+ end
726
+
727
+
728
+
729
+ #--------------------------------------------------------------
730
+ class Subseq
731
+ #remove_method :initialize
732
+ def initialize(*args) #override version in regarray.rb
733
+ super
734
+ end
735
+
736
+ def make_new_cursor; end
737
+ def throw_guard; end
738
+ def post_match; end
739
+
740
+ def at_construct_time(*)
741
+ super
742
+ HasCmatch===self or extend HasBmatch
743
+ end
744
+
745
+ alias generate_bmatch generate_bmatch_simple
746
+ alias generate_cmatch generate_cmatch_simple
747
+
748
+ end
749
+
750
+ #--------------------------------------------------------------
751
+ class Repeat
752
+ include CompileUtils
753
+
754
+ #remove_method :initialize
755
+ def initialize(reg,times)
756
+ Integer===times and times=times..times
757
+ times.exclude_end? and times=times.begin..times.end-1
758
+ assert times.begin <= times.end
759
+ assert times.begin < Infinity
760
+ assert times.begin >= 0
761
+ assert times.end >= 0
762
+ unless HasBmatch===reg || HasCmatch===reg
763
+ assert reg.itemrange==(1..1)
764
+ @itemrange=times
765
+ end
766
+ @reg,@times=reg,times
767
+ super
768
+ end
769
+
770
+ def at_construct_time(*)
771
+ (@times.begin<@times.end) and extend HasCmatch
772
+ super
773
+ HasCmatch===self or extend( HasBmatch )
774
+ end
775
+
776
+ def generate_bmatch
777
+ assert !@times.exclude_end?
778
+ assert @times.begin==@times.end
779
+ @times.begin.zero? and return ["true\n"]
780
+
781
+ "origpos=cu.pos\n" +
782
+ if @times.begin<=4
783
+ (matchline+"\n")*@times.begin.-(1) +
784
+ matchline(' or (cu.pos=origpos;nil)')+"\n"
785
+ else
786
+ "#{@times.begin}.times{ "+matchline(' or break(cu.pos=origpos)')+" }\n"
787
+ end
788
+ end
789
+
790
+ def matchline andword="and"
791
+ case(method=match_method @reg)
792
+ when "c"; "@reg.cmatch(progress) {"
793
+ when "b"; "@reg.bmatch progress #{andword}"
794
+ else "cu.skip @reg #{andword}"
795
+ end
796
+ end
797
+
798
+ def generate_cmatch
799
+ ir=@reg.itemrange
800
+ if "b"==match_method(@reg) and !@reg.is_a? Undoable and ir.begin==ir.end
801
+ assert(@times.begin!=@times.end)
802
+ #fixed iterations
803
+ result=if @times.begin<=2
804
+ [matchline("or progress.throw\n")]*@times.begin
805
+ else
806
+ ["#{@times.begin}.times{ #{matchline("or progress.throw")} }\n"]
807
+ end
808
+ #varying iterations
809
+ case variation=@times.end-@times.begin
810
+ when 0
811
+ when 1; result+=["progress.bt_stop{\n", matchline("or progress.throw\n"), "yield\n", "}\n", "yield\n"]
812
+ when Infinity;
813
+ iterline="count=0.upto(Infinity){|i| \n"
814
+ else
815
+ iterline="count=#{variation}.times{|i| \n"
816
+ end
817
+ iterline and \
818
+ result+=
819
+ ["oldpos=cu.pos\n",
820
+ iterline,
821
+ matchline("or break(i)\n"),
822
+ "}.downto(0){|i|\n",
823
+ "cu.pos=oldpos+i#{"*#{ir.begin}" unless ir.begin==1}\n",
824
+ "progress.bt_stop{\n",
825
+ "yield\n",
826
+ "}\n",
827
+ "}\n"
828
+ ]
829
+ return result.join
830
+
831
+ end
832
+
833
+ @rest=rest=@times.end-@times.begin
834
+ #why @rest?
835
+ if rest>10
836
+ rest.respond_to?(:infinite?) && rest.infinite? or
837
+ count=rest
838
+ recursive_proc=true
839
+ rest=10
840
+ end
841
+ matchcode=matchline
842
+ needs_close_brace= matchcode[-1]==?{
843
+ opener,closer=[count&&<<END0 ,<<END1],[<<END2]
844
+ (opt_matches+=1)>#{count} and progress.throw :RegRepeatEnd
845
+ END0
846
+ progress.bt_stop{
847
+ #{matchcode}
848
+ END1
849
+ #{'}' if needs_close_brace}
850
+ }
851
+ # progress.bt_backup
852
+ yield
853
+ END2
854
+ optional_iterations= [count&&" opt_matches=0\n"] +
855
+ opener*rest +
856
+ [recursive_proc ? " rest2[]\n" : " yield\n"] +
857
+ closer*rest
858
+
859
+ [recursive_proc &&
860
+ [" rest2=proc{\n",
861
+ count&&" progress.catch(:RegRepeatEnd){\n",
862
+ optional_iterations.join,
863
+ count&&" }\n",
864
+ " }\n"].join,
865
+ [ (matchline('or progress.throw')+"\n")*@times.begin,
866
+ recursive_proc ? " rest2[]\n" : optional_iterations,
867
+ " #{needs_close_brace ? "}"*@times.begin : "progress.throw" }\n"
868
+ ].join
869
+ ].join
870
+ end
871
+ end
872
+
873
+ #--------------------------------------------------------------
874
+ class ManyClass
875
+ #remove_method :initialize
876
+ def initialize(times=0..Infinity)
877
+ Integer===times and times=times..times
878
+ @times=times
879
+ extend @times.begin==@times.end ? HasBmatch : HasCmatch
880
+ end
881
+ def generate_cmatch
882
+ if @times.begin==@times.end
883
+ return "#{@times.begin}==cu.move(#{@times.begin}) or progress.throw\nyield\n"
884
+ end
885
+ code=@times.begin.zero? ? "" : "cu.rest_size>=#{@times.begin} or progress.throw\n"
886
+ code+=
887
+ if @times.end==Infinity
888
+ "cu.rest_size"
889
+ else
890
+ "[#{@times.end},cu.rest_size].min"
891
+ end + ".downto(#{@times.begin}){|i|\n"
892
+ code+=<<-END
893
+ progress.bt_stop{
894
+
895
+ cu.move(i)
896
+ yield
897
+ }
898
+ # progress.bt_backup
899
+ }
900
+ progress.throw
901
+ END
902
+ end
903
+
904
+ def generate_bmatch
905
+ assert @times.begin==@times.end
906
+ return "#{@times.begin}==cu.move(#{@times.begin})\n"
907
+ end
908
+ end
909
+
910
+ #--------------------------------------------------------------
911
+ class ManyLazyClass
912
+ include HasCmatch
913
+ #remove_method :initialize
914
+ def initialize(times=0..Infinity)
915
+ Integer===times and times=times..times
916
+ @times=times
917
+ extend @times.begin==@times.end ? HasBmatch : HasCmatch
918
+ end
919
+ def generate_cmatch
920
+ if @times.begin==@times.end
921
+ return "#{@times.begin}==cu.move(#{@times.begin}) or progress.throw\nyield\n"
922
+ end
923
+ code=@times.begin.zero? ? "" : " cu.rest_size>=#{@times.begin} or progress.throw\n"
924
+ code+=" #{@times.begin}.upto("
925
+ code+=if @times.end==Infinity
926
+ "cu.rest_size"
927
+ else
928
+ "[#{@times.end},cu.rest_size].min"
929
+ end + "){|i|\n"
930
+ code+=<<-END
931
+ progress.bt_stop{
932
+ cu.move i
933
+ yield
934
+ }
935
+ # progress.bt_backup
936
+ }
937
+ progress.throw
938
+ END
939
+ end
940
+ def generate_bmatch
941
+ assert @times.begin==@times.end
942
+ return "#{@times.begin}==cu.move(#{@times.begin})\n"
943
+ end
944
+ end
945
+ #--------------------------------------------------------------
946
+ class Or
947
+ include HasCmatch
948
+ def generate_cmatch
949
+ explode_regs
950
+ " i=0\n"+
951
+ (0...@regs.size).map{|i|
952
+ " progress.bt_stop{\n"+
953
+ case match_method @regs[i]
954
+ when "c"; " @regs_#{i}.cmatch(progress) {yield}\n"
955
+ when "b"; " cu.holding?{@regs_#{i}.bmatch(progress)} or progress.throw\n yield\n"
956
+ else " cu.skip(@regs_#{i}) or progress.throw\n yield\n"
957
+ end+
958
+ " }\n# progress.bt_backup\n"
959
+ }.join+
960
+ " progress.throw\n"
961
+ end
962
+ end
963
+
964
+ #--------------------------------------------------------------
965
+ class Xor
966
+ #the alternatives of xor should be converted to an array of procs
967
+ #so that I can jump about in it at will.
968
+
969
+ def xortail(h,progress,failevent)
970
+ (h...@regs.size).each{|j|
971
+ progress.send($bt_catch_method){
972
+ @regs[j].cmatch(progress) {
973
+ progress.throw failevent #fail whole xor matcher
974
+ }
975
+ }
976
+ }
977
+ end
978
+
979
+ CALLCOUNT="a"
980
+ def cmatch_lines(r,onsuccess="yield")
981
+ CALLCOUNT.succ!
982
+ result=["
983
+ origpos=cu.pos
984
+ failevent='RegXorFail_#{CALLCOUNT}'
985
+ progress.catch(failevent) {
986
+ "] +
987
+ r.map do|i|
988
+ <<-"END"
989
+ progress.#{$bt_catch_method}{
990
+ @regs_#{i}.cmatch(progress) {
991
+ finalpos=cu.pos
992
+ cu.pos=origpos #reset position after successful xor branch,
993
+ #{"xortail(#{i+1},progress,failevent);" unless i+1==@regs.size}
994
+ cu.pos=finalpos #re-consume matching one if the whole xor succeeds.
995
+ #{onsuccess}
996
+ }
997
+ }
998
+ END
999
+ end+["\n}\n"]
1000
+ result.join
1001
+ end
1002
+
1003
+ def generate_cmatch
1004
+ explode_regs
1005
+ cmatch_lines(0...@regs.size) +
1006
+ "\nprogress.throw\n"
1007
+ end
1008
+
1009
+ def generate_bmatch
1010
+ explode_regs
1011
+ cmatch_lines(0...@regs.size,"progress.throw(failevent,true)")
1012
+ end
1013
+ end
1014
+
1015
+ #--------------------------------------------------------------
1016
+ class LookAhead
1017
+ include CompileUtils
1018
+ def at_construct_time(*)
1019
+ super
1020
+ is_a?(HasCmatch) or extend HasBmatch
1021
+ end
1022
+
1023
+ def generate_cmatch
1024
+ @code ||= <<-END
1025
+ origpos=cu.pos
1026
+ @reg.cmatch(progress) {
1027
+ cu.pos=origpos
1028
+ yield
1029
+ }
1030
+ END
1031
+ end
1032
+
1033
+ def generate_bmatch
1034
+ huh
1035
+ end
1036
+ end
1037
+
1038
+
1039
+ #--------------------------------------------------------------
1040
+ class LookBack
1041
+ include CompileUtils
1042
+ def at_construct_time(*)
1043
+ super
1044
+ is_a?(HasCmatch) or extend HasBmatch
1045
+ end
1046
+
1047
+ def generate_cmatch
1048
+ case match_method @reg
1049
+ when "b","c";
1050
+ movecmd=@reg.itemrange.last==Infinity ? :begin! : "move(0-[#{@reg.itemrange.last},cu.pos].min)"
1051
+ [" origpos=cu.pos",
1052
+ " cu.pos>=#{@reg.itemrange.first} or progress.throw\n",
1053
+ " fudge=cu.#{movecmd}-#{@reg.itemrange.first}\n",
1054
+ " regs_ary(origpos,fudge).-@.cmatch(progress) {yield}\n"].join #not inlineable?
1055
+ else
1056
+ need0poscheck="cu.pos.nonzero?() &&" if @reg===nil
1057
+ " #{need0poscheck} cu.checkback(@reg) or progress.throw\n"+
1058
+ " yield\n"
1059
+ end
1060
+ end
1061
+
1062
+ def generate_bmatch
1063
+ case match_method @reg
1064
+ when "c"; raise "hell"
1065
+ when "b";
1066
+ movecmd=@reg.itemrange.last==Infinity ? :begin! : "move(0-[#{@reg.itemrange.last},cu.pos].min)"
1067
+ [" origpos=cu.pos",
1068
+ " if cu.pos>=#{@reg.itemrange.first}\n",
1069
+ " fudge=cu.#{movecmd}-#{@reg.itemrange.first}\n",
1070
+ " regs_ary(origpos,fudge).-@.bmatch(progress)\n",
1071
+ " end\n"].join #not inlineable?
1072
+ else
1073
+ need0poscheck="cu.pos.nonzero?() &&" if @reg===nil
1074
+ " #{need0poscheck} cu.checkback(@reg)\n"
1075
+ end
1076
+ end
1077
+ end
1078
+
1079
+ #--------------------------------------------------------------
1080
+ class Position
1081
+ include HasBmatch
1082
+
1083
+ def generate_bmatch
1084
+ if @positions.size>5
1085
+ " @positions.include? adjust_position(progress,cu.pos)\n"
1086
+ else
1087
+ need_pos=need_rpos=nil
1088
+
1089
+ result=@positions.map{|pos|
1090
+ if !Position.negative?(pos)
1091
+ need_pos=true
1092
+ "result= pos==#{pos}"
1093
+ else
1094
+ need_rpos=true
1095
+ "result= rpos==#{pos.nonzero? || 0}\n"+
1096
+ #"p :position_postmatch, result, rpos, #{pos}, cu.pos, cu.size\n"+
1097
+ "result"
1098
+ end
1099
+ }.join(" or \n")+"\n"
1100
+ need_pos and result="pos=cu.pos\n"+result
1101
+ need_rpos and result="rpos=cu.pos-cu.size\n"+result
1102
+ #"p :position_prematch, cu.pos\n"+
1103
+ result
1104
+ end
1105
+ end
1106
+ end
1107
+
1108
+
1109
+ #-------------------------------------
1110
+ module HasCmatch_And_Bound; end
1111
+
1112
+ #--------------------------------------------------------------
1113
+ class Bound
1114
+
1115
+ def at_construct_time(*)
1116
+ super
1117
+ extend(is_a?(HasCmatch) ? HasCmatch_And_Bound : HasBmatch)
1118
+ end
1119
+
1120
+ def generate_cmatch
1121
+ case match_method @reg
1122
+ when "c";
1123
+ [" origpos=cu.pos\n",
1124
+ " @reg.cmatch(progress) {\n",
1125
+ " progress.register_var(@name,origpos...cu.pos)\n",
1126
+ " yield\n",
1127
+ " }\n"].join
1128
+ when "b";
1129
+ [" origpos=cu.pos\n",
1130
+ " if @reg.bmatch progress\n",
1131
+ " progress.register_var(@name,origpos...cu.pos)\n",
1132
+ " yield\n",
1133
+ " end\n",
1134
+ " progress.throw\n"].join
1135
+ else
1136
+ [" progress.register_var(@name,cu.pos)\n",
1137
+ " if (cu.skip @reg)\n",
1138
+ " yield\n",
1139
+ " else\n",
1140
+ " progress.unregister_var @name\n",
1141
+ " progress.throw\n",
1142
+ " end\n"].join
1143
+ end
1144
+ end
1145
+
1146
+ def generate_bmatch
1147
+ case match_method @reg
1148
+ when "c"; raise "hell"
1149
+ when "b";
1150
+ [" origpos=cu.pos\n",
1151
+ " @reg.bmatch progress and\n",
1152
+ " progress.register_var(@name,origpos...cu.pos)\n"
1153
+ ].join
1154
+ else
1155
+ [" progress.register_var(@name,cu.pos)\n",
1156
+ " (cu.skip @reg) or\n",
1157
+ " progress.unregister_var @name\n"
1158
+ ].join
1159
+ end
1160
+ end
1161
+ end
1162
+
1163
+ #--------------------------------------------------------------
1164
+ module BackrefLike
1165
+ include HasBmatch
1166
+ def self.to_indexed(progress,vec)
1167
+ if ::Array==progress.cursor.data_class
1168
+ Array(vec)
1169
+ else
1170
+ vec.to_s
1171
+ end
1172
+ end
1173
+
1174
+
1175
+
1176
+
1177
+ def generate_bmatch
1178
+ " cells=formula_value(huh,progress) and \n"+
1179
+ " cells=::Reg::BackrefLike.to_indexed(progress,cells) and \n"+
1180
+ " cu.skip_literals cells\n"
1181
+ end
1182
+ instance_eval(&WrapCmatch)
1183
+ end
1184
+
1185
+ #--------------------------------------------------------------
1186
+ module BRLike
1187
+ include HasBmatch
1188
+ end
1189
+
1190
+ #--------------------------------------------------------------
1191
+ class Backref
1192
+ include HasBmatch
1193
+
1194
+ def generate_bmatch
1195
+ " cells=formula_value(huh,progress) and \n"+
1196
+ " cells=::Reg::BackrefLike.to_indexed(progress,cells) and \n"+
1197
+ " cu.skip_literals cells\n"
1198
+ end
1199
+ instance_eval(&WrapCmatch)
1200
+ end
1201
+
1202
+ #--------------------------------------------------------------
1203
+ class BR
1204
+ include HasBmatch
1205
+
1206
+ def generate_bmatch
1207
+ " cells=formula_value(huh,progress) and \n"+
1208
+ " cells=::Reg::BackrefLike.to_indexed(progress,cells) and \n"+
1209
+ " cu.skip_literals cells\n"
1210
+ end
1211
+ instance_eval(&WrapCmatch)
1212
+ end
1213
+
1214
+
1215
+ #--------------------------------------------------------------
1216
+ class Transform
1217
+ #include HasBmatch
1218
+ def generate_cmatch
1219
+ case match_method @reg
1220
+ when "c"
1221
+ " origpos=cu.pos\n"+
1222
+ " @reg.cmatch(progress){\n"+
1223
+ " progress.register_replace(origpos,cu.pos-origpos,@rep)\n"+
1224
+ " yield\n"+
1225
+ " }\n"
1226
+
1227
+ else " (#{generate_bmatch}) or progress.throw\n"+
1228
+ " yield\n"
1229
+
1230
+ end
1231
+ end
1232
+
1233
+ def generate_bmatch
1234
+ " origpos=cu.pos\n"+
1235
+ case match_method @reg
1236
+ when "c"; raise "hell"
1237
+ when "b"
1238
+ " @reg.bmatch(progress) and\n"
1239
+ else
1240
+ " cu.skip @reg and\n"
1241
+ end+
1242
+ " progress.register_replace(origpos,cu.pos-origpos,@rep)\n"
1243
+ end
1244
+ end
1245
+
1246
+ #--------------------------------------------------------------
1247
+ class Finally
1248
+ def at_construct_time(*)
1249
+ super
1250
+ HasCmatch===self or extend HasBmatch
1251
+ end
1252
+
1253
+ # include HasBmatch
1254
+ def generate_cmatch
1255
+ case match_method @reg
1256
+ when "c"; <<-END
1257
+ @reg.cmatch(progress) {
1258
+ progress.register_later progress,&@block
1259
+ yield
1260
+ }
1261
+ END
1262
+ else <<-END
1263
+ if (#{generate_bmatch})
1264
+ yield
1265
+ else
1266
+ progress.throw
1267
+ end
1268
+ END
1269
+ end
1270
+ end
1271
+
1272
+ def generate_bmatch
1273
+ case match_method @reg
1274
+ when "c"; raise "finally match compile error"
1275
+ when "b"; "@reg.bmatch progress"
1276
+ else "cu.skip @reg"
1277
+ end + " and
1278
+ (progress.register_later progress,&@block;true)\n"
1279
+ end
1280
+ end
1281
+
1282
+
1283
+ #--------------------------------------------------------------
1284
+ class SideEffect
1285
+ def generate_bmatch
1286
+
1287
+ "if result="+
1288
+ if HasBmatch===@reg
1289
+ "@reg.bmatch progress"
1290
+ else
1291
+ "cu.skip @reg"
1292
+ end+"\n"+
1293
+ "@block.call(progress)\n"+
1294
+ "result\n"+
1295
+ "end\n"
1296
+ end
1297
+
1298
+ def generate_cmatch
1299
+ if HasCmatch===@reg
1300
+ "@reg.cmatch(progress) {@block.call(progress); yield}\n"
1301
+ else
1302
+ "(#{generate_bmatch}) or progress.throw\n"+
1303
+ "yield\n"
1304
+ end
1305
+ end
1306
+ end
1307
+
1308
+ #--------------------------------------------------------------
1309
+ class Undo
1310
+ def generate_bmatch
1311
+ huh
1312
+ end
1313
+ def generate_cmatch
1314
+ huh
1315
+ end
1316
+ end
1317
+
1318
+ #--------------------------------------------------------------
1319
+ class Interpret
1320
+ def generate_bmatch
1321
+ huh
1322
+ end
1323
+ def generate_cmatch
1324
+ huh
1325
+ end
1326
+ end
1327
+
1328
+ #--------------------------------------------------------------
1329
+ class ::Set
1330
+ include HasBmatch
1331
+
1332
+ def generate_bmatch
1333
+ @bcode||=
1334
+ " self.include? cu.readahead1 and cu.move(1).nonzero?\n"
1335
+ end
1336
+ end
1337
+
1338
+ #--------------------------------------------------------------
1339
+ class Case
1340
+ def generate_bmatch
1341
+ huh
1342
+ end
1343
+ def generate_cmatch
1344
+ huh
1345
+ end
1346
+ end
1347
+
1348
+ =begin try to implement Hash/Object
1349
+ #--------------------------------------------------------------
1350
+ class Hash
1351
+ class Literals
1352
+ include Reg,Composite,CompileUtils
1353
+ def initialize(keys,vals)
1354
+ @keys,@regs=keys,Reg::Array.new(*vals)
1355
+ end
1356
+
1357
+ attr :keys
1358
+
1359
+ def subregs
1360
+ @keys.map{|key|
1361
+ if Reg.interesting_matcher? key
1362
+ Equal.new key
1363
+ else
1364
+ key
1365
+ end
1366
+ }+[@regs]
1367
+ end
1368
+
1369
+ def ===(other)
1370
+ huh
1371
+
1372
+ huh #but I also have to set up GraphPoint::HashValue context??
1373
+ return @regs===(other.indexes(*@keys) rescue return)
1374
+
1375
+
1376
+ result=true
1377
+ @regs.each_with_index{|r,i|
1378
+ r===actual[i] or break result=false
1379
+ }
1380
+ return result
1381
+ end
1382
+
1383
+ def generate_bmatch
1384
+ explode_regs
1385
+ huh
1386
+ " other=progress.cursor.readahead1\n"+
1387
+ " if (actual=other.values_at(*@keys) rescue nil)\n"+
1388
+ (0...@valmtrs.size).map{|i|
1389
+ case match_method @regs[i]
1390
+ when "c","b":
1391
+ " progress.with_context(GraphPoint::HashValue.huh,actual[#{i}]) and \n"
1392
+ " @regs_#{i}.bmatch(progress) and \n"
1393
+ else " @regs_#{i}===actual[#{i}] and \n"
1394
+ end
1395
+ }.join+
1396
+ " progress.cursor.move 1\n"+
1397
+ " end\n"
1398
+ end
1399
+
1400
+ def generate_cmatch
1401
+ explode_regs
1402
+ braces=0
1403
+ huh
1404
+ " other=progress.cursor.readahead1\n"+
1405
+ " if(actual=other.values_at(*@keys) rescue nil)\n"+
1406
+ (0...@valmtrs.size).map{|i|
1407
+ case match_method @regs[i]
1408
+ when "c":
1409
+ braces+=1
1410
+ " progress.with_context(GraphPoint::HashValue.huh,actual[#{i}]) and \n"+
1411
+ " @regs_#{i}.cmatch(progress) {\n"
1412
+ when "b":
1413
+ " progress.with_context(GraphPoint::HashValue.huh,actual[#{i}]) and \n"
1414
+ " @regs_#{i}.bmatch(progress) and \n"
1415
+ else " @regs_#{i}===actual[#{i}] and \n"
1416
+ end
1417
+ }.join+
1418
+ " progress.cursor.move(1).nonzero? and yield\n"+
1419
+ " #{%/}/*braces}\n"+
1420
+ " end\n"+
1421
+ " progress.throw\n"
1422
+ end
1423
+ end
1424
+
1425
+ #--------------------------------------------------------------
1426
+ class MatcherPair
1427
+ include Reg,Composite,CompileUtils
1428
+ def initialize(mkey,mval)
1429
+ @mkey,@mval=mkey,mval
1430
+
1431
+ end
1432
+
1433
+ def subregs
1434
+ [@mkey,@mval]
1435
+ end
1436
+
1437
+ def ===(other)
1438
+ raise NoMethodError
1439
+ huh
1440
+ other.each{|k,v|
1441
+ if @mkey===k
1442
+ @mval===v or return
1443
+ end
1444
+ }
1445
+ huh #also need to keep track of which keys of other actually matched something, for parent Reg::Hash
1446
+ end
1447
+
1448
+ def bmatch progress
1449
+ huh
1450
+ huh progress.with_context(huh,huh)
1451
+
1452
+ other=progress.cursor.readahead1
1453
+ other.each{|k,v|
1454
+ if @mkey===k
1455
+ @mval===v or break
1456
+ progress.context.seen_keys<<k
1457
+ end
1458
+ } and
1459
+ huh #advance cursor if match success
1460
+ end
1461
+
1462
+ def cmatch progress
1463
+ huh
1464
+
1465
+ end
1466
+
1467
+
1468
+ huh
1469
+ end
1470
+
1471
+ #--------------------------------------------------------------
1472
+ class CatchAll
1473
+ def initialize
1474
+ huh
1475
+ end
1476
+ end
1477
+
1478
+ end
1479
+ =end
1480
+
1481
+ =begin another try at Reg::Hash and Object
1482
+ warning "Reg::Hash, Reg::Object and friends need to be made possibly Undoable and Multiple again"
1483
+ warning "if the compiled implementations of those matchers are to be used"
1484
+ warning "take out the Reg::Hash/Object hacks in multiple_infection and undoable_infection"
1485
+ #--------------------------------------------------------------
1486
+ module Map
1487
+ def generate_generic(
1488
+ matchmeth=:cmatch,needdo=:do,
1489
+ final=nil
1490
+ )
1491
+ needdo ? final||="{yield}" : needif=:if
1492
+ %{
1493
+ h=progress.data
1494
+ litvals=h.values_at(*@literals_keys)
1495
+ h=h.dup
1496
+ huh "maybe need more generic form of h.dup"
1497
+ @literals_keys.each{|lit| h.delete lit }
1498
+ #{needif} @literals_vals.#{matchmeth}(progress.huh_with_new_data litvals) #{needdo}
1499
+ a=h.inject([]){|list,pair|
1500
+ if @literals_keys_set.include? pair.first
1501
+ list
1502
+ else
1503
+ list+pair
1504
+ end
1505
+ }<<h.default
1506
+ def a.matched_counts; @matched_counts end
1507
+ a.instance_variable_set( :@matched_counts, Array.new(huh @matchers.size+1,0) )
1508
+ @array_style.#{matchmeth}(progress.huh_with_new_data a) #{final}
1509
+ end
1510
+ }
1511
+ end
1512
+
1513
+ end
1514
+
1515
+ #--------------------------------------------------------------
1516
+ class Hash
1517
+
1518
+ include Reg,Composite
1519
+ include CausesBacktracking #of course, it's not implmented correctly, right now
1520
+ include Map
1521
+ attr :others
1522
+
1523
+ @@eventcount=0
1524
+ def initialize(*args)
1525
+ @matchers=[]
1526
+ @literals=[]
1527
+ @others=nil
1528
+ if 1==args.size #unordered list of pairs
1529
+ hashdat=args.first or return
1530
+ hashdat.key?(OB) and @others=hashdat.delete(OB)
1531
+ hashdat.each {|key,val|
1532
+ if !Reg.interesting_matcher? key
1533
+ Equals===key and key=key.unwrap
1534
+ @literals<<[key,val]
1535
+ else
1536
+ Fixed===key and key=key.unwrap
1537
+ @matchers<<[key,val]
1538
+ end
1539
+ }
1540
+ else #ordered list of pairs
1541
+ args.each{|pair|
1542
+ key,val=*pair
1543
+ consider_literals=true
1544
+ if !Reg.interesting_matcher? key and not Undoable===val and consider_literals
1545
+ Equals===key and key=key.unwrap
1546
+ @literals<<pair
1547
+ elsif key==OB
1548
+ @others=val
1549
+ else
1550
+ Fixed===key and key=key.unwrap
1551
+ @matchers<<pair
1552
+ consider_literals=false
1553
+ end
1554
+ }
1555
+ end
1556
+ #transform optional values to their final form
1557
+ [@literals,@matchers].each{|list| list.map!{|(k,val)|
1558
+ if Repeat===val
1559
+ # <<-end
1560
+ if val.itemrange==(0..1): [k,val.reg|huh(HashDefault.new(k))] #HashDefault not invented yet
1561
+ elsif val.itemrange==(1..1): [k,val.subregs.first]
1562
+ else raise(TypeError.new( "multiple matcher not expected in hash") )
1563
+ end
1564
+ else [k,val]
1565
+ end
1566
+ }}
1567
+
1568
+ @literals_keys=@literals.map{|(k,v)| k}
1569
+ @literals_vals=+@literals.map{|(k,v)| v}
1570
+
1571
+ incproc=proc{|i| proc{|pr| pr.cursor.data.matched_counts[i]+=1}}
1572
+ decproc=proc{|i| proc{|pr| pr.cursor.data.matched_counts[i]-=1}}
1573
+ all_matchers=@matchers+[OB,@others]
1574
+ i=-1
1575
+ warning "ordered hash matchers (at least) need to support matcher-by-matcher match attempt order"
1576
+ event="fail_hash_matcher#{@@eventcount+=1}"
1577
+ @array_style=[ @literals_vals,
1578
+ all_matchers.inject(OB){|conj,(k,v)| i+=1
1579
+ conj&-[
1580
+ [ OBS.l,
1581
+ k,
1582
+ v.reg.side_effect(&incproc[i]) \
1583
+ .undo(&decproc[i]) \
1584
+ |Reg.event(event)
1585
+ ].-@.*, OBS
1586
+ ]
1587
+ }.*,
1588
+ item_that{|item,pr|
1589
+ j=-1
1590
+ pr.cursor.data.matched_counts.all?{|mcount| j+=1
1591
+ mcount.nonzero? or all_matchers[j].last===item
1592
+ }
1593
+ }
1594
+ ].+@.fail_on(event)
1595
+
1596
+
1597
+ super
1598
+
1599
+
1600
+ assert !is_a?(Multiple) #should be no Multiples in subregs
1601
+ end
1602
+
1603
+ def ordered; self end
1604
+
1605
+ def subregs;
1606
+ lkeys=[];lvals=[]
1607
+ @literals.each{|(k,v)| lkeys<<k; lvals<<v}
1608
+ mkeys=[];mvals=[]
1609
+ @matchers.each{|(k,v)| mkeys<<k; mvals<<v}
1610
+
1611
+ lkeys+lvals+mkeys+mvals+
1612
+ (@others==nil ? [OB,@others] : [])
1613
+ end
1614
+
1615
+ def inspect
1616
+ warning 'is this right?'
1617
+ result=[]
1618
+ h=::Hash[*@literals.inject([]){|list,pair| list+pair}]
1619
+ result<<h.inspect.sub(/.(.*)./, "\\1") unless @literals.empty?
1620
+ h=::Hash[*@matchers.inject([]){|list,pair| list+pair}]
1621
+ result<<h.inspect.sub(/.(.*)./, "\\1") unless @matchers.empty?
1622
+ result<<"OB=>#{@others.inspect}" if defined? @others and @others!=nil
1623
+ return "+{#{result.join(", ")}}"
1624
+ end
1625
+
1626
+ #on_throw(...,[move,-1]) in gen_cmatch undoes that read1 call
1627
+ def make_new_cursor; "(
1628
+ result=::Sequence::OfHash.new(h=progress.cursor.read1,@literals_keys).
1629
+ unshift(h.values_at(@literals_keys))
1630
+ def result.matched_counts; @matched_counts end
1631
+ result.instance_variable_set( :@matched_counts, Array.new(huh @matchers.size+1,0) )
1632
+ result
1633
+ )"
1634
+ end
1635
+
1636
+ def throw_guard; "progress.on_throw(:RegMatchFail, :endcontext)\n" end
1637
+ def post_match; "progress.endcontext\n" end
1638
+
1639
+ remove_method :===
1640
+
1641
+ def generate_bmatch
1642
+ "@array_style.bmatch(progress)"
1643
+ end
1644
+
1645
+ def generate_cmatch
1646
+ "@array_style.cmatch(progress) {yield}"
1647
+ end
1648
+
1649
+ if false
1650
+ def ===(other)
1651
+ pr=Progress.new self, ::Sequence::SingleItem[other]
1652
+ progress.catch( :RegMatchSucceed ){progress.send($bt_catch_method){
1653
+ cmatch(pr) {progress.throw(:RegMatchSucceed, true)}
1654
+ }}
1655
+ warning "identical with Array#==="
1656
+ end
1657
+
1658
+ def literals_val_match_code i, matval, op="and"
1659
+ case match_method matval
1660
+ when "c":
1661
+ "@literals_val_#{i}.cmatch(progress) {"
1662
+ when "b":
1663
+ huh #setup cursor&context
1664
+ "@literals_val_#{i}.bmatch progress #{op}\n"
1665
+ else huh
1666
+ "@literals_val_#{i}===other[@literals_key_#{i}] #{op}\n"
1667
+ end
1668
+ end
1669
+
1670
+ def matchers_key_match_code j,matkey
1671
+ case match_method matkey
1672
+ when "c": huh
1673
+ when "b":
1674
+ huh #setup cursor&context
1675
+ "@matchers_key_#{j}.bmatch progress"
1676
+ else huh
1677
+ "@matchers_key_#{j}===okey"
1678
+ end
1679
+ end
1680
+
1681
+ def matchers_val_match_code j,matval
1682
+ case match_method matval
1683
+ when "c": huh
1684
+ when "b":
1685
+ huh #setup cursor&context
1686
+ "@matchers_val_#{j}.bmatch progress"
1687
+ else huh
1688
+ "@matchers_val_#{j}===other[okey]"
1689
+ end
1690
+ end
1691
+
1692
+ def catchall_val_match_code
1693
+ case match_method @others
1694
+ when "c": huh
1695
+ when "b": huh
1696
+ huh #setup cursor&context
1697
+ "@others.bmatch progress"
1698
+ else huh
1699
+ "@others===other[okey]"
1700
+ end
1701
+ end
1702
+
1703
+ def default_match_code(cmatches_too=nil)
1704
+ huh #handle cmatch here too
1705
+ <<-END
1706
+
1707
+ default=other.default
1708
+ defaultrest=nil
1709
+ defaultval||=proc{|unv,idx,&rest|
1710
+ case match_method unv
1711
+ when "c": huh
1712
+ #{!cmatches_too ? "raise 'hell'\n" :
1713
+ "defaultrest[idx+1,&rest]\n"
1714
+ }
1715
+ when "b":
1716
+ progress.with_context(GraphPoint::HashDefaultValue)
1717
+ unk.bmatch progress
1718
+ else unv===default
1719
+ end or progress.throw
1720
+ }
1721
+
1722
+ defaultrest||=proc{|idx,&rest|
1723
+ unseenmatchers[idx..-1].find_all{|(unk,unv)|
1724
+ case match_method unk
1725
+ when "c":
1726
+ #{!cmatches_too ? "raise 'hell'\n" :
1727
+ "defaultval.call unv,idx {}"
1728
+
1729
+
1730
+ }
1731
+ when "b":
1732
+ progress.with_context(GraphPoint::HashDefaultKey)
1733
+ unk.bmatch progress
1734
+ else
1735
+ unk===nil
1736
+ end or progress.throw
1737
+
1738
+ defaultval.call unv,idx {}
1739
+ }
1740
+ rest.call
1741
+ }
1742
+ END
1743
+ end
1744
+
1745
+ def generate_cmatch
1746
+ warning %#need to generate code that creates a new graphpoint context#
1747
+ #and changes that context on every hash key/val
1748
+ #also, ::Sequence::SingleItem stuff
1749
+ warning %#need calls to progress.with_context here#
1750
+ warning "I think it's ok now..."
1751
+ i=j=0
1752
+ @matchers.each{|(matkey,matval)|
1753
+ j+=1
1754
+ instance_variable_set("@matchers_key_#{j}",matkey)
1755
+ instance_variable_set("@matchers_val_#{j}",matval)
1756
+ }
1757
+ @unseenmatchers||=Set[*@matchers.map{|(key,v)| key}]
1758
+ <<-END+
1759
+ okey=nil
1760
+ unseenmatchers=@unseenmatchers.dup
1761
+ okey=nil
1762
+ matchersrest=proc{|idx,&rest|
1763
+ (idx...@matchers.size).each{|i|
1764
+ matkey,matval=@matchers[i]
1765
+ END
1766
+
1767
+ huh+ "backtracking in and matchers is a problem"+
1768
+ huh+ "some kinda loop needed"+
1769
+ " failevent=%[RegHashFail\#{@_callcount||=0;@_callcount+=1}]\n"+
1770
+ " progress.catch(failevent) {\n"+
1771
+ " progress.#{$bt_catch_method} {\n"+
1772
+ " progress.with_context(GraphPoint::HashKey,okey)\n"+
1773
+ " "+matchers_key_match_code( j,matkey)+" or progress.throw\n"+
1774
+
1775
+ " progress.#{$bt_catch_method} {\n"+
1776
+ " progress.with_context(GraphPoint::HashVal,okey)\n"+
1777
+ " "+matchers_val_match_code( j,matval)+" or progress.throw failevent\n"+
1778
+ " unseenmatchers.delete @matchers_val_#{j}\n"+
1779
+ " rest.call\n"+
1780
+ " }\n"+ #catch :RegMatchFail#2
1781
+ " progress.throw failevent\n"+
1782
+ " }\n"+ #catch :RegMatchFail#1
1783
+ " }\n"+ #catch failevent
1784
+ " progress.throw\n"+
1785
+ " }\n"+ #each
1786
+ " matchersrest.call i+1, &rest\n"+
1787
+ " }\n"+ #proc
1788
+
1789
+
1790
+
1791
+
1792
+ huh+
1793
+ @literals.to_a.map{|(litkey,matval)|
1794
+ i+=1
1795
+ instance_variable_set("@literals_key_#{i}",litkey)
1796
+ instance_variable_set("@literals_val_#{i}",matval)
1797
+ " progress.with_context(GraphPoint::HashValue,@matchers_key_#{i})\n"+
1798
+ " "+(literals_val_match_code i, matval, "or progress.throw\n")
1799
+ }.join+
1800
+ " (other.keys-@literals.map{|(key,val)| key }).each{|okey|\n"+
1801
+ " matchersrest.call 0 {\n"+ #attempt
1802
+ huh+ #finish unmatched keys,
1803
+
1804
+ huh+ #default processing
1805
+ " progress.with_context(GraphPoint::HashDefault)\n"+
1806
+ " "+catchall_val_match_code(" or progress.throw")+"\n"+
1807
+ huh+
1808
+ (unseenmatchers=unseenmatchers.to_a;'')+
1809
+ default_match_code(true)+
1810
+ " yield\n"+
1811
+ " }\n"+
1812
+ " other.empty? and (@others===other.default rescue false) || progress.throw\n"+
1813
+ " yield\n"+
1814
+
1815
+
1816
+ huh+ #" ensure\n progress.endcontext\n"
1817
+ huh+ #"must always endcontext before yield"
1818
+ huh #"lotsa end } were omitted"
1819
+ end
1820
+
1821
+ def generate_bmatch
1822
+ huh #need to generate code that creates a new graphpoint context
1823
+ #and changes that context on every hash key/val
1824
+ #also, ::Sequence::SingleItem stuff
1825
+ huh #need calls to progress.with_context here
1826
+ @unseenmatchers||=Set[*@matchers.map{|(key,v)| key}]
1827
+
1828
+ i=j=0
1829
+ " other=cu.readahead1\n"+
1830
+ " unseenmatchers=@unseenmatchers.dup\n"+
1831
+ " return unless\n"+ huh("cant return in bmatch")+
1832
+
1833
+ @literals.to_a.map{|(litkey,matval)|
1834
+ i+=1
1835
+ instance_variable_set("@literals_key_#{i}",litkey)
1836
+ instance_variable_set("@literals_val_#{i}",matval)
1837
+
1838
+ " progress.with_context(GraphPoint::HashValue,@literals_key_#{i}) && \n"+
1839
+ " "+literals_val_match_code(i, matval)
1840
+ }.join.sub(/ and *\n$/,"\n")+
1841
+ " (other.keys-@literals.keys).each{|okey|\n"+
1842
+ @matchers.to_a.map{|(matkey,matval)|
1843
+ j+=1
1844
+ instance_variable_set("@matchers_key_#{j}",matkey)
1845
+ instance_variable_set("@matchers_val_#{j}",matval)
1846
+ " if "+
1847
+ " progress.with_context(GraphPoint::HashKey,@matchers_key_#{j}) && \n"+
1848
+ matchers_key_match_code( j,matkey)+"\n"+
1849
+ " return unless "+ huh("cant return in bmatch")+
1850
+ " progress.with_context(GraphPoint::HashValue,@matchers_val_#{j}) && \n"+
1851
+ matchers_val_match_code( j,matval)+"\n"+
1852
+ " unseenmatchers.delete @matchers_val_#{j}\n"+
1853
+ " next\n"+
1854
+ " end\n"
1855
+ }.join+
1856
+ huh+ #default processing
1857
+ "huh.with_context"+
1858
+ " "+catchall_val_match_code+" or return\n"+ huh("cant return in bmatch")+
1859
+ " }\n"+
1860
+
1861
+ huh+
1862
+ default_match_code+
1863
+ " other.empty? and return (@others===other.default rescue false)\n"+ huh("cant return in bmatch")+
1864
+ " return true\n"+ huh("cant return in bmatch")+
1865
+
1866
+
1867
+ huh+ " ensure\n progress.end_context\n"+
1868
+ huh+ "advance cursor if match successful"
1869
+ end
1870
+ end
1871
+
1872
+ end
1873
+
1874
+ #--------------------------------------------------------------
1875
+ class RestrictHash
1876
+ warning "need 'assert(!is_a? Multiple)' in initialize()"
1877
+
1878
+ def generate_bmatch
1879
+ huh
1880
+ end
1881
+ def generate_cmatch
1882
+ huh
1883
+ end
1884
+ end
1885
+
1886
+ #--------------------------------------------------------------
1887
+ class Object
1888
+ include Map
1889
+ def initialize(*args)
1890
+ hash= (::Hash===args.last ? args.pop : {})
1891
+
1892
+ @vars=[]; @meths=[]; @meth_matchers=[]; @var_matchers=[]
1893
+ argmuncher=proc{|(item,val)|
1894
+ if ::String===item or ::Symbol===item
1895
+ item=item.to_s
1896
+ (/^@/===item ? @vars : @meths)<<[item.to_sym,val]
1897
+ elsif Regexp===item && item.source[/^\^?@/]
1898
+ @var_matchers<<[item,val]
1899
+ elsif And===item && Regexp===item.subregs[0] && item.subregs[0].source[/^\^?@/]
1900
+ @var_matchers<<[item,val]
1901
+ elsif Wrapper===item
1902
+ @meth_matchers<<[item.unwrap,val]
1903
+ else
1904
+ @meth_matchers<<[item,val]
1905
+ end
1906
+ }
1907
+ args.each( &argmuncher )
1908
+ hash.each( &argmuncher )
1909
+ @over_ivars=OverIvars.new(*@vars+@var_matchers)
1910
+ @over_meths=OverMethods.new(*@meths+@meth_matchers)
1911
+ #@meths[:class]=args.shift if (Class===args.first) and args.size%2==1
1912
+
1913
+ warning %#need to xform optional elements(using .-) just like in Reg::Hash too#
1914
+ super
1915
+ assert !is_a?(Multiple)
1916
+ end
1917
+
1918
+ def generate_bmatch
1919
+ "\n@over_ivars.bmatch(progress) && move(-1) && @over_meths.bmatch(progress)\n"
1920
+ end
1921
+ def generate_cmatch
1922
+ "\n@over_ivars.cmatch(progress) { move(-1); @over_meths.cmatch(progress) {yield} }\n"
1923
+ end
1924
+
1925
+ class OverIvars < Object
1926
+ def initialize(*args)
1927
+ hash= (::Hash===args.last ? args.pop : {})
1928
+
1929
+ @meths=@meth_matchers=[].freeze
1930
+ @vars=[]; @var_matchers=[]
1931
+ argmuncher=proc{|(item,val)|
1932
+ if ::Symbol===item or item.respond_to? :to_str
1933
+ item=item.to_str
1934
+ /^@/===item or raise ArgumentError
1935
+
1936
+ @vars<<[item.to_sym,val]
1937
+ else
1938
+ @var_matchers<<[item,val]
1939
+ end
1940
+ }
1941
+ args.each( &argmuncher )
1942
+ hash.each( &argmuncher )
1943
+
1944
+ @literals_keys=@vars.each{|(k,v)| k}
1945
+ @literals_vals=@vars.each{|(k,v)| v}
1946
+ @matchers=@var_matchers
1947
+
1948
+
1949
+ huh_build_@array_style
1950
+
1951
+ super()
1952
+ assert !is_a?(Multiple)
1953
+ end
1954
+ def generate_bmatch
1955
+ huh generate_generic
1956
+ end
1957
+ def generate_cmatch
1958
+ huh generate_generic
1959
+ end
1960
+
1961
+ end
1962
+
1963
+ class OverMethods < Object
1964
+ def initialize(*args)
1965
+ hash= (::Hash===args.last ? args.pop : {})
1966
+
1967
+ @vars=@var_matchers=[].freeze
1968
+ @meths=[]; @meth_matchers=[]
1969
+ argmuncher=proc{|(item,val)|
1970
+ if ::Symbol===item or item.respond_to? :to_str
1971
+ item=item.to_str
1972
+ @meths<<[item.to_sym,val]
1973
+ else
1974
+ @meth_matchers<<[item,val]
1975
+ end
1976
+ }
1977
+ args.each( &argmuncher )
1978
+ hash.each( &argmuncher )
1979
+
1980
+ @literals_keys=@meths.each{|(k,v)| k}
1981
+ @literals_vals=@meths.each{|(k,v)| v}
1982
+ @matchers=@meth_matchers
1983
+
1984
+ huh_build_@array_style
1985
+
1986
+ super()
1987
+ assert !is_a?(Multiple)
1988
+ end
1989
+ def generate_bmatch
1990
+ huh generate_generic
1991
+ end
1992
+ def generate_cmatch
1993
+ huh generate_generic
1994
+ end
1995
+ end
1996
+
1997
+
1998
+ end
1999
+ =end
2000
+
2001
+ #--------------------------------------------------------------
2002
+ class Not
2003
+ def generate_bmatch
2004
+ case match_method @reg
2005
+ when "c"; raise "hell"
2006
+ when "b";
2007
+ " cu.holding{ !@reg.bmatch progress }"+
2008
+ (" and cu.move(1)" if @reg.itemrange==(1..1)).to_s
2009
+ else " cu.skip self"
2010
+ end+"\n"
2011
+ end
2012
+
2013
+ def generate_cmatch
2014
+ case match_method @reg
2015
+ when "c";
2016
+ " progress.catch(:RegNotFail) {\n"+
2017
+ " progress.#{$bt_catch_method} {\n"+
2018
+ " @reg.cmatch(progress) {progress.throw :RegNotFail}\n"+
2019
+ " }\n"+
2020
+ " yield\n"+
2021
+ " }\n progress.throw\n"
2022
+ else " (#{generate_bmatch}) or progress.throw\n"+
2023
+ " yield\n"
2024
+ end
2025
+ end
2026
+ end
2027
+
2028
+
2029
+ #--------------------------------------------------------------
2030
+ class And
2031
+
2032
+ class ThreadProgress<Progress
2033
+ def initialize(matcher,parent)
2034
+ super(matcher,parent.cursor)
2035
+ parent.instance_variable_get( :@matchsucceed_stack).push( method( :process_laters ) )
2036
+ parent.instance_variable_get( :@undos_stack).push( method( :process_undos ) )
2037
+ @thread,@parent=nil,parent
2038
+ end
2039
+ attr_accessor :thread
2040
+ def lookup_var(name)
2041
+ super or @parent.lookup_var(name)
2042
+ end
2043
+ alias [] lookup_var
2044
+ end
2045
+ warning "need more and concurrency testing"
2046
+ warning %#need to discover dependancies among and alternatives#
2047
+ #(ie a variable capture in one alternative being used in a backref
2048
+ #in a subsequent alternative.)
2049
+ #then use those dependancies to sort @regs so that var cap is always before
2050
+ #backref
2051
+
2052
+ instance_eval(&WrapBmatch)
2053
+ def generate_cmatch
2054
+ return " yield\n" if @regs.empty?
2055
+ warning %#pull out ordinary matchers for processing outside the andmachine and its threads#
2056
+ warning "not sure whether to use progress's version of catch/throw here"
2057
+ maybe_progress="progress."
2058
+ #maybe_progress=nil
2059
+ @a_regs=(0...@regs.size).to_a
2060
+ @cb_regs,@a_regs=@a_regs.partition{|reg_n| /^[cb]/===match_method(@regs[reg_n])}
2061
+ unless @a_regs.empty?
2062
+ a_part=<<-A
2063
+ x=progress.cursor.readahead1
2064
+ progress.throw unless #{
2065
+ @a_regs.map{|a| " @regs[#{a}]===x"}.join(" and \n")
2066
+ }
2067
+ A
2068
+ return a_part+" progress.cursor.read1\n yield\n" if @cb_regs.empty?
2069
+ end
2070
+ @c_regs,@b_regs=@cb_regs.partition{|reg_n| /^c/===match_method(@regs[reg_n])}
2071
+ @c_regs=@regs.values_at(*@c_regs)
2072
+ @c_regs<<OB unless @c_regs.empty? or @a_regs.empty? #signal to andmachine that >=1 item must always match
2073
+ # @b_regs=@regs.values_at(*@b_regs)
2074
+ unless @b_regs.empty?
2075
+ b_part=<<-B
2076
+ cu=progress.cursor
2077
+ ends=[]
2078
+ pos=cu.pos
2079
+ #{@b_regs.map{|n| "
2080
+ @regs[#{n}].bmatch(progress) or progress.throw
2081
+ ends<<cu.pos
2082
+ "}.join("\n cu.pos=pos\n")}
2083
+ B
2084
+ return a_part.to_s+b_part+" cu.pos=ends.max\n yield\n" if @c_regs.empty?
2085
+ end
2086
+ return <<-C
2087
+ #p :and_cmatch
2088
+ #{a_part}#{b_part}
2089
+ ands=::Reg::AndMachine.new(progress,*@c_regs#{"+[OB*ends.max]" if b_part})
2090
+ #{maybe_progress}catch(:RegAndFail){
2091
+ loop{
2092
+ progress.bt_stop{
2093
+ ands.try_match or #{maybe_progress}throw :RegAndFail
2094
+ #p :and_yielding, progress.cursor.pos
2095
+
2096
+ yield
2097
+
2098
+ # progress.bt_backup
2099
+ }
2100
+ }
2101
+ }
2102
+ C
2103
+ end
2104
+
2105
+ false&& class Naive
2106
+ warn "unimplemented And::Naive"
2107
+ end
2108
+ end
2109
+
2110
+ #--------------------------------------------------------------
2111
+ class AndMachine
2112
+
2113
+ class Semaphore<SizedQueue
2114
+ #ick, i shouldn't have to build a semaphore in terms of a SizedQueue...
2115
+ #semaphore is the more primitive notion, SizedQueue should be built on it instead
2116
+ def initialize
2117
+ super(1)
2118
+ end
2119
+ undef_method :max=,:max,:<<,:push,:pop,:shift
2120
+
2121
+ private :enq,:deq
2122
+
2123
+ def wait
2124
+ deq
2125
+ end
2126
+
2127
+ def signal
2128
+ enq nil
2129
+ end
2130
+
2131
+ def signalled?
2132
+ size>0
2133
+ end
2134
+ end
2135
+
2136
+ def initialize progress, *regs
2137
+ @progress,@regs=progress,regs
2138
+
2139
+ @wake_main=Semaphore.new
2140
+
2141
+ @threads=[]
2142
+
2143
+ @threadctl=(0...@regs.size).map{Semaphore.new}
2144
+
2145
+ #create a thread for each subexpression
2146
+ #each thread gets its own progress.
2147
+ #however, all progresses have a dup of
2148
+ #the current cursor as their cursor.
2149
+ #threads are used, but they are run successively, not concurrently.
2150
+ #each thread starts the next in the series, and the last reawakens
2151
+ #the main thread (caller of try_match)
2152
+ #because of this serialization, ::Sequences don't need to be thread-safe, (to be used with threads here)
2153
+ #nor do we need to worry about backcaptures in one alternative that
2154
+ #are used in a backreference in a subsequent alternative,
2155
+ #creating an order dependancy between them.
2156
+ #(such order dependancies should be detected and cause internal reordering
2157
+ #to ensure captures threads before corresponding backrefs... not done currently.)
2158
+ @longest=nil
2159
+ if @regs.size.nonzero?
2160
+ start_thread 0,@progress.cursor.pos
2161
+ @wake_main.wait #wait til all children finish
2162
+ end
2163
+ end
2164
+
2165
+ def continuing_to_match progress
2166
+ @threads.each{|thr| thr_progress=thr[:progress]
2167
+ progress.variable_names.each{|vname|
2168
+ progress.raw_register_var vname,thr_progress.raw_variable(vname)
2169
+ }
2170
+ }
2171
+
2172
+ end
2173
+
2174
+ def sort_in pair
2175
+ @sortedthreads=(0...@threads.size).sort_by{|idx| @threads[idx][:pos]}
2176
+ end
2177
+
2178
+ def try_match
2179
+
2180
+ if @longest
2181
+ #after having inheirited its length, reawaken the longest thread
2182
+ @threadctl[@longest].signal
2183
+
2184
+ end
2185
+
2186
+ # @wake_main.wait #wait til all children finish
2187
+
2188
+ # if any thread woke us because
2189
+ #it failed, return false,
2190
+ if @wake_reason==ThreadFail
2191
+ @threads.each{|thr|thr.kill}
2192
+ @threads=nil
2193
+ return nil
2194
+ end
2195
+
2196
+ assert @threads.size == @regs.size
2197
+ assert @threadctl.size == @regs.size
2198
+
2199
+ if @longest
2200
+ sort_in @longest
2201
+ else
2202
+ @sortedthreads=(0...@threads.size).sort_by{|idx| @threads[idx][:pos]}
2203
+ end
2204
+
2205
+ warning %#otherwise, need to update progress with side effects from all threads#
2206
+
2207
+ #find longest thread
2208
+ @longest=@sortedthreads.pop
2209
+
2210
+ #p :found_longest, @longest, @wake_reason, @threads.size, t=@threads[1], t && t[:pos]
2211
+
2212
+ #update overall progress with length of longest thread
2213
+ @progress.cursor.pos=@threads[@longest][:pos]
2214
+
2215
+ #p :end_try_match, @progress.cursor.pos
2216
+
2217
+ continuing_to_match(@progress) if respond_to? :continuing_to_match
2218
+
2219
+ return true
2220
+ end
2221
+
2222
+ private
2223
+ def start_thread idx,origpos
2224
+ #huh "do I really need to create a new progress here?"
2225
+ #p=Progress.new(@regs[idx],@progress.cursor.position)
2226
+ andprogress=And::ThreadProgress.new(@regs[idx],@progress)
2227
+ @progress.child_threads.push @threads[idx]=
2228
+ Thread.new(andprogress,idx,origpos,&method(:thread))
2229
+ @threadctl[idx].signal
2230
+ end
2231
+
2232
+ def vmatch(reg,progress)
2233
+ case reg
2234
+ when HasCmatch; reg.cmatch(progress){yield}
2235
+ when HasBmatch; reg.bmatch(progress) and yield
2236
+ else progress.cursor.skip reg and yield
2237
+ end
2238
+ p( -1 )
2239
+ progress.throw
2240
+ end
2241
+
2242
+
2243
+ ThreadResync=1
2244
+ ThreadFail=2
2245
+ #
2246
+ def thread progress,idx,origpos
2247
+ warning %#progress var bindings should backup with @progress's bindings#
2248
+ warning %#each thread should wait for all threads that it depends on#
2249
+ warning %#need counting semaphores#
2250
+ progress.thread=Thread.current
2251
+ Thread.current[:progress]=progress
2252
+ @threadctl[idx].wait
2253
+ #p :start_thread, idx
2254
+ cu=progress.cursor
2255
+ progress.send($bt_catch_method){
2256
+ #p 0
2257
+ vmatch(@regs[idx],progress) {
2258
+ Thread.current[:pos]=cu.pos
2259
+ cu.pos=origpos
2260
+ if idx+1<@regs.size and !@longest
2261
+ warning %#each thread should awaken the threads dependant on it#
2262
+ warning %#need counting semaphores#
2263
+ start_thread(idx+1,origpos)
2264
+ else
2265
+ warning %#should awaken main thread after all subthreads sleep#
2266
+ warning %#need counting semaphores#
2267
+ @wake_reason=ThreadResync
2268
+ @wake_main.signal
2269
+ #p 1
2270
+ end
2271
+ @threadctl[idx].wait
2272
+ cu.pos=Thread.current[:pos]
2273
+ progress.throw
2274
+ }}
2275
+ #ensure
2276
+
2277
+ @wake_reason=ThreadFail
2278
+ #p 1.9, @wake_main.signalled?
2279
+ @wake_main.signal
2280
+ #p 2
2281
+ end
2282
+
2283
+ end
2284
+
2285
+ class Variable
2286
+ def generate_cmatch
2287
+ "@o.cmatch(progress){yield}\n"
2288
+ end
2289
+
2290
+ def generate_bmatch
2291
+ "@o.bmatch(progress)\n"
2292
+ end
2293
+ end
2294
+
2295
+ class InhibitBacktracking < Wrapper
2296
+ include HasBmatch
2297
+ instance_eval(&WrapCmatch)
2298
+ def generate_bmatch
2299
+ "@o.bmatch(progress)\n"
2300
+ end
2301
+ end
2302
+ module Reg
2303
+ def inhibit_bt
2304
+ InhibitBacktracking.new self
2305
+ end
2306
+ end
2307
+ end
2308
+
2309
+ #--------------------------------------------------------------
2310
+ #delete all interpreter-related stuff
2311
+
2312
+ #constants
2313
+ ::Reg.constants.grep(/MatchSet/).each{|k|
2314
+ ::Reg.__send__ :remove_const, k
2315
+ }
2316
+
2317
+ ::Reg::Progress.__send__ :remove_const, :Context
2318
+
2319
+ #methods
2320
+ meths=[]
2321
+ ::Reg.constants.each{|k|
2322
+ k=::Reg.const_get k
2323
+ if Module===k
2324
+ k.instance_methods.include? "mmatch" and meths<<[k,"mmatch"]
2325
+ k.instance_methods.include? "mmatch_full" and meths<<[k,"mmatch_full"]
2326
+ end
2327
+ }
2328
+
2329
+ meths+=[[Reg::Progress,"bt_match"],[Reg::Progress,"backtrack"],
2330
+ [Reg::Progress,"last_next_match"],[Reg::Reg,"multiple_infection"],
2331
+ [Reg::Multiple,"maybe_multiple"]
2332
+ ]
2333
+
2334
+ meths.each{|(k,m)|
2335
+ k.__send__ :undef_method,(m) if k.instance_methods.include? m
2336
+ }
2337
+
2338
+
2339
+
2340
+
2341
+