multimethod 0.0.1 → 0.1.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.
Files changed (53) hide show
  1. data/Manifest.txt +21 -54
  2. data/Rakefile +19 -2
  3. data/Releases.txt +7 -0
  4. data/lib/multimethod.rb +1 -0
  5. data/lib/multimethod/core_extensions.rb +6 -1
  6. data/lib/multimethod/method.rb +43 -185
  7. data/lib/multimethod/multimethod.rb +58 -3
  8. data/lib/multimethod/multimethod_version.rb +6 -0
  9. data/lib/multimethod/parameter.rb +124 -17
  10. data/lib/multimethod/signature.rb +322 -0
  11. data/lib/multimethod/table.rb +75 -30
  12. data/test/add_remove_test.rb +99 -0
  13. data/test/method_test.rb +2 -67
  14. data/test/multimethod_test.rb +18 -16
  15. data/test/signature_test.rb +190 -0
  16. metadata +23 -56
  17. data/.svn/README.txt +0 -2
  18. data/.svn/empty-file +0 -0
  19. data/.svn/entries +0 -68
  20. data/.svn/format +0 -1
  21. data/.svn/text-base/ChangeLog.svn-base +0 -3
  22. data/.svn/text-base/Manifest.txt.svn-base +0 -54
  23. data/.svn/text-base/README.txt.svn-base +0 -40
  24. data/.svn/text-base/Rakefile.svn-base +0 -132
  25. data/.svn/text-base/Releases.txt.svn-base +0 -7
  26. data/examples/.svn/README.txt +0 -2
  27. data/examples/.svn/empty-file +0 -0
  28. data/examples/.svn/entries +0 -22
  29. data/examples/.svn/format +0 -1
  30. data/examples/.svn/props/ex1.rb.svn-work +0 -5
  31. data/lib/.svn/README.txt +0 -2
  32. data/lib/.svn/empty-file +0 -0
  33. data/lib/.svn/entries +0 -24
  34. data/lib/.svn/format +0 -1
  35. data/lib/.svn/text-base/multimethod.rb.svn-base +0 -8
  36. data/lib/multimethod/.svn/README.txt +0 -2
  37. data/lib/multimethod/.svn/empty-file +0 -0
  38. data/lib/multimethod/.svn/entries +0 -53
  39. data/lib/multimethod/.svn/format +0 -1
  40. data/lib/multimethod/.svn/text-base/core_extensions.rb.svn-base +0 -38
  41. data/lib/multimethod/.svn/text-base/method.rb.svn-base +0 -232
  42. data/lib/multimethod/.svn/text-base/multimethod.rb.svn-base +0 -171
  43. data/lib/multimethod/.svn/text-base/parameter.rb.svn-base +0 -64
  44. data/lib/multimethod/.svn/text-base/table.rb.svn-base +0 -99
  45. data/test/.svn/README.txt +0 -2
  46. data/test/.svn/empty-file +0 -0
  47. data/test/.svn/entries +0 -53
  48. data/test/.svn/format +0 -1
  49. data/test/.svn/text-base/method_test.rb.svn-base +0 -89
  50. data/test/.svn/text-base/multimethod_test.rb.svn-base +0 -92
  51. data/test/.svn/text-base/parameter_test.rb.svn-base +0 -31
  52. data/test/.svn/text-base/test_base.rb.svn-base +0 -25
  53. data/test/.svn/text-base/usage_test.rb.svn-base +0 -146
@@ -0,0 +1,6 @@
1
+ # DO NOT EDIT
2
+ # This file is auto-generated by build scripts.
3
+ # See: rake update_version
4
+ module Multimethod
5
+ MultimethodVersion = '0.1.0'
6
+ end
@@ -1,5 +1,7 @@
1
1
  module Multimethod
2
2
  class Parameter
3
+ include Comparable
4
+
3
5
  RESTARG_SCORE = 9999
4
6
 
5
7
  attr_accessor :name
@@ -8,21 +10,123 @@ module Multimethod
8
10
  attr_accessor :default
9
11
  attr_accessor :restarg
10
12
 
11
- attr_accessor :method
13
+ attr_accessor :signature
14
+ attr_accessor :verbose
12
15
 
13
- def initialize(name, type = nil, default = nil, restarg = false)
14
- # $stderr.puts "initialize(#{name.inspect}, #{type}, #{restarg.inspect})"
15
- name = name.to_s
16
- if name.sub!(/^\*/, '')
17
- restarg = true
18
- end
16
+ def initialize(name = nil, type = nil, default = nil, restarg = false)
17
+ # $stderr.puts "initialize(#{name.inspect}, #{type}, #{default.inspect}, #{restarg.inspect})"
18
+ if name
19
+ # Default type if name is specified
20
+ type ||= Kernel
21
+ end
19
22
 
20
- name = name.intern unless name.kind_of?(Symbol)
21
- @name = name
22
23
  @i = nil
23
- @type = type || Kernel
24
+ @type = type
24
25
  @default = default
25
26
  @restarg = restarg
27
+ @verbose = false
28
+
29
+ self.name = name # may affect @restarg
30
+
31
+ @signature = nil
32
+ end
33
+
34
+
35
+ def name=(name)
36
+ if name
37
+ name = name.to_s
38
+ if name.sub!(/\A\*/, '')
39
+ @restarg = true
40
+ end
41
+
42
+ name = name.intern
43
+ end
44
+ @name = name
45
+ end
46
+
47
+
48
+ def <=>(p)
49
+ x = @type <=> p.type
50
+ x = ! @restarg == ! p.restarg ? 0 : 1 if x == 0
51
+ x = ! @default == ! p.default ? 0 : 1 if x == 0
52
+ # $stderr.puts "#{to_s} <=> #{p.to_s} => #{x.inspect}"
53
+ x
54
+ end
55
+
56
+
57
+ def scan_string(str, need_names = true)
58
+ str.sub!(/\A\s+/, '')
59
+
60
+ $stderr.puts " str=#{str.inspect}" if @verbose
61
+
62
+ if md = /\A(\w+(::\w+)*)\s+(\w+)/s.match(str)
63
+ # $stderr.puts " pre_match=#{md.pre_match.inspect}"
64
+ # $stderr.puts " md[0]=#{md[0].inspect}"
65
+ str = md.post_match
66
+ type = md[1]
67
+ name = md[3]
68
+ elsif md = /\A(\*?\w+)/s.match(str)
69
+ # $stderr.puts " pre_match=#{md.pre_match.inspect}"
70
+ # $stderr.puts " md[0]=#{md[0].inspect}"
71
+ str = md.post_match
72
+ type = nil
73
+ name = md[1]
74
+ else
75
+ raise NameError, "Syntax error in multimethod parameter: expected type and/or name at #{str.inspect}"
76
+ end
77
+
78
+ $stderr.puts " type=#{type.inspect}" if @verbose
79
+ $stderr.puts " name=#{name.inspect}" if @verbose
80
+
81
+ # Parse parameter default.
82
+ if md = /\A\s*=\s*/.match(str)
83
+ str = md.post_match
84
+
85
+ in_paren = 0
86
+ default = ''
87
+ until str.empty?
88
+ # $stderr.puts " default: str=#{str.inspect}"
89
+ # $stderr.puts " default: params=#{parameter_to_s}"
90
+
91
+ if md = /\A(\s+)/s.match(str)
92
+ str = md.post_match
93
+ default = default + md[1]
94
+ end
95
+
96
+ if md = /\A("([^"\\]|\\.)*")/s.match(str)
97
+ str = md.post_match
98
+ default = default + md[1]
99
+ elsif md = /\A('([^'\\]|\\.)*')/s.match(str)
100
+ str = md.post_match
101
+ default = default + md[1]
102
+ elsif md = /\A(\()/.match(str)
103
+ str = md.post_match
104
+ in_paren = in_paren + 1
105
+ default = default + md[1]
106
+ elsif in_paren > 0 && md = /\A(\))/s.match(str)
107
+ str = md.post_match
108
+ in_paren = in_paren - 1
109
+ default = default + md[1]
110
+ elsif md = /\A(\))/s.match(str)
111
+ break
112
+ elsif in_paren == 0 && md = /\A,/s.match(str)
113
+ break
114
+ elsif md = /\A(\w+)/s.match(str)
115
+ str = md.post_match
116
+ default = default + md[1]
117
+ elsif md = /\A(.)/s.match(str)
118
+ str = md.post_match
119
+ default = default + md[1]
120
+ end
121
+ end
122
+ end
123
+
124
+ self.name = name unless @name
125
+ type ||= Kernel
126
+ self.type = type unless @type
127
+ self.default = default unless @default
128
+
129
+ str
26
130
  end
27
131
 
28
132
 
@@ -39,24 +143,27 @@ module Multimethod
39
143
 
40
144
  def type_object
41
145
  if @type.kind_of?(String)
42
- @type = @method.multimethod.table.name_to_object(@type, @method.mod, @method)
146
+ @type = Table.instance.name_to_object(@type,
147
+ @signature.mod,
148
+ @signature.file,
149
+ @signature.line)
43
150
  end
44
151
  @type
45
152
  end
46
153
 
47
154
 
48
155
  def to_s
49
- @restarg ? "*#{@name}" : @name
156
+ "#{@type} #{to_ruby_arg}"
50
157
  end
158
+
51
159
 
52
-
53
- def to_s_long
54
- "#{@type} #{to_ruby}"
160
+ def to_ruby_arg
161
+ "#{to_s_name}#{@default ? ' = ' + @default : ''}"
55
162
  end
56
163
 
57
164
 
58
- def to_ruby
59
- "#{to_s}#{@default ? ' = ' + @default : ''}"
165
+ def to_s_name
166
+ (@restarg ? "*" : '') + (@name.to_s || "_arg_#{@i}")
60
167
  end
61
168
 
62
169
  end # class
@@ -0,0 +1,322 @@
1
+ module Multimethod
2
+
3
+ class Signature
4
+ include Comparable
5
+
6
+ attr_accessor :mod # The Module the signature is bound to.
7
+ attr_accessor :class_method # True if the signature is bound to the class.
8
+ attr_accessor :name # The name of the method signature.
9
+ attr_accessor :parameter # The parameters of the method, self included.
10
+
11
+ attr_accessor :min_args # The minimum # of arguments for this signature
12
+ attr_accessor :max_args # The maximum # of arguments for this signature;
13
+ # May be nil, if restargs
14
+ attr_accessor :restarg # The "*args" parameter or nil
15
+ attr_accessor :default # The first parameter with a default value.
16
+
17
+ attr_accessor :file
18
+ attr_accessor :line
19
+
20
+ attr_accessor :verbose
21
+
22
+ def initialize(*opts)
23
+ opts = Hash[*opts]
24
+
25
+ @mod = opts[:mod]
26
+ @name = opts[:name]
27
+ @class_method = false
28
+ @parameter = [ ]
29
+ @min_args = 0
30
+ @max_args = 0
31
+ @restarg = nil
32
+ @default = nil
33
+
34
+ @multimethod = nil
35
+
36
+ @verbose = nil
37
+
38
+ @score = { }
39
+
40
+ # Handle a string representation of a signature.
41
+ case params = opts[:string]
42
+ when String
43
+ scan_string(params)
44
+ end
45
+
46
+ # Handle other parameters.
47
+ case params = opts[:parameter]
48
+ when Array
49
+ scan_parameters(params)
50
+ when String
51
+ scan_parameters_string(params)
52
+ end
53
+ end
54
+
55
+
56
+ # For sort
57
+ def <=>(s)
58
+ x = @name.to_s <=> s.name.to_s
59
+ x = (! @class_method == ! s.class_method ? 0 : 1) if x == 0
60
+ x = @parameter <=> s.parameter if x == 0
61
+ # $stderr.puts "#{to_s} <=> #{s.to_s} => #{x.inspect}"
62
+ x
63
+ end
64
+
65
+
66
+ def mod
67
+ # THREAD CRITICAL BEGIN
68
+ if @mod && @mod.kind_of?(String)
69
+ @mod = Table.instance.name_to_object(@mod,
70
+ nil,
71
+ file, line)
72
+ end
73
+ # THREAD CRITICAL END
74
+
75
+ @mod
76
+ end
77
+
78
+
79
+ # Scan
80
+ def scan_string(str, need_names = true)
81
+
82
+ str.sub!(/\A\s+/, '')
83
+
84
+ if md = /\A(\w+(::\w+)*)#(\w+)/.match(str)
85
+ str = md.post_match
86
+ @mod = md[1] unless @mod
87
+ @name = md[3]
88
+ elsif md = /\A(\w+(::\w+)*)\.(\w+)/.match(str)
89
+ str = md.post_match
90
+ @mod = md[1] unless @mod
91
+ @class_method = true
92
+ @name = md[3]
93
+ elsif md = /\A((\w+(::\w+)*)\s+)?def\s+(self\.)?(\w+)/.match(str)
94
+ str = md.post_match
95
+ @mod = md[2] unless @mod
96
+ @class_method = ! ! md[4]
97
+ @name = md[5]
98
+ else
99
+ raise NameError, "Syntax error in multimethod signature at #{str.inspect}"
100
+ end
101
+
102
+ # Resolve mod name.
103
+ # FIXME!
104
+
105
+ # Add self parameter.
106
+ add_self
107
+
108
+ # Parse parameter list.
109
+ if md = /\A\(/.match(str)
110
+ str = md.post_match
111
+
112
+ str = scan_parameters_string(str, need_names)
113
+
114
+ $stderr.puts " str=#{str.inspect}" if @verbose
115
+
116
+ if md = /\A\)/.match(str)
117
+ str = md.post_match
118
+ else
119
+ raise NameError, "Syntax error in multimethod parameters expected ')' at #{str.inspect}"
120
+ end
121
+ end
122
+
123
+ str
124
+ end
125
+
126
+
127
+ def scan_parameters_string(str, need_names = true)
128
+ # @verbose = true
129
+
130
+ # Add self parameter at front.
131
+ add_self
132
+
133
+ $stderr.puts "scan_parameters_string(#{str.inspect})" if @verbose
134
+
135
+ until str.empty?
136
+ # Scan parameter
137
+ p = Parameter.new
138
+ p.verbose = @verbose
139
+ str = p.scan_string(str)
140
+ add_parameter(p)
141
+ $stderr.puts " params=#{parameter_to_s}" if @verbose
142
+
143
+ # Parse , or )
144
+ str.sub!(/\A\s+/, '')
145
+ if ! str.empty?
146
+ if md = /\A,/s.match(str)
147
+ str = md.post_match
148
+ elsif md = /\A\)/s.match(str)
149
+ $stderr.puts " DONE: #{to_s}\n Remaining: #{str.inspect}" if @verbose
150
+ break
151
+ else
152
+ raise NameError, "Syntax error in multimethod parameters: expected ',' or ')' at #{str.inspect}"
153
+ end
154
+ end
155
+
156
+ end
157
+
158
+ $stderr.puts "scan_parameters_string(...): DONE: #{to_s}\n Remaining: #{str}" if @verbose
159
+
160
+ str
161
+ end
162
+
163
+
164
+ def scan_parameters(params)
165
+ # Add self parameter at front.
166
+ add_self
167
+
168
+ until params.empty?
169
+ name = nil
170
+ type = nil
171
+ restarg = false
172
+ default = nil
173
+
174
+ if x = params.shift
175
+ case x
176
+ when Class
177
+ type = x
178
+ else
179
+ name = x
180
+ end
181
+ end
182
+
183
+ if ! name && (x = params.shift)
184
+ name = x
185
+ end
186
+
187
+ raise("Parameter name expected, found #{name.inspect}") unless name.kind_of?(String) || name.kind_of?(Symbol)
188
+ raise("Parameter type expected, found #{type.inspect}") unless type.kind_of?(Module) || type.nil?
189
+
190
+ p = Parameter.new(name, type, default)
191
+ add_parameter(p)
192
+ end
193
+
194
+ end
195
+
196
+
197
+ # Add self parameter at front.
198
+ def add_self
199
+ add_parameter(Parameter.new('self', mod)) if @parameter.empty?
200
+ end
201
+
202
+
203
+ def add_parameter(p)
204
+ if p.restarg
205
+ raise("Too many restargs") if @restarg
206
+ @restarg = p
207
+ @max_args = nil
208
+ end
209
+ if p.default
210
+ (@default ||= [ ]).push(p)
211
+ end
212
+
213
+ p.i = @parameter.size
214
+ @parameter.push(p)
215
+ p.signature = self
216
+
217
+ unless p.default || p.restarg
218
+ @min_args = @parameter.size
219
+ end
220
+
221
+ unless @restarg
222
+ @max_args = @parameter.size
223
+ end
224
+ end
225
+
226
+
227
+ def score_cached(args)
228
+ unless x = @score[args]
229
+ x = @score[args] =
230
+ score(args)
231
+ end
232
+ x
233
+ end
234
+
235
+
236
+ def score(args)
237
+
238
+ if @min_args > args.size
239
+ # Not enough args
240
+ score = nil
241
+ elsif @max_args && @max_args < args.size
242
+ # Too many args?
243
+ # $stderr.puts "max_args = #{@max_args}, args.size = #{args.size}"
244
+ score = nil
245
+ else
246
+ # Interpret how close the argument type is to the parameter's type.
247
+ i = -1
248
+ score = args.collect{|a| parameter_at(i = i + 1).score(a)}
249
+
250
+ # Handle score for trailing restargs.
251
+ if @restarg || @default
252
+ while (i = i + 1) < @parameter.size
253
+ # $stderr.puts " Adding score i=#{i}"
254
+ score << parameter_at(i).score(NilClass)
255
+ end
256
+ end
257
+
258
+ # If any argument cannot match, avoid this method.
259
+ score = nil if score.index(nil)
260
+ end
261
+
262
+ # if true || @name =~ /_bar$/
263
+ # $stderr.puts " Method: score #{self.to_s} #{args.inspect} => #{score.inspect}"
264
+ # end
265
+
266
+ score
267
+ end
268
+
269
+
270
+ def parameter_at(i)
271
+ if i >= @parameter.size && @restarg
272
+ @restarg
273
+ else
274
+ @parameter[i]
275
+ end
276
+ end
277
+
278
+
279
+ def to_s(name = nil)
280
+ name ||= @name || '_'
281
+ p = @parameter.clone
282
+ rcvr = p.shift
283
+ "#{rcvr.type.name}##{name}(#{parameter_to_s(p)})"
284
+ end
285
+
286
+
287
+ def parameter_to_s(p = nil)
288
+ p ||= @parameter
289
+ p.collect{|x| x.to_s}.join(', ')
290
+ end
291
+
292
+
293
+ def to_ruby_def(name = nil)
294
+ name ||= @name || '_'
295
+ "def #{name}(#{to_ruby_arg})"
296
+ end
297
+
298
+
299
+ def to_ruby_signature(name = nil)
300
+ name ||= @name || '_'
301
+ p = @parameter.clone
302
+ rcvr = p.shift
303
+ m = mod
304
+ "#{m && m.name}##{name}(#{to_ruby_arg})"
305
+ end
306
+
307
+
308
+ def to_ruby_arg
309
+ x = @parameter.clone
310
+ x.shift
311
+ x.collect{|x| x.to_ruby_arg}.join(', ')
312
+ end
313
+
314
+
315
+ def inspect
316
+ to_s
317
+ end
318
+
319
+ end # class
320
+ end # module
321
+
322
+