comparison trunk/src/cmd/DDoc.d @ 737:f88b5285b86b

Implemented DDocEmitter. Fixed quite a few bugs.
author Aziz K?ksal <aziz.koeksal@gmail.com>
date Sat, 09 Feb 2008 02:00:20 +0100
parents ca7607226caa
children 49fe21aa387c
comparison
equal deleted inserted replaced
736:2eee29aaa357 737:f88b5285b86b
5 module cmd.DDoc; 5 module cmd.DDoc;
6 6
7 import dil.doc.Parser; 7 import dil.doc.Parser;
8 import dil.doc.Macro; 8 import dil.doc.Macro;
9 import dil.doc.Doc; 9 import dil.doc.Doc;
10 import dil.ast.Node;
11 import dil.ast.Declarations,
12 dil.ast.Statements,
13 dil.ast.Expression,
14 dil.ast.Parameters,
15 dil.ast.Types;
10 import dil.ast.DefaultVisitor; 16 import dil.ast.DefaultVisitor;
17 import dil.lexer.Token;
11 import dil.semantic.Module; 18 import dil.semantic.Module;
12 import dil.semantic.Pass1; 19 import dil.semantic.Pass1;
13 import dil.semantic.Symbol; 20 import dil.semantic.Symbol;
14 import dil.semantic.Symbols; 21 import dil.semantic.Symbols;
15 import dil.Information; 22 import dil.Information;
16 import dil.File; 23 import dil.File;
17 import common; 24 import common;
18 25
19 import tango.stdc.time : time_t, time, ctime; 26 import tango.stdc.time : time_t, time, ctime;
20 import tango.stdc.string : strlen; 27 import tango.stdc.string : strlen;
28 import tango.text.Ascii : toUpper;
29 import tango.io.File;
30 import tango.io.FilePath;
21 31
22 void execute(string[] filePaths, string destDir, string[] macroPaths, 32 void execute(string[] filePaths, string destDir, string[] macroPaths,
23 bool incUndoc, InfoManager infoMan) 33 bool incUndoc, InfoManager infoMan)
24 { 34 {
25 // Parse macro files. 35 // Parse macro files.
33 } 43 }
34 44
35 // foreach (k, v; mtable.table) 45 // foreach (k, v; mtable.table)
36 // Stdout(k)("=")(v.text); 46 // Stdout(k)("=")(v.text);
37 47
38 Module[] modules;
39 foreach (filePath; filePaths) 48 foreach (filePath; filePaths)
40 { 49 {
41 auto mod = new Module(filePath, infoMan); 50 auto mod = new Module(filePath, infoMan);
42 modules ~= mod;
43 // Parse the file. 51 // Parse the file.
44 mod.parse(); 52 mod.parse();
45 if (mod.hasErrors) 53 if (mod.hasErrors)
46 continue; 54 continue;
47 55
48 // Start semantic analysis. 56 // Start semantic analysis.
49 auto pass1 = new SemanticPass1(mod); 57 auto pass1 = new SemanticPass1(mod);
50 pass1.start(); 58 pass1.start();
51 } 59
52 60 // Generate documentation.
53 foreach (mod; modules) 61 auto dest = new FilePath(destDir);
54 generateDocumentation(mod, mtable); 62 generateDocumentation(dest, mod, mtable, incUndoc);
63 }
55 } 64 }
56 65
57 void generateDocumentation(Module mod, MacroTable mtable) 66 void generateDocumentation(FilePath dest, Module mod, MacroTable mtable, bool incUndoc)
58 { 67 {
59 // Create a macro environment for this module. 68 // Create a macro environment for this module.
60 mtable = new MacroTable(mtable); 69 mtable = new MacroTable(mtable);
61 // Define runtime macros. 70 // Define runtime macros.
62 mtable.insert(new Macro("TITLE", mod.getFQN())); 71 mtable.insert("TITLE", mod.getFQN());
63 mtable.insert(new Macro("DOCFILENAME", mod.getFQN())); 72 mtable.insert("DOCFILENAME", mod.getFQN());
64 73
65 time_t time_val; 74 time_t time_val;
66 time(&time_val); 75 time(&time_val);
67 char* str = ctime(&time_val); 76 char* str = ctime(&time_val);
68 char[] time_str = str[0 .. strlen(str)]; 77 char[] time_str = str[0 .. strlen(str)];
69 mtable.insert(new Macro("DATETIME", time_str.dup)); 78 mtable.insert("DATETIME", time_str.dup);
70 mtable.insert(new Macro("YEAR", time_str[20..24].dup)); 79 mtable.insert("YEAR", time_str[20..24].dup);
71 80
72 if (mod.moduleDecl) 81 auto doc = new DDocEmitter(mtable, incUndoc);
73 { 82 doc.emit(mod);
74 auto ddocComment = getDDocComment(mod.moduleDecl); 83 // Set BODY macro to the text produced by the DDocEmitter.
75 if (auto copyright = ddocComment.getCopyright()) 84 mtable.insert("BODY", doc.text);
76 mtable.insert(new Macro("COPYRIGHT", copyright.text)); 85 // Do the macro expansion pass.
77 } 86 auto fileText = expandMacros(mtable, "$(DDOC)");
78 87 // Finally write the file out to the harddisk.
79 auto docEmitter = new DDocEmitter(); 88 dest.append(mod.getFQN() ~ ".html");
80 docEmitter.emit(mod); 89 auto file = new File(dest);
81 90 file.write(fileText);
82 mtable.insert(new Macro("BODY", docEmitter.text));
83 expandMacros(mtable, "$(DDOC)");
84 } 91 }
85 92
93 /// Traverses the syntax tree and writes DDoc macros to a string buffer.
86 class DDocEmitter : DefaultVisitor 94 class DDocEmitter : DefaultVisitor
87 { 95 {
88 char[] text; 96 char[] text;
89 97 bool includeUndocumented;
98 MacroTable mtable;
99
100 this(MacroTable mtable, bool includeUndocumented)
101 {
102 this.mtable = mtable;
103 this.includeUndocumented = includeUndocumented;
104 }
105
106 /// Entry method.
90 char[] emit(Module mod) 107 char[] emit(Module mod)
91 { 108 {
109 if (auto d = mod.moduleDecl)
110 {
111 if (ddoc(d))
112 {
113 if (auto copyright = cmnt.takeCopyright())
114 mtable.insert(new Macro("COPYRIGHT", copyright.text));
115 DESC({ writeComment(); });
116 }
117 }
118 MEMBERS("MODULE", { visitD(mod.root); });
119 write(\n);
92 return text; 120 return text;
93 } 121 }
122
123 char[] textSpan(Token* left, Token* right)
124 {
125 //assert(left && right && (left.end <= right.start || left is right));
126 //char[] result;
127 //TODO: filter out whitespace tokens.
128 return Token.textSpan(left, right);
129 }
130
131 bool isTemplatized; /// True if an aggregate declaration is templatized.
132 TemplateParameters tparams; /// The template parameters of the declaration.
133
134 DDocComment cmnt; /// Current comment.
135 DDocComment prevCmnt; /// Previous comment in scope.
136 /// An empty comment. Used for undocumented symbols.
137 static const DDocComment emptyCmnt;
138
139 static this()
140 {
141 this.emptyCmnt = new DDocComment(null, null, null);
142 }
143
144 /// Keeps track of previous comments in each scope.
145 scope class Scope
146 {
147 DDocComment old_prevCmnt;
148 this()
149 { // Save the previous comment of the parent scope.
150 old_prevCmnt = this.outer.prevCmnt;
151 // Entering a new scope. Set to null.
152 this.outer.prevCmnt = null;
153 }
154
155 ~this()
156 { // Restore the previous comment of the parent scope.
157 this.outer.prevCmnt = old_prevCmnt;
158 }
159 }
160
161 DDocComment ddoc(Node node)
162 {
163 auto c = getDDocComment(node);
164 this.cmnt = null;
165 if (c)
166 {
167 if (c.isDitto)
168 this.cmnt = this.prevCmnt;
169 else
170 {
171 this.cmnt = c;
172 this.prevCmnt = c;
173 }
174 }
175 else if (includeUndocumented)
176 this.cmnt = this.emptyCmnt;
177 return this.cmnt;
178 }
179
180 static char[][char[]] specialSections;
181 static this()
182 {
183 foreach (name; ["AUTHORS", "BUGS", "COPYRIGHT", "DATE", "DEPRECATED",
184 "EXAMPLES", "HISTORY", "LICENSE", "RETURNS", "SEE_ALSO",
185 "STANDARDS", "THROWS", "VERSION"])
186 specialSections[name] = name;
187 }
188
189 void writeComment()
190 {
191 auto c = this.cmnt;
192 assert(c !is null);
193 if (c.sections.length == 0)
194 return;
195 write("$(DDOC_SECTIONS ");
196 foreach (s; c.sections)
197 {
198 if (s is c.summary)
199 write("\n$(DDOC_SUMMARY ");
200 else if (s is c.description)
201 write("\n$(DDOC_DESCRIPTION ");
202 else if (auto name = toUpper(s.name.dup) in specialSections)
203 write("\n$(DDOC_" ~ *name ~ " ");
204 else if (s.Is("params"))
205 { // Process parameters section.
206 auto ps = new ParamsSection(s.name, s.text);
207 write("\n$(DDOC_PARAMS ");
208 foreach (i, paramName; ps.paramNames)
209 write("$(DDOC_PARAM_ROW ",
210 "$(DDOC_PARAM_ID ", paramName, ")",
211 "$(DDOC_PARAM_DESC ", ps.paramDescs[i], ")",
212 ")");
213 write(")");
214 continue;
215 }
216 else if (s.Is("macros"))
217 { // Declare the macros in this section.
218 auto ms = new MacrosSection(s.name, s.text);
219 mtable.insert(ms.macroNames, ms.macroTexts);
220 continue;
221 }
222 else
223 write("\n$(DDOC_SECTION $(DDOC_SECTION_H " ~ s.name ~ ":)");
224 write(s.text, ")");
225 }
226 write(")");
227 }
228
229 void writeTemplateParams()
230 {
231 if (!isTemplatized)
232 return;
233 text ~= "(" ~ (tparams ? textSpan(tparams.begin, tparams.end) : "") ~ ")";
234 isTemplatized = false;
235 tparams = null;
236 }
237
238 void writeInheritanceList(BaseClassType[] bases)
239 {
240 if (bases.length == 0)
241 return;
242 text ~= " : " ~ textSpan(bases[0].begin, bases[$-1].end);
243 }
244
245 void writeFuncHeader(Declaration d, FuncBodyStatement s)
246 {
247 auto begin = d.begin;
248 auto end = d.end.prev;
249 if (!s.isEmpty)
250 end = s.begin.prev;
251 text ~= textSpan(begin, end);
252 }
253
254 void write(char[][] strings...)
255 {
256 foreach (s; strings)
257 text ~= s;
258 }
259
260 void SYMBOL(char[][] strings...)
261 {
262 write("$(DDOC_PSYMBOL ");
263 write(strings);
264 write(")");
265 }
266
267 void DECL(void delegate() dg)
268 {
269 write("\n$(DDOC_DECL ");
270 dg();
271 write(")");
272 }
273
274 void DESC(void delegate() dg)
275 {
276 write("\n$(DDOC_DECL_DD ");
277 dg();
278 write(")");
279 }
280
281 void MEMBERS(char[] kind, void delegate() dg)
282 {
283 write("\n$(DDOC_"~kind~"_MEMBERS ");
284 dg();
285 write(")");
286 }
287
288 void writeClassOrInterface(T)(T d)
289 {
290 if (!ddoc(d))
291 return d;
292 scope s = new Scope();
293 DECL({
294 write(d.begin.srcText, " ");
295 SYMBOL(d.name.str);
296 writeTemplateParams();
297 writeInheritanceList(d.bases);
298 });
299 DESC({
300 writeComment();
301 MEMBERS(is(T == ClassDeclaration) ? "CLASS" : "INTERFACE", {
302 d.decls && super.visit(d.decls);
303 });
304 });
305 }
306
307 void writeStructOrUnion(T)(T d)
308 {
309 if (!ddoc(d))
310 return d;
311 scope s = new Scope();
312 DECL({
313 write(d.begin.srcText, d.name ? " " : "");
314 if (d.name)
315 SYMBOL(d.name.str);
316 writeTemplateParams();
317 });
318 DESC({
319 writeComment();
320 MEMBERS(is(T == StructDeclaration) ? "STRUCT" : "UNION", {
321 d.decls && super.visit(d.decls);
322 });
323 });
324 }
325
326 alias Declaration D;
327
328 override:
329 // D visit(ModuleDeclaration d)
330 // { return d; }
331
332 D visit(AliasDeclaration d)
333 {
334 if (!ddoc(d))
335 return d;
336 DECL({ write(textSpan(d.begin, d.end)); });
337 DESC({ writeComment(); });
338 return d;
339 }
340
341 D visit(TypedefDeclaration d)
342 {
343 if (!ddoc(d))
344 return d;
345 DECL({ write(textSpan(d.begin, d.end)); });
346 DESC({ writeComment(); });
347 return d;
348 }
349
350 D visit(EnumDeclaration d)
351 {
352 if (!ddoc(d))
353 return d;
354 scope s = new Scope();
355 DECL({
356 write("enum", d.name ? " " : "");
357 d.name && SYMBOL(d.name.str);
358 });
359 DESC({
360 writeComment();
361 MEMBERS("ENUM", { super.visit(d); });
362 });
363 return d;
364 }
365
366 D visit(EnumMemberDeclaration d)
367 {
368 if (!ddoc(d))
369 return d;
370 DECL({ SYMBOL(d.name.str); });
371 DESC({ writeComment(); });
372 return d;
373 }
374
375 D visit(TemplateDeclaration d)
376 {
377 if (d.begin.kind != TOK.Template)
378 { // This is a templatized class/interface/struct/union.
379 this.isTemplatized = true;
380 this.tparams = d.tparams;
381 return super.visit(d.decls);
382 }
383 if (!ddoc(d))
384 return d;
385 scope s = new Scope();
386 DECL({
387 write("template ");
388 SYMBOL(d.name.str);
389 write(textSpan(d.begin.next.next, d.decls.begin.prev));
390 });
391 DESC({
392 writeComment();
393 MEMBERS("TEMPLATE", {
394 super.visit(d.decls);
395 });
396 });
397 return d;
398 }
399
400 D visit(ClassDeclaration d)
401 {
402 writeClassOrInterface(d);
403 return d;
404 }
405
406 D visit(InterfaceDeclaration d)
407 {
408 writeClassOrInterface(d);
409 return d;
410 }
411
412 D visit(StructDeclaration d)
413 {
414 writeStructOrUnion(d);
415 return d;
416 }
417
418 D visit(UnionDeclaration d)
419 {
420 writeStructOrUnion(d);
421 return d;
422 }
423
424 D visit(ConstructorDeclaration d)
425 {
426 if (!ddoc(d))
427 return d;
428 DECL({ writeFuncHeader(d, d.funcBody); });
429 DESC({ writeComment(); });
430 return d;
431 }
432
433 D visit(StaticConstructorDeclaration d)
434 {
435 if (!ddoc(d))
436 return d;
437 DECL({ writeFuncHeader(d, d.funcBody); });
438 DESC({ writeComment(); });
439 return d;
440 }
441
442 D visit(DestructorDeclaration d)
443 {
444 if (!ddoc(d))
445 return d;
446 DECL({ writeFuncHeader(d, d.funcBody); });
447 DESC({ writeComment(); });
448 return d;
449 }
450
451 D visit(StaticDestructorDeclaration d)
452 {
453 if (!ddoc(d))
454 return d;
455 DECL({ writeFuncHeader(d, d.funcBody); });
456 DESC({ writeComment(); });
457 return d;
458 }
459
460 D visit(FunctionDeclaration d)
461 {
462 if (!ddoc(d))
463 return d;
464 DECL({ writeFuncHeader(d, d.funcBody); });
465 DESC({ writeComment(); });
466 return d;
467 }
468
469 D visit(NewDeclaration d)
470 {
471 if (!ddoc(d))
472 return d;
473 DECL({ writeFuncHeader(d, d.funcBody); });
474 DESC({ writeComment(); });
475 return d;
476 }
477
478 D visit(DeleteDeclaration d)
479 {
480 if (!ddoc(d))
481 return d;
482 DECL({ writeFuncHeader(d, d.funcBody); });
483 DESC({ writeComment(); });
484 return d;
485 }
486
487 D visit(VariablesDeclaration d)
488 {
489 if (!ddoc(d))
490 return d;
491 char[] type = "auto";
492 if (d.typeNode)
493 type = textSpan(d.typeNode.baseType.begin, d.typeNode.end);
494 foreach (name; d.names)
495 {
496 DECL({ write(type, " "); SYMBOL(name.str); });
497 DESC({ writeComment(); });
498 }
499 return d;
500 }
501
502 D visit(InvariantDeclaration d)
503 {
504 if (!ddoc(d))
505 return d;
506 DECL({ write("invariant"); });
507 DESC({ writeComment(); });
508 return d;
509 }
510
511 D visit(UnittestDeclaration d)
512 {
513 if (!ddoc(d))
514 return d;
515 DECL({ write("unittest"); });
516 DESC({ writeComment(); });
517 return d;
518 }
519
520 D visit(DebugDeclaration d)
521 { return d; }
522
523 D visit(VersionDeclaration d)
524 { return d; }
94 } 525 }