1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity low)
4 module dil.ast.ASTPrinter;
5
6 import dil.ast.Visitor,
7 dil.ast.Node,
8 dil.ast.Declarations,
9 dil.ast.Expressions,
10 dil.ast.Statements,
11 dil.ast.Types,
12 dil.ast.Parameters,
13 dil.ast.Precedence;
14 import dil.lexer.IdTable,
15 dil.lexer.Funcs;
16 import dil.semantic.TypesEnum;
17 import dil.Compilation;
18 import dil.String;
19 import dil.Enums;
20 import dil.Array;
21 import common;
22
23 /// Converts expressions like "TokenList.XYZ" to "toToken(TOK.XYZ)".
24 static struct TokenList
25 {
26 static Token* opDispatch(string kind)()
27 {
28 return mixin("toToken(TOK."~kind~")");
29 }
30 }
31
32 /// Traverses a Node tree and constructs a string representation.
33 class ASTPrinter : Visitor2
34 {
35 char[] text; /// The printed text.
36 TokenArray tarray; /// The pre-built tokens of the text (Lexer not required.)
37 cchar* prevEnd; /// End pointer of the previous token (t.end).
38 bool buildTokens; /// True if the tokens should be built.
39 uint_t lineNum; /// Current line number.
40 Token nlToken; /// Provides a newline token (depends on the platform).
41 cstring spaces; /// The current whitespace string.
42 cstring indent; /// The current indendation string.
43 cstring indentStep; /// The string used to increase the indentation level.
44 TemplateDecl currentTplDecl; /// Set by a wrapping TemplateDecl.
45
46 CompilationContext cc;
47 alias T = TokenList;
48
49 /// Constructs an ASTPrinter.
50 this(bool buildTokens, CompilationContext cc)
51 {
52 this.buildTokens = buildTokens;
53 this.cc = cc;
54 this.indentStep = " ";
55 // A newline token with a platform dependent string as its text.
56 nlToken.kind = TOK.Newline;
57 version(Windows)
58 const nl = "\r\n";
59 else version(OSX)
60 const nl = "\r";
61 else
62 const nl = "\n";
63 nlToken.text = nl;
64 }
65
66 /// Returns the tokens as a list.
67 Token[] tokens()
68 {
69 return tarray[][1..$-1]; // Exclude Newline and EOF.
70 }
71
72 /// Increments the line number and returns the newline token.
73 Token* Newline()
74 {
75 nlToken.nlval = cc.tables.lxtables.lookupNewline(++lineNum);
76 return &nlToken;
77 }
78
79 /// Starts the printer.
80 char[] print(Node n)
81 {
82 text = null;
83 tarray.len = 0;
84 tarray.cap = 2;
85 prevEnd = null;
86 spaces = null;
87 indent = null;
88 // First newline token.
89 auto t = *Newline;
90 t.text = null;
91 writeToken(&t);
92 // Start traversing the tree.
93 visitN(n);
94 // Terminate with EOF.
95 t = Token(TOK.EOF, null, null, null, null);
96 writeToken(&t);
97 fixTokens();
98 return text;
99 }
100
101 /// Constructs a new Token with the given parameters and pushes to an array.
102 void pushToken(TOK k, size_t start, size_t end, void* value)
103 { // Create new token and set its members.
104 if (tarray.rem == 0)
105 tarray.growX1_5();
106 assert(tarray.rem >= 1);
107 auto t = tarray.cur++;
108 t.kind = k;
109 t.ws = start ? prevEnd : null;
110 t.start = prevEnd + start;
111 t.end = prevEnd + end;
112 t.pvoid = value;
113 prevEnd += end;
114 }
115
116 /// When the emitted text is complete, the pointers in the tokens
117 /// are updated to point to the correct text fragments.
118 /// (Considering the text buffer might get relocated when appending to it.)
119 void fixTokens()
120 {
121 if (!buildTokens || !tarray.len)
122 return;
123 const offset = cast(size_t)text.ptr;
124 foreach (ref t; tarray[])
125 {
126 if (t.ws)
127 t.ws += offset;
128 t.start += offset;
129 t.end += offset;
130 }
131 }
132
133 /// Returns the token kind for p.
134 TOK protToTOK(PROT p)
135 {
136 TOK tk;
137 final switch (p)
138 {
139 case Protection.Private: tk = TOK.Private; break;
140 case Protection.Protected: tk = TOK.Protected; break;
141 case Protection.Package: tk = TOK.Package; break;
142 case Protection.Public: tk = TOK.Public; break;
143 case Protection.Export: tk = TOK.Export; break;
144 case Protection.None:
145 }
146 return tk;
147 }
148
149 /// Writes str to the text buffer.
150 void writeS(cstring str)
151 {
152 text ~= str;
153 }
154
155 /// Writes a list of tokens.
156 void write(Token*[] ts...)
157 {
158 foreach (t; ts)
159 writeToken(t);
160 }
161
162 /// Writes the contents of a token to the text.
163 void writeToken(Token* t)
164 {
165 if (t.kind == TOK.Invalid)
166 { // Special whitespace token?
167 spaces ~= t.text;
168 return;
169 }
170 auto tokenText = t.text;
171 if (buildTokens)
172 {
173 auto start = spaces.length;
174 auto end = start + tokenText.length;
175 pushToken(t.kind, start, end, t.pvoid);
176 }
177 writeS(spaces);
178 writeS(tokenText);
179 spaces = null; // Clear whitespace.
180 }
181
182 /// Writes the tokens between b and e (inclusive.)
183 void writeSpan(Token* b, Token* e)
184 {
185 for (auto t = b; t <= e; t++)
186 if (!t.isWhitespace())
187 writeToken(t);
188 }
189
190 /// Shortcuts.
191 alias w = write;
192 /// ditto
193 alias v = visitN;
194
195 /// Returns a new token containing 'n' number of whitespace characters.
196 Token* ws(uint n) @property
197 {
198 auto s = String(" ") * n;
199 auto t = new Token;
200 t.start = s.ptr;
201 t.end = s.end;
202 return t;
203 }
204
205 /// Returns a new token containing a single whitespace character.
206 Token* ws() @property
207 {
208 auto t = new Token;
209 t.text = " ";
210 return t;
211 }
212
213 /// Returns the current indentation as a token.
214 Token* ind() @property
215 {
216 auto t = new Token;
217 t.text = indent;
218 return t;
219 }
220
221 /// Returns a Token for an Identifier.
222 Token* id(Identifier* id)
223 {
224 auto t = new Token;
225 t.kind = TOK.Identifier;
226 t.text = id.str;
227 t.ident = id;
228 return t;
229 }
230
231 /// Increases/decreases indentation on construction/destruction.
232 scope class IndentLevel
233 {
234 this()
235 {
236 indent ~= indentStep;
237 }
238 ~this()
239 {
240 indent = indent[0 .. $-indentStep.length];
241 }
242 }
243
244 /// Decreases/increases indentation on construction/destruction.
245 scope class UnindentLevel
246 {
247 this()
248 {
249 assert(indent.length >= indentStep.length);
250 indent = indent[0 .. $-indentStep.length];
251 }
252 ~this()
253 {
254 indent ~= indentStep;
255 }
256 }
257
258 /// Sets/restores indentation on construction/destruction.
259 scope class SetIndentLevel
260 {
261 cstring old;
262 this(cstring i)
263 {
264 old = indent;
265 indent = i;
266 }
267 ~this()
268 {
269 indent = old;
270 }
271 }
272
273 /// Returns the currentTplDecl member and nulls it.
274 TemplateDecl getTplDecl()
275 {
276 auto n = currentTplDecl;
277 currentTplDecl = null;
278 return n;
279 }
280
281 /// Writes the template parameters and if-constraint of a TemplateDecl.
282 void writeTParams(TemplateDecl n)
283 {
284 v(n.tparams);
285 if (n.constraint)
286 {
287 w(ws, T.If, T.LParen);
288 v(n.constraint);
289 w(T.RParen);
290 }
291 }
292
293 void writeTParamsOnly(TemplateDecl n)
294 {
295 v(n.tparams);
296 }
297
298 void writeTConstraint(TemplateDecl n)
299 {
300 if (n.constraint)
301 {
302 w(ws, T.If, T.LParen);
303 v(n.constraint);
304 w(T.RParen);
305 }
306 }
307
308 /// Writes an expression, wrapped in parentheses if necessary.
309 void write(Expression n, PREC prec)
310 {
311 auto nextP = n.kind.precOf();
312 if (prec > nextP)
313 {
314 w(T.LParen);
315 v(n);
316 w(T.RParen);
317 }
318 else
319 v(n);
320 }
321
322 /// Writes a comma-separated list of Expressions.
323 void write(Expression[] es)
324 {
325 foreach (i, e; es)
326 {
327 if (i)
328 w(T.Comma, ws);
329 w(e, PREC.Assignment);
330 }
331 }
332
333 /// Writes a binary expression.
334 void write(BinaryExpr n)
335 {
336 auto prec = n.kind.precOf();
337 w(n.lhs, prec);
338 w(ws, n.optok, ws);
339 w(n.rhs, prec);
340 }
341
342 /// Writes a unary expression.
343 void write(UnaryExpr n)
344 {
345 w(n.una, n.kind.precOf());
346 }
347
348 void writeBlock(Node n)
349 {
350 w(Newline, ind, T.LBrace, Newline);
351 {
352 scope il = new IndentLevel;
353 v(n);
354 }
355 w(ind, T.RBrace, Newline);
356 }
357
358 void writeAggregateBody(Node n)
359 {
360 if (n is null)
361 w(T.Semicolon, Newline);
362 else
363 writeBlock(n);
364 }
365
366 /// Writes specification and/or default values.
367 void writeSpecDef(Node s, Node d)
368 {
369 if (s) {
370 w(ws, T.Colon, ws);
371 v(s);
372 }
373 if (d) {
374 w(ws, T.Equal, ws);
375 v(d);
376 }
377 }
378
379 void writeAttrDecl(Declaration n)
380 {
381 switch (n.kind)
382 {
383 case NodeKind.ProtectionDecl,
384 NodeKind.StorageClassDecl,
385 NodeKind.LinkageDecl,
386 NodeKind.AlignDecl,
387 NodeKind.PragmaDecl,
388 NodeKind.VariablesDecl:
389 {
390 scope il = new SetIndentLevel(" ");
391 v(n);
392 }
393 break;
394 case NodeKind.ColonBlockDecl:
395 v(n);
396 break;
397 case NodeKind.CompoundDecl:
398 writeBlock(n);
399 break;
400 case NodeKind.EmptyDecl:
401 w(T.Semicolon, Newline);
402 break;
403 default:
404 w(Newline);
405 v(n);
406 }
407 }
408
409 override:
410
411 void visit(IllegalDecl n)
412 {
413 assert(0);
414 }
415
416 void visit(CompoundDecl n)
417 {
418 foreach (x; n.decls)
419 v(x);
420 }
421
422 void visit(ColonBlockDecl n)
423 {
424 w(T.Colon, Newline);
425 v(n.decls);
426 }
427
428 void visit(EmptyDecl n)
429 {
430 w(ind, T.Semicolon, Newline);
431 }
432
433 void visit(ModuleDecl n)
434 {
435 w(T.Module);
436 if (n.type)
437 w(ws, T.LParen, n.type, T.RParen);
438 w(ws, n.fqn[0]);
439 foreach (ident; n.fqn[1..$])
440 w(T.Dot, ident);
441 w(T.Semicolon, Newline, Newline);
442 }
443
444 void visit(ImportDecl n)
445 {
446 w(ind);
447 if (n.isStatic)
448 w(T.Static, ws);
449 w(T.Import, ws);
450 foreach (i, fqn; n.moduleFQNs)
451 {
452 if (i)
453 w(T.Comma, ws);
454 if (auto aliasId = n.moduleAliases[i])
455 w(aliasId, ws, T.Equal, ws);
456 foreach (j, ident; fqn)
457 {
458 if (j)
459 w(T.Dot);
460 w(ident);
461 }
462 }
463 foreach (i, bindName; n.bindNames)
464 {
465 if (i == 0)
466 w(ws, T.Colon, ws);
467 else
468 w(T.Comma, ws);
469 if (auto bindAlias = n.bindAliases[i])
470 w(bindAlias, ws, T.Equal, ws);
471 w(bindName);
472 }
473 w(T.Semicolon, Newline);
474 }
475
476 void visit(AliasDecl n)
477 {
478 w(ind, T.Alias);
479 scope il = new SetIndentLevel(" ");
480 v(n.decl);
481 }
482
483 void visit(AliasesDecl n)
484 {
485 w(ind, T.Alias);
486 foreach (i, name; n.idents)
487 {
488 if (i)
489 w(T.Comma);
490 w(ws, name, ws, T.Equal, ws);
491 v(n.types[i]);
492 }
493 w(T.Semicolon, Newline);
494 }
495
496 void visit(AliasThisDecl n)
497 {
498 w(ind, T.Alias, ws, n.name, ws, T.This, T.Semicolon, Newline);
499 }
500
501 void visit(TypedefDecl n)
502 {
503 w(ind, T.Typedef);
504 scope il = new SetIndentLevel(" ");
505 v(n.decl);
506 }
507
508 void visit(EnumDecl n)
509 {
510 w(ind, T.Enum);
511 if (n.name)
512 w(ws, n.name);
513 if (n.baseType) {
514 w(ws, T.Colon, ws);
515 v(n.baseType);
516 }
517 if (!n.members)
518 w(T.Semicolon, Newline);
519 else
520 {
521 w(Newline, ind, T.LBrace, Newline);
522 {
523 scope il = new IndentLevel;
524 foreach (i, m; n.members)
525 {
526 if (i)
527 w(T.Comma, Newline);
528 v(m);
529 }
530 }
531 w(Newline, ind, T.RBrace, Newline);
532 }
533 w(Newline);
534 }
535
536 void visit(EnumMemberDecl n)
537 {
538 if (n.type) {
539 w(ind);
540 v(n.type);
541 w(ws, n.name);
542 }
543 else
544 w(ind, n.name);
545 if (n.value) {
546 w(ws, T.Equal, ws);
547 v(n.value);
548 }
549 }
550
551 void visit(TemplateDecl n)
552 {
553 if (n.isWrapper)
554 {
555 currentTplDecl = n;
556 v(n.decls);
557 return;
558 }
559 w(ind);
560 if (n.isMixin)
561 w(T.Mixin, ws);
562 w(T.Template, ws, n.name);
563 v(n.tparams);
564 if (n.constraint)
565 {
566 w(ws, T.If, T.LParen);
567 v(n.constraint);
568 w(T.RParen);
569 }
570 writeBlock(n.decls);
571 w(Newline);
572 }
573
574 void visit(ClassDecl n)
575 {
576 auto tplDecl = getTplDecl();
577 w(ind, T.Class, ws, n.name);
578 if (tplDecl)
579 writeTParams(tplDecl);
580 if (n.bases)
581 {
582 w(ws, T.Colon, ws);
583 foreach (i, b; n.bases)
584 {
585 if (i)
586 w(T.Comma, ws);
587 v(b);
588 }
589 }
590 writeAggregateBody(n.decls);
591 w(Newline);
592 }
593
594 void visit(InterfaceDecl n)
595 {
596 auto tplDecl = getTplDecl();
597 w(ind, T.Interface, ws, n.name);
598 if (tplDecl)
599 writeTParams(tplDecl);
600 if (n.bases)
601 {
602 w(ws, T.Colon, ws);
603 foreach (i, b; n.bases)
604 {
605 if (i)
606 w(T.Comma, ws);
607 v(b);
608 }
609 }
610 writeAggregateBody(n.decls);
611 w(Newline);
612 }
613
614 void visit(StructDecl n)
615 {
616 auto tplDecl = getTplDecl();
617 w(ind, T.Struct);
618 if (n.name)
619 w(ws, n.name);
620 if (tplDecl)
621 writeTParams(tplDecl);
622 writeAggregateBody(n.decls);
623 w(Newline);
624 }
625
626 void visit(UnionDecl n)
627 {
628 auto tplDecl = getTplDecl();
629 w(ind, T.Union);
630 if (n.name)
631 w(ws, n.name);
632 if (tplDecl)
633 writeTParams(tplDecl);
634 writeAggregateBody(n.decls);
635 w(Newline);
636 }
637
638 void visit(ConstructorDecl n)
639 {
640 auto tplDecl = getTplDecl();
641 w(ind, T.This);
642 if (tplDecl)
643 writeTParamsOnly(tplDecl);
644 v(n.params);
645 if (tplDecl)
646 writeTConstraint(tplDecl);
647 v(n.funcBody);
648 }
649
650 void visit(StaticCtorDecl n)
651 {
652 w(ind, T.Static, ws, T.This, T.LParen, T.RParen);
653 v(n.funcBody);
654 }
655
656 void visit(DestructorDecl n)
657 {
658 w(ind, T.Tilde, T.This, T.LParen, T.RParen);
659 v(n.funcBody);
660 }
661
662 void visit(StaticDtorDecl n)
663 {
664 w(ind, T.Static, ws, T.Tilde, T.This, T.LParen, T.RParen);
665 v(n.funcBody);
666 }
667
668 void visit(FunctionDecl n)
669 {
670 auto tplDecl = getTplDecl();
671 w(ind);
672 if (n.returnType)
673 v(n.returnType);
674 else
675 w(T.Auto);
676 w(ws);
677 w(n.name);
678 if (tplDecl)
679 writeTParamsOnly(tplDecl);
680 v(n.params);
681 if (tplDecl)
682 writeTConstraint(tplDecl);
683 v(n.funcBody);
684 }
685
686 void visit(VariablesDecl n)
687 {
688 w(ind);
689 if (n.type) {
690 v(n.type);
691 w(ws);
692 }
693 //else // Emitted by visit(StorageClassDecl).
694 // w(T.Auto, ws);
695 foreach (i, name; n.names)
696 {
697 if (i)
698 w(T.Comma, ws);
699 w(name);
700 if (auto init = n.inits[i]) {
701 w(ws, T.Equal, ws);
702 v(init);
703 }
704 }
705 w(T.Semicolon, Newline, Newline);
706 }
707
708 void visit(InvariantDecl n)
709 {
710 w(ind, T.Invariant, T.LParen, T.RParen);
711 v(n.funcBody);
712 }
713
714 void visit(UnittestDecl n)
715 {
716 w(ind, T.Unittest);
717 v(n.funcBody);
718 }
719
720 void visit(DebugDecl n)
721 {
722 w(ind, T.Debug);
723 if (n.isSpecification())
724 w(ws, T.Equal, ws, n.spec, T.Semicolon, Newline);
725 else
726 {
727 if (n.cond)
728 w(T.LParen, n.cond, T.RParen);
729 writeBlock(n.decls);
730 if (n.elseDecls) {
731 w(T.Else);
732 writeBlock(n.elseDecls);
733 }
734 }
735 w(Newline);
736 }
737
738 void visit(VersionDecl n)
739 {
740 w(ind, T.Version);
741 if (n.isSpecification())
742 w(ws, T.Equal, ws, n.spec, T.Semicolon, Newline);
743 else
744 {
745 w(T.LParen, n.cond, T.RParen);
746 writeBlock(n.decls);
747 if (n.elseDecls) {
748 w(T.Else);
749 writeBlock(n.elseDecls);
750 }
751 }
752 w(Newline);
753 }
754
755 void visit(StaticIfDecl n)
756 {
757 w(ind, T.Static, T.If, T.LParen);
758 v(n.condition);
759 w(T.RParen, Newline);
760 v(n.ifDecls);
761 if (n.elseDecls) {
762 w(T.Else, Newline);
763 v(n.elseDecls);
764 }
765 w(Newline, Newline);
766 }
767
768 void visit(StaticAssertDecl n)
769 {
770 w(ind, T.Static, ws, T.Assert, T.LParen);
771 v(n.condition);
772 if (n.message) {
773 w(T.Comma, ws);
774 v(n.message);
775 }
776 w(T.RParen, Newline, Newline);
777 }
778
779 void visit(NewDecl n)
780 {
781 w(ind, T.New);
782 v(n.params);
783 v(n.funcBody);
784 }
785
786 void visit(DeleteDecl n)
787 {
788 w(ind, T.Delete);
789 v(n.params);
790 v(n.funcBody);
791 }
792
793 void visit(ProtectionDecl n)
794 {
795 w(ind, protToTOK(n.prot).toToken());
796 writeAttrDecl(n.decls);
797 }
798
799 void visit(StorageClassDecl n)
800 {
801 w(ind);
802 TOK t;
803 Identifier* i;
804 final switch (n.stc)
805 {
806 case STC.Abstract: t = TOK.Abstract; break;
807 case STC.Auto: t = TOK.Auto; break;
808 case STC.Const: t = TOK.Const; break;
809 case STC.Deprecated: t = TOK.Deprecated; break;
810 case STC.Extern: t = TOK.Extern; break;
811 case STC.Final: t = TOK.Final; break;
812 case STC.Override: t = TOK.Override; break;
813 case STC.Scope: t = TOK.Scope; break;
814 case STC.Static: t = TOK.Static; break;
815 case STC.Synchronized: t = TOK.Synchronized; break;
816 case STC.In: t = TOK.In; break;
817 case STC.Out: t = TOK.Out; break;
818 case STC.Ref: t = TOK.Ref; break;
819 case STC.Lazy: t = TOK.Lazy; break;
820 case STC.Variadic: assert(0); break;
821 case STC.Immutable: t = TOK.Immutable; break;
822 case STC.Manifest: t = TOK.Enum; break;
823 case STC.Nothrow: t = TOK.Nothrow; break;
824 case STC.Pure: t = TOK.Pure; break;
825 case STC.Shared: t = TOK.Shared; break;
826 case STC.Gshared: t = TOK.Gshared; break;
827 case STC.Inout: t = TOK.Inout; break;
828 case STC.Disable: i = Ident.disable; break;
829 case STC.Property: i = Ident.property; break;
830 case STC.Safe: i = Ident.safe; break;
831 case STC.System: i = Ident.system; break;
832 case STC.Trusted: i = Ident.trusted; break;
833 case STC.None: assert(0);
834 }
835 if (i)
836 w(T.At, id(i));
837 else
838 w(t.toToken());
839 writeAttrDecl(n.decls);
840 }
841
842 void visit(LinkageDecl n)
843 {
844 w(ind, T.Extern, T.LParen);
845 final switch (n.linkageType)
846 {
847 case LINK.C: w(id(Ident.C)); break;
848 case LINK.Cpp: w(id(Ident.C), T.Plus2); break;
849 case LINK.D: w(id(Ident.D)); break;
850 case LINK.Windows: w(id(Ident.Windows)); break;
851 case LINK.Pascal: w(id(Ident.Pascal)); break;
852 case LINK.System: w(id(Ident.System)); break;
853 case LINK.None: assert(0);
854 }
855 w(T.RParen);
856 writeAttrDecl(n.decls);
857 }
858
859 void visit(AlignDecl n)
860 {
861 w(ind, T.Align);
862 if (n.sizetok)
863 w(T.LParen, n.sizetok, T.RParen);
864 writeAttrDecl(n.decls);
865 }
866
867 void visit(PragmaDecl n)
868 {
869 w(ind, T.Pragma, T.LParen, n.name);
870 if (n.args) {
871 w(T.Comma);
872 w(n.args);
873 }
874 w(T.RParen);
875 writeAttrDecl(n.decls);
876 }
877
878 void visit(MixinDecl n)
879 {
880 w(ind, T.Mixin);
881 if (n.isMixinExpr)
882 {
883 w(T.LParen);
884 v(n.argument);
885 w(T.RParen);
886 }
887 else
888 {
889 w(ws);
890 v(n.templateExpr);
891 if (n.mixinIdent)
892 w(ws, n.mixinIdent);
893 }
894 }
895
896
897 // Statements:
898 void visit(IllegalStmt n)
899 {
900 assert(0);
901 }
902
903 void visit(CompoundStmt n)
904 {
905 foreach (x; n.stmnts)
906 v(x);
907 }
908
909 void visit(EmptyStmt n)
910 {
911 w(ind, T.Semicolon, Newline);
912 }
913
914 void visit(FuncBodyStmt n)
915 {
916 if (n.isEmpty())
917 w(T.Semicolon, Newline);
918 else
919 {
920 if (n.inBody) {
921 w(Newline);
922 w(ind, T.In);
923 writeBlock(n.inBody);
924 }
925 if (n.outBody) {
926 if (!n.inBody)
927 w(Newline);
928 w(ind, T.Out);
929 if (n.outIdent)
930 w(T.LParen, n.outIdent, T.RParen);
931 writeBlock(n.outBody);
932 }
933 if (n.inBody || n.outBody)
934 w(ind, T.Body);
935 writeBlock(n.funcBody);
936 }
937 w(Newline);
938 }
939
940 void visit(ScopeStmt n)
941 {
942 v(n.stmnt);
943 }
944
945 void visit(LabeledStmt n)
946 {
947 {
948 scope ul = new UnindentLevel;
949 w(ind, n.label, T.Colon, Newline);
950 }
951 v(n.stmnt);
952 }
953
954 void visit(ExpressionStmt n)
955 {
956 w(ind);
957 v(n.expr);
958 w(T.Semicolon, Newline);
959 }
960
961 void visit(DeclarationStmt n)
962 {
963 v(n.decl);
964 }
965
966 void visit(IfStmt n)
967 {
968 w(ind, T.If, T.LParen);
969 if (auto var = n.variable ? n.variable.to!VariablesDecl : null)
970 {
971 if (var.type)
972 v(var.type);
973 else
974 w(T.Auto);
975 w(ws, var.names[0], ws, T.Equal, ws);
976 v(var.inits[0]);
977 }
978 else
979 v(n.condition);
980 w(T.RParen);
981 writeBlock(n.ifBody);
982 if (n.elseBody)
983 {
984 w(ind, T.Else);
985 writeBlock(n.elseBody);
986 }
987 }
988
989 void visit(WhileStmt n)
990 {
991 w(ind, T.While, T.LParen);
992 v(n.condition);
993 w(T.RParen);
994 writeBlock(n.whileBody);
995 }
996
997 void visit(DoWhileStmt n)
998 {
999 w(ind, T.Do);
1000 writeBlock(n.doBody);
1001 w(ind, T.While, T.LParen);
1002 v(n.condition);
1003 version(D1)
1004 w(T.RParen, Newline);
1005 else
1006 w(T.RParen, T.Semicolon, Newline);
1007 }
1008
1009 void visit(ForStmt n)
1010 {
1011 w(ind, T.For, T.LParen);
1012 if (n.init)
1013 {
1014 scope il = new SetIndentLevel("");
1015 v(n.init);
1016 // FIXME: remove trailing newlines.
1017 }
1018 else
1019 w(T.Semicolon);
1020 if (n.condition) {
1021 w(ws);
1022 v(n.condition);
1023 }
1024 w(T.Semicolon);
1025 if (n.increment) {
1026 w(ws);
1027 v(n.increment);
1028 }
1029 w(T.RParen);
1030 writeBlock(n.forBody);
1031 }
1032
1033 void visit(ForeachStmt n)
1034 {
1035 w(ind, n.tok.toToken(), T.LParen);
1036 foreach (i, param; n.params.items())
1037 {
1038 if (i)
1039 w(T.Comma, ws);
1040 v(param);
1041 }
1042 w(T.Semicolon, ws);
1043 v(n.aggregate);
1044 w(T.RParen);
1045 writeBlock(n.forBody);
1046 }
1047
1048 void visit(SwitchStmt n)
1049 {
1050 w(ind);
1051 if (n.isFinal)
1052 w(T.Final, ws);
1053 w(T.Switch, T.LParen);
1054 v(n.condition);
1055 w(T.RParen,);
1056 writeBlock(n.switchBody);
1057 }
1058
1059 void visit(CaseStmt n)
1060 {
1061 scope ul = new UnindentLevel;
1062 w(ind, T.Case, ws);
1063 foreach (i, value; n.values)
1064 {
1065 if (i)
1066 w(T.Comma, ws);
1067 v(value);
1068 }
1069 w(T.Colon, Newline);
1070 scope il = new IndentLevel;
1071 v(n.caseBody);
1072 }
1073
1074 void visit(CaseRangeStmt n)
1075 {
1076 scope ul = new UnindentLevel;
1077 w(ind, T.Case, ws);
1078 v(n.left);
1079 w(T.Dot2);
1080 v(n.right);
1081 w(T.Colon, Newline);
1082 scope il = new IndentLevel;
1083 v(n.caseBody);
1084 }
1085
1086 void visit(DefaultStmt n)
1087 {
1088 scope ul = new UnindentLevel;
1089 w(ind, T.Default, T.Colon, Newline);
1090 scope il = new IndentLevel;
1091 v(n.defaultBody);
1092 }
1093
1094 void visit(ContinueStmt n)
1095 {
1096 w(ind, T.Continue);
1097 if (n.label)
1098 w(ws, n.label);
1099 w(T.Semicolon, Newline);
1100 }
1101
1102 void visit(BreakStmt n)
1103 {
1104 w(ind, T.Break);
1105 if (n.label)
1106 w(ws, n.label);
1107 w(T.Semicolon, Newline);
1108 }
1109
1110 void visit(ReturnStmt n)
1111 {
1112 w(ind, T.Return);
1113 if (n.expr) {
1114 w(ws);
1115 v(n.expr);
1116 }
1117 w(T.Semicolon, Newline);
1118 }
1119
1120 void visit(GotoStmt n)
1121 {
1122 w(ind, T.Goto, ws);
1123 if (n.isGotoLabel)
1124 w(n.ident);
1125 else if (n.isGotoCase)
1126 {
1127 if (n.expr)
1128 v(n.expr);
1129 }
1130 else
1131 w(T.Default);
1132 w(T.Semicolon, Newline);
1133 }
1134
1135 void visit(WithStmt n)
1136 {
1137 w(ind, T.With, T.LParen);
1138 v(n.expr);
1139 w(T.RParen);
1140 writeBlock(n.withBody);
1141 }
1142
1143 void visit(SynchronizedStmt n)
1144 {
1145 w(ind, T.Synchronized);
1146 if (n.expr)
1147 {
1148 w(T.LParen);
1149 v(n.expr);
1150 w(T.RParen);
1151 }
1152 writeBlock(n.syncBody);
1153 }
1154
1155 void visit(TryStmt n)
1156 {
1157 w(ind, T.Try);
1158 writeBlock(n.tryBody);
1159 foreach (b; n.catchBodies)
1160 v(b);
1161 if (n.finallyBody)
1162 v(n.finallyBody);
1163 }
1164
1165 void visit(CatchStmt n)
1166 {
1167 w(ind, T.Catch);
1168 if (n.param)
1169 {
1170 w(T.LParen);
1171 v(n.param);
1172 w(T.RParen);
1173 }
1174 writeBlock(n.catchBody);
1175 }
1176
1177 void visit(FinallyStmt n)
1178 {
1179 w(ind, T.Finally);
1180 writeBlock(n.finallyBody);
1181 }
1182
1183 void visit(ScopeGuardStmt n)
1184 {
1185 w(ind, T.Scope, T.LParen, n.condition, T.RParen);
1186 writeBlock(n.scopeBody);
1187 }
1188
1189 void visit(ThrowStmt n)
1190 {
1191 w(ind, T.Throw, ws);
1192 v(n.expr);
1193 w(T.Semicolon, Newline);
1194 }
1195
1196 void visit(VolatileStmt n)
1197 {
1198 w(ind, T.Volatile);
1199 writeBlock(n.volatileBody);
1200 }
1201
1202 void visit(AsmBlockStmt n)
1203 {
1204 w(ind, T.LBrace);
1205 scope il = new IndentLevel;
1206 v(n.statements);
1207 w(T.RBrace);
1208 }
1209
1210 void visit(AsmStmt n)
1211 {
1212 w(ind, n.opcode);
1213 foreach (i, o; n.operands)
1214 {
1215 if (i)
1216 w(T.Comma, ws);
1217 v(o);
1218 }
1219 w(T.Semicolon, Newline);
1220 }
1221
1222 void visit(AsmAlignStmt n)
1223 {
1224 w(ind, T.Align, n.numtok, T.Semicolon, Newline);
1225 }
1226
1227 void visit(IllegalAsmStmt n)
1228 {
1229 assert(0);
1230 }
1231
1232 void visit(PragmaStmt n)
1233 {
1234 w(ind, T.Pragma, T.LParen, n.name);
1235 if (n.args) {
1236 w(T.Comma);
1237 w(n.args);
1238 }
1239 w(T.RParen);
1240 writeBlock(n.pragmaBody);
1241 }
1242
1243 void visit(MixinStmt n)
1244 {
1245 w(ind, T.Mixin, ws);
1246 v(n.templateExpr);
1247 if (n.mixinIdent)
1248 w(ws, n.mixinIdent);
1249 w(T.Semicolon, Newline);
1250 }
1251
1252 void visit(StaticIfStmt n)
1253 {
1254 w(ind, T.Static, ws, T.If, T.LParen);
1255 v(n.condition);
1256 w(T.RParen);
1257 writeBlock(n.ifBody);
1258 if (n.elseBody) {
1259 w(ind, T.Else);
1260 writeBlock(n.elseBody);
1261 }
1262 }
1263
1264 void visit(StaticAssertStmt n)
1265 {
1266 w(ind, T.Static, ws, T.Assert, T.LParen);
1267 v(n.condition);
1268 if (n.message) {
1269 w(T.Comma, ws);
1270 v(n.message);
1271 }
1272 w(T.RParen, Newline);
1273 }
1274
1275 void visit(DebugStmt n)
1276 {
1277 w(ind, T.Debug);
1278 if (n.cond)
1279 w(T.LParen, n.cond, T.RParen);
1280 writeBlock(n.mainBody);
1281 if (n.elseBody)
1282 {
1283 w(ind, T.Else);
1284 writeBlock(n.elseBody);
1285 }
1286 }
1287
1288 void visit(VersionStmt n)
1289 {
1290 w(ind, T.Version, T.LParen, n.cond, T.RParen);
1291 writeBlock(n.mainBody);
1292 if (n.elseBody)
1293 {
1294 w(ind, T.Else);
1295 writeBlock(n.elseBody);
1296 }
1297 }
1298
1299
1300 // Expressions:
1301 void visit(IllegalExpr n)
1302 {
1303 assert(0);
1304 }
1305
1306 void visit(CondExpr n)
1307 {
1308 w(n.condition, PREC.LogicalOr);
1309 w(ws, T.Question, ws);
1310 w(n.lhs, PREC.Expression);
1311 w(ws, T.Colon, ws);
1312 w(n.rhs, PREC.Conditional);
1313 }
1314
1315 void visit(CommaExpr n)
1316 { // lhs may be another CommaExpr.
1317 w(n.lhs, PREC.Expression);
1318 w(n.optok, ws);
1319 w(n.rhs, PREC.Assignment);
1320 }
1321
1322 void visit(OrOrExpr n)
1323 {
1324 w(n);
1325 }
1326
1327 void visit(AndAndExpr n)
1328 {
1329 w(n);
1330 }
1331
1332 void visit(OrExpr n)
1333 {
1334 w(n);
1335 }
1336
1337 void visit(XorExpr n)
1338 {
1339 w(n);
1340 }
1341
1342 void visit(AndExpr n)
1343 {
1344 w(n);
1345 }
1346
1347 void visit(EqualExpr n)
1348 {
1349 w(n);
1350 }
1351
1352 void visit(IdentityExpr n)
1353 {
1354 w(n.lhs, PREC.Relational);
1355 if (n.optok.kind == TOK.Exclaim)
1356 w(ws, T.Exclaim, T.Is, ws);
1357 else
1358 w(ws, T.Is, ws);
1359 w(n.rhs, PREC.Relational);
1360 }
1361
1362 void visit(RelExpr n)
1363 {
1364 w(n);
1365 }
1366
1367 void visit(InExpr n)
1368 {
1369 w(n.lhs, PREC.Relational);
1370 if (n.optok.kind == TOK.Exclaim)
1371 w(ws, T.Exclaim, T.In, ws);
1372 else
1373 w(ws, T.In, ws);
1374 w(n.rhs, PREC.Relational);
1375 }
1376
1377 void visit(LShiftExpr n)
1378 {
1379 w(n);
1380 }
1381
1382 void visit(RShiftExpr n)
1383 {
1384 w(n);
1385 }
1386
1387 void visit(URShiftExpr n)
1388 {
1389 w(n);
1390 }
1391
1392 void visit(PlusExpr n)
1393 {
1394 w(n);
1395 }
1396
1397 void visit(MinusExpr n)
1398 {
1399 w(n);
1400 }
1401
1402 void visit(CatExpr n)
1403 {
1404 w(n);
1405 }
1406
1407 void visit(MulExpr n)
1408 {
1409 w(n);
1410 }
1411
1412 void visit(DivExpr n)
1413 {
1414 w(n);
1415 }
1416
1417 void visit(ModExpr n)
1418 {
1419 w(n);
1420 }
1421
1422 void visit(PowExpr n)
1423 {
1424 w(n);
1425 }
1426
1427 void visit(AssignExpr n)
1428 {
1429 w(n);
1430 }
1431
1432 void visit(LShiftAssignExpr n)
1433 {
1434 w(n);
1435 }
1436
1437 void visit(RShiftAssignExpr n)
1438 {
1439 w(n);
1440 }
1441
1442 void visit(URShiftAssignExpr n)
1443 {
1444 w(n);
1445 }
1446
1447 void visit(OrAssignExpr n)
1448 {
1449 w(n);
1450 }
1451
1452 void visit(AndAssignExpr n)
1453 {
1454 w(n);
1455 }
1456
1457 void visit(PlusAssignExpr n)
1458 {
1459 w(n);
1460 }
1461
1462 void visit(MinusAssignExpr n)
1463 {
1464 w(n);
1465 }
1466
1467 void visit(DivAssignExpr n)
1468 {
1469 w(n);
1470 }
1471
1472 void visit(MulAssignExpr n)
1473 {
1474 w(n);
1475 }
1476
1477 void visit(ModAssignExpr n)
1478 {
1479 w(n);
1480 }
1481
1482 void visit(XorAssignExpr n)
1483 {
1484 w(n);
1485 }
1486
1487 void visit(CatAssignExpr n)
1488 {
1489 w(n);
1490 }
1491
1492 void visit(PowAssignExpr n)
1493 {
1494 w(n);
1495 }
1496
1497 void visit(RangeExpr n)
1498 {
1499 w(n);
1500 }
1501
1502 void visit(AddressExpr n)
1503 {
1504 w(T.Amp);
1505 w(n);
1506 }
1507
1508 void visit(PreIncrExpr n)
1509 {
1510 w(T.Plus2);
1511 w(n);
1512 }
1513
1514 void visit(PreDecrExpr n)
1515 {
1516 w(T.Minus2);
1517 w(n);
1518 }
1519
1520 void visit(PostIncrExpr n)
1521 {
1522 w(n);
1523 w(T.Plus2);
1524 }
1525
1526 void visit(PostDecrExpr n)
1527 {
1528 w(n);
1529 w(T.Minus2);
1530 }
1531
1532 void visit(DerefExpr n)
1533 {
1534 w(T.Star);
1535 w(n);
1536 }
1537
1538 void visit(SignExpr n)
1539 {
1540 if (n.isPos)
1541 w(T.Plus);
1542 else
1543 w(T.Minus);
1544 w(n);
1545 }
1546
1547 void visit(NotExpr n)
1548 {
1549 w(T.Exclaim);
1550 w(n);
1551 }
1552
1553 void visit(CompExpr n)
1554 {
1555 w(T.Tilde);
1556 w(n);
1557 }
1558
1559 void visit(CallExpr n)
1560 {
1561 w(n);
1562 w(T.LParen);
1563 w(n.args);
1564 w(T.RParen);
1565 }
1566
1567 void visit(NewExpr n)
1568 {
1569 if (n.frame) {
1570 v(n.frame);
1571 w(T.Dot);
1572 }
1573 w(T.New);
1574 if (n.newArgs)
1575 {
1576 w(T.LParen);
1577 w(n.newArgs);
1578 w(T.RParen);
1579 }
1580 w(ws);
1581 v(n.type);
1582 if (n.ctorArgs)
1583 {
1584 w(T.LParen);
1585 w(n.ctorArgs);
1586 w(T.RParen);
1587 }
1588 }
1589
1590 void visit(NewClassExpr n)
1591 {
1592 if (n.frame) {
1593 v(n.frame);
1594 w(T.Dot);
1595 }
1596 w(T.New);
1597 if (n.newArgs)
1598 {
1599 w(T.LParen);
1600 w(n.newArgs);
1601 w(T.RParen);
1602 }
1603 w(ws);
1604 w(T.Class);
1605 if (n.ctorArgs)
1606 {
1607 w(T.LParen);
1608 w(n.ctorArgs);
1609 w(T.RParen);
1610 }
1611 if (n.bases)
1612 {
1613 w(ws);
1614 foreach (i, b; n.bases)
1615 {
1616 if (i)
1617 w(T.Comma, ws);
1618 v(b);
1619 }
1620 }
1621 writeAggregateBody(n.decls);
1622 }
1623
1624 void visit(DeleteExpr n)
1625 {
1626 w(T.Delete);
1627 v(n.una);
1628 }
1629
1630 void visit(CastExpr n)
1631 {
1632 w(T.Cast, T.LParen);
1633 if (n.type)
1634 v(n.type);
1635 w(T.RParen);
1636 w(n);
1637 }
1638
1639 void visit(IndexExpr n)
1640 {
1641 w(n);
1642 w(T.LBracket);
1643 w(n.args);
1644 w(T.RBracket);
1645 }
1646
1647 void visit(SliceExpr n)
1648 {
1649 w(n);
1650 w(T.LBracket);
1651 if (n.range)
1652 w(n.range.to!RangeExpr);
1653 w(T.RBracket);
1654 }
1655
1656 void visit(ModuleScopeExpr n)
1657 {
1658 w(T.Dot);
1659 }
1660
1661 void visit(IdentifierExpr n)
1662 {
1663 if (n.next) {
1664 w(n.next, PREC.Primary);
1665 if (!n.next.Is!(ModuleScopeExpr))
1666 w(T.Dot);
1667 }
1668 w(n.name);
1669 }
1670
1671 void visit(SpecialTokenExpr n)
1672 {
1673 w(n.specialToken);
1674 }
1675
1676 void visit(TmplInstanceExpr n)
1677 {
1678 if (n.next) {
1679 w(n.next, PREC.Primary);
1680 if (!n.next.Is!(ModuleScopeExpr))
1681 w(T.Dot);
1682 }
1683 w(n.name, T.Exclaim, T.LParen);
1684 v(n.targs);
1685 w(T.RParen);
1686 }
1687
1688 void visit(ThisExpr n)
1689 {
1690 w(T.This);
1691 }
1692
1693 void visit(SuperExpr n)
1694 {
1695 w(T.Super);
1696 }
1697
1698 void visit(NullExpr n)
1699 {
1700 w(T.Null);
1701 }
1702
1703 void visit(DollarExpr n)
1704 {
1705 w(T.Dollar);
1706 }
1707
1708 void visit(BoolExpr n)
1709 {
1710 w(n.toBool ? T.True : T.False);
1711 }
1712
1713 void visit(IntExpr n)
1714 {
1715 if (auto t = n.begin) // Write the original Token if available.
1716 if (t.kind.In(TOK.Int32, TOK.Int64, TOK.UInt32, TOK.UInt64))
1717 return w(t);
1718
1719 void writeCast(TOK k)
1720 {
1721 w(T.Cast, T.LParen, k.toToken(), T.RParen);
1722 }
1723
1724 cstring txt;
1725 TOK k;
1726 auto num = n.number;
1727
1728 if (n.type)
1729 switch (n.type.tid)
1730 {
1731 case TYP.Int8: writeCast(TOK.Byte); goto case TYP.Int32;
1732 case TYP.Int16: writeCast(TOK.Short); goto case TYP.Int32;
1733 case TYP.Int32: txt = "{}"; k = TOK.Int32; break;
1734 case TYP.Int64: txt = "{}L"; k = TOK.Int64; break;
1735 case TYP.UInt8: writeCast(TOK.Ubyte); goto case TYP.UInt32;
1736 case TYP.UInt16: writeCast(TOK.Ushort); goto case TYP.UInt32;
1737 case TYP.UInt32: txt = "{}U"; k = TOK.UInt32; break;
1738 case TYP.UInt64: txt = "{}LU"; k = TOK.UInt32; break;
1739 default:
1740 assert(0, "unhandled int type");
1741 }
1742 else
1743 {
1744 txt = "{}";
1745 if (num & 0x8000_0000_0000_0000) {
1746 txt = "0x{:X}";
1747 k = TOK.UInt64;
1748 }
1749 else if (num & 0xFFFF_FFFF_0000_0000)
1750 k = TOK.Int64;
1751 else if (num & 0x8000_0000) {
1752 txt = "0x{:X}";
1753 k = TOK.UInt32;
1754 }
1755 else
1756 k = TOK.Int32;
1757 }
1758 assert(k);
1759
1760 txt = Format(txt, num); // Convert to text.
1761
1762 // Finally construct the integer Token.
1763 Token t;
1764 t.kind = k;
1765 t.text = txt;
1766
1767 if (k == TOK.Int64 || k == TOK.UInt64)
1768 {
1769 version(X86_64)
1770 t.intval.ulong_ = num;
1771 else
1772 t.intval = cc.tables.lxtables.lookupUlong(num);
1773 }
1774 else
1775 t.uint_ = cast(uint)num;
1776
1777 w(&t);
1778 }
1779
1780 void visit(FloatExpr n)
1781 {
1782 if (auto t = n.begin) // Write the original Token if available.
1783 if (t.kind.In(TOK.Float32, TOK.Float64, TOK.Float80,
1784 TOK.IFloat32, TOK.IFloat64, TOK.IFloat80))
1785 return w(t);
1786
1787 cstring txt;
1788 TOK k = TOK.Float64;
1789
1790 // FIXME: inaccurate conversion
1791 txt = Format("{}", n.number);
1792
1793 if (auto type = n.type)
1794 {
1795 type = type.baseType();
1796 switch (n.type.tid)
1797 {
1798 case TYP.Float32, TYP.IFloat32, TYP.CFloat32:
1799 txt ~= 'F'; k = TOK.Float32; break;
1800 case TYP.Float80, TYP.IFloat80, TYP.CFloat80:
1801 txt ~= 'L'; k = TOK.Float80; break;
1802 default:
1803 assert(0, "unhandled float type");
1804 }
1805 if (type.isImaginary)
1806 txt ~= 'i';
1807 }
1808
1809 Token t;
1810 t.kind = k;
1811 t.text = txt;
1812 t.mpfloat = n.number;
1813
1814 w(&t);
1815 }
1816
1817 void visit(ComplexExpr n)
1818 {
1819 w(T.LParen);
1820 auto c = n.number;
1821 auto re = new FloatExpr(c.re, n.reType);
1822 auto im = new FloatExpr(c.im, n.imType);
1823 v(re);
1824 w(ws, c.im.isNeg ? T.Minus : T.Plus, ws);
1825 v(im);
1826 w(T.RParen);
1827 }
1828
1829 void visit(CharExpr n)
1830 {
1831 auto c = n.charValue;
1832 cstring txt = `'`;
1833 txt ~= (c == '\'') ? `\'` : (c == '\\') ? `\\` : escapeNonPrintable(c);
1834 txt ~= `'`;
1835
1836 Token t;
1837 t.kind = TOK.Character;
1838 t.text = txt;
1839 t.dchar_ = c;
1840
1841 w(&t);
1842 }
1843
1844 void visit(StringExpr n)
1845 {
1846 if (n.begin && n.end)
1847 { // Write original tokens if available.
1848 writeSpan(n.begin, n.end);
1849 return;
1850 }
1851
1852 // NB: could be optimized to copying the string if no escaping is needed.
1853 // FIXME: the string might not be in UTF-8.
1854 auto s = n.getString();
1855 char[] txt = `"`.dup;
1856
1857 foreach (c; s)
1858 txt ~= (c == '\"') ? `\"` : (c == '\\') ? `\\` : escapeNonPrintable(c);
1859
1860 txt ~= `"`;
1861
1862 if (auto pf = n.postfix)
1863 txt ~= pf;
1864
1865 Token t;
1866 t.kind = TOK.String;
1867 t.text = txt;
1868 // FIXME: look up in lxtables.
1869 auto sv = new Token.StringValue;
1870 sv.str = cast(cbinstr)s;
1871 t.strval = sv;
1872
1873 w(&t);
1874 }
1875
1876 void visit(ArrayLiteralExpr n)
1877 {
1878 w(T.LBracket);
1879 w(n.values);
1880 w(T.RBracket);
1881 }
1882
1883 void visit(AArrayLiteralExpr n)
1884 {
1885 w(T.LBracket);
1886 foreach (i, value; n.values)
1887 {
1888 if (i)
1889 w(T.Comma, ws);
1890 w(n.keys[i], PREC.Assignment);
1891 w(T.Colon, ws);
1892 w(value, PREC.Assignment);
1893 }
1894 w(T.RBracket);
1895 }
1896
1897 void visit(AssertExpr n)
1898 {
1899 w(T.Assert, T.LParen);
1900 w(n.expr, PREC.Assignment);
1901 if (n.msg) {
1902 w(T.Comma, ws);
1903 w(n.expr, PREC.Assignment);
1904 }
1905 w(T.RParen);
1906 }
1907
1908 void visit(MixinExpr n)
1909 {
1910 w(T.Mixin, T.LParen);
1911 v(n.expr);
1912 w(T.RParen);
1913 }
1914
1915 void visit(ImportExpr n)
1916 {
1917 w(T.Import, T.LParen);
1918 v(n.expr);
1919 w(T.RParen);
1920 }
1921
1922 void visit(TypeExpr n)
1923 {
1924 v(n.typeNode);
1925 }
1926
1927 void visit(TypeofExpr n)
1928 {
1929 v(n.type);
1930 }
1931
1932 void visit(TypeDotIdExpr n)
1933 {
1934 w(T.LParen);
1935 v(n.type);
1936 w(T.RParen, T.Dot, n.ident);
1937 }
1938
1939 void visit(TypeidExpr n)
1940 {
1941 w(T.Typeid, T.LParen);
1942 v(n.type);
1943 w(T.RParen);
1944 }
1945
1946 void visit(IsExpr n)
1947 {
1948 w(T.Is, T.LParen);
1949 v(n.type);
1950 if (n.name)
1951 w(ws, n.name);
1952 if (n.opTok)
1953 {
1954 w(ws, n.opTok, ws);
1955 if (n.specTok)
1956 w(n.specTok);
1957 else
1958 v(n.specType);
1959 }
1960 if (n.name && n.specType && n.tparams)
1961 {
1962 w(T.Comma, ws);
1963 foreach (i, param; n.tparams.items())
1964 {
1965 if (i)
1966 w(T.Comma, ws);
1967 v(param);
1968 }
1969 }
1970 w(T.RParen);
1971 }
1972
1973 void visit(ParenExpr n)
1974 {
1975 w(T.LParen);
1976 v(n.next);
1977 w(T.RParen);
1978 }
1979
1980 void visit(FuncLiteralExpr n)
1981 {
1982 if (n.tok)
1983 w(n.tok, ws);
1984 if (n.returnType)
1985 v(n.returnType), w(ws);
1986 if (n.params)
1987 v(n.params), w(ws);
1988 // TODO: print n.params.postSTCs
1989 if (auto fb = n.funcBody.Is!FuncBodyStmt)
1990 {
1991 w(T.LBrace, Newline);
1992 {
1993 scope il = new IndentLevel;
1994 v(fb.funcBody); // FIXME: fb can have in/out contracts.
1995 }
1996 w(ind, T.RBrace);
1997 }
1998 else
1999 {
2000 assert(n.funcBody.isExpression);
2001 w(T.EqlGreater, ws);
2002 w(cast(Expression)n.funcBody, PREC.Assignment);
2003 }
2004 }
2005
2006 void visit(LambdaExpr n)
2007 {
2008 if (n.params.length == 1 &&
2009 !n.params.items[0].stcs && // No STCs.
2010 !n.params.items[0].type && // No type.
2011 !n.params.items[0].defValue) // No default value.
2012 w(n.params.items[0].name); // Single argument.
2013 else
2014 v(n.params);
2015 w(ws, T.EqlGreater, ws);
2016 w(n.expr, PREC.Assignment);
2017 }
2018
2019 void visit(TraitsExpr n)
2020 {
2021 w(T.Traits, T.LParen, n.name);
2022 if (n.targs) {
2023 w(T.Comma);
2024 v(n.targs);
2025 }
2026 w(T.RParen);
2027 }
2028
2029 void visit(VoidInitExpr n)
2030 {
2031 w(T.Void);
2032 }
2033
2034 void visit(ArrayInitExpr n)
2035 {
2036 w(T.LBracket);
2037 foreach (i, value; n.values)
2038 {
2039 if (i)
2040 w(T.Comma, ws);
2041 if (auto key = n.keys[i]) {
2042 w(key, PREC.Assignment);
2043 w(T.Colon, ws);
2044 }
2045 w(value, PREC.Assignment);
2046 }
2047 w(T.RBracket);
2048 }
2049
2050 void visit(StructInitExpr n)
2051 {
2052 w(T.LBrace);
2053 foreach (i, value; n.values)
2054 {
2055 if (i)
2056 w(T.Comma, ws);
2057 if (auto ident = n.idents[i])
2058 w(ident, T.Colon, ws);
2059 w(value, PREC.Assignment);
2060 }
2061 w(T.RBrace);
2062 }
2063
2064 void visit(AsmTypeExpr n)
2065 {
2066 w(n.prefix, ws, id(Ident.ptr), ws);
2067 w(n.una, PREC.Unary);
2068 }
2069
2070 void visit(AsmOffsetExpr n)
2071 {
2072 w(id(Ident.offsetof));
2073 w(n.una, PREC.Unary);
2074 }
2075
2076 void visit(AsmSegExpr n)
2077 {
2078 w(id(Ident.seg));
2079 w(n.una, PREC.Unary);
2080 }
2081
2082 void visit(AsmPostBracketExpr n)
2083 {
2084 w(n.una, PREC.Unary);
2085 w(T.LBracket);
2086 w(n.index, PREC.Expression);
2087 w(T.RBracket);
2088 }
2089
2090 void visit(AsmBracketExpr n)
2091 {
2092 w(T.LBracket);
2093 w(n.expr, PREC.Expression);
2094 w(T.RBracket);
2095 }
2096
2097 void visit(AsmLocalSizeExpr n)
2098 {
2099 w(id(Ident.__LOCAL_SIZE));
2100 }
2101
2102 void visit(AsmRegisterExpr n)
2103 {
2104 w(n.register);
2105 if (n.number)
2106 {
2107 if (n.register.ident is Ident.ST && n.number)
2108 {
2109 w(T.LBracket);
2110 v(n.number);
2111 w(T.RBracket);
2112 }
2113 else
2114 {
2115 w(T.Colon);
2116 w(n.number, PREC.Expression);
2117 }
2118 }
2119 }
2120
2121
2122 // Types:
2123 void visit(IllegalType n)
2124 {
2125 assert(0);
2126 }
2127
2128 void visit(IntegralType n)
2129 {
2130 w(n.tok.toToken());
2131 }
2132
2133 void visit(ModuleScopeType n)
2134 {
2135 w(T.Dot);
2136 }
2137
2138 void visit(IdentifierType n)
2139 {
2140 if (n.next) {
2141 v(n.next);
2142 if (!n.next.Is!(ModuleScopeType))
2143 w(T.Dot);
2144 }
2145 w(n.name);
2146 }
2147
2148 void visit(TypeofType n)
2149 {
2150 w(T.Typeof, T.LParen);
2151 if (n.isTypeofReturn)
2152 w(T.Return);
2153 else
2154 v(n.expr);
2155 w(T.RParen);
2156 }
2157
2158 void visit(TmplInstanceType n)
2159 {
2160 if (n.next) {
2161 v(n.next);
2162 if (!n.next.Is!(ModuleScopeType))
2163 w(T.Dot);
2164 }
2165 w(n.name, T.Exclaim, T.LParen);
2166 v(n.targs);
2167 w(T.RParen);
2168 }
2169
2170 void visit(PointerType n)
2171 {
2172 v(n.next);
2173 w(T.Star);
2174 }
2175
2176 void visit(ArrayType n)
2177 {
2178 v(n.next);
2179 w(T.LBracket);
2180 if (n.assocType)
2181 v(n.assocType);
2182 else if (n.index1)
2183 {
2184 v(n.index1);
2185 if (n.index2) {
2186 w(T.Dot2);
2187 v(n.index2);
2188 }
2189 }
2190 w(T.RBracket);
2191 }
2192
2193 void visit(FunctionType n)
2194 {
2195 v(n.next);
2196 w(ws, T.Function);
2197 v(n.params);
2198 }
2199
2200 void visit(DelegateType n)
2201 {
2202 v(n.next);
2203 w(ws, T.Delegate);
2204 v(n.params);
2205 }
2206
2207 void visit(BaseClassType n)
2208 {
2209 if (auto tok = protToTOK(n.prot))
2210 w(tok.toToken(), ws);
2211 v(n.next);
2212 }
2213
2214 void visit(ModifierType n)
2215 {
2216 w(n.mod);
2217 if (n.hasParen)
2218 {
2219 w(T.LParen);
2220 v(n.next);
2221 w(T.RParen);
2222 }
2223 else if (n.next)
2224 {
2225 w(ws);
2226 v(n.next);
2227 }
2228 }
2229
2230 // Parameters:
2231 void visit(Parameter n)
2232 {
2233 if (n.isCVariadic)
2234 return w(T.Dot3);
2235 if (n.stcs)
2236 {
2237 for (auto i = STC.max; i; i >>= 1)
2238 if (auto stc = n.stcs & i)
2239 {
2240 TOK t;
2241 switch (stc)
2242 {
2243 case STC.Const: t = TOK.Const; break;
2244 case STC.Immutable: t = TOK.Immutable; break;
2245 case STC.Inout: t = TOK.Inout; break;
2246 case STC.Shared: t = TOK.Shared; break;
2247 case STC.Final: t = TOK.Final; break;
2248 case STC.Scope: t = TOK.Scope; break;
2249 case STC.Static: t = TOK.Static; break;
2250 case STC.Auto: t = TOK.Auto; break;
2251 case STC.In: t = TOK.In; break;
2252 case STC.Out: t = TOK.Out; break;
2253 case STC.Ref: t = TOK.Ref; break;
2254 case STC.Variadic: continue;
2255 default:
2256 assert(0, EnumString(stc));
2257 }
2258 w(t.toToken(), ws);
2259 }
2260 }
2261 if (n.type)
2262 v(n.type);
2263 if (n.hasName)
2264 {
2265 if (n.type)
2266 w(ws);
2267 w(n.name);
2268 }
2269 if (n.defValue) {
2270 w(ws, T.Equal, ws);
2271 v(n.defValue);
2272 }
2273 if (n.isDVariadic)
2274 w(T.Dot3);
2275 }
2276
2277 void visit(Parameters n)
2278 {
2279 w(T.LParen);
2280 foreach (i, param; n.items())
2281 {
2282 if (i)
2283 w(T.Comma, ws);
2284 v(param);
2285 }
2286 w(T.RParen);
2287 }
2288
2289 void visit(TemplateAliasParam n)
2290 {
2291 w(T.Alias, ws, n.name);
2292 writeSpecDef(n.spec, n.def);
2293 }
2294
2295 void visit(TemplateTypeParam n)
2296 {
2297 w(n.name);
2298 writeSpecDef(n.specType, n.defType);
2299 }
2300
2301 void visit(TemplateThisParam n)
2302 {
2303 w(T.This, ws, n.name);
2304 writeSpecDef(n.specType, n.defType);
2305 }
2306
2307 void visit(TemplateValueParam n)
2308 {
2309 v(n.valueType);
2310 w(ws, n.name);
2311 writeSpecDef(n.specValue, n.defValue);
2312 }
2313
2314 void visit(TemplateTupleParam n)
2315 {
2316 w(n.name, T.Dot3);
2317 }
2318
2319 void visit(TemplateParameters n)
2320 {
2321 w(T.LParen);
2322 foreach (i, param; n.items())
2323 {
2324 if (i)
2325 w(T.Comma, ws);
2326 v(param);
2327 }
2328 w(T.RParen);
2329 }
2330
2331 void visit(TemplateArguments n)
2332 {
2333 foreach (i, x; n.items)
2334 {
2335 if (i)
2336 w(T.Comma, ws);
2337 v(x);
2338 }
2339 }
2340 }