rdl 1.1.1 → 2.0.0.rc1
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 +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))
|