diff -uNr doxygen/Makefile.in fdoxygen/Makefile.in --- doxygen/Makefile.in 2007-06-10 23:20:56.000000000 +0300 +++ fdoxygen/Makefile.in 2007-07-18 10:01:28.000000000 +0300 @@ -54,15 +54,15 @@ $(INSTTOOL) -m 755 bin/doxygen $(INSTALL)/bin $(INSTTOOL) -m 755 bin/doxytag $(INSTALL)/bin $(INSTTOOL) -d $(INSTALL)/$(MAN1DIR) - cat doc/doxygen.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" doxygen.1 + cat doc/doxygen.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" >doxygen.1 $(INSTTOOL) -m 644 doxygen.1 $(INSTALL)/$(MAN1DIR)/doxygen.1 rm doxygen.1 - cat doc/doxytag.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" doxytag.1 + cat doc/doxytag.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" >doxytag.1 $(INSTTOOL) -m 644 doxytag.1 $(INSTALL)/$(MAN1DIR)/doxytag.1 rm doxytag.1 - cat doc/doxywizard.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" doxywizard.1 + cat doc/doxywizard.1 | sed -e "s/DATE/$(DATE)/g" -e "s/VERSION/$(VERSION)/g" >doxywizard.1 $(INSTTOOL) -m 644 doxywizard.1 $(INSTALL)/$(MAN1DIR)/doxywizard.1 - rm doxywizard + rm doxywizard.1 install_docs: $(INSTTOOL) -d $(DOCDIR) diff -uNr doxygen/src/doxygen.cpp fdoxygen/src/doxygen.cpp --- doxygen/src/doxygen.cpp 2007-06-10 23:20:57.000000000 +0300 +++ fdoxygen/src/doxygen.cpp 2007-07-18 10:01:30.000000000 +0300 @@ -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" @@ -8741,6 +8742,9 @@ 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 -uNr doxygen/src/fortrancode.h fdoxygen/src/fortrancode.h --- doxygen/src/fortrancode.h 1970-01-01 03:00:00.000000000 +0300 +++ fdoxygen/src/fortrancode.h 2006-09-13 12:25:40.000000000 +0300 @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2006 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef CODE_H +#define CODE_H + +#include "qtbc.h" +#include + +class CodeOutputInterface; +class FileDef; +class MemberDef; + +void parseFortranCode(CodeOutputInterface &,const char *,const QCString &, + bool ,const char *,FileDef *fd=0, + int startLine=-1,int endLine=-1,bool inlineFragment=FALSE, + MemberDef *memberDef=0); +void resetFortranCodeParserState(); +void codeFreeScanner(); + +#endif diff -uNr doxygen/src/fortrancode.l fdoxygen/src/fortrancode.l --- doxygen/src/fortrancode.l 1970-01-01 03:00:00.000000000 +0300 +++ fdoxygen/src/fortrancode.l 2007-07-18 10:01:30.000000000 +0300 @@ -0,0 +1,928 @@ +/****************************************************************************** + * + * 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 +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "qtbc.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 + +//-------------------------------------------------------------------------------- + +/** + data of an use-statement +*/ +class UseEntry { + public: + QCString module; // just for debug + QStringList onlyNames; /* entries of the ONLY-part */ +}; + +/** + module name -> list of ONLY/remote entries + (module name = name of the module, which can be accessed via use-directive) +*/ +class UseSDict : public SDict +{ + public: + UseSDict() : SDict(17) {} +}; + +/*===================================================================*/ +/* + * statics + */ + +static QCString docBlock; //!< contents of all lines of a documentation block +static QCString currentModule=0; //!< name of the current enclosing module +static UseSDict *useMembers= new UseSDict; //!< info about used modules +static UseEntry *useEntry = 0; //!< current use statement info +static QStack useStack; //!< contains names of used modules for current program tree +static QStringList *currentUseNames= new QStringList; //! contains names of used modules of current program unit +static QCString str=""; //!> contents of fortran string + +static CodeOutputInterface * g_code; + +// TODO: is this still needed? if so, make it work +static QCString g_parmType; +static QCString g_parmName; + +static const char * g_inputString; //!< the code fragment as text +static int g_inputPosition; //!< read offset during parsing +static int g_inputLines; //!< number of line in the code fragment +static int g_yyLineNr; //!< current line number +static bool g_needsTermination; + +static bool g_insideBody; //!< inside subprog/program body? => create links +static const char * g_currentFontClass; + +static bool g_exampleBlock; +static QCString g_exampleName; +static QCString g_exampleFile; + +static FileDef * g_sourceFileDef; +static Definition * g_currentDefinition; +static MemberDef * g_currentMemberDef; +static bool g_includeCodeFragment; + + +static void endFontClass() +{ + if (g_currentFontClass) + { + g_code->endFontClass(); + g_currentFontClass=0; + } +} + +static void startFontClass(const char *s) +{ + endFontClass(); + g_code->startFontClass(s); + g_currentFontClass=s; +} + +static void setCurrentDoc(const QCString &name,const QCString &base,const QCString &anchor="") +{ + static bool searchEngineEnabled=Config_getBool("SEARCHENGINE"); + if (searchEngineEnabled) + { + Doxygen::searchIndex->setCurrentDoc(name,base,anchor); + } +} + +static void addToSearchIndex(const char *text) +{ + static bool searchEngineEnabled=Config_getBool("SEARCHENGINE"); + if (searchEngineEnabled) + { + Doxygen::searchIndex->addWord(text,FALSE); + } +} + +/*! start a new line of code, inserting a line number if g_sourceFileDef + * is TRUE. If a definition starts at the current line, then the line + * number is linked to the documentation of that definition. + */ +static void startCodeLine() +{ + if (g_sourceFileDef) + { + //QCString lineNumber,lineAnchor; + //lineNumber.sprintf("%05d",g_yyLineNr); + //lineAnchor.sprintf("l%05d",g_yyLineNr); + + Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr); + //printf("startCodeLine %d d=%s\n", g_yyLineNr,d ? d->name().data() : ""); + if (!g_includeCodeFragment && d) + { + g_currentDefinition = d; + g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr); + g_insideBody = FALSE; + g_parmType.resize(0); + g_parmName.resize(0); + QCString lineAnchor; + lineAnchor.sprintf("l%05d",g_yyLineNr); + if (g_currentMemberDef) + { + g_code->writeLineNumber(g_currentMemberDef->getReference(), + g_currentMemberDef->getOutputFileBase(), + g_currentMemberDef->anchor(),g_yyLineNr); + setCurrentDoc( + g_currentMemberDef->qualifiedName(), + g_sourceFileDef->getSourceFileBase(), + lineAnchor); + } + else if (d->isLinkableInProject()) + { + g_code->writeLineNumber(d->getReference(), + d->getOutputFileBase(), + 0,g_yyLineNr); + setCurrentDoc( + d->qualifiedName(), + g_sourceFileDef->getSourceFileBase(), + lineAnchor); + } + } + else + { + g_code->writeLineNumber(0,0,0,g_yyLineNr); + } + } + g_code->startCodeLine(); + if (g_currentFontClass) + { + g_code->startFontClass(g_currentFontClass); + } +} + + +static void endFontClass(); +static void endCodeLine() +{ + if (g_currentFontClass) { g_code->endFontClass(); } + g_code->endCodeLine(); +} + +/*! write a code fragment `text' that may span multiple lines, inserting + * line numbers for each line. + */ +static void codifyLines(char *text) +{ + //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text); + char *p=text,*sp=p; + char c; + bool done=FALSE; + while (!done) + { + sp=p; + while ((c=*p++) && c!='\n'); + if (c=='\n') + { + g_yyLineNr++; + *(p-1)='\0'; + g_code->codify(sp); + endCodeLine(); + if (g_yyLineNrcodify(sp); + done=TRUE; + } + } +} + +static void codifyLines(QCString str) +{ + char *tmp= (char *) malloc(str.length()+1); + strcpy(tmp, str); + codifyLines(tmp); + free(tmp); +} + +/*! writes a link to a fragment \a text that may span multiple lines, inserting + * line numbers for each line. If \a text contains newlines, the link will be + * split into multiple links with the same destination, one for each line. + */ +static void writeMultiLineCodeLink(CodeOutputInterface &ol, + const char *ref,const char *file, + const char *anchor,const char *text) +{ + bool done=FALSE; + char *p=(char *)text; + while (!done) + { + char *sp=p; + char c; + while ((c=*p++) && c!='\n'); + if (c=='\n') + { + g_yyLineNr++; + *(p-1)='\0'; + //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); + ol.writeCodeLink(ref,file,anchor,sp,0); + endCodeLine(); + if (g_yyLineNrisTypedef() || dst->isEnumerate()) return; // don't add types + //printf("======= addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data()); + if ((Config_getBool("REFERENCED_BY_RELATION") || Config_getBool("CALLER_GRAPH")) && + (src->isFunction())) + { + dst->addSourceReferencedBy(src); + } + if ((Config_getBool("REFERENCES_RELATION") || Config_getBool("CALL_GRAPH")) && (src->isFunction())) + { + src->addSourceReferences(dst); + } +} + +//------------------------------------------------------------------------------- +/** + searches for definition of a type + @param memberName the name of the type + @param moduleName name of enclosing module or null, if global entry + @param cd the entry, if found or null + @param useList array of data of USE-statement + @returns true, if type is found +*/ +static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName, + ClassDef *&cd, UseSDict *usedict=0) +{ + if (tname.isEmpty()) return FALSE; /* empty name => nothing to link */ + + //cout << "=== search for type: " << tname << endl; + + // search for type + if (cd= Doxygen::classSDict->find(tname)) { + //cout << "=== type found in global module" << endl; + return TRUE; + } + else if (moduleName && (cd= Doxygen::classSDict->find(moduleName+"::"+tname))) { + //cout << "=== type found in local module" << endl; + return TRUE; + } + else { + UseEntry *use; + for (UseSDict::Iterator di(*usedict); (use=di.current()); ++di) + if ((cd= Doxygen::classSDict->find(use->module+"::"+tname))) { + //cout << "=== type found in used module" << endl; + return TRUE; + } + } + + return FALSE; +} + +/** + searches for definition of function memberName + @param memberName the name of the function/variable + @param moduleName name of enclosing module or null, if global entry + @param memberDef the entry, if found or null + @param useList array of data of USE-statement + @returns true, if found +*/ +static bool getFortranDefs(const QCString &memberName, const QCString &moduleName, + MemberDef *&md, UseSDict *usedict=0) +{ + if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */ + + // search for function + MemberName *mn = Doxygen::functionNameSDict->find(memberName); + + if (mn) // name is known + { + MemberListIterator mli(*mn); + for (mli.toFirst();(md=mli.current());++mli) // all found functions with given name + { + FileDef *fd=md->getFileDef(); + GroupDef *gd=md->getGroupDef(); + + //cout << "found link with same name: " << fd->fileName() << " " << memberName; + //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl; + + if ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) + { + NamespaceDef *nspace= md->getNamespaceDef(); + + if (nspace == 0) { // found function in global scope + return TRUE; + } + else if (moduleName == nspace->name()) { // found in local scope + return TRUE; + } + else { // else search in used modules + QCString moduleName= nspace->name(); + UseEntry *ue= usedict->find(moduleName); + if (ue) { + // check if only-list exists and if current entry exists is this list + QStringList &only= ue->onlyNames; + if (only.isEmpty()) { + //cout << " found in module " << moduleName << " entry " << memberName << endl; + return TRUE; // whole module used + } + else + for ( QStringList::Iterator it = only.begin(); it != only.end(); ++it) + { + //cout << " search in only: " << moduleName << ":: " << memberName << "==" << (*it)<< endl; + if (memberName == (QCString)(*it)) + return TRUE; // found in ONLY-part of use list + } + } + } + } // if linkable + } // for + } + return FALSE; +} + +/** + gets the link to a generic procedure which depends not on the name, but on the parameter list + @TODO implementation +*/ +static bool getGenericProcedureLink(const ClassDef *cd, const char *memberText, CodeOutputInterface &ol) +{ + 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() == Entry::CLASS_SEC) && // 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() { + //cerr << "===> startUse " << yytext << endl; + useStack.push(currentUseNames); + currentUseNames= new QStringList; +} + +/** end use list */ +void endUseScope() +{ + //cerr << "===> endUse " << yytext << endl; + //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) {cerr << "fortrancode.l: stack empty!" << endl; exit(-1);} +} + +void addUse(QString moduleName) { + currentUseNames->append(moduleName); +} +//---------------------------------------------------------------------------- + +/* -----------------------------------------------------------------*/ +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); + +static int yyread(char *buf,int max_size) +{ + int c=0; + while( c < max_size && g_inputString[g_inputPosition] ) + { + *buf = g_inputString[g_inputPosition++] ; + c++; buf++; + } + return c; +} + +%} + +IDSYM [a-z_A-Z0-9] +ID [a-z_A-Z]+{IDSYM}* +SUBPROG (subroutine|function) +B [ \t] +BS [ \t]* +BS_ [ \t]+ +COMMA {BS},{BS} +ARGS {BS}("("[^)]*")"){BS} + +NUM_TYPE (complex|integer|logical|real) +KIND {ARGS} +CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"([0-9]+|{ARGS})) +TYPE_SPEC (({NUM_TYPE}("*"[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}PRECISION|{CHAR}) + +INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" +ATTR_SPEC (ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PRIVATE|PUBLIC|SAVE|TARGET) +ACCESS_SPEC (PRIVATE|PUBLIC) +/* Assume that attribute statements are almost the same as attributes. */ +ATTR_STMT {ATTR_SPEC}|DIMENSION +COMMANDS (BLOCK{BS}DATA|DO|SELECT|CASE|WHERE|IF|THEN|ELSE|MODULE{BS_}PROCEDURE) +IGNORE (IMPLICIT{BS}NONE|CONTAINS|WRITE|READ|ALLOCATE|DEALLOCATE|SIZE) + +/* | */ + +%option noyywrap +%option stack +%option caseless +/*%option debug*/ + +%x Start +%x SubCall +%x FuncDef +%x ClassName +%x ClassVar +%x Subprog +%x DocBlock +%x Use +%x UseOnly +%x TypeDecl +%x Declaration +%x DeclContLine +%x Parameterlist +%x String + +%% + /*==================================================================*/ + + /*-------- ignore ------------------------------------------------------------*/ + +{IGNORE}/{BS}"("? { // do not search keywords, intrinsics... @TODO: complete list + codifyLines(yytext); + } + /*-------- inner construct ---------------------------------------------------*/ + +{COMMANDS}/[,( \t\n].* { // hightlight rest of fortran statements + /* font class is defined e.g. in doxygen.css */ + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } +"end"{BS}{COMMANDS}/[ \t\n].* { + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + + /*-------- use statement -------------------------------------------*/ +"use"{BS_} { + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(Use); + } +{ID} { + startFontClass("keywordflow"); + codifyLines(yytext); + endFontClass(); + + /* append module name to use dict */ + useEntry = new UseEntry(); + useEntry->module = yytext; + useMembers->append(yytext, useEntry); + addUse(yytext); + } +,{BS}"ONLY" { // TODO: rename + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(UseOnly); + } +{BS},{BS} { codifyLines(yytext); } +{ID} { + codifyLines(yytext); + useEntry->onlyNames.append(yytext); + } +"\n" { + unput(*yytext); + yy_pop_state(); + } + + /*-------- fortran module -----------------------------------------*/ +("program"|"module"|"type"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { // + startUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + yy_push_state(YY_START); + BEGIN(ClassName); + if (!strcmp(yytext,"module")) currentModule="module"; + } +{ID} { + if (currentModule == "module") currentModule=yytext; + generateLink(*g_code,yytext); + yy_pop_state(); + } +\n { // interface may be without name + yy_pop_state(); + REJECT; + } +"end"{BS}"module".* { // just reset currentModule, rest is done in following rule + currentModule=0; + REJECT; + } +"end"{BS}("program"|"module"|"type"|"interface") { // + endUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + + /*-------- subprog definition -------------------------------------*/ +{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } +{SUBPROG}{BS_} { // Fortran subroutine or function found + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + yy_push_state(YY_START); + BEGIN(Subprog); + } +{ID} { // subroutine/function name + //cout << "===> start procedure " << yytext << endl; + startUseScope(); + generateLink(*g_code,yytext); + } +"(".* { // ignore rest of line + codifyLines(yytext); + } +"\n" { codifyLines(yytext); + yy_pop_state(); + } +"end"{BS}{SUBPROG}.* { // Fortran subroutine or function ends + //cout << "===> end function " << yytext << endl; + endUseScope(); + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + } + /*-------- variable declaration ----------------------------------*/ +"TYPE"{BS}"(" { + yy_push_state(YY_START); + BEGIN(TypeDecl); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +{ID} { // link type + g_insideBody=TRUE; + generateLink(*g_code,yytext); + g_insideBody=FALSE; + } +")" { + BEGIN(Declaration); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +{TYPE_SPEC}/[,:( ] { + yy_push_state(YY_START); + BEGIN(Declaration); + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +{ATTR_SPEC} { + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable deklaration + startFontClass("keywordtype"); + g_code->codify(yytext); + endFontClass(); + } +"&" { // continuation line + yy_push_state(YY_START); + BEGIN(DeclContLine); + } +"\n" { // declaration not yet finished + codifyLines(yytext); + yy_pop_state(); + } +"\n" { // end declaration line + codifyLines(yytext); + yy_pop_state(); + } + + /*-------- subprog calls -----------------------------------------*/ + +"call"{BS_} { + codifyLines(yytext); + yy_push_state(YY_START); + BEGIN(SubCall); + } +{ID} { // subroutine call + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + yy_pop_state(); + } +{ID}/{BS}"(" { // function call + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + } + + /*-------- comments ---------------------------------------------------*/ +\n?{BS}"!>" { // start comment line or comment block + yy_push_state(YY_START); + BEGIN(DocBlock); + docBlock=yytext; + } + +.* { // contents of current comment line + docBlock+=yytext; + } +"\n"{BS}("!>"|"!"+) { //| comment block (next line is also comment line) + docBlock+=yytext; + } +"\n" { // comment block ends at the end of this line + docBlock+=yytext; + // remove special comment (default config) + if (Config_getBool("STRIP_CODE_COMMENTS")) + { + g_yyLineNr+=((QCString)docBlock).contains('\n'); + endCodeLine(); + if (g_yyLineNr"!"[^>\n].*|"!"$ { // normal comment + startFontClass("comment"); + codifyLines(yytext); + endFontClass(); + } + /*------ preprocessor --------------------------------------------*/ +"#".*\n { startFontClass("preprocessor"); + codifyLines(yytext); + endFontClass(); + } + /*------ variable references? -------------------------------------*/ +{ID} { + g_insideBody=TRUE; + generateLink(*g_code, yytext); + g_insideBody=FALSE; + } + + /*------ strings --------------------------------------------------*/ +<*>"\\\\" { str+=yytext; /* ignore \\ */} +<*>"\\\"" { str+=yytext; /* ignore \" */} + +\" { // string ends with next quote without previous backspace + str+=yytext; + startFontClass("stringliteral"); + codifyLines(str); + endFontClass(); + yy_pop_state(); + } +<*>\" { /* string starts */ + yy_push_state(YY_START); + BEGIN(String); + str=yytext; + } +. {str+=yytext;} + + + /*-----------------------------------------------------------------------------*/ + +<*>\n { + codifyLines(yytext); + } +<*>. { + g_code->codify(yytext); + } +%% + +/*@ ---------------------------------------------------------------------------- + */ + +/*===================================================================*/ + +void resetFortranCodeParserState() {} + +void parseFortranCode(CodeOutputInterface &od,const char *className,const QCString &s, + bool exBlock, const char *exName,FileDef *fd, + int startLine,int endLine,bool inlineFragment, + MemberDef *memberDef) +{ + //printf("***parseCode() exBlock=%d exName=%s fd=%p\n",exBlock,exName,fd); + 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 -uNr doxygen/src/fortranscanner.h fdoxygen/src/fortranscanner.h --- doxygen/src/fortranscanner.h 1970-01-01 03:00:00.000000000 +0300 +++ fdoxygen/src/fortranscanner.h 2006-08-23 10:44:03.000000000 +0300 @@ -0,0 +1,50 @@ +/****************************************************************************** + * + * $Id: fortranscanner.h,v 1.1 2006/08/23 07:44:03 zdv058 Exp $ + * + * 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 -uNr doxygen/src/fortranscanner.l fdoxygen/src/fortranscanner.l --- doxygen/src/fortranscanner.l 1970-01-01 03:00:00.000000000 +0300 +++ fdoxygen/src/fortranscanner.l 2007-07-17 15:17:08.000000000 +0300 @@ -0,0 +1,1278 @@ +/* -*- mode: fundamental; indent-tabs-mode: 1; -*- */ +/***************************************************************************** + * Parser for Fortran90 F subset + * + * Copyright (C) by Anke Visser + * based on the work of Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +/* Developer notes. + * + * - Consider using startScope(), endScope() functions with module, program, + * subroutine or any other scope in fortran program. + * + * - Symbol modifiers (attributes) are collected using SymbolModifiers |= operator during + * substructure parsing. When substructure ends all modifiers are applied to actual + * entries in applyModifiers() functions. + * + * - How case insensitiveness should be handled in code? + * On one side we have arg->name and entry->name, on another side modifierMap[name]. + * In entries and arguments case is the same as in code, in modifier map case is lowered and + * then it is compared to lowered entry/argument names. + * + * - Do not like constructs like aa{BS} or {BS}bb. Should try to handle blank space + * with separate rule?: It seems it is often necessary, because we may parse something like + * "functionA" or "MyInterface". So constructs like `(^|[ \t])interface({BS_}{ID})?/[ \t\n]' + * are desired. + */ + +%{ + +#include +#include +#include +#include + +#include "qtbc.h" +#include +#include +#include +#include +#include +#include + +#include "fortranscanner.h" +#include "entry.h" +#include "message.h" +#include "config.h" +#include "doxygen.h" +#include "util.h" +#include "defargs.h" +#include "language.h" +#include "commentscan.h" +#include "fortrancode.h" +#include "pre.h" + +#include +using namespace std; + +#define YY_NEVER_INTERACTIVE 1 + +enum ScanVar { V_IGNORE, V_VARIABLE, V_PARAMETER}; + +// {{{ ----- Helper structs ----- +//! Holds modifiers (ie attributes) for one symbol (variable, function, etc) +struct SymbolModifiers { + enum Protection {NONE_P, PUBLIC, PRIVATE}; + enum Direction {NONE_D, IN, OUT, INOUT}; + + //!< This is only used with function return value. + QString type, returnName; + Protection protection; + Direction direction; + bool optional; + QString dimension; + bool allocatable; + bool external; + bool intrinsic; + bool parameter; + bool pointer; + bool target; + bool save; + + SymbolModifiers() : type(), returnName(), protection(NONE_P), direction(NONE_D), + optional(FALSE), dimension(), allocatable(FALSE), + external(FALSE), intrinsic(FALSE), parameter(FALSE), + pointer(FALSE), target(FALSE), save(FALSE) {} + + SymbolModifiers& operator|=(const SymbolModifiers &mdfs); + SymbolModifiers& operator|=(QString mdfrString); +}; +ostream& operator<<(ostream& out, const SymbolModifiers& mdfs); + + static const char *directionStrs[] = { + "", "intent(in)", "intent(out)", "intent(inout)"}; + +// }}} + +/* ----------------------------------------------------------------- + * + * statics + */ +static ParserInterface *g_thisParser; +static const char * inputString; +static int inputPosition; +static QFile inputFile; +static QCString yyFileName; +static int yyLineNr = 1 ; +static Entry* current_root = 0 ; +static Entry* global_root = 0 ; +static Entry* file_root = 0 ; +static Entry* current = 0 ; +static Entry* last_entry = 0 ; +static ScanVar v_type = V_IGNORE; // type of parsed variable +static QList moduleProcedures; // list of all interfaces which contain unresolved + // module procedures +static QCString docBlock; +static QCString docBlockName; +static bool docBlockInBody; +static bool docBlockJavaStyle; + +static MethodTypes mtype; +static bool gstat; +static Specifier virt; + +static QString debugStr; +static QCString result; // function result +static Argument *parameter; // element of parameter list +static QCString argType; // fortran type of an argument of a parameter list +static QCString argName; // last identifier name in variable list +static QCString initializer; // initial value of a variable +static QCString useModuleName; // name of module in the use statement +static Protection defaultProtection; + +static char stringStartSymbol; // single or double quote + +//! Accumulated modifiers of current statement, eg variable declaration. +static SymbolModifiers currentModifiers; +//! Holds program scope->symbol name->symbol modifiers. +static QMap > modifiers; + +//----------------------------------------------------------------------------- + +static int yyread(char *buf,int max_size); +static void startCommentBlock(bool); +static void handleCommentBlock(const QCString &doc,bool brief); +static void addCurrentEntry(); +static void addInterface(QString name); +static Argument *addFortranParameter(const QCString &type,const QCString &name, const QString docs); +static void scanner_abort(); + +static void startScope(Entry *scope); +static bool endScope(Entry *scope); +static QString getFullName(Entry *e); +static bool isTypeName(QString name); +static void resolveModuleProcedures(QList &moduleProcedures, Entry *current_root); + +//----------------------------------------------------------------------------- +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); +//----------------------------------------------------------------------------- + +%} + + //----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- +IDSYM [a-z_A-Z0-9] +NOTIDSYM [^a-z_A-Z0-9] +SEPARATE [:, \t] +ID [a-z_A-Z]+{IDSYM}* +PP_ID {ID} +LABELID [a-z_A-Z]+[a-z_A-Z0-9\-]* +SUBPROG (subroutine|function) +B [ \t] +BS [ \t]* +BS_ [ \t]+ +COMMA {BS},{BS} +ARGS {BS}("("[^)]*")"){BS} +NOARGS {BS}"\n" + +NUM_TYPE (complex|integer|logical|real) +KIND {ARGS} +CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"([0-9]+|{ARGS})) +TYPE_SPEC (({NUM_TYPE}("*"[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}PRECISION|{CHAR}|TYPE{ARGS}) + +INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" +ATTR_SPEC (ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PRIVATE|PUBLIC|SAVE|TARGET) +ACCESS_SPEC (PRIVATE|PUBLIC) +/* Assume that attribute statements are almost the same as attributes. */ +ATTR_STMT {ATTR_SPEC}|DIMENSION + +%option noyywrap +%option stack +%option caseless +/*%option debug */ + + //--------------------------------------------------------------------------------- + + /** fortran parsing states */ +%x Subprog +%x Parameterlist +%x SubprogBody +%x Start +%x Comment +%x Module +%x ModuleBody +%x AttributeList +%x Variable +%x Initialization +%x ArrayInitializer +%x Typedef +%x TypedefBody +%x InterfaceBody +%x StrIgnore +%x String +%x Use +%x UseOnly +%x ModuleProcedure + + /** comment parsing states */ +%x DocBlock +%x DocBackLine +%x EndDoc + +%% + + /*-----------------------------------------------------------------------------------*/ + +<*>"&".*"\n" { if (YY_START == String) REJECT; // "&" is ignored in strings + yyLineNr++;} /* line not finished -> read next line (text after "&" may be + comment and has to be ignored */ + + /*------ ignore strings */ +<*>"\\\\" { /* ignore \\ */} +<*>"\\\""|\\\' { /* ignore \" and \' */} + +\"|\' { // string ends with next quote without previous backspace + if(yytext[0]!=stringStartSymbol) REJECT; // single vs double quote + // cout << "string end: " << debugStr << endl; + yy_pop_state(); + } + +. {debugStr+=yytext;} // ignore String contents (especially '!') + +<*>\"|\' { /* string starts */ + if(YY_START == StrIgnore) REJECT; // ignore in simple comments + // cout << "string start: " << yytext[0] << yyLineNr << endl; + yy_push_state(YY_START); + stringStartSymbol=yytext[0]; // single or double quote + BEGIN(String); debugStr="!^!"; + } + + /*------ ignore simple comment (not documentation comments) */ + +<*>"!"/[^<>\n] { if (YY_START == String) REJECT; // "!" is ignored in strings + // skip comment line (without docu comments "!>" "!<" ) + /* ignore further "!" and ignore comments in Strings */ + if ((YY_START != StrIgnore) && (YY_START != String)) { + yy_push_state(YY_START); + BEGIN(StrIgnore); + debugStr="*!"; + //cout << "start comment "<< yyLineNr << endl; + } + } +.?/\n { yy_pop_state(); // comment ends with endline character + //cout << "end comment " << yyLineNr <<" "<< debugStr << endl; + } // comment line ends +. { debugStr+=yytext; } + + + /*------ use handling ------------------------------------------------------------*/ + +"use"{BS_} { + yy_push_state(YY_START); + BEGIN(Use); + } +{ID} { + //cout << "using dir "<< yytext << endl; + current->name=yytext; + current->fileName = yyFileName; + current->section=Entry::USINGDIR_SEC; + current_root->addSubEntry(current); + current = new Entry; + yy_pop_state(); + } +{ID}/, { + useModuleName=yytext; + } +,{BS}"ONLY" { BEGIN(UseOnly); + } +{BS},{BS} {} +{ID} { + current->name= useModuleName+"::"+yytext; + current->fileName = yyFileName; + current->section=Entry::USINGDECL_SEC; + current_root->addSubEntry(current); + current = new Entry ; + } +"\n" { + unput(*yytext); + yy_pop_state(); + } + + /*------ ignore special fortran statements */ +(^|[ \t])interface({BS_}{ID})?/[ \t\n] { // handle interface block + QString name = yytext; + int index = name.find("interface", 0, FALSE); + index = name.find(QRegExp("[^ \\t]"), index+9); + //cout<"end"{BS}"interface".* { + if(!endScope(current_root)) + yyterminate(); + yy_pop_state(); + //cout << "end interface " << yyLineNr + // <<", "<module{BS}procedure { yy_push_state(YY_START); + BEGIN(ModuleProcedure); + } +{ID} { + current->section = Entry::FUNCTION_SEC ; + current->name = yytext; + moduleProcedures.append(current); + addCurrentEntry(); + } +"\n" { unput(*yytext); + yy_pop_state(); + } +. {} + + /*------ module handling ------------------------------------------------------------*/ +module|program{BS_} { // + BEGIN(Module); + defaultProtection = Public; + } +"end"{BS}(module|program).* { // end module + resolveModuleProcedures(moduleProcedures, current_root); + if(!endScope(current_root)) + yyterminate(); + defaultProtection = Public; + BEGIN(Start); + } +{ID} { + //cout << "0=========> got module " << yytext << endl; + current->section = Entry::NAMESPACE_SEC; + current->name = yytext; + current->type = "module"; + current->fileName = yyFileName; + current->bodyLine = yyLineNr; // used for source reference + current->protection = Public ; + + addCurrentEntry(); + startScope(last_entry); + + BEGIN(ModuleBody); + } + + /*------- access specification --------------------------------------------------------------------------*/ + +private/{BS}(\n|"!") { defaultProtection = Private; } +public/{BS}(\n|"!") { defaultProtection = Public; } + + /*------- type definition -------------------------------------------------------------------------------*/ + +"type"({BS_}|({COMMA}{ACCESS_SPEC})) { /* type definition found : TYPE , access-spec::type-name |*/ + yy_push_state(YY_START); + BEGIN(Typedef); + current->protection = defaultProtection; + } +{ACCESS_SPEC} { + QString type= yytext; + } +{ID} { /* type name found */ + //cout << "=========> got typedef " << yytext << ": " << yyLineNr << endl; + current->section = Entry::CLASS_SEC; // was Entry::STRUCT_SEC; + current->spec = Entry::Struct; + current->name = yytext; + + /* if type is part of a module, mod name is necessary for output */ + if ((current_root) && + (current_root->section == Entry::CLASS_SEC || + current_root->section == Entry::NAMESPACE_SEC)) + //current_root->section == Entry::INTERFACE_SEC)) + { + current->name= current_root->name+"::"+current->name; + } + current->fileName = yyFileName; + current->bodyLine = yyLineNr; + addCurrentEntry(); + startScope(last_entry); + BEGIN(TypedefBody); + } +"end"{BS}"type".* { /* end type definition */ + //cout << "=========> got typedef end "<< endl; + if(!endScope(current_root)) + yyterminate(); + yy_pop_state(); + } + + /*------- module/global/typedef variable ---------------------------------------------------*/ + +{ +{TYPE_SPEC}/{SEPARATE} { + /* variable declaration starts */ + //cout << "4=========> got variable type: " << yytext << endl; + QString help=yytext; + help= help.simplifyWhiteSpace(); + argType= help.latin1(); + yy_push_state(AttributeList); + } +^{BS}{PP_ID}{KIND}? { /* check for preprocessor symbol expand to type */ + QString str = yytext; + str = str.stripWhiteSpace(); + DefineDict* defines = getFileDefineDict(); + QString name; + int index = str.find("("); + if(index != -1) + name = str.left(index).stripWhiteSpace(); + else + name = str; + + Define *define = (*defines)[name]; + if(define != NULL && isTypeName(define->definition)) { + argType = str; + yy_push_state(AttributeList); + } else { + REJECT; + } + } +{ATTR_STMT}/{BS}{ID} { + /* attribute statement starts */ + //cout << "5=========> Attribute statement: "<< yytext << endl; + QString tmp = yytext; + currentModifiers |= (tmp); + argType=""; + yy_push_state(YY_START); + /* goto attribute parsing, however there must not be one, + just catch "::" if it is there. */ + BEGIN( AttributeList ) ; + } +} +{ +{COMMA} {} +{BS} {} +{ATTR_SPEC} { /* update current modifiers */ + QString tmp = yytext; + currentModifiers |= (tmp); + } +"::" { /* end attribute list */ + BEGIN( Variable ); + } +. { /* unknown attribute, consider variable name */ + //cout<<"start variables, unput "<<*yytext<{BS} {} +{ID} { /* parse variable declaration */ + //cout << "5=========> got variable: " << argType << "::" << yytext << endl; + /* work around for bug in QCString.replace (QString works) */ + QString name=yytext; + /* remember attributes for the symbol */ + modifiers[current_root][name.lower()] |= currentModifiers; + argName= name.latin1(); + int last= yy_top_state(); + + v_type= V_IGNORE; + if (!argType.isEmpty() && last != SubprogBody) { // new variable entry + v_type = V_VARIABLE; + current->section = Entry::VARIABLE_SEC; + current->name = argName; + current->type = argType; + current->fileName = yyFileName; + current->bodyLine = yyLineNr; // used for source reference + addCurrentEntry(); + } else if(!argType.isEmpty()){ // deklaration of parameter list: add type for corr. parameter + parameter= addFortranParameter(argType,argName,docBlock); + if (parameter) v_type= V_PARAMETER; + // save, it may be function return type + modifiers[current_root][name.lower()].type = argType; + // any accumulated doc for argument should be emptied, + // because it is handled other way and this doc can be + // unexpectedly passed to the next member. + current->doc.resize(0); + current->brief.resize(0); + } + } +{ARGS} { /* dimension of the previous entry. */ + QString name(argName); + QString attr("dimension"); + attr += yytext; + modifiers[current_root][name] |= attr; + } +{COMMA} {} +{BS}"=" { yy_push_state(YY_START); + initializer=""; + BEGIN(Initialization); + } +"\n" { currentModifiers = SymbolModifiers(); + yy_pop_state(); // end variable deklaration list + yyLineNr++; + docBlock.resize(0); + } + +"(/" { initializer+=yytext; + BEGIN(ArrayInitializer); // initializer may contain comma + } +. { initializer+=yytext; } +"/)" { initializer+=yytext; + yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + } +{COMMA} { yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + } +"\n"|"!" { //| + yy_pop_state(); // end initialization + if (v_type == V_VARIABLE) last_entry->initializer= initializer; + unput(*yytext); + } +. { initializer+=yytext; } + + /*------ fortran subroutine/function handling ------------------------------------------------------------*/ + /* Start is initial condition */ + +{TYPE_SPEC}{BS}/{SUBPROG}{BS_} { + // TYPE_SPEC is for old function style function result + result= yytext; + result= result.stripWhiteSpace(); + current->type = result; + } +{BS}{SUBPROG}{BS_} { // Fortran subroutine or function found + //cout << "1=========> got subprog, type:" << yytext <section = Entry::FUNCTION_SEC ; + QCString subtype = yytext; subtype=subtype.lower().stripWhiteSpace(); + if (!current->type) current->type = subtype; + current->fileName = yyFileName; + current->bodyLine = yyLineNr; // used for source reference + current->startLine = -1; // ??? what is startLine for? + current->args.resize(0); + current->argList->clear(); + yy_push_state(Subprog); + docBlock.resize(0); + } +{BS} { /* ignore white space */ } +{ID} { current->name = yytext; + //cout << "1a==========> got " << current->type << " " << yytext << " " << yyLineNr << endl; + modifiers[current_root][current->name.lower()].returnName = current->name; + BEGIN(Parameterlist); + } +{ARGS} { + //current->type not yet available + QString arglist= yytext; + //cout << "3=========> got parameterlist " << yytext << endl; + yyLineNr+= arglist.contains('\n'); + arglist = arglist.replace(QRegExp("&[^\n]*\n"),""); + //cout << "3=========> got parameterlist " << arglist << endl; + current->args = arglist; + current->args = removeRedundantWhiteSpace(current->args); + stringToArgumentList(current->args, current->argList); + addCurrentEntry(); + startScope(last_entry); + BEGIN(SubprogBody); + } +{NOARGS} { + yyLineNr++; + //cout << "3=========> without parameterlist " <argList); + addCurrentEntry(); + startScope(last_entry); + BEGIN(SubprogBody); +} +result{BS}\({BS}{ID} { + result= yytext; + result= result.right(result.length()-result.find("(")-1); + result= result.stripWhiteSpace(); + modifiers[current_root->parent()][current_root->name.lower()].returnName = result; + //cout << "=====> got result " << result << endl; + } +"end"{BS}{SUBPROG}.* { + //cout << "1e=========> got end subprog: " << yytext << endl; + + /* args is used for parameters in list of functions, argList for + parameters in detailed function descripttion */ + //current->args = argListToString(current->argList); + //current->endBodyLine = yyLineNr; // ??? what ist endBodyLine for + if(!endScope(current_root)) + yyterminate(); + yy_pop_state() ; + } + + /*---- documentation comments --------------------------------------------------------------------*/ + +"!<" { /* backward docu comment (only one line) */ + if (v_type != V_IGNORE) { + yy_push_state(YY_START); + current->docLine = yyLineNr; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF"); + startCommentBlock(TRUE); + BEGIN(DocBackLine); + } + } +.* { // contents of current comment line + docBlock=yytext; + if (v_type == V_VARIABLE) { + Entry *tmp_entry = current; + current = last_entry; // temporarily switch to the previous entry + handleCommentBlock(docBlock,TRUE); + current=tmp_entry; + } + else if (v_type == V_PARAMETER) { + parameter->docs=docBlock; + } + yy_pop_state(); + } + +"!>" { + yy_push_state(YY_START); + current->docLine = yyLineNr; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF"); + startCommentBlock(TRUE); + BEGIN(DocBlock); + //cout << "start DocBlock " << endl; + } + +.* { // contents of current comment line + docBlock+=yytext; + } +"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line) + docBlock+="\n"; // \n is necessary for lists + yyLineNr++; + } +"\n" { // comment block ends at the end of this line + //cout <<"3=========> comment block : "<< docBlock << endl; + unput(*yytext); + handleCommentBlock(docBlock,TRUE); + yy_pop_state(); + } + + /*------------------------------------------------------------------------------------------------*/ + +<*>"\n" {yyLineNr++; + //if (debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << debugStr << " state: " <<> { + cerr << "==== Error: EOF reached in wrong state (end missing)" << endl; + scanner_abort(); + yyterminate(); + } + <*>. {debugStr+=yytext;} // ignore remaining text + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ +%% +//---------------------------------------------------------------------------- + +/** used to copy entry to an interface module procedure */ +static void copyEntry(Entry *dest, Entry *src) { + dest->type = src->type; + dest->fileName = src->fileName; + dest->bodyLine = src->bodyLine; + dest->args = src->args; + dest->argList = new ArgumentList(*src->argList); +} + +/** fill empty interface module procedures with info from + corresponding module subprogs + @TODO: handle procedures in used modules +*/ +void resolveModuleProcedures(QList &moduleProcedures, Entry *current_root) +{ + if (moduleProcedures.isEmpty()) return; + + EntryListIterator eli1(moduleProcedures); + // for all module procedures + for (Entry *ce1; (ce1=eli1.current()); ++eli1) { + // check all entries in this module + EntryListIterator eli2(*current_root->children()); + for (Entry *ce2; (ce2=eli2.current()); ++eli2) { + if (ce1->name == ce2->name) { + copyEntry(ce1, ce2); + } + } // for procedures in current module + } // for all interface module procedures + moduleProcedures.clear(); +} + +static bool isTypeName(QString name) +{ + name = name.lower(); + return name=="integer" || name == "real" || + name=="complex" || name == "logical"; +} + +/*! Extracts string which resides within parentheses of provided string. */ +static QString extractFromParens(const QString name) +{ + QString extracted = name; + int start = extracted.find("("); + if(start != -1) { + extracted.remove(0, start+1); + } + int end = extracted.findRev(")"); + if(end != -1) { + int length = extracted.length(); + extracted.remove(end, length); + } + extracted = extracted.stripWhiteSpace(); + + return extracted; +} + +/*! Adds passed modifiers to these modifiers.*/ +SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs) +{ + if(mdfs.protection!=NONE_P) protection = mdfs.protection; + if(mdfs.direction!=NONE_D) direction = mdfs.direction; + optional |= mdfs.optional; + if(!mdfs.dimension.isNull()) dimension = mdfs.dimension; + allocatable |= mdfs.allocatable; + external |= mdfs.external; + intrinsic |= mdfs.intrinsic; + parameter |= mdfs.parameter; + pointer |= mdfs.pointer; + target |= mdfs.target; + save |= mdfs.save; + return *this; +} + +/*! Extracts and adds passed modifier to these modifiers.*/ +SymbolModifiers& SymbolModifiers::operator|=(QString mdfString) +{ + mdfString = mdfString.lower(); + SymbolModifiers newMdf; + + if(mdfString.startsWith("dimension")) { + newMdf.dimension=mdfString; + } + else if(mdfString.contains("intent")) { + QString tmp = extractFromParens(mdfString); + bool isin = tmp.contains("in"); + bool isout = tmp.contains("out"); + if(isin && isout) newMdf.direction = SymbolModifiers::INOUT; + else if(isin) newMdf.direction = SymbolModifiers::IN; + else if(isout) newMdf.direction = SymbolModifiers::OUT; + } + else if(mdfString=="public") { + newMdf.protection = SymbolModifiers::PUBLIC; + } + else if(mdfString=="private") { + newMdf.protection = SymbolModifiers::PRIVATE; + } + else if(mdfString=="optional") { + newMdf.optional = TRUE; + } + else if(mdfString=="allocatable") { + newMdf.allocatable = TRUE; + } + else if(mdfString=="external") { + newMdf.external = TRUE; + } + else if(mdfString=="intrinsic") { + newMdf.intrinsic = TRUE; + } + else if(mdfString=="parameter") { + newMdf.parameter = TRUE; + } + else if(mdfString=="pointer") { + newMdf.pointer = TRUE; + } + else if(mdfString=="target") { + newMdf.target = TRUE; + } + else if(mdfString=="save") { + newMdf.save = TRUE; + } + + (*this) |= newMdf; + return *this; +} + +/*! For debugging purposes. */ +ostream& operator<<(ostream& out, const SymbolModifiers& mdfs) +{ + out<argList->count(); i++) { + Argument *arg = subprog->argList->at(i); + if(!byTypeName && arg->name.lower() == cname || + byTypeName && arg->type.lower() == cname) + return arg; + } + + return NULL; +} + +/*! Find function with given name in \a entry. */ +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; +} + +/*! Apply modifiers stored in \a mdfs to the \a typeName string. */ +static QString applyModifiers(QString typeName, SymbolModifiers& mdfs) { + if(!mdfs.dimension.isNull()) { + typeName += ","; + typeName += mdfs.dimension; + } + if(mdfs.direction!=SymbolModifiers::NONE_D) { + typeName += ","; + typeName += directionStrs[mdfs.direction]; + } + if(mdfs.optional) { + typeName += ","; + typeName += "optional"; + } + if(mdfs.allocatable) { + typeName += ","; + typeName += "allocatable"; + } + if(mdfs.external) { + typeName += ","; + typeName += "external"; + } + if(mdfs.intrinsic) { + typeName += ","; + typeName += "intrinsic"; + } + if(mdfs.parameter) { + typeName += ","; + typeName += "parameter"; + } + if(mdfs.pointer) { + typeName += ","; + typeName += "pointer"; + } + if(mdfs.target) { + typeName += ","; + typeName += "target"; + } + if(mdfs.save) { + typeName += ","; + typeName += "save"; + } + + return typeName; +} + +/*! Apply modifiers stored in \a mdfs to the \a arg argument. */ +static void applyModifiers(Argument *arg, SymbolModifiers& mdfs) +{ + QString tmp = arg->type; + arg->type = applyModifiers(tmp, mdfs); +} + +/*! Apply modifiers stored in \a mdfs to the \a ent entry. */ +static void applyModifiers(Entry *ent, SymbolModifiers& mdfs) +{ + QString tmp = ent->type; + ent->type = applyModifiers(tmp, mdfs); + + if(mdfs.protection == SymbolModifiers::PUBLIC) + ent->protection = Public; + else if(mdfs.protection == SymbolModifiers::PRIVATE) + ent->protection = Private; +} + +/*! Starts the new scope in fortran program. Consider using this function when + * starting module, interface, function or other program block. + * \see endScope() + */ +static void startScope(Entry *scope) { + //cout<<"start scope: "<name< mdfMap; + modifiers.insert(scope, mdfMap); +} + +/*! Ends scope in fortran program: may update subprogram arguments or module variable attributes. + * \see startScope() + */ +static bool endScope(Entry *scope) { + //cout<<"end scope: "<name<parent()) + current_root= current_root->parent(); /* end substructure */ + else { + cerr << "parse error in end " << endl; + scanner_abort(); + return FALSE; + } + + // update variables or subprogram arguments with modifiers + QMap& mdfsMap = modifiers[scope]; + + if(scope->section == Entry::FUNCTION_SEC) { + // iterate all symbol modifiers of the scope + for(QMap::Iterator it=mdfsMap.begin(); it!=mdfsMap.end(); it++) { + //cout<name.lower()].returnName<name.lower()].returnName.lower(); + if(modifiers[scope].contains(returnName)) { + scope->type = modifiers[scope][returnName].type; // returning type works + applyModifiers(scope, modifiers[scope][returnName]); // returning array works + } + + } else if(scope->section == Entry::CLASS_SEC) { // was INTERFACE_SEC + if(scope->parent()->section == Entry::FUNCTION_SEC) { // interface within function + // iterate functions of interface and + // try to find types for dummy(ie. argument) procedures. + //cout<<"Search in "<name<children()); + Entry *ce; + for (;(ce=eli.current());++eli) { + if(ce->section != Entry::FUNCTION_SEC) + continue; + + Argument *arg = findArgument(scope->parent(), ce->name, TRUE); + if(arg != NULL) { + // set type of dummy procedure argument to interface + arg->name = arg->type; + arg->type = scope->name; + } + } + } + + } else { // not function section or interface + // iterate variables: get and apply modifiers + EntryListIterator eli(*scope->children()); + Entry *ce; + for (;(ce=eli.current());++eli) { + if(ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC) + continue; + + //cout<name<<", "<name.lower())<name.lower())) + applyModifiers(ce, mdfsMap[ce->name.lower()]); + } + } + + // clear all modifiers of the scope + modifiers.remove(scope); + + return TRUE; +} + +//! Return full name of the entry. Sometimes we must combine several names recursively. +static QString getFullName(Entry *e) { + QString name = e->name; + if(e->section == Entry::CLASS_SEC // || e->section == Entry::INTERFACE_SEC + || !e->parent() || e->parent()->name.isEmpty()) + return name; + + return getFullName(e->parent())+"::"+name; +} + +static int yyread(char *buf,int max_size) +{ + int c=0; + while( c < max_size && inputString[inputPosition] ) + { + *buf = inputString[inputPosition++] ; + c++; buf++; + } + return c; +} + +static void initParser() +{ + last_entry = 0; +} + +static void initEntry() +{ + current->protection = defaultProtection ; + current->mtype = mtype; + current->virt = virt; + current->stat = gstat; + initGroupInfo(current); +} + +/** + adds current entry to current_root and creates new current +*/ +static void addCurrentEntry() +{ + //cout << "Adding entry " <name.data() << endl; + current_root->addSubEntry(current); + last_entry = current; + current = new Entry ; + initEntry(); +} + +/*! Adds interface to the root entry. + * \note Code was brought to this procedure from the parser, + * because there was/is idea to use it in several parts of the parser. + */ +static void addInterface(QString name) { + current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC; + current->spec = Entry::Interface; + current->name = name; + + /* if type is part of a module, mod name is necessary for output */ + if ((current_root) && + (current_root->section == Entry::CLASS_SEC || + current_root->section == Entry::NAMESPACE_SEC)) { + current->name= current_root->name+"::"+current->name; + } + if ((current_root) && + (current_root->section == Entry::FUNCTION_SEC)) { + current->name = getFullName(current_root) + "__" + QString(current->name); + } + + current->fileName = yyFileName; + current->bodyLine = yyLineNr; + addCurrentEntry(); +} + + +//----------------------------------------------------------------------------- + +/*! Update the argument \a name with additional \a type info. + */ +static Argument *addFortranParameter(const QCString &type,const QCString &name, const QString docs) +{ + //cout<<"addFortranParameter(): "<argList==0) return false; + ArgumentListIterator ali(*current_root->argList); + Argument *a; + for (ali.toFirst();(a=ali.current());++ali) + { + if (a->type.lower()==name.lower()) + { + ret=a; +//cout << "addParameter found: " << type << " , " << name << endl; + a->type=type.stripWhiteSpace(); + a->name=name.stripWhiteSpace(); + if(!docs.isNull()) + a->docs = docs; + break; + } + } // for + return ret; +} + + //---------------------------------------------------------------------------- +static void startCommentBlock(bool brief) +{ + if (brief) + { + current->briefFile = yyFileName; + current->briefLine = yyLineNr; + } + else + { + current->docFile = yyFileName; + current->docLine = yyLineNr; + } +} + + //---------------------------------------------------------------------------- +static void handleCommentBlock(const QCString &doc,bool brief) +{ + docBlockInBody = FALSE; + bool needsEntry = FALSE; + static bool hideInBodyDocs = Config_getBool("HIDE_IN_BODY_DOCS"); + int position=0; + if (docBlockInBody && hideInBodyDocs) return; + //fprintf(stderr,"call parseCommentBlock [%s]\n",doc.data()); + while (parseCommentBlock( + g_thisParser, + docBlockInBody ? last_entry : current, + doc, // text + yyFileName, // file + brief ? current->briefLine : current->docLine, // line of block start + docBlockInBody ? FALSE : brief, + docBlockInBody ? FALSE : docBlockJavaStyle, + docBlockInBody, + defaultProtection, + position, + needsEntry + )) { + //fprintf(stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry); + if (needsEntry) addCurrentEntry(); + } + //fprintf(stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry); + + if (needsEntry) addCurrentEntry(); +} + +//---------------------------------------------------------------------------- +static int level=0; +static void debugCompounds(Entry *rt) // print Entry structure (for debugging) +{ + level++; + printf("%d) debugCompounds(%s) line %d\n",level, rt->name.data(), rt->bodyLine); + EntryListIterator eli(*rt->children()); + Entry *ce; + for (;(ce=eli.current());++eli) + { + debugCompounds(ce); + } +level--; +} + + +static void parseMain(const char *fileName,const char *fileBuf,Entry *rt) +{ + initParser(); + + defaultProtection = Public; + inputString = fileBuf; + inputPosition = 0; + + //anonCount = 0; // don't reset per file + mtype = Method; + gstat = FALSE; + virt = Normal; + current_root = rt; + global_root = rt; + inputFile.setName(fileName); + if (inputFile.open(IO_ReadOnly)) + { + yyLineNr= 1 ; + yyFileName = fileName; + msg("Parsing file %s...\n",yyFileName.data()); + + current_root = rt ; + initParser(); + groupEnterFile(yyFileName,yyLineNr); + + current = new Entry; + current->name = yyFileName; + current->section = Entry::SOURCE_SEC; + current_root->addSubEntry(current); + file_root = current; + current = new Entry; + + fscanYYrestart( fscanYYin ); + { + BEGIN( Start ); + } + + fscanYYlex(); + groupLeaveFile(yyFileName,yyLineNr); + + //debugCompounds(rt); //debug + + rt->program.resize(0); + delete current; current=0; + moduleProcedures.clear(); + + inputFile.close(); + } +} + +//---------------------------------------------------------------------------- + +void FortranLanguageScanner::parseInput(const char *fileName,const char *fileBuf,Entry *root) +{ + g_thisParser = this; + ::parseMain(fileName,fileBuf,root); +} + +void FortranLanguageScanner::parseCode(CodeOutputInterface & codeOutIntf, + const char * scopeName, + const QCString & input, + bool isExampleBlock, + const char * exampleName, + FileDef * fileDef, + int startLine, + int endLine, + bool inlineFragment, + MemberDef *memberDef + ) +{ + ::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName, + fileDef,startLine,endLine,inlineFragment,memberDef); +} + +bool FortranLanguageScanner::needsPreprocessing(const QCString &extension) +{ + return true; + +} +void FortranLanguageScanner::resetCodeParserState() +{ + ::resetFortranCodeParserState(); +} + +void FortranLanguageScanner::parsePrototype(const char *text) +{ +} + +static void scanner_abort() { + cerr << "********************************************************************" << endl; + cerr << "Error in file " << yyFileName << " line:" << yyLineNr << " state:" << YY_START << endl; + cerr << "********************************************************************" << endl; + + 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 -uNr doxygen/src/libdoxygen.pro.in fdoxygen/src/libdoxygen.pro.in --- doxygen/src/libdoxygen.pro.in 2007-03-15 12:12:11.000000000 +0200 +++ fdoxygen/src/libdoxygen.pro.in 2007-07-18 10:01:30.000000000 +0300 @@ -82,6 +82,8 @@ printdocvisitor.h \ pycode.h \ pyscanner.h \ + fortrancode.h \ + fortranscanner.h \ qtbc.h \ reflist.h \ rtfdocvisitor.h \ @@ -190,6 +192,8 @@ pre.cpp \ pycode.cpp \ pyscanner.cpp \ + fortrancode.cpp \ + fortranscanner.cpp \ reflist.cpp \ rtfdocvisitor.cpp \ rtfgen.cpp \ diff -uNr doxygen/src/libdoxygen.t fdoxygen/src/libdoxygen.t --- doxygen/src/libdoxygen.t 2007-02-19 20:47:47.000000000 +0200 +++ fdoxygen/src/libdoxygen.t 2007-07-18 10:01:30.000000000 +0300 @@ -57,6 +57,12 @@ #$ 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 -uNr doxygen/src/pre.h fdoxygen/src/pre.h --- doxygen/src/pre.h 2007-02-19 20:47:47.000000000 +0200 +++ fdoxygen/src/pre.h 2007-07-18 10:01:31.000000000 +0300 @@ -21,9 +21,11 @@ #include "qtbc.h" #include //#include +#include "define.h" class BufStr; +DefineDict* getFileDefineDict(); void initPreprocessor(); void cleanUpPreprocessor(); void addSearchDir(const char *dir); diff -uNr doxygen/src/pre.l fdoxygen/src/pre.l --- doxygen/src/pre.l 2007-05-06 15:30:18.000000000 +0300 +++ fdoxygen/src/pre.l 2007-07-18 10:01:31.000000000 +0300 @@ -104,6 +104,9 @@ static bool g_lexInit = FALSE; +DefineDict* getFileDefineDict() { + return g_fileDefineDict; +} static void setFileName(const char *name) {