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.
- checksums.yaml +4 -0
- data/COPYING +0 -0
- data/History.txt +14 -0
- data/Makefile +59 -0
- data/README +87 -40
- data/article.txt +838 -0
- data/{assert.rb → lib/assert.rb} +3 -3
- data/{reg.rb → lib/reg.rb} +11 -4
- data/lib/reg/version.rb +21 -0
- data/lib/regarray.rb +455 -0
- data/{regarrayold.rb → lib/regarrayold.rb} +33 -7
- data/lib/regbackref.rb +73 -0
- data/lib/regbind.rb +230 -0
- data/{regcase.rb → lib/regcase.rb} +15 -5
- data/lib/regcompiler.rb +2341 -0
- data/{regcore.rb → lib/regcore.rb} +196 -85
- data/{regdeferred.rb → lib/regdeferred.rb} +35 -4
- data/{regposition.rb → lib/regevent.rb} +36 -38
- data/lib/reggraphpoint.rb +28 -0
- data/lib/reghash.rb +631 -0
- data/lib/reginstrumentation.rb +36 -0
- data/{regitem_that.rb → lib/regitem_that.rb} +32 -11
- data/{regknows.rb → lib/regknows.rb} +4 -2
- data/{reglogic.rb → lib/reglogic.rb} +76 -59
- data/{reglookab.rb → lib/reglookab.rb} +31 -21
- data/lib/regmatchset.rb +323 -0
- data/{regold.rb → lib/regold.rb} +27 -27
- data/{regpath.rb → lib/regpath.rb} +91 -1
- data/lib/regposition.rb +79 -0
- data/lib/regprogress.rb +1522 -0
- data/lib/regrepeat.rb +307 -0
- data/lib/regreplace.rb +254 -0
- data/lib/regslicing.rb +581 -0
- data/lib/regsubseq.rb +72 -0
- data/lib/regsugar.rb +361 -0
- data/lib/regvar.rb +180 -0
- data/lib/regxform.rb +212 -0
- data/{trace.rb → lib/trace_during.rb} +6 -4
- data/lib/warning.rb +37 -0
- data/parser.txt +26 -8
- data/philosophy.txt +18 -0
- data/reg.gemspec +58 -25
- data/regguide.txt +18 -0
- data/test/andtest.rb +46 -0
- data/test/regcompiler_test.rb +346 -0
- data/test/regdemo.rb +20 -0
- data/{item_thattest.rb → test/regitem_thattest.rb} +2 -2
- data/test/regtest.rb +2125 -0
- data/test/test_all.rb +32 -0
- data/test/test_reg.rb +19 -0
- metadata +108 -73
- data/calc.reg +0 -73
- data/forward_to.rb +0 -49
- data/numberset.rb +0 -200
- data/regarray.rb +0 -675
- data/regbackref.rb +0 -126
- data/regbind.rb +0 -74
- data/reggrid.csv +1 -2
- data/reghash.rb +0 -318
- data/regprogress.rb +0 -1054
- data/regreplace.rb +0 -114
- data/regsugar.rb +0 -230
- data/regtest.rb +0 -1078
- data/regvar.rb +0 -76
data/lib/regcompiler.rb
ADDED
@@ -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
|
+
|