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.
- checksums.yaml +7 -0
- data/bin/dhallish +65 -0
- data/lib/DhallishGrammar.rb +7338 -0
- data/lib/DhallishGrammar.treetop +498 -0
- data/lib/ast.rb +836 -0
- data/lib/dhallish.rb +70 -0
- data/lib/stdlib.rb +268 -0
- data/lib/types.rb +440 -0
- data/lib/utils.rb +46 -0
- metadata +68 -0
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
|