/*****************************************************************************
*
*
*
* Copyright (C) 1997-2010 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 <stdio.h>
#include <stdlib.h>
#include <qstack.h>
#include <qregexp.h>
#include <qtextstream.h>
#include "bufstr.h"
#include "debug.h"
#include "message.h"
#include "config.h"
#include "doxygen.h"
#include "util.h"
#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<CondCtx> 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 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<len;i++)
{
if (s[i]=='\n')
{
ADDCHAR('\n');
//fprintf(stderr,"---> skip %d\n",g_lineNr);
g_lineNr++;
}
}
}
else
{
ADDARRAY(s,len);
static int tabSize=Config_getInt("TAB_SIZE");
for (i=0;i<len;i++)
{
switch (s[i])
{
case '\n': g_col=0;
//fprintf(stderr,"---> 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
%%
<Scan>[^"'!\/\n\\#\\-]* { /* eat anything that is not " / or \n */
copyToOutput(yytext,yyleng);
}
<Scan>"\"\"\""! { /* start of python long comment */
if (g_lang!=SrcLangExt_Python)
{
REJECT;
}
else
{
g_pythonDocString = TRUE;
copyToOutput(yytext,yyleng);
BEGIN(CComment);
}
}
<Scan>"!>" {
if (g_lang!=SrcLangExt_F90)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(CComment);
}
}
<Scan>"\"" { /* start of a string */
copyToOutput(yytext,yyleng);
BEGIN(SkipString);
}
<Scan>' {
copyToOutput(yytext,yyleng);
BEGIN(SkipChar);
}
<Scan>\n { /* new line */
copyToOutput(yytext,yyleng);
}
<Scan>("//!"|"///").*/\n[ \t]*"//"[\/!][^\/] { /* start C++ style special comment block */
if (g_mlBrief) REJECT; // bail out if we do not need to convert
int i=3;
if (yytext[2]=='/')
{
while (i<yyleng && yytext[i]=='/') i++;
}
g_blockHeadCol=g_col;
copyToOutput("/**",3);
replaceAliases(yytext+i);
g_inSpecialComment=TRUE;
BEGIN(SComment);
}
<Scan>"//##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);
}
<Scan>"//"/.*\n { /* one line C++ comment */
copyToOutput(yytext,yyleng);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
<Scan>"/*"[*!]? { /* start of a C comment */
g_specialComment=yyleng==3;
copyToOutput(yytext,yyleng);
BEGIN(CComment);
}
<Scan>"#"("#")? {
if (g_lang!=SrcLangExt_Python)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(CComment);
}
}
<Scan>"--!" {
if (g_lang!=SrcLangExt_VHDL)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(CComment);
}
}
<CComment>"{@code"/[ \t\n] {
copyToOutput("@code",5);
g_lastCommentContext = YY_START;
g_javaBlock=1;
g_blockName=&yytext[1];
BEGIN(VerbatimCode);
}
<CComment,ReadLine>[\\@]("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);
}
<CComment,ReadLine>[\\@]("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);
}
<CComment,ReadLine>[\\@]("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);
}
<Scan>. { /* any other character */
copyToOutput(yytext,yyleng);
}
<Verbatim>[\\@]("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);
}
}
<VerbatimCode>"{" {
if (g_javaBlock==0)
{
REJECT;
}
else
{
g_javaBlock++;
copyToOutput(yytext,yyleng);
}
}
<VerbatimCode>"}" {
if (g_javaBlock==0)
{
REJECT;
}
else
{
g_javaBlock--;
if (g_javaBlock==0)
{
copyToOutput(" @endcode ",10);
BEGIN(g_lastCommentContext);
}
else
{
copyToOutput(yytext,yyleng);
}
}
}
<VerbatimCode>[\\@]("enddot"|"endcode"|"endmsc") { /* end of verbatim block */
copyToOutput(yytext,yyleng);
if (&yytext[4]==g_blockName)
{
BEGIN(g_lastCommentContext);
}
}
<VerbatimCode>^[ \t]*"//"[\!\/]? { /* skip leading comments */
if (!g_inSpecialComment)
{
copyToOutput(yytext,yyleng);
}
}
<Verbatim,VerbatimCode>[^@\/\\\n{}]* { /* any character not a backslash or new line or } */
copyToOutput(yytext,yyleng);
}
<Verbatim,VerbatimCode>\n { /* new line in verbatim block */
copyToOutput(yytext,yyleng);
}
<Verbatim,VerbatimCode>. { /* any other character */
copyToOutput(yytext,yyleng);
}
<SkipString>\\. { /* escaped character in string */
copyToOutput(yytext,yyleng);
}
<SkipString>"\"" { /* end of string */
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
<SkipString>. { /* any other string character */
copyToOutput(yytext,yyleng);
}
<SkipString>\n { /* new line inside string (illegal for some compilers) */
copyToOutput(yytext,yyleng);
}
<SkipChar>\\. { /* escaped character */
copyToOutput(yytext,yyleng);
}
<SkipChar>' { /* end of character literal */
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
<SkipChar>. { /* any other string character */
copyToOutput(yytext,yyleng);
}
<SkipChar>\n { /* new line character */
copyToOutput(yytext,yyleng);
}
<CComment>[^\\!@*\n{]* { /* anything that is not a '*' or command */
copyToOutput(yytext,yyleng);
}
<CComment>"*"+[^*/\\@\n]* { /* stars without slashes */
copyToOutput(yytext,yyleng);
}
<CComment>"\"\"\"" { /* end of Python docstring */
if (g_lang!=SrcLangExt_Python)
{
REJECT;
}
else
{
g_pythonDocString = FALSE;
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
}
<CComment>\n { /* new line in comment */
copyToOutput(yytext,yyleng);
}
<CComment>"*"+"/" { /* end of C comment */
if (g_lang==SrcLangExt_Python)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
}
<CComment>"\n"/[ \t]*[^#] { /* end of Python comment */
if (g_lang!=SrcLangExt_Python || g_pythonDocString)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
}
<CComment>"\n"/[ \t]*[^\-] { /* end of VHDL comment */
if (g_lang!=SrcLangExt_VHDL)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
}
<CComment>"\n"/[ \t]*[^!] { /* end of Fortran comment */
if (g_lang!=SrcLangExt_F90)
{
REJECT;
}
else
{
copyToOutput(yytext,yyleng);
BEGIN(Scan);
}
}
<CComment>. {
copyToOutput(yytext,yyleng);
}
<SComment>^[ \t]*"///"[\/]*/\n {
replaceComment(0);
}
<SComment>\n[ \t]*"///"[\/]*/\n {
replaceComment(1);
}
<SComment>^[ \t]*"///"[^\/\n]/.*\n {
replaceComment(0);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
<SComment>\n[ \t]*"///"[^\/\n]/.*\n {
replaceComment(1);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
<SComment>^[ \t]*"//!" | // just //!
<SComment>^[ \t]*"//!<"/.*\n | // or //!< something
<SComment>^[ \t]*"//!"[^<]/.*\n { // or //!something
replaceComment(0);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
<SComment>\n[ \t]*"//!" |
<SComment>\n[ \t]*"//!<"/.*\n |
<SComment>\n[ \t]*"//!"[^<\n]/.*\n {
replaceComment(1);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
<SComment>^[ \t]*"//##"/.*\n {
if (!g_inRoseComment)
{
REJECT;
}
else
{
replaceComment(0);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
}
<SComment>\n[ \t]*"//##"/.*\n {
if (!g_inRoseComment)
{
REJECT;
}
else
{
replaceComment(1);
g_readLineCtx=YY_START;
BEGIN(ReadLine);
}
}
<SComment>\n { /* end of special comment */
copyToOutput(" */",3);
copyToOutput(yytext,yyleng);
g_inSpecialComment=FALSE;
g_inRoseComment=FALSE;
BEGIN(Scan);
}
<ReadLine>[^\\@\n]*/\n {
copyToOutput(yytext,yyleng);
BEGIN(g_readLineCtx);
}
<CComment,ReadLine>[\\@][\\@][~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
copyToOutput(yytext,yyleng);
}
<CComment,ReadLine>[\\@]"cond"[ \t]+ { // conditional section
g_condCtx = YY_START;
BEGIN(CondLine);
}
<CComment,ReadLine>[\\@]"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)
{
ADDCHAR('/');
ADDCHAR('*');
if (g_specialComment)
{
ADDCHAR('*');
}
}
}
}
<CondLine>[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)
{
ADDCHAR('*');
ADDCHAR('/');
}
}
BEGIN(g_condCtx);
}
<CondLine>[ \t]*
<CComment,ReadLine>[\\@]"cond"[ \t\r]*/\n |
<CondLine>. { // 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);
}
<CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments
replaceAliases(yytext);
}
<CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments
g_lastBlockContext=YY_START;
g_blockCount=1;
g_aliasString=yytext;
BEGIN( ReadAliasArgs );
}
<ReadAliasArgs>[^{}\n\*]+ {
g_aliasString+=yytext;
}
<ReadAliasArgs>\n {
g_aliasString+=yytext;
g_lineNr++;
}
<ReadAliasArgs>"{" {
g_aliasString+=yytext;
g_blockCount++;
}
<ReadAliasArgs>"}" {
g_aliasString+=yytext;
g_blockCount--;
if (g_blockCount==0)
{
replaceAliases(g_aliasString);
BEGIN( g_lastBlockContext );
}
}
<ReadAliasArgs>. {
g_aliasString+=yytext;
}
<ReadLine>. {
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]);
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