reg 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+
2
+
3
+ That's a long story, and well worth telling.
4
+
5
+ A long time ago, I wanted a better regexp than regexp. My search ended
6
+ when I found an extremely obscure language called gema (the
7
+ general-purpose matcher). I'm guessing that I'm the only person to ever
8
+ take gema seriously. For a time, I became the worlds foremost expert on
9
+ gema. Gema is designed around the idea that all computation can be
10
+ modeled as pattern and replacement. Everything in gema is pattern and
11
+ replacement... essentially everything is done with regexps. I was
12
+ fascinated with the idea. This seemed to me to be a much better model
13
+ for most programming problems, which typically involve reading input,
14
+ tranforming it in some way, and writing it out again. Conventional
15
+ languages (starting with fortran, and including ruby) are based around
16
+ the idea of a program being a long string of formulas. This is great
17
+ for math-heavy stuff, but most programming is really about data
18
+ manipulation, not math.
19
+
20
+ But there was trouble in paradise. Gema was wonderful, but weird. The
21
+ syntax was cranky. The author had issued one version long ago then
22
+ disappeared. Gema code was hard to read, in part because
23
+ everythingwasalljammedtogether .
24
+ Ifyouinsertspacestomakeitmorer eadable,itchangesthesemanticso fyourprogram.
25
+ There were strange problems that I never tracked down or fully
26
+ characterized. The only data-type was the string. You had to be an
27
+ expert at avoiding the invisible pitfalls of the language to get
28
+ anywhere. But I did get surprisingly far. I managed to coax gema into
29
+ becoming a true parser, and parsing a toy language.
30
+ I wanted to write a compiler in gema. Yes, the whole compiler. And
31
+ parsing the toy language was already straining its capabilites. It
32
+ wasn't the data model; I actually figured out how to model all other
33
+ data types using strings. A match-and-replace language is actually much
34
+ better suited to most compiler tasks than an algol-like formula
35
+ language.
36
+
37
+ Eventually, I abandoned gema, determined to recreate it's glory in a
38
+ cleaner form. It was at about this time that I discovered ruby. The
39
+ successor to gema was ruma, the ruby matcher. Ruma would be basically
40
+ just like gema, but without the problems. Whitespace allowed between
41
+ tokens. Proper quotation mechanisms, including nested quotes. And the
42
+ language used in the actions (replacements) would be full ruby, instead
43
+ of gema's inadequate and crude action language.
44
+
45
+ Ruma got maybe halfway done... quite a ways, really. As part of ruma, I
46
+ needed a ruby lexer to make sense of the actions. This turned out to be
47
+ quite a lot harder than I had anticipated; I'm still working on that
48
+ lexer.
49
+
50
+ After grinding away at the lexer for a while, dreaming of ruma in the
51
+ meantime, I had a brainstorm. Ruma, like gema, was to be a string-based
52
+ language. It only operated on strings. In gema, that was just fine
53
+ because everything was strings and you just had to live with that. But
54
+ ruby has all these other types, a real type system. Wouldn't it be nice
55
+ to have those sophisticated search capabilites for other types too?
56
+ Well, since I proved to myself that all data types can be converted to
57
+ strings, why not convert the ruby data into strings and then match that
58
+ in ruma. Of course, it would be so much nicer to just do the matching
59
+ on the data in it's original form....
60
+
61
+ The breakthrough came when I realized how malleable ruby really is. I
62
+ had become accustomed to c, which I still love, but in so many ways
63
+ it's so much more limited. I didn't really have to write my own parser
64
+ and lexer; ruby could do it all for me. I just had to override a bunch
65
+ of operators.
66
+
67
+ After that, it was simple. All I do is override the right operators,
68
+ and ruby does the parsing and hands me the match expressions in
69
+ already-parsed form. Reg is amazingly small in the end. Most of the
70
+ effort and code went into the array matcher, but at least as much
71
+ functionality is to be had from the hash and object matchers, which
72
+ were trivial.
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'reg'
5
+ s.rubyforge_project = 'reg'
6
+ s.version = '0.4.6'
7
+ s.summary = 'The reg pattern matching/replacement language'
8
+ s.files = %w[item_thattest.rb regbackref.rb regknows.rb regtest.rb
9
+ numberset.rb regbind.rb reglogic.rb regvar.rb
10
+ COPYING parser.txt regcase.rb reglookab.rb
11
+ README regcore.rb regold.rb reg.gemspec reggrid.csv
12
+ assert.rb philosophy.txt regdeferred.rb regpath.rb
13
+ regposition.rb trace.rb calc.reg reg.rb
14
+ regguide.txt regprogress.rb reghash.rb regreplace.rb
15
+ forward_to.rb regarray.rb regarrayold.rb regitem_that.rb regsugar.rb]
16
+ s.require_path = '.'
17
+ s.has_rdoc = false
18
+ s.requirements=["none"]
19
+ s.add_dependency("cursor", [">= 0.9"])
20
+ s.author = 'Caleb Clausen'
21
+ end
22
+
23
+
24
+ if $0==__FILE__
25
+ Gem::manage_gems
26
+ Gem::Builder.new(spec).build
27
+ end
data/reg.rb ADDED
@@ -0,0 +1,33 @@
1
+ =begin copyright
2
+ reg - the ruby extended grammar
3
+ Copyright (C) 2005 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 'regcore'
20
+ require 'reglogic'
21
+ require 'reghash'
22
+ require 'regarray'
23
+ #require 'regarrayold' #old bt engine
24
+ require 'regprogress' #new bt engine
25
+ #enable one engine or the other, but not both
26
+
27
+ require 'regbackref'
28
+ require 'regitem_that'
29
+ require 'regknows'
30
+ require 'regsugar'
31
+ require 'regold' #will go away
32
+ Kernel.instance_eval( &Reg::TLA_pirate)
33
+ Reg::Sugar.include!
@@ -0,0 +1,675 @@
1
+ =begin copyright
2
+ reg - the ruby extended grammar
3
+ Copyright (C) 2005 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
+
20
+ require "assert"
21
+ require "pp"
22
+
23
+ module Reg
24
+ module Reg
25
+ def itemrange; 1..1 end #default match 1 item
26
+
27
+
28
+ #create a (vector) Reg that will match this pattern repeatedly.
29
+ #(creates a Reg::Repeat.)
30
+ #the argument determines the number of times to match.
31
+ #times may be a positive integer, zero, INFINITY, or a
32
+ #range over any of the above. if a range, the lower
33
+ #end may not be INFINITY! Reg#- and Reg#+ are shortcuts
34
+ #for the most common cases of multiplting by a range.
35
+ #(at least 0 and at most INFINITY.) watch out when
36
+ #multiplying with zero and INFINITY (including in a
37
+ #range), as you can easily create a situation where
38
+ #the number of matches to enumerate explodes exponentionaly,
39
+ #or even is infinite. i won't say too much here except
40
+ #that these are generally the same sorts of problems you
41
+ #can run into with Regexps as well.
42
+ def *(times=0..INFINITY)
43
+ Repeat.new(self,times)
44
+ end
45
+
46
+ #repeat this pattern up to atmost times. could match
47
+ #0 times as the minimum number of matches here is zero.
48
+ def -(atmost=1)
49
+ self*(0..atmost)
50
+ end
51
+
52
+ #repeat this pattern atleast times or more
53
+ def +(atleast=1)
54
+ self*(atleast..INFINITY)
55
+ end
56
+
57
+ end
58
+
59
+ #--------------------------
60
+ module Multiple
61
+ def ===(other)
62
+ method_missing(:===, other)
63
+ end
64
+
65
+ def maybe_multiple(needsmult) #better name needed
66
+ assert( needsmult.respond_to?( :mmatch))
67
+ class <<needsmult
68
+ undef_method :mmatch
69
+ include Multiple
70
+ #alias mmatch mmatch_multiple #this doesn't work... why?
71
+ def mmatch(a,s) mmatch_multiple(a,s); end #have to do this instead
72
+ end
73
+ assert( needsmult.respond_to?( :mmatch))
74
+ end
75
+
76
+ def maybe_multiples(*args) end
77
+ #we're already multiple; no need to try to become multiple again
78
+
79
+ def mmatch(arr,idx) #multiple match
80
+ abstract
81
+ end
82
+
83
+ #negated Reg::Multiple's are automatically lookaheads (not implemented yet)
84
+ def ~
85
+ ~(Lookahead.new self)
86
+ end
87
+
88
+
89
+ def starts_with
90
+ abstract
91
+ end
92
+
93
+ def ends_with
94
+ abstract
95
+ end
96
+
97
+ def matches_class
98
+ raise 'multiple regs match no single class'
99
+ end
100
+ end
101
+
102
+ #--------------------------
103
+ module Backtrace
104
+ # protected
105
+
106
+ def regs(ri) @regs[ri] end
107
+
108
+ def update_di(di,len); di+len; end
109
+ #--------------------------
110
+ $RegTraceEnable=$RegTraceDisable=nil
111
+ def trace_enabled?
112
+ @trace||=nil
113
+ $RegTraceEnable or (!$RegTraceDisable && @trace)
114
+ end
115
+
116
+ #--------------------------
117
+ def trace!
118
+ @trace=true
119
+ self
120
+ end
121
+
122
+ #--------------------------
123
+ def notrace!
124
+ @trace=false
125
+ self
126
+ end
127
+ end
128
+
129
+ #--------------------------
130
+ if false
131
+ class RR < ::Array
132
+ def inspect
133
+ [self,super].to_s
134
+ end
135
+
136
+ def rrflatten
137
+ result=[]
138
+ each{|i|
139
+ case i
140
+ when RR then result +=i.rrflatten
141
+ when Literal then result << i.unlit
142
+ else result << i
143
+ end
144
+ }
145
+ end
146
+
147
+ def +(other)
148
+ RR[*super]
149
+ end
150
+ end
151
+ Result=RR
152
+ else
153
+ RR=::Array
154
+ end
155
+
156
+ #--------------------------
157
+ class MatchSet
158
+
159
+ def next_match(ary,start)
160
+ abstract
161
+ end
162
+
163
+ def deep_copy
164
+ abstract
165
+ end
166
+
167
+ def ob_state
168
+ instance_variables.sort.map{|i| instance_variable_get i }
169
+ end
170
+
171
+ def ==(other)
172
+ self.class==other.class and ob_state==other.ob_state
173
+ end
174
+
175
+ end
176
+
177
+ #--------------------------
178
+ class SingleRepeatMatchSet < MatchSet
179
+ def initialize(startcnt,stepper,endcnt)
180
+ endcnt==startcnt and raise 'why even make it a set, then?'
181
+ (endcnt-startcnt)*stepper>0 or raise "tried to make null match set"
182
+ assert startcnt>=0
183
+ assert endcnt>=0
184
+ @matchtimes,@stepper,@endcnt=startcnt,stepper,endcnt
185
+ end
186
+
187
+ def next_match(arr,idx)
188
+ assert @stepper == -1 #'only greedy matching implemnted for now'
189
+ @endcnt<=@matchtimes or return nil
190
+ assert @matchtimes >=0
191
+ result=[RR[arr[idx...idx+@matchtimes]], @matchtimes]
192
+ assert ::Array===result.first.first
193
+ @matchtimes+=@stepper
194
+
195
+ assert @matchtimes >=-1
196
+
197
+ assert ::Array===result.first.first
198
+ return result
199
+ end
200
+
201
+ def deep_copy
202
+ dup
203
+ end
204
+ end
205
+
206
+
207
+ #--------------------------
208
+ class Repeat
209
+ include Reg,Backtrace,Multiple
210
+
211
+ attr :times
212
+
213
+ def max_matches; @times.end end
214
+
215
+ def regs(ri) @reg end
216
+
217
+ def initialize(reg,times)
218
+ Integer===times and times=times..times
219
+ times.exclude_end? and times=times.begin..times.end-1
220
+ assert times.begin <= times.end
221
+ assert times.begin < INFINITY
222
+ assert times.begin >= 0
223
+ assert times.end >= 0
224
+ if Multiple===reg
225
+ class<<self
226
+ #alias mmatch mmatch_multiple #this doesn't work... why?
227
+ def mmatch(a,s) mmatch_multiple(a,s); end #have to do this instead
228
+ end
229
+ else
230
+ assert reg.itemrange==(1..1)
231
+ @itemrange=times
232
+ end
233
+ @reg,@times=reg,times
234
+ end
235
+
236
+ def itemrange
237
+ defined? @itemrange and return @itemrange
238
+
239
+ i=@reg.itemrange
240
+ rf,rl=i.first,i.last
241
+ tf,tl=times.first,times.last
242
+ @itemrange = rf*tf ..
243
+ if tl==0 or rl==0
244
+ 0
245
+ elsif tl==INFINITY
246
+ #ought to emit warnings if trouble here...
247
+ #rl==INFINITY and maybe trouble
248
+ #rf==0 and trouble
249
+ INFINITY
250
+ elsif rl==INFINITY
251
+ #...and here
252
+ #maybe trouble #... combinatorial explosion
253
+ INFINITY
254
+ else
255
+ rl*tl
256
+ end
257
+ end
258
+
259
+
260
+ def enough_matches? matchcnt
261
+ @times===matchcnt
262
+ end
263
+
264
+ def inspect
265
+ if @times.end==INFINITY
266
+ "(#{@reg.inspect})+#{@times.begin}"
267
+ elsif @times.begin==0
268
+ "(#{@reg.inspect})-#{@times.end}"
269
+ elsif @times.begin==@times.end
270
+ "(#{@reg.inspect})*#{@times.begin}"
271
+ else
272
+ "(#{@reg.inspect})*(#{@times.begin}..#{@times.end})"
273
+ end
274
+ end
275
+
276
+ def subregs; @reg end
277
+
278
+ private
279
+
280
+ end
281
+
282
+
283
+
284
+
285
+ #--------------------------
286
+ class OrMatchSet < MatchSet
287
+ def initialize(orreg,idx,set,firstmatch)
288
+ @orreg,@idx,@set,@firstmatch=orreg,idx,set,firstmatch
289
+ assert @firstmatch.nil? || ::Array===@firstmatch.first.first
290
+ end
291
+
292
+ def ob_state
293
+ instance_variables.map{|i| instance_variable_get i }
294
+ end
295
+
296
+ def ==(other)
297
+ OrMatchSet===other and ob_state==other.ob_state
298
+ end
299
+
300
+ def next_match(ary,idx)
301
+ if @firstmatch
302
+ result,@firstmatch=@firstmatch,nil
303
+ assert ::Array===result
304
+ assert ::Array===result.first.first
305
+ assert 2==result.size
306
+ assert Integer===result.last
307
+ return result
308
+ end
309
+ @set and result= @set.next_match(ary,idx)
310
+ while result.nil?
311
+ @idx+=1
312
+ @idx >= @orreg.regs.size and return nil
313
+ x=@orreg.regs[@idx].mmatch(ary,idx)
314
+ @set,result=*if MatchSet===x then [x,x.next_match] else [nil,x] end
315
+ end
316
+ a=RR[nil]*@orreg.regs.size
317
+ a[idx]=result[0]
318
+ result[0]=a
319
+ assert ::Array===result.first.first
320
+ return result
321
+ end
322
+
323
+ def deep_copy
324
+ result=OrMatchSet.new(@orreg,@idx,@set && @set.deep_copy,@firstmatch)
325
+ assert self==result
326
+ return result
327
+ end
328
+ end
329
+
330
+ #--------------------------
331
+ class Or
332
+ def mmatch(arr,start)
333
+ assert start <= arr.size
334
+ @regs.each_with_index {|reg,i|
335
+ reg===arr[start] and
336
+ return OrMatchSet.new(self,i,nil,[arr[start]])
337
+ } unless start == arr.size
338
+ return nil
339
+ end
340
+
341
+ def itemrange
342
+ if true
343
+ min,max=INFINITY,0
344
+ @regs.each {|r|
345
+ min=r.itemrange.first if min>r.itemrange.first
346
+ max=r.itemrange.last if max<r.itemrange.last
347
+ }
348
+ return min..max
349
+ else
350
+ limits=@regs.map{|r|
351
+
352
+ i=(r.respond_to? :itemrange)? r.itemrange : 1..1
353
+ [i.first,i.last]
354
+ }.transpose
355
+ limits.first.sort.first .. limits.last.sort.last
356
+ end
357
+ end
358
+
359
+ private
360
+ def mmatch_multiple(arr,start)
361
+ mat=i=nil
362
+ @regs.each_with_index{|r,i|
363
+ if r.respond_to? :mmatch
364
+ mat=r.mmatch(arr,start) or next
365
+ if mat.respond_to? :next_match
366
+ return OrMatchSet.new(self,i,mat,mat.next_match(arr,start))
367
+ else
368
+ return OrMatchSet.new(self,i,nil,mat)
369
+ end
370
+ else
371
+ r===arr[start] and
372
+ return OrMatchSet.new(self,i,nil,[[[arr[start]]],1])
373
+ end
374
+ }
375
+
376
+ assert mat.nil?
377
+ return nil
378
+ end
379
+ end
380
+
381
+ #--------------------------
382
+ class Xor
383
+ def clean_result
384
+ huh
385
+ end
386
+
387
+ def itemrange
388
+ #min,max=INFINITY,0
389
+ #@regs.each {|r|
390
+ # min=[min,r.itemrange.first].sort.first
391
+ # max=[r.itemrange.last,max].sort.last
392
+ #}
393
+ #return min..max
394
+ limits=@regs.map{|r| i=r.itemrange; [i.first,i.last]}.transpose
395
+ limits.first.sort.first .. limits.last.sort.last
396
+ end
397
+
398
+ private
399
+ =begin
400
+ def mmatch_multiple(arr,start)
401
+ mat=i=nil
402
+ count=0
403
+ @regs.each_with_index{|reg,idx|
404
+ if reg.respond_to? :mmatch
405
+ mat=reg.mmatch(arr,start) or next
406
+ else
407
+ reg===arr[start] or next
408
+ mat=[[arr[start]],1]
409
+ end
410
+ count==0 or return nil
411
+ count=1
412
+ assert mat
413
+ }
414
+
415
+ return nil unless mat
416
+ assert count==1
417
+ mat.respond_to? :next_match and return XorMatchSet.new(reg,idx,mat,huh)
418
+
419
+ a=RR[nil]*regs.size
420
+ a[idx]=mat[0]
421
+ mat[0]=a
422
+ assert huh
423
+ assert ::Array===mat.first.first
424
+ return mat
425
+ end
426
+ =end
427
+
428
+ def mmatch_multiple arr, start
429
+ found=nil
430
+ @regs.each{|reg|
431
+ if m=reg.mmatch(arr, start)
432
+ return if found
433
+ found=m
434
+ end
435
+ }
436
+ return found
437
+ end
438
+
439
+ end
440
+
441
+ #--------------------------
442
+ class And
443
+ include Backtrace #shouldn't this be included only when needed?
444
+
445
+ def update_di(di,len) di; end
446
+
447
+
448
+ def clean_result
449
+ huh
450
+ end
451
+
452
+
453
+ def enough_matches? matchcnt
454
+ matchcnt==@regs.size
455
+ end
456
+
457
+ def itemrange
458
+ limits=@regs.map{|r| i=r.itemrange; [i.first,i.last]}.transpose
459
+ limits.first.sort.last .. limits.last.sort.last
460
+ end
461
+
462
+ private
463
+ def mmatch_multiple(arr,start)
464
+ #in this version, at least one of @regs is a multiple reg
465
+ assert( (0..arr.size).include?( start))
466
+ result,*bogus=huh.bt_match(arr,start,0,0,[RR[]])
467
+ result and AndMatchSet.new(self,result)
468
+ end
469
+ end
470
+
471
+ #--------------------------
472
+ class Array
473
+ include Reg,Backtrace
474
+
475
+ def max_matches; @regs.size end
476
+
477
+ def initialize(*regs)
478
+ @regs=regs
479
+ end
480
+
481
+ class <<self
482
+ alias new__nobooleans new
483
+ def new(*args)
484
+ # args.detect{|o| /^(AND|X?OR)$/.sym===o } or return new__nobooleans(*args)
485
+ # +[/^(AND|X?OR)$/.sym.splitter].match(args)
486
+ Pair===args.first and return OrderedHash.new(*args)
487
+ new__nobooleans(*args)
488
+ end
489
+ alias [] new
490
+ end
491
+
492
+ def matches_class; ::Array end
493
+
494
+ def -@ #subsequence inclusion
495
+ Subseq.new(*@regs)
496
+ end
497
+
498
+ def +@ #cvt to Reg::Array; that what we are already....
499
+ self
500
+ end
501
+
502
+ def maybe_multiples(*args) end #never do anything for Reg::Array
503
+
504
+ def enough_matches? matchcnt
505
+ matchcnt==@regs.size
506
+ end
507
+
508
+ def +(reg)
509
+ #not right... + should not modify self
510
+ if self.class==reg.class
511
+ @regs.concat reg.regs
512
+ else
513
+ super
514
+ end
515
+ end
516
+
517
+ def inspect
518
+ "+["+ @regs.collect{|r| r.inspect}.join(', ') +"]"
519
+ end
520
+
521
+ def subregs; @regs end
522
+ end
523
+
524
+
525
+
526
+
527
+
528
+
529
+
530
+ #--------------------------
531
+ class Subseq < ::Reg::Array
532
+ include Multiple
533
+
534
+ def max_matches; @regs.size end
535
+
536
+ def initialize(*regs)
537
+ regs.each{|reg| Multiple===reg and class<<self
538
+ undef mmatch
539
+ def mmatch(a,s) mmatch_multiple(a,s) end
540
+ end}
541
+
542
+ @regs=regs
543
+ end
544
+
545
+
546
+ def inspect
547
+ super.sub( /^\+/,'-')
548
+ end
549
+
550
+ def itemrange
551
+ #add the ranges of the individual items
552
+ @itemrange ||= #some caching...
553
+ @regs.inject(0){|sum,ob| sum+ob.begin } ..
554
+ @regs.inject(0){|sum,ob| sum+ob.end }
555
+ end
556
+
557
+ def -@ #subsequence inclusion... that's what we are, do nothing
558
+ self
559
+ end
560
+
561
+ def +@ #cvt to Reg::Array
562
+ Array.new(*@regs)
563
+ end
564
+
565
+ private
566
+
567
+ #tla of +[], regproc{}
568
+ assign_TLA true, :Reg=>:Array
569
+ assign_TLA :Res=>:Subseq
570
+ #no need to alias the constant name 'Reg', too.
571
+ #ruby does it for us.
572
+ end
573
+
574
+
575
+
576
+ #--------------------------
577
+ class None; end
578
+ class <<None
579
+ include Reg
580
+ def new; self end
581
+
582
+ def *(times)
583
+ times===0 ? Many[0] : self
584
+ end
585
+
586
+ def ~; Any; end
587
+
588
+ def &(other); self; end
589
+
590
+ def |(other) other end
591
+ def ^(other) other end
592
+
593
+ def ===(other); false; end
594
+ def matches_class; self; end
595
+ end
596
+
597
+ if defined? $RegAnyEnable #disabled for now -- these optimizations are broken
598
+
599
+ #--------------------------
600
+ class Any; end
601
+ class <<Any #maybe all this can be in Object's meta-class....
602
+ include Reg
603
+
604
+ #any is a singleton
605
+ def new; self end
606
+
607
+ def *(times)
608
+ Many.new(times)
609
+ end
610
+
611
+ def ~; None; end
612
+
613
+ def &(other); other; end
614
+
615
+ def |(other); self; end
616
+ def ^(other); ~other end
617
+
618
+ def ===(other); true;end
619
+ def matches_class; ::Object end
620
+ end
621
+
622
+ #--------------------------
623
+ class Many
624
+ include Reg
625
+ include Multiple
626
+
627
+ class <<self
628
+ @@RAMs={}
629
+ alias uncached__new new
630
+ def new times=0..INFINITY
631
+ @@RAMs[times] ||= uncached__new times
632
+ end
633
+ alias [] new
634
+ end
635
+
636
+ def initialize(times=0..INFINITY)
637
+ Integer===times and times=times..times
638
+ @times=times
639
+ end
640
+
641
+ def mmatch(arr,start)
642
+ left=arr.size-start
643
+ beg=@times.begin
644
+ beg<=left and
645
+ SingleRepeatMatchSet.new([left,@times.end].max, -1, beg)
646
+ end
647
+
648
+ def subregs; Any end
649
+
650
+ def inspect; "Any*(#{@times})"; end
651
+ end
652
+
653
+ #--------------------------
654
+ class ::Object
655
+ def reg
656
+ Any
657
+ end
658
+ end
659
+ OB=Any
660
+ OBS=Many[]
661
+
662
+ else #traditional and uncomplicated version of OB and OBS
663
+ OB=::Object.reg
664
+ OBS=OB+0 #std abbreviation for 0 or more of anything
665
+ def OBS.inspect
666
+ "OBS"
667
+ end
668
+ def OB.inspect
669
+ "OB"
670
+ end
671
+ end
672
+
673
+
674
+
675
+ end