pattern-match 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,710 @@
1
+ require 'pattern-match/version'
2
+
3
+ module PatternMatch
4
+ module Deconstructable
5
+ def call(*subpatterns)
6
+ pattern_matcher(*subpatterns)
7
+ end
8
+ end
9
+
10
+ class ::Object
11
+ def pattern_matcher(*subpatterns)
12
+ PatternObjectDeconstructor.new(self, *subpatterns)
13
+ end
14
+ end
15
+
16
+ module HasOrderedSubPatterns
17
+ private
18
+
19
+ def set_subpatterns_relation
20
+ super
21
+ @subpatterns.each_cons(2) do |a, b|
22
+ a.next = b
23
+ b.prev = a
24
+ end
25
+ end
26
+ end
27
+
28
+ class Pattern
29
+ attr_accessor :parent, :next, :prev
30
+
31
+ def initialize(*subpatterns)
32
+ @parent = nil
33
+ @next = nil
34
+ @prev = nil
35
+ @subpatterns = subpatterns.map {|i| i.kind_of?(Pattern) ? i : PatternValue.new(i) }
36
+ set_subpatterns_relation
37
+ end
38
+
39
+ def vars
40
+ @subpatterns.map(&:vars).flatten
41
+ end
42
+
43
+ def ancestors
44
+ root? ? [self] : parent.ancestors.unshift(self)
45
+ end
46
+
47
+ def quasibinding
48
+ vars.each_with_object({}) {|v, h| h[v.name] = v.val }
49
+ end
50
+
51
+ def &(pattern)
52
+ PatternAnd.new(self, pattern)
53
+ end
54
+
55
+ def |(pattern)
56
+ PatternOr.new(self, pattern)
57
+ end
58
+
59
+ def !@
60
+ PatternNot.new(self)
61
+ end
62
+
63
+ def to_a
64
+ [self, PatternQuantifier.new(0, true)]
65
+ end
66
+
67
+ def quantifier?
68
+ raise NotImplementedError
69
+ end
70
+
71
+ def quantified?
72
+ (@next and @next.quantifier?) or (root? ? false : @parent.quantified?)
73
+ end
74
+
75
+ def root
76
+ root? ? self : @parent.root
77
+ end
78
+
79
+ def root?
80
+ @parent == nil
81
+ end
82
+
83
+ def validate
84
+ @subpatterns.each(&:validate)
85
+ end
86
+
87
+ def match(vals)
88
+ if @next and @next.quantifier?
89
+ q = @next
90
+ repeating_match(vals, q.longest?) do |vs, rest|
91
+ if vs.length < q.min_k
92
+ next false
93
+ end
94
+ vs.all? {|v| yield(v) } and q.match(rest)
95
+ end
96
+ else
97
+ if vals.empty?
98
+ return false
99
+ end
100
+ val, *rest = vals
101
+ yield(val) and (@next ? @next.match(rest) : rest.empty?)
102
+ end
103
+ end
104
+
105
+ def append(pattern)
106
+ if @next
107
+ @next.append(pattern)
108
+ else
109
+ if @subpatterns.empty?
110
+ if root?
111
+ new_root = PatternAnd.new(self)
112
+ self.parent = new_root
113
+ end
114
+ pattern.parent = @parent
115
+ @next = pattern
116
+ else
117
+ @subpatterns[-1].append(pattern)
118
+ end
119
+ end
120
+ end
121
+
122
+ def inspect
123
+ "#<#{self.class.name}: subpatterns=#{@subpatterns.inspect}>"
124
+ end
125
+
126
+ private
127
+
128
+ def repeating_match(vals, longest)
129
+ quantifier = @next
130
+ lp = longest_patterns(vals)
131
+ (longest ? lp : lp.reverse).each do |(vs, rest)|
132
+ vars.each {|i| i.set_bind_to(quantifier) }
133
+ begin
134
+ if yield vs, rest
135
+ return true
136
+ end
137
+ rescue PatternNotMatch
138
+ end
139
+ vars.each {|i| i.unset_bind_to(quantifier) }
140
+ end
141
+ false
142
+ end
143
+
144
+ def longest_patterns(vals)
145
+ vals.length.downto(0).map do |n|
146
+ [vals.take(n), vals.drop(n)]
147
+ end
148
+ end
149
+
150
+ def set_subpatterns_relation
151
+ @subpatterns.each do |i|
152
+ i.parent = self
153
+ end
154
+ end
155
+ end
156
+
157
+ class PatternQuantifier < Pattern
158
+ attr_reader :min_k
159
+
160
+ def initialize(min_k, longest)
161
+ super()
162
+ @min_k = min_k
163
+ @longest = longest
164
+ end
165
+
166
+ def validate
167
+ super
168
+ raise MalformedPatternError unless @prev and ! @prev.quantifier?
169
+ raise MalformedPatternError unless @parent.kind_of?(HasOrderedSubPatterns)
170
+ seqs = ancestors.grep(PatternSequence).reverse
171
+ if seqs.any? {|i| i.next and i.next.quantifier? and not i.vars.empty? }
172
+ raise NotImplementedError
173
+ end
174
+ end
175
+
176
+ def quantifier?
177
+ true
178
+ end
179
+
180
+ def match(vals)
181
+ if @next
182
+ @next.match(vals)
183
+ else
184
+ vals.empty?
185
+ end
186
+ end
187
+
188
+ def longest?
189
+ @longest
190
+ end
191
+
192
+ def inspect
193
+ "#<#{self.class.name}: min_k=#{@min_k}, longest=#{@longest}>"
194
+ end
195
+ end
196
+
197
+ class PatternElement < Pattern
198
+ def quantifier?
199
+ false
200
+ end
201
+ end
202
+
203
+ class PatternDeconstructor < PatternElement
204
+ end
205
+
206
+ class PatternObjectDeconstructor < PatternDeconstructor
207
+ include HasOrderedSubPatterns
208
+
209
+ def initialize(deconstructor, *subpatterns)
210
+ super(*subpatterns)
211
+ @deconstructor = deconstructor
212
+ end
213
+
214
+ def match(vals)
215
+ super do |val|
216
+ deconstructed_vals = @deconstructor.deconstruct(val)
217
+ if @subpatterns.empty?
218
+ next deconstructed_vals.empty?
219
+ end
220
+ @subpatterns[0].match(deconstructed_vals)
221
+ end
222
+ end
223
+
224
+ def inspect
225
+ "#<#{self.class.name}: deconstructor=#{@deconstructor.inspect}, subpatterns=#{@subpatterns.inspect}>"
226
+ end
227
+ end
228
+
229
+ class PatternKeywordArgStyleDeconstructor < PatternDeconstructor
230
+ def initialize(klass, checker, getter, *keyarg_subpatterns)
231
+ spec = normalize_keyword_arg(keyarg_subpatterns)
232
+ super(*spec.values)
233
+ @klass = klass
234
+ @checker = checker
235
+ @getter = getter
236
+ @spec = spec
237
+ end
238
+
239
+ def match(vals)
240
+ super do |val|
241
+ next false unless val.kind_of?(@klass)
242
+ next false unless @spec.keys.all? {|k| val.__send__(@checker, k) }
243
+ @spec.all? do |k, pat|
244
+ pat.match([val.__send__(@getter, k)]) rescue false
245
+ end
246
+ end
247
+ end
248
+
249
+ def inspect
250
+ "#<#{self.class.name}: klass=#{@klass.inspect}, spec=#{@spec.inspect}>"
251
+ end
252
+
253
+ private
254
+
255
+ def normalize_keyword_arg(subpatterns)
256
+ syms = subpatterns.take_while {|i| i.kind_of?(Symbol) }
257
+ rest = subpatterns.drop(syms.length)
258
+ hash = case rest.length
259
+ when 0
260
+ {}
261
+ when 1
262
+ rest[0]
263
+ else
264
+ raise MalformedPatternError
265
+ end
266
+ variables = Hash[syms.map {|i| [i, PatternVariable.new(i)] }]
267
+ Hash[variables.merge(hash).map {|k, v| [k, v.kind_of?(Pattern) ? v : PatternValue.new(v)] }]
268
+ end
269
+ end
270
+
271
+ class PatternVariable < PatternElement
272
+ attr_reader :name, :val
273
+
274
+ def initialize(name)
275
+ super()
276
+ @name = name
277
+ @val = nil
278
+ @bind_to = nil
279
+ end
280
+
281
+ def match(vals)
282
+ super do |val|
283
+ bind(val)
284
+ true
285
+ end
286
+ end
287
+
288
+ def vars
289
+ [self]
290
+ end
291
+
292
+ def set_bind_to(quantifier)
293
+ n = nest_level(quantifier)
294
+ if n == 0
295
+ @val = @bind_to = []
296
+ else
297
+ outer = @val
298
+ (n - 1).times do
299
+ outer = outer[-1]
300
+ end
301
+ @bind_to = []
302
+ outer << @bind_to
303
+ end
304
+ end
305
+
306
+ def unset_bind_to(quantifier)
307
+ n = nest_level(quantifier)
308
+ @bind_to = nil
309
+ if n == 0
310
+ # do nothing
311
+ else
312
+ outer = @val
313
+ (n - 1).times do
314
+ outer = outer[-1]
315
+ end
316
+ outer.pop
317
+ end
318
+ end
319
+
320
+ def inspect
321
+ "#<#{self.class.name}: name=#{name.inspect}, val=#{@val.inspect}>"
322
+ end
323
+
324
+ private
325
+
326
+ def bind(val)
327
+ if quantified?
328
+ @bind_to << val
329
+ else
330
+ @val = val
331
+ end
332
+ end
333
+
334
+ def nest_level(quantifier)
335
+ raise PatternMatchError unless quantifier.kind_of?(PatternQuantifier)
336
+ qs = ancestors.map {|i| (i.next and i.next.quantifier?) ? i.next : nil }.compact.reverse
337
+ qs.index(quantifier) || (raise PatternMatchError)
338
+ end
339
+ end
340
+
341
+ class PatternValue < PatternElement
342
+ def initialize(val, compare_by = :===)
343
+ super()
344
+ @val = val
345
+ @compare_by = compare_by
346
+ end
347
+
348
+ def match(vals)
349
+ super do |val|
350
+ @val.__send__(@compare_by, val)
351
+ end
352
+ end
353
+
354
+ def inspect
355
+ "#<#{self.class.name}: val=#{@val.inspect}>"
356
+ end
357
+ end
358
+
359
+ class PatternSequence < PatternElement
360
+ include HasOrderedSubPatterns
361
+
362
+ class PatternRewind < PatternElement
363
+ attr_reader :ntimes
364
+
365
+ def initialize(ntimes, head_pattern, next_pattern)
366
+ super()
367
+ @ntimes = ntimes
368
+ @head = head_pattern
369
+ @next = next_pattern
370
+ end
371
+
372
+ def match(vals)
373
+ if @ntimes > 0
374
+ @ntimes -= 1
375
+ @head.match(vals)
376
+ else
377
+ @next ? @next.match(vals) : vals.empty?
378
+ end
379
+ end
380
+
381
+ def inspect
382
+ "#<#{self.class.name}: ntimes=#{@ntimes} head=#{@head.inspect} next=#{@next.inspect}>"
383
+ end
384
+ end
385
+
386
+ def match(vals)
387
+ if @next and @next.quantifier?
388
+ repeating_match(vals, @next.longest?) do |rewind|
389
+ if rewind.ntimes < @next.min_k
390
+ next false
391
+ end
392
+ rewind.match(vals)
393
+ end
394
+ else
395
+ with_rewind(make_rewind(1)) do |rewind|
396
+ rewind.match(vals)
397
+ end
398
+ end
399
+ end
400
+
401
+ def validate
402
+ super
403
+ raise MalformedPatternError if @subpatterns.empty?
404
+ raise MalformedPatternError unless @parent.kind_of?(HasOrderedSubPatterns)
405
+ end
406
+
407
+ private
408
+
409
+ def make_rewind(n)
410
+ PatternRewind.new(n, @subpatterns[0], (@next and @next.quantifier?) ? @next.next : @next)
411
+ end
412
+
413
+ def repeating_match(vals, longest)
414
+ quantifier = @next
415
+ lp = longest_patterns(vals)
416
+ (longest ? lp : lp.reverse).each do |rewind|
417
+ vars.each {|i| i.set_bind_to(quantifier) }
418
+ begin
419
+ with_rewind(rewind) do
420
+ if yield rewind
421
+ return true
422
+ end
423
+ end
424
+ rescue PatternNotMatch
425
+ end
426
+ vars.each {|i| i.unset_bind_to(quantifier) }
427
+ end
428
+ false
429
+ end
430
+
431
+ def longest_patterns(vals)
432
+ vals.length.downto(0).map do |n|
433
+ make_rewind(n)
434
+ end
435
+ end
436
+
437
+ def with_rewind(rewind)
438
+ @subpatterns[-1].next = rewind
439
+ yield rewind
440
+ ensure
441
+ @subpatterns[-1].next = nil
442
+ end
443
+ end
444
+
445
+ class PatternAnd < PatternElement
446
+ def match(vals)
447
+ super do |val|
448
+ @subpatterns.all? {|i| i.match([val]) }
449
+ end
450
+ end
451
+
452
+ def validate
453
+ super
454
+ raise MalformedPatternError if @subpatterns.empty?
455
+ end
456
+ end
457
+
458
+ class PatternOr < PatternElement
459
+ def match(vals)
460
+ super do |val|
461
+ @subpatterns.find do |i|
462
+ begin
463
+ i.match([val])
464
+ rescue PatternNotMatch
465
+ false
466
+ end
467
+ end
468
+ end
469
+ end
470
+
471
+ def validate
472
+ super
473
+ raise MalformedPatternError if @subpatterns.empty?
474
+ raise MalformedPatternError unless vars.empty?
475
+ end
476
+ end
477
+
478
+ class PatternNot < PatternElement
479
+ def match(vals)
480
+ super do |val|
481
+ begin
482
+ ! @subpatterns[0].match([val])
483
+ rescue PatternNotMatch
484
+ true
485
+ end
486
+ end
487
+ end
488
+
489
+ def validate
490
+ super
491
+ raise MalformedPatternError unless @subpatterns.length == 1
492
+ raise MalformedPatternError unless vars.empty?
493
+ end
494
+ end
495
+
496
+ class PatternCondition < PatternElement
497
+ def initialize(&condition)
498
+ super()
499
+ @condition = condition
500
+ end
501
+
502
+ def match(vals)
503
+ return false unless vals.empty?
504
+ if @condition.call
505
+ @next ? @next.match(vals) : true
506
+ else
507
+ false
508
+ end
509
+ end
510
+
511
+ def validate
512
+ super
513
+ raise MalformedPatternError if ancestors.find {|i| i.next and ! i.next.kind_of?(PatternCondition) }
514
+ end
515
+
516
+ def inspect
517
+ "#<#{self.class.name}: condition=#{@condition.inspect}>"
518
+ end
519
+ end
520
+
521
+ class Env < BasicObject
522
+ def initialize(ctx, val)
523
+ @ctx = ctx
524
+ @val = val
525
+ end
526
+
527
+ private
528
+
529
+ def with(pat_or_val, guard_proc = nil, &block)
530
+ ctx = @ctx
531
+ pat = pat_or_val.kind_of?(Pattern) ? pat_or_val : PatternValue.new(pat_or_val)
532
+ pat.append(PatternCondition.new { check_for_duplicate_vars(pat.vars) })
533
+ if guard_proc
534
+ pat.append(PatternCondition.new { with_quasibinding(ctx, pat.quasibinding, &guard_proc) })
535
+ end
536
+ pat.validate
537
+ if pat.match([@val])
538
+ ret = with_quasibinding(ctx, pat.quasibinding, &block)
539
+ ::Kernel.throw(:exit_match, ret)
540
+ else
541
+ nil
542
+ end
543
+ rescue PatternNotMatch
544
+ end
545
+
546
+ def guard(&block)
547
+ block
548
+ end
549
+
550
+ def ___
551
+ PatternQuantifier.new(0, true)
552
+ end
553
+
554
+ def ___?
555
+ PatternQuantifier.new(0, false)
556
+ end
557
+
558
+ def method_missing(name, *args)
559
+ ::Kernel.raise ::ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
560
+ case name.to_s
561
+ when /\A__(\d+)(\??)\z/
562
+ PatternQuantifier.new($1.to_i, ! $2.empty?)
563
+ else
564
+ PatternVariable.new(name)
565
+ end
566
+ end
567
+
568
+ def _(*vals)
569
+ case vals.length
570
+ when 0
571
+ uscore = PatternVariable.new(:_)
572
+ class << uscore
573
+ def [](*args)
574
+ Array.call(*args)
575
+ end
576
+
577
+ def vars
578
+ []
579
+ end
580
+
581
+ private
582
+
583
+ def bind(val)
584
+ end
585
+ end
586
+ uscore
587
+ when 1
588
+ PatternValue.new(vals[0])
589
+ when 2
590
+ PatternValue.new(vals[0], vals[1])
591
+ else
592
+ ::Kernel.raise MalformedPatternError
593
+ end
594
+ end
595
+
596
+ alias __ _
597
+ alias _l _
598
+
599
+ def Seq(*subpatterns)
600
+ PatternSequence.new(*subpatterns)
601
+ end
602
+
603
+ def And(*subpatterns)
604
+ PatternAnd.new(*subpatterns)
605
+ end
606
+
607
+ def Or(*subpatterns)
608
+ PatternOr.new(*subpatterns)
609
+ end
610
+
611
+ def Not(*subpatterns)
612
+ PatternNot.new(*subpatterns)
613
+ end
614
+
615
+ def check_for_duplicate_vars(vars)
616
+ vars.each_with_object({}) do |v, h|
617
+ if h.has_key?(v.name)
618
+ unless h[v.name] == v.val
619
+ return false
620
+ end
621
+ else
622
+ h[v.name] = v.val
623
+ end
624
+ end
625
+ true
626
+ end
627
+
628
+ class QuasiBindingModule < ::Module
629
+ end
630
+
631
+ def with_quasibinding(obj, quasibinding, &block)
632
+ quasibinding_module(obj).module_eval do
633
+ begin
634
+ quasibinding.each do |name, val|
635
+ stack = @stacks[name]
636
+ if stack.empty?
637
+ define_method(name) { stack[-1] }
638
+ private name
639
+ end
640
+ stack.push(val)
641
+ end
642
+ obj.instance_eval(&block)
643
+ ensure
644
+ quasibinding.each do |name, _|
645
+ @stacks[name].pop
646
+ if @stacks[name].empty?
647
+ remove_method(name)
648
+ end
649
+ end
650
+ end
651
+ end
652
+ end
653
+
654
+ def quasibinding_module(obj)
655
+ m = obj.singleton_class.ancestors.find {|i| i.kind_of?(QuasiBindingModule) }
656
+ unless m
657
+ m = QuasiBindingModule.new do
658
+ @stacks = ::Hash.new {|h, k| h[k] = [] }
659
+ end
660
+ obj.singleton_class.class_eval do
661
+ if respond_to?(:prepend, true)
662
+ prepend m
663
+ else
664
+ include m
665
+ end
666
+ end
667
+ end
668
+ m
669
+ end
670
+ end
671
+
672
+ class PatternNotMatch < Exception; end
673
+ class PatternMatchError < StandardError; end
674
+ class NoMatchingPatternError < PatternMatchError; end
675
+ class MalformedPatternError < PatternMatchError; end
676
+
677
+ # Make Pattern and its subclasses/Env private.
678
+ if respond_to?(:private_constant)
679
+ constants.each do |c|
680
+ klass = const_get(c)
681
+ next unless klass.kind_of?(Class)
682
+ if klass <= Pattern
683
+ private_constant c
684
+ end
685
+ end
686
+ private_constant :Env
687
+ end
688
+ end
689
+
690
+ module Kernel
691
+ private
692
+
693
+ def match(*vals, &block)
694
+ do_match = Proc.new do |val|
695
+ env = PatternMatch.const_get(:Env).new(self, val)
696
+ catch(:exit_match) do
697
+ env.instance_eval(&block)
698
+ raise PatternMatch::NoMatchingPatternError
699
+ end
700
+ end
701
+ case vals.length
702
+ when 0
703
+ do_match
704
+ when 1
705
+ do_match.(vals[0])
706
+ else
707
+ raise ArgumentError, "wrong number of arguments (#{vals.length} for 0..1)"
708
+ end
709
+ end
710
+ end