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