multimethod 0.0.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.
Files changed (55) hide show
  1. data/.svn/README.txt +2 -0
  2. data/.svn/empty-file +0 -0
  3. data/.svn/entries +68 -0
  4. data/.svn/format +1 -0
  5. data/.svn/text-base/ChangeLog.svn-base +3 -0
  6. data/.svn/text-base/Manifest.txt.svn-base +54 -0
  7. data/.svn/text-base/README.txt.svn-base +40 -0
  8. data/.svn/text-base/Rakefile.svn-base +132 -0
  9. data/.svn/text-base/Releases.txt.svn-base +7 -0
  10. data/ChangeLog +3 -0
  11. data/Manifest.txt +54 -0
  12. data/README.txt +40 -0
  13. data/Rakefile +132 -0
  14. data/Releases.txt +7 -0
  15. data/examples/.svn/README.txt +2 -0
  16. data/examples/.svn/empty-file +0 -0
  17. data/examples/.svn/entries +22 -0
  18. data/examples/.svn/format +1 -0
  19. data/examples/.svn/props/ex1.rb.svn-work +5 -0
  20. data/examples/ex1.rb +26 -0
  21. data/lib/.svn/README.txt +2 -0
  22. data/lib/.svn/empty-file +0 -0
  23. data/lib/.svn/entries +24 -0
  24. data/lib/.svn/format +1 -0
  25. data/lib/.svn/text-base/multimethod.rb.svn-base +8 -0
  26. data/lib/multimethod.rb +8 -0
  27. data/lib/multimethod/.svn/README.txt +2 -0
  28. data/lib/multimethod/.svn/empty-file +0 -0
  29. data/lib/multimethod/.svn/entries +53 -0
  30. data/lib/multimethod/.svn/format +1 -0
  31. data/lib/multimethod/.svn/text-base/core_extensions.rb.svn-base +38 -0
  32. data/lib/multimethod/.svn/text-base/method.rb.svn-base +232 -0
  33. data/lib/multimethod/.svn/text-base/multimethod.rb.svn-base +171 -0
  34. data/lib/multimethod/.svn/text-base/parameter.rb.svn-base +64 -0
  35. data/lib/multimethod/.svn/text-base/table.rb.svn-base +99 -0
  36. data/lib/multimethod/core_extensions.rb +38 -0
  37. data/lib/multimethod/method.rb +232 -0
  38. data/lib/multimethod/multimethod.rb +171 -0
  39. data/lib/multimethod/parameter.rb +64 -0
  40. data/lib/multimethod/table.rb +99 -0
  41. data/test/.svn/README.txt +2 -0
  42. data/test/.svn/empty-file +0 -0
  43. data/test/.svn/entries +53 -0
  44. data/test/.svn/format +1 -0
  45. data/test/.svn/text-base/method_test.rb.svn-base +89 -0
  46. data/test/.svn/text-base/multimethod_test.rb.svn-base +92 -0
  47. data/test/.svn/text-base/parameter_test.rb.svn-base +31 -0
  48. data/test/.svn/text-base/test_base.rb.svn-base +25 -0
  49. data/test/.svn/text-base/usage_test.rb.svn-base +146 -0
  50. data/test/method_test.rb +89 -0
  51. data/test/multimethod_test.rb +92 -0
  52. data/test/parameter_test.rb +31 -0
  53. data/test/test_base.rb +25 -0
  54. data/test/usage_test.rb +146 -0
  55. metadata +108 -0
@@ -0,0 +1,171 @@
1
+ module Multimethod
2
+ class Multimethod
3
+
4
+ attr_accessor :name
5
+ attr_accessor :method
6
+ attr_accessor :table
7
+
8
+ def initialize(name, *opts)
9
+ raise NameError, "multimethod name not specified" unless name && name.to_s.size > 0
10
+
11
+ @name = name
12
+ @name_i = 0
13
+ @method = [ ]
14
+ @dispatch = { }
15
+
16
+ @lookup_method = { }
17
+ end
18
+
19
+
20
+ def gensym(name = nil)
21
+ name ||= @name
22
+ "_multimethod_#{@name_i = @name_i + 1}_#{name}"
23
+ end
24
+
25
+
26
+ def new_method(mod, *args)
27
+ m = Method.new(mod, gensym, *args)
28
+ add_method(m)
29
+ m
30
+ end
31
+
32
+
33
+ def add_method(method)
34
+ # THREAD CRITICAL BEGIN
35
+ @method.push(method)
36
+ method.multimethod = self
37
+ @lookup_method = { } # flush cache
38
+ # THREAD CRITICAL END
39
+ end
40
+
41
+
42
+ def dispatch(rcvr, args)
43
+ apply_method(lookup_method(rcvr, args), rcvr, args)
44
+ end
45
+
46
+
47
+ # Interface to Multimethod::Table
48
+ def apply_method(meth, rcvr, args)
49
+ unless meth # && false
50
+ $stderr.puts "Available multimethods for #{rcvr.class.name}##{@name}(#{args}):"
51
+ $stderr.puts " " + @method.sort{|a,b| a.min_args <=> b.min_args }.collect{|x| x.to_s(name)}.join("\n ")
52
+ $stderr.puts "\n"
53
+ end
54
+ raise NameError, "Cannot find multimethod for #{rcvr.class.name}##{@name}(#{args})" unless meth
55
+ rcvr.send(meth.name, *args)
56
+ end
57
+
58
+
59
+ def lookup_method(rcvr, args)
60
+ args = args.clone
61
+ args.unshift(rcvr)
62
+ lookup_method_cached_(args)
63
+ end
64
+
65
+
66
+ def lookup_method_cached_(args)
67
+ args_type = args.collect{|x| x.class}
68
+
69
+ # THREAD CRITICAL BEGIN
70
+ unless result = @lookup_method[args_type]
71
+ result = @lookup_method[args_type] =
72
+ lookup_method_(args_type)
73
+ end
74
+ # THREAD CRITICAL END
75
+
76
+ result
77
+ end
78
+
79
+
80
+ def lookup_method_(args)
81
+ scores = score_methods(@method, args)
82
+ if scores.empty?
83
+ result = nil
84
+ else
85
+ result = scores[0][1]
86
+ raise("Ambigious method") if scores.select{|x| x[0] == result}.size > 1
87
+ end
88
+
89
+ #if @name.to_s == 'bar'
90
+ # $stderr.puts "args = " + args.collect{|x| x.class.name + ' ' + x.to_s}.join(", ")
91
+ # $stderr.puts "scores:\n " + scores.collect{|x| x.inspect}.join("\n ")
92
+ # end
93
+
94
+
95
+ result
96
+ end
97
+
98
+ def score_methods(meths, args)
99
+ scores = meths.collect do |meth|
100
+ score = meth.score_cached(args)
101
+ if score
102
+ score = [ score, meth ]
103
+ else
104
+ score = nil
105
+ end
106
+
107
+ score
108
+ end
109
+
110
+ scores.compact!
111
+ scores.sort!
112
+
113
+ # $stderr.puts "score_methods(#{args.inspect}) => \n#{scores.inspect}"
114
+
115
+ scores
116
+ end
117
+
118
+
119
+ def install_dispatch(mod)
120
+ # THREAD CRITICAL BEGIN
121
+ unless @dispatch[mod]
122
+ @dispatch[mod] = true
123
+ # $stderr.puts "install_dispatch(#{name}) into #{mod}\n";
124
+ mod.class_eval(body = <<-"end_eval", __FILE__, __LINE__)
125
+ def #{name}(*args)
126
+ ::#{table.class.name}.instance.dispatch(#{name.inspect}, self, args)
127
+ end
128
+ end_eval
129
+ # $stderr.puts "install_dispatch = #{body}"
130
+ end
131
+ # THREAD CRITICAL END
132
+ end
133
+
134
+
135
+ ##################################################
136
+ # Support
137
+ #
138
+
139
+ @@name_map = {
140
+ '@' => 'AT',
141
+ '=' => 'EQ',
142
+ '<' => 'LT',
143
+ '>' => 'GT',
144
+ '+' => 'ADD',
145
+ '-' => 'SUB',
146
+ '*' => 'MUL',
147
+ '/' => 'DIV',
148
+ '%' => 'MOD',
149
+ '^' => 'XOR',
150
+ '|' => 'OR',
151
+ '&' => 'AND',
152
+ '!' => 'NOT',
153
+ '~' => 'TIL',
154
+ nil => nil
155
+ };
156
+ @@name_map.delete(nil)
157
+
158
+ @@name_rx = Regexp.new('(' + @@name_map.keys.collect{|x| Regexp.quote(x)}.join('|') + ')')
159
+
160
+ def self.normalize_name(name)
161
+ name = name.to_s.clone
162
+ name.sub!(@@name_rx){|x| "_#{@@name_map[x] || '_'}_"}
163
+
164
+ name.intern
165
+ end
166
+
167
+ end # class
168
+
169
+ end # module
170
+
171
+
@@ -0,0 +1,64 @@
1
+ module Multimethod
2
+ class Parameter
3
+ RESTARG_SCORE = 9999
4
+
5
+ attr_accessor :name
6
+ attr_accessor :i
7
+ attr_accessor :type
8
+ attr_accessor :default
9
+ attr_accessor :restarg
10
+
11
+ attr_accessor :method
12
+
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
19
+
20
+ name = name.intern unless name.kind_of?(Symbol)
21
+ @name = name
22
+ @i = nil
23
+ @type = type || Kernel
24
+ @default = default
25
+ @restarg = restarg
26
+ end
27
+
28
+
29
+ def score(arg)
30
+ return RESTARG_SCORE if @restarg
31
+ score = all_types(arg).index(type_object)
32
+ end
33
+
34
+
35
+ def all_types(arg)
36
+ arg.ancestors
37
+ end
38
+
39
+
40
+ def type_object
41
+ if @type.kind_of?(String)
42
+ @type = @method.multimethod.table.name_to_object(@type, @method.mod, @method)
43
+ end
44
+ @type
45
+ end
46
+
47
+
48
+ def to_s
49
+ @restarg ? "*#{@name}" : @name
50
+ end
51
+
52
+
53
+ def to_s_long
54
+ "#{@type} #{to_ruby}"
55
+ end
56
+
57
+
58
+ def to_ruby
59
+ "#{to_s}#{@default ? ' = ' + @default : ''}"
60
+ end
61
+
62
+ end # class
63
+ end # module
64
+
@@ -0,0 +1,99 @@
1
+ module Multimethod
2
+ class Table
3
+
4
+ @@instance = nil
5
+ def self.instance
6
+ # THREAD CRITICAL BEGIN
7
+ @@instance ||= self.new
8
+ # TRREAD CRITICAL END
9
+ end
10
+
11
+ @@hook_var = :@@__multimethods
12
+
13
+ def initialize(*opts)
14
+ @method = { }
15
+
16
+ # Type name lookup cache
17
+ @name_to_object = { }
18
+ end
19
+
20
+
21
+ @@match_def = /^\s*def\s+(\w+)([(](.*)[)])?/
22
+ # Interface to Multimethod::Module mixin multimethod
23
+ def install_method(mod, body, file = nil, line = nil)
24
+ name = nil
25
+ params = nil
26
+
27
+ # Parse first line.
28
+ if md = body.match(@@match_def)
29
+ name = md[1]
30
+ params = md[3] || ''
31
+ else
32
+ raise("Cannot determine name(params) from body")
33
+ end
34
+
35
+ # Get our Multimethod for this
36
+ mm = lookup_multimethod(name)
37
+ mm.install_dispatch(mod)
38
+ m = mm.new_method(mod, params)
39
+
40
+ # Evaluate our method.
41
+ new_body = body.clone
42
+ new_body.sub!(@@match_def, m.to_ruby)
43
+
44
+ # if m.restarg
45
+ # $stderr.puts "install_method(#{mod}) => #{m.to_ruby}:\n#{new_body}"
46
+ # end
47
+
48
+ file ||= __FILE__; line ||= __LINE__
49
+ m.file = file; m.line = line
50
+ mod.module_eval(new_body, file || __FILE__, line || __LINE__)
51
+ end
52
+
53
+
54
+ def lookup_multimethod(name)
55
+ name = name.to_s
56
+
57
+ # THREAD CRITICAL BEGIN
58
+ unless mm = @method[name]
59
+ mm = Multimethod.new(name)
60
+ mm.table = self
61
+ @method[name] = mm
62
+ end
63
+ # THREAD CRITICAL END
64
+
65
+ mm
66
+ end
67
+
68
+
69
+ # Interface to code generated by #install_dispatch.
70
+ def dispatch(name, rcvr, args)
71
+ unless mm = @method[name]
72
+ raise NameError, 'No method for multmethod #{name}' unless mm
73
+ end
74
+ mm.dispatch(rcvr, args)
75
+ end
76
+
77
+
78
+ #################################################
79
+ # Support
80
+ #
81
+
82
+ def name_to_object(name, scope = nil, method = nil)
83
+ scope ||= Kernel
84
+ # THREAD CRITICAL
85
+ unless x = (@name_to_object[scope] ||= { })[name]
86
+ # $stderr.puts " name_to_object(#{name.inspect}) in #{scope}"
87
+ x =
88
+ @name_to_object[scope][name] =
89
+ scope.module_eval(name, method ? method.file : __FILE__, method ? method.line : __LINE__)
90
+ end
91
+
92
+ x
93
+ end
94
+
95
+ end # class
96
+
97
+ end # module
98
+
99
+
@@ -0,0 +1,2 @@
1
+ This is a Subversion working copy administrative directory.
2
+ Visit http://subversion.tigris.org/ for more information.
File without changes
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <wc-entries
3
+ xmlns="svn:">
4
+ <entry
5
+ committed-rev="3"
6
+ name=""
7
+ committed-date="2006-11-21T15:59:22.761488Z"
8
+ url="svn+ssh://rubyforge.org/var/svn/multimethod/multimethod/trunk/test"
9
+ last-author="kstephens"
10
+ kind="dir"
11
+ repos="svn+ssh://rubyforge.org/var/svn/multimethod"
12
+ revision="3"/>
13
+ <entry
14
+ committed-rev="3"
15
+ name="method_test.rb"
16
+ text-time="2006-11-21T05:04:48.000000Z"
17
+ committed-date="2006-11-21T15:59:22.761488Z"
18
+ checksum="40b4ca1c1c328339e7380b23522998e5"
19
+ last-author="kstephens"
20
+ kind="file"/>
21
+ <entry
22
+ committed-rev="3"
23
+ name="parameter_test.rb"
24
+ text-time="2006-11-21T07:25:08.000000Z"
25
+ committed-date="2006-11-21T15:59:22.761488Z"
26
+ checksum="6893f4ec384c8ed8b5bede4642783d13"
27
+ last-author="kstephens"
28
+ kind="file"/>
29
+ <entry
30
+ committed-rev="3"
31
+ name="usage_test.rb"
32
+ text-time="2006-11-21T06:51:40.000000Z"
33
+ committed-date="2006-11-21T15:59:22.761488Z"
34
+ checksum="c3b86cfd548e87f6a3abfba9b6e64c54"
35
+ last-author="kstephens"
36
+ kind="file"/>
37
+ <entry
38
+ committed-rev="3"
39
+ name="test_base.rb"
40
+ text-time="2006-11-20T21:58:18.000000Z"
41
+ committed-date="2006-11-21T15:59:22.761488Z"
42
+ checksum="52aa7dfea864715aede8ab3e5aba1ca0"
43
+ last-author="kstephens"
44
+ kind="file"/>
45
+ <entry
46
+ committed-rev="3"
47
+ name="multimethod_test.rb"
48
+ text-time="2006-11-21T07:23:21.000000Z"
49
+ committed-date="2006-11-21T15:59:22.761488Z"
50
+ checksum="4de3fbfd3e551eaecc1b9a550b08a80a"
51
+ last-author="kstephens"
52
+ kind="file"/>
53
+ </wc-entries>
@@ -0,0 +1 @@
1
+ 4
@@ -0,0 +1,89 @@
1
+ require 'test_base'
2
+
3
+ module Multimethod
4
+
5
+ class MethodTest < TestBase
6
+
7
+ class A < Object; end
8
+ class B < A; end
9
+ class C < Object; end
10
+ class D < B; end
11
+
12
+ def setup
13
+ super
14
+ end
15
+
16
+ def test_scan_parameter
17
+ assert_not_nil m1 = Method.new(Object, :m, [ A, :a, B, :b, :c, '*d' ])
18
+
19
+ assert_equal 5, m1.parameter.size
20
+
21
+ i = -1
22
+
23
+ i = i + 1
24
+ assert_equal :self, m1.parameter[i].name
25
+ assert_equal Object, m1.parameter[i].type
26
+ assert ! m1.parameter[i].restarg
27
+
28
+ i = i + 1
29
+ assert_equal :a, m1.parameter[i].name
30
+ assert_equal A, m1.parameter[i].type
31
+ assert ! m1.parameter[i].restarg
32
+
33
+ i = i + 1
34
+ assert_equal :b, m1.parameter[i].name
35
+ assert_equal B, m1.parameter[i].type
36
+ assert ! m1.parameter[i].restarg
37
+
38
+ i = i + 1
39
+ assert_equal :c, m1.parameter[i].name
40
+ assert_equal Kernel, m1.parameter[i].type
41
+ assert ! m1.parameter[i].restarg
42
+
43
+ i = i + 1
44
+ assert_equal :d, m1.parameter[i].name
45
+ assert_equal Kernel, m1.parameter[i].type
46
+ assert m1.parameter[i].restarg
47
+
48
+ m1
49
+ end
50
+
51
+ def test_scan_parameter_string
52
+ assert_not_nil m1 = Method.new(Object, :m, 'A a, B b, c = nil, *d')
53
+
54
+ assert_equal 5, m1.parameter.size
55
+
56
+ i = -1
57
+
58
+ i = i + 1
59
+ assert_equal :self, m1.parameter[i].name
60
+ assert_equal Object, m1.parameter[i].type
61
+ assert ! m1.parameter[i].restarg
62
+
63
+ i = i + 1
64
+ assert_equal :a, m1.parameter[i].name
65
+ assert_equal 'A', m1.parameter[i].type
66
+ assert ! m1.parameter[i].restarg
67
+
68
+ i = i + 1
69
+ assert_equal :b, m1.parameter[i].name
70
+ assert_equal 'B', m1.parameter[i].type
71
+ assert ! m1.parameter[i].restarg
72
+
73
+ i = i + 1
74
+ assert_equal :c, m1.parameter[i].name
75
+ assert_equal Kernel, m1.parameter[i].type
76
+ assert ! m1.parameter[i].restarg
77
+
78
+ i = i + 1
79
+ assert_equal :d, m1.parameter[i].name
80
+ assert_equal Kernel, m1.parameter[i].type
81
+ assert m1.parameter[i].restarg
82
+
83
+ m1
84
+ end
85
+
86
+ end # class
87
+
88
+ end # module
89
+