literal 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/literal/array.rb +183 -1
  3. data/lib/literal/data.rb +1 -7
  4. data/lib/literal/deferred_type.rb +23 -0
  5. data/lib/literal/enum.rb +9 -2
  6. data/lib/literal/properties/schema.rb +1 -1
  7. data/lib/literal/property.rb +4 -4
  8. data/lib/literal/transforms.rb +142 -0
  9. data/lib/literal/tuple.rb +60 -0
  10. data/lib/literal/types/any_type.rb +2 -2
  11. data/lib/literal/types/boolean_type.rb +2 -2
  12. data/lib/literal/types/constraint_type.rb +4 -2
  13. data/lib/literal/types/falsy_type.rb +2 -2
  14. data/lib/literal/types/frozen_type.rb +35 -1
  15. data/lib/literal/types/hash_type.rb +22 -1
  16. data/lib/literal/types/interface_type.rb +29 -1
  17. data/lib/literal/types/intersection_type.rb +25 -0
  18. data/lib/literal/types/json_data_type.rb +34 -4
  19. data/lib/literal/types/map_type.rb +18 -1
  20. data/lib/literal/types/never_type.rb +18 -3
  21. data/lib/literal/types/nilable_type.rb +18 -1
  22. data/lib/literal/types/not_type.rb +22 -1
  23. data/lib/literal/types/range_type.rb +18 -1
  24. data/lib/literal/types/set_type.rb +18 -1
  25. data/lib/literal/types/truthy_type.rb +17 -3
  26. data/lib/literal/types/tuple_type.rb +19 -2
  27. data/lib/literal/types/union_type.rb +21 -4
  28. data/lib/literal/types/void_type.rb +13 -3
  29. data/lib/literal/types.rb +51 -44
  30. data/lib/literal/version.rb +1 -1
  31. data/lib/literal.rb +11 -65
  32. metadata +7 -12
  33. data/lib/literal/data_property.rb +0 -16
  34. data/lib/literal/types/callable_type.rb +0 -31
  35. data/lib/literal/types/float_type.rb +0 -10
  36. data/lib/literal/types/integer_type.rb +0 -10
  37. data/lib/literal/types/lambda_type.rb +0 -12
  38. data/lib/literal/types/procable_type.rb +0 -12
  39. data/lib/literal/types/string_type.rb +0 -10
  40. data/lib/literal/types/symbol_type.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d40ba211b6f7b2ad3dad97feb118d3742b43dbd2f1773daa8d6979f597cbf428
4
- data.tar.gz: 1fb316fac766b9b25041c2bc201c8ddaed8d3e59ed5f5aec9c7f93f0306df921
3
+ metadata.gz: 8ee13d939895155fc3ce4ae1717036b565b0a59d7a4e87b50ea0a1390cc40bf3
4
+ data.tar.gz: 42df47f2efce39eb14976d226645adb7a9dacc681948a71a9254ca44e19d8d1f
5
5
  SHA512:
6
- metadata.gz: 37fee8ee4d14f41573f5a4ce2d27d3d98bc1b78a54a35d850a41fbeedfc7e1b1f6f8a3533cbb82c35b81212079d4f495ad17cde8651a7b56fff3e6ecb99b5078
7
- data.tar.gz: ca6fa6e45d9a344880ebbbfba69c82f9444809ad4b6645e89c4cc0c89c58e78ac66d2edf80612cb2084bac0b46b9c0a686458983f47ff5716f4016b9545f723c
6
+ metadata.gz: f3a9b286744de95185baea476e8d7387238912aeb7f956eeef4e7b203bd0a55313885baaf6c5e71e27eb6d252961d44f850ede7e09abbc05c323c88a099be6f6
7
+ data.tar.gz: f2cc33927ce452c94b3146dc7714cd9078cd532da5523afbbae5372ec2a5aa0319460db07525d9d7a5d709deb9ff929b804d0f3a86425f3d4844bf641a5a669b
data/lib/literal/array.rb CHANGED
@@ -23,7 +23,7 @@ class Literal::Array
23
23
  def >=(other)
24
24
  case other
25
25
  when Literal::Array::Generic
26
- @type >= other.type
26
+ Literal.subtype?(other.type, of: @type)
27
27
  else
28
28
  false
29
29
  end
@@ -176,6 +176,11 @@ class Literal::Array
176
176
  self
177
177
  end
178
178
 
179
+ def combination(...)
180
+ @__value__.combination(...)
181
+ self
182
+ end
183
+
179
184
  def compact
180
185
  # @TODO if this is an array of nils, we should return an emtpy array
181
186
  __with__(@__value__)
@@ -190,6 +195,19 @@ class Literal::Array
190
195
  @__value__.count(...)
191
196
  end
192
197
 
198
+ def delete(...)
199
+ @__value__.delete(...)
200
+ end
201
+
202
+ def delete_at(...)
203
+ @__value__.delete_at(...)
204
+ end
205
+
206
+ def delete_if(...)
207
+ @__value__.delete_if(...)
208
+ self
209
+ end
210
+
193
211
  def dig(...)
194
212
  @__value__.dig(...)
195
213
  end
@@ -221,15 +239,33 @@ class Literal::Array
221
239
  self
222
240
  end
223
241
 
242
+ def flatten(...)
243
+ __with__(@__value__.flatten(...))
244
+ end
245
+
224
246
  def first(...)
225
247
  @__value__.first(...)
226
248
  end
227
249
 
250
+ def flatten!(...)
251
+ @__value__.flatten!(...) ? self : nil
252
+ end
253
+
228
254
  def freeze
229
255
  @__value__.freeze
230
256
  super
231
257
  end
232
258
 
259
+ def hash
260
+ [self.class, @__value__].hash
261
+ end
262
+
263
+ def include?(...)
264
+ @__value__.include?(...)
265
+ end
266
+
267
+ alias_method :index, :find_index
268
+
233
269
  def insert(index, *value)
234
270
  Literal.check(actual: value, expected: _Array(@__type__)) do |c|
235
271
  c.fill_receiver(receiver: self, method: "#insert")
@@ -239,6 +275,21 @@ class Literal::Array
239
275
  self
240
276
  end
241
277
 
278
+ def inspect
279
+ @__value__.inspect
280
+ end
281
+
282
+ def intersect?(other)
283
+ case other
284
+ when ::Array
285
+ @__value__.intersect?(other)
286
+ when Literal::Array
287
+ @__value__.intersect?(other.__value__)
288
+ else
289
+ raise ArgumentError.new("Cannot perform `intersect?` with #{other.class.name}.")
290
+ end
291
+ end
292
+
242
293
  def intersection(*values)
243
294
  values.map! do |value|
244
295
  case value
@@ -252,6 +303,20 @@ class Literal::Array
252
303
  __with__(@__value__.intersection(*values))
253
304
  end
254
305
 
306
+ def join(...)
307
+ @__value__.join(...)
308
+ end
309
+
310
+ def keep_if(...)
311
+ return_value = @__value__.keep_if(...)
312
+ case return_value
313
+ when Array
314
+ self
315
+ else
316
+ return_value
317
+ end
318
+ end
319
+
255
320
  def last(...)
256
321
  @__value__.last(...)
257
322
  end
@@ -285,6 +350,10 @@ class Literal::Array
285
350
 
286
351
  alias_method :collect!, :map!
287
352
 
353
+ def sum(...)
354
+ @__value__.sum(...)
355
+ end
356
+
288
357
  def max(n = nil, &)
289
358
  if n
290
359
  __with__(@__value__.max(n, &))
@@ -305,14 +374,67 @@ class Literal::Array
305
374
  __with__(@__value__.minmax(...))
306
375
  end
307
376
 
377
+ def narrow(type)
378
+ unless Literal.subtype?(type, of: @__type__)
379
+ raise ArgumentError.new("Cannot narrow #{@__type__} to #{type}")
380
+ end
381
+
382
+ if __type__ != type
383
+ @__value__.each do |item|
384
+ Literal.check(actual: item, expected: type) do |c|
385
+ c.fill_receiver(receiver: self, method: "#narrow")
386
+ end
387
+ end
388
+ end
389
+
390
+ Literal::Array.allocate.__initialize_without_check__(
391
+ @__value__,
392
+ type:,
393
+ )
394
+ end
395
+
308
396
  def one?(...)
309
397
  @__value__.one?(...)
310
398
  end
311
399
 
400
+ def pack(...)
401
+ @__value__.pack(...)
402
+ end
403
+
312
404
  def pop(...)
313
405
  @__value__.pop(...)
314
406
  end
315
407
 
408
+ def product(*others, &)
409
+ if block_given?
410
+ @__value__.product(
411
+ *others.map do |other|
412
+ case other
413
+ when Array
414
+ other
415
+ when Literal::Array
416
+ other.__value__
417
+ end
418
+ end, &
419
+ )
420
+
421
+ self
422
+ elsif others.all?(Literal::Array)
423
+ tuple_type = Literal::Tuple(
424
+ @__type__,
425
+ *others.map(&:__type__)
426
+ )
427
+
428
+ values = @__value__.product(*others.map(&:__value__)).map do |tuple_values|
429
+ tuple_type.new(*tuple_values)
430
+ end
431
+
432
+ Literal::Array(tuple_type).new(*values)
433
+ else
434
+ @__value__.product(*others)
435
+ end
436
+ end
437
+
316
438
  def push(*value)
317
439
  Literal.check(actual: value, expected: _Array(@__type__)) do |c|
318
440
  c.fill_receiver(receiver: self, method: "#push")
@@ -354,14 +476,42 @@ class Literal::Array
354
476
  self
355
477
  end
356
478
 
479
+ alias_method :initialize_copy, :replace
480
+
481
+ def rotate(...)
482
+ __with__(@__value__.rotate(...))
483
+ end
484
+
485
+ def rotate!(...)
486
+ @__value__.rotate!(...)
487
+ self
488
+ end
489
+
357
490
  def sample(...)
358
491
  @__value__.sample(...)
359
492
  end
360
493
 
494
+ def select(...)
495
+ __with__(@__value__.select(...))
496
+ end
497
+
498
+ def select!(...)
499
+ @__value__.select!(...)
500
+ end
501
+
361
502
  def shift(...)
362
503
  @__value__.shift(...)
363
504
  end
364
505
 
506
+ def shuffle(...)
507
+ __with__(@__value__.shuffle(...))
508
+ end
509
+
510
+ def shuffle!(...)
511
+ @__value__.shuffle!(...)
512
+ self
513
+ end
514
+
365
515
  def size(...)
366
516
  @__value__.size(...)
367
517
  end
@@ -375,12 +525,18 @@ class Literal::Array
375
525
  self
376
526
  end
377
527
 
528
+ def take(...)
529
+ __with__(@__value__.take(...))
530
+ end
531
+
378
532
  def to_a
379
533
  @__value__.dup
380
534
  end
381
535
 
382
536
  alias_method :to_ary, :to_a
383
537
 
538
+ alias_method :to_s, :inspect
539
+
384
540
  def uniq
385
541
  __with__(@__value__.uniq)
386
542
  end
@@ -425,4 +581,30 @@ class Literal::Array
425
581
  @__value__.values_at(*indexes)
426
582
  )
427
583
  end
584
+
585
+ def |(other)
586
+ case other
587
+ when ::Array
588
+ Literal.check(actual: other, expected: _Array(@__type__)) do |c|
589
+ c.fill_receiver(receiver: self, method: "#|")
590
+ end
591
+
592
+ __with__(@__value__ | other)
593
+ when Literal::Array(@__type__)
594
+ __with__(@__value__ | other.__value__)
595
+ when Literal::Array
596
+ raise Literal::TypeError.new(
597
+ context: Literal::TypeError::Context.new(
598
+ expected: Literal::Array(@__type__),
599
+ actual: other
600
+ )
601
+ )
602
+ else
603
+ raise ArgumentError.new("Cannot perform `|` with #{other.class.name}.")
604
+ end
605
+ end
606
+
607
+ def fetch(...)
608
+ @__value__.fetch(...)
609
+ end
428
610
  end
data/lib/literal/data.rb CHANGED
@@ -9,17 +9,11 @@ class Literal::Data < Literal::DataStructure
9
9
  def literal_properties
10
10
  return @literal_properties if defined?(@literal_properties)
11
11
 
12
- if superclass.is_a?(Literal::Data)
12
+ if superclass < Literal::Data
13
13
  @literal_properties = superclass.literal_properties.dup
14
14
  else
15
15
  @literal_properties = Literal::Properties::DataSchema.new
16
16
  end
17
17
  end
18
-
19
- private
20
-
21
- def __literal_property_class__
22
- Literal::DataProperty
23
- end
24
18
  end
25
19
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Types::DeferredType
4
+ include Literal::Type
5
+
6
+ def initialize(&block)
7
+ @block = block
8
+ end
9
+
10
+ attr_reader :block
11
+
12
+ def inspect
13
+ "_Deferred"
14
+ end
15
+
16
+ def ===(other)
17
+ @block.call === other
18
+ end
19
+
20
+ def >=(other)
21
+ Literal.subtype?(other, of: @block.call)
22
+ end
23
+ end
data/lib/literal/enum.rb CHANGED
@@ -77,6 +77,9 @@ class Literal::Enum
77
77
  object = const_get(name)
78
78
 
79
79
  if self === object
80
+ if @values.key?(object.value)
81
+ raise ArgumentError.new("The value #{object.value} is already used by #{@values[object.value].name}.")
82
+ end
80
83
  object.instance_variable_set(:@name, name)
81
84
  @values[object.value] = object
82
85
  @members << object
@@ -155,8 +158,12 @@ class Literal::Enum
155
158
  method(:coerce).to_proc
156
159
  end
157
160
 
158
- def to_h(*args)
159
- @values.dup
161
+ def to_h(&)
162
+ if block_given?
163
+ @members.to_h(&)
164
+ else
165
+ @members.to_h { |it| [it, it.value] }
166
+ end
160
167
  end
161
168
  end
162
169
 
@@ -154,7 +154,7 @@ class Literal::Properties::Schema
154
154
  end
155
155
 
156
156
  def generate_initializer_body(buffer = +"")
157
- buffer << " properties = self.class.literal_properties.properties_index\n"
157
+ buffer << " __properties__ = self.class.literal_properties.properties_index\n"
158
158
  generate_initializer_handle_properties(@sorted_properties, buffer)
159
159
  end
160
160
 
@@ -157,7 +157,7 @@ class Literal::Property
157
157
 
158
158
  def generate_initializer_handle_property(buffer = +"")
159
159
  buffer << " # " << @name.name << "\n" <<
160
- " property = properties[:" << @name.name << "]\n"
160
+ " __property__ = __properties__[:" << @name.name << "]\n"
161
161
 
162
162
  if @kind == :keyword && ruby_keyword?
163
163
  generate_initializer_escape_keyword(buffer)
@@ -191,7 +191,7 @@ class Literal::Property
191
191
  def generate_initializer_coerce_property(buffer = +"")
192
192
  buffer <<
193
193
  escaped_name <<
194
- "= property.coerce(" <<
194
+ "= __property__.coerce(" <<
195
195
  escaped_name <<
196
196
  ", context: self)\n"
197
197
  end
@@ -204,12 +204,12 @@ class Literal::Property
204
204
  escaped_name <<
205
205
  "\n " <<
206
206
  escaped_name <<
207
- " = property.default_value\n end\n"
207
+ " = __property__.default_value\n end\n"
208
208
  end
209
209
 
210
210
  def generate_initializer_check_type(buffer = +"")
211
211
  buffer <<
212
- " property.check_initializer(self, " << escaped_name << ")\n"
212
+ " __property__.check_initializer(self, " << escaped_name << ")\n"
213
213
  end
214
214
 
215
215
  def generate_initializer_assign_value(buffer = +"")
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A map of core types to transform Procs mapping to the new type.
4
+ Literal::TRANSFORMS = {
5
+ Integer => {
6
+ abs: Integer,
7
+ ceil: Integer,
8
+ chr: String,
9
+ denominator: Integer,
10
+ even?: Literal::Types::BooleanType::Instance,
11
+ floor: Integer,
12
+ hash: Integer,
13
+ inspect: String,
14
+ integer?: true,
15
+ magnitude: Integer,
16
+ negative?: Literal::Types::BooleanType::Instance,
17
+ next: Integer,
18
+ numerator: Integer,
19
+ odd?: Literal::Types::BooleanType::Instance,
20
+ ord: Integer,
21
+ positive?: Literal::Types::BooleanType::Instance,
22
+ pred: Integer,
23
+ round: Integer,
24
+ size: Integer,
25
+ succ: Integer,
26
+ to_f: Float,
27
+ to_i: Integer,
28
+ to_int: Integer,
29
+ to_r: Rational,
30
+ to_s: String,
31
+ truncate: Integer,
32
+ zero?: Literal::Types::BooleanType::Instance,
33
+ },
34
+ String => {
35
+ ascii_only?: Literal::Types::BooleanType::Instance,
36
+ bytesize: Integer,
37
+ capitalize: String,
38
+ chomp: String,
39
+ chop: String,
40
+ downcase: String,
41
+ dump: String,
42
+ empty?: Literal::Types::BooleanType::Instance,
43
+ hash: Integer,
44
+ inspect: String,
45
+ length: Integer,
46
+ lstrip: String,
47
+ ord: Integer,
48
+ reverse: String,
49
+ rstrip: String,
50
+ scrub: String,
51
+ size: Integer,
52
+ strip: String,
53
+ swapcase: String,
54
+ to_str: String,
55
+ upcase: String,
56
+ valid_encoding?: Literal::Types::BooleanType::Instance,
57
+ },
58
+ Numeric => {
59
+ to_i: Integer,
60
+ to_f: Float,
61
+ to_s: String,
62
+ },
63
+ Array => {
64
+ size: Integer,
65
+ length: Integer,
66
+ empty?: Literal::Types::BooleanType::Instance,
67
+ sort: Array,
68
+ to_a: Array,
69
+ to_ary: Array,
70
+ },
71
+ Hash => {
72
+ empty?: Literal::Types::BooleanType::Instance,
73
+ inspect: String,
74
+ keys: Array,
75
+ length: Integer,
76
+ size: Integer,
77
+ to_a: Array,
78
+ to_h: Hash,
79
+ to_s: String,
80
+ values: Array,
81
+ },
82
+ Set => {
83
+ empty?: Literal::Types::BooleanType::Instance,
84
+ inspect: String,
85
+ length: Integer,
86
+ size: Integer,
87
+ to_a: Array,
88
+ to_s: String,
89
+ },
90
+ Float => {
91
+ abs: Float,
92
+ ceil: Integer,
93
+ floor: Integer,
94
+ nan?: Literal::Types::BooleanType::Instance,
95
+ negative?: Literal::Types::BooleanType::Instance,
96
+ positive?: Literal::Types::BooleanType::Instance,
97
+ round: Integer,
98
+ to_i: Integer,
99
+ to_s: String,
100
+ truncate: Integer,
101
+ zero?: Literal::Types::BooleanType::Instance,
102
+ },
103
+ Symbol => {
104
+ empty?: Literal::Types::BooleanType::Instance,
105
+ inspect: String,
106
+ length: Integer,
107
+ size: Integer,
108
+ to_s: String,
109
+ to_sym: Symbol,
110
+ },
111
+ Range => {
112
+ begin: Object,
113
+ end: Object,
114
+ exclude_end?: Literal::Types::BooleanType::Instance,
115
+ first: Object,
116
+ last: Object,
117
+ max: Object,
118
+ min: Object,
119
+ size: Integer,
120
+ to_a: Array,
121
+ to_s: String,
122
+ },
123
+ Regexp => {
124
+ casefold?: Literal::Types::BooleanType::Instance,
125
+ inspect: String,
126
+ source: String,
127
+ to_s: String,
128
+ },
129
+ Time => {
130
+ day: Integer,
131
+ hour: Integer,
132
+ inspect: String,
133
+ min: Integer,
134
+ month: Integer,
135
+ sec: Integer,
136
+ to_a: Array,
137
+ to_f: Float,
138
+ to_i: Integer,
139
+ to_s: String,
140
+ year: Integer,
141
+ },
142
+ }.transform_values! { |it| it.transform_keys(&:to_proc) }.freeze
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Tuple
4
+ class Generic
5
+ include Literal::Type
6
+
7
+ def initialize(*types)
8
+ @types = types
9
+ end
10
+
11
+ attr_reader :types
12
+
13
+ def ===(other)
14
+ return false unless Literal::Tuple === other
15
+ types = @types
16
+ other_types = other.__types__
17
+
18
+ return false unless types.size == other_types.size
19
+
20
+ i, len = 0, types.size
21
+ while i < len
22
+ return false unless Literal.subtype?(other_types[i], of: types[i])
23
+ i += 1
24
+ end
25
+
26
+ true
27
+ end
28
+
29
+ def >=(other)
30
+ case other
31
+ when Literal::Tuple::Generic
32
+ types = @types
33
+ other_types = other.types
34
+
35
+ return false unless types.size == other_types.size
36
+
37
+ i, len = 0, types.size
38
+ while i < len
39
+ return false unless Literal.subtype?(other_types[i], of: types[i])
40
+ i += 1
41
+ end
42
+
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ def new(*values)
50
+ Literal::Tuple.new(values, @types)
51
+ end
52
+ end
53
+
54
+ def initialize(values, types)
55
+ @__values__ = values
56
+ @__types__ = types
57
+ end
58
+
59
+ attr_reader :__values__, :__types__
60
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  # @api private
4
4
  class Literal::Types::AnyType
5
+ Instance = new.freeze
6
+
5
7
  include Literal::Type
6
8
 
7
9
  def inspect
@@ -16,7 +18,5 @@ class Literal::Types::AnyType
16
18
  !(other === nil)
17
19
  end
18
20
 
19
- Instance = new.freeze
20
-
21
21
  freeze
22
22
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  # @api private
4
4
  class Literal::Types::BooleanType
5
+ Instance = new.freeze
6
+
5
7
  include Literal::Type
6
8
 
7
9
  def inspect
@@ -21,7 +23,5 @@ class Literal::Types::BooleanType
21
23
  end
22
24
  end
23
25
 
24
- Instance = new.freeze
25
-
26
26
  freeze
27
27
  end
@@ -40,7 +40,7 @@ class Literal::Types::ConstraintType
40
40
  when Literal::Types::ConstraintType
41
41
  other_object_constraints = other.object_constraints
42
42
  return false unless @object_constraints.all? do |constraint|
43
- other_object_constraints.all? { |c| Literal.subtype?(c, of: constraint) }
43
+ other_object_constraints.any? { |c| Literal.subtype?(c, of: constraint) }
44
44
  end
45
45
 
46
46
  other_property_constraints = other.property_constraints
@@ -52,10 +52,12 @@ class Literal::Types::ConstraintType
52
52
  when Literal::Types::IntersectionType
53
53
  other_object_constraints = other.types
54
54
  return false unless @object_constraints.all? do |constraint|
55
- other_object_constraints.all? { |c| Literal.subtype?(c, of: constraint) }
55
+ other_object_constraints.any? { |c| Literal.subtype?(c, of: constraint) }
56
56
  end
57
57
 
58
58
  true
59
+ when Literal::Types::FrozenType
60
+ @object_constraints.all? { |constraint| Literal.subtype?(other.type, of: constraint) }
59
61
  else
60
62
  false
61
63
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  # @api private
4
4
  class Literal::Types::FalsyType
5
+ Instance = new.freeze
6
+
5
7
  include Literal::Type
6
8
 
7
9
  def initialize
@@ -25,7 +27,5 @@ class Literal::Types::FalsyType
25
27
  end
26
28
  end
27
29
 
28
- Instance = new.freeze
29
-
30
30
  freeze
31
31
  end