multimethod 0.2.0 → 0.2.1

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.
data/ChangeLog CHANGED
@@ -1,3 +1,7 @@
1
+ 2006-12-01 Kurt A. Stephens <kurt@umleta.com>
2
+
3
+ * Fixed ambigous method testing.
4
+
1
5
  2006-11-29 Kurt A. Stephens <kurt@umleta.com>
2
6
 
3
7
  * Major cleanup and documentation
data/Manifest.txt CHANGED
@@ -13,6 +13,7 @@ lib/multimethod/parameter.rb
13
13
  lib/multimethod/signature.rb
14
14
  lib/multimethod/table.rb
15
15
  test/add_remove_test.rb
16
+ test/ambiguous_test.rb
16
17
  test/method_test.rb
17
18
  test/multimethod_test.rb
18
19
  test/parameter_test.rb
data/Rakefile CHANGED
@@ -142,7 +142,7 @@ end
142
142
 
143
143
  task :make_manifest do
144
144
  open("Manifest.txt", "w") do |f|
145
- f.puts Dir['**/*'].reject { |fn| ! test(?f, fn) || fn =~ /CVS|.svn|(~$)/ }.sort.join("\n") + "\n"
145
+ f.puts Dir['**/*'].reject { |fn| ! test(?f, fn) || fn =~ /CVS|.svn|(~$)|(.gem$)|(^pkg\/)|(^doc\/)/ }.sort.join("\n") + "\n"
146
146
  end
147
147
  end
148
148
 
data/Releases.txt CHANGED
@@ -1,5 +1,12 @@
1
1
  = Multimethod Release History
2
2
 
3
+ == Release 0.2.1: 2006/12/01
4
+
5
+ * Fixed ambiguous method testing.
6
+ * Fixed method name normalization.
7
+ * TODO:
8
+ * Need more documentation: user's guide.
9
+
3
10
  == Release 0.2.0: 2006/11/29
4
11
 
5
12
  * Fixed default parameter scoring.
@@ -2,7 +2,7 @@ module Multimethod
2
2
  # Represents a Multimethod.
3
3
  #
4
4
  # A Multimethod has multiple implementations of a method based on the relative scoring
5
- # of the Methods based on the argument types of the message.
5
+ # of the argument types of the message.
6
6
  #
7
7
  # A Multimethod has a name.
8
8
  #
@@ -17,16 +17,21 @@ module Multimethod
17
17
  # The Multimethod::Table that owns this Multimethod.
18
18
  attr_accessor :table
19
19
 
20
+ # Enable debugging info.
21
+ attr_accessor :debug
22
+
20
23
  # Initialize a new Multimethod.
21
24
  def initialize(name, *opts)
22
25
  raise NameError, "multimethod name not specified" unless name && name.to_s.size > 0
23
-
26
+ @debug = nil
27
+
24
28
  @name = name
25
29
  @name_i = 0
26
30
  @method = [ ]
27
31
  @dispatch = { }
28
32
 
29
33
  @lookup_method = { }
34
+
30
35
  end
31
36
 
32
37
 
@@ -142,10 +147,16 @@ module Multimethod
142
147
  def lookup_method_(args)
143
148
  scores = score_methods(@method, args)
144
149
  if scores.empty?
150
+ # Method not found.
145
151
  result = nil
146
152
  else
153
+ # Check for ambiguous methods.
154
+ if result = ambiguous_methods(scores)
155
+ raise NameError, "Ambiguous methods for multimethod '#{@name}' for (#{args.collect{|x| x.name}.join(', ')}): #{result.collect{|x| x.to_s}.join(', ')}"
156
+ end
157
+
158
+ # Select best scoring method.
147
159
  result = scores[0][1]
148
- raise("Ambigious method") if scores.select{|x| x[0] == result}.size > 1
149
160
  end
150
161
 
151
162
  #if @name.to_s == 'bar'
@@ -158,6 +169,16 @@ module Multimethod
158
169
  end
159
170
 
160
171
 
172
+ def ambiguous_methods(scores)
173
+ # Check for additional methods with the same score as
174
+ # the first method.
175
+ scores = scores.select{|x| x[0] == scores[0][0]}
176
+
177
+ # Map to a list of signatures.
178
+ scores.size > 1 ? scores.collect{|x| x[1].signature} : nil
179
+ end
180
+
181
+
161
182
  # Returns a sorted list of scores and Methods that
162
183
  # match the argument types.
163
184
  def score_methods(meths, args)
@@ -175,7 +196,7 @@ module Multimethod
175
196
  scores.compact!
176
197
  scores.sort!
177
198
 
178
- # $stderr.puts %{ score_methods(#{args.inspect}) => \n#{scores.collect{|x| x.inspect}.join("\n")}}
199
+ $stderr.puts %{ score_methods(#{args.inspect}) => \n#{scores.collect{|x| x.inspect}.join("\n")}} if @debug
179
200
 
180
201
  scores
181
202
  end
@@ -230,6 +251,7 @@ end_eval
230
251
  '&' => 'AND',
231
252
  '!' => 'NOT',
232
253
  '~' => 'TIL',
254
+ '_' => '', # escape '_' as '_'
233
255
  nil => nil
234
256
  };
235
257
  @@name_map.delete(nil)
@@ -238,7 +260,7 @@ end_eval
238
260
 
239
261
  def self.normalize_name(name)
240
262
  name = name.to_s.clone
241
- name.sub!(@@name_rx){|x| "_#{@@name_map[x] || '_'}_"}
263
+ name.gsub!(@@name_rx){|x| "_#{@@name_map[x] || '_'}_"}
242
264
 
243
265
  name.intern
244
266
  end
@@ -2,5 +2,5 @@ module Multimethod
2
2
  # DO NOT EDIT
3
3
  # This file is auto-generated by build scripts.
4
4
  # See: rake update_version
5
- MultimethodVersion = '0.2.0'
5
+ MultimethodVersion = '0.2.1'
6
6
  end
@@ -14,6 +14,11 @@ module Multimethod
14
14
  class Parameter
15
15
  include Comparable
16
16
 
17
+ @@debug = nil
18
+ def self.debug=(x)
19
+ @@debug = x
20
+ end
21
+
17
22
  # The score base used for all Parameters with defaults.
18
23
  DEFAULT_SCORE_BASE = 200
19
24
 
@@ -48,13 +53,10 @@ module Multimethod
48
53
  # Initialize a new Parameter.
49
54
  def initialize(name = nil, type = nil, default = nil, restarg = false)
50
55
  # $stderr.puts "initialize(#{name.inspect}, #{type}, #{default.inspect}, #{restarg.inspect})"
51
- if name
52
- # Default type if name is specified
53
- type ||= Kernel
54
- end
55
56
 
56
57
  @i = nil
57
58
  @type = type
59
+ @type_object = nil
58
60
  @default = default
59
61
  @restarg = restarg
60
62
  @verbose = false
@@ -77,12 +79,13 @@ module Multimethod
77
79
  @name = name
78
80
  end
79
81
 
82
+
80
83
  # Compare two Parameters.
81
- # Only type and restarg are significant.
84
+ # Parameter name is insignificant.
82
85
  def <=>(p)
83
- x = @type <=> p.type
84
- x = ! @restarg == ! p.restarg ? 0 : 1 if x == 0
85
- x = ! @default == ! p.default ? 0 : 1 if x == 0
86
+ x = type_object <=> p.type_object
87
+ x = @restarg == p.restarg ? 0 : 1 if x == 0
88
+ x = @default == p.default ? 0 : 1 if x == 0
86
89
  # $stderr.puts "#{to_s} <=> #{p.to_s} => #{x.inspect}"
87
90
  x
88
91
  end
@@ -90,6 +93,12 @@ module Multimethod
90
93
 
91
94
  # Scan a string for a Parameter specification.
92
95
  def scan_string(str, need_names = true)
96
+ # @verbose ||= @@debug
97
+
98
+ type = nil
99
+ name = nil
100
+ default = nil
101
+
93
102
  str.sub!(/\A\s+/, '')
94
103
 
95
104
  $stderr.puts " str=#{str.inspect}" if @verbose
@@ -153,12 +162,14 @@ module Multimethod
153
162
  str = md.post_match
154
163
  default = default + md[1]
155
164
  end
165
+
166
+ $stderr.puts " default=#{default.inspect}" if @verbose
156
167
  end
157
168
  end
158
169
 
159
170
  self.name = name unless @name
160
- type ||= Kernel
161
171
  self.type = type unless @type
172
+ default = nil if default && default.empty?
162
173
  self.default = default unless @default
163
174
 
164
175
  str
@@ -171,6 +182,9 @@ module Multimethod
171
182
  # to the argument type. A lower distance means a tighter match
172
183
  # of this Parameter.
173
184
  #
185
+ # If +arg+ is nil, this Parameter is being matched as
186
+ # a restarg or a parameter default.
187
+ #
174
188
  # Parameters with restargs or unspecfied default arguments score lower, see RESTARG_SCORE, DEFAULT_SCORE.
175
189
  def score(arg)
176
190
  if @restarg
@@ -195,21 +209,30 @@ module Multimethod
195
209
  end
196
210
 
197
211
 
198
- # Resolves type by name
212
+ # Resolves type name
199
213
  def type_object
200
- if @type.kind_of?(String)
201
- @type = Table.instance.name_to_object(@type,
202
- @signature.mod,
203
- @signature.file,
204
- @signature.line)
214
+ unless @type_object
215
+ case @type
216
+ when String
217
+ @type_object = Table.instance.name_to_object(@type,
218
+ @signature && @signature.mod,
219
+ @signature && @signature.file,
220
+ @signature && @signature.line)
221
+ when Module
222
+ @type_object = @type
223
+ when NilClass
224
+ @type_object = Kernel
225
+ else
226
+ raise("Incorrect parameter type #{@type.inspect}")
227
+ end
205
228
  end
206
- @type
229
+ @type_object
207
230
  end
208
231
 
209
232
 
210
233
  # Returns a String representing this Parameter in a Signature string.
211
234
  def to_s
212
- "#{@type} #{to_ruby_arg}"
235
+ "#{@type}#{@type ? ' ' : ''}#{to_ruby_arg}"
213
236
  end
214
237
 
215
238
 
@@ -19,6 +19,9 @@ module Multimethod
19
19
  # A list of all Multimethod objects.
20
20
  attr_accessor :multimethod
21
21
 
22
+ # Enable debugging info.
23
+ attr_accessor :debug
24
+
22
25
  # Creates a new Table object.
23
26
  def initialize(*opts)
24
27
  @multimethod_by_name = { }
@@ -48,12 +48,13 @@ end
48
48
 
49
49
  # Remove asdfwert(AddRemoveA x)
50
50
  assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(AddRemoveA x)")
51
- assert 1, m.size
51
+ assert_equal 1, m.size
52
52
  m = m[0]
53
53
  assert_not_nil m
54
54
  assert_not_nil mm = m.multimethod
55
55
  assert_equal 'AddRemoveA#asdfwert(AddRemoveA x)', m.signature.to_s
56
56
  assert_equal AddRemoveA, m.signature.mod
57
+ assert_equal 'AddRemoveA', m.signature.parameter[1].type
57
58
  assert_equal AddRemoveA, m.signature.parameter[1].type_object
58
59
 
59
60
  AddRemoveA.remove_multimethod(m)
@@ -69,8 +70,9 @@ end
69
70
  m = m[0]
70
71
  assert_not_nil m
71
72
  assert_not_nil mm = m.multimethod
72
- assert_equal 'AddRemoveA#asdfwert(Kernel x)', m.signature.to_s
73
+ assert_equal 'AddRemoveA#asdfwert(x)', m.signature.to_s
73
74
  assert_equal AddRemoveA, m.signature.mod
75
+ assert_equal nil, m.signature.parameter[1].type
74
76
  assert_equal Kernel, m.signature.parameter[1].type_object
75
77
 
76
78
  AddRemoveA.remove_multimethod(m)
@@ -0,0 +1,46 @@
1
+ require 'test_base'
2
+
3
+
4
+ class Amb < Object
5
+
6
+ multimethod %q{
7
+ def amb(x = nil)
8
+ x = "Amb#amb(x = nil) : (#{x.class.name})"
9
+ x
10
+ end
11
+ }
12
+
13
+ multimethod %q{
14
+ def amb(Amb x = nil)
15
+ x = "Amb#amb(Amb x = nil) : (#{x.class.name})"
16
+ x
17
+ end
18
+ }
19
+
20
+ end
21
+
22
+
23
+ module Multimethod
24
+
25
+ class AmbiguousTest < TestBase
26
+
27
+ def setup
28
+ super
29
+ end
30
+
31
+ def test_call
32
+ assert_not_nil mm = Table.instance.lookup_multimethod(:amb)
33
+ # mm.debug = 1
34
+
35
+ a = Amb.new
36
+ assert_equal "Amb#amb(x = nil) : (Fixnum)", a.amb(1)
37
+ assert_equal "Amb#amb(Amb x = nil) : (Amb)", a.amb(a)
38
+ assert_raise(NameError) { a.amb() }
39
+
40
+ # mm.debug = nil
41
+ end
42
+
43
+
44
+ end # class
45
+
46
+ end # module
@@ -26,6 +26,55 @@ module Multimethod
26
26
  assert_equal nil, pB.score(C)
27
27
  assert_equal 1, pB.score(D)
28
28
  end
29
+
30
+
31
+ def test_parse
32
+ p2 = parse_parameter("Kernel y = nil")
33
+ assert_equal 'Kernel', p2.type
34
+ assert_equal :y, p2.name
35
+ assert_equal 'nil', p2.default
36
+ assert ! p2.restarg
37
+
38
+ p2
39
+ end
40
+
41
+
42
+ def test_equal
43
+ p1 = parse_parameter("x")
44
+ p2 = parse_parameter("x")
45
+ assert p1 == p2
46
+
47
+ p1 = parse_parameter("x")
48
+ p2 = parse_parameter("y")
49
+ assert p1 == p2
50
+
51
+ p1 = parse_parameter("x")
52
+ assert_equal Kernel, p1.type_object
53
+ p2 = parse_parameter("Kernel y")
54
+ assert_equal Kernel, p2.type_object
55
+ assert p1 == p2
56
+
57
+ p1 = parse_parameter("Kernel x")
58
+ p2 = parse_parameter("Kernel y")
59
+ assert p1 == p2
60
+
61
+ p1 = parse_parameter("Kernel x")
62
+ assert_equal nil, p1.default
63
+ p2 = parse_parameter("Kernel y = nil")
64
+ assert_equal 'nil', p2.default
65
+ assert p1 != p2
66
+
67
+ end
68
+
69
+
70
+ def parse_parameter(x)
71
+ p1 = Parameter.new
72
+ # p1.verbose = 1
73
+ x = p1.scan_string(x)
74
+ assert_equal 0, x.size
75
+ p1
76
+ end
77
+
29
78
  end # class
30
79
 
31
80
  end # module
@@ -44,13 +44,15 @@ module Multimethod
44
44
 
45
45
  i = i + 1
46
46
  assert_equal :c, m1.parameter[i].name
47
- assert_equal Kernel, m1.parameter[i].type
47
+ assert_equal nil, m1.parameter[i].type
48
+ assert_equal Kernel, m1.parameter[i].type_object
48
49
  assert ! m1.parameter[i].restarg
49
50
  assert ! m1.parameter[i].default
50
51
 
51
52
  i = i + 1
52
53
  assert_equal :d, m1.parameter[i].name
53
- assert_equal Kernel, m1.parameter[i].type
54
+ assert_equal nil, m1.parameter[i].type
55
+ assert_equal Kernel, m1.parameter[i].type_object
54
56
  assert m1.parameter[i].restarg
55
57
  assert ! m1.parameter[i].default
56
58
 
@@ -82,13 +84,15 @@ module Multimethod
82
84
 
83
85
  i = i + 1
84
86
  assert_equal :c, m1.parameter[i].name
85
- assert_equal Kernel, m1.parameter[i].type
87
+ assert_equal nil, m1.parameter[i].type
88
+ assert_equal Kernel, m1.parameter[i].type_object
86
89
  assert ! m1.parameter[i].restarg
87
90
  assert_equal 'nil', m1.parameter[i].default
88
91
 
89
92
  i = i + 1
90
93
  assert_equal :d, m1.parameter[i].name
91
- assert_equal Kernel, m1.parameter[i].type
94
+ assert_equal nil, m1.parameter[i].type
95
+ assert_equal Kernel, m1.parameter[i].type_object
92
96
  assert m1.parameter[i].restarg
93
97
  assert ! m1.parameter[i].default
94
98
 
data/test/test_base.rb CHANGED
@@ -13,6 +13,19 @@ module Multimethod
13
13
  assert true
14
14
  end
15
15
 
16
+ def test_normalize_name
17
+ assert_equal :foo , norm(:foo)
18
+ assert_equal :foo__bar , norm('foo_bar')
19
+ assert_equal :foo__bar__baz , norm('foo_bar_baz')
20
+ assert_equal :foo_ADD_ , norm('foo+')
21
+ assert_equal :foo_ADD__ADD_ , norm('foo++')
22
+ assert_equal :foo_EQ__EQ_ , norm('foo==')
23
+ end
24
+
25
+ def norm(x)
26
+ Multimethod.normalize_name(x)
27
+ end
28
+
16
29
  # Helpers.
17
30
  def assert_equal_float(x, y, eps = 1.0e-8)
18
31
  d = (x * eps).abs
@@ -20,6 +33,7 @@ module Multimethod
20
33
  assert y <= (x + d)
21
34
  end
22
35
 
36
+
23
37
  end # class
24
38
 
25
39
  end # module
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: multimethod
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2006-11-29 00:00:00 -05:00
6
+ version: 0.2.1
7
+ date: 2006-12-01 00:00:00 -05:00
8
8
  summary: "Supports Multimethod dispatching. For more details, see: http://multimethod.rubyforge.org/files/lib/multimethod_rb.html http://multimethod.rubyforge.org/files/README.txt http://multimethod.rubyforge.org/"
9
9
  require_paths:
10
10
  - lib
@@ -45,6 +45,7 @@ files:
45
45
  - lib/multimethod/signature.rb
46
46
  - lib/multimethod/table.rb
47
47
  - test/add_remove_test.rb
48
+ - test/ambiguous_test.rb
48
49
  - test/method_test.rb
49
50
  - test/multimethod_test.rb
50
51
  - test/parameter_test.rb