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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +6 -0
  3. data/README.md +211 -32
  4. data/gemfiles/Gemfile.travis +1 -1
  5. data/lib/rdl.rb +85 -18
  6. data/lib/rdl/info.rb +74 -0
  7. data/lib/rdl/query.rb +8 -9
  8. data/lib/rdl/typecheck.rb +1057 -0
  9. data/lib/rdl/types/annotated_arg.rb +5 -5
  10. data/lib/rdl/types/{nil.rb → bot.rb} +9 -13
  11. data/lib/rdl/types/dependent_arg.rb +5 -5
  12. data/lib/rdl/types/dots_query.rb +2 -0
  13. data/lib/rdl/types/finitehash.rb +67 -24
  14. data/lib/rdl/types/generic.rb +13 -21
  15. data/lib/rdl/types/intersection.rb +9 -8
  16. data/lib/rdl/types/method.rb +30 -32
  17. data/lib/rdl/types/nominal.rb +22 -16
  18. data/lib/rdl/types/optional.rb +8 -22
  19. data/lib/rdl/types/parser.racc +8 -3
  20. data/lib/rdl/types/parser.tab.rb +131 -118
  21. data/lib/rdl/types/singleton.rb +15 -10
  22. data/lib/rdl/types/structural.rb +6 -6
  23. data/lib/rdl/types/top.rb +6 -6
  24. data/lib/rdl/types/tuple.rb +56 -24
  25. data/lib/rdl/types/type.rb +9 -0
  26. data/lib/rdl/types/type_inferencer.rb +1 -1
  27. data/lib/rdl/types/union.rb +52 -26
  28. data/lib/rdl/types/var.rb +7 -6
  29. data/lib/rdl/types/vararg.rb +5 -6
  30. data/lib/rdl/types/wild_query.rb +9 -2
  31. data/lib/rdl/util.rb +9 -7
  32. data/lib/rdl/wrap.rb +90 -72
  33. data/lib/rdl_types.rb +2 -2
  34. data/rdl.gemspec +6 -8
  35. data/test/test_alias.rb +4 -3
  36. data/test/test_contract.rb +5 -4
  37. data/test/test_dsl.rb +2 -1
  38. data/test/test_generic.rb +30 -26
  39. data/test/test_intersection.rb +3 -3
  40. data/test/test_le.rb +129 -61
  41. data/test/test_lib_types.rb +3 -2
  42. data/test/test_member.rb +33 -46
  43. data/test/test_parser.rb +113 -116
  44. data/test/test_query.rb +2 -1
  45. data/test/test_rdl.rb +64 -6
  46. data/test/test_rdl_type.rb +3 -2
  47. data/test/test_type_contract.rb +30 -12
  48. data/test/test_typecheck.rb +893 -0
  49. data/test/test_types.rb +50 -54
  50. data/test/test_wrap.rb +2 -1
  51. data/types/ruby-2.x/_aliases.rb +13 -2
  52. data/types/ruby-2.x/bigdecimal.rb +60 -85
  53. data/types/ruby-2.x/bignum.rb +80 -119
  54. data/types/ruby-2.x/complex.rb +33 -40
  55. data/types/ruby-2.x/fixnum.rb +81 -120
  56. data/types/ruby-2.x/float.rb +79 -116
  57. data/types/ruby-2.x/integer.rb +187 -22
  58. data/types/ruby-2.x/nil.rb +12 -0
  59. data/types/ruby-2.x/numeric.rb +38 -38
  60. data/types/ruby-2.x/object.rb +3 -3
  61. data/types/ruby-2.x/random.rb +2 -0
  62. data/types/ruby-2.x/range.rb +20 -19
  63. data/types/ruby-2.x/rational.rb +40 -40
  64. data/types/ruby-2.x/regexp.rb +4 -4
  65. data/types/ruby-2.x/string.rb +15 -17
  66. metadata +17 -16
  67. data/lib/rdl/types/.#lexer.rex +0 -1
@@ -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
- end
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
- ":{@val}"
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?(NominalType) && @val.class == other.klass) ||
56
- (other.instance_of?(NominalType) && @val.is_a?(other.klass))
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.nil? || obj.equal?(@val)
67
+ obj.equal?(@val)
63
68
  end
64
69
 
65
70
  def instantiate(inst)
@@ -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 &&
@@ -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
 
@@ -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
- @@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
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 to_s
28
- "[#{@params.map { |t| t.to_s }.join(', ')}]"
20
+ def canonical
21
+ return @array if @array
22
+ return self
29
23
  end
30
24
 
31
- def eql?(other)
32
- self == other
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
- return self == other
48
- # Subtyping with Array not allowed
49
- # All positions of Tuple are invariant since tuples are mutable
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
- TupleType.new(*@params.map { |t| t.instantiate(inst) })
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
@@ -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
@@ -13,7 +13,7 @@ module RDL
13
13
  if current_types.size == 1
14
14
  current_types.to_a[0]
15
15
  elsif current_types.size == 0
16
- RDL::Type::NilType.new
16
+ $__rdl_nil_type
17
17
  else
18
18
  RDL::Type::UnionType.new(*self.unify_param_types(current_types))
19
19
  end
@@ -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? NilType
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 to_s # :nodoc:
47
- "#{@types.map { |t| t.to_s }.join(' or ')}"
33
+ def canonical
34
+ canonicalize!
35
+ return @canonical if @canonical
36
+ return self
48
37
  end
49
38
 
50
- def eql?(other)
51
- self == other
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 (other.instance_of? UnionType) && (other.types == @types)
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
- 41 + @types.hash
104
+ return @hash
79
105
  end
80
106
  end
81
107
  end
@@ -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
@@ -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))