typed.rb 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|