dhall 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,601 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dhall/ast"
4
+ require "dhall/builtins"
5
+
6
+ module Dhall
7
+ module Parser
8
+ def self.parse(*args)
9
+ CitrusParser.parse(*args)
10
+ end
11
+
12
+ def self.parse_file(*args)
13
+ CitrusParser.parse_file(*args)
14
+ end
15
+
16
+ module CompleteExpression
17
+ def value
18
+ capture(:expression).value
19
+ end
20
+ end
21
+
22
+ module Expression
23
+ def value
24
+ key =
25
+ [:let_binding, :lambda, :forall, :arrow, :if, :merge]
26
+ .find { |k| captures.key?(k) }
27
+
28
+ return public_send(key) if key
29
+
30
+ key =
31
+ [:empty_collection, :non_empty_optional]
32
+ .find { |k| captures.key?(k) }
33
+ key ? capture(key).value : super
34
+ end
35
+
36
+ def let_binding
37
+ LetBlock.for(
38
+ lets: captures(:let_binding).map(&:value),
39
+ body: capture(:expression).value
40
+ )
41
+ end
42
+
43
+ def lambda
44
+ Function.new(
45
+ var: capture(:nonreserved_label).value,
46
+ type: captures(:expression)[0].value,
47
+ body: captures(:expression)[1].value
48
+ )
49
+ end
50
+
51
+ def forall
52
+ Forall.new(
53
+ var: capture(:nonreserved_label).value,
54
+ type: captures(:expression)[0].value,
55
+ body: captures(:expression)[1].value
56
+ )
57
+ end
58
+
59
+ def arrow
60
+ Forall.of_arguments(
61
+ capture(:operator_expression).value,
62
+ body: capture(:expression).value
63
+ )
64
+ end
65
+
66
+ def if
67
+ If.new(
68
+ predicate: captures(:expression)[0].value,
69
+ then: captures(:expression)[1].value,
70
+ else: captures(:expression)[2].value
71
+ )
72
+ end
73
+
74
+ def merge
75
+ Merge.new(
76
+ record: captures(:import_expression)[0].value,
77
+ input: captures(:import_expression)[1].value,
78
+ type: capture(:application_expression)&.value
79
+ )
80
+ end
81
+ end
82
+
83
+ OPERATORS = {
84
+ import_alt_expression: :ImportFallback,
85
+ or_expression: :Or,
86
+ plus_expression: :Plus,
87
+ text_append_expression: :TextConcatenate,
88
+ list_append_expression: :ListConcatenate,
89
+ and_expression: :And,
90
+ combine_expression: :RecursiveRecordMerge,
91
+ prefer_expression: :RightBiasedRecordMerge,
92
+ combine_types_expression: :RecursiveRecordTypeMerge,
93
+ times_expression: :Times,
94
+ equal_expression: :Equal,
95
+ not_equal_expression: :NotEqual
96
+ }.freeze
97
+
98
+ OPERATORS.to_a.zip(
99
+ OPERATORS.to_a[1..-1] + [[:application_expression]]
100
+ ).each do |((rule, ast_class), (next_rule, _))|
101
+ const_set(rule.to_s.split(/_/).map(&:capitalize).join, Module.new do
102
+ define_method(:value) do
103
+ captures(next_rule).map(&:value).reduce do |lhs, rhs|
104
+ Operator.const_get(ast_class).new(lhs: lhs, rhs: rhs)
105
+ end
106
+ end
107
+ end)
108
+ end
109
+
110
+ module ApplicationExpression
111
+ def value
112
+ some = capture(:some) ? [Variable["Some"]] : []
113
+ els = some + captures(:import_expression).map(&:value)
114
+ els.reduce do |f, arg|
115
+ Application.for(function: f, argument: arg)
116
+ end
117
+ end
118
+ end
119
+
120
+ module SelectorExpression
121
+ def value
122
+ record = capture(:primitive_expression).value
123
+ selectors = captures(:selector).map(&:value)
124
+ selectors.reduce(record) do |rec, sels|
125
+ if sels.is_a?(Array)
126
+ return EmptyRecordProjection.new(record: rec) if sels.empty?
127
+ RecordProjection.new(record: rec, selectors: sels)
128
+ else
129
+ RecordSelection.new(record: rec, selector: sels)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ module Labels
136
+ def value
137
+ captures(:any_label).map(&:value)
138
+ end
139
+ end
140
+
141
+ module Label
142
+ def value
143
+ if first.string == "`"
144
+ matches[1].string
145
+ else
146
+ string
147
+ end
148
+ end
149
+ end
150
+
151
+ module NonreservedLabel
152
+ def value
153
+ if captures.key?(:label)
154
+ capture(:label).value
155
+ else
156
+ string
157
+ end
158
+ end
159
+ end
160
+
161
+ module NaturalLiteral
162
+ def value
163
+ Natural.new(value: string.to_i)
164
+ end
165
+ end
166
+
167
+ module IntegerLiteral
168
+ def value
169
+ Integer.new(value: string.to_i)
170
+ end
171
+ end
172
+
173
+ module NumericDoubleLiteral
174
+ def value
175
+ float = string.to_f
176
+ raise Citrus::ParseError, input if float.nan? || float.infinite?
177
+ Double.new(value: float)
178
+ end
179
+ end
180
+
181
+ module MinusInfinityLiteral
182
+ def value
183
+ Double.new(value: -Float::INFINITY)
184
+ end
185
+ end
186
+
187
+ module PlusInfinityLiteral
188
+ def value
189
+ Double.new(value: Float::INFINITY)
190
+ end
191
+ end
192
+
193
+ module Nan
194
+ def value
195
+ Double.new(value: Float::NAN)
196
+ end
197
+ end
198
+
199
+ module DoubleQuoteLiteral
200
+ def value
201
+ TextLiteral.for(
202
+ *captures(:double_quote_chunk)
203
+ .map(&:value)
204
+ .chunk { |s| s.is_a?(String) }
205
+ .flat_map do |(strs, group)|
206
+ strs ? group.map { |s| s.encode("UTF-16BE") }.join : group
207
+ end
208
+ )
209
+ end
210
+ end
211
+
212
+ module DoubleQuoteChunk
213
+ def value
214
+ if captures.key?(:double_quote_escaped)
215
+ capture(:double_quote_escaped).value
216
+ else
217
+ super
218
+ end
219
+ end
220
+ end
221
+
222
+ module DoubleQuoteEscaped
223
+ ESCAPES = {
224
+ "\"" => "\"",
225
+ "$" => "$",
226
+ "\\" => "\\",
227
+ "/" => "/",
228
+ "b" => "\b",
229
+ "f" => "\f",
230
+ "n" => "\n",
231
+ "r" => "\r",
232
+ "t" => "\t"
233
+ }.freeze
234
+
235
+ def value
236
+ ESCAPES.fetch(string) do
237
+ [string[1..-1]].pack("H*").force_encoding("UTF-16BE")
238
+ end
239
+ end
240
+ end
241
+
242
+ module SingleQuoteLiteral
243
+ def value
244
+ chunks = capture(:single_quote_continue).value
245
+ indent = chunks.join.split(/\n/, -1).map { |line|
246
+ line.match(/^( *|\t*)/).to_s.length
247
+ }.min
248
+
249
+ TextLiteral.for(
250
+ *chunks
251
+ .chunk { |c| c != "\n" }
252
+ .flat_map { |(line, chunk)| line ? chunk[indent..-1] : chunk }
253
+ )
254
+ end
255
+ end
256
+
257
+ module SingleQuoteContinue
258
+ def value
259
+ ([first].compact + captures(:single_quote_continue)).flat_map(&:value)
260
+ end
261
+ end
262
+
263
+ module Interpolation
264
+ def value
265
+ capture(:complete_expression).value
266
+ end
267
+ end
268
+
269
+ module EscapedQuotePair
270
+ def value
271
+ "''"
272
+ end
273
+ end
274
+
275
+ module EscapedInterpolation
276
+ def value
277
+ "${"
278
+ end
279
+ end
280
+
281
+ module NonEmptyListLiteral
282
+ def value
283
+ List.new(elements: captures(:expression).map(&:value))
284
+ end
285
+ end
286
+
287
+ module Identifier
288
+ def value
289
+ name = capture(:any_label).value
290
+
291
+ return Dhall::Bool.new(value: true) if name == "True"
292
+ return Dhall::Bool.new(value: false) if name == "False"
293
+
294
+ Dhall::Builtins::ALL[name]&.new ||
295
+ Variable.new(
296
+ name: name,
297
+ index: capture(:natural_literal)&.string.to_i
298
+ )
299
+ end
300
+ end
301
+
302
+ module PrimitiveExpression
303
+ def value
304
+ key = [
305
+ :complete_expression,
306
+ :record_type_or_literal,
307
+ :union_type_or_literal
308
+ ].find { |k| captures.key?(k) }
309
+ key ? capture(key).value : super
310
+ end
311
+ end
312
+
313
+ module EmptyUnionType
314
+ def value
315
+ UnionType.new(alternatives: {})
316
+ end
317
+ end
318
+
319
+ module UnionTypeOrLiteralVariantType
320
+ def value(label)
321
+ rest = capture(:non_empty_union_type_or_literal)&.value
322
+ type = UnionType.new(
323
+ alternatives: { label => capture(:expression)&.value }
324
+ )
325
+ if rest.is_a?(Union)
326
+ rest.with(alternatives: type.merge(rest.alternatives))
327
+ else
328
+ rest ? type.merge(rest) : type
329
+ end
330
+ end
331
+ end
332
+
333
+ module UnionLiteralVariantValue
334
+ def value(label)
335
+ Union.new(
336
+ tag: label,
337
+ value: capture(:expression).value,
338
+ alternatives: captures(:union_type_entry).map(&:value)
339
+ .reduce(UnionType.new(alternatives: {}), &:merge)
340
+ )
341
+ end
342
+ end
343
+
344
+ module UnionTypeEntry
345
+ def value
346
+ UnionType.new(
347
+ alternatives: {
348
+ capture(:any_label).value => capture(:expression)&.value
349
+ }
350
+ )
351
+ end
352
+ end
353
+
354
+ module NonEmptyUnionTypeOrLiteral
355
+ def value
356
+ key = [
357
+ :union_literal_variant_value,
358
+ :union_type_or_literal_variant_type
359
+ ].find { |k| captures.key?(k) }
360
+
361
+ if key
362
+ capture(key).value(capture(:any_label).value)
363
+ else
364
+ no_alts = UnionType.new(alternatives: {})
365
+ Union.from(no_alts, capture(:any_label).value, nil)
366
+ end
367
+ end
368
+ end
369
+
370
+ module EmptyRecordLiteral
371
+ def value
372
+ EmptyRecord.new
373
+ end
374
+ end
375
+
376
+ module EmptyRecordType
377
+ def value
378
+ Dhall::EmptyRecordType.new
379
+ end
380
+ end
381
+
382
+ module NonEmptyRecordTypeOrLiteral
383
+ def value
384
+ key = [
385
+ :non_empty_record_literal,
386
+ :non_empty_record_type
387
+ ].find { |k| captures.key?(k) }
388
+
389
+ capture(key).value(capture(:any_label).value)
390
+ end
391
+ end
392
+
393
+ module NonEmptyRecordLiteral
394
+ def value(first_key)
395
+ Record.new(
396
+ record: captures(:record_literal_entry).map(&:value).reduce(
397
+ first_key => capture(:expression).value
398
+ ) do |final, rec|
399
+ final.merge(rec) { raise TypeError, "duplicate field" }
400
+ end
401
+ )
402
+ end
403
+ end
404
+
405
+ module RecordLiteralEntry
406
+ def value
407
+ { capture(:any_label).value => capture(:expression).value }
408
+ end
409
+ end
410
+
411
+ module NonEmptyRecordType
412
+ def value(first_key)
413
+ RecordType.new(
414
+ record: captures(:record_type_entry).map(&:value).reduce(
415
+ { first_key => capture(:expression).value },
416
+ &:merge
417
+ )
418
+ )
419
+ end
420
+ end
421
+
422
+ RecordTypeEntry = RecordLiteralEntry
423
+
424
+ module EmptyCollection
425
+ def value
426
+ if captures.key?(:list)
427
+ EmptyList.new(element_type: capture(:import_expression).value)
428
+ else
429
+ OptionalNone.new(value_type: capture(:import_expression).value)
430
+ end
431
+ end
432
+ end
433
+
434
+ module NonEmptyOptional
435
+ def value
436
+ Optional.new(
437
+ value: capture(:expression).value,
438
+ value_type: capture(:import_expression).value
439
+ )
440
+ end
441
+ end
442
+
443
+ module AnnotatedExpression
444
+ def value
445
+ if matches[1].string.empty?
446
+ first.value
447
+ else
448
+ TypeAnnotation.new(
449
+ value: first.value,
450
+ type: capture(:expression).value
451
+ )
452
+ end
453
+ end
454
+ end
455
+
456
+ module LetBinding
457
+ def value
458
+ exprs = captures(:expression)
459
+ Let.new(
460
+ var: capture(:nonreserved_label).value,
461
+ assign: exprs.last.value,
462
+ type: exprs.length > 1 ? exprs.first.value : nil
463
+ )
464
+ end
465
+ end
466
+
467
+ module Import
468
+ def value
469
+ import_type = if captures.key?(:text)
470
+ Dhall::Import::Text
471
+ else
472
+ Dhall::Import::Expression
473
+ end
474
+
475
+ capture(:import_hashed).value(import_type)
476
+ end
477
+ end
478
+
479
+ module ImportHashed
480
+ def value(import_type)
481
+ integrity_check = capture(:hash)&.value
482
+ path = capture(:import_type).value
483
+ Dhall::Import.new(integrity_check, import_type, path)
484
+ end
485
+ end
486
+
487
+ module Hash
488
+ def value
489
+ protocol, data = string.split(/:/, 2)
490
+ Dhall::Import::IntegrityCheck.new(protocol, data)
491
+ end
492
+ end
493
+
494
+ module Http
495
+ SCHEME = {
496
+ "http" => Dhall::Import::Http,
497
+ "https" => Dhall::Import::Https
498
+ }.freeze
499
+
500
+ def value
501
+ http = capture(:http_raw)
502
+ SCHEME.fetch(http.capture(:scheme).value).new(
503
+ capture(:import_hashed)&.value(Dhall::Import::Expression),
504
+ http.capture(:authority).value,
505
+ *unescaped_components,
506
+ http.capture(:query)&.value
507
+ )
508
+ end
509
+
510
+ def unescaped_components
511
+ capture(:http_raw)
512
+ .capture(:path)
513
+ .captures(:path_component)
514
+ .map do |pc|
515
+ pc.value(URI.method(:unescape))
516
+ end
517
+ end
518
+ end
519
+
520
+ module Env
521
+ def value
522
+ Dhall::Import::EnvironmentVariable.new(
523
+ if captures.key?(:bash_environment_variable)
524
+ capture(:bash_environment_variable).string
525
+ else
526
+ capture(:posix_environment_variable).value.encode("utf-8")
527
+ end
528
+ )
529
+ end
530
+ end
531
+
532
+ module PosixEnvironmentVariable
533
+ def value
534
+ matches.map(&:value).join
535
+ end
536
+ end
537
+
538
+ module PosixEnvironmentVariableCharacter
539
+ ESCAPES = Dhall::Import::EnvironmentVariable::ESCAPES
540
+
541
+ def value
542
+ if first&.string == "\\"
543
+ ESCAPES.fetch(matches[1].string) {
544
+ raise "Invalid escape: #{string}"
545
+ }.encode("UTF-16BE")
546
+ else
547
+ string
548
+ end
549
+ end
550
+ end
551
+
552
+ module AbsolutePath
553
+ def value
554
+ Dhall::Import::AbsolutePath.new(*super)
555
+ end
556
+ end
557
+
558
+ module HerePath
559
+ def value
560
+ Dhall::Import::RelativePath.new(*capture(:path).value)
561
+ end
562
+ end
563
+
564
+ module ParentPath
565
+ def value
566
+ Dhall::Import::RelativeToParentPath.new(*capture(:path).value)
567
+ end
568
+ end
569
+
570
+ module HomePath
571
+ def value
572
+ Dhall::Import::RelativeToHomePath.new(*capture(:path).value)
573
+ end
574
+ end
575
+
576
+ module Path
577
+ def value
578
+ captures(:path_component).map(&:value)
579
+ end
580
+ end
581
+
582
+ module PathComponent
583
+ def value(unescaper=:itself.to_proc)
584
+ if captures.key?(:quoted_path_component)
585
+ capture(:quoted_path_component).value
586
+ else
587
+ unescaper.call(capture(:unquoted_path_component).value)
588
+ end
589
+ end
590
+ end
591
+
592
+ module Missing
593
+ def value
594
+ Dhall::Import::MissingImport.new
595
+ end
596
+ end
597
+ end
598
+ end
599
+
600
+ require "citrus"
601
+ Citrus.require "dhall/parser"