dhall 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30ff6e0ad066b23996b9a31b21e7f1a30b86b265
4
- data.tar.gz: 88efb36ee05894a74df3b691d67774b8e0eea185
3
+ metadata.gz: 55053ce451e1b675998712636c725380329d1fe6
4
+ data.tar.gz: d98038e234871e81f8c0ce3f6d2a8f7c941b8278
5
5
  SHA512:
6
- metadata.gz: ff197049f1d4ceb3e1742a4e3b44491192170c4bb1e32b7aef6bbaeb643f16b828ffdee1d78eaa628a9f0dd9d5dedd0f4c796556b2e72b92ed0705a0e8306430
7
- data.tar.gz: e92aad6b3921f4bd25ebdf8f20204b95ceec8814250ed90b5424b70a75991ca68de8bd60b0c2cf982541c560f9e1ba135f87f0b05f685d767588150b051c465f
6
+ metadata.gz: 4735802d9beb7c796d06ba6c887f5776ac24f12342ad754d911cc36593fb730f0c4d861b653b3667ad1590835014ffcf865e1aa4bd5a56f3c4d9b4e49274d808
7
+ data.tar.gz: 386cda30b6cd93ea7b6236bab460681554240355e25609c4791cb7d85cf6ff07f3b6a3d6b2a3727c2d6d9d8c2123105b4bcf1a03f1cb0272baa2651b4c0c7159
@@ -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
 
@@ -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)
@@ -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)
@@ -25,8 +25,11 @@ 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.1.3"
30
33
  spec.add_dependency "promise.rb", "~> 0.7.4"
31
34
  spec.add_dependency "value_semantics", "~> 3.0"
32
35
 
@@ -9,6 +9,7 @@ require "dhall/normalize"
9
9
  require "dhall/parser"
10
10
  require "dhall/resolve"
11
11
  require "dhall/typecheck"
12
+ require "dhall/types"
12
13
 
13
14
  module Dhall
14
15
  using Dhall::AsDhall
@@ -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
@@ -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)
@@ -128,7 +130,7 @@ module Dhall
128
130
  def flatten
129
131
  f, args = if function.is_a?(Application)
130
132
  function.flatten
131
- elsif function.is_a?(Builtin) &&
133
+ elsif function.is_a?(BuiltinFunction) &&
132
134
  (unfilled = function.unfill).is_a?(Application)
133
135
  unfilled.flatten
134
136
  else
@@ -216,7 +218,29 @@ module Dhall
216
218
  end
217
219
  end
218
220
 
219
- class FunctionProxy < Function
221
+ class RubyObjectRaw < Expression
222
+ def initialize(object)
223
+ @object = object
224
+ end
225
+
226
+ def unwrap
227
+ @object
228
+ end
229
+
230
+ def respond_to_missing?(m)
231
+ super || @object.respond_to?(m)
232
+ end
233
+
234
+ def method_missing(m, *args, &block)
235
+ if @object.respond_to?(m)
236
+ @object.public_send(m, *args, &block)
237
+ else
238
+ super
239
+ end
240
+ end
241
+ end
242
+
243
+ class FunctionProxyRaw < Function
220
244
  def initialize(callable, curry: true)
221
245
  @callable = if !curry
222
246
  callable
@@ -230,7 +254,7 @@ module Dhall
230
254
  end
231
255
 
232
256
  def call(*args, &block)
233
- @callable.call(*args.map { |arg| arg&.as_dhall }, &block).as_dhall
257
+ RubyObjectRaw.new(@callable.call(*args.map { |arg| arg&.as_dhall }, &block))
234
258
  end
235
259
 
236
260
  def as_json
@@ -238,6 +262,12 @@ module Dhall
238
262
  end
239
263
  end
240
264
 
265
+ class FunctionProxy < FunctionProxyRaw
266
+ def call(*args, &block)
267
+ super.unwrap.as_dhall
268
+ end
269
+ end
270
+
241
271
  class Bool < Expression
242
272
  include(ValueSemantics.for_attributes do
243
273
  value Bool()
@@ -344,9 +374,18 @@ module Dhall
344
374
 
345
375
  include(ValueSemantics.for_attributes do
346
376
  elements Util::ArrayOf.new(Expression, min: 1)
347
- element_type Either(nil, Expression), default: nil
377
+ type Either(nil, Expression), default: nil
348
378
  end)
349
379
 
380
+ def initialize(attrs)
381
+ if attrs.key?(:element_type)
382
+ et = attrs.delete(:element_type)
383
+ attrs[:type] = self.class.as_dhall.call(et) if et
384
+ end
385
+
386
+ super
387
+ end
388
+
350
389
  def self.of(*args, type: nil)
351
390
  if args.empty?
352
391
  EmptyList.new(element_type: type)
@@ -359,11 +398,13 @@ module Dhall
359
398
  Builtins[:List]
360
399
  end
361
400
 
362
- def type
363
- Dhall::Application.new(
364
- function: self.class.as_dhall,
365
- argument: element_type
366
- )
401
+ def element_type
402
+ if type.nil?
403
+ elsif type.is_a?(Application) && type.function == Builtins[:List]
404
+ type.argument
405
+ else
406
+ raise "Cannot get element_type of: #{type.inspect}"
407
+ end
367
408
  end
368
409
 
369
410
  def as_json
@@ -371,9 +412,10 @@ module Dhall
371
412
  end
372
413
 
373
414
  def map(type: nil, &block)
415
+ type = type.nil? ? nil : Builtins[:List].call(type.as_dhall)
374
416
  with(
375
- elements: elements.each_with_index.map(&block),
376
- element_type: type&.as_dhall
417
+ elements: elements.each_with_index.map(&block),
418
+ type: type
377
419
  )
378
420
  end
379
421
 
@@ -421,11 +463,22 @@ module Dhall
421
463
 
422
464
  class EmptyList < List
423
465
  include(ValueSemantics.for_attributes do
424
- element_type Either(nil, Expression)
466
+ type Either(nil, Expression)
425
467
  end)
426
468
 
469
+ def initialize(attrs)
470
+ if attrs.key?(:element_type)
471
+ et = attrs.delete(:element_type)
472
+ attrs[:type] = self.class.as_dhall.call(et) if et
473
+ end
474
+
475
+ super
476
+ end
477
+
427
478
  def as_json
428
479
  [4, element_type.as_json]
480
+ rescue
481
+ [28, type.as_json]
429
482
  end
430
483
 
431
484
  def map(type: nil)
@@ -460,6 +513,10 @@ module Dhall
460
513
  self
461
514
  end
462
515
 
516
+ def join(*)
517
+ ""
518
+ end
519
+
463
520
  def concat(other)
464
521
  other
465
522
  end
@@ -547,7 +604,7 @@ module Dhall
547
604
  include(ValueSemantics.for_attributes do
548
605
  record Expression
549
606
  input Expression
550
- type Either(Expression, nil)
607
+ type Either(Expression, nil), default: nil
551
608
  end)
552
609
 
553
610
  def as_json
@@ -556,6 +613,18 @@ module Dhall
556
613
  end
557
614
  end
558
615
 
616
+ class ToMap < Expression
617
+ include(ValueSemantics.for_attributes do
618
+ record Expression
619
+ type Either(Expression, nil), default: nil
620
+ end)
621
+
622
+ def as_json
623
+ [27, record.as_json] +
624
+ (type.nil? ? [] : [type.as_json])
625
+ end
626
+ end
627
+
559
628
  class RecordType < Expression
560
629
  include(ValueSemantics.for_attributes do
561
630
  record Util::HashOf.new(::String, Expression, min: 1)
@@ -793,11 +862,30 @@ module Dhall
793
862
  selectors Util::ArrayOf.new(::String, min: 1)
794
863
  end)
795
864
 
865
+ def self.for(record, selectors)
866
+ if selectors.empty?
867
+ EmptyRecordProjection.new(record: record)
868
+ else
869
+ new(record: record, selectors: selectors)
870
+ end
871
+ end
872
+
796
873
  def as_json
797
874
  [10, record.as_json, *selectors]
798
875
  end
799
876
  end
800
877
 
878
+ class RecordProjectionByExpression < Expression
879
+ include(ValueSemantics.for_attributes do
880
+ record Expression
881
+ selector Expression
882
+ end)
883
+
884
+ def as_json
885
+ [10, record.as_json, [selector.as_json]]
886
+ end
887
+ end
888
+
801
889
  class EmptyRecordProjection < Expression
802
890
  include(ValueSemantics.for_attributes do
803
891
  record Expression
@@ -1189,26 +1277,25 @@ module Dhall
1189
1277
  class Import < Expression
1190
1278
  class IntegrityCheck
1191
1279
  include(ValueSemantics.for_attributes do
1192
- protocol Either("sha256", :nocheck)
1193
- data Either(::String, nil)
1280
+ code ::Integer
1281
+ digest ::String
1194
1282
  end)
1195
1283
 
1196
1284
  class FailureException < StandardError; end
1197
1285
 
1198
- def initialize(protocol=:nocheck, data=nil)
1199
- super(
1200
- protocol: protocol,
1201
- data: data
1202
- )
1286
+ def to_s
1287
+ "#{Multihashes::TABLE[code].sub(/\Asha2-/, "sha")}:#{hexdigest}"
1203
1288
  end
1204
1289
 
1205
- def to_s
1206
- "#{@protocol}:#{@data}"
1290
+ def hexdigest
1291
+ digest.unpack("H*").first.encode(Encoding::UTF_8)
1207
1292
  end
1208
1293
 
1209
- def check(expr)
1210
- return expr if @protocol == :nocheck
1294
+ def ipfs
1295
+ "/ipfs/b#{Base32.encode("\x01\x55" + as_json).downcase.sub(/=*$/, "")}"
1296
+ end
1211
1297
 
1298
+ def check(expr)
1212
1299
  expr = expr.normalize
1213
1300
  return expr if expr.cache_key == to_s
1214
1301
 
@@ -1216,64 +1303,65 @@ module Dhall
1216
1303
  end
1217
1304
 
1218
1305
  def as_json
1219
- @protocol == :nocheck ? nil : [@protocol, @data]
1306
+ Multihashes.encode(digest, Multihashes::TABLE[code])
1220
1307
  end
1221
1308
  end
1222
1309
 
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)
1310
+ class NoIntegrityCheck < IntegrityCheck
1311
+ def initialize; end
1230
1312
 
1231
- def initialize(headers, authority, *path, query)
1232
- super(
1233
- headers: headers,
1234
- authority: authority,
1235
- path: path,
1236
- query: query,
1237
- )
1313
+ def to_s
1314
+ ""
1238
1315
  end
1239
1316
 
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
- )
1317
+ def hexdigest; end
1318
+
1319
+ def check(expr)
1320
+ expr
1247
1321
  end
1248
1322
 
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
- )
1323
+ def as_json
1324
+ nil
1325
+ end
1326
+ end
1327
+
1328
+ Location = LazyObject.new do
1329
+ UnionType.new(
1330
+ alternatives: {
1331
+ "Local" => Builtins[:Text],
1332
+ "Remote" => Builtins[:Text],
1333
+ "Environment" => Builtins[:Text],
1334
+ "Missing" => nil
1335
+ }
1336
+ )
1337
+ end
1338
+
1339
+ class URI
1340
+ include(ValueSemantics.for_attributes do
1341
+ uri ::URI
1342
+ headers Either(nil, Expression), default: nil
1343
+ end)
1344
+
1345
+ def with(attrs)
1346
+ if attrs.key?(:path)
1347
+ attrs[:uri] =
1348
+ uri + Util.path_components_to_uri(*attrs.delete(:path))
1349
+ end
1350
+
1351
+ super
1257
1352
  end
1258
1353
 
1259
1354
  def headers
1260
1355
  header_type = RecordType.new(
1261
1356
  record: {
1262
- "header" => Builtins[:Text],
1263
- "value" => Builtins[:Text]
1357
+ "mapKey" => Builtins[:Text],
1358
+ "mapValue" => Builtins[:Text]
1264
1359
  }
1265
1360
  )
1266
1361
 
1267
1362
  super || EmptyList.new(element_type: header_type)
1268
1363
  end
1269
1364
 
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
1365
  def chain_onto(relative_to)
1278
1366
  if headers.is_a?(Import)
1279
1367
  with(headers: headers.with(path: headers.real_path(relative_to)))
@@ -1284,23 +1372,43 @@ module Dhall
1284
1372
 
1285
1373
  def canonical
1286
1374
  with(
1287
- path: (path[1..-1] + [""])
1288
- .reduce([[], path.first]) { |(pth, prev), c|
1375
+ path: (path[1..-1] + [""]).reduce([[], path.first]) { |(pth, prev), c|
1289
1376
  c == ".." ? [pth, prev] : [pth + [prev], c]
1290
1377
  }.first.reject { |c| c == "." }
1291
1378
  )
1292
1379
  end
1293
1380
 
1381
+ def port
1382
+ uri.port && uri.port != uri.default_port ? uri.port : nil
1383
+ end
1384
+
1385
+ def authority
1386
+ [
1387
+ uri.userinfo,
1388
+ [uri.host, port].compact.join(":")
1389
+ ].compact.join("@")
1390
+ end
1391
+
1294
1392
  def origin
1295
- "#{scheme}://#{authority}"
1393
+ "#{uri.scheme}://#{authority}"
1296
1394
  end
1297
1395
 
1298
1396
  def to_s
1299
1397
  uri.to_s
1300
1398
  end
1301
1399
 
1400
+ def location
1401
+ Union.from(Location, "Remote", to_s.as_dhall)
1402
+ end
1403
+
1404
+ def path
1405
+ path = uri.path.split(/\//, -1).map(&::URI.method(:unescape))
1406
+ path = path[1..-1] if path.length > 1 && path.first.empty?
1407
+ path
1408
+ end
1409
+
1302
1410
  def as_json
1303
- [@headers&.as_json, authority, *path, query]
1411
+ [@headers&.as_json, authority, *path, uri.query]
1304
1412
  end
1305
1413
  end
1306
1414
 
@@ -1308,20 +1416,12 @@ module Dhall
1308
1416
  def resolve(resolver)
1309
1417
  resolver.resolve_http(self)
1310
1418
  end
1311
-
1312
- def scheme
1313
- "http"
1314
- end
1315
1419
  end
1316
1420
 
1317
1421
  class Https < URI
1318
1422
  def resolve(resolver)
1319
1423
  resolver.resolve_https(self)
1320
1424
  end
1321
-
1322
- def scheme
1323
- "https"
1324
- end
1325
1425
  end
1326
1426
 
1327
1427
  class Path
@@ -1366,6 +1466,10 @@ module Dhall
1366
1466
  pathname.to_s
1367
1467
  end
1368
1468
 
1469
+ def location
1470
+ Union.from(Location, "Local", to_s.as_dhall)
1471
+ end
1472
+
1369
1473
  def as_json
1370
1474
  path
1371
1475
  end
@@ -1376,8 +1480,8 @@ module Dhall
1376
1480
  Pathname.new("/").join(*path)
1377
1481
  end
1378
1482
 
1379
- def to_uri(scheme, authority)
1380
- scheme.new(nil, authority, *path, nil)
1483
+ def to_uri(scheme, base_uri)
1484
+ scheme.new(uri: base_uri + Util.path_components_to_uri(*path))
1381
1485
  end
1382
1486
 
1383
1487
  def chain_onto(relative_to)
@@ -1394,6 +1498,10 @@ module Dhall
1394
1498
  Pathname.new(".").join(*path)
1395
1499
  end
1396
1500
 
1501
+ def to_s
1502
+ "./#{pathname}"
1503
+ end
1504
+
1397
1505
  def chain_onto(relative_to)
1398
1506
  relative_to.with(
1399
1507
  path: relative_to.path[0..-2] + path
@@ -1473,6 +1581,10 @@ module Dhall
1473
1581
  end}"
1474
1582
  end
1475
1583
 
1584
+ def location
1585
+ Union.from(Location, "Environment", to_s.as_dhall)
1586
+ end
1587
+
1476
1588
  def hash
1477
1589
  @var.hash
1478
1590
  end
@@ -1480,7 +1592,7 @@ module Dhall
1480
1592
  def eql?(other)
1481
1593
  other.is_a?(self.class) && other.var == var
1482
1594
  end
1483
- alias eql? ==
1595
+ alias == eql?
1484
1596
 
1485
1597
  def as_json
1486
1598
  @var
@@ -1506,6 +1618,15 @@ module Dhall
1506
1618
  "missing"
1507
1619
  end
1508
1620
 
1621
+ def location
1622
+ Union.from(Location, "Missing", nil)
1623
+ end
1624
+
1625
+ def eql?(other)
1626
+ other.class == self.class
1627
+ end
1628
+ alias == eql?
1629
+
1509
1630
  def as_json
1510
1631
  []
1511
1632
  end
@@ -1525,9 +1646,16 @@ module Dhall
1525
1646
  end
1526
1647
  end
1527
1648
 
1649
+ class AsLocation
1650
+ def self.call(*)
1651
+ raise "AsLocation is only a marker, you don't actually call it"
1652
+ end
1653
+ end
1654
+
1528
1655
  IMPORT_TYPES = [
1529
1656
  Expression,
1530
- Text
1657
+ Text,
1658
+ AsLocation
1531
1659
  ].freeze
1532
1660
 
1533
1661
  PATH_TYPES = [
@@ -1537,14 +1665,14 @@ module Dhall
1537
1665
  ].freeze
1538
1666
 
1539
1667
  include(ValueSemantics.for_attributes do
1540
- integrity_check IntegrityCheck, default: IntegrityCheck.new
1668
+ integrity_check IntegrityCheck, default: NoIntegrityCheck.new
1541
1669
  import_type Class
1542
1670
  path Either(*PATH_TYPES)
1543
1671
  end)
1544
1672
 
1545
1673
  def initialize(integrity_check, import_type, path)
1546
1674
  super(
1547
- integrity_check: integrity_check || IntegrityCheck.new,
1675
+ integrity_check: integrity_check || NoIntegrityCheck.new,
1548
1676
  import_type: import_type,
1549
1677
  path: path
1550
1678
  )
@@ -1567,10 +1695,11 @@ module Dhall
1567
1695
  end
1568
1696
 
1569
1697
  def cache_key(relative_to)
1570
- if integrity_check.protocol == :nocheck
1698
+ key = integrity_check.to_s
1699
+ if key.empty?
1571
1700
  real_path(relative_to)
1572
1701
  else
1573
- integrity_check.to_s
1702
+ key
1574
1703
  end
1575
1704
  end
1576
1705