pattern-match 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ Gemfile.lock
3
+ pkg/*
4
+ vendor/*
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - ruby-head
data/BSDL ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (C) 2012 Kazuki Tsujimoto, All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
data/COPYING ADDED
@@ -0,0 +1,57 @@
1
+ Copyright (C) 2012 Kazuki Tsujimoto, All rights reserved.
2
+
3
+ You can redistribute it and/or modify it under either the terms of the
4
+ 2-clause BSDL (see the file BSDL), or the conditions below:
5
+
6
+ 1. You may make and give away verbatim copies of the source form of the
7
+ software without restriction, provided that you duplicate all of the
8
+ original copyright notices and associated disclaimers.
9
+
10
+ 2. You may modify your copy of the software in any way, provided that
11
+ you do at least ONE of the following:
12
+
13
+ a) place your modifications in the Public Domain or otherwise
14
+ make them Freely Available, such as by posting said
15
+ modifications to Usenet or an equivalent medium, or by allowing
16
+ the author to include your modifications in the software.
17
+
18
+ b) use the modified software only within your corporation or
19
+ organization.
20
+
21
+ c) give non-standard binaries non-standard names, with
22
+ instructions on where to get the original software distribution.
23
+
24
+ d) make other distribution arrangements with the author.
25
+
26
+ 3. You may distribute the software in object code or binary form,
27
+ provided that you do at least ONE of the following:
28
+
29
+ a) distribute the binaries and library files of the software,
30
+ together with instructions (in the manual page or equivalent)
31
+ on where to get the original distribution.
32
+
33
+ b) accompany the distribution with the machine-readable source of
34
+ the software.
35
+
36
+ c) give non-standard binaries non-standard names, with
37
+ instructions on where to get the original software distribution.
38
+
39
+ d) make other distribution arrangements with the author.
40
+
41
+ 4. You may modify and include the part of the software into any other
42
+ software (possibly commercial). But some files in the distribution
43
+ are not written by the author, so that they are not under these terms.
44
+
45
+ For the list of those files and their copying conditions, see the
46
+ file LEGAL.
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ = pattern-match
2
+ == About
3
+ A pattern matching library for Ruby.
4
+
5
+ == Installation
6
+ $ git clone git://github.com/k-tsj/pattern-match.git
7
+ $ cd pattern-match
8
+ $ gem build pattern-match.gemspec
9
+ $ gem install pattern-match-*.gem
10
+
11
+ or
12
+
13
+ $ gem install bundler (if you need)
14
+ $ echo "gem 'pattern-match', :git => 'git://github.com/k-tsj/pattern-match.git'" > Gemfile
15
+ $ bundle install --path vendor/bundle
16
+
17
+ == Example
18
+ See test/test_pattern-match.rb.
19
+
20
+ == Development
21
+ $ git clone git://github.com/k-tsj/pattern-match.git
22
+ $ cd pattern-match
23
+ $ gem install bundler (if you need)
24
+ $ bundle install --path vendor/bundle
25
+ $ rake test (or simply type "rake")
26
+ $ rake build
27
+
28
+ == Travis Build Status {<img src="https://secure.travis-ci.org/k-tsj/pattern-match.png"/>}[http://travis-ci.org/k-tsj/pattern-match]
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', __FILE__)
2
+ require 'bundler/setup'
3
+ require "bundler/gem_tasks"
4
+
5
+ require "rake/testtask"
6
+ task :default => :test
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/test_*.rb"]
10
+ end
@@ -0,0 +1,480 @@
1
+ # pattern-match.rb
2
+ #
3
+ # Copyright (C) 2012 Kazuki Tsujimoto, All rights reserved.
4
+
5
+ require 'pattern-match/version'
6
+
7
+ module PatternMatch
8
+ if Module.private_method_defined? :refine
9
+ SUPPORT_REFINEMENTS = true
10
+ else
11
+ SUPPORT_REFINEMENTS = false
12
+ Module.module_eval {
13
+ private
14
+
15
+ def refine(klass, &block)
16
+ klass.class_eval(&block)
17
+ end
18
+
19
+ def using(klass)
20
+ end
21
+ }
22
+ end
23
+
24
+ module Extractable
25
+ def call(*subpatterns)
26
+ if Object == self
27
+ raise MalformedPatternError unless subpatterns.length == 1
28
+ PatternObject.new(subpatterns[0])
29
+ else
30
+ PatternExtractor.new(self, *subpatterns)
31
+ end
32
+ end
33
+
34
+ if SUPPORT_REFINEMENTS
35
+ alias [] call
36
+ end
37
+ end
38
+
39
+ module NameSpace
40
+ refine Class do
41
+ include Extractable
42
+
43
+ private
44
+
45
+ def accept_self_instance_only(val)
46
+ raise PatternNotMatch unless val.is_a?(self)
47
+ end
48
+ end
49
+
50
+ refine Array.singleton_class do
51
+ def extract(val)
52
+ accept_self_instance_only(val)
53
+ val
54
+ end
55
+ end
56
+
57
+ refine Struct.singleton_class do
58
+ def extract(val)
59
+ accept_self_instance_only(val)
60
+ val.values
61
+ end
62
+ end
63
+
64
+ if SUPPORT_REFINEMENTS
65
+ def Struct.method_added(name)
66
+ if name == members[0]
67
+ this = self
68
+ PatternMatch::NameSpace.module_eval {
69
+ refine this.singleton_class do
70
+ include Extractable
71
+ end
72
+ }
73
+ end
74
+ end
75
+
76
+ refine Proc do
77
+ include Extractable
78
+
79
+ def extract(val)
80
+ [self === val]
81
+ end
82
+ end
83
+
84
+ refine Symbol do
85
+ include Extractable
86
+
87
+ def extract(val)
88
+ [self.to_proc === val]
89
+ end
90
+ end
91
+ end
92
+
93
+ refine Symbol do
94
+ def call(*args)
95
+ Proc.new {|obj| obj.send(self, *args) }
96
+ end
97
+ end
98
+
99
+ refine Regexp do
100
+ include Extractable
101
+
102
+ def extract(val)
103
+ m = Regexp.new("\\A#{source}\\z", options).match(val.to_s)
104
+ raise PatternNotMatch unless m
105
+ m.captures.empty? ? [m[0]] : m.captures
106
+ end
107
+ end
108
+ end
109
+
110
+ class Pattern
111
+ attr_accessor :parent, :next, :prev
112
+ attr_writer :pattern_match_env
113
+
114
+ def initialize(*subpatterns)
115
+ @parent = nil
116
+ @next = nil
117
+ @prev = nil
118
+ @pattern_match_env = nil
119
+ @subpatterns = subpatterns.map {|i| i.is_a?(Pattern) ? i : PatternValue.new(i) }
120
+ set_subpatterns_relation
121
+ end
122
+
123
+ def vars
124
+ @subpatterns.map(&:vars).inject([], :concat)
125
+ end
126
+
127
+ def binding
128
+ vars.each_with_object({}) {|v, h| h[v.name] = v.val }
129
+ end
130
+
131
+ def &(pattern)
132
+ PatternAnd.new(self, pattern)
133
+ end
134
+
135
+ def |(pattern)
136
+ PatternOr.new(self, pattern)
137
+ end
138
+
139
+ def !@
140
+ PatternNot.new(self)
141
+ end
142
+
143
+ def to_a
144
+ [self, PatternQuantifier.new(0)]
145
+ end
146
+
147
+ def quantified?
148
+ @next.is_a?(PatternQuantifier) || (root? ? false : @parent.quantified?)
149
+ end
150
+
151
+ def root?
152
+ @parent == nil
153
+ end
154
+
155
+ def validate
156
+ if root?
157
+ dup_vars = vars - vars.uniq {|i| i.name }
158
+ raise MalformedPatternError, "duplicate variables: #{dup_vars.map(&:name).join(', ')}" unless dup_vars.empty?
159
+ end
160
+ raise MalformedPatternError if @subpatterns.count {|i| i.is_a?(PatternQuantifier) } > 1
161
+ @subpatterns.each(&:validate)
162
+ end
163
+
164
+ def pattern_match_env
165
+ @pattern_match_env || @parent.pattern_match_env
166
+ end
167
+
168
+ private
169
+
170
+ def set_subpatterns_relation
171
+ @subpatterns.each do |i|
172
+ i.parent = self
173
+ end
174
+ @subpatterns.each_cons(2) do |a, b|
175
+ a.next = b
176
+ b.prev = a
177
+ end
178
+ end
179
+
180
+ def ancestors
181
+ ary = []
182
+ pat = self
183
+ until pat == nil
184
+ ary << pat
185
+ pat = pat.parent
186
+ end
187
+ ary
188
+ end
189
+ end
190
+
191
+ class PatternObject < Pattern
192
+ def initialize(spec)
193
+ raise MalformedPatternError unless spec.is_a?(Hash)
194
+ super(*spec.values)
195
+ @spec = spec.map {|k, pat| [k.to_proc, pat] }
196
+ rescue
197
+ raise MalformedPatternError
198
+ end
199
+
200
+ def match(val)
201
+ @spec.all? {|k, pat| pat.match(k.(val)) rescue raise PatternNotMatch }
202
+ end
203
+ end
204
+
205
+ class PatternExtractor < Pattern
206
+ def initialize(extractor, *subpatterns)
207
+ super(*subpatterns)
208
+ @extractor = extractor
209
+ end
210
+
211
+ def match(val)
212
+ extracted_vals = pattern_match_env.call_refined_method(@extractor, :extract, val)
213
+ k = extracted_vals.length - (@subpatterns.length - 2)
214
+ quantifier = @subpatterns.find {|i| i.is_a?(PatternQuantifier) }
215
+ if quantifier
216
+ return false unless quantifier.min_k <= k
217
+ else
218
+ return false unless @subpatterns.length == extracted_vals.length
219
+ end
220
+ @subpatterns.flat_map do |pat|
221
+ case
222
+ when pat.next.is_a?(PatternQuantifier)
223
+ []
224
+ when pat.is_a?(PatternQuantifier)
225
+ pat.prev.vars.each {|v| v.set_bind_to(pat) }
226
+ Array.new(k, pat.prev)
227
+ else
228
+ [pat]
229
+ end
230
+ end.zip(extracted_vals).all? do |pat, v|
231
+ pat.match(v)
232
+ end
233
+ end
234
+ end
235
+
236
+ class PatternQuantifier < Pattern
237
+ attr_reader :min_k
238
+
239
+ def initialize(min_k)
240
+ super()
241
+ @min_k = min_k
242
+ end
243
+
244
+ def match(val)
245
+ raise PatternMatchError, 'must not happen'
246
+ end
247
+
248
+ def validate
249
+ super
250
+ raise MalformedPatternError unless @prev
251
+ raise MalformedPatternError unless @parent.is_a?(PatternExtractor)
252
+ end
253
+ end
254
+
255
+ class PatternVariable < Pattern
256
+ attr_reader :name, :val
257
+
258
+ def initialize(name)
259
+ super()
260
+ @name = name
261
+ @val = nil
262
+ @bind_to = nil
263
+ end
264
+
265
+ def match(val)
266
+ bind(val)
267
+ true
268
+ end
269
+
270
+ def vars
271
+ [self]
272
+ end
273
+
274
+ def set_bind_to(quantifier)
275
+ if @val
276
+ outer = @val
277
+ (nest_level(quantifier) - 1).times do
278
+ outer = outer[-1]
279
+ end
280
+ @bind_to = []
281
+ outer << @bind_to
282
+ else
283
+ @val = @bind_to = []
284
+ end
285
+ end
286
+
287
+ private
288
+
289
+ def bind(val)
290
+ if quantified?
291
+ @bind_to << val
292
+ else
293
+ @val = val
294
+ end
295
+ end
296
+
297
+ def nest_level(quantifier)
298
+ qs = ancestors.map {|i| i.next.is_a?(PatternQuantifier) ? i.next : nil }.find_all {|i| i }.reverse
299
+ qs.index(quantifier) || (raise PatternMatchError)
300
+ end
301
+ end
302
+
303
+ class PatternValue < Pattern
304
+ def initialize(val)
305
+ super()
306
+ @val = val
307
+ end
308
+
309
+ def match(val)
310
+ @val === val
311
+ end
312
+ end
313
+
314
+ class PatternAnd < Pattern
315
+ def match(val)
316
+ @subpatterns.all? {|i| i.match(val) }
317
+ end
318
+ end
319
+
320
+ class PatternOr < Pattern
321
+ def match(val)
322
+ @subpatterns.find do |i|
323
+ begin
324
+ i.match(val)
325
+ rescue PatternNotMatch
326
+ false
327
+ end
328
+ end
329
+ end
330
+
331
+ def validate
332
+ super
333
+ raise MalformedPatternError unless vars.length == 0
334
+ end
335
+ end
336
+
337
+ class PatternNot < Pattern
338
+ def initialize(subpattern)
339
+ super
340
+ end
341
+
342
+ def match(val)
343
+ ! @subpatterns[0].match(val)
344
+ rescue PatternNotMatch
345
+ true
346
+ end
347
+
348
+ def validate
349
+ super
350
+ raise MalformedPatternError unless vars.length == 0
351
+ end
352
+ end
353
+
354
+ class Env < BasicObject
355
+ attr_reader :ret
356
+
357
+ def initialize(ctx, val)
358
+ @ctx = ctx
359
+ @val = val
360
+ @matched = false
361
+ @ret = nil
362
+ end
363
+
364
+ private
365
+
366
+ def with(pat_or_val, guard_proc = nil, &block)
367
+ pat = pat_or_val.is_a?(Pattern) ? pat_or_val : PatternValue.new(pat_or_val)
368
+ pat.validate
369
+ pat.pattern_match_env = self
370
+ if (! @matched) and pat.match(@val) and (guard_proc ? with_tmpbinding(@ctx, pat.binding, &guard_proc) : true)
371
+ @matched = true
372
+ @ret = with_tmpbinding(@ctx, pat.binding, &block)
373
+ end
374
+ rescue PatternNotMatch
375
+ end
376
+
377
+ def guard(&block)
378
+ block
379
+ end
380
+
381
+ def method_missing(name, *)
382
+ case name.to_s
383
+ when '___'
384
+ PatternQuantifier.new(0)
385
+ when /\A__(\d+)\z/
386
+ PatternQuantifier.new($1.to_i)
387
+ else
388
+ PatternVariable.new(name)
389
+ end
390
+ end
391
+
392
+ def _(*vals)
393
+ case vals.length
394
+ when 0
395
+ uscore = PatternVariable.new(:_)
396
+ uscore.pattern_match_env = self
397
+ class << uscore
398
+ def [](*args)
399
+ pattern_match_env.call_refined_method(::Array, :call, *args)
400
+ end
401
+
402
+ def match(val)
403
+ true
404
+ end
405
+
406
+ def vars
407
+ []
408
+ end
409
+ end
410
+ uscore
411
+ when 1
412
+ PatternValue.new(vals[0])
413
+ else
414
+ raise MalformedPatternError
415
+ end
416
+ end
417
+
418
+ alias __ _
419
+ alias _l _
420
+
421
+ def with_tmpbinding(obj, binding, &block)
422
+ tmpbinding_module(obj).instance_eval do
423
+ begin
424
+ binding.each do |name, val|
425
+ define_method(name) { val }
426
+ private name
427
+ end
428
+ obj.instance_eval(&block)
429
+ ensure
430
+ binding.each do |name, _|
431
+ remove_method(name) if private_method_defined? name
432
+ end
433
+ end
434
+ end
435
+ end
436
+
437
+ class TmpBindingModule < ::Module
438
+ end
439
+
440
+ def tmpbinding_module(obj)
441
+ m = obj.singleton_class.ancestors.find {|i| i.is_a? TmpBindingModule }
442
+ unless m
443
+ m = TmpBindingModule.new
444
+ obj.extend(m)
445
+ end
446
+ m
447
+ end
448
+ end
449
+
450
+ class PatternNotMatch < Exception; end
451
+ class PatternMatchError < StandardError; end
452
+ class MalformedPatternError < PatternMatchError; end
453
+ end
454
+
455
+ module Kernel
456
+ private
457
+
458
+ def match(*vals, &block)
459
+ do_match = Proc.new do |val|
460
+ env = PatternMatch::Env.new(self, val)
461
+ class << env
462
+ using ::PatternMatch::NameSpace
463
+
464
+ def call_refined_method(obj, name, *args)
465
+ obj.send(name, *args)
466
+ end
467
+ end
468
+ env.instance_eval(&block)
469
+ env.ret
470
+ end
471
+ case vals.length
472
+ when 0
473
+ do_match
474
+ when 1
475
+ do_match.(vals[0])
476
+ else
477
+ raise ArgumentError, "wrong number of arguments (#{vals.length} for 0..1)"
478
+ end
479
+ end
480
+ end
@@ -0,0 +1,3 @@
1
+ module PatternMatch
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'pattern-match/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'pattern-match'
6
+ s.version = PatternMatch::VERSION
7
+ s.authors = ['Kazuki Tsujimoto']
8
+ s.email = ['kazuki@callcc.net']
9
+ s.homepage = 'https://github.com/k-tsj/pattern-match'
10
+ s.summary = %q{A pattern matching library}
11
+ s.description = %w{
12
+ A pattern matching library.
13
+ }.join(' ')
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f) }
18
+ s.require_paths = ['lib']
19
+ s.add_development_dependency 'rake'
20
+ s.extra_rdoc_files = ['README.rdoc']
21
+ s.rdoc_options = ['--main', 'README.rdoc']
22
+ end
@@ -0,0 +1,376 @@
1
+ require 'test/unit'
2
+ require_relative '../lib/pattern-match'
3
+
4
+ class TestPatternMatch < Test::Unit::TestCase
5
+ def test_basic
6
+ this = self
7
+ ret = match([0, 1, 2, 3]) {
8
+ with(nil) { flunk }
9
+ with(_[a, Fixnum , 2, b]) {
10
+ assert_equal(this, self)
11
+ assert_equal(0, a)
12
+ assert_equal(3, b)
13
+ 4
14
+ }
15
+ with(_) { flunk }
16
+ }
17
+ assert_equal(4, ret)
18
+ assert_raise(NameError) { a }
19
+ assert_raise(NameError) { b }
20
+
21
+ ret = match(0) {
22
+ with(1) { flunk }
23
+ with(2) { flunk }
24
+ }
25
+ assert_nil(ret)
26
+
27
+ match(0) {
28
+ with(i, guard { i.odd? }) { flunk }
29
+ with(i, guard { i.even? }) { pass }
30
+ with(_) { flunk }
31
+ }
32
+
33
+ assert_raise(PatternMatch::MalformedPatternError) {
34
+ match(0) {
35
+ with(_[a, a]) {}
36
+ }
37
+ }
38
+ end
39
+
40
+ def test_variable_shadowing
41
+ skip 'not implemented yet'
42
+ match(0) {
43
+ with(a) {
44
+ match(1) {
45
+ with(a) {
46
+ assert_equal(1, a)
47
+ }
48
+ }
49
+ assert_equal(0, a)
50
+ }
51
+ }
52
+ end
53
+
54
+ def test_uscore
55
+ match([0, 1]) {
56
+ with(_[_, ! _(Float)]) {
57
+ assert_raise(NameError) { _ }
58
+ }
59
+ with(_) { flunk }
60
+ }
61
+ end
62
+
63
+ def test_splat
64
+ match([0, 1, 2]) {
65
+ with(_[a, *b]) {
66
+ assert_equal(0, a)
67
+ assert_equal([1, 2], b)
68
+ }
69
+ with(_) { flunk }
70
+ }
71
+
72
+ match([0, 1]) {
73
+ with(_[a, *b, c]) {
74
+ assert_equal(0, a)
75
+ assert_equal([], b)
76
+ assert_equal(1, c)
77
+ }
78
+ with(_) { flunk }
79
+ }
80
+
81
+ match([0, 1, 2]) {
82
+ with(_[a, *b, c]) {
83
+ assert_equal(0, a)
84
+ assert_equal([1], b)
85
+ assert_equal(2, c)
86
+ }
87
+ with(_) { flunk }
88
+ }
89
+
90
+ match([[0], [1], [2]]) {
91
+ with(_[*_[a]]) {
92
+ assert_equal([0, 1, 2], a)
93
+ }
94
+ with(_) { flunk }
95
+ }
96
+
97
+ assert_raise(PatternMatch::MalformedPatternError) {
98
+ match(0) {
99
+ with(_[*a, *b]) {}
100
+ }
101
+ }
102
+ end
103
+
104
+ def test_quantifier
105
+ match([0]) {
106
+ with(_[a, _[b, c], ___]) {
107
+ assert_equal(0, a)
108
+ assert_equal([], b)
109
+ assert_equal([], c)
110
+ }
111
+ with(_) { flunk }
112
+ }
113
+
114
+ match([0, [1, 2], [3, 4]]) {
115
+ with(_[a, _[b, c], ___]) {
116
+ assert_equal(0, a)
117
+ assert_equal([1, 3], b)
118
+ assert_equal([2, 4], c)
119
+ }
120
+ with(_) { flunk }
121
+ }
122
+
123
+ match([0, [1, 2], [3, 4]]) {
124
+ with(_[a, _[b, c], ___, d]) {
125
+ assert_equal(0, a)
126
+ assert_equal([1], b)
127
+ assert_equal([2], c)
128
+ assert_equal([3, 4], d)
129
+ }
130
+ with(_) { flunk }
131
+ }
132
+
133
+ match([0, [1, 2], [3, 4]]) {
134
+ with(_[a, _[b, c], __3]) { flunk }
135
+ with(_[a, _[b, c], __2]) {
136
+ assert_equal(0, a)
137
+ assert_equal([1, 3], b)
138
+ assert_equal([2, 4], c)
139
+ }
140
+ with(_) { flunk }
141
+ }
142
+
143
+ match([0, [1, 2], [3, 4]]) {
144
+ with(_[a, _[b, ___], ___]) {
145
+ assert_equal(0, a)
146
+ assert_equal([[1, 2], [3, 4]], b)
147
+ }
148
+ with(_) { flunk }
149
+ }
150
+
151
+ match([[0, [1, 2], [3, 4]], [5, [6, 7], [8, 9]], [10, [11, 12], [13, 14]]]) {
152
+ with(_[_[a, _[b, ___], ___], ___]) {
153
+ assert_equal([0, 5, 10], a)
154
+ assert_equal([[[1, 2], [3, 4]], [[6, 7], [8, 9]], [[11, 12], [13, 14]]], b)
155
+ }
156
+ with(_) { flunk }
157
+ }
158
+
159
+ assert_raise(PatternMatch::MalformedPatternError) {
160
+ match(0) {
161
+ with(_[___]) {}
162
+ }
163
+ }
164
+
165
+ assert_raise(PatternMatch::MalformedPatternError) {
166
+ match(0) {
167
+ with(_[_[___]]) {}
168
+ }
169
+ }
170
+
171
+ assert_raise(PatternMatch::MalformedPatternError) {
172
+ match(0) {
173
+ with(_[a, ___, ___]) {}
174
+ }
175
+ }
176
+
177
+ assert_raise(PatternMatch::MalformedPatternError) {
178
+ match(0) {
179
+ with(_[a, ___, *b]) {}
180
+ }
181
+ }
182
+ end
183
+
184
+ def test_and_or_not
185
+ match(1) {
186
+ with(_(0) & _(1)) { flunk }
187
+ with(_) { pass }
188
+ }
189
+
190
+ match(1) {
191
+ with(_(0) | _(1)) { pass }
192
+ with(_) { flunk }
193
+ }
194
+
195
+ match(1) {
196
+ with(_[] | _(1)) { pass }
197
+ with(_) { flunk }
198
+ }
199
+
200
+ match(1) {
201
+ with(! _(0)) { pass }
202
+ with(_) { flunk }
203
+ }
204
+
205
+ match(1) {
206
+ with(! _[]) { pass }
207
+ with(_) { flunk }
208
+ }
209
+
210
+ match(1) {
211
+ with(a & b) {
212
+ assert_equal(1, a)
213
+ assert_equal(1, b)
214
+ }
215
+ with(_) { flunk }
216
+ }
217
+
218
+ match(1) {
219
+ with(_(0) | _(1)) { pass }
220
+ with(_) { flunk }
221
+ }
222
+
223
+ assert_raise(PatternMatch::MalformedPatternError) {
224
+ match(1) {
225
+ with(a | b) {}
226
+ }
227
+ }
228
+
229
+ match(1) {
230
+ with(! _(0)) { pass }
231
+ with(_) { flunk }
232
+ }
233
+
234
+ assert_raise(PatternMatch::MalformedPatternError) {
235
+ match(1) {
236
+ with(! a) {}
237
+ }
238
+ }
239
+
240
+ assert_raise(PatternMatch::MalformedPatternError) {
241
+ match(1) {
242
+ with(a | ___) {}
243
+ }
244
+ }
245
+
246
+ assert_raise(PatternMatch::MalformedPatternError) {
247
+ match(1) {
248
+ with(a & ___) {}
249
+ }
250
+ }
251
+ end
252
+
253
+ def test_match_without_argument
254
+ assert_equal(1, 2.times.find(&match { with(1) { true } }))
255
+ end
256
+
257
+ def test_extractor_class_struct
258
+ s = Struct.new(:a, :b, :c)
259
+ match(s[0, 1, 2]) {
260
+ with(s.(a, b, c)) {
261
+ assert_equal(0, a)
262
+ assert_equal(1, b)
263
+ assert_equal(2, c)
264
+ }
265
+ with(_) { flunk }
266
+ }
267
+ end
268
+
269
+ def test_extractor_struct_with_refinements
270
+ skip 'refinements not supported' unless PatternMatch::SUPPORT_REFINEMENTS
271
+ s = Struct.new(:a, :b, :c)
272
+ match(s[0, 1, 2]) {
273
+ with(s[a, b, c]) {
274
+ assert_equal(0, a)
275
+ assert_equal(1, b)
276
+ assert_equal(2, c)
277
+ }
278
+ with(_) { flunk }
279
+ }
280
+ end
281
+
282
+ def test_extractor_obj_regexp
283
+ match('abc') {
284
+ with(/./.(a)) { flunk }
285
+ with(a & /.../.(b)) {
286
+ assert_equal('abc', a)
287
+ assert_equal('abc', b)
288
+ }
289
+ with(_) { flunk }
290
+ }
291
+
292
+ match('abc') {
293
+ with(a & /(.)(.)(.)/.(b, c ,d)) {
294
+ assert_equal('abc', a)
295
+ assert_equal('a', b)
296
+ assert_equal('b', c)
297
+ assert_equal('c', d)
298
+ }
299
+ with(_) { flunk }
300
+ }
301
+ end
302
+
303
+ def test_extractor_obj_regexp_with_refinements
304
+ skip 'refinements not supported' unless PatternMatch::SUPPORT_REFINEMENTS
305
+ match('abc') {
306
+ with(/./[a]) { flunk }
307
+ with(a & /.../[b]) {
308
+ assert_equal('abc', a)
309
+ assert_equal('abc', b)
310
+ }
311
+ with(_) { flunk }
312
+ }
313
+
314
+ match('abc') {
315
+ with(a & /(.)(.)(.)/[b, c ,d]) {
316
+ assert_equal('abc', a)
317
+ assert_equal('a', b)
318
+ assert_equal('b', c)
319
+ assert_equal('c', d)
320
+ }
321
+ with(_) { flunk }
322
+ }
323
+ end
324
+
325
+ def test_extractor_obj_proc_with_refinements
326
+ skip 'refinements not supported' unless PatternMatch::SUPPORT_REFINEMENTS
327
+ match(0) {
328
+ with((Proc.new {|i| i + 1 })[a]) {
329
+ assert_equal(1, a)
330
+ }
331
+ with(_) { flunk }
332
+ }
333
+ end
334
+
335
+ def test_extractor_obj_symbol_with_refinements
336
+ skip 'refinements not supported' unless PatternMatch::SUPPORT_REFINEMENTS
337
+ match(0) {
338
+ with(:to_s[a]) {
339
+ assert_equal('0', a)
340
+ }
341
+ with(_) { flunk }
342
+ }
343
+ end
344
+
345
+ def test_object
346
+ match(10) {
347
+ with(Object.(:to_i => a, :to_s.(16) => b, :no_method => c)) { flunk }
348
+ with(Object.(:to_i => a, :to_s.(16) => b)) {
349
+ assert_equal(10, a)
350
+ assert_equal('a', b)
351
+ }
352
+ with(_) { flunk }
353
+ }
354
+
355
+ assert_raise(PatternMatch::MalformedPatternError) {
356
+ match(10) {
357
+ with(Object.(a, b)) {}
358
+ }
359
+ }
360
+ end
361
+
362
+ def test_refine_after_requiring_library
363
+ c = Class.new
364
+ ::PatternMatch::NameSpace.module_eval {
365
+ refine c.singleton_class do
366
+ def extract(*)
367
+ [:c]
368
+ end
369
+ end
370
+ }
371
+ match(:c) {
372
+ with(c.(a)) { assert_equal(:c, a) }
373
+ with(_) { flunk }
374
+ }
375
+ end
376
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pattern-match
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kazuki Tsujimoto
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &8351800 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *8351800
25
+ description: A pattern matching library.
26
+ email:
27
+ - kazuki@callcc.net
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files:
31
+ - README.rdoc
32
+ files:
33
+ - .gitignore
34
+ - .travis.yml
35
+ - BSDL
36
+ - COPYING
37
+ - Gemfile
38
+ - README.rdoc
39
+ - Rakefile
40
+ - lib/pattern-match.rb
41
+ - lib/pattern-match/version.rb
42
+ - pattern-match.gemspec
43
+ - test/test_pattern-match.rb
44
+ homepage: https://github.com/k-tsj/pattern-match
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.rdoc
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ segments:
59
+ - 0
60
+ hash: 2652740272817572795
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ segments:
68
+ - 0
69
+ hash: 2652740272817572795
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 1.8.11
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: A pattern matching library
76
+ test_files: []