patm 0.1.0 → 1.0.0
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.
- 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]) }
|