matchmaker 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.
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{matchmaker}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Howard Yeh"]
12
+ s.date = %q{2009-11-30}
13
+ s.description = %q{A pattern matching library}
14
+ s.email = %q{hayeah@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README",
18
+ "README.markdown"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.markdown",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "lib/matchmaker.rb",
28
+ "matchmaker.gemspec",
29
+ "spec/case_spec.rb",
30
+ "spec/spec_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/hayeah/case}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.5}
36
+ s.summary = %q{Ruby Pattern Matching}
37
+ s.test_files = [
38
+ "spec/spec_helper.rb",
39
+ "spec/case_spec.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<rspec>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<rspec>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<rspec>, [">= 0"])
53
+ end
54
+ end
55
+
data/spec/case_spec.rb ADDED
@@ -0,0 +1,466 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Case do
4
+ def not_match
5
+ raise_error(Case::CaseFail)
6
+ end
7
+
8
+ def be_unbound
9
+ raise_error(Case::UnboundVariable)
10
+ end
11
+
12
+ TEST_CONSTANT_1 = :test_constant_1
13
+ it "DSL should preserve module nesting of original block" do
14
+ pending
15
+ Case(1) {
16
+ of(1) { TEST_CONSTANT_1 }
17
+ }.should == TEST_CONSTANT_1
18
+ end
19
+
20
+ it "should make a pattern object" do
21
+ pat = Case.pattern {
22
+ [:a,integer,string]
23
+ }
24
+ pat.should be_a(Case::Pattern)
25
+ Case([:a,10,"abc"]) {
26
+ of(pat)
27
+ }.should == true
28
+ end
29
+
30
+ it "should pattern match with shortcut" do
31
+ Case("a") {
32
+ of "a"
33
+ }.should == true
34
+ lambda {
35
+ Case("a") {
36
+ of 1
37
+ }
38
+ }.should not_match
39
+ end
40
+
41
+ it "should not allow star pattern except in structural patterns" do
42
+ lambda {
43
+ Case.new {
44
+ }
45
+ }.should raise_error
46
+ end
47
+
48
+ it "should not be used with no clauses" do
49
+ lambda { Case.new { } }.should raise_error(Case::NoClauses)
50
+ end
51
+
52
+ it "should build case object with clauses" do
53
+ Case.new do
54
+ of 1
55
+ of 2
56
+ end.should be_a(Case)
57
+ end
58
+
59
+ it "should build star pattern" do
60
+ star_pattern, literal_pattern = nil
61
+ Case.new {
62
+ literal_pattern = literal(3)
63
+ star_pattern = _!(literal_pattern)
64
+ } rescue Case::NoClauses
65
+ star_pattern.should be_a(Case::StarPattern)
66
+ star_pattern.pattern.should == literal_pattern
67
+ end
68
+
69
+ it "should return true for matched pattern (without action)" do
70
+ c = Case.new do
71
+ of 1
72
+ of :a
73
+ end
74
+ c.match(1).should == true
75
+ lambda { c.match(2) }.should not_match
76
+ c.match(:a).should == true
77
+ lambda { c.match(:b) }.should not_match
78
+ end
79
+
80
+ it "should return value of pattern matched action" do
81
+ c = Case.new do
82
+ of(1) { [1] }
83
+ of(:a) { [:a] }
84
+ of(:b) { throw(:throw_test) }
85
+ end
86
+ c.match(1).should == [1]
87
+ c.match(:a).should == [:a]
88
+ lambda { c.match(:b) }.should throw_symbol(:throw_test)
89
+ end
90
+
91
+ it "should throw error for unbound variable" do
92
+ c = Case.new do
93
+ of(literal(1,:a)) { b }
94
+ end
95
+ lambda { c.match(1) }.should be_unbound
96
+ end
97
+
98
+ it "should create binding for matched variable" do
99
+ c = Case.new do
100
+ of(literal(1,:a)) { a }
101
+ end
102
+ c.match(1).should == 1
103
+ end
104
+
105
+ it "should scope bindings"
106
+
107
+ it "should downcase symbol for variable binding" do
108
+ c = Case.new do
109
+ of(literal(1,:A)) { a }
110
+ end
111
+ c.match(1).should == 1
112
+ end
113
+
114
+ it "should bind to the same variable iff the values are equal" do
115
+ c = Case.new do
116
+ of([is(1,:a),is(1,:a)])
117
+ of([is(2,:A),is(1,:A)])
118
+ of([is(3,:B),is(3,:b)])
119
+ end
120
+ c.match([1,1]).should == true
121
+ lambda { c.match([2,1]) }.should not_match
122
+ c.match([3,3]).should == true
123
+ end
124
+
125
+ describe "guard" do
126
+ it "should add guard with the Pattern#when" do
127
+ c = Case.new do
128
+ of(literal(1).when{|o| false})
129
+ of(literal(2))
130
+ end
131
+ lambda { c.match(1) }.should not_match
132
+ c.match(2).should == true
133
+ end
134
+
135
+ it "should fail pattern if guard fails" do
136
+ c = Case.new do
137
+ of(literal(1){|o| false})
138
+ of(literal(2))
139
+ end
140
+ lambda { c.match(1) }.should not_match
141
+ c.match(2).should == true
142
+ end
143
+ end
144
+
145
+ describe "literal pattern" do
146
+ it "should match literal by object equality" do
147
+ o1 = Object.new
148
+ o2 = Object.new
149
+ c = Case.new do
150
+ of o1
151
+ end
152
+ c.match(o1).should == true
153
+ lambda { c.match(o2) }.should not_match
154
+ end
155
+ end
156
+
157
+ describe "class pattern" do
158
+ it "should match objects of a class" do
159
+ s1 = "a"
160
+ s2 = "b"
161
+ c = Case.new do
162
+ of(a(String))
163
+ end
164
+ c.match(s1).should == true
165
+ c.match(s2).should == true
166
+ end
167
+
168
+ it "should use guard" do
169
+ c = Case.new do
170
+ of(a(Integer))
171
+ of(a(String) { false })
172
+ end
173
+ c.match(1).should == true
174
+ lambda { c.match("a") }.should not_match
175
+ end
176
+ end
177
+
178
+ describe "integer pattern" do
179
+ it "should match a range" do
180
+ c = Case.new do
181
+ of(integer(1..100){ |i| i % 2 == 0})
182
+ end
183
+ c.match(2).should == true
184
+ c.match(100).should == true
185
+ lambda { c.match(1)}.should not_match
186
+ lambda { c.match(0)}.should not_match
187
+ lambda { c.match(102)}.should not_match
188
+ end
189
+
190
+ it "should match a set" do
191
+ c = Case.new do
192
+ of integer([2,100])
193
+ end
194
+ c.match(2).should == true
195
+ c.match(100).should == true
196
+ lambda { c.match(1)}.should not_match
197
+ lambda { c.match(0)}.should not_match
198
+ lambda { c.match(102)}.should not_match
199
+ end
200
+
201
+ it "should match any integer" do
202
+ c = Case.new do
203
+ of(integer)
204
+ end
205
+ c.match(2).should == true
206
+ c.match(100).should == true
207
+ lambda { c.match(:foo)}.should not_match
208
+ end
209
+ end
210
+
211
+ describe "symbol pattern" do
212
+ it "should match symbol by regexp" do
213
+ c = Case.new do
214
+ of(symbol(/^a.*/))
215
+ end
216
+ c.match(:a).should == true
217
+ c.match(:abc).should == true
218
+ lambda { c.match(:babc) }.should not_match
219
+ lambda { c.match(10) }.should not_match
220
+ end
221
+
222
+ it "should match a symbol by class, string, or symbol" do
223
+ c = Case.new do
224
+ of(symbol { |o| o.to_s.length == 1})
225
+ of(symbol(:ab,:AB)) { ab }
226
+ end
227
+ c.match(:a).should == true
228
+ lambda { c.match(1) }.should not_match
229
+ c.match(:ab).should == :ab
230
+ lambda { c.match(:bc) }.should not_match
231
+ end
232
+
233
+ it "should use guards" do
234
+ c = Case.new do
235
+ of(symbol { |o| false })
236
+ of(symbol(:a) { |o| false })
237
+ of(symbol(/.*/) { |o| false })
238
+ end
239
+ lambda { c.match(:b) }.should not_match
240
+ lambda { c.match(:a) }.should not_match
241
+ lambda { c.match(:faewfe) }.should not_match
242
+ end
243
+ end
244
+
245
+ describe "string pattern" do
246
+ it "should match string by regexp" do
247
+ c = Case.new do
248
+ of(string(/^a.*/))
249
+ end
250
+ c.match("a").should == true
251
+ c.match("abc").should == true
252
+ lambda { c.match("babc") }.should not_match
253
+ lambda { c.match(10) }.should not_match
254
+ end
255
+
256
+ it "should match a string by class, string, or symbol" do
257
+ c = Case.new do
258
+ of(string { |o| o.to_s.length == 1})
259
+ of(string("ab",:AB)) { ab }
260
+ end
261
+ c.match("a").should == true
262
+ lambda { c.match(1) }.should not_match
263
+ lambda { c.match("abc") }.should not_match
264
+ c.match("ab").should == "ab"
265
+ lambda { c.match("bc") }.should not_match
266
+ end
267
+
268
+ it "should use guards" do
269
+ c = Case.new do
270
+ of(string { |o| false })
271
+ of(string("a") { |o| false })
272
+ of(string(/.*/) { |o| false })
273
+ end
274
+ lambda { c.match("a") }.should not_match
275
+ lambda { c.match("abc") }.should not_match
276
+ lambda { c.match("abcde") }.should not_match
277
+ end
278
+ end
279
+
280
+ describe "bind pattern" do
281
+ it "should bind a pattern to a variable" do
282
+ c = Case.new do
283
+ sym = a(Symbol)
284
+ of(bind(sym,:A){|o| o == :foo }) { a }
285
+ of(bind(sym,:A){|o| o == :bar }) { a }
286
+ end
287
+ c.match(:foo).should == :foo
288
+ c.match(:bar).should == :bar
289
+ lambda { c.match(:qux) }.should not_match
290
+ end
291
+ end
292
+
293
+ describe "is pattern" do
294
+ it "should make range a integer range pattern" do
295
+ c = Case.new do
296
+ of(1..100)
297
+ end
298
+ c.match(1).should == true
299
+ c.match(100).should == true
300
+ lambda { c.match(0) }.should not_match
301
+ lambda { c.match(101) }.should not_match
302
+ end
303
+
304
+ it "should make array an array pattern" do
305
+ c = Case.new do
306
+ of([1,2])
307
+ end
308
+ c.match([1,2]).should == true
309
+ end
310
+
311
+ it "should make regexp a string pattern" do
312
+ c = Case.new do
313
+ of(/abc/)
314
+ end
315
+ c.match("0abc0").should == true
316
+ end
317
+
318
+ it "should make hash a hash pattern" do
319
+ c = Case.new do
320
+ of(:a => 1)
321
+ end
322
+ c.match(:a => 1).should == true
323
+ end
324
+
325
+ it "should make class a class pattern" do
326
+ c = Case.new do
327
+ of(String)
328
+ end
329
+ c.match("abc").should == true
330
+ end
331
+ end
332
+
333
+ describe "wildcard pattern" do
334
+ it "should match anything" do
335
+ c = Case.new do
336
+ of(_(:V) { |o| o != :foobarqux }) {
337
+ v
338
+ }
339
+ end
340
+ c.match(10).should == 10
341
+ c.match(:a).should == :a
342
+ c.match("a").should == "a"
343
+ c.match(Object.new).should be_a(Object)
344
+ lambda { c.match(:foobarqux) }.should not_match
345
+ end
346
+ end
347
+
348
+ describe "one_of pattern" do
349
+ it "matches one_of a number of patterns" do
350
+ c = Case.new do
351
+ of(one_of([1,"b"],:V)) {
352
+ v
353
+ }
354
+ end
355
+ c.match(1).should == 1
356
+ c.match("b").should == "b"
357
+ lambda { c.match("c") }.should not_match
358
+ end
359
+ end
360
+
361
+ describe "array pattern" do
362
+ it "should match the exact length of array" do
363
+ c = Case.new {
364
+ of [1,2,3]
365
+ }
366
+ c.match([1,2,3]).should == true
367
+ lambda { c.match([]) }.should not_match
368
+ lambda { c.match([1,2]) }.should not_match
369
+ lambda { c.match([1,2,3,4]) }.should not_match
370
+ end
371
+
372
+ it "should use guard" do
373
+ c = Case.new {
374
+ of(array([1,2,3]){ false })
375
+ }
376
+ lambda { c.match([1,2,3]) }.should not_match
377
+ end
378
+
379
+ it "should match patterns in array pattern" do
380
+ c = Case.new {
381
+ pattern = symbol(/^a.*/)
382
+ of([pattern,pattern,pattern]) { 2 }
383
+ of([]) { 1 }
384
+ }
385
+ c.match([]).should == 1
386
+ c.match([:a,:ab,:abc]).should == 2
387
+ lambda { c.match([:a,:ab,:abc,:abcd]) }.should not_match
388
+ end
389
+
390
+ it "should match tail" do
391
+ c = Case.new {
392
+ of([_!(1)])
393
+ }
394
+ c.match([]).should == true
395
+ c.match([1,1]).should == true
396
+ c.match([1,1,1]).should == true
397
+ c.match([1,1,1,1]).should == true
398
+ end
399
+
400
+ it "should bind tail" do
401
+ c = Case.new {
402
+ of([_!(1,:tail)]) { tail }
403
+ }
404
+ c.match([]).should == []
405
+ c.match([1,1]).should == [1,1]
406
+ end
407
+
408
+ it "should match heads then tail" do
409
+ c = Case.new {
410
+ of([:a,:b,:c,_!(1,:tail)]) { tail }
411
+ }
412
+ c.match([:a,:b,:c]).should == []
413
+ c.match([:a,:b,:c,1]).should == [1]
414
+ c.match([:a,:b,:c,1,1]).should == [1,1]
415
+ lambda { c.match([:a,:b,1,1,1,1,1]) }.should not_match
416
+ lambda { c.match([1,1,1,1,1]) }.should not_match
417
+ lambda { c.match([:a,:b,:c,1,2]) }.should not_match
418
+ end
419
+
420
+ it "should use guard in tail pattern" do
421
+ c = Case.new {
422
+ of [_!(1){ |o| false }]
423
+ of [_!(integer(){ |o| o % 2 == 0 }){ |tail| tail.length == 3 }]
424
+ }
425
+ lambda {c.match([1,1,1])}.should not_match
426
+ lambda {c.match([2,2,1])}.should not_match
427
+ c.match([2,2,2]).should == true
428
+ c.match([2,4,6]).should == true
429
+ lambda {c.match([2,4,6,8])}.should not_match
430
+ end
431
+
432
+ it "should match nested arrays" do
433
+ c = Case.new {
434
+ of [1,2,[3,4,_!(symbol)]]
435
+ }
436
+ c.match([1,2,[3,4,:a,:b,:c]]).should == true
437
+ c.match([1,2,[3,4]]).should == true
438
+ lambda { c.match([1,2,[3,4,5,6,7]]) }.should not_match
439
+ lambda { c.match([1,2,[3]]) }.should not_match
440
+ lambda { c.match([1,2]) }.should not_match
441
+ end
442
+ end
443
+
444
+ describe "hash pattern" do
445
+ it "should match required keys" do
446
+ c = Case.new {
447
+ of(hash(:a => 1, :b => 2))
448
+ }
449
+ c.match(:a => 1, :b => 2).should == true
450
+ c.match(:a => 1, :b => 2, :c => 3).should == true
451
+ lambda { c.match(1) } .should not_match
452
+ lambda { c.match(:a => 1, :b => 3) }.should not_match
453
+ lambda { c.match(:a => 1, :c => 3) }.should not_match
454
+ end
455
+
456
+ it "should match optional keys" do
457
+ c = Case.new {
458
+ of(hash([:a] => 1))
459
+ }
460
+ c.match(:a => 1).should == true
461
+ c.match(:a => nil).should == true
462
+ c.match({}).should == true
463
+ lambda { c.match(:a => 2)}.should not_match
464
+ end
465
+ end
466
+ end