literal 1.0.0.rc1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75c7cc8853143bd8f4ac74b7fc4a5b5d2812bd39c0b4f696a87c2ffc0fb31046
4
- data.tar.gz: 7781d6362e9ae8f75d0156809f95b0c1f8a37df855356173c9b6f09428419bc0
3
+ metadata.gz: b459adebd387b12382d4e049a1f9211f6330bc6f77d66a8380bfbe758a4fb224
4
+ data.tar.gz: d771162ac9e9939e20b9478225baa23dc5d3b4d2fd0f0daa3c821277053ea88f
5
5
  SHA512:
6
- metadata.gz: 6acc2552c11ff3c5f12877b5597f9a6a138bf4901f5fce3801004e286b522c6263fa2454621ac4f5511f553af073d5113d96cd1af1045bc9e44c014c15ab7420
7
- data.tar.gz: 6f9b205b3343426385185ebd0ea0e04164f6a06f9d42d6cb34c542c8d12a98ab327e274b336fb13d389e7380f5bd79831eb2b508f2d019cd6143658db40f3195
6
+ metadata.gz: 68c949539f7002388ccff9c0547c48988a3ccb9ae2cb3262f8ab9119f0187f8ff775c46d3b2aa8abbcc1bbb68a767427c99b48af898744c58bfa004fec3dd491
7
+ data.tar.gz: 872aeda1f2efb4023e9c34c324e8fba149737d5744bc735e57b0ffdbb52be6001f39360fa86c5f981d70e70b5698394eef228eef9dde2d5ca387180414aac555
data/README.md CHANGED
@@ -1,3 +1,3 @@
1
- # A Literal Ruby Gem [WIP]
1
+ # A Literal Ruby Gem
2
2
 
3
3
  See the website for [documentation](https://literal.fun/docs).
data/lib/literal/enum.rb CHANGED
@@ -142,8 +142,17 @@ class Literal::Enum
142
142
  @values.fetch(...)
143
143
  end
144
144
 
145
+ def coerce(value)
146
+ case value
147
+ when self
148
+ value
149
+ else
150
+ self[value]
151
+ end
152
+ end
153
+
145
154
  def to_proc
146
- method(:cast).to_proc
155
+ method(:coerce).to_proc
147
156
  end
148
157
 
149
158
  def to_h(*args)
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Flags
4
+ include Enumerable
5
+
6
+ def initialize(value = 0, **new_flags)
7
+ if new_flags.length > 0
8
+ value = self.class.calculate_from_hash(new_flags)
9
+ end
10
+
11
+ @value = value
12
+
13
+ freeze
14
+ end
15
+
16
+ def __initialize_from_value__(value)
17
+ @value = value
18
+ freeze
19
+ end
20
+
21
+ attr_reader :value
22
+
23
+ def self.define(**flags)
24
+ raise ArgumentError if frozen?
25
+ unique_values = flags.values
26
+ unique_values.uniq!
27
+
28
+ if unique_values.length != flags.length
29
+ raise Literal::ArgumentError.new("Flags must be unique.")
30
+ end
31
+
32
+ const_set(:FLAGS, flags.dup.freeze)
33
+
34
+ flags.each do |name, bit|
35
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
36
+ # frozen_string_literal: true
37
+
38
+ def #{name}?
39
+ @value & (2 ** #{bit}) > 0
40
+ end
41
+ RUBY
42
+ end
43
+
44
+ freeze
45
+ end
46
+
47
+ # () -> (Integer) -> Literal::Flags
48
+ def self.to_proc
49
+ proc { |value| new(value) }
50
+ end
51
+
52
+ # (Integer) -> String
53
+ def self.calculate_bit_string(value)
54
+ value.to_s(2).rjust(self::BITS, "0")
55
+ end
56
+
57
+ # (Array(Boolean)) -> Integer
58
+ def self.calculate_from_array(array)
59
+ array.reverse_each.with_index.reduce(0) do |value, (bit, index)|
60
+ value | (bit ? 1 << index : 0)
61
+ end
62
+ end
63
+
64
+ # (Hash(Symbol, Boolean)) -> Integer
65
+ def self.calculate_from_hash(hash)
66
+ flags = self::FLAGS
67
+ hash.reduce(0) do |value, (key, bit)|
68
+ value | (bit ? 2 ** flags.fetch(key) : 0)
69
+ end
70
+ end
71
+
72
+ # (Integer) -> Array(Symbol)
73
+ def self.calculate_tokens(value)
74
+ flags = self::FLAGS
75
+ flags.keys.select { |t| value & (2 ** flags.fetch(t)) > 0 }
76
+ end
77
+
78
+ # (Integer) -> Array(Boolean)
79
+ def self.calculate_array(value)
80
+ bits = self::BITS
81
+ Array.new(bits) { |i| (value & (2 ** (bits - 1 - i)) > 0) }
82
+ end
83
+
84
+ # (Array(Symbol)) -> Integer
85
+ def self.calculate_from_tokens(tokens)
86
+ flags = self::FLAGS
87
+ tokens.reduce(0) { |f, t| f | (2 ** flags.fetch(t)) }
88
+ end
89
+
90
+ # (Integer) -> Hash(Symbol, Boolean)
91
+ def self.calculate_hash_from_value(value)
92
+ self::FLAGS.transform_values do |bit|
93
+ (value & (2 ** bit)) > 0
94
+ end
95
+ end
96
+
97
+ # () -> Array(Symbol)
98
+ def self.keys
99
+ @flags.keys
100
+ end
101
+
102
+ # (String) -> Literal::Flags
103
+ def self.from_bit_string(bit_string)
104
+ from_int(bit_string.to_i(2))
105
+ end
106
+
107
+ # (Array(Boolean)) -> Literal::Flags
108
+ def self.from_array(array)
109
+ if array.length != self::BITS
110
+ raise Literal::ArgumentError.new("The array must have #{self::BITS} items.")
111
+ end
112
+
113
+ from_int(calculate_from_array(array))
114
+ end
115
+
116
+ # (Array(Symbol)) -> Literal::Flags
117
+ def self.from_tokens(tokens)
118
+ from_int(calculate_from_tokens(tokens))
119
+ end
120
+
121
+ # (Integer) -> Literal::Flags
122
+ def self.from_int(value)
123
+ allocate.__initialize_from_value__(value)
124
+ end
125
+
126
+ # (String) -> Integer
127
+ def self.unpack_int(value)
128
+ value.unpack1(self::PACKER)
129
+ end
130
+
131
+ # (String) -> Literal::Flags
132
+ def self.unpack(value)
133
+ new(unpack_int(value))
134
+ end
135
+
136
+ # () -> String
137
+ def pack
138
+ [@value].pack(self.class::PACKER)
139
+ end
140
+
141
+ # () -> String
142
+ def inspect
143
+ to_h.inspect
144
+ end
145
+
146
+ # () -> Hash(Symbol, Boolean)
147
+ def to_h
148
+ self.class.calculate_hash_from_value(@value)
149
+ end
150
+
151
+ def each
152
+ self.class::FLAGS.each do |key, bit|
153
+ yield key, @value & (2 ** bit) > 0
154
+ end
155
+ end
156
+
157
+ # (Symbol) -> Boolean
158
+ def [](key)
159
+ @value & (2 ** self.class::FLAGS.fetch(key)) > 0
160
+ end
161
+
162
+ def |(other)
163
+ case other
164
+ when Literal::Flags
165
+ self.class.new(@value | other.value)
166
+ when Integer
167
+ self.class.new(@value | other)
168
+ end
169
+ end
170
+
171
+ def &(other)
172
+ case other
173
+ when Literal::Flags
174
+ self.class.new(@value & other.value)
175
+ when Integer
176
+ self.class.new(@value & other)
177
+ end
178
+ end
179
+
180
+ def to_i
181
+ @value
182
+ end
183
+
184
+ def to_bit_string
185
+ self.class.calculate_bit_string(@value)
186
+ end
187
+
188
+ def to_tokens
189
+ self.class.calculate_tokens(@value)
190
+ end
191
+
192
+ def to_a
193
+ self.class.calculate_array(@value)
194
+ end
195
+
196
+ alias_method :deconstruct, :to_a
197
+
198
+ def deconstruct_keys(keys = nil)
199
+ if keys
200
+ flags = self.class::FLAGS
201
+ keys.to_h do |key|
202
+ [key, @value & (2 ** flags.fetch(key)) > 0]
203
+ end
204
+ else
205
+ to_h
206
+ end
207
+ end
208
+ end
209
+
210
+ class Literal::Flags8 < Literal::Flags
211
+ BYTES = 1
212
+ BITS = BYTES * 8
213
+ PACKER = "C"
214
+ end
215
+
216
+ class Literal::Flags16 < Literal::Flags
217
+ BYTES = 2
218
+ BITS = BYTES * 8
219
+ PACKER = "S"
220
+ end
221
+
222
+ class Literal::Flags32 < Literal::Flags
223
+ BYTES = 4
224
+ BITS = BYTES * 8
225
+ PACKER = "L"
226
+ end
227
+
228
+ class Literal::Flags64 < Literal::Flags
229
+ BYTES = 8
230
+ BITS = BYTES * 8
231
+ PACKER = "Q"
232
+ end
@@ -47,8 +47,10 @@ class Literal::Properties::Schema
47
47
  end
48
48
 
49
49
  def generate_initializer(buffer = +"")
50
- buffer << "alias initialize initialize\n"
51
- buffer << "def initialize(#{generate_initializer_params})\n"
50
+ buffer << "alias initialize initialize\n" \
51
+ "def initialize("
52
+ generate_initializer_params(buffer)
53
+ buffer << ")\n"
52
54
  generate_initializer_body(buffer)
53
55
  buffer << "" \
54
56
  "rescue Literal::TypeError => error\n" \
@@ -100,7 +102,7 @@ class Literal::Properties::Schema
100
102
  i, n = 0, sorted_properties.size
101
103
  while i < n
102
104
  property = sorted_properties[i]
103
- buffer << " @" << property.name.name << " == other.#{property.escaped_name}"
105
+ buffer << " @" << property.name.name << " == other." << property.escaped_name
104
106
  buffer << " &&\n " if i < n - 1
105
107
  i += 1
106
108
  end
@@ -25,6 +25,21 @@ class Literal::Types::ConstraintType
25
25
  true
26
26
  end
27
27
 
28
+ def record_literal_type_errors(context)
29
+ @object_constraints.each do |constraint|
30
+ next if constraint === context.actual
31
+
32
+ context.add_child(label: inspect, expected: constraint, actual: context.actual)
33
+ end
34
+
35
+ @property_constraints.each do |property, constraint|
36
+ actual = context.actual.public_send(property)
37
+ next if constraint === actual
38
+
39
+ context.add_child(label: ".#{property}", expected: constraint, actual:)
40
+ end
41
+ end
42
+
28
43
  private
29
44
 
30
45
  def inspect_constraints
@@ -18,4 +18,21 @@ class Literal::Types::HashType
18
18
 
19
19
  true
20
20
  end
21
+
22
+ def record_literal_type_errors(context)
23
+ unless Hash === context.actual
24
+ return
25
+ end
26
+
27
+ context.actual.each do |key, item|
28
+ unless @key_type === key
29
+ context.add_child(label: "[]", expected: @key_type, actual: key)
30
+ next
31
+ end
32
+
33
+ unless @value_type === item
34
+ context.add_child(label: "[#{key.inspect}]", expected: @value_type, actual: item)
35
+ end
36
+ end
37
+ end
21
38
  end
@@ -17,4 +17,12 @@ class Literal::Types::IntersectionType
17
17
  def nil?
18
18
  @types.all?(&:nil?)
19
19
  end
20
+
21
+ def record_literal_type_errors(context)
22
+ @types.each do |type|
23
+ next if type === context.actual
24
+
25
+ context.add_child(label: inspect, expected: type, actual: context.actual)
26
+ end
27
+ end
20
28
  end
@@ -22,8 +22,13 @@ class Literal::Types::MapType
22
22
  end
23
23
 
24
24
  context.actual.each do |key, item|
25
- unless @shape[key] === item
26
- context.add_child(label: "[#{key.inspect}]", expected: @shape[key], actual: item)
25
+ unless (expected = @shape[key])
26
+ context.add_child(label: "[]", expected: @shape.keys, actual: key)
27
+ next
28
+ end
29
+
30
+ unless expected === item
31
+ context.add_child(label: "[#{key.inspect}]", expected:, actual: item)
27
32
  end
28
33
  end
29
34
  end
@@ -23,4 +23,20 @@ class Literal::Types::TupleType
23
23
 
24
24
  true
25
25
  end
26
+
27
+ def record_literal_type_errors(context)
28
+ return unless Array === context.actual
29
+
30
+ len = [@types.size, context.actual.size].max
31
+ i = 0
32
+ while i < len
33
+ actual = context.actual[i]
34
+ if !(expected = @types[i])
35
+ context.add_child(label: "[#{i}]", expected: Literal::Types::NeverType, actual:)
36
+ elsif !(expected === actual)
37
+ context.add_child(label: "[#{i}]", expected:, actual:)
38
+ end
39
+ i += 1
40
+ end
41
+ end
26
42
  end
data/lib/literal/types.rb CHANGED
@@ -25,7 +25,6 @@ module Literal::Types
25
25
  autoload :ProcableType, "literal/types/procable_type"
26
26
  autoload :RangeType, "literal/types/range_type"
27
27
  autoload :SetType, "literal/types/set_type"
28
- autoload :ShapeType, "literal/types/shape_type"
29
28
  autoload :StringType, "literal/types/string_type"
30
29
  autoload :SymbolType, "literal/types/symbol_type"
31
30
  autoload :TruthyType, "literal/types/truthy_type"
@@ -39,7 +38,7 @@ module Literal::Types
39
38
  NilableLambdaType = NilableType.new(LambdaType)
40
39
  NilableProcableType = NilableType.new(ProcableType)
41
40
 
42
- # Matches any value except `nil`. Use `_Any?` or `_Unit` to match any value including `nil`.
41
+ # Matches any value except `nil`. Use `_Any?` or `_Void` to match any value including `nil`.
43
42
  def _Any
44
43
  AnyType
45
44
  end
@@ -293,20 +292,6 @@ module Literal::Types
293
292
  )
294
293
  end
295
294
 
296
- # Ensures a value matches the given shape of a Hash
297
- def _Shape(...)
298
- ShapeType.new(...)
299
- end
300
-
301
- # Nilable version of `_Shape`
302
- def _Shape?(...)
303
- NilableType.new(
304
- ShapeType.new(...),
305
- )
306
- end
307
-
308
- # Nilable version of `_Shape`
309
-
310
295
  # Matches if the value is a `String` and matches the given constraints.
311
296
  # If you don't need any constraints, use `String` instead of `_String`.
312
297
  def _String(...)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Literal
4
- VERSION = "1.0.0.rc1"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/literal.rb CHANGED
@@ -13,6 +13,11 @@ module Literal
13
13
  autoload :Property, "literal/property"
14
14
  autoload :Struct, "literal/struct"
15
15
  autoload :Types, "literal/types"
16
+ autoload :Flags, "literal/flags"
17
+ autoload :Flags8, "literal/flags"
18
+ autoload :Flags16, "literal/flags"
19
+ autoload :Flags32, "literal/flags"
20
+ autoload :Flags64, "literal/flags"
16
21
 
17
22
  # Errors
18
23
  autoload :Error, "literal/errors/error"
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: literal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-26 00:00:00.000000000 Z
11
+ date: 2024-11-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Enums, properties, generics, structured objects.
13
+ description: A literal Ruby gem.
14
14
  email:
15
15
  - joel@drapper.me
16
16
  executables: []
@@ -27,6 +27,7 @@ files:
27
27
  - lib/literal/errors/argument_error.rb
28
28
  - lib/literal/errors/error.rb
29
29
  - lib/literal/errors/type_error.rb
30
+ - lib/literal/flags.rb
30
31
  - lib/literal/null.rb
31
32
  - lib/literal/object.rb
32
33
  - lib/literal/properties.rb
@@ -71,11 +72,11 @@ files:
71
72
  - lib/literal/types/union_type.rb
72
73
  - lib/literal/types/void_type.rb
73
74
  - lib/literal/version.rb
74
- homepage: https://github.com/joeldrapper/literal
75
+ homepage: https://literal.fun
75
76
  licenses:
76
77
  - MIT
77
78
  metadata:
78
- homepage_uri: https://github.com/joeldrapper/literal
79
+ homepage_uri: https://literal.fun
79
80
  source_code_uri: https://github.com/joeldrapper/literal
80
81
  changelog_uri: https://github.com/joeldrapper/literal/blob/main/CHANGELOG.md
81
82
  funding_uri: https://github.com/sponsors/joeldrapper
@@ -98,5 +99,5 @@ requirements: []
98
99
  rubygems_version: 3.5.18
99
100
  signing_key:
100
101
  specification_version: 4
101
- summary: A literal Ruby gem.
102
+ summary: Enums, properties, generics, structured objects and runtime type checking.
102
103
  test_files: []