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
data/lib/yadriggy/c.rb
ADDED
data/lib/yadriggy/c/c.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy'
|
4
|
+
require 'yadriggy/c/ctypecheck'
|
5
|
+
require 'yadriggy/c/codegen'
|
6
|
+
require 'yadriggy/c/ffi'
|
7
|
+
require 'yadriggy/c/config'
|
8
|
+
|
9
|
+
module Yadriggy
|
10
|
+
module C
|
11
|
+
# yadriggy/c/ffi.rb also defiens methods in this module.
|
12
|
+
|
13
|
+
# An error thrown during compilation.
|
14
|
+
#
|
15
|
+
class BuildError < RuntimeError
|
16
|
+
attr_accessor :all_messages
|
17
|
+
|
18
|
+
# @param [Array<String>] msg an array of strings.
|
19
|
+
def initialize(msg)
|
20
|
+
super(msg.empty? ? '' : msg.is_a?(String) ? msg : msg[0])
|
21
|
+
@all_messages = msg.is_a?(String) ? [msg] : msg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@syntax = Yadriggy::define_syntax do
|
26
|
+
expr <= Name | Number | Binary | Unary |
|
27
|
+
ConstPathRef | StringLiteral | ArrayRef |
|
28
|
+
Paren | typedecl | method_call
|
29
|
+
stmnt <= expr | Return | ForLoop | Loop | Conditional
|
30
|
+
exprs <= Exprs + { expressions: [ stmnt ] } | stmnt
|
31
|
+
|
32
|
+
Name <= {}
|
33
|
+
Number <= {}
|
34
|
+
SymbolLiteral <= nil
|
35
|
+
InstanceVariable <= {}
|
36
|
+
GlobalVariable <= nil
|
37
|
+
Reserved <= nil
|
38
|
+
Const <= Name
|
39
|
+
ConstPathRef <= { scope: (ConstPathRef | Const), name: Const }
|
40
|
+
StringLiteral <= {}
|
41
|
+
Paren <= { expression: expr }
|
42
|
+
Return <= { values: expr | nil } # only single return value
|
43
|
+
ForLoop <= {vars: Name, set: Dots, body: exprs }
|
44
|
+
Loop <= { op: Symbol, cond: expr, body: exprs }
|
45
|
+
Conditional <= { op: Symbol, cond: expr, then: exprs,
|
46
|
+
all_elsif: [expr * exprs], else: (exprs) }
|
47
|
+
method_call <= Call +
|
48
|
+
{ receiver: (expr), op: (Symbol), name: Name,
|
49
|
+
args: [ expr ], block_arg: nil }
|
50
|
+
|
51
|
+
arrayof_name <= Identifier + { name: 'arrayof' }
|
52
|
+
arrayof <= Call + { receiver: nil, op: nil, name: arrayof_name,
|
53
|
+
args: [ expr ], block_arg: nil, block: nil }
|
54
|
+
typedecl_hash <= HashLiteral + { pairs: [ Label * label_value ] }
|
55
|
+
label_value <= Const | ConstPathRef | arrayof | StringLiteral
|
56
|
+
typedecl_name <= Identifier + { name: 'typedecl' }
|
57
|
+
typedecl <= Call +
|
58
|
+
{ name: typedecl_name, args: [ typedecl_hash ] }
|
59
|
+
|
60
|
+
return_type <= Unary + { expr: Const | ConstPathRef | arrayof }
|
61
|
+
func_body <= return_type | stmnt |
|
62
|
+
Exprs + { expressions: [ (return_type), stmnt ] }
|
63
|
+
|
64
|
+
Parameters <= { params: [ Identifier ], optionals: nil,
|
65
|
+
rest_of_params: nil, params_after_rest: nil,
|
66
|
+
keywords: nil, rest_of_keywords: nil,
|
67
|
+
block_param: nil }
|
68
|
+
Block <= Parameters + { body: func_body }
|
69
|
+
Def <= Parameters + { singular: nil, name: Identifier,
|
70
|
+
body: func_body, rescue: nil }
|
71
|
+
Program <= { elements: exprs }
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Syntax] the syntax.
|
75
|
+
def self.syntax
|
76
|
+
@syntax
|
77
|
+
end
|
78
|
+
|
79
|
+
# Compiles methods into binary code.
|
80
|
+
#
|
81
|
+
# @param [Proc|Method|UnboundMethod|Object] obj the exposed method
|
82
|
+
# or a block. If `obj` is neither a method or a block,
|
83
|
+
# all the public methods available on `obj` are exposed.
|
84
|
+
# The methods invoked by the exposed methods are also compiled.
|
85
|
+
# @param [String] lib_name the library name.
|
86
|
+
# @param [String] dir the directory name.
|
87
|
+
# @param [String] module_name the module name where the exposed methods
|
88
|
+
# are attached when the generated Ruby script is executed.
|
89
|
+
# If `method_name` is nil, no Ruby script is generated.
|
90
|
+
# @return [Module] the module object where the exposed methods
|
91
|
+
# are attached. It does not have a name.
|
92
|
+
def self.compile(obj, lib_name=nil, dir=Config::WorkDir, module_name=nil)
|
93
|
+
mod, funcs = compile0(obj, lib_name, dir, module_name,
|
94
|
+
ClangTypeChecker, CodeGen)[0]
|
95
|
+
mod
|
96
|
+
end
|
97
|
+
|
98
|
+
# @private
|
99
|
+
# @return [Pair<Module,Array<String>>]
|
100
|
+
def self.compile0(obj, lib_name, dir, module_name,
|
101
|
+
typechecker_class, gen_class)
|
102
|
+
begin
|
103
|
+
compile1(obj, lib_name, dir, module_name,
|
104
|
+
typechecker_class, gen_class)
|
105
|
+
rescue SyntaxError, CheckError, BuildError => err
|
106
|
+
raise err if Yadriggy.debug > 0
|
107
|
+
warn err.message
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# @private
|
113
|
+
# @return [Pair<Module,Array<String>>]
|
114
|
+
def self.compile1(obj, lib_name, dir, module_name,
|
115
|
+
typechecker_class, gen_class)
|
116
|
+
lib_name0 = obj.class.name
|
117
|
+
method_objs = if obj.is_a?(Proc) || obj.is_a?(Method) ||
|
118
|
+
obj.is_a?(UnboundMethod)
|
119
|
+
lib_name0 += obj.object_id.to_s(16)
|
120
|
+
[obj]
|
121
|
+
else
|
122
|
+
obj.public_methods(false).map do |name|
|
123
|
+
obj.method(name)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
lib_name = lib_name0.gsub('::', '_').downcase if lib_name.nil?
|
127
|
+
|
128
|
+
raise BuildError.new('no methods specified') if method_objs.size < 1
|
129
|
+
|
130
|
+
checker = typechecker_class.new(@syntax)
|
131
|
+
pub_methods = compiled_methods(checker, method_objs)
|
132
|
+
|
133
|
+
dir += File::Separator unless dir.end_with?(File::Separator)
|
134
|
+
FileUtils.mkdir_p(dir)
|
135
|
+
printer = Yadriggy::FilePrinter.new(gen_class.c_src_file(dir, lib_name))
|
136
|
+
gen = gen_class.new(printer, checker, pub_methods)
|
137
|
+
|
138
|
+
generate_funcs(pub_methods[0], gen, printer)
|
139
|
+
gen.build_lib(lib_name, dir)
|
140
|
+
|
141
|
+
attach_funcs(pub_methods, checker, gen, module_name, lib_name, dir)
|
142
|
+
end
|
143
|
+
|
144
|
+
# @private
|
145
|
+
# @return [Array<ASTree>] the ASTs of compiled methods.
|
146
|
+
def self.compiled_methods(checker, method_objs)
|
147
|
+
ast = nil
|
148
|
+
pub_methods = method_objs.map do |mthd|
|
149
|
+
if ast.nil?
|
150
|
+
ast = Yadriggy::reify(mthd)
|
151
|
+
else
|
152
|
+
ast = ast.reify(mthd)
|
153
|
+
end
|
154
|
+
|
155
|
+
if ast.nil?
|
156
|
+
raise SyntaxError.new(
|
157
|
+
"cannot locate the source: #{mthd.name.to_s} in #{mthd.receiver.class}")
|
158
|
+
end
|
159
|
+
|
160
|
+
@syntax.raise_error unless @syntax.check(ast.tree)
|
161
|
+
checker.typecheck(ast.tree)
|
162
|
+
ast
|
163
|
+
end
|
164
|
+
|
165
|
+
return pub_methods
|
166
|
+
end
|
167
|
+
|
168
|
+
# @private
|
169
|
+
def self.generate_funcs(ast, gen, printer)
|
170
|
+
gen.name_global_variables
|
171
|
+
gen.headers
|
172
|
+
gen.variable_declarations
|
173
|
+
ast.astrees.each do |e|
|
174
|
+
gen.prototype(e.tree)
|
175
|
+
end
|
176
|
+
gen.preamble
|
177
|
+
ast.astrees.each do |e|
|
178
|
+
printer << :nl
|
179
|
+
gen.c_function(e.tree)
|
180
|
+
end
|
181
|
+
|
182
|
+
printer.output
|
183
|
+
printer.close
|
184
|
+
|
185
|
+
raise BuildError.new(gen.error_messages) if gen.errors?
|
186
|
+
end
|
187
|
+
|
188
|
+
# @private
|
189
|
+
# @return [Pair<Module,Array<String>>] the module where the methods
|
190
|
+
# are attached. The second element is method names.
|
191
|
+
def self.attach_funcs(pub_methods, checker, gen, module_name,
|
192
|
+
lib_name, dir)
|
193
|
+
func_names = pub_methods.map { |ast| gen.c_function_name(ast.tree) }
|
194
|
+
func_types = pub_methods.map { |ast| checker.type(ast.tree) }
|
195
|
+
|
196
|
+
func_names, func_types = gen.expand_functions(func_names, func_types)
|
197
|
+
|
198
|
+
unless module_name.nil?
|
199
|
+
make_attach_file(module_name, func_names, func_types,
|
200
|
+
lib_name, dir)
|
201
|
+
end
|
202
|
+
|
203
|
+
[attach(Module.new, func_names, func_types, lib_name, dir),
|
204
|
+
func_names]
|
205
|
+
end
|
206
|
+
|
207
|
+
# Compiles and runs a block.
|
208
|
+
#
|
209
|
+
# @param [String] lib_name the library name.
|
210
|
+
# @param [String] dir the directory name.
|
211
|
+
# @param [Object...] args the arguments to the block.
|
212
|
+
# @return [Object] the result of running the given block.
|
213
|
+
def self.run(lib_name=nil, *args, dir: Config::WorkDir, &block)
|
214
|
+
raise BuildError.new('no block given') if block.nil?
|
215
|
+
mod, mths = compile0(block, lib_name, dir, nil,
|
216
|
+
ClangTypeChecker, CodeGen)
|
217
|
+
mod.method(mths[0]).call(*args)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,481 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy/c/config'
|
4
|
+
require 'yadriggy/c/ffi'
|
5
|
+
require 'yadriggy/c/ctype'
|
6
|
+
|
7
|
+
module Yadriggy
|
8
|
+
module C
|
9
|
+
|
10
|
+
# C-code generator
|
11
|
+
#
|
12
|
+
# Since Checker implements Visitor pattern, use it for code
|
13
|
+
# generation.
|
14
|
+
#
|
15
|
+
class CodeGen < Checker
|
16
|
+
# printer is a Printer object.
|
17
|
+
# Only main_method is converted into an extern function.
|
18
|
+
# Other methods are converted into static functions.
|
19
|
+
# @param [Array<ASTree>] public_methods publicly exported methods.
|
20
|
+
def initialize(printer, typechecker, public_methods)
|
21
|
+
super()
|
22
|
+
@printer = printer
|
23
|
+
@typechecker = typechecker
|
24
|
+
@func_counter = 0
|
25
|
+
@func_names = {}
|
26
|
+
@nerrors = 0
|
27
|
+
@messages = []
|
28
|
+
@public_methods = {}
|
29
|
+
public_methods.each {|m| @public_methods[m.tree] = m.tree }
|
30
|
+
@gvariables = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Tests whether a type error was found.
|
34
|
+
#
|
35
|
+
def errors?
|
36
|
+
@nerrors > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
# Gets an array of error messages.
|
40
|
+
#
|
41
|
+
def error_messages
|
42
|
+
@messages
|
43
|
+
end
|
44
|
+
|
45
|
+
# Gets the type checker.
|
46
|
+
#
|
47
|
+
def typechecker
|
48
|
+
@typechecker
|
49
|
+
end
|
50
|
+
|
51
|
+
# Gets the printer given when object construction.
|
52
|
+
#
|
53
|
+
def printer
|
54
|
+
@printer
|
55
|
+
end
|
56
|
+
|
57
|
+
rule(:typedecl) do
|
58
|
+
# nothing to be printed
|
59
|
+
end
|
60
|
+
|
61
|
+
rule(:return_type) do
|
62
|
+
# nothing to be printed
|
63
|
+
end
|
64
|
+
|
65
|
+
rule(Number) do
|
66
|
+
@printer << ast.value.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
rule(Name) do
|
70
|
+
@printer << ast.name
|
71
|
+
end
|
72
|
+
|
73
|
+
rule(IdentifierOrCall) do
|
74
|
+
t = @typechecker.type(ast)
|
75
|
+
rt = ResultType.role(t)
|
76
|
+
unless rt.nil?
|
77
|
+
mdef = rt.method_def
|
78
|
+
@printer << c_function_name(mdef) << '()'
|
79
|
+
else
|
80
|
+
it = InstanceType.role(t)
|
81
|
+
unless it.nil?
|
82
|
+
@printer << it.object
|
83
|
+
else
|
84
|
+
@printer << ast.name
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
rule(Const) do
|
90
|
+
t = @typechecker.type(ast)
|
91
|
+
@printer << InstanceType.role(t)&.object
|
92
|
+
end
|
93
|
+
|
94
|
+
rule(ConstPathRef) do
|
95
|
+
t = @typechecker.type(ast)
|
96
|
+
@printer << InstanceType.role(t)&.object
|
97
|
+
end
|
98
|
+
|
99
|
+
rule(InstanceVariable) do
|
100
|
+
t = @typechecker.type(ast)
|
101
|
+
vname = @gvariables[InstanceType.role(t)&.object]
|
102
|
+
error(ast, 'unknown instance variable') if vname.nil?
|
103
|
+
@printer << vname
|
104
|
+
end
|
105
|
+
|
106
|
+
rule(Exprs) do
|
107
|
+
ast.expressions.map do |e|
|
108
|
+
check(e)
|
109
|
+
unless e.is_a?(Conditional) ||
|
110
|
+
e.is_a?(Loop) || e.is_a?(ForLoop)
|
111
|
+
@printer << ';' << :nl
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
rule(ArrayLiteral) do
|
117
|
+
@printer << ' { '
|
118
|
+
ast.elements.map do |e|
|
119
|
+
check(e)
|
120
|
+
@printer << ', '
|
121
|
+
end
|
122
|
+
@printer << ' } '
|
123
|
+
end
|
124
|
+
|
125
|
+
rule(StringLiteral) do
|
126
|
+
@printer << '"' << ast.value.gsub(/\n/, '\\n') << '"'
|
127
|
+
end
|
128
|
+
|
129
|
+
rule(Paren) do
|
130
|
+
@printer << '('
|
131
|
+
check(ast.expression)
|
132
|
+
@printer << ')'
|
133
|
+
end
|
134
|
+
|
135
|
+
rule(Unary) do
|
136
|
+
@printer << ast.real_operator.to_s
|
137
|
+
check(ast.expr)
|
138
|
+
end
|
139
|
+
|
140
|
+
rule(Binary) do
|
141
|
+
check(ast.left)
|
142
|
+
@printer << ' ' << ast.op.to_s << ' '
|
143
|
+
check(ast.right)
|
144
|
+
end
|
145
|
+
|
146
|
+
rule(ArrayRef) do
|
147
|
+
check(ast.array)
|
148
|
+
ast.indexes.each do |idx|
|
149
|
+
@printer << '['
|
150
|
+
check(idx)
|
151
|
+
@printer << ']'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
rule(Dots) do
|
156
|
+
error(expr, 'a range object is not available')
|
157
|
+
end
|
158
|
+
|
159
|
+
rule(Call) do
|
160
|
+
if @typechecker.method_with_block?(ast.name.name)
|
161
|
+
call_with_block(ast)
|
162
|
+
else
|
163
|
+
t = @typechecker.type(ast)
|
164
|
+
mdef = ResultType.role(t).method_def
|
165
|
+
|
166
|
+
if mdef.nil?
|
167
|
+
@printer << ast.name.name << '('
|
168
|
+
else
|
169
|
+
@printer << c_function_name(mdef) << '('
|
170
|
+
end
|
171
|
+
|
172
|
+
ast.args.each_with_index do |e, i|
|
173
|
+
@printer << ', ' if i > 0
|
174
|
+
check(e)
|
175
|
+
end
|
176
|
+
@printer << ')'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def call_with_block(call_ast)
|
181
|
+
loop_param = call_ast.block.params[0]
|
182
|
+
@printer << 'for (' << c_type(RubyClass::Integer) << ' '
|
183
|
+
check(loop_param)
|
184
|
+
@printer << ' = ('
|
185
|
+
check(ast.receiver)
|
186
|
+
@printer << ') - 1; '
|
187
|
+
check(loop_param)
|
188
|
+
@printer << ' >= 0; '
|
189
|
+
check(loop_param)
|
190
|
+
@printer << '--) {'
|
191
|
+
@printer.down
|
192
|
+
local_var_declarations(ast.block)
|
193
|
+
check(ast.block.body)
|
194
|
+
@printer << ';' unless ast.block.body.is_a?(Exprs)
|
195
|
+
@printer.up
|
196
|
+
@printer << '}' << :nl
|
197
|
+
end
|
198
|
+
|
199
|
+
rule(Conditional) do
|
200
|
+
case ast.op
|
201
|
+
when :unless, :unless_mod
|
202
|
+
error(ast, "a bad control statement")
|
203
|
+
when :ifop
|
204
|
+
@printer << '('
|
205
|
+
check(ast.cond)
|
206
|
+
@printer << ') ? ('
|
207
|
+
check(ast.then)
|
208
|
+
@printer << ') : ('
|
209
|
+
check(ast.else)
|
210
|
+
@printer << ')'
|
211
|
+
else
|
212
|
+
@printer << "if ("
|
213
|
+
check(ast.cond)
|
214
|
+
@printer << ') {'
|
215
|
+
@printer.down
|
216
|
+
check(ast.then)
|
217
|
+
@printer << ';' unless ast.then.is_a?(Exprs)
|
218
|
+
@printer.up
|
219
|
+
unless ast.else.nil?
|
220
|
+
@printer << '} else {'
|
221
|
+
@printer.down
|
222
|
+
check(ast.else)
|
223
|
+
@printer << ';' unless ast.else.is_a?(Exprs)
|
224
|
+
@printer.up
|
225
|
+
end
|
226
|
+
@printer << '}' << :nl
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
rule(Loop) do
|
231
|
+
case ast.op
|
232
|
+
when :until, :while_mod, :until_mod
|
233
|
+
error(ast, "#{ast.op} is not available")
|
234
|
+
else
|
235
|
+
@printer << 'while ('
|
236
|
+
check(ast.cond)
|
237
|
+
@printer << ') {'
|
238
|
+
@printer.down
|
239
|
+
check(ast.body)
|
240
|
+
@printer << ';' unless ast.body.is_a?(Exprs)
|
241
|
+
@printer.up
|
242
|
+
@printer << '}' << :nl
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
rule(ForLoop) do
|
247
|
+
var_name = ast.vars[0].name
|
248
|
+
@printer << 'for (' << var_name << ' = '
|
249
|
+
check(ast.set.left)
|
250
|
+
@printer << '; ' << var_name
|
251
|
+
if ast.set.op == :'...'
|
252
|
+
@printer << ' < '
|
253
|
+
else
|
254
|
+
@printer << ' <= '
|
255
|
+
end
|
256
|
+
check(ast.set.right)
|
257
|
+
@printer << '; ++' << var_name << ') {' << :nl
|
258
|
+
@printer.down
|
259
|
+
check(ast.body)
|
260
|
+
@printer << ';' unless ast.body.is_a?(Exprs)
|
261
|
+
@printer.up
|
262
|
+
@printer << '}' << :nl
|
263
|
+
end
|
264
|
+
|
265
|
+
rule(Return) do
|
266
|
+
@printer << 'return '
|
267
|
+
check(ast.values[0]) # ast.values.size is < 2.
|
268
|
+
end
|
269
|
+
|
270
|
+
rule(Block) do
|
271
|
+
def_function(ast, c_function_name(ast))
|
272
|
+
end
|
273
|
+
|
274
|
+
rule(Def) do
|
275
|
+
def_function(ast, c_function_name(ast))
|
276
|
+
end
|
277
|
+
|
278
|
+
# Obtains the file name of the generated source code.
|
279
|
+
# A subclass can redefine this method.
|
280
|
+
#
|
281
|
+
# @param [String] dir a directory name.
|
282
|
+
# @param [String] lib_name a library name.
|
283
|
+
# @return [String] a source file name.
|
284
|
+
def self.c_src_file(dir, lib_name)
|
285
|
+
"#{dir}#{lib_name}.c"
|
286
|
+
end
|
287
|
+
|
288
|
+
# Runs a compiler.
|
289
|
+
# A subclass can redefine this method.
|
290
|
+
#
|
291
|
+
# @param [String] lib_name a library name.
|
292
|
+
# @param [String] dir a directory name.
|
293
|
+
# @return [void]
|
294
|
+
def build_lib(lib_name, dir='./')
|
295
|
+
file_name = self.class.c_src_file(dir, lib_name)
|
296
|
+
lib_file_name = "#{dir}lib#{lib_name}#{Config::LibExtension}"
|
297
|
+
cmd = "#{build_cmd} #{Config::CoptOutput}#{lib_file_name} #{file_name}"
|
298
|
+
system(cmd)
|
299
|
+
status = $?.exitstatus
|
300
|
+
raise BuildError.new(["exit #{status}"]) if status > 0
|
301
|
+
end
|
302
|
+
|
303
|
+
# Obtains compiler command.
|
304
|
+
# A subclass can redefine this method.
|
305
|
+
#
|
306
|
+
# @return [String] command.
|
307
|
+
def build_cmd
|
308
|
+
Config::Compiler
|
309
|
+
end
|
310
|
+
|
311
|
+
# Prints `#include` derectives.
|
312
|
+
#
|
313
|
+
# @return [void]
|
314
|
+
def headers()
|
315
|
+
Config::Headers.each {|h| @printer << h << :nl }
|
316
|
+
@printer << :nl
|
317
|
+
end
|
318
|
+
|
319
|
+
# Gives a name to each global variable.
|
320
|
+
# @return [void]
|
321
|
+
def name_global_variables()
|
322
|
+
id = 0
|
323
|
+
@typechecker.instance_variables.each do |obj|
|
324
|
+
@gvariables[obj] = "_gvar_#{id}_"
|
325
|
+
id += 1
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Prints variable declarations.
|
330
|
+
# @return [void]
|
331
|
+
def variable_declarations()
|
332
|
+
@gvariables.each do |obj, name|
|
333
|
+
if obj.is_a?(CType::CArray)
|
334
|
+
@printer << 'static ' << c_type(obj.type) << ' ' << name
|
335
|
+
obj.sizes.each {|s| @printer << '[' << s << ']' }
|
336
|
+
@printer << ';' << :nl
|
337
|
+
end
|
338
|
+
end
|
339
|
+
@printer << :nl
|
340
|
+
end
|
341
|
+
|
342
|
+
# Prints a function prototype.
|
343
|
+
#
|
344
|
+
# @param [Def|Block] expr the function.
|
345
|
+
# @return [void]
|
346
|
+
def prototype(expr)
|
347
|
+
t = @typechecker.type(expr)
|
348
|
+
return if ForeignMethodType.role(t)
|
349
|
+
|
350
|
+
@printer << 'static ' if @public_methods[expr].nil?
|
351
|
+
|
352
|
+
fname_str = c_function_name(expr)
|
353
|
+
mt = MethodType.role(t)
|
354
|
+
if mt
|
355
|
+
parameters(expr, fname_str, mt)
|
356
|
+
@printer << ';' << :nl
|
357
|
+
else
|
358
|
+
error(expr, "bad method #{fname_str}")
|
359
|
+
end
|
360
|
+
self
|
361
|
+
end
|
362
|
+
|
363
|
+
# Prints a preamble. This method is invoked right after printing
|
364
|
+
# function prototypes. A subclass can override this method.
|
365
|
+
# The original implementation does not print anything.
|
366
|
+
#
|
367
|
+
def preamble
|
368
|
+
end
|
369
|
+
|
370
|
+
# Appends implicitly generated functions.
|
371
|
+
# A subclass can override this method.
|
372
|
+
# The original implementation does not append any.
|
373
|
+
#
|
374
|
+
# @param [Array<String>] func_names the names of the generated
|
375
|
+
# functions.
|
376
|
+
# @param [Array<Type>] func_names the types of the original methods.
|
377
|
+
# @return [Array<String>, Array<Type>] the names and types.
|
378
|
+
def expand_functions(func_names, func_types)
|
379
|
+
return func_names, func_types
|
380
|
+
end
|
381
|
+
|
382
|
+
# Prints a function implementation.
|
383
|
+
#
|
384
|
+
# @param [Def|Block] expr the function.
|
385
|
+
# @return [void]
|
386
|
+
def c_function(expr)
|
387
|
+
check(expr)
|
388
|
+
end
|
389
|
+
|
390
|
+
# Gets the function name in C after the translation from a Ruby
|
391
|
+
# method into a C function.
|
392
|
+
#
|
393
|
+
# @param [Block|Def|Call] expr an expression.
|
394
|
+
# @return [String] the function name for `expr`.
|
395
|
+
def c_function_name(expr)
|
396
|
+
return expr.name.name if expr.is_a?(Def) &&
|
397
|
+
@public_methods.include?(expr)
|
398
|
+
|
399
|
+
fname_str = @func_names[expr]
|
400
|
+
if fname_str.nil?
|
401
|
+
@func_counter += 1
|
402
|
+
fname_str = if expr.is_a?(Block)
|
403
|
+
"yadriggy_blk#{@func_counter}"
|
404
|
+
else
|
405
|
+
"#{expr.name.name}_#{@func_counter}"
|
406
|
+
end
|
407
|
+
@func_names[expr] = fname_str
|
408
|
+
end
|
409
|
+
fname_str
|
410
|
+
end
|
411
|
+
|
412
|
+
private
|
413
|
+
|
414
|
+
def def_function(expr, fname)
|
415
|
+
t = @typechecker.type(expr)
|
416
|
+
return if ForeignMethodType.role(t)
|
417
|
+
|
418
|
+
@printer << 'static ' if @public_methods[expr].nil?
|
419
|
+
|
420
|
+
mt = MethodType.role(t)
|
421
|
+
if mt
|
422
|
+
parameters(expr, fname, mt)
|
423
|
+
else
|
424
|
+
error(expr, 'not a function')
|
425
|
+
end
|
426
|
+
|
427
|
+
@printer << ' {'
|
428
|
+
@printer.down
|
429
|
+
native_t = NativeMethodType.role(t)
|
430
|
+
if native_t
|
431
|
+
@printer << native_t.body
|
432
|
+
else
|
433
|
+
local_var_declarations(expr)
|
434
|
+
check(expr.body)
|
435
|
+
@printer << ';' unless expr.body.is_a?(Exprs)
|
436
|
+
end
|
437
|
+
@printer.up
|
438
|
+
@printer << '}' << :nl
|
439
|
+
end
|
440
|
+
|
441
|
+
def parameters(expr, fname_str, mtype)
|
442
|
+
ret_type = mtype.result_type
|
443
|
+
@printer << c_type(ret_type) << ' '
|
444
|
+
@printer << fname_str << '('
|
445
|
+
param_types = mtype.params
|
446
|
+
if param_types.is_a?(Array)
|
447
|
+
expr.params.each_with_index do |p, i|
|
448
|
+
@printer << ', ' if i > 0
|
449
|
+
@printer << c_type(param_types[i]) << ' ' << p.name
|
450
|
+
end
|
451
|
+
else
|
452
|
+
error(expr, 'bad parameter types')
|
453
|
+
end
|
454
|
+
@printer << ')'
|
455
|
+
end
|
456
|
+
|
457
|
+
# @param [Def|Block] def_or_block
|
458
|
+
def local_var_declarations(def_or_block)
|
459
|
+
local_vars = @typechecker.local_vars_table[def_or_block]
|
460
|
+
if local_vars.nil?
|
461
|
+
error(def_or_block, 'bad function definition or block')
|
462
|
+
else
|
463
|
+
local_vars.each do |name, type|
|
464
|
+
@printer << c_type(type) << ' ' << name.to_s << ';' << :nl
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
# @param [Type] type
|
470
|
+
def c_type(type)
|
471
|
+
CFI::c_type_name(type)
|
472
|
+
end
|
473
|
+
|
474
|
+
def error(ast, msg)
|
475
|
+
@nerrors += 1
|
476
|
+
@messages << "#{ast.source_location_string}: #{msg}"
|
477
|
+
end
|
478
|
+
|
479
|
+
end # end of CodeGen
|
480
|
+
end
|
481
|
+
end
|