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