dhallish 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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