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.
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))