pattern-match 0.1.0 → 0.1.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.
- 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
|