Mercurial > projects > dil
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 } |