rdl 1.1.1 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +6 -0
- data/README.md +211 -32
- data/gemfiles/Gemfile.travis +1 -1
- data/lib/rdl.rb +85 -18
- data/lib/rdl/info.rb +74 -0
- data/lib/rdl/query.rb +8 -9
- data/lib/rdl/typecheck.rb +1057 -0
- data/lib/rdl/types/annotated_arg.rb +5 -5
- data/lib/rdl/types/{nil.rb → bot.rb} +9 -13
- data/lib/rdl/types/dependent_arg.rb +5 -5
- data/lib/rdl/types/dots_query.rb +2 -0
- data/lib/rdl/types/finitehash.rb +67 -24
- data/lib/rdl/types/generic.rb +13 -21
- data/lib/rdl/types/intersection.rb +9 -8
- data/lib/rdl/types/method.rb +30 -32
- data/lib/rdl/types/nominal.rb +22 -16
- data/lib/rdl/types/optional.rb +8 -22
- data/lib/rdl/types/parser.racc +8 -3
- data/lib/rdl/types/parser.tab.rb +131 -118
- data/lib/rdl/types/singleton.rb +15 -10
- data/lib/rdl/types/structural.rb +6 -6
- data/lib/rdl/types/top.rb +6 -6
- data/lib/rdl/types/tuple.rb +56 -24
- data/lib/rdl/types/type.rb +9 -0
- data/lib/rdl/types/type_inferencer.rb +1 -1
- data/lib/rdl/types/union.rb +52 -26
- data/lib/rdl/types/var.rb +7 -6
- data/lib/rdl/types/vararg.rb +5 -6
- data/lib/rdl/types/wild_query.rb +9 -2
- data/lib/rdl/util.rb +9 -7
- data/lib/rdl/wrap.rb +90 -72
- data/lib/rdl_types.rb +2 -2
- data/rdl.gemspec +6 -8
- data/test/test_alias.rb +4 -3
- data/test/test_contract.rb +5 -4
- data/test/test_dsl.rb +2 -1
- data/test/test_generic.rb +30 -26
- data/test/test_intersection.rb +3 -3
- data/test/test_le.rb +129 -61
- data/test/test_lib_types.rb +3 -2
- data/test/test_member.rb +33 -46
- data/test/test_parser.rb +113 -116
- data/test/test_query.rb +2 -1
- data/test/test_rdl.rb +64 -6
- data/test/test_rdl_type.rb +3 -2
- data/test/test_type_contract.rb +30 -12
- data/test/test_typecheck.rb +893 -0
- data/test/test_types.rb +50 -54
- data/test/test_wrap.rb +2 -1
- data/types/ruby-2.x/_aliases.rb +13 -2
- data/types/ruby-2.x/bigdecimal.rb +60 -85
- data/types/ruby-2.x/bignum.rb +80 -119
- data/types/ruby-2.x/complex.rb +33 -40
- data/types/ruby-2.x/fixnum.rb +81 -120
- data/types/ruby-2.x/float.rb +79 -116
- data/types/ruby-2.x/integer.rb +187 -22
- data/types/ruby-2.x/nil.rb +12 -0
- data/types/ruby-2.x/numeric.rb +38 -38
- data/types/ruby-2.x/object.rb +3 -3
- data/types/ruby-2.x/random.rb +2 -0
- data/types/ruby-2.x/range.rb +20 -19
- data/types/ruby-2.x/rational.rb +40 -40
- data/types/ruby-2.x/regexp.rb +4 -4
- data/types/ruby-2.x/string.rb +15 -17
- metadata +17 -16
- data/lib/rdl/types/.#lexer.rex +0 -1
data/lib/rdl/types/singleton.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
class SingletonType < Type
|
5
3
|
attr_reader :val
|
4
|
+
attr_reader :nominal
|
6
5
|
|
7
6
|
@@cache = {}
|
8
7
|
@@cache.compare_by_identity
|
@@ -20,17 +19,19 @@ module RDL::Type
|
|
20
19
|
|
21
20
|
def initialize(val)
|
22
21
|
@val = val
|
23
|
-
|
24
|
-
|
25
|
-
def eql?(other)
|
26
|
-
self == other
|
22
|
+
@nominal = NominalType.new(val.class)
|
27
23
|
end
|
28
24
|
|
29
25
|
def ==(other)
|
26
|
+
return false if other.nil?
|
27
|
+
other = other.canonical
|
30
28
|
return (other.instance_of? self.class) && (other.val.equal? @val)
|
31
29
|
end
|
32
30
|
|
31
|
+
alias eql? ==
|
32
|
+
|
33
33
|
def match(other)
|
34
|
+
other = other.canonical
|
34
35
|
other = other.type if other.instance_of? AnnotatedArgType
|
35
36
|
return true if other.instance_of? WildQuery
|
36
37
|
return self == other
|
@@ -42,7 +43,9 @@ module RDL::Type
|
|
42
43
|
|
43
44
|
def to_s
|
44
45
|
if @val.instance_of? Symbol
|
45
|
-
"
|
46
|
+
":#{@val}"
|
47
|
+
elsif @val.nil?
|
48
|
+
"nil"
|
46
49
|
else
|
47
50
|
@val.to_s
|
48
51
|
# "Singleton(#{@val.to_s})"
|
@@ -50,16 +53,18 @@ module RDL::Type
|
|
50
53
|
end
|
51
54
|
|
52
55
|
def <=(other)
|
56
|
+
other = other.canonical
|
53
57
|
other.instance_of?(TopType) ||
|
58
|
+
(@val.nil? && (not (other.instance_of?(SingletonType)))) ||
|
54
59
|
(other.instance_of?(SingletonType) && other.val == @val) ||
|
55
|
-
(other.instance_of?(
|
56
|
-
(
|
60
|
+
(other.instance_of?(UnionType) && other.types.any? { |ot| self <= ot }) ||
|
61
|
+
(@nominal <= other)
|
57
62
|
end
|
58
63
|
|
59
64
|
def member?(obj, *args)
|
60
65
|
t = RDL::Util.rdl_type obj
|
61
66
|
return t <= self if t
|
62
|
-
obj.
|
67
|
+
obj.equal?(@val)
|
63
68
|
end
|
64
69
|
|
65
70
|
def instantiate(inst)
|
data/lib/rdl/types/structural.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
class StructuralType < Type
|
5
3
|
attr_reader :methods
|
@@ -36,6 +34,7 @@ module RDL::Type
|
|
36
34
|
end
|
37
35
|
|
38
36
|
def <=(other)
|
37
|
+
other = other.canonical
|
39
38
|
return true if other.instance_of? TopType
|
40
39
|
# in theory a StructuralType could contain all the methods of a NominalType or GenericType,
|
41
40
|
# but it seems unlikely in practice, so disallow this case.
|
@@ -57,15 +56,16 @@ module RDL::Type
|
|
57
56
|
StructuralType.new(Hash[*@methods.each_pair.map { |m, t| [m, t.instantiate(inst)] }.flatten])
|
58
57
|
end
|
59
58
|
|
60
|
-
def eql?(other)
|
61
|
-
self == other
|
62
|
-
end
|
63
|
-
|
64
59
|
def ==(other) # :nodoc:
|
60
|
+
return false if other.nil?
|
61
|
+
other = other.canonical
|
65
62
|
return (other.instance_of? StructuralType) && (other.methods == @methods)
|
66
63
|
end
|
67
64
|
|
65
|
+
alias eql? ==
|
66
|
+
|
68
67
|
def match(other)
|
68
|
+
other = other.canonical
|
69
69
|
other = other.type if other.instance_of? AnnotatedArgType
|
70
70
|
return true if other.instance_of? WildQuery
|
71
71
|
return (@methods.length == other.methods.length &&
|
data/lib/rdl/types/top.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
class TopType < Type
|
5
3
|
@@cache = nil
|
@@ -21,21 +19,23 @@ module RDL::Type
|
|
21
19
|
"%any"
|
22
20
|
end
|
23
21
|
|
24
|
-
def eql?(other)
|
25
|
-
self == other
|
26
|
-
end
|
27
|
-
|
28
22
|
def ==(other)
|
23
|
+
return false if other.nil?
|
24
|
+
other = other.canonical
|
29
25
|
other.instance_of? TopType
|
30
26
|
end
|
31
27
|
|
28
|
+
alias eql? ==
|
29
|
+
|
32
30
|
def match(other)
|
31
|
+
other = other.canonical
|
33
32
|
other = other.type if other.instance_of? AnnotatedArgType
|
34
33
|
return true if other.instance_of? WildQuery
|
35
34
|
return self == other
|
36
35
|
end
|
37
36
|
|
38
37
|
def <=(other)
|
38
|
+
other = other.canonical
|
39
39
|
other.instance_of? TopType
|
40
40
|
end
|
41
41
|
|
data/lib/rdl/types/tuple.rb
CHANGED
@@ -1,55 +1,84 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
# A specialized GenericType for tuples, i.e., fixed-sized arrays
|
5
3
|
class TupleType < Type
|
6
4
|
attr_reader :params
|
5
|
+
attr_reader :array # either nil or array type if self has been promoted to array
|
6
|
+
attr_reader :ubounds # upper bounds this tuple has been compared with using <=
|
7
|
+
attr_reader :lbounds # lower bounds...
|
7
8
|
|
8
|
-
|
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
|
9
|
+
# no caching because array might be mutated
|
10
|
+
def initialize(*params)
|
17
11
|
raise RuntimeError, "Attempt to create tuple 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
12
|
@params = params
|
13
|
+
@array = nil # emphasize initially this is a tuple, not an array
|
14
|
+
@cant_promote = false
|
15
|
+
@ubounds = []
|
16
|
+
@lbounds = []
|
24
17
|
super()
|
25
18
|
end
|
26
19
|
|
27
|
-
def
|
28
|
-
|
20
|
+
def canonical
|
21
|
+
return @array if @array
|
22
|
+
return self
|
29
23
|
end
|
30
24
|
|
31
|
-
def
|
32
|
-
|
25
|
+
def to_s
|
26
|
+
return @array.to_s if @array
|
27
|
+
return "[#{@params.map { |t| t.to_s }.join(', ')}]"
|
33
28
|
end
|
34
29
|
|
35
30
|
def ==(other) # :nodoc:
|
31
|
+
return false if other.nil?
|
32
|
+
return (@array == other) if @array
|
33
|
+
other = other.canonical
|
36
34
|
return (other.instance_of? TupleType) && (other.params == @params)
|
37
35
|
end
|
38
36
|
|
37
|
+
alias eql? ==
|
38
|
+
|
39
39
|
def match(other)
|
40
|
+
return @array.match(other) if @array
|
41
|
+
other = other.canonical
|
40
42
|
other = other.type if other.instance_of? AnnotatedArgType
|
41
43
|
return true if other.instance_of? WildQuery
|
42
44
|
return @params.length == other.params.length && @params.zip(other.params).all? { |t,o| t.match(o) }
|
43
45
|
end
|
44
46
|
|
47
|
+
def promote!
|
48
|
+
return false if @cant_promote
|
49
|
+
@array = GenericType.new($__rdl_array_type, UnionType.new(*@params))
|
50
|
+
# note since we promoted this, lbounds and ubounds will be ignored in future constraints, which
|
51
|
+
# is good because otherwise we'd get infinite loops
|
52
|
+
return (@lbounds.all? { |lbound| lbound <= self }) && (@ubounds.all? { |ubound| self <= ubound })
|
53
|
+
end
|
54
|
+
|
55
|
+
def cant_promote!
|
56
|
+
raise RuntimeError, "already promoted!" if @array
|
57
|
+
@cant_promote = true
|
58
|
+
end
|
59
|
+
|
45
60
|
def <=(other)
|
61
|
+
return @array <= other if @array
|
62
|
+
other = other.canonical
|
46
63
|
return true if other.instance_of? TopType
|
47
|
-
|
48
|
-
|
49
|
-
|
64
|
+
other = other.array if other.instance_of?(TupleType) && other.array
|
65
|
+
if other.instance_of? TupleType
|
66
|
+
# Tuples are immutable, so covariant subtyping allowed
|
67
|
+
return false unless @params.length == other.params.length
|
68
|
+
return false unless @params.zip(other.params).all? { |left, right| left <= right }
|
69
|
+
# subyping check passed
|
70
|
+
ubounds << other
|
71
|
+
other.lbounds << self
|
72
|
+
return true
|
73
|
+
elsif (other.instance_of? GenericType) && (other.base == $__rdl_array_type)
|
74
|
+
r = promote!
|
75
|
+
return (self <= other) && r
|
76
|
+
end
|
77
|
+
return false
|
50
78
|
end
|
51
79
|
|
52
80
|
def member?(obj, *args)
|
81
|
+
return @array.member?(obj, *args) if @array
|
53
82
|
t = RDL::Util.rdl_type obj
|
54
83
|
return t <= self if t
|
55
84
|
return false unless obj.instance_of?(Array) && obj.size == @params.size
|
@@ -57,11 +86,14 @@ module RDL::Type
|
|
57
86
|
end
|
58
87
|
|
59
88
|
def instantiate(inst)
|
60
|
-
|
89
|
+
return @array.instantiate(inst) if @array
|
90
|
+
return TupleType.new(*@params.map { |t| t.instantiate(inst) })
|
61
91
|
end
|
62
92
|
|
63
93
|
def hash
|
94
|
+
# note don't change hash value if @array becomes non-nil
|
64
95
|
73 * @params.hash
|
65
96
|
end
|
97
|
+
|
66
98
|
end
|
67
99
|
end
|
data/lib/rdl/types/type.rb
CHANGED
@@ -20,5 +20,14 @@ module RDL::Type
|
|
20
20
|
return (@@contract_cache[self] = c) # assignment evaluates to c
|
21
21
|
end
|
22
22
|
|
23
|
+
def nil_type?
|
24
|
+
is_a?(SingletonType) && @val.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
# default behavior, override in appropriate subclasses
|
28
|
+
def canonical
|
29
|
+
return self
|
30
|
+
end
|
31
|
+
|
23
32
|
end
|
24
33
|
end
|
data/lib/rdl/types/union.rb
CHANGED
@@ -1,61 +1,80 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
class UnionType < Type
|
5
3
|
attr_reader :types
|
6
4
|
|
7
|
-
@@cache = {}
|
8
|
-
|
9
5
|
class << self
|
10
6
|
alias :__new__ :new
|
11
7
|
end
|
12
8
|
|
13
9
|
def self.new(*types)
|
10
|
+
return $__rdl_nil_type if types.size == 0
|
14
11
|
ts = []
|
12
|
+
# flatten nested unions, check that all args are types
|
15
13
|
types.each { |t|
|
16
|
-
if t.instance_of?
|
17
|
-
next
|
18
|
-
elsif t.instance_of? TopType
|
19
|
-
ts = [t]
|
20
|
-
break
|
21
|
-
elsif t.instance_of? UnionType
|
14
|
+
if t.instance_of? UnionType
|
22
15
|
ts.concat t.types
|
23
16
|
else
|
24
17
|
raise RuntimeError, "Attempt to create union type with non-type" unless t.is_a? Type
|
25
18
|
ts << t
|
26
19
|
end
|
27
20
|
}
|
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
21
|
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
|
22
|
+
return UnionType.__new__(ts)
|
39
23
|
end
|
40
24
|
|
41
25
|
def initialize(types)
|
42
26
|
@types = types
|
27
|
+
@canonical = false
|
28
|
+
@canonicalized = false
|
29
|
+
@hash = 41 + @types.hash # don't rehash if @types changes
|
43
30
|
super()
|
44
31
|
end
|
45
32
|
|
46
|
-
def
|
47
|
-
|
33
|
+
def canonical
|
34
|
+
canonicalize!
|
35
|
+
return @canonical if @canonical
|
36
|
+
return self
|
48
37
|
end
|
49
38
|
|
50
|
-
def
|
51
|
-
|
39
|
+
def canonicalize!
|
40
|
+
return if @canonicalized
|
41
|
+
# for any type such that a supertype is already in ts, set its position to nil
|
42
|
+
for i in 0..(@types.length-1)
|
43
|
+
for j in (i+1)..(@types.length-1)
|
44
|
+
next if (@types[j].nil?) || (@types[i].nil?)
|
45
|
+
(@types[i] = nil; break) if @types[i] <= @types[j]
|
46
|
+
(@types[j] = nil) if @types[j] <= @types[i]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@types.delete(nil) # eliminate any "deleted" elements
|
50
|
+
@types.sort! { |a, b| a.object_id <=> b.object_id } # canonicalize order
|
51
|
+
@types.uniq!
|
52
|
+
@canonical = @types[0] if @types.size == 1
|
53
|
+
@canonicalized = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s # :nodoc:
|
57
|
+
return @canonical.to_s if @canonical
|
58
|
+
return "#{@types.map { |t| t.to_s }.join(' or ')}"
|
52
59
|
end
|
53
60
|
|
54
61
|
def ==(other) # :nodoc:
|
55
|
-
return
|
62
|
+
return false if other.nil?
|
63
|
+
canonicalize!
|
64
|
+
return @canonical == other if @canonical
|
65
|
+
other = other.canonical
|
66
|
+
return false unless other.instance_of? UnionType
|
67
|
+
other.canonicalize!
|
68
|
+
return false unless @types.length == other.types.length
|
69
|
+
return @types.all? { |t| other.types.any? { |ot| t == ot } }
|
56
70
|
end
|
57
71
|
|
72
|
+
alias eql? ==
|
73
|
+
|
58
74
|
def match(other)
|
75
|
+
canonicalize!
|
76
|
+
return @canonical.match(other) if @canonical
|
77
|
+
other = other.canonical
|
59
78
|
other = other.type if other.instance_of? AnnotatedArgType
|
60
79
|
return true if other.instance_of? WildQuery
|
61
80
|
return false if @types.length != other.types.length
|
@@ -63,19 +82,26 @@ module RDL::Type
|
|
63
82
|
end
|
64
83
|
|
65
84
|
def <=(other)
|
85
|
+
canonicalize!
|
86
|
+
return @canonical <= other if @canonical
|
87
|
+
other = other.canonical
|
66
88
|
@types.all? { |t| t <= other }
|
67
89
|
end
|
68
90
|
|
69
91
|
def member?(obj, *args)
|
92
|
+
canonicalize!
|
93
|
+
return @canonical.member?(obj, *args) if @canonical
|
70
94
|
@types.any? { |t| t.member?(obj, *args) }
|
71
95
|
end
|
72
96
|
|
73
97
|
def instantiate(inst)
|
98
|
+
canonicalize!
|
99
|
+
return @canonical.instantiate(inst) if @canonical
|
74
100
|
return UnionType.new(*(@types.map { |t| t.instantiate(inst) }))
|
75
101
|
end
|
76
102
|
|
77
103
|
def hash # :nodoc:
|
78
|
-
|
104
|
+
return @hash
|
79
105
|
end
|
80
106
|
end
|
81
107
|
end
|
data/lib/rdl/types/var.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
class VarType < Type
|
5
3
|
attr_reader :name
|
@@ -26,15 +24,18 @@ module RDL::Type
|
|
26
24
|
return @name.to_s
|
27
25
|
end
|
28
26
|
|
29
|
-
def eql?(other)
|
30
|
-
self == other
|
31
|
-
end
|
32
|
-
|
33
27
|
def ==(other)
|
28
|
+
return false if other.nil?
|
29
|
+
other = other.canonical
|
34
30
|
return (other.instance_of? self.class) && (other.name.to_s == @name.to_s)
|
35
31
|
end
|
36
32
|
|
33
|
+
alias eql? ==
|
34
|
+
|
35
|
+
alias <= == # hack to allow rdl_query to work with union types...
|
36
|
+
|
37
37
|
def match(other)
|
38
|
+
other = other.canonical
|
38
39
|
other = other.type if other.instance_of? AnnotatedArgType
|
39
40
|
return true if other.instance_of? WildQuery
|
40
41
|
return self == other
|
data/lib/rdl/types/vararg.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'type'
|
2
|
-
|
3
1
|
module RDL::Type
|
4
2
|
class VarargType < Type
|
5
3
|
attr_reader :type
|
@@ -33,15 +31,16 @@ module RDL::Type
|
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
36
|
-
def eql?(other)
|
37
|
-
self == other
|
38
|
-
end
|
39
|
-
|
40
34
|
def ==(other) # :nodoc:
|
35
|
+
return false if other.nil?
|
36
|
+
other = other.canonical
|
41
37
|
return (other.instance_of? VarargType) && (other.type == @type)
|
42
38
|
end
|
43
39
|
|
40
|
+
alias eql? ==
|
41
|
+
|
44
42
|
def match(other)
|
43
|
+
other = other.canonical
|
45
44
|
other = other.type if other.instance_of? AnnotatedArgType
|
46
45
|
return true if other.instance_of? WildQuery
|
47
46
|
return (other.instance_of? VarargType) && (@type.match(other.type))
|