RubyToC 1.0.0.4

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/support.rb ADDED
@@ -0,0 +1,317 @@
1
+
2
+ class Environment
3
+
4
+ attr_accessor :env
5
+
6
+ def initialize
7
+ @env = [{}]
8
+ end
9
+
10
+ def depth
11
+ @env.length
12
+ end
13
+
14
+ def add(id, val)
15
+ raise "Adding illegal identifier #{id.inspect}" unless String === id or Symbol === id
16
+ current[id] = val
17
+ end
18
+
19
+ def extend
20
+ @env.unshift({})
21
+ end
22
+
23
+ def unextend
24
+ @env.shift
25
+ end
26
+
27
+ def lookup(id)
28
+
29
+ # HACK: if id is :self, cheat for now until we have full defn remapping
30
+ if id == :self then
31
+ return Type.fucked
32
+ end
33
+
34
+ @env.each do |closure|
35
+ return closure[id] if closure.has_key? id
36
+ end
37
+
38
+ raise NameError, "Unbound var: #{id.inspect} in #{@env.inspect}"
39
+ end
40
+
41
+ def current
42
+ @env.first
43
+ end
44
+
45
+ def scope
46
+ self.extend
47
+ yield
48
+ self.unextend
49
+ end
50
+
51
+ end
52
+
53
+ class FunctionTable
54
+
55
+ def initialize
56
+ @functions = Hash.new do |h,k|
57
+ h[k] = []
58
+ end
59
+ end
60
+
61
+ def cheat(name) # HACK: just here for debugging
62
+ puts "\n# WARNING: FunctionTable.cheat called from #{caller[0]}" if $DEBUG
63
+ @functions[name]
64
+ end
65
+
66
+ def [](name) # HACK: just here for transition
67
+ puts "\n# WARNING: FunctionTable.[] called from #{caller[0]}" if $DEBUG
68
+ @functions[name].first
69
+ end
70
+
71
+ def has_key?(name) # HACK: just here for transition
72
+ puts "\n# WARNING: FunctionTable.has_key? called from #{caller[0]}" if $DEBUG
73
+ @functions.has_key?(name)
74
+ end
75
+
76
+ def add_function(name, type)
77
+ @functions[name] << type
78
+ type
79
+ end
80
+
81
+ def unify(name, type)
82
+ success = false
83
+ @functions[name].each do |o| # unify(type)
84
+ begin
85
+ o.unify type
86
+ success = true
87
+ rescue
88
+ # ignore
89
+ end
90
+ end
91
+ unless success then
92
+ yield(name, type) if block_given?
93
+ end
94
+ type
95
+ end
96
+
97
+ end
98
+
99
+ class FunctionType
100
+
101
+ attr_accessor :receiver_type
102
+ attr_accessor :formal_types
103
+ attr_accessor :return_type
104
+
105
+ def initialize(receiver_type, formal_types, return_type)
106
+ raise "nil not allowed" if formal_types.nil? or return_type.nil?
107
+ @receiver_type = receiver_type
108
+ @formal_types = formal_types
109
+ @return_type = return_type
110
+ end
111
+
112
+ def ==(other)
113
+ return nil unless other.class == self.class
114
+
115
+ return false unless other.receiver_type == self.receiver_type
116
+ return false unless other.return_type == self.return_type
117
+ return false unless other.formal_types == self.formal_types
118
+ return true
119
+ end
120
+
121
+ def unify_components(other)
122
+ raise TypeError, "Unable to unify: different number of args #{self.inspect} vs #{other.inspect}" unless
123
+ @formal_types.length == other.formal_types.length
124
+
125
+ @formal_types.each_with_index do |t, i|
126
+ t.unify other.formal_types[i]
127
+ end
128
+
129
+ @receiver_type.unify other.receiver_type
130
+ @return_type.unify other.return_type
131
+ # rescue RuntimeError # print more complete warning message
132
+ # raise "Unable to unify\n#{self}\nwith\n#{other}"
133
+ end
134
+
135
+ def to_s
136
+ formals = formal_types.map do |t|
137
+ t.inspect
138
+ end
139
+
140
+ "function(#{receiver_type.inspect}, [#{formals.join ', '}], #{return_type.inspect})"
141
+ end
142
+
143
+ end
144
+
145
+ class Handle
146
+
147
+ attr_accessor :contents
148
+
149
+ def initialize(contents)
150
+ @contents = contents
151
+ end
152
+
153
+ def ==(other)
154
+ return nil unless other.class == self.class
155
+ return other.contents == self.contents
156
+ end
157
+
158
+ end
159
+
160
+ class Type
161
+
162
+ # REFACTOR: nuke this
163
+ KNOWN_TYPES = {
164
+ :unknown => "Unknown",
165
+ :unknown_list => "Unknown list",
166
+ :long => "Integer",
167
+ :long_list => "Integer list",
168
+ :str => "String",
169
+ :str_list => "String list",
170
+ :void => "Void",
171
+ :bool => "Bool",
172
+ :bool_list => "Bool list",
173
+ :value => "Value",
174
+ :value_list => "Value list",
175
+ :function => "Function",
176
+ :file => "File",
177
+ :float => "Float",
178
+ :float_list => "Float list",
179
+ :symbol => "Symbol",
180
+ :zclass => "Class",
181
+ :homo => "Homogenous",
182
+ :hetero => "Heterogenous",
183
+ :fucked => "Untranslatable type",
184
+ }
185
+
186
+ TYPES = {}
187
+
188
+ def self.method_missing(type, *args)
189
+ raise "Unknown type Type.#{type}" unless KNOWN_TYPES.has_key?(type)
190
+ case type
191
+ when :unknown then
192
+ return self.new(type)
193
+ when :function then
194
+ if args.size == 2 then
195
+ $stderr.puts "\nWARNING: adding Type.unknown for #{caller[0]}" if $DEBUG
196
+ args.unshift Type.unknown
197
+ end
198
+ return self.new(FunctionType.new(*args))
199
+ else
200
+ if type.to_s =~ /(.*)_list$/ then
201
+ TYPES[type] = self.new($1.intern, true) unless TYPES.has_key?(type)
202
+ return TYPES[type]
203
+ else
204
+ TYPES[type] = self.new(type) unless TYPES.has_key?(type)
205
+ return TYPES[type]
206
+ end
207
+ end
208
+ end
209
+
210
+ def self.unknown_list
211
+ self.new(:unknown, true)
212
+ end
213
+
214
+ attr_accessor :type
215
+ attr_accessor :list
216
+
217
+ def initialize(type, list=false)
218
+ # HACK
219
+ unless KNOWN_TYPES.has_key? type or type.class.name =~ /Type$/ then
220
+ raise "Unknown type Type.new(#{type.inspect})"
221
+ end
222
+ @type = Handle.new type
223
+ @list = list
224
+ end
225
+
226
+ def function?
227
+ not KNOWN_TYPES.has_key? self.type.contents
228
+ end
229
+
230
+ def unknown?
231
+ self.type.contents == :unknown
232
+ end
233
+
234
+ def list?
235
+ @list
236
+ end
237
+
238
+ # REFACTOR: this should be named type, but that'll break code at the moment
239
+ def list_type
240
+ @type.contents
241
+ end
242
+
243
+ def eql?(other)
244
+ return nil unless other.class == self.class
245
+
246
+ other.type == self.type && other.list? == self.list?
247
+ end
248
+
249
+ alias :== :eql?
250
+
251
+ def hash
252
+ type.contents.hash ^ @list.hash
253
+ end
254
+
255
+ def unify(other)
256
+ return other.unify(self) if Array === other
257
+ return self if other == self and (not self.unknown?)
258
+ return self if other.nil?
259
+ if self.unknown? and other.unknown? then
260
+ # link types between unknowns
261
+ self.type = other.type
262
+ self.list = other.list? or self.list? # HACK may need to be tri-state
263
+ elsif self.unknown? then
264
+ # other's type is now my type
265
+ self.type.contents = other.type.contents
266
+ self.list = other.list?
267
+ elsif other.unknown? then
268
+ # my type is now other's type
269
+ other.type.contents = self.type.contents
270
+ other.list = self.list?
271
+ elsif self.function? and other.function? then
272
+ self_fun = self.type.contents
273
+ other_fun = other.type.contents
274
+
275
+ self_fun.unify_components other_fun
276
+ else
277
+ raise TypeError, "Unable to unify #{self.inspect} with #{other.inspect}"
278
+ end
279
+ return self
280
+ end
281
+
282
+ def to_s
283
+ str = "Type.#{self.type.contents}"
284
+ str << "_list" if self.list?
285
+ str
286
+ end
287
+
288
+ def inspect
289
+ to_s
290
+ end unless $DEBUG
291
+
292
+ end
293
+
294
+ ##
295
+ # Unique creates unique variable names.
296
+
297
+ class Unique
298
+
299
+ ##
300
+ # Variable names will be prefixed by +prefix+
301
+
302
+ def initialize(prefix)
303
+ @prefix = prefix
304
+ @curr = 'a'
305
+ end
306
+
307
+ ##
308
+ # Generate a new unique variable name
309
+
310
+ def next
311
+ var = "#{@prefix}_#{@curr}"
312
+ @curr.succ!
313
+ return var
314
+ end
315
+
316
+ end
317
+
data/test_all.rb ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ $TESTING = true
4
+
5
+ Dir.glob("test_*.rb").each do |f|
6
+ require f
7
+ end
8
+
9
+ require 'test/unit'
data/test_extras.rb ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ $TESTING = true
4
+
5
+ require 'type_checker'
6
+ require 'test/unit'
7
+
8
+ class RandomCode # ZenTest SKIP
9
+ def specific_method(x)
10
+ x = 0 # make x and y to be longs
11
+ return c.to_i > 0
12
+ end
13
+
14
+ def generic_method(x)
15
+ specific_method(x)
16
+ end
17
+
18
+ def meth_b(x)
19
+ # nothing to do so we don't hint what x is
20
+ end
21
+
22
+ def meth_a(x)
23
+ meth_b(x)
24
+ end
25
+
26
+ end
27
+
28
+ class TestExtraTypeChecker < Test::Unit::TestCase # ZenTest SKIP
29
+
30
+ def setup
31
+ @parser = ParseTree.new
32
+ @rewriter = Rewriter.new
33
+ @type_checker = TypeChecker.new
34
+ end
35
+
36
+ # HACK: this shouldn't be in test code. use augment or something
37
+ def util_process(klass, meth)
38
+ sexp = @parser.parse_tree_for_method klass, meth
39
+ sexp = [sexp] unless meth.nil?
40
+ result = []
41
+ sexp.each do | sub_exp|
42
+ result << @type_checker.process(@rewriter.process(sub_exp))
43
+ end
44
+ return result
45
+ end
46
+
47
+ def util_unify_function
48
+ a = Type.function(Type.unknown, [ Type.unknown ], Type.unknown)
49
+ b = Type.function(Type.long, [ Type.str ], Type.void)
50
+ a.unify b
51
+ act = a.list_type
52
+ bct = b.list_type
53
+ return act, bct
54
+ end
55
+
56
+ def test_unify_function_whole
57
+ act, bct = util_unify_function
58
+ assert_equal act, bct
59
+ end
60
+
61
+ def test_unify_function_receiver
62
+ act, bct = util_unify_function
63
+ assert_equal act.receiver_type.list_type, bct.receiver_type.list_type
64
+ assert_equal act.receiver_type.list_type.object_id, bct.receiver_type.list_type.object_id
65
+ assert_equal act, bct
66
+ end
67
+
68
+ def test_unify_function_args
69
+ act, bct = util_unify_function.map { |x| x.formal_types }
70
+ assert_equal act.first.list_type, bct.first.list_type
71
+ assert_equal act.first.list_type.object_id, bct.first.list_type.object_id
72
+ end
73
+
74
+ def test_unify_function_return
75
+ act, bct = util_unify_function
76
+ assert_equal act.return_type.list_type, bct.return_type.list_type
77
+ assert_equal act.return_type.list_type.object_id, bct.return_type.list_type.object_id
78
+ end
79
+
80
+ def test_type_inference_across_args_known
81
+ generic = util_process(RandomCode, :generic_method).first
82
+ # puts
83
+ # pp @type_checker.functions
84
+ specific = util_process(RandomCode, :specific_method).first
85
+ # puts
86
+ # pp @type_checker.functions
87
+
88
+ # pp generic
89
+ # pp specific
90
+
91
+ args_g = generic[2] # FIX FUCK this is horrid
92
+ args_s = specific[2] # FIX FUCK this is horrid
93
+
94
+ #assert_equal(args_s[1].sexp_type.list_type.object_id, # FIX demeter
95
+ # args_s[2].sexp_type.list_type.object_id,
96
+ # "#specific_method's arguments are unified")
97
+
98
+ assert_equal(Type.long, args_s[1].sexp_type,
99
+ "#specific_method's x is a Long")
100
+ assert_equal(Type.long, args_g[1].sexp_type, # FAILS
101
+ "#generic_method's x is a Long")
102
+
103
+ assert_equal(args_g[1].sexp_type.list_type.object_id,
104
+ args_s[1].sexp_type.list_type.object_id,
105
+ "#specific_method's x and #generic_method's x are unified")
106
+
107
+ # assert_equal(args_g[2].sexp_type.list_type.object_id,
108
+ # args_s[2].sexp_type.list_type.object_id,
109
+ # "#specific_method's y and #generic_method's y are unified")
110
+
111
+ # assert_equal(Type.long, args_s[2].sexp_type,
112
+ # "#specific_method's y is a Long")
113
+ # assert_equal(Type.long, args_g[2].sexp_type,
114
+ # "#generic_method's y is a Long")
115
+ end
116
+
117
+ def test_type_inference_across_args_unknown
118
+ meth_a = util_process(RandomCode, :meth_a).first
119
+ meth_b = util_process(RandomCode, :meth_b).first
120
+
121
+ args_a = meth_a[2][1] # FIX FUCK this is horrid
122
+ args_b = meth_b[2][1] # FIX FUCK this is horrid
123
+
124
+ assert_equal(args_a.sexp_type.list_type,
125
+ args_b.sexp_type.list_type,
126
+ "#meth_a and meth_b arguments are the same after unification")
127
+
128
+ assert_equal(args_a.sexp_type.list_type.object_id,
129
+ args_b.sexp_type.list_type.object_id,
130
+ "#meth_a and meth_b arguments are unified by object_id")
131
+ end
132
+
133
+ def test_process_defn_return_val
134
+ ignore = util_process(RandomCode, :meth_a)
135
+ result = util_process(RandomCode, :meth_b).first
136
+
137
+ assert_equal(:meth_b, result[1])
138
+ # FIX: this is the worst API in my codebase - demeter
139
+ assert_equal(Type.void, result.sexp_type.list_type.return_type)
140
+ end
141
+
142
+ end
143
+