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
@@ -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
|