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/History.txt +69 -0
- data/Makefile +49 -0
- data/Manifest.txt +98 -0
- data/README.txt +75 -0
- data/demo/char.rb +13 -0
- data/demo/factorial.rb +11 -0
- data/demo/hello.rb +11 -0
- data/demo/misc.rb +25 -0
- data/demo/newarray.rb +11 -0
- data/demo/strcat.rb +12 -0
- data/rewrite.rb +32 -0
- data/rewriter.rb +356 -0
- data/ruby_to_c.rb +680 -0
- data/support.rb +317 -0
- data/test_all.rb +9 -0
- data/test_extras.rb +143 -0
- data/test_rewriter.rb +292 -0
- data/test_ruby_to_c.rb +533 -0
- data/test_support.rb +525 -0
- data/test_type_checker.rb +838 -0
- data/test_typed_sexp_processor.rb +134 -0
- data/translate.rb +31 -0
- data/type.rb +33 -0
- data/type_checker.rb +922 -0
- data/typed_sexp_processor.rb +88 -0
- data/validate.sh +49 -0
- data/zcomparable.rb +300 -0
- metadata +74 -0
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
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
|
+
|