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.
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