dhall 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -190,13 +214,56 @@ module Dhall
190
214
  end
191
215
 
192
216
  class Import
217
+ class IntegrityCheck
218
+ def self.decode(integrity_check)
219
+ return unless integrity_check
220
+
221
+ IntegrityCheck.new(
222
+ Multihashes.decode(integrity_check).select { |k, _|
223
+ [:code, :digest].include?(k)
224
+ }
225
+ )
226
+ end
227
+ end
228
+
229
+ class URI
230
+ def self.decode(headers, authority, *path, query)
231
+ uri = ::URI.scheme_list[name.split(/::/).last.upcase].build(
232
+ Parser.parse(authority, root: :authority).value.merge(
233
+ path: Util.path_components_to_uri(*path).path
234
+ )
235
+ )
236
+ uri.instance_variable_set(:@query, query)
237
+ new(headers: headers, uri: uri)
238
+ end
239
+ end
240
+
241
+ class Path
242
+ def self.decode(*args)
243
+ new(*args)
244
+ end
245
+ end
246
+
247
+ class EnvironmentVariable
248
+ def self.decode(*args)
249
+ new(*args)
250
+ end
251
+ end
252
+
253
+ class MissingImport
254
+ def self.decode(*args)
255
+ new(*args)
256
+ end
257
+ end
258
+
193
259
  def self.decode(integrity_check, import_type, path_type, *parts)
194
260
  parts[0] = Dhall.decode(parts[0]) if path_type < 2 && !parts[0].nil?
261
+ path_type = PATH_TYPES.fetch(path_type)
195
262
 
196
263
  new(
197
- IntegrityCheck.new(*integrity_check),
264
+ IntegrityCheck.decode(integrity_check),
198
265
  IMPORT_TYPES[import_type],
199
- PATH_TYPES[path_type].new(*parts)
266
+ path_type.decode(*parts)
200
267
  )
201
268
  end
202
269
  end
@@ -275,6 +342,8 @@ module Dhall
275
342
  nil,
276
343
  Import,
277
344
  LetBlock,
278
- TypeAnnotation
345
+ TypeAnnotation,
346
+ ToMap,
347
+ EmptyList
279
348
  ].freeze
280
349
  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,71 @@ 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
124
- end
125
- end
126
- end
101
+ class Natural_even < BuiltinFunction
102
+ protected
127
103
 
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)
104
+ def uncurried_call(nat)
105
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
134
106
 
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
107
+ Dhall::Bool.new(value: nat.even?)
143
108
  end
144
109
  end
145
110
 
146
- class Natural_isZero < Builtin
147
- def call(nat)
148
- if nat.is_a?(Dhall::Natural)
149
- Dhall::Bool.new(value: nat.zero?)
111
+ class Natural_fold < BuiltinFunction
112
+ protected
113
+
114
+ def uncurried_call(nat, type, f, z)
115
+ return unfill(nat, type, f, z) unless nat.is_a?(Dhall::Natural)
116
+
117
+ if nat.zero?
118
+ z.normalize
150
119
  else
151
- super
120
+ f.call(Natural_fold.new.call(nat.pred, type, f, z))
152
121
  end
153
122
  end
154
123
  end
155
124
 
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
125
+ class Natural_isZero < BuiltinFunction
126
+ protected
127
+
128
+ def uncurried_call(nat)
129
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
130
+
131
+ Dhall::Bool.new(value: nat.zero?)
163
132
  end
164
133
  end
165
134
 
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
135
+ class Natural_odd < BuiltinFunction
136
+ protected
137
+
138
+ def uncurried_call(nat)
139
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
140
+
141
+ Dhall::Bool.new(value: nat.odd?)
173
142
  end
174
143
  end
175
144
 
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
145
+ class Natural_show < BuiltinFunction
146
+ protected
147
+
148
+ def uncurried_call(nat)
149
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
150
+
151
+ Dhall::Text.new(value: nat.to_s)
183
152
  end
184
153
  end
185
154
 
186
- class List_build < Builtin
187
- include(ValueSemantics.for_attributes do
188
- type Either(nil, Expression), default: nil
189
- end)
155
+ class Natural_toInteger < BuiltinFunction
156
+ protected
157
+
158
+ def uncurried_call(nat)
159
+ return unfill(nat) unless nat.is_a?(Dhall::Natural)
160
+
161
+ Dhall::Integer.new(value: nat.value)
162
+ end
163
+ end
190
164
 
165
+ class List_build < BuiltinFunction
191
166
  def fusion(*args)
192
167
  _, arg, = args
193
168
  if arg.is_a?(Application) &&
@@ -199,19 +174,17 @@ module Dhall
199
174
  end
200
175
  end
201
176
 
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
177
  protected
213
178
 
214
- def cons
179
+ def uncurried_call(type, arg)
180
+ arg.call(
181
+ List.new.call(type),
182
+ cons(type),
183
+ EmptyList.new(element_type: type)
184
+ )
185
+ end
186
+
187
+ def cons(type)
215
188
  Function.of_arguments(
216
189
  type,
217
190
  List.new.call(type.shift(1, "_", 0)),
@@ -220,56 +193,33 @@ module Dhall
220
193
  end
221
194
  end
222
195
 
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
196
+ class List_fold < BuiltinFunction
197
+ protected
237
198
 
238
- class List_head < Builtin
239
- include(ValueSemantics.for_attributes do
240
- type Either(nil, Expression), default: nil
241
- end)
199
+ def uncurried_call(ltype, list, ztype, f, z)
200
+ return unfill(ltype, list, ztype, f, z) unless list.is_a?(Dhall::List)
242
201
 
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
202
+ list.reduce(z, &f).normalize
251
203
  end
252
204
  end
253
205
 
254
- class List_indexed < Builtin
255
- include(ValueSemantics.for_attributes do
256
- type Either(nil, Expression), default: nil
257
- end)
206
+ class List_head < BuiltinFunction
207
+ protected
258
208
 
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
209
+ def uncurried_call(type, list)
210
+ return unfill(type, list) unless list.is_a?(Dhall::List)
211
+
212
+ list.first
267
213
  end
214
+ end
268
215
 
216
+ class List_indexed < BuiltinFunction
269
217
  protected
270
218
 
271
- def _call(arg)
272
- arg.map(type: indexed_type(type)) { |x, idx|
219
+ def uncurried_call(type, list)
220
+ return unfill(type, list) unless list.is_a?(Dhall::List)
221
+
222
+ list.map(type: indexed_type(type)) { |x, idx|
273
223
  Record.new(
274
224
  record: {
275
225
  "index" => Dhall::Natural.new(value: idx),
@@ -289,59 +239,37 @@ module Dhall
289
239
  end
290
240
  end
291
241
 
292
- class List_last < Builtin
293
- include(ValueSemantics.for_attributes do
294
- type Either(nil, Expression), default: nil
295
- end)
242
+ class List_last < BuiltinFunction
243
+ protected
296
244
 
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
245
+ def uncurried_call(type, list)
246
+ return unfill(type, list) unless list.is_a?(Dhall::List)
247
+
248
+ list.last
305
249
  end
306
250
  end
307
251
 
308
- class List_length < Builtin
309
- include(ValueSemantics.for_attributes do
310
- type Either(nil, Expression), default: nil
311
- end)
252
+ class List_length < BuiltinFunction
253
+ protected
312
254
 
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
255
+ def uncurried_call(type, list)
256
+ return unfill(type, list) unless list.is_a?(Dhall::List)
257
+
258
+ Dhall::Natural.new(value: list.length)
321
259
  end
322
260
  end
323
261
 
324
- class List_reverse < Builtin
325
- include(ValueSemantics.for_attributes do
326
- type Either(nil, Expression), default: nil
327
- end)
262
+ class List_reverse < BuiltinFunction
263
+ protected
328
264
 
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
265
+ def uncurried_call(type, list)
266
+ return unfill(type, list) unless list.is_a?(Dhall::List)
267
+
268
+ list.reverse
337
269
  end
338
270
  end
339
271
 
340
- class Optional_build < Builtin
341
- include(ValueSemantics.for_attributes do
342
- type Either(nil, Expression), default: nil
343
- end)
344
-
272
+ class Optional_build < BuiltinFunction
345
273
  def fusion(*args)
346
274
  _, arg, = args
347
275
  if arg.is_a?(Application) &&
@@ -353,19 +281,17 @@ module Dhall
353
281
  end
354
282
  end
355
283
 
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
284
  protected
367
285
 
368
- def some
286
+ def uncurried_call(type, f)
287
+ f.call(
288
+ Optional.new.call(type),
289
+ some(type),
290
+ OptionalNone.new(value_type: type)
291
+ )
292
+ end
293
+
294
+ def some(type)
369
295
  Function.of_arguments(
370
296
  type,
371
297
  body: Dhall::Optional.new(
@@ -376,24 +302,19 @@ module Dhall
376
302
  end
377
303
  end
378
304
 
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)
305
+ class Optional_fold < BuiltinFunction
306
+ protected
386
307
 
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
308
+ def uncurried_call(type, optional, ztype, f, z)
309
+ unless optional.is_a?(Dhall::Optional)
310
+ return unfill(type, optional, ztype, f, z)
392
311
  end
312
+
313
+ optional.reduce(z, &f)
393
314
  end
394
315
  end
395
316
 
396
- class Text_show < Builtin
317
+ class Text_show < BuiltinFunction
397
318
  ENCODE = (Hash.new { |_, x| "\\u%04x" % x.ord }).merge(
398
319
  "\"" => "\\\"",
399
320
  "\\" => "\\\\",
@@ -404,17 +325,17 @@ module Dhall
404
325
  "\t" => "\\t"
405
326
  )
406
327
 
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
328
+ protected
329
+
330
+ def uncurried_call(text)
331
+ return unfill(text) unless text.is_a?(Dhall::Text)
332
+
333
+ Dhall::Text.new(
334
+ value: "\"#{text.to_s.gsub(
335
+ /["\$\\\b\f\n\r\t\u0000-\u001F]/,
336
+ &ENCODE
337
+ )}\""
338
+ )
418
339
  end
419
340
  end
420
341