patm 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +54 -2
- data/benchmark/benchmark.rb +3 -3
- data/lib/patm.rb +53 -54
- data/patm.gemspec +1 -1
- data/spec/patm_spec.rb +37 -6
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e616e4136344078fc78da517f68682dd2ace261
|
4
|
+
data.tar.gz: 759ff1f8066ad8b0f9ea3e1cd28573458b62fc5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3471bde54cc18238144344e507e66a40cf116bfea3aebb3c4b6c85c18e27edb7931f0db1474f684a5dc6c1044a9a9e89c4a46f8b222e9056ed562d877658abfd
|
7
|
+
data.tar.gz: cb68d3f85838c99f3f39089f78f53e64f5ec8f21734c111563c18e773a17ad29388e563952185aa636b2d0fbef838ed4a4491ec42fae7c3491c2392a6fa6aa24
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ require 'patm'
|
|
10
10
|
# With case(simple but slow)
|
11
11
|
def match(obj)
|
12
12
|
p = Patm
|
13
|
-
_xs = Patm
|
13
|
+
_xs = Patm._xs
|
14
14
|
case obj
|
15
15
|
when m = Patm.match([:x, p._1, p._2])
|
16
16
|
[m._2, m._1]
|
@@ -29,11 +29,35 @@ match([])
|
|
29
29
|
# => nil
|
30
30
|
```
|
31
31
|
|
32
|
+
```ruby
|
33
|
+
# With DSL
|
34
|
+
class A
|
35
|
+
extend ::Patm::DSL
|
36
|
+
|
37
|
+
define_matcher :match1 do|r|
|
38
|
+
p = Patm
|
39
|
+
r.on [:x, p._1, p._2] do|m|
|
40
|
+
[m._1, m._2]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
define_matcher :match2 do|r|
|
45
|
+
r.on [:a, Patm._xs & Patm._1] do|m, _self|
|
46
|
+
_self.match1(m._1)
|
47
|
+
end
|
48
|
+
# ...
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
A.new.match1([:x, 1, 2])
|
53
|
+
# => [1, 2]
|
54
|
+
```
|
55
|
+
|
32
56
|
```ruby
|
33
57
|
# With pre-built Rule
|
34
58
|
rule = Patm::Rule.new do|r|
|
35
59
|
p = Patm
|
36
|
-
_xs = Patm
|
60
|
+
_xs = Patm._xs
|
37
61
|
r.on [:x, p._1, p._2] do|m|
|
38
62
|
[m._2, m._1]
|
39
63
|
end
|
@@ -76,9 +100,37 @@ class A
|
|
76
100
|
end
|
77
101
|
```
|
78
102
|
|
103
|
+
## Patterns
|
104
|
+
|
105
|
+
### `1`, `:x`, String, ...
|
106
|
+
|
107
|
+
Normal pattern matches if `pattern === value` is true.
|
108
|
+
|
109
|
+
### Array
|
110
|
+
|
111
|
+
`[1, 2, _xs]` matches `[1, 2]`, `[1, 2, 3]`, `[1, 2, 3, 4]`, etc.
|
112
|
+
`[1, _xs, 2]` matches `[1, 2]`, `[1, 10, 2]`, etc.
|
113
|
+
|
114
|
+
Note: More than one `_xs` in same array is invalid.
|
115
|
+
|
116
|
+
### Capture
|
117
|
+
|
118
|
+
`_1`, `_2`, etc matches any value, and capture the value as correspond match group.
|
119
|
+
|
120
|
+
### Compose
|
121
|
+
|
122
|
+
`_1&[_any, _any]` matches any two element array, and capture the array as _1.
|
123
|
+
`Patm.or(1, 2)` matches 1 or 2.
|
124
|
+
|
79
125
|
|
80
126
|
## Changes
|
81
127
|
|
128
|
+
### 1.0.0
|
129
|
+
|
130
|
+
- DSL
|
131
|
+
- Compile is enabled by default
|
132
|
+
- Change interface
|
133
|
+
|
82
134
|
### 0.1.0
|
83
135
|
|
84
136
|
- Faster matching with pattern compilation
|
data/benchmark/benchmark.rb
CHANGED
@@ -8,7 +8,7 @@ def match_with_case(obj)
|
|
8
8
|
_2 = P._2
|
9
9
|
_3 = P._3
|
10
10
|
_4 = P._4
|
11
|
-
_xs = P
|
11
|
+
_xs = P._xs
|
12
12
|
|
13
13
|
case obj
|
14
14
|
when m = P.match([:assign, [:var_field, [:@ident, _1, [_2, _3]]], _4])
|
@@ -31,7 +31,7 @@ end
|
|
31
31
|
_2 = P._2
|
32
32
|
_3 = P._3
|
33
33
|
_4 = P._4
|
34
|
-
_xs = P
|
34
|
+
_xs = P._xs
|
35
35
|
|
36
36
|
r.on [:assign, [:var_field, [:@ident, _1, [_2, _3]]], _4] do|m|
|
37
37
|
:as
|
@@ -53,7 +53,7 @@ end
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
@rule = P::Rule.new(&@ruledef)
|
56
|
+
@rule = P::Rule.new(false, &@ruledef)
|
57
57
|
@compiled_rule = P::Rule.new(true, &@ruledef)
|
58
58
|
|
59
59
|
def match_with_rule(obj)
|
data/lib/patm.rb
CHANGED
@@ -1,33 +1,30 @@
|
|
1
1
|
module Patm
|
2
2
|
class Pattern
|
3
|
-
def execute(match, obj); true; end
|
4
|
-
|
5
|
-
def rest?
|
6
|
-
false
|
7
|
-
end
|
8
3
|
|
9
4
|
def self.build_from(plain)
|
10
5
|
case plain
|
11
6
|
when Pattern
|
12
7
|
plain
|
13
8
|
when Array
|
14
|
-
|
9
|
+
array = plain.map{|a| build_from(a)}
|
10
|
+
rest_index = array.index(&:rest?)
|
11
|
+
if rest_index
|
12
|
+
head = array[0...rest_index]
|
13
|
+
rest = array[rest_index]
|
14
|
+
tail = array[(rest_index+1)..-1]
|
15
|
+
Arr.new(head, rest, tail)
|
16
|
+
else
|
17
|
+
Arr.new(array)
|
18
|
+
end
|
15
19
|
else
|
16
20
|
Obj.new(plain)
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
head = array[0...rest_index]
|
25
|
-
rest = array[rest_index]
|
26
|
-
tail = array[(rest_index+1)..-1]
|
27
|
-
Arr.new(head, rest, tail)
|
28
|
-
else
|
29
|
-
Arr.new(array)
|
30
|
-
end
|
24
|
+
def execute(match, obj); true; end
|
25
|
+
|
26
|
+
def rest?
|
27
|
+
false
|
31
28
|
end
|
32
29
|
|
33
30
|
def &(rhs)
|
@@ -271,15 +268,38 @@ module Patm
|
|
271
268
|
end
|
272
269
|
end
|
273
270
|
|
274
|
-
ANY = Pattern::Any.new
|
275
271
|
GROUP = 100.times.map{|i| Pattern::Group.new(i) }
|
276
|
-
|
272
|
+
|
273
|
+
def self.or(*pats)
|
274
|
+
Pattern::Or.new(pats.map{|p| Pattern.build_from(p) })
|
275
|
+
end
|
276
|
+
|
277
|
+
def self._any
|
278
|
+
@any ||= Pattern::Any.new
|
279
|
+
end
|
280
|
+
|
281
|
+
def self._xs
|
282
|
+
@xs = Pattern::ArrRest.new
|
283
|
+
end
|
284
|
+
|
285
|
+
class <<self
|
286
|
+
GROUP.each do|g|
|
287
|
+
define_method "_#{g.index}" do
|
288
|
+
g
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.match(plain_pat)
|
294
|
+
CaseBinder.new Pattern.build_from(plain_pat)
|
295
|
+
end
|
277
296
|
|
278
297
|
class Rule
|
279
|
-
def initialize(compile =
|
298
|
+
def initialize(compile = true, &block)
|
280
299
|
@compile = compile
|
281
300
|
# { Pattern => Proc }
|
282
301
|
@rules = []
|
302
|
+
@else = ->(obj){nil}
|
283
303
|
block[self]
|
284
304
|
end
|
285
305
|
|
@@ -292,31 +312,27 @@ module Patm
|
|
292
312
|
end
|
293
313
|
|
294
314
|
def else(&block)
|
295
|
-
|
296
|
-
@rules << [ANY.compile, lambda {|m,o| block[o] }]
|
297
|
-
else
|
298
|
-
@rules << [ANY, lambda {|m,o| block[o] }]
|
299
|
-
end
|
315
|
+
@else = block
|
300
316
|
end
|
301
317
|
|
302
|
-
def apply(obj)
|
318
|
+
def apply(obj, _self = nil)
|
303
319
|
match = Match.new
|
304
320
|
@rules.each do|(pat, block)|
|
305
321
|
if pat.execute(match, obj)
|
306
|
-
return block.call(match,
|
322
|
+
return block.call(match, _self)
|
307
323
|
end
|
308
324
|
end
|
309
|
-
|
325
|
+
@else[obj, _self]
|
310
326
|
end
|
311
327
|
end
|
312
328
|
|
313
329
|
class RuleCache
|
314
|
-
def initialize(compile =
|
330
|
+
def initialize(compile = true)
|
315
331
|
@compile = compile
|
316
332
|
@rules = {}
|
317
333
|
end
|
318
|
-
def match(rule_name, obj, &rule)
|
319
|
-
(@rules[rule_name] ||= ::Patm::Rule.new(@compile, &rule)).apply(obj)
|
334
|
+
def match(rule_name, obj, _self = nil, &rule)
|
335
|
+
(@rules[rule_name] ||= ::Patm::Rule.new(@compile, &rule)).apply(obj, _self)
|
320
336
|
end
|
321
337
|
end
|
322
338
|
|
@@ -358,29 +374,12 @@ module Patm
|
|
358
374
|
end
|
359
375
|
end
|
360
376
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
Match.new(
|
368
|
-
Pattern::Arr.new(
|
369
|
-
head.map{|e| Pattern.build_from(e)},
|
370
|
-
rest_spat,
|
371
|
-
tail.map{|e| Pattern.build_from(e)}
|
372
|
-
)
|
373
|
-
)
|
374
|
-
end
|
375
|
-
|
376
|
-
def self.or(*pats)
|
377
|
-
Pattern::Or.new(pats.map{|p| Pattern.build_from(p) })
|
378
|
-
end
|
379
|
-
|
380
|
-
class <<self
|
381
|
-
GROUP.each do|g|
|
382
|
-
define_method "_#{g.index}" do
|
383
|
-
g
|
377
|
+
module DSL
|
378
|
+
def define_matcher(name, &rule)
|
379
|
+
@patm_rules ||= RuleCache.new
|
380
|
+
rules = @patm_rules
|
381
|
+
define_method name do|obj|
|
382
|
+
rules.match(name, obj, self, &rule)
|
384
383
|
end
|
385
384
|
end
|
386
385
|
end
|
data/patm.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.platform = Gem::Platform::RUBY
|
3
3
|
s.name = 'patm'
|
4
|
-
s.version = '
|
4
|
+
s.version = '1.0.0'
|
5
5
|
s.summary = 'PATtern Matching library'
|
6
6
|
s.description = 'Pattern matching library for plain data structure'
|
7
7
|
s.required_ruby_version = '>= 1.9.0'
|
data/spec/patm_spec.rb
CHANGED
@@ -51,7 +51,7 @@ describe "Usage:" do
|
|
51
51
|
|
52
52
|
it 'with predefined Rule' do
|
53
53
|
p = Patm
|
54
|
-
r = p::Rule.new do|r|
|
54
|
+
r = p::Rule.new(false) do|r|
|
55
55
|
r.on [1, p._1, p._2] do|m|
|
56
56
|
[m._1, m._2]
|
57
57
|
end
|
@@ -100,6 +100,37 @@ describe "Usage:" do
|
|
100
100
|
|
101
101
|
rs.match(:pattern_1, [1, 3, 5]) {|r| fail "should not reach here" }.should == [3, 5]
|
102
102
|
end
|
103
|
+
|
104
|
+
it 'with DSL' do
|
105
|
+
o = Object.new
|
106
|
+
class <<o
|
107
|
+
extend ::Patm::DSL
|
108
|
+
define_matcher :match1 do|r|
|
109
|
+
r.on [1, 2, ::Patm._1] do|m|
|
110
|
+
m._1
|
111
|
+
end
|
112
|
+
r.else do|obj|
|
113
|
+
obj.to_s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
define_matcher :match2 do|r|
|
118
|
+
r.on [1] do
|
119
|
+
1
|
120
|
+
end
|
121
|
+
r.on [1, ::Patm._xs & ::Patm._1] do|m, _self|
|
122
|
+
_self.match1(m._1)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
o.match1([1, 2, 3]).should == 3
|
128
|
+
o.match1([1, 2, 4]).should == 4
|
129
|
+
o.match1([1, 2]).should == "[1, 2]"
|
130
|
+
|
131
|
+
o.match2([1]).should == 1
|
132
|
+
o.match2([1, 2, 3]).should == "[2, 3]"
|
133
|
+
end
|
103
134
|
end
|
104
135
|
|
105
136
|
describe Patm::Pattern do
|
@@ -133,12 +164,12 @@ describe Patm::Pattern do
|
|
133
164
|
it { should_not match_to [1,2,3] }
|
134
165
|
end
|
135
166
|
|
136
|
-
pattern Patm
|
167
|
+
pattern Patm._any do
|
137
168
|
it { should match_to 1 }
|
138
169
|
it { should match_to ["foo", "bar"] }
|
139
170
|
end
|
140
171
|
|
141
|
-
pattern [1, Patm
|
172
|
+
pattern [1, Patm._any, 3] do
|
142
173
|
it { should match_to [1, 2, 3] }
|
143
174
|
it { should match_to [1, 0, 3] }
|
144
175
|
it { should_not match_to [1, 0, 4] }
|
@@ -164,18 +195,18 @@ describe Patm::Pattern do
|
|
164
195
|
it { should_not match_to(['x', 1, 2]).and_capture(1, 2) }
|
165
196
|
end
|
166
197
|
|
167
|
-
pattern [0, 1, Patm
|
198
|
+
pattern [0, 1, Patm._xs] do
|
168
199
|
it { should_not match_to([0]) }
|
169
200
|
it { should match_to([0, 1]) }
|
170
201
|
it { should match_to([0, 1, 2, 3]) }
|
171
202
|
end
|
172
203
|
|
173
|
-
pattern [0, 1, Patm
|
204
|
+
pattern [0, 1, Patm._xs & Patm._1] do
|
174
205
|
it { should match_to([0, 1]).and_capture([]) }
|
175
206
|
it { should match_to([0, 1, 2, 3]).and_capture([2, 3]) }
|
176
207
|
end
|
177
208
|
|
178
|
-
pattern [0, 1, Patm
|
209
|
+
pattern [0, 1, Patm._xs, 2] do
|
179
210
|
it { should match_to([0,1,2]) }
|
180
211
|
it { should match_to([0,1,10,20,30,2]) }
|
181
212
|
it { should_not match_to([0,1]) }
|