yadriggy 1.1.0 → 1.2.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 +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')
|