1 //          Copyright Brian Schott (Hackerpilot) 2012.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dparse.astprinter;
7 
8 import std.array;
9 import std.stdio;
10 import std.string;
11 
12 import dparse.ast;
13 import dparse.formatter;
14 import dparse.lexer;
15 
16 
17 /**
18 * AST visitor that outputs an XML representation of the AST to its file.
19 */
20 class XMLPrinter : ASTVisitor
21 {
22 	override void visit(const AddExpression addExpression)
23 	{
24 		output.writeln("<addExpression operator=\"", str(addExpression.operator), "\">");
25 		output.writeln("<left>");
26 		visit(addExpression.left);
27 		output.writeln("</left>");
28 		if (addExpression.right !is null)
29 		{
30 			output.writeln("<right>");
31 			visit(addExpression.right);
32 			output.writeln("</right>");
33 		}
34 		output.writeln("</addExpression>");
35 	}
36 
37 	override void visit(const AliasDeclaration aliasDeclaration)
38 	{
39 		output.writeln("<aliasDeclaration>");
40 		writeDdoc(aliasDeclaration.comment);
41 		aliasDeclaration.accept(this);
42 		output.writeln("</aliasDeclaration>");
43 	}
44 
45 	override void visit(const AlignAttribute alignAttribute)
46 	{
47 		if (alignAttribute.assignExpression is null)
48 			output.writeln("<alignAttribute/>");
49 		else
50 		{
51 			output.write("<alignAttribute align=\"");
52 			format(output.lockingTextWriter, alignAttribute.assignExpression);
53 			output.writeln("\"/>");
54 		}
55 	}
56 
57 	override void visit(const AndAndExpression andAndExpression)
58 	{
59 		output.writeln("<andAndExpression>");
60 		output.writeln("<left>");
61 		visit(andAndExpression.left);
62 		output.writeln("</left>");
63 		if (andAndExpression.right !is null)
64 		{
65 			output.writeln("<right>");
66 			visit(andAndExpression.right);
67 			output.writeln("</right>");
68 		}
69 		output.writeln("</andAndExpression>");
70 	}
71 
72 	override void visit(const AndExpression andExpression)
73 	{
74 		output.writeln("<andExpression>");
75 		output.writeln("<left>");
76 		visit(andExpression.left);
77 		output.writeln("</left>");
78 		if (andExpression.right !is null)
79 		{
80 			output.writeln("<right>");
81 			visit(andExpression.right);
82 			output.writeln("</right>");
83 		}
84 		output.writeln("</andExpression>");
85 	}
86 
87 	override void visit(const AsmInstruction asmInstruction)
88 	{
89 		output.writeln("<asmInstruction>");
90 		if (asmInstruction.hasAlign)
91 		{
92 			output.writeln("<align>");
93 			visit(asmInstruction.identifierOrIntegerOrOpcode);
94 			output.writeln("</align>");
95 		}
96 		if (asmInstruction.asmInstruction !is null)
97 		{
98 			output.writeln("<label label=\"",
99 					asmInstruction.identifierOrIntegerOrOpcode.text, "\"/>");
100 			asmInstruction.asmInstruction.accept(this);
101 		}
102 		else if (asmInstruction.identifierOrIntegerOrOpcode != tok!"")
103 			visit(asmInstruction.identifierOrIntegerOrOpcode);
104 		if (asmInstruction.operands !is null)
105 		{
106 			visit(asmInstruction.operands);
107 		}
108 		output.writeln("</asmInstruction>");
109 	}
110 
111 	override void visit(const AssignExpression assignExpression)
112 	{
113 		if (assignExpression.expression is null)
114 			output.writeln("<expression>");
115 		else
116 			output.writeln("<expression operator=\"",
117 					xmlAttributeEscape(str(assignExpression.operator)), "\">");
118 		assignExpression.accept(this);
119 		output.writeln("</expression>");
120 	}
121 
122 	override void visit(const AtAttribute atAttribute)
123 	{
124 		output.writeln("<atAttribute>");
125 		if (atAttribute.identifier.type != tok!"")
126 			output.writeln("<identifier>", atAttribute.identifier.text, "</identifier>");
127 		atAttribute.accept(this);
128 		output.writeln("</atAttribute>");
129 	}
130 
131 	override void visit(const Attribute attribute)
132 	{
133 		if (attribute.attribute == tok!"")
134 		{
135 			output.writeln("<attribute>");
136 			attribute.accept(this);
137 			output.writeln("</attribute>");
138 		}
139 		else if (attribute.identifierChain is null)
140 			output.writeln("<attribute attribute=\"", str(attribute.attribute.type), "\"/>");
141 		else
142 		{
143 			output.writeln("<attribute attribute=\"", str(attribute.attribute.type), "\">");
144 			visit(attribute.identifierChain);
145 			output.writeln("</attribute>");
146 		}
147 	}
148 
149 	override void visit(const AutoDeclaration autoDec)
150 	{
151 		output.writeln("<autoDeclaration>");
152 		output.writeln("<storageClasses>");
153 		foreach (sc; autoDec.storageClasses)
154 			visit(sc);
155 		output.writeln("</storageClasses>");
156 
157 		foreach (part; autoDec.parts)
158 			visit(part);
159 		output.writeln("</autoDeclaration>");
160 	}
161 
162 	override void visit(const AutoDeclarationPart part)
163 	{
164 		output.writeln("<autoDeclarationPart>");
165 
166 		output.writeln("<item>");
167 		output.writeln("<name line=\"", part.identifier.line, "\">", part.identifier.text, "</name>");
168 		visit(part.initializer);
169 		output.writeln("</item>");
170 		output.writeln("</autoDeclarationPart>");
171 	}
172 
173 	override void visit(const BreakStatement breakStatement)
174 	{
175 		if (breakStatement.label.type == tok!"")
176 			output.writeln("<breakStatement/>");
177 		else
178 			output.writeln("<breakStatement label=\"", breakStatement.label.text, "\"/>");
179 	}
180 
181 	override void visit(const BitfieldWidth bitfieldWidth)
182 	{
183 		output.writeln("<bitfieldWidth>");
184 		bitfieldWidth.accept(this);
185 		output.writeln("</bitfieldWidth>");
186 	}
187 
188 	override void visit(const CaseRangeStatement caseRangeStatement)
189 	{
190 		output.writeln("<caseRangeStatement>");
191 		if (caseRangeStatement.low !is null)
192 		{
193 			output.writeln("<low>");
194 			visit(caseRangeStatement.low);
195 			output.writeln("</low>");
196 		}
197 		if (caseRangeStatement.high !is null)
198 		{
199 			output.writeln("<high>");
200 			visit(caseRangeStatement.high);
201 			output.writeln("</high>");
202 		}
203 		if (caseRangeStatement.declarationsAndStatements !is null)
204 			visit(caseRangeStatement.declarationsAndStatements);
205 		output.writeln("</caseRangeStatement>");
206 	}
207 
208 	override void visit(const Catch catch_)
209 	{
210 		output.writeln("<catch>");
211 		catch_.accept(this);
212 		output.writeln("</catch>");
213 	}
214 
215 	override void visit(const ClassDeclaration classDec)
216 	{
217 		output.writeln("<classDeclaration line=\"", classDec.name.line, "\">");
218 		writeName(classDec.name.text);
219 		writeDdoc(classDec.comment);
220 		classDec.accept(this);
221 		output.writeln("</classDeclaration>");
222 	}
223 
224 	override void visit(const ConditionalDeclaration conditionalDeclaration)
225 	{
226 		output.writeln("<conditionalDeclaration>");
227 		visit(conditionalDeclaration.compileCondition);
228 		output.writeln("<trueDeclarations style=\"", conditionalDeclaration.trueStyle, "\">");
229 		foreach (dec; conditionalDeclaration.trueDeclarations)
230 			visit(dec);
231 		output.writeln("</trueDeclarations>");
232 		if (conditionalDeclaration.falseDeclarations.length > 0)
233 		{
234 			output.writeln("<falseDeclarations style=\"", conditionalDeclaration.trueStyle, "\">");
235 			foreach (dec; conditionalDeclaration.falseDeclarations)
236 				visit(dec);
237 			output.writeln("</falseDeclarations>");
238 		}
239 		output.writeln("</conditionalDeclaration>");
240 	}
241 
242 	override void visit(const ConditionalStatement conditionalStatement)
243 	{
244 		output.writeln("<conditionalStatement>");
245 		visit(conditionalStatement.compileCondition);
246 		output.writeln("<trueStatement>");
247 		visit(conditionalStatement.trueStatement);
248 		output.writeln("</trueStatement>");
249 		if (conditionalStatement.falseStatement !is null)
250 		{
251 			output.writeln("<falseStatement>");
252 			visit(conditionalStatement.falseStatement);
253 			output.writeln("</falseStatement>");
254 		}
255 		output.writeln("</conditionalStatement>");
256 	}
257 
258 	override void visit(const ContinueStatement continueStatement)
259 	{
260 		if (continueStatement.label.type == tok!"")
261 			output.writeln("<continueStatement/>");
262 		else
263 			output.writeln("<continueStatement label=\"", continueStatement.label.text, "\"/>");
264 	}
265 
266 	override void visit(const DebugCondition debugCondition)
267 	{
268 		if (debugCondition.identifierOrInteger.type == tok!"")
269 			output.writeln("<debugCondition/>");
270 		else
271 			output.writeln("<debugCondition condition=\"",
272 					debugCondition.identifierOrInteger.text, "\"/>");
273 	}
274 
275 	override void visit(const DebugSpecification debugSpecification)
276 	{
277 		if (debugSpecification.identifierOrInteger.type == tok!"")
278 			output.writeln("<debugSpecification/>");
279 		else
280 			output.writeln("<debugSpecification condition=\"",
281 					debugSpecification.identifierOrInteger.text, "\"/>");
282 	}
283 
284 	override void visit(const Declarator declarator)
285 	{
286 		output.writeln("<declarator line=\"", declarator.name.line, "\">");
287 		writeName(declarator.name.text);
288 		writeDdoc(declarator.comment);
289 		declarator.accept(this);
290 		output.writeln("</declarator>");
291 	}
292 
293 	override void visit(const Deprecated deprecated_)
294 	{
295 		if (deprecated_.assignExpression !is null)
296 		{
297 			output.writeln("<deprecated>");
298 			visit(deprecated_.assignExpression);
299 			output.writeln("</deprecated>");
300 		}
301 		else
302 			output.writeln("<deprecated/>");
303 	}
304 
305 	override void visit(const EnumDeclaration enumDec)
306 	{
307 		output.writeln("<enumDeclaration line=\"", enumDec.name.line, "\">");
308 		writeDdoc(enumDec.comment);
309 		if (enumDec.name.type == tok!"identifier")
310 			writeName(enumDec.name.text);
311 		enumDec.accept(this);
312 		output.writeln("</enumDeclaration>");
313 	}
314 
315 	override void visit(const AnonymousEnumMember enumMember)
316 	{
317 		output.writeln("<anonymousEnumMember line=\"", enumMember.name.line, "\">");
318 		writeDdoc(enumMember.comment);
319 		if (enumMember.type !is null)
320 			visit(enumMember.type);
321 		output.write("<name>", enumMember.name.text, "</name>");
322 		if (enumMember.assignExpression !is null)
323 			visit(enumMember.assignExpression);
324 		output.writeln("</anonymousEnumMember>");
325 	}
326 
327 	override void visit(const EnumMember enumMem)
328 	{
329 		output.writeln("<enumMember line=\"", enumMem.name.line, "\">");
330 		writeDdoc(enumMem.comment);
331 		enumMem.accept(this);
332 		output.writeln("</enumMember>");
333 	}
334 
335 	override void visit(const EqualExpression equalExpression)
336 	{
337 		output.writeln("<equalExpression operator=\"", str(equalExpression.operator), "\">");
338 		output.writeln("<left>");
339 		visit(equalExpression.left);
340 		output.writeln("</left>");
341 		output.writeln("<right>");
342 		visit(equalExpression.right);
343 		output.writeln("</right>");
344 		output.writeln("</equalExpression>");
345 	}
346 
347 	override void visit(const Finally finally_)
348 	{
349 		output.writeln("<finally>");
350 		finally_.accept(this);
351 		output.writeln("</finally>");
352 	}
353 
354 	override void visit(const ForStatement forStatement)
355 	{
356 		output.writeln("<forStatement>");
357 		if (forStatement.initialization !is null)
358 		{
359 			output.writeln("<initialization>");
360 			visit(forStatement.initialization);
361 			output.writeln("</initialization>");
362 		}
363 		if (forStatement.test !is null)
364 		{
365 			output.writeln("<test>");
366 			visit(forStatement.test);
367 			output.writeln("</test>");
368 		}
369 		if (forStatement.increment !is null)
370 		{
371 			output.writeln("<increment>");
372 			visit(forStatement.increment);
373 			output.writeln("</increment>");
374 		}
375 		if (forStatement.declarationOrStatement !is null)
376 			visit(forStatement.declarationOrStatement);
377 		output.writeln("</forStatement>");
378 	}
379 
380 	override void visit(const ForeachStatement foreachStatement)
381 	{
382 		output.writeln("<foreachStatement type=\"", str(foreachStatement.type), "\">");
383 		if (foreachStatement.foreachType !is null)
384 			visit(foreachStatement.foreachType);
385 		if (foreachStatement.foreachTypeList !is null)
386 			visit(foreachStatement.foreachTypeList);
387 		output.writeln("<low>");
388 		visit(foreachStatement.low);
389 		output.writeln("</low>");
390 		if (foreachStatement.high !is null)
391 		{
392 			output.writeln("<high>");
393 			visit(foreachStatement.high);
394 			output.writeln("</high>");
395 		}
396 		visit(foreachStatement.declarationOrStatement);
397 		output.writeln("</foreachStatement>");
398 	}
399 
400 	override void visit(const ForeachType foreachType)
401 	{
402 		output.writeln("<foreachType>");
403 		if (foreachType.isAlias) output.writeln("<alias/>");
404 		if (foreachType.isEnum) output.writeln("<enum/>");
405 		if (foreachType.isRef) output.writeln("<ref/>");
406 		if (foreachType.isScope) output.writeln("<scope/>");
407 		foreach (constructor; foreachType.typeConstructors)
408 		{
409 			output.writeln("<typeConstructor>", str(constructor), "</typeConstructor>");
410 		}
411 		if (foreachType.type !is null)
412 			visit(foreachType.type);
413 		visit(foreachType.identifier);
414 		output.writeln("</foreachType>");
415 
416 	}
417 
418 	override void visit(const FunctionDeclaration functionDec)
419 	{
420 		output.writeln("<functionDeclaration line=\"", functionDec.name.line, "\">");
421 		writeName(functionDec.name.text);
422 		writeDdoc(functionDec.comment);
423 		if (functionDec.hasAuto)
424 			output.writeln("<auto/>");
425 		if (functionDec.hasRef)
426 			output.writeln("<ref/>");
427 		functionDec.accept(this);
428 		output.writeln("</functionDeclaration>");
429 	}
430 
431 	override void visit(const FunctionLiteralExpression functionLiteralExpression)
432 	{
433 		with (functionLiteralExpression)
434 		{
435 			output.write("<functionLiteralExpression type=\"",
436 				functionOrDelegate != tok!"" ? str(functionOrDelegate) : "auto",
437 				"\"");
438 			if (identifier.type != tok!"")
439 				output.write(" identifier=\"", identifier, "\"");
440 			output.write(" line=\"", line, "\"");
441 			output.write(" column=\"", column, "\"");
442 			final switch (returnRefType) with (ReturnRefType)
443 			{
444 			case noRef: break;
445 			case ref_: output.write(" ref=\"ref\""); break;
446 			case autoRef: output.write(" ref=\"auto ref\""); break;
447 			}
448 			output.writeln(">");
449 			accept(this);
450 			output.writeln("</functionLiteralExpression>");
451 		}
452 	}
453 
454 	override void visit(const GotoStatement gotoStatement)
455 	{
456 		if (gotoStatement.label.type == tok!"default")
457 			output.writeln("<gotoStatement default=\"true\"/>");
458 		else if (gotoStatement.label.type == tok!"identifier")
459 			output.writeln("<gotoStatement label=\"", gotoStatement.label.text, "\"/>");
460 		else
461 		{
462 			output.writeln("<gotoStatement>");
463 			output.writeln("<case>");
464 			if (gotoStatement.expression)
465 				visit(gotoStatement.expression);
466 			output.writeln("</case>");
467 			output.writeln("</gotoStatement>");
468 		}
469 	}
470 
471 	override void visit(const IdentityExpression identityExpression)
472 	{
473 		if (identityExpression.negated)
474 			output.writeln("<identityExpression operator=\"!is\">");
475 		else
476 			output.writeln("<identityExpression operator=\"is\">");
477 		output.writeln("<left>");
478 		visit(identityExpression.left);
479 		output.writeln("</left>");
480 		output.writeln("<right>");
481 		visit(identityExpression.right);
482 		output.writeln("</right>");
483 		output.writeln("</identityExpression>");
484 	}
485 
486 	override void visit(const IfStatement ifStatement)
487 	{
488 		output.writeln("<ifStatement>");
489 
490 		ifStatement.condition.accept(this);
491 
492 		output.writeln("<then>");
493 		ifStatement.thenStatement.accept(this);
494 		output.writeln("</then>");
495 
496 		if (ifStatement.elseStatement !is null)
497 		{
498 			output.writeln("<else>");
499 			ifStatement.elseStatement.accept(this);
500 			output.writeln("</else>");
501 		}
502 		output.writeln("</ifStatement>");
503 	}
504 
505 	override void visit(const IfCondition ifCondition)
506 	{
507 		output.writeln("<condition>");
508 		foreach (constructor; ifCondition.typeCtors)
509 		{
510 			output.writeln("<typeConstructor>", str(constructor), "</typeConstructor>");
511 		}
512 		if (ifCondition.identifier.type != tok!"")
513 		{
514 			if (ifCondition.scope_)
515 				output.writeln("<scope/>");
516 			else if (ifCondition.type is null)
517 				output.writeln("<auto/>");
518 
519 			// in case `scope T` is possible eventually, check type separately
520 			if (ifCondition.type)
521 				visit(ifCondition.type);
522 			visit(ifCondition.identifier);
523 		}
524 		ifCondition.expression.accept(this);
525 		output.writeln("</condition>");
526 	}
527 
528 	override void visit(const ImportBind importBind)
529 	{
530 		if (importBind.right.type == tok!"")
531 			output.writeln("<importBind symbol=\"", importBind.left.text, "\"/>");
532 		else
533 			output.writeln("<importBind symbol=\"", importBind.right.text,
534 					"\" rename=\"", importBind.left.text, "\"/>");
535 	}
536 
537 	override void visit(const InExpression inExpression)
538 	{
539 		if (inExpression.negated)
540 			output.writeln("<inExpression operator=\"!in\">");
541 		else
542 			output.writeln("<inExpression operator=\"in\">");
543 		output.writeln("<left>");
544 		visit(inExpression.left);
545 		output.writeln("</left>");
546 		output.writeln("<right>");
547 		visit(inExpression.right);
548 		output.writeln("</right>");
549 		output.writeln("</inExpression>");
550 	}
551 
552 	override void visit(const Initialize initialize)
553 	{
554 		if (initialize.statementNoCaseNoDefault is null)
555 			output.writeln("<initialize/>");
556 		else
557 		{
558 			output.writeln("<initialize>");
559 			visit(initialize.statementNoCaseNoDefault);
560 			output.writeln("</initialize>");
561 		}
562 	}
563 
564 	override void visit(const Initializer initializer)
565 	{
566 		if (initializer.nonVoidInitializer is null)
567 			output.writeln("<initializer void=\"true\"/>");
568 		else
569 		{
570 			output.writeln("<initializer>");
571 			visit(initializer.nonVoidInitializer);
572 			output.writeln("</initializer>");
573 		}
574 	}
575 
576 	override void visit(const InterfaceDeclaration interfaceDec)
577 	{
578 		output.writeln("<interfaceDeclaration line=\"", interfaceDec.name.line, "\">");
579 		writeName(interfaceDec.name.text);
580 		writeDdoc(interfaceDec.comment);
581 		interfaceDec.accept(this);
582 		output.writeln("</interfaceDeclaration>");
583 	}
584 
585 	override void visit(const Invariant invariant_)
586 	{
587 		output.writeln("<invariant>");
588 		writeDdoc(invariant_.comment);
589 		invariant_.accept(this);
590 		output.writeln("</invariant>");
591 	}
592 
593 	override void visit(const IsExpression isExpression)
594 	{
595 		output.writeln("<isExpression>");
596 		visit(isExpression.type);
597 		if (isExpression.identifier.type != tok!"")
598 			visit(isExpression.identifier);
599 		if (isExpression.typeSpecialization !is null)
600 		{
601 			if (isExpression.equalsOrColon == tok!":")
602 				output.writeln("<colon/>");
603 			else
604 				output.writeln("<equals/>");
605 			visit(isExpression.typeSpecialization);
606 			if (isExpression.templateParameterList !is null)
607 				visit(isExpression.templateParameterList);
608 		}
609 		output.writeln("</isExpression>");
610 	}
611 
612 	override void visit(const KeyValuePair keyValuePair)
613 	{
614 		output.writeln("<keyValuePair>");
615 		output.writeln("<key>");
616 		visit(keyValuePair.key);
617 		output.writeln("</key>");
618 		output.writeln("<value>");
619 		visit(keyValuePair.value);
620 		output.writeln("</value>");
621 		output.writeln("</keyValuePair>");
622 	}
623 
624 	override void visit(const LabeledStatement labeledStatement)
625 	{
626 		output.writeln("<labeledStatement label=\"", labeledStatement.identifier.text, "\">");
627 		if (labeledStatement.declarationOrStatement !is null)
628 			visit(labeledStatement.declarationOrStatement);
629 		output.writeln("</labeledStatement>");
630 	}
631 
632 	override void visit(const LinkageAttribute linkageAttribute)
633 	{
634 		if (linkageAttribute.hasPlusPlus)
635 		{
636 			output.write("<linkageAttribute linkage=\"C++\"");
637 			if (linkageAttribute.typeIdentifierPart !is null && linkageAttribute.typeIdentifierPart.typeIdentifierPart !is null)
638 			{
639 				output.write(" namespace=\"");
640 				format(output.lockingTextWriter, linkageAttribute.typeIdentifierPart);
641 				output.writeln("\"/>");
642 			}
643 			else if (linkageAttribute.classOrStruct == tok!"class")
644 				output.writeln(" mangleAs=\"class\"/>");
645 			else if (linkageAttribute.classOrStruct == tok!"struct")
646 				output.writeln(" mangleAs=\"struct\"/>");
647 			else
648 				output.writeln("/>");
649 		}
650 		else if (linkageAttribute.identifier.text == "Objective")
651 			output.writeln("<linkageAttribute linkage=\"Objective-C\"/>");
652 		else
653 			output.writeln("<linkageAttribute linkage=\"",
654 					linkageAttribute.identifier.text, "\"/>");
655 	}
656 
657 	override void visit(const MemberFunctionAttribute memberFunctionAttribute)
658 	{
659 		output.writeln("<memberFunctionAttribute>");
660 		if (memberFunctionAttribute.atAttribute is null)
661 			output.writeln(str(memberFunctionAttribute.tokenType));
662 		else
663 			memberFunctionAttribute.accept(this);
664 		output.writeln("</memberFunctionAttribute>");
665 	}
666 
667 	override void visit(const Module module_)
668 	{
669 		output.writeln("<?xml version=\"1.0\"?>");
670 		output.writeln("<module>");
671 		module_.accept(this);
672 		output.writeln("</module>");
673 	}
674 
675 	override void visit(const MulExpression mulExpression)
676 	{
677 		output.writeln("<mulExpression operator=\"", str(mulExpression.operator), "\">");
678 		output.writeln("<left>");
679 		visit(mulExpression.left);
680 		output.writeln("</left>");
681 		if (mulExpression.right !is null)
682 		{
683 			output.writeln("<right>");
684 			visit(mulExpression.right);
685 			output.writeln("</right>");
686 		}
687 		output.writeln("</mulExpression>");
688 	}
689 
690 	override void visit(const OrOrExpression orOrExpression)
691 	{
692 		output.writeln("<orOrExpression>");
693 		output.writeln("<left>");
694 		visit(orOrExpression.left);
695 		output.writeln("</left>");
696 		if (orOrExpression.right !is null)
697 		{
698 			output.writeln("<right>");
699 			visit(orOrExpression.right);
700 			output.writeln("</right>");
701 		}
702 		output.writeln("</orOrExpression>");
703 	}
704 
705 	override void visit(const ParameterAttribute pa)
706 	{
707 		output.writeln("<parameterAttribute>");
708 		if (pa.atAttribute)
709 			visit(pa.atAttribute);
710 		else
711 			writeln(str(pa.idType));
712 		output.writeln("</parameterAttribute>");
713 	}
714 
715 	override void visit(const Parameter param)
716 	{
717 		output.writeln("<parameter>");
718 		if (param.name.type == tok!"identifier")
719 			writeName(param.name.text);
720 		param.accept(this);
721 		if (param.vararg)
722 			output.writeln("<vararg/>");
723 		output.writeln("</parameter>");
724 	}
725 
726 	override void visit(const PowExpression powExpression)
727 	{
728 		output.writeln("<powExpression>");
729 		output.writeln("<left>");
730 		visit(powExpression.left);
731 		output.writeln("</left>");
732 		if (powExpression.right !is null)
733 		{
734 			output.writeln("<right>");
735 			visit(powExpression.right);
736 			output.writeln("</right>");
737 		}
738 		output.writeln("</powExpression>");
739 	}
740 
741 	override void visit(const RelExpression relExpression)
742 	{
743 		output.writeln("<relExpression operator=\"",
744 				xmlAttributeEscape(str(relExpression.operator)), "\">");
745 		output.writeln("<left>");
746 		visit(relExpression.left);
747 		output.writeln("</left>");
748 		output.writeln("<right>");
749 		visit(relExpression.right);
750 		output.writeln("</right>");
751 		output.writeln("</relExpression>");
752 	}
753 
754 	override void visit(const ReturnStatement returnStatement)
755 	{
756 		if (returnStatement.expression is null)
757 			output.writeln("<returnStatement/>");
758 		else
759 		{
760 			output.writeln("<returnStatement>");
761 			returnStatement.accept(this);
762 			output.writeln("</returnStatement>");
763 		}
764 	}
765 
766 	override void visit(const ShiftExpression shiftExpression)
767 	{
768 		output.writeln("<shiftExpression operator=\"",
769 				xmlAttributeEscape(str(shiftExpression.operator)), "\">");
770 		output.writeln("<left>");
771 		visit(shiftExpression.left);
772 		output.writeln("</left>");
773 		output.writeln("<right>");
774 		visit(shiftExpression.right);
775 		output.writeln("</right>");
776 		output.writeln("</shiftExpression>");
777 	}
778 
779 	override void visit(const SingleImport singleImport)
780 	{
781 		if (singleImport.rename.type == tok!"")
782 			output.writeln("<singleImport>");
783 		else
784 			output.writeln("<singleImport rename=\"", singleImport.rename.text, "\">");
785 		visit(singleImport.identifierChain);
786 		output.writeln("</singleImport>");
787 	}
788 
789 	override void visit(const StructDeclaration structDec)
790 	{
791 		output.writeln("<structDeclaration line=\"", structDec.name.line, "\">");
792 		writeName(structDec.name.text);
793 		writeDdoc(structDec.comment);
794 		structDec.accept(this);
795 		output.writeln("</structDeclaration>");
796 	}
797 
798 	override void visit(const TemplateAliasParameter templateAliasParameter)
799 	{
800 		output.writeln("<templateAliasParameter>");
801 		if (templateAliasParameter.type !is null)
802 			visit(templateAliasParameter.type);
803 		visit(templateAliasParameter.identifier);
804 		if (templateAliasParameter.colonExpression !is null)
805 		{
806 			output.writeln("<specialization>");
807 			visit(templateAliasParameter.colonExpression);
808 			output.writeln("</specialization>");
809 		}
810 		else if (templateAliasParameter.colonType !is null)
811 		{
812 			output.writeln("<specialization>");
813 			visit(templateAliasParameter.colonType);
814 			output.writeln("</specialization>");
815 		}
816 
817 		if (templateAliasParameter.assignExpression !is null)
818 		{
819 			output.writeln("<default>");
820 			visit(templateAliasParameter.assignExpression);
821 			output.writeln("</default>");
822 		}
823 		else if (templateAliasParameter.assignType !is null)
824 		{
825 			output.writeln("<default>");
826 			visit(templateAliasParameter.assignType);
827 			output.writeln("</default>");
828 		}
829 
830 		output.writeln("</templateAliasParameter>");
831 	}
832 
833 	override void visit(const TemplateDeclaration templateDeclaration)
834 	{
835 		writeDdoc(templateDeclaration.comment);
836 		output.writeln("<templateDeclaration line=\"", templateDeclaration.name.line, "\">");
837 		writeName(templateDeclaration.name.text);
838 		visit(templateDeclaration.templateParameters);
839 		if (templateDeclaration.constraint !is null)
840 			visit(templateDeclaration.constraint);
841 		foreach (dec; templateDeclaration.declarations)
842 		{
843 			if (dec !is null)
844 				visit(dec);
845 		}
846 		output.writeln("</templateDeclaration>");
847 	}
848 
849 	override void visit(const Token token)
850 	{
851 		string tagName;
852 		switch (token.type)
853 		{
854 		case tok!"":
855 			return;
856 		case tok!"identifier":
857 			tagName = "identifier";
858 			break;
859 		case tok!"doubleLiteral":
860 			tagName = "doubleLiteral";
861 			break;
862 		case tok!"idoubleLiteral":
863 			tagName = "idoubleLiteral";
864 			break;
865 		case tok!"floatLiteral":
866 			tagName = "floatLiteral";
867 			break;
868 		case tok!"ifloatLiteral":
869 			tagName = "ifloatLiteral";
870 			break;
871 		case tok!"intLiteral":
872 			tagName = "intLiteral";
873 			break;
874 		case tok!"uintLiteral":
875 			tagName = "uintLiteral";
876 			break;
877 		case tok!"longLiteral":
878 			tagName = "longLiteral";
879 			break;
880 		case tok!"ulongLiteral":
881 			tagName = "ulongLiteral";
882 			break;
883 		case tok!"realLiteral":
884 			tagName = "realLiteral";
885 			break;
886 		case tok!"irealLiteral":
887 			tagName = "irealLiteral";
888 			break;
889 		case tok!"characterLiteral":
890 			tagName = "characterLiteral";
891 			break;
892 		case tok!"stringLiteral":
893 			tagName = "stringLiteral";
894 			break;
895 		case tok!"dstringLiteral":
896 			tagName = "dstringLiteral";
897 			break;
898 		case tok!"wstringLiteral":
899 			tagName = "wstringLiteral";
900 			break;
901 		case tok!"scriptLine":
902 			tagName = "scriptLine";
903 			break;
904 		case tok!"$":
905 			output.writeln("<dollar/>");
906 			return;
907 		case tok!".":
908 			output.writeln("<dot/>");
909 			return;
910 		default:
911 			output.writeln("<", str(token.type), "/>");
912 			return;
913 		}
914 		output.writeln("<", tagName, ">", xmlEscape(token.text), "</", tagName, ">");
915 	}
916 
917 	override void visit(const Type type)
918 	{
919 		auto app = appender!string();
920 		auto formatter = new Formatter!(typeof(app))(app);
921 		formatter.format(type);
922 		output.writeln("<type pretty=\"", xmlAttributeEscape(app.data), "\">");
923 		type.accept(this);
924 		output.writeln("</type>");
925 	}
926 
927 	override void visit(const Type2 type2)
928 	{
929 		if (type2.builtinType != tok!"")
930 		{
931 			output.writeln("<type2>", str(type2.builtinType), "</type2>");
932 			if (type2.typeIdentifierPart !is null)
933 				visit(type2.typeIdentifierPart);
934 		}
935 		else
936 		{
937 			output.writeln("<type2>");
938 			type2.accept(this);
939 			output.writeln("</type2>");
940 		}
941 	}
942 
943 	override void visit(const TypeSuffix typeSuffix)
944 	{
945 		if (typeSuffix.star != tok!"")
946 			output.writeln("<typeSuffix type=\"*\"/>");
947 		else if (typeSuffix.array)
948 		{
949 			if (typeSuffix.low is null && typeSuffix.type is null)
950 				output.writeln("<typeSuffix type=\"[]\"/>");
951 			else
952 			{
953 				if (typeSuffix.low is null)
954 				{
955 					output.writeln("<typeSuffix type=\"[]\">");
956 					visit(typeSuffix.type);
957 					output.writeln("</typeSuffix>");
958 				}
959 				else
960 				{
961 					output.writeln("<typeSuffix type=\"[]\">");
962 					if (typeSuffix.high !is null)
963 					{
964 						output.writeln("<low>");
965 						visit(typeSuffix.low);
966 						output.writeln("</low>");
967 						output.writeln("<high>");
968 						visit(typeSuffix.high);
969 						output.writeln("</high>");
970 					}
971 					else
972 						visit(typeSuffix.low);
973 					output.writeln("</typeSuffix>");
974 				}
975 			}
976 		}
977 		else
978 		{
979 			visit(typeSuffix.delegateOrFunction);
980 			visit(typeSuffix.parameters);
981 			foreach (attr; typeSuffix.memberFunctionAttributes)
982 			{
983 				if (attr !is null)
984 					visit(attr);
985 			}
986 		}
987 	}
988 
989 	override void visit(const UnaryExpression unaryExpression)
990 	{
991 		output.writeln("<unaryExpression>");
992 		if (unaryExpression.prefix != tok!"")
993 		{
994 			output.writeln("<prefix>", xmlEscape(str(unaryExpression.prefix.type)), "</prefix>");
995 			unaryExpression.unaryExpression.accept(this);
996 		}
997 		else
998 		{
999 			if (unaryExpression.suffix != tok!"")
1000 			{
1001 				assert(unaryExpression.suffix.text == "");
1002 				unaryExpression.unaryExpression.accept(this);
1003 				output.writeln("<suffix>", str(unaryExpression.suffix.type), "</suffix>");
1004 			}
1005 			else
1006 				unaryExpression.accept(this);
1007 		}
1008 		output.writeln("</unaryExpression>");
1009 	}
1010 
1011 	override void visit(const UnionDeclaration unionDeclaration)
1012 	{
1013 		output.writeln("<unionDeclaration line=\"", unionDeclaration.name.line, "\">");
1014 		if (unionDeclaration.name != tok!"")
1015 			writeName(unionDeclaration.name.text);
1016 		if (unionDeclaration.templateParameters !is null)
1017 			visit(unionDeclaration.templateParameters);
1018 		if (unionDeclaration.constraint !is null)
1019 			visit(unionDeclaration.constraint);
1020 		if (unionDeclaration.structBody !is null)
1021 			visit(unionDeclaration.structBody);
1022 		output.writeln("</unionDeclaration>");
1023 	}
1024 
1025 	override void visit(const Unittest unittest_)
1026 	{
1027 		output.writeln("<unittest>");
1028 		unittest_.accept(this);
1029 		output.writeln("</unittest>");
1030 	}
1031 
1032 	override void visit(const VariableDeclaration variableDeclaration)
1033 	{
1034 		output.writeln("<variableDeclaration>");
1035 		writeDdoc(variableDeclaration.comment);
1036 		variableDeclaration.accept(this);
1037 		output.writeln("</variableDeclaration>");
1038 	}
1039 
1040 	override void visit(const XorExpression xorExpression)
1041 	{
1042 		output.writeln("<xorExpression>");
1043 		output.writeln("<left>");
1044 		visit(xorExpression.left);
1045 		output.writeln("</left>");
1046 		if (xorExpression.right !is null)
1047 		{
1048 			output.writeln("<right>");
1049 			visit(xorExpression.right);
1050 			output.writeln("</right>");
1051 		}
1052 		output.writeln("</xorExpression>");
1053 	}
1054 
1055 	override void visit(const Index index)
1056 	{
1057 		output.writeln("<index>");
1058 		if (index.high)
1059 		{
1060 			output.writeln("<low>");
1061 			visit(index.low);
1062 			output.writeln("</low>");
1063 
1064 			output.writeln("<high>");
1065 			visit(index.high);
1066 			output.writeln("</high>");
1067 		}
1068 		else
1069 			visit(index.low);
1070 		output.writeln("</index>");
1071 	}
1072 
1073 	// dfmt off
1074 	override void visit(const AliasInitializer aliasInitializer) { mixin (tagAndAccept!"aliasInitializer"); }
1075 	override void visit(const AliasThisDeclaration aliasThisDeclaration) { mixin (tagAndAccept!"aliasThisDeclaration"); }
1076 	override void visit(const AnonymousEnumDeclaration anonymousEnumDeclaration) { mixin (tagAndAccept!"anonymousEnumDeclaration"); }
1077 	override void visit(const ArgumentList argumentList) { mixin (tagAndAccept!"argumentList"); }
1078 	override void visit(const Arguments arguments) { mixin (tagAndAccept!"arguments"); }
1079 	override void visit(const ArrayInitializer arrayInitializer) { mixin (tagAndAccept!"arrayInitializer"); }
1080 	override void visit(const ArrayLiteral arrayLiteral) { mixin (tagAndAccept!"arrayLiteral"); }
1081 	override void visit(const ArrayMemberInitialization arrayMemberInitialization) { mixin (tagAndAccept!"arrayMemberInitialization"); }
1082 	override void visit(const AsmAddExp asmAddExp) { mixin (tagAndAccept!"asmAddExp"); }
1083 	override void visit(const AsmAndExp asmAndExp) { mixin (tagAndAccept!"asmAndExp"); }
1084 	override void visit(const AsmBrExp asmBrExp) { mixin (tagAndAccept!"asmBrExp"); }
1085 	override void visit(const AsmEqualExp asmEqualExp) { mixin (tagAndAccept!"asmEqualExp"); }
1086 	override void visit(const AsmExp asmExp) { mixin (tagAndAccept!"asmExp"); }
1087 	override void visit(const AsmLogAndExp asmLogAndExp) { mixin (tagAndAccept!"asmLogAndExp"); }
1088 	override void visit(const AsmLogOrExp asmLogOrExp) { mixin (tagAndAccept!"asmLogOrExp"); }
1089 	override void visit(const AsmMulExp asmMulExp) { mixin (tagAndAccept!"asmMulExp"); }
1090 	override void visit(const AsmOrExp asmOrExp) { mixin (tagAndAccept!"asmOrExp"); }
1091 	override void visit(const AsmPrimaryExp asmPrimaryExp) { mixin (tagAndAccept!"asmPrimaryExp"); }
1092 	override void visit(const AsmRelExp asmRelExp) { mixin (tagAndAccept!"asmRelExp"); }
1093 	override void visit(const AsmShiftExp asmShiftExp) { mixin (tagAndAccept!"asmShiftExp"); }
1094 	override void visit(const AsmStatement asmStatement) { mixin (tagAndAccept!"asmStatement"); }
1095 	override void visit(const AsmTypePrefix asmTypePrefix) { mixin (tagAndAccept!"asmTypePrefix"); }
1096 	override void visit(const AsmUnaExp asmUnaExp) { mixin (tagAndAccept!"asmUnaExp"); }
1097 	override void visit(const AsmXorExp asmXorExp) { mixin (tagAndAccept!"asmXorExp"); }
1098 	override void visit(const AssocArrayLiteral assocArrayLiteral) { mixin (tagAndAccept!"assocArrayLiteral"); }
1099 	override void visit(const AssertExpression assertExpression) { mixin (tagAndAccept!"assertExpression"); }
1100 	override void visit(const AssertArguments assertArguments) { mixin (tagAndAccept!"assertArguments"); }
1101 	override void visit(const AttributeDeclaration attributeDeclaration) { mixin (tagAndAccept!"attributeDeclaration"); }
1102 	override void visit(const BaseClass baseClass) { mixin (tagAndAccept!"baseClass"); }
1103 	override void visit(const BaseClassList baseClassList) { mixin (tagAndAccept!"baseClassList"); }
1104 	override void visit(const BlockStatement blockStatement) { mixin (tagAndAccept!"blockStatement"); }
1105 	override void visit(const CaseStatement caseStatement) { mixin (tagAndAccept!"caseStatement"); }
1106 	override void visit(const CastExpression castExpression) { mixin (tagAndAccept!"castExpression"); }
1107 	override void visit(const CastQualifier castQualifier) { mixin (tagAndAccept!"castQualifier"); }
1108 	override void visit(const Catches catches) { mixin (tagAndAccept!"catches"); }
1109 	override void visit(const CmpExpression cmpExpression) { mixin (tagAndAccept!"cmpExpression"); }
1110 	override void visit(const CompileCondition compileCondition) { mixin (tagAndAccept!"compileCondition"); }
1111 	override void visit(const Constraint constraint) { mixin (tagAndAccept!"constraint"); }
1112 	override void visit(const Constructor constructor) { mixin (tagAndAccept!"constructor"); }
1113 	override void visit(const Declaration declaration) { mixin (tagAndAccept!"declaration"); }
1114 	override void visit(const DeclarationOrStatement declarationOrStatement) { mixin (tagAndAccept!"declarationOrStatement"); }
1115 	override void visit(const DeclarationsAndStatements declarationsAndStatements) { mixin (tagAndAccept!"declarationsAndStatements"); }
1116 	override void visit(const DeclaratorIdentifierList declaratorIdentifierList) { mixin (tagAndAccept!"declaratorIdentifierList"); }
1117 	override void visit(const DefaultStatement defaultStatement) { mixin (tagAndAccept!"defaultStatement"); }
1118 	override void visit(const DeleteExpression deleteExpression) { mixin (tagAndAccept!"deleteExpression"); }
1119 	override void visit(const DeleteStatement deleteStatement) { mixin (tagAndAccept!"deleteStatement"); }
1120 	override void visit(const Destructor destructor) { mixin (tagAndAccept!"destructor"); }
1121 	override void visit(const DoStatement doStatement) { mixin (tagAndAccept!"doStatement"); }
1122 	override void visit(const EnumBody enumBody) { mixin (tagAndAccept!"enumBody"); }
1123 	override void visit(const EponymousTemplateDeclaration eponymousTemplateDeclaration) { mixin (tagAndAccept!"eponymousTemplateDeclaration"); }
1124 	override void visit(const Expression expression) { mixin (tagAndAccept!"expression"); }
1125 	override void visit(const ExpressionStatement expressionStatement) { mixin (tagAndAccept!"expressionStatement"); }
1126 	override void visit(const FinalSwitchStatement finalSwitchStatement) { mixin (tagAndAccept!"finalSwitchStatement"); }
1127 	override void visit(const ForeachTypeList foreachTypeList) { mixin (tagAndAccept!"foreachTypeList"); }
1128 	override void visit(const FunctionAttribute functionAttribute) { mixin (tagAndAccept!"functionAttribute"); }
1129 	override void visit(const FunctionBody functionBody) { mixin (tagAndAccept!"functionBody"); }
1130 	override void visit(const FunctionCallExpression functionCallExpression) { mixin (tagAndAccept!"functionCallExpression"); }
1131 	override void visit(const IdentifierChain identifierChain) { mixin (tagAndAccept!"identifierChain"); }
1132 	override void visit(const IdentifierOrTemplateChain identifierOrTemplateChain) { mixin (tagAndAccept!"identifierOrTemplateChain"); }
1133 	override void visit(const IdentifierOrTemplateInstance identifierOrTemplateInstance) { mixin (tagAndAccept!"identifierOrTemplateInstance"); }
1134 	override void visit(const ImportBindings importBindings) { mixin (tagAndAccept!"importBindings"); }
1135 	override void visit(const ImportDeclaration importDeclaration) { mixin (tagAndAccept!"importDeclaration"); }
1136 	override void visit(const ImportExpression importExpression) { mixin (tagAndAccept!"importExpression"); }
1137 	override void visit(const IndexExpression indexExpression) { mixin (tagAndAccept!"indexExpression"); }
1138 	override void visit(const InStatement inStatement) { mixin (tagAndAccept!"inStatement"); }
1139 	override void visit(const InContractExpression inContractExpression) { mixin (tagAndAccept!"inContractExpression"); }
1140 	override void visit(const InOutContractExpression inOutContractExpression) { mixin (tagAndAccept!"inOutContractExpression"); }
1141 	override void visit(const KeyValuePairs keyValuePairs) { mixin (tagAndAccept!"keyValuePairs"); }
1142 	override void visit(const MixinExpression mixinExpression) { mixin (tagAndAccept!"mixinExpression"); }
1143 	override void visit(const MixinTemplateDeclaration mixinTemplateDeclaration) { mixin (tagAndAccept!"mixinTemplateDeclaration"); }
1144 	override void visit(const MixinTemplateName mixinTemplateName) { mixin (tagAndAccept!"mixinTemplateName"); }
1145 	override void visit(const ModuleDeclaration moduleDeclaration) { mixin (tagAndAccept!"moduleDeclaration"); }
1146 	override void visit(const LastCatch lastCatch) { mixin (tagAndAccept!"lastCatch"); }
1147 	override void visit(const NewExpression newExpression) { mixin (tagAndAccept!"newExpression"); }
1148 	override void visit(const NonVoidInitializer nonVoidInitializer) { mixin (tagAndAccept!"nonVoidInitializer"); }
1149 	override void visit(const Operands operands) { mixin (tagAndAccept!"operands"); }
1150 	override void visit(const OrExpression orExpression) { mixin (tagAndAccept!"orExpression"); }
1151 	override void visit(const OutStatement outStatement) { mixin (tagAndAccept!"outStatement"); }
1152 	override void visit(const MixinDeclaration mixinDeclaration) { mixin (tagAndAccept!"mixinDeclaration"); }
1153 	override void visit(const OutContractExpression outContractExpression) { mixin (tagAndAccept!"outContractExpression"); }
1154 	override void visit(const Parameters parameters) { mixin (tagAndAccept!"parameters"); }
1155 	override void visit(const Postblit postblit) { mixin (tagAndAccept!"postblit"); }
1156 	override void visit(const NewAnonClassExpression newAnonClassExpression) { mixin (tagAndAccept!"newAnonClassExpression"); }
1157 	override void visit(const PragmaDeclaration pragmaDeclaration) { mixin (tagAndAccept!"pragmaDeclaration"); }
1158 	override void visit(const PragmaExpression pragmaExpression) { mixin (tagAndAccept!"pragmaExpression"); }
1159 	override void visit(const PrimaryExpression primaryExpression) { mixin (tagAndAccept!"primaryExpression"); }
1160 	override void visit(const Register register) { mixin (tagAndAccept!"register"); }
1161 	override void visit(const ScopeGuardStatement scopeGuardStatement) { mixin (tagAndAccept!"scopeGuardStatement"); }
1162 	override void visit(const SharedStaticConstructor sharedStaticConstructor) { mixin (tagAndAccept!"sharedStaticConstructor"); }
1163 	override void visit(const SharedStaticDestructor sharedStaticDestructor) { mixin (tagAndAccept!"sharedStaticDestructor"); }
1164 	override void visit(const ShortenedFunctionBody shortenedFunctionBody) { mixin (tagAndAccept!"shortenedFunctionBody"); }
1165 	override void visit(const StatementNoCaseNoDefault statementNoCaseNoDefault) { mixin (tagAndAccept!"statementNoCaseNoDefault"); }
1166 	override void visit(const StaticAssertDeclaration staticAssertDeclaration) { mixin (tagAndAccept!"staticAssertDeclaration"); }
1167 	override void visit(const StaticAssertStatement staticAssertStatement) { mixin (tagAndAccept!"staticAssertStatement"); }
1168 	override void visit(const StaticConstructor staticConstructor) { mixin (tagAndAccept!"staticConstructor"); }
1169 	override void visit(const StaticDestructor staticDestructor) { mixin (tagAndAccept!"staticDestructor"); }
1170 	override void visit(const StaticIfCondition staticIfCondition) { mixin (tagAndAccept!"staticIfCondition"); }
1171 	override void visit(const StorageClass storageClass) { mixin (tagAndAccept!"storageClass"); }
1172 	override void visit(const StructBody structBody) { mixin (tagAndAccept!"structBody"); }
1173 	override void visit(const StructInitializer structInitializer) { mixin (tagAndAccept!"structInitializer"); }
1174 	override void visit(const StructMemberInitializers structMemberInitializers) { mixin (tagAndAccept!"structMemberInitializers"); }
1175 	override void visit(const StructMemberInitializer structMemberInitializer) { mixin (tagAndAccept!"structMemberInitializer"); }
1176 	override void visit(const SwitchStatement switchStatement) { mixin (tagAndAccept!"switchStatement"); }
1177 	override void visit(const Symbol symbol) { mixin (tagAndAccept!"symbol"); }
1178 	override void visit(const SynchronizedStatement synchronizedStatement) { mixin (tagAndAccept!"synchronizedStatement"); }
1179 	override void visit(const Statement statement) { mixin (tagAndAccept!"statement"); }
1180 	override void visit(const TemplateArgumentList templateArgumentList) { mixin (tagAndAccept!"templateArgumentList"); }
1181 	override void visit(const TemplateArguments templateArguments) { mixin (tagAndAccept!"templateArguments"); }
1182 	override void visit(const TemplateArgument templateArgument) { mixin (tagAndAccept!"templateArgument"); }
1183 	override void visit(const TemplateMixinExpression templateMixinExpression) { mixin (tagAndAccept!"templateMixinExpression"); }
1184 	override void visit(const TemplateParameterList templateParameterList) { mixin (tagAndAccept!"templateParameterList"); }
1185 	override void visit(const TemplateParameters templateParameters) { mixin (tagAndAccept!"templateParameters"); }
1186 	override void visit(const TemplateParameter templateParameter) { mixin (tagAndAccept!"templateParameter"); }
1187 	override void visit(const TemplateSingleArgument templateSingleArgument) { mixin (tagAndAccept!"templateSingleArgument"); }
1188 	override void visit(const TemplateThisParameter templateThisParameter) { mixin (tagAndAccept!"templateThisParameter"); }
1189 	override void visit(const TemplateTupleParameter templateTupleParameter) { mixin (tagAndAccept!"templateTupleParameter"); }
1190 	override void visit(const TemplateTypeParameter templateTypeParameter) { mixin (tagAndAccept!"templateTypeParameter"); }
1191 	override void visit(const TemplateValueParameterDefault templateValueParameterDefault) { mixin (tagAndAccept!"templateValueParameterDefault"); }
1192 	override void visit(const TemplateValueParameter templateValueParameter) { mixin (tagAndAccept!"templateValueParameter"); }
1193 	override void visit(const TernaryExpression ternaryExpression) { mixin (tagAndAccept!"ternaryExpression"); }
1194 	override void visit(const TypeIdentifierPart typeIdentifierPart) { mixin (tagAndAccept!"typeIdentifierPart"); }
1195 	override void visit(const ThrowExpression throwExpression) { mixin (tagAndAccept!"throwExpression"); }
1196 	override void visit(const TryStatement tryStatement) { mixin (tagAndAccept!"tryStatement"); }
1197 	override void visit(const TemplateInstance templateInstance) { mixin (tagAndAccept!"templateInstance"); }
1198 	override void visit(const TypeofExpression typeofExpression) { mixin (tagAndAccept!"typeofExpression"); }
1199 	override void visit(const TypeSpecialization typeSpecialization) { mixin (tagAndAccept!"typeSpecialization"); }
1200 	override void visit(const TraitsExpression traitsExpression) { mixin (tagAndAccept!"traitsExpression"); }
1201 	override void visit(const Vector vector) { mixin (tagAndAccept!"vector"); }
1202 	override void visit(const VersionCondition versionCondition) { mixin (tagAndAccept!"versionCondition"); }
1203 	override void visit(const VersionSpecification versionSpecification) { mixin (tagAndAccept!"versionSpecification"); }
1204 	override void visit(const WhileStatement whileStatement) { mixin (tagAndAccept!"whileStatement"); }
1205 	override void visit(const WithStatement withStatement) { mixin (tagAndAccept!"withStatement"); }
1206 	override void visit(const TypeidExpression typeidExpression) { mixin (tagAndAccept!"typeidExpression"); }
1207 	// dfmt on
1208 
1209 	alias visit = ASTVisitor.visit;
1210 
1211 	private static string xmlEscape(string s)
1212 	{
1213 		return s.translate(['<' : "&lt;", '>' : "&gt;", '&' : "&amp;"]);
1214 	}
1215 
1216 	private static string xmlAttributeEscape(string s)
1217 	{
1218 		return s.translate(['<' : "&lt;", '>' : "&gt;", '&' : "&amp;", '\"'
1219 				: "&quot;", '\'' : "&apos;"]);
1220 	}
1221 
1222 	private void writeName(string name)
1223 	{
1224 		output.writeln("<name>", name, "</name>");
1225 	}
1226 
1227 	private void writeDdoc(string comment)
1228 	{
1229 		if (comment.ptr is null)
1230 			return;
1231 		output.writeln("<ddoc>", xmlEscape(comment), "</ddoc>");
1232 	}
1233 
1234 	/**
1235 	* File that output  is written to.
1236 	*/
1237 	File output;
1238 }
1239 
1240 private:
1241 
1242 template tagAndAccept(string tagName)
1243 {
1244 	immutable tagAndAccept = `output.writeln("<` ~ tagName ~ `>");` ~ tagName
1245 		~ `.accept(this);` ~ `output.writeln("</` ~ tagName ~ `>");`;
1246 }