diff options
author | Dimitri van Heesch <dimitri@stack.nl> | 2007-08-15 18:35:31 (GMT) |
---|---|---|
committer | Dimitri van Heesch <dimitri@stack.nl> | 2007-08-15 18:35:31 (GMT) |
commit | 4a93397673029e3cfd4c9e2d0501a109f858b87c (patch) | |
tree | 4d3b01807f8eb1364767b33ddb31bfd49b37ef3a /src | |
parent | e832cfbdc161adb81ec992f6ab4356f81b55f6e5 (diff) | |
download | Doxygen-4a93397673029e3cfd4c9e2d0501a109f858b87c.zip Doxygen-4a93397673029e3cfd4c9e2d0501a109f858b87c.tar.gz Doxygen-4a93397673029e3cfd4c9e2d0501a109f858b87c.tar.bz2 |
Release-1.5.3-20070815
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.in | 2 | ||||
-rw-r--r-- | src/classdef.cpp | 1 | ||||
-rw-r--r-- | src/code.l | 4 | ||||
-rw-r--r-- | src/commentscan.l | 7 | ||||
-rw-r--r-- | src/declinfo.l | 2 | ||||
-rw-r--r-- | src/defargs.h | 3 | ||||
-rw-r--r-- | src/defargs.l | 9 | ||||
-rw-r--r-- | src/doctokenizer.l | 7 | ||||
-rw-r--r-- | src/doxygen.cpp | 59 | ||||
-rw-r--r-- | src/fortrancode.h | 35 | ||||
-rw-r--r-- | src/fortrancode.l | 959 | ||||
-rw-r--r-- | src/fortranscanner.h | 50 | ||||
-rw-r--r-- | src/fortranscanner.l | 1333 | ||||
-rw-r--r-- | src/htmlgen.cpp | 1 | ||||
-rw-r--r-- | src/htmlhelp.cpp | 5 | ||||
-rw-r--r-- | src/index.cpp | 26 | ||||
-rw-r--r-- | src/latexgen.cpp | 32 | ||||
-rw-r--r-- | src/libdoxygen.pro.in | 4 | ||||
-rw-r--r-- | src/libdoxygen.t | 6 | ||||
-rw-r--r-- | src/memberdef.cpp | 28 | ||||
-rw-r--r-- | src/memberdef.h | 1 | ||||
-rw-r--r-- | src/msc.cpp | 2 | ||||
-rw-r--r-- | src/pagedef.cpp | 2 | ||||
-rw-r--r-- | src/pre.h | 2 | ||||
-rw-r--r-- | src/pre.l | 3 | ||||
-rw-r--r-- | src/scanner.l | 100 | ||||
-rw-r--r-- | src/util.cpp | 109 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | src/xmlgen.cpp | 3 |
29 files changed, 2676 insertions, 121 deletions
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. { @@ -2607,8 +2607,8 @@ OPERATOR {ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP} g_code->codify(yytext); } <MemberCall2,FuncCall>{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<formulaNewLines;i++) formLabel+='\n'; return formLabel; } @@ -955,14 +958,17 @@ MAILADR [a-z_A-Z0-9.+\-]+"@"[a-z_A-Z0-9\-]+("."[a-z_A-Z0-9\-]+)+[a-z_A-Z0-9\-] formulaText="\\begin"; formulaEnv=&yytext[2]; formulaText+=formulaEnv; + formulaNewLines=0; BEGIN(ReadFormulaLong); } <Comment>{CMD}"f$" { // start of a inline formula formulaText="$"; + formulaNewLines=0; BEGIN(ReadFormulaShort); } <Comment>{CMD}"f[" { // start of a block formula formulaText="\\["; + formulaNewLines=0; BEGIN(ReadFormulaLong); } <Comment>{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; } <ReadFormulaLong,ReadFormulaShort>\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]+) <Start>{B}+ { addType(); } -<Start>{B}*"("({ID}"::")*{B}*"*"({B}*("const"|"volatile"){B}+)? { +<Start>{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]* <FuncQual>"="{B}*"0" { g_argList->pureSpecifier=TRUE; } +<FuncQual>")"{B}*"["[^]]*"]" { // for functions returning a pointer to an array, + // i.e. ")[]" in "int (*f(int))[4]" with argsString="(int))[4]" + g_extraTypeChars=yytext; + } <ReadDocBlock>[^\*\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():"<none>", // baseClassName.data(), // baseClass?baseClass->name().data():"<none>", // 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():"<none>",tcd); + //printf("Looking for %s inside nd=%s result=%p (%s) cd=%p\n", + // scopeName.data(),nd?nd->name().data():"<none>",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 <stdio.h> + +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 <stdio.h> +#include <assert.h> +#include <ctype.h> +#include <qregexp.h> +#include <qdir.h> +#include <qstringlist.h> +#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<UseEntry> +{ + public: + UseSDict() : SDict<UseEntry>(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<QStringList> 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() : "<null>"); + 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_yyLineNr<g_inputLines) + { + startCodeLine(); + } + } + else + { + g_code->codify(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_yyLineNr<g_inputLines) + { + startCodeLine(); + } + } + else + { + //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); + ol.writeCodeLink(ref,file,anchor,sp,0); + done=TRUE; + } + } +} + +/** + generates dictionay entries that are used if REFERENCED_BY_RELATION ... options are set + (e.g. the "referenced by ..." list after the function documentation) +*/ + +static void addDocCrossReference(MemberDef *src, MemberDef *dst) +{ + if (dst->isTypedef() || 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 ------------------------------------------------------------*/ + +<Start>{IGNORE}/{BS}"("? { // do not search keywords, intrinsics... @TODO: complete list + codifyLines(yytext); + } + /*-------- inner construct ---------------------------------------------------*/ + +<Start>{COMMANDS}/[,( \t\n].* { // hightlight rest of fortran statements + /* font class is defined e.g. in doxygen.css */ + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } +<Start>"end"{BS}{COMMANDS}/[ \t\n].* { + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + + /*-------- use statement -------------------------------------------*/ +<Start>"use"{BS_} { + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(Use); + } +<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); + } +<Use>,{BS}"ONLY" { // TODO: rename + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(UseOnly); + } +<UseOnly>{BS},{BS} { codifyLines(yytext); } +<UseOnly>{ID} { + codifyLines(yytext); + useEntry->onlyNames.append(yytext); + } +<Use,UseOnly>"\n" { + unput(*yytext); + yy_pop_state(); + } + + /*-------- fortran module -----------------------------------------*/ +<Start>("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"; + } +<ClassName>{ID} { + if (currentModule == "module") currentModule=yytext; + generateLink(*g_code,yytext); + yy_pop_state(); + } +<ClassName>\n { // interface may be without name + yy_pop_state(); + REJECT; + } +<Start>"end"{BS}"module".* { // just reset currentModule, rest is done in following rule + currentModule=0; + REJECT; + } +<Start>"end"{BS}("program"|"module"|"type"|"interface") { // + endUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + + /*-------- subprog definition -------------------------------------*/ +<Start>{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } +<Start>{SUBPROG}{BS_} { // Fortran subroutine or function found + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + yy_push_state(YY_START); + BEGIN(Subprog); + } +<Subprog>{ID} { // subroutine/function name + //cout << "===> start procedure " << yytext << endl; + startUseScope(); + generateLink(*g_code,yytext); + } +<Subprog>"(".* { // ignore rest of line + codifyLines(yytext); + } +<Subprog>"\n" { codifyLines(yytext); + yy_pop_state(); + } +<Start>"end"{BS}{SUBPROG}.* { // Fortran subroutine or function ends + //cout << "===> end function " << yytext << endl; + endUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + /*-------- variable declaration ----------------------------------*/ +<Start>"TYPE"{BS}"(" { + yy_push_state(YY_START); + BEGIN(TypeDecl); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +<TypeDecl>{ID} { // link type + g_insideBody=TRUE; + generateLink(*g_code,yytext); + g_insideBody=FALSE; + } +<TypeDecl>")" { + BEGIN(Declaration); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +<Start>{TYPE_SPEC}/[,:( ] { + yy_push_state(YY_START); + BEGIN(Declaration); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +<Start>{ATTR_SPEC} { + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +<Declaration>({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable deklaration + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +<Declaration>"&" { // continuation line + yy_push_state(YY_START); + BEGIN(DeclContLine); + } +<DeclContLine>"\n" { // declaration not yet finished + codifyLines(yytext); + yy_pop_state(); + } +<Declaration>"\n" { // end declaration line + codifyLines(yytext); + yy_pop_state(); + } + + /*-------- subprog calls -----------------------------------------*/ + +<Start>"call"{BS_} { + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(SubCall); + } +<SubCall>{ID} { // subroutine call + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + yy_pop_state(); + } +<Start>{ID}{BS}/"(" { // function call + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + } + + /*-------- comments ---------------------------------------------------*/ +<Start>\n?{BS}"!>" { // start comment line or comment block + yy_push_state(YY_START); + BEGIN(DocBlock); + docBlock=yytext; + } + +<DocBlock>.* { // contents of current comment line + docBlock+=yytext; + } +<DocBlock>"\n"{BS}("!>"|"!"+) { //| comment block (next line is also comment line) + docBlock+=yytext; + } +<DocBlock>"\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<g_inputLines) + { + startCodeLine(); + } + } + else // do not remove comment + { + startFontClass("comment"); + codifyLines(docBlock); + endFontClass(); + } + yy_pop_state(); + } + +<*>"!"[^>\n].*|"!"$ { // normal comment + startFontClass("comment"); + codifyLines(yytext); + endFontClass(); + } + /*------ preprocessor --------------------------------------------*/ +<Start>"#".*\n { startFontClass("preprocessor"); + codifyLines(yytext); + endFontClass(); + } + /*------ variable references? -------------------------------------*/ +<Start>{ID} { + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + } + + /*------ strings --------------------------------------------------*/ +<*>"\\\\" { str+=yytext; /* ignore \\ */} +<*>"\\\"" { str+=yytext; /* ignore \" */} + +<String>\" { // 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; + } +<String>. {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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> + +#include "qtbc.h" +#include <qarray.h> +#include <qstack.h> +#include <qregexp.h> +#include <unistd.h> +#include <qfile.h> +#include <qmap.h> + +#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<Entry> 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<Entry*,QMap<QString,SymbolModifiers> > 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<Entry> &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>\"|\' { // 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(); + } + +<String>. {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; + } + } +<StrIgnore>.?/\n { yy_pop_state(); // comment ends with endline character + //cout << "end comment " << yyLineNr <<" "<< debugStr << endl; + } // comment line ends +<StrIgnore>. { debugStr+=yytext; } + + + /*------ use handling ------------------------------------------------------------*/ + +<Start,ModuleBody,TypedefBody,SubprogBody>"use"{BS_} { + yy_push_state(YY_START); + BEGIN(Use); + } +<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(); + } +<Use>{ID}/, { + useModuleName=yytext; + } +<Use>,{BS}"ONLY" { BEGIN(UseOnly); + } +<UseOnly>{BS},{BS} {} +<UseOnly>{ID} { + current->name= useModuleName+"::"+yytext; + current->fileName = yyFileName; + current->section=Entry::USINGDECL_SEC; + current_root->addSubEntry(current); + current = new Entry ; + } +<Use,UseOnly>"\n" { + unput(*yytext); + yy_pop_state(); + } + + /*------ ignore special fortran statements */ +<Start,ModuleBody,SubprogBody>(^|[ \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<<name<<", "<<index<<endl; + if(index!=-1) + name = name.right(name.length()-index); + else // interface without name, must be inside subprog + name = "interface"; + addInterface(name); + yy_push_state(InterfaceBody); + startScope(last_entry); + } +<InterfaceBody>"end"{BS}"interface".* { + if(!endScope(current_root)) + yyterminate(); + yy_pop_state(); + //cout << "end interface " << yyLineNr + // <<", "<<Interface<<endl; + } +<InterfaceBody>module{BS}procedure { yy_push_state(YY_START); + BEGIN(ModuleProcedure); + } +<ModuleProcedure>{ID} { + current->section = Entry::FUNCTION_SEC ; + current->name = yytext; + moduleProcedures.append(current); + addCurrentEntry(); + } +<ModuleProcedure>"\n" { unput(*yytext); + yy_pop_state(); + } +<InterfaceBody>. {} + + /*------ module handling ------------------------------------------------------------*/ +<Start>module|program{BS_} { // + BEGIN(Module); + defaultProtection = Public; + } +<Start,ModuleBody>"end"{BS}(module|program).* { // end module + resolveModuleProcedures(moduleProcedures, current_root); + if(!endScope(current_root)) + yyterminate(); + defaultProtection = Public; + BEGIN(Start); + } +<Module>{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 --------------------------------------------------------------------------*/ + +<ModuleBody>private/{BS}(\n|"!") { defaultProtection = Private; } +<ModuleBody>public/{BS}(\n|"!") { defaultProtection = Public; } + + /*------- type definition -------------------------------------------------------------------------------*/ + +<Start,ModuleBody>"type"({BS_}|({COMMA}{ACCESS_SPEC})) { /* type definition found : TYPE , access-spec::type-name |*/ + yy_push_state(YY_START); + BEGIN(Typedef); + current->protection = defaultProtection; + } +<Typedef>{ACCESS_SPEC} { + QString type= yytext; + } +<Typedef>{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); + } +<TypedefBody>"end"{BS}"type".* { /* end type definition */ + //cout << "=========> got typedef end "<< endl; + if(!endScope(current_root)) + yyterminate(); + yy_pop_state(); + } + + /*------- module/global/typedef variable ---------------------------------------------------*/ + +<Start,ModuleBody,TypedefBody,SubprogBody>{ +{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 ) ; + } +} +<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<<endl; + unput(*yytext); + BEGIN( Variable ); + } +} + +<Variable>{BS} {} +<Variable>{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); + } + } +<Variable>{ARGS} { /* dimension of the previous entry. */ + QString name(argName); + QString attr("dimension"); + attr += yytext; + modifiers[current_root][name] |= attr; + } +<Variable>{COMMA} {} +<Variable>{BS}"=" { yy_push_state(YY_START); + initializer=""; + BEGIN(Initialization); + } +<Variable>"\n" { currentModifiers = SymbolModifiers(); + yy_pop_state(); // end variable deklaration list + yyLineNr++; + docBlock.resize(0); + } + +<Initialization>"(/" { initializer+=yytext; + BEGIN(ArrayInitializer); // initializer may contain comma + } +<ArrayInitializer>. { initializer+=yytext; } +<ArrayInitializer>"/)" { initializer+=yytext; + yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + } +<Initialization>{COMMA} { yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + } +<Initialization>"\n"|"!" { //| + yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + unput(*yytext); + } +<Initialization>. { initializer+=yytext; } + + /*------ fortran subroutine/function handling ------------------------------------------------------------*/ + /* Start is initial condition */ + +<Start,ModuleBody,InterfaceBody>{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { + // TYPE_SPEC is for old function style function result + result= yytext; + result= result.stripWhiteSpace(); + current->type = result; + } +<Start,ModuleBody,SubprogBody,InterfaceBody>{BS}{SUBPROG}{BS_} { // Fortran subroutine or function found + //cout << "1=========> got subprog, type:" << yytext <<endl; + current->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); + } +<Subprog>{BS} { /* ignore white space */ } +<Subprog>{ID} { current->name = yytext; + //cout << "1a==========> got " << current->type << " " << yytext << " " << yyLineNr << endl; + modifiers[current_root][current->name.lower()].returnName = current->name; + BEGIN(Parameterlist); + } +<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); + } +<Parameterlist>{NOARGS} { + yyLineNr++; + //cout << "3=========> without parameterlist " <<endl; + stringToArgumentList("", current->argList); + addCurrentEntry(); + startScope(last_entry); + BEGIN(SubprogBody); +} +<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; + } +<SubprogBody>"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 --------------------------------------------------------------------*/ + +<Variable>"!<" { /* 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); + } + } +<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(); + } + +<Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody>"!>" { + 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; + } + +<DocBlock>.* { // contents of current comment line + docBlock+=yytext; + } +<DocBlock>"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line) + docBlock+="\n"; // \n is necessary for lists + yyLineNr++; + } +<DocBlock>"\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: " <<YY_START << endl; + debugStr=""; + } + + /*---- error: EOF in wrong state --------------------------------------------------------------------*/ + <SubprogBody,ModuleBody,String,StrIgnore,InterfaceBody><<EOF>> { + 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<Entry> &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<<mdfs.protection<<", "<<mdfs.direction<<", "<<mdfs.optional<< +// ", "<<(mdfs.dimension.isNull() ? "" : mdfs.dimension.latin1())<< +// ", "<<mdfs.allocatable<<", "<<mdfs.external<<", "<<mdfs.intrinsic; +// +// return out; +//} + +/*! Find argument with given name in \a subprog entry. */ +static Argument *findArgument(Entry* subprog, QString name, bool byTypeName = FALSE) +{ + QCString cname(name.lower()); + for (unsigned int i=0; i<subprog->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: "<<scope->name<<endl; + current_root= scope; /* start substructure */ + + QMap<QString,SymbolModifiers> 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: "<<scope->name<<endl; + if (current_root->parent()) + { + current_root= current_root->parent(); /* end substructure */ + } + else + { + fprintf(stderr,"parse error in end <scopename>"); + scanner_abort(); + return FALSE; + } + + // update variables or subprogram arguments with modifiers + QMap<QString,SymbolModifiers>& mdfsMap = modifiers[scope]; + + if(scope->section == Entry::FUNCTION_SEC) + { + // iterate all symbol modifiers of the scope + for(QMap<QString,SymbolModifiers>::Iterator it=mdfsMap.begin(); it!=mdfsMap.end(); it++) { + //cout<<it.key()<<": "<<it.data()<<endl; + Argument *arg = findArgument(scope, it.key()); + + if(arg) + applyModifiers(arg, it.data()); + } + + // find return type for function + //cout<<"RETURN NAME "<<modifiers[current_root][scope->name.lower()].returnName<<endl; + QString returnName = modifiers[current_root][scope->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 "<<scope->name<<endl; + EntryListIterator eli(*scope->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<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl; + if(mdfsMap.contains(ce->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 " <<current->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(): "<<name<<" DOCS:"<<(docs.isNull()?QString("null"):docs)<<endl; + Argument *ret = 0; + if (current_root->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 << "<a class=\"codeRef\" "; diff --git a/src/htmlhelp.cpp b/src/htmlhelp.cpp index 5cbb5c7..d40e50a 100644 --- a/src/htmlhelp.cpp +++ b/src/htmlhelp.cpp @@ -450,6 +450,11 @@ void HtmlHelp::createProjectFile() t << "tab_b.gif" << endl; t << "tab_l.gif" << endl; t << "tab_r.gif" << endl; + if (Config_getBool("HTML_DYNAMIC_SECTIONS")) + { + t << "open.gif" << endl; + t << "closed.gif" << endl; + } f.close(); } else diff --git a/src/index.cpp b/src/index.cpp index c745659..316963e 100644 --- a/src/index.cpp +++ b/src/index.cpp @@ -2589,7 +2589,7 @@ void writeGroupIndexItem(GroupDef *gd,MemberList *ml,const QCString &title, first=FALSE; if (htmlHelp) { - htmlHelp->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<m_indent+1;i++) t << "\\>"; + for (int i=0;i<m_indent+2;i++) t << "\\>"; 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); @@ -21,9 +21,11 @@ #include "qtbc.h" #include <stdio.h> //#include <qfile.h> +#include "define.h" class BufStr; +DefineDict* getFileDefineDict(); void initPreprocessor(); void cleanUpPreprocessor(); void addSearchDir(const char *dir); @@ -104,6 +104,9 @@ static QStack<bool> 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}+)?) } } } +<FindMembers>[0-9]{ID} { // some number where we did not expect one + } <FindMembers>"." { if (insideJava || insideCS || insideD) { @@ -2691,14 +2695,18 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) } <IDLProp>. { } -<Array>"]" { current->args += *yytext ; +<Array>"]" { current->args += *yytext ; if (--squareCount<=0) BEGIN( FindMembers ) ; } -<Array>"[" { current->args += *yytext ; +<FuncFuncArray>"]" { current->args += *yytext ; + if (--squareCount<=0) + BEGIN( Function ) ; + } +<Array,FuncFuncArray>"[" { current->args += *yytext ; squareCount++; } -<Array>. { current->args += *yytext ; } +<Array,FuncFuncArray>. { current->args += *yytext ; } <SkipSquare>"[" { squareCount++; } <SkipSquare>"]" { if (--squareCount<=0) @@ -3155,7 +3163,7 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) <ReadBody,ReadNSBody,ReadBodyIntf>. { current->program += yytext ; } <FindMembers>"("/{BN}*({TSCOPE}{BN}*"::")*{TSCOPE}{BN}*")"{BN}*"(" | /* typedef void (A<int>::func_t)(args...) */ -<FindMembers>("("({BN}*{TSCOPE}{BN}*"::")*({BN}*"*"{BN}*)+)+ { /* typedef void (A::*ptr_t)(args...) */ +<FindMembers>("("({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}+)?) } <FuncPtr>{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 ); + } } } <FuncPtr>. { //printf("Error: FuncPtr `%c' unexpected at line %d of %s\n",*yytext,yyLineNr,yyFileName); } +<FuncPtrOperator>"("{BN}*")"{BN}*/"(" { + current->name += yytext; + current->name = current->name.simplifyWhiteSpace(); + lineCount(); + } +<FuncPtrOperator>\n { + yyLineNr++; + current->name += *yytext; + } +<FuncPtrOperator>"(" { + unput(*yytext); + BEGIN( EndFuncPtr ); + } +<FuncPtrOperator>. { + current->name += *yytext; + } <EndFuncPtr>")"{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); } -<EndFuncPtr>"(" { // a function returning a function +<EndFuncPtr>"(" { // 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 ) ; } <EndFuncPtr>"["[^\n\]]*"]" { funcPtrType+=yytext; @@ -3228,6 +3265,12 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->type+=funcPtrType.data()+1; BEGIN(Function); } +<FuncFuncEnd>")"{BN}*/"[" { // function returning a pointer to an array + lineCount(); + current->type+=funcPtrType; + current->args+=")"; + BEGIN(FuncFuncArray); + } <FuncFuncEnd>. { 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}+)?) <Prototype>"operator"{B}*"("{B}*")" { current->name+=yytext; } -<Prototype>"(" { +<Prototype>"(" { current->args+=*yytext; currentArgumentContext = PrototypeQual; fullArgString = current->args.copy(); copyArgString = ¤t->args; BEGIN( ReadFuncArgType ) ; } -<Prototype>"("({ID}"::")*({B}*"*")+ { +<Prototype>"("({ID}"::")*({B}*[&*])+ { current->type+=current->name+yytext; current->name.resize(0); BEGIN( PrototypePtr ); @@ -4990,10 +5029,20 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) <PrototypePtr>{SCOPENAME} { current->name+=yytext; } +<PrototypePtr>"(" { + current->args+=*yytext; + currentArgumentContext = PrototypeQual; + fullArgString = current->args.copy(); + copyArgString = ¤t->args; + BEGIN( ReadFuncArgType ) ; + } <PrototypePtr>")" { current->type+=')'; BEGIN( Prototype ); } +<PrototypePtr>. { + current->name+=yytext; + } <PrototypeQual>"{" { BEGIN( PrototypeSkipLine); } @@ -5010,7 +5059,7 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->virt = Pure; current->argList->pureSpecifier=TRUE; } -<PrototypeQual>"throw"{B}*"(" { +<PrototypeQual>"throw"{B}*"(" { current->exception = "throw("; BEGIN(PrototypeExc); } @@ -5018,10 +5067,13 @@ TYPEDEFPREFIX (("typedef"{BN}+)?)((("volatile"|"const"){BN}+)?) current->exception += ')'; BEGIN(PrototypeQual); } -<PrototypeExc>. { +<PrototypeExc>. { current->exception += *yytext; } -<Prototype,PrototypeQual>. { +<PrototypeQual>. { + current->args += *yytext; + } +<Prototype>. { current->name += *yytext; } <PrototypeSkipLine>. { 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():"<none>",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("<isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>", + //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?scope->name().data():"<global>", // item?item->name().data():"<none>", // 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():"<global>"); - 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<e) result+=*p++; + } + else + { + result+="&"; + } + } + else + { + result+="&"; + } + break; case '\'': result+="'"; break; case '"': result+="""; break; default: result+=c; break; @@ -6193,27 +6239,28 @@ QCString expandAliasRec(const QCString s) static QCString replaceAliasArgument(const QCString &aliasValue,int paramNum, const QCString ¶mValue) { - QCString result = aliasValue; + QCString result; QCString paramMarker; paramMarker.sprintf("\\%d",paramNum); int markerLen = paramMarker.length(); int p=0,i; while ((i=aliasValue.find(paramMarker,p))!=-1) // search for marker { + result+=aliasValue.mid(p,i-p); //printf("Found marker '%s' at %d len=%d for param '%s' in '%s'\n", // paramMarker.data(),i,markerLen,paramValue.data(),aliasValue.data()); if (i==0 || aliasValue.at(i-1)!='\\') // found unescaped marker { - QCString before = result.left(i); - QCString after = result.mid(i+markerLen); - result = before + paramValue + after; - p=i+paramValue.length(); + result += paramValue; + p=i+markerLen; } else // ignore escaped markers { + result += aliasValue.mid(i,markerLen); p=i+1; } } + result+=aliasValue.right(aliasValue.length()-p); result = expandAliasRec(substitute(result,"\\,",",")); //printf("replaceAliasArgument('%s',%d,'%s')->%s\n", // aliasValue.data(),paramNum,paramValue.data(),result.data()); @@ -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 << " <compound refid=\"" << cd->getOutputFileBase() << "\" kind=\"" << cd->compoundTypeString() << "\"><name>" << convertToXML(cd->name()) << "</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); } } |