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