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 +4 -0
- data/Manifest.txt +1 -0
- data/Rakefile +1 -1
- data/Releases.txt +7 -0
- data/lib/multimethod/multimethod.rb +27 -5
- data/lib/multimethod/multimethod_version.rb +1 -1
- data/lib/multimethod/parameter.rb +40 -17
- data/lib/multimethod/table.rb +3 -0
- data/test/add_remove_test.rb +4 -2
- data/test/ambiguous_test.rb +46 -0
- data/test/parameter_test.rb +49 -0
- data/test/signature_test.rb +8 -4
- data/test/test_base.rb +14 -0
- metadata +3 -2
data/ChangeLog
CHANGED
data/Manifest.txt
CHANGED
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
|
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
|
-
|
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.
|
263
|
+
name.gsub!(@@name_rx){|x| "_#{@@name_map[x] || '_'}_"}
|
242
264
|
|
243
265
|
name.intern
|
244
266
|
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
|
-
#
|
84
|
+
# Parameter name is insignificant.
|
82
85
|
def <=>(p)
|
83
|
-
x =
|
84
|
-
x =
|
85
|
-
x =
|
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
|
212
|
+
# Resolves type name
|
199
213
|
def type_object
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
@
|
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
|
|
data/lib/multimethod/table.rb
CHANGED
data/test/add_remove_test.rb
CHANGED
@@ -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
|
-
|
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(
|
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
|
data/test/parameter_test.rb
CHANGED
@@ -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
|
data/test/signature_test.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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.
|
7
|
-
date: 2006-
|
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
|