]> git.saurik.com Git - wxWidgets.git/blob - utils/HelpGen/src/HelpGen.cpp
d4bb7670507f9f599f9899b1c5d00097d48a7bcf
[wxWidgets.git] / utils / HelpGen / src / HelpGen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: HelpGen.cpp
3 // Purpose: Main program file for HelpGen
4 // Author: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
5 // Modified by:
6 // Created: 06/01/99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 VZ
9 // Licence: GPL
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13 TODO (+ means fixed)
14
15 (i) small fixes in the current version
16
17 +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
18 2. Document typedefs
19 3. Document global variables
20 4. Document #defines
21 +5. Program options
22
23 (ii) plans for version 2
24 1. Use wxTextFile for direct file access to avoid one scan method problems
25 2. Use command line parsrer class for the options
26
27 */
28
29 // =============================================================================
30 // declarations
31 // =============================================================================
32
33 // -----------------------------------------------------------------------------
34 // headers
35 // -----------------------------------------------------------------------------
36
37 // wxWindows
38 #include "wx/wxprec.h"
39
40 #ifndef WX_PRECOMP
41 #include <wx/string.h>
42 #include <wx/log.h>
43 #include <wx/file.h>
44 #include <wx/dynarray.h>
45 #endif // WX_PRECOMP
46
47 // C++ parsing classes
48 #include "cjparser.h"
49
50 // standard headers
51 #include <stdio.h>
52 #include <time.h>
53
54 // -----------------------------------------------------------------------------
55 // private functions
56 // -----------------------------------------------------------------------------
57
58 // return the label for the given function name (i.e. argument of \label)
59 static wxString MakeLabel(const char *classname, const char *funcname = NULL);
60
61 // return the whole \helpref{arg}{arg_label} string
62 static wxString MakeHelpref(const char *argument);
63
64 // quotes special TeX characters in place
65 static void TeXFilter(wxString* str);
66
67 // get all comments associated with this context
68 static wxString GetAllComments(const spContext& ctx);
69
70 // get the string with current time (returns pointer to static buffer)
71 // timeFormat is used for the call of strftime(3)
72 static const char *GetCurrentTime(const char *timeFormat);
73
74 // -----------------------------------------------------------------------------
75 // private classes
76 // -----------------------------------------------------------------------------
77
78 // add a function which sanitazes the string before writing it to the file
79 class wxTeXFile : public wxFile
80 {
81 public:
82 wxTeXFile() : wxFile() { }
83
84 bool WriteTeX(const wxString& s)
85 {
86 wxString t(s);
87 TeXFilter(&t);
88
89 return wxFile::Write(t);
90 }
91 };
92
93 class HelpGenVisitor : public spVisitor
94 {
95 public:
96 // ctor
97 HelpGenVisitor();
98
99 virtual void VisitFile( spFile& fl );
100 virtual void VisitClass( spClass& cl );
101 virtual void VisitEnumeration( spEnumeration& en );
102 virtual void VisitTypeDef( spTypeDef& td );
103 virtual void VisitPreprocessorLine( spPreprocessorLine& pd );
104 virtual void VisitAttribute( spAttribute& attr );
105 virtual void VisitOperation( spOperation& op );
106 virtual void VisitParameter( spParameter& param );
107
108 void EndVisit();
109
110 // shut up g++ warning (ain't it stupid?)
111 virtual ~HelpGenVisitor() { }
112
113 protected:
114 // (re)initialize the state
115 void Reset();
116
117 // insert documentation for enums/typedefs coming immediately before the
118 // class declaration into the class documentation
119 void InsertTypedefDocs();
120 void InsertEnumDocs();
121
122 // write the headers for corresponding sections (only once)
123 void InsertDataStructuresHeader();
124 void InsertMethodsHeader();
125
126 // terminate the function documentation if it was started
127 void CloseFunction();
128
129 wxTeXFile m_file; // file we're writing to now
130
131 // state variables
132 bool m_inClass, // TRUE after file successfully opened
133 m_inTypesSection, // enums & typedefs go there
134 m_inMethodSection, // functions go here
135 m_isFirstParam, // first parameter of current function?
136 m_inFunction; // we're parsing a function declaration
137
138 // holders for "saved" documentation
139 wxString m_textStoredEnums,
140 m_textStoredTypedefs,
141 m_textStoredFunctionComment;
142
143 // headers included by this file
144 wxArrayString m_headers;
145 };
146
147 // -----------------------------------------------------------------------------
148 // private functions
149 // -----------------------------------------------------------------------------
150
151 // =============================================================================
152 // implementation
153 // =============================================================================
154
155 // this function never returns
156 static void usage()
157 {
158 wxLogError("usage: HelpGen [-q|-v] <header files...>\n");
159
160 exit(1);
161 }
162
163 int main(int argc, char **argv)
164 {
165 if ( argc < 2 ) {
166 usage();
167 }
168
169 int first;
170 for ( first = 1; (first < argc) && argv[first][0] == '-'; first++ ) {
171 switch ( argv[first][1] ) {
172 case 'v':
173 // be verbose
174 wxLog::GetActiveTarget()->SetVerbose();
175 break;
176
177 case 'q':
178 // be quiet
179 wxLog::GetActiveTarget()->SetVerbose(false);
180 break;
181
182 default:
183 usage();
184 }
185 }
186
187 // create a parser object and a visitor derivation
188 CJSourceParser parser;
189 HelpGenVisitor visitor;
190
191 // parse all files
192 for ( int i = first; i < argc; i++ ) {
193 spContext *ctxTop = parser.ParseFile(argv[i]);
194 if ( !ctxTop ) {
195 wxLogWarning("File '%s' couldn't be processed.", argv[i]);
196 }
197 else {
198 ((spFile *)ctxTop)->mFileName = argv[i];
199 visitor.VisitAll(*ctxTop);
200 visitor.EndVisit();
201 }
202 }
203
204 return 0;
205 }
206
207 // -----------------------------------------------------------------------------
208 // HelpGenVisitor implementation
209 // -----------------------------------------------------------------------------
210
211 HelpGenVisitor::HelpGenVisitor()
212 {
213 Reset();
214 }
215
216 void HelpGenVisitor::Reset()
217 {
218 m_inClass =
219 m_inFunction =
220 m_inTypesSection =
221 m_inMethodSection = false;
222
223 m_textStoredTypedefs =
224 m_textStoredEnums =
225 m_textStoredFunctionComment = "";
226 m_headers.Empty();
227 }
228
229 void HelpGenVisitor::InsertTypedefDocs()
230 {
231 m_file.WriteTeX(m_textStoredTypedefs);
232 m_textStoredTypedefs.Empty();
233 }
234
235 void HelpGenVisitor::InsertEnumDocs()
236 {
237 m_file.WriteTeX(m_textStoredEnums);
238 m_textStoredEnums.Empty();
239 }
240
241 void HelpGenVisitor::InsertDataStructuresHeader()
242 {
243 if ( !m_inTypesSection ) {
244 m_inTypesSection = true;
245
246 m_file.WriteTeX("\\wxheading{Data structures}\n\n");
247 }
248 }
249
250 void HelpGenVisitor::InsertMethodsHeader()
251 {
252 if ( !m_inMethodSection ) {
253 m_inMethodSection = true;
254
255 m_file.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
256 }
257 }
258
259 void HelpGenVisitor::CloseFunction()
260 {
261 if ( m_inFunction ) {
262 m_inFunction = false;
263
264 wxString totalText;
265 if ( m_isFirstParam ) {
266 // no params found
267 totalText << "\\void";
268 }
269
270 totalText << "}\n\n";
271
272 if ( !m_textStoredFunctionComment.IsEmpty() )
273 totalText << m_textStoredFunctionComment << '\n';
274
275 m_file.WriteTeX(totalText);
276 }
277 }
278
279 void HelpGenVisitor::EndVisit()
280 {
281 CloseFunction();
282
283 wxLogInfo("%s: finished parsing the current file.",
284 GetCurrentTime("%H:%M:%S"));
285 }
286
287 void HelpGenVisitor::VisitFile( spFile& file )
288 {
289 wxLogInfo("%s: started to parse classes from file '%s'...",
290 GetCurrentTime("%H:%M:%S"), file.mFileName.c_str());
291 }
292
293 void HelpGenVisitor::VisitClass( spClass& cl )
294 {
295 wxString name = cl.GetName();
296
297 // the file name is built from the class name by removing the leading "wx"
298 // if any and converting it to the lower case
299 wxString filename = name;
300 if ( filename(0, 2) == "wx" ) {
301 filename.erase(0, 2);
302 }
303
304 filename.MakeLower();
305 filename += ".tex";
306
307 m_inClass = m_file.Open(filename, wxFile::write);
308 if ( !m_inClass ) {
309 wxLogError("Can't generate documentation for the class '%s'.",
310 name.c_str());
311
312 return;
313 }
314
315 m_inMethodSection =
316 m_inTypesSection = false;
317
318 wxLogInfo("Created new file '%s' for class '%s'.",
319 filename.c_str(), name.c_str());
320
321 // the entire text we're writing to file
322 wxString totalText;
323
324 // write out the header
325 {
326 wxString header;
327 header.Printf("% automatically generated by HelpGen from %s at %s\n"
328 "\\section{\\class{%s}}\\label{%s}\n",
329 filename.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
330 name.c_str(), wxString(name).MakeLower().c_str());
331
332 totalText << header << '\n';
333 }
334
335 // if the header includes other headers they must be related to it... try to
336 // automatically generate the "See also" clause
337 if ( !m_headers.IsEmpty() ) {
338 // correspondence between wxWindows headers and class names
339 static const char *headers[] = {
340 "object",
341 "defs",
342 "string",
343 "dynarray",
344 "file",
345 "time",
346 };
347
348 // NULL here means not to insert anything in "See also" for the
349 // corresponding header
350 static const char *classes[] = {
351 NULL,
352 NULL,
353 NULL,
354 NULL,
355 "wxFile",
356 "wxTime",
357 };
358
359 wxASSERT_MSG( WXSIZEOF(headers) == WXSIZEOF(classes),
360 "arrays must be in sync!" );
361
362 wxArrayInt interestingClasses;
363
364 size_t count = m_headers.Count(), index;
365 for ( size_t n = 0; n < count; n++ ) {
366 wxString baseHeaderName = m_headers[n].Before('.');
367 if ( baseHeaderName(0, 3) != "wx/" )
368 continue;
369
370 baseHeaderName.erase(0, 3);
371 for ( index = 0; index < WXSIZEOF(headers); index++ ) {
372 if ( Stricmp(baseHeaderName, headers[index]) == 0 )
373 break;
374 }
375
376 if ( (index < WXSIZEOF(headers)) && classes[index] ) {
377 // interesting header
378 interestingClasses.Add(index);
379 }
380 }
381
382 if ( !interestingClasses.IsEmpty() ) {
383 // do generate "See also" clause
384 totalText << "\\wxheading{See also:}\n\n";
385
386 count = interestingClasses.Count();
387 for ( index = 0; index < count; index++ ) {
388 if ( index > 0 )
389 totalText << ", ";
390
391 totalText << MakeHelpref(classes[interestingClasses[index]]);
392 }
393
394 totalText << "\n\n";
395 }
396 }
397
398 // the comment before the class generally explains what is it for so put it
399 // in place of the class description
400 if ( cl.HasComments() ) {
401 wxString comment = GetAllComments(cl);
402
403 totalText << '\n' << comment << '\n';
404 }
405
406 // derived from section
407 wxString derived = "\\wxheading{Derived from}\n\n";
408
409 const StrListT& baseClasses = cl.mSuperClassNames;
410 if ( baseClasses.size() == 0 ) {
411 derived << "No base class";
412 }
413 else {
414 bool first = true;
415 for ( StrListT::const_iterator i = baseClasses.begin();
416 i != baseClasses.end();
417 i++ ) {
418 if ( !first ) {
419 // separate from the previous one
420 derived << "\\\\\n";
421 }
422 else {
423 first = false;
424 }
425
426 wxString baseclass = *i;
427 derived << "\\helpref{" << baseclass << "}"
428 "{ " << baseclass.MakeLower() << "}";
429 }
430 }
431 totalText << derived << "\n\n";
432
433 // write all this to file
434 m_file.WriteTeX(totalText);
435
436 // if there were any enums/typedefs before, insert their documentation now
437 InsertDataStructuresHeader();
438 InsertTypedefDocs();
439 InsertEnumDocs();
440 }
441
442 void HelpGenVisitor::VisitEnumeration( spEnumeration& en )
443 {
444 CloseFunction();
445
446 if ( m_inMethodSection ) {
447 // FIXME that's a bug, but tell the user aboit it nevertheless... we
448 // should be smart enough to process even the enums which come after the
449 // functions
450 wxLogWarning("enum '%s' ignored, please put it before the class "
451 "methods.", en.GetName().c_str());
452 return;
453 }
454
455 // simply copy the enum text in the docs
456 wxString enumeration = GetAllComments(en);
457 enumeration << "{\\small \\begin{verbatim}\n"
458 << en.mEnumContent
459 << "\n\\end{verbatim}}\n";
460
461 // remember for later use if we're not inside a class yet
462 if ( !m_inClass ) {
463 if ( !m_textStoredEnums.IsEmpty() ) {
464 m_textStoredEnums << '\n';
465 }
466
467 m_textStoredEnums << enumeration;
468 }
469 else {
470 // write the header for this section if not done yet
471 InsertDataStructuresHeader();
472
473 enumeration << '\n';
474 m_file.WriteTeX(enumeration);
475 }
476 }
477
478 void HelpGenVisitor::VisitTypeDef( spTypeDef& td )
479 {
480 CloseFunction();
481
482 if ( m_inMethodSection ) {
483 // FIXME that's a bug, but tell the user aboit it nevertheless...
484 wxLogWarning("typedef '%s' ignored, please put it before the class "
485 "methods.", td.GetName().c_str());
486 return;
487 }
488
489 wxString typedefdoc;
490 typedefdoc << "{\\small \\begin{verbatim}\n"
491 << "typedef " << td.mOriginalType << ' ' << td.GetName()
492 << "\n\\end{verbatim}}\n"
493 << GetAllComments(td);
494
495 // remember for later use if we're not inside a class yet
496 if ( !m_inClass ) {
497 if ( !m_textStoredTypedefs.IsEmpty() ) {
498 m_textStoredTypedefs << '\n';
499 }
500
501 m_textStoredTypedefs << typedefdoc;
502 }
503 else {
504 // write the header for this section if not done yet
505 InsertDataStructuresHeader();
506
507 typedefdoc << '\n';
508 m_file.WriteTeX(typedefdoc);
509 }
510 }
511
512 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine& pd )
513 {
514 switch ( pd.GetStatementType() ) {
515 case SP_PREP_DEF_INCLUDE_FILE:
516 m_headers.Add(pd.CPP_GetIncludedFileNeme());
517 break;
518
519 case SP_PREP_DEF_DEFINE_SYMBOL:
520 // TODO decide if it's a constant and document it if it is
521 break;
522 }
523 }
524
525 void HelpGenVisitor::VisitAttribute( spAttribute& attr )
526 {
527 CloseFunction();
528
529 // only document the public member variables
530 if ( !m_inClass || !attr.IsPublic() )
531 return;
532
533 wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str());
534 }
535
536 void HelpGenVisitor::VisitOperation( spOperation& op )
537 {
538 CloseFunction();
539
540 if ( !m_inClass || !op.IsInClass() ) {
541 // FIXME that's a bug too
542 wxLogWarning("skipped global function '%s'.", op.GetName().c_str());
543
544 return;
545 }
546
547 if ( op.mVisibility == SP_VIS_PRIVATE ) {
548 // FIXME should we document protected functions?
549 return;
550 }
551
552 InsertMethodsHeader();
553
554 // save state info
555 m_inFunction =
556 m_isFirstParam = true;
557
558 m_textStoredFunctionComment = GetAllComments(op);
559
560 // start function documentation
561 wxString totalText;
562 const char *funcname = op.GetName().c_str();
563 const char *classname = op.GetClass().GetName().c_str();
564
565 // check for the special case of dtor
566 wxString dtor;
567 if ( (funcname[0] == '~') && (strcmp(funcname + 1, classname) == 0) ) {
568 dtor.Printf("\\destruct{%s}", classname);
569 funcname = dtor;
570 }
571
572 totalText.Printf("\\membersection{%s::%s}\\label{%s}\n"
573 "\\%sfunc{%s%s}{%s}{",
574 classname, funcname,
575 MakeLabel(classname, funcname).c_str(),
576 op.mIsConstant ? "const" : "",
577 op.mIsVirtual ? "virtual " : "",
578 op.mRetType.c_str(),
579 funcname);
580
581 m_file.WriteTeX(totalText);
582 }
583
584 void HelpGenVisitor::VisitParameter( spParameter& param )
585 {
586 if ( !m_inFunction )
587 return;
588
589 wxString totalText;
590 if ( m_isFirstParam ) {
591 m_isFirstParam = false;
592 }
593 else {
594 totalText << ", ";
595 }
596
597 totalText << "\\param{" << param.mType << " }{" << param.GetName();
598 wxString defvalue = param.mInitVal;
599 if ( !defvalue.IsEmpty() ) {
600 totalText << " = " << defvalue;
601 }
602
603 totalText << '}';
604
605 m_file.WriteTeX(totalText);
606 }
607
608 // -----------------------------------------------------------------------------
609 // global function implementation
610 // -----------------------------------------------------------------------------
611
612 static wxString MakeLabel(const char *classname, const char *funcname)
613 {
614 wxString label(classname);
615 if ( funcname && funcname[0] == '\\' ) {
616 // we may have some special TeX macro - so far only \destruct exists,
617 // but may be later others will be added
618 static const char *macros[] = { "destruct" };
619 static const char *replacement[] = { "dtor" };
620
621 size_t n;
622 for ( n = 0; n < WXSIZEOF(macros); n++ ) {
623 if ( strncmp(funcname + 1, macros[n], strlen(macros[n])) == 0 ) {
624 // found
625 break;
626 }
627 }
628
629 if ( n == WXSIZEOF(macros) ) {
630 wxLogWarning("unknown function name '%s' - leaving as is.",
631 funcname);
632 }
633 else {
634 funcname = replacement[n];
635 }
636 }
637
638 if ( funcname )
639 label << funcname;
640
641 label.MakeLower();
642
643 return label;
644 }
645
646 static wxString MakeHelpref(const char *argument)
647 {
648 wxString helpref;
649 helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}';
650
651 return helpref;
652 }
653
654 static void TeXFilter(wxString* str)
655 {
656 // FIXME may be done much more quickly
657 str->Replace("&", "\\&");
658 str->Replace("_", "\\_");
659 }
660
661 static wxString GetAllComments(const spContext& ctx)
662 {
663 wxString comment;
664 const MCommentListT& comments = ctx.GetCommentList();
665 for ( MCommentListT::const_iterator i = comments.begin();
666 i != comments.end();
667 i++ ) {
668 comment << (*i)->GetText();
669 }
670
671 return comment;
672 }
673
674 static const char *GetCurrentTime(const char *timeFormat)
675 {
676 static char s_timeBuffer[128];
677 time_t timeNow;
678 struct tm *ptmNow;
679
680 time(&timeNow);
681 ptmNow = localtime(&timeNow);
682
683 strftime(s_timeBuffer, WXSIZEOF(s_timeBuffer), timeFormat, ptmNow);
684
685 return s_timeBuffer;
686 }
687
688 /* vi: set tw=80 et ts=4 sw=4: */