literal 1.2.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/literal/array.rb +183 -1
- data/lib/literal/data.rb +1 -7
- data/lib/literal/deferred_type.rb +23 -0
- data/lib/literal/enum.rb +9 -2
- data/lib/literal/properties/schema.rb +1 -1
- data/lib/literal/property.rb +4 -4
- data/lib/literal/transforms.rb +142 -0
- data/lib/literal/tuple.rb +60 -0
- data/lib/literal/types/any_type.rb +2 -2
- data/lib/literal/types/boolean_type.rb +2 -2
- data/lib/literal/types/constraint_type.rb +4 -2
- data/lib/literal/types/falsy_type.rb +2 -2
- data/lib/literal/types/frozen_type.rb +35 -1
- data/lib/literal/types/hash_type.rb +22 -1
- data/lib/literal/types/interface_type.rb +29 -1
- data/lib/literal/types/intersection_type.rb +25 -0
- data/lib/literal/types/json_data_type.rb +34 -4
- data/lib/literal/types/map_type.rb +18 -1
- data/lib/literal/types/never_type.rb +18 -3
- data/lib/literal/types/nilable_type.rb +18 -1
- data/lib/literal/types/not_type.rb +22 -1
- data/lib/literal/types/range_type.rb +18 -1
- data/lib/literal/types/set_type.rb +18 -1
- data/lib/literal/types/truthy_type.rb +17 -3
- data/lib/literal/types/tuple_type.rb +19 -2
- data/lib/literal/types/union_type.rb +21 -4
- data/lib/literal/types/void_type.rb +13 -3
- data/lib/literal/types.rb +51 -44
- data/lib/literal/version.rb +1 -1
- data/lib/literal.rb +11 -65
- metadata +7 -12
- data/lib/literal/data_property.rb +0 -16
- data/lib/literal/types/callable_type.rb +0 -31
- data/lib/literal/types/float_type.rb +0 -10
- data/lib/literal/types/integer_type.rb +0 -10
- data/lib/literal/types/lambda_type.rb +0 -12
- data/lib/literal/types/procable_type.rb +0 -12
- data/lib/literal/types/string_type.rb +0 -10
- data/lib/literal/types/symbol_type.rb +0 -10
@@ -2,13 +2,47 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::FrozenType
|
5
|
+
ALWAYS_FROZEN = Set[Symbol, Integer, Float, Numeric, true, false, nil].freeze
|
6
|
+
|
7
|
+
include Literal::Type
|
8
|
+
|
5
9
|
def initialize(type)
|
10
|
+
if ALWAYS_FROZEN.include?(type)
|
11
|
+
warn "_Frozen type is redundant for #{type.inspect} since it is always frozen."
|
12
|
+
end
|
13
|
+
|
6
14
|
@type = type
|
7
15
|
end
|
8
16
|
|
9
|
-
|
17
|
+
attr_reader :type
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
"_Frozen(#{@type.inspect})"
|
21
|
+
end
|
10
22
|
|
11
23
|
def ===(value)
|
12
24
|
value.frozen? && @type === value
|
13
25
|
end
|
26
|
+
|
27
|
+
def >=(other)
|
28
|
+
case other
|
29
|
+
when Literal::Types::FrozenType
|
30
|
+
@type >= other.type
|
31
|
+
when Literal::Types::ConstraintType
|
32
|
+
type_match = false
|
33
|
+
frozen_match = Literal.subtype?(other.property_constraints[:frozen?], of: true)
|
34
|
+
|
35
|
+
other.object_constraints.each do |constraint|
|
36
|
+
frozen_match ||= ALWAYS_FROZEN.include?(constraint)
|
37
|
+
type_match ||= Literal.subtype?(constraint, of: @type)
|
38
|
+
return true if frozen_match && type_match
|
39
|
+
end
|
40
|
+
|
41
|
+
false
|
42
|
+
else
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
freeze
|
14
48
|
end
|
@@ -2,12 +2,18 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::HashType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(key_type, value_type)
|
6
8
|
@key_type = key_type
|
7
9
|
@value_type = value_type
|
8
10
|
end
|
9
11
|
|
10
|
-
|
12
|
+
attr_reader :key_type, :value_type
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"_Hash(#{@key_type.inspect}, #{@value_type.inspect})"
|
16
|
+
end
|
11
17
|
|
12
18
|
def ===(value)
|
13
19
|
return false unless Hash === value
|
@@ -19,6 +25,19 @@ class Literal::Types::HashType
|
|
19
25
|
true
|
20
26
|
end
|
21
27
|
|
28
|
+
def >=(other)
|
29
|
+
case other
|
30
|
+
when Literal::Types::HashType
|
31
|
+
(
|
32
|
+
Literal.subtype?(other.key_type, of: @key_type)
|
33
|
+
) && (
|
34
|
+
Literal.subtype?(other.value_type, of: @value_type)
|
35
|
+
)
|
36
|
+
else
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
22
41
|
def record_literal_type_errors(context)
|
23
42
|
unless Hash === context.actual
|
24
43
|
return
|
@@ -35,4 +54,6 @@ class Literal::Types::HashType
|
|
35
54
|
end
|
36
55
|
end
|
37
56
|
end
|
57
|
+
|
58
|
+
freeze
|
38
59
|
end
|
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::InterfaceType
|
5
|
+
# TODO: We can generate this and make it much more extensive.
|
6
|
+
METHOD_TYPE_MAPPINGS = {
|
7
|
+
:call => Set[Proc, Method],
|
8
|
+
:to_proc => Set[Proc, Method],
|
9
|
+
:to_s => Set[String],
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
include Literal::Type
|
13
|
+
|
5
14
|
def initialize(*methods)
|
6
15
|
raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
|
7
16
|
@methods = methods
|
@@ -9,9 +18,28 @@ class Literal::Types::InterfaceType
|
|
9
18
|
|
10
19
|
attr_reader :methods
|
11
20
|
|
12
|
-
def inspect
|
21
|
+
def inspect
|
22
|
+
"_Interface(#{@methods.map(&:inspect).join(', ')})"
|
23
|
+
end
|
13
24
|
|
14
25
|
def ===(value)
|
15
26
|
@methods.all? { |m| value.respond_to?(m) }
|
16
27
|
end
|
28
|
+
|
29
|
+
def >=(other)
|
30
|
+
case other
|
31
|
+
when Literal::Types::InterfaceType
|
32
|
+
@methods.all? { |m| other.methods.include?(m) }
|
33
|
+
when Module
|
34
|
+
@methods.map { |m| METHOD_TYPE_MAPPINGS[m] }.all? { |types| types&.include?(other) }
|
35
|
+
when Literal::Types::IntersectionType
|
36
|
+
other.types.any? { |type| Literal.subtype?(type, of: self) }
|
37
|
+
when Literal::Types::ConstraintType
|
38
|
+
other.object_constraints.any? { |type| Literal.subtype?(type, of: self) }
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
freeze
|
17
45
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::IntersectionType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(*types)
|
6
8
|
raise Literal::ArgumentError.new("_Intersection type must have at least one type.") if types.size < 1
|
7
9
|
|
@@ -27,4 +29,27 @@ class Literal::Types::IntersectionType
|
|
27
29
|
context.add_child(label: inspect, expected: type, actual: context.actual)
|
28
30
|
end
|
29
31
|
end
|
32
|
+
|
33
|
+
def >=(other)
|
34
|
+
case other
|
35
|
+
when Literal::Types::IntersectionType
|
36
|
+
@types.all? do |type|
|
37
|
+
other.types.any? do |other_type|
|
38
|
+
Literal.subtype?(other_type, of: type)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
when Literal::Types::ConstraintType
|
42
|
+
@types.all? do |type|
|
43
|
+
other.object_constraints.any? do |object_constraint|
|
44
|
+
Literal.subtype?(object_constraint, of: type)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
when Literal::Types::FrozenType
|
48
|
+
@types.all? { |type| Literal.subtype?(other.type, of: type) }
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
freeze
|
30
55
|
end
|
@@ -1,10 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::JSONDataType
|
5
|
+
Instance = new.freeze
|
6
6
|
|
7
|
-
|
7
|
+
include Literal::Type
|
8
|
+
|
9
|
+
COMPATIBLE_TYPES = Set[
|
10
|
+
Integer,
|
11
|
+
Float,
|
12
|
+
String,
|
13
|
+
true,
|
14
|
+
false,
|
15
|
+
nil,
|
16
|
+
Literal::Types::BooleanType::Instance,
|
17
|
+
Instance
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def inspect = "_JSONData"
|
21
|
+
|
22
|
+
def ===(value)
|
8
23
|
case value
|
9
24
|
when String, Integer, Float, true, false, nil
|
10
25
|
true
|
@@ -19,7 +34,7 @@ module Literal::Types::JSONDataType
|
|
19
34
|
end
|
20
35
|
end
|
21
36
|
|
22
|
-
def
|
37
|
+
def record_literal_type_errors(context)
|
23
38
|
case value = context.actual
|
24
39
|
when String, Integer, Float, true, false, nil
|
25
40
|
# nothing to do
|
@@ -35,5 +50,20 @@ module Literal::Types::JSONDataType
|
|
35
50
|
end
|
36
51
|
end
|
37
52
|
|
53
|
+
def >=(other)
|
54
|
+
return true if COMPATIBLE_TYPES.include?(other)
|
55
|
+
|
56
|
+
case other
|
57
|
+
when Literal::Types::ArrayType
|
58
|
+
self >= other.type
|
59
|
+
when Literal::Types::HashType
|
60
|
+
(self >= other.key_type) && (self >= other.value_type)
|
61
|
+
when Literal::Types::ConstraintType
|
62
|
+
other.object_constraints.any? { |type| self >= type }
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
38
68
|
freeze
|
39
69
|
end
|
@@ -2,10 +2,14 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::MapType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(**shape)
|
6
8
|
@shape = shape
|
7
9
|
end
|
8
10
|
|
11
|
+
attr_reader :shape
|
12
|
+
|
9
13
|
def inspect
|
10
14
|
"_Map(#{@shape.inspect})"
|
11
15
|
end
|
@@ -23,7 +27,7 @@ class Literal::Types::MapType
|
|
23
27
|
|
24
28
|
@shape.each do |key, expected|
|
25
29
|
unless context.actual.key?(key) || expected === nil
|
26
|
-
context.add_child(label: "[]", expected
|
30
|
+
context.add_child(label: "[#{key.inspect}]", expected:, actual: nil)
|
27
31
|
next
|
28
32
|
end
|
29
33
|
|
@@ -33,4 +37,17 @@ class Literal::Types::MapType
|
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
40
|
+
|
41
|
+
def >=(other)
|
42
|
+
case other
|
43
|
+
when Literal::Types::MapType
|
44
|
+
other_shape = other.shape
|
45
|
+
|
46
|
+
@shape.all? do |k, v|
|
47
|
+
Literal.subtype?(other_shape[k], of: v)
|
48
|
+
end
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
36
53
|
end
|
@@ -1,12 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::NeverType
|
5
|
+
Instance = new.freeze
|
6
6
|
|
7
|
-
|
7
|
+
include Literal::Type
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"_Never"
|
11
|
+
end
|
12
|
+
|
13
|
+
def ===(value)
|
8
14
|
false
|
9
15
|
end
|
10
16
|
|
17
|
+
def >=(other)
|
18
|
+
case other
|
19
|
+
when Literal::Types::NeverTypeClass
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
11
26
|
freeze
|
12
27
|
end
|
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::NilableType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Nilable(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
nil === value || @type === value
|
@@ -16,5 +22,16 @@ class Literal::Types::NilableType
|
|
16
22
|
@type.record_literal_type_errors(ctx) if @type.respond_to?(:record_literal_type_errors)
|
17
23
|
end
|
18
24
|
|
25
|
+
def >=(other)
|
26
|
+
case other
|
27
|
+
when Literal::Types::NilableType
|
28
|
+
Literal.subtype?(other.type, of: @type)
|
29
|
+
when nil
|
30
|
+
true
|
31
|
+
else
|
32
|
+
Literal.subtype?(other, of: @type)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
19
36
|
freeze
|
20
37
|
end
|
@@ -2,13 +2,34 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::NotType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Not(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
!(@type === value)
|
13
19
|
end
|
20
|
+
|
21
|
+
def >=(other)
|
22
|
+
case other
|
23
|
+
when Literal::Types::NotType
|
24
|
+
Literal.subtype?(other.type, of: @type)
|
25
|
+
when Literal::Types::ConstraintType
|
26
|
+
other.object_constraints.any? { |constraint| Literal.subtype?(constraint, of: self) }
|
27
|
+
when Literal::Types::IntersectionType
|
28
|
+
other.types.any? { |type| Literal.subtype?(type, of: self) }
|
29
|
+
else
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
freeze
|
14
35
|
end
|
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::RangeType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Range(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
Range === value && (
|
@@ -17,4 +23,15 @@ class Literal::Types::RangeType
|
|
17
23
|
)
|
18
24
|
)
|
19
25
|
end
|
26
|
+
|
27
|
+
def >=(other)
|
28
|
+
case other
|
29
|
+
when Literal::Types::RangeType
|
30
|
+
Literal.subtype?(other.type, of: @type)
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
freeze
|
20
37
|
end
|
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::SetType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(type)
|
6
8
|
@type = type
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"_Set(#{@type.inspect})"
|
15
|
+
end
|
10
16
|
|
11
17
|
def ===(value)
|
12
18
|
return false unless Set === value
|
@@ -27,4 +33,15 @@ class Literal::Types::SetType
|
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
36
|
+
|
37
|
+
def >=(other)
|
38
|
+
case other
|
39
|
+
when Literal::Types::SetType
|
40
|
+
Literal.subtype?(other.type, of: @type)
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
freeze
|
30
47
|
end
|
@@ -1,12 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::TruthyType
|
5
|
+
Instance = new.freeze
|
6
|
+
include Literal::Type
|
6
7
|
|
7
|
-
def
|
8
|
+
def inspect
|
9
|
+
"_Truthy"
|
10
|
+
end
|
11
|
+
|
12
|
+
def ===(value)
|
8
13
|
!!value
|
9
14
|
end
|
10
15
|
|
16
|
+
def >=(other)
|
17
|
+
case other
|
18
|
+
when Literal::Types::TruthyType, true
|
19
|
+
true
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
11
25
|
freeze
|
12
26
|
end
|
@@ -2,13 +2,19 @@
|
|
2
2
|
|
3
3
|
# @api private
|
4
4
|
class Literal::Types::TupleType
|
5
|
+
include Literal::Type
|
6
|
+
|
5
7
|
def initialize(*types)
|
6
8
|
raise Literal::ArgumentError.new("_Tuple type must have at least one type.") if types.size < 1
|
7
9
|
|
8
10
|
@types = types
|
9
11
|
end
|
10
12
|
|
11
|
-
|
13
|
+
attr_reader :types
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"_Tuple(#{@types.map(&:inspect).join(', ')})"
|
17
|
+
end
|
12
18
|
|
13
19
|
def ===(value)
|
14
20
|
return false unless Array === value
|
@@ -32,11 +38,22 @@ class Literal::Types::TupleType
|
|
32
38
|
while i < len
|
33
39
|
actual = context.actual[i]
|
34
40
|
if !(expected = @types[i])
|
35
|
-
context.add_child(label: "[#{i}]", expected: Literal::Types::NeverType, actual:)
|
41
|
+
context.add_child(label: "[#{i}]", expected: Literal::Types::NeverType::Instance, actual:)
|
36
42
|
elsif !(expected === actual)
|
37
43
|
context.add_child(label: "[#{i}]", expected:, actual:)
|
38
44
|
end
|
39
45
|
i += 1
|
40
46
|
end
|
41
47
|
end
|
48
|
+
|
49
|
+
def >=(other)
|
50
|
+
case other
|
51
|
+
when Literal::Types::TupleType
|
52
|
+
@types == other.types
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
freeze
|
42
59
|
end
|
@@ -12,7 +12,11 @@ class Literal::Types::UnionType
|
|
12
12
|
@types.freeze
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
attr_reader :types
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"_Union(#{@types.inspect})"
|
19
|
+
end
|
16
20
|
|
17
21
|
def ===(value)
|
18
22
|
types = @types
|
@@ -46,9 +50,20 @@ class Literal::Types::UnionType
|
|
46
50
|
ctx.children.clear if ctx.children.none? { |c| c.children.any? }
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
def >=(other)
|
54
|
+
case other
|
55
|
+
when Literal::Types::UnionType
|
56
|
+
other.types.all? do |other_type|
|
57
|
+
@types.any? do |type|
|
58
|
+
Literal.subtype?(type, of: other_type)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
else
|
62
|
+
@types.any? do |type|
|
63
|
+
Literal.subtype?(other, of: type)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
52
67
|
|
53
68
|
private
|
54
69
|
|
@@ -57,4 +72,6 @@ class Literal::Types::UnionType
|
|
57
72
|
(Literal::Types::UnionType === type) ? load_types(type.types) : @types << type
|
58
73
|
end
|
59
74
|
end
|
75
|
+
|
76
|
+
freeze
|
60
77
|
end
|
@@ -1,10 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# @api private
|
4
|
-
|
5
|
-
|
4
|
+
class Literal::Types::VoidType
|
5
|
+
Instance = new.freeze
|
6
6
|
|
7
|
-
|
7
|
+
include Literal::Type
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"_Void"
|
11
|
+
end
|
12
|
+
|
13
|
+
def ===(_)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def >=(_)
|
8
18
|
true
|
9
19
|
end
|
10
20
|
|