pattern-match 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/pattern-match.rb +59 -25
- data/lib/pattern-match/version.rb +1 -1
- data/test/test_pattern-match.rb +37 -5
- metadata +6 -6
data/lib/pattern-match.rb
CHANGED
@@ -26,8 +26,11 @@ module PatternMatch
|
|
26
26
|
if Object == self
|
27
27
|
raise MalformedPatternError unless subpatterns.length == 1
|
28
28
|
PatternObject.new(subpatterns[0])
|
29
|
+
elsif Hash == self
|
30
|
+
raise MalformedPatternError unless subpatterns.length == 1
|
31
|
+
PatternHash.new(subpatterns[0])
|
29
32
|
else
|
30
|
-
|
33
|
+
PatternDeconstructor.new(self, *subpatterns)
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
@@ -47,7 +50,7 @@ module PatternMatch
|
|
47
50
|
private
|
48
51
|
|
49
52
|
def accept_self_instance_only(val)
|
50
|
-
raise PatternNotMatch unless val.
|
53
|
+
raise PatternNotMatch unless val.kind_of?(self)
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
@@ -141,7 +144,7 @@ module PatternMatch
|
|
141
144
|
@next = nil
|
142
145
|
@prev = nil
|
143
146
|
@pattern_match_env = nil
|
144
|
-
@subpatterns = subpatterns.map {|i| i.
|
147
|
+
@subpatterns = subpatterns.map {|i| i.kind_of?(Pattern) ? i : PatternValue.new(i) }
|
145
148
|
set_subpatterns_relation
|
146
149
|
end
|
147
150
|
|
@@ -170,7 +173,7 @@ module PatternMatch
|
|
170
173
|
end
|
171
174
|
|
172
175
|
def quantified?
|
173
|
-
@next.
|
176
|
+
@next.kind_of?(PatternQuantifier) || (root? ? false : @parent.quantified?)
|
174
177
|
end
|
175
178
|
|
176
179
|
def root?
|
@@ -182,7 +185,7 @@ module PatternMatch
|
|
182
185
|
dup_vars = vars - vars.uniq {|i| i.name }
|
183
186
|
raise MalformedPatternError, "duplicate variables: #{dup_vars.map(&:name).join(', ')}" unless dup_vars.empty?
|
184
187
|
end
|
185
|
-
raise MalformedPatternError if @subpatterns.count {|i| i.
|
188
|
+
raise MalformedPatternError if @subpatterns.count {|i| i.kind_of?(PatternQuantifier) } > 1
|
186
189
|
@subpatterns.each(&:validate)
|
187
190
|
end
|
188
191
|
|
@@ -215,7 +218,6 @@ module PatternMatch
|
|
215
218
|
|
216
219
|
class PatternObject < Pattern
|
217
220
|
def initialize(spec)
|
218
|
-
raise MalformedPatternError unless spec.is_a?(Hash)
|
219
221
|
super(*spec.values)
|
220
222
|
@spec = spec.map {|k, pat| [k.to_proc, pat] }
|
221
223
|
rescue
|
@@ -227,7 +229,20 @@ module PatternMatch
|
|
227
229
|
end
|
228
230
|
end
|
229
231
|
|
230
|
-
class
|
232
|
+
class PatternHash < Pattern
|
233
|
+
def initialize(spec)
|
234
|
+
super(*spec.values)
|
235
|
+
@spec = spec
|
236
|
+
end
|
237
|
+
|
238
|
+
def match(val)
|
239
|
+
raise PatternNotMatch unless val.kind_of?(Hash)
|
240
|
+
raise PatternNotMatch unless @spec.keys.all? {|k| val.has_key?(k) }
|
241
|
+
@spec.all? {|k, pat| pat.match(val[k]) rescue raise PatternNotMatch }
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
class PatternDeconstructor < Pattern
|
231
246
|
def initialize(extractor, *subpatterns)
|
232
247
|
super(*subpatterns)
|
233
248
|
@extractor = extractor
|
@@ -236,7 +251,7 @@ module PatternMatch
|
|
236
251
|
def match(val)
|
237
252
|
extracted_vals = pattern_match_env.call_refined_method(@extractor, :deconstruct, val)
|
238
253
|
k = extracted_vals.length - (@subpatterns.length - 2)
|
239
|
-
quantifier = @subpatterns.find {|i| i.
|
254
|
+
quantifier = @subpatterns.find {|i| i.kind_of?(PatternQuantifier) }
|
240
255
|
if quantifier
|
241
256
|
return false unless quantifier.min_k <= k
|
242
257
|
else
|
@@ -244,9 +259,9 @@ module PatternMatch
|
|
244
259
|
end
|
245
260
|
@subpatterns.flat_map do |pat|
|
246
261
|
case
|
247
|
-
when pat.next.
|
262
|
+
when pat.next.kind_of?(PatternQuantifier)
|
248
263
|
[]
|
249
|
-
when pat.
|
264
|
+
when pat.kind_of?(PatternQuantifier)
|
250
265
|
pat.prev.vars.each {|v| v.set_bind_to(pat) }
|
251
266
|
Array.new(k, pat.prev)
|
252
267
|
else
|
@@ -273,7 +288,7 @@ module PatternMatch
|
|
273
288
|
def validate
|
274
289
|
super
|
275
290
|
raise MalformedPatternError unless @prev
|
276
|
-
raise MalformedPatternError unless @parent.
|
291
|
+
raise MalformedPatternError unless @parent.kind_of?(PatternDeconstructor)
|
277
292
|
end
|
278
293
|
end
|
279
294
|
|
@@ -320,7 +335,7 @@ module PatternMatch
|
|
320
335
|
end
|
321
336
|
|
322
337
|
def nest_level(quantifier)
|
323
|
-
qs = ancestors.map {|i| i.next.
|
338
|
+
qs = ancestors.map {|i| i.next.kind_of?(PatternQuantifier) ? i.next : nil }.find_all {|i| i }.reverse
|
324
339
|
qs.index(quantifier) || (raise PatternMatchError)
|
325
340
|
end
|
326
341
|
end
|
@@ -374,24 +389,22 @@ module PatternMatch
|
|
374
389
|
end
|
375
390
|
|
376
391
|
class Env < BasicObject
|
377
|
-
attr_reader :ret
|
378
|
-
|
379
392
|
def initialize(ctx, val)
|
380
393
|
@ctx = ctx
|
381
394
|
@val = val
|
382
|
-
@matched = false
|
383
|
-
@ret = nil
|
384
395
|
end
|
385
396
|
|
386
397
|
private
|
387
398
|
|
388
399
|
def with(pat_or_val, guard_proc = nil, &block)
|
389
|
-
pat = pat_or_val.
|
400
|
+
pat = pat_or_val.kind_of?(Pattern) ? pat_or_val : PatternValue.new(pat_or_val)
|
390
401
|
pat.validate
|
391
402
|
pat.pattern_match_env = self
|
392
|
-
if
|
393
|
-
|
394
|
-
|
403
|
+
if pat.match(@val) and (guard_proc ? with_tmpbinding(@ctx, pat.binding, &guard_proc) : true)
|
404
|
+
ret = with_tmpbinding(@ctx, pat.binding, &block)
|
405
|
+
::Kernel.throw(:exit_match, ret)
|
406
|
+
else
|
407
|
+
nil
|
395
408
|
end
|
396
409
|
rescue PatternNotMatch
|
397
410
|
end
|
@@ -468,13 +481,19 @@ module PatternMatch
|
|
468
481
|
end
|
469
482
|
|
470
483
|
def tmpbinding_module(obj)
|
471
|
-
m = obj.singleton_class.ancestors.find {|i| i.
|
484
|
+
m = obj.singleton_class.ancestors.find {|i| i.kind_of?(TmpBindingModule) }
|
472
485
|
unless m
|
473
486
|
m = TmpBindingModule.new
|
474
487
|
m.instance_eval do
|
475
488
|
@stacks = ::Hash.new {|h, k| h[k] = [] }
|
476
489
|
end
|
477
|
-
obj.
|
490
|
+
obj.singleton_class.class_eval do
|
491
|
+
if respond_to?(:prepend, true)
|
492
|
+
prepend m
|
493
|
+
else
|
494
|
+
include m
|
495
|
+
end
|
496
|
+
end
|
478
497
|
end
|
479
498
|
m
|
480
499
|
end
|
@@ -482,7 +501,20 @@ module PatternMatch
|
|
482
501
|
|
483
502
|
class PatternNotMatch < Exception; end
|
484
503
|
class PatternMatchError < StandardError; end
|
504
|
+
class NoMatchingPatternError < PatternMatchError; end
|
485
505
|
class MalformedPatternError < PatternMatchError; end
|
506
|
+
|
507
|
+
# Make Pattern and its subclasses/Env private.
|
508
|
+
if respond_to?(:private_constant)
|
509
|
+
constants.each do |c|
|
510
|
+
klass = const_get(c)
|
511
|
+
next unless klass.kind_of?(Class)
|
512
|
+
if klass.ancestors.find {|i| i == Pattern }
|
513
|
+
private_constant c
|
514
|
+
end
|
515
|
+
end
|
516
|
+
private_constant :Env
|
517
|
+
end
|
486
518
|
end
|
487
519
|
|
488
520
|
module Kernel
|
@@ -490,7 +522,7 @@ module Kernel
|
|
490
522
|
|
491
523
|
def match(*vals, &block)
|
492
524
|
do_match = Proc.new do |val|
|
493
|
-
env = PatternMatch
|
525
|
+
env = PatternMatch.const_get(:Env).new(self, val)
|
494
526
|
class << env
|
495
527
|
using ::PatternMatch::NameSpace
|
496
528
|
|
@@ -498,8 +530,10 @@ module Kernel
|
|
498
530
|
obj.__send__(name, *args)
|
499
531
|
end
|
500
532
|
end
|
501
|
-
|
502
|
-
|
533
|
+
catch(:exit_match) do
|
534
|
+
env.instance_eval(&block)
|
535
|
+
raise ::PatternMatch::NoMatchingPatternError
|
536
|
+
end
|
503
537
|
end
|
504
538
|
case vals.length
|
505
539
|
when 0
|
data/test/test_pattern-match.rb
CHANGED
@@ -6,6 +6,7 @@ class TestPatternMatch < Test::Unit::TestCase
|
|
6
6
|
this = self
|
7
7
|
ret = match([0, 1, 2, 3]) {
|
8
8
|
with(nil) { flunk }
|
9
|
+
with(_[a, 0, 0, b]) { flunk }
|
9
10
|
with(_[a, Fixnum , 2, b]) {
|
10
11
|
assert_equal(this, self)
|
11
12
|
assert_equal(0, a)
|
@@ -18,11 +19,12 @@ class TestPatternMatch < Test::Unit::TestCase
|
|
18
19
|
assert_raise(NameError) { a }
|
19
20
|
assert_raise(NameError) { b }
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
assert_raise(PatternMatch::NoMatchingPatternError) {
|
23
|
+
match(0) {
|
24
|
+
with(1) { flunk }
|
25
|
+
with(2) { flunk }
|
26
|
+
}
|
24
27
|
}
|
25
|
-
assert_nil(ret)
|
26
28
|
|
27
29
|
match(0) {
|
28
30
|
with(i, guard { i.odd? }) { flunk }
|
@@ -95,6 +97,18 @@ class TestPatternMatch < Test::Unit::TestCase
|
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
100
|
+
def test_override_singleton_method
|
101
|
+
skip 'Module#prepend not supported' unless Module.private_method_defined?(:prepend)
|
102
|
+
match(0) {
|
103
|
+
with(_test_override_singleton_method) {
|
104
|
+
def self._test_override_singleton_method
|
105
|
+
1
|
106
|
+
end
|
107
|
+
assert_equal(0, _test_override_singleton_method)
|
108
|
+
}
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
98
112
|
def test_uscore
|
99
113
|
match([0, 1, Fixnum]) {
|
100
114
|
with(_[_, ! _(Float), _(Fixnum, :==)]) {
|
@@ -295,7 +309,7 @@ class TestPatternMatch < Test::Unit::TestCase
|
|
295
309
|
end
|
296
310
|
|
297
311
|
def test_match_without_argument
|
298
|
-
assert_equal(1, 2.times.find(&match { with(1) { true } }))
|
312
|
+
assert_equal(1, 2.times.find(&match { with(1) { true }; with(_) { false } }))
|
299
313
|
end
|
300
314
|
|
301
315
|
def test_extractor_class
|
@@ -453,6 +467,24 @@ class TestPatternMatch < Test::Unit::TestCase
|
|
453
467
|
}
|
454
468
|
end
|
455
469
|
|
470
|
+
def test_hash
|
471
|
+
match({a: 0, b: 1}) {
|
472
|
+
with(Hash.(a: a, b: b, c: c)) { flunk }
|
473
|
+
with(Hash.(a: a, b: b)) {
|
474
|
+
assert_equal(0, a)
|
475
|
+
assert_equal(1, b)
|
476
|
+
}
|
477
|
+
with(_) { flunk }
|
478
|
+
}
|
479
|
+
|
480
|
+
match({a: 0, b: 1}) {
|
481
|
+
with(Hash.(a: a)) {
|
482
|
+
assert_equal(0, a)
|
483
|
+
}
|
484
|
+
with(_) { flunk }
|
485
|
+
}
|
486
|
+
end
|
487
|
+
|
456
488
|
def test_refine_after_requiring_library
|
457
489
|
c = Class.new
|
458
490
|
::PatternMatch::NameSpace.module_eval {
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pattern-match
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &8646440 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *8646440
|
25
25
|
description: A pattern matching library.
|
26
26
|
email:
|
27
27
|
- kazuki@callcc.net
|
@@ -57,7 +57,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
57
|
version: '0'
|
58
58
|
segments:
|
59
59
|
- 0
|
60
|
-
hash:
|
60
|
+
hash: 1580258758057961953
|
61
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
@@ -66,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
66
|
version: '0'
|
67
67
|
segments:
|
68
68
|
- 0
|
69
|
-
hash:
|
69
|
+
hash: 1580258758057961953
|
70
70
|
requirements: []
|
71
71
|
rubyforge_project:
|
72
72
|
rubygems_version: 1.8.11
|