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 +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README +310 -0
- data/README.markdown +310 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/matchmaker.rb +479 -0
- data/matchmaker.gemspec +55 -0
- data/spec/case_spec.rb +466 -0
- data/spec/spec_helper.rb +9 -0
- metadata +77 -0
data/.document
ADDED
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
|
+
|