rdl 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rails_types.rb +1 -0
- data/lib/rdl.rb +56 -0
- data/lib/rdl/config.rb +121 -0
- data/lib/rdl/contracts/and.rb +29 -0
- data/lib/rdl/contracts/contract.rb +7 -0
- data/lib/rdl/contracts/flat.rb +31 -0
- data/lib/rdl/contracts/or.rb +25 -0
- data/lib/rdl/contracts/proc.rb +24 -0
- data/lib/rdl/switch.rb +20 -0
- data/lib/rdl/types/annotated_arg.rb +41 -0
- data/lib/rdl/types/finitehash.rb +81 -0
- data/lib/rdl/types/generic.rb +100 -0
- data/lib/rdl/types/intersection.rb +66 -0
- data/lib/rdl/types/lexer.rex +39 -0
- data/lib/rdl/types/lexer.rex.rb +148 -0
- data/lib/rdl/types/method.rb +219 -0
- data/lib/rdl/types/nil.rb +50 -0
- data/lib/rdl/types/nominal.rb +80 -0
- data/lib/rdl/types/optional.rb +54 -0
- data/lib/rdl/types/parser.racc +150 -0
- data/lib/rdl/types/parser.tab.rb +654 -0
- data/lib/rdl/types/singleton.rb +62 -0
- data/lib/rdl/types/structural.rb +72 -0
- data/lib/rdl/types/top.rb +50 -0
- data/lib/rdl/types/tuple.rb +61 -0
- data/lib/rdl/types/type.rb +24 -0
- data/lib/rdl/types/type_inferencer.rb +73 -0
- data/lib/rdl/types/union.rb +74 -0
- data/lib/rdl/types/var.rb +51 -0
- data/lib/rdl/types/vararg.rb +54 -0
- data/lib/rdl/types/wild.rb +26 -0
- data/lib/rdl/util.rb +56 -0
- data/lib/rdl/wrap.rb +505 -0
- data/lib/rdl_types.rb +2 -0
- data/types/other/chronic.rb +5 -0
- data/types/other/paperclip_attachment.rb +7 -0
- data/types/other/securerandom.rb +4 -0
- data/types/rails-4.2.1/fixnum.rb +3 -0
- data/types/rails-4.2.1/string.rb +3 -0
- data/types/rails-tmp/action_dispatch.rb +406 -0
- data/types/rails-tmp/active_record.rb +406 -0
- data/types/rails-tmp/devise_contracts.rb +216 -0
- data/types/ruby-2.2.0/_aliases.rb +4 -0
- data/types/ruby-2.2.0/abbrev.rb +5 -0
- data/types/ruby-2.2.0/array.rb +137 -0
- data/types/ruby-2.2.0/base64.rb +10 -0
- data/types/ruby-2.2.0/basic_object.rb +13 -0
- data/types/ruby-2.2.0/benchmark.rb +11 -0
- data/types/ruby-2.2.0/bigdecimal.rb +15 -0
- data/types/ruby-2.2.0/bigmath.rb +12 -0
- data/types/ruby-2.2.0/class.rb +17 -0
- data/types/ruby-2.2.0/complex.rb +42 -0
- data/types/ruby-2.2.0/coverage.rb +6 -0
- data/types/ruby-2.2.0/csv.rb +5 -0
- data/types/ruby-2.2.0/date.rb +6 -0
- data/types/ruby-2.2.0/dir.rb +38 -0
- data/types/ruby-2.2.0/encoding.rb +23 -0
- data/types/ruby-2.2.0/enumerable.rb +98 -0
- data/types/ruby-2.2.0/enumerator.rb +26 -0
- data/types/ruby-2.2.0/exception.rb +15 -0
- data/types/ruby-2.2.0/file.rb +124 -0
- data/types/ruby-2.2.0/fileutils.rb +6 -0
- data/types/ruby-2.2.0/fixnum.rb +45 -0
- data/types/ruby-2.2.0/float.rb +54 -0
- data/types/ruby-2.2.0/gem.rb +245 -0
- data/types/ruby-2.2.0/hash.rb +72 -0
- data/types/ruby-2.2.0/integer.rb +31 -0
- data/types/ruby-2.2.0/io.rb +103 -0
- data/types/ruby-2.2.0/kernel.rb +89 -0
- data/types/ruby-2.2.0/marshal.rb +5 -0
- data/types/ruby-2.2.0/matchdata.rb +26 -0
- data/types/ruby-2.2.0/math.rb +53 -0
- data/types/ruby-2.2.0/numeric.rb +46 -0
- data/types/ruby-2.2.0/object.rb +73 -0
- data/types/ruby-2.2.0/pathname.rb +106 -0
- data/types/ruby-2.2.0/process.rb +127 -0
- data/types/ruby-2.2.0/random.rb +15 -0
- data/types/ruby-2.2.0/range.rb +38 -0
- data/types/ruby-2.2.0/rational.rb +31 -0
- data/types/ruby-2.2.0/regexp.rb +30 -0
- data/types/ruby-2.2.0/set.rb +58 -0
- data/types/ruby-2.2.0/string.rb +145 -0
- data/types/ruby-2.2.0/strscan.rb +7 -0
- data/types/ruby-2.2.0/symbol.rb +29 -0
- data/types/ruby-2.2.0/time.rb +68 -0
- data/types/ruby-2.2.0/uri.rb +18 -0
- data/types/ruby-2.2.0/yaml.rb +5 -0
- metadata +131 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
class SingletonType < Type
|
5
|
+
attr_reader :val
|
6
|
+
|
7
|
+
@@cache = {}
|
8
|
+
@@cache.compare_by_identity
|
9
|
+
|
10
|
+
class << self
|
11
|
+
alias :__new__ :new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.new(val)
|
15
|
+
t = @@cache[val]
|
16
|
+
return t if t
|
17
|
+
t = self.__new__ val
|
18
|
+
return (@@cache[val] = t) # assignment evaluates to t
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(val)
|
22
|
+
@val = val
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
self == other
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
return (other.instance_of? self.class) && (other.val.equal? @val)
|
31
|
+
end
|
32
|
+
|
33
|
+
def hash # :nodoc:
|
34
|
+
return @val.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
if @val.instance_of? Symbol
|
39
|
+
":{@val}"
|
40
|
+
else
|
41
|
+
"Singleton(#{@val.to_s})"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def <=(other)
|
46
|
+
other.instance_of?(TopType) ||
|
47
|
+
(other.instance_of?(SingletonType) && other.val == @val) ||
|
48
|
+
(other.instance_of?(NominalType) && @val.class == other.klass) ||
|
49
|
+
(other.instance_of?(NominalType) && @val.is_a?(other.klass))
|
50
|
+
end
|
51
|
+
|
52
|
+
def member?(obj, *args)
|
53
|
+
t = RDL::Util.rdl_type obj
|
54
|
+
return t <= self if t
|
55
|
+
obj.nil? || obj.equal?(@val)
|
56
|
+
end
|
57
|
+
|
58
|
+
def instantiate(inst)
|
59
|
+
return self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
class StructuralType < Type
|
5
|
+
attr_reader :methods
|
6
|
+
|
7
|
+
@@cache = {}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
alias :__new__ :new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.new(methods)
|
14
|
+
t = @@cache[methods]
|
15
|
+
return t if t
|
16
|
+
t = StructuralType.__new__(methods)
|
17
|
+
return (@@cache[methods] = t) # assignment evaluates to t
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a new StructuralType.
|
21
|
+
#
|
22
|
+
# [+methods+] Map from method names as symbols to their types.
|
23
|
+
def initialize(methods)
|
24
|
+
raise "methods can't be empty" if methods.empty?
|
25
|
+
methods.each { |m, t|
|
26
|
+
raise RuntimeError, "Method names in StructuralType must be symbols" unless m.instance_of? Symbol
|
27
|
+
raise RuntimeError, "Got #{t.class} where MethodType expected" unless t.instance_of? MethodType
|
28
|
+
# Note intersection types not allowed as subtyping would be tricky
|
29
|
+
}
|
30
|
+
@methods = methods
|
31
|
+
super()
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s # :nodoc:
|
35
|
+
"[ " + @methods.each_pair.map { |m, t| "#{m.to_s}: #{t.to_s}" }.sort.join(", ") + " ]"
|
36
|
+
end
|
37
|
+
|
38
|
+
def <=(other)
|
39
|
+
return true if other.instance_of? TopType
|
40
|
+
# in theory a StructuralType could contain all the methods of a NominalType or GenericType,
|
41
|
+
# but it seems unlikely in practice, so disallow this case.
|
42
|
+
return RuntimeError, "Structural subtype can't be subtype of #{other.class}" unless other.instance_of? StructuralType
|
43
|
+
# allow width subtyping
|
44
|
+
other.methods.each_pair { |m, t|
|
45
|
+
return false unless @methods.has_key?(m) && @methods[m] <= t
|
46
|
+
}
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
|
50
|
+
def member?(obj, *args)
|
51
|
+
t = RDL::Util.rdl_type obj
|
52
|
+
return t <= self if t
|
53
|
+
return NominalType.new(obj.class) <= self
|
54
|
+
end
|
55
|
+
|
56
|
+
def instantiate(inst)
|
57
|
+
StructuralType.new(Hash[*@methods.each_pair.map { |m, t| [m, t.instantiate(inst)] }.flatten])
|
58
|
+
end
|
59
|
+
|
60
|
+
def eql?(other)
|
61
|
+
self == other
|
62
|
+
end
|
63
|
+
|
64
|
+
def ==(other) # :nodoc:
|
65
|
+
return (other.instance_of? StructuralType) && (other.methods == @methods)
|
66
|
+
end
|
67
|
+
|
68
|
+
def hash # :nodoc:
|
69
|
+
@methods.hash
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
class TopType < Type
|
5
|
+
@@cache = nil
|
6
|
+
|
7
|
+
class << self
|
8
|
+
alias :__new__ :new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.new
|
12
|
+
@@cache = TopType.__new__ unless @@cache
|
13
|
+
return @@cache
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"%any"
|
22
|
+
end
|
23
|
+
|
24
|
+
def eql?(other)
|
25
|
+
self == other
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
other.instance_of? TopType
|
30
|
+
end
|
31
|
+
|
32
|
+
def <=(other)
|
33
|
+
other.instance_of? TopType
|
34
|
+
end
|
35
|
+
|
36
|
+
def member?(obj, *args)
|
37
|
+
t = RDL::Util.rdl_type obj
|
38
|
+
return t <= self if t
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def instantiate(inst)
|
43
|
+
return self
|
44
|
+
end
|
45
|
+
|
46
|
+
def hash
|
47
|
+
17
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
# A specialized GenericType for tuples, i.e., fixed-sized arrays
|
5
|
+
class TupleType < Type
|
6
|
+
attr_reader :params
|
7
|
+
|
8
|
+
@@cache = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
alias :__new__ :new
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.new(*params)
|
15
|
+
t = @@cache[params]
|
16
|
+
return t if t
|
17
|
+
raise RuntimeError, "Attempt to create generic type with non-type param" unless params.all? { |p| p.is_a? Type }
|
18
|
+
t = TupleType.__new__(params)
|
19
|
+
return (@@cache[params] = t) # assignment evaluates to t
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(params)
|
23
|
+
@params = params
|
24
|
+
super()
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"[#{@params.map { |t| t.to_s }.join(', ')}]"
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other)
|
32
|
+
self == other
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other) # :nodoc:
|
36
|
+
return (other.instance_of? TupleType) && (other.params == @params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def <=(other)
|
40
|
+
return true if other.instance_of? TopType
|
41
|
+
return self == other
|
42
|
+
# Subtyping with Array not allowed
|
43
|
+
# All positions of Tuple are invariant since tuples are mutable
|
44
|
+
end
|
45
|
+
|
46
|
+
def member?(obj, *args)
|
47
|
+
t = RDL::Util.rdl_type obj
|
48
|
+
return t <= self if t
|
49
|
+
return false unless obj.instance_of?(Array) && obj.size == @params.size
|
50
|
+
return @params.zip(obj).all? { |formal, actual| formal.member?(actual, *args) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def instantiate(inst)
|
54
|
+
TupleType.new(*@params.map { |t| t.instantiate(inst) })
|
55
|
+
end
|
56
|
+
|
57
|
+
def hash
|
58
|
+
h = 73 * @params.hash
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RDL::Type
|
2
|
+
# Abstract base class for all types. This class
|
3
|
+
# should never be instantiated directly.
|
4
|
+
|
5
|
+
class TypeError < StandardError; end
|
6
|
+
|
7
|
+
class Type
|
8
|
+
|
9
|
+
@@contract_cache = {}
|
10
|
+
|
11
|
+
def to_contract
|
12
|
+
c = @@contract_cache[self]
|
13
|
+
return c if c
|
14
|
+
|
15
|
+
slf = self # Bind self to slf since contracts are executed in scope of associated method
|
16
|
+
c = RDL::Contract::FlatContract.new(to_s) { |obj|
|
17
|
+
raise TypeError, "Expecting #{to_s}, got object of class #{RDL::Util.rdl_type_or_class(obj)}" unless slf.member?(obj)
|
18
|
+
true
|
19
|
+
}
|
20
|
+
return (@@contract_cache[self] = c) # assignment evaluates to c
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module RDL
|
2
|
+
class TypeInferencer
|
3
|
+
def self.infer_type(it)
|
4
|
+
current_types = Set.new
|
5
|
+
it_types = it.map {|t| t.rdl_type}
|
6
|
+
it_types = it_types.to_set
|
7
|
+
|
8
|
+
it_types.each {|t|
|
9
|
+
subtype = current_types.any? {|ct| t.le(ct)}
|
10
|
+
current_types.add(t) if not subtype
|
11
|
+
}
|
12
|
+
|
13
|
+
if current_types.size == 1
|
14
|
+
current_types.to_a[0]
|
15
|
+
elsif current_types.size == 0
|
16
|
+
RDL::Type::NilType.new
|
17
|
+
else
|
18
|
+
RDL::Type::UnionType.new(*self.unify_param_types(current_types))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def self.extract_types(param_type)
|
25
|
+
param_type.instance_of?(RDL::Type::UnionType) ? param_type.types.to_a : [param_type]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Unifies i.e. #<Set: {Array<String>, Array<Array<String>>}> into
|
29
|
+
# Array<(Array<String> or String)>
|
30
|
+
# If this step is not called, then infer_type for
|
31
|
+
# [["a", "b"], [["c"]]].rdl_type would return
|
32
|
+
# (Array<Array<String>> or Array<String>) instead of
|
33
|
+
# (Array<(Array<String> or String)>)
|
34
|
+
def self.unify_param_types(type_set)
|
35
|
+
non_param_classes = []
|
36
|
+
parameterized_classes = {}
|
37
|
+
|
38
|
+
type_set.each {|member_type|
|
39
|
+
if member_type.instance_of? RDL::Type::GenericType
|
40
|
+
nominal_type = member_type.base
|
41
|
+
|
42
|
+
tparam_set = parameterized_classes.fetch(nominal_type) {|n_type|
|
43
|
+
cls = eval(n_type.name.to_s)
|
44
|
+
type_parameters = cls.instance_variable_get :@__cls_params
|
45
|
+
[].fill([], 0, type_parameters.size)
|
46
|
+
}
|
47
|
+
|
48
|
+
cls = eval(nominal_type.name.to_s)
|
49
|
+
type_parameters = cls.instance_variable_get :@__cls_params
|
50
|
+
((0..(type_parameters.size - 1)).map {|tparam_index|
|
51
|
+
extract_types(member_type.params[tparam_index])
|
52
|
+
}).each_with_index {|type_parameter,index|
|
53
|
+
tparam_set[index]+=type_parameter
|
54
|
+
}
|
55
|
+
|
56
|
+
parameterized_classes[nominal_type] = tparam_set
|
57
|
+
else
|
58
|
+
non_param_classes << member_type
|
59
|
+
end
|
60
|
+
}
|
61
|
+
|
62
|
+
parameterized_classes.each {|nominal, type_set|
|
63
|
+
t = type_set.map {|unioned_type_parameter|
|
64
|
+
RDL::Type::UnionType.new(*unify_param_types(unioned_type_parameter))
|
65
|
+
}
|
66
|
+
|
67
|
+
non_param_classes << RDL::Type::GenericType.new(nominal, *t)
|
68
|
+
}
|
69
|
+
|
70
|
+
non_param_classes
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
class UnionType < Type
|
5
|
+
attr_reader :types
|
6
|
+
|
7
|
+
@@cache = {}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
alias :__new__ :new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.new(*types)
|
14
|
+
ts = []
|
15
|
+
types.each { |t|
|
16
|
+
if t.instance_of? NilType
|
17
|
+
next
|
18
|
+
elsif t.instance_of? TopType
|
19
|
+
ts = [t]
|
20
|
+
break
|
21
|
+
elsif t.instance_of? UnionType
|
22
|
+
ts.concat t.types
|
23
|
+
else
|
24
|
+
raise RuntimeError, "Attempt to create union type with non-type" unless t.is_a? Type
|
25
|
+
ts << t
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
ts.sort! { |a,b| a.object_id <=> b.object_id }
|
30
|
+
ts.uniq!
|
31
|
+
|
32
|
+
return NilType.new if ts.size == 0
|
33
|
+
return ts[0] if ts.size == 1
|
34
|
+
|
35
|
+
t = @@cache[ts]
|
36
|
+
return t if t
|
37
|
+
t = UnionType.__new__(ts)
|
38
|
+
return (@@cache[ts] = t) # assignment evaluates to t
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(types)
|
42
|
+
@types = types
|
43
|
+
super()
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s # :nodoc:
|
47
|
+
"#{@types.map { |t| t.to_s }.join(' or ')}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def eql?(other)
|
51
|
+
self == other
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(other) # :nodoc:
|
55
|
+
return (other.instance_of? UnionType) && (other.types == @types)
|
56
|
+
end
|
57
|
+
|
58
|
+
def <=(other)
|
59
|
+
@types.all? { |t| t <= other }
|
60
|
+
end
|
61
|
+
|
62
|
+
def member?(obj, *args)
|
63
|
+
@types.any? { |t| t.member?(obj, *args) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def instantiate(inst)
|
67
|
+
return UnionType.new(*(@types.map { |t| t.instantiate(inst) }))
|
68
|
+
end
|
69
|
+
|
70
|
+
def hash # :nodoc:
|
71
|
+
41 + @types.hash
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative 'type'
|
2
|
+
|
3
|
+
module RDL::Type
|
4
|
+
class VarType < Type
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
@@cache = {}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
alias :__new__ :new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.new(name)
|
14
|
+
name = name.to_s.to_sym
|
15
|
+
t = @@cache[name]
|
16
|
+
return t if t
|
17
|
+
t = self.__new__ name
|
18
|
+
return (@@cache[name] = t) # assignment evaluates to t
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(name)
|
22
|
+
@name = name
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s # :nodoc:
|
26
|
+
return @name.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def eql?(other)
|
30
|
+
self == other
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
return (other.instance_of? self.class) && (other.name.to_s == @name.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
def hash # :nodoc:
|
38
|
+
return @name.to_s.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def member?(obj, vars_wild: false)
|
42
|
+
return true if vars_wild
|
43
|
+
raise TypeError, "Unbound type variable #{@name}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def instantiate(inst)
|
47
|
+
return inst[@name] if inst[@name]
|
48
|
+
return self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|