dhall 0.1.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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