From 4a93397673029e3cfd4c9e2d0501a109f858b87c Mon Sep 17 00:00:00 2001 From: Dimitri van Heesch Date: Wed, 15 Aug 2007 18:35:31 +0000 Subject: Release-1.5.3-20070815 --- INSTALL | 4 +- README | 4 +- configure | 2 +- doc/docblocks.doc | 4 +- doc/formulas.doc | 4 +- doc/index.doc | 2 +- libmd5/md5.c | 1 + src/Makefile.in | 2 +- src/classdef.cpp | 1 + src/code.l | 4 +- src/commentscan.l | 7 + src/declinfo.l | 2 +- src/defargs.h | 3 +- src/defargs.l | 9 +- src/doctokenizer.l | 7 +- src/doxygen.cpp | 59 ++- src/fortrancode.h | 35 ++ src/fortrancode.l | 959 ++++++++++++++++++++++++++++++++++ src/fortranscanner.h | 50 ++ src/fortranscanner.l | 1333 +++++++++++++++++++++++++++++++++++++++++++++++ src/htmlgen.cpp | 1 + src/htmlhelp.cpp | 5 + src/index.cpp | 26 +- src/latexgen.cpp | 32 +- src/libdoxygen.pro.in | 4 + src/libdoxygen.t | 6 + src/memberdef.cpp | 28 +- src/memberdef.h | 1 + src/msc.cpp | 2 +- src/pagedef.cpp | 2 +- src/pre.h | 2 + src/pre.l | 3 + src/scanner.l | 100 +++- src/util.cpp | 109 ++-- src/util.h | 2 +- src/xmlgen.cpp | 3 +- winbuild/Doxygen.vcproj | 56 ++ 37 files changed, 2743 insertions(+), 131 deletions(-) create mode 100644 src/fortrancode.h create mode 100644 src/fortrancode.l create mode 100644 src/fortranscanner.h create mode 100644 src/fortranscanner.l diff --git a/INSTALL b/INSTALL index 6cdd10f..076f508 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ -DOXYGEN Version 1.5.3 +DOXYGEN Version 1.5.3-20070815 Please read the installation section of the manual (http://www.doxygen.org/install.html) for instructions. -------- -Dimitri van Heesch (27 July 2007) +Dimitri van Heesch (15 August 2007) diff --git a/README b/README index 9e961e6..b63eb0f 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -DOXYGEN Version 1.5.3 +DOXYGEN Version 1.5.3_20070815 Please read INSTALL for compilation instructions. @@ -17,4 +17,4 @@ to subscribe to the lists or to visit the archives. Enjoy, -Dimitri van Heesch (dimitri@stack.nl) (27 July 2007) +Dimitri van Heesch (dimitri@stack.nl) (15 August 2007) diff --git a/configure b/configure index e6d005c..5d6029e 100755 --- a/configure +++ b/configure @@ -20,7 +20,7 @@ doxygen_version_minor=5 doxygen_version_revision=3 #NOTE: Setting version_mmn to "NO" will omit mmn info from the package. -doxygen_version_mmn=NO +doxygen_version_mmn=20070815 bin_dirs=`echo $PATH | sed -e "s/:/ /g"` diff --git a/doc/docblocks.doc b/doc/docblocks.doc index d364858..9ed876e 100644 --- a/doc/docblocks.doc +++ b/doc/docblocks.doc @@ -93,12 +93,12 @@ or Some people like to make their comment blocks more visible in the documentation. For this purpose you can use the following: - \verbatim -/************************************************ +/********************************************//** * ... text ***********************************************/ \endverbatim +(note the 2 slashes to end the normal comment block and start a special comment block). or diff --git a/doc/formulas.doc b/doc/formulas.doc index 1ff846a..f77a6f3 100644 --- a/doc/formulas.doc +++ b/doc/formulas.doc @@ -95,9 +95,9 @@ valid commands in \f$\mbox{\LaTeX}\f$'s math-mode. For the third command the section should contain valid command for the specific environment. \warning Currently, doxygen is not very fault tolerant in recovering -from typos in formulas. It may have to be necessary to remove the +from typos in formulas. It may be necessary to remove the file formula.repository that is written to the html directory to -a rid of an incorrect formula +get rid of an incorrect formula. \htmlonly Go to the next section or return to the diff --git a/doc/index.doc b/doc/index.doc index 3c4eae3..c3e5c8a 100644 --- a/doc/index.doc +++ b/doc/index.doc @@ -124,7 +124,7 @@ 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.

diff --git a/libmd5/md5.c b/libmd5/md5.c index 6cafb26..0763598 100644 --- a/libmd5/md5.c +++ b/libmd5/md5.c @@ -19,6 +19,7 @@ * - Ian Jackson . * Still in the public domain. */ + #include /* for memcpy() */ #include /* for stupid systems */ diff --git a/src/Makefile.in b/src/Makefile.in index bef323e..880902f 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -48,6 +48,6 @@ distclean: clean -$(RM) scanner.cpp code.cpp config.cpp pre.cpp ce_lex.cpp \ ce_parse.cpp ce_parse.h doxytag.cpp tag.cpp commentscan.cpp \ declinfo.cpp defargs.cpp commentcnv.cpp doctokenizer.cpp \ - pycode.cpp pyscanner.cpp + pycode.cpp pyscanner.cpp fortrancode.cpp fortranscanner.cpp FORCE: diff --git a/src/classdef.cpp b/src/classdef.cpp index 1991a4a..58be2bb 100644 --- a/src/classdef.cpp +++ b/src/classdef.cpp @@ -2709,6 +2709,7 @@ void ClassDef::setGroupDefForAllMembers(GroupDef *gd,Grouping::GroupPri_t pri,co void ClassDef::addInnerCompound(Definition *d) { + //printf("**** %s::addInnerCompound(%s)\n",name().data(),d->name().data()); if (d->definitionType()==Definition::TypeClass) // only classes can be // nested in classes. { diff --git a/src/code.l b/src/code.l index fd5722a..1f123b2 100644 --- a/src/code.l +++ b/src/code.l @@ -2607,8 +2607,8 @@ OPERATOR {ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP} g_code->codify(yytext); } {KEYWORD}/([^a-z_A-Z0-9]) { - addParmType(); - g_parmName=yytext; + //addParmType(); + //g_parmName=yytext; startFontClass("keyword"); g_code->codify(yytext); endFontClass(); diff --git a/src/commentscan.l b/src/commentscan.l index 1b55244..a91dc97 100644 --- a/src/commentscan.l +++ b/src/commentscan.l @@ -354,6 +354,7 @@ static OutputContext inContext; // are we inside the brief, details static bool briefEndsAtDot; // does the brief description stop at a dot? static QCString formulaText; // Running text of a formula static QCString formulaEnv; // environment name +static int formulaNewLines; // amount of new lines in the formula static QCString *pOutputString; // pointer to string to which the output is appended. static QCString outputXRef; // temp argument of todo/test/../xrefitem commands static QCString blockName; // preformatted block name (e.g. verbatim, latexonly,...) @@ -566,6 +567,8 @@ static QCString addFormula() { formLabel.sprintf("\\form#%d",f->getId()); } + int i; + for (i=0;i{CMD}"f$" { // start of a inline formula formulaText="$"; + formulaNewLines=0; BEGIN(ReadFormulaShort); } {CMD}"f[" { // start of a block formula formulaText="\\["; + formulaNewLines=0; BEGIN(ReadFormulaLong); } {CMD}"{" { // begin of a group @@ -1056,6 +1062,7 @@ MAILADR [a-z_A-Z0-9.+\-]+"@"[a-z_A-Z0-9\-]+("."[a-z_A-Z0-9\-]+)+[a-z_A-Z0-9\-] formulaText+=yytext; } \n { // new line + formulaNewLines++; formulaText+=*yytext; yyLineNr++; } diff --git a/src/declinfo.l b/src/declinfo.l index 3ca4c97..54c0a2a 100644 --- a/src/declinfo.l +++ b/src/declinfo.l @@ -155,7 +155,7 @@ ID ([a-z_A-Z][a-z_A-Z0-9]*)|(@[0-9]+) {B}+ { addType(); } -{B}*"("({ID}"::")*{B}*"*"({B}*("const"|"volatile"){B}+)? { +{B}*"("({ID}"::")*{B}*[&*]({B}*("const"|"volatile"){B}+)? { addType(); QCString text=yytext; type+=text.stripWhiteSpace(); diff --git a/src/defargs.h b/src/defargs.h index fe9ea28..b7c6aa1 100644 --- a/src/defargs.h +++ b/src/defargs.h @@ -23,6 +23,7 @@ class ArgumentList; -extern void stringToArgumentList(const char *argsString,ArgumentList* &argList); +extern void stringToArgumentList(const char *argsString,ArgumentList* &argList, + QCString *extraTypeChars=0); #endif diff --git a/src/defargs.l b/src/defargs.l index 94578ff..5939504 100644 --- a/src/defargs.l +++ b/src/defargs.l @@ -71,6 +71,7 @@ static QCString g_curArgName; static QCString g_curArgDocs; static QCString g_curArgAttrib; static QCString g_curArgArray; +static QCString g_extraTypeChars; static int g_argRoundCount; static int g_argSharpCount; static int g_argCurlyCount; @@ -359,6 +360,10 @@ ID [a-z_A-Z][a-z_A-Z0-9]* "="{B}*"0" { g_argList->pureSpecifier=TRUE; } +")"{B}*"["[^]]*"]" { // for functions returning a pointer to an array, + // i.e. ")[]" in "int (*f(int))[4]" with argsString="(int))[4]" + g_extraTypeChars=yytext; + } [^\*\n]+ { g_curArgDocs+=yytext; } @@ -402,7 +407,7 @@ ID [a-z_A-Z][a-z_A-Z0-9]* * \param al a reference to resulting argument list pointer. */ -void stringToArgumentList(const char *argsString,ArgumentList* &al) +void stringToArgumentList(const char *argsString,ArgumentList* &al,QCString *extraTypeChars) { if (al==0) return; if (argsString==0) return; @@ -411,6 +416,7 @@ void stringToArgumentList(const char *argsString,ArgumentList* &al) g_curArgDocs.resize(0); g_curArgAttrib.resize(0); g_curArgArray.resize(0); + g_extraTypeChars.resize(0); g_argRoundCount = 0; g_argSharpCount = 0; g_argCurlyCount = 0; @@ -425,6 +431,7 @@ void stringToArgumentList(const char *argsString,ArgumentList* &al) defargsYYrestart( defargsYYin ); BEGIN( Start ); defargsYYlex(); + if (extraTypeChars) *extraTypeChars=g_extraTypeChars; //printf("stringToArgumentList(%s) result=%s\n",argsString,argListToString(al).data()); } diff --git a/src/doctokenizer.l b/src/doctokenizer.l index 8fd3856..901773e 100644 --- a/src/doctokenizer.l +++ b/src/doctokenizer.l @@ -321,11 +321,12 @@ FUNCARG "("{FUNCCHAR}*")"({BLANK}*("volatile"|"const"))? OPNEW {BLANK}+"new"({BLANK}*"[]")? OPDEL {BLANK}+"delete"({BLANK}*"[]")? OPNORM {OPNEW}|{OPDEL}|"+"|"-"|"*"|"/"|"%"|"^"|"&"|"|"|"~"|"!"|"="|"<"|">"|"+="|"-="|"*="|"/="|"%="|"^="|"&="|"|="|"<<"|">>"|"<<="|">>="|"=="|"!="|"<="|">="|"&&"|"||"|"++"|"--"|","|"->*"|"->"|"[]"|"()" -OPCAST {BLANK}+[^(\r\n.,]+ -OPMASK ({BLANK}*{OPNORM}{FUNCARG}?)|({OPCAST}{FUNCARG}) +OPCAST {BLANK}+[^<(\r\n.,][^(\r\n.,]* +OPMASK ({BLANK}*{OPNORM}{FUNCARG}) +OPMASKOPT ({BLANK}*{OPNORM}{FUNCARG}?)|({OPCAST}{FUNCARG}) LNKWORD1 ("::"|"#")?{SCOPEMASK} CVSPEC {BLANK}*("const"|"volatile") -LNKWORD2 {SCOPEPRE}*"operator"{OPMASK} +LNKWORD2 ({SCOPEPRE}*"operator"{OPMASK})|(("::"|"#"){SCOPEPRE}*"operator"{OPMASKOPT}) LNKWORD3 ([0-9a-z_A-Z\-]+("/"|"\\"))*[0-9a-z_A-Z\-]+("."[0-9a-z_A-Z]+)+ CHARWORD [^ \t\n\r\\@<>()\[\]:;\?{}&%$#,.] CHARWORDQ [^ \t\n\r\\@<>()\[\]:;\?{}&%$#,."] diff --git a/src/doxygen.cpp b/src/doxygen.cpp index 49ea331..3fba678 100644 --- a/src/doxygen.cpp +++ b/src/doxygen.cpp @@ -68,6 +68,7 @@ #include "parserintf.h" #include "htags.h" #include "pyscanner.h" +#include "fortranscanner.h" #include "code.h" #include "objcache.h" #include "store.h" @@ -1149,8 +1150,6 @@ static void addClassToContext(EntryNav *rootNav) else // new class { ClassDef::CompoundType sec = convertToCompoundType(root->section,root->spec); - Debug::print(Debug::Classes,0," New class `%s' (sec=0x%08x)! #tArgLists=%d\n", - fullName.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1); QCString className; QCString namespaceName; @@ -1168,6 +1167,8 @@ static void addClassToContext(EntryNav *rootNav) } cd=new ClassDef(root->fileName,root->startLine,fullName,sec, tagName,refFileName); + Debug::print(Debug::Classes,0," New class `%s' (sec=0x%08x)! #tArgLists=%d cd=%p\n", + fullName.data(),root->section,root->tArgLists ? (int)root->tArgLists->count() : -1,cd); cd->setDocumentation(root->doc,root->docFile,root->docLine); // copy docs to definition cd->setBriefDescription(root->brief,root->briefFile,root->briefLine); cd->setIsObjectiveC(root->objc); @@ -1279,6 +1280,7 @@ static void resolveClassNestingRelations() { QCString c,n; extractNamespaceName(cd->name(),c,n,TRUE); + n = stripAnonymousNamespaceScope(n); if (cd->name().contains("::")==nestingLevel && !n.isEmpty()) { cd->visited=TRUE; @@ -1316,14 +1318,15 @@ static void resolveClassNestingRelations() if (cd->name().contains("::")==nestingLevel && !cd->visited) { cd->visited=TRUE; + QCString name = stripAnonymousNamespaceScope(cd->name()); //printf("Level=%d processing=%s\n",nestingLevel,cd->name().data()); // also add class to the correct structural context Definition *d = findScopeFromQualifiedName(Doxygen::globalScope, - cd->name(),cd->getFileDef()); + name,cd->getFileDef()); if (d==0) // we didn't find anything, create the scope artificially // anyway, so we can at least relate scopes properly. { - Definition *d = buildScopeFromQualifiedName(cd->name(),cd->name().contains("::")); + Definition *d = buildScopeFromQualifiedName(name,name.contains("::")); if (d!=cd) // avoid recursion in case of redundant scopes, i.e: namespace N { class N::C {}; } // for this case doxygen assumes the exitance of a namespace N::N in which C is to be found! { @@ -1331,7 +1334,7 @@ static void resolveClassNestingRelations() cd->setOuterScope(d); warn(cd->getDefFileName(),cd->getDefLine(), "Warning: Internal inconsistency: scope for class %s not " - "found!",cd->name().data() + "found while looking in the global scope!",name.data() ); } } @@ -2127,8 +2130,10 @@ static MemberDef *addVariableToFile( { if (md->getFileDef() && ! // not a php array - (getLanguageFromFileName(md->getFileDef()->name())==SrcLangExt_PHP) && - (md->argsString()!=root->args && root->args.find('[')!=-1) + ( + (getLanguageFromFileName(md->getFileDef()->name())==SrcLangExt_PHP) && + (md->argsString()!=root->args && root->args.find('[')!=-1) + ) ) // not a php array variable { @@ -2600,7 +2605,7 @@ static void addMethodToClass(EntryNav *rootNav,ClassDef *cd, FileDef *fd=rootNav->fileDef(); int l,i; - static QRegExp re("([a-z_A-Z0-9: ]*[ *]*[ ]*"); + static QRegExp re("([a-z_A-Z0-9: ]*[ &*]*[ ]*"); if (!root->type.isEmpty() && (i=re.match(root->type,0,&l))!=-1) // function variable { @@ -2784,10 +2789,6 @@ static void buildFunctionList(EntryNav *rootNav) ClassDef *cd=0; // check if this function's parent is a class - static QRegExp re("([a-z_A-Z0-9: ]*[ *]*[ ]*"); - //printf("root->parent=`%s' %x cd=%p root->type.find(re,0)=%d\n", - // root->parent->name.data(),root->parent->section,getClass(root->parent->name), - // root->type.find(re,0)); QCString scope=rootNav->parent()->name(); //stripAnonymousNamespaceScope(root->parent->name); scope=stripTemplateSpecifiersFromScope(scope,FALSE); @@ -2826,12 +2827,14 @@ static void buildFunctionList(EntryNav *rootNav) } + static QRegExp re("([a-z_A-Z0-9: ]*[ &*]*[ ]*"); if (!rootNav->parent()->name().isEmpty() && (rootNav->parent()->section() & Entry::COMPOUND_MASK) && cd && // do some fuzzy things to exclude function pointers - (root->type.isEmpty() || root->type.find(re,0)==-1 || - root->type.find(")(")!=-1 || root->type.find("operator")!=-1 + (root->type.isEmpty() || + (root->type.find(re,0)==-1 || root->args.find(")[")!=-1) || // type contains ..(..* and args not )[.. -> function pointer + root->type.find(")(")!=-1 || root->type.find("operator")!=-1 // type contains ..)(.. and not "operator" ) ) { @@ -3122,6 +3125,10 @@ static void buildFunctionList(EntryNav *rootNav) //printf("unrelated function %d `%s' `%s' `%s'\n", // root->parent->section,root->type.data(),rname.data(),root->args.data()); } + else + { + Debug::print(Debug::Functions,0," --> %s not processed!\n",rname.data()); + } } else if (rname.isEmpty()) { @@ -4030,7 +4037,7 @@ static bool findClassRelation( // int *tempArgIndex; // for (;(tempArgIndex=qdi.current());++qdi) // { - // printf("(%s->%d) ",qdi.currentKey().data(),*tempArgIndex); + // printf("(%s->%d) ",qdi.currentKey(),*tempArgIndex); // } //} //printf("\n"); @@ -4077,8 +4084,8 @@ static bool findClassRelation( ); //printf("baseClassName=%s baseClass=%p cd=%p explicitGlobalScope=%d\n", // baseClassName.data(),baseClass,cd,explicitGlobalScope); - //printf(" root->name=`%s' baseClassName=`%s' baseClass=%s templSpec=%s\n", - // root->name.data(), + //printf(" scope=`%s' baseClassName=`%s' baseClass=%s templSpec=%s\n", + // cd ? cd->name().data():"", // baseClassName.data(), // baseClass?baseClass->name().data():"", // templSpec.data() @@ -4091,11 +4098,12 @@ static bool findClassRelation( if (!isRecursiveBaseClass(rootNav->name(),baseClassName) || explicitGlobalScope) { Debug::print( - Debug::Classes,0," class relation %s inherited/used by %s found (%s and %s)\n", + Debug::Classes,0," class relation %s inherited/used by %s found (%s and %s) templSpec='%s'\n", baseClassName.data(), rootNav->name().data(), (bi->prot==Private)?"private":((bi->prot==Protected)?"protected":"public"), - (bi->virt==Normal)?"normal":"virtual" + (bi->virt==Normal)?"normal":"virtual", + templSpec.data() ); int i=baseClassName.find('<'); @@ -4142,17 +4150,19 @@ static bool findClassRelation( bool found=baseClass!=0 && (baseClass!=cd || mode==TemplateInstances); if (!found && si!=-1) { + QCString tmpTemplSpec; // replace any namespace aliases replaceNamespaceAliases(baseClassName,si); baseClass=getResolvedClass(cd, cd->getFileDef(), baseClassName, &baseClassTypeDef, - &templSpec, + &tmpTemplSpec, mode==Undocumented, TRUE ); found=baseClass!=0 && baseClass!=cd; + if (found) templSpec = tmpTemplSpec; } //printf("root->name=%s biName=%s baseClassName=%s\n", @@ -4227,6 +4237,8 @@ static bool findClassRelation( else { baseClass=Doxygen::classSDict->find(baseClassName); + //printf("*** classDDict->find(%s)=%p biName=%s templSpec=%s\n", + // baseClassName.data(),baseClass,biName.data(),templSpec.data()); if (baseClass==0) { baseClass=new ClassDef(root->fileName,root->startLine, @@ -5384,8 +5396,8 @@ static void findMember(EntryNav *rootNav, // don't be fooled by anonymous scopes tcd=cd; } - //printf("Looking for %s inside nd=%s result=%p\n", - // scopeName.data(),nd?nd->name().data():"",tcd); + //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n", + // scopeName.data(),nd?nd->name().data():"",tcd,tcd?tcd->name().data():"",cd); if (cd && tcd==cd) // member's classes match { @@ -8825,6 +8837,9 @@ void initDoxygen() ParserInterface *defaultParser = new CLanguageScanner; Doxygen::parserManager = new ParserManager(defaultParser); Doxygen::parserManager->registerParser(".py",new PythonLanguageScanner); + Doxygen::parserManager->registerParser(".f90", new FortranLanguageScanner); + Doxygen::parserManager->registerParser(".F90", new FortranLanguageScanner); + // register any additional parsers here... diff --git a/src/fortrancode.h b/src/fortrancode.h new file mode 100644 index 0000000..5f0366e --- /dev/null +++ b/src/fortrancode.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2006 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. + * + */ + +#ifndef CODE_H +#define CODE_H + +#include "qtbc.h" +#include + +class CodeOutputInterface; +class FileDef; +class MemberDef; + +void parseFortranCode(CodeOutputInterface &,const char *,const QCString &, + bool ,const char *,FileDef *fd=0, + int startLine=-1,int endLine=-1,bool inlineFragment=FALSE, + MemberDef *memberDef=0); +void resetFortranCodeParserState(); +void codeFreeScanner(); + +#endif diff --git a/src/fortrancode.l b/src/fortrancode.l new file mode 100644 index 0000000..b780052 --- /dev/null +++ b/src/fortrancode.l @@ -0,0 +1,959 @@ +/****************************************************************************** + * + * Parser for syntax hightlighting and references for Fortran90 F subset + * + * Copyright (C) by Anke Visser + * based on the work of 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. + * + */ + +/** + @TODO - continutation lines not always recognized + - merging of use-statements with same module name and different only-names + - rename part of use-statement + - links to interface functions + - references to variables +**/ + +%{ + +/* + * includes + */ +#include "qtbc.h" +#include +#include +#include +#include +#include +#include +#include "entry.h" +#include "doxygen.h" +#include "message.h" +#include "outputlist.h" +#include "util.h" +#include "membername.h" +#include "searchindex.h" +#include "defargs.h" + +#define YY_NEVER_INTERACTIVE 1 +#define YY_NO_TOP_STATE 1 + +//-------------------------------------------------------------------------------- + +/** + data of an use-statement +*/ +class UseEntry +{ + public: + QCString module; // just for debug + QStringList onlyNames; /* entries of the ONLY-part */ +}; + +/** + module name -> list of ONLY/remote entries + (module name = name of the module, which can be accessed via use-directive) +*/ +class UseSDict : public SDict +{ + public: + UseSDict() : SDict(17) {} +}; + +/*===================================================================*/ +/* + * statics + */ + +static QCString docBlock; //!< contents of all lines of a documentation block +static QCString currentModule=0; //!< name of the current enclosing module +static UseSDict *useMembers= new UseSDict; //!< info about used modules +static UseEntry *useEntry = 0; //!< current use statement info +static QStack useStack; //!< contains names of used modules for current program tree +static QStringList *currentUseNames= new QStringList; //! contains names of used modules of current program unit +static QCString str=""; //!> contents of fortran string + +static CodeOutputInterface * g_code; + +// TODO: is this still needed? if so, make it work +static QCString g_parmType; +static QCString g_parmName; + +static const char * g_inputString; //!< the code fragment as text +static int g_inputPosition; //!< read offset during parsing +static int g_inputLines; //!< number of line in the code fragment +static int g_yyLineNr; //!< current line number +static bool g_needsTermination; + +static bool g_insideBody; //!< inside subprog/program body? => create links +static const char * g_currentFontClass; + +static bool g_exampleBlock; +static QCString g_exampleName; +static QCString g_exampleFile; + +static FileDef * g_sourceFileDef; +static Definition * g_currentDefinition; +static MemberDef * g_currentMemberDef; +static bool g_includeCodeFragment; + + +static void endFontClass() +{ + if (g_currentFontClass) + { + g_code->endFontClass(); + g_currentFontClass=0; + } +} + +static void startFontClass(const char *s) +{ + endFontClass(); + g_code->startFontClass(s); + g_currentFontClass=s; +} + +static void setCurrentDoc(const QCString &name,const QCString &base,const QCString &anchor="") +{ + static bool searchEngineEnabled=Config_getBool("SEARCHENGINE"); + if (searchEngineEnabled) + { + Doxygen::searchIndex->setCurrentDoc(name,base,anchor); + } +} + +static void addToSearchIndex(const char *text) +{ + static bool searchEngineEnabled=Config_getBool("SEARCHENGINE"); + if (searchEngineEnabled) + { + Doxygen::searchIndex->addWord(text,FALSE); + } +} + +/*! start a new line of code, inserting a line number if g_sourceFileDef + * is TRUE. If a definition starts at the current line, then the line + * number is linked to the documentation of that definition. + */ +static void startCodeLine() +{ + if (g_sourceFileDef) + { + //QCString lineNumber,lineAnchor; + //lineNumber.sprintf("%05d",g_yyLineNr); + //lineAnchor.sprintf("l%05d",g_yyLineNr); + + Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr); + //printf("startCodeLine %d d=%s\n", g_yyLineNr,d ? d->name().data() : ""); + if (!g_includeCodeFragment && d) + { + g_currentDefinition = d; + g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr); + g_insideBody = FALSE; + g_parmType.resize(0); + g_parmName.resize(0); + QCString lineAnchor; + lineAnchor.sprintf("l%05d",g_yyLineNr); + if (g_currentMemberDef) + { + g_code->writeLineNumber(g_currentMemberDef->getReference(), + g_currentMemberDef->getOutputFileBase(), + g_currentMemberDef->anchor(),g_yyLineNr); + setCurrentDoc( + g_currentMemberDef->qualifiedName(), + g_sourceFileDef->getSourceFileBase(), + lineAnchor); + } + else if (d->isLinkableInProject()) + { + g_code->writeLineNumber(d->getReference(), + d->getOutputFileBase(), + 0,g_yyLineNr); + setCurrentDoc( + d->qualifiedName(), + g_sourceFileDef->getSourceFileBase(), + lineAnchor); + } + } + else + { + g_code->writeLineNumber(0,0,0,g_yyLineNr); + } + } + g_code->startCodeLine(); + if (g_currentFontClass) + { + g_code->startFontClass(g_currentFontClass); + } +} + + +static void endFontClass(); +static void endCodeLine() +{ + if (g_currentFontClass) { g_code->endFontClass(); } + g_code->endCodeLine(); +} + +/*! write a code fragment `text' that may span multiple lines, inserting + * line numbers for each line. + */ +static void codifyLines(char *text) +{ + //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text); + char *p=text,*sp=p; + char c; + bool done=FALSE; + while (!done) + { + sp=p; + while ((c=*p++) && c!='\n'); + if (c=='\n') + { + g_yyLineNr++; + *(p-1)='\0'; + g_code->codify(sp); + endCodeLine(); + if (g_yyLineNrcodify(sp); + done=TRUE; + } + } +} + +static void codifyLines(QCString str) +{ + char *tmp= (char *) malloc(str.length()+1); + strcpy(tmp, str); + codifyLines(tmp); + free(tmp); +} + +/*! writes a link to a fragment \a text that may span multiple lines, inserting + * line numbers for each line. If \a text contains newlines, the link will be + * split into multiple links with the same destination, one for each line. + */ +static void writeMultiLineCodeLink(CodeOutputInterface &ol, + const char *ref,const char *file, + const char *anchor,const char *text) +{ + bool done=FALSE; + char *p=(char *)text; + while (!done) + { + char *sp=p; + char c; + while ((c=*p++) && c!='\n'); + if (c=='\n') + { + g_yyLineNr++; + *(p-1)='\0'; + //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); + ol.writeCodeLink(ref,file,anchor,sp,0); + endCodeLine(); + if (g_yyLineNrisTypedef() || dst->isEnumerate()) return; // don't add types + //printf("======= addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data()); + if ((Config_getBool("REFERENCED_BY_RELATION") || Config_getBool("CALLER_GRAPH")) && + (src->isFunction())) + { + dst->addSourceReferencedBy(src); + } + if ((Config_getBool("REFERENCES_RELATION") || Config_getBool("CALL_GRAPH")) && (src->isFunction())) + { + src->addSourceReferences(dst); + } +} + +//------------------------------------------------------------------------------- +/** + searches for definition of a type + @param memberName the name of the type + @param moduleName name of enclosing module or null, if global entry + @param cd the entry, if found or null + @param useList array of data of USE-statement + @returns true, if type is found +*/ +static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName, + ClassDef *&cd, UseSDict *usedict=0) +{ + if (tname.isEmpty()) return FALSE; /* empty name => nothing to link */ + + //cout << "=== search for type: " << tname << endl; + + // search for type + if ((cd=Doxygen::classSDict->find(tname))) + { + //cout << "=== type found in global module" << endl; + return TRUE; + } + else if (moduleName && (cd= Doxygen::classSDict->find(moduleName+"::"+tname))) + { + //cout << "=== type found in local module" << endl; + return TRUE; + } + else + { + UseEntry *use; + for (UseSDict::Iterator di(*usedict); (use=di.current()); ++di) + if ((cd= Doxygen::classSDict->find(use->module+"::"+tname))) + { + //cout << "=== type found in used module" << endl; + return TRUE; + } + } + + return FALSE; +} + +/** + searches for definition of function memberName + @param memberName the name of the function/variable + @param moduleName name of enclosing module or null, if global entry + @param memberDef the entry, if found or null + @param useList array of data of USE-statement + @returns true, if found +*/ +static bool getFortranDefs(const QCString &memberName, const QCString &moduleName, + MemberDef *&md, UseSDict *usedict=0) +{ + if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */ + + // search for function + MemberName *mn = Doxygen::functionNameSDict->find(memberName); + + if (mn) // name is known + { + MemberListIterator mli(*mn); + for (mli.toFirst();(md=mli.current());++mli) // all found functions with given name + { + FileDef *fd=md->getFileDef(); + GroupDef *gd=md->getGroupDef(); + + //cout << "found link with same name: " << fd->fileName() << " " << memberName; + //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl; + + if ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) + { + NamespaceDef *nspace= md->getNamespaceDef(); + + if (nspace == 0) + { // found function in global scope + return TRUE; + } + else if (moduleName == nspace->name()) + { // found in local scope + return TRUE; + } + else + { // else search in used modules + QCString moduleName= nspace->name(); + UseEntry *ue= usedict->find(moduleName); + if (ue) + { + // check if only-list exists and if current entry exists is this list + QStringList &only= ue->onlyNames; + if (only.isEmpty()) + { + //cout << " found in module " << moduleName << " entry " << memberName << endl; + return TRUE; // whole module used + } + else + { + for ( QStringList::Iterator it = only.begin(); it != only.end(); ++it) + { + //cout << " search in only: " << moduleName << ":: " << memberName << "==" << (*it)<< endl; + if (memberName == (QCString)(*it)) + return TRUE; // found in ONLY-part of use list + } + } + } + } + } // if linkable + } // for + } + return FALSE; +} + +/** + gets the link to a generic procedure which depends not on the name, but on the parameter list + @TODO implementation +*/ +static bool getGenericProcedureLink(const ClassDef *cd, + const char *memberText, + CodeOutputInterface &ol) +{ + (void)cd; + (void)memberText; + (void)ol; + return FALSE; +} + +static bool getLink(UseSDict *usedict, // dictonary with used modules + const char *memberText, // exact member text + CodeOutputInterface &ol, + const char *text) +{ + MemberDef *md; + QCString memberName= removeRedundantWhiteSpace(memberText); + //printf("Trying `%s'::`%s'\n",c.data(),m.data()); + + if (getFortranDefs(memberName, currentModule, md, usedict) && md->isLinkable()) + { + //if (md->isVariable()) return FALSE; // variables aren't handled yet + + Definition *d = md->getOuterScope()==Doxygen::globalScope ? + md->getBodyDef() : md->getOuterScope(); + if (md->getGroupDef()) d = md->getGroupDef(); + if (d && d->isLinkable()) + { + if (g_currentDefinition && g_currentMemberDef && md!=g_currentMemberDef && g_insideBody) + { + addDocCrossReference(g_currentMemberDef,md); + } + ol.linkableSymbol(g_yyLineNr,md->name(),md, + g_currentMemberDef ? g_currentMemberDef : g_currentDefinition); + writeMultiLineCodeLink(ol,md->getReference(), + md->getOutputFileBase(), + md->anchor(), + text ? text : memberText); + addToSearchIndex(text ? text : memberText); + return TRUE; + } + } + return FALSE; +} + + +static void generateLink(CodeOutputInterface &ol, char *lname) +{ + ClassDef *cd=0; + + // check if lname is a linkable type or interface + if ( (getFortranTypeDefs(lname, currentModule, cd, useMembers)) && cd->isLinkable() ) + { + if ( (cd->compoundType() == ClassDef::Class) && // was Entry::INTERFACE_SEC) && + (getGenericProcedureLink(cd, lname, ol)) ) + { + //cout << "=== generic procedure resolved" << endl; + } + else + { // write type or interface link + ol.linkableSymbol(g_yyLineNr, lname, cd, g_currentMemberDef?g_currentMemberDef:g_currentDefinition); + writeMultiLineCodeLink(ol,cd->getReference(),cd->getOutputFileBase(),0,lname); + addToSearchIndex(lname); + } + } + // check for function/variable + else if (getLink(useMembers, lname, ol, lname)) + { + //cout << "=== found link for " << lname << endl; + } + else + { + // nothing found, just write out the word + ol.linkableSymbol(g_yyLineNr, lname, 0, g_currentMemberDef?g_currentMemberDef:g_currentDefinition); + //startFontClass("charliteral"); //test + codifyLines(lname); + //endFontClass(); //test + addToSearchIndex(lname); + } +} + +/*! counts the number of lines in the input */ +static int countLines() +{ + const char *p=g_inputString; + char c; + int count=1; + while ((c=*p)) + { + p++ ; + if (c=='\n') count++; + } + if (p>g_inputString && *(p-1)!='\n') + { // last line does not end with a \n, so we add an extra + // line and explicitly terminate the line after parsing. + count++, + g_needsTermination=TRUE; + } + return count; +} + +//---------------------------------------------------------------------------- +/** start use list */ +void startUseScope() +{ + //fprintf("===> startUse %s",yytext); + useStack.push(currentUseNames); + currentUseNames= new QStringList; +} + +/** end use list */ +void endUseScope() +{ + //fprintf(stderr,"===> endUse %s",yytext); + //UseSDict::Iterator di(*useMembers); UseEntry *ue; + //for (di.toFirst(); ue=di.current(); ++di){cout << ue->module ;} cout << endl; + + for ( QStringList::Iterator it = currentUseNames->begin(); it != currentUseNames->end(); ++it) + { + useMembers->remove(*it); + } + delete currentUseNames; + currentUseNames= useStack.pop(); + if (currentUseNames == 0) + { + fprintf(stderr,"fortrancode.l: stack empty!"); + //exit(-1); + } +} + +void addUse(QString moduleName) +{ + currentUseNames->append(moduleName); +} +//---------------------------------------------------------------------------- + +/* -----------------------------------------------------------------*/ +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); + +static int yyread(char *buf,int max_size) +{ + int c=0; + while( c < max_size && g_inputString[g_inputPosition] ) + { + *buf = g_inputString[g_inputPosition++] ; + c++; buf++; + } + return c; +} + +%} + +IDSYM [a-z_A-Z0-9] +ID [a-z_A-Z]+{IDSYM}* +SUBPROG (subroutine|function) +B [ \t] +BS [ \t]* +BS_ [ \t]+ +COMMA {BS},{BS} +ARGS {BS}("("[^)]*")"){BS} + +NUM_TYPE (complex|integer|logical|real) +KIND {ARGS} +CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"([0-9]+|{ARGS})) +TYPE_SPEC (({NUM_TYPE}("*"[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}PRECISION|{CHAR}) + +INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" +ATTR_SPEC (ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PRIVATE|PUBLIC|SAVE|TARGET) +ACCESS_SPEC (PRIVATE|PUBLIC) +/* Assume that attribute statements are almost the same as attributes. */ +ATTR_STMT {ATTR_SPEC}|DIMENSION +COMMANDS (BLOCK{BS}DATA|DO|SELECT|CASE|WHERE|IF|THEN|ELSE|MODULE{BS_}PROCEDURE) +IGNORE (IMPLICIT{BS}NONE|CONTAINS|WRITE|READ|ALLOCATE|DEALLOCATE|SIZE) + +/* | */ + +%option noyywrap +%option stack +%option caseless +/*%option debug*/ + +%x Start +%x SubCall +%x FuncDef +%x ClassName +%x ClassVar +%x Subprog +%x DocBlock +%x Use +%x UseOnly +%x TypeDecl +%x Declaration +%x DeclContLine +%x Parameterlist +%x String + +%% + /*==================================================================*/ + + /*-------- ignore ------------------------------------------------------------*/ + +{IGNORE}/{BS}"("? { // do not search keywords, intrinsics... @TODO: complete list + codifyLines(yytext); + } + /*-------- inner construct ---------------------------------------------------*/ + +{COMMANDS}/[,( \t\n].* { // hightlight rest of fortran statements + /* font class is defined e.g. in doxygen.css */ + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } +"end"{BS}{COMMANDS}/[ \t\n].* { + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + + /*-------- use statement -------------------------------------------*/ +"use"{BS_} { + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(Use); + } +{ID} { + startFontClass("keywordflow"); + codifyLines(yytext); + endFontClass(); + + /* append module name to use dict */ + useEntry = new UseEntry(); + useEntry->module = yytext; + useMembers->append(yytext, useEntry); + addUse(yytext); + } +,{BS}"ONLY" { // TODO: rename + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(UseOnly); + } +{BS},{BS} { codifyLines(yytext); } +{ID} { + codifyLines(yytext); + useEntry->onlyNames.append(yytext); + } +"\n" { + unput(*yytext); + yy_pop_state(); + } + + /*-------- fortran module -----------------------------------------*/ +("program"|"module"|"type"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { // + startUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + yy_push_state(YY_START); + BEGIN(ClassName); + if (!strcmp(yytext,"module")) currentModule="module"; + } +{ID} { + if (currentModule == "module") currentModule=yytext; + generateLink(*g_code,yytext); + yy_pop_state(); + } +\n { // interface may be without name + yy_pop_state(); + REJECT; + } +"end"{BS}"module".* { // just reset currentModule, rest is done in following rule + currentModule=0; + REJECT; + } +"end"{BS}("program"|"module"|"type"|"interface") { // + endUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + + /*-------- subprog definition -------------------------------------*/ +{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } +{SUBPROG}{BS_} { // Fortran subroutine or function found + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + yy_push_state(YY_START); + BEGIN(Subprog); + } +{ID} { // subroutine/function name + //cout << "===> start procedure " << yytext << endl; + startUseScope(); + generateLink(*g_code,yytext); + } +"(".* { // ignore rest of line + codifyLines(yytext); + } +"\n" { codifyLines(yytext); + yy_pop_state(); + } +"end"{BS}{SUBPROG}.* { // Fortran subroutine or function ends + //cout << "===> end function " << yytext << endl; + endUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + /*-------- variable declaration ----------------------------------*/ +"TYPE"{BS}"(" { + yy_push_state(YY_START); + BEGIN(TypeDecl); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +{ID} { // link type + g_insideBody=TRUE; + generateLink(*g_code,yytext); + g_insideBody=FALSE; + } +")" { + BEGIN(Declaration); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +{TYPE_SPEC}/[,:( ] { + yy_push_state(YY_START); + BEGIN(Declaration); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +{ATTR_SPEC} { + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable deklaration + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +"&" { // continuation line + yy_push_state(YY_START); + BEGIN(DeclContLine); + } +"\n" { // declaration not yet finished + codifyLines(yytext); + yy_pop_state(); + } +"\n" { // end declaration line + codifyLines(yytext); + yy_pop_state(); + } + + /*-------- subprog calls -----------------------------------------*/ + +"call"{BS_} { + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(SubCall); + } +{ID} { // subroutine call + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + yy_pop_state(); + } +{ID}{BS}/"(" { // function call + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + } + + /*-------- comments ---------------------------------------------------*/ +\n?{BS}"!>" { // start comment line or comment block + yy_push_state(YY_START); + BEGIN(DocBlock); + docBlock=yytext; + } + +.* { // contents of current comment line + docBlock+=yytext; + } +"\n"{BS}("!>"|"!"+) { //| comment block (next line is also comment line) + docBlock+=yytext; + } +"\n" { // comment block ends at the end of this line + docBlock+=yytext; + // remove special comment (default config) + if (Config_getBool("STRIP_CODE_COMMENTS")) + { + g_yyLineNr+=((QCString)docBlock).contains('\n'); + endCodeLine(); + if (g_yyLineNr"!"[^>\n].*|"!"$ { // normal comment + startFontClass("comment"); + codifyLines(yytext); + endFontClass(); + } + /*------ preprocessor --------------------------------------------*/ +"#".*\n { startFontClass("preprocessor"); + codifyLines(yytext); + endFontClass(); + } + /*------ variable references? -------------------------------------*/ +{ID} { + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + } + + /*------ strings --------------------------------------------------*/ +<*>"\\\\" { str+=yytext; /* ignore \\ */} +<*>"\\\"" { str+=yytext; /* ignore \" */} + +\" { // string ends with next quote without previous backspace + str+=yytext; + startFontClass("stringliteral"); + codifyLines(str); + endFontClass(); + yy_pop_state(); + } +<*>\" { /* string starts */ + yy_push_state(YY_START); + BEGIN(String); + str=yytext; + } +. {str+=yytext;} + + + /*-----------------------------------------------------------------------------*/ + +<*>\n { + codifyLines(yytext); + } +<*>. { + g_code->codify(yytext); + } +%% + +/*@ ---------------------------------------------------------------------------- + */ + +/*===================================================================*/ + +void resetFortranCodeParserState() {} + +void parseFortranCode(CodeOutputInterface &od,const char *className,const QCString &s, + bool exBlock, const char *exName,FileDef *fd, + int startLine,int endLine,bool inlineFragment, + MemberDef *memberDef) +{ + //printf("***parseCode() exBlock=%d exName=%s fd=%p\n",exBlock,exName,fd); + + // used parameters + (void)memberDef; + (void)className; + + if (s.isEmpty()) return; + g_code = &od; + g_inputString = s; + g_inputPosition = 0; + g_currentFontClass = 0; + g_needsTermination = FALSE; + if (endLine!=-1) + g_inputLines = endLine+1; + else + g_inputLines = countLines(); + + if (startLine!=-1) + g_yyLineNr = startLine; + else + g_yyLineNr = 1; + + g_exampleBlock = exBlock; + g_exampleName = exName; + g_sourceFileDef = fd; + if (exBlock && fd==0) + { + // create a dummy filedef for the example + g_sourceFileDef = new FileDef("",exName); + } + if (g_sourceFileDef) + { + setCurrentDoc(g_sourceFileDef->name(),g_sourceFileDef->getSourceFileBase()); + } + g_currentDefinition = 0; + g_currentMemberDef = 0; + if (!g_exampleName.isEmpty()) + { + g_exampleFile = convertNameToFile(g_exampleName+"-example"); + } + g_includeCodeFragment = inlineFragment; + startCodeLine(); + g_parmName.resize(0); + g_parmType.resize(0); + fcodeYYrestart( fcodeYYin ); + BEGIN( Start ); + fcodeYYlex(); + if (g_needsTermination) + { + endFontClass(); + g_code->endCodeLine(); + } + if (exBlock && g_sourceFileDef) + { + // delete the temporary file definition used for this example + delete g_sourceFileDef; + g_sourceFileDef=0; + } + return; +} + +#if !defined(YY_FLEX_SUBMINOR_VERSION) +extern "C" { // some bogus code to keep the compiler happy + void fcodeYYdummy() { yy_flex_realloc(0,0); } +} +#elif YY_FLEX_SUBMINOR_VERSION<33 +#error "You seem to be using a version of flex newer than 2.5.4 but older than 2.5.33. These versions do NOT work with doxygen! Please use version <=2.5.4 or >=2.5.33 or expect things to be parsed wrongly!" +#endif + diff --git a/src/fortranscanner.h b/src/fortranscanner.h new file mode 100644 index 0000000..2e16c67 --- /dev/null +++ b/src/fortranscanner.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2006 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. + * + */ + +#ifndef SCANNER_FORTRAN_H +#define SCANNER_FORTRAN_H + +#include "parserintf.h" + +/** \brief Fortran language parser using state-based lexical scanning. + * + * This is the Fortran language parser for doxygen. + */ +class FortranLanguageScanner : public ParserInterface +{ + public: + virtual ~FortranLanguageScanner() {} + void parseInput(const char *fileName, + const char *fileBuf, + Entry *root); + bool needsPreprocessing(const QCString &extension); + void parseCode(CodeOutputInterface &codeOutIntf, + const char *scopeName, + const QCString &input, + bool isExampleBlock, + const char *exampleName=0, + FileDef *fileDef=0, + int startLine=-1, + int endLine=-1, + bool inlineFragment=FALSE, + MemberDef *memberDef=0 + ); + void resetCodeParserState(); + void parsePrototype(const char *text); +}; + +#endif diff --git a/src/fortranscanner.l b/src/fortranscanner.l new file mode 100644 index 0000000..e4ef1f8 --- /dev/null +++ b/src/fortranscanner.l @@ -0,0 +1,1333 @@ +/* -*- mode: fundamental; indent-tabs-mode: 1; -*- */ +/***************************************************************************** + * Parser for Fortran90 F subset + * + * Copyright (C) by Anke Visser + * based on the work of 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. + * + */ + +/* Developer notes. + * + * - Consider using startScope(), endScope() functions with module, program, + * subroutine or any other scope in fortran program. + * + * - Symbol modifiers (attributes) are collected using SymbolModifiers |= operator during + * substructure parsing. When substructure ends all modifiers are applied to actual + * entries in applyModifiers() functions. + * + * - How case insensitiveness should be handled in code? + * On one side we have arg->name and entry->name, on another side modifierMap[name]. + * In entries and arguments case is the same as in code, in modifier map case is lowered and + * then it is compared to lowered entry/argument names. + * + * - Do not like constructs like aa{BS} or {BS}bb. Should try to handle blank space + * with separate rule?: It seems it is often necessary, because we may parse something like + * "functionA" or "MyInterface". So constructs like `(^|[ \t])interface({BS_}{ID})?/[ \t\n]' + * are desired. + */ + +%{ + +#include +#include +#include +#include + +#include "qtbc.h" +#include +#include +#include +#include +#include +#include + +#include "fortranscanner.h" +#include "entry.h" +#include "message.h" +#include "config.h" +#include "doxygen.h" +#include "util.h" +#include "defargs.h" +#include "language.h" +#include "commentscan.h" +#include "fortrancode.h" +#include "pre.h" + +#define YY_NEVER_INTERACTIVE 1 + +enum ScanVar { V_IGNORE, V_VARIABLE, V_PARAMETER}; + +// {{{ ----- Helper structs ----- +//! Holds modifiers (ie attributes) for one symbol (variable, function, etc) +struct SymbolModifiers { + enum Protection {NONE_P, PUBLIC, PRIVATE}; + enum Direction {NONE_D, IN, OUT, INOUT}; + + //!< This is only used with function return value. + QString type, returnName; + Protection protection; + Direction direction; + bool optional; + QString dimension; + bool allocatable; + bool external; + bool intrinsic; + bool parameter; + bool pointer; + bool target; + bool save; + + SymbolModifiers() : type(), returnName(), protection(NONE_P), direction(NONE_D), + optional(FALSE), dimension(), allocatable(FALSE), + external(FALSE), intrinsic(FALSE), parameter(FALSE), + pointer(FALSE), target(FALSE), save(FALSE) {} + + SymbolModifiers& operator|=(const SymbolModifiers &mdfs); + SymbolModifiers& operator|=(QString mdfrString); +}; + +//ostream& operator<<(ostream& out, const SymbolModifiers& mdfs); + +static const char *directionStrs[] = +{ + "", "intent(in)", "intent(out)", "intent(inout)" +}; + +// }}} + +/* ----------------------------------------------------------------- + * + * statics + */ +static ParserInterface *g_thisParser; +static const char * inputString; +static int inputPosition; +static QFile inputFile; +static QCString yyFileName; +static int yyLineNr = 1 ; +static Entry* current_root = 0 ; +static Entry* global_root = 0 ; +static Entry* file_root = 0 ; +static Entry* current = 0 ; +static Entry* last_entry = 0 ; +static ScanVar v_type = V_IGNORE; // type of parsed variable +static QList moduleProcedures; // list of all interfaces which contain unresolved + // module procedures +static QCString docBlock; +static QCString docBlockName; +static bool docBlockInBody; +static bool docBlockJavaStyle; + +static MethodTypes mtype; +static bool gstat; +static Specifier virt; + +static QString debugStr; +static QCString result; // function result +static Argument *parameter; // element of parameter list +static QCString argType; // fortran type of an argument of a parameter list +static QCString argName; // last identifier name in variable list +static QCString initializer; // initial value of a variable +static QCString useModuleName; // name of module in the use statement +static Protection defaultProtection; + +static char stringStartSymbol; // single or double quote + +//! Accumulated modifiers of current statement, eg variable declaration. +static SymbolModifiers currentModifiers; +//! Holds program scope->symbol name->symbol modifiers. +static QMap > modifiers; + +//----------------------------------------------------------------------------- + +static int yyread(char *buf,int max_size); +static void startCommentBlock(bool); +static void handleCommentBlock(const QCString &doc,bool brief); +static void addCurrentEntry(); +static void addInterface(QString name); +static Argument *addFortranParameter(const QCString &type,const QCString &name, const QString docs); +static void scanner_abort(); + +static void startScope(Entry *scope); +static bool endScope(Entry *scope); +static QString getFullName(Entry *e); +static bool isTypeName(QString name); +static void resolveModuleProcedures(QList &moduleProcedures, Entry *current_root); + +//----------------------------------------------------------------------------- +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); +//----------------------------------------------------------------------------- + +%} + + //----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- +IDSYM [a-z_A-Z0-9] +NOTIDSYM [^a-z_A-Z0-9] +SEPARATE [:, \t] +ID [a-z_A-Z]+{IDSYM}* +PP_ID {ID} +LABELID [a-z_A-Z]+[a-z_A-Z0-9\-]* +SUBPROG (subroutine|function) +B [ \t] +BS [ \t]* +BS_ [ \t]+ +COMMA {BS},{BS} +ARGS {BS}("("[^)]*")"){BS} +NOARGS {BS}"\n" + +NUM_TYPE (complex|integer|logical|real) +KIND {ARGS} +CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"([0-9]+|{ARGS})) +TYPE_SPEC (({NUM_TYPE}("*"[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}PRECISION|{CHAR}|TYPE{ARGS}) + +INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" +ATTR_SPEC (ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PRIVATE|PUBLIC|SAVE|TARGET) +ACCESS_SPEC (PRIVATE|PUBLIC) +/* Assume that attribute statements are almost the same as attributes. */ +ATTR_STMT {ATTR_SPEC}|DIMENSION + +%option noyywrap +%option stack +%option caseless +/*%option debug */ + + //--------------------------------------------------------------------------------- + + /** fortran parsing states */ +%x Subprog +%x Parameterlist +%x SubprogBody +%x Start +%x Comment +%x Module +%x ModuleBody +%x AttributeList +%x Variable +%x Initialization +%x ArrayInitializer +%x Typedef +%x TypedefBody +%x InterfaceBody +%x StrIgnore +%x String +%x Use +%x UseOnly +%x ModuleProcedure + + /** comment parsing states */ +%x DocBlock +%x DocBackLine +%x EndDoc + +%% + + /*-----------------------------------------------------------------------------------*/ + +<*>"&".*"\n" { if (YY_START == String) REJECT; // "&" is ignored in strings + yyLineNr++;} /* line not finished -> read next line (text after "&" may be + comment and has to be ignored */ + + /*------ ignore strings */ +<*>"\\\\" { /* ignore \\ */} +<*>"\\\""|\\\' { /* ignore \" and \' */} + +\"|\' { // string ends with next quote without previous backspace + if(yytext[0]!=stringStartSymbol) REJECT; // single vs double quote + // cout << "string end: " << debugStr << endl; + yy_pop_state(); + } + +. {debugStr+=yytext;} // ignore String contents (especially '!') + +<*>\"|\' { /* string starts */ + if(YY_START == StrIgnore) REJECT; // ignore in simple comments + // cout << "string start: " << yytext[0] << yyLineNr << endl; + yy_push_state(YY_START); + stringStartSymbol=yytext[0]; // single or double quote + BEGIN(String); debugStr="!^!"; + } + + /*------ ignore simple comment (not documentation comments) */ + +<*>"!"/[^<>\n] { if (YY_START == String) REJECT; // "!" is ignored in strings + // skip comment line (without docu comments "!>" "!<" ) + /* ignore further "!" and ignore comments in Strings */ + if ((YY_START != StrIgnore) && (YY_START != String)) { + yy_push_state(YY_START); + BEGIN(StrIgnore); + debugStr="*!"; + //cout << "start comment "<< yyLineNr << endl; + } + } +.?/\n { yy_pop_state(); // comment ends with endline character + //cout << "end comment " << yyLineNr <<" "<< debugStr << endl; + } // comment line ends +. { debugStr+=yytext; } + + + /*------ use handling ------------------------------------------------------------*/ + +"use"{BS_} { + yy_push_state(YY_START); + BEGIN(Use); + } +{ID} { + //cout << "using dir "<< yytext << endl; + current->name=yytext; + current->fileName = yyFileName; + current->section=Entry::USINGDIR_SEC; + current_root->addSubEntry(current); + current = new Entry; + yy_pop_state(); + } +{ID}/, { + useModuleName=yytext; + } +,{BS}"ONLY" { BEGIN(UseOnly); + } +{BS},{BS} {} +{ID} { + current->name= useModuleName+"::"+yytext; + current->fileName = yyFileName; + current->section=Entry::USINGDECL_SEC; + current_root->addSubEntry(current); + current = new Entry ; + } +"\n" { + unput(*yytext); + yy_pop_state(); + } + + /*------ ignore special fortran statements */ +(^|[ \t])interface({BS_}{ID})?/[ \t\n] { // handle interface block + QString name = yytext; + int index = name.find("interface", 0, FALSE); + index = name.find(QRegExp("[^ \\t]"), index+9); + //cout<"end"{BS}"interface".* { + if(!endScope(current_root)) + yyterminate(); + yy_pop_state(); + //cout << "end interface " << yyLineNr + // <<", "<module{BS}procedure { yy_push_state(YY_START); + BEGIN(ModuleProcedure); + } +{ID} { + current->section = Entry::FUNCTION_SEC ; + current->name = yytext; + moduleProcedures.append(current); + addCurrentEntry(); + } +"\n" { unput(*yytext); + yy_pop_state(); + } +. {} + + /*------ module handling ------------------------------------------------------------*/ +module|program{BS_} { // + BEGIN(Module); + defaultProtection = Public; + } +"end"{BS}(module|program).* { // end module + resolveModuleProcedures(moduleProcedures, current_root); + if(!endScope(current_root)) + yyterminate(); + defaultProtection = Public; + BEGIN(Start); + } +{ID} { + //cout << "0=========> got module " << yytext << endl; + current->section = Entry::NAMESPACE_SEC; + current->name = yytext; + current->type = "module"; + current->fileName = yyFileName; + current->bodyLine = yyLineNr; // used for source reference + current->protection = Public ; + + addCurrentEntry(); + startScope(last_entry); + + BEGIN(ModuleBody); + } + + /*------- access specification --------------------------------------------------------------------------*/ + +private/{BS}(\n|"!") { defaultProtection = Private; } +public/{BS}(\n|"!") { defaultProtection = Public; } + + /*------- type definition -------------------------------------------------------------------------------*/ + +"type"({BS_}|({COMMA}{ACCESS_SPEC})) { /* type definition found : TYPE , access-spec::type-name |*/ + yy_push_state(YY_START); + BEGIN(Typedef); + current->protection = defaultProtection; + } +{ACCESS_SPEC} { + QString type= yytext; + } +{ID} { /* type name found */ + //cout << "=========> got typedef " << yytext << ": " << yyLineNr << endl; + current->section = Entry::CLASS_SEC; // was Entry::STRUCT_SEC; + current->spec = Entry::Struct; + current->name = yytext; + + /* if type is part of a module, mod name is necessary for output */ + if ((current_root) && + (current_root->section == Entry::CLASS_SEC || + current_root->section == Entry::NAMESPACE_SEC)) + //current_root->section == Entry::INTERFACE_SEC)) + { + current->name= current_root->name+"::"+current->name; + } + current->fileName = yyFileName; + current->bodyLine = yyLineNr; + addCurrentEntry(); + startScope(last_entry); + BEGIN(TypedefBody); + } +"end"{BS}"type".* { /* end type definition */ + //cout << "=========> got typedef end "<< endl; + if(!endScope(current_root)) + yyterminate(); + yy_pop_state(); + } + + /*------- module/global/typedef variable ---------------------------------------------------*/ + +{ +{TYPE_SPEC}/{SEPARATE} { + /* variable declaration starts */ + //cout << "4=========> got variable type: " << yytext << endl; + QString help=yytext; + help= help.simplifyWhiteSpace(); + argType= help.latin1(); + yy_push_state(AttributeList); + } +^{BS}{PP_ID}{KIND}? { /* check for preprocessor symbol expand to type */ + QString str = yytext; + str = str.stripWhiteSpace(); + DefineDict* defines = getFileDefineDict(); + QString name; + int index = str.find("("); + if(index != -1) + name = str.left(index).stripWhiteSpace(); + else + name = str; + + Define *define = (*defines)[name]; + if(define != NULL && isTypeName(define->definition)) { + argType = str; + yy_push_state(AttributeList); + } else { + REJECT; + } + } +{ATTR_STMT}{BS}/{ID} { + /* attribute statement starts */ + //cout << "5=========> Attribute statement: "<< yytext << endl; + QString tmp = yytext; + currentModifiers |= tmp.stripWhiteSpace(); + argType=""; + yy_push_state(YY_START); + /* goto attribute parsing, however there must not be one, + just catch "::" if it is there. */ + BEGIN( AttributeList ) ; + } +} +{ +{COMMA} {} +{BS} {} +{ATTR_SPEC} { /* update current modifiers */ + QString tmp = yytext; + currentModifiers |= (tmp); + } +"::" { /* end attribute list */ + BEGIN( Variable ); + } +. { /* unknown attribute, consider variable name */ + //cout<<"start variables, unput "<<*yytext<{BS} {} +{ID} { /* parse variable declaration */ + //cout << "5=========> got variable: " << argType << "::" << yytext << endl; + /* work around for bug in QCString.replace (QString works) */ + QString name=yytext; + /* remember attributes for the symbol */ + modifiers[current_root][name.lower()] |= currentModifiers; + argName= name.latin1(); + int last= yy_top_state(); + + v_type= V_IGNORE; + if (!argType.isEmpty() && last != SubprogBody) { // new variable entry + v_type = V_VARIABLE; + current->section = Entry::VARIABLE_SEC; + current->name = argName; + current->type = argType; + current->fileName = yyFileName; + current->bodyLine = yyLineNr; // used for source reference + addCurrentEntry(); + } else if(!argType.isEmpty()){ // deklaration of parameter list: add type for corr. parameter + parameter= addFortranParameter(argType,argName,docBlock); + if (parameter) v_type= V_PARAMETER; + // save, it may be function return type + modifiers[current_root][name.lower()].type = argType; + // any accumulated doc for argument should be emptied, + // because it is handled other way and this doc can be + // unexpectedly passed to the next member. + current->doc.resize(0); + current->brief.resize(0); + } + } +{ARGS} { /* dimension of the previous entry. */ + QString name(argName); + QString attr("dimension"); + attr += yytext; + modifiers[current_root][name] |= attr; + } +{COMMA} {} +{BS}"=" { yy_push_state(YY_START); + initializer=""; + BEGIN(Initialization); + } +"\n" { currentModifiers = SymbolModifiers(); + yy_pop_state(); // end variable deklaration list + yyLineNr++; + docBlock.resize(0); + } + +"(/" { initializer+=yytext; + BEGIN(ArrayInitializer); // initializer may contain comma + } +. { initializer+=yytext; } +"/)" { initializer+=yytext; + yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + } +{COMMA} { yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + } +"\n"|"!" { //| + yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + unput(*yytext); + } +. { initializer+=yytext; } + + /*------ fortran subroutine/function handling ------------------------------------------------------------*/ + /* Start is initial condition */ + +{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { + // TYPE_SPEC is for old function style function result + result= yytext; + result= result.stripWhiteSpace(); + current->type = result; + } +{BS}{SUBPROG}{BS_} { // Fortran subroutine or function found + //cout << "1=========> got subprog, type:" << yytext <section = Entry::FUNCTION_SEC ; + QCString subtype = yytext; subtype=subtype.lower().stripWhiteSpace(); + if (!current->type) current->type = subtype; + current->fileName = yyFileName; + current->bodyLine = yyLineNr; // used for source reference + current->startLine = -1; // ??? what is startLine for? + current->args.resize(0); + current->argList->clear(); + yy_push_state(Subprog); + docBlock.resize(0); + } +{BS} { /* ignore white space */ } +{ID} { current->name = yytext; + //cout << "1a==========> got " << current->type << " " << yytext << " " << yyLineNr << endl; + modifiers[current_root][current->name.lower()].returnName = current->name; + BEGIN(Parameterlist); + } +{ARGS} { + //current->type not yet available + QString arglist= yytext; + //cout << "3=========> got parameterlist " << yytext << endl; + yyLineNr+= arglist.contains('\n'); + arglist = arglist.replace(QRegExp("&[^\n]*\n"),""); + //cout << "3=========> got parameterlist " << arglist << endl; + current->args = arglist; + current->args = removeRedundantWhiteSpace(current->args); + stringToArgumentList(current->args, current->argList); + addCurrentEntry(); + startScope(last_entry); + BEGIN(SubprogBody); + } +{NOARGS} { + yyLineNr++; + //cout << "3=========> without parameterlist " <argList); + addCurrentEntry(); + startScope(last_entry); + BEGIN(SubprogBody); +} +result{BS}\({BS}{ID} { + result= yytext; + result= result.right(result.length()-result.find("(")-1); + result= result.stripWhiteSpace(); + modifiers[current_root->parent()][current_root->name.lower()].returnName = result; + //cout << "=====> got result " << result << endl; + } +"end"{BS}{SUBPROG}.* { + //cout << "1e=========> got end subprog: " << yytext << endl; + + /* args is used for parameters in list of functions, argList for + parameters in detailed function descripttion */ + //current->args = argListToString(current->argList); + //current->endBodyLine = yyLineNr; // ??? what ist endBodyLine for + if(!endScope(current_root)) + yyterminate(); + yy_pop_state() ; + } + + /*---- documentation comments --------------------------------------------------------------------*/ + +"!<" { /* backward docu comment (only one line) */ + if (v_type != V_IGNORE) { + yy_push_state(YY_START); + current->docLine = yyLineNr; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF"); + startCommentBlock(TRUE); + BEGIN(DocBackLine); + } + } +.* { // contents of current comment line + docBlock=yytext; + if (v_type == V_VARIABLE) { + Entry *tmp_entry = current; + current = last_entry; // temporarily switch to the previous entry + handleCommentBlock(docBlock,TRUE); + current=tmp_entry; + } + else if (v_type == V_PARAMETER) { + parameter->docs=docBlock; + } + yy_pop_state(); + } + +"!>" { + yy_push_state(YY_START); + current->docLine = yyLineNr; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF"); + startCommentBlock(TRUE); + BEGIN(DocBlock); + //cout << "start DocBlock " << endl; + } + +.* { // contents of current comment line + docBlock+=yytext; + } +"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line) + docBlock+="\n"; // \n is necessary for lists + yyLineNr++; + } +"\n" { // comment block ends at the end of this line + //cout <<"3=========> comment block : "<< docBlock << endl; + unput(*yytext); + handleCommentBlock(docBlock,TRUE); + yy_pop_state(); + } + + /*------------------------------------------------------------------------------------------------*/ + +<*>"\n" {yyLineNr++; + //if (debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << debugStr << " state: " <<> { + fprintf(stderr,"==== Error: EOF reached in wrong state (end missing)"); + scanner_abort(); + yyterminate(); + } + <*>. {debugStr+=yytext;} // ignore remaining text + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ +%% +//---------------------------------------------------------------------------- + +/** used to copy entry to an interface module procedure */ +static void copyEntry(Entry *dest, Entry *src) +{ + dest->type = src->type; + dest->fileName = src->fileName; + dest->bodyLine = src->bodyLine; + dest->args = src->args; + dest->argList = new ArgumentList(*src->argList); +} + +/** fill empty interface module procedures with info from + corresponding module subprogs + @TODO: handle procedures in used modules +*/ +void resolveModuleProcedures(QList &moduleProcedures, Entry *current_root) +{ + if (moduleProcedures.isEmpty()) return; + + EntryListIterator eli1(moduleProcedures); + // for all module procedures + for (Entry *ce1; (ce1=eli1.current()); ++eli1) + { + // check all entries in this module + EntryListIterator eli2(*current_root->children()); + for (Entry *ce2; (ce2=eli2.current()); ++eli2) + { + if (ce1->name == ce2->name) + { + copyEntry(ce1, ce2); + } + } // for procedures in current module + } // for all interface module procedures + moduleProcedures.clear(); +} + +static bool isTypeName(QString name) +{ + name = name.lower(); + return name=="integer" || name == "real" || + name=="complex" || name == "logical"; +} + +/*! Extracts string which resides within parentheses of provided string. */ +static QString extractFromParens(const QString name) +{ + QString extracted = name; + int start = extracted.find("("); + if(start != -1) + { + extracted.remove(0, start+1); + } + int end = extracted.findRev(")"); + if(end != -1) + { + int length = extracted.length(); + extracted.remove(end, length); + } + extracted = extracted.stripWhiteSpace(); + + return extracted; +} + +/*! Adds passed modifiers to these modifiers.*/ +SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs) +{ + if(mdfs.protection!=NONE_P) protection = mdfs.protection; + if(mdfs.direction!=NONE_D) direction = mdfs.direction; + optional |= mdfs.optional; + if(!mdfs.dimension.isNull()) dimension = mdfs.dimension; + allocatable |= mdfs.allocatable; + external |= mdfs.external; + intrinsic |= mdfs.intrinsic; + parameter |= mdfs.parameter; + pointer |= mdfs.pointer; + target |= mdfs.target; + save |= mdfs.save; + return *this; +} + +/*! Extracts and adds passed modifier to these modifiers.*/ +SymbolModifiers& SymbolModifiers::operator|=(QString mdfString) +{ + mdfString = mdfString.lower(); + SymbolModifiers newMdf; + + if (mdfString.startsWith("dimension")) + { + newMdf.dimension=mdfString; + } + else if (mdfString.contains("intent")) + { + QString tmp = extractFromParens(mdfString); + bool isin = tmp.contains("in"); + bool isout = tmp.contains("out"); + if(isin && isout) newMdf.direction = SymbolModifiers::INOUT; + else if(isin) newMdf.direction = SymbolModifiers::IN; + else if(isout) newMdf.direction = SymbolModifiers::OUT; + } + else if (mdfString=="public") + { + newMdf.protection = SymbolModifiers::PUBLIC; + } + else if (mdfString=="private") + { + newMdf.protection = SymbolModifiers::PRIVATE; + } + else if (mdfString=="optional") + { + newMdf.optional = TRUE; + } + else if (mdfString=="allocatable") + { + newMdf.allocatable = TRUE; + } + else if (mdfString=="external") + { + newMdf.external = TRUE; + } + else if(mdfString=="intrinsic") + { + newMdf.intrinsic = TRUE; + } + else if(mdfString=="parameter") + { + newMdf.parameter = TRUE; + } + else if(mdfString=="pointer") + { + newMdf.pointer = TRUE; + } + else if(mdfString=="target") + { + newMdf.target = TRUE; + } + else if(mdfString=="save") + { + newMdf.save = TRUE; + } + + (*this) |= newMdf; + return *this; +} + +/*! For debugging purposes. */ +//ostream& operator<<(ostream& out, const SymbolModifiers& mdfs) +//{ +// out<argList->count(); i++) + { + Argument *arg = subprog->argList->at(i); + if(!byTypeName && arg->name.lower() == cname || + byTypeName && arg->type.lower() == cname) + return arg; + } + + return NULL; +} + +/*! Find function with given name in \a entry. */ +#if 0 +static Entry *findFunction(Entry* entry, QString name) +{ + QCString cname(name.lower()); + + EntryListIterator eli(*entry->children()); + Entry *ce; + for (;(ce=eli.current());++eli) + { + if(ce->section != Entry::FUNCTION_SEC) + continue; + + if(ce->name.lower() == cname) + return ce; + } + + return NULL; +} +#endif + +/*! Apply modifiers stored in \a mdfs to the \a typeName string. */ +static QString applyModifiers(QString typeName, SymbolModifiers& mdfs) +{ + if(!mdfs.dimension.isNull()) + { + typeName += ","; + typeName += mdfs.dimension; + } + if(mdfs.direction!=SymbolModifiers::NONE_D) + { + typeName += ","; + typeName += directionStrs[mdfs.direction]; + } + if(mdfs.optional) + { + typeName += ","; + typeName += "optional"; + } + if(mdfs.allocatable) + { + typeName += ","; + typeName += "allocatable"; + } + if(mdfs.external) + { + typeName += ","; + typeName += "external"; + } + if(mdfs.intrinsic) + { + typeName += ","; + typeName += "intrinsic"; + } + if(mdfs.parameter) + { + typeName += ","; + typeName += "parameter"; + } + if(mdfs.pointer) + { + typeName += ","; + typeName += "pointer"; + } + if(mdfs.target) + { + typeName += ","; + typeName += "target"; + } + if(mdfs.save) + { + typeName += ","; + typeName += "save"; + } + + return typeName; +} + +/*! Apply modifiers stored in \a mdfs to the \a arg argument. */ +static void applyModifiers(Argument *arg, SymbolModifiers& mdfs) +{ + QString tmp = arg->type; + arg->type = applyModifiers(tmp, mdfs); +} + +/*! Apply modifiers stored in \a mdfs to the \a ent entry. */ +static void applyModifiers(Entry *ent, SymbolModifiers& mdfs) +{ + QString tmp = ent->type; + ent->type = applyModifiers(tmp, mdfs); + + if(mdfs.protection == SymbolModifiers::PUBLIC) + ent->protection = Public; + else if(mdfs.protection == SymbolModifiers::PRIVATE) + ent->protection = Private; +} + +/*! Starts the new scope in fortran program. Consider using this function when + * starting module, interface, function or other program block. + * \see endScope() + */ +static void startScope(Entry *scope) +{ + //cout<<"start scope: "<name< mdfMap; + modifiers.insert(scope, mdfMap); +} + +/*! Ends scope in fortran program: may update subprogram arguments or module variable attributes. + * \see startScope() + */ +static bool endScope(Entry *scope) +{ + //cout<<"end scope: "<name<parent()) + { + current_root= current_root->parent(); /* end substructure */ + } + else + { + fprintf(stderr,"parse error in end "); + scanner_abort(); + return FALSE; + } + + // update variables or subprogram arguments with modifiers + QMap& mdfsMap = modifiers[scope]; + + if(scope->section == Entry::FUNCTION_SEC) + { + // iterate all symbol modifiers of the scope + for(QMap::Iterator it=mdfsMap.begin(); it!=mdfsMap.end(); it++) { + //cout<name.lower()].returnName<name.lower()].returnName.lower(); + if(modifiers[scope].contains(returnName)) + { + scope->type = modifiers[scope][returnName].type; // returning type works + applyModifiers(scope, modifiers[scope][returnName]); // returning array works + } + + } + else if(scope->section == Entry::CLASS_SEC) + { // was INTERFACE_SEC + if(scope->parent()->section == Entry::FUNCTION_SEC) + { // interface within function + // iterate functions of interface and + // try to find types for dummy(ie. argument) procedures. + //cout<<"Search in "<name<children()); + Entry *ce; + for (;(ce=eli.current());++eli) + { + if(ce->section != Entry::FUNCTION_SEC) + continue; + + Argument *arg = findArgument(scope->parent(), ce->name, TRUE); + if(arg != NULL) + { + // set type of dummy procedure argument to interface + arg->name = arg->type; + arg->type = scope->name; + } + } + } + + } + else + { // not function section or interface + // iterate variables: get and apply modifiers + EntryListIterator eli(*scope->children()); + Entry *ce; + for (;(ce=eli.current());++eli) + { + if(ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC) + continue; + + //cout<name<<", "<name.lower())<name.lower())) + applyModifiers(ce, mdfsMap[ce->name.lower()]); + } + } + + // clear all modifiers of the scope + modifiers.remove(scope); + + return TRUE; +} + +//! Return full name of the entry. Sometimes we must combine several names recursively. +static QString getFullName(Entry *e) +{ + QString name = e->name; + if(e->section == Entry::CLASS_SEC // || e->section == Entry::INTERFACE_SEC + || !e->parent() || e->parent()->name.isEmpty()) + return name; + + return getFullName(e->parent())+"::"+name; +} + +static int yyread(char *buf,int max_size) +{ + int c=0; + while( c < max_size && inputString[inputPosition] ) + { + *buf = inputString[inputPosition++] ; + c++; buf++; + } + return c; +} + +static void initParser() +{ + last_entry = 0; +} + +static void initEntry() +{ + current->protection = defaultProtection ; + current->mtype = mtype; + current->virt = virt; + current->stat = gstat; + initGroupInfo(current); +} + +/** + adds current entry to current_root and creates new current +*/ +static void addCurrentEntry() +{ + //cout << "Adding entry " <name.data() << endl; + current_root->addSubEntry(current); + last_entry = current; + current = new Entry ; + initEntry(); +} + +/*! Adds interface to the root entry. + * \note Code was brought to this procedure from the parser, + * because there was/is idea to use it in several parts of the parser. + */ +static void addInterface(QString name) +{ + current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC; + current->spec = Entry::Interface; + current->name = name; + + /* if type is part of a module, mod name is necessary for output */ + if ((current_root) && + (current_root->section == Entry::CLASS_SEC || + current_root->section == Entry::NAMESPACE_SEC)) + { + current->name= current_root->name+"::"+current->name; + } + if ((current_root) && + (current_root->section == Entry::FUNCTION_SEC)) + { + current->name = getFullName(current_root) + "__" + QString(current->name); + } + + current->fileName = yyFileName; + current->bodyLine = yyLineNr; + addCurrentEntry(); +} + + +//----------------------------------------------------------------------------- + +/*! Update the argument \a name with additional \a type info. + */ +static Argument *addFortranParameter(const QCString &type,const QCString &name, const QString docs) +{ + //cout<<"addFortranParameter(): "<argList==0) return FALSE; + ArgumentListIterator ali(*current_root->argList); + Argument *a; + for (ali.toFirst();(a=ali.current());++ali) + { + if (a->type.lower()==name.lower()) + { + ret=a; +//cout << "addParameter found: " << type << " , " << name << endl; + a->type=type.stripWhiteSpace(); + a->name=name.stripWhiteSpace(); + if(!docs.isNull()) + a->docs = docs; + break; + } + } // for + return ret; +} + + //---------------------------------------------------------------------------- +static void startCommentBlock(bool brief) +{ + if (brief) + { + current->briefFile = yyFileName; + current->briefLine = yyLineNr; + } + else + { + current->docFile = yyFileName; + current->docLine = yyLineNr; + } +} + + //---------------------------------------------------------------------------- +static void handleCommentBlock(const QCString &doc,bool brief) +{ + docBlockInBody = FALSE; + bool needsEntry = FALSE; + static bool hideInBodyDocs = Config_getBool("HIDE_IN_BODY_DOCS"); + int position=0; + if (docBlockInBody && hideInBodyDocs) return; + //fprintf(stderr,"call parseCommentBlock [%s]\n",doc.data()); + while (parseCommentBlock( + g_thisParser, + docBlockInBody ? last_entry : current, + doc, // text + yyFileName, // file + brief ? current->briefLine : current->docLine, // line of block start + docBlockInBody ? FALSE : brief, + docBlockInBody ? FALSE : docBlockJavaStyle, + docBlockInBody, + defaultProtection, + position, + needsEntry + )) + { + //fprintf(stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry); + if (needsEntry) addCurrentEntry(); + } + //fprintf(stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry); + + if (needsEntry) addCurrentEntry(); +} + +//---------------------------------------------------------------------------- +static int level=0; +static void debugCompounds(Entry *rt) // print Entry structure (for debugging) +{ + level++; + printf("%d) debugCompounds(%s) line %d\n",level, rt->name.data(), rt->bodyLine); + EntryListIterator eli(*rt->children()); + Entry *ce; + for (;(ce=eli.current());++eli) + { + debugCompounds(ce); + } +level--; +} + + +static void parseMain(const char *fileName,const char *fileBuf,Entry *rt) +{ + initParser(); + + defaultProtection = Public; + inputString = fileBuf; + inputPosition = 0; + + //anonCount = 0; // don't reset per file + mtype = Method; + gstat = FALSE; + virt = Normal; + current_root = rt; + global_root = rt; + inputFile.setName(fileName); + if (inputFile.open(IO_ReadOnly)) + { + yyLineNr= 1 ; + yyFileName = fileName; + msg("Parsing file %s...\n",yyFileName.data()); + + current_root = rt ; + initParser(); + groupEnterFile(yyFileName,yyLineNr); + + current = new Entry; + current->name = yyFileName; + current->section = Entry::SOURCE_SEC; + current_root->addSubEntry(current); + file_root = current; + current = new Entry; + + fscanYYrestart( fscanYYin ); + { + BEGIN( Start ); + } + + fscanYYlex(); + groupLeaveFile(yyFileName,yyLineNr); + + //debugCompounds(rt); //debug + + rt->program.resize(0); + delete current; current=0; + moduleProcedures.clear(); + + inputFile.close(); + } +} + +//---------------------------------------------------------------------------- + +void FortranLanguageScanner::parseInput(const char *fileName,const char *fileBuf,Entry *root) +{ + g_thisParser = this; + ::parseMain(fileName,fileBuf,root); +} + +void FortranLanguageScanner::parseCode(CodeOutputInterface & codeOutIntf, + const char * scopeName, + const QCString & input, + bool isExampleBlock, + const char * exampleName, + FileDef * fileDef, + int startLine, + int endLine, + bool inlineFragment, + MemberDef *memberDef + ) +{ + ::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName, + fileDef,startLine,endLine,inlineFragment,memberDef); +} + +bool FortranLanguageScanner::needsPreprocessing(const QCString &extension) +{ + (void)extension; + return TRUE; +} +void FortranLanguageScanner::resetCodeParserState() +{ + ::resetFortranCodeParserState(); +} + +void FortranLanguageScanner::parsePrototype(const char *text) +{ + (void)text; +} + +static void scanner_abort() +{ + fprintf(stderr,"********************************************************************\n"); + fprintf(stderr,"Error in file %s line: %d, state: %d\n",yyFileName.data(),yyLineNr,YY_START); + fprintf(stderr,"********************************************************************\n"); + + EntryListIterator eli(*global_root->children()); + Entry *ce; + bool start=FALSE; + + for (;(ce=eli.current());++eli) + { + if (ce == file_root) start=TRUE; + if (start) ce->reset(); + } + + return; + //exit(-1); +} + +//---------------------------------------------------------------------------- + +#if !defined(YY_FLEX_SUBMINOR_VERSION) +//---------------------------------------------------------------------------- +extern "C" { // some bogus code to keep the compiler happy + void fscannerYYdummy() { yy_flex_realloc(0,0); } +} +#endif + diff --git a/src/htmlgen.cpp b/src/htmlgen.cpp index 5442ed7..1cf596d 100644 --- a/src/htmlgen.cpp +++ b/src/htmlgen.cpp @@ -930,6 +930,7 @@ void HtmlGenerator::writeCodeLink(const char *ref,const char *f, const char *tooltip) { QCString *dest; + //printf("writeCodeLink(ref=%s,f=%s,anchor=%s,name=%s,tooltip=%s)\n",ref,f,anchor,name,tooltip); if (ref) { t << "addContentsItem(TRUE, convertToHtml(title), gd->getOutputFileBase(),0); + htmlHelp->addContentsItem(TRUE, convertToHtml(title,TRUE), gd->getOutputFileBase(),0); htmlHelp->incContentsDepth(); } if (ftvHelp) @@ -2715,7 +2715,7 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) SectionInfo *si=0; if (!pd->name().isEmpty()) si=Doxygen::sectionDict[pd->name()]; if (htmlHelp) htmlHelp->addContentsItem(FALSE, - convertToHtml(pd->title()), + convertToHtml(pd->title(),TRUE), gd->getOutputFileBase(), si ? si->label.data() : 0 ); @@ -2723,7 +2723,7 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) gd->getReference(), gd->getOutputFileBase(), si ? si->label.data() : 0, - convertToHtml(pd->title()) + convertToHtml(pd->title(),TRUE) ); } @@ -2762,7 +2762,7 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) { if (htmlHelp) { - htmlHelp->addContentsItem(TRUE, convertToHtml(theTranslator->trNamespaces()), gd->getOutputFileBase(), 0); + htmlHelp->addContentsItem(TRUE, convertToHtml(theTranslator->trNamespaces(),TRUE), gd->getOutputFileBase(), 0); htmlHelp->incContentsDepth(); } @@ -2779,11 +2779,11 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) { if (htmlHelp) { - htmlHelp->addContentsItem(FALSE, convertToHtml(nsd->name()), nsd->getOutputFileBase()); + htmlHelp->addContentsItem(FALSE, convertToHtml(nsd->name(),TRUE), nsd->getOutputFileBase()); } if (ftvHelp) { - ftvHelp->addContentsItem(FALSE, nsd->getReference(), nsd->getOutputFileBase(), 0, convertToHtml(nsd->name())); + ftvHelp->addContentsItem(FALSE, nsd->getReference(), nsd->getOutputFileBase(), 0, convertToHtml(nsd->name(),TRUE)); } } if (htmlHelp) htmlHelp->decContentsDepth(); @@ -2795,7 +2795,7 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) { if (htmlHelp) { - htmlHelp->addContentsItem(TRUE, convertToHtml(theTranslator->trClasses()), gd->getOutputFileBase(), 0); + htmlHelp->addContentsItem(TRUE, convertToHtml(theTranslator->trClasses(),TRUE), gd->getOutputFileBase(), 0); htmlHelp->incContentsDepth(); } @@ -2832,7 +2832,7 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) if (htmlHelp) { htmlHelp->addContentsItem(TRUE, - convertToHtml(theTranslator->trFile(TRUE,FALSE)), + convertToHtml(theTranslator->trFile(TRUE,FALSE),TRUE), gd->getOutputFileBase(), 0); htmlHelp->incContentsDepth(); } @@ -2850,9 +2850,9 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) while (fd) { if (htmlHelp) - htmlHelp->addContentsItem(FALSE,convertToHtml(fd->name()),fd->getOutputFileBase()); + htmlHelp->addContentsItem(FALSE,convertToHtml(fd->name(),TRUE),fd->getOutputFileBase()); if (ftvHelp) - ftvHelp->addContentsItem(FALSE, fd->getReference(), fd->getOutputFileBase(), 0, convertToHtml(fd->name())); + ftvHelp->addContentsItem(FALSE, fd->getReference(), fd->getOutputFileBase(), 0, convertToHtml(fd->name(),TRUE)); fd=fileList->next(); } if (htmlHelp) @@ -2866,7 +2866,7 @@ void writeGroupTreeNode(OutputList &ol, GroupDef *gd,int level) { if (htmlHelp) { - htmlHelp->addContentsItem(TRUE, convertToHtml(theTranslator->trExamples()), gd->getOutputFileBase(), 0); + htmlHelp->addContentsItem(TRUE, convertToHtml(theTranslator->trExamples(),TRUE), gd->getOutputFileBase(), 0); htmlHelp->incContentsDepth(); } @@ -2991,9 +2991,9 @@ void writeDirTreeNode(OutputList &ol, DirDef *dd,int level) while (fd) { if (htmlHelp) - htmlHelp->addContentsItem(FALSE,convertToHtml(fd->name()),fd->getOutputFileBase()); + htmlHelp->addContentsItem(FALSE,convertToHtml(fd->name(),TRUE),fd->getOutputFileBase()); if (ftvHelp) - ftvHelp->addContentsItem(FALSE, fd->getReference(), fd->getOutputFileBase(), 0, convertToHtml(fd->name())); + ftvHelp->addContentsItem(FALSE, fd->getReference(), fd->getOutputFileBase(), 0, convertToHtml(fd->name(),TRUE)); fd=fileList->next(); } } diff --git a/src/latexgen.cpp b/src/latexgen.cpp index d7257f1..9a02020 100644 --- a/src/latexgen.cpp +++ b/src/latexgen.cpp @@ -53,10 +53,12 @@ //} -static QCString escapeLabelName(const char *s) +static QCString escapeLabelName(LatexGenerator *g,const char *s) { QCString result; const char *p=s; + char str[2]; + str[1]=0; char c; while ((c=*p++)) { @@ -65,7 +67,7 @@ static QCString escapeLabelName(const char *s) case '%': result+="\\%"; break; case '|': result+="\\tt{\"|}"; break; case '!': result+="\"!"; break; - default: result+=c; + default: str[0]=c; g->docify(str); break; } } return result; @@ -1069,7 +1071,7 @@ void LatexGenerator::endTitleHead(const char *fileName,const char *name) if (name) { t << "\\label{" << fileName << "}\\index{" - << name << "@{"; + << escapeLabelName(this,name) << "@{"; escapeMakeIndexChars(this,t,name); t << "}}" << endl; } @@ -1139,15 +1141,15 @@ void LatexGenerator::startMemberDoc(const char *clname, t << "\\index{"; if (clname) { - t << clname << "@{"; - docify(clname); + t << escapeLabelName(this,clname) << "@{"; + escapeMakeIndexChars(this,t,clname); t << "}!"; } - t << escapeLabelName(memname) << "@{"; + t << escapeLabelName(this,memname) << "@{"; escapeMakeIndexChars(this,t,memname); t << "}}" << endl; - t << "\\index{" << escapeLabelName(memname) << "@{"; + t << "\\index{" << escapeLabelName(this,memname) << "@{"; escapeMakeIndexChars(this,t,memname); t << "}"; if (clname) @@ -1221,12 +1223,12 @@ void LatexGenerator::addIndexItem(const char *s1,const char *s2) { if (s1) { - t << "\\index{" << escapeLabelName(s1) << "@{"; + t << "\\index{" << escapeLabelName(this,s1) << "@{"; escapeMakeIndexChars(this,t,s1); t << "}"; if (s2) { - t << "!" << escapeLabelName(s2) << "@{"; + t << "!" << escapeLabelName(this,s2) << "@{"; escapeMakeIndexChars(this,t,s2); t << "}"; } @@ -1356,9 +1358,9 @@ void LatexGenerator::startAnonTypeScope(int indent) { t << "\\begin{tabbing}" << endl; t << "xx\\=xx\\=xx\\=xx\\=xx\\=xx\\=xx\\=xx\\=xx\\=\\kill" << endl; - //printf("LatexGenerator::startMemberItem() insideTabbing=TRUE\n"); insideTabbing=TRUE; } + m_indent=indent; } void LatexGenerator::endAnonTypeScope(int indent) @@ -1368,6 +1370,7 @@ void LatexGenerator::endAnonTypeScope(int indent) t << endl << "\\end{tabbing}"; insideTabbing=FALSE; } + m_indent=indent; } void LatexGenerator::startMemberTemplateParams() @@ -1401,7 +1404,7 @@ void LatexGenerator::endMemberItem() if (insideTabbing) { t << "\\\\"; - } + } templateMemberItem = FALSE; t << endl; } @@ -1414,7 +1417,7 @@ void LatexGenerator::startMemberDescription() } else { - for (int i=0;i"; + for (int i=0;i"; t << "{\\em "; } } @@ -1427,18 +1430,17 @@ void LatexGenerator::endMemberDescription() } else { - t << "}\\\\"; - m_indent=0; + t << "}\\\\\n"; } } void LatexGenerator::writeNonBreakableSpace(int) { + //printf("writeNonBreakbleSpace()\n"); if (insideTabbing) { t << "\\>"; - m_indent++; } else t << "~"; diff --git a/src/libdoxygen.pro.in b/src/libdoxygen.pro.in index 12dfbd7..78ed341 100644 --- a/src/libdoxygen.pro.in +++ b/src/libdoxygen.pro.in @@ -82,6 +82,8 @@ HEADERS = bufstr.h \ printdocvisitor.h \ pycode.h \ pyscanner.h \ + fortrancode.h \ + fortranscanner.h \ qtbc.h \ reflist.h \ rtfdocvisitor.h \ @@ -190,6 +192,8 @@ SOURCES = ce_lex.cpp \ pre.cpp \ pycode.cpp \ pyscanner.cpp \ + fortrancode.cpp \ + fortranscanner.cpp \ reflist.cpp \ rtfdocvisitor.cpp \ rtfgen.cpp \ diff --git a/src/libdoxygen.t b/src/libdoxygen.t index 541ec97..f2cc6cb 100644 --- a/src/libdoxygen.t +++ b/src/libdoxygen.t @@ -57,6 +57,12 @@ sub GenerateDep { #$ GenerateDep("pycode.cpp","pycode.l"); $(LEX) -PpycodeYY -t pycode.l | $(INCBUFSIZE) >pycode.cpp +#$ GenerateDep("fortranscanner.cpp","fortranscanner.l"); + $(LEX) -i -PfscanYY -t fortranscanner.l | $(INCBUFSIZE) >fortranscanner.cpp + +#$ GenerateDep("fortrancode.cpp","fortrancode.l"); + $(LEX) -i -PfcodeYY -t fortrancode.l | $(INCBUFSIZE) >fortrancode.cpp + #$ GenerateDep("pre.cpp","pre.l"); $(LEX) -PpreYY -t pre.l | $(INCBUFSIZE) >pre.cpp diff --git a/src/memberdef.cpp b/src/memberdef.cpp index 14d1c2d..49a1794 100644 --- a/src/memberdef.cpp +++ b/src/memberdef.cpp @@ -259,6 +259,10 @@ static bool writeDefArgumentList(OutputList &ol,ClassDef *cd, ol.endParameterName(TRUE,TRUE,!md->isObjCMethod()); } ol.popGeneratorState(); + if (md->extraTypeChars()) + { + ol.docify(md->extraTypeChars()); + } if (defArgList->constSpecifier) { ol.docify(" const"); @@ -334,6 +338,7 @@ class MemberDefImpl QCString write; // property write accessor QCString exception; // exceptions that can be thrown QCString initializer; // initializer + QCString extraTypeChars; // extra type info found after the argument list int initLines; // number of lines in the initializer int memSpec; // The specifiers present for this member @@ -525,7 +530,7 @@ void MemberDefImpl::init(Definition *def, if (!args.isEmpty()) { declArgList = new ArgumentList; - stringToArgumentList(args,declArgList); + stringToArgumentList(args,declArgList,&extraTypeChars); //printf("setDeclArgList %s to %p const=%d\n",args.data(), // declArgList,declArgList->constSpecifier); } @@ -725,6 +730,11 @@ QCString MemberDef::getOutputFileBase() const QCString MemberDef::getReference() const { makeResident(); + QCString ref = Definition::getReference(); + if (!ref.isEmpty()) + { + return ref; + } if (m_impl->templateMaster) { return m_impl->templateMaster->getReference(); @@ -1406,7 +1416,10 @@ void MemberDef::writeDeclaration(OutputList &ol, //printf("endMember %s annoClassDef=%p annEnumType=%p\n", // name().data(),annoClassDef,annEnumType); ol.endMemberItem(); - if (endAnonScopeNeeded) ol.endAnonTypeScope(--s_indentLevel); + if (endAnonScopeNeeded) + { + ol.endAnonTypeScope(--s_indentLevel); + } // write brief description if (!briefDescription().isEmpty() && @@ -2872,6 +2885,7 @@ void MemberDef::setTagInfo(TagInfo *ti) if (ti) { makeResident(); + //printf("%s: Setting tag name=%s anchor=%s\n",name().data(),ti->tagName.data(),ti->anchor.data()); m_impl->anc=ti->anchor; setReference(ti->tagName); m_impl->explicitOutputFileBase = stripExtension(ti->fileName); @@ -2905,7 +2919,13 @@ const char *MemberDef::declaration() const const char *MemberDef::definition() const { makeResident(); - return m_impl->def; + return m_impl->def; +} + +const char *MemberDef::extraTypeChars() const +{ + makeResident(); + return m_impl->extraTypeChars; } const char *MemberDef::typeString() const @@ -3661,6 +3681,7 @@ void MemberDef::flushToDisk() const marshalQCString (Doxygen::symbolStorage,m_impl->write); marshalQCString (Doxygen::symbolStorage,m_impl->exception); marshalQCString (Doxygen::symbolStorage,m_impl->initializer); + marshalQCString (Doxygen::symbolStorage,m_impl->extraTypeChars); marshalInt (Doxygen::symbolStorage,m_impl->initLines); marshalInt (Doxygen::symbolStorage,m_impl->memSpec); marshalInt (Doxygen::symbolStorage,(int)m_impl->mtype); @@ -3760,6 +3781,7 @@ void MemberDef::loadFromDisk() const m_impl->write = unmarshalQCString (Doxygen::symbolStorage); m_impl->exception = unmarshalQCString (Doxygen::symbolStorage); m_impl->initializer = unmarshalQCString (Doxygen::symbolStorage); + m_impl->extraTypeChars = unmarshalQCString (Doxygen::symbolStorage); m_impl->initLines = unmarshalInt (Doxygen::symbolStorage); m_impl->memSpec = unmarshalInt (Doxygen::symbolStorage); m_impl->mtype = (MemberDef::MemberType)unmarshalInt (Doxygen::symbolStorage); diff --git a/src/memberdef.h b/src/memberdef.h index 385b353..8715ec9 100644 --- a/src/memberdef.h +++ b/src/memberdef.h @@ -89,6 +89,7 @@ class MemberDef : public Definition const char *argsString() const; const char *excpString() const; const char *bitfieldString() const; + const char *extraTypeChars() const; const QCString &initializer() const; int initializerLines() const; int getMemberSpecifiers() const; diff --git a/src/msc.cpp b/src/msc.cpp index f68858a..eb1ae6d 100644 --- a/src/msc.cpp +++ b/src/msc.cpp @@ -148,7 +148,7 @@ QString getMscImageMapFromFile(const QString& inFile, const QString& outDir, QDir::setCurrent(outDir); //printf("Going to dir %s\n",QDir::currentDirPath().data()); - QCString mscExe = "mscgen"; + QCString mscExe = Config_getString("MSCGEN_PATH")+"mscgen"+portable_commandExtension(); QCString mscArgs = "-T ismap -i \""; mscArgs+=inFile + ".msc\" -o \""; mscArgs+=outFile + "\""; diff --git a/src/pagedef.cpp b/src/pagedef.cpp index 7ed229f..3859640 100644 --- a/src/pagedef.cpp +++ b/src/pagedef.cpp @@ -79,7 +79,7 @@ void PageDef::writeDocumentation(OutputList &ol) (si=Doxygen::sectionDict.find(pageName))!=0) { ol.startSection(si->label,si->title,si->type); - ol.docify(si->title); + ol.parseDoc(docFile(),docLine(),this,0,si->title,TRUE,FALSE); stringToSearchIndex(getOutputFileBase(), theTranslator->trPage(TRUE,TRUE)+" "+si->title, si->title); diff --git a/src/pre.h b/src/pre.h index e015d56..5bc0f8b 100644 --- a/src/pre.h +++ b/src/pre.h @@ -21,9 +21,11 @@ #include "qtbc.h" #include //#include +#include "define.h" class BufStr; +DefineDict* getFileDefineDict(); void initPreprocessor(); void cleanUpPreprocessor(); void addSearchDir(const char *dir); diff --git a/src/pre.l b/src/pre.l index f46a138..cc92d66 100644 --- a/src/pre.l +++ b/src/pre.l @@ -104,6 +104,9 @@ static QStack g_condStack; static bool g_lexInit = FALSE; +DefineDict* getFileDefineDict() { + return g_fileDefineDict; +} static void setFileName(const char *name) { diff --git a/src/scanner.l b/src/scanner.l index 584f1b2..4fd024a 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -666,6 +666,7 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) %x MemberSpecSkip %x EndTemplate %x FuncPtr +%x FuncPtrOperator %x EndFuncPtr %x ReadFuncArgType %x ReadTempArgs @@ -686,6 +687,7 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) %x FuncFunc %x FuncFuncEnd %x FuncFuncType +%x FuncFuncArray %x CopyArgString %x CopyArgPHPString %x CopyArgRound @@ -1866,6 +1868,8 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) } } } +[0-9]{ID} { // some number where we did not expect one + } "." { if (insideJava || insideCS || insideD) { @@ -2691,14 +2695,18 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) } . { } -"]" { current->args += *yytext ; +"]" { current->args += *yytext ; if (--squareCount<=0) BEGIN( FindMembers ) ; } -"[" { current->args += *yytext ; +"]" { current->args += *yytext ; + if (--squareCount<=0) + BEGIN( Function ) ; + } +"[" { current->args += *yytext ; squareCount++; } -. { current->args += *yytext ; } +. { current->args += *yytext ; } "[" { squareCount++; } "]" { if (--squareCount<=0) @@ -3155,7 +3163,7 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) . { current->program += yytext ; } "("/{BN}*({TSCOPE}{BN}*"::")*{TSCOPE}{BN}*")"{BN}*"(" | /* typedef void (A::func_t)(args...) */ -("("({BN}*{TSCOPE}{BN}*"::")*({BN}*"*"{BN}*)+)+ { /* typedef void (A::*ptr_t)(args...) */ +("("({BN}*{TSCOPE}{BN}*"::")*({BN}*[*&]{BN}*)+)+ { /* typedef void (A::*ptr_t)(args...) or int (*func(int))[] */ current->bodyLine = yyLineNr; lineCount(); addType(current); @@ -3166,18 +3174,41 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) } {SCOPENAME} { current->name = yytext; - if (current->name=="const" || current->name=="volatile") + if (nameIsOperator(current->name)) { - funcPtrType += current->name; + BEGIN( FuncPtrOperator ); } else { - BEGIN( EndFuncPtr ); + if (current->name=="const" || current->name=="volatile") + { + funcPtrType += current->name; + } + else + { + BEGIN( EndFuncPtr ); + } } } . { //printf("Error: FuncPtr `%c' unexpected at line %d of %s\n",*yytext,yyLineNr,yyFileName); } +"("{BN}*")"{BN}*/"(" { + current->name += yytext; + current->name = current->name.simplifyWhiteSpace(); + lineCount(); + } +\n { + yyLineNr++; + current->name += *yytext; + } +"(" { + unput(*yytext); + BEGIN( EndFuncPtr ); + } +. { + current->name += *yytext; + } ")"{BN}*/";" { // a variable with extra braces lineCount(); current->type+=funcPtrType.data()+1; @@ -3194,10 +3225,16 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->args += ")"; BEGIN(FindMembers); } -"(" { // a function returning a function +"(" { // a function returning a function or + // a function returning a pointer to an array current->args += *yytext ; - roundCount=0; - BEGIN( FuncFunc ); + //roundCount=0; + //BEGIN( FuncFunc ); + current->bodyLine = yyLineNr; + currentArgumentContext = FuncFuncEnd; + fullArgString=current->args.copy(); + copyArgString=¤t->args; + BEGIN( ReadFuncArgType ) ; } "["[^\n\]]*"]" { funcPtrType+=yytext; @@ -3228,6 +3265,12 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->type+=funcPtrType.data()+1; BEGIN(Function); } +")"{BN}*/"[" { // function returning a pointer to an array + lineCount(); + current->type+=funcPtrType; + current->args+=")"; + BEGIN(FuncFuncArray); + } . { current->args += *yytext; } @@ -3827,31 +3870,27 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) // was: current->args.simplifyWhiteSpace(); current->fileName = yyFileName; current->startLine = yyLineNr; + static QRegExp re("([^)]*\\[*&][^)]*)"); // (...*...) if (*yytext!=';' || (current_root->section&Entry::COMPOUND_MASK) ) { int tempArg=current->name.find('<'); QCString tempName; - static QRegExp re("operator[^a-z_A-Z0-9]"); if (tempArg==-1) tempName=current->name; else tempName=current->name.left(tempArg); - if (/*(current->type.isEmpty() && tempName.find(re)==-1) || */ - current->type.left(8)=="typedef " - ) + if (current->type.isEmpty() && + (current->type.find(re,0)!=-1 || current->type.left(8)=="typedef ")) { - //printf("Scanner.l: found in class variable: `%s' `%s' `%s'\n", - // current->type.data(),current->name.data(),current->args.data()); + //printf("Scanner.l: found in class variable: `%s' `%s' `%s'\n", current->type.data(),current->name.data(),current->args.data()); current->section = Entry::VARIABLE_SEC ; } else { - //printf("Scanner.l: found in class function: `%s' `%s' `%s'\n", - // current->type.data(),current->name.data(),current->args.data()); + //printf("Scanner.l: found in class function: `%s' `%s' `%s'\n", current->type.data(),current->name.data(),current->args.data()); current->section = Entry::FUNCTION_SEC ; current->proto = *yytext==';'; } } else // a global function prototype or function variable { - static QRegExp re("([^)]*\\*[^)]*)"); // (...*...) //printf("Scanner.l: prototype? type=`%s' name=`%s' args=`%s'\n",current->type.data(),current->name.data(),current->args.data()); if (!current->type.isEmpty() && (current->type.find(re,0)!=-1 || current->type.left(8)=="typedef ")) @@ -4975,14 +5014,14 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) "operator"{B}*"("{B}*")" { current->name+=yytext; } -"(" { +"(" { current->args+=*yytext; currentArgumentContext = PrototypeQual; fullArgString = current->args.copy(); copyArgString = ¤t->args; BEGIN( ReadFuncArgType ) ; } -"("({ID}"::")*({B}*"*")+ { +"("({ID}"::")*({B}*[&*])+ { current->type+=current->name+yytext; current->name.resize(0); BEGIN( PrototypePtr ); @@ -4990,10 +5029,20 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) {SCOPENAME} { current->name+=yytext; } +"(" { + current->args+=*yytext; + currentArgumentContext = PrototypeQual; + fullArgString = current->args.copy(); + copyArgString = ¤t->args; + BEGIN( ReadFuncArgType ) ; + } ")" { current->type+=')'; BEGIN( Prototype ); } +. { + current->name+=yytext; + } "{" { BEGIN( PrototypeSkipLine); } @@ -5010,7 +5059,7 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->virt = Pure; current->argList->pureSpecifier=TRUE; } -"throw"{B}*"(" { +"throw"{B}*"(" { current->exception = "throw("; BEGIN(PrototypeExc); } @@ -5018,10 +5067,13 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->exception += ')'; BEGIN(PrototypeQual); } -. { +. { current->exception += *yytext; } -. { +. { + current->args += *yytext; + } +. { current->name += *yytext; } . { diff --git a/src/util.cpp b/src/util.cpp index 5ab4a29..0a271c4 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -690,17 +690,20 @@ static Definition *followPath(Definition *start,FileDef *fileScope,const QCStrin int l; Definition *current=start; ps=0; + //printf("followPath: start='%s' path='%s'\n",start?start->name().data():"",path.data()); // for each part of the explicit scope while ((is=getScopeFragment(path,ps,&l))!=-1) { // try to resolve the part if it is a typedef MemberDef *typeDef=0; QCString qualScopePart = substTypedef(current,fileScope,path.mid(is,l),&typeDef); + //printf(" qualScopePart=%s\n",qualScopePart.data()); if (typeDef) { ClassDef *type = newResolveTypedef(fileScope,typeDef); if (type) { + //printf("Found type %s\n",type->name().data()); return type; } } @@ -714,14 +717,15 @@ static Definition *followPath(Definition *start,FileDef *fileScope,const QCStrin //printf("==> next==0!\n"); if (current->definitionType()==Definition::TypeNamespace) { - current = endOfPathIsUsedClass( + next = endOfPathIsUsedClass( ((NamespaceDef *)current)->getUsedClasses(),qualScopePart); } else if (current->definitionType()==Definition::TypeFile) { - current = endOfPathIsUsedClass( + next = endOfPathIsUsedClass( ((FileDef *)current)->getUsedClasses(),qualScopePart); } + current = next; if (current==0) break; } else // continue to follow scope @@ -875,7 +879,7 @@ int isAccessibleFrom(Definition *scope,FileDef *fileScope,Definition *item) // repeat for the parent scope i=isAccessibleFrom(scope->getOuterScope(),fileScope,item); //printf("> result=%d\n",i); - result= (i==-1) ? -1 : i+1; + result= (i==-1) ? -1 : i+2; } done: visitedDict.remove(key); @@ -914,7 +918,7 @@ int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope, if (visitedDict.find(key)) return -1; // already looked at this visitedDict.insert(key,(void *)0x8); - //printf("name().data():"", + //printf(" name().data():"", // item?item->name().data():"", // explicitScopePart.data()); int result=0; // assume we found it @@ -922,12 +926,12 @@ int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope, if (newScope) // explicitScope is inside scope => newScope is the result { Definition *itemScope = item->getOuterScope(); - //printf("scope traversal successful %s<->%s!\n",item->getOuterScope()->name().data(),newScope->name().data()); - if (newScope && newScope->definitionType()==Definition::TypeClass) - { - //ClassDef *cd = (ClassDef *)newScope; - //printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses()); - } + //printf(" scope traversal successful %s<->%s!\n",item->getOuterScope()->name().data(),newScope->name().data()); + //if (newScope && newScope->definitionType()==Definition::TypeClass) + //{ + // ClassDef *cd = (ClassDef *)newScope; + // printf("---> Class %s: bases=%p\n",cd->name().data(),cd->baseClasses()); + //} if (itemScope==newScope) // exact match of scopes => distance==0 { //printf("> found it\n"); @@ -941,9 +945,14 @@ int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope, // inheritance is also ok. Example: looking for B::I, where // class A { public: class I {} }; // class B : public A {} + // but looking for B::I, where + // class A { public: class I {} }; + // class B { public: class I {} }; + // will find A::I, so we still prefer a direct match and give this one a distance of 1 + result=1; - //printf("outerScope(%s) is base class of newScope(%s)\n", - // outerScope->name().data(),newScope->name().data()); + //printf("scope(%s) is base class of newScope(%s)\n", + // scope->name().data(),newScope->name().data()); } else { @@ -998,7 +1007,7 @@ int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope, item,explicitScopePart); } //printf("> result=%d\n",i); - result = (i==-1) ? -1 : i+1; + result = (i==-1) ? -1 : i+2; } } else // failed to resolve explicitScope @@ -1033,11 +1042,11 @@ int isAccessibleFromWithExpScope(Definition *scope,FileDef *fileScope, int i=isAccessibleFromWithExpScope(scope->getOuterScope(),fileScope, item,explicitScopePart); //printf("> result=%d\n",i); - result= (i==-1) ? -1 : i+1; + result= (i==-1) ? -1 : i+2; } } done: - //printf("> result=%d\n",result); + //printf(" > result=%d\n",result); visitedDict.remove(key); //Doxygen::lookupCache.insert(key,new int(result)); return result; @@ -1060,7 +1069,7 @@ static void getResolvedSymbol(Definition *scope, QCString &bestResolvedType ) { - //printf(" found type %x name=%s d=%p\n", + //printf(" => found type %x name=%s d=%p\n", // d->definitionType(),d->name().data(),d); // only look at classes and members that are enums or typedefs @@ -1160,6 +1169,13 @@ static void getResolvedSymbol(Definition *scope, bestTemplSpec = ""; bestResolvedType = enumType->qualifiedName(); } + else if (md->isReference()) // external reference + { + bestMatch = 0; + bestTypedef = md; + bestTemplSpec = spec; + bestResolvedType = type; + } else { //printf(" no match\n"); @@ -1285,6 +1301,9 @@ ClassDef *getResolvedClassRec(Definition *scope, //printf("Searching for %s result=%p\n",key.data(),pval); if (pval) { + //printf("LookupInfo %p %p '%s' %p\n", + // pval->classDef, pval->typeDef, pval->templSpec.data(), + // pval->resolvedType.data()); if (pTemplSpec) *pTemplSpec=pval->templSpec; if (pTypeDef) *pTypeDef=pval->typeDef; if (pResolvedType) *pResolvedType=pval->resolvedType; @@ -1662,10 +1681,12 @@ void linkifyText(const TextGeneratorIntf &out,Definition *scope, MemberDef *md=0; NamespaceDef *nd=0; GroupDef *gd=0; + //printf("** Match word '%s'\n",matchWord.data()); MemberDef *typeDef=0; if ((cd=getResolvedClass(scope,fileScope,matchWord,&typeDef))) { + //printf("Found class %s\n",cd->name().data()); // add link to the result if (external ? cd->isLinkable() : cd->isLinkableInProject()) { @@ -1675,6 +1696,7 @@ void linkifyText(const TextGeneratorIntf &out,Definition *scope, } else if (typeDef) { + //printf("Found typedef %s\n",typeDef->name().data()); if (external ? typeDef->isLinkable() : typeDef->isLinkableInProject()) { out.writeLink(typeDef->getReference(), @@ -1693,6 +1715,10 @@ void linkifyText(const TextGeneratorIntf &out,Definition *scope, found=TRUE; } } + else + { + //printf(" -> nothing\n"); + } QCString scopeName; if (scope && @@ -1714,14 +1740,11 @@ void linkifyText(const TextGeneratorIntf &out,Definition *scope, ) { //printf("Found ref scope=%s\n",d?d->name().data():""); - if ((external ? md->isLinkable() : md->isLinkableInProject())) - { - //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(), - // md->anchor(),word); - out.writeLink(md->getReference(),md->getOutputFileBase(), - md->anchor(),word); - found=TRUE; - } + //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(), + // md->anchor(),word); + out.writeLink(md->getReference(),md->getOutputFileBase(), + md->anchor(),word); + found=TRUE; } } @@ -4854,7 +4877,7 @@ QCString convertToXML(const char *s) } /*! Converts a string to a HTML-encoded string */ -QCString convertToHtml(const char *s) +QCString convertToHtml(const char *s,bool keepEntities) { QCString result; if (s==0) return result; @@ -4866,7 +4889,30 @@ QCString convertToHtml(const char *s) { case '<': result+="<"; break; case '>': result+=">"; break; - case '&': result+="&"; break; + case '&': if (keepEntities) + { + const char *e=p; + char ce; + while ((ce=*e++)) + { + if (ce==';' || (!(isId(ce) || ce=='#'))) break; + } + if (ce==';') // found end of an entity + { + // copy entry verbatim + result+=c; + while (p%s\n", // aliasValue.data(),paramNum,paramValue.data(),result.data()); diff --git a/src/util.h b/src/util.h index d594a2e..00f44bc 100644 --- a/src/util.h +++ b/src/util.h @@ -234,7 +234,7 @@ QCString insertTemplateSpecifierInScope(const QCString &scope,const QCString &te QCString stripScope(const char *name); -QCString convertToHtml(const char *s); +QCString convertToHtml(const char *s,bool keepEntities=TRUE); QCString convertToXML(const char *s); diff --git a/src/xmlgen.cpp b/src/xmlgen.cpp index e66bea3..fb21518 100644 --- a/src/xmlgen.cpp +++ b/src/xmlgen.cpp @@ -1170,6 +1170,8 @@ static void generateXMLForClass(ClassDef *cd,QTextStream &ti) if (cd->name().find('@')!=-1) return; // skip anonymous compounds. if (cd->templateMaster()!=0) return; // skip generated template instances. + msg("Generating XML output for class %s\n",cd->name().data()); + ti << " getOutputFileBase() << "\" kind=\"" << cd->compoundTypeString() << "\">" << convertToXML(cd->name()) << "" << endl; @@ -1886,7 +1888,6 @@ void generateXML() ClassDef *cd; for (cli.toFirst();(cd=cli.current());++cli) { - msg("Generating XML output for class %s\n",cd->name().data()); generateXMLForClass(cd,t); } } diff --git a/winbuild/Doxygen.vcproj b/winbuild/Doxygen.vcproj index 767f018..917dd5f 100644 --- a/winbuild/Doxygen.vcproj +++ b/winbuild/Doxygen.vcproj @@ -1505,6 +1505,30 @@ + + + + + + + + + + @@ -1529,6 +1553,30 @@ + + + + + + + + + + + + + + -- cgit v0.12