1 /*****************************************************************************
5 * Copyright (C) 1997-2015 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.
17 %option never-interactive
18 %option prefix="commentcnvYY"
28 #include <qtextstream.h>
37 #include "condparser.h"
42 #define YY_NO_UNISTD_H 1
44 #define ADDCHAR(c) g_outBuf->addChar(c)
45 #define ADDARRAY(a,s) g_outBuf->addArray(a,s)
49 CondCtx(int line,QCString id,bool b)
50 : lineNr(line),sectionId(id), skip(b) {}
63 static BufStr * g_inBuf;
64 static BufStr * g_outBuf;
65 static int g_inBufPos;
67 static int g_blockHeadCol;
68 static bool g_mlBrief;
69 static int g_readLineCtx;
71 static QCString g_fileName;
74 static QStack<CondCtx> g_condStack;
75 static QStack<CommentCtx> g_commentStack;
76 static QCString g_blockName;
77 static int g_lastCommentContext;
78 static bool g_inSpecialComment;
79 static bool g_inRoseComment;
80 static int g_stringContext;
81 static int g_charContext;
82 static int g_javaBlock;
83 static bool g_specialComment;
85 static QCString g_aliasString;
86 static int g_blockCount;
87 static bool g_lastEscaped;
88 static int g_lastBlockContext;
89 static bool g_pythonDocString;
90 static int g_nestingCount;
92 static SrcLangExt g_lang;
93 static bool isFixedForm; // For Fortran
95 static void replaceCommentMarker(const char *s,int len)
99 // copy leading blanks
100 while ((c=*p) && (c==' ' || c=='\t' || c=='\n'))
106 // replace start of comment marker by blanks and the last character by a *
108 while ((c=*p) && (c=='/' || c=='!' || c=='#'))
112 if (*p=='<') // comment-after-item marker
117 if (c=='!') // end after first !
129 if (blanks>1) ADDCHAR('*');
132 // copy comment line to output
133 ADDARRAY(p,len-(int)(p-s));
136 static inline int computeIndent(const char *s)
139 static int tabSize=Config_getInt(TAB_SIZE);
145 else if (c=='\t') col+=tabSize-(col%tabSize);
151 static inline void copyToOutput(const char *s,int len)
154 if (g_skip) // only add newlines.
161 //fprintf(stderr,"---> skip %d\n",g_lineNr);
169 static int tabSize=Config_getInt(TAB_SIZE);
175 //fprintf(stderr,"---> copy %d\n",g_lineNr);
177 case '\t': g_col+=tabSize-(g_col%tabSize); break;
178 default: g_col++; break;
184 static void startCondSection(const char *sectId)
186 //printf("startCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
188 bool expResult = prs.parse(g_fileName,g_lineNr,sectId);
189 g_condStack.push(new CondCtx(g_lineNr,sectId,g_skip));
190 if (!expResult) // not enabled
196 static void endCondSection()
198 if (g_condStack.isEmpty())
200 warn(g_fileName,g_lineNr,"Found \\endcond command without matching \\cond");
205 CondCtx *ctx = g_condStack.pop();
208 //printf("endCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
211 /** copies string \a s with length \a len to the output, while
212 * replacing any alias commands found in the string.
214 static void replaceAliases(const char *s)
216 QCString result = resolveAliasCmd(s);
217 //printf("replaceAliases(%s)->'%s'\n",s,result.data());
218 copyToOutput(result,result.length());
223 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
225 static int yyread(char *buf,int max_size)
227 int bytesInBuf = g_inBuf->curPos()-g_inBufPos;
228 int bytesToCopy = QMIN(max_size,bytesInBuf);
229 memcpy(buf,g_inBuf->data()+g_inBufPos,bytesToCopy);
230 g_inBufPos+=bytesToCopy;
234 void replaceComment(int offset);
253 <Scan>[^"'!\/\n\\#-,]* { /* eat anything that is not " / , or \n */
254 copyToOutput(yytext,(int)yyleng);
256 <Scan>[,] { /* eat , so we have a nice separator in long initialization lines */
257 copyToOutput(yytext,(int)yyleng);
259 <Scan>"\"\"\""! { /* start of python long comment */
260 if (g_lang!=SrcLangExt_Python)
266 g_pythonDocString = TRUE;
268 g_commentStack.clear(); /* to be on the save side */
269 copyToOutput(yytext,(int)yyleng);
271 g_commentStack.push(new CommentCtx(g_lineNr));
275 if (g_lang!=SrcLangExt_Fortran)
281 copyToOutput(yytext,(int)yyleng);
283 g_commentStack.clear(); /* to be on the save side */
285 g_commentStack.push(new CommentCtx(g_lineNr));
288 <Scan>[Cc\*][><!]/.*\n {
289 if (g_lang!=SrcLangExt_Fortran)
295 /* check for fixed format; we might have some conditional as part of multilene if like C<5 .and. & */
296 if (isFixedForm && (g_col == 0))
298 copyToOutput(yytext,(int)yyleng);
300 g_commentStack.clear(); /* to be on the save side */
302 g_commentStack.push(new CommentCtx(g_lineNr));
311 if (g_lang!=SrcLangExt_Fortran)
317 copyToOutput(yytext,(int)yyleng);
321 if (g_lang!=SrcLangExt_Fortran)
329 copyToOutput(yytext,(int)yyleng);
337 <Scan>"\"" { /* start of a string */
338 copyToOutput(yytext,(int)yyleng);
339 g_stringContext = YY_START;
343 copyToOutput(yytext,(int)yyleng);
344 g_charContext = YY_START;
345 if (g_lang!=SrcLangExt_VHDL)
350 <Scan>\n { /* new line */
351 copyToOutput(yytext,(int)yyleng);
353 <Scan>"//!"/.*\n[ \t]*"//"[\/!][^\/] | /* start C++ style special comment block */
354 <Scan>("///"[/]*)/[^/].*\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */
357 REJECT; // bail out if we do not need to convert
364 while (i<(int)yyleng && yytext[i]=='/') i++;
366 g_blockHeadCol=g_col;
367 copyToOutput("/**",3);
368 replaceAliases(yytext+i);
369 g_inSpecialComment=TRUE;
371 g_readLineCtx=SComment;
375 <Scan>"//##Documentation".*/\n { /* Start of Rational Rose ANSI C++ comment block */
376 if (g_mlBrief) REJECT;
377 int i=17; //=strlen("//##Documentation");
378 g_blockHeadCol=g_col;
379 copyToOutput("/**",3);
380 replaceAliases(yytext+i);
381 g_inRoseComment=TRUE;
384 <Scan>"//"[!\/]/.*\n[ \t]*"//"[|\/][ \t]*[@\\]"}" { // next line contains an end marker, see bug 752712
385 g_inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
386 copyToOutput(yytext,(int)yyleng);
387 g_readLineCtx=YY_START;
390 <Scan>"//"/.*\n { /* one line C++ comment */
391 g_inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
392 copyToOutput(yytext,(int)yyleng);
393 g_readLineCtx=YY_START;
396 <Scan>"/**/" { /* avoid matching next rule for empty C comment, see bug 711723 */
397 copyToOutput(yytext,(int)yyleng);
399 <Scan>"/*"[*!]? { /* start of a C comment */
400 g_specialComment=(int)yyleng==3;
402 g_commentStack.clear(); /* to be on the save side */
403 copyToOutput(yytext,(int)yyleng);
405 g_commentStack.push(new CommentCtx(g_lineNr));
408 if (g_lang!=SrcLangExt_Python)
414 copyToOutput(yytext,(int)yyleng);
416 g_commentStack.clear(); /* to be on the save side */
418 g_commentStack.push(new CommentCtx(g_lineNr));
422 if (g_lang!=SrcLangExt_VHDL)
428 copyToOutput(yytext,(int)yyleng);
430 g_commentStack.clear(); /* to be on the save side */
432 g_commentStack.push(new CommentCtx(g_lineNr));
436 if (g_lang!=SrcLangExt_Fortran)
442 copyToOutput(yytext,(int)yyleng);
444 g_commentStack.clear(); /* to be on the save side */
446 g_commentStack.push(new CommentCtx(g_lineNr));
449 <CComment>"{@code"/[ \t\n] {
450 copyToOutput("@code",5);
451 g_lastCommentContext = YY_START;
453 g_blockName=&yytext[1];
456 <CComment,ReadLine>[\\@]("dot"|"code"|"msc"|"startuml")/[^a-z_A-Z0-9] { /* start of a verbatim block */
457 copyToOutput(yytext,(int)yyleng);
458 g_lastCommentContext = YY_START;
460 if (qstrcmp(&yytext[1],"startuml")==0)
466 g_blockName=&yytext[1];
470 <CComment,ReadLine>[\\@]("f$"|"f["|"f{") {
471 copyToOutput(yytext,(int)yyleng);
472 g_blockName=&yytext[1];
473 if (g_blockName.at(1)=='[')
475 g_blockName.at(1)=']';
477 else if (g_blockName.at(1)=='{')
479 g_blockName.at(1)='}';
481 g_lastCommentContext = YY_START;
484 <CComment,ReadLine>[\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"docbookonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */
485 copyToOutput(yytext,(int)yyleng);
486 g_blockName=&yytext[1];
487 g_lastCommentContext = YY_START;
490 <Scan>. { /* any ather character */
491 copyToOutput(yytext,(int)yyleng);
493 <Verbatim>[\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}") { /* end of verbatim block */
494 copyToOutput(yytext,(int)yyleng);
495 if (&yytext[1]==g_blockName) // end of formula
497 BEGIN(g_lastCommentContext);
499 else if (&yytext[4]==g_blockName)
501 BEGIN(g_lastCommentContext);
512 copyToOutput(yytext,(int)yyleng);
525 copyToOutput(" @endcode ",10);
526 BEGIN(g_lastCommentContext);
530 copyToOutput(yytext,(int)yyleng);
534 <VerbatimCode>[\\@]("enddot"|"endcode"|"endmsc"|"enduml") { /* end of verbatim block */
535 copyToOutput(yytext,(int)yyleng);
536 if (&yytext[4]==g_blockName)
538 BEGIN(g_lastCommentContext);
541 <VerbatimCode>^[ \t]*"//"[\!\/]? { /* skip leading comments */
542 if (!g_inSpecialComment)
544 copyToOutput(yytext,(int)yyleng);
549 while (yytext[l]==' ' || yytext[l]=='\t')
553 copyToOutput(yytext,l);
554 if (yyleng-l==3) // ends with //! or ///
556 copyToOutput(" * ",3);
560 copyToOutput("//",2);
564 <Verbatim,VerbatimCode>[^@\/\\\n{}]* { /* any character not a backslash or new line or } */
565 copyToOutput(yytext,(int)yyleng);
567 <Verbatim,VerbatimCode>\n { /* new line in verbatim block */
568 copyToOutput(yytext,(int)yyleng);
570 <Verbatim>^[ \t]*"///" {
571 if (g_blockName=="dot" || g_blockName=="msc" || g_blockName=="uml" || g_blockName.at(0)=='f')
573 // see bug 487871, strip /// from dot images and formulas.
575 while (yytext[l]==' ' || yytext[l]=='\t')
579 copyToOutput(yytext,l);
582 else // even slashes are verbatim (e.g. \verbatim, \code)
587 <Verbatim,VerbatimCode>. { /* any other character */
588 copyToOutput(yytext,(int)yyleng);
590 <SkipString>\\. { /* escaped character in string */
591 copyToOutput(yytext,(int)yyleng);
593 <SkipString>"\"" { /* end of string */
594 copyToOutput(yytext,(int)yyleng);
595 BEGIN(g_stringContext);
597 <SkipString>. { /* any other string character */
598 copyToOutput(yytext,(int)yyleng);
600 <SkipString>\n { /* new line inside string (illegal for some compilers) */
601 copyToOutput(yytext,(int)yyleng);
603 <SkipChar>\\. { /* escaped character */
604 copyToOutput(yytext,(int)yyleng);
606 <SkipChar>' { /* end of character literal */
607 copyToOutput(yytext,(int)yyleng);
608 BEGIN(g_charContext);
610 <SkipChar>. { /* any other string character */
611 copyToOutput(yytext,(int)yyleng);
613 <SkipChar>\n { /* new line character */
614 copyToOutput(yytext,(int)yyleng);
617 <CComment>[^\\!@*\n{\"\/]* { /* anything that is not a '*' or command */
618 copyToOutput(yytext,(int)yyleng);
620 <CComment>"*"+[^*/\\@\n{\"]* { /* stars without slashes */
621 copyToOutput(yytext,(int)yyleng);
623 <CComment>"\"\"\"" { /* end of Python docstring */
624 if (g_lang!=SrcLangExt_Python)
630 g_pythonDocString = FALSE;
631 copyToOutput(yytext,(int)yyleng);
635 <CComment>\n { /* new line in comment */
636 copyToOutput(yytext,(int)yyleng);
637 /* in case of Fortran always end of comment */
638 if (g_lang==SrcLangExt_Fortran)
643 <CComment>"/"+"*" { /* nested C comment */
645 g_commentStack.push(new CommentCtx(g_lineNr));
646 copyToOutput(yytext,(int)yyleng);
648 <CComment>"*"+"/" { /* end of C comment */
649 if (g_lang==SrcLangExt_Python)
655 copyToOutput(yytext,(int)yyleng);
656 if (g_nestingCount<=0)
663 delete g_commentStack.pop();
667 <CComment>"\n"/[ \t]*[^#] { /* end of Python comment */
668 if (g_lang!=SrcLangExt_Python || g_pythonDocString)
674 copyToOutput(yytext,(int)yyleng);
678 <CComment>"\n"/[ \t]*[^\-] { /* end of VHDL comment */
679 if (g_lang!=SrcLangExt_VHDL)
685 copyToOutput(yytext,(int)yyleng);
689 /* removed for bug 674842 (bug was introduced in rev 768)
691 g_charContext = YY_START;
692 copyToOutput(yytext,(int)yyleng);
696 g_stringContext = YY_START;
697 copyToOutput(yytext,(int)yyleng);
702 copyToOutput(yytext,(int)yyleng);
704 <SComment>^[ \t]*"///"[\/]*/\n {
707 <SComment>\n[ \t]*"///"[\/]*/\n {
710 <SComment>^[ \t]*"///"[^\/\n]/.*\n {
712 g_readLineCtx=YY_START;
715 <SComment>\n[ \t]*"//"[\/!]("<")?[ \t]*[\\@]"}".*\n {
716 /* See Bug 752712: end the multiline comment when finding a @} or \} command */
717 copyToOutput(" */",3);
718 copyToOutput(yytext,(int)yyleng);
719 g_inSpecialComment=FALSE;
720 g_inRoseComment=FALSE;
723 <SComment>\n[ \t]*"///"[^\/\n]/.*\n {
725 g_readLineCtx=YY_START;
728 <SComment>^[ \t]*"//!" | // just //!
729 <SComment>^[ \t]*"//!<"/.*\n | // or //!< something
730 <SComment>^[ \t]*"//!"[^<]/.*\n { // or //!something
732 g_readLineCtx=YY_START;
735 <SComment>\n[ \t]*"//!" |
736 <SComment>\n[ \t]*"//!<"/.*\n |
737 <SComment>\n[ \t]*"//!"[^<\n]/.*\n {
739 g_readLineCtx=YY_START;
742 <SComment>^[ \t]*"//##"/.*\n {
743 if (!g_inRoseComment)
750 g_readLineCtx=YY_START;
754 <SComment>\n[ \t]*"//##"/.*\n {
755 if (!g_inRoseComment)
762 g_readLineCtx=YY_START;
766 <SComment>\n { /* end of special comment */
767 copyToOutput(" */",3);
768 copyToOutput(yytext,(int)yyleng);
769 g_inSpecialComment=FALSE;
770 g_inRoseComment=FALSE;
773 <ReadLine>[^\\@\n]*/\n {
774 copyToOutput(yytext,(int)yyleng);
775 BEGIN(g_readLineCtx);
777 <CComment,ReadLine>[\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
778 copyToOutput(yytext,(int)yyleng);
780 <CComment,ReadLine>[\\@]"cond"/[^a-z_A-Z0-9] { // conditional section
781 g_condCtx = YY_START;
784 <CComment,ReadLine>[\\@]"endcond"/[^a-z_A-Z0-9] { // end of conditional section
787 if (YY_START==CComment && oldSkip && !g_skip)
789 //printf("** Adding start of comment!\n");
790 if (g_lang!=SrcLangExt_Python &&
791 g_lang!=SrcLangExt_VHDL &&
792 g_lang!=SrcLangExt_Markdown &&
793 g_lang!=SrcLangExt_Fortran)
797 if (g_specialComment)
804 <CondLine>[!()&| \ta-z_A-Z0-9.\-]+ {
806 startCondSection(yytext);
807 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
810 if (g_lang!=SrcLangExt_Python &&
811 g_lang!=SrcLangExt_VHDL &&
812 g_lang!=SrcLangExt_Markdown &&
813 g_lang!=SrcLangExt_Fortran)
819 if (g_readLineCtx==SComment)
829 <CComment,ReadLine>[\\@]"cond"[ \t\r]*/\n |
830 <CondLine>. { // forgot section id?
831 if (YY_START!=CondLine) g_condCtx=YY_START;
833 startCondSection(" "); // fake section id causing the section to be hidden unconditionally
834 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
837 //printf("** Adding terminator for comment!\n");
838 if (g_lang!=SrcLangExt_Python &&
839 g_lang!=SrcLangExt_VHDL)
845 if (*yytext=='\n') g_lineNr++;
846 if (g_readLineCtx==SComment)
855 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments
856 replaceAliases(yytext);
858 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments
859 g_lastBlockContext=YY_START;
861 g_aliasString=yytext;
863 BEGIN( ReadAliasArgs );
865 <ReadAliasArgs>^[ \t]*"//"[/!]/[^\n]+ { // skip leading special comments (see bug 618079)
867 <ReadAliasArgs>"*/" { // oops, end of comment in the middle of an alias?
868 if (g_lang==SrcLangExt_Python)
872 else // abort the alias, restart scanning
874 copyToOutput(g_aliasString,g_aliasString.length());
875 copyToOutput(yytext,(int)yyleng);
879 <ReadAliasArgs>[^{}\n\\\*]+ {
880 g_aliasString+=yytext;
883 <ReadAliasArgs>"\\" {
884 if (g_lastEscaped) g_lastEscaped=FALSE;
885 else g_lastEscaped=TRUE;
886 g_aliasString+=yytext;
889 g_aliasString+=yytext;
894 g_aliasString+=yytext;
895 if (!g_lastEscaped) g_blockCount++;
899 g_aliasString+=yytext;
900 if (!g_lastEscaped) g_blockCount--;
903 replaceAliases(g_aliasString);
904 BEGIN( g_lastBlockContext );
909 g_aliasString+=yytext;
913 copyToOutput(yytext,(int)yyleng);
918 void replaceComment(int offset)
920 if (g_mlBrief || g_skip)
922 copyToOutput(yytext,(int)yyleng);
926 //printf("replaceComment(%s)\n",yytext);
927 int i=computeIndent(&yytext[offset]);
928 if (i==g_blockHeadCol)
930 replaceCommentMarker(yytext,(int)yyleng);
934 copyToOutput(" */",3);
935 int i;for (i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
936 g_inSpecialComment=FALSE;
942 // simplified way to know if this is fixed form
943 // duplicate in fortrancode.l
944 static bool recognizeFixedForm(const char* contents)
952 switch(contents[i]) {
964 if(column==1) return TRUE;
968 if(column>1 && column<7) return FALSE;
973 if(column==7) return TRUE;
981 /*! This function does three things:
982 * -# It converts multi-line C++ style comment blocks (that are aligned)
983 * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO).
984 * -# It replaces aliases with their definition (see ALIASES)
985 * -# It handles conditional sections (cond...endcond blocks)
987 void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName)
989 //printf("convertCppComments(%s)\n",fileName);
994 g_mlBrief = Config_getBool(MULTILINE_CPP_IS_BRIEF);
996 g_fileName = fileName;
997 g_lang = getLanguageFromFileName(fileName);
998 g_pythonDocString = FALSE;
1000 g_condStack.clear();
1001 g_condStack.setAutoDelete(TRUE);
1002 g_commentStack.clear();
1003 g_commentStack.setAutoDelete(TRUE);
1005 printlex(yy_flex_debug, TRUE, __FILE__, fileName);
1006 isFixedForm = FALSE;
1007 if (g_lang==SrcLangExt_Fortran)
1009 isFixedForm = recognizeFixedForm(inBuf->data());
1012 if (g_lang==SrcLangExt_Markdown)
1016 g_commentStack.push(new CommentCtx(g_lineNr));
1023 while (!g_condStack.isEmpty())
1025 CondCtx *ctx = g_condStack.pop();
1026 QCString sectionInfo = " ";
1027 if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label %s ",ctx->sectionId.data());
1028 warn(g_fileName,ctx->lineNr,"Conditional section%sdoes not have "
1029 "a corresponding \\endcond command within this file.",sectionInfo.data());
1031 if (g_nestingCount>0 && g_lang!=SrcLangExt_Markdown)
1033 QCString tmp= "(probable line reference: ";
1035 while (!g_commentStack.isEmpty())
1037 CommentCtx *ctx = g_commentStack.pop();
1038 if (!first) tmp += ", ";
1039 tmp += QCString().setNum(ctx->lineNr);
1044 warn(g_fileName,g_lineNr,"Reached end of file while still inside a (nested) comment. "
1045 "Nesting level %d %s",g_nestingCount+1,tmp.data()); // add one for "normal" expected end of comment
1047 g_commentStack.clear();
1049 if (Debug::isFlagSet(Debug::CommentCnv))
1051 g_outBuf->at(g_outBuf->curPos())='\0';
1052 msg("-------------\n%s\n-------------\n",g_outBuf->data());
1054 printlex(yy_flex_debug, FALSE, __FILE__, fileName);
1058 //----------------------------------------------------------------------------
1059 #if !defined(YY_FLEX_SUBMINOR_VERSION)
1060 extern "C" { // some bogus code to keep the compiler happy
1061 void commentcnvYYdummy() { yy_flex_realloc(0,0); }