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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 12f994fd40b7d71db8aa86c0875aa723347a8edd
4
+ data.tar.gz: 13a8daf59012cddba4a106476f9609c901cd6f9b
5
+ SHA512:
6
+ metadata.gz: c9f9d208d55df978a5e1fa6f6014f955ffd3c10922970ce9d9801c9ed56bfd0775840f43978283b6ebd9139dfc0a3b595b81473b16823342a413c1e4fd0d37ea
7
+ data.tar.gz: aaa9b40826ca304563e4e5eb34e60f74efd3a5a47985bac003f2bd77c60f7ea25405bec24e4e3cffe5fa261a338370592cad42b68ebd25f52818a2192bc01abd
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'open3'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ desc 'type checks the library itself'
7
+ task :check_lib do
8
+ $TYPECHECK = false
9
+ $LOAD_TO_TYPECHECK = true
10
+ require_relative './lib/init'
11
+ $LOAD_TO_TYPECHECK = false
12
+ puts ' * Dependencies'
13
+ Kernel.computed_dependencies.each { |f| puts " - #{f}" }
14
+ puts ' * Type Checking'
15
+ cmd = "./bin/typed.rb #{Kernel.computed_dependencies.join(' ')}"
16
+ Open3.popen3(cmd) do |stdin, stdout, stderr, waith|
17
+ { :out => stdout, :err => stderr }.each do |key, stream|
18
+ Thread.new do
19
+ until (raw_line = stream.gets).nil? do
20
+ puts raw_line
21
+ end
22
+ end
23
+ end
24
+ waith.join
25
+ end
26
+ end
data/bin/typed.rb ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ require 'pry'
4
+ require_relative '../lib/typed'
5
+ require 'benchmark'
6
+
7
+ require 'set'
8
+
9
+ class TargetFinder
10
+ # Generate a list of target files by expanding globbing patterns
11
+ # (if any). If args is empty, recursively find all Ruby source
12
+ # files under the current directory
13
+ # @return [Array] array of file paths
14
+ def find(args)
15
+ return target_files_in_dir if args.empty?
16
+
17
+ files = []
18
+
19
+ args.uniq.each do |arg|
20
+ files += if File.directory?(arg)
21
+ target_files_in_dir(arg.chomp(File::SEPARATOR))
22
+ else
23
+ process_explicit_path(arg)
24
+ end
25
+ end
26
+
27
+ files.map { |f| File.expand_path(f) }.uniq
28
+ end
29
+
30
+ # Finds all Ruby source files under the current or other supplied
31
+ # directory. A Ruby source file is defined as a file with the `.rb`
32
+ # extension or a file with no extension that has a ruby shebang line
33
+ # as its first line.
34
+ # It is possible to specify includes and excludes using the config file,
35
+ # so you can include other Ruby files like Rakefiles and gemspecs.
36
+ # @param base_dir Root directory under which to search for
37
+ # ruby source files
38
+ # @return [Array] Array of filenames
39
+ def target_files_in_dir(base_dir = Dir.pwd)
40
+ # Support Windows: Backslashes from command-line -> forward slashes
41
+ base_dir.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
42
+ all_files = find_files(base_dir, File::FNM_DOTMATCH)
43
+ hidden_files = Set.new(all_files - find_files(base_dir, 0))
44
+
45
+ target_files = all_files.select do |file|
46
+ to_inspect?(file, hidden_files)
47
+ end
48
+
49
+ target_files
50
+ end
51
+
52
+ def to_inspect?(file, hidden_files)
53
+ unless hidden_files.include?(file)
54
+ return true if File.extname(file) == '.rb'
55
+ return true if ruby_executable?(file)
56
+ end
57
+ false
58
+ end
59
+
60
+ # Search for files recursively starting at the given base directory using
61
+ # the given flags that determine how the match is made. Excluded files will
62
+ # be removed later by the caller, but as an optimization find_files removes
63
+ # the top level directories that are excluded in configuration in the
64
+ # normal way (dir/**/*).
65
+ def find_files(base_dir, flags)
66
+ wanted_toplevel_dirs = toplevel_dirs(base_dir, flags)
67
+ wanted_toplevel_dirs.map! { |dir| dir << '/**/*' }
68
+
69
+ pattern = if wanted_toplevel_dirs.empty?
70
+ # We need this special case to avoid creating the pattern
71
+ # /**/* which searches the entire file system.
72
+ ["#{base_dir}/**/*"]
73
+ else
74
+ # Search the non-excluded top directories, but also add files
75
+ # on the top level, which would otherwise not be found.
76
+ wanted_toplevel_dirs.unshift("#{base_dir}/*")
77
+ end
78
+ Dir.glob(pattern, flags).select { |path| FileTest.file?(path) }
79
+ end
80
+
81
+ def toplevel_dirs(base_dir, flags)
82
+ Dir.glob(File.join(base_dir, '*'), flags).select do |dir|
83
+ File.directory?(dir) && !(dir.end_with?('/.') || dir.end_with?('/..'))
84
+ end
85
+ end
86
+
87
+ def ruby_executable?(file)
88
+ return false unless File.extname(file).empty?
89
+ first_line = File.open(file, &:readline)
90
+ first_line =~ /#!.*ruby/
91
+ rescue EOFError, ArgumentError => e
92
+ warn "Unprocessable file #{file}: #{e.class}, #{e.message}" if debug?
93
+ false
94
+ end
95
+
96
+ def process_explicit_path(path)
97
+ if path.include?('*')
98
+ Dir[path]
99
+ else
100
+ [path]
101
+ end
102
+ end
103
+ end
104
+
105
+ time = Benchmark.realtime do
106
+ files_to_check = TargetFinder.new.find(ARGV).reject { |f| f == File.expand_path(__FILE__) }
107
+ TypedRb::Language.new.check_files(files_to_check)
108
+ end
109
+
110
+ puts "Finished in #{time} seconds"
@@ -0,0 +1,131 @@
1
+ require_relative './runtime/ast_parser'
2
+
3
+ module TypedRb
4
+ class Language
5
+ include Model
6
+ include Types
7
+
8
+ attr_reader :unification_result
9
+
10
+ def check(expr)
11
+ restore_prelude
12
+ $TYPECHECK = true
13
+ eval(expr, TOPLEVEL_BINDING)
14
+ $TYPECHECK = false
15
+ TypedRb.log(binding, :debug, 'Normalize top level')
16
+ ::BasicObject::TypeRegistry.normalize_types!
17
+ TypingContext.clear(:top_level)
18
+ check_result = check_type(parse(expr))
19
+ ::BasicObject::TypeRegistry.check_super_type_annotations
20
+ @unification_result = run_unification
21
+ check_result
22
+ end
23
+
24
+ def gen_bin_prelude
25
+ File.open(File.join(File.dirname(__FILE__), 'prelude_registry.bin'), 'w') do |f|
26
+ f.write(Marshal.dump(::BasicObject::TypeRegistry.send(:registry)))
27
+ end
28
+ File.open(File.join(File.dirname(__FILE__), 'prelude_generic_registry.bin'), 'w') do |f|
29
+ f.write(Marshal.dump(::BasicObject::TypeRegistry.send(:generic_types_registry)))
30
+ end
31
+ File.open(File.join(File.dirname(__FILE__), 'prelude_existential_registry.bin'), 'w') do |f|
32
+ f.write(Marshal.dump(::BasicObject::TypeRegistry.send(:existential_types_registry)))
33
+ end
34
+ end
35
+
36
+ def load_bin_prelude
37
+ old_value = $TYPECHECK
38
+ $TYPECHECK = false
39
+ require_relative('./prelude')
40
+ $TYPECHECK = old_value
41
+ ::BasicObject::TypeRegistry.clear
42
+ File.open(File.join(File.dirname(__FILE__), 'prelude_registry.bin'), 'r') do |f|
43
+ ::BasicObject::TypeRegistry.registry = Marshal.load(f.read)
44
+ end
45
+ File.open(File.join(File.dirname(__FILE__), 'prelude_generic_registry.bin'), 'r') do |f|
46
+ ::BasicObject::TypeRegistry.generic_types_registry = Marshal.load(f.read)
47
+ end
48
+ File.open(File.join(File.dirname(__FILE__), 'prelude_existential_registry.bin'), 'r') do |f|
49
+ ::BasicObject::TypeRegistry.existential_types_registry = Marshal.load(f.read)
50
+ end
51
+ ::BasicObject::TypeRegistry.clear_parsing_registries
52
+ true
53
+ rescue
54
+ false
55
+ end
56
+
57
+ def restore_prelude
58
+ unless load_bin_prelude
59
+ ::BasicObject::TypeRegistry.clear
60
+ $TYPECHECK = true
61
+ load File.join(File.dirname(__FILE__), 'prelude.rb')
62
+ $TYPECHECK = false
63
+ ::BasicObject::TypeRegistry.normalize_types!
64
+ gen_bin_prelude
65
+ TypingContext.clear(:top_level)
66
+ ::BasicObject::TypeRegistry.clear_parsing_registries
67
+ end
68
+ end
69
+
70
+ def check_files(files)
71
+ ::BasicObject::TypeRegistry.clear
72
+ $TYPECHECK = true
73
+ prelude_path = File.join(File.dirname(__FILE__), 'prelude.rb')
74
+ load prelude_path
75
+ Kernel.reset_dependencies
76
+ Kernel.with_dependency_tracking do
77
+ files.each { |file| load file if file != prelude_path }
78
+ end
79
+ ordered_files = Kernel.computed_dependencies.select do |file|
80
+ files.include?(file)
81
+ end
82
+ ordered_files += files.select { |file| !ordered_files.include?(file) }
83
+ $TYPECHECK = false
84
+ TypedRb.log(binding, :debug, 'Normalize top level')
85
+ ::BasicObject::TypeRegistry.normalize_types!
86
+ TypingContext.clear(:top_level)
87
+ check_result = nil
88
+ ordered_files.each do |file|
89
+ puts "*** FILE #{file}"
90
+ $TYPECHECK_FILE = file
91
+ expr = File.open(file, 'r').read
92
+ #begin
93
+ check_result = check_type(parse(expr))
94
+ #rescue TypedRb::Types::UncomparableTypes => e
95
+ # puts e.backtrace.join("\n")
96
+ # puts e.message
97
+ # exit(-1)
98
+ #rescue TypedRb::TypeCheckError => e
99
+ # puts e.message
100
+ #end
101
+ end
102
+ ::BasicObject::TypeRegistry.check_super_type_annotations
103
+ @unification_result = run_unification
104
+ check_result
105
+ end
106
+
107
+ def check_file(path)
108
+ check_files([path])
109
+ end
110
+
111
+ def parse(expr)
112
+ Model::GenSym.reset
113
+ parser = AstParser.new
114
+ parser.parse(expr)
115
+ end
116
+
117
+ def check_type(expr)
118
+ expr.check_type(TypingContext.top_level)
119
+ end
120
+
121
+ def run_unification
122
+ constraints = Types::TypingContext.all_constraints
123
+ unif = Types::Polymorphism::Unification.new(constraints)
124
+ unif.run(true)
125
+ end
126
+
127
+ def type_variables
128
+ TypingContext.all_variables
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,104 @@
1
+ require_relative '../model'
2
+
3
+ module TypedRb
4
+ module Model
5
+ # abstraction
6
+ class TmAbs < Expr
7
+ attr_accessor :args, :term, :arity, :abs_type
8
+ def initialize(args, term, abs_type, node)
9
+ super(node, type)
10
+ @args = args
11
+ @term = term
12
+ @abs_type = abs_type
13
+ @arity = args.count { |(arg_type, _, _)| arg_type == :arg }
14
+ @instantiation_count = 0
15
+ end
16
+
17
+ def check_type(context)
18
+ with_fresh_bindings(context) do |var_type_args, var_type_return, context|
19
+ type_term = term.check_type(context)
20
+ fail TypeCheckError.new("Invalid 'return' statement inside abstraction", type_term.node) if type_term.stack_jump? && type_term.return?
21
+ if type_term.stack_jump? && type_term.break?
22
+ Types::TyGenericFunction.new(var_type_args, type_term, resolve_ruby_method_parameters, node)
23
+ elsif type_term.stack_jump? && (type_term.next? || type_term.return?)
24
+ wrapped_type = type_term.wrapped_type.check_type(context)
25
+ if var_type_return.compatible?(wrapped_type, :gt)
26
+ Types::TyGenericFunction.new(var_type_args, var_type_return, resolve_ruby_method_parameters, node)
27
+ else
28
+ # TODO: improve message
29
+ error_msg = "Error parsing abstraction, incompatible break return type found: #{var_type_return} < #{wrapped_type}"
30
+ fail TypeCheckError.new(error_msg, node)
31
+ end
32
+ elsif type_term.either?
33
+ max_either_type = type_term.check_type(context, [:normal, :return, :next])
34
+ if var_type_return.compatible?(max_either_type, :gt)
35
+ if type_term.break?
36
+ either_return = Types::TyEither.new(node)
37
+ either_return[:break] = type_term[:break]
38
+ either_return[:normal] = var_type_return
39
+ Types::TyGenericFunction.new(var_type_args, either_return, resolve_ruby_method_parameters, node)
40
+ else
41
+ Types::TyGenericFunction.new(var_type_args, var_type_return, resolve_ruby_method_parameters, node)
42
+ end
43
+ else
44
+ error_msg = "Error parsing abstraction, incompatible either return type found: #{var_type_return} < #{type_term}}"
45
+ fail TypeCheckError.new(error_msg, node)
46
+ end
47
+ elsif var_type_return.compatible?(type_term, :gt)
48
+ Types::TyGenericFunction.new(var_type_args, var_type_return, resolve_ruby_method_parameters, node)
49
+ else
50
+ # TODO: improve message
51
+ error_msg = "Error parsing abstraction, incompatible return type found: #{var_type_return} < #{type_term}"
52
+ fail TypeCheckError.new(error_msg, node)
53
+ end
54
+ end
55
+ end
56
+
57
+ # abstractions are polymorphic universal types by default,
58
+ # we need new bindings in the type variables with each instantiation of the lambda.
59
+ def with_fresh_bindings(context)
60
+ orig_context = Types::TypingContext.type_variables_register
61
+ Types::TypingContext.push_context(:lambda)
62
+ fresh_args = args.map do |(type, var, opt)|
63
+ if type == :mlhs
64
+ context = var.check_type(:lambda, context)
65
+ var
66
+ else
67
+ type_var_arg = Types::TypingContext.type_variable_for_abstraction(:lambda, "#{var}", context)
68
+ type_var_arg.node = node
69
+ context = case type
70
+ when :arg, :block
71
+ context.add_binding(var, type_var_arg)
72
+ when :optarg
73
+ declared_arg_type = opt.check_type(orig_context)
74
+ if type_var_arg.compatible?(declared_arg_type, :gt)
75
+ context.add_binding(var, type_var_arg)
76
+ end
77
+ end
78
+ type_var_arg
79
+ end
80
+ end
81
+
82
+ return_type_var_arg = Types::TypingContext.type_variable_for_abstraction(:lambda, nil, context)
83
+ return_type_var_arg.node = node
84
+ lambda_type = yield fresh_args, return_type_var_arg, context
85
+ lambda_type.local_typing_context = Types::TypingContext.pop_context
86
+ lambda_type
87
+ end
88
+
89
+ protected
90
+
91
+ def resolve_ruby_method_parameters
92
+ args.map do |(arg_type, val, _)|
93
+ if arg_type == :optarg
94
+ [:opt, val]
95
+ elsif arg_type == :blockarg
96
+ [:block, val]
97
+ else
98
+ [:req, val]
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../model'
2
+
3
+ module TypedRb
4
+ module Model
5
+ class TmArrayLiteral < Expr
6
+ attr_reader :elements
7
+ def initialize(elements, node)
8
+ super(node)
9
+ @elements = elements
10
+ end
11
+
12
+ def check_type(context)
13
+ element_types = elements.map { |element| element.check_type(context) }
14
+ max_type = element_types.max rescue Types::TyObject.new(Object, node)
15
+ type_var = Types::Polymorphism::TypeVariable.new('Array:T',
16
+ :node => node,
17
+ :gen_name => false,
18
+ :upper_bound => max_type,
19
+ :lower_bound => max_type)
20
+ type_var.bind(max_type)
21
+ Types::TyGenericObject.new(Array, [type_var], node)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '../model'
3
+
4
+ module TypedRb
5
+ module Model
6
+ # booleans
7
+ class TmBoolean < Expr
8
+ attr_accessor :val
9
+ def initialize(node)
10
+ super(node, Types::TyBoolean.new(node))
11
+ @val = node.type == 'true' ? true : false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../model'
2
+
3
+ module TypedRb
4
+ module Model
5
+ # abstraction
6
+ class TmBooleanOperator < Expr
7
+ attr_accessor :operator, :lhs, :rhs
8
+
9
+ def initialize(operator, lhs, rhs, node)
10
+ super(node)
11
+ @lhs = lhs
12
+ @rhs = rhs
13
+ @operator = operator
14
+ end
15
+
16
+ def check_type(context)
17
+ lhs_type = @lhs.check_type(context)
18
+ rhs_type = @rhs.check_type(context)
19
+ if lhs_type.is_a?(Types::Polymorphism::TypeVariable) || rhs_type.is_a?(Types::Polymorphism::TypeVariable)
20
+ var = Types::TypingContext.local_type_variable
21
+ var.compatible?(lhs_type, :gt)
22
+ var.compatible?(rhs_type, :gt)
23
+ var
24
+ else
25
+ types = [lhs_type, rhs_type].reject { |type| type.is_a?(Types::TyUnit) || type.is_a?(Types::TyError) }
26
+ type = types.max rescue Types::TyObject.new(Object, node)
27
+ type = Types::TyUnit.new if type.nil?
28
+ type.node = node
29
+ type
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../model'
2
+
3
+ module TypedRb
4
+ module Model
5
+ class TmBreak < Expr
6
+ attr_reader :elements
7
+ def initialize(elements, node)
8
+ super(node)
9
+ @elements = elements
10
+ end
11
+
12
+ def check_type(context)
13
+ returned_type = if elements.size == 0
14
+ Types::TyUnit.new(node)
15
+ elsif elements.size == 1
16
+ elements.first.check_type(context)
17
+ else
18
+ TmArrayLiteral.new(elements, node).check_type(context)
19
+ end
20
+ Types::TyStackJump.break(returned_type, node)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '../model'
3
+
4
+ module TypedRb
5
+ module Model
6
+ class TmCaseWhen < Expr
7
+ attr_reader :case_statement, :when_statements, :default_statement
8
+ def initialize(node, case_statement, when_statements, default_statement)
9
+ super(node, nil)
10
+ @case_statement = case_statement
11
+ @when_statements = when_statements
12
+ @default_statement = default_statement
13
+ end
14
+
15
+ def check_type(context)
16
+ conditions = build_conditionals(case_statement, when_statements)
17
+ conditions = conditions.reduce([]) do |acc, (node, condition, then_statement)|
18
+ next_condition = TmIfElse.new(node, condition, then_statement, nil)
19
+ prev_condition = acc.last
20
+ prev_condition.else_expr = next_condition unless prev_condition.nil?
21
+ acc << next_condition
22
+ end
23
+ conditions.last.else_expr = default_statement if default_statement
24
+ conditions.first.check_type(context)
25
+ end
26
+
27
+ protected
28
+
29
+ def build_conditionals(case_statement, when_statements)
30
+ when_statements.map do |when_statement|
31
+ node, conditional, then_statement = when_statement
32
+ condition = TmSend.new(case_statement, :===, [conditional], node)
33
+ [node, condition, then_statement]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '../model'
3
+
4
+ module TypedRb
5
+ module Model
6
+ # Class expression
7
+ class TmClass < Expr
8
+ attr_reader :class_name, :super_class_name, :body
9
+
10
+ def initialize(class_name, super_class_name, body, node)
11
+ super(node)
12
+ @class_name = class_name
13
+ @super_class_name = super_class_name
14
+ @body = body
15
+ end
16
+
17
+ def check_type(context)
18
+ class_ruby_type = Types::TypingContext.find_namespace(class_name)
19
+ class_type = Runtime::TypeParser.parse_singleton_object_type(class_ruby_type.name)
20
+ context = context.add_binding(:self, class_type)
21
+ Types::TypingContext.namespace_push(class_name)
22
+ result_type = if class_type.is_a?(Types::TyGenericSingletonObject)
23
+ # If the type is generic, we will collect all the restrictions
24
+ # found while processing the class body in a local type_context.
25
+ # This context will be complemented with the remaining restrictions
26
+ # coming from type var application when the generic type becomes
27
+ # concrete to yield the final type.
28
+ TmClass.with_fresh_bindings(class_type, context, node) do
29
+ body.check_type(context) if body
30
+ end
31
+ else
32
+ body.check_type(context) if body
33
+ end
34
+ Types::TypingContext.namespace_pop
35
+ result_type || Types::TyUnit.new(node)
36
+ end
37
+
38
+ def self.with_fresh_bindings(generic_class, _context, node)
39
+ Types::TypingContext.push_context(:class)
40
+ # Deal with upper/lower bounds here if required
41
+ generic_class.type_vars.each do |type_var|
42
+ type_var = Types::TypingContext.type_variable_for_generic_type(type_var)
43
+ type_var.node = node
44
+
45
+ if type_var.upper_bound
46
+ type_var.compatible?(type_var.upper_bound, :lt)
47
+ end
48
+
49
+ if type_var.lower_bound
50
+ type_var.compatible?(type_var.lower_bound, :gt)
51
+ end
52
+ end
53
+ body_return_type = yield if block_given?
54
+ # Since every single time we find the generic type the same instance
55
+ # will be returned, the local_typing_context will still be associated.
56
+ # This is the reason we need to build a new typing context cloning this
57
+ # one while type materialization.
58
+ generic_class.local_typing_context = Types::TypingContext.pop_context
59
+ body_return_type
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../model'
2
+
3
+ module TypedRb
4
+ module Model
5
+ # A constant expression
6
+ class TmConst < Expr
7
+ attr_reader :val
8
+
9
+ def initialize(val, node)
10
+ super(node)
11
+ @val = val
12
+ end
13
+
14
+ def check_type(_context)
15
+ value_ruby_type = Types::TypingContext.find_namespace(@val)
16
+ type = if value_ruby_type.instance_of?(Class)
17
+ Runtime::TypeParser.parse_singleton_object_type(value_ruby_type.name)
18
+ elsif value_ruby_type.instance_of?(Module)
19
+ Runtime::TypeParser.parse_existential_object_type(value_ruby_type.name)
20
+ else
21
+ # Must be a user defined constant
22
+ Types::TyObject.new(value_ruby_type.class)
23
+ end
24
+ type.node = node
25
+ type
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '../model'
3
+
4
+ module TypedRb
5
+ module Model
6
+ class TmDefined < Expr
7
+ attr_reader :expression
8
+ def initialize(expression, node)
9
+ super(node)
10
+ @expression = expression
11
+ end
12
+
13
+ def check_type(context)
14
+ expression.check_type(context)
15
+ Types::TyString.new(node)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '../model'
3
+
4
+ module TypedRb
5
+ module Model
6
+ class TmError < Expr
7
+ def initialize(node)
8
+ super(node)
9
+ end
10
+
11
+ def check_type(_context)
12
+ Types::TyError.new(node)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '../model'
3
+
4
+ module TypedRb
5
+ module Model
6
+ # floats
7
+ class TmFloat < Expr
8
+ attr_accessor :val
9
+ def initialize(node)
10
+ super(node, Types::TyFloat.new(node))
11
+ @val = node.children.first
12
+ end
13
+ end
14
+ end
15
+ end