multimethod 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -10,8 +10,10 @@ module Multimethod
10
10
 
11
11
  @@hook_var = :@@__multimethods
12
12
 
13
+ attr_accessor :method
13
14
  def initialize(*opts)
14
- @method = { }
15
+ @multimethod_by_name = { }
16
+ @multimethod = [ ]
15
17
 
16
18
  # Type name lookup cache
17
19
  @name_to_object = { }
@@ -21,33 +23,74 @@ module Multimethod
21
23
  @@match_def = /^\s*def\s+(\w+)([(](.*)[)])?/
22
24
  # Interface to Multimethod::Module mixin multimethod
23
25
  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
-
26
+ file ||= __FILE__
27
+ line ||= __LINE__
28
+ verbose = nil
29
+ #if body =~ /def bar\(x, y\)/
30
+ # verbose = 1
31
+ #end
32
+
33
+ # Parse the signature from the method body
34
+ signature = Signature.new
35
+ signature.mod = mod
36
+ signature.verbose = verbose
37
+ signature.file = file
38
+ signature.line = line
39
+ new_body = signature.scan_string(body.clone)
40
+
35
41
  # Get our Multimethod for this
36
- mm = lookup_multimethod(name)
42
+ mm = lookup_multimethod(signature.name)
37
43
  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)
44
+ m = mm.new_method_from_signature(signature)
45
+
46
+ # Replace the multimethod signature with a plain Ruby signature.
47
+ new_body = m.to_ruby_def + new_body
48
+
49
+ #if true || m.signature.restarg
50
+ # $stderr.puts "install_method(#{mod}) => #{m.to_ruby_signature}:\n#{new_body}"
51
+ #end
52
+
53
+ # Evaluate the new method body.
54
+ mod.module_eval(new_body, file, line)
55
+ end
56
+
57
+
58
+ def find_multimethod(x)
59
+ case x
60
+ when String
61
+ signature = Signature.new(:string => x)
62
+ when Method
63
+ signature = x.signature
64
+ end
65
+
66
+ x = @multimethod.select{|mm| mm.matches_signature(signature)}
67
+
68
+ x
69
+ end
70
+
43
71
 
44
- # if m.restarg
45
- # $stderr.puts "install_method(#{mod}) => #{m.to_ruby}:\n#{new_body}"
46
- # end
72
+ def find_method(x)
73
+ case x
74
+ when String
75
+ signature = Signature.new(:string => x)
76
+ when Method
77
+ signature = x.signature
78
+ end
79
+
80
+ x = @multimethod.select{|mm| mm.matches_signature(signature)}
81
+ # $stderr.puts "find_method(#{x}) => #{x.inspect}"
82
+ x = x.collect{|mm| mm.find_method(signature)}.flatten
47
83
 
48
- file ||= __FILE__; line ||= __LINE__
49
- m.file = file; m.line = line
50
- mod.module_eval(new_body, file || __FILE__, line || __LINE__)
84
+ # $stderr.puts "find_method(#{x}) => #{x.inspect}"
85
+ x
86
+ end
87
+
88
+
89
+ def remove_method(signature)
90
+ x = find_method(signature)
91
+ raise("Found #{x.size} multimethods: #{x.inspect}") if x.size > 1
92
+ x = x[0]
93
+ x.multimethod.remove_method(x)
51
94
  end
52
95
 
53
96
 
@@ -55,10 +98,11 @@ module Multimethod
55
98
  name = name.to_s
56
99
 
57
100
  # THREAD CRITICAL BEGIN
58
- unless mm = @method[name]
101
+ unless mm = @multimethod_by_name[name]
59
102
  mm = Multimethod.new(name)
60
103
  mm.table = self
61
- @method[name] = mm
104
+ @multimethod_by_name[name] = mm
105
+ @multimethod << mm
62
106
  end
63
107
  # THREAD CRITICAL END
64
108
 
@@ -68,7 +112,7 @@ module Multimethod
68
112
 
69
113
  # Interface to code generated by #install_dispatch.
70
114
  def dispatch(name, rcvr, args)
71
- unless mm = @method[name]
115
+ unless mm = @multimethod_by_name[name]
72
116
  raise NameError, 'No method for multmethod #{name}' unless mm
73
117
  end
74
118
  mm.dispatch(rcvr, args)
@@ -79,15 +123,16 @@ module Multimethod
79
123
  # Support
80
124
  #
81
125
 
82
- def name_to_object(name, scope = nil, method = nil)
126
+ def name_to_object(name, scope = nil, file = nil, line = nil)
83
127
  scope ||= Kernel
84
- # THREAD CRITICAL
128
+ # THREAD CRITICAL BEGIN
85
129
  unless x = (@name_to_object[scope] ||= { })[name]
86
130
  # $stderr.puts " name_to_object(#{name.inspect}) in #{scope}"
87
131
  x =
88
132
  @name_to_object[scope][name] =
89
- scope.module_eval(name, method ? method.file : __FILE__, method ? method.line : __LINE__)
133
+ scope.module_eval(name, file || __FILE__, line || __LINE__)
90
134
  end
135
+ # THREAD CRITICAL END
91
136
 
92
137
  x
93
138
  end
@@ -0,0 +1,99 @@
1
+ require 'test_base'
2
+
3
+
4
+ class AddRemoveA < Object
5
+ end
6
+
7
+ module Multimethod
8
+
9
+ class AddRemoveTest < TestBase
10
+
11
+ def setup
12
+ super
13
+
14
+ AddRemoveA.class_eval {
15
+ multimethod %q{
16
+ def asdfwert(x)
17
+ x = "A#asdfwert(x) : (#{x.class.name})"
18
+ x
19
+ end
20
+ }
21
+
22
+ multimethod %q{
23
+ def asdfwert(AddRemoveA x)
24
+ x = "A#asdfwert(A x) : (#{x.class.name})"
25
+ x
26
+ end
27
+ }
28
+
29
+ }
30
+
31
+ end
32
+
33
+
34
+ def test_find_multimethod
35
+ assert_not_nil mm = Table.instance.find_multimethod("AddRemoveA#asdfwert(x)")
36
+ assert 1, mm.size
37
+ mm = mm[0]
38
+ assert_not_nil mm
39
+ assert_equal 'asdfwert', mm.name
40
+
41
+ mm
42
+ end
43
+
44
+
45
+ def test_remove
46
+ # Check method counts
47
+ assert_method_count(2)
48
+
49
+ # Remove asdfwert(AddRemoveA x)
50
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(AddRemoveA x)")
51
+ assert 1, m.size
52
+ m = m[0]
53
+ assert_not_nil m
54
+ assert_not_nil mm = m.multimethod
55
+ assert_equal 'AddRemoveA#asdfwert(AddRemoveA x)', m.signature.to_s
56
+ assert_equal AddRemoveA, m.signature.mod
57
+ assert_equal AddRemoveA, m.signature.parameter[1].type_object
58
+
59
+ AddRemoveA.remove_multimethod(m)
60
+
61
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(AddRemoveA x)")
62
+ assert 0, m.size
63
+
64
+ assert_method_count(1)
65
+
66
+ # Remove asdfwert(x)
67
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(x)")
68
+ assert 1, m.size
69
+ m = m[0]
70
+ assert_not_nil m
71
+ assert_not_nil mm = m.multimethod
72
+ assert_equal 'AddRemoveA#asdfwert(Kernel x)', m.signature.to_s
73
+ assert_equal AddRemoveA, m.signature.mod
74
+ assert_equal Kernel, m.signature.parameter[1].type_object
75
+
76
+ AddRemoveA.remove_multimethod(m)
77
+
78
+ assert_not_nil m = Table.instance.find_method("AddRemoveA#asdfwert(x)")
79
+ assert 0, m.size
80
+
81
+ assert_method_count(1, 0)
82
+ end
83
+
84
+
85
+ def assert_method_count(impl, dispatch = 1)
86
+ mm = test_find_multimethod
87
+
88
+ methods = AddRemoveA.instance_methods(false).select{|x| x =~ /\d_asdfwert$/}
89
+ assert impl, methods.size
90
+ assert impl, mm.method.size
91
+
92
+ methods = AddRemoveA.instance_methods(false).select{|x| x = 'asdfwert'}
93
+ assert dispatch, methods.size
94
+ end
95
+
96
+
97
+ end # class
98
+
99
+ end # module
@@ -13,74 +13,9 @@ module Multimethod
13
13
  super
14
14
  end
15
15
 
16
- def test_scan_parameter
17
- assert_not_nil m1 = Method.new(Object, :m, [ A, :a, B, :b, :c, '*d' ])
18
16
 
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
17
+ def test_basic
18
+ true
84
19
  end
85
20
 
86
21
  end # class
@@ -10,10 +10,12 @@ module Multimethod
10
10
  class D < B; end
11
11
  class E < A; end
12
12
 
13
+
13
14
  def setup
14
15
  super
15
16
  end
16
17
 
18
+
17
19
  def test_score
18
20
  # Make some argument lists.
19
21
  types = [ A, B, C, D, E ]
@@ -36,26 +38,26 @@ module Multimethod
36
38
 
37
39
  rs = Parameter::RESTARG_SCORE
38
40
 
39
- assert_not_nil m1 = Method.new(Object, :m, [ A, :x, A, :y ])
40
- assert_equal 3, m1.min_args
41
- assert_equal 3, m1.max_args
41
+ assert_not_nil m1 = Method.new(:m_1, Object, :m, [ A, :x, A, :y ])
42
+ assert_equal 3, m1.signature.min_args
43
+ assert_equal 3, m1.signature.max_args
42
44
 
43
- assert_not_nil m2 = Method.new(Object, :m, [ B, :x, A, :y ])
44
- assert_equal 3, m2.min_args
45
- assert_equal 3, m2.max_args
45
+ assert_not_nil m2 = Method.new(:m_2, Object, :m, [ B, :x, A, :y ])
46
+ assert_equal 3, m2.signature.min_args
47
+ assert_equal 3, m2.signature.max_args
46
48
 
47
- assert_not_nil m3 = Method.new(Object, :m, [ C, :x, '*rest' ])
48
- assert_equal 2, m3.min_args
49
- assert_equal nil, m3.max_args
50
- assert_not_nil m3.restarg
49
+ assert_not_nil m3 = Method.new(:m_3, Object, :m, [ C, :x, '*rest' ])
50
+ assert_equal 2, m3.signature.min_args
51
+ assert_equal nil, m3.signature.max_args
52
+ assert_not_nil m3.signature.restarg
51
53
 
52
- assert_not_nil m4 = Method.new(Object, :m, [ D, :x, :y ])
53
- assert_equal 3, m4.min_args
54
- assert_equal 3, m4.max_args
54
+ assert_not_nil m4 = Method.new(:m_4, Object, :m, [ D, :x, :y ])
55
+ assert_equal 3, m4.signature.min_args
56
+ assert_equal 3, m4.signature.max_args
55
57
 
56
- assert_not_nil m5 = Method.new(Object, :m, [ E, :y, :y ])
57
- assert_equal 3, m4.min_args
58
- assert_equal 3, m4.max_args
58
+ assert_not_nil m5 = Method.new(:m_5, Object, :m, [ E, :y, :y ])
59
+ assert_equal 3, m4.signature.min_args
60
+ assert_equal 3, m4.signature.max_args
59
61
 
60
62
  # 5 == 1.class.ancestors.index(Object)
61
63
  assert_equal [ 5, 0, 0 ] , m1.score(c[:a_a])
@@ -0,0 +1,190 @@
1
+ require 'test_base'
2
+
3
+ module Multimethod
4
+
5
+ class SignatureTest < TestBase
6
+
7
+ class A < Object; end
8
+ class B < A; end
9
+ class C < Object; end
10
+ class D < B; end
11
+
12
+
13
+ def setup
14
+ super
15
+ end
16
+
17
+
18
+ def test_scan_parameter
19
+ assert_not_nil m1 = Signature.new(:mod => Object,
20
+ :name => :m,
21
+ :parameter => [ A, :a, B, :b, :c, '*d' ])
22
+
23
+ assert_equal 5, m1.parameter.size
24
+
25
+ i = -1
26
+
27
+ i = i + 1
28
+ assert_equal :self, m1.parameter[i].name
29
+ assert_equal Object, m1.parameter[i].type
30
+ assert ! m1.parameter[i].restarg
31
+ assert ! m1.parameter[i].default
32
+
33
+ i = i + 1
34
+ assert_equal :a, m1.parameter[i].name
35
+ assert_equal A, m1.parameter[i].type
36
+ assert ! m1.parameter[i].restarg
37
+ assert ! m1.parameter[i].default
38
+
39
+ i = i + 1
40
+ assert_equal :b, m1.parameter[i].name
41
+ assert_equal B, m1.parameter[i].type
42
+ assert ! m1.parameter[i].restarg
43
+ assert ! m1.parameter[i].default
44
+
45
+ i = i + 1
46
+ assert_equal :c, m1.parameter[i].name
47
+ assert_equal Kernel, m1.parameter[i].type
48
+ assert ! m1.parameter[i].restarg
49
+ assert ! m1.parameter[i].default
50
+
51
+ i = i + 1
52
+ assert_equal :d, m1.parameter[i].name
53
+ assert_equal Kernel, m1.parameter[i].type
54
+ assert m1.parameter[i].restarg
55
+ assert ! m1.parameter[i].default
56
+
57
+ m1
58
+ end
59
+
60
+
61
+ def assert_signature(m1)
62
+ assert_equal 5, m1.parameter.size
63
+
64
+ i = -1
65
+
66
+ i = i + 1
67
+ assert_equal :self, m1.parameter[i].name
68
+ assert_equal Object, m1.parameter[i].type
69
+ assert ! m1.parameter[i].restarg
70
+ assert ! m1.parameter[i].default
71
+
72
+ i = i + 1
73
+ assert_equal :a, m1.parameter[i].name
74
+ assert_equal 'A', m1.parameter[i].type
75
+ assert ! m1.parameter[i].restarg
76
+ assert ! m1.parameter[i].default
77
+
78
+ i = i + 1
79
+ assert_equal :b, m1.parameter[i].name
80
+ assert_equal 'B', m1.parameter[i].type
81
+ assert ! m1.parameter[i].restarg
82
+
83
+ i = i + 1
84
+ assert_equal :c, m1.parameter[i].name
85
+ assert_equal Kernel, m1.parameter[i].type
86
+ assert ! m1.parameter[i].restarg
87
+ assert_equal 'nil', m1.parameter[i].default
88
+
89
+ i = i + 1
90
+ assert_equal :d, m1.parameter[i].name
91
+ assert_equal Kernel, m1.parameter[i].type
92
+ assert m1.parameter[i].restarg
93
+ assert ! m1.parameter[i].default
94
+
95
+ m1
96
+ end
97
+
98
+
99
+ def test_scan_parameter_string
100
+ assert_not_nil m1 = Signature.new(:mod => Object, :name => :m, :parameter => 'A a, B b, c = nil, *d')
101
+ assert_signature m1
102
+ assert ! m1.class_method
103
+ end
104
+
105
+
106
+ def test_scan_string
107
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
108
+
109
+ assert_signature m1
110
+ assert ! m1.class_method
111
+ end
112
+
113
+
114
+ def test_scan_string_2
115
+ assert_not_nil m1 = Signature.new(:string => 'Object def m(A a, B b, c = nil, *d)')
116
+
117
+ assert_signature m1
118
+ assert ! m1.class_method
119
+ end
120
+
121
+
122
+ def test_scan_string_class_method
123
+ assert_not_nil m1 = Signature.new(:string => 'Object.m(A a, B b, c = nil, *d)')
124
+
125
+ assert_signature m1
126
+ assert m1.class_method
127
+ end
128
+
129
+
130
+ def test_scan_string_class_method_2
131
+ assert_not_nil m1 = Signature.new(:string => 'Object def self.m(A a, B b, c = nil, *d)')
132
+
133
+ assert_signature m1
134
+ assert m1.class_method
135
+ end
136
+
137
+
138
+ def test_scan_string_balanced_parens
139
+ assert_not_nil m1 = Signature.new(:string => 'Object.m(A a, B b = call_method(foo(bar, "45,67"), \',\'), c = nil, *d)')
140
+
141
+ assert_signature m1
142
+ assert m1.class_method
143
+ assert_equal :b, m1.parameter[2].name
144
+ assert m1.parameter[2].default
145
+ assert_equal 'call_method(foo(bar, "45,67"), \',\')', m1.parameter[2].default
146
+ end
147
+
148
+ def test_scan_default_at_end
149
+ assert_not_nil m1 = Signature.new(:string => 'Object.m(A a, B b = call_method(foo(bar, "45,67"), \',\'), c = nil)')
150
+
151
+ assert m1.class_method
152
+ assert_equal :c, m1.parameter[3].name
153
+ assert m1.parameter[3].default
154
+ assert_equal 'nil', m1.parameter[3].default
155
+ end
156
+
157
+
158
+ def test_cmp
159
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
160
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
161
+
162
+ assert m1 == m2
163
+
164
+
165
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
166
+ assert_not_nil m2 = Signature.new(:string => 'Object#x(A a, B b, c = nil, *d)')
167
+
168
+ assert m1 != m2
169
+
170
+
171
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
172
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A q, B x, c = nil, *args)')
173
+
174
+ assert m1 == m2
175
+
176
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
177
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A q, B x, c = nil, d)')
178
+
179
+ assert m1 != m2
180
+
181
+ assert_not_nil m1 = Signature.new(:string => 'Object#m(A a, B b, c = nil, *d)')
182
+ assert_not_nil m2 = Signature.new(:string => 'Object#m(A q, B x, c = nil)')
183
+
184
+ assert m1 != m2
185
+ end
186
+
187
+ end # class
188
+
189
+ end # module
190
+