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 private:
92 
93 	void setUnittest(const Unittest test)
94 	{
95 //		import std.stdio;
96 		if (test.comment is null)
97 			return;
98 		if (prevNodeStack.length == 0)
99 			return;
100 		if (prevNodeStack[$ - 1] == 0)
101 			return;
102 //		writeln("Mapping unittest at ", test.blockStatement.startLocation,
103 //			" to declaration at ", prevNodeStack[$ - 1]);
104 		mapping[prevNodeStack[$ - 1]] ~= TestRange(
105 			test.blockStatement.startLocation,
106 			test.blockStatement.endLocation,
107 			test.comment);
108 	}
109 
110 	void pushScope()
111 	{
112 		prevNodeStack.length = prevNodeStack.length + 1;
113 		prevNodeStack[$ - 1] = 0;
114 	}
115 
116 	void popScope()
117 	{
118 		prevNodeStack = prevNodeStack[0 .. $ - 1];
119 	}
120 
121 	void setPrevNode(T)(const T node)
122 	{
123 		prevNodeStack[$ - 1] = cast(size_t) (cast(void*) node);
124 	}
125 
126 	size_t[] prevNodeStack;
127 	TestRange[][size_t] mapping;
128  }