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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Howard Yeh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,310 @@
1
+
2
+ Matchmaker is a powerful and expressive DSL that
3
+ brings pattern matching into Ruby. Often we need
4
+ to check if an object is what we expect, and if
5
+ so, we want to take it apart. Pattern matching is
6
+ an expressive idiom from functional programming
7
+ languages to make checking on an object and taking
8
+ it apart concisely readable.
9
+
10
+ # Install
11
+
12
+ > sudo gem install hayeah-matchmaker
13
+ > irb -rubygems
14
+ irb> require 'matchmaker'
15
+ irb> Case(1) { of(1) }
16
+ # => true
17
+
18
+ # Case Statement
19
+
20
+ A case statement can have many alternative
21
+ patterns to match. The patterns are run in
22
+ sequence until the first one that matches. If no
23
+ pattern matches, a `Case::NoMatch` exception is
24
+ raised.
25
+
26
+ Case(1) {
27
+ of(1)
28
+ of(:b)
29
+ } # => true
30
+
31
+ Case(2) {
32
+ of(1)
33
+ of(:b)
34
+ } # => raise Case::NoMatch
35
+
36
+ Case(:b) {
37
+ of(1)
38
+ of(:b)
39
+ } # => true
40
+
41
+ Matchmaker uses the Case DSL to build a runnable
42
+ pattern matcher. You can store the pattern matcher
43
+ in a constant or variable, so Matchmaker doesn't
44
+ have to build it everytime you use it.
45
+
46
+ c = Case.new {
47
+ of(1)
48
+ of(:b)
49
+ }
50
+ c.match(1) => true
51
+ c.match(0) => Case::NoMatch
52
+ c.match(:b) => true
53
+
54
+ For each pattern in the case statement, there can
55
+ be an action associated with it. The action is run
56
+ when the pattern of that branch matches, and the
57
+ result of the action returned:
58
+
59
+ c = Case.new {
60
+ of(1) { :a }
61
+ of(2) { :b }
62
+ of(3) # { true } by default
63
+ }
64
+ c.match(1) # => :a
65
+ c.match(2) # => :b
66
+ c.match(3) # => true
67
+
68
+ # Simple Patterns
69
+
70
+ Literals ruby objects are matched by `==`
71
+
72
+ of(1) # matches 1
73
+ of(:a) # matches :a
74
+ of("a") # matches "a"
75
+
76
+ A regex is used to match string
77
+
78
+ # matches "0abc0"
79
+ of(/abc/)
80
+
81
+ A class is used to match objects of the that class
82
+
83
+ # matches "abc"
84
+ of(String)
85
+
86
+ A range can be used to match a range of integers
87
+
88
+ # matches integer within 1 and 10
89
+ of(1..10)
90
+
91
+ An array should be an array of patterns that
92
+ matches an array object, such that each element in
93
+ the array should match its pattern.
94
+
95
+ # matches ["a",[],{}]
96
+ of([String,Array,Hash])
97
+
98
+ # Destructuring and Pattern Guard
99
+
100
+ There are patterns to match common ruby
101
+ objects. For example, Case#string is a method of
102
+ the DSL to build the string pattern.
103
+
104
+ of(string) # matches any string
105
+ of(string("a")) # matches "a"
106
+ of(string(/foo/)) # matches any string of that regexp
107
+
108
+ These pattern methods can make variable bindings
109
+ of the matched object,
110
+
111
+ Case([1,"a"]) {
112
+ of([1,string(/a/,:a)]) { a }
113
+ } # => "a"
114
+
115
+ In this example, the pattern sets the variable
116
+ `:a` to the matched string. These variables are
117
+ actually bindings. Binding of the same name in
118
+ different patterns must equal to each other,
119
+ otherwise the overall pattern won't match.
120
+
121
+ c = Case.new {
122
+ of([string(/a/,:a),string(/b/,:a)])
123
+ }
124
+ c.match(["ab","ab"]) # => true
125
+ c.match(["ab","ba"]) # Case::NoMatch
126
+
127
+ A guard is an arbitray ruby block associated with
128
+ a pattern, so that a pattern matches iff the guard
129
+ returns true.
130
+
131
+ c = Case.new {
132
+ of(string(/a/) {|s| s.length == 2 })
133
+ }
134
+ c.match("ab") # => true
135
+ c.match("ba") # => true
136
+ c.match("abc") # Case::NoMatch
137
+
138
+ # Value Patterns
139
+
140
+ These patterns are used to match a single Ruby
141
+ object. See later for structural patterns for ways
142
+ to combine these value patterns. All these methods
143
+ can make binding and take a guard, as in
144
+
145
+ of(some_pattern(pattern_spec,:binding) {|matched_value|
146
+ some_more_test_on(matched_value)
147
+ })
148
+
149
+ ## Case#_
150
+
151
+ This is the wildcard that matches anything
152
+
153
+ of(_)
154
+
155
+ You can use binding to say "I don't care what this
156
+ and that are, but I want them to be the same", like so,
157
+
158
+ of([_(:a),1,_(:a)])
159
+
160
+ The pattern matcher binds whatever matches the
161
+ first and last element of the array to ":a", the
162
+ binding fails unless those elements are the same.
163
+
164
+ ## Case#a
165
+
166
+ Matches any object of a class.
167
+
168
+ of(a(String))
169
+
170
+ ## Case#literal
171
+
172
+ Matches an object by its `==` method.
173
+
174
+ of(literal(1))
175
+ of(literal(:b))
176
+
177
+ ## Case#integer
178
+
179
+ Matches an integer.
180
+
181
+ of(integer) # any integer
182
+ of(integer(1)) # matches 1
183
+ of(integer(1..10)) # matches a range
184
+ # matches any number in the set
185
+ of(integer([1,3,5,7,9]))
186
+
187
+ ## Case#string
188
+
189
+ of(string)
190
+ of(string("abc"))
191
+ of(string(/abc/))
192
+
193
+ ## Case#symbol
194
+
195
+ of(string)
196
+ of(string(:abc))
197
+ # matches a symbol if its to_s matches the regexp.
198
+ of(string(/abc/))
199
+
200
+
201
+ # Structural Patterns
202
+
203
+ For "container" objects like arrays and hashes,
204
+ pattern matching is a nice way to specify the
205
+ properties of the object, and taking it apart.
206
+
207
+
208
+ ## Case#array
209
+
210
+ of([]) # == of(array([]))
211
+ of([1,2]) # matches exactly [1,2]
212
+ of([symbol,string,symbol])
213
+ # matches an array the first and last element are
214
+ # the same symbol, the second element is a string
215
+ of([symbol(nil,:a),string,symbol(nil,:a)])
216
+
217
+ You can match the tail of an array by using the
218
+ special method `Case#_!`
219
+
220
+ c = Case.new {
221
+ of([1,2,_!(integer)])
222
+ }
223
+ c.match([1]) # Case::NoMatch
224
+ c.match([1,2]) # => true
225
+ c.match([1,2,3]) # => true
226
+ c.match([1,2,3,4,5]) # => true
227
+ c.match([1,2,3,:a]) # => Case::NoMatch
228
+
229
+ this pattern matches any array whose first two
230
+ elements are 1 and 2, the rest are integers.
231
+
232
+
233
+ ## Case#hash
234
+
235
+ Hashes are pattern matched by specifying the what
236
+ pattern a key's value should match. A key can be
237
+ either be required, or optional, for the pattern
238
+ matching to succeed.
239
+
240
+ c = Case.new {
241
+ of("required" => integer, ["optional"] => string)
242
+ }
243
+ c.match("required" => 10, "optional" => "a") # => true
244
+ c.match("required" => 10, "optional" => 1) # Case::NoMatch
245
+ c.match("required" => 10) # => true
246
+ c.match("required" => "a") # => Case::NoMatch
247
+
248
+ The optional key is denoted by wrapping that in an
249
+ array.
250
+
251
+ ## Case#one_of
252
+
253
+ This pattern matches if any one of its array of
254
+ patterns matches.
255
+
256
+ c = Case.new {
257
+ of(one_of([integer,string]))
258
+ }
259
+ c.match(10) # => true
260
+ c.match("yay") # => true
261
+ c.match(:foo) # => Case::NoMatch
262
+
263
+ # Higher Order Pattern
264
+
265
+ The pattern matcher is just constructed with a
266
+ bunch of objects. So it's possible to save a
267
+ pattern in a variable, and use that to build
268
+ larger patterns.
269
+
270
+ Build a two element array of a custom pattern,
271
+
272
+ c = Case.new {
273
+ my_pattern = [symbol,string]
274
+ of([my_pattern,my_pattern])
275
+ }
276
+
277
+ Generate pattern with lambda,
278
+
279
+ c = Case.new {
280
+ pattern = lambda {|pattern1,pattern2| [pattern1,pattern2]}
281
+ of(pattern.call(symbol,string))
282
+ of(pattern.call(string,symbol))
283
+ }
284
+
285
+ ## Case#is
286
+
287
+ This method is used to coerce an object into
288
+ pattern if it isn't already.
289
+
290
+ is(/regexp/) # string(regexp)
291
+ is(some_class) # matches by class
292
+ is(obj) # literal(obj) for any object
293
+
294
+ Mostly useful for higher order stuff you are
295
+ doing.
296
+
297
+ ## Case#bind
298
+
299
+ This is used to matched values to variables. It
300
+ takes a pattern, and a binding name, and possibly
301
+ a guard.
302
+
303
+ c = Case.new {
304
+ foo = is([string])
305
+ of(bind(foo,:a)) { [1,a] }
306
+ of(bind([foo],:a)) { [2,a] }
307
+ }
308
+ c.match(["a"]) # => [1,["a"]]
309
+ c.match([["a"]]) # => [2,[["a"]]]
310
+
data/README.markdown ADDED
@@ -0,0 +1,310 @@
1
+
2
+ Matchmaker is a powerful and expressive DSL that
3
+ brings pattern matching into Ruby. Often we need
4
+ to check if an object is what we expect, and if
5
+ so, we want to take it apart. Pattern matching is
6
+ an expressive idiom from functional programming
7
+ languages to make checking on an object and taking
8
+ it apart concisely readable.
9
+
10
+ # Install
11
+
12
+ > sudo gem install hayeah-matchmaker
13
+ > irb -rubygems
14
+ irb> require 'matchmaker'
15
+ irb> Case(1) { of(1) }
16
+ # => true
17
+
18
+ # Case Statement
19
+
20
+ A case statement can have many alternative
21
+ patterns to match. The patterns are run in
22
+ sequence until the first one that matches. If no
23
+ pattern matches, a `Case::NoMatch` exception is
24
+ raised.
25
+
26
+ Case(1) {
27
+ of(1)
28
+ of(:b)
29
+ } # => true
30
+
31
+ Case(2) {
32
+ of(1)
33
+ of(:b)
34
+ } # => raise Case::NoMatch
35
+
36
+ Case(:b) {
37
+ of(1)
38
+ of(:b)
39
+ } # => true
40
+
41
+ Matchmaker uses the Case DSL to build a runnable
42
+ pattern matcher. You can store the pattern matcher
43
+ in a constant or variable, so Matchmaker doesn't
44
+ have to build it everytime you use it.
45
+
46
+ c = Case.new {
47
+ of(1)
48
+ of(:b)
49
+ }
50
+ c.match(1) # => true
51
+ c.match(0) # => Case::NoMatch
52
+ c.match(:b) # => true
53
+
54
+ For each pattern in the case statement, there can
55
+ be an action associated with it. The action is run
56
+ when the pattern of that branch matches, and the
57
+ result of the action returned:
58
+
59
+ c = Case.new {
60
+ of(1) { :a }
61
+ of(2) { :b }
62
+ of(3) # { true } by default
63
+ }
64
+ c.match(1) # => :a
65
+ c.match(2) # => :b
66
+ c.match(3) # => true
67
+
68
+ # Simple Patterns
69
+
70
+ Literals ruby objects are matched by `==`
71
+
72
+ of(1) # matches 1
73
+ of(:a) # matches :a
74
+ of("a") # matches "a"
75
+
76
+ A regex is used to match string
77
+
78
+ # matches "0abc0"
79
+ of(/abc/)
80
+
81
+ A class is used to match objects of the that class
82
+
83
+ # matches "abc"
84
+ of(String)
85
+
86
+ A range can be used to match a range of integers
87
+
88
+ # matches integer within 1 and 10
89
+ of(1..10)
90
+
91
+ An array should be an array of patterns that
92
+ matches an array object, such that each element in
93
+ the array should match its pattern.
94
+
95
+ # matches ["a",[],{}]
96
+ of([String,Array,Hash])
97
+
98
+ # Destructuring and Pattern Guard
99
+
100
+ There are patterns to match common ruby
101
+ objects. For example, Case#string is a method of
102
+ the DSL to build the string pattern.
103
+
104
+ of(string) # matches any string
105
+ of(string("a")) # matches "a"
106
+ of(string(/foo/)) # matches any string of that regexp
107
+
108
+ These pattern methods can make variable bindings
109
+ of the matched object,
110
+
111
+ Case([1,"a"]) {
112
+ of([1,string(/a/,:a)]) { a }
113
+ } # => "a"
114
+
115
+ In this example, the pattern sets the variable
116
+ `:a` to the matched string. These variables are
117
+ actually bindings. Binding of the same name in
118
+ different patterns must equal to each other,
119
+ otherwise the overall pattern won't match.
120
+
121
+ c = Case.new {
122
+ of([string(/a/,:a),string(/b/,:a)])
123
+ }
124
+ c.match(["ab","ab"]) # => true
125
+ c.match(["ab","ba"]) # Case::NoMatch
126
+
127
+ A guard is an arbitray ruby block associated with
128
+ a pattern, so that a pattern matches iff the guard
129
+ returns true.
130
+
131
+ c = Case.new {
132
+ of(string(/a/) {|s| s.length == 2 })
133
+ }
134
+ c.match("ab") # => true
135
+ c.match("ba") # => true
136
+ c.match("abc") # Case::NoMatch
137
+
138
+ # Value Patterns
139
+
140
+ These patterns are used to match a single Ruby
141
+ object. See later for structural patterns for ways
142
+ to combine these value patterns. All these methods
143
+ can make binding and take a guard, as in
144
+
145
+ of(some_pattern(pattern_spec,:binding) {|matched_value|
146
+ some_more_test_on(matched_value)
147
+ })
148
+
149
+ ## Case#_
150
+
151
+ This is the wildcard that matches anything
152
+
153
+ of(_)
154
+
155
+ You can use binding to say "I don't care what this
156
+ and that are, but I want them to be the same", like so,
157
+
158
+ of([_(:a),1,_(:a)])
159
+
160
+ The pattern matcher binds whatever matches the
161
+ first and last element of the array to ":a", the
162
+ binding fails unless those elements are the same.
163
+
164
+ ## Case#a
165
+
166
+ Matches any object of a class.
167
+
168
+ of(a(String))
169
+
170
+ ## Case#literal
171
+
172
+ Matches an object by its `==` method.
173
+
174
+ of(literal(1))
175
+ of(literal(:b))
176
+
177
+ ## Case#integer
178
+
179
+ Matches an integer.
180
+
181
+ of(integer) # any integer
182
+ of(integer(1)) # matches 1
183
+ of(integer(1..10)) # matches a range
184
+ of(integer([1,3,5,7,9])) # matches any number in the set
185
+
186
+ ## Case#string
187
+
188
+ of(string)
189
+ of(string("abc"))
190
+ of(string(/abc/))
191
+
192
+ ## Case#symbol
193
+
194
+ of(string)
195
+ of(string(:abc))
196
+ # matches a symbol if its to_s matches the regexp.
197
+ of(string(/abc/))
198
+
199
+
200
+ # Structural Patterns
201
+
202
+ For "container" objects like arrays and hashes,
203
+ pattern matching is a nice way to specify the
204
+ properties of the object, and taking it apart.
205
+
206
+
207
+ ## Case#array
208
+
209
+ of([]) # == of(array([]))
210
+ of([1,2]) # matches exactly [1,2]
211
+ of([symbol,string,symbol])
212
+
213
+ # matches an array the first and last element are
214
+ # the same symbol, the second element is a string
215
+ of([symbol(nil,:a),string,symbol(nil,:a)])
216
+
217
+ You can match the tail of an array by using the
218
+ special method `Case#_!`
219
+
220
+ c = Case.new {
221
+ of([1,2,_!(integer)])
222
+ }
223
+ c.match([1]) # Case::NoMatch
224
+ c.match([1,2]) # => true
225
+ c.match([1,2,3]) # => true
226
+ c.match([1,2,3,4,5]) # => true
227
+ c.match([1,2,3,:a]) # Case::NoMatch
228
+
229
+ this pattern matches any array whose first two
230
+ elements are 1 and 2, the rest are integers.
231
+
232
+
233
+ ## Case#hash
234
+
235
+ Hashes are pattern matched by specifying the what
236
+ pattern a key's value should match. A key can be
237
+ either be required, or optional, for the pattern
238
+ matching to succeed.
239
+
240
+ c = Case.new {
241
+ of("required" => integer, ["optional"] => string)
242
+ }
243
+ c.match("required" => 10, "optional" => "a") # => true
244
+ c.match("required" => 10, "optional" => 1) # Case::NoMatch
245
+ c.match("required" => 10) # => true
246
+ c.match("required" => "a") # => Case::NoMatch
247
+
248
+ The optional key is denoted by wrapping that in an
249
+ array.
250
+
251
+ ## Case#one_of
252
+
253
+ This pattern matches if any one of its array of
254
+ patterns matches.
255
+
256
+ c = Case.new {
257
+ of(one_of([integer,string]))
258
+ }
259
+ c.match(10) # => true
260
+ c.match("yay") # => true
261
+ c.match(:foo) # => Case::NoMatch
262
+
263
+ # Higher Order Pattern
264
+
265
+ The pattern matcher is just constructed with a
266
+ bunch of objects. So it's possible to save a
267
+ pattern in a variable, and use that to build
268
+ larger patterns.
269
+
270
+ Build a two element array of a custom pattern,
271
+
272
+ c = Case.new {
273
+ my_pattern = [symbol,string]
274
+ of([my_pattern,my_pattern])
275
+ }
276
+
277
+ Generate pattern with lambda,
278
+
279
+ c = Case.new {
280
+ pattern = lambda {|pattern1,pattern2| [pattern1,pattern2]}
281
+ of(pattern.call(symbol,string))
282
+ of(pattern.call(string,symbol))
283
+ }
284
+
285
+ ## Case#is
286
+
287
+ This method is used to coerce an object into
288
+ pattern if it isn't already.
289
+
290
+ is(/regexp/) # string(regexp)
291
+ is(some_class) # matches by class
292
+ is(obj) # literal(obj) for any object
293
+
294
+ Mostly useful for higher order stuff you are
295
+ doing.
296
+
297
+ ## Case#bind
298
+
299
+ This is used to matched values to variables. It
300
+ takes a pattern, and a binding name, and possibly
301
+ a guard.
302
+
303
+ c = Case.new {
304
+ foo = is([string])
305
+ of(bind(foo,:a)) { [1,a] }
306
+ of(bind([foo],:a)) { [2,a] }
307
+ }
308
+ c.match(["a"]) # => [1,["a"]]
309
+ c.match([["a"]]) # => [2,[["a"]]]
310
+