1 /*****************************************************************************
5 * Copyright (C) 1997-2012 by Dimitri van Heesch.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation under the terms of the GNU General Public License is hereby
9 * granted. No representations are made about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 * See the GNU General Public License for more details.
13 * Documents produced by Doxygen are derivative works derived from the
14 * input used in their production; they are not affected by this license.
20 #define YY_NEVER_INTERACTIVE 1
28 #include <qtextstream.h>
37 #include "condparser.h"
43 #define ADDCHAR(c) g_outBuf->addChar(c)
44 #define ADDARRAY(a,s) g_outBuf->addArray(a,s)
54 CondCtx(int line,QCString id,bool b)
55 : lineNr(line),sectionId(id), skip(b) {}
61 static BufStr * g_inBuf;
62 static BufStr * g_outBuf;
63 static int g_inBufPos;
65 static int g_blockHeadCol;
66 static bool g_mlBrief;
67 static int g_readLineCtx;
69 static QCString g_fileName;
72 static QStack<CondCtx> g_condStack;
73 static QCString g_blockName;
74 static int g_lastCommentContext;
75 static bool g_inSpecialComment;
76 static bool g_inRoseComment;
77 static int g_stringContext;
78 static int g_charContext;
79 static int g_javaBlock;
80 static bool g_specialComment;
82 static QCString g_aliasString;
83 static int g_blockCount;
84 static bool g_lastEscaped;
85 static int g_lastBlockContext;
86 static bool g_pythonDocString;
88 static GuardType guardType; // kind of guard for conditional section
90 static SrcLangExt g_lang;
92 static void replaceCommentMarker(const char *s,int len)
96 // copy leading blanks
97 while ((c=*p) && (c==' ' || c=='\t' || c=='\n'))
103 // replace start of comment marker by blanks and the last character by a *
105 while ((c=*p) && (c=='/' || c=='!' || c=='#'))
109 if (*p=='<') // comment-after-item marker
114 if (c=='!') // end after first !
126 if (blanks>1) ADDCHAR('*');
129 // copy comment line to output
130 ADDARRAY(p,len-(int)(p-s));
133 static inline int computeIndent(const char *s)
136 static int tabSize=Config_getInt("TAB_SIZE");
142 else if (c=='\t') col+=tabSize-(col%tabSize);
148 static inline void copyToOutput(const char *s,int len)
151 if (g_skip) // only add newlines.
158 //fprintf(stderr,"---> skip %d\n",g_lineNr);
166 static int tabSize=Config_getInt("TAB_SIZE");
172 //fprintf(stderr,"---> copy %d\n",g_lineNr);
174 case '\t': g_col+=tabSize-(g_col%tabSize); break;
175 default: g_col++; break;
181 static void startCondSection(const char *sectId)
184 bool expResult = prs.parse(g_fileName,g_lineNr,sectId);
185 g_condStack.push(new CondCtx(g_lineNr,sectId,g_skip));
186 if (guardType == Guard_Cond) // found @cond
188 if (!expResult) // not enabled
193 else if (guardType == Guard_CondNot) // found @notcond
195 if (expResult) // enabled
202 static void endCondSection()
204 if (g_condStack.isEmpty())
206 warn(g_fileName,g_lineNr,"Found \\endcond command without matching \\cond");
211 CondCtx *ctx = g_condStack.pop();
216 /** copies string \a s with length \a len to the output, while
217 * replacing any alias commands found in the string.
219 static void replaceAliases(const char *s)
221 QCString result = resolveAliasCmd(s);
222 //printf("replaceAliases(%s)->'%s'\n",s,result.data());
223 copyToOutput(result,result.length());
228 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
230 static int yyread(char *buf,int max_size)
232 int bytesInBuf = g_inBuf->curPos()-g_inBufPos;
233 int bytesToCopy = QMIN(max_size,bytesInBuf);
234 memcpy(buf,g_inBuf->data()+g_inBufPos,bytesToCopy);
235 g_inBufPos+=bytesToCopy;
239 void replaceComment(int offset);
258 <Scan>[^"'!\/\n\\#\\-]* { /* eat anything that is not " / or \n */
259 copyToOutput(yytext,(int)yyleng);
261 <Scan>"\"\"\""! { /* start of python long comment */
262 if (g_lang!=SrcLangExt_Python)
268 g_pythonDocString = TRUE;
269 copyToOutput(yytext,(int)yyleng);
273 <Scan>"!>"|"!<"|"!!" {
274 if (g_lang!=SrcLangExt_Fortran)
280 copyToOutput(yytext,(int)yyleng);
284 <Scan>"\"" { /* start of a string */
285 copyToOutput(yytext,(int)yyleng);
286 g_stringContext = YY_START;
290 copyToOutput(yytext,(int)yyleng);
291 g_charContext = YY_START;
294 <Scan>\n { /* new line */
295 copyToOutput(yytext,(int)yyleng);
297 <Scan>("//!"|"///")/.*\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */
300 REJECT; // bail out if we do not need to convert
307 while (i<(int)yyleng && yytext[i]=='/') i++;
309 g_blockHeadCol=g_col;
310 copyToOutput("/**",3);
311 replaceAliases(yytext+i);
312 g_inSpecialComment=TRUE;
314 g_readLineCtx=SComment;
318 <Scan>"//##Documentation".*/\n { /* Start of Rational Rose ANSI C++ comment block */
319 if (g_mlBrief) REJECT;
320 int i=17; //=strlen("//##Documentation");
321 g_blockHeadCol=g_col;
322 copyToOutput("/**",3);
323 replaceAliases(yytext+i);
324 g_inRoseComment=TRUE;
327 <Scan>"//"/.*\n { /* one line C++ comment */
328 g_inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
329 copyToOutput(yytext,(int)yyleng);
330 g_readLineCtx=YY_START;
333 <Scan>"/*"[*!]? { /* start of a C comment */
334 g_specialComment=(int)yyleng==3;
335 copyToOutput(yytext,(int)yyleng);
339 if (g_lang!=SrcLangExt_Python)
345 copyToOutput(yytext,(int)yyleng);
350 if (g_lang!=SrcLangExt_VHDL)
356 copyToOutput(yytext,(int)yyleng);
360 <Scan>"!>"|"!<"|"!!" {
361 if (g_lang!=SrcLangExt_Fortran)
367 copyToOutput(yytext,(int)yyleng);
371 <CComment>"{@code"/[ \t\n] {
372 copyToOutput("@code",5);
373 g_lastCommentContext = YY_START;
375 g_blockName=&yytext[1];
378 <CComment,ReadLine>[\\@]("dot"|"code"|"msc")/[^a-z_A-Z0-9] { /* start of a verbatim block */
379 copyToOutput(yytext,(int)yyleng);
380 g_lastCommentContext = YY_START;
382 g_blockName=&yytext[1];
385 <CComment,ReadLine>[\\@]("f$"|"f["|"f{"[a-z]*) {
386 copyToOutput(yytext,(int)yyleng);
387 g_blockName=&yytext[1];
388 if (g_blockName.at(1)=='[')
390 g_blockName.at(1)=']';
392 else if (g_blockName.at(1)=='{')
394 g_blockName.at(1)='}';
396 g_lastCommentContext = YY_START;
399 <CComment,ReadLine>[\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */
400 copyToOutput(yytext,(int)yyleng);
401 g_blockName=&yytext[1];
402 g_lastCommentContext = YY_START;
405 <Scan>. { /* any other character */
406 copyToOutput(yytext,(int)yyleng);
408 <Verbatim>[\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}") { /* end of verbatim block */
409 copyToOutput(yytext,(int)yyleng);
410 if (yytext[1]=='f') // end of formula
412 BEGIN(g_lastCommentContext);
414 else if (&yytext[4]==g_blockName)
416 BEGIN(g_lastCommentContext);
427 copyToOutput(yytext,(int)yyleng);
440 copyToOutput(" @endcode ",10);
441 BEGIN(g_lastCommentContext);
445 copyToOutput(yytext,(int)yyleng);
449 <VerbatimCode>[\\@]("enddot"|"endcode"|"endmsc") { /* end of verbatim block */
450 copyToOutput(yytext,(int)yyleng);
451 if (&yytext[4]==g_blockName)
453 BEGIN(g_lastCommentContext);
456 <VerbatimCode>^[ \t]*"//"[\!\/]? { /* skip leading comments */
457 if (!g_inSpecialComment)
459 copyToOutput(yytext,(int)yyleng);
464 while (yytext[l]==' ' || yytext[l]=='\t')
468 copyToOutput(yytext,l);
469 if (yyleng-l==3) // ends with //! or ///
471 copyToOutput(" * ",3);
475 copyToOutput("//",2);
479 <Verbatim,VerbatimCode>[^@\/\\\n{}]* { /* any character not a backslash or new line or } */
480 copyToOutput(yytext,(int)yyleng);
482 <Verbatim,VerbatimCode>\n { /* new line in verbatim block */
483 copyToOutput(yytext,(int)yyleng);
485 <Verbatim>^[ \t]*"///" {
486 if (g_blockName=="dot" || g_blockName=="msc" || g_blockName.at(0)=='f')
488 // see bug 487871, strip /// from dot images and formulas.
490 while (yytext[l]==' ' || yytext[l]=='\t')
494 copyToOutput(yytext,l);
497 else // even slashes are verbatim (e.g. \verbatim, \code)
502 <Verbatim,VerbatimCode>. { /* any other character */
503 copyToOutput(yytext,(int)yyleng);
505 <SkipString>\\. { /* escaped character in string */
506 copyToOutput(yytext,(int)yyleng);
508 <SkipString>"\"" { /* end of string */
509 copyToOutput(yytext,(int)yyleng);
510 BEGIN(g_stringContext);
512 <SkipString>. { /* any other string character */
513 copyToOutput(yytext,(int)yyleng);
515 <SkipString>\n { /* new line inside string (illegal for some compilers) */
516 copyToOutput(yytext,(int)yyleng);
518 <SkipChar>\\. { /* escaped character */
519 copyToOutput(yytext,(int)yyleng);
521 <SkipChar>' { /* end of character literal */
522 copyToOutput(yytext,(int)yyleng);
523 BEGIN(g_charContext);
525 <SkipChar>. { /* any other string character */
526 copyToOutput(yytext,(int)yyleng);
528 <SkipChar>\n { /* new line character */
529 copyToOutput(yytext,(int)yyleng);
532 <CComment>[^\\!@*\n{\"]* { /* anything that is not a '*' or command */
533 copyToOutput(yytext,(int)yyleng);
535 <CComment>"*"+[^*/\\@\n{\"]* { /* stars without slashes */
536 copyToOutput(yytext,(int)yyleng);
538 <CComment>"\"\"\"" { /* end of Python docstring */
539 if (g_lang!=SrcLangExt_Python)
545 g_pythonDocString = FALSE;
546 copyToOutput(yytext,(int)yyleng);
550 <CComment>\n { /* new line in comment */
551 copyToOutput(yytext,(int)yyleng);
553 <CComment>"*"+"/" { /* end of C comment */
554 if (g_lang==SrcLangExt_Python)
560 copyToOutput(yytext,(int)yyleng);
564 <CComment>"\n"/[ \t]*[^#] { /* end of Python comment */
565 if (g_lang!=SrcLangExt_Python || g_pythonDocString)
571 copyToOutput(yytext,(int)yyleng);
575 <CComment>"\n"/[ \t]*[^\-] { /* end of VHDL comment */
576 if (g_lang!=SrcLangExt_VHDL)
582 copyToOutput(yytext,(int)yyleng);
586 <CComment>"\n"/[ \t]*[^!] { /* end of Fortran comment */
587 if (g_lang!=SrcLangExt_Fortran)
593 copyToOutput(yytext,(int)yyleng);
597 /* removed for bug 674842 (bug was introduced in rev 768)
599 g_charContext = YY_START;
600 copyToOutput(yytext,(int)yyleng);
604 g_stringContext = YY_START;
605 copyToOutput(yytext,(int)yyleng);
610 copyToOutput(yytext,(int)yyleng);
612 <SComment>^[ \t]*"///"[\/]*/\n {
615 <SComment>\n[ \t]*"///"[\/]*/\n {
618 <SComment>^[ \t]*"///"[^\/\n]/.*\n {
620 g_readLineCtx=YY_START;
623 <SComment>\n[ \t]*"///"[^\/\n]/.*\n {
625 g_readLineCtx=YY_START;
628 <SComment>^[ \t]*"//!" | // just //!
629 <SComment>^[ \t]*"//!<"/.*\n | // or //!< something
630 <SComment>^[ \t]*"//!"[^<]/.*\n { // or //!something
632 g_readLineCtx=YY_START;
635 <SComment>\n[ \t]*"//!" |
636 <SComment>\n[ \t]*"//!<"/.*\n |
637 <SComment>\n[ \t]*"//!"[^<\n]/.*\n {
639 g_readLineCtx=YY_START;
642 <SComment>^[ \t]*"//##"/.*\n {
643 if (!g_inRoseComment)
650 g_readLineCtx=YY_START;
654 <SComment>\n[ \t]*"//##"/.*\n {
655 if (!g_inRoseComment)
662 g_readLineCtx=YY_START;
666 <SComment>\n { /* end of special comment */
667 copyToOutput(" */",3);
668 copyToOutput(yytext,(int)yyleng);
669 g_inSpecialComment=FALSE;
670 g_inRoseComment=FALSE;
673 <ReadLine>[^\\@\n]*/\n {
674 copyToOutput(yytext,(int)yyleng);
675 BEGIN(g_readLineCtx);
677 <CComment,ReadLine>[\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
678 copyToOutput(yytext,(int)yyleng);
680 <CComment,ReadLine>[\\@]"cond"[ \t]+ { // conditional section
681 g_condCtx = YY_START;
682 guardType = Guard_Cond;
685 <CComment,ReadLine>[\\@]"endcond"/[^a-z_A-Z0-9] { // end of conditional section
688 if (YY_START==CComment && oldSkip && !g_skip)
690 //printf("** Adding start of comment!\n");
691 if (g_lang!=SrcLangExt_Python &&
692 g_lang!=SrcLangExt_VHDL &&
693 g_lang!=SrcLangExt_Fortran)
697 if (g_specialComment)
704 <CondLine>[!()&| \ta-z_A-Z0-9.\-]+ {
706 startCondSection(yytext);
707 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
710 if (g_lang!=SrcLangExt_Python &&
711 g_lang!=SrcLangExt_VHDL &&
712 g_lang!=SrcLangExt_Fortran)
718 if (g_readLineCtx==SComment)
728 <CComment,ReadLine>[\\@]"cond"[ \t\r]*/\n |
729 <CondLine>. { // forgot section id?
730 guardType = Guard_Cond;
731 if (YY_START!=CondLine) g_condCtx=YY_START;
733 startCondSection(" "); // fake section id causing the section to be hidden unconditionally
734 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
737 //printf("** Adding terminator for comment!\n");
738 if (g_lang!=SrcLangExt_Python &&
739 g_lang!=SrcLangExt_VHDL)
745 if (*yytext=='\n') g_lineNr++;
746 if (g_readLineCtx==SComment)
755 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments
756 replaceAliases(yytext);
758 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments
759 g_lastBlockContext=YY_START;
761 g_aliasString=yytext;
763 BEGIN( ReadAliasArgs );
765 <ReadAliasArgs>^[ \t]*"//"[/!]/[^\n]+ { // skip leading special comments (see bug 618079)
767 <ReadAliasArgs>"*/" { // oops, end of comment in the middle of an alias?
768 if (g_lang==SrcLangExt_Python)
772 else // abort the alias, restart scanning
774 copyToOutput(g_aliasString,g_aliasString.length());
775 copyToOutput(yytext,(int)yyleng);
779 <ReadAliasArgs>[^{}\n\\\*]+ {
780 g_aliasString+=yytext;
783 <ReadAliasArgs>"\\" {
784 if (g_lastEscaped) g_lastEscaped=FALSE;
785 else g_lastEscaped=TRUE;
786 g_aliasString+=yytext;
789 g_aliasString+=yytext;
794 g_aliasString+=yytext;
795 if (!g_lastEscaped) g_blockCount++;
799 g_aliasString+=yytext;
800 if (!g_lastEscaped) g_blockCount--;
803 replaceAliases(g_aliasString);
804 BEGIN( g_lastBlockContext );
809 g_aliasString+=yytext;
813 copyToOutput(yytext,(int)yyleng);
818 void replaceComment(int offset)
820 if (g_mlBrief || g_skip)
822 copyToOutput(yytext,(int)yyleng);
826 //printf("replaceComment(%s)\n",yytext);
827 int i=computeIndent(&yytext[offset]);
828 if (i==g_blockHeadCol)
830 replaceCommentMarker(yytext,(int)yyleng);
834 copyToOutput(" */",3);
835 int i;for (i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
836 g_inSpecialComment=FALSE;
842 /*! This function does three things:
843 * -# It converts multi-line C++ style comment blocks (that are aligned)
844 * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO).
845 * -# It replaces aliases with their definition (see ALIASES)
846 * -# It handles conditional sections (cond...endcond blocks)
848 void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName)
850 //printf("convertCppComments(%s)\n",fileName);
855 g_mlBrief = Config_getBool("MULTILINE_CPP_IS_BRIEF");
857 g_fileName = fileName;
858 g_lang = getLanguageFromFileName(fileName);
859 g_pythonDocString = FALSE;
862 g_condStack.setAutoDelete(TRUE);
863 if (g_lang==SrcLangExt_Markdown)
872 while (!g_condStack.isEmpty())
874 CondCtx *ctx = g_condStack.pop();
875 QCString sectionInfo = " ";
876 if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label %s ",ctx->sectionId.data());
877 warn(g_fileName,ctx->lineNr,"Conditional section%sdoes not have "
878 "a corresponding \\endcond command within this file.",sectionInfo.data());
880 if (Debug::isFlagSet(Debug::CommentCnv))
882 g_outBuf->at(g_outBuf->curPos())='\0';
883 msg("-------------\n%s\n-------------\n",g_outBuf->data());
888 //----------------------------------------------------------------------------
889 #if !defined(YY_FLEX_SUBMINOR_VERSION)
890 extern "C" { // some bogus code to keep the compiler happy
891 void commentcnvYYdummy() { yy_flex_realloc(0,0); }