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 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
- PatternExtractor.new(self, *subpatterns)
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.is_a?(self)
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.is_a?(Pattern) ? i : PatternValue.new(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.is_a?(PatternQuantifier) || (root? ? false : @parent.quantified?)
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.is_a?(PatternQuantifier) } > 1
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 PatternExtractor < Pattern
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.is_a?(PatternQuantifier) }
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.is_a?(PatternQuantifier)
262
+ when pat.next.kind_of?(PatternQuantifier)
248
263
  []
249
- when pat.is_a?(PatternQuantifier)
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.is_a?(PatternExtractor)
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.is_a?(PatternQuantifier) ? i.next : nil }.find_all {|i| i }.reverse
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.is_a?(Pattern) ? pat_or_val : PatternValue.new(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 (! @matched) and pat.match(@val) and (guard_proc ? with_tmpbinding(@ctx, pat.binding, &guard_proc) : true)
393
- @matched = true
394
- @ret = with_tmpbinding(@ctx, pat.binding, &block)
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.is_a? TmpBindingModule }
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.extend(m)
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::Env.new(self, val)
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
- env.instance_eval(&block)
502
- env.ret
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
@@ -1,3 +1,3 @@
1
1
  module PatternMatch
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -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
- ret = match(0) {
22
- with(1) { flunk }
23
- with(2) { flunk }
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.0
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-03-17 00:00:00.000000000 Z
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: &8625920 !ruby/object:Gem::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: *8625920
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: -900071050850383955
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: -900071050850383955
69
+ hash: 1580258758057961953
70
70
  requirements: []
71
71
  rubyforge_project:
72
72
  rubygems_version: 1.8.11