pattern-match 0.0.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/.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: []