yadriggy 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +3 -0
- data/README.md +12 -9
- data/Rakefile +5 -0
- data/lib/yadriggy.rb +1 -1
- data/lib/yadriggy/algebra.rb +31 -3
- data/lib/yadriggy/assert.rb +8 -8
- data/lib/yadriggy/ast.rb +71 -15
- data/lib/yadriggy/ast_location.rb +1 -1
- data/lib/yadriggy/c/c.rb +5 -5
- data/lib/yadriggy/c/codegen.rb +12 -27
- data/lib/yadriggy/c/config.rb +1 -1
- data/lib/yadriggy/c/ctype.rb +5 -5
- data/lib/yadriggy/c/ctypecheck.rb +6 -6
- data/lib/yadriggy/c/ffi.rb +8 -8
- data/lib/yadriggy/checker.rb +60 -26
- data/lib/yadriggy/eval.rb +5 -1
- data/lib/yadriggy/eval_all.rb +13 -0
- data/lib/yadriggy/pretty_print.rb +14 -6
- data/lib/yadriggy/py.rb +11 -0
- data/lib/yadriggy/py/codegen.rb +457 -0
- data/lib/yadriggy/py/import.rb +90 -0
- data/lib/yadriggy/py/py_typechecker.rb +62 -0
- data/lib/yadriggy/py/python.rb +130 -0
- data/lib/yadriggy/ruby_typecheck.rb +96 -45
- data/lib/yadriggy/ruby_typeinfer.rb +60 -25
- data/lib/yadriggy/source_code.rb +27 -17
- data/lib/yadriggy/syntax.rb +23 -8
- data/lib/yadriggy/type.rb +38 -38
- data/lib/yadriggy/typecheck.rb +18 -5
- data/lib/yadriggy/version.rb +1 -1
- data/yadriggy.gemspec +2 -1
- metadata +24 -4
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright (C) 2018- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy'
|
4
|
+
|
5
|
+
module Yadriggy
|
6
|
+
module Py
|
7
|
+
# The import statement in Python.
|
8
|
+
class Import
|
9
|
+
@@src = ''
|
10
|
+
@@state = 0
|
11
|
+
|
12
|
+
# @api private
|
13
|
+
def self.source
|
14
|
+
src = @@src
|
15
|
+
@@src = ''
|
16
|
+
@@state = 0
|
17
|
+
src
|
18
|
+
end
|
19
|
+
|
20
|
+
# `import` keyword.
|
21
|
+
# @param [String|Symbol] name a module name etc.
|
22
|
+
def import(name)
|
23
|
+
if @@state == 1 || @@state == 2
|
24
|
+
@@src << ', ' << name.to_s
|
25
|
+
@@state = 1
|
26
|
+
elsif @@state == 3
|
27
|
+
@@src << ' import ' << name.to_s
|
28
|
+
@@state = 1
|
29
|
+
else
|
30
|
+
Import.error('import')
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# `as` keyword.
|
36
|
+
# @param [String|Symbol] name an alias.
|
37
|
+
def as(name)
|
38
|
+
if @@state == 1
|
39
|
+
@@src << ' as ' << name.to_s
|
40
|
+
@@state = 2
|
41
|
+
else
|
42
|
+
Import.error('as')
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# `import` keyword.
|
48
|
+
# @param [String|Symbol] name a module name.
|
49
|
+
def self.import(name)
|
50
|
+
error('import') if @@state == 3
|
51
|
+
@@src << "\nimport " << name.to_s
|
52
|
+
@@state = 1
|
53
|
+
Import.new
|
54
|
+
end
|
55
|
+
|
56
|
+
# `from` keyword.
|
57
|
+
# @param [String|Symbol] name a module name.
|
58
|
+
def self.from(name)
|
59
|
+
error('from') if @@state == 3
|
60
|
+
@@src << "\nfrom " << name.to_s
|
61
|
+
@@state = 3
|
62
|
+
Import.new
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api private
|
66
|
+
def self.error(name)
|
67
|
+
self.source
|
68
|
+
raise RuntimeError.new("bad call to Import\##{name}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convenience module.
|
73
|
+
# Use this module by including it.
|
74
|
+
module PyImport
|
75
|
+
# `import` statement.
|
76
|
+
# @param [String|Symbol] name a module name.
|
77
|
+
# @return [Import]
|
78
|
+
def pyimport(name)
|
79
|
+
Import.import(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
# `from ... import` statement.
|
83
|
+
# @param [String|Symbol] name a module name.
|
84
|
+
# @return [Import]
|
85
|
+
def pyfrom(name)
|
86
|
+
Import.from(name)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Copyright (C) 2018- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy/py/python'
|
4
|
+
|
5
|
+
module Yadriggy
|
6
|
+
module Py
|
7
|
+
class PyTypeChecker < RubyTypeChecker
|
8
|
+
def initialize()
|
9
|
+
super(Py::Syntax)
|
10
|
+
@free_variables = Hash.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Hash<Object,String>] all free variables. A hash table from
|
14
|
+
# values to their free variable names.
|
15
|
+
def references
|
16
|
+
@free_variables
|
17
|
+
end
|
18
|
+
|
19
|
+
# Makes the references set empty. The references set is a set of constants
|
20
|
+
# returned by {#references}.
|
21
|
+
#
|
22
|
+
def clear_references
|
23
|
+
@free_variables = Hash.new
|
24
|
+
end
|
25
|
+
|
26
|
+
rule(Name) do
|
27
|
+
type = proceed(ast, type_env)
|
28
|
+
collect_free_variables(ast, type)
|
29
|
+
type
|
30
|
+
end
|
31
|
+
|
32
|
+
# Collect free variables.
|
33
|
+
# @param [Name|VariableCall] an_ast
|
34
|
+
def collect_free_variables(an_ast, type)
|
35
|
+
unless InstanceType.role(type).nil?
|
36
|
+
obj = type.object
|
37
|
+
unless obj.is_a?(Numeric) || obj.is_a?(String) || obj.is_a?(Symbol) || obj.is_a?(Module)
|
38
|
+
@free_variables[obj] = an_ast.name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Computes the type of the {Call} expression
|
44
|
+
# by searching the receiver class for the called method.
|
45
|
+
# If the method is not found or the method is provided by
|
46
|
+
# `Object` or its super class, {DynType} is returned.
|
47
|
+
#
|
48
|
+
# This overrides the super's method but if the called method is not
|
49
|
+
# found, it returns DynType; it does not raise an error.
|
50
|
+
def lookup_ruby_classes(type_env, arg_types, recv_type, method_name)
|
51
|
+
begin
|
52
|
+
mth = Type.get_instance_method_object(recv_type, method_name)
|
53
|
+
rescue CheckError
|
54
|
+
return DynType
|
55
|
+
end
|
56
|
+
return DynType if mth.owner > Object
|
57
|
+
new_tenv = type_env.new_base_tenv(recv_type.exact_type)
|
58
|
+
get_return_type(ast, mth, new_tenv, arg_types)
|
59
|
+
end
|
60
|
+
end # class PyTypeChecker
|
61
|
+
end # module Py
|
62
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Copyright (C) 2018- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'pycall'
|
4
|
+
require 'yadriggy'
|
5
|
+
require 'yadriggy/py/codegen'
|
6
|
+
require 'yadriggy/py/py_typechecker'
|
7
|
+
require 'yadriggy/py/import'
|
8
|
+
|
9
|
+
module Yadriggy
|
10
|
+
module Py
|
11
|
+
|
12
|
+
Syntax = Yadriggy.define_syntax do
|
13
|
+
expr = Name | Number | Super | Binary | Unary | ternary |
|
14
|
+
StringLiteral | Lambda |
|
15
|
+
ArrayLiteral | Paren | lambda_call | fun_call | ArrayRef | HashLiteral
|
16
|
+
stmnt = Return | ForLoop | Loop | if_stmnt | Break |
|
17
|
+
BeginEnd | Def | ModuleDef
|
18
|
+
exprs = Exprs | stmnt | expr
|
19
|
+
|
20
|
+
Name = { name: String }
|
21
|
+
Number = { value: Numeric }
|
22
|
+
VariableCall = Name
|
23
|
+
InstanceVariable = nil
|
24
|
+
GlobalVariable = nil
|
25
|
+
Reserved = Name
|
26
|
+
Const = Name
|
27
|
+
Binary = { left: expr, op: Symbol, right: expr }
|
28
|
+
ArrayRef = { array: expr, indexes: expr }
|
29
|
+
ArrayRefField = ArrayRef
|
30
|
+
Assign = { left: [expr] | expr, op: Symbol,
|
31
|
+
right: [expr] | expr }
|
32
|
+
Dots = Binary
|
33
|
+
Unary = { op: Symbol, operand: expr }
|
34
|
+
StringLiteral = { value: String }
|
35
|
+
ArrayLiteral = { elements: ForLoop | [ expr ] }
|
36
|
+
Paren = { expression: expr }
|
37
|
+
HashLiteral = { pairs: [ (expr|Label|SymbolLiteral) * expr ] }
|
38
|
+
Return = { values: [ expr ] }
|
39
|
+
ForLoop = {vars: [ Identifier ], set: expr, body: exprs }
|
40
|
+
Loop = { op: :while, cond: expr, body: exprs }
|
41
|
+
Break = { values: nil }
|
42
|
+
if_stmnt = Conditional + { op: :if, cond: expr, then: exprs,
|
43
|
+
all_elsif: [expr * exprs], else: (exprs) }
|
44
|
+
ternary = Conditional + { op: :ifop, cond: expr, then: expr,
|
45
|
+
all_elsif: nil, else: expr }
|
46
|
+
Parameters = { params: [ Identifier ],
|
47
|
+
optionals: [ Identifier * expr ],
|
48
|
+
rest_of_params: (Identifier),
|
49
|
+
params_after_rest: [ Identifier ],
|
50
|
+
keywords: [ Label * expr ],
|
51
|
+
rest_of_keywords: (Identifier),
|
52
|
+
block_param: (Identifier) }
|
53
|
+
Block = Parameters + { body: exprs }
|
54
|
+
Lambda = Block + { body: expr } # -> (x) { x + 1 }
|
55
|
+
lambda_name = { name: "lambda" }
|
56
|
+
lambda_call = Call + { receiver: nil, op: nil, name: lambda_name,
|
57
|
+
args: nil, block_arg: nil, block: Block }
|
58
|
+
fun_call = Call + { receiver: (expr), op: (Symbol), name: Identifier,
|
59
|
+
args: [ expr ], block_arg: nil, block: nil }
|
60
|
+
Command = fun_call
|
61
|
+
Exprs = { expressions: [ exprs ] }
|
62
|
+
Rescue = { types: [ Const | ConstPathRef ],
|
63
|
+
parameter: (Identifier),
|
64
|
+
body: (exprs), nested_rescue: (Rescue),
|
65
|
+
else: (exprs), ensure: (exprs) }
|
66
|
+
BeginEnd = { body: exprs, rescue: (Rescue) }
|
67
|
+
Def = Parameters +
|
68
|
+
{ singular: (expr), name: Identifier, body: exprs,
|
69
|
+
rescue: (Rescue) }
|
70
|
+
ModuleDef = { name: Const | ConstPathRef, body: exprs,
|
71
|
+
rescue: (Rescue) }
|
72
|
+
ClassDef = ModuleDef +
|
73
|
+
{ superclass: (Const | ConstPathRef) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.run(&blk)
|
77
|
+
ast = Yadriggy::reify(blk)
|
78
|
+
Syntax.raise_error unless Syntax.check(ast.tree)
|
79
|
+
checker = PyTypeChecker.new
|
80
|
+
checker.typecheck(ast.tree.body)
|
81
|
+
PyCall.exec(Import.source)
|
82
|
+
init_free_variables(checker)
|
83
|
+
gen = CodeGen.new(Printer.new, checker)
|
84
|
+
ast.astrees.each {|t| gen.print(t) unless t == ast }
|
85
|
+
last_expr = generate_except_last(ast.tree.body, gen)
|
86
|
+
PyCall.exec(gen.printer.output)
|
87
|
+
unless last_expr.nil?
|
88
|
+
PyCall.eval(CodeGen.new(Printer.new, checker).print(last_expr).printer.output)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.init_free_variables(checker)
|
93
|
+
unless checker.references.empty?
|
94
|
+
gen = CodeGen.new(Printer.new, checker)
|
95
|
+
args = gen.print_free_vars_initializer
|
96
|
+
PyCall.exec(gen.printer.output)
|
97
|
+
f = PyCall.eval(CodeGen::FreeVarInitName)
|
98
|
+
f.call(args)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.generate_except_last(ast, gen)
|
103
|
+
if expr_or_subtype(ast)
|
104
|
+
ast
|
105
|
+
elsif ast.is_a?(Exprs) && expr_or_subtype(ast.expressions[-1])
|
106
|
+
ast.expressions[0...-1].each do |e|
|
107
|
+
gen.print(e)
|
108
|
+
gen.newline
|
109
|
+
end
|
110
|
+
ast.expressions[-1]
|
111
|
+
else
|
112
|
+
gen.print(ast)
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.expr_or_subtype(ast)
|
118
|
+
if ast.nil? || ast.is_a?(Assign)
|
119
|
+
false
|
120
|
+
else
|
121
|
+
usertype = ast.usertype
|
122
|
+
if usertype == :fun_call
|
123
|
+
ast.name.name != 'print'
|
124
|
+
else
|
125
|
+
usertype == :expr || usertype == :lambda_call || usertype == :ternary
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -1,29 +1,26 @@
|
|
1
1
|
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
2
|
|
3
3
|
require 'yadriggy/typecheck'
|
4
|
-
require 'set'
|
5
4
|
|
6
5
|
module Yadriggy
|
7
|
-
# Type checker for Ruby
|
6
|
+
# Type checker for Ruby.
|
7
|
+
# Most values are typed as DynType but local variables
|
8
|
+
# are identified. So `type(ast)` returns
|
9
|
+
# a {LocalVarType} object if `ast` is of a local variable.
|
10
|
+
# A {LocalVarType} is a {Type} object that represents not
|
11
|
+
# only the value's type but also the fact that the value
|
12
|
+
# comes from a local variable.
|
13
|
+
#
|
14
|
+
# The type of a free variable is the type of the current value
|
15
|
+
# of that variable. {#type} returns an {InstanceType} object.
|
16
|
+
#
|
17
|
+
# This checker also attempts to recursivly trace a call-graph
|
18
|
+
# to reify and type-check the ASTs of the called methods.
|
8
19
|
#
|
9
20
|
class RubyTypeChecker < TypeChecker
|
10
21
|
def initialize(syntax=nil)
|
11
22
|
super()
|
12
23
|
@syntax = syntax
|
13
|
-
@referred_objects = Set.new
|
14
|
-
end
|
15
|
-
|
16
|
-
# @return [Set<Object>] all the constants that the type-checked code has
|
17
|
-
# referred to. Numbers and classes are excluced.
|
18
|
-
def references()
|
19
|
-
@referred_objects
|
20
|
-
end
|
21
|
-
|
22
|
-
# Makes the references set empty. The references set is a set of constants
|
23
|
-
# returned by {#references}.
|
24
|
-
#
|
25
|
-
def clear_references
|
26
|
-
@referred_objects = Set.new
|
27
24
|
end
|
28
25
|
|
29
26
|
# Typing rules
|
@@ -33,38 +30,56 @@ module Yadriggy
|
|
33
30
|
# the current value of that variable and gives the value type to that AST.
|
34
31
|
|
35
32
|
rule(Assign) do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
if vtype.nil?
|
44
|
-
bind_local_var(type_env, ast.left, rtype)
|
33
|
+
if ast.left.is_a?(Array)
|
34
|
+
type_multi_assign(ast.left, ast.op, ast.right)
|
35
|
+
else
|
36
|
+
ast_right = ast.right
|
37
|
+
if ast_right.is_a?(Array)
|
38
|
+
ast_right.each {|e| type(e) }
|
39
|
+
type_assign(ast.left, ast.op, DynType)
|
45
40
|
else
|
46
|
-
|
47
|
-
LocalVarType.role(vtype)&.definition = ast.left
|
48
|
-
vtype
|
41
|
+
type_assign(ast.left, ast.op, type(ast_right))
|
49
42
|
end
|
50
|
-
else
|
51
|
-
ltype
|
52
43
|
end
|
53
44
|
end
|
54
45
|
|
55
|
-
|
56
|
-
|
46
|
+
# @api private
|
47
|
+
# @param [Array(ASTnode)] ast_left left operand
|
48
|
+
#
|
49
|
+
def type_multi_assign(ast_left, ast_op, ast_right)
|
50
|
+
if ast_right.is_a?(Array)
|
51
|
+
rtypes = ast_right.map {|e| type(e) }
|
52
|
+
ast_left.each_with_index do |v, i|
|
53
|
+
type_assign(v, ast_op,
|
54
|
+
i < rtypes.size ? rtypes[i] : RubyClass::NilClass)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
type(ast_right)
|
58
|
+
ast_left.each_with_index {|v, i| type_assign(v, ast_op, DynType) }
|
59
|
+
end
|
60
|
+
DynType
|
57
61
|
end
|
58
62
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
# @api private
|
64
|
+
# @param [ASTnode] ast_left the left operand
|
65
|
+
# @param [Type] rtype the type of the right operand.
|
66
|
+
def type_assign(ast_left, ast_op, rtype)
|
67
|
+
ltype = type(ast_left)
|
68
|
+
if ast_op != :'=' # if op is += etc.
|
69
|
+
LocalVarType.role(ltype)&.definition = ast_left
|
70
|
+
elsif ast_left.is_a?(IdentifierOrCall)
|
71
|
+
vtype = type_env.bound_name?(ast_left)
|
72
|
+
if vtype.nil?
|
73
|
+
bind_local_var(type_env, ast_left, DynType)
|
74
|
+
else
|
75
|
+
LocalVarType.role(vtype)&.definition = ast_left
|
65
76
|
end
|
66
77
|
end
|
67
|
-
|
78
|
+
DynType
|
79
|
+
end
|
80
|
+
|
81
|
+
rule(Name) do
|
82
|
+
get_name_type(ast, type_env)
|
68
83
|
end
|
69
84
|
|
70
85
|
# Gets the type of a given name, which may be a local variable
|
@@ -122,11 +137,13 @@ module Yadriggy
|
|
122
137
|
|
123
138
|
rule(Unary) do
|
124
139
|
type(ast.operand)
|
140
|
+
DynType
|
125
141
|
end
|
126
142
|
|
127
143
|
rule(Binary) do
|
128
144
|
type(ast.right)
|
129
145
|
type(ast.left)
|
146
|
+
DynType
|
130
147
|
end
|
131
148
|
|
132
149
|
rule(Dots) do
|
@@ -151,6 +168,7 @@ module Yadriggy
|
|
151
168
|
end
|
152
169
|
|
153
170
|
rule(ArrayLiteral) do
|
171
|
+
ast.elements.each {|t| type(t) }
|
154
172
|
RubyClass::Array
|
155
173
|
end
|
156
174
|
|
@@ -159,6 +177,7 @@ module Yadriggy
|
|
159
177
|
end
|
160
178
|
|
161
179
|
rule(HashLiteral) do
|
180
|
+
ast.pairs.each {|kv| type(kv[1]) }
|
162
181
|
RubyClass::Hash
|
163
182
|
end
|
164
183
|
|
@@ -191,17 +210,27 @@ module Yadriggy
|
|
191
210
|
# get_return_type().
|
192
211
|
#
|
193
212
|
rule(Call) do
|
194
|
-
|
213
|
+
# f.() is equivalent to f.call()
|
214
|
+
method_name = ast.name ? ast.name.to_sym : 'call'
|
195
215
|
if method_name == :lambda
|
216
|
+
type_args_and_block(ast)
|
196
217
|
RubyClass::Proc
|
197
218
|
elsif method_name == :raise
|
219
|
+
type_args_and_block(ast)
|
198
220
|
RubyClass::Exception
|
199
221
|
else
|
200
222
|
get_call_expr_type(ast, type_env, method_name)
|
201
223
|
end
|
202
224
|
end
|
203
225
|
|
226
|
+
def type_args_and_block(call_ast)
|
227
|
+
call_ast.args.each {|t| type(t) }
|
228
|
+
type(call_ast.block)
|
229
|
+
end
|
230
|
+
|
204
231
|
rule(ArrayRef) do
|
232
|
+
type(ast.array)
|
233
|
+
ast.indexes.each {|t| type(t) }
|
205
234
|
DynType
|
206
235
|
end
|
207
236
|
|
@@ -225,6 +254,7 @@ module Yadriggy
|
|
225
254
|
|
226
255
|
rule(ForLoop) do
|
227
256
|
ast.vars.each {|v| bind_local_var(type_env, v, DynType) }
|
257
|
+
type(ast.set)
|
228
258
|
type(ast.body)
|
229
259
|
DynType
|
230
260
|
end
|
@@ -311,6 +341,7 @@ module Yadriggy
|
|
311
341
|
end
|
312
342
|
type_assert_subsume(mtype.result, res_t, 'bad result type')
|
313
343
|
end
|
344
|
+
bind_local_var(type_env, ast.name, mtype)
|
314
345
|
mtype
|
315
346
|
end
|
316
347
|
|
@@ -379,6 +410,13 @@ module Yadriggy
|
|
379
410
|
# @return [ResultType] the type of the resulting value.
|
380
411
|
def get_call_expr_type(call_ast, type_env, method_name)
|
381
412
|
arg_types = call_ast.args.map {|t| type(t) }
|
413
|
+
get_call_expr_type_with_argtypes(call_ast, type_env, method_name,
|
414
|
+
arg_types)
|
415
|
+
end
|
416
|
+
|
417
|
+
# @api private
|
418
|
+
def get_call_expr_type_with_argtypes(call_ast, type_env, method_name,
|
419
|
+
arg_types)
|
382
420
|
type(call_ast.block_arg)
|
383
421
|
type(call_ast.block)
|
384
422
|
|
@@ -413,7 +451,10 @@ module Yadriggy
|
|
413
451
|
end
|
414
452
|
end
|
415
453
|
|
416
|
-
# @private
|
454
|
+
# @api private
|
455
|
+
# Attempts to find a method by {TypeChecker#typedef}, which
|
456
|
+
# searches the method table in this typechecker.
|
457
|
+
#
|
417
458
|
def lookup_builtin(recv_type, method_name)
|
418
459
|
et = recv_type.exact_type
|
419
460
|
if DynType == et
|
@@ -428,7 +469,15 @@ module Yadriggy
|
|
428
469
|
end
|
429
470
|
end
|
430
471
|
|
431
|
-
#
|
472
|
+
# Computes the type of the {Call} expression
|
473
|
+
# by searching the receiver class for the called method.
|
474
|
+
#
|
475
|
+
# @param [TypeEnv] type_env a type environment.
|
476
|
+
# @param [Array<Type>] arg_types the types of the actual arguments.
|
477
|
+
# @param [Type] recv_type the receiver type.
|
478
|
+
# @param [String|Symbol] method_name the name of the called method.
|
479
|
+
# @raise [CheckError] if the method is not found in the receiver class.
|
480
|
+
# @return [ResultType] the result type.
|
432
481
|
def lookup_ruby_classes(type_env, arg_types, recv_type, method_name)
|
433
482
|
begin
|
434
483
|
mth = Type.get_instance_method_object(recv_type, method_name)
|
@@ -443,14 +492,17 @@ module Yadriggy
|
|
443
492
|
# It returns a {ResultType}.
|
444
493
|
#
|
445
494
|
# Override this method to delimit reification. The implementation
|
446
|
-
# in this class reifies any method.
|
495
|
+
# in this class reifies any method. If its source code is not found,
|
496
|
+
# {#get_return_type} reports an error.
|
497
|
+
#
|
498
|
+
# This method {#get_return_type} does not have to
|
447
499
|
# return a ResultType, which can be used in a later phase to
|
448
500
|
# obtain the invoked method.
|
449
501
|
# This method is invoked by rule(Call). See rule(Call) for more details.
|
450
502
|
#
|
451
503
|
# @param [Call] an_ast the Call node.
|
452
504
|
# @param [Proc|Method|UnboundMethod] mthd the method invoked by an_ast.
|
453
|
-
# if `mthd` is nil, {get_return_type} reports an error.
|
505
|
+
# if `mthd` is nil, {#get_return_type} reports an error.
|
454
506
|
# @param [TypeEnv] new_tenv a type environment.
|
455
507
|
# @param [Array<Type>] arg_types the types of the actual arguments.
|
456
508
|
# @return [ResultType] the result type.
|
@@ -458,7 +510,6 @@ module Yadriggy
|
|
458
510
|
m_ast = an_ast.root.reify(mthd)
|
459
511
|
type_assert_false(m_ast.nil?, "no source code: for #{mthd}")
|
460
512
|
(@syntax.check(m_ast.tree) || @syntax.raise_error) if @syntax
|
461
|
-
|
462
513
|
mtype = MethodType.role(type(m_ast.tree, new_tenv))
|
463
514
|
type_assert(mtype, 'not a method type')
|
464
515
|
type_assert_params(mtype.params, arg_types, 'argument type mismatch')
|