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
@@ -0,0 +1,838 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
$TESTING = true
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require 'type_checker'
|
7
|
+
require 'r2ctestcase'
|
8
|
+
|
9
|
+
# Test::Unit::Assertions.use_pp = false
|
10
|
+
|
11
|
+
class DumbClass # ZenTest SKIP
|
12
|
+
def empty
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class X # ZenTest SKIP
|
17
|
+
VALUE = 42
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestTypeChecker < R2CTestCase
|
21
|
+
|
22
|
+
def setup
|
23
|
+
@type_checker = TypeChecker.new
|
24
|
+
@processor = @type_checker
|
25
|
+
@type_checker.env.add :argl, Type.long
|
26
|
+
@type_checker.env.add :args, Type.str
|
27
|
+
@type_checker.env.add :arrayl, Type.long_list
|
28
|
+
@type_checker.env.add :arrayl2, Type.long_list
|
29
|
+
@type_checker.env.add :arrays, Type.str_list
|
30
|
+
@type_checker.genv.add :SyntaxError, Type.fucked
|
31
|
+
@type_checker.genv.add :Exception, Type.fucked
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_and
|
35
|
+
input = t(:and, t(:true), t(:false))
|
36
|
+
output = t(:and, t(:true, Type.bool), t(:false, Type.bool), Type.bool)
|
37
|
+
|
38
|
+
assert_equal output, @type_checker.process(input)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_bootstrap
|
42
|
+
# bootstrap is automatically called by initialize
|
43
|
+
# TODO should we check for EVERYTHING we expect?
|
44
|
+
|
45
|
+
assert_equal Type.file, @type_checker.genv.lookup(:$stdin)
|
46
|
+
assert_equal Type.file, @type_checker.genv.lookup(:$stdout)
|
47
|
+
assert_equal Type.file, @type_checker.genv.lookup(:$stderr)
|
48
|
+
|
49
|
+
assert_equal(Type.function(Type.long, [Type.long], Type.bool),
|
50
|
+
@type_checker.functions[:>])
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_functions
|
54
|
+
# bootstrap populates functions
|
55
|
+
assert @type_checker.functions.has_key?(:puts)
|
56
|
+
assert_equal(Type.function(Type.long, [Type.long], Type.bool),
|
57
|
+
@type_checker.functions[:>])
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_env
|
61
|
+
@type_checker.env.add :blah, Type.long
|
62
|
+
assert_equal Type.long, @type_checker.env.lookup(:blah)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_genv
|
66
|
+
assert_equal Type.file, @type_checker.genv.lookup(:$stderr)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_translate
|
70
|
+
result = @type_checker.translate DumbClass, :empty
|
71
|
+
expect = t(:defn,
|
72
|
+
:empty,
|
73
|
+
t(:args),
|
74
|
+
t(:scope,
|
75
|
+
t(:block,
|
76
|
+
t(:nil, Type.value), Type.unknown), Type.void),
|
77
|
+
Type.function(Type.unknown, [], Type.void))
|
78
|
+
assert_equal(expect, result)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_process_args
|
82
|
+
@type_checker.env.extend
|
83
|
+
|
84
|
+
input = t(:args, :foo, :bar)
|
85
|
+
output = t(:args,
|
86
|
+
t(:foo, Type.unknown),
|
87
|
+
t(:bar, Type.unknown))
|
88
|
+
|
89
|
+
assert_equal output, @type_checker.process(input)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_process_args_empty
|
93
|
+
input = t(:args)
|
94
|
+
output = t(:args)
|
95
|
+
# TODO: this should be superseded by the new array functionality
|
96
|
+
|
97
|
+
assert_equal output, @type_checker.process(input)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_process_array_single
|
101
|
+
add_fake_var :arg1, Type.long
|
102
|
+
|
103
|
+
input = t(:array, t(:lvar, :arg1))
|
104
|
+
output = t(:array, t(:lvar, :arg1, Type.long))
|
105
|
+
|
106
|
+
result = @type_checker.process(input)
|
107
|
+
|
108
|
+
assert_equal Type.homo, result.sexp_type
|
109
|
+
assert_equal [ Type.long ], result.sexp_types
|
110
|
+
assert_equal output, result
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_process_array_multiple
|
114
|
+
add_fake_var :arg1, Type.long
|
115
|
+
add_fake_var :arg2, Type.str
|
116
|
+
|
117
|
+
input = t(:array, t(:lvar, :arg1), t(:lvar, :arg2))
|
118
|
+
output = t(:array,
|
119
|
+
t(:lvar, :arg1, Type.long),
|
120
|
+
t(:lvar, :arg2, Type.str))
|
121
|
+
|
122
|
+
assert_equal output, @type_checker.process(input)
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_process_call_defined
|
126
|
+
add_fake_function :name, Type.void, Type.long, Type.str
|
127
|
+
input = t(:call,
|
128
|
+
nil,
|
129
|
+
:name,
|
130
|
+
t(:arglist, t(:str, "foo")))
|
131
|
+
output = t(:call,
|
132
|
+
nil,
|
133
|
+
:name,
|
134
|
+
t(:arglist, t(:str, "foo", Type.str)),
|
135
|
+
Type.long)
|
136
|
+
|
137
|
+
assert_equal output, @type_checker.process(input)
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_process_call_defined_rhs
|
141
|
+
add_fake_function :name3, Type.long, Type.long, Type.str
|
142
|
+
input = t(:call,
|
143
|
+
t(:lit, 1),
|
144
|
+
:name3,
|
145
|
+
t(:arglist, t(:str, "foo")))
|
146
|
+
output = t(:call,
|
147
|
+
t(:lit, 1, Type.long),
|
148
|
+
:name3,
|
149
|
+
t(:arglist, t(:str, "foo", Type.str)),
|
150
|
+
Type.long)
|
151
|
+
|
152
|
+
assert_equal output, @type_checker.process(input)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_process_call_undefined
|
156
|
+
input = t(:call, nil, :name, nil)
|
157
|
+
output = t(:call, nil, :name, nil, Type.unknown)
|
158
|
+
|
159
|
+
assert_equal output, @type_checker.process(input)
|
160
|
+
# FIX returns unknown in s()
|
161
|
+
assert_equal(Type.function(Type.unknown, [], Type.unknown),
|
162
|
+
@type_checker.functions[:name])
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_process_call_unify_1
|
166
|
+
add_fake_var :number, Type.long
|
167
|
+
input = t(:call,
|
168
|
+
t(:lit, 1),
|
169
|
+
:==,
|
170
|
+
t(:arglist,
|
171
|
+
t(:lvar, :number)))
|
172
|
+
output = t(:call,
|
173
|
+
t(:lit, 1, Type.long),
|
174
|
+
:==,
|
175
|
+
t(:arglist,
|
176
|
+
t(:lvar, :number, Type.long)),
|
177
|
+
Type.bool)
|
178
|
+
|
179
|
+
assert_equal output, @type_checker.process(input)
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_process_call_unify_2
|
183
|
+
add_fake_var :number1, Type.unknown
|
184
|
+
add_fake_var :number2, Type.unknown
|
185
|
+
|
186
|
+
input = t(:call,
|
187
|
+
t(:lit, 1),
|
188
|
+
:==,
|
189
|
+
t(:arglist, t(:lvar, :number1)))
|
190
|
+
output = t(:call,
|
191
|
+
t(:lit, 1, Type.long),
|
192
|
+
:==,
|
193
|
+
t(:arglist,
|
194
|
+
t(:lvar, :number1, Type.long)),
|
195
|
+
Type.bool)
|
196
|
+
|
197
|
+
assert_equal output, @type_checker.process(input)
|
198
|
+
|
199
|
+
input = t(:call,
|
200
|
+
t(:lvar, :number2),
|
201
|
+
:==,
|
202
|
+
t(:arglist, t(:lit, 1)))
|
203
|
+
output = t(:call,
|
204
|
+
t(:lvar, :number2, Type.long),
|
205
|
+
:==,
|
206
|
+
t(:arglist,
|
207
|
+
t(:lit, 1, Type.long)),
|
208
|
+
Type.bool)
|
209
|
+
|
210
|
+
assert_equal output, @type_checker.process(input)
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_process_call_unify_3
|
214
|
+
a_type = Type.unknown
|
215
|
+
add_fake_var :a, a_type # TODO: Type.unknown
|
216
|
+
|
217
|
+
# def unify_3_outer(a)
|
218
|
+
#
|
219
|
+
# unk
|
220
|
+
# ^
|
221
|
+
# |
|
222
|
+
# outer(., ., [+])
|
223
|
+
|
224
|
+
# assume the environment got everything set up correctly
|
225
|
+
add_fake_function(:unify_3_outer, Type.void, Type.void, a_type)
|
226
|
+
|
227
|
+
assert_equal(a_type,
|
228
|
+
@type_checker.functions[:unify_3_outer].list_type.formal_types[0])
|
229
|
+
|
230
|
+
# unify_3_inner(a) # call
|
231
|
+
#
|
232
|
+
# outer(., ., [+])
|
233
|
+
# |
|
234
|
+
# v
|
235
|
+
# unk
|
236
|
+
# ^
|
237
|
+
# |
|
238
|
+
# inner(., ., [+])
|
239
|
+
|
240
|
+
@type_checker.process(t(:call, t(:nil),
|
241
|
+
:unify_3_inner,
|
242
|
+
t(:arglist, t(:lvar, :a))))
|
243
|
+
|
244
|
+
assert_equal a_type, @type_checker.env.lookup(:a)
|
245
|
+
assert_equal(@type_checker.env.lookup(:a),
|
246
|
+
@type_checker.functions[:unify_3_inner].list_type.formal_types[0])
|
247
|
+
|
248
|
+
# def unify_3_inner(a)
|
249
|
+
# a = 1
|
250
|
+
# end
|
251
|
+
#
|
252
|
+
# outer(., ., [+])
|
253
|
+
# |
|
254
|
+
# v
|
255
|
+
# long
|
256
|
+
# ^
|
257
|
+
# |
|
258
|
+
# inner(., ., [+])
|
259
|
+
|
260
|
+
@type_checker.env.scope do
|
261
|
+
@type_checker.env.add :a, a_type
|
262
|
+
|
263
|
+
@type_checker.process t(:lasgn, :a, t(:lit, 1))
|
264
|
+
end
|
265
|
+
|
266
|
+
assert_equal a_type, Type.long
|
267
|
+
|
268
|
+
assert_equal(@type_checker.functions[:unify_3_inner].list_type.formal_types[0],
|
269
|
+
@type_checker.functions[:unify_3_outer].list_type.formal_types[0])
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_defn_call_unify
|
273
|
+
|
274
|
+
# pre-registered function, presumibly through another :call elsewhere
|
275
|
+
add_fake_function :specific, Type.unknown, Type.unknown, Type.unknown
|
276
|
+
|
277
|
+
# now in specific, unify with a long
|
278
|
+
# puts "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
279
|
+
# pp @type_checker.functions
|
280
|
+
s = @type_checker.process(s(:defn, :specific,
|
281
|
+
s(:args, :x),
|
282
|
+
s(:scope,
|
283
|
+
s(:block,
|
284
|
+
s(:lasgn, :x, s(:lit, 2))))))
|
285
|
+
# pp @type_checker.functions
|
286
|
+
s_type = @type_checker.functions[:specific]
|
287
|
+
|
288
|
+
# p s_type
|
289
|
+
|
290
|
+
assert_equal(Type.long,
|
291
|
+
s_type.list_type.formal_types[0])
|
292
|
+
# HACK flunk "eric hasn't finished writing me yet. guilt. guilt. guilt."
|
293
|
+
end
|
294
|
+
|
295
|
+
def test_process_call_case_equal_long
|
296
|
+
add_fake_var :number, Type.unknown
|
297
|
+
|
298
|
+
input = t(:call,
|
299
|
+
t(:lit, 1),
|
300
|
+
:===,
|
301
|
+
t(:arglist, t(:lvar, :number)))
|
302
|
+
output = t(:call,
|
303
|
+
t(:lit, 1, Type.long),
|
304
|
+
:case_equal_long,
|
305
|
+
t(:arglist,
|
306
|
+
t(:lvar, :number, Type.long)),
|
307
|
+
Type.bool)
|
308
|
+
|
309
|
+
assert_equal output, @type_checker.process(input)
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_process_call_case_equal_string
|
313
|
+
add_fake_var :string, Type.unknown
|
314
|
+
|
315
|
+
input = t(:call,
|
316
|
+
t(:str, 'foo'),
|
317
|
+
:===,
|
318
|
+
t(:arglist, t(:lvar, :string)))
|
319
|
+
output = t(:call,
|
320
|
+
t(:str, 'foo', Type.str),
|
321
|
+
:case_equal_str,
|
322
|
+
t(:arglist,
|
323
|
+
t(:lvar, :string, Type.str)),
|
324
|
+
Type.bool)
|
325
|
+
|
326
|
+
assert_equal output, @type_checker.process(input)
|
327
|
+
end
|
328
|
+
|
329
|
+
# HACK: putting class X above w/ some consts
|
330
|
+
def test_process_class
|
331
|
+
input = s(:class, :X, :Object,
|
332
|
+
s(:defn, :meth,
|
333
|
+
s(:args, :x),
|
334
|
+
s(:scope,
|
335
|
+
s(:block,
|
336
|
+
s(:lasgn, :x, s(:const, :VALUE))))))
|
337
|
+
output = t(:class, :X, :Object,
|
338
|
+
t(:defn, :meth,
|
339
|
+
t(:args, t(:x, Type.long)),
|
340
|
+
t(:scope,
|
341
|
+
t(:block,
|
342
|
+
t(:lasgn, :x,
|
343
|
+
t(:const, :VALUE, Type.long),
|
344
|
+
Type.long),
|
345
|
+
Type.unknown),
|
346
|
+
Type.void),
|
347
|
+
Type.function(Type.unknown, [Type.long], Type.void)),
|
348
|
+
Type.zclass)
|
349
|
+
|
350
|
+
assert_equal output, @type_checker.process(input)
|
351
|
+
end
|
352
|
+
|
353
|
+
def test_process_const
|
354
|
+
assert_raises NameError do
|
355
|
+
@type_checker.process s(:const, :NonExistant)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_process_cvar
|
360
|
+
input = s(:cvar, :name)
|
361
|
+
output = t(:cvar, :name, Type.unknown)
|
362
|
+
|
363
|
+
assert_equal output, @type_checker.process(input)
|
364
|
+
end
|
365
|
+
|
366
|
+
def test_process_cvasgn
|
367
|
+
input = s(:cvasgn, :name, s(:lit, 4))
|
368
|
+
output = t(:cvasgn, :name, t(:lit, 4, Type.long), Type.unknown)
|
369
|
+
|
370
|
+
assert_equal output, @type_checker.process(input)
|
371
|
+
end
|
372
|
+
|
373
|
+
def test_process_block
|
374
|
+
input = t(:block, t(:return, t(:nil)))
|
375
|
+
# FIX: should this really be void for return?
|
376
|
+
output = t(:block,
|
377
|
+
t(:return,
|
378
|
+
t(:nil, Type.value),
|
379
|
+
Type.void),
|
380
|
+
Type.unknown)
|
381
|
+
|
382
|
+
assert_equal output, @type_checker.process(input)
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_process_block_multiple
|
386
|
+
input = t(:block,
|
387
|
+
t(:str, :foo),
|
388
|
+
t(:return, t(:nil)))
|
389
|
+
output = t(:block,
|
390
|
+
t(:str, :foo, Type.str),
|
391
|
+
t(:return,
|
392
|
+
t(:nil, Type.value),
|
393
|
+
Type.void),
|
394
|
+
Type.unknown)
|
395
|
+
|
396
|
+
assert_equal output, @type_checker.process(input)
|
397
|
+
end
|
398
|
+
|
399
|
+
def test_process_dasgn_curr
|
400
|
+
@type_checker.env.extend
|
401
|
+
input = t(:dasgn_curr, :x)
|
402
|
+
output = t(:dasgn_curr, :x, Type.unknown)
|
403
|
+
|
404
|
+
assert_equal output, @type_checker.process(input)
|
405
|
+
# HACK: is this a valid test??? it was in ruby_to_c:
|
406
|
+
# assert_equal Type.long, @type_checker.env.lookup(:x)
|
407
|
+
end
|
408
|
+
|
409
|
+
def test_process_defn
|
410
|
+
function_type = Type.function s(), Type.void
|
411
|
+
input = t(:defn,
|
412
|
+
:empty,
|
413
|
+
t(:args),
|
414
|
+
t(:scope))
|
415
|
+
output = t(:defn,
|
416
|
+
:empty,
|
417
|
+
t(:args),
|
418
|
+
t(:scope, Type.void),
|
419
|
+
function_type)
|
420
|
+
|
421
|
+
assert_equal output, @type_checker.process(input)
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_process_dstr
|
425
|
+
add_fake_var :var, Type.str
|
426
|
+
input = t(:dstr,
|
427
|
+
"var is ",
|
428
|
+
t(:lvar, :var),
|
429
|
+
t(:str, ". So there."))
|
430
|
+
output = t(:dstr, "var is ",
|
431
|
+
t(:lvar, :var, Type.str),
|
432
|
+
t(:str, ". So there.", Type.str),
|
433
|
+
Type.str)
|
434
|
+
|
435
|
+
assert_equal output, @type_checker.process(input)
|
436
|
+
end
|
437
|
+
|
438
|
+
def test_process_dvar
|
439
|
+
add_fake_var :dvar, Type.long
|
440
|
+
input = t(:dvar, :dvar)
|
441
|
+
output = t(:dvar, :dvar, Type.long)
|
442
|
+
|
443
|
+
assert_equal output, @type_checker.process(input)
|
444
|
+
end
|
445
|
+
|
446
|
+
def test_process_false
|
447
|
+
input = t(:false)
|
448
|
+
output = t(:false, Type.bool)
|
449
|
+
|
450
|
+
assert_equal output, @type_checker.process(input)
|
451
|
+
end
|
452
|
+
|
453
|
+
def test_gasgn
|
454
|
+
input = s(:gasgn, :$blah, s(:lit, 42))
|
455
|
+
expected = t(:gasgn, :$blah, t(:lit, 42, Type.long), Type.long)
|
456
|
+
|
457
|
+
assert_equal expected, @type_checker.process(input)
|
458
|
+
end
|
459
|
+
|
460
|
+
def test_process_gvar_defined
|
461
|
+
add_fake_gvar :$arg, Type.long
|
462
|
+
input = t(:gvar, :$arg)
|
463
|
+
output = t(:gvar, :$arg, Type.long)
|
464
|
+
|
465
|
+
assert_equal output, @type_checker.process(input)
|
466
|
+
end
|
467
|
+
|
468
|
+
def test_process_gvar_undefined
|
469
|
+
input = t(:gvar, :$arg)
|
470
|
+
output = t(:gvar, :$arg, Type.unknown)
|
471
|
+
|
472
|
+
assert_equal output, @type_checker.process(input)
|
473
|
+
end
|
474
|
+
|
475
|
+
def test_process_iasgn
|
476
|
+
input = s(:iasgn, :@blah, s(:lit, 42))
|
477
|
+
expected = t(:iasgn, :@blah, t(:lit, 42, Type.long), Type.long)
|
478
|
+
|
479
|
+
assert_equal expected, @type_checker.process(input)
|
480
|
+
end
|
481
|
+
|
482
|
+
def test_process_if
|
483
|
+
input = t(:if,
|
484
|
+
t(:call,
|
485
|
+
t(:lit, 1),
|
486
|
+
:==,
|
487
|
+
t(:arglist, t(:lit, 2))),
|
488
|
+
t(:str, "not equal"),
|
489
|
+
nil)
|
490
|
+
output = t(:if,
|
491
|
+
t(:call,
|
492
|
+
t(:lit, 1, Type.long),
|
493
|
+
:==,
|
494
|
+
t(:arglist,
|
495
|
+
t(:lit, 2, Type.long)),
|
496
|
+
Type.bool),
|
497
|
+
t(:str, "not equal", Type.str),
|
498
|
+
nil,
|
499
|
+
Type.str)
|
500
|
+
|
501
|
+
assert_equal output, @type_checker.process(input)
|
502
|
+
end
|
503
|
+
|
504
|
+
def test_process_if_else
|
505
|
+
input = t(:if,
|
506
|
+
t(:call,
|
507
|
+
t(:lit, 1),
|
508
|
+
:==,
|
509
|
+
t(:arglist, t(:lit, 2))),
|
510
|
+
t(:str, "not equal"),
|
511
|
+
t(:str, "equal"))
|
512
|
+
output = t(:if,
|
513
|
+
t(:call,
|
514
|
+
t(:lit, 1, Type.long),
|
515
|
+
:==,
|
516
|
+
t(:arglist, t(:lit, 2, Type.long)),
|
517
|
+
Type.bool),
|
518
|
+
t(:str, "not equal", Type.str),
|
519
|
+
t(:str, "equal", Type.str),
|
520
|
+
Type.str)
|
521
|
+
|
522
|
+
assert_equal output, @type_checker.process(input)
|
523
|
+
end
|
524
|
+
|
525
|
+
def test_process_iter
|
526
|
+
@type_checker.env.extend
|
527
|
+
var_type = Type.long_list
|
528
|
+
add_fake_var :array, var_type
|
529
|
+
input = t(:iter,
|
530
|
+
t(:call,
|
531
|
+
t(:lvar, :array),
|
532
|
+
:each,
|
533
|
+
nil),
|
534
|
+
t(:dasgn_curr, :x),
|
535
|
+
t(:call,
|
536
|
+
nil,
|
537
|
+
:puts,
|
538
|
+
t(:arglist,
|
539
|
+
t(:call,
|
540
|
+
t(:dvar, :x),
|
541
|
+
:to_s,
|
542
|
+
nil))))
|
543
|
+
output = t(:iter,
|
544
|
+
t(:call,
|
545
|
+
t(:lvar, :array, var_type),
|
546
|
+
:each,
|
547
|
+
nil,
|
548
|
+
Type.unknown),
|
549
|
+
t(:dasgn_curr, :x, Type.long),
|
550
|
+
t(:call,
|
551
|
+
nil,
|
552
|
+
:puts,
|
553
|
+
t(:arglist,
|
554
|
+
t(:call,
|
555
|
+
t(:dvar, :x, Type.long),
|
556
|
+
:to_s,
|
557
|
+
nil,
|
558
|
+
Type.str)),
|
559
|
+
Type.void),
|
560
|
+
Type.void)
|
561
|
+
|
562
|
+
assert_equal output, @type_checker.process(input)
|
563
|
+
end
|
564
|
+
|
565
|
+
def test_process_ivar
|
566
|
+
@type_checker.env.add :@blah, Type.long
|
567
|
+
input = s(:ivar, :@blah)
|
568
|
+
expected = t(:ivar, :@blah, Type.long)
|
569
|
+
|
570
|
+
assert_equal expected, @type_checker.process(input)
|
571
|
+
end
|
572
|
+
|
573
|
+
def test_process_lasgn
|
574
|
+
@type_checker.env.extend # FIX: this is a design flaw... examine irb sess:
|
575
|
+
# require 'sexp_processor'
|
576
|
+
# require 'type_checker'
|
577
|
+
# tc = TypeChecker.new
|
578
|
+
# s = t(:lasgn, :var, t(:str, "foo"))
|
579
|
+
# tc.process(s)
|
580
|
+
# => raises
|
581
|
+
# tc.env.extend
|
582
|
+
# tc.process(s)
|
583
|
+
# => raises elsewhere... etc etc etc
|
584
|
+
# makes debugging very difficult
|
585
|
+
input = t(:lasgn, :var, t(:str, "foo"))
|
586
|
+
output = t(:lasgn, :var,
|
587
|
+
t(:str, "foo", Type.str),
|
588
|
+
Type.str)
|
589
|
+
|
590
|
+
assert_equal output, @type_checker.process(input)
|
591
|
+
end
|
592
|
+
|
593
|
+
def test_process_lasgn_array
|
594
|
+
@type_checker.env.extend
|
595
|
+
input = t(:lasgn,
|
596
|
+
:var,
|
597
|
+
t(:array,
|
598
|
+
t(:str, "foo"),
|
599
|
+
t(:str, "bar")))
|
600
|
+
output = t(:lasgn, :var,
|
601
|
+
t(:array,
|
602
|
+
t(:str, "foo", Type.str),
|
603
|
+
t(:str, "bar", Type.str)),
|
604
|
+
Type.str_list)
|
605
|
+
|
606
|
+
assert_equal output, @type_checker.process(input)
|
607
|
+
end
|
608
|
+
|
609
|
+
def test_process_lit_long
|
610
|
+
input = t(:lit, 1)
|
611
|
+
output = t(:lit, 1, Type.long)
|
612
|
+
|
613
|
+
assert_equal output, @type_checker.process(input)
|
614
|
+
end
|
615
|
+
|
616
|
+
def test_process_lit_sym
|
617
|
+
input = t(:lit, :sym)
|
618
|
+
output = t(:lit, :sym, Type.symbol)
|
619
|
+
|
620
|
+
assert_equal output, @type_checker.process(input)
|
621
|
+
end
|
622
|
+
|
623
|
+
def test_process_lit_float
|
624
|
+
input = t(:lit, 1.0)
|
625
|
+
output = t(:lit, 1.0, Type.float)
|
626
|
+
|
627
|
+
assert_equal output, @type_checker.process(input)
|
628
|
+
end
|
629
|
+
|
630
|
+
def test_process_lvar
|
631
|
+
add_fake_var :arg, Type.long
|
632
|
+
input = t(:lvar, :arg)
|
633
|
+
output = t(:lvar, :arg, Type.long)
|
634
|
+
|
635
|
+
assert_equal output, @type_checker.process(input)
|
636
|
+
end
|
637
|
+
|
638
|
+
def test_process_nil
|
639
|
+
input = t(:nil)
|
640
|
+
output = t(:nil, Type.value)
|
641
|
+
|
642
|
+
assert_equal output, @type_checker.process(input)
|
643
|
+
end
|
644
|
+
|
645
|
+
def test_process_not
|
646
|
+
input = t(:not, t(:true))
|
647
|
+
output = t(:not, t(:true, Type.bool), Type.bool)
|
648
|
+
|
649
|
+
assert_equal output, @type_checker.process(input)
|
650
|
+
end
|
651
|
+
|
652
|
+
def test_process_or
|
653
|
+
input = t(:or, t(:true), t(:false))
|
654
|
+
output = t(:or, t(:true, Type.bool), t(:false, Type.bool), Type.bool)
|
655
|
+
|
656
|
+
assert_equal output, @type_checker.process(input)
|
657
|
+
end
|
658
|
+
|
659
|
+
def test_process_rescue
|
660
|
+
assert_raises RuntimeError do
|
661
|
+
@type_checker.process s(:rescue, s(:true), s(:true))
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
def test_process_return
|
666
|
+
input = t(:return, t(:nil))
|
667
|
+
output = t(:return, t(:nil, Type.value), Type.void)
|
668
|
+
|
669
|
+
assert_equal output, @type_checker.process(input)
|
670
|
+
end
|
671
|
+
|
672
|
+
# HACK is this test valid? I don't think so
|
673
|
+
# def test_process_return_empty
|
674
|
+
# input = t(:return)
|
675
|
+
# output = t(:return, t(:nil, Type.value), Type.void)
|
676
|
+
#
|
677
|
+
# assert_equal output, @type_checker.process(input)
|
678
|
+
# end
|
679
|
+
|
680
|
+
def test_process_str
|
681
|
+
input = t(:str, "foo")
|
682
|
+
output = t(:str, "foo", Type.str)
|
683
|
+
|
684
|
+
assert_equal output, @type_checker.process(input)
|
685
|
+
end
|
686
|
+
|
687
|
+
def test_process_scope
|
688
|
+
input = t(:scope,
|
689
|
+
t(:block,
|
690
|
+
t(:return, t(:nil))))
|
691
|
+
output = t(:scope,
|
692
|
+
t(:block,
|
693
|
+
t(:return,
|
694
|
+
t(:nil, Type.value),
|
695
|
+
Type.void),
|
696
|
+
Type.unknown), # FIX ? do we care about block?
|
697
|
+
Type.void)
|
698
|
+
|
699
|
+
assert_equal output, @type_checker.process(input)
|
700
|
+
end
|
701
|
+
|
702
|
+
def test_process_scope_empty
|
703
|
+
input = t(:scope)
|
704
|
+
output = t(:scope, Type.void)
|
705
|
+
|
706
|
+
assert_equal output, @type_checker.process(input)
|
707
|
+
end
|
708
|
+
|
709
|
+
def test_process_true
|
710
|
+
input = t(:true)
|
711
|
+
output = t(:true, Type.bool)
|
712
|
+
|
713
|
+
assert_equal output, @type_checker.process(input)
|
714
|
+
end
|
715
|
+
|
716
|
+
def test_process_unless
|
717
|
+
input = t(:if,
|
718
|
+
t(:call,
|
719
|
+
t(:lit, 1),
|
720
|
+
:==,
|
721
|
+
t(:arglist, t(:lit, 2))),
|
722
|
+
nil,
|
723
|
+
t(:str, "equal"))
|
724
|
+
output = t(:if,
|
725
|
+
t(:call,
|
726
|
+
t(:lit, 1, Type.long),
|
727
|
+
:==,
|
728
|
+
t(:arglist,
|
729
|
+
t(:lit, 2, Type.long)),
|
730
|
+
Type.bool),
|
731
|
+
nil,
|
732
|
+
t(:str, "equal", Type.str),
|
733
|
+
Type.str)
|
734
|
+
|
735
|
+
assert_equal output, @type_checker.process(input)
|
736
|
+
end
|
737
|
+
|
738
|
+
def test_process_while
|
739
|
+
input = t(:while, t(:true), t(:call, t(:lit, 1), :to_s, nil), true)
|
740
|
+
expected = t(:while,
|
741
|
+
t(:true, Type.bool),
|
742
|
+
t(:call, t(:lit, 1, Type.long), :to_s, nil,
|
743
|
+
Type.str), true)
|
744
|
+
|
745
|
+
assert_equal expected, @type_checker.process(input)
|
746
|
+
end
|
747
|
+
|
748
|
+
def add_fake_function(name, reciever_type, return_type, *arg_types)
|
749
|
+
@type_checker.functions.add_function(name,
|
750
|
+
Type.function(reciever_type, arg_types, return_type))
|
751
|
+
end
|
752
|
+
|
753
|
+
def add_fake_var(name, type)
|
754
|
+
@type_checker.env.extend
|
755
|
+
@type_checker.env.add name, type
|
756
|
+
end
|
757
|
+
|
758
|
+
def add_fake_gvar(name, type)
|
759
|
+
@type_checker.genv.add name, type
|
760
|
+
end
|
761
|
+
|
762
|
+
# HACK: this needs to be set up w/ determine args in test_type_checker
|
763
|
+
# "unknown_args" => {
|
764
|
+
# "ParseTree" => [:defn, :unknown_args,
|
765
|
+
# [:scope, [:block, [:args, :arg1, :arg2], [:return, [:lvar, :arg1]]]]],
|
766
|
+
# "Rewriter" => s(:defn, :unknown_args,
|
767
|
+
# s(:args, :arg1, :arg2),
|
768
|
+
# s(:scope,
|
769
|
+
# s(:block,
|
770
|
+
# s(:return, s(:lvar, :arg1))))),
|
771
|
+
# "TypeChecker" => t(:defn, :unknown_args,
|
772
|
+
# t(:args,
|
773
|
+
# t(:arg1, Type.long),
|
774
|
+
# t(:arg2, Type.str)),
|
775
|
+
# t(:scope,
|
776
|
+
# t(:block,
|
777
|
+
# t(:return,
|
778
|
+
# t(:lvar,
|
779
|
+
# :arg1,
|
780
|
+
# Type.long),
|
781
|
+
# Type.void),
|
782
|
+
# Type.unknown),
|
783
|
+
# Type.void),
|
784
|
+
# Type.function(Type.unknown, [Type.long, Type.str], Type.long)),
|
785
|
+
# "R2CRewriter" => :same,
|
786
|
+
# "RubyToC" => "long
|
787
|
+
# unknown_args(long arg1, str arg2) {
|
788
|
+
# return arg1;
|
789
|
+
# }",
|
790
|
+
# },
|
791
|
+
|
792
|
+
# "determine_args" => {
|
793
|
+
# "ParseTree" => [:defn, :determine_args,
|
794
|
+
# [:scope, [:block,
|
795
|
+
# [:args],
|
796
|
+
# [:call, [:lit, 5], :==,
|
797
|
+
# [:array,
|
798
|
+
# [:fcall, :unknown_args,
|
799
|
+
# [:array, [:lit, 4], [:str, "known"]]]]]]]],
|
800
|
+
# "Rewriter" => s(:defn, :determine_args,
|
801
|
+
# s(:args),
|
802
|
+
# s(:scope,
|
803
|
+
# s(:block,
|
804
|
+
# s(:call,
|
805
|
+
# s(:lit, 5), :==,
|
806
|
+
# s(:arglist, s(:call, nil,
|
807
|
+
# :unknown_args,
|
808
|
+
# s(:arglist,
|
809
|
+
# s(:lit, 4),
|
810
|
+
# s(:str, "known")))))))),
|
811
|
+
# "TypeChecker" => t(:defn, :determine_args,
|
812
|
+
# t(:args),
|
813
|
+
# t(:scope,
|
814
|
+
# t(:block,
|
815
|
+
# t(:call,
|
816
|
+
# t(:lit,
|
817
|
+
# 5,
|
818
|
+
# Type.long),
|
819
|
+
# :==,
|
820
|
+
# t(:arglist,
|
821
|
+
# t(:call,
|
822
|
+
# nil,
|
823
|
+
# :unknown_args,
|
824
|
+
# t(:arglist,
|
825
|
+
# t(:lit, 4, Type.long),
|
826
|
+
# t(:str, "known", Type.str)),
|
827
|
+
# Type.long)),
|
828
|
+
# Type.bool),
|
829
|
+
# Type.unknown),
|
830
|
+
# Type.void),
|
831
|
+
# Type.function(Type.unknown, [], Type.void)),
|
832
|
+
# "R2CRewriter" => :same,
|
833
|
+
# "RubyToC" => "void\ndetermine_args() {\n5 == unknown_args(4, \"known\");\n}",
|
834
|
+
# },
|
835
|
+
|
836
|
+
|
837
|
+
end
|
838
|
+
|