rdl 1.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 (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