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