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