rdl 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rails_types.rb +1 -0
  3. data/lib/rdl.rb +56 -0
  4. data/lib/rdl/config.rb +121 -0
  5. data/lib/rdl/contracts/and.rb +29 -0
  6. data/lib/rdl/contracts/contract.rb +7 -0
  7. data/lib/rdl/contracts/flat.rb +31 -0
  8. data/lib/rdl/contracts/or.rb +25 -0
  9. data/lib/rdl/contracts/proc.rb +24 -0
  10. data/lib/rdl/switch.rb +20 -0
  11. data/lib/rdl/types/annotated_arg.rb +41 -0
  12. data/lib/rdl/types/finitehash.rb +81 -0
  13. data/lib/rdl/types/generic.rb +100 -0
  14. data/lib/rdl/types/intersection.rb +66 -0
  15. data/lib/rdl/types/lexer.rex +39 -0
  16. data/lib/rdl/types/lexer.rex.rb +148 -0
  17. data/lib/rdl/types/method.rb +219 -0
  18. data/lib/rdl/types/nil.rb +50 -0
  19. data/lib/rdl/types/nominal.rb +80 -0
  20. data/lib/rdl/types/optional.rb +54 -0
  21. data/lib/rdl/types/parser.racc +150 -0
  22. data/lib/rdl/types/parser.tab.rb +654 -0
  23. data/lib/rdl/types/singleton.rb +62 -0
  24. data/lib/rdl/types/structural.rb +72 -0
  25. data/lib/rdl/types/top.rb +50 -0
  26. data/lib/rdl/types/tuple.rb +61 -0
  27. data/lib/rdl/types/type.rb +24 -0
  28. data/lib/rdl/types/type_inferencer.rb +73 -0
  29. data/lib/rdl/types/union.rb +74 -0
  30. data/lib/rdl/types/var.rb +51 -0
  31. data/lib/rdl/types/vararg.rb +54 -0
  32. data/lib/rdl/types/wild.rb +26 -0
  33. data/lib/rdl/util.rb +56 -0
  34. data/lib/rdl/wrap.rb +505 -0
  35. data/lib/rdl_types.rb +2 -0
  36. data/types/other/chronic.rb +5 -0
  37. data/types/other/paperclip_attachment.rb +7 -0
  38. data/types/other/securerandom.rb +4 -0
  39. data/types/rails-4.2.1/fixnum.rb +3 -0
  40. data/types/rails-4.2.1/string.rb +3 -0
  41. data/types/rails-tmp/action_dispatch.rb +406 -0
  42. data/types/rails-tmp/active_record.rb +406 -0
  43. data/types/rails-tmp/devise_contracts.rb +216 -0
  44. data/types/ruby-2.2.0/_aliases.rb +4 -0
  45. data/types/ruby-2.2.0/abbrev.rb +5 -0
  46. data/types/ruby-2.2.0/array.rb +137 -0
  47. data/types/ruby-2.2.0/base64.rb +10 -0
  48. data/types/ruby-2.2.0/basic_object.rb +13 -0
  49. data/types/ruby-2.2.0/benchmark.rb +11 -0
  50. data/types/ruby-2.2.0/bigdecimal.rb +15 -0
  51. data/types/ruby-2.2.0/bigmath.rb +12 -0
  52. data/types/ruby-2.2.0/class.rb +17 -0
  53. data/types/ruby-2.2.0/complex.rb +42 -0
  54. data/types/ruby-2.2.0/coverage.rb +6 -0
  55. data/types/ruby-2.2.0/csv.rb +5 -0
  56. data/types/ruby-2.2.0/date.rb +6 -0
  57. data/types/ruby-2.2.0/dir.rb +38 -0
  58. data/types/ruby-2.2.0/encoding.rb +23 -0
  59. data/types/ruby-2.2.0/enumerable.rb +98 -0
  60. data/types/ruby-2.2.0/enumerator.rb +26 -0
  61. data/types/ruby-2.2.0/exception.rb +15 -0
  62. data/types/ruby-2.2.0/file.rb +124 -0
  63. data/types/ruby-2.2.0/fileutils.rb +6 -0
  64. data/types/ruby-2.2.0/fixnum.rb +45 -0
  65. data/types/ruby-2.2.0/float.rb +54 -0
  66. data/types/ruby-2.2.0/gem.rb +245 -0
  67. data/types/ruby-2.2.0/hash.rb +72 -0
  68. data/types/ruby-2.2.0/integer.rb +31 -0
  69. data/types/ruby-2.2.0/io.rb +103 -0
  70. data/types/ruby-2.2.0/kernel.rb +89 -0
  71. data/types/ruby-2.2.0/marshal.rb +5 -0
  72. data/types/ruby-2.2.0/matchdata.rb +26 -0
  73. data/types/ruby-2.2.0/math.rb +53 -0
  74. data/types/ruby-2.2.0/numeric.rb +46 -0
  75. data/types/ruby-2.2.0/object.rb +73 -0
  76. data/types/ruby-2.2.0/pathname.rb +106 -0
  77. data/types/ruby-2.2.0/process.rb +127 -0
  78. data/types/ruby-2.2.0/random.rb +15 -0
  79. data/types/ruby-2.2.0/range.rb +38 -0
  80. data/types/ruby-2.2.0/rational.rb +31 -0
  81. data/types/ruby-2.2.0/regexp.rb +30 -0
  82. data/types/ruby-2.2.0/set.rb +58 -0
  83. data/types/ruby-2.2.0/string.rb +145 -0
  84. data/types/ruby-2.2.0/strscan.rb +7 -0
  85. data/types/ruby-2.2.0/symbol.rb +29 -0
  86. data/types/ruby-2.2.0/time.rb +68 -0
  87. data/types/ruby-2.2.0/uri.rb +18 -0
  88. data/types/ruby-2.2.0/yaml.rb +5 -0
  89. 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