typed.rb 0.0.11
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/Rakefile +26 -0
- data/bin/typed.rb +110 -0
- data/lib/typed/language.rb +131 -0
- data/lib/typed/model/tm_abs.rb +104 -0
- data/lib/typed/model/tm_array_literal.rb +25 -0
- data/lib/typed/model/tm_boolean.rb +15 -0
- data/lib/typed/model/tm_boolean_operator.rb +34 -0
- data/lib/typed/model/tm_break.rb +24 -0
- data/lib/typed/model/tm_case_when.rb +38 -0
- data/lib/typed/model/tm_class.rb +63 -0
- data/lib/typed/model/tm_const.rb +29 -0
- data/lib/typed/model/tm_defined.rb +19 -0
- data/lib/typed/model/tm_error.rb +16 -0
- data/lib/typed/model/tm_float.rb +15 -0
- data/lib/typed/model/tm_for.rb +42 -0
- data/lib/typed/model/tm_fun.rb +165 -0
- data/lib/typed/model/tm_global_var.rb +22 -0
- data/lib/typed/model/tm_global_var_assignment.rb +20 -0
- data/lib/typed/model/tm_hash_literal.rb +32 -0
- data/lib/typed/model/tm_if_else.rb +24 -0
- data/lib/typed/model/tm_instance_var.rb +23 -0
- data/lib/typed/model/tm_instance_var_assignment.rb +32 -0
- data/lib/typed/model/tm_int.rb +15 -0
- data/lib/typed/model/tm_local_var_asgn.rb +35 -0
- data/lib/typed/model/tm_mass_asgn.rb +60 -0
- data/lib/typed/model/tm_mlhs.rb +87 -0
- data/lib/typed/model/tm_module.rb +51 -0
- data/lib/typed/model/tm_next.rb +24 -0
- data/lib/typed/model/tm_nil.rb +14 -0
- data/lib/typed/model/tm_range_literal.rb +30 -0
- data/lib/typed/model/tm_regexp.rb +27 -0
- data/lib/typed/model/tm_rescue.rb +27 -0
- data/lib/typed/model/tm_return.rb +24 -0
- data/lib/typed/model/tm_s_class.rb +30 -0
- data/lib/typed/model/tm_self.rb +22 -0
- data/lib/typed/model/tm_send.rb +300 -0
- data/lib/typed/model/tm_sequencing.rb +53 -0
- data/lib/typed/model/tm_string.rb +15 -0
- data/lib/typed/model/tm_string_interpolation.rb +21 -0
- data/lib/typed/model/tm_super.rb +27 -0
- data/lib/typed/model/tm_symbol.rb +15 -0
- data/lib/typed/model/tm_symbol_interpolation.rb +21 -0
- data/lib/typed/model/tm_try.rb +29 -0
- data/lib/typed/model/tm_var.rb +28 -0
- data/lib/typed/model/tm_while.rb +43 -0
- data/lib/typed/model.rb +48 -0
- data/lib/typed/prelude.rb +939 -0
- data/lib/typed/prelude_existential_registry.bin +0 -0
- data/lib/typed/prelude_generic_registry.bin +0 -0
- data/lib/typed/prelude_registry.bin +0 -0
- data/lib/typed/runtime/ast_parser.rb +589 -0
- data/lib/typed/runtime/method_signature_processor.rb +72 -0
- data/lib/typed/runtime/normalization/validations.rb +47 -0
- data/lib/typed/runtime/normalization.rb +196 -0
- data/lib/typed/runtime/parser_context.rb +36 -0
- data/lib/typed/runtime/type_parser.rb +215 -0
- data/lib/typed/runtime/type_registry.rb +170 -0
- data/lib/typed/runtime/type_signature_processor.rb +34 -0
- data/lib/typed/runtime.rb +33 -0
- data/lib/typed/type_signature/parser.rb +240 -0
- data/lib/typed/types/polymorphism/existential_type_variable.rb +13 -0
- data/lib/typed/types/polymorphism/generic_comparisons.rb +134 -0
- data/lib/typed/types/polymorphism/generic_variables.rb +24 -0
- data/lib/typed/types/polymorphism/type_variable.rb +138 -0
- data/lib/typed/types/polymorphism/type_variable_register.rb +298 -0
- data/lib/typed/types/polymorphism/unification.rb +579 -0
- data/lib/typed/types/ty_boolean.rb +15 -0
- data/lib/typed/types/ty_dynamic.rb +39 -0
- data/lib/typed/types/ty_either.rb +168 -0
- data/lib/typed/types/ty_error.rb +18 -0
- data/lib/typed/types/ty_existential_type.rb +22 -0
- data/lib/typed/types/ty_function.rb +144 -0
- data/lib/typed/types/ty_generic_function.rb +115 -0
- data/lib/typed/types/ty_generic_object.rb +180 -0
- data/lib/typed/types/ty_generic_singleton_object.rb +238 -0
- data/lib/typed/types/ty_object.rb +256 -0
- data/lib/typed/types/ty_singleton_object.rb +78 -0
- data/lib/typed/types/ty_stack_jump.rb +44 -0
- data/lib/typed/types/ty_top_level_object.rb +38 -0
- data/lib/typed/types.rb +60 -0
- data/lib/typed/typing_context.rb +206 -0
- data/lib/typed/version.rb +3 -0
- data/lib/typed.rb +161 -0
- data/spec/lib/ast_parser_spec.rb +101 -0
- data/spec/lib/examples/animals.rb +44 -0
- data/spec/lib/examples/counter.rb +16 -0
- data/spec/lib/examples/if.rb +31 -0
- data/spec/lib/language_spec.rb +36 -0
- data/spec/lib/model/tm_abs_spec.rb +66 -0
- data/spec/lib/model/tm_array_literal_spec.rb +36 -0
- data/spec/lib/model/tm_case_when_spec.rb +39 -0
- data/spec/lib/model/tm_class_spec.rb +67 -0
- data/spec/lib/model/tm_defined_spec.rb +10 -0
- data/spec/lib/model/tm_for_spec.rb +150 -0
- data/spec/lib/model/tm_fun_spec.rb +11 -0
- data/spec/lib/model/tm_hash_literal_spec.rb +40 -0
- data/spec/lib/model/tm_mass_asgn_spec.rb +104 -0
- data/spec/lib/model/tm_module_spec.rb +42 -0
- data/spec/lib/model/tm_regexp_spec.rb +9 -0
- data/spec/lib/model/tm_return_spec.rb +47 -0
- data/spec/lib/model/tm_s_class_spec.rb +27 -0
- data/spec/lib/model/tm_self_spec.rb +19 -0
- data/spec/lib/model/tm_string_interpolation_spec.rb +10 -0
- data/spec/lib/model/tm_symbol_interpolation_spec.rb +10 -0
- data/spec/lib/model/tm_symbol_spec.rb +9 -0
- data/spec/lib/model/tm_while_spec.rb +141 -0
- data/spec/lib/polymorphism/type_variable_spec.rb +14 -0
- data/spec/lib/polymorphism/unification_spec.rb +328 -0
- data/spec/lib/prelude/array_spec.rb +263 -0
- data/spec/lib/prelude/class_spec.rb +12 -0
- data/spec/lib/prelude/enumerable_spec.rb +278 -0
- data/spec/lib/prelude/enumerator_spec.rb +101 -0
- data/spec/lib/prelude/hash_spec.rb +361 -0
- data/spec/lib/prelude/kernel_spec.rb +23 -0
- data/spec/lib/prelude/object_spec.rb +22 -0
- data/spec/lib/prelude/pair_spec.rb +16 -0
- data/spec/lib/prelude/showable_spec.rb +31 -0
- data/spec/lib/prelude/string_spec.rb +98 -0
- data/spec/lib/runtime/normalization_spec.rb +29 -0
- data/spec/lib/runtime/validations_spec.rb +56 -0
- data/spec/lib/runtime_spec.rb +503 -0
- data/spec/lib/type_signature/parser_spec.rb +239 -0
- data/spec/lib/types/comparisons_spec.rb +35 -0
- data/spec/lib/types/polymorphism/generic_comparisons_spec.rb +492 -0
- data/spec/lib/types/polymorphism/type_variable_register_spec.rb +128 -0
- data/spec/lib/types/ty_dynamic_spec.rb +103 -0
- data/spec/lib/types/ty_either_spec.rb +288 -0
- data/spec/lib/types/ty_error_spec.rb +18 -0
- data/spec/lib/types/ty_generic_object_spec.rb +78 -0
- data/spec/lib/types/ty_generic_singleton_object_spec.rb +288 -0
- data/spec/lib/types/typing_context_spec.rb +86 -0
- data/spec/lib/types_spec.rb +174 -0
- data/spec/lib/typing/boolean_asgn_spec.rb +134 -0
- data/spec/lib/typing/break_spec.rb +79 -0
- data/spec/lib/typing/generics_spec.rb +191 -0
- data/spec/lib/typing/instance_vars_spec.rb +103 -0
- data/spec/lib/typing/next_spec.rb +29 -0
- data/spec/lib/typing/op_asgn_spec.rb +104 -0
- data/spec/lib/typing/overriden_methods_spec.rb +31 -0
- data/spec/lib/typing/subtyping_spec.rb +112 -0
- data/spec/lib/typing/tm_boolean_operator_spec.rb +100 -0
- data/spec/lib/typing/tm_boolean_spec.rb +61 -0
- data/spec/lib/typing/tm_const_spec.rb +28 -0
- data/spec/lib/typing/tm_defined_spec.rb +12 -0
- data/spec/lib/typing/tm_fun_spec.rb +347 -0
- data/spec/lib/typing/tm_global_var_spec.rb +33 -0
- data/spec/lib/typing/tm_if_else_spec.rb +104 -0
- data/spec/lib/typing/tm_ignore_spec.rb +24 -0
- data/spec/lib/typing/tm_instance_vars_spec.rb +117 -0
- data/spec/lib/typing/tm_local_var_asgn_spec.rb +134 -0
- data/spec/lib/typing/tm_mlhs_spec.rb +164 -0
- data/spec/lib/typing/tm_module_spec.rb +89 -0
- data/spec/lib/typing/tm_raise_spec.rb +31 -0
- data/spec/lib/typing/tm_range_literal_spec.rb +25 -0
- data/spec/lib/typing/tm_regexp_spec.rb +14 -0
- data/spec/lib/typing/tm_return_spec.rb +45 -0
- data/spec/lib/typing/tm_send_casting_spec.rb +26 -0
- data/spec/lib/typing/tm_send_class_methods_spec.rb +42 -0
- data/spec/lib/typing/tm_send_generic_apply_spec.rb +103 -0
- data/spec/lib/typing/tm_send_generic_methods_spec.rb +77 -0
- data/spec/lib/typing/tm_send_initialize_spec.rb +68 -0
- data/spec/lib/typing/tm_send_lambda_spec.rb +135 -0
- data/spec/lib/typing/tm_send_spec.rb +217 -0
- data/spec/lib/typing/tm_send_yield_block_spec.rb +308 -0
- data/spec/lib/typing/tm_sequencing_spec.rb +174 -0
- data/spec/lib/typing/tm_string_interpolation_spec.rb +19 -0
- data/spec/lib/typing/tm_super_spec.rb +63 -0
- data/spec/lib/typing/tm_symbol_interpolation_spec.rb +19 -0
- data/spec/lib/typing/tm_symbol_spec.rb +14 -0
- data/spec/lib/typing/tm_try_spec.rb +73 -0
- data/spec/spec_helper.rb +140 -0
- metadata +216 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module TypedRb
|
2
|
+
module Runtime
|
3
|
+
module Normalization
|
4
|
+
module Validations
|
5
|
+
def validate_signature(type, normalized_signature)
|
6
|
+
return if type == :class_variable || type == :instance_variable
|
7
|
+
return if normalized_signature.is_a?(TypedRb::Types::TyFunction)
|
8
|
+
fail ::TypedRb::Types::TypeParsingError,
|
9
|
+
"Error parsing receiver, method signature: #{type} :: '#{normalized_signature}'"
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate_signatures(normalized_signatures, klass, method)
|
13
|
+
arities = normalized_signatures.map(&:arity)
|
14
|
+
duplicated_arities = arities.select { |arity| arities.count(arity) > 1 }
|
15
|
+
duplicated_arities.each do |arity|
|
16
|
+
duplicated = normalized_signatures.select { |signature| signature.arity == arity }
|
17
|
+
unless duplicated.count == 2 && duplicated.first.block_type.nil? != duplicated.last.block_type.nil?
|
18
|
+
error_message = "Duplicated arity '#{arity}' for method '#{klass}' / '#{method}'"
|
19
|
+
fail ::TypedRb::Types::TypeParsingError, error_message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_method(class_methods_info, klass, method, method_type)
|
25
|
+
if method_type == :instance
|
26
|
+
unless (class_methods_info[:instance_methods]).include?(method.to_sym)
|
27
|
+
fail ::TypedRb::Types::TypeParsingError,
|
28
|
+
"Declared typed instance method '#{method}' not found for class '#{klass}'"
|
29
|
+
end
|
30
|
+
elsif method_type == :class
|
31
|
+
unless class_methods_info[:all_methods].include?(method.to_sym)
|
32
|
+
fail ::TypedRb::Types::TypeParsingError,
|
33
|
+
"Declared typed class method '#{method}' not found for class '#{klass}'"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_function_signature(klass, method, signature, method_type)
|
39
|
+
if signature.is_a?(Hash)
|
40
|
+
join = method_type == :instance ? '#' : '::'
|
41
|
+
fail ::TypedRb::Types::TypeParsingError, "Declared method #{klass}#{join}#{method}(#{signature}) is not a valid arrow"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require_relative '../runtime'
|
2
|
+
|
3
|
+
module TypedRb
|
4
|
+
module Runtime
|
5
|
+
module Normalization
|
6
|
+
include Validations
|
7
|
+
ts '#normalize_generic_types! / -> unit'
|
8
|
+
def normalize_generic_types!
|
9
|
+
initial_value = @generic_types_registry || Hash.call(Class, TypedRb::Types::TyGenericSingletonObject).new
|
10
|
+
@generic_types_registry = generic_types_parser_registry.each_with_object(initial_value) do |type_info, acc|
|
11
|
+
generic_singleton_object = build_generic_singleton_object(type_info)
|
12
|
+
acc[generic_singleton_object.ruby_type] = generic_singleton_object
|
13
|
+
end
|
14
|
+
generic_types_parser_registry.each do |type_info|
|
15
|
+
check_generic_super_type(type_info)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ts '#normalize_methods! / -> unit'
|
20
|
+
def normalize_methods!
|
21
|
+
@registry = @registry || {}
|
22
|
+
parser_registry.each_pair do |object_key, method_signatures|
|
23
|
+
method_type, class_name = parse_object_key(object_key)
|
24
|
+
klass = parse_class(class_name)
|
25
|
+
@registry[[method_type, klass]] = normalize_method_signatures(method_signatures, klass, method_type)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ts '#check_super_type_annotations / -> unit'
|
30
|
+
def check_super_type_annotations
|
31
|
+
@generic_types_registry.values.each do |type|
|
32
|
+
type.super_type.self_materialize if type.super_type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_generic_singleton_object(type_info)
|
37
|
+
type_class, info = type_info
|
38
|
+
TypedRb.log(binding, :debug, "Normalising generic type: #{type_class}")
|
39
|
+
info[:type] = Class.for_name(type_class)
|
40
|
+
info[:parameters] = info[:parameters].map do |parameter|
|
41
|
+
::TypedRb::Runtime::TypeParser.parse(parameter, info[:type])
|
42
|
+
end
|
43
|
+
::TypedRb::Types::TyGenericSingletonObject.new(info[:type], info[:parameters])
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_generic_super_type(type_info)
|
47
|
+
_, info = type_info
|
48
|
+
super_type = build_generic_super_type(info)
|
49
|
+
@generic_types_registry[info[:type]].super_type = super_type if super_type
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_generic_super_type(info)
|
53
|
+
with_super_type = valid_super_type?(info[:type], info[:super_type])
|
54
|
+
if with_super_type
|
55
|
+
TypedRb.log(binding, :debug, "Normalising generic super type: #{info[:super_type][:type]} for #{info[:type]}")
|
56
|
+
build_generic_singleton_object([info[:super_type][:type], info[:super_type]])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def valid_super_type?(base_class, super_type_info)
|
61
|
+
return false if super_type_info.nil?
|
62
|
+
valid = base_class.ancestors.map(&:name).detect { |klass_name| klass_name == super_type_info[:type].to_s }
|
63
|
+
return true if valid
|
64
|
+
fail ::TypedRb::Types::TypeParsingError,
|
65
|
+
"Super type annotation '#{super_type_info[:type]}' not a super class of '#{base_class}'"
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_class(class_name)
|
69
|
+
return :main if class_name == :main
|
70
|
+
Class.for_name(class_name.to_s)
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_methods(klass)
|
74
|
+
return find_methods_for_top_level_object if klass == :main
|
75
|
+
find_methods_for_class(klass)
|
76
|
+
end
|
77
|
+
|
78
|
+
def collect_methods(object, options)
|
79
|
+
messages = if options[:instance]
|
80
|
+
[:public_instance_methods, :protected_instance_methods, :private_instance_methods]
|
81
|
+
else
|
82
|
+
[:public_methods, :protected_methods, :private_methods]
|
83
|
+
end
|
84
|
+
messages.inject([]) do |acc, message|
|
85
|
+
acc + object.send(message)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def find_methods_for_top_level_object
|
90
|
+
all_instance_methods = collect_methods(TOPLEVEL_BINDING, instance: false)
|
91
|
+
all_methods = collect_methods(TOPLEVEL_BINDING.receiver.class, instance: false)
|
92
|
+
build_class_methods_info(:main, all_instance_methods, all_methods)
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_methods_for_class(klass)
|
96
|
+
all_instance_methods = collect_methods(klass, instance: true)
|
97
|
+
all_methods = collect_methods(klass, instance: false)
|
98
|
+
build_class_methods_info(klass, all_instance_methods, all_methods)
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_class_methods_info(klass, all_instance_methods, all_methods)
|
102
|
+
{
|
103
|
+
:class => klass,
|
104
|
+
:instance_methods => all_instance_methods,
|
105
|
+
:all_methods => all_methods
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
ts '#normalize_signature! / Class -> String -> TypedRb::Types::TyFunction'
|
110
|
+
def normalize_signature!(klass, type)
|
111
|
+
normalized_signature = ::TypedRb::Runtime::TypeParser.parse(type, klass)
|
112
|
+
::TypedRb::Model::TmFun.with_fresh_bindings(klass, normalized_signature)
|
113
|
+
end
|
114
|
+
|
115
|
+
def normalize_method_signatures(method_signatures, klass, method_type)
|
116
|
+
method_signatures.each_with_object({}) do |method_info, signatures_acc|
|
117
|
+
method, signatures = method_info
|
118
|
+
validate_method(find_methods(klass), klass, method, method_type)
|
119
|
+
normalized_signatures = signatures.map do |signature|
|
120
|
+
validate_function_signature(klass, method, signature, method_type)
|
121
|
+
normalized_method = normalize_signature!(klass, signature)
|
122
|
+
validate_signature(method_type, normalized_method)
|
123
|
+
compute_parameters_info(method_type, klass, method, normalized_method, signature)
|
124
|
+
normalized_method
|
125
|
+
end
|
126
|
+
if method_type == :instance_variable || method_type == :class_variable
|
127
|
+
# TODO: print a warning if the declaration of the variable is duplicated
|
128
|
+
signatures_acc[method] = normalized_signatures.first
|
129
|
+
else
|
130
|
+
validate_signatures(normalized_signatures, klass, method)
|
131
|
+
signatures_acc[method] = normalized_signatures.sort { |fa, fb| fa.arity <=> fb.arity }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def compute_parameters_info(method_type, klass, method, normalized_method, signature)
|
137
|
+
return if method_type == :instance_variable || method_type == :class_variable
|
138
|
+
ruby_params = if method_type == :instance
|
139
|
+
if klass == :main
|
140
|
+
TOPLEVEL_BINDING.receiver.method(method).parameters
|
141
|
+
else
|
142
|
+
klass.instance_method(method).parameters
|
143
|
+
end
|
144
|
+
else
|
145
|
+
if klass == :main
|
146
|
+
TOPLEVEL_BINDING.receiver.class.method(method).parameters
|
147
|
+
else
|
148
|
+
klass.method(method).parameters
|
149
|
+
end
|
150
|
+
end
|
151
|
+
ruby_params_clean = ruby_params.reject { |(kind, _)| kind == :block }
|
152
|
+
min, max = ruby_params_clean.each_with_object([0, 0]) do |(kind, _), acc|
|
153
|
+
acc[1] += 1
|
154
|
+
acc[1] = Float::INFINITY if kind == :rest
|
155
|
+
acc[0] += 1 if kind == :req
|
156
|
+
end
|
157
|
+
|
158
|
+
signature_clean = signature.reject { |acc| acc.is_a?(Hash) && acc[:kind] == :block_arg }
|
159
|
+
if signature_clean.count < min || signature_clean.count > max
|
160
|
+
fail ::TypedRb::Types::TypeParsingError,
|
161
|
+
"Type signature declaration for method '#{klass}.#{method}': '#{signature_clean}' inconsistent with method parameters #{ruby_params.inspect}"
|
162
|
+
end
|
163
|
+
|
164
|
+
count = 0
|
165
|
+
parameters_info = signature_clean.map do |signature_value|
|
166
|
+
type, name = if count > ruby_params_clean.count
|
167
|
+
ruby_params_clean.last
|
168
|
+
else
|
169
|
+
ruby_params_clean[count]
|
170
|
+
end
|
171
|
+
count += 1
|
172
|
+
|
173
|
+
if signature_value.is_a?(Hash) && signature_value[:kind] == :rest
|
174
|
+
[:rest, name]
|
175
|
+
elsif type == :rest
|
176
|
+
[:opt, name]
|
177
|
+
else
|
178
|
+
[type, name]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
normalized_method.parameters_info = parameters_info
|
183
|
+
end
|
184
|
+
|
185
|
+
ts '#object_key / String -> String -> String'
|
186
|
+
def object_key(kind, receiver)
|
187
|
+
"#{kind}|#{receiver}"
|
188
|
+
end
|
189
|
+
|
190
|
+
ts '#parse_object_key / String -> Symbol'
|
191
|
+
def parse_object_key(object_key)
|
192
|
+
object_key.split('|').map(&:to_sym)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module TypedRb
|
2
|
+
# Helper class used to keep an stack of type signatures
|
3
|
+
# being parsed.
|
4
|
+
class ParsingContext
|
5
|
+
def initialize
|
6
|
+
@types_stack = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def push(type)
|
10
|
+
@types_stack << type
|
11
|
+
end
|
12
|
+
|
13
|
+
def pop
|
14
|
+
@types_stack.pop
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_type(type)
|
18
|
+
push type
|
19
|
+
result = yield
|
20
|
+
pop
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def context_name
|
25
|
+
@types_stack.last.join('::')
|
26
|
+
end
|
27
|
+
|
28
|
+
def path_name
|
29
|
+
@types_stack.map { |key| key[1] }.join('::')
|
30
|
+
end
|
31
|
+
|
32
|
+
def singleton_class?
|
33
|
+
@types_stack.last.first == :self rescue false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require_relative '../runtime'
|
2
|
+
|
3
|
+
module TypedRb
|
4
|
+
module Runtime
|
5
|
+
class TypeParser
|
6
|
+
class << self
|
7
|
+
def parse(type, klass)
|
8
|
+
fail TypedRb::Types::TypeParsingError, 'Error parsing type: nil value.' if type.nil?
|
9
|
+
if type == 'unit'
|
10
|
+
Types::TyUnit.new
|
11
|
+
elsif type == 'Boolean'
|
12
|
+
Types::TyBoolean.new
|
13
|
+
elsif type.is_a?(Array)
|
14
|
+
parse_function_type(type, klass)
|
15
|
+
elsif type.is_a?(Hash) && (type[:kind] == :type_var || type[:kind] == :method_type_var)
|
16
|
+
maybe_class = Class.for_name(type[:type]) rescue false
|
17
|
+
if maybe_class
|
18
|
+
type[:type] = maybe_class
|
19
|
+
else
|
20
|
+
type[:type] = "#{klass}:#{type[:type]}"
|
21
|
+
end
|
22
|
+
parse_type_var(type)
|
23
|
+
elsif type.is_a?(Hash) && type[:kind] == :generic_type
|
24
|
+
parse_concrete_type(type, klass)
|
25
|
+
elsif type.is_a?(Hash) && type[:kind] == :rest
|
26
|
+
parse_rest_args(type, klass)
|
27
|
+
elsif type == :unit
|
28
|
+
Types::TyUnit.new
|
29
|
+
else
|
30
|
+
parse_singleton_object_type(type)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_type_var(type)
|
35
|
+
if type[:binding] == '<'
|
36
|
+
upper_bound = Class.for_name(type[:bound]) rescue type[:bound]
|
37
|
+
upper_bound = if upper_bound.is_a?(Module)
|
38
|
+
Types::TySingletonObject.new(upper_bound)
|
39
|
+
else
|
40
|
+
Types::Polymorphism::TypeVariable.new(upper_bound, :gen_name => false)
|
41
|
+
end
|
42
|
+
Types::Polymorphism::TypeVariable.new(type[:type],
|
43
|
+
:upper_bound => upper_bound,
|
44
|
+
:gen_name => false)
|
45
|
+
elsif type[:binding] == '>'
|
46
|
+
lower_bound = Class.for_name(type[:bound])
|
47
|
+
lower_bound = if lower_bound.is_a?(Module)
|
48
|
+
Types::TySingletonObject.new(lower_bound)
|
49
|
+
else
|
50
|
+
Types::Polymorphism::TypeVariable.new(lower_bound, :gen_name => false)
|
51
|
+
end
|
52
|
+
Types::Polymorphism::TypeVariable.new(type[:type],
|
53
|
+
:lower_bound => lower_bound,
|
54
|
+
:gen_name => false)
|
55
|
+
elsif type[:type].is_a?(Module)
|
56
|
+
type_object = Types::TyObject.new(type[:type])
|
57
|
+
type_var = Types::Polymorphism::TypeVariable.new("#{type[:type]}:T",
|
58
|
+
:gen_name => false,
|
59
|
+
:upper_bound => type_object,
|
60
|
+
:lower_bound => type_object)
|
61
|
+
type_var.bind(type_object)
|
62
|
+
type_var
|
63
|
+
else
|
64
|
+
Types::Polymorphism::TypeVariable.new(type[:type], :gen_name => false)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_rest_args(type, klass)
|
69
|
+
parsed_parameter = parse(type[:parameters].first, klass)
|
70
|
+
if parsed_parameter.is_a?(Types::Polymorphism::TypeVariable) ||
|
71
|
+
parsed_parameter.is_a?(Types::TyGenericSingletonObject)
|
72
|
+
# TODO: should I use #parse_singleton_object_type here?
|
73
|
+
Types::TyGenericSingletonObject.new(Array, [parsed_parameter])
|
74
|
+
else
|
75
|
+
type_var = Types::Polymorphism::TypeVariable.new('Array:T', :gen_name => false,
|
76
|
+
:upper_bound => parsed_parameter,
|
77
|
+
:lower_bound => parsed_parameter)
|
78
|
+
type_var.bind(parsed_parameter)
|
79
|
+
Types::TyGenericObject.new(Array, [type_var])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_concrete_type(type, klass)
|
84
|
+
# parameter_names -> container class type vars
|
85
|
+
TypedRb.log(binding, :debug, "Parsing concrete type #{type} within #{klass}")
|
86
|
+
|
87
|
+
#TODO: GET RID OF THIS?
|
88
|
+
parameter_names = BasicObject::TypeRegistry.type_vars_for(klass).each_with_object({}) do |variable, acc|
|
89
|
+
acc[variable.name.split(':').last] = variable
|
90
|
+
end
|
91
|
+
# this is the concrete argument to parse
|
92
|
+
# it might refer to type vars in the container class
|
93
|
+
ruby_type = Class.for_name(type[:type])
|
94
|
+
is_generic = false
|
95
|
+
concrete_type_vars = []
|
96
|
+
# for each parameter:
|
97
|
+
# - klass.is_variable? -> variable -> generic singletion
|
98
|
+
# - klass.is not variable? -> bound_type -> generic object
|
99
|
+
rrt = BasicObject::TypeRegistry.type_vars_for(ruby_type)
|
100
|
+
rrt.each_with_index do |type_var, i|
|
101
|
+
#TODO: GET RID OF THIS?
|
102
|
+
param = type[:parameters][i]
|
103
|
+
maybe_bound_param = parameter_names[param[:type]]
|
104
|
+
parsed_type_var = if maybe_bound_param
|
105
|
+
is_generic = true
|
106
|
+
maybe_bound_param
|
107
|
+
else
|
108
|
+
if param[:kind] == :generic_type
|
109
|
+
# It is a nested generic type
|
110
|
+
#klass = Class.for_name(param[:type])
|
111
|
+
bound = parse(param, klass)
|
112
|
+
concrete_param = Types::Polymorphism::TypeVariable.new(type_var.name,
|
113
|
+
:upper_bound => bound,
|
114
|
+
:lower_bound => bound,
|
115
|
+
:gen_name => false)
|
116
|
+
concrete_param.bind(bound)
|
117
|
+
is_generic = bound.is_a?(Types::TyGenericSingletonObject) ? true : false
|
118
|
+
concrete_param
|
119
|
+
elsif param[:bound]
|
120
|
+
# A type parameter that is not bound in the generic type declaration.
|
121
|
+
# It has to be local to the method or a wildcard '?'
|
122
|
+
is_generic = true
|
123
|
+
# TODO: add some reference to the method if the variable is method specific?
|
124
|
+
if param[:type] == '?' # [? < Type]
|
125
|
+
param[:type] = "#{type_var.name}:#{type_application_counter}:#{param[:type]}"
|
126
|
+
else # [E < Type]
|
127
|
+
param[:type] = "#{type_var.name}:#{param[:type]}:#{type_application_counter}"
|
128
|
+
end
|
129
|
+
parse(param, klass)
|
130
|
+
elsif param[:type] == '?' # [?]
|
131
|
+
is_generic = true
|
132
|
+
Types::Polymorphism::TypeVariable.new(param[:type], :gen_name => false)
|
133
|
+
elsif param[:sub_kind] == :method_type_var # method[E] / [E]
|
134
|
+
# A type parameter that is not bound in the generic type declaration.
|
135
|
+
# It has to be local to the method
|
136
|
+
is_generic = true
|
137
|
+
Types::Polymorphism::TypeVariable.new("#{klass}:#{param[:type]}", :gen_name => false)
|
138
|
+
else # [Type]
|
139
|
+
begin
|
140
|
+
# The Generic type is bound to a concrete type: bound == upper_bound == lower_bound
|
141
|
+
bound = Types::TySingletonObject.new(Class.for_name(param[:type]))
|
142
|
+
concrete_param = Types::Polymorphism::TypeVariable.new(type_var.name,
|
143
|
+
:upper_bound => bound,
|
144
|
+
:lower_bound => bound,
|
145
|
+
:gen_name => false)
|
146
|
+
concrete_param.bind(bound)
|
147
|
+
concrete_param
|
148
|
+
rescue NameError # [E] / E != ruby type
|
149
|
+
# TODO: transform this into the method_type_var shown before
|
150
|
+
is_generic = true
|
151
|
+
Types::Polymorphism::TypeVariable.new(param[:type], :gen_name => false)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
concrete_type_vars << parsed_type_var
|
156
|
+
end
|
157
|
+
|
158
|
+
if is_generic
|
159
|
+
Types::TyGenericSingletonObject.new(ruby_type, concrete_type_vars)
|
160
|
+
else
|
161
|
+
Types::TyGenericObject.new(ruby_type, concrete_type_vars)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def type_application_counter
|
166
|
+
@type_application_counter ||= 0
|
167
|
+
@type_application_counter += 1
|
168
|
+
end
|
169
|
+
|
170
|
+
def parse_existential_object_type(type)
|
171
|
+
ruby_type = Class.for_name(type)
|
172
|
+
BasicObject::TypeRegistry.find_existential_type(ruby_type)
|
173
|
+
rescue StandardError => e
|
174
|
+
TypedRb.log(binding, :error, "Error parsing existential object from type #{type}, #{e.message}")
|
175
|
+
raise TypedRb::Types::TypeParsingError, "Unknown Ruby type #{type}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def parse_singleton_object_type(type, node = nil)
|
179
|
+
ruby_type = Class.for_name(type)
|
180
|
+
generic_type = BasicObject::TypeRegistry.find_generic_type(ruby_type)
|
181
|
+
if generic_type
|
182
|
+
generic_type.node = node
|
183
|
+
generic_type
|
184
|
+
else
|
185
|
+
Types::TySingletonObject.new(ruby_type, node)
|
186
|
+
end
|
187
|
+
rescue StandardError => e
|
188
|
+
TypedRb.log(binding, :error, "Error parsing singleton object from type #{type}, #{e.message}")
|
189
|
+
raise TypedRb::Types::TypeParsingError, "Unknown Ruby type #{type}"
|
190
|
+
end
|
191
|
+
|
192
|
+
def parse_function_type(arg_types, klass)
|
193
|
+
return_type = parse(arg_types.pop, klass)
|
194
|
+
block_type = if arg_types.last.is_a?(Hash) && arg_types.last[:kind] == :block_arg
|
195
|
+
block_type = arg_types.pop
|
196
|
+
parse_function_type(block_type[:block], klass)
|
197
|
+
end
|
198
|
+
parsed_arg_types = arg_types.map { |arg| parse(arg, klass) }
|
199
|
+
is_generic = (parsed_arg_types + [return_type]).any? do |var|
|
200
|
+
var.is_a?(Types::TyGenericSingletonObject) ||
|
201
|
+
var.is_a?(Types::Polymorphism::TypeVariable)
|
202
|
+
end
|
203
|
+
|
204
|
+
is_generic ||= block_type.generic? if block_type
|
205
|
+
|
206
|
+
function_class = is_generic ? Types::TyGenericFunction : Types::TyFunction
|
207
|
+
function_type = function_class.new(parsed_arg_types, return_type, arg_types)
|
208
|
+
function_type.local_typing_context = Types::TypingContext.empty_typing_context if function_type.generic?
|
209
|
+
function_type.with_block_type(block_type) if block_type
|
210
|
+
function_type
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require_relative '../runtime'
|
2
|
+
|
3
|
+
class BasicObject
|
4
|
+
class TypeRegistry
|
5
|
+
class << self
|
6
|
+
include TypedRb::Runtime::Normalization
|
7
|
+
|
8
|
+
ts '.clear_parsing_registries / -> unit'
|
9
|
+
def clear_parsing_registries
|
10
|
+
generic_types_parser_registry.clear
|
11
|
+
parser_registry.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
ts '.clear / -> unit'
|
15
|
+
def clear
|
16
|
+
generic_types_registry.clear
|
17
|
+
registry.clear
|
18
|
+
clear_parsing_registries
|
19
|
+
end
|
20
|
+
|
21
|
+
ts '.register_type_information / Symbol -> String -> String -> Object -> unit'
|
22
|
+
def register_type_information(kind, receiver, method, type_ast)
|
23
|
+
methods = methods_for(kind, receiver)[method] || []
|
24
|
+
methods << type_ast
|
25
|
+
methods_for(kind, receiver)[method] = methods
|
26
|
+
end
|
27
|
+
|
28
|
+
ts '.register_generic_type_information / Hash[Object][Object] -> Hash[Object][Object] -> unit'
|
29
|
+
def register_generic_type_information(generic_type_information, generic_super_type_information)
|
30
|
+
unless generic_type_information.is_a?(String) # TODO: String when super annotations for non-generic types
|
31
|
+
generic_type_information[:super_type] = generic_super_type_information
|
32
|
+
if generic_types_parser_registry[generic_type_information[:type]]
|
33
|
+
fail ::TypedRb::Types::TypeParsingError,
|
34
|
+
"Duplicated generic type definition for #{generic_type_information[:type]}"
|
35
|
+
else
|
36
|
+
generic_types_parser_registry[generic_type_information[:type]] = generic_type_information
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_existential_type(type)
|
42
|
+
existential_type = existential_types_registry[type]
|
43
|
+
if existential_type.nil?
|
44
|
+
existential_type = TypedRb::Types::TyExistentialType.new(type)
|
45
|
+
@existential_types_registry[type] = existential_type
|
46
|
+
end
|
47
|
+
existential_type
|
48
|
+
end
|
49
|
+
|
50
|
+
ts '.find_generic_type / Class -> TypedRb::Types::TyGenericSingletonObject'
|
51
|
+
def find_generic_type(type)
|
52
|
+
@generic_types_registry[type]
|
53
|
+
end
|
54
|
+
|
55
|
+
ts '.type_vars_for / Class -> Array[TypedRb::Types::Polymorphism::TypeVariable]'
|
56
|
+
def type_vars_for(klass)
|
57
|
+
singleton_object = find_generic_type(klass)
|
58
|
+
if singleton_object
|
59
|
+
singleton_object.type_vars.map do |type_var|
|
60
|
+
::TypedRb::Types::Polymorphism::TypeVariable.new(type_var.variable,
|
61
|
+
:upper_bound => type_var.upper_bound,
|
62
|
+
:lower_bound => type_var.lower_bound,
|
63
|
+
:gen_name => false)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
Array.call(TypedRb::Types::Polymorphism::TypeVariable).new
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
ts '.type_var? / Class -> String -> Boolean'
|
71
|
+
def type_var?(klass, variable)
|
72
|
+
singleton_object = generic_types_registry[klass]
|
73
|
+
if singleton_object
|
74
|
+
singleton_object.type_vars.any? do |type_var|
|
75
|
+
type_var.variable == variable
|
76
|
+
end
|
77
|
+
else
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# TODO: Generic types are retrieved without enforcing type constraints
|
83
|
+
# because they haven't been materialised.
|
84
|
+
ts '.find / Symbol -> Class -> String -> Array[TypedRb::Types::TyFunction]'
|
85
|
+
def find(kind, klass, message)
|
86
|
+
class_data = registry[[kind, klass]]
|
87
|
+
if class_data
|
88
|
+
# TODO: What should we when the class is in the registry but the method is missing?
|
89
|
+
# The class has been typed but only partially?
|
90
|
+
# Dynamic invocation or error?
|
91
|
+
# Maybe an additional @dynamic annotation can be added to distinguish the desired outcome.
|
92
|
+
# Preferred outcome might be nil to catch errors in unification, safer assumption.
|
93
|
+
# class_data[message.to_s] || nil # ::TypedRb::Types::TyDynamicFunction.new(klass, message)
|
94
|
+
class_data[message.to_s] || [::TypedRb::Types::TyDynamicFunction.new(klass, message)]
|
95
|
+
elsif kind == :instance_variable || kind == :class_variable
|
96
|
+
nil
|
97
|
+
else
|
98
|
+
# if registered?(klass)
|
99
|
+
# nil
|
100
|
+
# else
|
101
|
+
result = Array.('TypedRb::Types::TyFunction').new
|
102
|
+
result << ::TypedRb::Types::TyDynamicFunction.new(klass, message)
|
103
|
+
result
|
104
|
+
# end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
ts '.registered? / Class -> Boolean'
|
109
|
+
def registered?(klass)
|
110
|
+
registered_classes = registry.keys.map(&:last)
|
111
|
+
registered_classes.include?(klass)
|
112
|
+
end
|
113
|
+
|
114
|
+
ts '.normalize_types! / -> unit'
|
115
|
+
def normalize_types!
|
116
|
+
normalize_generic_types!
|
117
|
+
normalize_methods!
|
118
|
+
end
|
119
|
+
|
120
|
+
def registry=(registry)
|
121
|
+
@registry = registry
|
122
|
+
end
|
123
|
+
|
124
|
+
def generic_types_registry=(registry)
|
125
|
+
@generic_types_registry = registry
|
126
|
+
end
|
127
|
+
|
128
|
+
def existential_types_registry=(registry)
|
129
|
+
@existential_types_registry = registry
|
130
|
+
end
|
131
|
+
|
132
|
+
protected
|
133
|
+
|
134
|
+
ts '.registry / -> Hash[ Array[Object] ][ Hash[String][TypedRb::Types::TyFunction] ]'
|
135
|
+
def registry
|
136
|
+
@registry ||= {}
|
137
|
+
@registry
|
138
|
+
end
|
139
|
+
|
140
|
+
def existential_types_registry
|
141
|
+
@existential_types_registry ||= {}
|
142
|
+
end
|
143
|
+
|
144
|
+
ts '.generic_types_registry / -> Hash[Class][ TypedRb::Types::TyGenericSingletonObject ]'
|
145
|
+
def generic_types_registry
|
146
|
+
@generic_types_registry ||= {}
|
147
|
+
@generic_types_registry
|
148
|
+
end
|
149
|
+
|
150
|
+
ts '.parser_registry / -> Hash[ String ][ Hash[String][Object] ]'
|
151
|
+
def parser_registry
|
152
|
+
@parser_registry ||= {}
|
153
|
+
@parser_registry
|
154
|
+
end
|
155
|
+
|
156
|
+
ts '.generic_types_parser_registry / -> Hash[String][ Hash[Object][Object] ]'
|
157
|
+
def generic_types_parser_registry
|
158
|
+
@generic_types_parser_registry ||= {}
|
159
|
+
@generic_types_parser_registry
|
160
|
+
end
|
161
|
+
|
162
|
+
ts '.methods_for / Symbol -> String -> Hash[String][Object]'
|
163
|
+
def methods_for(kind, receiver)
|
164
|
+
method_registry = parser_registry[object_key(kind, receiver)] || {}
|
165
|
+
parser_registry[object_key(kind, receiver)] = method_registry
|
166
|
+
method_registry
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|