dhall 0.1.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.
- checksums.yaml +7 -0
- data/COPYING +676 -0
- data/README.md +283 -0
- data/bin/json-to-dhall +8 -0
- data/bin/yaml-to-dhall +8 -0
- data/dhall.gemspec +36 -0
- data/lib/dhall.rb +37 -0
- data/lib/dhall/as_dhall.rb +195 -0
- data/lib/dhall/ast.rb +1559 -0
- data/lib/dhall/binary.rb +277 -0
- data/lib/dhall/builtins.rb +425 -0
- data/lib/dhall/normalize.rb +420 -0
- data/lib/dhall/parser.citrus +500 -0
- data/lib/dhall/parser.rb +601 -0
- data/lib/dhall/resolve.rb +383 -0
- data/lib/dhall/typecheck.rb +1209 -0
- data/lib/dhall/util.rb +130 -0
- data/lib/dhall/visitor.rb +23 -0
- metadata +163 -0
data/lib/dhall/ast.rb
ADDED
@@ -0,0 +1,1559 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
require "value_semantics"
|
5
|
+
|
6
|
+
require "dhall/as_dhall"
|
7
|
+
require "dhall/util"
|
8
|
+
|
9
|
+
module Dhall
|
10
|
+
using AsDhall
|
11
|
+
|
12
|
+
class Expression
|
13
|
+
def call(*args)
|
14
|
+
args.reduce(self) { |f, arg|
|
15
|
+
Application.new(function: f, argument: arg)
|
16
|
+
}.normalize
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_proc
|
20
|
+
method(:call).to_proc
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch(k)
|
24
|
+
RecordSelection.new(record: self, selector: k)
|
25
|
+
end
|
26
|
+
|
27
|
+
def slice(*keys)
|
28
|
+
RecordProjection.new(record: self, selectors: keys)
|
29
|
+
end
|
30
|
+
|
31
|
+
def +(other)
|
32
|
+
Operator::Plus.new(lhs: self, rhs: other)
|
33
|
+
end
|
34
|
+
|
35
|
+
def *(other)
|
36
|
+
case other
|
37
|
+
when Natural
|
38
|
+
other * self
|
39
|
+
else
|
40
|
+
Operator::Times.new(lhs: self, rhs: other)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def <<(other)
|
45
|
+
Operator::TextConcatenate.new(lhs: self, rhs: other)
|
46
|
+
end
|
47
|
+
|
48
|
+
def concat(other)
|
49
|
+
Operator::ListConcatenate.new(lhs: self, rhs: other)
|
50
|
+
end
|
51
|
+
|
52
|
+
def &(other)
|
53
|
+
if self == other
|
54
|
+
self
|
55
|
+
elsif other.is_a?(Bool)
|
56
|
+
other & self
|
57
|
+
else
|
58
|
+
Operator::And.new(lhs: self, rhs: other)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def |(other)
|
63
|
+
if self == other
|
64
|
+
self
|
65
|
+
elsif other.is_a?(Bool)
|
66
|
+
other | self
|
67
|
+
else
|
68
|
+
Operator::Or.new(lhs: self, rhs: other)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def dhall_eq(other)
|
73
|
+
if self == other
|
74
|
+
Bool.new(value: true)
|
75
|
+
elsif other.is_a?(Bool)
|
76
|
+
other.dhall_eq(self)
|
77
|
+
else
|
78
|
+
Operator::Equal.new(lhs: self, rhs: other)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def deep_merge(other)
|
83
|
+
case other
|
84
|
+
when EmptyRecord
|
85
|
+
other.deep_merge(self)
|
86
|
+
else
|
87
|
+
Operator::RecursiveRecordMerge.new(lhs: self, rhs: other)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def merge(other)
|
92
|
+
case other
|
93
|
+
when EmptyRecord
|
94
|
+
other.merge(self)
|
95
|
+
else
|
96
|
+
Operator::RightBiasedRecordMerge.new(lhs: self, rhs: other)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def deep_merge_type(other)
|
101
|
+
case other
|
102
|
+
when EmptyRecordType
|
103
|
+
other.deep_merge_type(self)
|
104
|
+
else
|
105
|
+
Operator::RecursiveRecordTypeMerge.new(lhs: self, rhs: other)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def as_dhall
|
110
|
+
self
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Application < Expression
|
115
|
+
include(ValueSemantics.for_attributes do
|
116
|
+
function Expression
|
117
|
+
argument Expression
|
118
|
+
end)
|
119
|
+
|
120
|
+
def self.for(function:, argument:)
|
121
|
+
if function == Variable["Some"]
|
122
|
+
Optional.new(value: argument)
|
123
|
+
elsif function == Variable["None"]
|
124
|
+
OptionalNone.new(value_type: argument)
|
125
|
+
else
|
126
|
+
new(function: function, argument: argument)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def flatten
|
131
|
+
f, args = if function.is_a?(Application)
|
132
|
+
function.flatten
|
133
|
+
elsif function.is_a?(Builtin) &&
|
134
|
+
(unfilled = function.unfill).is_a?(Application)
|
135
|
+
unfilled.flatten
|
136
|
+
else
|
137
|
+
[function, []]
|
138
|
+
end
|
139
|
+
|
140
|
+
[f, args + [argument]]
|
141
|
+
end
|
142
|
+
|
143
|
+
def as_json
|
144
|
+
function, arguments = flatten
|
145
|
+
[0, function.as_json, *arguments.map(&:as_json)]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Function < Expression
|
150
|
+
include(ValueSemantics.for_attributes do
|
151
|
+
var Util::AllOf.new(::String, Util::Not.new(Util::BuiltinName))
|
152
|
+
type Either(nil, Expression) # nil is not allowed in proper Dhall
|
153
|
+
body Expression
|
154
|
+
end)
|
155
|
+
|
156
|
+
def self.of_arguments(*types, body:)
|
157
|
+
types.reverse.reduce(body) do |inner, type|
|
158
|
+
new(
|
159
|
+
var: "_",
|
160
|
+
type: type,
|
161
|
+
body: inner
|
162
|
+
)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def call(*args)
|
167
|
+
args.map! { |arg| arg&.as_dhall }
|
168
|
+
return super if args.length > 1
|
169
|
+
|
170
|
+
body.substitute(
|
171
|
+
Variable.new(name: var),
|
172
|
+
args.first.shift(1, var, 0)
|
173
|
+
).shift(-1, var, 0).normalize
|
174
|
+
end
|
175
|
+
|
176
|
+
alias [] call
|
177
|
+
|
178
|
+
def as_json
|
179
|
+
if var == "_"
|
180
|
+
[1, type.as_json, body.as_json]
|
181
|
+
else
|
182
|
+
[1, var, type.as_json, body.as_json]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class Forall < Function
|
188
|
+
def as_json
|
189
|
+
if var == "_"
|
190
|
+
[2, type.as_json, body.as_json]
|
191
|
+
else
|
192
|
+
[2, var, type.as_json, body.as_json]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class Bool < Expression
|
198
|
+
include(ValueSemantics.for_attributes do
|
199
|
+
value Bool()
|
200
|
+
end)
|
201
|
+
|
202
|
+
def reduce(when_true, when_false)
|
203
|
+
value ? when_true : when_false
|
204
|
+
end
|
205
|
+
|
206
|
+
def &(other)
|
207
|
+
reduce(other, with(value: false))
|
208
|
+
end
|
209
|
+
|
210
|
+
def |(other)
|
211
|
+
reduce(with(value: true), other)
|
212
|
+
end
|
213
|
+
|
214
|
+
def dhall_eq(other)
|
215
|
+
if other.is_a?(Bool)
|
216
|
+
reduce(other, with(value: self == other))
|
217
|
+
else
|
218
|
+
reduce(other, super)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def !@
|
223
|
+
with(value: !value)
|
224
|
+
end
|
225
|
+
|
226
|
+
def ===(other)
|
227
|
+
self == other || value === other
|
228
|
+
end
|
229
|
+
|
230
|
+
def to_s
|
231
|
+
reduce("True", "False")
|
232
|
+
end
|
233
|
+
|
234
|
+
def as_json
|
235
|
+
value
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class Variable < Expression
|
240
|
+
include(ValueSemantics.for_attributes do
|
241
|
+
name String, default: "_"
|
242
|
+
index (0..Float::INFINITY), default: 0
|
243
|
+
end)
|
244
|
+
|
245
|
+
def self.[](name, index=0)
|
246
|
+
new(name: name, index: index)
|
247
|
+
end
|
248
|
+
|
249
|
+
def to_s
|
250
|
+
"#{name}@#{index}"
|
251
|
+
end
|
252
|
+
|
253
|
+
def as_json
|
254
|
+
if name == "_"
|
255
|
+
index
|
256
|
+
elsif index.zero?
|
257
|
+
name
|
258
|
+
else
|
259
|
+
[name, index]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class Operator < Expression
|
265
|
+
include(ValueSemantics.for_attributes do
|
266
|
+
lhs Expression
|
267
|
+
rhs Expression
|
268
|
+
end)
|
269
|
+
|
270
|
+
def as_json
|
271
|
+
[3, OPERATORS.index(self.class), lhs.as_json, rhs.as_json]
|
272
|
+
end
|
273
|
+
|
274
|
+
class Or < Operator; end
|
275
|
+
class And < Operator; end
|
276
|
+
class Equal < Operator; end
|
277
|
+
class NotEqual < Operator; end
|
278
|
+
class Plus < Operator; end
|
279
|
+
class Times < Operator; end
|
280
|
+
class TextConcatenate < Operator; end
|
281
|
+
class ListConcatenate < Operator; end
|
282
|
+
class RecursiveRecordMerge < Operator; end
|
283
|
+
class RightBiasedRecordMerge < Operator; end
|
284
|
+
class RecursiveRecordTypeMerge < Operator; end
|
285
|
+
class ImportFallback < Operator; end
|
286
|
+
|
287
|
+
OPERATORS = [
|
288
|
+
Or, And, Equal, NotEqual,
|
289
|
+
Plus, Times,
|
290
|
+
TextConcatenate, ListConcatenate,
|
291
|
+
RecursiveRecordMerge, RightBiasedRecordMerge, RecursiveRecordTypeMerge,
|
292
|
+
ImportFallback
|
293
|
+
].freeze
|
294
|
+
end
|
295
|
+
|
296
|
+
class List < Expression
|
297
|
+
include Enumerable
|
298
|
+
|
299
|
+
include(ValueSemantics.for_attributes do
|
300
|
+
elements Util::ArrayOf.new(Expression, min: 1)
|
301
|
+
element_type Either(nil, Expression), default: nil
|
302
|
+
end)
|
303
|
+
|
304
|
+
def self.of(*args, type: nil)
|
305
|
+
if args.empty?
|
306
|
+
EmptyList.new(element_type: type)
|
307
|
+
else
|
308
|
+
List.new(elements: args, element_type: type)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def type
|
313
|
+
Dhall::Application.new(
|
314
|
+
function: Dhall::Variable["List"],
|
315
|
+
argument: element_type
|
316
|
+
)
|
317
|
+
end
|
318
|
+
|
319
|
+
def as_json
|
320
|
+
[4, nil, *elements.map(&:as_json)]
|
321
|
+
end
|
322
|
+
|
323
|
+
def map(type: nil, &block)
|
324
|
+
with(elements: elements.each_with_index.map(&block), element_type: type)
|
325
|
+
end
|
326
|
+
|
327
|
+
def each(&block)
|
328
|
+
elements.each(&block)
|
329
|
+
self
|
330
|
+
end
|
331
|
+
|
332
|
+
def reduce(*z)
|
333
|
+
elements.reverse.reduce(*z) { |acc, x| yield x, acc }
|
334
|
+
end
|
335
|
+
|
336
|
+
def length
|
337
|
+
elements.length
|
338
|
+
end
|
339
|
+
|
340
|
+
def [](idx)
|
341
|
+
Optional.for(elements[idx.to_i], type: element_type)
|
342
|
+
end
|
343
|
+
|
344
|
+
def first
|
345
|
+
Optional.for(elements.first, type: element_type)
|
346
|
+
end
|
347
|
+
|
348
|
+
def last
|
349
|
+
Optional.for(elements.last, type: element_type)
|
350
|
+
end
|
351
|
+
|
352
|
+
def reverse
|
353
|
+
with(elements: elements.reverse)
|
354
|
+
end
|
355
|
+
|
356
|
+
def join(sep=$,)
|
357
|
+
elements.map(&:to_s).join(sep)
|
358
|
+
end
|
359
|
+
|
360
|
+
def concat(other)
|
361
|
+
if other.is_a?(List) && !other.is_a?(EmptyList)
|
362
|
+
with(elements: elements + other.elements)
|
363
|
+
else
|
364
|
+
super
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
class EmptyList < List
|
370
|
+
include(ValueSemantics.for_attributes do
|
371
|
+
element_type Either(nil, Expression)
|
372
|
+
end)
|
373
|
+
|
374
|
+
def as_json
|
375
|
+
[4, element_type.as_json]
|
376
|
+
end
|
377
|
+
|
378
|
+
def map(type: nil)
|
379
|
+
type.nil? ? self : with(element_type: type)
|
380
|
+
end
|
381
|
+
|
382
|
+
def each
|
383
|
+
self
|
384
|
+
end
|
385
|
+
|
386
|
+
def reduce(z)
|
387
|
+
z
|
388
|
+
end
|
389
|
+
|
390
|
+
def length
|
391
|
+
0
|
392
|
+
end
|
393
|
+
|
394
|
+
def [](_)
|
395
|
+
OptionalNone.new(value_type: element_type)
|
396
|
+
end
|
397
|
+
|
398
|
+
def first
|
399
|
+
OptionalNone.new(value_type: element_type)
|
400
|
+
end
|
401
|
+
|
402
|
+
def last
|
403
|
+
OptionalNone.new(value_type: element_type)
|
404
|
+
end
|
405
|
+
|
406
|
+
def reverse
|
407
|
+
self
|
408
|
+
end
|
409
|
+
|
410
|
+
def concat(other)
|
411
|
+
other
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
class Optional < Expression
|
416
|
+
include(ValueSemantics.for_attributes do
|
417
|
+
value Expression
|
418
|
+
value_type Either(nil, Expression), default: nil
|
419
|
+
end)
|
420
|
+
|
421
|
+
def self.for(value, type: nil)
|
422
|
+
if value.nil?
|
423
|
+
OptionalNone.new(value_type: type)
|
424
|
+
else
|
425
|
+
Optional.new(value: value, value_type: type)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def initialize(normalized: false, **attrs)
|
430
|
+
@normalized = normalized
|
431
|
+
super(**attrs)
|
432
|
+
end
|
433
|
+
|
434
|
+
def type
|
435
|
+
return unless value_type
|
436
|
+
|
437
|
+
Dhall::Application.new(
|
438
|
+
function: Dhall::Variable["Optional"],
|
439
|
+
argument: value_type
|
440
|
+
)
|
441
|
+
end
|
442
|
+
|
443
|
+
def map(type: nil, &block)
|
444
|
+
with(value: block[value], value_type: type)
|
445
|
+
end
|
446
|
+
|
447
|
+
def reduce(_, &block)
|
448
|
+
block[value]
|
449
|
+
end
|
450
|
+
|
451
|
+
def to_s
|
452
|
+
value.to_s
|
453
|
+
end
|
454
|
+
|
455
|
+
def as_json
|
456
|
+
[5, @normalized ? nil : value_type&.as_json, value.as_json]
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
class OptionalNone < Optional
|
461
|
+
include(ValueSemantics.for_attributes do
|
462
|
+
value_type Expression
|
463
|
+
end)
|
464
|
+
|
465
|
+
def map(type: nil)
|
466
|
+
type.nil? ? self : with(value_type: type)
|
467
|
+
end
|
468
|
+
|
469
|
+
def reduce(z)
|
470
|
+
z
|
471
|
+
end
|
472
|
+
|
473
|
+
def to_s
|
474
|
+
""
|
475
|
+
end
|
476
|
+
|
477
|
+
def as_json
|
478
|
+
[0, Variable["None"].as_json, value_type.as_json]
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
class Merge < Expression
|
483
|
+
include(ValueSemantics.for_attributes do
|
484
|
+
record Expression
|
485
|
+
input Expression
|
486
|
+
type Either(Expression, nil)
|
487
|
+
end)
|
488
|
+
|
489
|
+
def as_json
|
490
|
+
[6, record.as_json, input.as_json] +
|
491
|
+
(type.nil? ? [] : [type.as_json])
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
class RecordType < Expression
|
496
|
+
include(ValueSemantics.for_attributes do
|
497
|
+
record Util::HashOf.new(::String, Expression, min: 1)
|
498
|
+
end)
|
499
|
+
|
500
|
+
def self.for(types)
|
501
|
+
if types.empty?
|
502
|
+
EmptyRecordType.new
|
503
|
+
else
|
504
|
+
RecordType.new(record: types)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
def merge_type(other)
|
509
|
+
return self if other.is_a?(EmptyRecordType)
|
510
|
+
|
511
|
+
with(record: record.merge(other.record))
|
512
|
+
end
|
513
|
+
|
514
|
+
def deep_merge_type(other)
|
515
|
+
return super unless other.class == RecordType
|
516
|
+
|
517
|
+
with(record: Hash[record.merge(other.record) { |_, v1, v2|
|
518
|
+
v1.deep_merge_type(v2)
|
519
|
+
}.sort])
|
520
|
+
end
|
521
|
+
|
522
|
+
def keys
|
523
|
+
record.keys
|
524
|
+
end
|
525
|
+
|
526
|
+
def slice(keys)
|
527
|
+
RecordType.for(record.select { |k, _| keys.include?(k) })
|
528
|
+
end
|
529
|
+
|
530
|
+
def ==(other)
|
531
|
+
other.respond_to?(:record) && record.to_a == other.record.to_a
|
532
|
+
end
|
533
|
+
|
534
|
+
def eql?(other)
|
535
|
+
self == other
|
536
|
+
end
|
537
|
+
|
538
|
+
def as_json
|
539
|
+
[7, Hash[record.to_a.map { |k, v| [k, v.as_json] }.sort]]
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
class EmptyRecordType < RecordType
|
544
|
+
include(ValueSemantics.for_attributes {})
|
545
|
+
|
546
|
+
def slice(*)
|
547
|
+
self
|
548
|
+
end
|
549
|
+
|
550
|
+
def record
|
551
|
+
{}
|
552
|
+
end
|
553
|
+
|
554
|
+
def merge_type(other)
|
555
|
+
other
|
556
|
+
end
|
557
|
+
|
558
|
+
def deep_merge_type(other)
|
559
|
+
other
|
560
|
+
end
|
561
|
+
|
562
|
+
def as_json
|
563
|
+
[7, {}]
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
class Record < Expression
|
568
|
+
include Enumerable
|
569
|
+
|
570
|
+
include(ValueSemantics.for_attributes do
|
571
|
+
record Util::HashOf.new(::String, Expression, min: 1)
|
572
|
+
end)
|
573
|
+
|
574
|
+
def self.for(record)
|
575
|
+
if record.empty?
|
576
|
+
EmptyRecord.new
|
577
|
+
else
|
578
|
+
new(record: record)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
def each(&block)
|
583
|
+
record.each(&block)
|
584
|
+
self
|
585
|
+
end
|
586
|
+
|
587
|
+
def to_h
|
588
|
+
record
|
589
|
+
end
|
590
|
+
|
591
|
+
def keys
|
592
|
+
record.keys
|
593
|
+
end
|
594
|
+
|
595
|
+
def values
|
596
|
+
record.values
|
597
|
+
end
|
598
|
+
|
599
|
+
def [](k)
|
600
|
+
record[k.to_s]
|
601
|
+
end
|
602
|
+
|
603
|
+
def fetch(k, default=nil, &block)
|
604
|
+
record.fetch(k.to_s, *default, &block)
|
605
|
+
end
|
606
|
+
|
607
|
+
def slice(*keys)
|
608
|
+
keys = keys.map(&:to_s)
|
609
|
+
if record.respond_to?(:slice)
|
610
|
+
self.class.for(record.slice(*keys))
|
611
|
+
else
|
612
|
+
self.class.for(record.select { |k, _| keys.include?(k) })
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
def dig(*keys)
|
617
|
+
if keys.empty?
|
618
|
+
raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
|
619
|
+
end
|
620
|
+
|
621
|
+
key, *rest = keys.map(&:to_s)
|
622
|
+
v = record.fetch(key) { return nil }
|
623
|
+
return v if rest.empty?
|
624
|
+
|
625
|
+
v.dig(*rest)
|
626
|
+
end
|
627
|
+
|
628
|
+
def deep_merge(other)
|
629
|
+
other = other.as_dhall
|
630
|
+
return super unless other.is_a?(Record)
|
631
|
+
|
632
|
+
with(record: Hash[record.merge(other.record) { |_, v1, v2|
|
633
|
+
v1.deep_merge(v2)
|
634
|
+
}.sort])
|
635
|
+
end
|
636
|
+
|
637
|
+
def merge(other)
|
638
|
+
other = other.as_dhall
|
639
|
+
return super unless other.is_a?(Record)
|
640
|
+
|
641
|
+
with(record: Hash[record.merge(other.record).sort])
|
642
|
+
end
|
643
|
+
|
644
|
+
def map(&block)
|
645
|
+
with(record: Hash[record.map(&block)])
|
646
|
+
end
|
647
|
+
|
648
|
+
def ==(other)
|
649
|
+
other.respond_to?(:record) && record.to_a == other.record.to_a
|
650
|
+
end
|
651
|
+
|
652
|
+
def eql?(other)
|
653
|
+
self == other
|
654
|
+
end
|
655
|
+
|
656
|
+
def with(attrs)
|
657
|
+
self.class.new({ record: record }.merge(attrs))
|
658
|
+
end
|
659
|
+
|
660
|
+
def as_json
|
661
|
+
[8, Hash[record.to_a.map { |k, v| [k, v.as_json] }.sort]]
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
class EmptyRecord < Expression
|
666
|
+
include Enumerable
|
667
|
+
|
668
|
+
include(ValueSemantics.for_attributes {})
|
669
|
+
|
670
|
+
def each
|
671
|
+
self
|
672
|
+
end
|
673
|
+
|
674
|
+
def to_h
|
675
|
+
{}
|
676
|
+
end
|
677
|
+
|
678
|
+
def keys
|
679
|
+
[]
|
680
|
+
end
|
681
|
+
|
682
|
+
def fetch(k, default=nil, &block)
|
683
|
+
{}.fetch(k, *default, &block)
|
684
|
+
end
|
685
|
+
|
686
|
+
def slice(*)
|
687
|
+
self
|
688
|
+
end
|
689
|
+
|
690
|
+
def deep_merge(other)
|
691
|
+
other
|
692
|
+
end
|
693
|
+
|
694
|
+
def merge(other)
|
695
|
+
other
|
696
|
+
end
|
697
|
+
|
698
|
+
def map
|
699
|
+
self
|
700
|
+
end
|
701
|
+
|
702
|
+
def as_json
|
703
|
+
[8, {}]
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
class RecordSelection < Expression
|
708
|
+
include(ValueSemantics.for_attributes do
|
709
|
+
record Expression
|
710
|
+
selector ::String
|
711
|
+
end)
|
712
|
+
|
713
|
+
def call(value)
|
714
|
+
if record.is_a?(UnionType)
|
715
|
+
record.get_constructor(selector).call(value)
|
716
|
+
else
|
717
|
+
Application.new(function: self, argument: value)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
def as_json
|
722
|
+
[9, record.as_json, selector]
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
class RecordProjection < Expression
|
727
|
+
include(ValueSemantics.for_attributes do
|
728
|
+
record Expression
|
729
|
+
selectors Util::ArrayOf.new(::String, min: 1)
|
730
|
+
end)
|
731
|
+
|
732
|
+
def as_json
|
733
|
+
[10, record.as_json, *selectors]
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
class EmptyRecordProjection < Expression
|
738
|
+
include(ValueSemantics.for_attributes do
|
739
|
+
record Expression
|
740
|
+
end)
|
741
|
+
|
742
|
+
def selectors
|
743
|
+
[]
|
744
|
+
end
|
745
|
+
|
746
|
+
def as_json
|
747
|
+
[10, record.as_json]
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
class UnionType < Expression
|
752
|
+
include(ValueSemantics.for_attributes do
|
753
|
+
alternatives Util::HashOf.new(::String, Either(Expression, nil))
|
754
|
+
end)
|
755
|
+
|
756
|
+
def record
|
757
|
+
alternatives
|
758
|
+
end
|
759
|
+
|
760
|
+
def ==(other)
|
761
|
+
other.is_a?(UnionType) && alternatives.to_a == other.alternatives.to_a
|
762
|
+
end
|
763
|
+
|
764
|
+
def eql?(other)
|
765
|
+
self == other
|
766
|
+
end
|
767
|
+
|
768
|
+
def merge(other)
|
769
|
+
with(alternatives: alternatives.merge(other.alternatives))
|
770
|
+
end
|
771
|
+
|
772
|
+
def fetch(k, default=nil)
|
773
|
+
if alternatives.fetch(k)
|
774
|
+
super(k)
|
775
|
+
else
|
776
|
+
Union.from(self, k, nil)
|
777
|
+
end
|
778
|
+
rescue KeyError
|
779
|
+
block_given? ? yield : (default || raise)
|
780
|
+
end
|
781
|
+
|
782
|
+
def get_constructor(selector)
|
783
|
+
var = Util::BuiltinName === selector ? "_" : selector
|
784
|
+
type = alternatives.fetch(selector)
|
785
|
+
body = Union.from(self, selector, Variable[var])
|
786
|
+
Function.new(var: var, type: type, body: body)
|
787
|
+
end
|
788
|
+
|
789
|
+
def constructor_types
|
790
|
+
alternatives.each_with_object({}) do |(k, type), ctypes|
|
791
|
+
ctypes[k] = if type.nil?
|
792
|
+
self
|
793
|
+
else
|
794
|
+
var = Util::BuiltinName === k ? "_" : k
|
795
|
+
Forall.new(var: var, type: type, body: self)
|
796
|
+
end
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
def as_json
|
801
|
+
[11, Hash[alternatives.to_a.map { |k, v| [k, v&.as_json] }.sort]]
|
802
|
+
end
|
803
|
+
end
|
804
|
+
|
805
|
+
class Union < Expression
|
806
|
+
include(ValueSemantics.for_attributes do
|
807
|
+
tag ::String
|
808
|
+
value Either(Expression, nil)
|
809
|
+
alternatives UnionType
|
810
|
+
end)
|
811
|
+
|
812
|
+
def self.from(alts, tag, value)
|
813
|
+
new(
|
814
|
+
tag: tag,
|
815
|
+
value: value && TypeAnnotation.new(
|
816
|
+
value: value,
|
817
|
+
type: alts.alternatives[tag]
|
818
|
+
),
|
819
|
+
alternatives: alts.with(
|
820
|
+
alternatives: alts.alternatives.reject { |alt, _| alt == tag }
|
821
|
+
)
|
822
|
+
)
|
823
|
+
end
|
824
|
+
|
825
|
+
def to_s
|
826
|
+
value.nil? ? tag : extract.to_s
|
827
|
+
end
|
828
|
+
|
829
|
+
def extract
|
830
|
+
if value.nil?
|
831
|
+
tag.to_sym
|
832
|
+
elsif value.is_a?(TypeAnnotation)
|
833
|
+
value.value
|
834
|
+
else
|
835
|
+
value
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
def reduce(handlers)
|
840
|
+
handlers = handlers.to_h
|
841
|
+
handler = handlers.fetch(tag.to_sym) { handlers.fetch(tag) }
|
842
|
+
if value.nil?
|
843
|
+
handler
|
844
|
+
else
|
845
|
+
(handler.respond_to?(:to_proc) ? handler.to_proc : handler)
|
846
|
+
.call(extract)
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
def selection_syntax
|
851
|
+
RecordSelection.new(
|
852
|
+
record: alternatives.merge(
|
853
|
+
UnionType.new(alternatives: { tag => value&.type })
|
854
|
+
),
|
855
|
+
selector: tag
|
856
|
+
)
|
857
|
+
end
|
858
|
+
|
859
|
+
def syntax
|
860
|
+
if value.nil?
|
861
|
+
selection_syntax
|
862
|
+
else
|
863
|
+
Application.new(
|
864
|
+
function: selection_syntax,
|
865
|
+
argument: value.is_a?(TypeAnnotation) ? value.value : value
|
866
|
+
)
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
def as_json
|
871
|
+
if value.nil? || value.respond_to?(:type)
|
872
|
+
syntax.as_json
|
873
|
+
else
|
874
|
+
[12, tag, value&.as_json, alternatives.as_json.last]
|
875
|
+
end
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
class If < Expression
|
880
|
+
include(ValueSemantics.for_attributes do
|
881
|
+
predicate Expression
|
882
|
+
self.then Expression
|
883
|
+
self.else Expression
|
884
|
+
end)
|
885
|
+
|
886
|
+
def as_json
|
887
|
+
[14, predicate.as_json, self.then.as_json, self.else.as_json]
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
class Natural < Expression
|
892
|
+
include(ValueSemantics.for_attributes do
|
893
|
+
value (0..Float::INFINITY)
|
894
|
+
end)
|
895
|
+
|
896
|
+
def coerce(other)
|
897
|
+
[other.as_dhall, self]
|
898
|
+
end
|
899
|
+
|
900
|
+
def +(other)
|
901
|
+
other = other.as_dhall
|
902
|
+
if other.is_a?(Natural)
|
903
|
+
with(value: value + other.value)
|
904
|
+
else
|
905
|
+
super
|
906
|
+
end
|
907
|
+
end
|
908
|
+
|
909
|
+
def *(other)
|
910
|
+
other = other.as_dhall
|
911
|
+
return self if zero?
|
912
|
+
if other.is_a?(Natural)
|
913
|
+
with(value: value * other.value)
|
914
|
+
else
|
915
|
+
super
|
916
|
+
end
|
917
|
+
end
|
918
|
+
|
919
|
+
def to_i
|
920
|
+
value
|
921
|
+
end
|
922
|
+
|
923
|
+
def to_s
|
924
|
+
value.to_s
|
925
|
+
end
|
926
|
+
|
927
|
+
def even?
|
928
|
+
value.even?
|
929
|
+
end
|
930
|
+
|
931
|
+
def odd?
|
932
|
+
value.odd?
|
933
|
+
end
|
934
|
+
|
935
|
+
def zero?
|
936
|
+
value.zero?
|
937
|
+
end
|
938
|
+
|
939
|
+
def pred
|
940
|
+
with(value: [0, value - 1].max)
|
941
|
+
end
|
942
|
+
|
943
|
+
def ===(other)
|
944
|
+
self == other || value === other
|
945
|
+
end
|
946
|
+
|
947
|
+
def as_json
|
948
|
+
[15, value]
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
952
|
+
class Integer < Expression
|
953
|
+
include(ValueSemantics.for_attributes do
|
954
|
+
value ::Integer
|
955
|
+
end)
|
956
|
+
|
957
|
+
def to_s
|
958
|
+
"#{value >= 0 ? "+" : ""}#{value}"
|
959
|
+
end
|
960
|
+
|
961
|
+
def to_i
|
962
|
+
value
|
963
|
+
end
|
964
|
+
|
965
|
+
def ===(other)
|
966
|
+
self == other || value === other
|
967
|
+
end
|
968
|
+
|
969
|
+
def as_json
|
970
|
+
[16, value]
|
971
|
+
end
|
972
|
+
end
|
973
|
+
|
974
|
+
class Double < Expression
|
975
|
+
include(ValueSemantics.for_attributes do
|
976
|
+
value ::Float
|
977
|
+
end)
|
978
|
+
|
979
|
+
def to_s
|
980
|
+
value.to_s
|
981
|
+
end
|
982
|
+
|
983
|
+
def to_f
|
984
|
+
value
|
985
|
+
end
|
986
|
+
|
987
|
+
def ===(other)
|
988
|
+
self == other || value === other
|
989
|
+
end
|
990
|
+
|
991
|
+
def coerce(other)
|
992
|
+
return [other, self] if other.is_a?(Double)
|
993
|
+
[Double.new(value: other.to_f), self]
|
994
|
+
end
|
995
|
+
|
996
|
+
def eql?(other)
|
997
|
+
other.is_a?(Double) && to_cbor == other.to_cbor
|
998
|
+
end
|
999
|
+
|
1000
|
+
def single?
|
1001
|
+
[value].pack("g").unpack("g").first == value
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
def as_json
|
1005
|
+
self
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def to_json
|
1009
|
+
value.to_json
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
def to_cbor(packer=nil)
|
1013
|
+
if [0, Float::INFINITY, -Float::INFINITY].include?(value) || value.nan?
|
1014
|
+
return value.to_cbor(packer)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# Dhall spec requires *not* using half-precision CBOR floats
|
1018
|
+
bytes = single? ? [0xFA, value].pack("Cg") : [0xFB, value].pack("CG")
|
1019
|
+
if packer
|
1020
|
+
packer.buffer.write(bytes)
|
1021
|
+
packer
|
1022
|
+
else
|
1023
|
+
bytes
|
1024
|
+
end
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
class Text < Expression
|
1029
|
+
include(ValueSemantics.for_attributes do
|
1030
|
+
value ::String, coerce: ->(s) { s.encode("UTF-8") }
|
1031
|
+
end)
|
1032
|
+
|
1033
|
+
def <<(other)
|
1034
|
+
if other.is_a?(Text)
|
1035
|
+
with(value: value + other.value)
|
1036
|
+
else
|
1037
|
+
super
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def to_s
|
1042
|
+
value
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def ===(other)
|
1046
|
+
self == other || value === other
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def as_json
|
1050
|
+
[18, value]
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
class TextLiteral < Expression
|
1055
|
+
include(ValueSemantics.for_attributes do
|
1056
|
+
chunks Util::ArrayOf.new(Expression, min: 3)
|
1057
|
+
end)
|
1058
|
+
|
1059
|
+
def self.for(*chunks)
|
1060
|
+
fixed =
|
1061
|
+
([""] + chunks)
|
1062
|
+
.flat_map { |c| ["", c, ""] }
|
1063
|
+
.map { |c| c.is_a?(Expression) ? c : Text.new(value: c.to_s) }
|
1064
|
+
.chunk { |x| x.is_a?(Text) }.flat_map do |(is_text, group)|
|
1065
|
+
is_text ? group.reduce(&:<<) : group
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
fixed.length == 1 ? fixed.first : new(chunks: fixed)
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
def as_json
|
1072
|
+
[18, *chunks.map { |chunk| chunk.is_a?(Text) ? chunk.value : chunk.as_json }]
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
class Import < Expression
|
1077
|
+
class IntegrityCheck
|
1078
|
+
include(ValueSemantics.for_attributes do
|
1079
|
+
protocol Either("sha256", :nocheck)
|
1080
|
+
data Either(::String, nil)
|
1081
|
+
end)
|
1082
|
+
|
1083
|
+
class FailureException < StandardError; end
|
1084
|
+
|
1085
|
+
def initialize(protocol=:nocheck, data=nil)
|
1086
|
+
super(
|
1087
|
+
protocol: protocol,
|
1088
|
+
data: data
|
1089
|
+
)
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
def to_s
|
1093
|
+
"#{@protocol}:#{@data}"
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def check(expr)
|
1097
|
+
return expr if @protocol == :nocheck
|
1098
|
+
|
1099
|
+
expr = expr.normalize
|
1100
|
+
return expr if expr.cache_key == to_s
|
1101
|
+
|
1102
|
+
raise FailureException, "#{expr} does not match #{self}"
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def as_json
|
1106
|
+
@protocol == :nocheck ? nil : [@protocol, @data]
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
class URI
|
1111
|
+
include(ValueSemantics.for_attributes do
|
1112
|
+
headers Either(nil, Expression)
|
1113
|
+
authority ::String
|
1114
|
+
path ArrayOf(::String)
|
1115
|
+
query Either(nil, ::String)
|
1116
|
+
end)
|
1117
|
+
|
1118
|
+
HeaderType = RecordType.new(
|
1119
|
+
record: {
|
1120
|
+
"header" => Variable["Text"],
|
1121
|
+
"value" => Variable["Text"]
|
1122
|
+
}
|
1123
|
+
)
|
1124
|
+
|
1125
|
+
def initialize(headers, authority, *path, query)
|
1126
|
+
super(
|
1127
|
+
headers: headers,
|
1128
|
+
authority: authority,
|
1129
|
+
path: path,
|
1130
|
+
query: query,
|
1131
|
+
)
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
def with(hash)
|
1135
|
+
self.class.new(
|
1136
|
+
hash.fetch(:headers, headers),
|
1137
|
+
hash.fetch(:authority, authority),
|
1138
|
+
*hash.fetch(:path, path),
|
1139
|
+
hash.fetch(:query, query)
|
1140
|
+
)
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def self.from_uri(uri)
|
1144
|
+
(uri.scheme == "https" ? Https : Http).new(
|
1145
|
+
nil,
|
1146
|
+
"#{uri.host}:#{uri.port}",
|
1147
|
+
*uri.path.split(/\//)[1..-1],
|
1148
|
+
uri.query,
|
1149
|
+
nil
|
1150
|
+
)
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
def headers
|
1154
|
+
super || EmptyList.new(element_type: HeaderType)
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
def uri
|
1158
|
+
escaped_path = path.map do |c|
|
1159
|
+
::URI.encode_www_form_component(c).gsub("+", "%20")
|
1160
|
+
end
|
1161
|
+
URI("#{scheme}://#{authority}/#{escaped_path.join("/")}?#{query}")
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
def chain_onto(relative_to)
|
1165
|
+
if headers.is_a?(Import)
|
1166
|
+
with(headers: headers.with(path: headers.real_path(relative_to)))
|
1167
|
+
else
|
1168
|
+
self
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
def canonical
|
1173
|
+
with(
|
1174
|
+
path: (path[1..-1] + [""])
|
1175
|
+
.reduce([[], path.first]) { |(pth, prev), c|
|
1176
|
+
c == ".." ? [pth, prev] : [pth + [prev], c]
|
1177
|
+
}.first.reject { |c| c == "." }
|
1178
|
+
)
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
def origin
|
1182
|
+
"#{scheme}://#{authority}"
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
def to_s
|
1186
|
+
uri.to_s
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
def as_json
|
1190
|
+
[@headers&.as_json, authority, *path, query]
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
class Http < URI
|
1195
|
+
def resolve(resolver)
|
1196
|
+
resolver.resolve_http(self)
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
def scheme
|
1200
|
+
"http"
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
class Https < URI
|
1205
|
+
def resolve(resolver)
|
1206
|
+
resolver.resolve_https(self)
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
def scheme
|
1210
|
+
"https"
|
1211
|
+
end
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
class Path
|
1215
|
+
include(ValueSemantics.for_attributes do
|
1216
|
+
path ArrayOf(::String)
|
1217
|
+
end)
|
1218
|
+
|
1219
|
+
def initialize(*path)
|
1220
|
+
super(path: path)
|
1221
|
+
end
|
1222
|
+
|
1223
|
+
def with(path:)
|
1224
|
+
self.class.new(*path)
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
def self.from_string(s)
|
1228
|
+
parts = s.to_s.split(/\//)
|
1229
|
+
if parts.first == ""
|
1230
|
+
AbsolutePath.new(*parts[1..-1])
|
1231
|
+
elsif parts.first == "~"
|
1232
|
+
RelativeToHomePath.new(*parts[1..-1])
|
1233
|
+
else
|
1234
|
+
RelativePath.new(*parts)
|
1235
|
+
end
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
def canonical
|
1239
|
+
self.class.from_string(pathname.cleanpath)
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
def resolve(resolver)
|
1243
|
+
resolver.resolve_path(self)
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
def origin
|
1247
|
+
"localhost"
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
def to_s
|
1251
|
+
pathname.to_s
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
def as_json
|
1255
|
+
path
|
1256
|
+
end
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
class AbsolutePath < Path
|
1260
|
+
def pathname
|
1261
|
+
Pathname.new("/").join(*path)
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
def to_uri(scheme, authority)
|
1265
|
+
scheme.new(nil, authority, *path, nil)
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
def chain_onto(relative_to)
|
1269
|
+
if relative_to.is_a?(URI)
|
1270
|
+
raise ImportBannedException, "remote import cannot import #{self}"
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
self
|
1274
|
+
end
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
class RelativePath < Path
|
1278
|
+
def pathname
|
1279
|
+
Pathname.new(".").join(*path)
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
def chain_onto(relative_to)
|
1283
|
+
relative_to.with(
|
1284
|
+
path: relative_to.path[0..-2] + path
|
1285
|
+
)
|
1286
|
+
end
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
class RelativeToParentPath < Path
|
1290
|
+
def pathname
|
1291
|
+
Pathname.new("..").join(*path)
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
def chain_onto(relative_to)
|
1295
|
+
relative_to.with(
|
1296
|
+
path: relative_to.path[0..-2] + [".."] + path
|
1297
|
+
)
|
1298
|
+
end
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
class RelativeToHomePath < Path
|
1302
|
+
def pathname
|
1303
|
+
Pathname.new("~").join(*@path)
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
def chain_onto(*)
|
1307
|
+
if relative_to.is_a?(URI)
|
1308
|
+
raise ImportBannedException, "remote import cannot import #{self}"
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
self
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
class EnvironmentVariable
|
1316
|
+
ESCAPES = {
|
1317
|
+
"\"" => "\"",
|
1318
|
+
"\\" => "\\",
|
1319
|
+
"a" => "\a",
|
1320
|
+
"b" => "\b",
|
1321
|
+
"f" => "\f",
|
1322
|
+
"n" => "\n",
|
1323
|
+
"r" => "\r",
|
1324
|
+
"t" => "\t",
|
1325
|
+
"v" => "\v"
|
1326
|
+
}.freeze
|
1327
|
+
|
1328
|
+
def self.decode(var)
|
1329
|
+
var.gsub(/\\[\"\\abfnrtv]/) do |escape|
|
1330
|
+
ESCAPES.fetch(escape[1])
|
1331
|
+
end
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
def initialize(var)
|
1335
|
+
@var = var
|
1336
|
+
end
|
1337
|
+
|
1338
|
+
def chain_onto(relative_to)
|
1339
|
+
if relative_to.is_a?(URI)
|
1340
|
+
raise ImportBannedException, "remote import cannot import #{self}"
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
real_path.chain_onto(relative_to)
|
1344
|
+
end
|
1345
|
+
|
1346
|
+
def canonical
|
1347
|
+
real_path.canonical
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
def real_path
|
1351
|
+
val = ENV.fetch(@var) do
|
1352
|
+
raise ImportFailedException, "No #{self}"
|
1353
|
+
end
|
1354
|
+
if val =~ /\Ahttps?:\/\//
|
1355
|
+
URI.from_uri(URI(val))
|
1356
|
+
else
|
1357
|
+
Path.from_string(val)
|
1358
|
+
end
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
def resolve(resolver)
|
1362
|
+
Promise.resolve(nil).then do
|
1363
|
+
real_path.resolve(resolver)
|
1364
|
+
end
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
def origin
|
1368
|
+
"localhost"
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
def to_s
|
1372
|
+
"env:#{as_json}"
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
def as_json
|
1376
|
+
@var.gsub(/[\"\\\a\b\f\n\r\t\v]/) do |c|
|
1377
|
+
"\\" + ESCAPES.find { |(_, v)| v == c }.first
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
class MissingImport
|
1383
|
+
def chain_onto(*)
|
1384
|
+
self
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
def canonical
|
1388
|
+
self
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
def resolve(*)
|
1392
|
+
Promise.new.reject(ImportFailedException.new("missing"))
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
def origin; end
|
1396
|
+
|
1397
|
+
def to_s
|
1398
|
+
"missing"
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
def as_json
|
1402
|
+
[]
|
1403
|
+
end
|
1404
|
+
end
|
1405
|
+
|
1406
|
+
class Expression
|
1407
|
+
def self.call(import_value)
|
1408
|
+
Dhall.load_raw(import_value)
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
class Text
|
1413
|
+
def self.call(import_value)
|
1414
|
+
Dhall::Text.new(value: import_value)
|
1415
|
+
end
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
IMPORT_TYPES = [
|
1419
|
+
Expression,
|
1420
|
+
Text
|
1421
|
+
].freeze
|
1422
|
+
|
1423
|
+
PATH_TYPES = [
|
1424
|
+
Http, Https,
|
1425
|
+
AbsolutePath, RelativePath, RelativeToParentPath, RelativeToHomePath,
|
1426
|
+
EnvironmentVariable, MissingImport
|
1427
|
+
].freeze
|
1428
|
+
|
1429
|
+
include(ValueSemantics.for_attributes do
|
1430
|
+
integrity_check IntegrityCheck, default: IntegrityCheck.new
|
1431
|
+
import_type Class
|
1432
|
+
path Either(*PATH_TYPES)
|
1433
|
+
end)
|
1434
|
+
|
1435
|
+
def initialize(integrity_check, import_type, path)
|
1436
|
+
super(
|
1437
|
+
integrity_check: integrity_check || IntegrityCheck.new,
|
1438
|
+
import_type: import_type,
|
1439
|
+
path: path
|
1440
|
+
)
|
1441
|
+
end
|
1442
|
+
|
1443
|
+
def with(options)
|
1444
|
+
self.class.new(
|
1445
|
+
options.fetch(:integrity_check, integrity_check),
|
1446
|
+
options.fetch(:import_type, import_type),
|
1447
|
+
options.fetch(:path, path)
|
1448
|
+
)
|
1449
|
+
end
|
1450
|
+
|
1451
|
+
def real_path(relative_to)
|
1452
|
+
path.chain_onto(relative_to).canonical
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
def parse_and_check(raw)
|
1456
|
+
integrity_check.check(import_type.call(raw))
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
def cache_key(relative_to)
|
1460
|
+
if integrity_check.protocol == :nocheck
|
1461
|
+
real_path(relative_to).to_s
|
1462
|
+
else
|
1463
|
+
integrity_check.to_s
|
1464
|
+
end
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
def as_json
|
1468
|
+
[
|
1469
|
+
24,
|
1470
|
+
integrity_check&.as_json,
|
1471
|
+
IMPORT_TYPES.index(import_type),
|
1472
|
+
PATH_TYPES.index(path.class),
|
1473
|
+
*path.as_json
|
1474
|
+
]
|
1475
|
+
end
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
class Let < Expression
|
1479
|
+
include(ValueSemantics.for_attributes do
|
1480
|
+
var Util::AllOf.new(::String, Util::Not.new(Util::BuiltinName))
|
1481
|
+
assign Expression
|
1482
|
+
type Either(nil, Expression)
|
1483
|
+
end)
|
1484
|
+
|
1485
|
+
def as_json
|
1486
|
+
[var, type&.as_json, assign.as_json]
|
1487
|
+
end
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
class LetIn < Expression
|
1491
|
+
include(ValueSemantics.for_attributes do
|
1492
|
+
let Let
|
1493
|
+
body Expression
|
1494
|
+
end)
|
1495
|
+
|
1496
|
+
def desugar
|
1497
|
+
Application.new(
|
1498
|
+
function: Function.new(
|
1499
|
+
var: let.var,
|
1500
|
+
type: let.type,
|
1501
|
+
body: body
|
1502
|
+
),
|
1503
|
+
argument: let.assign
|
1504
|
+
)
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
def eliminate
|
1508
|
+
body.substitute(
|
1509
|
+
Dhall::Variable[let.var],
|
1510
|
+
let.assign.shift(1, let.var, 0)
|
1511
|
+
).shift(-1, let.var, 0)
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
def as_json
|
1515
|
+
[25, *let.as_json, body.as_json]
|
1516
|
+
end
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
class LetBlock < Expression
|
1520
|
+
include(ValueSemantics.for_attributes do
|
1521
|
+
lets Util::ArrayOf.new(Let, min: 2)
|
1522
|
+
body Expression
|
1523
|
+
end)
|
1524
|
+
|
1525
|
+
def self.for(lets:, body:)
|
1526
|
+
if lets.length == 1
|
1527
|
+
LetIn.new(let: lets.first, body: body)
|
1528
|
+
else
|
1529
|
+
new(lets: lets, body: body)
|
1530
|
+
end
|
1531
|
+
end
|
1532
|
+
|
1533
|
+
def unflatten
|
1534
|
+
lets.reverse.reduce(body) do |inside, let|
|
1535
|
+
letin = LetIn.new(let: let, body: inside)
|
1536
|
+
block_given? ? (yield letin) : letin
|
1537
|
+
end
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
def desugar
|
1541
|
+
unflatten(&:desugar)
|
1542
|
+
end
|
1543
|
+
|
1544
|
+
def as_json
|
1545
|
+
[25, *lets.flat_map(&:as_json), body.as_json]
|
1546
|
+
end
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
class TypeAnnotation < Expression
|
1550
|
+
include(ValueSemantics.for_attributes do
|
1551
|
+
value Expression
|
1552
|
+
type Expression
|
1553
|
+
end)
|
1554
|
+
|
1555
|
+
def as_json
|
1556
|
+
[26, value.as_json, type.as_json]
|
1557
|
+
end
|
1558
|
+
end
|
1559
|
+
end
|