RubyToC 1.0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
+