yadriggy 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +108 -0
- data/Rakefile +10 -0
- data/lib/yadriggy.rb +32 -0
- data/lib/yadriggy/algebra.rb +497 -0
- data/lib/yadriggy/ast.rb +1839 -0
- data/lib/yadriggy/ast_location.rb +73 -0
- data/lib/yadriggy/ast_value.rb +428 -0
- data/lib/yadriggy/c.rb +11 -0
- data/lib/yadriggy/c/c.rb +220 -0
- data/lib/yadriggy/c/codegen.rb +481 -0
- data/lib/yadriggy/c/config.rb +51 -0
- data/lib/yadriggy/c/ctype.rb +118 -0
- data/lib/yadriggy/c/ctypecheck.rb +449 -0
- data/lib/yadriggy/c/ffi.rb +301 -0
- data/lib/yadriggy/c/opencl.rb +458 -0
- data/lib/yadriggy/c/program.rb +86 -0
- data/lib/yadriggy/c1.rb +10 -0
- data/lib/yadriggy/checker.rb +216 -0
- data/lib/yadriggy/eval.rb +200 -0
- data/lib/yadriggy/eval_all.rb +159 -0
- data/lib/yadriggy/pretty_print.rb +492 -0
- data/lib/yadriggy/printer.rb +82 -0
- data/lib/yadriggy/ruby_typecheck.rb +468 -0
- data/lib/yadriggy/ruby_typeinfer.rb +335 -0
- data/lib/yadriggy/source_code.rb +168 -0
- data/lib/yadriggy/syntax.rb +524 -0
- data/lib/yadriggy/type.rb +754 -0
- data/lib/yadriggy/typecheck.rb +277 -0
- data/lib/yadriggy/version.rb +5 -0
- data/yadriggy.gemspec +33 -0
- metadata +149 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
module Yadriggy
|
4
|
+
module C
|
5
|
+
|
6
|
+
# Compiler options etc.
|
7
|
+
#
|
8
|
+
module Config
|
9
|
+
# Host OS
|
10
|
+
# @return [Symbol] `:linux`, `:macos`, `:unknown`.
|
11
|
+
HostOS = case RbConfig::CONFIG['host_os']
|
12
|
+
when /linux/
|
13
|
+
:linux
|
14
|
+
when /darwin/
|
15
|
+
:macos
|
16
|
+
else
|
17
|
+
:unknown
|
18
|
+
end
|
19
|
+
|
20
|
+
# Working directory.
|
21
|
+
WorkDir = './yadriggy_tmp'
|
22
|
+
|
23
|
+
# Compiler command.
|
24
|
+
# @return [String]
|
25
|
+
Compiler = 'gcc -shared -fPIC -Ofast '
|
26
|
+
|
27
|
+
# Compiler option specifying the output file.
|
28
|
+
# @return [String]
|
29
|
+
CoptOutput = '-o '
|
30
|
+
|
31
|
+
# The suffix to the name of a shared library such as `.so`.
|
32
|
+
# It has to start with a dot.
|
33
|
+
LibExtension = HostOS == :macos ? '.dylib' : '.so'
|
34
|
+
|
35
|
+
# Lines inserted in the generated C source file.
|
36
|
+
Headers = [
|
37
|
+
'#include <stdint.h>',
|
38
|
+
'#include <time.h>',
|
39
|
+
'#include <math.h>',
|
40
|
+
'#include <stdio.h>'
|
41
|
+
]
|
42
|
+
|
43
|
+
# Compiler option for OpenCL
|
44
|
+
# @return [String]
|
45
|
+
OpenCLoptions = '-framework opencl '
|
46
|
+
|
47
|
+
# Lines inserted in the generated OpenCL source file.
|
48
|
+
OpenCLHeaders = [ '#include <OpenCL/opencl.h>' ]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy'
|
4
|
+
require 'yadriggy/c/ffi'
|
5
|
+
|
6
|
+
module Yadriggy
|
7
|
+
module C
|
8
|
+
# Types availalbe in C code.
|
9
|
+
module CType
|
10
|
+
def typedecl(arg) end
|
11
|
+
def arrayof(t) t end
|
12
|
+
|
13
|
+
# Single-precision floating point numbers.
|
14
|
+
# This is used only as a type name.
|
15
|
+
# Note that `Float` represents double-precision floating point numbers.
|
16
|
+
# Do not make an instance.
|
17
|
+
class Float32
|
18
|
+
end
|
19
|
+
|
20
|
+
# @private
|
21
|
+
# An alias `RubyClass[Float32]`.
|
22
|
+
Float32Type = RubyClass[Float32]
|
23
|
+
|
24
|
+
Void = Yadriggy::Void # void
|
25
|
+
Int = Integer # int32_t
|
26
|
+
|
27
|
+
IntArray = Yadriggy::C::IntArray
|
28
|
+
FloatArray = Yadriggy::C::FloatArray
|
29
|
+
Float32Array = Yadriggy::C::Float32Array
|
30
|
+
|
31
|
+
# Types available only as a value of instance variable.
|
32
|
+
class IvarObj
|
33
|
+
include Yadriggy::C::CType
|
34
|
+
end
|
35
|
+
|
36
|
+
# @private
|
37
|
+
# Array type available only in C code.
|
38
|
+
# See also {IntArray} and {FloatArray} in `ffi.rb`.
|
39
|
+
class CArray < IvarObj
|
40
|
+
# @private
|
41
|
+
def typedecl(arg) end
|
42
|
+
|
43
|
+
# debug mode.
|
44
|
+
attr_accessor :debug
|
45
|
+
|
46
|
+
# @return [Array<Integer>] sizes array size.
|
47
|
+
attr_reader :sizes
|
48
|
+
|
49
|
+
# @param [Array<Integer>] sizes size of each dimension.
|
50
|
+
def initialize(*sizes)
|
51
|
+
raise 'unknown array size' unless sizes.size > 0
|
52
|
+
@sizes = sizes
|
53
|
+
@debug = false
|
54
|
+
end
|
55
|
+
|
56
|
+
# @private
|
57
|
+
# @abstract
|
58
|
+
# @return [Type] the element type.
|
59
|
+
def type()
|
60
|
+
Undef
|
61
|
+
end
|
62
|
+
|
63
|
+
# @private
|
64
|
+
def check_range(indexes)
|
65
|
+
raise 'wrong number of indexes' unless indexes.size == @sizes.size
|
66
|
+
indexes.each_index do |i|
|
67
|
+
if indexes[i] >= @sizes[i]
|
68
|
+
raise "out of range: #{self.to_s}[#{indexes[i]}]"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Array of 32bit integers available only in C.
|
75
|
+
#
|
76
|
+
# @see IntArray
|
77
|
+
class IntCArray < CArray
|
78
|
+
def type()
|
79
|
+
RubyClass::Integer
|
80
|
+
end
|
81
|
+
|
82
|
+
def [](*indexes)
|
83
|
+
typedecl foreign: Int
|
84
|
+
check_range(indexes)
|
85
|
+
raise 'IntCArray is not available in Ruby' unless @debug
|
86
|
+
end
|
87
|
+
|
88
|
+
def []=(*indexes)
|
89
|
+
typedecl foreign: Void
|
90
|
+
check_range(indexes[0, indexes.size - 1])
|
91
|
+
raise 'IntCArray is not available in Ruby' unless @debug
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Array of floating point numbers available only in C.
|
96
|
+
#
|
97
|
+
# @see FloatArray
|
98
|
+
class FloatCArray < CArray
|
99
|
+
def type()
|
100
|
+
RubyClass::Float
|
101
|
+
end
|
102
|
+
|
103
|
+
def [](*indexes)
|
104
|
+
typedecl foreign: Float
|
105
|
+
check_range(indexes)
|
106
|
+
raise 'FloatCArray is not available in Ruby' unless @debug
|
107
|
+
end
|
108
|
+
|
109
|
+
def []=(*indexes)
|
110
|
+
typedecl foreign: Void
|
111
|
+
check_range(indexes[0, indexes.size - 1])
|
112
|
+
raise 'FloatCArray is not available in Ruby' unless @debug
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,449 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy/c/ctype'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Yadriggy
|
7
|
+
module C
|
8
|
+
|
9
|
+
class ArrayType < CompositeType
|
10
|
+
attr_reader :element_type
|
11
|
+
|
12
|
+
def initialize(element_type)
|
13
|
+
super(Array, element_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def element_type()
|
17
|
+
first_arg
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Type for expressions ending with a return statement.
|
22
|
+
#
|
23
|
+
class WithReturnType < OptionalRole
|
24
|
+
end
|
25
|
+
|
26
|
+
# Type for native methods.
|
27
|
+
#
|
28
|
+
class NativeMethodType < OptionalRole
|
29
|
+
# @return [String] the method body.
|
30
|
+
attr_reader :body
|
31
|
+
|
32
|
+
# @param [MethodType] method_type a method type.
|
33
|
+
# @param [String] body_code the body of a native method.
|
34
|
+
def initialize(method_type, body_code)
|
35
|
+
super(method_type)
|
36
|
+
@body = body_code
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Type for foreign methods.
|
41
|
+
#
|
42
|
+
class ForeignMethodType < OptionalRole
|
43
|
+
# @param [MethodType] method_type a method type.
|
44
|
+
def initialize(method_type)
|
45
|
+
super(method_type)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ClangTypeChecker < RubyTypeInferer
|
50
|
+
include Yadriggy::C::CType
|
51
|
+
|
52
|
+
# @return [Hash<Def,Hash<Symbol,Type>>] a map from functions to their
|
53
|
+
# local variables.
|
54
|
+
attr_reader :local_vars_table
|
55
|
+
|
56
|
+
# @return [Set<IvarObj>] accessed instance variables.
|
57
|
+
attr_reader :instance_variables
|
58
|
+
|
59
|
+
def initialize(syntax=nil)
|
60
|
+
super(syntax || C::syntax)
|
61
|
+
@local_vars_table = {}
|
62
|
+
@instance_variables = Set.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def valid_var_type?(t)
|
66
|
+
if t.is_a?(Type)
|
67
|
+
et = t.exact_type
|
68
|
+
et == Integer || et == Float || et == String || et == Float32
|
69
|
+
else
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid_type?(t)
|
75
|
+
valid_var_type?(t) || ArrayType.role(t)
|
76
|
+
end
|
77
|
+
|
78
|
+
def is_subsumed_by?(sub_type, super_type)
|
79
|
+
(valid_var_type?(sub_type) && valid_var_type?(super_type)) ||
|
80
|
+
sub_type <= super_type
|
81
|
+
end
|
82
|
+
|
83
|
+
rule(:typedecl) do
|
84
|
+
unless ast.args.nil?
|
85
|
+
type_assert(ast.args.size == 1, 'bad typedecl')
|
86
|
+
type(ast.args[0])
|
87
|
+
end
|
88
|
+
Void
|
89
|
+
end
|
90
|
+
|
91
|
+
rule(:typedecl_hash) do
|
92
|
+
ast.pairs.each do |e|
|
93
|
+
t = typedecl_type(e[1])
|
94
|
+
declare_type(e[0].name, e[0], t)
|
95
|
+
end
|
96
|
+
Void
|
97
|
+
end
|
98
|
+
|
99
|
+
# @private
|
100
|
+
# @param [ASTnode] type_expr
|
101
|
+
def typedecl_type(type_expr)
|
102
|
+
if type_expr.is_a?(Call)
|
103
|
+
type_assert(type_expr.args.size == 1, 'bad array type')
|
104
|
+
etype = RubyClass[type_expr.args[0].value]
|
105
|
+
type_assert(etype != Undef, 'cannot resolve a type name')
|
106
|
+
type_assert(valid_var_type?(etype), "bad array type: #{etype}")
|
107
|
+
ArrayType.new(etype)
|
108
|
+
else
|
109
|
+
rt = type_expr.value
|
110
|
+
type_assert(rt != Undef, 'cannot resolve a type name')
|
111
|
+
if rt.is_a?(Module) && rt <= FFIArray
|
112
|
+
ArrayType.new(RubyClass[rt.element_type])
|
113
|
+
else
|
114
|
+
RubyClass[rt]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# @private
|
120
|
+
def declare_type(name, name_ast, t)
|
121
|
+
if name == 'return' || name == 'foreign'
|
122
|
+
type_assert(valid_type?(t) || t == Void,
|
123
|
+
"bad return type: #{t.name}")
|
124
|
+
check_duplicate(name, t)
|
125
|
+
type_env.bind_name(name.to_sym, t)
|
126
|
+
elsif name == 'native'
|
127
|
+
type_assert(t.is_a?(String), 'bad native argument. not String.')
|
128
|
+
type_assert(type_env.bound_name?(:native).nil?,
|
129
|
+
'duplicate declaration: native')
|
130
|
+
type_env.bind_name(:native, InstanceType.new(t))
|
131
|
+
else
|
132
|
+
type_assert(valid_type?(t), "bad parameter type: #{name}")
|
133
|
+
check_duplicate(name, t)
|
134
|
+
bind_local_var(type_env, name_ast, t, false)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @private
|
139
|
+
def check_duplicate(name, t)
|
140
|
+
old_type = type_env.bound_name?(name)
|
141
|
+
type_assert(old_type.nil? || old_type == t,
|
142
|
+
"incompatible or duplicate declaration: #{name}")
|
143
|
+
end
|
144
|
+
|
145
|
+
rule(:return_type) do
|
146
|
+
typedecl_type(ast.expr)
|
147
|
+
end
|
148
|
+
|
149
|
+
rule(Number) do
|
150
|
+
if ast.value.is_a?(Float)
|
151
|
+
RubyClass::Float
|
152
|
+
else
|
153
|
+
RubyClass::Integer
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
rule(Const) do
|
158
|
+
t = proceed(ast)
|
159
|
+
type_assert(t != Undef, 'unknown constant')
|
160
|
+
type_assert(valid_var_type?(t), 'bad constant type')
|
161
|
+
t
|
162
|
+
end
|
163
|
+
|
164
|
+
rule(ConstPathRef) do
|
165
|
+
v = ast.value
|
166
|
+
type_assert(v != Undef, 'unknown constant')
|
167
|
+
t = InstanceType.new(v)
|
168
|
+
type_assert(valid_var_type?(t), 'bad constant type')
|
169
|
+
t
|
170
|
+
end
|
171
|
+
|
172
|
+
rule(InstanceVariable) do
|
173
|
+
v = ast.value
|
174
|
+
key = type_env.context
|
175
|
+
if v == Undef
|
176
|
+
get_instance_variable_type(key, ast, false, DynType)
|
177
|
+
else
|
178
|
+
type_assert(v.is_a?(IvarObj), 'badly typed instance variable')
|
179
|
+
@instance_variables << v
|
180
|
+
get_instance_variable_type(key, ast, true, InstanceType.new(v))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
rule(Assign) do
|
185
|
+
rtype = type(ast.right)
|
186
|
+
type_assert(valid_var_type?(rtype), 'bad assigned value')
|
187
|
+
if ast.left.is_a?(ArrayRef)
|
188
|
+
type(ast.left)
|
189
|
+
else
|
190
|
+
type_assert(ast.left.is_a?(IdentifierOrCall), 'bad assignment')
|
191
|
+
ltype = type_env.bound_name?(ast.left)
|
192
|
+
if ltype.nil? # if a new name is found,
|
193
|
+
bind_local_var(type_env, ast.left, rtype)
|
194
|
+
else
|
195
|
+
LocalVarType.role(ltype)&.definition = ast.left
|
196
|
+
ltype
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
rule(Binary) do
|
202
|
+
t1 = type(ast.left)
|
203
|
+
t2 = type(ast.right)
|
204
|
+
binary_cexpr_type(ast.op, t1, t2)
|
205
|
+
end
|
206
|
+
|
207
|
+
# @private
|
208
|
+
def binary_cexpr_type(op, t1, t2)
|
209
|
+
case op
|
210
|
+
when :+, :-, :*, :/
|
211
|
+
if t1 <= RubyClass::Float || t2 <= RubyClass::Float
|
212
|
+
return RubyClass::Float
|
213
|
+
elsif t1 <= Float32Type || t2 <= Float32Type
|
214
|
+
return Float32Type
|
215
|
+
else
|
216
|
+
return RubyClass::Integer
|
217
|
+
end
|
218
|
+
when :%
|
219
|
+
type_assert(t1 <= RubyClass::Integer &&
|
220
|
+
t2 <= RubyClass::Integer, 'bad operand type')
|
221
|
+
return RubyClass::Integer
|
222
|
+
when :<, :>, :<=, :>=, :==, :'&&', :'||'
|
223
|
+
return RubyClass::Boolean
|
224
|
+
else
|
225
|
+
type_assert(false, "bad operator: #{ast.op}")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
rule(ArrayRef) do
|
230
|
+
array_type = type(ast.array)
|
231
|
+
indexes = ast.indexes
|
232
|
+
if InstanceType.role(array_type)&.object.is_a?(IvarObj)
|
233
|
+
sizes = array_type.object.sizes
|
234
|
+
type_assert(indexes.size == sizes.size, 'bad array index')
|
235
|
+
indexes.each do |idx|
|
236
|
+
type_assert(type(idx) <= RubyClass::Integer, 'bad array index')
|
237
|
+
end
|
238
|
+
array_type.object.type
|
239
|
+
else
|
240
|
+
type_assert(indexes.size == 1, 'bad array index')
|
241
|
+
itype = type(indexes[0])
|
242
|
+
type_assert(itype <= RubyClass::Integer, 'bad array index')
|
243
|
+
|
244
|
+
atype = ArrayType.role(array_type)
|
245
|
+
type_assert_false(atype.nil?, 'bad array access')
|
246
|
+
atype.element_type
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
rule(Unary) do
|
251
|
+
t = type(ast.expr)
|
252
|
+
type_assert(ast.op == :-@, "bad operator: #{ast.op}")
|
253
|
+
t
|
254
|
+
end
|
255
|
+
|
256
|
+
rule(Conditional) do
|
257
|
+
type(ast.cond)
|
258
|
+
t1 = type(ast.then)
|
259
|
+
ast.all_elsif.each do |cond_then|
|
260
|
+
type(cond_then[0])
|
261
|
+
type(cond_then[1])
|
262
|
+
end
|
263
|
+
t2 = type(ast.else)
|
264
|
+
if WithReturnType.role(t1) && WithReturnType.role(t2)
|
265
|
+
WithReturnType.new(UnionType.make(t1, t2))
|
266
|
+
elsif ast.op == :ifop
|
267
|
+
UnionType.make(t1, t2)
|
268
|
+
else
|
269
|
+
Void
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
rule(Loop) do
|
274
|
+
type(ast.cond)
|
275
|
+
type(ast.body, type_env)
|
276
|
+
Void
|
277
|
+
end
|
278
|
+
|
279
|
+
rule(ForLoop) do
|
280
|
+
ast.vars.each {|v| bind_local_var(type_env, v.name,
|
281
|
+
RubyClass::Integer) }
|
282
|
+
type_assert(type(ast.set.left) <= RubyClass::Integer, 'bad for-range')
|
283
|
+
type_assert(type(ast.set.right) <= RubyClass::Integer, 'bad for-range')
|
284
|
+
type(ast.body, type_env)
|
285
|
+
Void
|
286
|
+
end
|
287
|
+
|
288
|
+
rule(Return) do
|
289
|
+
t = proceed(ast)
|
290
|
+
ret_type = type_env.bound_name?(:return)
|
291
|
+
if ret_type == Void
|
292
|
+
type_assert(ast.values.size == 0, 'bad return')
|
293
|
+
else
|
294
|
+
type_assert(is_subsumed_by?(t, ret_type), 'bad return type')
|
295
|
+
end
|
296
|
+
WithReturnType.new(t)
|
297
|
+
end
|
298
|
+
|
299
|
+
rule(Call) do
|
300
|
+
method_name = ast.name.name
|
301
|
+
if method_with_block?(method_name)
|
302
|
+
type_assert(ast.block,
|
303
|
+
"no block given: #{method_name}")
|
304
|
+
type_assert(ast.receiver,
|
305
|
+
"no receiver given: #{method_name}")
|
306
|
+
typecheck_call_with_block(ast)
|
307
|
+
else
|
308
|
+
type_assert_false(ast.block,
|
309
|
+
"a block is not taken: #{method_name}")
|
310
|
+
t = proceed(ast)
|
311
|
+
type_assert(ResultType.role(t), "bad call to: #{method_name}")
|
312
|
+
t
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Specifies the names of methods with a block.
|
317
|
+
#
|
318
|
+
# @param [String] name a method name.
|
319
|
+
# @see {CodeGen#call_with_block}
|
320
|
+
# @see {#typecheck_call_with_block}
|
321
|
+
def method_with_block?(name)
|
322
|
+
name == 'times'
|
323
|
+
end
|
324
|
+
|
325
|
+
def typecheck_call_with_block(call_ast)
|
326
|
+
type_assert(ast.name.name == 'times',
|
327
|
+
"no such method: #{ast.name.name}")
|
328
|
+
type_assert(type(ast.receiver) == RubyClass::Integer,
|
329
|
+
'the receiver must be an integer')
|
330
|
+
type_assert(ast.block.params.size == 1,
|
331
|
+
"wrong number of block parameters")
|
332
|
+
type_as(ast.block.params[0], RubyClass::Integer)
|
333
|
+
tenv = type_env.new_tenv
|
334
|
+
tenv.bind_name(ast.block.params[0], RubyClass::Integer)
|
335
|
+
tenv.bind_name(:return, Void)
|
336
|
+
type(ast.block, tenv)
|
337
|
+
Void
|
338
|
+
end
|
339
|
+
|
340
|
+
rule(Block) do
|
341
|
+
if ast.params.size == 0 && type_env.bound_name?(:return).nil? &&
|
342
|
+
ast.body.is_a?(Return)
|
343
|
+
case ast.body.values[0].usertype
|
344
|
+
when :expr, :method_call
|
345
|
+
type_env.bind_name(:return, type(ast.body.values[0]))
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# a new type environment is created by a caller
|
350
|
+
# (i.e. typecheck_call_with_block).
|
351
|
+
def_block_rule(true, type_env)
|
352
|
+
end
|
353
|
+
|
354
|
+
rule(Def) do
|
355
|
+
def_block_rule(false, type_env.new_tenv)
|
356
|
+
end
|
357
|
+
|
358
|
+
private
|
359
|
+
|
360
|
+
def def_block_rule(is_block, new_tenv)
|
361
|
+
type_block(ast, new_tenv)
|
362
|
+
|
363
|
+
ptypes = ast.params.map { |v| new_tenv.bound_name?(v.name) }
|
364
|
+
ptypes.each_with_index do |t, i|
|
365
|
+
type_assert(t, "missing parameter type: #{ast.params[i].name}")
|
366
|
+
end
|
367
|
+
result_t = new_tenv.bound_name?(:return)
|
368
|
+
result_t = new_tenv.bound_name?(:foreign) if result_t.nil?
|
369
|
+
type_assert(result_t, 'no return type specified')
|
370
|
+
|
371
|
+
mtype = MethodType.new(ast, ptypes, result_t)
|
372
|
+
type_env.bind_name(ast.name.name, mtype.result) unless is_block
|
373
|
+
|
374
|
+
code = new_tenv.bound_name?(:native)
|
375
|
+
if code
|
376
|
+
ins_t = InstanceType.role(code)
|
377
|
+
type_assert(ins_t, 'bad native declaration')
|
378
|
+
return NativeMethodType.new(mtype, ins_t.object)
|
379
|
+
end
|
380
|
+
|
381
|
+
foreign_value = new_tenv.bound_name?(:foreign)
|
382
|
+
if foreign_value && !is_block
|
383
|
+
mtype2 = MethodType.new(nil, DynType, result_t)
|
384
|
+
return ForeignMethodType.new(mtype2)
|
385
|
+
end
|
386
|
+
|
387
|
+
type_assert_later_unless(is_block) do
|
388
|
+
body_t = type(ast.body, new_tenv)
|
389
|
+
wt = WithReturnType.role(body_t)
|
390
|
+
if result_t == Void
|
391
|
+
type_assert(wt.nil? || body_t == Void, 'non-void return statement')
|
392
|
+
else
|
393
|
+
type_assert(wt, 'no return statement')
|
394
|
+
type_assert(is_subsumed_by?(result_t, body_t), 'bad result type')
|
395
|
+
end
|
396
|
+
|
397
|
+
local_vars = {}
|
398
|
+
new_tenv.each do |name, type|
|
399
|
+
lvt = LocalVarType.role(type)
|
400
|
+
local_vars[name] = type unless lvt.nil?
|
401
|
+
end
|
402
|
+
ast.params.each do |p|
|
403
|
+
local_vars.delete(p.name.to_sym)
|
404
|
+
end
|
405
|
+
|
406
|
+
@local_vars_table[ast] = local_vars
|
407
|
+
end
|
408
|
+
mtype
|
409
|
+
end
|
410
|
+
|
411
|
+
def type_assert_later_unless(value, &proc)
|
412
|
+
unless value
|
413
|
+
check_later(&proc)
|
414
|
+
else
|
415
|
+
yield
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Do typing the given block according to typedecl.
|
420
|
+
#
|
421
|
+
def type_block(block_ast, new_env)
|
422
|
+
expr0 = nil
|
423
|
+
expr1 = nil
|
424
|
+
if block_ast.body.is_a?(Exprs)
|
425
|
+
size = block_ast.body.expressions.size
|
426
|
+
expr0 = block_ast.body.expressions[0] if size > 0
|
427
|
+
expr1 = block_ast.body.expressions[1] if size > 1
|
428
|
+
else
|
429
|
+
expr0 = block_ast.body
|
430
|
+
end
|
431
|
+
|
432
|
+
if expr0&.usertype == :return_type
|
433
|
+
new_env.bind_name(:return, type(expr0))
|
434
|
+
end
|
435
|
+
|
436
|
+
if expr0&.usertype == :typedecl
|
437
|
+
type(expr0, new_env)
|
438
|
+
elsif expr1&.usertype == :typedecl
|
439
|
+
type(expr1, new_env)
|
440
|
+
end
|
441
|
+
|
442
|
+
unless new_env.bound_name?(:return) || new_env.bound_name?(:foreign)
|
443
|
+
new_env.bind_name(:return, Void)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|