filigree 0.3.2 → 0.3.3

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