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  }