pattern-match 0.5.0 → 0.5.1

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.
@@ -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