1 /*****************************************************************************
5 * Copyright (C) 1997-2014 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)
48 CondCtx(int line,QCString id,bool b)
49 : lineNr(line),sectionId(id), skip(b) {}
62 static BufStr * g_inBuf;
63 static BufStr * g_outBuf;
64 static int g_inBufPos;
66 static int g_blockHeadCol;
67 static bool g_mlBrief;
68 static int g_readLineCtx;
70 static QCString g_fileName;
73 static QStack<CondCtx> g_condStack;
74 static QStack<CommentCtx> g_commentStack;
75 static QCString g_blockName;
76 static int g_lastCommentContext;
77 static bool g_inSpecialComment;
78 static bool g_inRoseComment;
79 static int g_stringContext;
80 static int g_charContext;
81 static int g_javaBlock;
82 static bool g_specialComment;
84 static QCString g_aliasString;
85 static int g_blockCount;
86 static bool g_lastEscaped;
87 static int g_lastBlockContext;
88 static bool g_pythonDocString;
89 static int g_nestingCount;
91 static SrcLangExt g_lang;
92 static bool isFixedForm; // For Fortran
94 static void replaceCommentMarker(const char *s,int len)
98 // copy leading blanks
99 while ((c=*p) && (c==' ' || c=='\t' || c=='\n'))
105 // replace start of comment marker by blanks and the last character by a *
107 while ((c=*p) && (c=='/' || c=='!' || c=='#'))
111 if (*p=='<') // comment-after-item marker
116 if (c=='!') // end after first !
128 if (blanks>1) ADDCHAR('*');
131 // copy comment line to output
132 ADDARRAY(p,len-(int)(p-s));
135 static inline int computeIndent(const char *s)
138 static int tabSize=Config_getInt("TAB_SIZE");
144 else if (c=='\t') col+=tabSize-(col%tabSize);
150 static inline void copyToOutput(const char *s,int len)
153 if (g_skip) // only add newlines.
160 //fprintf(stderr,"---> skip %d\n",g_lineNr);
168 static int tabSize=Config_getInt("TAB_SIZE");
174 //fprintf(stderr,"---> copy %d\n",g_lineNr);
176 case '\t': g_col+=tabSize-(g_col%tabSize); break;
177 default: g_col++; break;
183 static void startCondSection(const char *sectId)
185 //printf("startCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
187 bool expResult = prs.parse(g_fileName,g_lineNr,sectId);
188 g_condStack.push(new CondCtx(g_lineNr,sectId,g_skip));
189 if (!expResult) // not enabled
195 static void endCondSection()
197 if (g_condStack.isEmpty())
199 warn(g_fileName,g_lineNr,"Found \\endcond command without matching \\cond");
204 CondCtx *ctx = g_condStack.pop();
207 //printf("endCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
210 /** copies string \a s with length \a len to the output, while
211 * replacing any alias commands found in the string.
213 static void replaceAliases(const char *s)
215 QCString result = resolveAliasCmd(s);
216 //printf("replaceAliases(%s)->'%s'\n",s,result.data());
217 copyToOutput(result,result.length());
222 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
224 static int yyread(char *buf,int max_size)
226 int bytesInBuf = g_inBuf->curPos()-g_inBufPos;
227 int bytesToCopy = QMIN(max_size,bytesInBuf);
228 memcpy(buf,g_inBuf->data()+g_inBufPos,bytesToCopy);
229 g_inBufPos+=bytesToCopy;
233 void replaceComment(int offset);
252 <Scan>[^"'!\/\n\\#-,]* { /* eat anything that is not " / , or \n */
253 copyToOutput(yytext,(int)yyleng);
255 <Scan>[,] { /* eat , so we have a nice separator in long initialization lines */
256 copyToOutput(yytext,(int)yyleng);
258 <Scan>"\"\"\""! { /* start of python long comment */
259 if (g_lang!=SrcLangExt_Python)
265 g_pythonDocString = TRUE;
267 g_commentStack.clear(); /* to be on the save side */
268 copyToOutput(yytext,(int)yyleng);
270 g_commentStack.push(new CommentCtx(g_lineNr));
274 if (g_lang!=SrcLangExt_Fortran)
280 copyToOutput(yytext,(int)yyleng);
282 g_commentStack.clear(); /* to be on the save side */
284 g_commentStack.push(new CommentCtx(g_lineNr));
287 <Scan>[Cc\*][><!]/.*\n {
288 if (g_lang!=SrcLangExt_Fortran)
294 /* check for fixed format; we might have some conditional as part of multilene if like C<5 .and. & */
295 if (isFixedForm && (g_col == 0))
297 copyToOutput(yytext,(int)yyleng);
299 g_commentStack.clear(); /* to be on the save side */
301 g_commentStack.push(new CommentCtx(g_lineNr));
310 if (g_lang!=SrcLangExt_Fortran)
316 copyToOutput(yytext,(int)yyleng);
320 if (g_lang!=SrcLangExt_Fortran)
328 copyToOutput(yytext,(int)yyleng);
336 <Scan>"\"" { /* start of a string */
337 copyToOutput(yytext,(int)yyleng);
338 g_stringContext = YY_START;
342 copyToOutput(yytext,(int)yyleng);
343 g_charContext = YY_START;
344 if (g_lang!=SrcLangExt_VHDL)
349 <Scan>\n { /* new line */
350 copyToOutput(yytext,(int)yyleng);
352 <Scan>"//!"/.*\n[ \t]*"//"[\/!][^\/] | /* start C++ style special comment block */
353 <Scan>("///"[/]*)/[^/].*\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */
356 REJECT; // bail out if we do not need to convert
363 while (i<(int)yyleng && yytext[i]=='/') i++;
365 g_blockHeadCol=g_col;
366 copyToOutput("/**",3);
367 replaceAliases(yytext+i);
368 g_inSpecialComment=TRUE;
370 g_readLineCtx=SComment;
374 <Scan>"//##Documentation".*/\n { /* Start of Rational Rose ANSI C++ comment block */
375 if (g_mlBrief) REJECT;
376 int i=17; //=strlen("//##Documentation");
377 g_blockHeadCol=g_col;
378 copyToOutput("/**",3);
379 replaceAliases(yytext+i);
380 g_inRoseComment=TRUE;
383 <Scan>"//"/.*\n { /* one line C++ comment */
384 g_inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
385 copyToOutput(yytext,(int)yyleng);
386 g_readLineCtx=YY_START;
389 <Scan>"/**/" { /* avoid matching next rule for empty C comment, see bug 711723 */
390 copyToOutput(yytext,(int)yyleng);
392 <Scan>"/*"[*!]? { /* start of a C comment */
393 g_specialComment=(int)yyleng==3;
395 g_commentStack.clear(); /* to be on the save side */
396 copyToOutput(yytext,(int)yyleng);
398 g_commentStack.push(new CommentCtx(g_lineNr));
401 if (g_lang!=SrcLangExt_Python)
407 copyToOutput(yytext,(int)yyleng);
409 g_commentStack.clear(); /* to be on the save side */
411 g_commentStack.push(new CommentCtx(g_lineNr));
415 if (g_lang!=SrcLangExt_VHDL)
421 copyToOutput(yytext,(int)yyleng);
423 g_commentStack.clear(); /* to be on the save side */
425 g_commentStack.push(new CommentCtx(g_lineNr));
429 if (g_lang!=SrcLangExt_Fortran)
435 copyToOutput(yytext,(int)yyleng);
437 g_commentStack.clear(); /* to be on the save side */
439 g_commentStack.push(new CommentCtx(g_lineNr));
442 <CComment>"{@code"/[ \t\n] {
443 copyToOutput("@code",5);
444 g_lastCommentContext = YY_START;
446 g_blockName=&yytext[1];
449 <CComment,ReadLine>[\\@]("dot"|"code"|"msc"|"startuml")/[^a-z_A-Z0-9] { /* start of a verbatim block */
450 copyToOutput(yytext,(int)yyleng);
451 g_lastCommentContext = YY_START;
453 if (qstrcmp(&yytext[1],"startuml")==0)
459 g_blockName=&yytext[1];
463 <CComment,ReadLine>[\\@]("f$"|"f["|"f{"[a-z]*) {
464 copyToOutput(yytext,(int)yyleng);
465 g_blockName=&yytext[1];
466 if (g_blockName.at(1)=='[')
468 g_blockName.at(1)=']';
470 else if (g_blockName.at(1)=='{')
472 g_blockName.at(1)='}';
474 g_lastCommentContext = YY_START;
477 <CComment,ReadLine>[\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"docbookonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */
478 copyToOutput(yytext,(int)yyleng);
479 g_blockName=&yytext[1];
480 g_lastCommentContext = YY_START;
483 <Scan>. { /* any ather character */
484 copyToOutput(yytext,(int)yyleng);
486 <Verbatim>[\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"docbookonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}") { /* end of verbatim block */
487 copyToOutput(yytext,(int)yyleng);
488 if (yytext[1]=='f') // end of formula
490 BEGIN(g_lastCommentContext);
492 else if (&yytext[4]==g_blockName)
494 BEGIN(g_lastCommentContext);
505 copyToOutput(yytext,(int)yyleng);
518 copyToOutput(" @endcode ",10);
519 BEGIN(g_lastCommentContext);
523 copyToOutput(yytext,(int)yyleng);
527 <VerbatimCode>[\\@]("enddot"|"endcode"|"endmsc"|"enduml") { /* end of verbatim block */
528 copyToOutput(yytext,(int)yyleng);
529 if (&yytext[4]==g_blockName)
531 BEGIN(g_lastCommentContext);
534 <VerbatimCode>^[ \t]*"//"[\!\/]? { /* skip leading comments */
535 if (!g_inSpecialComment)
537 copyToOutput(yytext,(int)yyleng);
542 while (yytext[l]==' ' || yytext[l]=='\t')
546 copyToOutput(yytext,l);
547 if (yyleng-l==3) // ends with //! or ///
549 copyToOutput(" * ",3);
553 copyToOutput("//",2);
557 <Verbatim,VerbatimCode>[^@\/\\\n{}]* { /* any character not a backslash or new line or } */
558 copyToOutput(yytext,(int)yyleng);
560 <Verbatim,VerbatimCode>\n { /* new line in verbatim block */
561 copyToOutput(yytext,(int)yyleng);
563 <Verbatim>^[ \t]*"///" {
564 if (g_blockName=="dot" || g_blockName=="msc" || g_blockName=="uml" || g_blockName.at(0)=='f')
566 // see bug 487871, strip /// from dot images and formulas.
568 while (yytext[l]==' ' || yytext[l]=='\t')
572 copyToOutput(yytext,l);
575 else // even slashes are verbatim (e.g. \verbatim, \code)
580 <Verbatim,VerbatimCode>. { /* any other character */
581 copyToOutput(yytext,(int)yyleng);
583 <SkipString>\\. { /* escaped character in string */
584 copyToOutput(yytext,(int)yyleng);
586 <SkipString>"\"" { /* end of string */
587 copyToOutput(yytext,(int)yyleng);
588 BEGIN(g_stringContext);
590 <SkipString>. { /* any other string character */
591 copyToOutput(yytext,(int)yyleng);
593 <SkipString>\n { /* new line inside string (illegal for some compilers) */
594 copyToOutput(yytext,(int)yyleng);
596 <SkipChar>\\. { /* escaped character */
597 copyToOutput(yytext,(int)yyleng);
599 <SkipChar>' { /* end of character literal */
600 copyToOutput(yytext,(int)yyleng);
601 BEGIN(g_charContext);
603 <SkipChar>. { /* any other string character */
604 copyToOutput(yytext,(int)yyleng);
606 <SkipChar>\n { /* new line character */
607 copyToOutput(yytext,(int)yyleng);
610 <CComment>[^\\!@*\n{\"\/]* { /* anything that is not a '*' or command */
611 copyToOutput(yytext,(int)yyleng);
613 <CComment>"*"+[^*/\\@\n{\"]* { /* stars without slashes */
614 copyToOutput(yytext,(int)yyleng);
616 <CComment>"\"\"\"" { /* end of Python docstring */
617 if (g_lang!=SrcLangExt_Python)
623 g_pythonDocString = FALSE;
624 copyToOutput(yytext,(int)yyleng);
628 <CComment>\n { /* new line in comment */
629 copyToOutput(yytext,(int)yyleng);
630 /* in case of Fortran always end of comment */
631 if (g_lang==SrcLangExt_Fortran)
636 <CComment>"/"+"*" { /* nested C comment */
638 g_commentStack.push(new CommentCtx(g_lineNr));
639 copyToOutput(yytext,(int)yyleng);
641 <CComment>"*"+"/" { /* end of C comment */
642 if (g_lang==SrcLangExt_Python)
648 copyToOutput(yytext,(int)yyleng);
649 if (g_nestingCount<=0)
656 delete g_commentStack.pop();
660 <CComment>"\n"/[ \t]*[^#] { /* end of Python comment */
661 if (g_lang!=SrcLangExt_Python || g_pythonDocString)
667 copyToOutput(yytext,(int)yyleng);
671 <CComment>"\n"/[ \t]*[^\-] { /* end of VHDL comment */
672 if (g_lang!=SrcLangExt_VHDL)
678 copyToOutput(yytext,(int)yyleng);
682 /* removed for bug 674842 (bug was introduced in rev 768)
684 g_charContext = YY_START;
685 copyToOutput(yytext,(int)yyleng);
689 g_stringContext = YY_START;
690 copyToOutput(yytext,(int)yyleng);
695 copyToOutput(yytext,(int)yyleng);
697 <SComment>^[ \t]*"///"[\/]*/\n {
700 <SComment>\n[ \t]*"///"[\/]*/\n {
703 <SComment>^[ \t]*"///"[^\/\n]/.*\n {
705 g_readLineCtx=YY_START;
708 <SComment>\n[ \t]*"///"[^\/\n]/.*\n {
710 g_readLineCtx=YY_START;
713 <SComment>^[ \t]*"//!" | // just //!
714 <SComment>^[ \t]*"//!<"/.*\n | // or //!< something
715 <SComment>^[ \t]*"//!"[^<]/.*\n { // or //!something
717 g_readLineCtx=YY_START;
720 <SComment>\n[ \t]*"//!" |
721 <SComment>\n[ \t]*"//!<"/.*\n |
722 <SComment>\n[ \t]*"//!"[^<\n]/.*\n {
724 g_readLineCtx=YY_START;
727 <SComment>^[ \t]*"//##"/.*\n {
728 if (!g_inRoseComment)
735 g_readLineCtx=YY_START;
739 <SComment>\n[ \t]*"//##"/.*\n {
740 if (!g_inRoseComment)
747 g_readLineCtx=YY_START;
751 <SComment>\n { /* end of special comment */
752 copyToOutput(" */",3);
753 copyToOutput(yytext,(int)yyleng);
754 g_inSpecialComment=FALSE;
755 g_inRoseComment=FALSE;
758 <ReadLine>[^\\@\n]*/\n {
759 copyToOutput(yytext,(int)yyleng);
760 BEGIN(g_readLineCtx);
762 <CComment,ReadLine>[\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
763 copyToOutput(yytext,(int)yyleng);
765 <CComment,ReadLine>[\\@]"cond"/[^a-z_A-Z0-9] { // conditional section
766 g_condCtx = YY_START;
769 <CComment,ReadLine>[\\@]"endcond"/[^a-z_A-Z0-9] { // end of conditional section
772 if (YY_START==CComment && oldSkip && !g_skip)
774 //printf("** Adding start of comment!\n");
775 if (g_lang!=SrcLangExt_Python &&
776 g_lang!=SrcLangExt_VHDL &&
777 g_lang!=SrcLangExt_Fortran)
781 if (g_specialComment)
788 <CondLine>[!()&| \ta-z_A-Z0-9.\-]+ {
790 startCondSection(yytext);
791 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
794 if (g_lang!=SrcLangExt_Python &&
795 g_lang!=SrcLangExt_VHDL &&
796 g_lang!=SrcLangExt_Fortran)
802 if (g_readLineCtx==SComment)
812 <CComment,ReadLine>[\\@]"cond"[ \t\r]*/\n |
813 <CondLine>. { // forgot section id?
814 if (YY_START!=CondLine) g_condCtx=YY_START;
816 startCondSection(" "); // fake section id causing the section to be hidden unconditionally
817 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
820 //printf("** Adding terminator for comment!\n");
821 if (g_lang!=SrcLangExt_Python &&
822 g_lang!=SrcLangExt_VHDL)
828 if (*yytext=='\n') g_lineNr++;
829 if (g_readLineCtx==SComment)
838 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments
839 replaceAliases(yytext);
841 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments
842 g_lastBlockContext=YY_START;
844 g_aliasString=yytext;
846 BEGIN( ReadAliasArgs );
848 <ReadAliasArgs>^[ \t]*"//"[/!]/[^\n]+ { // skip leading special comments (see bug 618079)
850 <ReadAliasArgs>"*/" { // oops, end of comment in the middle of an alias?
851 if (g_lang==SrcLangExt_Python)
855 else // abort the alias, restart scanning
857 copyToOutput(g_aliasString,g_aliasString.length());
858 copyToOutput(yytext,(int)yyleng);
862 <ReadAliasArgs>[^{}\n\\\*]+ {
863 g_aliasString+=yytext;
866 <ReadAliasArgs>"\\" {
867 if (g_lastEscaped) g_lastEscaped=FALSE;
868 else g_lastEscaped=TRUE;
869 g_aliasString+=yytext;
872 g_aliasString+=yytext;
877 g_aliasString+=yytext;
878 if (!g_lastEscaped) g_blockCount++;
882 g_aliasString+=yytext;
883 if (!g_lastEscaped) g_blockCount--;
886 replaceAliases(g_aliasString);
887 BEGIN( g_lastBlockContext );
892 g_aliasString+=yytext;
896 copyToOutput(yytext,(int)yyleng);
901 void replaceComment(int offset)
903 if (g_mlBrief || g_skip)
905 copyToOutput(yytext,(int)yyleng);
909 //printf("replaceComment(%s)\n",yytext);
910 int i=computeIndent(&yytext[offset]);
911 if (i==g_blockHeadCol)
913 replaceCommentMarker(yytext,(int)yyleng);
917 copyToOutput(" */",3);
918 int i;for (i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
919 g_inSpecialComment=FALSE;
925 // simplified way to know if this is fixed form
926 // duplicate in fortrancode.l
927 static bool recognizeFixedForm(const char* contents)
935 switch(contents[i]) {
947 if(column==1) return TRUE;
951 if(column>1 && column<7) return FALSE;
956 if(column==7) return TRUE;
964 /*! This function does three things:
965 * -# It converts multi-line C++ style comment blocks (that are aligned)
966 * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO).
967 * -# It replaces aliases with their definition (see ALIASES)
968 * -# It handles conditional sections (cond...endcond blocks)
970 void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName)
972 //printf("convertCppComments(%s)\n",fileName);
977 g_mlBrief = Config_getBool("MULTILINE_CPP_IS_BRIEF");
979 g_fileName = fileName;
980 g_lang = getLanguageFromFileName(fileName);
981 g_pythonDocString = FALSE;
984 g_condStack.setAutoDelete(TRUE);
985 g_commentStack.clear();
986 g_commentStack.setAutoDelete(TRUE);
988 printlex(yy_flex_debug, TRUE, __FILE__, fileName);
990 if (g_lang==SrcLangExt_Fortran)
992 isFixedForm = recognizeFixedForm(inBuf->data());
995 if (g_lang==SrcLangExt_Markdown)
999 g_commentStack.push(new CommentCtx(g_lineNr));
1006 while (!g_condStack.isEmpty())
1008 CondCtx *ctx = g_condStack.pop();
1009 QCString sectionInfo = " ";
1010 if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label %s ",ctx->sectionId.data());
1011 warn(g_fileName,ctx->lineNr,"Conditional section%sdoes not have "
1012 "a corresponding \\endcond command within this file.",sectionInfo.data());
1014 if (g_nestingCount>0 && g_lang!=SrcLangExt_Markdown)
1016 QCString tmp= "(probable line reference: ";
1018 while (!g_commentStack.isEmpty())
1020 CommentCtx *ctx = g_commentStack.pop();
1021 if (!first) tmp += ", ";
1022 tmp += QCString().setNum(ctx->lineNr);
1027 warn(g_fileName,g_lineNr,"Reached end of file while still inside a (nested) comment. "
1028 "Nesting level %d %s",g_nestingCount+1,tmp.data()); // add one for "normal" expected end of comment
1030 g_commentStack.clear();
1031 if (Debug::isFlagSet(Debug::CommentCnv))
1033 g_outBuf->at(g_outBuf->curPos())='\0';
1034 msg("-------------\n%s\n-------------\n",g_outBuf->data());
1036 printlex(yy_flex_debug, FALSE, __FILE__, fileName);
1040 //----------------------------------------------------------------------------
1041 #if !defined(YY_FLEX_SUBMINOR_VERSION)
1042 extern "C" { // some bogus code to keep the compiler happy
1043 void commentcnvYYdummy() { yy_flex_realloc(0,0); }