literal 1.2.0 → 1.4.0

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 (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