filigree 0.3.2 → 0.3.3

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: 508a55d16a4f1321ba4c9042758b1a497bf85827
4
- data.tar.gz: d5ce1b0d135d319bb5a20425bf74ce6d8ade72c9
3
+ metadata.gz: 178a6b1f4c1c7e4fa5572856e84f8e42e61ef423
4
+ data.tar.gz: 0b4d734851d85411da754286fbe7562dd894e8a7
5
5
  SHA512:
6
- metadata.gz: b0683038d43739552cd653c997fd28b8c1f435db879166bc2c50475817528fb8061ef2b5fd11c3c948b8c005e237ab6d81d24963e77d8ca0b60534cb6af8eead
7
- data.tar.gz: 1bc7acb9d54d99a95ff8229c384d57b3929e6ab24b3478fbe6ebeb19888d4339c6f058ca39bbc078db452f568f69c011cb46874d90b27819abbbeb4f0c3c8411
6
+ metadata.gz: 37ed52f1a1780895bfdb255625f9edd5a649c96595395851a1d99ae79ba695e7f45f4e5d443452342ce3aa77db665b24abf9a71c67579233bf806ce3177e41c0
7
+ data.tar.gz: 67ded0ee569137d4dd0b00d99e753af2db406beff41f8a6994b76bfff52521b4b7f08316c4d3ff3128f0180b7b1cea91aa2c1cbd1e7900278a2c46d5e7ae242b
data/README.md CHANGED
@@ -164,7 +164,11 @@ Filigree's implementation of the visitor pattern is built on the pattern matchin
164
164
  ```Ruby
165
165
  class Binary < Struct.new(:x, :y)
166
166
  extend Filigree::Destructurable
167
- include Filigree::Visitor
167
+ include Filigree::Visitable
168
+
169
+ def children
170
+ [x, y]
171
+ end
168
172
 
169
173
  def destructure(_)
170
174
  [x, y]
@@ -1,7 +1,7 @@
1
- # Author: Chris Wailes <chris.wailes@gmail.com>
2
- # Project: Filigree
3
- # Date: 2013/4/19
4
- # Description: An implementation of an AbstractClass module.
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Filigree
3
+ # Date: 2013/4/19
4
+ # Description: An implementation of an AbstractClass module.
5
5
 
6
6
  ############
7
7
  # Requires #
@@ -1,7 +1,7 @@
1
- # Author: Chris Wailes <chris.wailes@gmail.com>
2
- # Project: Filigree
3
- # Date: 2013/05/04
4
- # Description: Pattern matching for Ruby.
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Filigree
3
+ # Date: 2013/05/04
4
+ # Description: Pattern matching for Ruby.
5
5
 
6
6
  ############
7
7
  # Requires #
@@ -22,146 +22,6 @@ require 'filigree/class'
22
22
  # An error that indicates that no pattern matched a given object.
23
23
  class MatchError < RuntimeError; end
24
24
 
25
- ###########
26
- # Methods #
27
- ###########
28
-
29
- # This is an implementation of pattern matching. The objects passed to match
30
- # are tested against the patterns defined inside the match block. The return
31
- # value of `match` will be the result of evaluating the block given to `with`.
32
-
33
- # The most basic pattern is the literal. Here, the object or objects being
34
- # matched will be tested for equality with value passed to `with`. In the
35
- # example below, the call to `match` will return `:one`. Similar to the
36
- # literal pattern is the wildcard pattern `_`. This will match any object.
37
-
38
- # You may also match against variables. This can sometimes conflict with the
39
- # next kind of pattern, which is a binding pattern. Here, the pattern will
40
- # match any object, and then make the object it matched available to the with
41
- # block via an attribute reader. This is accomplished using the method_missing
42
- # callback, so if there is a variable or function with that name you might
43
- # accidentally compare against a variable. To bind to a name that is already
44
- # in scope you can use the {Filigree::MatchEnvironment#Bind} method. In
45
- # addition, class and destructuring pattern results (see bellow) can be bound
46
- # to a variable by using the {Filigree::BasicPattern#as} method.
47
-
48
- # If you wish to match string patterns you may use regular expressions. Any
49
- # object that isn't a string will fail to match against a regular expression.
50
- # If the object being matched is a string then the regular expressions `match?`
51
- # method is used. The result of the regular expression match is available
52
- # inside the with block via the match_data accessor.
53
-
54
- # When a class is used in a pattern it will match any object that is an
55
- # instance of that class. If you wish to compare one regular expression to
56
- # another, or one class to another, you can force the comparison using the
57
- # {Filigree::MatchEnvironment#Literal} method.
58
- #
59
- # Destructuring patterns allow you to match against an instance of a class,
60
- # while simultaneously binding values stored inside the object to variables
61
- # in the context of the with block. A class that is destructurable must
62
- # include the {Filigree::Destructurable} module. You can then destructure an
63
- # object as shown bellow.
64
-
65
- # Both `match` and `with` can take multiple arguments. When this happens, each
66
- # object is paired up with the corresponding pattern. If they all match, then
67
- # the `with` clause matches. In this way you can match against tuples.
68
-
69
- # Any with clause can be given a guard clause by passing a lambda as the last
70
- # argument to `with`. These are evaluated after the pattern is matched, and
71
- # any bindings made in the pattern are available to the guard clause.
72
-
73
- # If you wish to evaluate the same body on matching any of several patterns you
74
- # may list them in order and then specify the body for the last pattern in the
75
- # group.
76
-
77
- # Patterns are evaluated in the order in which they are defined and the first
78
- # pattern to match is the one chosen. You may define helper methods inside the
79
- # match block. They will be re-defined every time the match statement is
80
- # evaluated, so you should move any definitions outside any match calls that
81
- # are being evaluated often.
82
-
83
- # @example The literal pattern
84
- # def foo(n)
85
- # match 1 do
86
- # with(1) { :one }
87
- # with(2) { :two }
88
- # with(_) { :other }
89
- # end
90
- # end
91
- #
92
- # foo(1)
93
-
94
- # @example Matching against variables
95
- # var = 42
96
- # match 42 do
97
- # with(var) { :hoopy }
98
- # with(0) { :zero }
99
- # end
100
-
101
- # @example Binding patterns
102
- # # Returns 42
103
- # match 42 do
104
- # with(x) { x }
105
- # end
106
-
107
- # x = 3
108
- # # Returns 42
109
- # match 42 do
110
- # with(Bind(:x)) { x }
111
- # with(42) { :hoopy }
112
- # end
113
-
114
- # @example Regular expression and class instance pattern
115
- # def matcher(object)
116
- # match object do
117
- # with(/hoopy/) { 42 }
118
- # with(Integer) { 'hoopy' }
119
- # end
120
- # end
121
-
122
- # # Returns 42
123
- # matcher('hoopy')
124
- # # Returns 'hoopy'
125
- # matcher(42)
126
-
127
- # @example Destructuring an object
128
- # class Foo
129
- # include Filigree::Destructurable
130
- # def initialize(a, b)
131
- # @a = a
132
- # @b = b
133
- # end
134
- #
135
- # def destructure(_)
136
- # [@a, @b]
137
- # end
138
- # end
139
-
140
- # # Returns true
141
- # match Foo.new(4, 2) do
142
- # with(Foo.(4, 2)) { true }
143
- # with(_) { false }
144
- # end
145
-
146
- # @example Using guard clauses
147
- # match o do
148
- # with(n, -> { n < 0 }) { :NEG }
149
- # with(0) { :ZERO }
150
- # with(n, -> { n > 0 }) { :POS }
151
- # end
152
- #
153
- # @param [Object] objects Objects to be matched
154
- # @param [Proc] block Block containing with clauses.
155
- #
156
- # @return [Object] Result of evaluating the matched pattern's block
157
- def match(*objects, &block)
158
- me = Filigree::MatchEnvironment.new
159
-
160
- me.instance_exec &block
161
-
162
- me.find_match(objects)
163
- end
164
-
165
25
  #######################
166
26
  # Classes and Modules #
167
27
  #######################
@@ -172,6 +32,142 @@ module Filigree
172
32
  # Methods #
173
33
  ###########
174
34
 
35
+ # This is an implementation of pattern matching. The objects passed to match
36
+ # are tested against the patterns defined inside the match block. The return
37
+ # value of `match` will be the result of evaluating the block given to `with`.
38
+
39
+ # The most basic pattern is the literal. Here, the object or objects being
40
+ # matched will be tested for equality with value passed to `with`. In the
41
+ # example below, the call to `match` will return `:one`. Similar to the
42
+ # literal pattern is the wildcard pattern `_`. This will match any object.
43
+
44
+ # You may also match against variables. This can sometimes conflict with the
45
+ # next kind of pattern, which is a binding pattern. Here, the pattern will
46
+ # match any object, and then make the object it matched available to the with
47
+ # block via an attribute reader. This is accomplished using the method_missing
48
+ # callback, so if there is a variable or function with that name you might
49
+ # accidentally compare against a variable. To bind to a name that is already
50
+ # in scope you can use the {Filigree::MatchEnvironment#Bind} method. In
51
+ # addition, class and destructuring pattern results (see bellow) can be bound
52
+ # to a variable by using the {Filigree::BasicPattern#as} method.
53
+
54
+ # If you wish to match string patterns you may use regular expressions. Any
55
+ # object that isn't a string will fail to match against a regular expression.
56
+ # If the object being matched is a string then the regular expressions `match?`
57
+ # method is used. The result of the regular expression match is available
58
+ # inside the with block via the match_data accessor.
59
+
60
+ # When a class is used in a pattern it will match any object that is an
61
+ # instance of that class. If you wish to compare one regular expression to
62
+ # another, or one class to another, you can force the comparison using the
63
+ # {Filigree::MatchEnvironment#Literal} method.
64
+ #
65
+ # Destructuring patterns allow you to match against an instance of a class,
66
+ # while simultaneously binding values stored inside the object to variables
67
+ # in the context of the with block. A class that is destructurable must
68
+ # include the {Filigree::Destructurable} module. You can then destructure an
69
+ # object as shown bellow.
70
+
71
+ # Both `match` and `with` can take multiple arguments. When this happens, each
72
+ # object is paired up with the corresponding pattern. If they all match, then
73
+ # the `with` clause matches. In this way you can match against tuples.
74
+
75
+ # Any with clause can be given a guard clause by passing a lambda as the last
76
+ # argument to `with`. These are evaluated after the pattern is matched, and
77
+ # any bindings made in the pattern are available to the guard clause.
78
+
79
+ # If you wish to evaluate the same body on matching any of several patterns you
80
+ # may list them in order and then specify the body for the last pattern in the
81
+ # group.
82
+
83
+ # Patterns are evaluated in the order in which they are defined and the first
84
+ # pattern to match is the one chosen. You may define helper methods inside the
85
+ # match block. They will be re-defined every time the match statement is
86
+ # evaluated, so you should move any definitions outside any match calls that
87
+ # are being evaluated often.
88
+
89
+ # @example The literal pattern
90
+ # def foo(n)
91
+ # match 1 do
92
+ # with(1) { :one }
93
+ # with(2) { :two }
94
+ # with(_) { :other }
95
+ # end
96
+ # end
97
+ #
98
+ # foo(1)
99
+
100
+ # @example Matching against variables
101
+ # var = 42
102
+ # match 42 do
103
+ # with(var) { :hoopy }
104
+ # with(0) { :zero }
105
+ # end
106
+
107
+ # @example Binding patterns
108
+ # # Returns 42
109
+ # match 42 do
110
+ # with(x) { x }
111
+ # end
112
+
113
+ # x = 3
114
+ # # Returns 42
115
+ # match 42 do
116
+ # with(Bind(:x)) { x }
117
+ # with(42) { :hoopy }
118
+ # end
119
+
120
+ # @example Regular expression and class instance pattern
121
+ # def matcher(object)
122
+ # match object do
123
+ # with(/hoopy/) { 42 }
124
+ # with(Integer) { 'hoopy' }
125
+ # end
126
+ # end
127
+
128
+ # # Returns 42
129
+ # matcher('hoopy')
130
+ # # Returns 'hoopy'
131
+ # matcher(42)
132
+
133
+ # @example Destructuring an object
134
+ # class Foo
135
+ # include Filigree::Destructurable
136
+ # def initialize(a, b)
137
+ # @a = a
138
+ # @b = b
139
+ # end
140
+ #
141
+ # def destructure(_)
142
+ # [@a, @b]
143
+ # end
144
+ # end
145
+
146
+ # # Returns true
147
+ # match Foo.new(4, 2) do
148
+ # with(Foo.(4, 2)) { true }
149
+ # with(_) { false }
150
+ # end
151
+
152
+ # @example Using guard clauses
153
+ # match o do
154
+ # with(n, -> { n < 0 }) { :NEG }
155
+ # with(0) { :ZERO }
156
+ # with(n, -> { n > 0 }) { :POS }
157
+ # end
158
+ #
159
+ # @param [Object] objects Objects to be matched
160
+ # @param [Proc] block Block containing with clauses.
161
+ #
162
+ # @return [Object] Result of evaluating the matched pattern's block
163
+ def match(*objects, &block)
164
+ me = Filigree::MatchEnvironment.new
165
+
166
+ me.instance_exec &block
167
+
168
+ me.find_match(objects)
169
+ end
170
+
175
171
  # Wrap non-pattern objects in pattern objects so they can all be treated
176
172
  # in the same way during pattern sorting and matching.
177
173
  #
@@ -1,8 +1,8 @@
1
- # Author: Chris Wailes <chris.wailes@gmail.com>
2
- # Project: Filigree
3
- # Date: 2013/4/19
4
- # Description: This file specifies the version number of Filigree.
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Filigree
3
+ # Date: 2013/4/19
4
+ # Description: This file specifies the version number of Filigree.
5
5
 
6
6
  module Filigree
7
- VERSION = '0.3.2'
7
+ VERSION = '0.3.3'
8
8
  end
@@ -32,7 +32,7 @@ module Filigree
32
32
  #
33
33
  # @param [Object] objects Objects to pattern match.
34
34
  #
35
- # @return [Object] Result of calling the matched pattern's block
35
+ # @return [Object, MatchError] Result of calling the matched pattern's block or MatchError if nothing was found
36
36
  #
37
37
  # @raise [MatchError] Raised when no matching pattern is found and
38
38
  # strict matching is enabled.
@@ -58,7 +58,7 @@ module Filigree
58
58
  # If we didn't find anything we raise a MatchError.
59
59
  raise MatchError
60
60
  else
61
- nil
61
+ MatchError
62
62
  end
63
63
  end
64
64
 
@@ -230,27 +230,37 @@ module Filigree
230
230
  # Visit this object with the provided visitor in pre-, post-, or
231
231
  # in-order traversal.
232
232
  #
233
- # @param [Visitor] visitor Visitor to call
234
- # @param [:preorder, :inorder, :postorder] method How to visit
233
+ # @param [Visitor] visitor Visitor to call
234
+ # @param [:preorder, :inorder, :postorder, :downup] method How to visit
235
235
  #
236
- # @return [void]
236
+ # @return [Boolean] If a pattern matched or not
237
237
  def visit(visitor, method = :preorder)
238
238
  case method
239
239
  when :preorder
240
- visitor.visit(self)
241
- children.flatten.compact.each { |child| child.visit(visitor, :preorder) }
240
+ res = (visitor.visit(self) != MatchError)
241
+ children.flatten.compact.inject(false) { |mod, child| child.visit(visitor, :preorder) || mod } || res
242
242
 
243
243
  when :inorder
244
+ mod = false
244
245
  nodes = [self]
245
246
 
247
+ # Not all Ruby implementations support modifying arrays while
248
+ # you are iterating over them.
246
249
  while node = nodes.shift
247
250
  nodes += node.children.flatten.compact
248
- visitor.visit(node)
251
+ mod = visitor.visit(node) || mod
249
252
  end
250
253
 
254
+ mod
255
+
251
256
  when :postorder
252
- children.flatten.compact.each { |child| child.visit(visitor, :postorder) }
253
- visitor.visit(self)
257
+ res = children.flatten.compact.inject(false) { |mod, child| child.visit(visitor, :postorder) || mod }
258
+ (visitor.visit(self) != MatchError) || res
259
+
260
+ when :downup
261
+ res = (visitor.visit(self) != MatchError)
262
+ res = children.flatten.compact.inject(false) { |mod, child| child.visit(visitor, :downup) || mod } || res
263
+ (visitor.visit(self) != MatchError) || res
254
264
  end
255
265
  end
256
266
  end
@@ -1,7 +1,7 @@
1
- # Author: Chris Wailes <chris.wailes@gmail.com>
2
- # Project: Filigree
3
- # Date: 2013/05/04
4
- # Description: Test cases for the match method.
1
+ # Author: Chris Wailes <chris.wailes@gmail.com>
2
+ # Project: Filigree
3
+ # Date: 2013/05/04
4
+ # Description: Test cases for the match method.
5
5
 
6
6
  ############
7
7
  # Requires #
@@ -19,6 +19,8 @@ require 'filigree/match'
19
19
 
20
20
  class MatchTester < Minitest::Test
21
21
 
22
+ include Filigree
23
+
22
24
  ####################
23
25
  # Internal Classes #
24
26
  ####################
@@ -230,7 +230,7 @@ class VisitorTester < Minitest::Test
230
230
  def test_nonstrict_matching
231
231
  nsmv = NonStrictMatchVisitor.new
232
232
 
233
- assert_nil(nsmv.visit 0)
233
+ assert_equal(MatchError, nsmv.visit(0))
234
234
  end
235
235
 
236
236
  def test_simple_visitor
@@ -305,5 +305,15 @@ class VisitorTester < Minitest::Test
305
305
  tree.visit(nv, :postorder)
306
306
 
307
307
  assert_equal expected, nv.vals
308
+
309
+ # Test down-up
310
+ nv = NodeVisitor.new
311
+ expected = ['F', 'B', 'A', 'A', 'D', 'C', 'C', 'E', 'E', 'D', 'B', 'G', 'I', 'H', 'H', 'I', 'G', 'F']
312
+ tree.visit(nv, :downup)
313
+
314
+ assert_equal expected, nv.vals
315
+
316
+ # Test behaviour whith no match.
317
+ assert(!tree.visit(HelperMethodVisitor.new), 'Visitable object falsely reported a matching.')
308
318
  end
309
319
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filigree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Wailes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-16 00:00:00.000000000 Z
11
+ date: 2015-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler