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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6f5f8bcb3f5f3031bac6f117418b1d3bc917e8a
4
- data.tar.gz: 56f203d9076b6905894ffb752954c7caa41fcca6
3
+ metadata.gz: 0e616e4136344078fc78da517f68682dd2ace261
4
+ data.tar.gz: 759ff1f8066ad8b0f9ea3e1cd28573458b62fc5d
5
5
  SHA512:
6
- metadata.gz: 7f01fa35d647fea7658029071682a77dea11e7da92968087c383028f13476ba7a7745a7f04093457c1a45beb92bc1328fab4c9feb464f55550ef3df08b38bb40
7
- data.tar.gz: 9ac471509e902916d52693fc7fe0bac69fb84106b8a539bcbd1f6a6657c34150e6214e73d0555b28c32ac6038bbdfaa7f40073160327ac024a9be16c2d7d1bdd
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::ARRAY_REST
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::ARRAY_REST
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
@@ -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::ARRAY_REST
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::ARRAY_REST
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)
@@ -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
- build_from_array(plain)
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 self.build_from_array(array)
21
- array = array.map{|a| build_from(a)}
22
- rest_index = array.index(&:rest?)
23
- if rest_index
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
- ARRAY_REST = Pattern::ArrRest.new
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 = false, &block)
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
- if @compile
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, obj)
322
+ return block.call(match, _self)
307
323
  end
308
324
  end
309
- nil
325
+ @else[obj, _self]
310
326
  end
311
327
  end
312
328
 
313
329
  class RuleCache
314
- def initialize(compile = false)
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
- def self.match(plain_pat)
362
- CaseBinder.new Pattern.build_from(plain_pat)
363
- end
364
-
365
- def self.match_array(head, rest_spat = nil, tail = [])
366
- # TODO: deprecated
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
@@ -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 = '0.1.0'
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'
@@ -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::ANY do
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::ANY, 3] do
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::ARRAY_REST] do
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::ARRAY_REST & Patm._1] do
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::ARRAY_REST, 2] do
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]) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - todesking