1 /** 2 * D Documentation Generator 3 * Copyright: © 2014 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt Boost License 1.0) 6 */ 7 8 module unittest_preprocessor; 9 10 import std.typecons; 11 import dparse.ast; 12 import dparse.lexer; 13 14 /** 15 * $(UL $(LI First field: the byte index of the opening brace of the unittest) 16 * $(LI Second field: the byte index of the closing brace of the unittest) 17 * $(LI Third field: the comment attached to the unittest)) 18 */ 19 alias TestRange = Tuple!(size_t, size_t, string); 20 21 /** 22 * Params: 23 * m = the module 24 * Returns: A mapping of declaration addresses to an array of documentation 25 * unittest blocks for that declaration 26 */ 27 TestRange[][size_t] getUnittestMap(const Module m) 28 { 29 UnittestVisitor visitor = new UnittestVisitor; 30 visitor.visit(m); 31 return visitor.mapping; 32 } 33 34 private: 35 36 class UnittestVisitor : ASTVisitor 37 { 38 alias visit = ASTVisitor.visit; 39 40 override void visit(const ModuleDeclaration modDec) 41 { 42 setPrevNode(modDec); 43 } 44 45 override void visit(const Unittest uTest) 46 { 47 setUnittest(uTest); 48 } 49 50 override void visit(const FunctionDeclaration fd) 51 { 52 setPrevNode(fd); 53 } 54 55 override void visit(const TemplateDeclaration td) 56 { 57 setPrevNode(td); 58 pushScope(); 59 td.accept(this); 60 popScope(); 61 } 62 63 mixin template VisitScope(T) 64 { 65 override void visit(const T s) 66 { 67 pushScope(); 68 s.accept(this); 69 popScope(); 70 } 71 } 72 73 mixin VisitScope!Module; 74 mixin VisitScope!BlockStatement; 75 mixin VisitScope!StructBody; 76 77 mixin template VisitAggregate(T) 78 { 79 override void visit(const T d) 80 { 81 setPrevNode(d); 82 d.accept(this); 83 } 84 } 85 86 mixin VisitAggregate!ClassDeclaration; 87 mixin VisitAggregate!InterfaceDeclaration; 88 mixin VisitAggregate!StructDeclaration; 89 mixin VisitAggregate!UnionDeclaration; 90 91 // Optimization: don't allow visit() for these AST nodes to result in visit() 92 // calls for their subnodes. This avoids most of the dynamic cast overhead. 93 override void visit(const AssignExpression assignExpression) {} 94 override void visit(const CmpExpression cmpExpression) {} 95 override void visit(const TernaryExpression ternaryExpression) {} 96 override void visit(const IdentityExpression identityExpression) {} 97 override void visit(const InExpression inExpression) {} 98 99 private: 100 101 void setUnittest(const Unittest test) 102 { 103 // import std.stdio; 104 if (test.comment is null) 105 return; 106 if (prevNodeStack.length == 0) 107 return; 108 if (prevNodeStack[$ - 1] == 0) 109 return; 110 // writeln("Mapping unittest at ", test.blockStatement.startLocation, 111 // " to declaration at ", prevNodeStack[$ - 1]); 112 mapping[prevNodeStack[$ - 1]] ~= TestRange( 113 test.blockStatement.startLocation, 114 test.blockStatement.endLocation, 115 test.comment); 116 } 117 118 void pushScope() 119 { 120 prevNodeStack.length = prevNodeStack.length + 1; 121 prevNodeStack[$ - 1] = 0; 122 } 123 124 void popScope() 125 { 126 prevNodeStack = prevNodeStack[0 .. $ - 1]; 127 } 128 129 void setPrevNode(T)(const T node) 130 { 131 prevNodeStack[$ - 1] = cast(size_t) (cast(void*) node); 132 } 133 134 size_t[] prevNodeStack; 135 TestRange[][size_t] mapping; 136 }