dhallish 0.2.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.
data/lib/ast.rb ADDED
@@ -0,0 +1,836 @@
1
+ require_relative 'types.rb'
2
+ require_relative 'utils.rb'
3
+ require 'open-uri'
4
+
5
+ module Dhallish
6
+ module Ast
7
+ include Dhallish
8
+
9
+ # Ast-Node for all operations that take two
10
+ # args of the same type and returns something of that type.
11
+ # `op` must be an ruby-Operator as String/Symbol
12
+ class BinaryArithOpNode
13
+ attr_accessor :lhs
14
+ attr_accessor :rhs
15
+
16
+ def initialize(types, lhs, rhs, op, &block)
17
+ @op = op
18
+ @types = types
19
+ @block = block
20
+ @lhs = lhs
21
+ @rhs = rhs
22
+ end
23
+
24
+ def compute_type(ctx)
25
+ lhs_type = @lhs.compute_type(ctx)
26
+ rhs_type = @rhs.compute_type(ctx)
27
+ assert ("Wrong Operator types for #{@op}. left: #{lhs_type}, right: #{rhs_type}") { lhs_type == rhs_type and @types.include? lhs_type }
28
+ lhs_type
29
+ end
30
+
31
+ def evaluate(ctx)
32
+ lhs = @lhs.evaluate ctx
33
+ rhs = @rhs.evaluate ctx
34
+ @block.call lhs, rhs
35
+ end
36
+ end
37
+
38
+ # Ast-Node that takes two Ints, Naturals, Bools or Texts and
39
+ # returns a Boolean
40
+ class ComparisonOpNode
41
+ attr_accessor :lhs
42
+ attr_accessor :rhs
43
+ attr_accessor :op
44
+
45
+ def initialize(lhs, rhs, op, &block)
46
+ @block = block
47
+ @op = op
48
+ @lhs = lhs
49
+ @rhs = rhs
50
+ end
51
+
52
+ def compute_type(ctx)
53
+ lhs_type, _ = @lhs.compute_type(ctx)
54
+ rhs_type, _ = @rhs.compute_type(ctx)
55
+ case @op
56
+ when "<", "<=" ">", ">="
57
+ assert ("operator \"#{@op}\" expects two numbers as operators") { (Types::Numbers + [Types::Text]).include? lhs_type }
58
+ when "==", "!="
59
+ assert ("operator \"#{@op}\" bad operator type") { (Types::Numbers + [Types::Text, Types::Bool]).include? lhs_type }
60
+ end
61
+ assert ("operator \"#{@op}\" expects two operators of same Type. left: #{lhs_type}, right: #{rhs_type}") { lhs_type == rhs_type }
62
+ Types::Bool
63
+
64
+ end
65
+
66
+ def evaluate(ctx)
67
+ lhs = @lhs.evaluate ctx
68
+ rhs = @rhs.evaluate ctx
69
+ @block.call lhs, rhs
70
+ end
71
+ end
72
+
73
+ class IfThenElseNode
74
+ attr_accessor :cond
75
+ attr_accessor :iftrue
76
+ attr_accessor :iffalse
77
+ def initialize(cond, iftrue, iffalse)
78
+ @cond = cond
79
+ @iftrue = iftrue
80
+ @iffalse = iffalse
81
+ end
82
+
83
+ def compute_type(ctx)
84
+ cond_type, _ = @cond.compute_type(ctx)
85
+ assert ("Condition in If-Then-Else-Statement not of type Bool") { cond_type == Types::Bool }
86
+ then_type = @iftrue.compute_type(ctx)
87
+ else_type = @iffalse.compute_type(ctx)
88
+ assert ("If-Then-Else: expressions in both branches have to be of the same type") { then_type == else_type }
89
+ then_type
90
+ end
91
+
92
+ def evaluate(ctx)
93
+ cond = @cond.evaluate ctx
94
+ if cond
95
+ @iftrue.evaluate ctx
96
+ else
97
+ @iffalse.evaluate ctx
98
+ end
99
+ end
100
+ end
101
+
102
+ class VariableNode
103
+ attr_accessor :varname
104
+ def initialize(varname)
105
+ @varname = varname
106
+ end
107
+
108
+ # returns a Type for a defined Variable @varname that does not have Type 'Type'
109
+ # returns a Value of Type 'Type' if @varname is a Typevariable
110
+ def compute_type(ctx)
111
+ assert ("Undefined Variable \"#{@varname}\"") { !ctx[@varname].nil? }
112
+ type = ctx[@varname]
113
+ assert ("WTF?!") { Types::is_a_type? type }
114
+ ctx[@varname]
115
+ end
116
+
117
+ def evaluate(ctx)
118
+ ctx[@varname]
119
+ end
120
+ end
121
+
122
+ # `parts` should be a list of ruby-strings
123
+ # and other ast-nodes that shall be interpolated.
124
+ class TextInterpolationNode
125
+ attr_accessor :parts
126
+ def initialize(parts)
127
+ @parts = parts
128
+ end
129
+
130
+ def compute_type(ctx)
131
+ parts.each_with_index { |part, idx|
132
+ assert ("TextInterpolationNode: expression at index #{idx} not a Text") { part.is_a? String or part.compute_type(ctx) == Types::Text }
133
+ }
134
+ Types::Text
135
+ end
136
+
137
+ def evaluate(ctx)
138
+ tmp = @parts.reduce("") { |str, part|
139
+ if part.is_a? String
140
+ str + part
141
+ else
142
+ val = part.evaluate ctx
143
+ str + val
144
+ end
145
+ }
146
+ tmp
147
+ end
148
+ end
149
+
150
+ # (let varname [: expected_type] = varval)+ in ...
151
+ # vars shall be [[varname1, expected_type1, varval1], ...]
152
+ # where expected_type can be nil if no type was given
153
+ class LetInNode
154
+ attr_accessor :vars
155
+ attr_accessor :inexpr
156
+ def initialize(vars, inexpr)
157
+ @vars = vars
158
+ @inexpr = inexpr
159
+ end
160
+
161
+ def compute_type(ctx)
162
+ new_ctx = Context.new ctx
163
+ @vars.each { |decl|
164
+ name, annot_type_expr, val = decl
165
+
166
+ act_type = val.compute_type(new_ctx)
167
+
168
+ if !annot_type_expr.nil?
169
+ type_type = annot_type_expr.compute_type(new_ctx)
170
+ assert ("not a type after type annotation") { type_type.is_a? Types::Type }
171
+ annot_type = type_type.metadata
172
+ unification = Types::unification(annot_type, act_type)
173
+ assert ("Actual type of #{name}'s value (#{act_type}) doesn't match annotated type (#{annot_type})") { !unification.nil? }
174
+ end
175
+
176
+ new_ctx[name] = act_type
177
+ }
178
+
179
+ @inexpr.compute_type new_ctx
180
+ end
181
+
182
+ def evaluate(ctx)
183
+ newctx = Context.new(ctx)
184
+ vars.each { |elm|
185
+ varname, expected_type_ast, expr = elm
186
+ val = expr.evaluate newctx
187
+ newctx[varname] = val
188
+ }
189
+ inexpr.evaluate newctx
190
+ end
191
+ end
192
+
193
+ # argname should be the name of the argument.
194
+ # argtype should be an Ast-Node, as does body.
195
+ class FunctionDefinitionNode
196
+ attr_accessor :argname
197
+ attr_accessor :body
198
+ def initialize(argname, argtype, body)
199
+ @argname = argname
200
+ @argtype = argtype
201
+ @body = body
202
+ end
203
+
204
+ def compute_type(ctx)
205
+ argtype = @argtype.compute_type ctx
206
+ assert("expected type as argument annotation: #{argtype}") { argtype.is_a? Types::Type }
207
+ argtype = argtype.metadata
208
+
209
+ unres = nil
210
+ if argtype.is_a? Types::Type
211
+ assert("DEBUG: wann passiert sowas? #{argtype.inspect}") { argtype.metadata.nil? }
212
+ argtype = Types::Type.new(Types::Unresolved.new(@argname))
213
+ unres = @argname
214
+ end
215
+
216
+ newctx = Context.new ctx
217
+ newctx[@argname] = argtype
218
+
219
+ Types::Function.new(argtype, @body.compute_type(newctx), unres)
220
+ end
221
+
222
+ def evaluate(ctx)
223
+ Function.new(@argname, @body, ctx)
224
+ end
225
+
226
+ end
227
+
228
+ class FunctionCallNode
229
+ attr_accessor :fn
230
+ attr_accessor :arg
231
+ def initialize(fn, arg)
232
+ @fn = fn
233
+ @arg = arg
234
+ end
235
+
236
+ def compute_type(ctx)
237
+ arg_type = @arg.compute_type ctx
238
+ fn_type = @fn.compute_type ctx
239
+
240
+ assert("only functions can be called, not #{fn_type}") { fn_type.is_a? Types::Function }
241
+
242
+ if arg_type.is_a? Types::Type and !fn_type.unres.nil?
243
+ assert ("argument type mismatch: expected: #{fn_type.argtype}, got: #{arg_type}") { fn_type.argtype.is_a? Types::Type }
244
+ Types::resolve(fn_type.restype, fn_type.unres, arg_type.metadata)
245
+ else
246
+ unification = Types::unification(fn_type.argtype, arg_type)
247
+ assert ("argument type mismatch: expected: #{fn_type.argtype}, got: #{arg_type}") { !unification.nil? }
248
+ fn_type.restype
249
+ end
250
+ end
251
+
252
+ def evaluate(ctx)
253
+ fn = @fn.evaluate ctx
254
+ arg = @arg.evaluate ctx
255
+ fn.call arg
256
+ end
257
+ end
258
+
259
+ class ListConcatNode
260
+ def initialize(lhs, rhs)
261
+ @lhs = lhs
262
+ @rhs = rhs
263
+ end
264
+
265
+ def compute_type(ctx)
266
+ lhs_type = @lhs.compute_type(ctx)
267
+ rhs_type = @rhs.compute_type(ctx)
268
+
269
+ assert ("List Concat: left operand not a list but #{lhs_type}") { lhs_type.is_a? Types::List }
270
+ assert ("List Concatenation operands type mismatch. Left: #{lhs_type}, Right: #{rhs_type}") { rhs_type == lhs_type }
271
+
272
+ lhs_type
273
+ end
274
+
275
+ def evaluate(ctx)
276
+ lhs = @lhs.evaluate(ctx)
277
+ rhs = @rhs.evaluate(ctx)
278
+ lhs + rhs
279
+ end
280
+ end
281
+
282
+ class ListNode
283
+ attr_accessor :list
284
+ attr_accessor :type_node
285
+
286
+ def initialize(list, type_node)
287
+ @list = list
288
+ @type_node = type_node
289
+ end
290
+
291
+ def compute_type(ctx)
292
+ elem_type = nil
293
+ if !@type_node.nil?
294
+ annot_type = @type_node.compute_type ctx
295
+ assert ("Annotated Type not a type") { annot_type.is_a? Types::Type }
296
+ elem_type = annot_type.metadata
297
+ end
298
+
299
+ if @list.length > 0
300
+ if !elem_type.nil?
301
+ assert ("First list element's type mismatches annotated type") { list[0].compute_type(ctx) == elem_type }
302
+ else
303
+ elem_type = list[0].compute_type(ctx)
304
+ end
305
+
306
+ @list.each_with_index { |e, idx|
307
+ t = e.compute_type(ctx)
308
+ assert ("Type mismatch: element at index #{idx} has type #{t}, expected #{elem_type}") { t == elem_type }
309
+ }
310
+ end
311
+
312
+ Types::List.new elem_type
313
+ end
314
+
315
+ def evaluate(ctx)
316
+ res = []
317
+ list.each { |node|
318
+ res.append node.evaluate(ctx)
319
+ }
320
+ res
321
+ end
322
+ end
323
+
324
+ class OptionalNode
325
+ # expression should be a some value for prefix "Some" or a Type for prefix "None"
326
+ attr_accessor :expression
327
+
328
+ def initialize(prefix, expression)
329
+ @isSome = (prefix == "Some")
330
+ @expression = expression
331
+ end
332
+
333
+ def compute_type(ctx)
334
+ if @isSome
335
+ elem_type = @expression.compute_type ctx
336
+ else
337
+ type_type = @expression.compute_type ctx
338
+ assert ("Expression after \"None\" not a type") { type_type.is_a? Types::Type }
339
+ elem_type = type_type.metadata
340
+ end
341
+ Types::Optional.new elem_type
342
+ end
343
+
344
+ def evaluate(ctx)
345
+ if @isSome
346
+ @expression.evaluate(ctx)
347
+ else
348
+ nil
349
+ end
350
+ end
351
+ end
352
+
353
+ class TypeAnnotationNode
354
+ attr_accessor :expr
355
+ attr_accessor :type
356
+ def initialize(expr, type)
357
+ @expr = expr
358
+ @type = type
359
+ end
360
+
361
+ def compute_type(ctx)
362
+ act_type = @expr.compute_type ctx
363
+ type_type = @type.compute_type ctx
364
+ assert ("Annotated expression not a type") { type_type.is_a? Types::Type }
365
+ exp_type = type_type.metadata
366
+ assert ("Expression does not match annotated type. Actual: #{act_type}, expected: #{exp_type}") { Types::unification act_type, exp_type }
367
+ exp_type
368
+ end
369
+
370
+ def evaluate(ctx)
371
+ @expr.evaluate ctx
372
+ end
373
+ end
374
+
375
+ # `hash` shall map Keys to Ast-Nodes
376
+ class RecordNode
377
+ attr_accessor :hash
378
+ def initialize(hash)
379
+ @hash = hash
380
+ end
381
+
382
+ def compute_type(ctx)
383
+ types = {}
384
+ @hash.each { |name, expr|
385
+ types[name] = expr.compute_type ctx
386
+ }
387
+ Types::Record.new types
388
+ end
389
+
390
+ def evaluate(ctx)
391
+ vals = {}
392
+ @hash.each { |key, node|
393
+ vals[key] = node.evaluate ctx
394
+ }
395
+ vals
396
+ end
397
+ end
398
+
399
+ class RecordTypeNode
400
+ attr_accessor :hash
401
+ def initialize(hash)
402
+ @hash = hash
403
+ end
404
+
405
+ def compute_type(ctx)
406
+ types = {}
407
+ @hash.each { |name, type_expr|
408
+ type_type = type_expr.compute_type ctx
409
+ assert ("annotated expression of record member \"#{name}\" not a type") { type_type.is_a? Types::Type }
410
+ types[name] = type_type.metadata
411
+ }
412
+
413
+ Types::Type.new(Types::Record.new types)
414
+ end
415
+
416
+ def evaluate(ctx)
417
+ types = {}
418
+ @hash.each { |key, node|
419
+ types[key] = node.evaluate ctx
420
+ }
421
+ Types::Record.new types
422
+ end
423
+ end
424
+
425
+ # rec: ast node of something that will be a record, key: name of key to access in record (string)
426
+ class RecordUnionSelector
427
+ attr_accessor :rec
428
+ attr_accessor :key
429
+ def initialize(rec, key)
430
+ @rec = rec
431
+ @key = key
432
+ end
433
+
434
+ def compute_type(ctx)
435
+ rectype = @rec.compute_type ctx
436
+ assert("`.` can only be used on records and unions, the key `#{@key}` must exist") {
437
+ ((rectype.is_a? Types::Record or rectype.is_a? Types::Union) and !rectype.types[@key].nil?) \
438
+ or (rectype.is_a? Types::Type and rectype.metadata.is_a? Types::Union)
439
+ }
440
+ if rectype.is_a? Types::Union
441
+ Types::Optional.new rectype.types[@key]
442
+ elsif rectype.is_a? Types::Record
443
+ rectype.types[@key]
444
+ else
445
+ union_type = rectype.metadata
446
+ Types::Function.new union_type.types[@key], union_type
447
+ end
448
+ end
449
+
450
+ def evaluate(ctx)
451
+ rec = @rec.evaluate ctx
452
+ if rec.is_a? Union
453
+ rec.select @key
454
+ elsif rec.is_a? Hash # <== Record
455
+ rec[@key]
456
+ else
457
+ BuiltinFunction.new { |val| Union.new @key, val, rec }
458
+ end
459
+ end
460
+ end
461
+
462
+ # keys: ruby-list of keys to access (strings)
463
+ class RecordProjection
464
+ attr_accessor :rec
465
+ attr_accessor :keys
466
+ def initialize(rec, keys)
467
+ @rec = rec
468
+ @keys = keys
469
+ end
470
+
471
+ def compute_type(ctx)
472
+ rectype = @rec.compute_type ctx
473
+ assert("`.` can only be used on records") { rectype.is_a? Types::Record }
474
+ newrectype = {}
475
+ @keys.each { |key|
476
+ assert("missing key in projection: `#{key}`") { !rectype.types[key].nil? }
477
+ newrectype[key] = rectype.types[key]
478
+ }
479
+ Types::Record.new(newrectype)
480
+ end
481
+
482
+ def evaluate(ctx)
483
+ rec = @rec.evaluate ctx
484
+ newrecvals = {}
485
+ @keys.each { |key|
486
+ newrecvals[key] = rec[key]
487
+ }
488
+ newrecvals
489
+ end
490
+ end
491
+
492
+ class RecordRecursiveMergeNode
493
+ attr_accessor :lhs
494
+ attr_accessor :rhs
495
+
496
+ # lhs and rhs should be RecordNodes
497
+ def initialize(lhs, rhs)
498
+ @lhs = lhs
499
+ @rhs = rhs
500
+ end
501
+
502
+ def compute_type(ctx)
503
+ lhs = @lhs.compute_type ctx
504
+ rhs = @rhs.compute_type ctx
505
+ ::Dhallish::mergeRecordTypes(lhs, rhs)
506
+ end
507
+
508
+ def evaluate(ctx)
509
+ lhs = @lhs.evaluate(ctx)
510
+ rhs = @rhs.evaluate(ctx)
511
+ ::Dhallish::mergeRecordsRecursively(lhs, rhs)
512
+ end
513
+ end
514
+
515
+ class RecordNonRecursiveMergeNode
516
+ attr_accessor :lhs
517
+ attr_accessor :rhs
518
+
519
+ # lhs and rhs should be RecordNodes
520
+ def initialize(lhs, rhs)
521
+ @lhs = lhs
522
+ @rhs = rhs
523
+ end
524
+
525
+ def compute_type(ctx)
526
+ lhs = @lhs.compute_type ctx
527
+ rhs = @rhs.compute_type ctx
528
+ ::Dhallish::mergeRecordTypesPrefereRight(lhs, rhs)
529
+ end
530
+
531
+ def evaluate(ctx)
532
+ lhs = @lhs.evaluate(ctx)
533
+ rhs = @rhs.evaluate(ctx)
534
+ ::Dhallish::mergeRecordsPrefereRight(lhs, rhs)
535
+ end
536
+ end
537
+
538
+ class RecordTypeRecursiveMergeNode
539
+ attr_accessor :lhs
540
+ attr_accessor :rhs
541
+
542
+ def initialize(lhs, rhs)
543
+ @lhs = lhs
544
+ @rhs = rhs
545
+ end
546
+
547
+ def compute_type(ctx)
548
+ lhs = @lhs.compute_type ctx
549
+ rhs = @rhs.compute_type ctx
550
+ assert("`//\\\\` can only merge record types") {
551
+ lhs.is_a? Types::Type and rhs.is_a? Types::Type and
552
+ lhs.metadata.is_a? Types::Record and rhs.metadata.is_a? Types::Record }
553
+ Types::Type.new(::Dhallish::mergeRecordTypes(lhs.metadata, rhs.metadata))
554
+ end
555
+
556
+ def evaluate(ctx)
557
+ lhs = @lhs.evaluate(ctx)
558
+ rhs = @rhs.evaluate(ctx)
559
+ ::Dhallish::mergeRecordTypes(lhs, rhs)
560
+ end
561
+ end
562
+
563
+ class FunctionType
564
+ attr_accessor :lhs
565
+ attr_accessor :rhs
566
+ attr_accessor :type_var
567
+
568
+ # lhs and rhs should be values of type Type
569
+ # if Function Type contains "forall" , e.g. "forall(t : Type) -> Natural", type_var should contain the introduced label as a string
570
+ # otherwise, type_var shall be nil
571
+ def initialize(lhs, rhs, type_var=nil)
572
+ @lhs = lhs
573
+ @rhs = rhs
574
+ @type_var = type_var
575
+ end
576
+
577
+ def compute_type(ctx)
578
+ lhs_type = @lhs.compute_type ctx
579
+ assert ("Expression for argument type not a type") { lhs_type.is_a? Types::Type }
580
+ lhs_type = lhs_type.metadata
581
+ new_ctx = ctx
582
+ type_var = nil
583
+ if !@type_var.nil? and lhs_type.is_a? Types::Type
584
+ new_ctx = Context.new ctx
585
+ lhs_type = new_ctx[@type_var] = Types::Type.new (Types::Unresolved.new @type_var)
586
+ type_var = @type_var
587
+ end
588
+ rhs_type = @rhs.compute_type new_ctx
589
+ assert ("Expression for result type not a type") { rhs_type.is_a? Types::Type }
590
+ rhs_type = rhs_type.metadata
591
+ Types::Type.new (Types::Function.new lhs_type, rhs_type, type_var)
592
+ end
593
+
594
+ def evaluate(ctx)
595
+ lhs = @lhs.evaluate(ctx)
596
+
597
+ if !type_var.nil? and lhs.is_a? Types::Type
598
+ new_ctx = Context.new(ctx)
599
+ new_ctx[type_var] = Types::Unresolved.new(type_var)
600
+ rhs = @rhs.evaluate(new_ctx)
601
+ else
602
+ rhs = @rhs.evaluate(ctx)
603
+ end
604
+
605
+ Types::Function.new(lhs, rhs, type_var)
606
+ end
607
+ end
608
+
609
+ class Import
610
+ attr_accessor :prefix
611
+ attr_accessor :src
612
+ attr_accessor :import_as_text
613
+ attr_accessor :buf
614
+
615
+ def initialize(prefix, src, import_as_text)
616
+ @prefix = prefix
617
+ @src = src
618
+ @import_as_text = import_as_text
619
+ @buf = nil
620
+ end
621
+
622
+ def compute_type(ctx)
623
+ new_path = nil
624
+ text = nil
625
+ if prefix == "env:"
626
+ text = ENV[@src]
627
+ if text.nil?
628
+ raise NameError, "#{@src}: no such environment variable set"
629
+ end
630
+ else
631
+ src = @src
632
+ if prefix == "./" or prefix == "../"
633
+ basedir = ctx["<#DIR#>"]
634
+ if basedir.is_a? String
635
+ src = File.join basedir , (@prefix + src)
636
+ new_path = File.dirname src
637
+ else
638
+ opath = basedir.path
639
+ basedir.path = File.join basedir.path, (@prefix + src)
640
+ src = basedir.to_s
641
+ basedir.path = File.dirname basedir.path
642
+ new_path = URI basedir.to_s
643
+ basedir.path = opath
644
+ end
645
+ elsif prefix == "~/"
646
+ # ruby does not expand '~'
647
+ src = File.join ENV['HOME'], src
648
+ new_path = ENV['HOME']
649
+ else
650
+ src = @prefix + src
651
+ new_path = URI src
652
+ new_path.path = File.dirname new_path.path
653
+ end
654
+
655
+ file = open(src)
656
+ text = file.read
657
+
658
+ if text.nil?
659
+ raise IOError, "url or file not found"
660
+ end
661
+ end
662
+
663
+ if @import_as_text
664
+ @buf = text
665
+ Types::Text
666
+ else
667
+ # treat as dhallish expression
668
+ @buf, type = Dhallish::evaluate(text, Dhallish::empty_context(new_path))
669
+ type
670
+ end
671
+ end
672
+
673
+ def evaluate(ctx) @buf end
674
+ end
675
+
676
+ class UnionLiteral
677
+ # map: label -> type, can be empty
678
+ attr_accessor :map
679
+ attr_accessor :init_label
680
+ attr_accessor :init_val
681
+ attr_accessor :type
682
+
683
+ def initialize(map, init_label, init_val)
684
+ @map = map
685
+ @init_label = init_label
686
+ @init_val = init_val
687
+ end
688
+
689
+ def compute_type(ctx)
690
+ types = {}
691
+ @map.keys.each { |key|
692
+ node = @map[key]
693
+ assert ("duplicate labels not allowed in union literals") { !types.include? key }
694
+ type_type = node.compute_type(ctx)
695
+ assert ("Type annotation in Union Literal not a type") { type_type.is_a? Types::Type }
696
+ @map[key] = types[key] = type_type.metadata
697
+ }
698
+ assert ("duplicate labels not allowed in union literals") { !types.include? @init_label }
699
+ types[@init_label] = @init_val.compute_type(ctx)
700
+ @type = Types::Union.new(types)
701
+ @type
702
+ end
703
+
704
+ def evaluate(ctx)
705
+ Union.new @init_label, @init_val.evaluate(ctx), @type
706
+ end
707
+ end
708
+
709
+ class UnionType
710
+ # map: maps labels to ast-nodes that should be types
711
+ attr_accessor :map
712
+
713
+ def initialize(map)
714
+ @map = map
715
+ end
716
+
717
+ def compute_type(ctx)
718
+ types_map = {}
719
+ @map.each { |key, type|
720
+ assert ("Duplicate labels in Union Types are not allowed") { !types_map.include? key }
721
+ type_type = type.compute_type ctx
722
+ assert ("annotated type in union type not a type") { type_type.is_a? Types::Type }
723
+ types_map[key] = type_type.metadata
724
+ }
725
+ Types::Type.new(Types::Union.new types_map)
726
+ end
727
+
728
+ def evaluate(ctx)
729
+ res = {}
730
+ @map.each { |key, node|
731
+ res[key] = node.evaluate ctx
732
+ }
733
+ Types::Union.new(res)
734
+ end
735
+ end
736
+
737
+ class UnionMerge
738
+ attr_accessor :handler
739
+ attr_accessor :union
740
+ def initialize(handler, union)
741
+ @handler = handler
742
+ @union = union
743
+ end
744
+
745
+ def compute_type(ctx)
746
+ hdlr = @handler.compute_type ctx
747
+ unin = @union.compute_type ctx
748
+
749
+ assert("`merge` expects a record as first and a union as second argument (both with the same keys and they shall not be empty)") {
750
+ hdlr.is_a? Types::Record and unin.is_a? Types::Union and hdlr.types.size == unin.types.size and hdlr.types.size > 0
751
+ }
752
+ rettype = nil
753
+ hdlr.types.each { |key, fntype|
754
+ argtype = unin.types[key]
755
+ assert("`merge` record (of functions) and union must have the same fields") { !argtype.nil? and fntype.is_a? Types::Function }
756
+ if rettype.nil?
757
+ rettype = fntype.restype
758
+ else
759
+ # TODO: unification instead of ==
760
+ assert("`merge` functions must all have the same return type") { fntype.restype == rettype }
761
+ end
762
+ assert("`merge` functions must take as argument the type of its union field") { fntype.argtype == argtype }
763
+ }
764
+ rettype
765
+ end
766
+
767
+ def evaluate(ctx)
768
+ hdlr = @handler.evaluate ctx
769
+ unin = @union.evaluate ctx
770
+ fn = hdlr[unin.init_label]
771
+ fn.call(unin.init_val)
772
+ end
773
+ end
774
+
775
+ class Import_Alternative
776
+ attr_accessor :lhs
777
+ attr_accessor :rhs
778
+
779
+ def initialize(lhs, rhs)
780
+ @lhs = lhs
781
+ @rhs = rhs
782
+ @lhs_succeded = false
783
+ end
784
+
785
+ def compute_type(ctx)
786
+ begin
787
+ type = @lhs.compute_type ctx
788
+ @lhs_succeded = true
789
+ type
790
+ rescue AssertionError, DhallError => e
791
+ raise e
792
+ rescue
793
+ @rhs.compute_type ctx
794
+ end
795
+ end
796
+
797
+ def evaluate(ctx)
798
+ if @lhs_succeded
799
+ @lhs.evaluate ctx
800
+ else
801
+ @rhs.evaluate ctx
802
+ end
803
+ end
804
+ end
805
+
806
+ class Literal_Node
807
+ attr_accessor :val
808
+ attr_accessor :type
809
+
810
+ def initialize(val, type)
811
+ @val = val
812
+ @type = type
813
+ end
814
+
815
+ def compute_type(ctx)
816
+ @type
817
+ end
818
+
819
+ def evaluate(ctx)
820
+ @val
821
+ end
822
+ end
823
+
824
+ class GetContext
825
+ def initialize() @typectx = nil end
826
+
827
+ def compute_type(ctx)
828
+ @typectx = ctx
829
+ Types::Record.new({})
830
+ end
831
+
832
+ def evaluate(ctx) { "<#TYPES#>" => @typectx, "<#VALS#>" => ctx } end
833
+ end
834
+
835
+ end
836
+ end