typed.rb 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Rakefile +26 -0
- data/bin/typed.rb +110 -0
- data/lib/typed/language.rb +131 -0
- data/lib/typed/model/tm_abs.rb +104 -0
- data/lib/typed/model/tm_array_literal.rb +25 -0
- data/lib/typed/model/tm_boolean.rb +15 -0
- data/lib/typed/model/tm_boolean_operator.rb +34 -0
- data/lib/typed/model/tm_break.rb +24 -0
- data/lib/typed/model/tm_case_when.rb +38 -0
- data/lib/typed/model/tm_class.rb +63 -0
- data/lib/typed/model/tm_const.rb +29 -0
- data/lib/typed/model/tm_defined.rb +19 -0
- data/lib/typed/model/tm_error.rb +16 -0
- data/lib/typed/model/tm_float.rb +15 -0
- data/lib/typed/model/tm_for.rb +42 -0
- data/lib/typed/model/tm_fun.rb +165 -0
- data/lib/typed/model/tm_global_var.rb +22 -0
- data/lib/typed/model/tm_global_var_assignment.rb +20 -0
- data/lib/typed/model/tm_hash_literal.rb +32 -0
- data/lib/typed/model/tm_if_else.rb +24 -0
- data/lib/typed/model/tm_instance_var.rb +23 -0
- data/lib/typed/model/tm_instance_var_assignment.rb +32 -0
- data/lib/typed/model/tm_int.rb +15 -0
- data/lib/typed/model/tm_local_var_asgn.rb +35 -0
- data/lib/typed/model/tm_mass_asgn.rb +60 -0
- data/lib/typed/model/tm_mlhs.rb +87 -0
- data/lib/typed/model/tm_module.rb +51 -0
- data/lib/typed/model/tm_next.rb +24 -0
- data/lib/typed/model/tm_nil.rb +14 -0
- data/lib/typed/model/tm_range_literal.rb +30 -0
- data/lib/typed/model/tm_regexp.rb +27 -0
- data/lib/typed/model/tm_rescue.rb +27 -0
- data/lib/typed/model/tm_return.rb +24 -0
- data/lib/typed/model/tm_s_class.rb +30 -0
- data/lib/typed/model/tm_self.rb +22 -0
- data/lib/typed/model/tm_send.rb +300 -0
- data/lib/typed/model/tm_sequencing.rb +53 -0
- data/lib/typed/model/tm_string.rb +15 -0
- data/lib/typed/model/tm_string_interpolation.rb +21 -0
- data/lib/typed/model/tm_super.rb +27 -0
- data/lib/typed/model/tm_symbol.rb +15 -0
- data/lib/typed/model/tm_symbol_interpolation.rb +21 -0
- data/lib/typed/model/tm_try.rb +29 -0
- data/lib/typed/model/tm_var.rb +28 -0
- data/lib/typed/model/tm_while.rb +43 -0
- data/lib/typed/model.rb +48 -0
- data/lib/typed/prelude.rb +939 -0
- data/lib/typed/prelude_existential_registry.bin +0 -0
- data/lib/typed/prelude_generic_registry.bin +0 -0
- data/lib/typed/prelude_registry.bin +0 -0
- data/lib/typed/runtime/ast_parser.rb +589 -0
- data/lib/typed/runtime/method_signature_processor.rb +72 -0
- data/lib/typed/runtime/normalization/validations.rb +47 -0
- data/lib/typed/runtime/normalization.rb +196 -0
- data/lib/typed/runtime/parser_context.rb +36 -0
- data/lib/typed/runtime/type_parser.rb +215 -0
- data/lib/typed/runtime/type_registry.rb +170 -0
- data/lib/typed/runtime/type_signature_processor.rb +34 -0
- data/lib/typed/runtime.rb +33 -0
- data/lib/typed/type_signature/parser.rb +240 -0
- data/lib/typed/types/polymorphism/existential_type_variable.rb +13 -0
- data/lib/typed/types/polymorphism/generic_comparisons.rb +134 -0
- data/lib/typed/types/polymorphism/generic_variables.rb +24 -0
- data/lib/typed/types/polymorphism/type_variable.rb +138 -0
- data/lib/typed/types/polymorphism/type_variable_register.rb +298 -0
- data/lib/typed/types/polymorphism/unification.rb +579 -0
- data/lib/typed/types/ty_boolean.rb +15 -0
- data/lib/typed/types/ty_dynamic.rb +39 -0
- data/lib/typed/types/ty_either.rb +168 -0
- data/lib/typed/types/ty_error.rb +18 -0
- data/lib/typed/types/ty_existential_type.rb +22 -0
- data/lib/typed/types/ty_function.rb +144 -0
- data/lib/typed/types/ty_generic_function.rb +115 -0
- data/lib/typed/types/ty_generic_object.rb +180 -0
- data/lib/typed/types/ty_generic_singleton_object.rb +238 -0
- data/lib/typed/types/ty_object.rb +256 -0
- data/lib/typed/types/ty_singleton_object.rb +78 -0
- data/lib/typed/types/ty_stack_jump.rb +44 -0
- data/lib/typed/types/ty_top_level_object.rb +38 -0
- data/lib/typed/types.rb +60 -0
- data/lib/typed/typing_context.rb +206 -0
- data/lib/typed/version.rb +3 -0
- data/lib/typed.rb +161 -0
- data/spec/lib/ast_parser_spec.rb +101 -0
- data/spec/lib/examples/animals.rb +44 -0
- data/spec/lib/examples/counter.rb +16 -0
- data/spec/lib/examples/if.rb +31 -0
- data/spec/lib/language_spec.rb +36 -0
- data/spec/lib/model/tm_abs_spec.rb +66 -0
- data/spec/lib/model/tm_array_literal_spec.rb +36 -0
- data/spec/lib/model/tm_case_when_spec.rb +39 -0
- data/spec/lib/model/tm_class_spec.rb +67 -0
- data/spec/lib/model/tm_defined_spec.rb +10 -0
- data/spec/lib/model/tm_for_spec.rb +150 -0
- data/spec/lib/model/tm_fun_spec.rb +11 -0
- data/spec/lib/model/tm_hash_literal_spec.rb +40 -0
- data/spec/lib/model/tm_mass_asgn_spec.rb +104 -0
- data/spec/lib/model/tm_module_spec.rb +42 -0
- data/spec/lib/model/tm_regexp_spec.rb +9 -0
- data/spec/lib/model/tm_return_spec.rb +47 -0
- data/spec/lib/model/tm_s_class_spec.rb +27 -0
- data/spec/lib/model/tm_self_spec.rb +19 -0
- data/spec/lib/model/tm_string_interpolation_spec.rb +10 -0
- data/spec/lib/model/tm_symbol_interpolation_spec.rb +10 -0
- data/spec/lib/model/tm_symbol_spec.rb +9 -0
- data/spec/lib/model/tm_while_spec.rb +141 -0
- data/spec/lib/polymorphism/type_variable_spec.rb +14 -0
- data/spec/lib/polymorphism/unification_spec.rb +328 -0
- data/spec/lib/prelude/array_spec.rb +263 -0
- data/spec/lib/prelude/class_spec.rb +12 -0
- data/spec/lib/prelude/enumerable_spec.rb +278 -0
- data/spec/lib/prelude/enumerator_spec.rb +101 -0
- data/spec/lib/prelude/hash_spec.rb +361 -0
- data/spec/lib/prelude/kernel_spec.rb +23 -0
- data/spec/lib/prelude/object_spec.rb +22 -0
- data/spec/lib/prelude/pair_spec.rb +16 -0
- data/spec/lib/prelude/showable_spec.rb +31 -0
- data/spec/lib/prelude/string_spec.rb +98 -0
- data/spec/lib/runtime/normalization_spec.rb +29 -0
- data/spec/lib/runtime/validations_spec.rb +56 -0
- data/spec/lib/runtime_spec.rb +503 -0
- data/spec/lib/type_signature/parser_spec.rb +239 -0
- data/spec/lib/types/comparisons_spec.rb +35 -0
- data/spec/lib/types/polymorphism/generic_comparisons_spec.rb +492 -0
- data/spec/lib/types/polymorphism/type_variable_register_spec.rb +128 -0
- data/spec/lib/types/ty_dynamic_spec.rb +103 -0
- data/spec/lib/types/ty_either_spec.rb +288 -0
- data/spec/lib/types/ty_error_spec.rb +18 -0
- data/spec/lib/types/ty_generic_object_spec.rb +78 -0
- data/spec/lib/types/ty_generic_singleton_object_spec.rb +288 -0
- data/spec/lib/types/typing_context_spec.rb +86 -0
- data/spec/lib/types_spec.rb +174 -0
- data/spec/lib/typing/boolean_asgn_spec.rb +134 -0
- data/spec/lib/typing/break_spec.rb +79 -0
- data/spec/lib/typing/generics_spec.rb +191 -0
- data/spec/lib/typing/instance_vars_spec.rb +103 -0
- data/spec/lib/typing/next_spec.rb +29 -0
- data/spec/lib/typing/op_asgn_spec.rb +104 -0
- data/spec/lib/typing/overriden_methods_spec.rb +31 -0
- data/spec/lib/typing/subtyping_spec.rb +112 -0
- data/spec/lib/typing/tm_boolean_operator_spec.rb +100 -0
- data/spec/lib/typing/tm_boolean_spec.rb +61 -0
- data/spec/lib/typing/tm_const_spec.rb +28 -0
- data/spec/lib/typing/tm_defined_spec.rb +12 -0
- data/spec/lib/typing/tm_fun_spec.rb +347 -0
- data/spec/lib/typing/tm_global_var_spec.rb +33 -0
- data/spec/lib/typing/tm_if_else_spec.rb +104 -0
- data/spec/lib/typing/tm_ignore_spec.rb +24 -0
- data/spec/lib/typing/tm_instance_vars_spec.rb +117 -0
- data/spec/lib/typing/tm_local_var_asgn_spec.rb +134 -0
- data/spec/lib/typing/tm_mlhs_spec.rb +164 -0
- data/spec/lib/typing/tm_module_spec.rb +89 -0
- data/spec/lib/typing/tm_raise_spec.rb +31 -0
- data/spec/lib/typing/tm_range_literal_spec.rb +25 -0
- data/spec/lib/typing/tm_regexp_spec.rb +14 -0
- data/spec/lib/typing/tm_return_spec.rb +45 -0
- data/spec/lib/typing/tm_send_casting_spec.rb +26 -0
- data/spec/lib/typing/tm_send_class_methods_spec.rb +42 -0
- data/spec/lib/typing/tm_send_generic_apply_spec.rb +103 -0
- data/spec/lib/typing/tm_send_generic_methods_spec.rb +77 -0
- data/spec/lib/typing/tm_send_initialize_spec.rb +68 -0
- data/spec/lib/typing/tm_send_lambda_spec.rb +135 -0
- data/spec/lib/typing/tm_send_spec.rb +217 -0
- data/spec/lib/typing/tm_send_yield_block_spec.rb +308 -0
- data/spec/lib/typing/tm_sequencing_spec.rb +174 -0
- data/spec/lib/typing/tm_string_interpolation_spec.rb +19 -0
- data/spec/lib/typing/tm_super_spec.rb +63 -0
- data/spec/lib/typing/tm_symbol_interpolation_spec.rb +19 -0
- data/spec/lib/typing/tm_symbol_spec.rb +14 -0
- data/spec/lib/typing/tm_try_spec.rb +73 -0
- data/spec/spec_helper.rb +140 -0
- metadata +216 -0
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,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
|