dhall 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,383 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "promise.rb"
5
+ require "set"
6
+
7
+ require "dhall/ast"
8
+ require "dhall/binary"
9
+ require "dhall/util"
10
+
11
+ module Dhall
12
+ class ImportFailedException < StandardError; end
13
+ class ImportBannedException < ImportFailedException; end
14
+ class ImportLoopException < ImportBannedException; end
15
+
16
+ module Resolvers
17
+ ReadPathSources = lambda do |sources|
18
+ sources.map do |source|
19
+ Promise.resolve(nil).then { source.pathname.binread }
20
+ end
21
+ end
22
+
23
+ PreflightCORS = lambda do |source, parent_origin|
24
+ uri = source.uri
25
+ if parent_origin != "localhost" && parent_origin != source.origin
26
+ req = Net::HTTP::Options.new(uri)
27
+ req["Origin"] = parent_origin
28
+ req["Access-Control-Request-Method"] = "GET"
29
+ req["Access-Control-Request-Headers"] =
30
+ source.headers.map { |h| h.fetch("header").to_s }.join(",")
31
+ r = Net::HTTP.start(
32
+ uri.hostname,
33
+ uri.port,
34
+ use_ssl: uri.scheme == "https"
35
+ ) { |http| http.request(req) }
36
+
37
+ raise ImportFailedException, source if r.code != "200"
38
+ unless r["Access-Control-Allow-Origin"] == parent_origin ||
39
+ r["Access-Control-Allow-Origin"] == "*"
40
+ raise ImportBannedException, source
41
+ end
42
+ end
43
+ end
44
+
45
+ ReadHttpSources = lambda do |sources, parent_origin|
46
+ sources.map do |source|
47
+ Promise.resolve(nil).then do
48
+ PreflightCORS.call(source, parent_origin)
49
+ uri = source.uri
50
+ req = Net::HTTP::Get.new(uri)
51
+ source.headers.each do |header|
52
+ req[header.fetch("header").to_s] = header.fetch("value").to_s
53
+ end
54
+ r = Net::HTTP.start(
55
+ uri.hostname,
56
+ uri.port,
57
+ use_ssl: uri.scheme == "https"
58
+ ) { |http| http.request(req) }
59
+
60
+ raise ImportFailedException, source if r.code != "200"
61
+ r.body
62
+ end
63
+ end
64
+ end
65
+
66
+ RejectSources = lambda do |sources|
67
+ sources.map do |source|
68
+ Promise.new.reject(ImportBannedException.new(source))
69
+ end
70
+ end
71
+
72
+ class ReadPathAndIPFSSources
73
+ def initialize(
74
+ path_reader: ReadPathSources,
75
+ http_reader: ReadHttpSources,
76
+ https_reader: http_reader,
77
+ public_gateway: "cloudflare-ipfs.com"
78
+ )
79
+ @path_reader = path_reader
80
+ @http_reader = http_reader
81
+ @https_reader = https_reader
82
+ @public_gateway = public_gateway
83
+ end
84
+
85
+ def arity
86
+ 1
87
+ end
88
+
89
+ def call(sources)
90
+ @path_reader.call(sources).map.with_index do |promise, idx|
91
+ source = sources[idx]
92
+ if source.is_a?(Import::AbsolutePath) &&
93
+ ["ipfs", "ipns"].include?(source.path.first)
94
+ gateway_fallback(source, promise)
95
+ else
96
+ promise
97
+ end
98
+ end
99
+ end
100
+
101
+ def to_proc
102
+ method(:call).to_proc
103
+ end
104
+
105
+ protected
106
+
107
+ def gateway_fallback(source, promise)
108
+ promise.catch {
109
+ @http_reader.call([
110
+ source.to_uri(Import::Http, "localhost:8000")
111
+ ], "localhost").first
112
+ }.catch do
113
+ @https_reader.call([
114
+ source.to_uri(Import::Https, @public_gateway)
115
+ ], "localhost").first
116
+ end
117
+ end
118
+ end
119
+
120
+ class ResolutionSet
121
+ def initialize(reader)
122
+ @reader = reader
123
+ @parents = []
124
+ @set = Hash.new { |h, k| h[k] = [] }
125
+ end
126
+
127
+ def register(source)
128
+ p = Promise.new
129
+ if @parents.include?(source)
130
+ p.reject(ImportLoopException.new(source))
131
+ else
132
+ @set[source] << p
133
+ end
134
+ p
135
+ end
136
+
137
+ def resolutions
138
+ sources, promises = @set.to_a.transpose
139
+ [Array(sources), Array(promises)]
140
+ end
141
+
142
+ def reader
143
+ lambda do |sources|
144
+ if @reader.arity == 2
145
+ @reader.call(sources, @parents.last&.origin || "localhost")
146
+ else
147
+ @reader.call(sources)
148
+ end
149
+ end
150
+ end
151
+
152
+ def child(parent_source)
153
+ dup.tap do |c|
154
+ c.instance_eval do
155
+ @parents = @parents.dup + [parent_source]
156
+ @set = Hash.new { |h, k| h[k] = [] }
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ class Standard
163
+ def initialize(
164
+ path_reader: ReadPathSources,
165
+ http_reader: ReadHttpSources,
166
+ https_reader: http_reader
167
+ )
168
+ @path_resolutions = ResolutionSet.new(path_reader)
169
+ @http_resolutions = ResolutionSet.new(http_reader)
170
+ @https_resolutions = ResolutionSet.new(https_reader)
171
+ @cache = {}
172
+ end
173
+
174
+ def cache_fetch(key, &fallback)
175
+ @cache.fetch(key) do
176
+ Promise.resolve(nil).then(&fallback).then do |result|
177
+ @cache[key] = result
178
+ end
179
+ end
180
+ end
181
+
182
+ def resolve_path(path_source)
183
+ @path_resolutions.register(path_source)
184
+ end
185
+
186
+ def resolve_http(http_source)
187
+ http_source.headers.resolve(
188
+ resolver: self,
189
+ relative_to: Dhall::Import::RelativePath.new
190
+ ).then do |headers|
191
+ @http_resolutions.register(
192
+ http_source.with(headers: headers.normalize)
193
+ )
194
+ end
195
+ end
196
+
197
+ def resolve_https(https_source)
198
+ https_source.headers.resolve(
199
+ resolver: self,
200
+ relative_to: Dhall::Import::RelativePath.new
201
+ ).then do |headers|
202
+ @https_resolutions.register(
203
+ https_source.with(headers: headers.normalize)
204
+ )
205
+ end
206
+ end
207
+
208
+ def finish!
209
+ [
210
+ @path_resolutions,
211
+ @http_resolutions,
212
+ @https_resolutions
213
+ ].each do |rset|
214
+ Util.match_result_promises(*rset.resolutions, &rset.reader)
215
+ end
216
+ freeze
217
+ end
218
+
219
+ def child(parent_source)
220
+ dup.tap do |c|
221
+ c.instance_eval do
222
+ @path_resolutions = @path_resolutions.child(parent_source)
223
+ @http_resolutions = @http_resolutions.child(parent_source)
224
+ @https_resolutions = @https_resolutions.child(parent_source)
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ class Default < Standard
231
+ def initialize(
232
+ path_reader: ReadPathSources,
233
+ http_reader: ReadHttpSources,
234
+ https_reader: http_reader,
235
+ ipfs_public_gateway: "cloudflare-ipfs.com"
236
+ )
237
+ super(
238
+ path_reader: ReadPathAndIPFSSources.new(
239
+ path_reader: path_reader,
240
+ http_reader: http_reader,
241
+ https_reader: https_reader,
242
+ public_gateway: ipfs_public_gateway
243
+ ),
244
+ http_reader: http_reader,
245
+ https_reader: https_reader
246
+ )
247
+ end
248
+ end
249
+
250
+ class LocalOnly < Standard
251
+ def initialize(path_reader: ReadPathSources)
252
+ super(
253
+ path_reader: path_reader,
254
+ http_reader: RejectSources,
255
+ https_reader: RejectSources
256
+ )
257
+ end
258
+ end
259
+
260
+ class None < Default
261
+ def initialize
262
+ super(
263
+ path_reader: RejectSources,
264
+ http_reader: RejectSources,
265
+ https_reader: RejectSources
266
+ )
267
+ end
268
+ end
269
+ end
270
+
271
+ class ExpressionResolver
272
+ @@registry = {}
273
+
274
+ def self.for(expr)
275
+ @@registry.find { |k, _| k === expr }.last.new(expr)
276
+ end
277
+
278
+ def self.register_for(kase)
279
+ @@registry[kase] = self
280
+ end
281
+
282
+ def initialize(expr)
283
+ @expr = expr
284
+ end
285
+
286
+ def resolve(**kwargs)
287
+ Util.promise_all_hash(
288
+ @expr.to_h.each_with_object({}) { |(attr, value), h|
289
+ h[attr] = ExpressionResolver.for(value).resolve(**kwargs)
290
+ }
291
+ ).then { |h| @expr.with(h) }
292
+ end
293
+
294
+ class ImportResolver < ExpressionResolver
295
+ register_for Import
296
+
297
+ def resolve(resolver:, relative_to:)
298
+ Promise.resolve(nil).then do
299
+ resolver.cache_fetch(@expr.cache_key(relative_to)) do
300
+ resolve_raw(resolver: resolver, relative_to: relative_to)
301
+ end
302
+ end
303
+ end
304
+
305
+ def resolve_raw(resolver:, relative_to:)
306
+ real_path = @expr.real_path(relative_to)
307
+ real_path.resolve(resolver).then do |result|
308
+ @expr.parse_and_check(result).resolve(
309
+ resolver: resolver.child(real_path),
310
+ relative_to: real_path
311
+ )
312
+ end
313
+ end
314
+ end
315
+
316
+ class FallbackResolver < ExpressionResolver
317
+ register_for Operator::ImportFallback
318
+
319
+ def resolve(**kwargs)
320
+ ExpressionResolver.for(@expr.lhs).resolve(**kwargs).catch do
321
+ ExpressionResolver.for(@expr.rhs).resolve(**kwargs)
322
+ end
323
+ end
324
+ end
325
+
326
+ class ArrayResolver < ExpressionResolver
327
+ register_for Util::ArrayOf.new(Expression)
328
+
329
+ def resolve(**kwargs)
330
+ Promise.all(
331
+ @expr.map { |e| ExpressionResolver.for(e).resolve(**kwargs) }
332
+ )
333
+ end
334
+ end
335
+
336
+ class HashResolver < ExpressionResolver
337
+ register_for Util::HashOf.new(
338
+ ValueSemantics::Anything,
339
+ ValueSemantics::Either.new([Expression, nil])
340
+ )
341
+
342
+ def resolve(**kwargs)
343
+ Util.promise_all_hash(Hash[@expr.map do |k, v|
344
+ [k, ExpressionResolver.for(v).resolve(**kwargs)]
345
+ end])
346
+ end
347
+ end
348
+
349
+ class RecordResolver < ExpressionResolver
350
+ register_for Record
351
+
352
+ def resolve(**kwargs)
353
+ ExpressionResolver.for(@expr.record).resolve(**kwargs).then do |h|
354
+ @expr.with(record: h)
355
+ end
356
+ end
357
+ end
358
+
359
+ register_for Expression
360
+
361
+ class IdentityResolver < ExpressionResolver
362
+ register_for Object
363
+
364
+ def resolve(*)
365
+ Promise.resolve(@expr)
366
+ end
367
+ end
368
+ end
369
+
370
+ class Expression
371
+ def resolve(
372
+ resolver: Resolvers::Default.new,
373
+ relative_to: Import::Path.from_string(Pathname.pwd + "file")
374
+ )
375
+ p = ExpressionResolver.for(self).resolve(
376
+ resolver: resolver,
377
+ relative_to: relative_to
378
+ )
379
+ resolver.finish!
380
+ p
381
+ end
382
+ end
383
+ end
@@ -0,0 +1,1209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dhall/ast"
4
+ require "dhall/normalize"
5
+
6
+ module Dhall
7
+ module TypeChecker
8
+ def self.assert(type, assertion, message)
9
+ raise TypeError, message unless assertion === type
10
+ type
11
+ end
12
+
13
+ def self.assert_type(expr, assertion, message, context:)
14
+ aexpr = self.for(expr).annotate(context)
15
+ type = aexpr.type
16
+ raise TypeError, "#{message}: #{type}" unless assertion === type
17
+ aexpr
18
+ end
19
+
20
+ def self.assert_types_match(a, b, message, context:)
21
+ atype = self.for(a).annotate(context).type
22
+ btype = self.for(b).annotate(context).type
23
+ raise TypeError, "#{message}: #{atype}, #{btype}" unless atype == btype
24
+ atype
25
+ end
26
+
27
+ def self.for(expr)
28
+ @typecheckers.each do |node_matcher, (typechecker, extras)|
29
+ if node_matcher === expr
30
+ msg = [:call, :for, :new].find { |m| typechecker.respond_to?(m) }
31
+ return typechecker.public_send(msg, expr, *extras)
32
+ end
33
+ end
34
+
35
+ raise TypeError, "Unknown expression: #{expr.inspect}"
36
+ end
37
+
38
+ def self.register(typechecker, node_type, *extras)
39
+ @typecheckers ||= {}
40
+ @typecheckers[node_type] ||= [typechecker, extras]
41
+ end
42
+
43
+ def self.type_of(expr)
44
+ return if expr.nil?
45
+ TypeChecker.for(expr).annotate(TypeChecker::Context.new).type
46
+ end
47
+
48
+ class Context
49
+ def initialize(bindings=Hash.new([]))
50
+ @bindings = bindings.freeze
51
+ freeze
52
+ end
53
+
54
+ def fetch(var)
55
+ @bindings[var.name][var.index] ||
56
+ (raise TypeError, "Free variable: #{var}")
57
+ end
58
+
59
+ def add(ftype)
60
+ self.class.new(@bindings.merge(
61
+ ftype.var => [ftype.type] + @bindings[ftype.var]
62
+ )).shift(1, ftype.var, 0)
63
+ end
64
+
65
+ def shift(amount, name, min_index)
66
+ self.class.new(@bindings.merge(
67
+ Hash[@bindings.map do |var, bindings|
68
+ [var, bindings.map { |b| b.shift(amount, name, min_index) }]
69
+ end]
70
+ ))
71
+ end
72
+ end
73
+
74
+ KINDS = [
75
+ Dhall::Variable["Type"],
76
+ Dhall::Variable["Kind"],
77
+ Dhall::Variable["Sort"]
78
+ ].freeze
79
+
80
+ class Variable
81
+ TypeChecker.register self, Dhall::Variable
82
+
83
+ def initialize(var)
84
+ @var = var
85
+ end
86
+
87
+ BUILTIN = {
88
+ "Type" => Dhall::Variable["Kind"],
89
+ "Kind" => Dhall::Variable["Sort"],
90
+ "Bool" => Dhall::Variable["Type"],
91
+ "Natural" => Dhall::Variable["Type"],
92
+ "Integer" => Dhall::Variable["Type"],
93
+ "Double" => Dhall::Variable["Type"],
94
+ "Text" => Dhall::Variable["Type"],
95
+ "List" => Dhall::Forall.of_arguments(
96
+ Dhall::Variable["Type"],
97
+ body: Dhall::Variable["Type"]
98
+ ),
99
+ "Optional" => Dhall::Forall.of_arguments(
100
+ Dhall::Variable["Type"],
101
+ body: Dhall::Variable["Type"]
102
+ ),
103
+ "None" => Dhall::Forall.new(
104
+ var: "A",
105
+ type: Dhall::Variable["Type"],
106
+ body: Dhall::Application.new(
107
+ function: Dhall::Variable["Optional"],
108
+ argument: Dhall::Variable["A"]
109
+ )
110
+ )
111
+ }.freeze
112
+
113
+ def annotate(context)
114
+ raise TypeError, "Sort has no Type, Kind, or Sort" if @var.name == "Sort"
115
+
116
+ Dhall::TypeAnnotation.new(
117
+ value: @var,
118
+ type: BUILTIN.fetch(@var.name) { context.fetch(@var) }
119
+ )
120
+ end
121
+ end
122
+
123
+ class Literal
124
+ TypeChecker.register self, Dhall::Bool
125
+ TypeChecker.register self, Dhall::Natural
126
+ TypeChecker.register self, Dhall::Text
127
+ TypeChecker.register self, Dhall::Integer
128
+ TypeChecker.register self, Dhall::Double
129
+
130
+ def initialize(lit)
131
+ @lit = lit
132
+ @type = Dhall::Variable[lit.class.name.split(/::/).last]
133
+ end
134
+
135
+ def annotate(*)
136
+ Dhall::TypeAnnotation.new(
137
+ value: @lit,
138
+ type: @type
139
+ )
140
+ end
141
+ end
142
+
143
+ class TextLiteral
144
+ TypeChecker.register self, Dhall::TextLiteral
145
+
146
+ def initialize(lit)
147
+ @lit = lit
148
+ end
149
+
150
+ class Chunks
151
+ def initialize(chunks)
152
+ @chunks = chunks
153
+ end
154
+
155
+ def map
156
+ self.class.new(@chunks.map { |c|
157
+ if c.is_a?(Dhall::Text)
158
+ c
159
+ else
160
+ yield c
161
+ end
162
+ })
163
+ end
164
+
165
+ def to_a
166
+ @chunks
167
+ end
168
+ end
169
+
170
+ def annotate(context)
171
+ chunks = Chunks.new(@lit.chunks).map { |c|
172
+ TypeChecker.for(c).annotate(context).tap do |annotated|
173
+ TypeChecker.assert annotated.type, Dhall::Variable["Text"],
174
+ "Cannot interpolate #{annotated.type}"
175
+ end
176
+ }.to_a
177
+
178
+ Dhall::TypeAnnotation.new(
179
+ value: @lit.with(chunks: chunks),
180
+ type: Dhall::Variable["Text"]
181
+ )
182
+ end
183
+ end
184
+
185
+ class If
186
+ TypeChecker.register self, Dhall::If
187
+
188
+ def initialize(expr)
189
+ @expr = expr
190
+ @predicate = TypeChecker.for(expr.predicate)
191
+ @then = TypeChecker.for(expr.then)
192
+ @else = TypeChecker.for(expr.else)
193
+ end
194
+
195
+ class AnnotatedIf
196
+ def initialize(expr, apred, athen, aelse, context:)
197
+ TypeChecker.assert apred.type, Dhall::Variable["Bool"],
198
+ "If must have a predicate of type Bool"
199
+ TypeChecker.assert_type athen.type, Dhall::Variable["Type"],
200
+ "If branches must have types of type Type",
201
+ context: context
202
+ TypeChecker.assert aelse.type, athen.type,
203
+ "If branches have mismatched types"
204
+ @expr = expr.with(predicate: apred, then: athen, else: aelse)
205
+ end
206
+
207
+ def annotation
208
+ Dhall::TypeAnnotation.new(
209
+ value: @expr,
210
+ type: @expr.then.type
211
+ )
212
+ end
213
+ end
214
+
215
+ def annotate(context)
216
+ AnnotatedIf.new(
217
+ @expr,
218
+ @predicate.annotate(context),
219
+ @then.annotate(context),
220
+ @else.annotate(context),
221
+ context: context
222
+ ).annotation
223
+ end
224
+ end
225
+
226
+ class Operator
227
+ {
228
+ Dhall::Operator::And => Dhall::Variable["Bool"],
229
+ Dhall::Operator::Or => Dhall::Variable["Bool"],
230
+ Dhall::Operator::Equal => Dhall::Variable["Bool"],
231
+ Dhall::Operator::NotEqual => Dhall::Variable["Bool"],
232
+ Dhall::Operator::Plus => Dhall::Variable["Natural"],
233
+ Dhall::Operator::Times => Dhall::Variable["Natural"],
234
+ Dhall::Operator::TextConcatenate => Dhall::Variable["Text"]
235
+ }.each do |node_type, type|
236
+ TypeChecker.register self, node_type, type
237
+ end
238
+
239
+ def initialize(expr, type)
240
+ @expr = expr
241
+ @lhs = TypeChecker.for(expr.lhs)
242
+ @rhs = TypeChecker.for(expr.rhs)
243
+ @type = type
244
+ end
245
+
246
+ def annotate(context)
247
+ annotated_lhs = @lhs.annotate(context)
248
+ annotated_rhs = @rhs.annotate(context)
249
+ types = [annotated_lhs.type, annotated_rhs.type]
250
+ if types.any? { |t| t != @type }
251
+ raise TypeError, "Operator arguments not #{@type}: #{types}"
252
+ end
253
+
254
+ Dhall::TypeAnnotation.new(
255
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
256
+ type: @type
257
+ )
258
+ end
259
+ end
260
+
261
+ class OperatorListConcatenate
262
+ TypeChecker.register self, Dhall::Operator::ListConcatenate
263
+
264
+ def initialize(expr)
265
+ @expr = expr
266
+ @lhs = TypeChecker.for(expr.lhs)
267
+ @rhs = TypeChecker.for(expr.rhs)
268
+ end
269
+
270
+ module IsList
271
+ def self.===(other)
272
+ other.is_a?(Dhall::Application) &&
273
+ other.function == Dhall::Variable["List"]
274
+ end
275
+ end
276
+
277
+ def annotate(context)
278
+ annotated_lhs = @lhs.annotate(context)
279
+ annotated_rhs = @rhs.annotate(context)
280
+
281
+ types = [annotated_lhs.type, annotated_rhs.type]
282
+ assertion = Util::ArrayOf.new(Util::AllOf.new(IsList, types.first))
283
+ TypeChecker.assert types, assertion,
284
+ "Operator arguments wrong: #{types}"
285
+
286
+ Dhall::TypeAnnotation.new(
287
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
288
+ type: types.first
289
+ )
290
+ end
291
+ end
292
+
293
+ class OperatorRecursiveRecordMerge
294
+ TypeChecker.register self, Dhall::Operator::RecursiveRecordMerge
295
+
296
+ def initialize(expr)
297
+ @expr = expr
298
+ @lhs = TypeChecker.for(expr.lhs)
299
+ @rhs = TypeChecker.for(expr.rhs)
300
+ end
301
+
302
+ def annotate(context)
303
+ annotated_lhs = @lhs.annotate(context)
304
+ annotated_rhs = @rhs.annotate(context)
305
+
306
+ type = annotated_lhs.type.deep_merge_type(annotated_rhs.type)
307
+
308
+ TypeChecker.assert type, Dhall::RecordType,
309
+ "RecursiveRecordMerge got #{type}"
310
+
311
+ # Annotate to sanity check
312
+ TypeChecker.for(type).annotate(context)
313
+
314
+ Dhall::TypeAnnotation.new(
315
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
316
+ type: type
317
+ )
318
+ end
319
+ end
320
+
321
+ class OperatorRightBiasedRecordMerge
322
+ TypeChecker.register self, Dhall::Operator::RightBiasedRecordMerge
323
+
324
+ def initialize(expr)
325
+ @expr = expr
326
+ end
327
+
328
+ def check(context)
329
+ annotated_lhs = TypeChecker.assert_type @expr.lhs, Dhall::RecordType,
330
+ "RecursiveRecordMerge got",
331
+ context: context
332
+
333
+ annotated_rhs = TypeChecker.assert_type @expr.rhs, Dhall::RecordType,
334
+ "RecursiveRecordMerge got",
335
+ context: context
336
+
337
+ TypeChecker.assert_types_match annotated_lhs.type, annotated_rhs.type,
338
+ "RecursiveRecordMerge got mixed kinds",
339
+ context: context
340
+
341
+ [annotated_lhs, annotated_rhs]
342
+ end
343
+
344
+ def annotate(context)
345
+ annotated_lhs, annotated_rhs = check(context)
346
+
347
+ Dhall::TypeAnnotation.new(
348
+ value: @expr.with(lhs: annotated_lhs, rhs: annotated_rhs),
349
+ type: TypeChecker.for(
350
+ annotated_lhs.type.merge_type(annotated_rhs.type)
351
+ ).annotate(context).value
352
+ )
353
+ end
354
+ end
355
+
356
+ class OperatorRecursiveRecordTypeMerge
357
+ TypeChecker.register self, Dhall::Operator::RecursiveRecordTypeMerge
358
+
359
+ def initialize(expr)
360
+ @expr = expr
361
+ end
362
+
363
+ def annotate(context)
364
+ kind = TypeChecker.assert_types_match(
365
+ @expr.lhs, @expr.rhs,
366
+ "RecursiveRecordTypeMerge mixed kinds",
367
+ context: context
368
+ )
369
+
370
+ type = @expr.lhs.deep_merge_type(@expr.rhs)
371
+
372
+ TypeChecker.assert type, Dhall::RecordType,
373
+ "RecursiveRecordMerge got #{type}"
374
+
375
+ # Annotate to sanity check
376
+ TypeChecker.for(type).annotate(context)
377
+
378
+ Dhall::TypeAnnotation.new(value: @expr, type: kind)
379
+ end
380
+ end
381
+
382
+ class EmptyList
383
+ TypeChecker.register self, Dhall::EmptyList
384
+
385
+ def initialize(expr)
386
+ @expr = expr
387
+ end
388
+
389
+ def annotate(context)
390
+ TypeChecker.assert_type @expr.element_type, Dhall::Variable["Type"],
391
+ "EmptyList element type not of type Type",
392
+ context: context
393
+
394
+ Dhall::TypeAnnotation.new(type: @expr.type, value: @expr)
395
+ end
396
+ end
397
+
398
+ class List
399
+ TypeChecker.register self, Dhall::List
400
+
401
+ def initialize(list)
402
+ @list = list
403
+ end
404
+
405
+ class AnnotatedList
406
+ def initialize(alist)
407
+ @alist = alist
408
+ end
409
+
410
+ def annotation
411
+ list = @alist.with(element_type: element_type)
412
+ Dhall::TypeAnnotation.new(type: list.type, value: list)
413
+ end
414
+
415
+ def element_type
416
+ @alist.first.value&.type || @alist.element_type
417
+ end
418
+
419
+ def element_types
420
+ @alist.to_a.map(&:type)
421
+ end
422
+ end
423
+
424
+ def annotate(context)
425
+ alist = AnnotatedList.new(@list.map(type: @list.element_type) { |el|
426
+ TypeChecker.for(el).annotate(context)
427
+ })
428
+
429
+ TypeChecker.assert alist.element_types,
430
+ Util::ArrayOf.new(alist.element_type),
431
+ "Non-homogenous List"
432
+
433
+ TypeChecker.assert_type alist.element_type, Dhall::Variable["Type"],
434
+ "List type not of type Type", context: context
435
+
436
+ alist.annotation
437
+ end
438
+ end
439
+
440
+ class OptionalNone
441
+ TypeChecker.register self, Dhall::OptionalNone
442
+
443
+ def initialize(expr)
444
+ @expr = expr
445
+ end
446
+
447
+ def annotate(context)
448
+ TypeChecker.assert(
449
+ TypeChecker.for(@expr.value_type).annotate(context).type,
450
+ Dhall::Variable["Type"],
451
+ "OptionalNone element type not of type Type"
452
+ )
453
+
454
+ Dhall::TypeAnnotation.new(type: @expr.type, value: @expr)
455
+ end
456
+ end
457
+
458
+ class Optional
459
+ TypeChecker.register self, Dhall::Optional
460
+
461
+ def initialize(some)
462
+ @some = some
463
+ end
464
+
465
+ def annotate(context)
466
+ asome = @some.map do |el|
467
+ TypeChecker.for(el).annotate(context)
468
+ end
469
+ some = asome.with(value_type: asome.value.type)
470
+
471
+ type_type = TypeChecker.for(some.value_type).annotate(context).type
472
+ TypeChecker.assert type_type, Dhall::Variable["Type"],
473
+ "Some type not of type Type, was: #{type_type}"
474
+
475
+ Dhall::TypeAnnotation.new(type: some.type, value: some)
476
+ end
477
+ end
478
+
479
+ class EmptyAnonymousType
480
+ TypeChecker.register self, Dhall::EmptyRecordType
481
+
482
+ def initialize(expr)
483
+ @expr = expr
484
+ end
485
+
486
+ def annotate(*)
487
+ Dhall::TypeAnnotation.new(
488
+ value: @expr,
489
+ type: Dhall::Variable["Type"]
490
+ )
491
+ end
492
+ end
493
+
494
+ class AnonymousType
495
+ TypeChecker.register self, Dhall::RecordType
496
+ TypeChecker.register self, Dhall::UnionType
497
+
498
+ def initialize(type)
499
+ @type = type
500
+ end
501
+
502
+ def annotate(context)
503
+ kinds = @type.record.values.compact.map do |mtype|
504
+ TypeChecker.for(mtype).annotate(context).type
505
+ end
506
+
507
+ TypeChecker.assert (kinds - KINDS), [],
508
+ "AnonymousType field kind not one of #{KINDS}"
509
+
510
+ TypeChecker.assert kinds, Util::ArrayAllTheSame,
511
+ "AnonymousType field kinds not all the same"
512
+
513
+ type = kinds.first || KINDS.first
514
+ Dhall::TypeAnnotation.new(value: @type, type: type)
515
+ end
516
+ end
517
+
518
+ class EmptyRecord
519
+ TypeChecker.register self, Dhall::EmptyRecord
520
+
521
+ def initialize(expr)
522
+ @expr = expr
523
+ end
524
+
525
+ def annotate(*)
526
+ Dhall::TypeAnnotation.new(
527
+ value: @expr,
528
+ type: Dhall::EmptyRecordType.new
529
+ )
530
+ end
531
+ end
532
+
533
+ class Record
534
+ TypeChecker.register self, Dhall::Record
535
+
536
+ def initialize(record)
537
+ @record = record
538
+ end
539
+
540
+ def annotate(context)
541
+ arecord = @record.map do |k, v|
542
+ [k, TypeChecker.for(v).annotate(context)]
543
+ end
544
+
545
+ Dhall::TypeAnnotation.new(
546
+ value: arecord,
547
+ type: TypeChecker.for(Dhall::RecordType.for(Hash[
548
+ arecord.record.map { |k, v| [k, v.type] }
549
+ ])).annotate(context).value
550
+ )
551
+ end
552
+ end
553
+
554
+ class RecordSelection
555
+ TypeChecker.register self, Dhall::RecordSelection
556
+
557
+ def initialize(selection)
558
+ @selection = selection
559
+ @record = selection.record
560
+ @selector = selection.selector
561
+ end
562
+
563
+ class Selector
564
+ def self.for(annotated_record)
565
+ if annotated_record.type == Dhall::Variable["Type"]
566
+ TypeSelector.new(annotated_record.value)
567
+ elsif annotated_record.type.class == Dhall::RecordType
568
+ new(annotated_record.type)
569
+ else
570
+ raise TypeError, "RecordSelection on #{annotated_record.type}"
571
+ end
572
+ end
573
+
574
+ def initialize(type)
575
+ @fetch_from = type.record
576
+ end
577
+
578
+ def select(selector)
579
+ @fetch_from.fetch(selector) do
580
+ raise TypeError, "#{@fetch_from} has no field #{@selector}"
581
+ end
582
+ end
583
+ end
584
+
585
+ class TypeSelector < Selector
586
+ def initialize(union)
587
+ normalized = union.normalize
588
+ TypeChecker.assert normalized, Dhall::UnionType,
589
+ "RecordSelection on #{normalized}"
590
+ @fetch_from = normalized.constructor_types
591
+ end
592
+ end
593
+
594
+ def annotate(context)
595
+ arecord = TypeChecker.for(@record).annotate(context)
596
+ selector = Selector.for(arecord)
597
+
598
+ Dhall::TypeAnnotation.new(
599
+ value: @selection.with(record: arecord),
600
+ type: selector.select(@selector)
601
+ )
602
+ end
603
+ end
604
+
605
+ class RecordProjection
606
+ TypeChecker.register self, Dhall::EmptyRecordProjection
607
+ TypeChecker.register self, Dhall::RecordProjection
608
+
609
+ def initialize(projection)
610
+ @projection = projection
611
+ @record = TypeChecker.for(projection.record)
612
+ @selectors = projection.selectors
613
+ end
614
+
615
+ def annotate(context)
616
+ arecord = @record.annotate(context)
617
+
618
+ TypeChecker.assert arecord.type.class.name, "Dhall::RecordType",
619
+ "RecordProjection on #{arecord.type}"
620
+
621
+ slice = arecord.type.slice(@selectors)
622
+ TypeChecker.assert slice.keys, @selectors,
623
+ "#{arecord.type} missing one of: #{@selectors}"
624
+
625
+ Dhall::TypeAnnotation.new(
626
+ value: @projection.with(record: arecord),
627
+ type: slice
628
+ )
629
+ end
630
+ end
631
+
632
+ class Union
633
+ TypeChecker.register self, Dhall::Union
634
+
635
+ def initialize(union)
636
+ @union = union
637
+ @value = TypeChecker.for(union.value)
638
+ end
639
+
640
+ def annotate(context)
641
+ annotated_value = @value.annotate(context)
642
+
643
+ type = Dhall::UnionType.new(
644
+ alternatives: { @union.tag => annotated_value.type }
645
+ ).merge(@union.alternatives)
646
+
647
+ # Annotate to sanity check
648
+ TypeChecker.for(type).annotate(context)
649
+
650
+ Dhall::TypeAnnotation.new(
651
+ value: @union.with(value: annotated_value),
652
+ type: type
653
+ )
654
+ end
655
+ end
656
+
657
+ class Merge
658
+ TypeChecker.register self, Dhall::Merge
659
+
660
+ def initialize(merge)
661
+ @merge = merge
662
+ @record = TypeChecker.for(merge.record)
663
+ @union = TypeChecker.for(merge.input)
664
+ end
665
+
666
+ class Handlers
667
+ def initialize(annotation)
668
+ @type = annotation.type
669
+
670
+ TypeChecker.assert @type, Dhall::RecordType,
671
+ "Merge expected Record got: #{@type}"
672
+ end
673
+
674
+ def output_type(output_annotation=nil)
675
+ @type.record.values.reduce(output_annotation) do |type_acc, htype|
676
+ htype = htype.body.shift(-1, htype.var, 0) if htype.is_a?(Dhall::Forall)
677
+
678
+ if type_acc && htype.normalize != type_acc.normalize
679
+ raise TypeError, "Handler output types must all match"
680
+ end
681
+
682
+ htype
683
+ end
684
+ end
685
+
686
+ def keys
687
+ @type.record.keys
688
+ end
689
+
690
+ def fetch_input_type(k)
691
+ type = @type.record.fetch(k) do
692
+ raise TypeError, "No merge handler for alternative: #{k}"
693
+ end
694
+
695
+ TypeChecker.assert type, Dhall::Forall, "Handler is not a function"
696
+
697
+ type.type
698
+ end
699
+ end
700
+
701
+ class AnnotatedMerge
702
+ def initialize(merge:, record:, input:)
703
+ @merge = merge.with(record: record, input: input)
704
+ @handlers = Handlers.new(record)
705
+ @record = record
706
+ @union = input
707
+
708
+ TypeChecker.assert @union.type, Dhall::UnionType,
709
+ "Merge expected Union got: #{@union.type}"
710
+
711
+ assert_union_and_handlers_match
712
+ end
713
+
714
+ def annotation
715
+ Dhall::TypeAnnotation.new(
716
+ value: @merge,
717
+ type: type
718
+ )
719
+ end
720
+
721
+ def type
722
+ @type ||= @handlers.output_type(@merge.type)
723
+ end
724
+
725
+ def assert_kind(context)
726
+ kind = TypeChecker.for(type).annotate(context).type
727
+
728
+ TypeChecker.assert(
729
+ kind,
730
+ Dhall::Variable["Type"],
731
+ "Merge must have kind Type"
732
+ )
733
+
734
+ kind
735
+ end
736
+
737
+ def assert_union_and_handlers_match
738
+ extras = @handlers.keys - @union.type.alternatives.keys
739
+ TypeChecker.assert extras, [],
740
+ "Merge handlers unknown alternatives: #{extras}"
741
+
742
+ @union.type.alternatives.each do |k, atype|
743
+ atype.nil? || TypeChecker.assert(
744
+ @handlers.fetch_input_type(k),
745
+ atype,
746
+ "Handler argument does not match alternative type: #{atype}"
747
+ )
748
+ end
749
+ end
750
+ end
751
+
752
+ def annotate(context)
753
+ amerge = AnnotatedMerge.new(
754
+ merge: @merge,
755
+ record: @record.annotate(context),
756
+ input: @union.annotate(context)
757
+ )
758
+ amerge.assert_kind(context)
759
+ amerge.annotation
760
+ end
761
+ end
762
+
763
+ class Forall
764
+ TypeChecker.register self, Dhall::Forall
765
+
766
+ def initialize(expr)
767
+ @expr = expr
768
+ @var = expr.var
769
+ @var_type = expr.type
770
+ @input = TypeChecker.for(expr.type)
771
+ @output = TypeChecker.for(expr.body)
772
+ end
773
+
774
+ module FunctionKind
775
+ def self.for(inkind, outkind)
776
+ if inkind.nil? || outkind.nil?
777
+ raise TypeError, "FunctionType part of this is a term"
778
+ end
779
+
780
+ raise TypeError, "Dependent types are not allowed" if outkind > inkind
781
+
782
+ if outkind.zero?
783
+ Term.new
784
+ else
785
+ Polymorphic.new(inkind, outkind)
786
+ end
787
+ end
788
+
789
+ class Term
790
+ def kind
791
+ KINDS.first
792
+ end
793
+ end
794
+
795
+ class Polymorphic
796
+ def initialize(inkind, outkind)
797
+ @inkind = inkind
798
+ @outkind = outkind
799
+ end
800
+
801
+ def kind
802
+ KINDS[[@outkind, @inkind].max]
803
+ end
804
+ end
805
+ end
806
+
807
+ def annotate(context)
808
+ inkind = KINDS.index(@input.annotate(context).type)
809
+ outkind = KINDS.index(@output.annotate(context.add(@expr)).type)
810
+
811
+ Dhall::TypeAnnotation.new(
812
+ value: @expr,
813
+ type: FunctionKind.for(inkind, outkind).kind
814
+ )
815
+ end
816
+ end
817
+
818
+ class Function
819
+ TypeChecker.register self, Dhall::Function
820
+
821
+ def initialize(func)
822
+ @func = func
823
+ @type = Dhall::Forall.new(
824
+ var: func.var,
825
+ type: func.type,
826
+ body: Dhall::Variable["UNKNOWN"]
827
+ )
828
+ @output = TypeChecker.for(func.body)
829
+ end
830
+
831
+ def annotate(context)
832
+ abody = @output.annotate(context.add(@type))
833
+
834
+ Dhall::TypeAnnotation.new(
835
+ value: @func.with(body: abody),
836
+ type: TypeChecker.for(
837
+ @type.with(body: abody.type)
838
+ ).annotate(context).value
839
+ )
840
+ end
841
+ end
842
+
843
+ class Application
844
+ TypeChecker.register self, Dhall::Application
845
+
846
+ def initialize(app)
847
+ @app = app
848
+ @func = TypeChecker.for(app.function)
849
+ @arg = app.argument
850
+ end
851
+
852
+ def annotate(context)
853
+ afunc = @func.annotate(context)
854
+
855
+ TypeChecker.assert afunc.type, Dhall::Forall,
856
+ "Application LHS is not a function"
857
+
858
+ aarg = TypeChecker.for(
859
+ Dhall::TypeAnnotation.new(value: @arg, type: afunc.type.type)
860
+ ).annotate(context)
861
+
862
+ Dhall::TypeAnnotation.new(
863
+ value: @app.with(function: afunc, argument: aarg),
864
+ type: afunc.type.call(aarg.value)
865
+ )
866
+ end
867
+ end
868
+
869
+ TypeChecker.register ->(blk) { LetIn.for(blk.unflatten) }, Dhall::LetBlock
870
+
871
+ class LetIn
872
+ TypeChecker.register self, Dhall::LetIn
873
+
874
+ def self.for(letin)
875
+ if letin.let.type
876
+ LetInAnnotated.new(letin)
877
+ else
878
+ LetIn.new(letin)
879
+ end
880
+ end
881
+
882
+ def initialize(letin)
883
+ @letin = letin
884
+ @let = @letin.let
885
+ end
886
+
887
+ def annotate(context)
888
+ alet = @let.with(type: assign_type(context))
889
+ type = TypeChecker.for(@letin.eliminate).annotate(context).type
890
+ abody = Dhall::TypeAnnotation.new(value: @letin.body, type: type)
891
+ Dhall::TypeAnnotation.new(
892
+ value: @letin.with(let: alet, body: abody),
893
+ type: type
894
+ )
895
+ end
896
+
897
+ protected
898
+
899
+ def assign_type(context)
900
+ TypeChecker.for(@let.assign).annotate(context).type
901
+ end
902
+ end
903
+
904
+ class LetInAnnotated < LetIn
905
+ protected
906
+
907
+ def assign_type(context)
908
+ TypeChecker.for(
909
+ Dhall::TypeAnnotation.new(
910
+ value: @let.assign,
911
+ type: @let.type
912
+ )
913
+ ).annotate(context).type
914
+ end
915
+ end
916
+
917
+ class TypeAnnotation
918
+ TypeChecker.register self, Dhall::TypeAnnotation
919
+
920
+ def initialize(expr)
921
+ @expr = expr
922
+ end
923
+
924
+ def annotate(context)
925
+ redo_annotation = TypeChecker.for(@expr.value).annotate(context)
926
+
927
+ if redo_annotation.type.normalize == @expr.type.normalize
928
+ redo_annotation
929
+ else
930
+ raise TypeError, "TypeAnnotation does not match: " \
931
+ "#{@expr.type}, #{redo_annotation.type}"
932
+ end
933
+ end
934
+ end
935
+
936
+ BUILTIN_TYPES = {
937
+ "Natural/build" => Dhall::Forall.of_arguments(
938
+ Dhall::Forall.new(
939
+ var: "natural",
940
+ type: Dhall::Variable["Type"],
941
+ body: Dhall::Forall.new(
942
+ var: "succ",
943
+ type: Dhall::Forall.of_arguments(
944
+ Dhall::Variable["natural"],
945
+ body: Dhall::Variable["natural"]
946
+ ),
947
+ body: Dhall::Forall.new(
948
+ var: "zero",
949
+ type: Dhall::Variable["natural"],
950
+ body: Dhall::Variable["natural"]
951
+ )
952
+ )
953
+ ),
954
+ body: Dhall::Variable["Natural"]
955
+ ),
956
+ "Natural/fold" => Dhall::Forall.of_arguments(
957
+ Dhall::Variable["Natural"],
958
+ body: Dhall::Forall.new(
959
+ var: "natural",
960
+ type: Dhall::Variable["Type"],
961
+ body: Dhall::Forall.new(
962
+ var: "succ",
963
+ type: Dhall::Forall.of_arguments(
964
+ Dhall::Variable["natural"],
965
+ body: Dhall::Variable["natural"]
966
+ ),
967
+ body: Dhall::Forall.new(
968
+ var: "zero",
969
+ type: Dhall::Variable["natural"],
970
+ body: Dhall::Variable["natural"]
971
+ )
972
+ )
973
+ )
974
+ ),
975
+ "Natural/isZero" => Dhall::Forall.of_arguments(
976
+ Dhall::Variable["Natural"],
977
+ body: Dhall::Variable["Bool"]
978
+ ),
979
+ "Natural/even" => Dhall::Forall.of_arguments(
980
+ Dhall::Variable["Natural"],
981
+ body: Dhall::Variable["Bool"]
982
+ ),
983
+ "Natural/odd" => Dhall::Forall.of_arguments(
984
+ Dhall::Variable["Natural"],
985
+ body: Dhall::Variable["Bool"]
986
+ ),
987
+ "Natural/toInteger" => Dhall::Forall.of_arguments(
988
+ Dhall::Variable["Natural"],
989
+ body: Dhall::Variable["Integer"]
990
+ ),
991
+ "Natural/show" => Dhall::Forall.of_arguments(
992
+ Dhall::Variable["Natural"],
993
+ body: Dhall::Variable["Text"]
994
+ ),
995
+ "Text/show" => Dhall::Forall.of_arguments(
996
+ Dhall::Variable["Text"],
997
+ body: Dhall::Variable["Text"]
998
+ ),
999
+ "List/build" => Dhall::Forall.new(
1000
+ var: "a",
1001
+ type: Dhall::Variable["Type"],
1002
+ body: Dhall::Forall.of_arguments(
1003
+ Dhall::Forall.new(
1004
+ var: "list",
1005
+ type: Dhall::Variable["Type"],
1006
+ body: Dhall::Forall.new(
1007
+ var: "cons",
1008
+ type: Dhall::Forall.of_arguments(
1009
+ Dhall::Variable["a"],
1010
+ Dhall::Variable["list"],
1011
+ body: Dhall::Variable["list"]
1012
+ ),
1013
+ body: Dhall::Forall.new(
1014
+ var: "nil",
1015
+ type: Dhall::Variable["list"],
1016
+ body: Dhall::Variable["list"]
1017
+ )
1018
+ )
1019
+ ),
1020
+ body: Dhall::Application.new(
1021
+ function: Dhall::Variable["List"],
1022
+ argument: Dhall::Variable["a"]
1023
+ )
1024
+ )
1025
+ ),
1026
+ "List/fold" => Dhall::Forall.new(
1027
+ var: "a",
1028
+ type: Dhall::Variable["Type"],
1029
+ body: Dhall::Forall.of_arguments(
1030
+ Dhall::Application.new(
1031
+ function: Dhall::Variable["List"],
1032
+ argument: Dhall::Variable["a"]
1033
+ ),
1034
+ body: Dhall::Forall.new(
1035
+ var: "list",
1036
+ type: Dhall::Variable["Type"],
1037
+ body: Dhall::Forall.new(
1038
+ var: "cons",
1039
+ type: Dhall::Forall.of_arguments(
1040
+ Dhall::Variable["a"],
1041
+ Dhall::Variable["list"],
1042
+ body: Dhall::Variable["list"]
1043
+ ),
1044
+ body: Dhall::Forall.new(
1045
+ var: "nil",
1046
+ type: Dhall::Variable["list"],
1047
+ body: Dhall::Variable["list"]
1048
+ )
1049
+ )
1050
+ )
1051
+ )
1052
+ ),
1053
+ "List/length" => Dhall::Forall.new(
1054
+ var: "a",
1055
+ type: Dhall::Variable["Type"],
1056
+ body: Dhall::Forall.of_arguments(
1057
+ Dhall::Application.new(
1058
+ function: Dhall::Variable["List"],
1059
+ argument: Dhall::Variable["a"]
1060
+ ),
1061
+ body: Dhall::Variable["Natural"]
1062
+ )
1063
+ ),
1064
+ "List/head" => Dhall::Forall.new(
1065
+ var: "a",
1066
+ type: Dhall::Variable["Type"],
1067
+ body: Dhall::Forall.of_arguments(
1068
+ Dhall::Application.new(
1069
+ function: Dhall::Variable["List"],
1070
+ argument: Dhall::Variable["a"]
1071
+ ),
1072
+ body: Dhall::Application.new(
1073
+ function: Dhall::Variable["Optional"],
1074
+ argument: Dhall::Variable["a"]
1075
+ )
1076
+ )
1077
+ ),
1078
+ "List/last" => Dhall::Forall.new(
1079
+ var: "a",
1080
+ type: Dhall::Variable["Type"],
1081
+ body: Dhall::Forall.of_arguments(
1082
+ Dhall::Application.new(
1083
+ function: Dhall::Variable["List"],
1084
+ argument: Dhall::Variable["a"]
1085
+ ),
1086
+ body: Dhall::Application.new(
1087
+ function: Dhall::Variable["Optional"],
1088
+ argument: Dhall::Variable["a"]
1089
+ )
1090
+ )
1091
+ ),
1092
+ "List/indexed" => Dhall::Forall.new(
1093
+ var: "a",
1094
+ type: Dhall::Variable["Type"],
1095
+ body: Dhall::Forall.of_arguments(
1096
+ Dhall::Application.new(
1097
+ function: Dhall::Variable["List"],
1098
+ argument: Dhall::Variable["a"]
1099
+ ),
1100
+ body: Dhall::Application.new(
1101
+ function: Dhall::Variable["List"],
1102
+ argument: Dhall::RecordType.new(
1103
+ record: {
1104
+ "index" => Dhall::Variable["Natural"],
1105
+ "value" => Dhall::Variable["a"]
1106
+ }
1107
+ )
1108
+ )
1109
+ )
1110
+ ),
1111
+ "List/reverse" => Dhall::Forall.new(
1112
+ var: "a",
1113
+ type: Dhall::Variable["Type"],
1114
+ body: Dhall::Forall.of_arguments(
1115
+ Dhall::Application.new(
1116
+ function: Dhall::Variable["List"],
1117
+ argument: Dhall::Variable["a"]
1118
+ ),
1119
+ body: Dhall::Application.new(
1120
+ function: Dhall::Variable["List"],
1121
+ argument: Dhall::Variable["a"]
1122
+ )
1123
+ )
1124
+ ),
1125
+ "Optional/fold" => Dhall::Forall.new(
1126
+ var: "a",
1127
+ type: Dhall::Variable["Type"],
1128
+ body: Dhall::Forall.of_arguments(
1129
+ Dhall::Application.new(
1130
+ function: Dhall::Variable["Optional"],
1131
+ argument: Dhall::Variable["a"]
1132
+ ),
1133
+ body: Dhall::Forall.new(
1134
+ var: "optional",
1135
+ type: Dhall::Variable["Type"],
1136
+ body: Dhall::Forall.new(
1137
+ var: "just",
1138
+ type: Dhall::Forall.of_arguments(
1139
+ Dhall::Variable["a"],
1140
+ body: Dhall::Variable["optional"]
1141
+ ),
1142
+ body: Dhall::Forall.new(
1143
+ var: "nothing",
1144
+ type: Dhall::Variable["optional"],
1145
+ body: Dhall::Variable["optional"]
1146
+ )
1147
+ )
1148
+ )
1149
+ )
1150
+ ),
1151
+ "Optional/build" => Dhall::Forall.new(
1152
+ var: "a",
1153
+ type: Dhall::Variable["Type"],
1154
+ body: Dhall::Forall.of_arguments(
1155
+ Dhall::Forall.new(
1156
+ var: "optional",
1157
+ type: Dhall::Variable["Type"],
1158
+ body: Dhall::Forall.new(
1159
+ var: "just",
1160
+ type: Dhall::Forall.of_arguments(
1161
+ Dhall::Variable["a"],
1162
+ body: Dhall::Variable["optional"]
1163
+ ),
1164
+ body: Dhall::Forall.new(
1165
+ var: "nothing",
1166
+ type: Dhall::Variable["optional"],
1167
+ body: Dhall::Variable["optional"]
1168
+ )
1169
+ )
1170
+ ),
1171
+ body: Dhall::Application.new(
1172
+ function: Dhall::Variable["Optional"],
1173
+ argument: Dhall::Variable["a"]
1174
+ )
1175
+ )
1176
+ ),
1177
+ "Integer/show" => Dhall::Forall.of_arguments(
1178
+ Dhall::Variable["Integer"],
1179
+ body: Dhall::Variable["Text"]
1180
+ ),
1181
+ "Integer/toDouble" => Dhall::Forall.of_arguments(
1182
+ Dhall::Variable["Integer"],
1183
+ body: Dhall::Variable["Double"]
1184
+ ),
1185
+ "Double/show" => Dhall::Forall.of_arguments(
1186
+ Dhall::Variable["Double"],
1187
+ body: Dhall::Variable["Text"]
1188
+ )
1189
+ }.freeze
1190
+
1191
+ class Builtin
1192
+ TypeChecker.register self, Dhall::Builtin
1193
+
1194
+ def initialize(builtin)
1195
+ @expr = builtin
1196
+ @name = builtin.as_json
1197
+ end
1198
+
1199
+ def annotate(*)
1200
+ Dhall::TypeAnnotation.new(
1201
+ value: @expr,
1202
+ type: BUILTIN_TYPES.fetch(@name) do
1203
+ raise TypeError, "Unknown Builtin #{@name}"
1204
+ end
1205
+ )
1206
+ end
1207
+ end
1208
+ end
1209
+ end