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