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 }