typed.rb 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +26 -0
  3. data/bin/typed.rb +110 -0
  4. data/lib/typed/language.rb +131 -0
  5. data/lib/typed/model/tm_abs.rb +104 -0
  6. data/lib/typed/model/tm_array_literal.rb +25 -0
  7. data/lib/typed/model/tm_boolean.rb +15 -0
  8. data/lib/typed/model/tm_boolean_operator.rb +34 -0
  9. data/lib/typed/model/tm_break.rb +24 -0
  10. data/lib/typed/model/tm_case_when.rb +38 -0
  11. data/lib/typed/model/tm_class.rb +63 -0
  12. data/lib/typed/model/tm_const.rb +29 -0
  13. data/lib/typed/model/tm_defined.rb +19 -0
  14. data/lib/typed/model/tm_error.rb +16 -0
  15. data/lib/typed/model/tm_float.rb +15 -0
  16. data/lib/typed/model/tm_for.rb +42 -0
  17. data/lib/typed/model/tm_fun.rb +165 -0
  18. data/lib/typed/model/tm_global_var.rb +22 -0
  19. data/lib/typed/model/tm_global_var_assignment.rb +20 -0
  20. data/lib/typed/model/tm_hash_literal.rb +32 -0
  21. data/lib/typed/model/tm_if_else.rb +24 -0
  22. data/lib/typed/model/tm_instance_var.rb +23 -0
  23. data/lib/typed/model/tm_instance_var_assignment.rb +32 -0
  24. data/lib/typed/model/tm_int.rb +15 -0
  25. data/lib/typed/model/tm_local_var_asgn.rb +35 -0
  26. data/lib/typed/model/tm_mass_asgn.rb +60 -0
  27. data/lib/typed/model/tm_mlhs.rb +87 -0
  28. data/lib/typed/model/tm_module.rb +51 -0
  29. data/lib/typed/model/tm_next.rb +24 -0
  30. data/lib/typed/model/tm_nil.rb +14 -0
  31. data/lib/typed/model/tm_range_literal.rb +30 -0
  32. data/lib/typed/model/tm_regexp.rb +27 -0
  33. data/lib/typed/model/tm_rescue.rb +27 -0
  34. data/lib/typed/model/tm_return.rb +24 -0
  35. data/lib/typed/model/tm_s_class.rb +30 -0
  36. data/lib/typed/model/tm_self.rb +22 -0
  37. data/lib/typed/model/tm_send.rb +300 -0
  38. data/lib/typed/model/tm_sequencing.rb +53 -0
  39. data/lib/typed/model/tm_string.rb +15 -0
  40. data/lib/typed/model/tm_string_interpolation.rb +21 -0
  41. data/lib/typed/model/tm_super.rb +27 -0
  42. data/lib/typed/model/tm_symbol.rb +15 -0
  43. data/lib/typed/model/tm_symbol_interpolation.rb +21 -0
  44. data/lib/typed/model/tm_try.rb +29 -0
  45. data/lib/typed/model/tm_var.rb +28 -0
  46. data/lib/typed/model/tm_while.rb +43 -0
  47. data/lib/typed/model.rb +48 -0
  48. data/lib/typed/prelude.rb +939 -0
  49. data/lib/typed/prelude_existential_registry.bin +0 -0
  50. data/lib/typed/prelude_generic_registry.bin +0 -0
  51. data/lib/typed/prelude_registry.bin +0 -0
  52. data/lib/typed/runtime/ast_parser.rb +589 -0
  53. data/lib/typed/runtime/method_signature_processor.rb +72 -0
  54. data/lib/typed/runtime/normalization/validations.rb +47 -0
  55. data/lib/typed/runtime/normalization.rb +196 -0
  56. data/lib/typed/runtime/parser_context.rb +36 -0
  57. data/lib/typed/runtime/type_parser.rb +215 -0
  58. data/lib/typed/runtime/type_registry.rb +170 -0
  59. data/lib/typed/runtime/type_signature_processor.rb +34 -0
  60. data/lib/typed/runtime.rb +33 -0
  61. data/lib/typed/type_signature/parser.rb +240 -0
  62. data/lib/typed/types/polymorphism/existential_type_variable.rb +13 -0
  63. data/lib/typed/types/polymorphism/generic_comparisons.rb +134 -0
  64. data/lib/typed/types/polymorphism/generic_variables.rb +24 -0
  65. data/lib/typed/types/polymorphism/type_variable.rb +138 -0
  66. data/lib/typed/types/polymorphism/type_variable_register.rb +298 -0
  67. data/lib/typed/types/polymorphism/unification.rb +579 -0
  68. data/lib/typed/types/ty_boolean.rb +15 -0
  69. data/lib/typed/types/ty_dynamic.rb +39 -0
  70. data/lib/typed/types/ty_either.rb +168 -0
  71. data/lib/typed/types/ty_error.rb +18 -0
  72. data/lib/typed/types/ty_existential_type.rb +22 -0
  73. data/lib/typed/types/ty_function.rb +144 -0
  74. data/lib/typed/types/ty_generic_function.rb +115 -0
  75. data/lib/typed/types/ty_generic_object.rb +180 -0
  76. data/lib/typed/types/ty_generic_singleton_object.rb +238 -0
  77. data/lib/typed/types/ty_object.rb +256 -0
  78. data/lib/typed/types/ty_singleton_object.rb +78 -0
  79. data/lib/typed/types/ty_stack_jump.rb +44 -0
  80. data/lib/typed/types/ty_top_level_object.rb +38 -0
  81. data/lib/typed/types.rb +60 -0
  82. data/lib/typed/typing_context.rb +206 -0
  83. data/lib/typed/version.rb +3 -0
  84. data/lib/typed.rb +161 -0
  85. data/spec/lib/ast_parser_spec.rb +101 -0
  86. data/spec/lib/examples/animals.rb +44 -0
  87. data/spec/lib/examples/counter.rb +16 -0
  88. data/spec/lib/examples/if.rb +31 -0
  89. data/spec/lib/language_spec.rb +36 -0
  90. data/spec/lib/model/tm_abs_spec.rb +66 -0
  91. data/spec/lib/model/tm_array_literal_spec.rb +36 -0
  92. data/spec/lib/model/tm_case_when_spec.rb +39 -0
  93. data/spec/lib/model/tm_class_spec.rb +67 -0
  94. data/spec/lib/model/tm_defined_spec.rb +10 -0
  95. data/spec/lib/model/tm_for_spec.rb +150 -0
  96. data/spec/lib/model/tm_fun_spec.rb +11 -0
  97. data/spec/lib/model/tm_hash_literal_spec.rb +40 -0
  98. data/spec/lib/model/tm_mass_asgn_spec.rb +104 -0
  99. data/spec/lib/model/tm_module_spec.rb +42 -0
  100. data/spec/lib/model/tm_regexp_spec.rb +9 -0
  101. data/spec/lib/model/tm_return_spec.rb +47 -0
  102. data/spec/lib/model/tm_s_class_spec.rb +27 -0
  103. data/spec/lib/model/tm_self_spec.rb +19 -0
  104. data/spec/lib/model/tm_string_interpolation_spec.rb +10 -0
  105. data/spec/lib/model/tm_symbol_interpolation_spec.rb +10 -0
  106. data/spec/lib/model/tm_symbol_spec.rb +9 -0
  107. data/spec/lib/model/tm_while_spec.rb +141 -0
  108. data/spec/lib/polymorphism/type_variable_spec.rb +14 -0
  109. data/spec/lib/polymorphism/unification_spec.rb +328 -0
  110. data/spec/lib/prelude/array_spec.rb +263 -0
  111. data/spec/lib/prelude/class_spec.rb +12 -0
  112. data/spec/lib/prelude/enumerable_spec.rb +278 -0
  113. data/spec/lib/prelude/enumerator_spec.rb +101 -0
  114. data/spec/lib/prelude/hash_spec.rb +361 -0
  115. data/spec/lib/prelude/kernel_spec.rb +23 -0
  116. data/spec/lib/prelude/object_spec.rb +22 -0
  117. data/spec/lib/prelude/pair_spec.rb +16 -0
  118. data/spec/lib/prelude/showable_spec.rb +31 -0
  119. data/spec/lib/prelude/string_spec.rb +98 -0
  120. data/spec/lib/runtime/normalization_spec.rb +29 -0
  121. data/spec/lib/runtime/validations_spec.rb +56 -0
  122. data/spec/lib/runtime_spec.rb +503 -0
  123. data/spec/lib/type_signature/parser_spec.rb +239 -0
  124. data/spec/lib/types/comparisons_spec.rb +35 -0
  125. data/spec/lib/types/polymorphism/generic_comparisons_spec.rb +492 -0
  126. data/spec/lib/types/polymorphism/type_variable_register_spec.rb +128 -0
  127. data/spec/lib/types/ty_dynamic_spec.rb +103 -0
  128. data/spec/lib/types/ty_either_spec.rb +288 -0
  129. data/spec/lib/types/ty_error_spec.rb +18 -0
  130. data/spec/lib/types/ty_generic_object_spec.rb +78 -0
  131. data/spec/lib/types/ty_generic_singleton_object_spec.rb +288 -0
  132. data/spec/lib/types/typing_context_spec.rb +86 -0
  133. data/spec/lib/types_spec.rb +174 -0
  134. data/spec/lib/typing/boolean_asgn_spec.rb +134 -0
  135. data/spec/lib/typing/break_spec.rb +79 -0
  136. data/spec/lib/typing/generics_spec.rb +191 -0
  137. data/spec/lib/typing/instance_vars_spec.rb +103 -0
  138. data/spec/lib/typing/next_spec.rb +29 -0
  139. data/spec/lib/typing/op_asgn_spec.rb +104 -0
  140. data/spec/lib/typing/overriden_methods_spec.rb +31 -0
  141. data/spec/lib/typing/subtyping_spec.rb +112 -0
  142. data/spec/lib/typing/tm_boolean_operator_spec.rb +100 -0
  143. data/spec/lib/typing/tm_boolean_spec.rb +61 -0
  144. data/spec/lib/typing/tm_const_spec.rb +28 -0
  145. data/spec/lib/typing/tm_defined_spec.rb +12 -0
  146. data/spec/lib/typing/tm_fun_spec.rb +347 -0
  147. data/spec/lib/typing/tm_global_var_spec.rb +33 -0
  148. data/spec/lib/typing/tm_if_else_spec.rb +104 -0
  149. data/spec/lib/typing/tm_ignore_spec.rb +24 -0
  150. data/spec/lib/typing/tm_instance_vars_spec.rb +117 -0
  151. data/spec/lib/typing/tm_local_var_asgn_spec.rb +134 -0
  152. data/spec/lib/typing/tm_mlhs_spec.rb +164 -0
  153. data/spec/lib/typing/tm_module_spec.rb +89 -0
  154. data/spec/lib/typing/tm_raise_spec.rb +31 -0
  155. data/spec/lib/typing/tm_range_literal_spec.rb +25 -0
  156. data/spec/lib/typing/tm_regexp_spec.rb +14 -0
  157. data/spec/lib/typing/tm_return_spec.rb +45 -0
  158. data/spec/lib/typing/tm_send_casting_spec.rb +26 -0
  159. data/spec/lib/typing/tm_send_class_methods_spec.rb +42 -0
  160. data/spec/lib/typing/tm_send_generic_apply_spec.rb +103 -0
  161. data/spec/lib/typing/tm_send_generic_methods_spec.rb +77 -0
  162. data/spec/lib/typing/tm_send_initialize_spec.rb +68 -0
  163. data/spec/lib/typing/tm_send_lambda_spec.rb +135 -0
  164. data/spec/lib/typing/tm_send_spec.rb +217 -0
  165. data/spec/lib/typing/tm_send_yield_block_spec.rb +308 -0
  166. data/spec/lib/typing/tm_sequencing_spec.rb +174 -0
  167. data/spec/lib/typing/tm_string_interpolation_spec.rb +19 -0
  168. data/spec/lib/typing/tm_super_spec.rb +63 -0
  169. data/spec/lib/typing/tm_symbol_interpolation_spec.rb +19 -0
  170. data/spec/lib/typing/tm_symbol_spec.rb +14 -0
  171. data/spec/lib/typing/tm_try_spec.rb +73 -0
  172. data/spec/spec_helper.rb +140 -0
  173. 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