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  }