/***************************************************************************** * * $Id: commentcnv.l,v 1.80 2001/03/19 19:27:41 root Exp $ * * Copyright (C) 1997-2011 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. * */ %{ #define YY_NEVER_INTERACTIVE 1 #include #include #include #include #include #include #include "bufstr.h" #include "debug.h" #include "message.h" #include "config.h" #include "doxygen.h" #include "util.h" #include #define ADDCHAR(c) g_outBuf->addChar(c) #define ADDARRAY(a,s) g_outBuf->addArray(a,s) struct CondCtx { CondCtx(int line,QCString id,bool b) : lineNr(line),sectionId(id), skip(b) {} int lineNr; QCString sectionId; bool skip; }; static BufStr * g_inBuf; static BufStr * g_outBuf; static int g_inBufPos; static int g_col; static int g_blockHeadCol; static bool g_mlBrief; static int g_readLineCtx; static bool g_skip; static QCString g_fileName; static int g_lineNr; static int g_condCtx; static QStack g_condStack; static QCString g_blockName; static int g_lastCommentContext; static bool g_inSpecialComment; static bool g_inRoseComment; static int g_javaBlock; static bool g_specialComment; static QCString g_aliasString; static int g_blockCount; static bool g_lastEscaped; static int g_lastBlockContext; static bool g_pythonDocString; static SrcLangExt g_lang; static void replaceCommentMarker(const char *s,int len) { const char *p=s; char c; // copy blanks while ((c=*p) && (c==' ' || c=='\t' || c=='\n')) { ADDCHAR(c); g_lineNr += c=='\n'; p++; } // replace start of comment marker by spaces while ((c=*p) && (c=='/' || c=='!' || c=='#')) { ADDCHAR(' '); p++; if (*p=='<') // comment-after-item marker { ADDCHAR(' '); p++; } if (c=='!') // end after first ! { break; } } // copy comment line to output ADDARRAY(p,len-(p-s)); } static inline int computeIndent(const char *s) { int col=0; static int tabSize=Config_getInt("TAB_SIZE"); const char *p=s; char c; while ((c=*p++)) { if (c==' ') col++; else if (c=='\t') col+=tabSize-(col%tabSize); else break; } return col; } static inline void copyToOutput(const char *s,int len) { int i; if (g_skip) // only add newlines. { for (i=0;i skip %d\n",g_lineNr); g_lineNr++; } } } else if (len>0) { ADDARRAY(s,len); static int tabSize=Config_getInt("TAB_SIZE"); for (i=0;i copy %d\n",g_lineNr); g_lineNr++; break; case '\t': g_col+=tabSize-(g_col%tabSize); break; default: g_col++; break; } } } } static void startCondSection(const char *sectId) { g_condStack.push(new CondCtx(g_lineNr,sectId,g_skip)); if (Config_getList("ENABLED_SECTIONS").find(sectId)!=-1) { //printf("*** Section is enabled!\n"); } else { //printf("*** Section is disabled!\n"); g_skip=TRUE; } } static void endCondSection() { if (g_condStack.isEmpty()) { warn(g_fileName,g_lineNr,"Found \\endcond command without matching \\cond"); g_skip=FALSE; } else { CondCtx *ctx = g_condStack.pop(); g_skip=ctx->skip; } } #if 0 /** remove and executes cond and endcond commands in \a s */ static QCString handleCondCmdInAliases(const QCString &s) { QCString result; //printf("handleCondCmdInAliases(%s)\n",s.data()); static QRegExp cmdPat("[\\\\@][a-z_A-Z][a-z_A-Z0-9]*"); int p=0,i,l; while ((i=cmdPat.match(s,p,&l))!=-1) { result+=s.mid(p,i-p); QCString cmd = s.mid(i+1,l-1); //printf("Found command %s\n",cmd.data()); if (cmd=="cond") { int sp=i+l,ep; const char *arg=s.data()+sp; char c; // skip spaces while ((c=*arg) && (c==' ' || c=='\t')) arg++,sp++; // read argument if (*arg=='\n') // no arg { startCondSection(" "); ep=sp; } else // get argument { ep=sp; while ((c=*arg) && isId(c)) arg++,ep++; if (ep>sp) { QCString id = s.mid(sp,ep-sp); //printf("Found conditional section id %s\n",id.data()); startCondSection(id); } else // invalid identifier { } } p=ep; } else if (cmd=="endcond") { endCondSection(); p=i+l; } else { result+=s.mid(i,l); p=i+l; } } result+=s.right(s.length()-p); return result; } #endif /** copies string \a s with length \a len to the output, while * replacing any alias commands found in the string. */ static void replaceAliases(const char *s) { QCString result = resolveAliasCmd(s); //printf("replaceAliases(%s)->'%s'\n",s,result.data()); copyToOutput(result,result.length()); } #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); static int yyread(char *buf,int max_size) { int bytesInBuf = g_inBuf->curPos()-g_inBufPos; int bytesToCopy = QMIN(max_size,bytesInBuf); memcpy(buf,g_inBuf->data()+g_inBufPos,bytesToCopy); g_inBufPos+=bytesToCopy; return bytesToCopy; } void replaceComment(int offset); %} %option noyywrap %x Scan %x SkipString %x SkipChar %x SComment %x CComment %x Verbatim %x VerbatimCode %x ReadLine %x CondLine %x ReadAliasArgs %% [^"'!\/\n\\#\\-]* { /* eat anything that is not " / or \n */ copyToOutput(yytext,yyleng); } "\"\"\""! { /* start of python long comment */ if (g_lang!=SrcLangExt_Python) { REJECT; } else { g_pythonDocString = TRUE; copyToOutput(yytext,yyleng); BEGIN(CComment); } } "!>" { if (g_lang!=SrcLangExt_F90) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(CComment); } } "\"" { /* start of a string */ copyToOutput(yytext,yyleng); BEGIN(SkipString); } ' { copyToOutput(yytext,yyleng); BEGIN(SkipChar); } \n { /* new line */ copyToOutput(yytext,yyleng); } ("//!"|"///").*/\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */ if (g_mlBrief) { REJECT; // bail out if we do not need to convert } else { int i=3; if (yytext[2]=='/') { while (i<(int)yyleng && yytext[i]=='/') i++; } g_blockHeadCol=g_col; copyToOutput("/**",3); replaceAliases(yytext+i); g_inSpecialComment=TRUE; BEGIN(SComment); } } "//##Documentation".*/\n { /* Start of Rational Rose ANSI C++ comment block */ if (g_mlBrief) REJECT; int i=17; //=strlen("//##Documentation"); g_blockHeadCol=g_col; copyToOutput("/**",3); replaceAliases(yytext+i); g_inRoseComment=TRUE; BEGIN(SComment); } "//"/.*\n { /* one line C++ comment */ copyToOutput(yytext,yyleng); g_readLineCtx=YY_START; BEGIN(ReadLine); } "/*"[*!]? { /* start of a C comment */ g_specialComment=yyleng==3; copyToOutput(yytext,yyleng); BEGIN(CComment); } "#"("#")? { if (g_lang!=SrcLangExt_Python) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(CComment); } } "--!" { if (g_lang!=SrcLangExt_VHDL) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(CComment); } } "!>" { if (g_lang!=SrcLangExt_F90) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(CComment); } } "{@code"/[ \t\n] { copyToOutput("@code",5); g_lastCommentContext = YY_START; g_javaBlock=1; g_blockName=&yytext[1]; BEGIN(VerbatimCode); } [\\@]("dot"|"code"|"msc")/[^a-z_A-Z0-9] { /* start of a verbatim block */ copyToOutput(yytext,yyleng); g_lastCommentContext = YY_START; g_javaBlock=0; g_blockName=&yytext[1]; BEGIN(VerbatimCode); } [\\@]("f$"|"f["|"f{"[a-z]*) { copyToOutput(yytext,yyleng); g_blockName=&yytext[1]; if (g_blockName.at(1)=='[') { g_blockName.at(1)=']'; } else if (g_blockName.at(1)=='{') { g_blockName.at(1)='}'; } g_lastCommentContext = YY_START; BEGIN(Verbatim); } [\\@]("verbatim"|"latexonly"|"htmlonly"|"xmlonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */ copyToOutput(yytext,yyleng); g_blockName=&yytext[1]; g_lastCommentContext = YY_START; BEGIN(Verbatim); } . { /* any other character */ copyToOutput(yytext,yyleng); } [\\@]("endverbatim"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}") { /* end of verbatim block */ copyToOutput(yytext,yyleng); if (yytext[1]=='f') // end of formula { BEGIN(g_lastCommentContext); } else if (&yytext[4]==g_blockName) { BEGIN(g_lastCommentContext); } } "{" { if (g_javaBlock==0) { REJECT; } else { g_javaBlock++; copyToOutput(yytext,yyleng); } } "}" { if (g_javaBlock==0) { REJECT; } else { g_javaBlock--; if (g_javaBlock==0) { copyToOutput(" @endcode ",10); BEGIN(g_lastCommentContext); } else { copyToOutput(yytext,yyleng); } } } [\\@]("enddot"|"endcode"|"endmsc") { /* end of verbatim block */ copyToOutput(yytext,yyleng); if (&yytext[4]==g_blockName) { BEGIN(g_lastCommentContext); } } ^[ \t]*"//"[\!\/]? { /* skip leading comments */ if (!g_inSpecialComment) { copyToOutput(yytext,yyleng); } } [^@\/\\\n{}]* { /* any character not a backslash or new line or } */ copyToOutput(yytext,yyleng); } \n { /* new line in verbatim block */ copyToOutput(yytext,yyleng); } ^[ \t]*"///" { if (g_blockName=="dot" || g_blockName=="msc" || g_blockName.at(0)=='f') { // see bug 487871, strip /// from dot images and formulas. copyToOutput(" ",3); } else // even slashes are verbatim (e.g. \verbatim, \code) { REJECT; } } . { /* any other character */ copyToOutput(yytext,yyleng); } \\. { /* escaped character in string */ copyToOutput(yytext,yyleng); } "\"" { /* end of string */ copyToOutput(yytext,yyleng); BEGIN(Scan); } . { /* any other string character */ copyToOutput(yytext,yyleng); } \n { /* new line inside string (illegal for some compilers) */ copyToOutput(yytext,yyleng); } \\. { /* escaped character */ copyToOutput(yytext,yyleng); } ' { /* end of character literal */ copyToOutput(yytext,yyleng); BEGIN(Scan); } . { /* any other string character */ copyToOutput(yytext,yyleng); } \n { /* new line character */ copyToOutput(yytext,yyleng); } [^\\!@*\n{]* { /* anything that is not a '*' or command */ copyToOutput(yytext,yyleng); } "*"+[^*/\\@\n]* { /* stars without slashes */ copyToOutput(yytext,yyleng); } "\"\"\"" { /* end of Python docstring */ if (g_lang!=SrcLangExt_Python) { REJECT; } else { g_pythonDocString = FALSE; copyToOutput(yytext,yyleng); BEGIN(Scan); } } \n { /* new line in comment */ copyToOutput(yytext,yyleng); } "*"+"/" { /* end of C comment */ if (g_lang==SrcLangExt_Python) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(Scan); } } "\n"/[ \t]*[^#] { /* end of Python comment */ if (g_lang!=SrcLangExt_Python || g_pythonDocString) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(Scan); } } "\n"/[ \t]*[^\-] { /* end of VHDL comment */ if (g_lang!=SrcLangExt_VHDL) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(Scan); } } "\n"/[ \t]*[^!] { /* end of Fortran comment */ if (g_lang!=SrcLangExt_F90) { REJECT; } else { copyToOutput(yytext,yyleng); BEGIN(Scan); } } . { copyToOutput(yytext,yyleng); } ^[ \t]*"///"[\/]*/\n { replaceComment(0); } \n[ \t]*"///"[\/]*/\n { replaceComment(1); } ^[ \t]*"///"[^\/\n]/.*\n { replaceComment(0); g_readLineCtx=YY_START; BEGIN(ReadLine); } \n[ \t]*"///"[^\/\n]/.*\n { replaceComment(1); g_readLineCtx=YY_START; BEGIN(ReadLine); } ^[ \t]*"//!" | // just //! ^[ \t]*"//!<"/.*\n | // or //!< something ^[ \t]*"//!"[^<]/.*\n { // or //!something replaceComment(0); g_readLineCtx=YY_START; BEGIN(ReadLine); } \n[ \t]*"//!" | \n[ \t]*"//!<"/.*\n | \n[ \t]*"//!"[^<\n]/.*\n { replaceComment(1); g_readLineCtx=YY_START; BEGIN(ReadLine); } ^[ \t]*"//##"/.*\n { if (!g_inRoseComment) { REJECT; } else { replaceComment(0); g_readLineCtx=YY_START; BEGIN(ReadLine); } } \n[ \t]*"//##"/.*\n { if (!g_inRoseComment) { REJECT; } else { replaceComment(1); g_readLineCtx=YY_START; BEGIN(ReadLine); } } \n { /* end of special comment */ copyToOutput(" */",3); copyToOutput(yytext,yyleng); g_inSpecialComment=FALSE; g_inRoseComment=FALSE; BEGIN(Scan); } [^\\@\n]*/\n { copyToOutput(yytext,yyleng); BEGIN(g_readLineCtx); } [\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command copyToOutput(yytext,yyleng); } [\\@]"cond"[ \t]+ { // conditional section g_condCtx = YY_START; BEGIN(CondLine); } [\\@]"endcond"/[^a-z_A-Z0-9] { // end of conditional section bool oldSkip=g_skip; endCondSection(); if (YY_START==CComment && oldSkip && !g_skip) { //printf("** Adding start of comment!\n"); if (g_lang!=SrcLangExt_Python && g_lang!=SrcLangExt_VHDL && g_lang!=SrcLangExt_F90) { ADDCHAR('/'); ADDCHAR('*'); if (g_specialComment) { ADDCHAR('*'); } } } } [a-z_A-Z][a-z_A-Z0-9.\-]* { bool oldSkip=g_skip; startCondSection(yytext); if (g_condCtx==CComment && !oldSkip && g_skip) { //printf("** Adding terminator for comment!\n"); if (g_lang!=SrcLangExt_Python && g_lang!=SrcLangExt_VHDL && g_lang!=SrcLangExt_F90) { ADDCHAR('*'); ADDCHAR('/'); } } BEGIN(g_condCtx); } [ \t]* [\\@]"cond"[ \t\r]*/\n | . { // forgot section id? if (YY_START!=CondLine) g_condCtx=YY_START; bool oldSkip=g_skip; startCondSection(" "); // fake section id causing the section to be hidden unconditionally if (g_condCtx==CComment && !oldSkip && g_skip) { //printf("** Adding terminator for comment!\n"); if (g_lang!=SrcLangExt_Python && g_lang!=SrcLangExt_VHDL) { ADDCHAR('*'); ADDCHAR('/'); } } if (*yytext=='\n') g_lineNr++; BEGIN(g_condCtx); } [\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments replaceAliases(yytext); } [\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments g_lastBlockContext=YY_START; g_blockCount=1; g_aliasString=yytext; g_lastEscaped=0; BEGIN( ReadAliasArgs ); } ^[ \t]*"//"[/!]/[^\n]+ { // skip leading special comments (see bug 618079) } [^{}\n\\\*]+ { g_aliasString+=yytext; g_lastEscaped=FALSE; } "\\" { if (g_lastEscaped) g_lastEscaped=FALSE; else g_lastEscaped=TRUE; g_aliasString+=yytext; } \n { g_aliasString+=yytext; g_lineNr++; g_lastEscaped=FALSE; } "{" { g_aliasString+=yytext; if (!g_lastEscaped) g_blockCount++; g_lastEscaped=FALSE; } "}" { g_aliasString+=yytext; if (!g_lastEscaped) g_blockCount--; if (g_blockCount==0) { replaceAliases(g_aliasString); BEGIN( g_lastBlockContext ); } g_lastEscaped=FALSE; } . { g_aliasString+=yytext; g_lastEscaped=FALSE; } . { copyToOutput(yytext,yyleng); } %% void replaceComment(int offset) { if (g_mlBrief) { copyToOutput(yytext,yyleng); } else { //printf("replaceComment(%s)\n",yytext); int i=computeIndent(&yytext[offset]); if (i==g_blockHeadCol) { replaceCommentMarker(yytext,yyleng); } else { copyToOutput(" */",3); int i;for (i=yyleng-1;i>=0;i--) unput(yytext[i]); g_inSpecialComment=FALSE; BEGIN(Scan); } } } /*! This function does three things: * -# It converts multi-line C++ style comment blocks (that are aligned) * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO). * -# It replaces aliases with their definition (see ALIASES) * -# It handles conditional sections (cond...endcond blocks) */ void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName) { //printf("convertCppComments(%s)\n",fileName); g_inBuf = inBuf; g_outBuf = outBuf; g_inBufPos = 0; g_col = 0; g_mlBrief = Config_getBool("MULTILINE_CPP_IS_BRIEF"); g_skip = FALSE; g_fileName = fileName; g_lang = getLanguageFromFileName(fileName); g_pythonDocString = FALSE; g_lineNr = 1; g_condStack.clear(); g_condStack.setAutoDelete(TRUE); BEGIN(Scan); yylex(); while (!g_condStack.isEmpty()) { CondCtx *ctx = g_condStack.pop(); QCString sectionInfo = " "; if (ctx->sectionId!=" ") sectionInfo.sprintf(" with label %s ",ctx->sectionId.data()); warn(g_fileName,ctx->lineNr,"Conditional section%sdoes not have " "a corresponding \\endcond command within this file.",sectionInfo.data()); } if (Debug::isFlagSet(Debug::CommentCnv)) { g_outBuf->at(g_outBuf->curPos())='\0'; msg("-------------\n%s\n-------------\n",g_outBuf->data()); } } //---------------------------------------------------------------------------- #if !defined(YY_FLEX_SUBMINOR_VERSION) extern "C" { // some bogus code to keep the compiler happy void commentcnvYYdummy() { yy_flex_realloc(0,0); } } #endif