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.
@@ -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