dhall 0.1.0 → 0.5.1

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.
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "base32"
4
+ require "lazy_object"
5
+ require "multihashes"
3
6
  require "uri"
4
7
  require "value_semantics"
5
8
 
@@ -33,18 +36,13 @@ module Dhall
33
36
  end
34
37
 
35
38
  def *(other)
36
- case other
37
- when Natural
39
+ if other.is_a?(Natural) && other.zero?
38
40
  other * self
39
41
  else
40
42
  Operator::Times.new(lhs: self, rhs: other)
41
43
  end
42
44
  end
43
45
 
44
- def <<(other)
45
- Operator::TextConcatenate.new(lhs: self, rhs: other)
46
- end
47
-
48
46
  def concat(other)
49
47
  Operator::ListConcatenate.new(lhs: self, rhs: other)
50
48
  end
@@ -72,7 +70,7 @@ module Dhall
72
70
  def dhall_eq(other)
73
71
  if self == other
74
72
  Bool.new(value: true)
75
- elsif other.is_a?(Bool)
73
+ elsif other == Bool.new(value: true)
76
74
  other.dhall_eq(self)
77
75
  else
78
76
  Operator::Equal.new(lhs: self, rhs: other)
@@ -106,6 +104,14 @@ module Dhall
106
104
  end
107
105
  end
108
106
 
107
+ def annotate(type)
108
+ TypeAnnotation.new(value: self, type: type)
109
+ end
110
+
111
+ def to_s
112
+ inspect
113
+ end
114
+
109
115
  def as_dhall
110
116
  self
111
117
  end
@@ -118,9 +124,7 @@ module Dhall
118
124
  end)
119
125
 
120
126
  def self.for(function:, argument:)
121
- if function == Variable["Some"]
122
- Optional.new(value: argument)
123
- elsif function == Variable["None"]
127
+ if function == Builtins[:None]
124
128
  OptionalNone.new(value_type: argument)
125
129
  else
126
130
  new(function: function, argument: argument)
@@ -130,7 +134,7 @@ module Dhall
130
134
  def flatten
131
135
  f, args = if function.is_a?(Application)
132
136
  function.flatten
133
- elsif function.is_a?(Builtin) &&
137
+ elsif function.is_a?(BuiltinFunction) &&
134
138
  (unfilled = function.unfill).is_a?(Application)
135
139
  unfilled.flatten
136
140
  else
@@ -148,7 +152,7 @@ module Dhall
148
152
 
149
153
  class Function < Expression
150
154
  include(ValueSemantics.for_attributes do
151
- var Util::AllOf.new(::String, Util::Not.new(Util::BuiltinName))
155
+ var ::String
152
156
  type Either(nil, Expression) # nil is not allowed in proper Dhall
153
157
  body Expression
154
158
  end)
@@ -163,7 +167,8 @@ module Dhall
163
167
  end
164
168
  end
165
169
 
166
- def call(*args)
170
+ def call(*args, &block)
171
+ args += [block] if block
167
172
  args.map! { |arg| arg&.as_dhall }
168
173
  return super if args.length > 1
169
174
 
@@ -174,6 +179,29 @@ module Dhall
174
179
  end
175
180
 
176
181
  alias [] call
182
+ alias === call
183
+
184
+ def <<(other)
185
+ FunctionProxy.new(
186
+ ->(*args, &block) { call(other.call(*args, &block)) },
187
+ curry: false
188
+ )
189
+ end
190
+
191
+ def >>(other)
192
+ FunctionProxy.new(
193
+ ->(*args, &block) { other.call(call(*args, &block)) },
194
+ curry: false
195
+ )
196
+ end
197
+
198
+ def binding
199
+ to_proc.binding
200
+ end
201
+
202
+ def curry
203
+ self
204
+ end
177
205
 
178
206
  def as_json
179
207
  if var == "_"
@@ -194,6 +222,56 @@ module Dhall
194
222
  end
195
223
  end
196
224
 
225
+ class RubyObjectRaw < Expression
226
+ def initialize(object)
227
+ @object = object
228
+ end
229
+
230
+ def unwrap
231
+ @object
232
+ end
233
+
234
+ def respond_to_missing?(m)
235
+ super || @object.respond_to?(m)
236
+ end
237
+
238
+ def method_missing(m, *args, &block)
239
+ if @object.respond_to?(m)
240
+ @object.public_send(m, *args, &block)
241
+ else
242
+ super
243
+ end
244
+ end
245
+ end
246
+
247
+ class FunctionProxyRaw < Function
248
+ def initialize(callable, curry: true)
249
+ @callable = if !curry
250
+ callable
251
+ elsif callable.respond_to?(:curry)
252
+ callable.curry
253
+ elsif callable.respond_to?(:to_proc)
254
+ callable.to_proc.curry
255
+ else
256
+ callable.method(:call).to_proc.curry
257
+ end
258
+ end
259
+
260
+ def call(*args, &block)
261
+ RubyObjectRaw.new(@callable.call(*args.map { |arg| arg&.as_dhall }, &block))
262
+ end
263
+
264
+ def as_json
265
+ raise "Cannot serialize #{self}"
266
+ end
267
+ end
268
+
269
+ class FunctionProxy < FunctionProxyRaw
270
+ def call(*args, &block)
271
+ super.unwrap.as_dhall
272
+ end
273
+ end
274
+
197
275
  class Bool < Expression
198
276
  include(ValueSemantics.for_attributes do
199
277
  value Bool()
@@ -234,6 +312,10 @@ module Dhall
234
312
  def as_json
235
313
  value
236
314
  end
315
+
316
+ def self.as_dhall
317
+ Builtins[:Bool]
318
+ end
237
319
  end
238
320
 
239
321
  class Variable < Expression
@@ -253,8 +335,6 @@ module Dhall
253
335
  def as_json
254
336
  if name == "_"
255
337
  index
256
- elsif index.zero?
257
- name
258
338
  else
259
339
  [name, index]
260
340
  end
@@ -271,6 +351,33 @@ module Dhall
271
351
  [3, OPERATORS.index(self.class), lhs.as_json, rhs.as_json]
272
352
  end
273
353
 
354
+ module FetchFromMerge
355
+ def fetch_second_record(first, second, selector)
356
+ rec = self.class.new(
357
+ self.class::FETCH2K => second.slice(selector),
358
+ self.class::FETCH1K => first
359
+ ).normalize
360
+
361
+ if rec.class == self.class
362
+ RecordSelection.new(record: rec, selector: selector)
363
+ else
364
+ rec.fetch(selector)
365
+ end
366
+ end
367
+
368
+ def fetch(selector)
369
+ first = public_send(self.class::FETCH1K)
370
+ second = public_send(self.class::FETCH2K)
371
+ if first.is_a?(Record)
372
+ first.fetch(selector) { second.fetch(selector) }
373
+ elsif second.is_a?(Record)
374
+ fetch_second_record(first, second, selector)
375
+ else
376
+ super
377
+ end
378
+ end
379
+ end
380
+
274
381
  class Or < Operator; end
275
382
  class And < Operator; end
276
383
  class Equal < Operator; end
@@ -279,17 +386,27 @@ module Dhall
279
386
  class Times < Operator; end
280
387
  class TextConcatenate < Operator; end
281
388
  class ListConcatenate < Operator; end
282
- class RecursiveRecordMerge < Operator; end
283
- class RightBiasedRecordMerge < Operator; end
389
+ class RecursiveRecordMerge < Operator
390
+ FETCH1K = :lhs
391
+ FETCH2K = :rhs
392
+ include FetchFromMerge
393
+ end
394
+ class RightBiasedRecordMerge < Operator
395
+ FETCH1K = :rhs
396
+ FETCH2K = :lhs
397
+ include FetchFromMerge
398
+ end
284
399
  class RecursiveRecordTypeMerge < Operator; end
285
400
  class ImportFallback < Operator; end
401
+ class Equivalent < Operator; end
286
402
 
287
403
  OPERATORS = [
288
404
  Or, And, Equal, NotEqual,
289
405
  Plus, Times,
290
406
  TextConcatenate, ListConcatenate,
291
407
  RecursiveRecordMerge, RightBiasedRecordMerge, RecursiveRecordTypeMerge,
292
- ImportFallback
408
+ ImportFallback,
409
+ Equivalent
293
410
  ].freeze
294
411
  end
295
412
 
@@ -298,9 +415,18 @@ module Dhall
298
415
 
299
416
  include(ValueSemantics.for_attributes do
300
417
  elements Util::ArrayOf.new(Expression, min: 1)
301
- element_type Either(nil, Expression), default: nil
418
+ type Either(nil, Expression), default: nil
302
419
  end)
303
420
 
421
+ def initialize(attrs)
422
+ if attrs.key?(:element_type)
423
+ et = attrs.delete(:element_type)
424
+ attrs[:type] = self.class.as_dhall.call(et) if et
425
+ end
426
+
427
+ super
428
+ end
429
+
304
430
  def self.of(*args, type: nil)
305
431
  if args.empty?
306
432
  EmptyList.new(element_type: type)
@@ -309,11 +435,17 @@ module Dhall
309
435
  end
310
436
  end
311
437
 
312
- def type
313
- Dhall::Application.new(
314
- function: Dhall::Variable["List"],
315
- argument: element_type
316
- )
438
+ def self.as_dhall
439
+ Builtins[:List]
440
+ end
441
+
442
+ def element_type
443
+ if type.nil?
444
+ elsif type.is_a?(Application) && type.function == Builtins[:List]
445
+ type.argument
446
+ else
447
+ raise "Cannot get element_type of: #{type.inspect}"
448
+ end
317
449
  end
318
450
 
319
451
  def as_json
@@ -321,7 +453,11 @@ module Dhall
321
453
  end
322
454
 
323
455
  def map(type: nil, &block)
324
- with(elements: elements.each_with_index.map(&block), element_type: type)
456
+ type = type.nil? ? nil : Builtins[:List].call(type.as_dhall)
457
+ with(
458
+ elements: elements.each_with_index.map(&block),
459
+ type: type
460
+ )
325
461
  end
326
462
 
327
463
  def each(&block)
@@ -368,11 +504,22 @@ module Dhall
368
504
 
369
505
  class EmptyList < List
370
506
  include(ValueSemantics.for_attributes do
371
- element_type Either(nil, Expression)
507
+ type Either(nil, Expression)
372
508
  end)
373
509
 
510
+ def initialize(attrs)
511
+ if attrs.key?(:element_type)
512
+ et = attrs.delete(:element_type)
513
+ attrs[:type] = self.class.as_dhall.call(et) if et
514
+ end
515
+
516
+ super
517
+ end
518
+
374
519
  def as_json
375
520
  [4, element_type.as_json]
521
+ rescue
522
+ [28, type.as_json]
376
523
  end
377
524
 
378
525
  def map(type: nil)
@@ -407,6 +554,10 @@ module Dhall
407
554
  self
408
555
  end
409
556
 
557
+ def join(*)
558
+ ""
559
+ end
560
+
410
561
  def concat(other)
411
562
  other
412
563
  end
@@ -426,6 +577,10 @@ module Dhall
426
577
  end
427
578
  end
428
579
 
580
+ def self.as_dhall
581
+ Builtins[:Natural]
582
+ end
583
+
429
584
  def initialize(normalized: false, **attrs)
430
585
  @normalized = normalized
431
586
  super(**attrs)
@@ -435,7 +590,7 @@ module Dhall
435
590
  return unless value_type
436
591
 
437
592
  Dhall::Application.new(
438
- function: Dhall::Variable["Optional"],
593
+ function: Builtins[:Optional],
439
594
  argument: value_type
440
595
  )
441
596
  end
@@ -462,6 +617,10 @@ module Dhall
462
617
  value_type Expression
463
618
  end)
464
619
 
620
+ def self.as_dhall
621
+ Builtins[:None]
622
+ end
623
+
465
624
  def map(type: nil)
466
625
  type.nil? ? self : with(value_type: type)
467
626
  end
@@ -475,7 +634,10 @@ module Dhall
475
634
  end
476
635
 
477
636
  def as_json
478
- [0, Variable["None"].as_json, value_type.as_json]
637
+ Application.new(
638
+ function: self.class.as_dhall,
639
+ argument: value_type
640
+ ).as_json
479
641
  end
480
642
  end
481
643
 
@@ -483,7 +645,7 @@ module Dhall
483
645
  include(ValueSemantics.for_attributes do
484
646
  record Expression
485
647
  input Expression
486
- type Either(Expression, nil)
648
+ type Either(Expression, nil), default: nil
487
649
  end)
488
650
 
489
651
  def as_json
@@ -492,6 +654,18 @@ module Dhall
492
654
  end
493
655
  end
494
656
 
657
+ class ToMap < Expression
658
+ include(ValueSemantics.for_attributes do
659
+ record Expression
660
+ type Either(Expression, nil), default: nil
661
+ end)
662
+
663
+ def as_json
664
+ [27, record.as_json] +
665
+ (type.nil? ? [] : [type.as_json])
666
+ end
667
+ end
668
+
495
669
  class RecordType < Expression
496
670
  include(ValueSemantics.for_attributes do
497
671
  record Util::HashOf.new(::String, Expression, min: 1)
@@ -729,11 +903,38 @@ module Dhall
729
903
  selectors Util::ArrayOf.new(::String, min: 1)
730
904
  end)
731
905
 
906
+ def self.for(record, selectors)
907
+ if selectors.empty?
908
+ EmptyRecordProjection.new(record: record)
909
+ else
910
+ new(record: record, selectors: selectors)
911
+ end
912
+ end
913
+
914
+ def fetch(selector)
915
+ record.fetch(selector)
916
+ end
917
+
732
918
  def as_json
733
919
  [10, record.as_json, *selectors]
734
920
  end
735
921
  end
736
922
 
923
+ class RecordProjectionByExpression < Expression
924
+ include(ValueSemantics.for_attributes do
925
+ record Expression
926
+ selector Expression
927
+ end)
928
+
929
+ def fetch(selector)
930
+ record.fetch(selector)
931
+ end
932
+
933
+ def as_json
934
+ [10, record.as_json, [selector.as_json]]
935
+ end
936
+ end
937
+
737
938
  class EmptyRecordProjection < Expression
738
939
  include(ValueSemantics.for_attributes do
739
940
  record Expression
@@ -750,9 +951,22 @@ module Dhall
750
951
 
751
952
  class UnionType < Expression
752
953
  include(ValueSemantics.for_attributes do
753
- alternatives Util::HashOf.new(::String, Either(Expression, nil))
954
+ alternatives Util::HashOf.new(::String, Either(Expression, nil)), default: {}
754
955
  end)
755
956
 
957
+ def empty?
958
+ alternatives.empty?
959
+ end
960
+
961
+ def [](k)
962
+ alternatives.fetch(k)
963
+ end
964
+
965
+ def without(*keys)
966
+ keys.map!(&:to_s)
967
+ with(alternatives: alternatives.reject { |k, _| keys.include?(k) })
968
+ end
969
+
756
970
  def record
757
971
  alternatives
758
972
  end
@@ -765,8 +979,8 @@ module Dhall
765
979
  self == other
766
980
  end
767
981
 
768
- def merge(other)
769
- with(alternatives: alternatives.merge(other.alternatives))
982
+ def merge(other, &block)
983
+ with(alternatives: alternatives.merge(other.alternatives, &block))
770
984
  end
771
985
 
772
986
  def fetch(k, default=nil)
@@ -780,10 +994,9 @@ module Dhall
780
994
  end
781
995
 
782
996
  def get_constructor(selector)
783
- var = Util::BuiltinName === selector ? "_" : selector
784
997
  type = alternatives.fetch(selector)
785
- body = Union.from(self, selector, Variable[var])
786
- Function.new(var: var, type: type, body: body)
998
+ body = Union.from(self, selector, Variable[selector])
999
+ Function.new(var: selector, type: type, body: body)
787
1000
  end
788
1001
 
789
1002
  def constructor_types
@@ -791,8 +1004,7 @@ module Dhall
791
1004
  ctypes[k] = if type.nil?
792
1005
  self
793
1006
  else
794
- var = Util::BuiltinName === k ? "_" : k
795
- Forall.new(var: var, type: type, body: self)
1007
+ Forall.new(var: k, type: type, body: self)
796
1008
  end
797
1009
  end
798
1010
  end
@@ -805,31 +1017,28 @@ module Dhall
805
1017
  class Union < Expression
806
1018
  include(ValueSemantics.for_attributes do
807
1019
  tag ::String
808
- value Either(Expression, nil)
1020
+ value Expression
809
1021
  alternatives UnionType
810
1022
  end)
811
1023
 
812
1024
  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 }
1025
+ if value.nil?
1026
+ Enum.new(tag: tag, alternatives: alts.without(tag))
1027
+ else
1028
+ new(
1029
+ tag: tag,
1030
+ value: TypeAnnotation.new(value: value, type: alts[tag]),
1031
+ alternatives: alts.without(tag)
821
1032
  )
822
- )
1033
+ end
823
1034
  end
824
1035
 
825
1036
  def to_s
826
- value.nil? ? tag : extract.to_s
1037
+ extract.to_s
827
1038
  end
828
1039
 
829
1040
  def extract
830
- if value.nil?
831
- tag.to_sym
832
- elsif value.is_a?(TypeAnnotation)
1041
+ if value.is_a?(TypeAnnotation)
833
1042
  value.value
834
1043
  else
835
1044
  value
@@ -839,12 +1048,8 @@ module Dhall
839
1048
  def reduce(handlers)
840
1049
  handlers = handlers.to_h
841
1050
  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
1051
+ (handler.respond_to?(:to_proc) ? handler.to_proc : handler)
1052
+ .call(extract)
848
1053
  end
849
1054
 
850
1055
  def selection_syntax
@@ -857,18 +1062,14 @@ module Dhall
857
1062
  end
858
1063
 
859
1064
  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
1065
+ Application.new(
1066
+ function: selection_syntax,
1067
+ argument: value.is_a?(TypeAnnotation) ? value.value : value
1068
+ )
868
1069
  end
869
1070
 
870
1071
  def as_json
871
- if value.nil? || value.respond_to?(:type)
1072
+ if value.respond_to?(:type)
872
1073
  syntax.as_json
873
1074
  else
874
1075
  [12, tag, value&.as_json, alternatives.as_json.last]
@@ -876,11 +1077,36 @@ module Dhall
876
1077
  end
877
1078
  end
878
1079
 
1080
+ class Enum < Union
1081
+ include(ValueSemantics.for_attributes do
1082
+ tag ::String
1083
+ alternatives UnionType
1084
+ end)
1085
+
1086
+ def reduce(handlers)
1087
+ handlers = handlers.to_h
1088
+ handler = handlers.fetch(tag.to_sym) { handlers.fetch(tag) }
1089
+ handler
1090
+ end
1091
+
1092
+ def to_s
1093
+ tag
1094
+ end
1095
+
1096
+ def extract
1097
+ tag.to_sym
1098
+ end
1099
+
1100
+ def as_json
1101
+ selection_syntax.as_json
1102
+ end
1103
+ end
1104
+
879
1105
  class If < Expression
880
1106
  include(ValueSemantics.for_attributes do
881
1107
  predicate Expression
882
- self.then Expression
883
- self.else Expression
1108
+ def_attr :then, Expression
1109
+ def_attr :else, Expression
884
1110
  end)
885
1111
 
886
1112
  def as_json
@@ -893,6 +1119,10 @@ module Dhall
893
1119
  value (0..Float::INFINITY)
894
1120
  end)
895
1121
 
1122
+ def self.as_dhall
1123
+ Builtins[:Natural]
1124
+ end
1125
+
896
1126
  def coerce(other)
897
1127
  [other.as_dhall, self]
898
1128
  end
@@ -954,6 +1184,10 @@ module Dhall
954
1184
  value ::Integer
955
1185
  end)
956
1186
 
1187
+ def self.as_dhall
1188
+ Builtins[:Integer]
1189
+ end
1190
+
957
1191
  def to_s
958
1192
  "#{value >= 0 ? "+" : ""}#{value}"
959
1193
  end
@@ -976,6 +1210,10 @@ module Dhall
976
1210
  value ::Float
977
1211
  end)
978
1212
 
1213
+ def self.as_dhall
1214
+ Builtins[:Double]
1215
+ end
1216
+
979
1217
  def to_s
980
1218
  value.to_s
981
1219
  end
@@ -1030,12 +1268,16 @@ module Dhall
1030
1268
  value ::String, coerce: ->(s) { s.encode("UTF-8") }
1031
1269
  end)
1032
1270
 
1271
+ def self.as_dhall
1272
+ Builtins[:Text]
1273
+ end
1274
+
1275
+ def empty?
1276
+ value.empty?
1277
+ end
1278
+
1033
1279
  def <<(other)
1034
- if other.is_a?(Text)
1035
- with(value: value + other.value)
1036
- else
1037
- super
1038
- end
1280
+ with(value: value + other.value)
1039
1281
  end
1040
1282
 
1041
1283
  def to_s
@@ -1068,6 +1310,14 @@ module Dhall
1068
1310
  fixed.length == 1 ? fixed.first : new(chunks: fixed)
1069
1311
  end
1070
1312
 
1313
+ def start_empty?
1314
+ chunks.first.empty?
1315
+ end
1316
+
1317
+ def end_empty?
1318
+ chunks.last.empty?
1319
+ end
1320
+
1071
1321
  def as_json
1072
1322
  [18, *chunks.map { |chunk| chunk.is_a?(Text) ? chunk.value : chunk.as_json }]
1073
1323
  end
@@ -1076,89 +1326,90 @@ module Dhall
1076
1326
  class Import < Expression
1077
1327
  class IntegrityCheck
1078
1328
  include(ValueSemantics.for_attributes do
1079
- protocol Either("sha256", :nocheck)
1080
- data Either(::String, nil)
1329
+ code ::Integer
1330
+ digest ::String
1081
1331
  end)
1082
1332
 
1083
1333
  class FailureException < StandardError; end
1084
1334
 
1085
- def initialize(protocol=:nocheck, data=nil)
1086
- super(
1087
- protocol: protocol,
1088
- data: data
1089
- )
1335
+ def to_s
1336
+ "#{Multihashes::TABLE[code].sub(/\Asha2-/, "sha")}:#{hexdigest}"
1090
1337
  end
1091
1338
 
1092
- def to_s
1093
- "#{@protocol}:#{@data}"
1339
+ def hexdigest
1340
+ digest.unpack("H*").first.encode(Encoding::UTF_8)
1094
1341
  end
1095
1342
 
1096
- def check(expr)
1097
- return expr if @protocol == :nocheck
1343
+ def ipfs
1344
+ "/ipfs/b#{Base32.encode("\x01\x55" + as_json).downcase.sub(/=*$/, "")}"
1345
+ end
1098
1346
 
1347
+ def check(expr)
1099
1348
  expr = expr.normalize
1100
1349
  return expr if expr.cache_key == to_s
1101
1350
 
1102
- raise FailureException, "#{expr} does not match #{self}"
1351
+ raise FailureException, "#{expr} hash #{expr.cache_key}" \
1352
+ " does not match #{self}"
1103
1353
  end
1104
1354
 
1105
1355
  def as_json
1106
- @protocol == :nocheck ? nil : [@protocol, @data]
1356
+ Multihashes.encode(digest, Multihashes::TABLE[code])
1107
1357
  end
1108
1358
  end
1109
1359
 
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)
1360
+ class NoIntegrityCheck < IntegrityCheck
1361
+ def initialize; end
1117
1362
 
1118
- HeaderType = RecordType.new(
1119
- record: {
1120
- "header" => Variable["Text"],
1121
- "value" => Variable["Text"]
1122
- }
1123
- )
1363
+ def to_s
1364
+ ""
1365
+ end
1124
1366
 
1125
- def initialize(headers, authority, *path, query)
1126
- super(
1127
- headers: headers,
1128
- authority: authority,
1129
- path: path,
1130
- query: query,
1131
- )
1367
+ def hexdigest; end
1368
+
1369
+ def check(expr)
1370
+ expr.normalize
1132
1371
  end
1133
1372
 
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
- )
1373
+ def as_json
1374
+ nil
1141
1375
  end
1376
+ end
1142
1377
 
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
- )
1378
+ Location = LazyObject.new do
1379
+ UnionType.new(
1380
+ alternatives: {
1381
+ "Local" => Builtins[:Text],
1382
+ "Remote" => Builtins[:Text],
1383
+ "Environment" => Builtins[:Text],
1384
+ "Missing" => nil
1385
+ }
1386
+ )
1387
+ end
1388
+
1389
+ class URI
1390
+ include(ValueSemantics.for_attributes do
1391
+ uri ::URI
1392
+ headers Either(nil, Expression), default: nil
1393
+ end)
1394
+
1395
+ def with(attrs)
1396
+ if attrs.key?(:path)
1397
+ attrs[:uri] =
1398
+ uri + Util.path_components_to_uri(*attrs.delete(:path))
1399
+ end
1400
+
1401
+ super
1151
1402
  end
1152
1403
 
1153
1404
  def headers
1154
- super || EmptyList.new(element_type: HeaderType)
1155
- end
1405
+ header_type = RecordType.new(
1406
+ record: {
1407
+ "mapKey" => Builtins[:Text],
1408
+ "mapValue" => Builtins[:Text]
1409
+ }
1410
+ )
1156
1411
 
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}")
1412
+ super || EmptyList.new(element_type: header_type)
1162
1413
  end
1163
1414
 
1164
1415
  def chain_onto(relative_to)
@@ -1171,23 +1422,43 @@ module Dhall
1171
1422
 
1172
1423
  def canonical
1173
1424
  with(
1174
- path: (path[1..-1] + [""])
1175
- .reduce([[], path.first]) { |(pth, prev), c|
1425
+ path: (path[1..-1] + [""]).reduce([[], path.first]) { |(pth, prev), c|
1176
1426
  c == ".." ? [pth, prev] : [pth + [prev], c]
1177
1427
  }.first.reject { |c| c == "." }
1178
1428
  )
1179
1429
  end
1180
1430
 
1431
+ def port
1432
+ uri.port && uri.port != uri.default_port ? uri.port : nil
1433
+ end
1434
+
1435
+ def authority
1436
+ [
1437
+ uri.userinfo,
1438
+ [uri.host, port].compact.join(":")
1439
+ ].compact.join("@")
1440
+ end
1441
+
1181
1442
  def origin
1182
- "#{scheme}://#{authority}"
1443
+ "#{uri.scheme}://#{authority}"
1183
1444
  end
1184
1445
 
1185
1446
  def to_s
1186
1447
  uri.to_s
1187
1448
  end
1188
1449
 
1450
+ def location
1451
+ Union.from(Location, "Remote", to_s.as_dhall)
1452
+ end
1453
+
1454
+ def path
1455
+ path = uri.path.split(/\//, -1)
1456
+ path = path[1..-1] if path.length > 1 && path.first.empty?
1457
+ path
1458
+ end
1459
+
1189
1460
  def as_json
1190
- [@headers&.as_json, authority, *path, query]
1461
+ [@headers&.as_json, authority, *path, uri.query]
1191
1462
  end
1192
1463
  end
1193
1464
 
@@ -1195,20 +1466,12 @@ module Dhall
1195
1466
  def resolve(resolver)
1196
1467
  resolver.resolve_http(self)
1197
1468
  end
1198
-
1199
- def scheme
1200
- "http"
1201
- end
1202
1469
  end
1203
1470
 
1204
1471
  class Https < URI
1205
1472
  def resolve(resolver)
1206
1473
  resolver.resolve_https(self)
1207
1474
  end
1208
-
1209
- def scheme
1210
- "https"
1211
- end
1212
1475
  end
1213
1476
 
1214
1477
  class Path
@@ -1225,13 +1488,15 @@ module Dhall
1225
1488
  end
1226
1489
 
1227
1490
  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])
1491
+ prefix, *suffix = s.to_s.split(/\//)
1492
+ if prefix == ""
1493
+ AbsolutePath.new(*suffix)
1494
+ elsif prefix == "~"
1495
+ RelativeToHomePath.new(*suffix)
1496
+ elsif prefix == ".."
1497
+ RelativeToParentPath.new(*suffix)
1233
1498
  else
1234
- RelativePath.new(*parts)
1499
+ RelativePath.new(prefix, *suffix)
1235
1500
  end
1236
1501
  end
1237
1502
 
@@ -1251,6 +1516,10 @@ module Dhall
1251
1516
  pathname.to_s
1252
1517
  end
1253
1518
 
1519
+ def location
1520
+ Union.from(Location, "Local", to_s.as_dhall)
1521
+ end
1522
+
1254
1523
  def as_json
1255
1524
  path
1256
1525
  end
@@ -1261,8 +1530,8 @@ module Dhall
1261
1530
  Pathname.new("/").join(*path)
1262
1531
  end
1263
1532
 
1264
- def to_uri(scheme, authority)
1265
- scheme.new(nil, authority, *path, nil)
1533
+ def to_uri(scheme, base_uri)
1534
+ scheme.new(uri: base_uri + Util.path_components_to_uri(*path))
1266
1535
  end
1267
1536
 
1268
1537
  def chain_onto(relative_to)
@@ -1279,6 +1548,10 @@ module Dhall
1279
1548
  Pathname.new(".").join(*path)
1280
1549
  end
1281
1550
 
1551
+ def to_s
1552
+ "./#{pathname}"
1553
+ end
1554
+
1282
1555
  def chain_onto(relative_to)
1283
1556
  relative_to.with(
1284
1557
  path: relative_to.path[0..-2] + path
@@ -1303,7 +1576,7 @@ module Dhall
1303
1576
  Pathname.new("~").join(*@path)
1304
1577
  end
1305
1578
 
1306
- def chain_onto(*)
1579
+ def chain_onto(relative_to)
1307
1580
  if relative_to.is_a?(URI)
1308
1581
  raise ImportBannedException, "remote import cannot import #{self}"
1309
1582
  end
@@ -1313,23 +1586,7 @@ module Dhall
1313
1586
  end
1314
1587
 
1315
1588
  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
1589
+ attr_reader :var
1333
1590
 
1334
1591
  def initialize(var)
1335
1592
  @var = var
@@ -1340,28 +1597,27 @@ module Dhall
1340
1597
  raise ImportBannedException, "remote import cannot import #{self}"
1341
1598
  end
1342
1599
 
1343
- real_path.chain_onto(relative_to)
1600
+ self
1601
+ end
1602
+
1603
+ def path
1604
+ []
1605
+ end
1606
+
1607
+ def with(path:)
1608
+ Path.from_string(path.join("/"))
1344
1609
  end
1345
1610
 
1346
1611
  def canonical
1347
- real_path.canonical
1612
+ self
1348
1613
  end
1349
1614
 
1350
1615
  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
1616
+ self
1359
1617
  end
1360
1618
 
1361
1619
  def resolve(resolver)
1362
- Promise.resolve(nil).then do
1363
- real_path.resolve(resolver)
1364
- end
1620
+ resolver.resolve_environment(self)
1365
1621
  end
1366
1622
 
1367
1623
  def origin
@@ -1369,13 +1625,27 @@ module Dhall
1369
1625
  end
1370
1626
 
1371
1627
  def to_s
1372
- "env:#{as_json}"
1628
+ escapes = Parser::PosixEnvironmentVariableCharacter::ESCAPES
1629
+ "env:#{@var.gsub(/[\"\\\a\b\f\n\r\t\v]/) do |c|
1630
+ "\\" + escapes.find { |(_, v)| v == c }.first
1631
+ end}"
1632
+ end
1633
+
1634
+ def location
1635
+ Union.from(Location, "Environment", to_s.as_dhall)
1373
1636
  end
1374
1637
 
1638
+ def hash
1639
+ @var.hash
1640
+ end
1641
+
1642
+ def eql?(other)
1643
+ other.is_a?(self.class) && other.var == var
1644
+ end
1645
+ alias == eql?
1646
+
1375
1647
  def as_json
1376
- @var.gsub(/[\"\\\a\b\f\n\r\t\v]/) do |c|
1377
- "\\" + ESCAPES.find { |(_, v)| v == c }.first
1378
- end
1648
+ @var
1379
1649
  end
1380
1650
  end
1381
1651
 
@@ -1398,26 +1668,44 @@ module Dhall
1398
1668
  "missing"
1399
1669
  end
1400
1670
 
1671
+ def location
1672
+ Union.from(Location, "Missing", nil)
1673
+ end
1674
+
1675
+ def eql?(other)
1676
+ other.class == self.class
1677
+ end
1678
+ alias == eql?
1679
+
1401
1680
  def as_json
1402
1681
  []
1403
1682
  end
1404
1683
  end
1405
1684
 
1406
1685
  class Expression
1407
- def self.call(import_value)
1408
- Dhall.load_raw(import_value)
1686
+ def self.call(import_value, deadline: Util::NoDeadline.new)
1687
+ return import_value if import_value.is_a?(Dhall::Expression)
1688
+
1689
+ Dhall.load_raw(import_value, timeout: deadline.timeout)
1409
1690
  end
1410
1691
  end
1411
1692
 
1412
1693
  class Text
1413
- def self.call(import_value)
1694
+ def self.call(import_value, deadline: Util::NoDeadline.new)
1414
1695
  Dhall::Text.new(value: import_value)
1415
1696
  end
1416
1697
  end
1417
1698
 
1699
+ class AsLocation
1700
+ def self.call(*)
1701
+ raise "AsLocation is only a marker, you don't actually call it"
1702
+ end
1703
+ end
1704
+
1418
1705
  IMPORT_TYPES = [
1419
1706
  Expression,
1420
- Text
1707
+ Text,
1708
+ AsLocation
1421
1709
  ].freeze
1422
1710
 
1423
1711
  PATH_TYPES = [
@@ -1427,14 +1715,14 @@ module Dhall
1427
1715
  ].freeze
1428
1716
 
1429
1717
  include(ValueSemantics.for_attributes do
1430
- integrity_check IntegrityCheck, default: IntegrityCheck.new
1718
+ integrity_check IntegrityCheck, default: NoIntegrityCheck.new
1431
1719
  import_type Class
1432
1720
  path Either(*PATH_TYPES)
1433
1721
  end)
1434
1722
 
1435
1723
  def initialize(integrity_check, import_type, path)
1436
1724
  super(
1437
- integrity_check: integrity_check || IntegrityCheck.new,
1725
+ integrity_check: integrity_check || NoIntegrityCheck.new,
1438
1726
  import_type: import_type,
1439
1727
  path: path
1440
1728
  )
@@ -1452,15 +1740,18 @@ module Dhall
1452
1740
  path.chain_onto(relative_to).canonical
1453
1741
  end
1454
1742
 
1455
- def parse_and_check(raw)
1456
- integrity_check.check(import_type.call(raw))
1743
+ def parse_resolve_check(raw, deadline: Util::NoDeadline.new, **kwargs)
1744
+ import_type.call(raw, deadline: deadline).resolve(**kwargs).then do |e|
1745
+ integrity_check.check(TypeChecker.annotate(e))
1746
+ end
1457
1747
  end
1458
1748
 
1459
1749
  def cache_key(relative_to)
1460
- if integrity_check.protocol == :nocheck
1461
- real_path(relative_to).to_s
1750
+ key = integrity_check.to_s
1751
+ if key.empty?
1752
+ real_path(relative_to)
1462
1753
  else
1463
- integrity_check.to_s
1754
+ key
1464
1755
  end
1465
1756
  end
1466
1757
 
@@ -1477,7 +1768,7 @@ module Dhall
1477
1768
 
1478
1769
  class Let < Expression
1479
1770
  include(ValueSemantics.for_attributes do
1480
- var Util::AllOf.new(::String, Util::Not.new(Util::BuiltinName))
1771
+ var ::String
1481
1772
  assign Expression
1482
1773
  type Either(nil, Expression)
1483
1774
  end)
@@ -1493,6 +1784,19 @@ module Dhall
1493
1784
  body Expression
1494
1785
  end)
1495
1786
 
1787
+ def lets
1788
+ [let]
1789
+ end
1790
+
1791
+ def flatten
1792
+ flattened = body.is_a?(LetIn) ? body.flatten : body
1793
+ if flattened.is_a?(LetBlock)
1794
+ LetBlock.new(lets: lets + flattened.lets, body: flattened.body)
1795
+ else
1796
+ LetBlock.new(lets: lets, body: body)
1797
+ end
1798
+ end
1799
+
1496
1800
  def desugar
1497
1801
  Application.new(
1498
1802
  function: Function.new(
@@ -1512,24 +1816,16 @@ module Dhall
1512
1816
  end
1513
1817
 
1514
1818
  def as_json
1515
- [25, *let.as_json, body.as_json]
1819
+ flatten.as_json
1516
1820
  end
1517
1821
  end
1518
1822
 
1519
- class LetBlock < Expression
1823
+ class LetBlock
1520
1824
  include(ValueSemantics.for_attributes do
1521
- lets Util::ArrayOf.new(Let, min: 2)
1825
+ lets Util::ArrayOf.new(Let)
1522
1826
  body Expression
1523
1827
  end)
1524
1828
 
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
1829
  def unflatten
1534
1830
  lets.reverse.reduce(body) do |inside, let|
1535
1831
  letin = LetIn.new(let: let, body: inside)
@@ -1537,10 +1833,6 @@ module Dhall
1537
1833
  end
1538
1834
  end
1539
1835
 
1540
- def desugar
1541
- unflatten(&:desugar)
1542
- end
1543
-
1544
1836
  def as_json
1545
1837
  [25, *lets.flat_map(&:as_json), body.as_json]
1546
1838
  end
@@ -1556,4 +1848,14 @@ module Dhall
1556
1848
  [26, value.as_json, type.as_json]
1557
1849
  end
1558
1850
  end
1851
+
1852
+ class Assertion < Expression
1853
+ include(ValueSemantics.for_attributes do
1854
+ type Expression
1855
+ end)
1856
+
1857
+ def as_json
1858
+ [19, type.as_json]
1859
+ end
1860
+ end
1559
1861
  end