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 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