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 +5 -5
- data/bin/dhall-compile +38 -4
- data/bin/json-to-dhall +1 -1
- data/bin/yaml-to-dhall +1 -1
- data/dhall.gemspec +4 -0
- data/lib/dhall/as_dhall.rb +6 -0
- data/lib/dhall/ast.rb +291 -116
- data/lib/dhall/binary.rb +92 -17
- data/lib/dhall/builtins.rb +179 -238
- data/lib/dhall/coder.rb +6 -1
- data/lib/dhall/normalize.rb +59 -31
- data/lib/dhall/parser.citrus +73 -43
- data/lib/dhall/parser.rb +136 -103
- data/lib/dhall/resolve.rb +42 -32
- data/lib/dhall/typecheck.rb +171 -34
- data/lib/dhall/types.rb +19 -0
- data/lib/dhall/util.rb +49 -0
- data/lib/dhall.rb +1 -0
- metadata +60 -4
- data/lib/dhall/visitor.rb +0 -23
data/lib/dhall/parser.rb
CHANGED
@@ -21,23 +21,21 @@ module Dhall
|
|
21
21
|
|
22
22
|
module Expression
|
23
23
|
def value
|
24
|
-
|
25
|
-
[:let_binding, :lambda, :forall, :arrow, :if, :merge]
|
26
|
-
.find { |k| captures.key?(k) }
|
27
|
-
|
28
|
-
return public_send(key) if key
|
24
|
+
return list if string =~ /\A\[\s*\]/
|
29
25
|
|
30
26
|
key =
|
31
|
-
[:
|
27
|
+
[:let_binding, :lambda, :forall, :arrow, :if, :merge, :tomap, :assert]
|
32
28
|
.find { |k| captures.key?(k) }
|
33
|
-
|
29
|
+
|
30
|
+
key ? public_send(key) : super
|
34
31
|
end
|
35
32
|
|
36
33
|
def let_binding
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
captures(:let_binding).reverse.reduce(
|
35
|
+
capture(:expression).value
|
36
|
+
) do |inside, let|
|
37
|
+
LetIn.new(let: let.value, body: inside)
|
38
|
+
end
|
41
39
|
end
|
42
40
|
|
43
41
|
def lambda
|
@@ -78,6 +76,21 @@ module Dhall
|
|
78
76
|
type: capture(:application_expression)&.value
|
79
77
|
)
|
80
78
|
end
|
79
|
+
|
80
|
+
def list
|
81
|
+
EmptyList.new(type: capture(:application_expression).value)
|
82
|
+
end
|
83
|
+
|
84
|
+
def tomap
|
85
|
+
ToMap.new(
|
86
|
+
record: capture(:import_expression).value,
|
87
|
+
type: capture(:application_expression).value
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def assert
|
92
|
+
Assertion.new(type: capture(:expression).value)
|
93
|
+
end
|
81
94
|
end
|
82
95
|
|
83
96
|
OPERATORS = {
|
@@ -92,7 +105,8 @@ module Dhall
|
|
92
105
|
combine_types_expression: :RecursiveRecordTypeMerge,
|
93
106
|
times_expression: :Times,
|
94
107
|
equal_expression: :Equal,
|
95
|
-
not_equal_expression: :NotEqual
|
108
|
+
not_equal_expression: :NotEqual,
|
109
|
+
equivalent_expression: :Equivalent
|
96
110
|
}.freeze
|
97
111
|
|
98
112
|
OPERATORS.to_a.zip(
|
@@ -122,9 +136,9 @@ module Dhall
|
|
122
136
|
if captures.key?(:merge)
|
123
137
|
merge
|
124
138
|
elsif captures.key?(:some)
|
125
|
-
Optional.new(
|
126
|
-
|
127
|
-
)
|
139
|
+
Optional.new(value: capture(:import_expression).value)
|
140
|
+
elsif captures.key?(:tomap)
|
141
|
+
ToMap.new(record: capture(:import_expression).value)
|
128
142
|
else
|
129
143
|
super
|
130
144
|
end
|
@@ -142,11 +156,11 @@ module Dhall
|
|
142
156
|
module SelectorExpression
|
143
157
|
def value
|
144
158
|
record = capture(:primitive_expression).value
|
145
|
-
|
146
|
-
selectors.reduce(record) do |rec, sels|
|
159
|
+
captures(:selector).map(&:value).reduce(record) do |rec, sels|
|
147
160
|
if sels.is_a?(Array)
|
148
|
-
|
149
|
-
|
161
|
+
RecordProjection.for(rec, sels)
|
162
|
+
elsif sels.is_a?(Dhall::Expression)
|
163
|
+
RecordProjectionByExpression.new(record: rec, selector: sels)
|
150
164
|
else
|
151
165
|
RecordSelection.new(record: rec, selector: sels)
|
152
166
|
end
|
@@ -154,9 +168,15 @@ module Dhall
|
|
154
168
|
end
|
155
169
|
end
|
156
170
|
|
157
|
-
module
|
171
|
+
module Selector
|
158
172
|
def value
|
159
|
-
captures(:
|
173
|
+
if captures.key?(:type_selector)
|
174
|
+
capture(:expression).value
|
175
|
+
elsif captures.key?(:labels)
|
176
|
+
captures(:any_label).map(&:value)
|
177
|
+
else
|
178
|
+
super
|
179
|
+
end
|
160
180
|
end
|
161
181
|
end
|
162
182
|
|
@@ -237,7 +257,7 @@ module Dhall
|
|
237
257
|
.map(&:value)
|
238
258
|
.chunk { |s| s.is_a?(String) }
|
239
259
|
.flat_map do |(strs, group)|
|
240
|
-
strs ? group.
|
260
|
+
strs ? group.join : group
|
241
261
|
end
|
242
262
|
)
|
243
263
|
end
|
@@ -266,20 +286,44 @@ module Dhall
|
|
266
286
|
"t" => "\t"
|
267
287
|
}.freeze
|
268
288
|
|
289
|
+
NON_CHARACTERS = [
|
290
|
+
(0xD800..0xDFFF),
|
291
|
+
(0xFFFE..0xFFFF),
|
292
|
+
(0x1FFFE..0x1FFFF),
|
293
|
+
(0x2FFFE..0x2FFFF),
|
294
|
+
(0x3FFFE..0x3FFFF),
|
295
|
+
(0x4FFFE..0x4FFFF),
|
296
|
+
(0x5FFFE..0x5FFFF),
|
297
|
+
(0x6FFFE..0x6FFFF),
|
298
|
+
(0x7FFFE..0x7FFFF),
|
299
|
+
(0x8FFFE..0x8FFFF),
|
300
|
+
(0x9FFFE..0x9FFFF),
|
301
|
+
(0xAFFFE..0xAFFFF),
|
302
|
+
(0xBFFFE..0xBFFFF),
|
303
|
+
(0xCFFFE..0xCFFFF),
|
304
|
+
(0xDFFFE..0xDFFFF),
|
305
|
+
(0xEFFFE..0xEFFFF),
|
306
|
+
(0xFFFFE..0xFFFFF),
|
307
|
+
(0x10FFFE..0x10FFFF)
|
308
|
+
].freeze
|
309
|
+
|
269
310
|
def value
|
270
311
|
ESCAPES.fetch(string) do
|
271
|
-
|
312
|
+
code = string.sub(/\Au\{?([A-F0-9]+)\}?/, "\\1").to_i(16)
|
313
|
+
NON_CHARACTERS.each do |range|
|
314
|
+
raise Citrus::ParseError, input if range.include?(code)
|
315
|
+
end
|
316
|
+
[code].pack("U*")
|
272
317
|
end
|
273
318
|
end
|
274
319
|
end
|
275
320
|
|
276
321
|
module SingleQuoteLiteral
|
277
322
|
def value
|
278
|
-
chunks = capture(:single_quote_continue).value
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
indent = 0 if raw.end_with?("\n")
|
323
|
+
chunks = capture(:single_quote_continue).value.flat_map do |chunk|
|
324
|
+
chunk.is_a?(String) ? chunk.gsub(/\r\n/, "\n").chars : chunk
|
325
|
+
end
|
326
|
+
indent = Util.indent_size(chunks.join)
|
283
327
|
|
284
328
|
TextLiteral.for(
|
285
329
|
*chunks
|
@@ -295,6 +339,12 @@ module Dhall
|
|
295
339
|
end
|
296
340
|
end
|
297
341
|
|
342
|
+
module EndOfLine
|
343
|
+
def value
|
344
|
+
"\n"
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
298
348
|
module Interpolation
|
299
349
|
def value
|
300
350
|
capture(:complete_expression).value
|
@@ -342,7 +392,7 @@ module Dhall
|
|
342
392
|
key = [
|
343
393
|
:complete_expression,
|
344
394
|
:record_type_or_literal,
|
345
|
-
:
|
395
|
+
:union_type
|
346
396
|
].find { |k| captures.key?(k) }
|
347
397
|
key ? capture(key).value : super
|
348
398
|
end
|
@@ -354,31 +404,6 @@ module Dhall
|
|
354
404
|
end
|
355
405
|
end
|
356
406
|
|
357
|
-
module UnionTypeOrLiteralVariantType
|
358
|
-
def value(label)
|
359
|
-
rest = capture(:non_empty_union_type_or_literal)&.value
|
360
|
-
type = UnionType.new(
|
361
|
-
alternatives: { label => capture(:expression)&.value }
|
362
|
-
)
|
363
|
-
if rest.is_a?(Union)
|
364
|
-
rest.with(alternatives: type.merge(rest.alternatives))
|
365
|
-
else
|
366
|
-
rest ? type.merge(rest) : type
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
module UnionLiteralVariantValue
|
372
|
-
def value(label)
|
373
|
-
Union.new(
|
374
|
-
tag: label,
|
375
|
-
value: capture(:expression).value,
|
376
|
-
alternatives: captures(:union_type_entry).map(&:value)
|
377
|
-
.reduce(UnionType.new(alternatives: {}), &:merge)
|
378
|
-
)
|
379
|
-
end
|
380
|
-
end
|
381
|
-
|
382
407
|
module UnionTypeEntry
|
383
408
|
def value
|
384
409
|
UnionType.new(
|
@@ -389,19 +414,9 @@ module Dhall
|
|
389
414
|
end
|
390
415
|
end
|
391
416
|
|
392
|
-
module
|
417
|
+
module NonEmptyUnionType
|
393
418
|
def value
|
394
|
-
|
395
|
-
:union_literal_variant_value,
|
396
|
-
:union_type_or_literal_variant_type
|
397
|
-
].find { |k| captures.key?(k) }
|
398
|
-
|
399
|
-
if key
|
400
|
-
capture(key).value(capture(:any_label).value)
|
401
|
-
else
|
402
|
-
no_alts = UnionType.new(alternatives: {})
|
403
|
-
Union.from(no_alts, capture(:any_label).value, nil)
|
404
|
-
end
|
419
|
+
captures(:union_type_entry).map(&:value).reduce(&:merge)
|
405
420
|
end
|
406
421
|
end
|
407
422
|
|
@@ -459,25 +474,6 @@ module Dhall
|
|
459
474
|
|
460
475
|
RecordTypeEntry = RecordLiteralEntry
|
461
476
|
|
462
|
-
module EmptyCollection
|
463
|
-
def value
|
464
|
-
if captures.key?(:list)
|
465
|
-
EmptyList.new(element_type: capture(:import_expression).value)
|
466
|
-
else
|
467
|
-
OptionalNone.new(value_type: capture(:import_expression).value)
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
module NonEmptyOptional
|
473
|
-
def value
|
474
|
-
Optional.new(
|
475
|
-
value: capture(:expression).value,
|
476
|
-
value_type: capture(:import_expression).value
|
477
|
-
)
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
477
|
module AnnotatedExpression
|
482
478
|
def value
|
483
479
|
if matches[1].string.empty?
|
@@ -506,6 +502,8 @@ module Dhall
|
|
506
502
|
def value
|
507
503
|
import_type = if captures.key?(:text)
|
508
504
|
Dhall::Import::Text
|
505
|
+
elsif captures.key?(:location)
|
506
|
+
Dhall::Import::AsLocation
|
509
507
|
else
|
510
508
|
Dhall::Import::Expression
|
511
509
|
end
|
@@ -525,7 +523,28 @@ module Dhall
|
|
525
523
|
module Hash
|
526
524
|
def value
|
527
525
|
protocol, data = string.split(/:/, 2)
|
528
|
-
Dhall::Import::IntegrityCheck.new(
|
526
|
+
Dhall::Import::IntegrityCheck.new(
|
527
|
+
code: Multihashes::TABLE.key(
|
528
|
+
protocol.sub(/\Asha(\d{3})/, "sha2-\\1")
|
529
|
+
),
|
530
|
+
digest: [data].pack("H*")
|
531
|
+
)
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
module Scheme
|
536
|
+
def value
|
537
|
+
::URI.scheme_list[string.upcase]
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
module Authority
|
542
|
+
def value
|
543
|
+
{
|
544
|
+
userinfo: capture(:userinfo)&.value,
|
545
|
+
host: capture(:host).value,
|
546
|
+
port: capture(:port)&.value
|
547
|
+
}
|
529
548
|
end
|
530
549
|
end
|
531
550
|
|
@@ -535,23 +554,24 @@ module Dhall
|
|
535
554
|
"https" => Dhall::Import::Https
|
536
555
|
}.freeze
|
537
556
|
|
557
|
+
def http(key)
|
558
|
+
@http ||= capture(:http_raw)
|
559
|
+
@http.capture(key)&.value
|
560
|
+
end
|
561
|
+
|
538
562
|
def value
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
*unescaped_components,
|
544
|
-
http.capture(:query)&.value
|
563
|
+
uri = http(:scheme).build(
|
564
|
+
http(:authority).merge(
|
565
|
+
path: http(:url_path) || "/"
|
566
|
+
)
|
545
567
|
)
|
546
|
-
end
|
547
568
|
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
end
|
569
|
+
uri.instance_variable_set(:@query, http(:query))
|
570
|
+
|
571
|
+
SCHEME.fetch(uri.scheme).new(
|
572
|
+
headers: capture(:import_expression)&.value,
|
573
|
+
uri: uri
|
574
|
+
)
|
555
575
|
end
|
556
576
|
end
|
557
577
|
|
@@ -628,15 +648,28 @@ module Dhall
|
|
628
648
|
end
|
629
649
|
|
630
650
|
module PathComponent
|
631
|
-
def value(
|
651
|
+
def value(escaper=:itself.to_proc)
|
632
652
|
if captures.key?(:quoted_path_component)
|
633
|
-
capture(:quoted_path_component).value
|
653
|
+
escaper.call(capture(:quoted_path_component).value)
|
634
654
|
else
|
635
|
-
|
655
|
+
capture(:unquoted_path_component).value
|
636
656
|
end
|
637
657
|
end
|
638
658
|
end
|
639
659
|
|
660
|
+
module UrlPath
|
661
|
+
def value
|
662
|
+
"/" + matches.map { |pc|
|
663
|
+
if pc.captures.key?(:path_component)
|
664
|
+
# We escape here because ruby stdlib URI just stores path unparsed
|
665
|
+
pc.value(Util.method(:uri_escape))
|
666
|
+
else
|
667
|
+
pc.string[1..-1]
|
668
|
+
end
|
669
|
+
}.join("/")
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
640
673
|
module Missing
|
641
674
|
def value
|
642
675
|
Dhall::Import::MissingImport.new
|
data/lib/dhall/resolve.rb
CHANGED
@@ -16,13 +16,13 @@ module Dhall
|
|
16
16
|
module Resolvers
|
17
17
|
ReadPathSources = lambda do |sources|
|
18
18
|
sources.map do |source|
|
19
|
-
|
19
|
+
Util::LazyPromise.new { source.pathname.binread }
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
ReadEnvironmentSources = lambda do |sources|
|
24
24
|
sources.map do |source|
|
25
|
-
|
25
|
+
Util::LazyPromise.new do
|
26
26
|
ENV.fetch(source.var) do
|
27
27
|
raise ImportFailedException, "No #{source}"
|
28
28
|
end
|
@@ -38,16 +38,10 @@ module Dhall
|
|
38
38
|
req["Origin"] = parent_origin
|
39
39
|
req["Access-Control-Request-Method"] = "GET"
|
40
40
|
req["Access-Control-Request-Headers"] =
|
41
|
-
source.headers.map { |h|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
use_ssl: uri.scheme == "https",
|
46
|
-
open_timeout: timeout,
|
47
|
-
ssl_timeout: timeout,
|
48
|
-
read_timeout: timeout,
|
49
|
-
write_timeout: timeout
|
50
|
-
) { |http| http.request(req) }
|
41
|
+
source.headers.to_a.map { |h|
|
42
|
+
(h.fetch("header") { h.fetch("mapKey") }).to_s
|
43
|
+
}.join(",")
|
44
|
+
r = Util.net_http_req_with_timeout(uri, req, timeout: timeout)
|
51
45
|
|
52
46
|
raise ImportFailedException, source if r.code != "200"
|
53
47
|
unless r["Access-Control-Allow-Origin"] == parent_origin ||
|
@@ -59,23 +53,21 @@ module Dhall
|
|
59
53
|
|
60
54
|
ReadHttpSources = lambda do |sources, parent_origin|
|
61
55
|
sources.map do |source|
|
62
|
-
|
56
|
+
Util::LazyPromise.new do
|
63
57
|
PreflightCORS.call(source, parent_origin)
|
64
58
|
timeout = source.deadline.timeout
|
65
59
|
uri = source.uri
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
r = loop do
|
61
|
+
req = Net::HTTP::Get.new(uri)
|
62
|
+
source.headers.each do |header|
|
63
|
+
req[(header.fetch("header") { header.fetch("mapKey") }).to_s] =
|
64
|
+
(header.fetch("value") { header.fetch("mapValue") }).to_s
|
65
|
+
end
|
66
|
+
r = Util.net_http_req_with_timeout(uri, req, timeout: timeout)
|
67
|
+
|
68
|
+
break r unless ["301", "302", "303", "307", "308"].include?(r.code)
|
69
|
+
uri = URI(r["Location"])
|
69
70
|
end
|
70
|
-
r = Net::HTTP.start(
|
71
|
-
uri.hostname,
|
72
|
-
uri.port,
|
73
|
-
use_ssl: uri.scheme == "https",
|
74
|
-
open_timeout: timeout,
|
75
|
-
ssl_timeout: timeout,
|
76
|
-
read_timeout: timeout,
|
77
|
-
write_timeout: timeout
|
78
|
-
) { |http| http.request(req) }
|
79
71
|
|
80
72
|
raise ImportFailedException, source if r.code != "200"
|
81
73
|
r.body
|
@@ -86,7 +78,7 @@ module Dhall
|
|
86
78
|
StandardReadHttpSources = lambda do |sources, parent_origin|
|
87
79
|
ReadHttpSources.call(sources, parent_origin).map do |source_promise|
|
88
80
|
source_promise.then do |s|
|
89
|
-
s = s.force_encoding("UTF-8")
|
81
|
+
s = s.dup.force_encoding("UTF-8")
|
90
82
|
unless s.valid_encoding?
|
91
83
|
raise ImportFailedException, "#{s.inspect} is not valid UTF-8"
|
92
84
|
end
|
@@ -106,7 +98,7 @@ module Dhall
|
|
106
98
|
path_reader: ReadPathSources,
|
107
99
|
http_reader: ReadHttpSources,
|
108
100
|
https_reader: http_reader,
|
109
|
-
public_gateway: "cloudflare-ipfs.com"
|
101
|
+
public_gateway: URI("https://cloudflare-ipfs.com")
|
110
102
|
)
|
111
103
|
@path_reader = path_reader
|
112
104
|
@http_reader = http_reader
|
@@ -139,7 +131,7 @@ module Dhall
|
|
139
131
|
def gateway_fallback(source, promise)
|
140
132
|
promise.catch {
|
141
133
|
@http_reader.call([
|
142
|
-
source.to_uri(Import::Http, "localhost:8000")
|
134
|
+
source.to_uri(Import::Http, URI("http://localhost:8000"))
|
143
135
|
], "localhost").first
|
144
136
|
}.catch do
|
145
137
|
@https_reader.call([
|
@@ -182,11 +174,11 @@ module Dhall
|
|
182
174
|
|
183
175
|
def fetch(key, &block)
|
184
176
|
if key.is_a?(String) && key.start_with?("sha256:")
|
185
|
-
file = @dir + key.sub(/^sha256:/, "")
|
177
|
+
file = @dir + key.sub(/^sha256:/, "1220")
|
186
178
|
return Dhall.from_binary(file.binread) if file.exist?
|
187
179
|
|
188
180
|
Promise.resolve(nil).then(&block).then do |result|
|
189
|
-
file.open("wb") { |fh| fh.write(result.
|
181
|
+
file.open("wb") { |fh| fh.write(result.to_cbor) }
|
190
182
|
result
|
191
183
|
end
|
192
184
|
else
|
@@ -359,7 +351,7 @@ module Dhall
|
|
359
351
|
http_reader: ReadHttpSources,
|
360
352
|
https_reader: http_reader,
|
361
353
|
environment_reader: ReadEnvironmentSources,
|
362
|
-
ipfs_public_gateway: "cloudflare-ipfs.com",
|
354
|
+
ipfs_public_gateway: URI("https://cloudflare-ipfs.com"),
|
363
355
|
cache: RamCache.new,
|
364
356
|
max_depth: 50
|
365
357
|
)
|
@@ -427,9 +419,25 @@ module Dhall
|
|
427
419
|
).then { |h| @expr.with(h) }
|
428
420
|
end
|
429
421
|
|
422
|
+
class ImportAsLocationResolver < ExpressionResolver
|
423
|
+
def resolve(resolver:, relative_to:)
|
424
|
+
Promise.resolve(nil).then do
|
425
|
+
@expr.real_path(relative_to).location
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
430
|
class ImportResolver < ExpressionResolver
|
431
431
|
register_for Import
|
432
432
|
|
433
|
+
def self.new(expr)
|
434
|
+
if expr.import_type == Import::AsLocation
|
435
|
+
ImportAsLocationResolver.new(expr)
|
436
|
+
else
|
437
|
+
super
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
433
441
|
def resolve(resolver:, relative_to:)
|
434
442
|
Promise.resolve(nil).then do
|
435
443
|
resolver.cache_fetch(@expr.cache_key(relative_to)) do
|
@@ -441,7 +449,9 @@ module Dhall
|
|
441
449
|
def resolve_raw(resolver:, relative_to:)
|
442
450
|
real_path = @expr.real_path(relative_to)
|
443
451
|
real_path.resolve(resolver).then do |result|
|
444
|
-
@expr.
|
452
|
+
@expr.parse_resolve_check(
|
453
|
+
result,
|
454
|
+
deadline: resolver.deadline,
|
445
455
|
resolver: resolver.child(real_path),
|
446
456
|
relative_to: real_path
|
447
457
|
)
|