dhallish 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,498 @@
1
+ require File.join(File.dirname(__FILE__), 'ast.rb')
2
+ require File.join(File.dirname(__FILE__), 'types.rb')
3
+ require File.join(File.dirname(__FILE__), 'utils.rb')
4
+
5
+ grammar DhallishGrammar
6
+
7
+ rule root
8
+ space? exp:expression space? { def to_node() exp.to_node() end }
9
+ end
10
+
11
+ rule expression
12
+ exp:if_then_else_expression { def to_node() exp.to_node() end } /
13
+ exp:let_expression { def to_node() exp.to_node() end } /
14
+ exp:import_alternative_expression { def to_node() exp.to_node() end }
15
+ end
16
+
17
+ rule eol
18
+ "\r"? "\n"
19
+ end
20
+
21
+ ## Comments
22
+ rule single_line_comment
23
+ "--" (!"\n" .)* "\n"
24
+ end
25
+
26
+ rule multi_line_comment
27
+ "{-" multi_line_comment_continue
28
+ end
29
+
30
+ rule multi_line_comment_chunk
31
+ multi_line_comment / (!"{-" .)
32
+ end
33
+
34
+ rule multi_line_comment_continue
35
+ "-}" / multi_line_comment_chunk multi_line_comment_continue
36
+ end
37
+
38
+ rule space
39
+ (" " / "\t" / eol / single_line_comment / multi_line_comment)+
40
+ end
41
+
42
+ ## Number Literals
43
+ rule integer_literal
44
+ ("+" / "-") [0-9]+ { def to_node() Dhallish::Ast::Literal_Node.new(text_value.to_i, Dhallish::Types::Integer) end }
45
+ end
46
+
47
+ rule exponent
48
+ "e" ("+" / "-")? [0-9]+
49
+
50
+ end
51
+
52
+ rule double_literal
53
+ ("+" / "-")? [0-9]+ (("." [0-9]+ exponent?) / exponent) { def to_node() Dhallish::Ast::Literal_Node.new(text_value.to_f, Dhallish::Types::Double) end }
54
+
55
+ end
56
+
57
+ rule natural_literal
58
+ [0-9]+ { def to_node() Dhallish::Ast::Literal_Node.new(text_value.to_i, Dhallish::Types::Natural) end }
59
+ end
60
+
61
+ ## Bools
62
+ rule bool_literal
63
+ "True" { def to_node() Dhallish::Ast::Literal_Node.new(true, Dhallish::Types::Bool) end }
64
+ /
65
+ "False" { def to_node() Dhallish::Ast::Literal_Node.new(false, Dhallish::Types::Bool) end }
66
+ end
67
+
68
+ ## Lists
69
+ rule empty_list_literal
70
+ "[" space? "]" space? ":" space? "List" space type:expression
71
+ {
72
+ def to_node()
73
+ Dhallish::Ast::ListNode.new [], type.to_node()
74
+ end
75
+ }
76
+ end
77
+
78
+ rule non_empty_list_literal
79
+ "[" space? fst:expression tail:(space? "," space? exp:expression)* space? "]" annot:(space? ":" space? "List" space type:expression)?
80
+ {
81
+ def to_node()
82
+ list = []
83
+ list.append fst.to_node()
84
+ tail.elements.each { |node|
85
+ list.append node.exp.to_node()
86
+ }
87
+ if annot.respond_to? :type
88
+ Dhallish::Ast::ListNode.new list, annot.type.to_node()
89
+ else
90
+ Dhallish::Ast::ListNode.new list, nil
91
+ end
92
+ end
93
+ }
94
+ end
95
+
96
+ rule list_literal
97
+ exp:empty_list_literal { def to_node() exp.to_node() end } /
98
+ exp:non_empty_list_literal { def to_node() exp.to_node() end }
99
+ end
100
+
101
+ ## Records
102
+ rule record_type_literal
103
+ "{" space? "}" { def to_node() Dhallish::Ast::RecordTypeNode.new({}) end } /
104
+ "{" space? fstkey:label space? ":" space? fstexp:expression tail:(space? "," space? key:label space? ":" space? exp:expression)* space? "}"
105
+ {
106
+ def to_node()
107
+ data = { fstkey.text_value => fstexp.to_node() }
108
+ tail.elements.each { |node|
109
+ key = node.key.text_value
110
+ assert("no key should apeare multiple times in a record: `#{key}`") { !data.key?(key) }
111
+ data[key] = node.exp.to_node()
112
+ }
113
+ Dhallish::Ast::RecordTypeNode.new data
114
+ end
115
+ }
116
+ end
117
+
118
+ rule record_literal
119
+ "{" space? "=" space? "}" { def to_node() Dhallish::Ast::RecordNode.new({}) end } /
120
+ "{" space? fstkey:label space? "=" space? fstexp:expression tail:(space? "," space? key:label space? "=" space? exp:expression)* space? "}"
121
+ {
122
+ def to_node()
123
+ data = { fstkey.text_value => fstexp.to_node() }
124
+ tail.elements.each { |node|
125
+ key = node.key.text_value
126
+ assert("no key should apeare multiple times in a record: `#{key}`") { !data.key?(key) }
127
+ data[key] = node.exp.to_node()
128
+ }
129
+ Dhallish::Ast::RecordNode.new data
130
+ end
131
+ }
132
+ end
133
+
134
+ ## Strings
135
+ rule text_literal
136
+ '"' tail:(exp:('${' space? innerexp:expression space? '}') / esc:('\"') / any:(!'"' .))* '"'
137
+ {
138
+ def to_node()
139
+ parts = []
140
+ tail.elements.each { |node|
141
+ if node.respond_to?(:exp)
142
+ parts.push node.exp.innerexp.to_node()
143
+ elsif node.respond_to?(:esc)
144
+ parts.push "\""
145
+ else
146
+ parts.push node.any.text_value
147
+ end
148
+ }
149
+ Dhallish::Ast::TextInterpolationNode.new parts
150
+ end
151
+ }
152
+ end
153
+
154
+ ## Optionals
155
+ rule optional_literal
156
+ prefix:("Some" / "None") space exp:or_expression { def to_node() Dhallish::Ast::OptionalNode.new prefix.text_value, exp.to_node() end }
157
+ end
158
+
159
+ ## Type Literals
160
+ rule forall_literal
161
+ ("forall" / "∀") space? "(" space? lb:label space? ":" space? type:expression space? ")" space? ("->" / "→") space? res_type:expression
162
+ {
163
+ def to_node()
164
+ Dhallish::Ast::FunctionType.new(type.to_node(), res_type.to_node(), lb.text_value)
165
+ end
166
+ }
167
+ end
168
+
169
+ ## Union Literals
170
+ rule union_literal
171
+ "<" space? start:(lb:label space? ":" space? type:record_merge_expression space? "|" space?)* lb:label space? "=" space? expr:add_sub_expression tail:(space? "|" space? lb:label space? ":" space? type:record_merge_expression)* space? ">"
172
+ {
173
+ def to_node()
174
+ typed_labels = {}
175
+ start.elements.each { |node|
176
+ typed_labels[node.lb.text_value] = node.type.to_node()
177
+ }
178
+ tail.elements.each { |node|
179
+ typed_labels[node.lb.text_value] = node.type.to_node()
180
+ }
181
+ Dhallish::Ast::UnionLiteral.new(typed_labels, lb.text_value, expr.to_node())
182
+ end
183
+ }
184
+ end
185
+
186
+ rule empty_union_type_literal
187
+ "<" space? ">"
188
+ end
189
+
190
+ rule union_type_literal
191
+ empty_union_type_literal { def to_node() Dhallish::Ast::UnionType.new({}) end } /
192
+ "<" space? lb:label space? ":" space? type:record_merge_expression tail:(space? "|" space? lb:label space? ":" space? type:record_merge_expression)* space? ">"
193
+ {
194
+ def to_node()
195
+ type_map = {lb.text_value => type.to_node()}
196
+ tail.elements.each { |node|
197
+ type_map[node.lb.text_value] = node.type.to_node()
198
+ }
199
+ Dhallish::Ast::UnionType.new(type_map)
200
+ end
201
+ }
202
+
203
+ end
204
+
205
+ ## Rules for operator expressions
206
+ ## rules are reversely ordered by their precedence
207
+ ## e.g. if rule X is over rule Y, the operator associated with rule Y has a stronger precedence than Xs operator
208
+
209
+ rule import_alternative_expression
210
+ exp:annotated_expression tail:(space? "?" space? alternative:annotated_expression)*
211
+ {
212
+ def to_node()
213
+ tail.elements.reduce(exp.to_node()) { |tree, node|
214
+ Dhallish::Ast::Import_Alternative.new tree, node.alternative.to_node()
215
+ }
216
+ end
217
+ }
218
+ end
219
+
220
+ rule annotated_expression
221
+ exp:function_type_expression tail:(space? ":" space? type_expr:function_type_expression)*
222
+ {
223
+ def to_node()
224
+ tail.elements.reduce(exp.to_node()) { |tree, node|
225
+ Dhallish::Ast::TypeAnnotationNode.new tree, node.type_expr.to_node()
226
+ }
227
+ end
228
+ }
229
+ end
230
+
231
+ rule function_type_expression
232
+ arg_type:or_expression tail:(space? ("->" / "→") space? res_type:or_expression)*
233
+ {
234
+ def to_node()
235
+ if tail.elements.size == 0
236
+ arg_type.to_node()
237
+ else
238
+ tree = nil
239
+ tail.elements.reverse.each { |node|
240
+ if tree.nil?
241
+ tree = node.res_type.to_node()
242
+ else
243
+ tree = Dhallish::Ast::FunctionType.new node.res_type.to_node(), tree
244
+ end
245
+ }
246
+ Dhallish::Ast::FunctionType.new arg_type.to_node(), tree
247
+ end
248
+ end
249
+ }
250
+ end
251
+
252
+ rule or_expression
253
+ exp:and_expression tail:(space? "||" space? exp:and_expression)*
254
+ {
255
+ def to_node()
256
+ tail.elements.reduce(exp.to_node()) { |tree, node|
257
+ Dhallish::Ast::BinaryArithOpNode.new([Dhallish::Types::Bool], tree, node.exp.to_node(), "||") { |x, y| x || y }
258
+ }
259
+ end
260
+ }
261
+ end
262
+
263
+ rule and_expression
264
+ exp:comparison_expression tail:(space? "&&" space? exp:comparison_expression)*
265
+ {
266
+ def to_node()
267
+ tail.elements.reduce(exp.to_node()) { |tree, node|
268
+ Dhallish::Ast::BinaryArithOpNode.new([Dhallish::Types::Bool], tree, node.exp.to_node(), "&&") { |x, y| x && y }
269
+ }
270
+ end
271
+ }
272
+ end
273
+
274
+ rule comparison_expression
275
+ exp:add_sub_expression tail:(space? op:("==" / "!=" / "<=" / "<" / ">=" / ">") space? exp:add_sub_expression)*
276
+ {
277
+ def to_node()
278
+ tail.elements.reduce(exp.to_node()) { |tree, node|
279
+ Dhallish::Ast::ComparisonOpNode.new(tree, node.exp.to_node(), node.op.text_value) { |lhs_val, rhs_val| lhs_val.send(node.op.text_value, rhs_val )}
280
+ }
281
+ end
282
+ }
283
+ end
284
+
285
+ rule add_sub_expression
286
+ exp:mult_div_expression tail:(space? op:("+"/"-") space? exp:mult_div_expression)*
287
+ {
288
+ def to_node()
289
+ tail.elements.reduce(exp.to_node()) { |tree, node|
290
+ exp = node.exp.to_node()
291
+ if node.op.text_value == "+"
292
+ Dhallish::Ast::BinaryArithOpNode.new(Dhallish::Types::Numbers, tree, exp, "+") { |x, y| x + y }
293
+ else
294
+ Dhallish::Ast::BinaryArithOpNode.new([Dhallish::Types::Double, Dhallish::Types::Integer], tree, exp, "-") { |x, y| x - y }
295
+ end
296
+ }
297
+ end
298
+ }
299
+ end
300
+
301
+ rule mult_div_expression
302
+ exp:list_concat_expression tail:(space? op:("*"/"/") space? exp:list_concat_expression)*
303
+ {
304
+ def to_node()
305
+ tail.elements.reduce(exp.to_node()) { |tree, node|
306
+ exp = node.exp.to_node()
307
+ if node.op.text_value == "*"
308
+ Dhallish::Ast::BinaryArithOpNode.new(Dhallish::Types::Numbers, tree, exp, "*") { |x, y| x * y }
309
+ else
310
+ Dhallish::Ast::BinaryArithOpNode.new([Dhallish::Types::Double, Dhallish::Types::Integer], tree, exp, "/") { |x, y| x / y }
311
+ end
312
+
313
+ }
314
+ end
315
+ }
316
+ end
317
+
318
+ rule list_concat_expression
319
+ exp:text_append_expression tail:(space? "#" space? exp:text_append_expression)*
320
+ {
321
+ def to_node()
322
+ tail.elements.reduce(exp.to_node()) { |tree, node|
323
+ Dhallish::Ast::ListConcatNode.new(tree, node.exp.to_node())
324
+ }
325
+ end
326
+ }
327
+ end
328
+
329
+ rule text_append_expression
330
+ exp:record_merge_expression tail:(space? "++" space? exp:record_merge_expression)*
331
+ {
332
+ def to_node()
333
+ tail.elements.reduce(exp.to_node()) { |tree, node|
334
+ Dhallish::Ast::BinaryArithOpNode.new([Dhallish::Types::Text], tree, node.exp.to_node(), "+") { |x, y| x + y }
335
+ }
336
+ end
337
+ }
338
+ end
339
+
340
+ rule record_merge_expression
341
+ exp:application_expression tail:(space? op:("//\\\\" / "⩓" / "/\\" / "∧" / "//" / "⫽") space? exp:application_expression)*
342
+ {
343
+ def to_node()
344
+ tail.elements.reduce(exp.to_node()) { |tree, node|
345
+ if node.op.text_value == "//\\\\" or node.op.text_value == "⩓"
346
+ Dhallish::Ast::RecordTypeRecursiveMergeNode.new tree, node.exp.to_node()
347
+ elsif node.op.text_value == "/\\" or node.op.text_value == "∧"
348
+ Dhallish::Ast::RecordRecursiveMergeNode.new tree, node.exp.to_node()
349
+ else
350
+ Dhallish::Ast::RecordNonRecursiveMergeNode.new tree, node.exp.to_node()
351
+ end
352
+ }
353
+ end
354
+ }
355
+ end
356
+
357
+ rule application_expression
358
+ fn:proj_sel_expression tail:(space? exp:(!reserved arg:proj_sel_expression))*
359
+ {
360
+ def to_node()
361
+ tail.elements.reduce(fn.to_node()) { |tree, node|
362
+ Dhallish::Ast::FunctionCallNode.new(tree, node.exp.arg.to_node())
363
+ }
364
+ end
365
+ }
366
+ end
367
+
368
+ rule key_list
369
+ "{" space? "}" { def to_list() [] end } /
370
+ "{" space? head:label tail:(space? "," space? lbl:label)* space? "}"
371
+ {
372
+ def to_list()
373
+ tail.elements.reduce([head.text_value]) { |list, node|
374
+ list.push node.lbl.text_value
375
+ list
376
+ }
377
+ end
378
+ }
379
+ end
380
+
381
+ rule proj_sel_expression
382
+ pe:primitive_expression tail:(space? "." space? sel:(a:label / b:key_list))*
383
+ {
384
+ def to_node()
385
+ tail.elements.reduce(pe.to_node()) { |tree, node|
386
+ if node.sel.respond_to? :a
387
+ Dhallish::Ast::RecordUnionSelector.new tree, node.sel.a.text_value
388
+ else
389
+ Dhallish::Ast::RecordProjection.new tree, node.sel.b.to_list()
390
+ end
391
+ }
392
+ end
393
+ }
394
+ end
395
+
396
+ rule primitive_expression
397
+ exp:bool_literal { def to_node() exp.to_node() end } /
398
+ exp:double_literal { def to_node() exp.to_node() end } /
399
+ exp:natural_literal { def to_node() exp.to_node() end } /
400
+ exp:integer_literal { def to_node() exp.to_node() end } /
401
+ exp:text_literal { def to_node() exp.to_node() end } /
402
+ exp:list_literal { def to_node() exp.to_node() end } /
403
+ exp:function_definition { def to_node() exp.to_node() end } /
404
+ exp:optional_literal { def to_node() exp.to_node() end } /
405
+ exp:forall_literal { def to_node() exp.to_node() end } /
406
+ exp:import_expression { def to_node() exp.to_node() end } /
407
+ exp:union_merge { def to_node() exp.to_node() end } /
408
+ exp:label { def to_node() exp.to_node() end } /
409
+ exp:record_literal { def to_node() exp.to_node() end } /
410
+ exp:record_type_literal { def to_node() exp.to_node() end } /
411
+ exp:union_literal { def to_node() exp.to_node() end } /
412
+ exp:union_type_literal { def to_node() exp.to_node() end } /
413
+ "(" space? exp:expression space? ")" { def to_node() exp.to_node() end } /
414
+ "???" { def to_node() Dhallish::Ast::GetContext.new end }
415
+ end
416
+
417
+ ## End of operator expressions
418
+
419
+ rule if_then_else_expression
420
+ "if" space cond:expression space
421
+ "then" space exp_true:expression space
422
+ "else" space exp_false:expression
423
+ {
424
+ def to_node()
425
+ Dhallish::Ast::IfThenElseNode.new cond.to_node(), exp_true.to_node(), exp_false.to_node()
426
+ end
427
+ }
428
+ end
429
+
430
+ rule label
431
+ ("_" / [a-zA-Z]) ("_" / "-" / "/" / [a-zA-Z0-9])* { def to_node() Dhallish::Ast::VariableNode.new text_value end } /
432
+ "`" lb:(([a-zA-Z0-9] / "-" / "/" / "_" / ":" / "." / "$")+) "`" { def to_node() Dhallish::Ast::VariableNode.new lb.text_value end }
433
+ end
434
+
435
+ rule let_expression
436
+ declarations:("let" space var:label space? annot:(":" space? exp:expression space?)? "=" space? val:expression space)+ "in" space in_expr:expression
437
+ {
438
+ def to_node()
439
+ vars = []
440
+ declarations.elements.each { |node|
441
+ typeannot = nil
442
+ if node.respond_to? :annot and node.annot.respond_to? :exp
443
+ typeannot = node.annot.exp.to_node()
444
+ end
445
+ vars.append [node.var.text_value, typeannot, node.val.to_node()]
446
+ }
447
+ Dhallish::Ast::LetInNode.new vars, in_expr.to_node()
448
+ end
449
+ }
450
+ end
451
+
452
+ rule function_definition
453
+ ("\\" / "λ") space? "(" space? lb:label space? ":" space? type_exp:expression space? ")" space? ("->" / "→") space? exp:expression
454
+ {
455
+ def to_node()
456
+ Dhallish::Ast::FunctionDefinitionNode.new lb.text_value, type_exp.to_node(), exp.to_node()
457
+ end
458
+ }
459
+ end
460
+
461
+ rule forbidden_label
462
+ reserved / "Text" / "Bool" / "Optional" / "None" / "Some" / "True" / "False" / "Type" /
463
+ "Natural" ("/" ("show" / "toInteger" / "toDouble"))? /
464
+ "Integer" ("/" ("show" / "toNatural" / "toDouble"))? /
465
+ "Double" ("/" ("show" / "toNatural" / "toInteger"))? /
466
+ "List" ("/" ("length" / "reduce" / "head" / "tail"))? /
467
+ "Optional" ("/" ("fold"))?
468
+ end
469
+
470
+ rule reserved
471
+ "if" / "then" / "else" / "let" / "in" / "as" / "?"
472
+ end
473
+
474
+ rule import_expression
475
+ prefix:("../" / "./" / "~/" / "env:" / "http:" / "https:") src:import_source as_type:(space "as" space import_type:"Text")?
476
+ {
477
+ def to_node()
478
+ import_as_text = (as_type.respond_to? :import_type and as_type.import_type.text_value == "Text")
479
+ Dhallish::Ast::Import.new prefix.text_value, src.text_value, import_as_text
480
+ end
481
+ }
482
+ end
483
+
484
+ rule import_source
485
+ # can be an environment variable, url or path
486
+ (!([\s] / "\\" / "<" / ">" / "?" / "," / "[" / "]" / "{" / "}" / "#" / "(" / ")") .)*
487
+ end
488
+
489
+ rule union_merge
490
+ "merge" space? handler:primitive_expression space? union:primitive_expression
491
+ {
492
+ def to_node()
493
+ Dhallish::Ast::UnionMerge.new(handler.to_node(), union.to_node())
494
+ end
495
+ }
496
+ end
497
+
498
+ end