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.
@@ -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