dhall 0.3.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 30ff6e0ad066b23996b9a31b21e7f1a30b86b265
4
- data.tar.gz: 88efb36ee05894a74df3b691d67774b8e0eea185
2
+ SHA256:
3
+ metadata.gz: c6ba874fec536af3a57ea4a0ca59f040076ebc03ed639dcd44a5a2b79e8bf252
4
+ data.tar.gz: 4408343df3c63a8c96f6e191f3109a2110ae4b6f3ab06aee58c15b2e7439f6ef
5
5
  SHA512:
6
- metadata.gz: ff197049f1d4ceb3e1742a4e3b44491192170c4bb1e32b7aef6bbaeb643f16b828ffdee1d78eaa628a9f0dd9d5dedd0f4c796556b2e72b92ed0705a0e8306430
7
- data.tar.gz: e92aad6b3921f4bd25ebdf8f20204b95ceec8814250ed90b5424b70a75991ca68de8bd60b0c2cf982541c560f9e1ba135f87f0b05f685d767588150b051c465f
6
+ metadata.gz: e3fdb1ed15e039261aedfc608d612eb4739b7a0204aa26524d66e8999d14ed71cc4e2b5964077f88cf860a99454548c7c2aae2188de9a9687fb173e780441fe4
7
+ data.tar.gz: 0b9b101c0ccaec5d6be7bbfab0a3698c1eb81f91c06ba9efe663391ce56e8e71613fb0f4cd37af12cc68e7973278fcd9f47190d80a0369c14230a3e0480cdda1
data/bin/dhall-compile CHANGED
@@ -13,19 +13,43 @@ def compile(source)
13
13
  resolver: Dhall::Resolvers::Default.new(
14
14
  max_depth: Float::INFINITY
15
15
  )
16
- ).then(&:to_binary)
16
+ )
17
+ end
18
+
19
+ module FilenameWriter
20
+ def self.write(_, out, dhall)
21
+ warn out
22
+ out.dirname.mkpath
23
+ out.write(dhall.to_binary)
24
+ end
25
+ end
26
+
27
+ module CacheWriter
28
+ def self.write(output_directory, out, dhall)
29
+ base = "1220#{dhall.digest.hexdigest}"
30
+ out = out.dirname + base
31
+ if output_directory
32
+ out = output_directory + base
33
+ out.dirname.mkpath
34
+ end
35
+ warn out
36
+ out.write(dhall.to_cbor)
37
+ end
17
38
  end
18
39
 
19
40
  def compile_file(file_path, relative_to: Pathname.new("."))
41
+ $stderr.print "#{file_path} => "
20
42
  out = file_path.sub_ext(@extension)
21
43
  if @output_directory
22
44
  out = @output_directory + out.relative_path_from(relative_to)
23
- out.dirname.mkpath
24
45
  end
25
- warn "#{file_path} => #{out}"
26
- compile(file_path.expand_path).then(&out.method(:write))
46
+ compile(file_path.expand_path).then do |dhall|
47
+ @writer.write(@output_directory, out, dhall)
48
+ end
27
49
  end
28
50
 
51
+ @writer = FilenameWriter
52
+ # rubocop:disable Metrics/BlockLength
29
53
  opt_parser = OptionParser.new do |opts|
30
54
  opts.banner = "Usage: dhall-compile [options] [-] [files_and_dirs]"
31
55
 
@@ -45,11 +69,21 @@ opt_parser = OptionParser.new do |opts|
45
69
  @extension = ext ? ".#{ext}" : ""
46
70
  end
47
71
 
72
+ opts.on(
73
+ "-c",
74
+ "--cache",
75
+ "Write output in standard dhall file cache format"
76
+ ) do
77
+ @extension = ""
78
+ @writer = CacheWriter
79
+ end
80
+
48
81
  opts.on("-h", "--help", "Show this usage information") do
49
82
  warn opts
50
83
  exit
51
84
  end
52
85
  end
86
+ # rubocop:enable Metrics/BlockLength
53
87
 
54
88
  opt_parser.parse!
55
89
 
data/bin/json-to-dhall CHANGED
@@ -5,4 +5,4 @@ require "dhall"
5
5
  require "json"
6
6
  using Dhall::AsDhall
7
7
 
8
- STDOUT.write(CBOR.encode(JSON.parse(STDIN.read).as_dhall))
8
+ STDOUT.write(JSON.parse(STDIN.read).as_dhall.to_binary)
data/bin/yaml-to-dhall CHANGED
@@ -5,4 +5,4 @@ require "dhall"
5
5
  require "yaml"
6
6
  using Dhall::AsDhall
7
7
 
8
- STDOUT.write(CBOR.encode(YAML.safe_load(STDIN.read, [Symbol]).as_dhall))
8
+ STDOUT.write(YAML.safe_load(STDIN.read, [Symbol]).as_dhall.to_binary)
data/dhall.gemspec CHANGED
@@ -25,12 +25,16 @@ Gem::Specification.new do |spec|
25
25
  spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
27
 
28
+ spec.add_dependency "base32", "~> 0.3.2"
28
29
  spec.add_dependency "cbor", "~> 0.5.9.3"
29
30
  spec.add_dependency "citrus", "~> 3.0"
31
+ spec.add_dependency "lazy_object", "~> 0.0.3"
32
+ spec.add_dependency "multihashes", "~> 0.2.0"
30
33
  spec.add_dependency "promise.rb", "~> 0.7.4"
31
34
  spec.add_dependency "value_semantics", "~> 3.0"
32
35
 
33
36
  spec.add_development_dependency "abnf", "~> 0.0.1"
37
+ spec.add_development_dependency "minitest-fail-fast", "~> 0.1.0"
34
38
  spec.add_development_dependency "simplecov", "~> 0.16.1"
35
39
  spec.add_development_dependency "webmock", "~> 3.5"
36
40
  end
@@ -284,5 +284,11 @@ module Dhall
284
284
  FunctionProxy.new(self)
285
285
  end
286
286
  end
287
+
288
+ refine ::Method do
289
+ def as_dhall
290
+ to_proc.as_dhall
291
+ end
292
+ end
287
293
  end
288
294
  end
data/lib/dhall/ast.rb CHANGED
@@ -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,8 +36,7 @@ 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)
@@ -68,7 +70,7 @@ module Dhall
68
70
  def dhall_eq(other)
69
71
  if self == other
70
72
  Bool.new(value: true)
71
- elsif other.is_a?(Bool)
73
+ elsif other == Bool.new(value: true)
72
74
  other.dhall_eq(self)
73
75
  else
74
76
  Operator::Equal.new(lhs: self, rhs: other)
@@ -102,6 +104,10 @@ module Dhall
102
104
  end
103
105
  end
104
106
 
107
+ def annotate(type)
108
+ TypeAnnotation.new(value: self, type: type)
109
+ end
110
+
105
111
  def to_s
106
112
  inspect
107
113
  end
@@ -128,7 +134,7 @@ module Dhall
128
134
  def flatten
129
135
  f, args = if function.is_a?(Application)
130
136
  function.flatten
131
- elsif function.is_a?(Builtin) &&
137
+ elsif function.is_a?(BuiltinFunction) &&
132
138
  (unfilled = function.unfill).is_a?(Application)
133
139
  unfilled.flatten
134
140
  else
@@ -216,7 +222,29 @@ module Dhall
216
222
  end
217
223
  end
218
224
 
219
- class FunctionProxy < Function
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
220
248
  def initialize(callable, curry: true)
221
249
  @callable = if !curry
222
250
  callable
@@ -230,7 +258,7 @@ module Dhall
230
258
  end
231
259
 
232
260
  def call(*args, &block)
233
- @callable.call(*args.map { |arg| arg&.as_dhall }, &block).as_dhall
261
+ RubyObjectRaw.new(@callable.call(*args.map { |arg| arg&.as_dhall }, &block))
234
262
  end
235
263
 
236
264
  def as_json
@@ -238,6 +266,12 @@ module Dhall
238
266
  end
239
267
  end
240
268
 
269
+ class FunctionProxy < FunctionProxyRaw
270
+ def call(*args, &block)
271
+ super.unwrap.as_dhall
272
+ end
273
+ end
274
+
241
275
  class Bool < Expression
242
276
  include(ValueSemantics.for_attributes do
243
277
  value Bool()
@@ -317,6 +351,33 @@ module Dhall
317
351
  [3, OPERATORS.index(self.class), lhs.as_json, rhs.as_json]
318
352
  end
319
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
+
320
381
  class Or < Operator; end
321
382
  class And < Operator; end
322
383
  class Equal < Operator; end
@@ -325,17 +386,27 @@ module Dhall
325
386
  class Times < Operator; end
326
387
  class TextConcatenate < Operator; end
327
388
  class ListConcatenate < Operator; end
328
- class RecursiveRecordMerge < Operator; end
329
- 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
330
399
  class RecursiveRecordTypeMerge < Operator; end
331
400
  class ImportFallback < Operator; end
401
+ class Equivalent < Operator; end
332
402
 
333
403
  OPERATORS = [
334
404
  Or, And, Equal, NotEqual,
335
405
  Plus, Times,
336
406
  TextConcatenate, ListConcatenate,
337
407
  RecursiveRecordMerge, RightBiasedRecordMerge, RecursiveRecordTypeMerge,
338
- ImportFallback
408
+ ImportFallback,
409
+ Equivalent
339
410
  ].freeze
340
411
  end
341
412
 
@@ -344,9 +415,18 @@ module Dhall
344
415
 
345
416
  include(ValueSemantics.for_attributes do
346
417
  elements Util::ArrayOf.new(Expression, min: 1)
347
- element_type Either(nil, Expression), default: nil
418
+ type Either(nil, Expression), default: nil
348
419
  end)
349
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
+
350
430
  def self.of(*args, type: nil)
351
431
  if args.empty?
352
432
  EmptyList.new(element_type: type)
@@ -359,11 +439,13 @@ module Dhall
359
439
  Builtins[:List]
360
440
  end
361
441
 
362
- def type
363
- Dhall::Application.new(
364
- function: self.class.as_dhall,
365
- argument: element_type
366
- )
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
367
449
  end
368
450
 
369
451
  def as_json
@@ -371,9 +453,10 @@ module Dhall
371
453
  end
372
454
 
373
455
  def map(type: nil, &block)
456
+ type = type.nil? ? nil : Builtins[:List].call(type.as_dhall)
374
457
  with(
375
- elements: elements.each_with_index.map(&block),
376
- element_type: type&.as_dhall
458
+ elements: elements.each_with_index.map(&block),
459
+ type: type
377
460
  )
378
461
  end
379
462
 
@@ -421,11 +504,22 @@ module Dhall
421
504
 
422
505
  class EmptyList < List
423
506
  include(ValueSemantics.for_attributes do
424
- element_type Either(nil, Expression)
507
+ type Either(nil, Expression)
425
508
  end)
426
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
+
427
519
  def as_json
428
520
  [4, element_type.as_json]
521
+ rescue
522
+ [28, type.as_json]
429
523
  end
430
524
 
431
525
  def map(type: nil)
@@ -460,6 +554,10 @@ module Dhall
460
554
  self
461
555
  end
462
556
 
557
+ def join(*)
558
+ ""
559
+ end
560
+
463
561
  def concat(other)
464
562
  other
465
563
  end
@@ -547,7 +645,7 @@ module Dhall
547
645
  include(ValueSemantics.for_attributes do
548
646
  record Expression
549
647
  input Expression
550
- type Either(Expression, nil)
648
+ type Either(Expression, nil), default: nil
551
649
  end)
552
650
 
553
651
  def as_json
@@ -556,6 +654,18 @@ module Dhall
556
654
  end
557
655
  end
558
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
+
559
669
  class RecordType < Expression
560
670
  include(ValueSemantics.for_attributes do
561
671
  record Util::HashOf.new(::String, Expression, min: 1)
@@ -793,11 +903,38 @@ module Dhall
793
903
  selectors Util::ArrayOf.new(::String, min: 1)
794
904
  end)
795
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
+
796
918
  def as_json
797
919
  [10, record.as_json, *selectors]
798
920
  end
799
921
  end
800
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
+
801
938
  class EmptyRecordProjection < Expression
802
939
  include(ValueSemantics.for_attributes do
803
940
  record Expression
@@ -968,8 +1105,8 @@ module Dhall
968
1105
  class If < Expression
969
1106
  include(ValueSemantics.for_attributes do
970
1107
  predicate Expression
971
- self.then Expression
972
- self.else Expression
1108
+ def_attr :then, Expression
1109
+ def_attr :else, Expression
973
1110
  end)
974
1111
 
975
1112
  def as_json
@@ -1189,91 +1326,92 @@ module Dhall
1189
1326
  class Import < Expression
1190
1327
  class IntegrityCheck
1191
1328
  include(ValueSemantics.for_attributes do
1192
- protocol Either("sha256", :nocheck)
1193
- data Either(::String, nil)
1329
+ code ::Integer
1330
+ digest ::String
1194
1331
  end)
1195
1332
 
1196
1333
  class FailureException < StandardError; end
1197
1334
 
1198
- def initialize(protocol=:nocheck, data=nil)
1199
- super(
1200
- protocol: protocol,
1201
- data: data
1202
- )
1335
+ def to_s
1336
+ "#{Multihashes::TABLE[code].sub(/\Asha2-/, "sha")}:#{hexdigest}"
1203
1337
  end
1204
1338
 
1205
- def to_s
1206
- "#{@protocol}:#{@data}"
1339
+ def hexdigest
1340
+ digest.unpack("H*").first.encode(Encoding::UTF_8)
1207
1341
  end
1208
1342
 
1209
- def check(expr)
1210
- return expr if @protocol == :nocheck
1343
+ def ipfs
1344
+ "/ipfs/b#{Base32.encode("\x01\x55" + as_json).downcase.sub(/=*$/, "")}"
1345
+ end
1211
1346
 
1347
+ def check(expr)
1212
1348
  expr = expr.normalize
1213
1349
  return expr if expr.cache_key == to_s
1214
1350
 
1215
- raise FailureException, "#{expr} does not match #{self}"
1351
+ raise FailureException, "#{expr} hash #{expr.cache_key}" \
1352
+ " does not match #{self}"
1216
1353
  end
1217
1354
 
1218
1355
  def as_json
1219
- @protocol == :nocheck ? nil : [@protocol, @data]
1356
+ Multihashes.encode(digest, Multihashes::TABLE[code])
1220
1357
  end
1221
1358
  end
1222
1359
 
1223
- class URI
1224
- include(ValueSemantics.for_attributes do
1225
- headers Either(nil, Expression)
1226
- authority ::String
1227
- path ArrayOf(::String)
1228
- query Either(nil, ::String)
1229
- end)
1360
+ class NoIntegrityCheck < IntegrityCheck
1361
+ def initialize; end
1230
1362
 
1231
- def initialize(headers, authority, *path, query)
1232
- super(
1233
- headers: headers,
1234
- authority: authority,
1235
- path: path,
1236
- query: query,
1237
- )
1363
+ def to_s
1364
+ ""
1238
1365
  end
1239
1366
 
1240
- def with(hash)
1241
- self.class.new(
1242
- hash.fetch(:headers, headers),
1243
- hash.fetch(:authority, authority),
1244
- *hash.fetch(:path, path),
1245
- hash.fetch(:query, query)
1246
- )
1367
+ def hexdigest; end
1368
+
1369
+ def check(expr)
1370
+ expr.normalize
1247
1371
  end
1248
1372
 
1249
- def self.from_uri(uri)
1250
- (uri.scheme == "https" ? Https : Http).new(
1251
- nil,
1252
- "#{uri.host}:#{uri.port}",
1253
- *uri.path.split(/\//)[1..-1],
1254
- uri.query,
1255
- nil
1256
- )
1373
+ def as_json
1374
+ nil
1375
+ end
1376
+ end
1377
+
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
1257
1402
  end
1258
1403
 
1259
1404
  def headers
1260
1405
  header_type = RecordType.new(
1261
1406
  record: {
1262
- "header" => Builtins[:Text],
1263
- "value" => Builtins[:Text]
1407
+ "mapKey" => Builtins[:Text],
1408
+ "mapValue" => Builtins[:Text]
1264
1409
  }
1265
1410
  )
1266
1411
 
1267
1412
  super || EmptyList.new(element_type: header_type)
1268
1413
  end
1269
1414
 
1270
- def uri
1271
- escaped_path = path.map do |c|
1272
- ::URI.encode_www_form_component(c).gsub("+", "%20")
1273
- end
1274
- URI("#{scheme}://#{authority}/#{escaped_path.join("/")}?#{query}")
1275
- end
1276
-
1277
1415
  def chain_onto(relative_to)
1278
1416
  if headers.is_a?(Import)
1279
1417
  with(headers: headers.with(path: headers.real_path(relative_to)))
@@ -1284,23 +1422,43 @@ module Dhall
1284
1422
 
1285
1423
  def canonical
1286
1424
  with(
1287
- path: (path[1..-1] + [""])
1288
- .reduce([[], path.first]) { |(pth, prev), c|
1425
+ path: (path[1..-1] + [""]).reduce([[], path.first]) { |(pth, prev), c|
1289
1426
  c == ".." ? [pth, prev] : [pth + [prev], c]
1290
1427
  }.first.reject { |c| c == "." }
1291
1428
  )
1292
1429
  end
1293
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
+
1294
1442
  def origin
1295
- "#{scheme}://#{authority}"
1443
+ "#{uri.scheme}://#{authority}"
1296
1444
  end
1297
1445
 
1298
1446
  def to_s
1299
1447
  uri.to_s
1300
1448
  end
1301
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
+
1302
1460
  def as_json
1303
- [@headers&.as_json, authority, *path, query]
1461
+ [@headers&.as_json, authority, *path, uri.query]
1304
1462
  end
1305
1463
  end
1306
1464
 
@@ -1308,20 +1466,12 @@ module Dhall
1308
1466
  def resolve(resolver)
1309
1467
  resolver.resolve_http(self)
1310
1468
  end
1311
-
1312
- def scheme
1313
- "http"
1314
- end
1315
1469
  end
1316
1470
 
1317
1471
  class Https < URI
1318
1472
  def resolve(resolver)
1319
1473
  resolver.resolve_https(self)
1320
1474
  end
1321
-
1322
- def scheme
1323
- "https"
1324
- end
1325
1475
  end
1326
1476
 
1327
1477
  class Path
@@ -1366,6 +1516,10 @@ module Dhall
1366
1516
  pathname.to_s
1367
1517
  end
1368
1518
 
1519
+ def location
1520
+ Union.from(Location, "Local", to_s.as_dhall)
1521
+ end
1522
+
1369
1523
  def as_json
1370
1524
  path
1371
1525
  end
@@ -1376,8 +1530,8 @@ module Dhall
1376
1530
  Pathname.new("/").join(*path)
1377
1531
  end
1378
1532
 
1379
- def to_uri(scheme, authority)
1380
- 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))
1381
1535
  end
1382
1536
 
1383
1537
  def chain_onto(relative_to)
@@ -1394,6 +1548,10 @@ module Dhall
1394
1548
  Pathname.new(".").join(*path)
1395
1549
  end
1396
1550
 
1551
+ def to_s
1552
+ "./#{pathname}"
1553
+ end
1554
+
1397
1555
  def chain_onto(relative_to)
1398
1556
  relative_to.with(
1399
1557
  path: relative_to.path[0..-2] + path
@@ -1473,6 +1631,10 @@ module Dhall
1473
1631
  end}"
1474
1632
  end
1475
1633
 
1634
+ def location
1635
+ Union.from(Location, "Environment", to_s.as_dhall)
1636
+ end
1637
+
1476
1638
  def hash
1477
1639
  @var.hash
1478
1640
  end
@@ -1480,7 +1642,7 @@ module Dhall
1480
1642
  def eql?(other)
1481
1643
  other.is_a?(self.class) && other.var == var
1482
1644
  end
1483
- alias eql? ==
1645
+ alias == eql?
1484
1646
 
1485
1647
  def as_json
1486
1648
  @var
@@ -1506,6 +1668,15 @@ module Dhall
1506
1668
  "missing"
1507
1669
  end
1508
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
+
1509
1680
  def as_json
1510
1681
  []
1511
1682
  end
@@ -1525,9 +1696,16 @@ module Dhall
1525
1696
  end
1526
1697
  end
1527
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
+
1528
1705
  IMPORT_TYPES = [
1529
1706
  Expression,
1530
- Text
1707
+ Text,
1708
+ AsLocation
1531
1709
  ].freeze
1532
1710
 
1533
1711
  PATH_TYPES = [
@@ -1537,14 +1715,14 @@ module Dhall
1537
1715
  ].freeze
1538
1716
 
1539
1717
  include(ValueSemantics.for_attributes do
1540
- integrity_check IntegrityCheck, default: IntegrityCheck.new
1718
+ integrity_check IntegrityCheck, default: NoIntegrityCheck.new
1541
1719
  import_type Class
1542
1720
  path Either(*PATH_TYPES)
1543
1721
  end)
1544
1722
 
1545
1723
  def initialize(integrity_check, import_type, path)
1546
1724
  super(
1547
- integrity_check: integrity_check || IntegrityCheck.new,
1725
+ integrity_check: integrity_check || NoIntegrityCheck.new,
1548
1726
  import_type: import_type,
1549
1727
  path: path
1550
1728
  )
@@ -1562,15 +1740,18 @@ module Dhall
1562
1740
  path.chain_onto(relative_to).canonical
1563
1741
  end
1564
1742
 
1565
- def parse_and_check(raw, deadline: Util::NoDeadline.new)
1566
- integrity_check.check(import_type.call(raw, deadline: deadline))
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
1567
1747
  end
1568
1748
 
1569
1749
  def cache_key(relative_to)
1570
- if integrity_check.protocol == :nocheck
1750
+ key = integrity_check.to_s
1751
+ if key.empty?
1571
1752
  real_path(relative_to)
1572
1753
  else
1573
- integrity_check.to_s
1754
+ key
1574
1755
  end
1575
1756
  end
1576
1757
 
@@ -1609,10 +1790,10 @@ module Dhall
1609
1790
 
1610
1791
  def flatten
1611
1792
  flattened = body.is_a?(LetIn) ? body.flatten : body
1612
- if flattened.is_a?(LetIn) || flattened.is_a?(LetBlock)
1613
- LetBlock.for(lets: [let] + flattened.lets, body: flattened.body)
1793
+ if flattened.is_a?(LetBlock)
1794
+ LetBlock.new(lets: lets + flattened.lets, body: flattened.body)
1614
1795
  else
1615
- self
1796
+ LetBlock.new(lets: lets, body: body)
1616
1797
  end
1617
1798
  end
1618
1799
 
@@ -1635,28 +1816,16 @@ module Dhall
1635
1816
  end
1636
1817
 
1637
1818
  def as_json
1638
- [25, *let.as_json, body.as_json]
1819
+ flatten.as_json
1639
1820
  end
1640
1821
  end
1641
1822
 
1642
- class LetBlock < Expression
1823
+ class LetBlock
1643
1824
  include(ValueSemantics.for_attributes do
1644
- lets Util::ArrayOf.new(Let, min: 2)
1825
+ lets Util::ArrayOf.new(Let)
1645
1826
  body Expression
1646
1827
  end)
1647
1828
 
1648
- def self.for(lets:, body:)
1649
- if lets.length == 1
1650
- LetIn.new(let: lets.first, body: body)
1651
- else
1652
- new(lets: lets, body: body)
1653
- end
1654
- end
1655
-
1656
- def flatten
1657
- unflatten.flatten
1658
- end
1659
-
1660
1829
  def unflatten
1661
1830
  lets.reverse.reduce(body) do |inside, let|
1662
1831
  letin = LetIn.new(let: let, body: inside)
@@ -1664,10 +1833,6 @@ module Dhall
1664
1833
  end
1665
1834
  end
1666
1835
 
1667
- def desugar
1668
- unflatten(&:desugar)
1669
- end
1670
-
1671
1836
  def as_json
1672
1837
  [25, *lets.flat_map(&:as_json), body.as_json]
1673
1838
  end
@@ -1683,4 +1848,14 @@ module Dhall
1683
1848
  [26, value.as_json, type.as_json]
1684
1849
  end
1685
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
1686
1861
  end