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
26 #include <qtextstream.h>
35 #include "condparser.h"
40 #define YY_NO_UNISTD_H 1
42 #define ADDCHAR(c) g_outBuf->addChar(c)
43 #define ADDARRAY(a,s) g_outBuf->addArray(a,s)
47 CondCtx(int line,QCString id,bool b)
48 : 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 QStack<CommentCtx> g_commentStack;
74 static QCString g_blockName;
75 static int g_lastCommentContext;
76 static bool g_inSpecialComment;
77 static bool g_inRoseComment;
78 static int g_stringContext;
79 static int g_charContext;
80 static int g_javaBlock;
81 static bool g_specialComment;
83 static QCString g_aliasString;
84 static int g_blockCount;
85 static bool g_lastEscaped;
86 static int g_lastBlockContext;
87 static bool g_pythonDocString;
88 static int g_nestingCount;
90 static SrcLangExt g_lang;
91 static bool isFixedForm; // For Fortran
93 static void replaceCommentMarker(const char *s,int len)
97 // copy leading blanks
98 while ((c=*p) && (c==' ' || c=='\t' || c=='\n'))
104 // replace start of comment marker by blanks and the last character by a *
106 while ((c=*p) && (c=='/' || c=='!' || c=='#'))
110 if (*p=='<') // comment-after-item marker
115 if (c=='!') // end after first !
127 if (blanks>1) ADDCHAR('*');
130 // copy comment line to output
131 ADDARRAY(p,len-(int)(p-s));
134 static inline int computeIndent(const char *s)
137 static int tabSize=Config_getInt(TAB_SIZE);
143 else if (c=='\t') col+=tabSize-(col%tabSize);
149 static inline void copyToOutput(const char *s,int len)
152 if (g_skip) // only add newlines.
159 //fprintf(stderr,"---> skip %d\n",g_lineNr);
167 static int tabSize=Config_getInt(TAB_SIZE);
173 //fprintf(stderr,"---> copy %d\n",g_lineNr);
175 case '\t': g_col+=tabSize-(g_col%tabSize); break;
176 default: g_col++; break;
182 static void startCondSection(const char *sectId)
184 //printf("startCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
186 bool expResult = prs.parse(g_fileName,g_lineNr,sectId);
187 g_condStack.push(new CondCtx(g_lineNr,sectId,g_skip));
188 if (!expResult) // not enabled
194 static void endCondSection()
196 if (g_condStack.isEmpty())
198 warn(g_fileName,g_lineNr,"Found \\endcond command without matching \\cond");
203 CondCtx *ctx = g_condStack.pop();
206 //printf("endCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
209 /** copies string \a s with length \a len to the output, while
210 * replacing any alias commands found in the string.
212 static void replaceAliases(const char *s)
214 QCString result = resolveAliasCmd(s);
215 //printf("replaceAliases(%s)->'%s'\n",s,result.data());
216 copyToOutput(result,result.length());
221 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
223 static int yyread(char *buf,int max_size)
225 int bytesInBuf = g_inBuf->curPos()-g_inBufPos;
226 int bytesToCopy = QMIN(max_size,bytesInBuf);
227 memcpy(buf,g_inBuf->data()+g_inBufPos,bytesToCopy);
228 g_inBufPos+=bytesToCopy;
232 void replaceComment(int offset);
251 <Scan>[^"'!\/\n\\#-,]* { /* eat anything that is not " / , or \n */
252 copyToOutput(yytext,(int)yyleng);
254 <Scan>[,] { /* eat , so we have a nice separator in long initialization lines */
255 copyToOutput(yytext,(int)yyleng);
257 <Scan>"\"\"\""! { /* start of python long comment */
258 if (g_lang!=SrcLangExt_Python)
264 g_pythonDocString = TRUE;
266 g_commentStack.clear(); /* to be on the save side */
267 copyToOutput(yytext,(int)yyleng);
269 g_commentStack.push(new CommentCtx(g_lineNr));
273 if (g_lang!=SrcLangExt_Fortran)
279 copyToOutput(yytext,(int)yyleng);
281 g_commentStack.clear(); /* to be on the save side */
283 g_commentStack.push(new CommentCtx(g_lineNr));
286 <Scan>[Cc\*][><!]/.*\n {
287 if (g_lang!=SrcLangExt_Fortran)
293 /* check for fixed format; we might have some conditional as part of multilene if like C<5 .and. & */
294 if (isFixedForm && (g_col == 0))
296 copyToOutput(yytext,(int)yyleng);
298 g_commentStack.clear(); /* to be on the save side */
300 g_commentStack.push(new CommentCtx(g_lineNr));
309 if (g_lang!=SrcLangExt_Fortran)
315 copyToOutput(yytext,(int)yyleng);
319 if (g_lang!=SrcLangExt_Fortran)
327 copyToOutput(yytext,(int)yyleng);
335 <Scan>"\"" { /* start of a string */
336 copyToOutput(yytext,(int)yyleng);
337 g_stringContext = YY_START;
341 copyToOutput(yytext,(int)yyleng);
342 g_charContext = YY_START;
343 if (g_lang!=SrcLangExt_VHDL)
348 <Scan>\n { /* new line */
349 copyToOutput(yytext,(int)yyleng);
351 <Scan>"//!"/.*\n[ \t]*"//"[\/!][^\/] | /* start C++ style special comment block */
352 <Scan>("///"[/]*)/[^/].*\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */
355 REJECT; // bail out if we do not need to convert
362 while (i<(int)yyleng && yytext[i]=='/') i++;
364 g_blockHeadCol=g_col;
365 copyToOutput("/**",3);
366 replaceAliases(yytext+i);
367 g_inSpecialComment=TRUE;
369 g_readLineCtx=SComment;
373 <Scan>"//##Documentation".*/\n { /* Start of Rational Rose ANSI C++ comment block */
374 if (g_mlBrief) REJECT;
375 int i=17; //=strlen("//##Documentation");
376 g_blockHeadCol=g_col;
377 copyToOutput("/**",3);
378 replaceAliases(yytext+i);
379 g_inRoseComment=TRUE;
382 <Scan>"//"[!\/]/.*\n[ \t]*"//"[|\/][ \t]*[@\\]"}" { // next line contains an end marker, see bug 752712
383 g_inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
384 copyToOutput(yytext,(int)yyleng);
385 g_readLineCtx=YY_START;
388 <Scan>"//"/.*\n { /* one line C++ comment */
389 g_inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
390 copyToOutput(yytext,(int)yyleng);
391 g_readLineCtx=YY_START;
394 <Scan>"/**/" { /* avoid matching next rule for empty C comment, see bug 711723 */
395 copyToOutput(yytext,(int)yyleng);
397 <Scan>"/*"[*!]? { /* start of a C comment */
398 g_specialComment=(int)yyleng==3;
400 g_commentStack.clear(); /* to be on the save side */
401 copyToOutput(yytext,(int)yyleng);
403 g_commentStack.push(new CommentCtx(g_lineNr));
406 if (g_lang!=SrcLangExt_Python)
412 copyToOutput(yytext,(int)yyleng);
414 g_commentStack.clear(); /* to be on the save side */
416 g_commentStack.push(new CommentCtx(g_lineNr));
420 if (g_lang!=SrcLangExt_VHDL)
426 copyToOutput(yytext,(int)yyleng);
428 g_commentStack.clear(); /* to be on the save side */
430 g_commentStack.push(new CommentCtx(g_lineNr));
434 if (g_lang!=SrcLangExt_Fortran)
440 copyToOutput(yytext,(int)yyleng);
442 g_commentStack.clear(); /* to be on the save side */
444 g_commentStack.push(new CommentCtx(g_lineNr));
447 <CComment>"{@code"/[ \t\n] {
448 copyToOutput("@code",5);
449 g_lastCommentContext = YY_START;
451 g_blockName=&yytext[1];
454 <CComment,ReadLine>[\\@]("dot"|"code"|"msc"|"startuml")/[^a-z_A-Z0-9] { /* start of a verbatim block */
455 copyToOutput(yytext,(int)yyleng);
456 g_lastCommentContext = YY_START;
458 if (qstrcmp(&yytext[1],"startuml")==0)
464 g_blockName=&yytext[1];
468 <CComment,ReadLine>[\\@]("f$"|"f["|"f{") {
469 copyToOutput(yytext,(int)yyleng);
470 g_blockName=&yytext[1];
471 if (g_blockName.at(1)=='[')
473 g_blockName.at(1)=']';
475 else if (g_blockName.at(1)=='{')
477 g_blockName.at(1)='}';
479 g_lastCommentContext = YY_START;
482 <CComment,ReadLine>[\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"docbookonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */
483 copyToOutput(yytext,(int)yyleng);
484 g_blockName=&yytext[1];
485 g_lastCommentContext = YY_START;
488 <Scan>. { /* any ather character */
489 copyToOutput(yytext,(int)yyleng);
491 <Verbatim>[\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}") { /* end of verbatim block */
492 copyToOutput(yytext,(int)yyleng);
493 if (&yytext[1]==g_blockName) // end of formula
495 BEGIN(g_lastCommentContext);
497 else if (&yytext[4]==g_blockName)
499 BEGIN(g_lastCommentContext);
510 copyToOutput(yytext,(int)yyleng);
523 copyToOutput(" @endcode ",10);
524 BEGIN(g_lastCommentContext);
528 copyToOutput(yytext,(int)yyleng);
532 <VerbatimCode>[\\@]("enddot"|"endcode"|"endmsc"|"enduml") { /* end of verbatim block */
533 copyToOutput(yytext,(int)yyleng);
534 if (&yytext[4]==g_blockName)
536 BEGIN(g_lastCommentContext);
539 <VerbatimCode>^[ \t]*"//"[\!\/]? { /* skip leading comments */
540 if (!g_inSpecialComment)
542 copyToOutput(yytext,(int)yyleng);
547 while (yytext[l]==' ' || yytext[l]=='\t')
551 copyToOutput(yytext,l);
552 if (yyleng-l==3) // ends with //! or ///
554 copyToOutput(" * ",3);
558 copyToOutput("//",2);
562 <Verbatim,VerbatimCode>[^@\/\\\n{}]* { /* any character not a backslash or new line or } */
563 copyToOutput(yytext,(int)yyleng);
565 <Verbatim,VerbatimCode>\n { /* new line in verbatim block */
566 copyToOutput(yytext,(int)yyleng);
568 <Verbatim>^[ \t]*"///" {
569 if (g_blockName=="dot" || g_blockName=="msc" || g_blockName=="uml" || g_blockName.at(0)=='f')
571 // see bug 487871, strip /// from dot images and formulas.
573 while (yytext[l]==' ' || yytext[l]=='\t')
577 copyToOutput(yytext,l);
580 else // even slashes are verbatim (e.g. \verbatim, \code)
585 <Verbatim,VerbatimCode>. { /* any other character */
586 copyToOutput(yytext,(int)yyleng);
588 <SkipString>\\. { /* escaped character in string */
589 copyToOutput(yytext,(int)yyleng);
591 <SkipString>"\"" { /* end of string */
592 copyToOutput(yytext,(int)yyleng);
593 BEGIN(g_stringContext);
595 <SkipString>. { /* any other string character */
596 copyToOutput(yytext,(int)yyleng);
598 <SkipString>\n { /* new line inside string (illegal for some compilers) */
599 copyToOutput(yytext,(int)yyleng);
601 <SkipChar>\\. { /* escaped character */
602 copyToOutput(yytext,(int)yyleng);
604 <SkipChar>' { /* end of character literal */
605 copyToOutput(yytext,(int)yyleng);
606 BEGIN(g_charContext);
608 <SkipChar>. { /* any other string character */
609 copyToOutput(yytext,(int)yyleng);
611 <SkipChar>\n { /* new line character */
612 copyToOutput(yytext,(int)yyleng);
615 <CComment>[^\\!@*\n{\"\/]* { /* anything that is not a '*' or command */
616 copyToOutput(yytext,(int)yyleng);
618 <CComment>"*"+[^*/\\@\n{\"]* { /* stars without slashes */
619 copyToOutput(yytext,(int)yyleng);
621 <CComment>"\"\"\"" { /* end of Python docstring */
622 if (g_lang!=SrcLangExt_Python)
628 g_pythonDocString = FALSE;
629 copyToOutput(yytext,(int)yyleng);
633 <CComment>\n { /* new line in comment */
634 copyToOutput(yytext,(int)yyleng);
635 /* in case of Fortran always end of comment */
636 if (g_lang==SrcLangExt_Fortran)
641 <CComment>"/"+"*" { /* nested C comment */
643 g_commentStack.push(new CommentCtx(g_lineNr));
644 copyToOutput(yytext,(int)yyleng);
646 <CComment>"*"+"/" { /* end of C comment */
647 if (g_lang==SrcLangExt_Python)
653 copyToOutput(yytext,(int)yyleng);
654 if (g_nestingCount<=0)
661 delete g_commentStack.pop();
665 <CComment>"\n"/[ \t]*[^#] { /* end of Python comment */
666 if (g_lang!=SrcLangExt_Python || g_pythonDocString)
672 copyToOutput(yytext,(int)yyleng);
676 <CComment>"\n"/[ \t]*[^\-] { /* end of VHDL comment */
677 if (g_lang!=SrcLangExt_VHDL)
683 copyToOutput(yytext,(int)yyleng);
687 /* removed for bug 674842 (bug was introduced in rev 768)
689 g_charContext = YY_START;
690 copyToOutput(yytext,(int)yyleng);
694 g_stringContext = YY_START;
695 copyToOutput(yytext,(int)yyleng);
700 copyToOutput(yytext,(int)yyleng);
702 <SComment>^[ \t]*"///"[\/]*/\n {
705 <SComment>\n[ \t]*"///"[\/]*/\n {
708 <SComment>^[ \t]*"///"[^\/\n]/.*\n {
710 g_readLineCtx=YY_START;
713 <SComment>\n[ \t]*"//"[\/!]("<")?[ \t]*[\\@]"}".*\n {
714 /* See Bug 752712: end the multiline comment when finding a @} or \} command */
715 copyToOutput(" */",3);
716 copyToOutput(yytext,(int)yyleng);
717 g_inSpecialComment=FALSE;
718 g_inRoseComment=FALSE;
721 <SComment>\n[ \t]*"///"[^\/\n]/.*\n {
723 g_readLineCtx=YY_START;
726 <SComment>^[ \t]*"//!" | // just //!
727 <SComment>^[ \t]*"//!<"/.*\n | // or //!< something
728 <SComment>^[ \t]*"//!"[^<]/.*\n { // or //!something
730 g_readLineCtx=YY_START;
733 <SComment>\n[ \t]*"//!" |
734 <SComment>\n[ \t]*"//!<"/.*\n |
735 <SComment>\n[ \t]*"//!"[^<\n]/.*\n {
737 g_readLineCtx=YY_START;
740 <SComment>^[ \t]*"//##"/.*\n {
741 if (!g_inRoseComment)
748 g_readLineCtx=YY_START;
752 <SComment>\n[ \t]*"//##"/.*\n {
753 if (!g_inRoseComment)
760 g_readLineCtx=YY_START;
764 <SComment>\n { /* end of special comment */
765 copyToOutput(" */",3);
766 copyToOutput(yytext,(int)yyleng);
767 g_inSpecialComment=FALSE;
768 g_inRoseComment=FALSE;
771 <ReadLine>[^\\@\n]*/\n {
772 copyToOutput(yytext,(int)yyleng);
773 BEGIN(g_readLineCtx);
775 <CComment,ReadLine>[\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
776 copyToOutput(yytext,(int)yyleng);
778 <CComment,ReadLine>[\\@]"cond"/[^a-z_A-Z0-9] { // conditional section
779 g_condCtx = YY_START;
782 <CComment,ReadLine>[\\@]"endcond"/[^a-z_A-Z0-9] { // end of conditional section
785 if (YY_START==CComment && oldSkip && !g_skip)
787 //printf("** Adding start of comment!\n");
788 if (g_lang!=SrcLangExt_Python &&
789 g_lang!=SrcLangExt_VHDL &&
790 g_lang!=SrcLangExt_Markdown &&
791 g_lang!=SrcLangExt_Fortran)
795 if (g_specialComment)
802 <CondLine>[!()&| \ta-z_A-Z0-9.\-]+ {
804 startCondSection(yytext);
805 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
808 if (g_lang!=SrcLangExt_Python &&
809 g_lang!=SrcLangExt_VHDL &&
810 g_lang!=SrcLangExt_Markdown &&
811 g_lang!=SrcLangExt_Fortran)
817 if (g_readLineCtx==SComment)
827 <CComment,ReadLine>[\\@]"cond"[ \t\r]*/\n |
828 <CondLine>. { // forgot section id?
829 if (YY_START!=CondLine) g_condCtx=YY_START;
831 startCondSection(" "); // fake section id causing the section to be hidden unconditionally
832 if ((g_condCtx==CComment || g_readLineCtx==SComment) &&
835 //printf("** Adding terminator for comment!\n");
836 if (g_lang!=SrcLangExt_Python &&
837 g_lang!=SrcLangExt_VHDL)
843 if (*yytext=='\n') g_lineNr++;
844 if (g_readLineCtx==SComment)
853 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments
854 replaceAliases(yytext);
856 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments
857 g_lastBlockContext=YY_START;
859 g_aliasString=yytext;
861 BEGIN( ReadAliasArgs );
863 <ReadAliasArgs>^[ \t]*"//"[/!]/[^\n]+ { // skip leading special comments (see bug 618079)
865 <ReadAliasArgs>"*/" { // oops, end of comment in the middle of an alias?
866 if (g_lang==SrcLangExt_Python)
870 else // abort the alias, restart scanning
872 copyToOutput(g_aliasString,g_aliasString.length());
873 copyToOutput(yytext,(int)yyleng);
877 <ReadAliasArgs>[^{}\n\\\*]+ {
878 g_aliasString+=yytext;
881 <ReadAliasArgs>"\\" {
882 if (g_lastEscaped) g_lastEscaped=FALSE;
883 else g_lastEscaped=TRUE;
884 g_aliasString+=yytext;
887 g_aliasString+=yytext;
892 g_aliasString+=yytext;
893 if (!g_lastEscaped) g_blockCount++;
897 g_aliasString+=yytext;
898 if (!g_lastEscaped) g_blockCount--;
901 replaceAliases(g_aliasString);
902 BEGIN( g_lastBlockContext );
907 g_aliasString+=yytext;
911 copyToOutput(yytext,(int)yyleng);
916 void replaceComment(int offset)
918 if (g_mlBrief || g_skip)
920 copyToOutput(yytext,(int)yyleng);
924 //printf("replaceComment(%s)\n",yytext);
925 int i=computeIndent(&yytext[offset]);
926 if (i==g_blockHeadCol)
928 replaceCommentMarker(yytext,(int)yyleng);
932 copyToOutput(" */",3);
933 int i;for (i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
934 g_inSpecialComment=FALSE;
940 // simplified way to know if this is fixed form
941 // duplicate in fortrancode.l
942 static bool recognizeFixedForm(const char* contents)
950 switch(contents[i]) {
962 if(column==1) return TRUE;
966 if(column>1 && column<7) return FALSE;
971 if(column==7) return TRUE;
979 /*! This function does three things:
980 * -# It converts multi-line C++ style comment blocks (that are aligned)
981 * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO).
982 * -# It replaces aliases with their definition (see ALIASES)
983 * -# It handles conditional sections (cond...endcond blocks)
985 void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName)
987 //printf("convertCppComments(%s)\n",fileName);
992 g_mlBrief = Config_getBool(MULTILINE_CPP_IS_BRIEF);
994 g_fileName = fileName;
995 g_lang = getLanguageFromFileName(fileName);
996 g_pythonDocString = FALSE;
999 g_condStack.setAutoDelete(TRUE);
1000 g_commentStack.clear();
1001 g_commentStack.setAutoDelete(TRUE);
1003 printlex(yy_flex_debug, TRUE, __FILE__, fileName);
1004 isFixedForm = FALSE;
1005 if (g_lang==SrcLangExt_Fortran)
1007 isFixedForm = recognizeFixedForm(inBuf->data());
1010 if (g_lang==SrcLangExt_Markdown)
1014 g_commentStack.push(new CommentCtx(g_lineNr));
1021 while (!g_condStack.isEmpty())
1023 CondCtx *ctx = g_condStack.pop();
1024 QCString sectionInfo = " ";
1025 if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label %s ",ctx->sectionId.data());
1026 warn(g_fileName,ctx->lineNr,"Conditional section%sdoes not have "
1027 "a corresponding \\endcond command within this file.",sectionInfo.data());
1029 if (g_nestingCount>0 && g_lang!=SrcLangExt_Markdown)
1031 QCString tmp= "(probable line reference: ";
1033 while (!g_commentStack.isEmpty())
1035 CommentCtx *ctx = g_commentStack.pop();
1036 if (!first) tmp += ", ";
1037 tmp += QCString().setNum(ctx->lineNr);
1042 warn(g_fileName,g_lineNr,"Reached end of file while still inside a (nested) comment. "
1043 "Nesting level %d %s",g_nestingCount+1,tmp.data()); // add one for "normal" expected end of comment
1045 g_commentStack.clear();
1047 if (Debug::isFlagSet(Debug::CommentCnv))
1049 g_outBuf->at(g_outBuf->curPos())='\0';
1050 msg("-------------\n%s\n-------------\n",g_outBuf->data());
1052 printlex(yy_flex_debug, FALSE, __FILE__, fileName);
1056 //----------------------------------------------------------------------------
1057 #if !defined(YY_FLEX_SUBMINOR_VERSION)
1058 extern "C" { // some bogus code to keep the compiler happy
1059 void commentcnvYYdummy() { yy_flex_realloc(0,0); }