Skip to content
← Back

src/syntax/process/printer/base.ghul

1
namespace Syntax.Process.Printer is
2
use IO.Std
3
4
use Trees
5
use Source
6
7
class Base : StrictVisitor abstract is
8
_indent: int
9
_depth: int
10
_run_on: bool
11
_indent_needed: bool
12
_want_locations: bool
13
_current_line: int
14
_result: System.Text.StringBuilder
15
16
init(want_locations: bool) is
17
super.init()
18
19
_want_locations = want_locations
20
_depth = 0
21
_indent = 2
22
_result = System.Text.StringBuilder()
23
_current_line = 1
24
si
25
26
result: string => _result.to_string()
27
28
// The statement terminator, as one overridable call. The plain
29
// printer writes it; the formatter, which lays out one statement
30
// per line, marks it instead and drops the ones a line break makes
31
// redundant.
32
write_terminator() is
33
write_line(";")
34
si
35
36
write_line(value: string) is
37
write(value)
38
write_line()
39
si
40
41
write(value: string) is
42
write_indent()
43
_result.append(value)
44
si
45
46
write(c: char) is
47
write_indent()
48
_result.append(c)
49
si
50
51
write_line() is
52
_current_line = _current_line + 1
53
_indent_needed = true
54
_result.append('\n')
55
si
56
57
indent() is
58
_depth = _depth + 1
59
si
60
61
outdent() is
62
_depth = _depth - 1
63
si
64
65
write_indent() is
66
if _indent_needed then
67
let i mut = 0
68
while i < _indent*_depth do
69
_result.append(' ')
70
i = i + 1
71
od
72
_indent_needed = false
73
fi
74
si
75
76
location(node: Node) is
77
location(node.location)
78
si
79
80
location(location: LOCATION) is
81
let new_line = location.start_line
82
if new_line != _current_line then
83
_current_line = new_line
84
if _want_locations then
85
write("#{_current_line} ")
86
fi
87
fi
88
si
89
90
write(node: Node) is
91
node.accept(self)
92
si
93
94
write_name(name: string) is
95
write(name)
96
si
97
98
visit(identifier: Identifiers.Identifier) is
99
location(identifier)
100
write_name(identifier.name)
101
si
102
103
visit(identifier: Identifiers.QUALIFIED) is
104
location(identifier)
105
identifier.qualifier.accept(self)
106
write('.')
107
108
// An operator reached through a qualifier - `use Ghul.Pipes.`>>` -
109
// is an identifier only under its backtick.
110
if Lexical.TOKENIZER.is_operator_name(identifier.name) then
111
write('`')
112
fi
113
114
write_name(identifier.name)
115
si
116
117
visit(modifier: Modifiers.Modifier) is
118
location(modifier)
119
write(modifier.name)
120
si
121
122
visit(modifiers: Modifiers.LIST) is
123
location(modifiers)
124
if let modifiers.access_modifier? then
125
access_modifier.accept(self)
126
write(' ')
127
fi
128
if let modifiers.storage_class? then
129
storage_class.accept(self)
130
write(' ')
131
fi
132
if modifiers.is_abstract then
133
write("abstract ")
134
fi
135
if modifiers.is_pure then
136
write("pure ")
137
fi
138
if modifiers.is_stable then
139
write("stable ")
140
fi
141
si
142
143
visit(variables: Variables.LIST) is
144
location(variables)
145
let first mut = true
146
for v in variables do
147
if !first then
148
write(", ")
149
fi
150
v.accept(self)
151
first = false
152
od
153
si
154
155
visit(definitions: Definitions.LIST) is
156
location(definitions)
157
for d in definitions do
158
d.accept(self)
159
160
// yuck...
161
if isa Variables.VARIABLE(d) then
162
let variable = d
163
if let variable.name? /\ name.name.starts_with("$") then
164
write_terminator()
165
fi
166
fi
167
od
168
si
169
170
visit(`enum: Definitions.ENUM) is
171
location(`enum)
172
write("enum ")
173
`enum.name.accept(self)
174
write_line(" is")
175
indent()
176
let seen_any mut = false
177
for member in `enum.members do
178
if seen_any then
179
write_line(",")
180
fi
181
member.accept(self)
182
seen_any = true
183
od
184
write_line()
185
outdent()
186
write_line("si")
187
si
188
189
visit(member: Definitions.ENUM_MEMBER) is
190
location(member)
191
member.name.accept(self)
192
if member.initializer? then
193
write(" = ")
194
member.initializer!.accept(self)
195
fi
196
si
197
198
visit(functions: Definitions.FUNCTION_GROUP) is
199
write_line("function group ")
200
indent()
201
for f in functions.functions do
202
f.accept(self)
203
od
204
outdent()
205
si
206
207
visit(pragma: Pragmas.PRAGMA) is
208
write("@")
209
210
pragma.name.accept(self)
211
write("(")
212
213
pragma.arguments.accept(self)
214
write_line(")")
215
si
216
217
visit(pragma: Definitions.PRAGMA) is
218
pragma.definition.accept(self)
219
si
220
221
visit(type_expression: TypeExpressions.UNDEFINED) is
222
write("???")
223
si
224
225
visit(type_expression: TypeExpressions.INFER) is
226
write("infer")
227
si
228
229
visit(array: TypeExpressions.ARRAY_) is
230
array.element.accept(self)
231
write("[]")
232
si
233
234
visit(pointer: TypeExpressions.POINTER) is
235
pointer.element.accept(self)
236
write(" ptr")
237
si
238
239
visit(optional: TypeExpressions.OPTIONAL) is
240
optional.element.accept(self)
241
write("?")
242
si
243
244
visit(reference: TypeExpressions.REFERENCE) is
245
reference.element.accept(self)
246
write(" ref")
247
si
248
249
pre(member: TypeExpressions.MEMBER) -> bool => true
250
visit(member: TypeExpressions.MEMBER) is
251
member.left.accept(self)
252
write('.')
253
member.name.accept(self)
254
si
255
256
visit(functions: TypeExpressions.FUNCTION_GROUP) is
257
write("function group ")
258
for f in functions.functions do
259
f.accept(self)
260
write(' ')
261
od
262
si
263
264
visit(named: TypeExpressions.NAMED) is
265
location(named)
266
named.name.accept(self)
267
268
if named.is_argument_pack then
269
write("..")
270
fi
271
si
272
273
visit(tuple: TypeExpressions.TUPLE) is
274
location(tuple)
275
write("(")
276
tuple.elements.accept(self)
277
write(")")
278
si
279
280
visit(element: TypeExpressions.NAMED_TUPLE_ELEMENT) is
281
location(element)
282
element.name.accept(self)
283
write(": ")
284
element.type_expression.accept(self)
285
si
286
287
visit(constraint: TypeExpressions.TYPE_PARAMETER_CONSTRAINT) is
288
location(constraint)
289
write(constraint.keyword)
290
si
291
292
visit(types: TypeExpressions.LIST) is
293
location(types)
294
let seen_any mut = false
295
for t in types do
296
if seen_any then
297
write(", ")
298
fi
299
t.accept(self)
300
seen_any = true
301
od
302
si
303
304
visit(`none: Expressions.Literals.NONE) is
305
location(`none)
306
write("none")
307
si
308
309
visit(identifier: Expressions.IDENTIFIER) is
310
location(identifier)
311
identifier.identifier.accept(self)
312
si
313
314
visit(`super: Expressions.SUPER) is
315
location(`super)
316
write("super")
317
si
318
319
visit(construct: Expressions.CONSTRUCT) is
320
location(construct)
321
write('_')
322
write('(')
323
construct.arguments.accept(self)
324
write(')')
325
si
326
327
visit(`cast: Expressions.CAST) is
328
location(`cast)
329
write("cast")
330
331
// A cast whose target was written parenthesised is printed
332
// back that way, whichever reading it settled on: dropping
333
// the parentheses would make the source say the typed
334
// reading outright, and adding a call around it would say
335
// the other.
336
if `cast.is_ambiguous then
337
write('(')
338
`cast.type_expression!.accept(self)
339
write(')')
340
write('(')
341
`cast.call_arguments!.accept(self)
342
write(')')
343
344
return
345
fi
346
347
if let type_expression = `cast.type_expression then
348
write(' ')
349
type_expression.accept(self)
350
fi
351
352
write('(')
353
`cast.right.accept(self)
354
write(')')
355
si
356
357
visit(`isa: Expressions.ISA) is
358
location(`isa)
359
write("isa ")
360
`isa.type_expression.accept(self)
361
write('(')
362
`isa.right.accept(self)
363
write(')')
364
si
365
366
visit(field_equals: Expressions.FIELD_EQUALS) is
367
location(field_equals)
368
field_equals.left.accept(self)
369
write(" =~ ")
370
field_equals.right.accept(self)
371
si
372
373
visit(hash_operand: Expressions.HASH_OPERAND) is
374
location(hash_operand)
375
hash_operand.left.accept(self)
376
si
377
378
// A synthesized body has no source to print. The formatter
379
// reaches one only when a whole file is round-tripped, and
380
// what it stands for is a comparison the compiler emits
381
// rather than anything the author wrote.
382
visit(memberwise_equals: Expressions.MEMBERWISE_EQUALS) is
383
location(memberwise_equals)
384
si
385
386
visit(memberwise_hash: Expressions.MEMBERWISE_HASH) is
387
location(memberwise_hash)
388
si
389
390
visit(`typeof: Expressions.TYPEOF) is
391
location(`typeof)
392
write("typeof ")
393
`typeof.type_expression.accept(self)
394
si
395
396
visit(`default: Expressions.DEFAULT) is
397
location(`default)
398
write("_")
399
400
let type_expression = `default.type_expression
401
402
if type_expression? then
403
write('[')
404
type_expression.accept(self)
405
write(']')
406
fi
407
si
408
409
visit(tuple: Expressions.TUPLE) is
410
location(tuple)
411
write('(')
412
tuple.elements.accept(self)
413
write(')')
414
si
415
416
visit(call: Expressions.CALL) is
417
location(call)
418
if call.is_thread_first /\ call.arguments.count >= 1 then
419
call.arguments.expressions[0].accept(self)
420
write(if call.propagates_absence then " ~> " else " |> " fi)
421
call.function.accept(self)
422
write('(')
423
let seen mut = false
424
for i in 1..call.arguments.count do
425
if seen then
426
write(',')
427
fi
428
call.arguments.expressions[i].accept(self)
429
seen = true
430
od
431
if call.arguments.has_trailing_comma then
432
write(',')
433
fi
434
write(')')
435
else
436
call.function.accept(self)
437
write('(')
438
call.arguments.accept(self)
439
write(')')
440
fi
441
si
442
443
visit(member: Expressions.MEMBER) is
444
location(member)
445
member.left.accept(self)
446
if member.is_coalesce then
447
write("?.")
448
else
449
// `123.get_type()` scans `123.` as the start of a float.
450
if isa Expressions.Literals.INTEGER(member.left) then
451
write(' ')
452
fi
453
454
write('.')
455
fi
456
457
// An operator named as a member is an identifier only under its
458
// backtick: `a.=~(b)` scans `.=~` as one operator.
459
if Lexical.TOKENIZER.is_operator_name(member.identifier.name) then
460
write('`')
461
fi
462
463
member.identifier.accept(self)
464
si
465
466
visit(index: Expressions.INDEX) is
467
location(index)
468
index.left.accept(self)
469
write('[')
470
index.index.accept(self)
471
write(']')
472
si
473
474
visit(unary: Expressions.UNARY) is
475
location(unary)
476
unary.operation.accept(self)
477
write(' ')
478
unary.right.accept(self)
479
si
480
481
visit(binary: Expressions.BINARY) is
482
location(binary)
483
binary.left.accept(self)
484
write(' ')
485
486
if binary.actual_operation? then
487
write(binary.actual_operation)
488
else
489
binary.operation.accept(self)
490
fi
491
492
write(' ')
493
binary.right.accept(self)
494
si
495
496
visit(expressions: Expressions.LIST) is
497
location(expressions)
498
let seen_any mut = false
499
for e in expressions do
500
if seen_any then
501
write(',')
502
fi
503
e.accept(self)
504
seen_any = true
505
od
506
507
if expressions.has_trailing_comma then
508
write(',')
509
fi
510
si
511
512
visit(literal: Expressions.Literals.Literal) is
513
location(literal)
514
write(literal.value_string)
515
si
516
517
write_escape_char(c: char) is
518
let ci = cast int(c)
519
if ci < 32 then
520
write("\\{string.format("X", ci)}")
521
elif ci == 34 then
522
write("\\")
523
write(cast char(34))
524
elif ci == 39 then
525
write("'")
526
elif ci == 92 then
527
write("\\\\")
528
else
529
write(c)
530
fi
531
si
532
533
visit(`string: Expressions.Literals.STRING) is
534
location(`string)
535
write(cast char(34))
536
for c in `string.value_string do
537
write_escape_char(c)
538
od
539
write(cast char(34))
540
si
541
542
visit(interpolation: Expressions.STRING_INTERPOLATION) is
543
location(interpolation)
544
545
let in_expression mut = false
546
547
write("\"")
548
for e in interpolation.values do
549
if in_expression then
550
write("{{")
551
552
e.expression.accept(self)
553
554
if e.format? then
555
write(":{e.format}")
556
fi
557
558
if in_expression then
559
write("}}")
560
fi
561
else
562
// TODO: no quotes around string literals
563
e.expression.accept(self)
564
fi
565
566
in_expression = !in_expression
567
od
568
write("\"")
569
si
570
571
visit(integer: Expressions.Literals.INTEGER) is
572
location(integer)
573
write(integer.value_string)
574
si
575
576
visit(float: Expressions.Literals.FLOAT) is
577
location(float)
578
write(float.value_string)
579
si
580
581
visit(character: Expressions.Literals.CHARACTER) is
582
location(character)
583
write("'")
584
write_escape_char(character.value_string[0])
585
write("'")
586
si
587
588
visit(boolean: Expressions.Literals.BOOLEAN) is
589
location(boolean)
590
write(boolean.value_string)
591
si
592
593
visit(left: Trees.Expressions.SIMPLE_LEFT_EXPRESSION) is
594
left.expression.accept(self)
595
si
596
597
visit(destructure_left: Trees.Expressions.DESTRUCTURING_LEFT_EXPRESSION) is
598
let seen_any mut = false
599
write("(")
600
for e in destructure_left.elements do
601
if seen_any then
602
write(", ")
603
fi
604
605
e.accept(self)
606
607
seen_any = true
608
od
609
write(")")
610
si
611
612
visit(list: Statements.LIST) is
613
location(list)
614
for s in list do
615
s.accept(self)
616
od
617
si
618
619
visit(assign: Statements.ASSIGNMENT) is
620
location(assign)
621
assign.left.accept(self)
622
write(" = ")
623
assign.right.accept(self)
624
write_terminator()
625
si
626
627
visit(expression: Statements.EXPRESSION) is
628
location(expression)
629
expression.expression.accept(self)
630
write_terminator()
631
si
632
633
visit(r: Statements.RETURN) is
634
location(r)
635
write("return")
636
if r.expression? then
637
write(' ')
638
r.expression!.accept(self)
639
fi
640
write_terminator()
641
si
642
643
visit(t: Statements.THROW) is
644
location(t)
645
write("throw")
646
if t.expression? then
647
write(' ')
648
t.expression!.accept(self)
649
fi
650
write_terminator()
651
si
652
653
visit(`await: Expressions.AWAIT) is
654
location(`await)
655
write("await")
656
write(' ')
657
`await.operand.accept(self)
658
si
659
660
visit(y: Statements.YIELD) is
661
location(y)
662
write("yield")
663
write(' ')
664
y.expression.accept(self)
665
write_terminator()
666
si
667
668
visit(y: Statements.YIELD_ALL) is
669
location(y)
670
write("yield in")
671
write(' ')
672
y.expression.accept(self)
673
write_terminator()
674
si
675
676
visit(t: Statements.ASSERT) is
677
location(t)
678
write("assert")
679
write(' ')
680
t.expression.accept(self)
681
682
if t.message? then
683
write(" else ")
684
t.message!.accept(self)
685
fi
686
687
write_terminator()
688
si
689
690
visit(i: Statements.IF) is
691
location(i)
692
let is_first mut = true
693
let seen_else mut = false
694
695
696
for b in i.branches do
697
location(b)
698
699
if seen_else then
700
IoC.CONTAINER.instance.logger.error(b.location, "broken if statement")
701
fi
702
703
assert !seen_else
704
705
if let b.condition? then
706
if is_first then
707
write("if ")
708
else
709
write("elif ")
710
fi
711
condition.accept(self)
712
write_line(" then")
713
else
714
seen_else = true
715
write_line("else")
716
fi
717
indent()
718
b.body.accept(self)
719
outdent()
720
is_first = false
721
od
722
write_line("fi")
723
si
724
725
visit(`case: Statements.CASE) is
726
location(`case)
727
write("case ")
728
`case.expression.accept(self)
729
write_line()
730
for m in `case.matches do
731
m.accept(self)
732
od
733
write_line("esac")
734
si
735
736
visit(match: Statements.CASE_MATCH) is
737
location(match)
738
if let match.expressions? then
739
write("when ")
740
expressions.accept(self)
741
write_line(":")
742
else
743
write_line("default")
744
fi
745
indent()
746
match.statements.accept(self)
747
outdent()
748
si
749
750
visit(`try: Statements.TRY) is
751
location(`try)
752
write_line("try")
753
indent()
754
`try.body.accept(self)
755
outdent()
756
for c in `try.catches do
757
c.accept(self)
758
od
759
let `finally = `try.`finally
760
761
if `finally? then
762
write_line("finally")
763
indent()
764
`finally.accept(self)
765
outdent()
766
fi
767
write_line("yrt")
768
si
769
770
visit(`catch: Statements.CATCH) is
771
location(`catch)
772
write("catch ")
773
774
if let `catch.variable? then
775
variable.accept(self)
776
fi
777
write_line()
778
indent()
779
`catch.body.accept(self)
780
outdent()
781
si
782
783
visit(`do: Statements.DO) is
784
location(`do)
785
if let `do.binding? then
786
write("while let ")
787
binding.accept(self)
788
write(" ")
789
elif let `do.condition? then
790
write("while ")
791
condition.accept(self)
792
write(" ")
793
fi
794
write_line("do")
795
indent()
796
`do.body.accept(self)
797
outdent()
798
write_line("od")
799
si
800
801
visit(labelled: Statements.LABELLED) is
802
location(labelled)
803
labelled.label.accept(self)
804
write(": ")
805
labelled.statement.accept(self)
806
si
807
808
visit(`break: Statements.BREAK) is
809
location(`break)
810
write("break")
811
812
if let `break.expression? then
813
write(' ')
814
expression.accept(self)
815
fi
816
write_terminator()
817
si
818
819
visit(`continue: Statements.CONTINUE) is
820
location(`continue)
821
write("continue")
822
let label = `continue.label
823
824
if label? then
825
write(' ')
826
label.accept(self)
827
fi
828
write_terminator()
829
si
830
831
visit(pragma: Statements.PRAGMA) is
832
if let pragma.statement? then
833
statement.accept(self)
834
fi
835
si
836
837
// A body-less function, property accessor or indexer accessor still
838
// needs a body node, so the parser hands back this rather than
839
// leaving the slot absent. There is nothing written to render:
840
// the caller's `after_body` is what emits the terminating `;`.
841
visit(block: Bodies.NULL) is si
842
si
843
si