1 /*****************************************************************************
3 * $Id: commentcnv.l,v 1.80 2001/03/19 19:27:41 root Exp $
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
27 #include <qtextstream.h>
41 #define ADDCHAR(c) g_outBuf->addChar(c)
42 #define ADDARRAY(a,s) g_outBuf->addArray(a,s)
46 CondCtx(int line,QCString id,bool b)
47 : lineNr(line),sectionId(id), skip(b) {}
53 static BufStr * g_inBuf;
54 static BufStr * g_outBuf;
55 static int g_inBufPos;
57 static int g_blockHeadCol;
58 static bool g_mlBrief;
59 static int g_readLineCtx;
61 static QCString g_fileName;
64 static QStack<CondCtx> g_condStack;
65 static QCString g_blockName;
66 static int g_lastCommentContext;
67 static bool g_inSpecialComment;
68 static bool g_inRoseComment;
69 static int g_stringContext;
70 static int g_charContext;
71 static int g_javaBlock;
72 static bool g_specialComment;
74 static QCString g_aliasString;
75 static int g_blockCount;
76 static bool g_lastEscaped;
77 static int g_lastBlockContext;
78 static bool g_pythonDocString;
81 static SrcLangExt g_lang;
83 static void replaceCommentMarker(const char *s,int len)
87 // copy leading blanks
88 while ((c=*p) && (c==' ' || c=='\t' || c=='\n'))
94 // replace start of comment marker by blanks and the last character by a *
96 while ((c=*p) && (c=='/' || c=='!' || c=='#'))
100 if (*p=='<') // comment-after-item marker
105 if (c=='!') // end after first !
117 if (blanks>1) ADDCHAR('*');
120 // copy comment line to output
121 ADDARRAY(p,len-(p-s));
124 static inline int computeIndent(const char *s)
127 static int tabSize=Config_getInt("TAB_SIZE");
133 else if (c=='\t') col+=tabSize-(col%tabSize);
139 static inline void copyToOutput(const char *s,int len)
142 if (g_skip) // only add newlines.
149 //fprintf(stderr,"---> skip %d\n",g_lineNr);
157 static int tabSize=Config_getInt("TAB_SIZE");
163 //fprintf(stderr,"---> copy %d\n",g_lineNr);
165 case '\t': g_col+=tabSize-(g_col%tabSize); break;
166 default: g_col++; break;
172 static void startCondSection(const char *sectId)
174 g_condStack.push(new CondCtx(g_lineNr,sectId,g_skip));
175 if (Config_getList("ENABLED_SECTIONS").find(sectId)!=-1)
177 //printf("*** Section is enabled!\n");
181 //printf("*** Section is disabled!\n");
186 static void endCondSection()
188 if (g_condStack.isEmpty())
190 warn(g_fileName,g_lineNr,"Found \\endcond command without matching \\cond");
195 CondCtx *ctx = g_condStack.pop();
200 /** copies string \a s with length \a len to the output, while
201 * replacing any alias commands found in the string.
203 static void replaceAliases(const char *s)
205 QCString result = resolveAliasCmd(s);
206 //printf("replaceAliases(%s)->'%s'\n",s,result.data());
207 copyToOutput(result,result.length());
212 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
214 static int yyread(char *buf,int max_size)
216 int bytesInBuf = g_inBuf->curPos()-g_inBufPos;
217 int bytesToCopy = QMIN(max_size,bytesInBuf);
218 memcpy(buf,g_inBuf->data()+g_inBufPos,bytesToCopy);
219 g_inBufPos+=bytesToCopy;
223 void replaceComment(int offset);
242 <Scan>[^"'!\/\n\\#\\-]* { /* eat anything that is not " / or \n */
243 copyToOutput(yytext,(int)yyleng);
245 <Scan>"\"\"\""! { /* start of python long comment */
246 if (g_lang!=SrcLangExt_Python)
252 g_pythonDocString = TRUE;
253 copyToOutput(yytext,(int)yyleng);
258 if (g_lang!=SrcLangExt_Fortran)
264 copyToOutput(yytext,(int)yyleng);
268 <Scan>"\"" { /* start of a string */
269 copyToOutput(yytext,(int)yyleng);
270 g_stringContext = YY_START;
274 copyToOutput(yytext,(int)yyleng);
275 g_charContext = YY_START;
278 <Scan>\n { /* new line */
279 copyToOutput(yytext,(int)yyleng);
281 <Scan>("//!"|"///").*/\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */
284 REJECT; // bail out if we do not need to convert
291 while (i<(int)yyleng && yytext[i]=='/') i++;
293 g_blockHeadCol=g_col;
294 copyToOutput("/**",3);
295 replaceAliases(yytext+i);
296 g_inSpecialComment=TRUE;
300 <Scan>"//##Documentation".*/\n { /* Start of Rational Rose ANSI C++ comment block */
301 if (g_mlBrief) REJECT;
302 int i=17; //=strlen("//##Documentation");
303 g_blockHeadCol=g_col;
304 copyToOutput("/**",3);
305 replaceAliases(yytext+i);
306 g_inRoseComment=TRUE;
309 <Scan>"//"/.*\n { /* one line C++ comment */
310 copyToOutput(yytext,(int)yyleng);
311 g_readLineCtx=YY_START;
314 <Scan>"/*"[*!]? { /* start of a C comment */
315 g_specialComment=(int)yyleng==3;
316 copyToOutput(yytext,(int)yyleng);
320 if (g_lang!=SrcLangExt_Python)
326 copyToOutput(yytext,(int)yyleng);
331 if (g_lang!=SrcLangExt_VHDL)
337 copyToOutput(yytext,(int)yyleng);
342 if (g_lang!=SrcLangExt_Fortran)
348 copyToOutput(yytext,(int)yyleng);
352 <CComment>"{@code"/[ \t\n] {
353 copyToOutput("@code",5);
354 g_lastCommentContext = YY_START;
356 g_blockName=&yytext[1];
359 <CComment,ReadLine>[\\@]("dot"|"code"|"msc")/[^a-z_A-Z0-9] { /* start of a verbatim block */
360 copyToOutput(yytext,(int)yyleng);
361 g_lastCommentContext = YY_START;
363 g_blockName=&yytext[1];
366 <CComment,ReadLine>[\\@]("f$"|"f["|"f{"[a-z]*) {
367 copyToOutput(yytext,(int)yyleng);
368 g_blockName=&yytext[1];
369 if (g_blockName.at(1)=='[')
371 g_blockName.at(1)=']';
373 else if (g_blockName.at(1)=='{')
375 g_blockName.at(1)='}';
377 g_lastCommentContext = YY_START;
380 <CComment,ReadLine>[\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */
381 copyToOutput(yytext,(int)yyleng);
382 g_blockName=&yytext[1];
383 g_lastCommentContext = YY_START;
386 <Scan>. { /* any other character */
387 copyToOutput(yytext,(int)yyleng);
389 <Verbatim>[\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}") { /* end of verbatim block */
390 copyToOutput(yytext,(int)yyleng);
391 if (yytext[1]=='f') // end of formula
393 BEGIN(g_lastCommentContext);
395 else if (&yytext[4]==g_blockName)
397 BEGIN(g_lastCommentContext);
408 copyToOutput(yytext,(int)yyleng);
421 copyToOutput(" @endcode ",10);
422 BEGIN(g_lastCommentContext);
426 copyToOutput(yytext,(int)yyleng);
430 <VerbatimCode>[\\@]("enddot"|"endcode"|"endmsc") { /* end of verbatim block */
431 copyToOutput(yytext,(int)yyleng);
432 if (&yytext[4]==g_blockName)
434 BEGIN(g_lastCommentContext);
437 <VerbatimCode>^[ \t]*"//"[\!\/]? { /* skip leading comments */
438 if (!g_inSpecialComment)
440 copyToOutput(yytext,(int)yyleng);
445 while (yytext[l]==' ' || yytext[l]=='\t')
449 copyToOutput(yytext,l);
450 if (yyleng-l==3) // ends with //! or ///
452 copyToOutput(" * ",3);
456 copyToOutput("//",2);
460 <Verbatim,VerbatimCode>[^@\/\\\n{}]* { /* any character not a backslash or new line or } */
461 copyToOutput(yytext,(int)yyleng);
463 <Verbatim,VerbatimCode>\n { /* new line in verbatim block */
464 copyToOutput(yytext,(int)yyleng);
466 <Verbatim>^[ \t]*"///" {
467 if (g_blockName=="dot" || g_blockName=="msc" || g_blockName.at(0)=='f')
469 // see bug 487871, strip /// from dot images and formulas.
471 while (yytext[l]==' ' || yytext[l]=='\t')
475 copyToOutput(yytext,l);
478 else // even slashes are verbatim (e.g. \verbatim, \code)
483 <Verbatim,VerbatimCode>. { /* any other character */
484 copyToOutput(yytext,(int)yyleng);
486 <SkipString>\\. { /* escaped character in string */
487 copyToOutput(yytext,(int)yyleng);
489 <SkipString>"\"" { /* end of string */
490 copyToOutput(yytext,(int)yyleng);
491 BEGIN(g_stringContext);
493 <SkipString>. { /* any other string character */
494 copyToOutput(yytext,(int)yyleng);
496 <SkipString>\n { /* new line inside string (illegal for some compilers) */
497 copyToOutput(yytext,(int)yyleng);
499 <SkipChar>\\. { /* escaped character */
500 copyToOutput(yytext,(int)yyleng);
502 <SkipChar>' { /* end of character literal */
503 copyToOutput(yytext,(int)yyleng);
504 BEGIN(g_charContext);
506 <SkipChar>. { /* any other string character */
507 copyToOutput(yytext,(int)yyleng);
509 <SkipChar>\n { /* new line character */
510 copyToOutput(yytext,(int)yyleng);
513 <CComment>[^\\!@*\n{\"]* { /* anything that is not a '*' or command */
514 copyToOutput(yytext,(int)yyleng);
516 <CComment>"*"+[^*/\\@\n{\"]* { /* stars without slashes */
517 copyToOutput(yytext,(int)yyleng);
519 <CComment>"\"\"\"" { /* end of Python docstring */
520 if (g_lang!=SrcLangExt_Python)
526 g_pythonDocString = FALSE;
527 copyToOutput(yytext,(int)yyleng);
531 <CComment>\n { /* new line in comment */
532 copyToOutput(yytext,(int)yyleng);
534 <CComment>"*"+"/" { /* end of C comment */
535 if (g_lang==SrcLangExt_Python)
541 copyToOutput(yytext,(int)yyleng);
545 <CComment>"\n"/[ \t]*[^#] { /* end of Python comment */
546 if (g_lang!=SrcLangExt_Python || g_pythonDocString)
552 copyToOutput(yytext,(int)yyleng);
556 <CComment>"\n"/[ \t]*[^\-] { /* end of VHDL comment */
557 if (g_lang!=SrcLangExt_VHDL)
563 copyToOutput(yytext,(int)yyleng);
567 <CComment>"\n"/[ \t]*[^!] { /* end of Fortran comment */
568 if (g_lang!=SrcLangExt_Fortran)
574 copyToOutput(yytext,(int)yyleng);
578 /* removed for bug 674842 (bug was introduced in rev 768)
580 g_charContext = YY_START;
581 copyToOutput(yytext,(int)yyleng);
585 g_stringContext = YY_START;
586 copyToOutput(yytext,(int)yyleng);
591 copyToOutput(yytext,(int)yyleng);
593 <SComment>^[ \t]*"///"[\/]*/\n {
596 <SComment>\n[ \t]*"///"[\/]*/\n {
599 <SComment>^[ \t]*"///"[^\/\n]/.*\n {
601 g_readLineCtx=YY_START;
604 <SComment>\n[ \t]*"///"[^\/\n]/.*\n {
606 g_readLineCtx=YY_START;
609 <SComment>^[ \t]*"//!" | // just //!
610 <SComment>^[ \t]*"//!<"/.*\n | // or //!< something
611 <SComment>^[ \t]*"//!"[^<]/.*\n { // or //!something
613 g_readLineCtx=YY_START;
616 <SComment>\n[ \t]*"//!" |
617 <SComment>\n[ \t]*"//!<"/.*\n |
618 <SComment>\n[ \t]*"//!"[^<\n]/.*\n {
620 g_readLineCtx=YY_START;
623 <SComment>^[ \t]*"//##"/.*\n {
624 if (!g_inRoseComment)
631 g_readLineCtx=YY_START;
635 <SComment>\n[ \t]*"//##"/.*\n {
636 if (!g_inRoseComment)
643 g_readLineCtx=YY_START;
647 <SComment>\n { /* end of special comment */
648 copyToOutput(" */",3);
649 copyToOutput(yytext,(int)yyleng);
650 g_inSpecialComment=FALSE;
651 g_inRoseComment=FALSE;
654 <ReadLine>[^\\@\n]*/\n {
655 copyToOutput(yytext,(int)yyleng);
656 BEGIN(g_readLineCtx);
658 <CComment,ReadLine>[\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
659 copyToOutput(yytext,(int)yyleng);
661 <CComment,ReadLine>[\\@]"cond"[ \t]+ { // conditional section
662 g_condCtx = YY_START;
665 <CComment,ReadLine>[\\@]"endcond"/[^a-z_A-Z0-9] { // end of conditional section
668 if (YY_START==CComment && oldSkip && !g_skip)
670 //printf("** Adding start of comment!\n");
671 if (g_lang!=SrcLangExt_Python &&
672 g_lang!=SrcLangExt_VHDL &&
673 g_lang!=SrcLangExt_Fortran)
677 if (g_specialComment)
684 <CondLine>[a-z_A-Z][a-z_A-Z0-9.\-]* {
686 startCondSection(yytext);
687 if (g_condCtx==CComment && !oldSkip && g_skip)
689 //printf("** Adding terminator for comment!\n");
690 if (g_lang!=SrcLangExt_Python &&
691 g_lang!=SrcLangExt_VHDL &&
692 g_lang!=SrcLangExt_Fortran)
701 <CComment,ReadLine>[\\@]"cond"[ \t\r]*/\n |
702 <CondLine>. { // forgot section id?
703 if (YY_START!=CondLine) g_condCtx=YY_START;
705 startCondSection(" "); // fake section id causing the section to be hidden unconditionally
706 if (g_condCtx==CComment && !oldSkip && g_skip)
708 //printf("** Adding terminator for comment!\n");
709 if (g_lang!=SrcLangExt_Python &&
710 g_lang!=SrcLangExt_VHDL)
716 if (*yytext=='\n') g_lineNr++;
719 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments
720 replaceAliases(yytext);
722 <CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments
723 g_lastBlockContext=YY_START;
725 g_aliasString=yytext;
727 BEGIN( ReadAliasArgs );
729 <ReadAliasArgs>^[ \t]*"//"[/!]/[^\n]+ { // skip leading special comments (see bug 618079)
731 <ReadAliasArgs>"*/" { // oops, end of comment in the middle of an alias?
732 if (g_lang==SrcLangExt_Python)
736 else // abort the alias, restart scanning
738 copyToOutput(g_aliasString,g_aliasString.length());
739 copyToOutput(yytext,(int)yyleng);
743 <ReadAliasArgs>[^{}\n\\\*]+ {
744 g_aliasString+=yytext;
747 <ReadAliasArgs>"\\" {
748 if (g_lastEscaped) g_lastEscaped=FALSE;
749 else g_lastEscaped=TRUE;
750 g_aliasString+=yytext;
753 g_aliasString+=yytext;
758 g_aliasString+=yytext;
759 if (!g_lastEscaped) g_blockCount++;
763 g_aliasString+=yytext;
764 if (!g_lastEscaped) g_blockCount--;
767 replaceAliases(g_aliasString);
768 BEGIN( g_lastBlockContext );
773 g_aliasString+=yytext;
777 copyToOutput(yytext,(int)yyleng);
782 void replaceComment(int offset)
786 copyToOutput(yytext,(int)yyleng);
790 //printf("replaceComment(%s)\n",yytext);
791 int i=computeIndent(&yytext[offset]);
792 if (i==g_blockHeadCol)
794 replaceCommentMarker(yytext,(int)yyleng);
798 copyToOutput(" */",3);
799 int i;for (i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
800 g_inSpecialComment=FALSE;
806 /*! This function does three things:
807 * -# It converts multi-line C++ style comment blocks (that are aligned)
808 * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO).
809 * -# It replaces aliases with their definition (see ALIASES)
810 * -# It handles conditional sections (cond...endcond blocks)
812 void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName)
814 //printf("convertCppComments(%s)\n",fileName);
819 g_mlBrief = Config_getBool("MULTILINE_CPP_IS_BRIEF");
821 g_fileName = fileName;
822 g_lang = getLanguageFromFileName(fileName);
823 g_pythonDocString = FALSE;
826 g_condStack.setAutoDelete(TRUE);
827 if (g_lang==SrcLangExt_Markdown)
836 while (!g_condStack.isEmpty())
838 CondCtx *ctx = g_condStack.pop();
839 QCString sectionInfo = " ";
840 if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label %s ",ctx->sectionId.data());
841 warn(g_fileName,ctx->lineNr,"Conditional section%sdoes not have "
842 "a corresponding \\endcond command within this file.",sectionInfo.data());
844 if (Debug::isFlagSet(Debug::CommentCnv))
846 g_outBuf->at(g_outBuf->curPos())='\0';
847 msg("-------------\n%s\n-------------\n",g_outBuf->data());
852 //----------------------------------------------------------------------------
853 #if !defined(YY_FLEX_SUBMINOR_VERSION)
854 extern "C" { // some bogus code to keep the compiler happy
855 void commentcnvYYdummy() { yy_flex_realloc(0,0); }