dhall 0.3.0 → 0.5.2

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.
data/lib/dhall/binary.rb CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  require "cbor"
4
4
  require "digest/sha2"
5
+ require "multihashes"
5
6
 
6
7
  require "dhall/ast"
7
8
  require "dhall/builtins"
9
+ require "dhall/parser"
8
10
 
9
11
  module Dhall
10
12
  def self.from_binary(cbor_binary)
@@ -34,10 +36,13 @@ module Dhall
34
36
  CBOR.encode(as_json)
35
37
  end
36
38
  end
37
- alias to_binary to_cbor
39
+
40
+ def to_binary
41
+ CBOR.encode(::CBOR::Tagged.new(55799, self))
42
+ end
38
43
 
39
44
  def digest(digest: Digest::SHA2.new(256))
40
- (digest << normalize.to_binary).freeze
45
+ (digest << normalize.to_cbor).freeze
41
46
  end
42
47
 
43
48
  def cache_key
@@ -80,15 +85,21 @@ module Dhall
80
85
 
81
86
  class List
82
87
  def self.decode(type, *els)
83
- type = type.nil? ? nil : Dhall.decode(type)
88
+ type = type.nil? ? nil : Builtins[:List].call(Dhall.decode(type))
84
89
  if els.empty?
85
- EmptyList.new(element_type: type)
90
+ EmptyList.new(type: type)
86
91
  else
87
- List.new(elements: els.map(&Dhall.method(:decode)), element_type: type)
92
+ List.new(elements: els.map(&Dhall.method(:decode)), type: type)
88
93
  end
89
94
  end
90
95
  end
91
96
 
97
+ class EmptyList
98
+ def self.decode(type)
99
+ EmptyList.new(type: Dhall.decode(type))
100
+ end
101
+ end
102
+
92
103
  class Optional
93
104
  def self.decode(type, value=nil)
94
105
  if value.nil?
@@ -102,6 +113,15 @@ module Dhall
102
113
  end
103
114
  end
104
115
 
116
+ class ToMap
117
+ def self.decode(record, type=nil)
118
+ new(
119
+ record: Dhall.decode(record),
120
+ type: type.nil? ? nil : Dhall.decode(type)
121
+ )
122
+ end
123
+ end
124
+
105
125
  class Merge
106
126
  def self.decode(record, input, type=nil)
107
127
  new(
@@ -140,10 +160,14 @@ module Dhall
140
160
 
141
161
  class RecordProjection
142
162
  def self.decode(record, *selectors)
143
- if selectors.empty?
144
- EmptyRecordProjection.new(record: Dhall.decode(record))
163
+ record = Dhall.decode(record)
164
+ if selectors.length == 1 && selectors[0].is_a?(Array)
165
+ RecordProjectionByExpression.new(
166
+ record: record,
167
+ selector: Dhall.decode(selectors[0][0])
168
+ )
145
169
  else
146
- new(record: Dhall.decode(record), selectors: selectors)
170
+ self.for(record, selectors)
147
171
  end
148
172
  end
149
173
  end
@@ -189,30 +213,79 @@ module Dhall
189
213
  end
190
214
  end
191
215
 
216
+ class Assertion
217
+ def self.decode(type)
218
+ new(type: Dhall.decode(type))
219
+ end
220
+ end
221
+
192
222
  class Import
223
+ class IntegrityCheck
224
+ def self.decode(integrity_check)
225
+ return unless integrity_check
226
+
227
+ IntegrityCheck.new(
228
+ Multihashes.decode(integrity_check).select { |k, _|
229
+ [:code, :digest].include?(k)
230
+ }
231
+ )
232
+ end
233
+ end
234
+
235
+ class URI
236
+ def self.decode(headers, authority, *path, query)
237
+ uri = ::URI.scheme_list[name.split(/::/).last.upcase].build(
238
+ Parser.parse(authority, root: :authority).value.merge(
239
+ path: "/#{path.join("/")}"
240
+ )
241
+ )
242
+ uri.instance_variable_set(:@query, query)
243
+ new(headers: headers, uri: uri)
244
+ end
245
+ end
246
+
247
+ class Path
248
+ def self.decode(*args)
249
+ new(*args)
250
+ end
251
+ end
252
+
253
+ class EnvironmentVariable
254
+ def self.decode(*args)
255
+ new(*args)
256
+ end
257
+ end
258
+
259
+ class MissingImport
260
+ def self.decode(*args)
261
+ new(*args)
262
+ end
263
+ end
264
+
193
265
  def self.decode(integrity_check, import_type, path_type, *parts)
194
266
  parts[0] = Dhall.decode(parts[0]) if path_type < 2 && !parts[0].nil?
267
+ path_type = PATH_TYPES.fetch(path_type)
195
268
 
196
269
  new(
197
- IntegrityCheck.new(*integrity_check),
270
+ IntegrityCheck.decode(integrity_check),
198
271
  IMPORT_TYPES[import_type],
199
- PATH_TYPES[path_type].new(*parts)
272
+ path_type.decode(*parts)
200
273
  )
201
274
  end
202
275
  end
203
276
 
204
- class LetBlock
277
+ class LetIn
205
278
  def self.decode(*parts)
206
279
  body = Dhall.decode(parts.pop)
207
- lets = parts.each_slice(3).map do |(var, type, assign)|
280
+ parts.each_slice(3).map { |(var, type, assign)|
208
281
  Let.new(
209
282
  var: var,
210
283
  assign: Dhall.decode(assign),
211
284
  type: type.nil? ? nil : Dhall.decode(type)
212
285
  )
286
+ }.reverse.reduce(body) do |inside, let|
287
+ LetIn.new(let: let, body: inside)
213
288
  end
214
-
215
- self.for(lets: lets, body: body)
216
289
  end
217
290
  end
218
291
 
@@ -268,13 +341,15 @@ module Dhall
268
341
  Integer,
269
342
  nil,
270
343
  TextLiteral,
271
- nil,
344
+ Assertion,
272
345
  nil,
273
346
  nil,
274
347
  nil,
275
348
  nil,
276
349
  Import,
277
- LetBlock,
278
- TypeAnnotation
350
+ LetIn,
351
+ TypeAnnotation,
352
+ ToMap,
353
+ EmptyList
279
354
  ].freeze
280
355
  end
@@ -6,93 +6,74 @@ module Dhall
6
6
  class Builtin < Expression
7
7
  include(ValueSemantics.for_attributes {})
8
8
 
9
- def call(*args)
10
- # Do not auto-normalize builtins to avoid recursion loop
11
- args.reduce(self) do |f, arg|
9
+ def as_json
10
+ self.class.name&.split(/::/)&.last&.tr("_", "/").to_s
11
+ end
12
+ end
13
+
14
+ class BuiltinFunction < Builtin
15
+ include(ValueSemantics.for_attributes do
16
+ partial_application ArrayOf(Expression), default: []
17
+ end)
18
+
19
+ def unfill(*args)
20
+ (args.empty? ? partial_application : args).reduce(self.class.new) do |f, arg|
12
21
  Application.new(function: f, argument: arg)
13
22
  end
14
23
  end
15
24
 
16
- def unfill
17
- attributes.reduce(self.class.new) do |f, attr|
18
- if send(attr.name).nil?
19
- f
20
- else
21
- Application.new(function: f, argument: send(attr.name))
22
- end
25
+ def call(*new_args)
26
+ args = partial_application + new_args
27
+ if args.length == method(:uncurried_call).arity
28
+ uncurried_call(*args)
29
+ else
30
+ with(partial_application: args)
23
31
  end
24
32
  end
25
33
 
26
34
  def as_json
27
- if (unfilled = unfill).class != self.class
35
+ if (unfilled = unfill) != self
28
36
  unfilled.as_json
29
37
  else
30
- self.class.name&.split(/::/)&.last&.tr("_", "/").to_s
38
+ super
31
39
  end
32
40
  end
41
+ end
33
42
 
34
- protected
43
+ module Builtins
44
+ # rubocop:disable Style/ClassAndModuleCamelCase
35
45
 
36
- def attributes
37
- self.class.value_semantics.attributes
38
- end
46
+ class Double_show < BuiltinFunction
47
+ protected
39
48
 
40
- def fill_next_if_valid(value)
41
- with(attributes.each_with_object({}) do |attr, h|
42
- if !send(attr.name).nil?
43
- h[attr.name] = send(attr.name)
44
- elsif attr.validate?(value)
45
- h[attr.name] = value
46
- value = nil
47
- else
48
- return nil
49
- end
50
- end)
51
- end
49
+ def uncurried_call(arg)
50
+ return unfill(arg) unless arg.is_a?(Dhall::Double)
52
51
 
53
- def full?
54
- attributes.all? { |attr| !send(attr.name).nil? }
52
+ Dhall::Text.new(value: arg.to_s)
53
+ end
55
54
  end
56
55
 
57
- def fill_or_call(arg, &block)
58
- full? ? block[arg] : fill_next_if_valid(arg)
59
- end
60
- end
56
+ class Integer_show < BuiltinFunction
57
+ protected
61
58
 
62
- module Builtins
63
- # rubocop:disable Style/ClassAndModuleCamelCase
59
+ def uncurried_call(arg)
60
+ return unfill(arg) unless arg.is_a?(Dhall::Integer)
64
61
 
65
- class Double_show < Builtin
66
- def call(arg)
67
- if arg.is_a?(Dhall::Double)
68
- Dhall::Text.new(value: arg.to_s)
69
- else
70
- super
71
- end
62
+ Dhall::Text.new(value: arg.to_s)
72
63
  end
73
64
  end
74
65
 
75
- class Integer_show < Builtin
76
- def call(arg)
77
- if arg.is_a?(Dhall::Integer)
78
- Dhall::Text.new(value: arg.to_s)
79
- else
80
- super
81
- end
82
- end
83
- end
66
+ class Integer_toDouble < BuiltinFunction
67
+ protected
84
68
 
85
- class Integer_toDouble < Builtin
86
- def call(arg)
87
- if arg.is_a?(Dhall::Integer)
88
- Dhall::Double.new(value: arg.value.to_f)
89
- else
90
- super
91
- end
69
+ def uncurried_call(arg)
70
+ return unfill(arg) unless arg.is_a?(Dhall::Integer)
71
+
72
+ Dhall::Double.new(value: arg.value.to_f)
92
73
  end
93
74
  end
94
75
 
95
- class Natural_build < Builtin
76
+ class Natural_build < BuiltinFunction
96
77
  def fusion(arg, *bogus)
97
78
  if bogus.empty? &&
98
79
  arg.is_a?(Application) &&
@@ -103,7 +84,9 @@ module Dhall
103
84
  end
104
85
  end
105
86
 
106
- def call(arg)
87
+ protected
88
+
89
+ def uncurried_call(arg)
107
90
  arg.call(
108
91
  Natural.new,
109
92
  Function.of_arguments(
@@ -115,79 +98,91 @@ module Dhall
115
98
  end
116
99
  end
117
100
 
118
- class Natural_even < Builtin
119
- def call(nat)
120
- if nat.is_a?(Dhall::Natural)
121
- Dhall::Bool.new(value: nat.even?)
122
- else
123
- super
101
+ class Natural_subtract < BuiltinFunction
102
+ protected
103
+
104
+ def uncurried_call(x, y)
105
+ return y if zero?(x) || zero?(y)
106
+
107
+ return Dhall::Natural.new(value: 0) if x == y
108
+
109
+ unless x.is_a?(Dhall::Natural) && y.is_a?(Dhall::Natural)
110
+ return unfill(x, y)
124
111
  end
112
+
113
+ Dhall::Natural.new(value: [y.to_i - x.to_i, 0].max)
114
+ end
115
+
116
+ def zero?(x)
117
+ Natural_isZero.new.call(x) === true
125
118
  end
126
119
  end
127
120
 
128
- class Natural_fold < Builtin
129
- include(ValueSemantics.for_attributes do
130
- nat Either(nil, Dhall::Natural), default: nil
131
- type Either(nil, Expression), default: nil
132
- f Either(nil, Expression), default: nil
133
- end)
121
+ class Natural_even < BuiltinFunction
122
+ protected
134
123
 
135
- def call(arg)
136
- fill_or_call(arg) do
137
- if @nat.zero?
138
- arg.normalize
139
- else
140
- @f.call(with(nat: nat.pred).call(arg))
141
- end
142
- end || super
124
+ def uncurried_call(nat)
125
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
126
+
127
+ Dhall::Bool.new(value: nat.even?)
143
128
  end
144
129
  end
145
130
 
146
- class Natural_isZero < Builtin
147
- def call(nat)
148
- if nat.is_a?(Dhall::Natural)
149
- Dhall::Bool.new(value: nat.zero?)
131
+ class Natural_fold < BuiltinFunction
132
+ protected
133
+
134
+ def uncurried_call(nat, type, f, z)
135
+ return unfill(nat, type, f, z) unless nat.is_a?(Dhall::Natural)
136
+
137
+ if nat.zero?
138
+ z.normalize
150
139
  else
151
- super
140
+ f.call(Natural_fold.new.call(nat.pred, type, f, z))
152
141
  end
153
142
  end
154
143
  end
155
144
 
156
- class Natural_odd < Builtin
157
- def call(nat)
158
- if nat.is_a?(Dhall::Natural)
159
- Dhall::Bool.new(value: nat.odd?)
160
- else
161
- super
162
- end
145
+ class Natural_isZero < BuiltinFunction
146
+ protected
147
+
148
+ def uncurried_call(nat)
149
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
150
+
151
+ Dhall::Bool.new(value: nat.zero?)
163
152
  end
164
153
  end
165
154
 
166
- class Natural_show < Builtin
167
- def call(nat)
168
- if nat.is_a?(Dhall::Natural)
169
- Dhall::Text.new(value: nat.to_s)
170
- else
171
- super
172
- end
155
+ class Natural_odd < BuiltinFunction
156
+ protected
157
+
158
+ def uncurried_call(nat)
159
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
160
+
161
+ Dhall::Bool.new(value: nat.odd?)
173
162
  end
174
163
  end
175
164
 
176
- class Natural_toInteger < Builtin
177
- def call(nat)
178
- if nat.is_a?(Dhall::Natural)
179
- Dhall::Integer.new(value: nat.value)
180
- else
181
- super
182
- end
165
+ class Natural_show < BuiltinFunction
166
+ protected
167
+
168
+ def uncurried_call(nat)
169
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
170
+
171
+ Dhall::Text.new(value: nat.to_s)
183
172
  end
184
173
  end
185
174
 
186
- class List_build < Builtin
187
- include(ValueSemantics.for_attributes do
188
- type Either(nil, Expression), default: nil
189
- end)
175
+ class Natural_toInteger < BuiltinFunction
176
+ protected
190
177
 
178
+ def uncurried_call(nat)
179
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
180
+
181
+ Dhall::Integer.new(value: nat.value)
182
+ end
183
+ end
184
+
185
+ class List_build < BuiltinFunction
191
186
  def fusion(*args)
192
187
  _, arg, = args
193
188
  if arg.is_a?(Application) &&
@@ -199,19 +194,17 @@ module Dhall
199
194
  end
200
195
  end
201
196
 
202
- def call(arg)
203
- fill_or_call(arg) do
204
- arg.call(
205
- List.new.call(type),
206
- cons,
207
- EmptyList.new(element_type: type)
208
- )
209
- end
210
- end
211
-
212
197
  protected
213
198
 
214
- def cons
199
+ def uncurried_call(type, arg)
200
+ arg.call(
201
+ List.new.call(type),
202
+ cons(type),
203
+ EmptyList.new(element_type: type)
204
+ )
205
+ end
206
+
207
+ def cons(type)
215
208
  Function.of_arguments(
216
209
  type,
217
210
  List.new.call(type.shift(1, "_", 0)),
@@ -220,56 +213,33 @@ module Dhall
220
213
  end
221
214
  end
222
215
 
223
- class List_fold < Builtin
224
- include(ValueSemantics.for_attributes do
225
- ltype Either(nil, Expression), default: nil
226
- list Either(nil, List), default: nil
227
- ztype Either(nil, Expression), default: nil
228
- f Either(nil, Expression), default: nil
229
- end)
230
-
231
- def call(arg)
232
- fill_or_call(arg) do
233
- list.reduce(arg, &f).normalize
234
- end || super
235
- end
236
- end
216
+ class List_fold < BuiltinFunction
217
+ protected
237
218
 
238
- class List_head < Builtin
239
- include(ValueSemantics.for_attributes do
240
- type Either(nil, Expression), default: nil
241
- end)
219
+ def uncurried_call(ltype, list, ztype, f, z)
220
+ return unfill(ltype, list, ztype, f, z) unless list.is_a?(Dhall::List)
242
221
 
243
- def call(arg)
244
- fill_or_call(arg) do
245
- if arg.is_a?(Dhall::List)
246
- arg.first
247
- else
248
- super
249
- end
250
- end
222
+ list.reduce(z, &f).normalize
251
223
  end
252
224
  end
253
225
 
254
- class List_indexed < Builtin
255
- include(ValueSemantics.for_attributes do
256
- type Either(nil, Expression), default: nil
257
- end)
226
+ class List_head < BuiltinFunction
227
+ protected
228
+
229
+ def uncurried_call(type, list)
230
+ return unfill(type, list) unless list.is_a?(Dhall::List)
258
231
 
259
- def call(arg)
260
- fill_or_call(arg) do
261
- if arg.is_a?(Dhall::List)
262
- _call(arg)
263
- else
264
- super
265
- end
266
- end
232
+ list.first
267
233
  end
234
+ end
268
235
 
236
+ class List_indexed < BuiltinFunction
269
237
  protected
270
238
 
271
- def _call(arg)
272
- arg.map(type: indexed_type(type)) { |x, idx|
239
+ def uncurried_call(type, list)
240
+ return unfill(type, list) unless list.is_a?(Dhall::List)
241
+
242
+ list.map(type: indexed_type(type)) { |x, idx|
273
243
  Record.new(
274
244
  record: {
275
245
  "index" => Dhall::Natural.new(value: idx),
@@ -289,59 +259,37 @@ module Dhall
289
259
  end
290
260
  end
291
261
 
292
- class List_last < Builtin
293
- include(ValueSemantics.for_attributes do
294
- type Either(nil, Expression), default: nil
295
- end)
262
+ class List_last < BuiltinFunction
263
+ protected
296
264
 
297
- def call(arg)
298
- fill_or_call(arg) do
299
- if arg.is_a?(Dhall::List)
300
- arg.last
301
- else
302
- super
303
- end
304
- end
265
+ def uncurried_call(type, list)
266
+ return unfill(type, list) unless list.is_a?(Dhall::List)
267
+
268
+ list.last
305
269
  end
306
270
  end
307
271
 
308
- class List_length < Builtin
309
- include(ValueSemantics.for_attributes do
310
- type Either(nil, Expression), default: nil
311
- end)
272
+ class List_length < BuiltinFunction
273
+ protected
312
274
 
313
- def call(arg)
314
- fill_or_call(arg) do
315
- if arg.is_a?(Dhall::List)
316
- Dhall::Natural.new(value: arg.length)
317
- else
318
- super
319
- end
320
- end
275
+ def uncurried_call(type, list)
276
+ return unfill(type, list) unless list.is_a?(Dhall::List)
277
+
278
+ Dhall::Natural.new(value: list.length)
321
279
  end
322
280
  end
323
281
 
324
- class List_reverse < Builtin
325
- include(ValueSemantics.for_attributes do
326
- type Either(nil, Expression), default: nil
327
- end)
282
+ class List_reverse < BuiltinFunction
283
+ protected
328
284
 
329
- def call(arg)
330
- fill_or_call(arg) do
331
- if arg.is_a?(Dhall::List)
332
- arg.reverse
333
- else
334
- super
335
- end
336
- end
285
+ def uncurried_call(type, list)
286
+ return unfill(type, list) unless list.is_a?(Dhall::List)
287
+
288
+ list.reverse
337
289
  end
338
290
  end
339
291
 
340
- class Optional_build < Builtin
341
- include(ValueSemantics.for_attributes do
342
- type Either(nil, Expression), default: nil
343
- end)
344
-
292
+ class Optional_build < BuiltinFunction
345
293
  def fusion(*args)
346
294
  _, arg, = args
347
295
  if arg.is_a?(Application) &&
@@ -353,19 +301,17 @@ module Dhall
353
301
  end
354
302
  end
355
303
 
356
- def call(arg)
357
- fill_or_call(arg) do
358
- arg.call(
359
- Optional.new.call(type),
360
- some,
361
- OptionalNone.new(value_type: type)
362
- )
363
- end
364
- end
365
-
366
304
  protected
367
305
 
368
- def some
306
+ def uncurried_call(type, f)
307
+ f.call(
308
+ Optional.new.call(type),
309
+ some(type),
310
+ OptionalNone.new(value_type: type)
311
+ )
312
+ end
313
+
314
+ def some(type)
369
315
  Function.of_arguments(
370
316
  type,
371
317
  body: Dhall::Optional.new(
@@ -376,24 +322,19 @@ module Dhall
376
322
  end
377
323
  end
378
324
 
379
- class Optional_fold < Builtin
380
- include(ValueSemantics.for_attributes do
381
- type Either(nil, Expression), default: nil
382
- optional Either(nil, Optional), default: nil
383
- ztype Either(nil, Expression), default: nil
384
- f Either(nil, Expression), default: nil
385
- end)
325
+ class Optional_fold < BuiltinFunction
326
+ protected
386
327
 
387
- def call(*args)
388
- args.reduce(self) do |fold, arg|
389
- fold.fill_or_call(arg) do
390
- fold.optional.reduce(arg, &fold.f)
391
- end || super
328
+ def uncurried_call(type, optional, ztype, f, z)
329
+ unless optional.is_a?(Dhall::Optional)
330
+ return unfill(type, optional, ztype, f, z)
392
331
  end
332
+
333
+ optional.reduce(z, &f)
393
334
  end
394
335
  end
395
336
 
396
- class Text_show < Builtin
337
+ class Text_show < BuiltinFunction
397
338
  ENCODE = (Hash.new { |_, x| "\\u%04x" % x.ord }).merge(
398
339
  "\"" => "\\\"",
399
340
  "\\" => "\\\\",
@@ -404,17 +345,17 @@ module Dhall
404
345
  "\t" => "\\t"
405
346
  )
406
347
 
407
- def call(arg)
408
- if arg.is_a?(Dhall::Text)
409
- Dhall::Text.new(
410
- value: "\"#{arg.value.gsub(
411
- /["\$\\\b\f\n\r\t\u0000-\u001F]/,
412
- &ENCODE
413
- )}\""
414
- )
415
- else
416
- super
417
- end
348
+ protected
349
+
350
+ def uncurried_call(text)
351
+ return unfill(text) unless text.is_a?(Dhall::Text)
352
+
353
+ Dhall::Text.new(
354
+ value: "\"#{text.to_s.gsub(
355
+ /["\$\\\b\f\n\r\t\u0000-\u001F]/,
356
+ &ENCODE
357
+ )}\""
358
+ )
418
359
  end
419
360
  end
420
361