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