diff options
Diffstat (limited to 'src/fortrancode.l')
-rw-r--r-- | src/fortrancode.l | 2089 |
1 files changed, 1100 insertions, 989 deletions
diff --git a/src/fortrancode.l b/src/fortrancode.l index c4532f3..df6acf4 100644 --- a/src/fortrancode.l +++ b/src/fortrancode.l @@ -4,10 +4,11 @@ * * Copyright (C) by Anke Visser * based on the work of Dimitri van Heesch. + * Copyright (C) 2020 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 + * 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. * @@ -20,17 +21,23 @@ @todo - continuation 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 + - links to interface functions - references to variables **/ %option never-interactive %option case-insensitive +%option reentrant %option prefix="fortrancodeYY" +%option extra-type="struct fortrancodeYY_state *" +%option noyy_top_state +%top{ +#include <stdint.h> +} %{ /* - * includes + * includes */ #include <stdio.h> #include <assert.h> @@ -54,6 +61,10 @@ #include "namespacedef.h" #include "tooltip.h" #include "fortrancode.h" +#include "fortranscanner.h" +#include "containers.h" + +const int fixedCommentAfter = 72; // Toggle for some debugging info //#define DBG_CTX(x) fprintf x @@ -63,6 +74,8 @@ #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 +#define USE_STATE2STRING 0 + /* * For fixed formatted code position 6 is of importance (continuation character). * The following variables and macros keep track of the column number @@ -73,18 +86,18 @@ int yy_old_start = 0; int yy_my_start = 0; int yy_end = 1; -#define YY_USER_ACTION {yy_old_start = yy_my_start; yy_my_start = yy_end; yy_end += yyleng;} +#define YY_USER_ACTION {yy_old_start = yy_my_start; yy_my_start = yy_end; yy_end += static_cast<int>(yyleng);} #define YY_FTN_RESET {yy_old_start = 0; yy_my_start = 0; yy_end = 1;} #define YY_FTN_REJECT {yy_end = yy_my_start; yy_my_start = yy_old_start; REJECT;} - + //-------------------------------------------------------------------------------- /** data of an use-statement */ -class UseEntry +class UseEntry { - public: + public: QCString module; // just for debug QCStringList onlyNames; /* entries of the ONLY-part */ }; @@ -93,7 +106,7 @@ class UseEntry 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> +class UseSDict : public SDict<UseEntry> { public: UseSDict() : SDict<UseEntry>(17) {} @@ -102,233 +115,918 @@ class UseSDict : public SDict<UseEntry> /** Contains names of used modules and names of local variables. */ -class Scope +class Scope { public: QCStringList useNames; //!< contains names of used modules - QDict<void> localVars; //!< contains names of local variables - QDict<void> externalVars; //!< contains names of external entities - - Scope() : localVars(7, FALSE /*caseSensitive*/), externalVars(7, FALSE /*caseSensitive*/) {} + StringUnorderedSet localVars; //!< contains names of local variables + StringUnorderedSet externalVars; //!< contains names of external entities }; /*===================================================================*/ -/* - * statics +/* + * statics */ - -static QCString docBlock; //!< contents of all lines of a documentation block -static QCString currentModule=0; //!< name of the current enclosing module -static QCString currentClass=0; //!< name of the current enclosing class -static UseSDict *useMembers= new UseSDict; //!< info about used modules -static UseEntry *useEntry = 0; //!< current use statement info -static QList<Scope> scopeStack; -static bool g_isExternal = false; -// static QCStringList *currentUseNames= new QCStringList; //! 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 int g_contLineNr; //!< current, local, line number for continuation determination -static int *g_hasContLine = NULL; //!< signals whether or not a line has a continuation line (fixed source form) -static bool g_needsTermination; -static const Definition *g_searchCtx; -static bool g_collectXRefs; -static bool g_isFixedForm; - -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 char stringStartSymbol; // single or double quote + +struct fortrancodeYY_state +{ + QCString docBlock; //!< contents of all lines of a documentation block + QCString currentModule=0; //!< name of the current enclosing module + UseSDict * useMembers= 0; //!< info about used modules + UseEntry * useEntry = 0; //!< current use statement info + QList<Scope> scopeStack; + bool isExternal = false; + QCString str=""; //!> contents of fortran string + + CodeOutputInterface * code = 0; + + const char * inputString = 0; //!< the code fragment as text + yy_size_t inputPosition = 0; //!< read offset during parsing + int inputLines = 0; //!< number of line in the code fragment + int yyLineNr = 0; //!< current line number + int contLineNr = 0; //!< current, local, line number for continuation determination + int *hasContLine = 0; //!< signals whether or not a line has a continuation line (fixed source form) + bool needsTermination = false; + const Definition *searchCtx = 0; + bool collectXRefs = false; + bool isFixedForm = false; + + bool insideBody = false; //!< inside subprog/program body? => create links + const char * currentFontClass = 0; + + bool exampleBlock = false; + QCString exampleName; + QCString exampleFile; + + FileDef * sourceFileDef = 0; + const Definition * currentDefinition = 0; + const MemberDef * currentMemberDef = 0; + bool includeCodeFragment = false; + + char stringStartSymbol = '\0'; // single or double quote // count in variable declaration to filter out // declared from referenced names -static int bracketCount = 0; + int bracketCount = 0; // signal when in type / class /procedure declaration -static int inTypeDecl = 0; + int inTypeDecl = 0; -static bool g_endComment; + bool endComment = false; +}; +#if USE_STATE2STRING static const char *stateToString(int state); +#endif + +static bool getFortranNamespaceDefs(const QCString &mname, + NamespaceDef *&cd); +static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName, + ClassDef *&cd, UseSDict *usedict=0); + +//---------------------------------------------------------------------------- + +static void endFontClass(yyscan_t yyscanner); +static void startFontClass(yyscan_t yyscanner,const char *s); +static void setCurrentDoc(yyscan_t yyscanner,const QCString &anchor); +static void addToSearchIndex(yyscan_t yyscanner,const char *text); +static void startCodeLine(yyscan_t yyscanner); +static void endCodeLine(yyscan_t yyscanner); +static void nextCodeLine(yyscan_t yyscanner); +static void codifyLines(yyscan_t yyscanner,const char *text); +static void writeMultiLineCodeLink(yyscan_t yyscanner,CodeOutputInterface &ol, + Definition *d,const char *text); +static bool getGenericProcedureLink(yyscan_t yyscanner,const ClassDef *cd, + const char *memberText, + CodeOutputInterface &ol); +static bool getLink(yyscan_t yyscanner,UseSDict *usedict, // dictionary with used modules + const char *memberText, // exact member text + CodeOutputInterface &ol, + const char *text); +static void generateLink(yyscan_t yyscanner,CodeOutputInterface &ol, char *lname); +static int countLines(yyscan_t yyscanner); +static void startScope(yyscan_t yyscanner); +static void endScope(yyscan_t yyscanner); +static void addUse(yyscan_t yyscanner,const QCString &moduleName); +static void addLocalVar(yyscan_t yyscanner,const QCString &varName); +static MemberDef *getFortranDefs(yyscan_t yyscanner,const QCString &memberName, const QCString &moduleName, + UseSDict *usedict=0); +static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size); + + +//------------------------------------------------------------------- + +static std::mutex g_docCrossReferenceMutex; +static std::mutex g_countFlowKeywordsMutex; + +/* -----------------------------------------------------------------*/ +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size); + +%} + +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_L0 ("("[^)]*")") +ARGS_L1a [^()]*"("[^)]*")"[^)]* +ARGS_L1 ("("{ARGS_L1a}*")") +ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")" +ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2}) + +NUM_TYPE (complex|integer|logical|real) +LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.) +KIND {ARGS} +CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS})) +TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|{CHAR}|TYPE|CLASS|PROCEDURE|ENUMERATOR) + +INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" +ATTR_SPEC (IMPLICIT|ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PROTECTED|PRIVATE|PUBLIC|SAVE|TARGET|(NON_)?RECURSIVE|PURE|IMPURE|ELEMENTAL|VALUE|NOPASS|DEFERRED|CONTIGUOUS|VOLATILE) +ACCESS_SPEC (PROTECTED|PRIVATE|PUBLIC) +/* Assume that attribute statements are almost the same as attributes. */ +ATTR_STMT {ATTR_SPEC}|DIMENSION +FLOW (DO|SELECT|CASE|SELECT{BS}(CASE|TYPE)|WHERE|IF|THEN|ELSE|WHILE|FORALL|ELSEWHERE|ELSEIF|RETURN|CONTINUE|EXIT|GO{BS}TO) +COMMANDS (FORMAT|CONTAINS|MODULE{BS_}PROCEDURE|WRITE|READ|ALLOCATE|ALLOCATED|ASSOCIATED|PRESENT|DEALLOCATE|NULLIFY|SIZE|INQUIRE|OPEN|CLOSE|FLUSH|DATA|COMMON) +IGNORE (CALL) +PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?0 +LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?")" + +/* | */ + +%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 Import +%x Declaration +%x DeclarationBinding +%x DeclContLine +%x Parameterlist +%x String +%x Subprogend + +%% + /*==================================================================*/ + + /*-------- ignore ------------------------------------------------------------*/ + +<Start>{IGNORE}/{BS}"(" { // do not search keywords, intrinsics... TODO: complete list + codifyLines(yyscanner,yytext); + } + /*-------- inner construct ---------------------------------------------------*/ + +<Start>{COMMANDS}/{BS}[,( \t\n] { // highlight + /* font class is defined e.g. in doxygen.css */ + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>{FLOW}/{BS}[,( \t\n] { + if (yyextra->isFixedForm) + { + if ((yy_my_start == 1) && ((yytext[0] == 'c') || (yytext[0] == 'C'))) YY_FTN_REJECT; + } + if (yyextra->currentMemberDef && yyextra->currentMemberDef->isFunction()) + { + std::lock_guard<std::mutex> lock(g_countFlowKeywordsMutex); + MemberDefMutable *mdm = toMemberDefMutable(yyextra->currentMemberDef); + if (mdm) + { + mdm->incrementFlowKeyWordCount(); + } + } + /* font class is defined e.g. in doxygen.css */ + startFontClass(yyscanner,"keywordflow"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>{BS}(CASE|CLASS|TYPE){BS_}(IS|DEFAULT) { + startFontClass(yyscanner,"keywordflow"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>{BS}"end"({BS}{FLOW})/[ \t\n] { // list is a bit long as not all have possible end + startFontClass(yyscanner,"keywordflow"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>"implicit"{BS}("none"|{TYPE_SPEC}) { + startFontClass(yyscanner,"keywordtype"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>^{BS}"namelist"/[//] { // Namelist specification + startFontClass(yyscanner,"keywordtype"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } + /*-------- use statement -------------------------------------------*/ +<Start>"use"{BS_} { + startFontClass(yyscanner,"keywordtype"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(Use); + } +<Use>"ONLY" { // TODO: rename + startFontClass(yyscanner,"keywordtype"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(UseOnly); + } +<Use>{ID} { + QCString tmp = yytext; + tmp = tmp.lower(); + yyextra->insideBody=TRUE; + generateLink(yyscanner,*yyextra->code, yytext); + yyextra->insideBody=FALSE; + + /* append module name to use dict */ + yyextra->useEntry = new UseEntry(); + //yyextra->useEntry->module = yytext; + //yyextra->useMembers->append(yytext, yyextra->useEntry); + //addUse(yytext); + yyextra->useEntry->module = tmp; + yyextra->useMembers->append(tmp, yyextra->useEntry); + addUse(yyscanner,tmp); + } +<Use,UseOnly,Import>{BS},{BS} { codifyLines(yyscanner,yytext); } +<UseOnly,Import>{BS}&{BS}"\n" { codifyLines(yyscanner,yytext); + yyextra->contLineNr++; + YY_FTN_RESET} +<UseOnly>{ID} { + QCString tmp = yytext; + tmp = tmp.lower(); + yyextra->useEntry->onlyNames.append(tmp); + yyextra->insideBody=TRUE; + generateLink(yyscanner,*yyextra->code, yytext); + yyextra->insideBody=FALSE; + } +<Use,UseOnly,Import>"\n" { + unput(*yytext); + yy_pop_state(yyscanner); + YY_FTN_RESET + } +<*>"import"{BS}/"\n" | +<*>"import"{BS_} { + startFontClass(yyscanner,"keywordtype"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(Import); + } +<Import>{ID} { + yyextra->insideBody=TRUE; + generateLink(yyscanner,*yyextra->code, yytext); + yyextra->insideBody=FALSE; + } +<Import>("ONLY"|"NONE"|"ALL") { + startFontClass(yyscanner,"keywordtype"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } + /*-------- fortran module -----------------------------------------*/ +<Start>("block"{BS}"data"|"program"|"module"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { // + startScope(yyscanner); + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(ClassName); + if (!qstricmp(yytext,"module")) yyextra->currentModule="module"; + } +<Start>("enum")/{BS_}|{BS}{COMMA}{BS}{LANGUAGE_BIND_SPEC}|\n { // + startScope(yyscanner); + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(ClassName); + } +<*>{LANGUAGE_BIND_SPEC} { // + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>("type")/{BS_}|({COMMA}({ACCESS_SPEC}|ABSTRACT|EXTENDS))|\n { // + startScope(yyscanner); + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(ClassName); + } +<ClassName>{ID} { + if (yyextra->currentModule == "module") + { + yyextra->currentModule=yytext; + yyextra->currentModule = yyextra->currentModule.lower(); + } + generateLink(yyscanner,*yyextra->code,yytext); + yy_pop_state(yyscanner); + } +<ClassName>({ACCESS_SPEC}|ABSTRACT|EXTENDS)/[,:( ] { //| variable declaration + startFontClass(yyscanner,"keyword"); + yyextra->code->codify(yytext); + endFontClass(yyscanner); + } +<ClassName>\n { // interface may be without name + yy_pop_state(yyscanner); + YY_FTN_REJECT; + } +<Start>^{BS}"end"({BS_}"enum").* { + YY_FTN_REJECT; + } +<Start>^{BS}"end"({BS_}"type").* { + YY_FTN_REJECT; + } +<Start>^{BS}"end"({BS_}"module").* { // just reset yyextra->currentModule, rest is done in following rule + yyextra->currentModule=0; + YY_FTN_REJECT; + } + /*-------- subprog definition -------------------------------------*/ +<Start>({PREFIX}{BS_})?{TYPE_SPEC}{BS_}({PREFIX}{BS_})?{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Start>({PREFIX}{BS_})?{SUBPROG}{BS_} { // Fortran subroutine or function found + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(Subprog); + } +<Subprog>{ID} { // subroutine/function name + DBG_CTX((stderr, "===> start subprogram %s\n", yytext)); + startScope(yyscanner); + generateLink(yyscanner,*yyextra->code,yytext); + } +<Subprog>"result"/{BS}"("[^)]*")" { + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<Subprog>"("[^)]*")" { // ignore rest of line + codifyLines(yyscanner,yytext); + } +<Subprog,Subprogend>"\n" { codifyLines(yyscanner,yytext); + yyextra->contLineNr++; + yy_pop_state(yyscanner); + YY_FTN_RESET + } +<Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface")?{BS} { // Fortran subroutine or function ends + //cout << "===> end function " << yytext << endl; + endScope(yyscanner); + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(Subprogend); + } +<Subprogend>{ID}/{BS}(\n|!|;) { + generateLink(yyscanner,*yyextra->code,yytext); + yy_pop_state(yyscanner); + } +<Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface"){BS}/(\n|!|;) { // Fortran subroutine or function ends + //cout << "===> end function " << yytext << endl; + endScope(yyscanner); + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } + /*-------- variable declaration ----------------------------------*/ +<Start>^{BS}"real"/[,:( ] { // real is a bit tricky as it is a data type but also a function. + yy_push_state(YY_START,yyscanner); + BEGIN(Declaration); + startFontClass(yyscanner,"keywordtype"); + yyextra->code->codify(yytext); + endFontClass(yyscanner); + } +<Start>{TYPE_SPEC}/[,:( ] { + QCString typ = yytext; + typ = removeRedundantWhiteSpace(typ.lower()); + if (typ.startsWith("real")) YY_FTN_REJECT; + if (typ == "type" || typ == "class" || typ == "procedure") yyextra->inTypeDecl = 1; + yy_push_state(YY_START,yyscanner); + BEGIN(Declaration); + startFontClass(yyscanner,"keywordtype"); + yyextra->code->codify(yytext); + endFontClass(yyscanner); + } +<Start>{ATTR_SPEC} { + if (QCString(yytext) == "external") + { + yy_push_state(YY_START,yyscanner); + BEGIN(Declaration); + yyextra->isExternal = true; + } + startFontClass(yyscanner,"keywordtype"); + yyextra->code->codify(yytext); + endFontClass(yyscanner); + } +<Declaration>({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable declaration + if (QCString(yytext) == "external") yyextra->isExternal = true; + startFontClass(yyscanner,"keywordtype"); + yyextra->code->codify(yytext); + endFontClass(yyscanner); + } +<Declaration>{ID} { // local var + if (yyextra->isFixedForm && yy_my_start == 1) + { + startFontClass(yyscanner,"comment"); + yyextra->code->codify(yytext); + endFontClass(yyscanner); + } + else if (yyextra->currentMemberDef && + ((yyextra->currentMemberDef->isFunction() && (yyextra->currentMemberDef->typeString()!=QCString("subroutine") || yyextra->inTypeDecl)) || + yyextra->currentMemberDef->isVariable() || yyextra->currentMemberDef->isEnumValue() + ) + ) + { + generateLink(yyscanner,*yyextra->code, yytext); + } + else + { + yyextra->code->codify(yytext); + addLocalVar(yyscanner,yytext); + } + } +<Declaration>{BS}("=>"|"="){BS} { // Procedure binding + BEGIN(DeclarationBinding); + yyextra->code->codify(yytext); + } +<DeclarationBinding>{ID} { // Type bound procedure link + generateLink(yyscanner,*yyextra->code, yytext); + yy_pop_state(yyscanner); + } +<Declaration>[(] { // start of array or type / class specification + yyextra->bracketCount++; + yyextra->code->codify(yytext); + } + +<Declaration>[)] { // end array specification + yyextra->bracketCount--; + if (!yyextra->bracketCount) yyextra->inTypeDecl = 0; + yyextra->code->codify(yytext); + } + +<Declaration,DeclarationBinding>"&" { // continuation line + yyextra->code->codify(yytext); + if (!yyextra->isFixedForm) + { + yy_push_state(YY_START,yyscanner); + BEGIN(DeclContLine); + } + } +<DeclContLine>"\n" { // declaration not yet finished + yyextra->contLineNr++; + codifyLines(yyscanner,yytext); + yyextra->bracketCount = 0; + yy_pop_state(yyscanner); + YY_FTN_RESET + } +<Declaration,DeclarationBinding>"\n" { // end declaration line (?) + if (yyextra->endComment) + { + yyextra->endComment=FALSE; + } + else + { + codifyLines(yyscanner,yytext); + } + yyextra->bracketCount = 0; + yyextra->contLineNr++; + if (!(yyextra->hasContLine && yyextra->hasContLine[yyextra->contLineNr - 1])) + { + yyextra->isExternal = false; + yy_pop_state(yyscanner); + } + YY_FTN_RESET + } + + /*-------- subprog calls -----------------------------------------*/ + +<Start>"call"{BS_} { + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + yy_push_state(YY_START,yyscanner); + BEGIN(SubCall); + } +<SubCall>{ID} { // subroutine call + yyextra->insideBody=TRUE; + generateLink(yyscanner,*yyextra->code, yytext); + yyextra->insideBody=FALSE; + yy_pop_state(yyscanner); + } +<Start>{ID}{BS}/"(" { // function call + if (yyextra->isFixedForm && yy_my_start == 6) + { + // fixed form continuation line + YY_FTN_REJECT; + } + else if (QCString(yytext).stripWhiteSpace().lower() == "type") + { + yy_push_state(YY_START,yyscanner); + BEGIN(Declaration); + startFontClass(yyscanner,"keywordtype"); + yyextra->code->codify(QCString(yytext).stripWhiteSpace()); + endFontClass(yyscanner); + yyextra->code->codify(yytext + 4); + } + else + { + yyextra->insideBody=TRUE; + generateLink(yyscanner,*yyextra->code, yytext); + yyextra->insideBody=FALSE; + } + } + + /*-------- comments ---------------------------------------------------*/ +<Start,Declaration,DeclarationBinding>\n?{BS}"!>"|"!<" { // start comment line or comment block + if (yytext[0] == '\n') + { + yyextra->contLineNr++; + yy_old_start = 0; + yy_my_start = 1; + yy_end = static_cast<int>(yyleng); + } + // Actually we should see if ! on position 6, can be continuation + // but the chance is very unlikely, so no effort to solve it here + yy_push_state(YY_START,yyscanner); + BEGIN(DocBlock); + yyextra->docBlock=yytext; + } +<Declaration,DeclarationBinding>{BS}"!<" { // start comment line or comment block + yy_push_state(YY_START,yyscanner); + BEGIN(DocBlock); + yyextra->docBlock=yytext; + } + +<DocBlock>.* { // contents of current comment line + yyextra->docBlock+=yytext; + } +<DocBlock>"\n"{BS}("!>"|"!<"|"!!") { // comment block (next line is also comment line) + yyextra->contLineNr++; + yy_old_start = 0; + yy_my_start = 1; + yy_end = static_cast<int>(yyleng); + // Actually we should see if ! on position 6, can be continuation + // but the chance is very unlikely, so no effort to solve it here + yyextra->docBlock+=yytext; + } +<DocBlock>"\n" { // comment block ends at the end of this line + // remove special comment (default config) + yyextra->contLineNr++; + if (Config_getBool(STRIP_CODE_COMMENTS)) + { + yyextra->yyLineNr+=((QCString)yyextra->docBlock).contains('\n'); + yyextra->yyLineNr+=1; + nextCodeLine(yyscanner); + yyextra->endComment=TRUE; + } + else // do not remove comment + { + startFontClass(yyscanner,"comment"); + codifyLines(yyscanner,yyextra->docBlock); + endFontClass(yyscanner); + } + unput(*yytext); + yyextra->contLineNr--; + yy_pop_state(yyscanner); + YY_FTN_RESET + } + +<*>"!"[^><\n].*|"!"$ { // normal comment + if(YY_START == String) YY_FTN_REJECT; // ignore in strings + if (yyextra->isFixedForm && yy_my_start == 6) YY_FTN_REJECT; + startFontClass(yyscanner,"comment"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } + +<*>^[Cc*].* { // normal comment + if(! yyextra->isFixedForm) YY_FTN_REJECT; + + startFontClass(yyscanner,"comment"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<*>"assignment"/{BS}"("{BS}"="{BS}")" { + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } +<*>"operator"/{BS}"("[^)]*")" { + startFontClass(yyscanner,"keyword"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + } + + /*------ preprocessor --------------------------------------------*/ +<Start>"#".*\n { + if (yyextra->isFixedForm && yy_my_start == 6) YY_FTN_REJECT; + yyextra->contLineNr++; + startFontClass(yyscanner,"preprocessor"); + codifyLines(yyscanner,yytext); + endFontClass(yyscanner); + YY_FTN_RESET + } + /*------ variable references? -------------------------------------*/ + +<Start>"%"{BS}{ID} { // ignore references to elements + yyextra->code->codify(yytext); + } +<Start>{ID} { + yyextra->insideBody=TRUE; + generateLink(yyscanner,*yyextra->code, yytext); + yyextra->insideBody=FALSE; + } + /*------ strings --------------------------------------------------*/ +<String>\n { // string with \n inside + yyextra->contLineNr++; + yyextra->str+=yytext; + startFontClass(yyscanner,"stringliteral"); + codifyLines(yyscanner,yyextra->str); + endFontClass(yyscanner); + yyextra->str = ""; + YY_FTN_RESET + } +<String>\"|\' { // string ends with next quote without previous backspace + if(yytext[0]!=yyextra->stringStartSymbol) YY_FTN_REJECT; // single vs double quote + yyextra->str+=yytext; + startFontClass(yyscanner,"stringliteral"); + codifyLines(yyscanner,yyextra->str); + endFontClass(yyscanner); + yy_pop_state(yyscanner); + } +<String>. {yyextra->str+=yytext;} + +<*>\"|\' { /* string starts */ + /* if(YY_START == StrIgnore) YY_FTN_REJECT; // ignore in simple comments */ + if (yyextra->isFixedForm && yy_my_start == 6) YY_FTN_REJECT; + yy_push_state(YY_START,yyscanner); + yyextra->stringStartSymbol=yytext[0]; // single or double quote + BEGIN(String); + yyextra->str=yytext; + } + /*-----------------------------------------------------------------------------*/ + +<*>\n { + if (yyextra->endComment) + { + yyextra->endComment=FALSE; + } + else + { + codifyLines(yyscanner,yytext); + // comment cannot extend over the end of a line so should always be terminated at the end of the line. + if (yyextra->currentFontClass && !strcmp(yyextra->currentFontClass,"comment")) endFontClass(yyscanner); + } + yyextra->contLineNr++; + YY_FTN_RESET + } +<*>^{BS}"type"{BS}"=" { yyextra->code->codify(yytext); } + +<*>[\x80-\xFF]* { // keep utf8 characters together... + if (yyextra->isFixedForm && yy_my_start > fixedCommentAfter) + { + startFontClass(yyscanner,"comment"); + codifyLines(yyscanner,yytext); + } + else + { + yyextra->code->codify(yytext); + } + } +<*>. { + if (yyextra->isFixedForm && yy_my_start > fixedCommentAfter) + { + //yy_push_state(YY_START,yyscanner); + //BEGIN(DocBlock); + //yyextra->docBlock=yytext; + startFontClass(yyscanner,"comment"); + codifyLines(yyscanner,yytext); + } + else + { + yyextra->code->codify(yytext); + } + } +<*>{LOG_OPER} { // Fortran logical comparison keywords + yyextra->code->codify(yytext); + } +<*><<EOF>> { + if (YY_START == DocBlock) { + if (!Config_getBool(STRIP_CODE_COMMENTS)) + { + startFontClass(yyscanner,"comment"); + codifyLines(yyscanner,yyextra->docBlock); + endFontClass(yyscanner); + } + } + yyterminate(); + } +%% + +/*@ ---------------------------------------------------------------------------- + */ -static void endFontClass() +static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size) { - if (g_currentFontClass) + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yy_size_t inputPosition = yyextra->inputPosition; + const char *s = yyextra->inputString + inputPosition; + yy_size_t c=0; + while( c < max_size && *s) { - g_code->endFontClass(); - g_currentFontClass=0; + *buf++ = *s++; + c++; } + yyextra->inputPosition += c; + return c; } -static void startFontClass(const char *s) +static void endFontClass(yyscan_t yyscanner) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (yyextra->currentFontClass) + { + yyextra->code->endFontClass(); + yyextra->currentFontClass=0; + } +} + +static void startFontClass(yyscan_t yyscanner,const char *s) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; // if font class is already set don't stop and start it. // strcmp does not like null pointers as input. - if (!g_currentFontClass || !s || strcmp(g_currentFontClass,s)) + if (!yyextra->currentFontClass || !s || strcmp(yyextra->currentFontClass,s)) { - endFontClass(); - g_code->startFontClass(s); - g_currentFontClass=s; + endFontClass(yyscanner); + yyextra->code->startFontClass(s); + yyextra->currentFontClass=s; } } -static void setCurrentDoc(const QCString &anchor) +static void setCurrentDoc(yyscan_t yyscanner,const QCString &anchor) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (Doxygen::searchIndex) { - if (g_searchCtx) + if (yyextra->searchCtx) { - Doxygen::searchIndex->setCurrentDoc(g_searchCtx,g_searchCtx->anchor(),FALSE); + yyextra->code->setCurrentDoc(yyextra->searchCtx,yyextra->searchCtx->anchor(),FALSE); } else { - Doxygen::searchIndex->setCurrentDoc(g_sourceFileDef,anchor,TRUE); + yyextra->code->setCurrentDoc(yyextra->sourceFileDef,anchor,TRUE); } } } -static void addToSearchIndex(const char *text) +static void addToSearchIndex(yyscan_t yyscanner,const char *text) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (Doxygen::searchIndex) { - Doxygen::searchIndex->addWord(text,FALSE); + yyextra->code->addWord(text,FALSE); } } -/*! start a new line of code, inserting a line number if g_sourceFileDef +/*! start a new line of code, inserting a line number if yyextra->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() +static void startCodeLine(yyscan_t yyscanner) { - if (g_sourceFileDef) + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (yyextra->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) + //lineNumber.sprintf("%05d",yyextra->yyLineNr); + //lineAnchor.sprintf("l%05d",yyextra->yyLineNr); + + const Definition *d = yyextra->sourceFileDef->getSourceDefinition(yyextra->yyLineNr); + //printf("startCodeLine %d d=%s\n", yyextra->yyLineNr,d ? d->name().data() : "<null>"); + if (!yyextra->includeCodeFragment && d) { - g_currentDefinition = d; - g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr); - g_insideBody = FALSE; - g_endComment = FALSE; - g_parmType.resize(0); - g_parmName.resize(0); + yyextra->currentDefinition = d; + yyextra->currentMemberDef = yyextra->sourceFileDef->getSourceMember(yyextra->yyLineNr); + yyextra->insideBody = FALSE; + yyextra->endComment = FALSE; QCString lineAnchor; - lineAnchor.sprintf("l%05d",g_yyLineNr); - if (g_currentMemberDef) + lineAnchor.sprintf("l%05d",yyextra->yyLineNr); + if (yyextra->currentMemberDef) { - g_code->writeLineNumber(g_currentMemberDef->getReference(), - g_currentMemberDef->getOutputFileBase(), - g_currentMemberDef->anchor(),g_yyLineNr); - setCurrentDoc(lineAnchor); + yyextra->code->writeLineNumber(yyextra->currentMemberDef->getReference(), + yyextra->currentMemberDef->getOutputFileBase(), + yyextra->currentMemberDef->anchor(),yyextra->yyLineNr); + setCurrentDoc(yyscanner,lineAnchor); } else if (d->isLinkableInProject()) { - g_code->writeLineNumber(d->getReference(), - d->getOutputFileBase(), - 0,g_yyLineNr); - setCurrentDoc(lineAnchor); + yyextra->code->writeLineNumber(d->getReference(), + d->getOutputFileBase(), + 0,yyextra->yyLineNr); + setCurrentDoc(yyscanner,lineAnchor); } } else { - g_code->writeLineNumber(0,0,0,g_yyLineNr); + yyextra->code->writeLineNumber(0,0,0,yyextra->yyLineNr); } } - g_code->startCodeLine(g_sourceFileDef); - if (g_currentFontClass) + yyextra->code->startCodeLine(yyextra->sourceFileDef); + if (yyextra->currentFontClass) { - g_code->startFontClass(g_currentFontClass); + yyextra->code->startFontClass(yyextra->currentFontClass); } } -static void endFontClass(); -static void endCodeLine() +static void endCodeLine(yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + endFontClass(yyscanner); + yyextra->code->endCodeLine(); +} + +static void nextCodeLine(yyscan_t yyscanner) { - endFontClass(); - g_code->endCodeLine(); + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + const char * fc = yyextra->currentFontClass; + endCodeLine(yyscanner); + if (yyextra->yyLineNr<yyextra->inputLines) + { + yyextra->currentFontClass = fc; + startCodeLine(yyscanner); + } } /*! write a code fragment 'text' that may span multiple lines, inserting * line numbers for each line. */ -static void codifyLines(char *text) +static void codifyLines(yyscan_t yyscanner,const char *text) { - //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text); - char *p=text,*sp=p; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + //printf("codifyLines(%d,\"%s\")\n",yyextra->yyLineNr,text); + const char *p=text,*sp=p; char c; bool done=FALSE; - const char * tmp_currentFontClass = g_currentFontClass; 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(); - } - if (tmp_currentFontClass) - { - startFontClass(tmp_currentFontClass); - } + yyextra->yyLineNr++; + int l = (int)(p-sp-1); + char *tmp = (char*)malloc(l+1); + memcpy(tmp,sp,l); + tmp[l]='\0'; + yyextra->code->codify(tmp); + free(tmp); + nextCodeLine(yyscanner); } else { - g_code->codify(sp); + yyextra->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 + * 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, +static void writeMultiLineCodeLink(yyscan_t yyscanner,CodeOutputInterface &ol, Definition *d,const char *text) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; static bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS); - TooltipManager::instance()->addTooltip(d); + TooltipManager::instance().addTooltip(ol,d); QCString ref = d->getReference(); QCString file = d->getOutputFileBase(); QCString anchor = d->anchor(); - QCString tooltip; + QCString tooltip; if (!sourceTooltips) // fall back to simple "title" tooltips { tooltip = d->briefDescriptionAsTooltip(); @@ -342,15 +1040,11 @@ static void writeMultiLineCodeLink(CodeOutputInterface &ol, while ((c=*p++) && c!='\n') { } if (c=='\n') { - g_yyLineNr++; + yyextra->yyLineNr++; *(p-1)='\0'; //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); ol.writeCodeLink(ref,file,anchor,sp,tooltip); - endCodeLine(); - if (g_yyLineNr<g_inputLines) - { - startCodeLine(); - } + nextCodeLine(yyscanner); } else { @@ -373,7 +1067,7 @@ static bool getFortranNamespaceDefs(const QCString &mname, if (mname.isEmpty()) return FALSE; /* empty name => nothing to link */ // search for module - if ((cd=Doxygen::namespaceSDict->find(mname))) return TRUE; + if ((cd=Doxygen::namespaceLinkedMap->find(mname))) return TRUE; return FALSE; } @@ -384,34 +1078,34 @@ static bool getFortranNamespaceDefs(const QCString &mname, @param moduleName name of enclosing module or null, if global entry @param cd the entry, if found or null @param usedict dictionary of data of USE-statement - @returns true, if type is found + @returns true, if type is found */ -static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName, - ClassDef *&cd, UseSDict *usedict=0) +static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName, + ClassDef *&cd, UseSDict *usedict) { 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))) + // search for type + if ((cd=Doxygen::classLinkedMap->find(tname))) { //cout << "=== type found in global module" << endl; return TRUE; } - else if (moduleName && (cd= Doxygen::classSDict->find(moduleName+"::"+tname))) + else if (moduleName && (cd= Doxygen::classLinkedMap->find(moduleName+"::"+tname))) { //cout << "=== type found in local module" << endl; return TRUE; } - else + else { UseEntry *use; for (UseSDict::Iterator di(*usedict); (use=di.current()); ++di) { - if ((cd= Doxygen::classSDict->find(use->module+"::"+tname))) + if ((cd= Doxygen::classLinkedMap->find(use->module+"::"+tname))) { - //cout << "=== type found in used module" << endl; + //cout << "=== type found in used module" << endl; return TRUE; } } @@ -424,96 +1118,101 @@ static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName 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 md the entry, if found or null @param usedict array of data of USE-statement - @returns true, if found + @returns MemberDef pointer, if found, or nullptr otherwise */ -static bool getFortranDefs(const QCString &memberName, const QCString &moduleName, - MemberDef *&md, UseSDict *usedict=0) +static MemberDef *getFortranDefs(yyscan_t yyscanner,const QCString &memberName, const QCString &moduleName, + UseSDict *usedict) { - if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (memberName.isEmpty()) return nullptr; /* empty name => nothing to link */ // look in local variables - QListIterator<Scope> it(scopeStack); + QListIterator<Scope> it(yyextra->scopeStack); Scope *scope; for (it.toLast();(scope=it.current());--it) { - if (scope->localVars.find(memberName) && (!scope->externalVars.find(memberName))) - return FALSE; + std::string lowMemName = memberName.lower().str(); + if (scope->localVars.find(lowMemName)!=std::end(scope->localVars) && // local var + scope->externalVars.find(lowMemName)==std::end(scope->externalVars)) // and not external + { + return nullptr; + } } // search for function - MemberName *mn = Doxygen::functionNameSDict->find(memberName); + MemberName *mn = Doxygen::functionNameLinkedMap->find(memberName); if (!mn) { - mn = Doxygen::memberNameSDict->find(memberName); + mn = Doxygen::memberNameLinkedMap->find(memberName); } if (mn) // name is known { - MemberNameIterator mli(*mn); - for (mli.toFirst();(md=mli.current());++mli) // all found functions with given name - { - const FileDef *fd=md->getFileDef(); - const GroupDef *gd=md->getGroupDef(); - const ClassDef *cd=md->getClassDef(); - - //cout << "found link with same name: " << fd->fileName() << " " << memberName; - //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl; + // all found functions with given name + for (const auto &md : *mn) + { + const FileDef *fd=md->getFileDef(); + const GroupDef *gd=md->getGroupDef(); + const ClassDef *cd=md->getClassDef(); - if ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) - { - const NamespaceDef *nspace= md->getNamespaceDef(); + //cout << "found link with same name: " << fd->fileName() << " " << memberName; + //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl; - if (nspace == 0) - { // found function in global scope - if(cd == 0) { // Skip if bound to type - return TRUE; + if ((gd && gd->isLinkable()) || (fd && fd->isLinkable())) + { + const NamespaceDef *nspace= md->getNamespaceDef(); + + if (nspace == 0) + { // found function in global scope + if(cd == 0) + { // Skip if bound to type + return md.get(); + } + } + else if (moduleName == nspace->name()) + { // found in local scope + return md.get(); + } + else + { // else search in used modules + QCString usedModuleName= nspace->name(); + UseEntry *ue= usedict->find(usedModuleName); + if (ue) + { + // check if only-list exists and if current entry exists is this list + QCStringList &only= ue->onlyNames; + if (only.isEmpty()) + { + //cout << " found in module " << usedModuleName << " entry " << memberName << endl; + return md.get(); // whole module used + } + else + { + for ( QCStringList::Iterator lit = only.begin(); lit != only.end(); ++lit) + { + //cout << " search in only: " << usedModuleName << ":: " << memberName << "==" << (*it)<< endl; + if (memberName == *lit) + { + return md.get(); // found in ONLY-part of use list + } } - } - 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 - QCStringList &only= ue->onlyNames; - if (only.isEmpty()) - { - //cout << " found in module " << moduleName << " entry " << memberName << endl; - return TRUE; // whole module used - } - else - { - for ( QCStringList::Iterator it = only.begin(); it != only.end(); ++it) - { - //cout << " search in only: " << moduleName << ":: " << memberName << "==" << (*it)<< endl; - if (memberName == *it) - { - return TRUE; // found in ONLY-part of use list - } - } - } - } - } - } // if linkable - } // for + } + } + } + } // if linkable + } // for } - return FALSE; + return nullptr; } /** 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) +static bool getGenericProcedureLink(yyscan_t yyscanner,const ClassDef *cd, + const char *memberText, + CodeOutputInterface &ol) { (void)cd; (void)memberText; @@ -521,787 +1220,159 @@ static bool getGenericProcedureLink(const ClassDef *cd, return FALSE; } -static bool getLink(UseSDict *usedict, // dictionary with used modules +static bool getLink(yyscan_t yyscanner,UseSDict *usedict, // dictionary with used modules const char *memberText, // exact member text - CodeOutputInterface &ol, - const char *text) + CodeOutputInterface &ol, + const char *text) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; MemberDef *md=0; QCString memberName= removeRedundantWhiteSpace(memberText); - if (getFortranDefs(memberName, currentModule, md, usedict) && md->isLinkable()) - { + if ((md=getFortranDefs(yyscanner,memberName, yyextra->currentModule, usedict)) && md->isLinkable()) + { if (md->isVariable() && (md->getLanguage()!=SrcLangExt_Fortran)) return FALSE; // Non Fortran variables aren't handled yet, // see also linkifyText in util.cpp const Definition *d = md->getOuterScope()==Doxygen::globalScope ? - md->getBodyDef() : md->getOuterScope(); + md->getBodyDef() : md->getOuterScope(); if (md->getGroupDef()) d = md->getGroupDef(); if (d && d->isLinkable()) { - if (g_currentDefinition && g_currentMemberDef && - md!=g_currentMemberDef && g_insideBody && g_collectXRefs) - { - addDocCrossReference(g_currentMemberDef,md); - } - writeMultiLineCodeLink(ol,md,text ? text : memberText); - addToSearchIndex(text ? text : memberText); + if (yyextra->currentDefinition && yyextra->currentMemberDef && + md!=yyextra->currentMemberDef && yyextra->insideBody && yyextra->collectXRefs) + { + std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex); + addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(md)); + } + writeMultiLineCodeLink(yyscanner,ol,md,text ? text : memberText); + addToSearchIndex(yyscanner, text ? text : memberText); return TRUE; - } + } } return FALSE; } -static void generateLink(CodeOutputInterface &ol, char *lname) +static void generateLink(yyscan_t yyscanner,CodeOutputInterface &ol, char *lname) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; ClassDef *cd=0; NamespaceDef *nsd=0; QCString tmp = lname; tmp = removeRedundantWhiteSpace(tmp.lower()); - + // check if lowercase lname is a linkable type or interface - if ( (getFortranTypeDefs(tmp, currentModule, cd, useMembers)) && cd->isLinkable() ) + if ( (getFortranTypeDefs(tmp, yyextra->currentModule, cd, yyextra->useMembers)) && cd->isLinkable() ) { if ( (cd->compoundType() == ClassDef::Class) && // was Entry::INTERFACE_SEC) && - (getGenericProcedureLink(cd, tmp, ol)) ) + (getGenericProcedureLink(yyscanner, cd, tmp, ol)) ) { - //cout << "=== generic procedure resolved" << endl; - } - else + //cout << "=== generic procedure resolved" << endl; + } + else { // write type or interface link - writeMultiLineCodeLink(ol,cd,tmp); - addToSearchIndex(tmp.data()); + writeMultiLineCodeLink(yyscanner, ol,cd,tmp); + addToSearchIndex(yyscanner, tmp); } } // check for module else if ( (getFortranNamespaceDefs(tmp, nsd)) && nsd->isLinkable() ) { // write module link - writeMultiLineCodeLink(ol,nsd,tmp); - addToSearchIndex(tmp.data()); + writeMultiLineCodeLink(yyscanner,ol,nsd,tmp); + addToSearchIndex(yyscanner,tmp); } // check for function/variable - else if (getLink(useMembers, tmp, ol, tmp)) + else if (getLink(yyscanner,yyextra->useMembers, tmp, ol, tmp)) { //cout << "=== found link for lowercase " << lname << endl; } - else + else { // nothing found, just write out the word //startFontClass("charliteral"); //test - codifyLines(tmp); - //endFontClass(); //test - addToSearchIndex(tmp.data()); + codifyLines(yyscanner,tmp); + //endFontClass(yyscanner); //test + addToSearchIndex(yyscanner,tmp); } } /*! counts the number of lines in the input */ -static int countLines() +static int countLines(yyscan_t yyscanner) { - const char *p=g_inputString; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + const char *p=yyextra->inputString; char c; int count=1; - while ((c=*p)) - { - p++ ; - if (c=='\n') count++; + while ((c=*p)) + { + p++ ; + if (c=='\n') count++; } - if (p>g_inputString && *(p-1)!='\n') + if (p>yyextra->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; - } + count++, + yyextra->needsTermination=TRUE; + } return count; } //---------------------------------------------------------------------------- /** start scope */ -static void startScope() +static void startScope(yyscan_t yyscanner) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; DBG_CTX((stderr, "===> startScope %s",yytext)); Scope *scope = new Scope; - scopeStack.append(scope); + yyextra->scopeStack.append(scope); } /** end scope */ -static void endScope() +static void endScope(yyscan_t yyscanner) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; DBG_CTX((stderr,"===> endScope %s",yytext)); - if (scopeStack.isEmpty()) + if (yyextra->scopeStack.isEmpty()) { - DBG_CTX((stderr,"WARNING: fortrancode.l: stack empty!\n")); + DBG_CTX((stderr,"WARNING: fortrancode.l: stack empty!\n")); return; } - Scope *scope = scopeStack.getLast(); - scopeStack.removeLast(); - for ( QCStringList::Iterator it = scope->useNames.begin(); it != scope->useNames.end(); ++it) + Scope *scope = yyextra->scopeStack.getLast(); + yyextra->scopeStack.removeLast(); + for ( QCStringList::Iterator it = scope->useNames.begin(); it != scope->useNames.end(); ++it) { - useMembers->remove(*it); + yyextra->useMembers->remove(*it); } delete scope; } -static void addUse(const QCString &moduleName) +static void addUse(yyscan_t yyscanner,const QCString &moduleName) { - if (!scopeStack.isEmpty()) - scopeStack.getLast()->useNames.append(moduleName); + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (!yyextra->scopeStack.isEmpty()) + yyextra->scopeStack.getLast()->useNames.append(moduleName); } -static void addLocalVar(const QCString &varName) +static void addLocalVar(yyscan_t yyscanner,const QCString &varName) { - if (!scopeStack.isEmpty()) + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (!yyextra->scopeStack.isEmpty()) { - scopeStack.getLast()->localVars.insert(varName, (void*)1); - if (g_isExternal) scopeStack.getLast()->externalVars.insert(varName, (void*)1); + std::string lowVarName = varName.lower().str(); + yyextra->scopeStack.getLast()->localVars.insert(lowVarName); + if (yyextra->isExternal) yyextra->scopeStack.getLast()->externalVars.insert(lowVarName); } } -//---------------------------------------------------------------------------- - -/* -----------------------------------------------------------------*/ -#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_L0 ("("[^)]*")") -ARGS_L1a [^()]*"("[^)]*")"[^)]* -ARGS_L1 ("("{ARGS_L1a}*")") -ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")" -ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2}) - -NUM_TYPE (complex|integer|logical|real) -LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.) -KIND {ARGS} -CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS})) -TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|{CHAR}|TYPE|CLASS|PROCEDURE|ENUMERATOR) - -INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" -ATTR_SPEC (IMPLICIT|ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PROTECTED|PRIVATE|PUBLIC|SAVE|TARGET|(NON_)?RECURSIVE|PURE|IMPURE|ELEMENTAL|VALUE|NOPASS|DEFERRED|CONTIGUOUS|VOLATILE) -ACCESS_SPEC (PROTECTED|PRIVATE|PUBLIC) -/* Assume that attribute statements are almost the same as attributes. */ -ATTR_STMT {ATTR_SPEC}|DIMENSION -FLOW (DO|SELECT|CASE|SELECT{BS}(CASE|TYPE)|WHERE|IF|THEN|ELSE|WHILE|FORALL|ELSEWHERE|ELSEIF|RETURN|CONTINUE|EXIT|GO{BS}TO) -COMMANDS (FORMAT|CONTAINS|MODULE{BS_}PROCEDURE|WRITE|READ|ALLOCATE|ALLOCATED|ASSOCIATED|PRESENT|DEALLOCATE|NULLIFY|SIZE|INQUIRE|OPEN|CLOSE|FLUSH|DATA|COMMON) -IGNORE (CALL) -PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?0 -LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?")" - -/* | */ - -%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 Import -%x Declaration -%x DeclarationBinding -%x DeclContLine -%x Parameterlist -%x String -%x Subprogend - -%% - /*==================================================================*/ - - /*-------- ignore ------------------------------------------------------------*/ - -<Start>{IGNORE}/{BS}"(" { // do not search keywords, intrinsics... TODO: complete list - codifyLines(yytext); - } - /*-------- inner construct ---------------------------------------------------*/ - -<Start>{COMMANDS}/{BS}[,( \t\n] { // highlight - /* font class is defined e.g. in doxygen.css */ - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } -<Start>{FLOW}/{BS}[,( \t\n] { - if (g_isFixedForm) - { - if ((yy_my_start == 1) && ((yytext[0] == 'c') || (yytext[0] == 'C'))) YY_FTN_REJECT; - } - if (g_currentMemberDef && g_currentMemberDef->isFunction()) - { - g_currentMemberDef->incrementFlowKeyWordCount(); - } - /* font class is defined e.g. in doxygen.css */ - startFontClass("keywordflow"); - codifyLines(yytext); - endFontClass(); - } -<Start>{BS}(CASE|CLASS|TYPE){BS_}(IS|DEFAULT) { - startFontClass("keywordflow"); - codifyLines(yytext); - endFontClass(); - } -<Start>{BS}"end"({BS}{FLOW})/[ \t\n] { // list is a bit long as not all have possible end - startFontClass("keywordflow"); - codifyLines(yytext); - endFontClass(); - } -<Start>"implicit"{BS}("none"|{TYPE_SPEC}) { - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - } -<Start>^{BS}"namelist"/[//] { // Namelist specification - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - } - /*-------- use statement -------------------------------------------*/ -<Start>"use"{BS_} { - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(Use); - } -<Use>"ONLY" { // TODO: rename - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(UseOnly); - } -<Use>{ID} { - QCString tmp = yytext; - tmp = tmp.lower(); - g_insideBody=TRUE; - generateLink(*g_code, yytext); - g_insideBody=FALSE; - - /* append module name to use dict */ - useEntry = new UseEntry(); - //useEntry->module = yytext; - //useMembers->append(yytext, useEntry); - //addUse(yytext); - useEntry->module = tmp; - useMembers->append(tmp, useEntry); - addUse(tmp); - } -<Use,UseOnly,Import>{BS},{BS} { codifyLines(yytext); } -<UseOnly,Import>{BS}&{BS}"\n" { codifyLines(yytext); - g_contLineNr++; - YY_FTN_RESET} -<UseOnly>{ID} { - QCString tmp = yytext; - tmp = tmp.lower(); - useEntry->onlyNames.append(tmp); - g_insideBody=TRUE; - generateLink(*g_code, yytext); - g_insideBody=FALSE; - } -<Use,UseOnly,Import>"\n" { - unput(*yytext); - yy_pop_state();YY_FTN_RESET - } -<*>"import"{BS}/"\n" | -<*>"import"{BS_} { - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(Import); - } -<Import>{ID} { - g_insideBody=TRUE; - generateLink(*g_code, yytext); - g_insideBody=FALSE; - } -<Import>("ONLY"|"NONE"|"ALL") { - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - } - /*-------- fortran module -----------------------------------------*/ -<Start>("block"{BS}"data"|"program"|"module"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { // - startScope(); - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(ClassName); - if (!qstricmp(yytext,"module")) currentModule="module"; - } -<Start>("enum")/{BS_}|{BS}{COMMA}{BS}{LANGUAGE_BIND_SPEC}|\n { // - startScope(); - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(ClassName); - currentClass="class"; - } -<*>{LANGUAGE_BIND_SPEC} { // - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } -<Start>("type")/{BS_}|({COMMA}({ACCESS_SPEC}|ABSTRACT|EXTENDS))|\n { // - startScope(); - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(ClassName); - currentClass="class"; - } -<ClassName>{ID} { - if (currentModule == "module") - { - currentModule=yytext; - currentModule = currentModule.lower(); - } - generateLink(*g_code,yytext); - yy_pop_state(); - } -<ClassName>({ACCESS_SPEC}|ABSTRACT|EXTENDS)/[,:( ] { //| variable declaration - startFontClass("keyword"); - g_code->codify(yytext); - endFontClass(); - } -<ClassName>\n { // interface may be without name - yy_pop_state(); - YY_FTN_REJECT; - } -<Start>^{BS}"end"({BS_}"enum").* { // just reset currentClass, rest is done in following rule - currentClass=0; - YY_FTN_REJECT; - } -<Start>^{BS}"end"({BS_}"type").* { // just reset currentClass, rest is done in following rule - currentClass=0; - YY_FTN_REJECT; - } -<Start>^{BS}"end"({BS_}"module").* { // just reset currentModule, rest is done in following rule - currentModule=0; - YY_FTN_REJECT; - } - /*-------- subprog definition -------------------------------------*/ -<Start>({PREFIX}{BS_})?{TYPE_SPEC}{BS_}({PREFIX}{BS_})?{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } -<Start>({PREFIX}{BS_})?{SUBPROG}{BS_} { // Fortran subroutine or function found - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(Subprog); - } -<Subprog>{ID} { // subroutine/function name - DBG_CTX((stderr, "===> start subprogram %s\n", yytext)); - startScope(); - generateLink(*g_code,yytext); - } -<Subprog>"result"/{BS}"("[^)]*")" { - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } -<Subprog>"("[^)]*")" { // ignore rest of line - codifyLines(yytext); - } -<Subprog,Subprogend>"\n" { codifyLines(yytext); - g_contLineNr++; - yy_pop_state(); - YY_FTN_RESET - } -<Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface")?{BS} { // Fortran subroutine or function ends - //cout << "===> end function " << yytext << endl; - endScope(); - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - yy_push_state(YY_START); - BEGIN(Subprogend); - } -<Subprogend>{ID}/{BS}(\n|!|;) { - generateLink(*g_code,yytext); - yy_pop_state(); - } -<Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface"){BS}/(\n|!|;) { // Fortran subroutine or function ends - //cout << "===> end function " << yytext << endl; - endScope(); - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } - /*-------- variable declaration ----------------------------------*/ -<Start>^{BS}"real"/[,:( ] { // real is a bit tricky as it is a data type but also a function. - yy_push_state(YY_START); - BEGIN(Declaration); - startFontClass("keywordtype"); - g_code->codify(yytext); - endFontClass(); - } -<Start>{TYPE_SPEC}/[,:( ] { - QCString typ = yytext; - typ = removeRedundantWhiteSpace(typ.lower()); - if (typ.startsWith("real")) YY_FTN_REJECT; - if (typ == "type" || typ == "class" || typ == "procedure") inTypeDecl = 1; - yy_push_state(YY_START); - BEGIN(Declaration); - startFontClass("keywordtype"); - g_code->codify(yytext); - endFontClass(); - } -<Start>{ATTR_SPEC} { - if (QCString(yytext) == "external") - { - yy_push_state(YY_START); - BEGIN(Declaration); - g_isExternal = true; - } - startFontClass("keywordtype"); - g_code->codify(yytext); - endFontClass(); - } -<Declaration>({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable declaration - if (QCString(yytext) == "external") g_isExternal = true; - startFontClass("keywordtype"); - g_code->codify(yytext); - endFontClass(); - } -<Declaration>{ID} { // local var - if (g_isFixedForm && yy_my_start == 1) - { - startFontClass("comment"); - g_code->codify(yytext); - endFontClass(); - } - else if (g_currentMemberDef && - ((g_currentMemberDef->isFunction() && (g_currentMemberDef->typeString()!=QCString("subroutine") || inTypeDecl)) || - g_currentMemberDef->isVariable() || g_currentMemberDef->isEnumValue() - ) - ) - { - generateLink(*g_code, yytext); - } - else - { - g_code->codify(yytext); - addLocalVar(yytext); - } - } -<Declaration>{BS}("=>"|"="){BS} { // Procedure binding - BEGIN(DeclarationBinding); - g_code->codify(yytext); - } -<DeclarationBinding>{ID} { // Type bound procedure link - generateLink(*g_code, yytext); - yy_pop_state(); - } -<Declaration>[(] { // start of array or type / class specification - bracketCount++; - g_code->codify(yytext); - } - -<Declaration>[)] { // end array specification - bracketCount--; - if (!bracketCount) inTypeDecl = 0; - g_code->codify(yytext); - } - -<Declaration,DeclarationBinding>"&" { // continuation line - g_code->codify(yytext); - if (!g_isFixedForm) - { - yy_push_state(YY_START); - BEGIN(DeclContLine); - } - } -<DeclContLine>"\n" { // declaration not yet finished - g_contLineNr++; - codifyLines(yytext); - bracketCount = 0; - yy_pop_state(); - YY_FTN_RESET - } -<Declaration,DeclarationBinding>"\n" { // end declaration line (?) - if (g_endComment) - { - g_endComment=FALSE; - } - else - { - codifyLines(yytext); - } - bracketCount = 0; - g_contLineNr++; - if (!(g_hasContLine && g_hasContLine[g_contLineNr - 1])) - { - g_isExternal = false; - yy_pop_state(); - } - YY_FTN_RESET - } - - /*-------- subprog calls -----------------------------------------*/ - -<Start>"call"{BS_} { - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - 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 - if (g_isFixedForm && yy_my_start == 6) - { - // fixed form continuation line - YY_FTN_REJECT; - } - else if (QCString(yytext).stripWhiteSpace().lower() == "type") - { - yy_push_state(YY_START); - BEGIN(Declaration); - startFontClass("keywordtype"); - g_code->codify(QCString(yytext).stripWhiteSpace()); - endFontClass(); - g_code->codify(yytext + 4); - } - else - { - g_insideBody=TRUE; - generateLink(*g_code, yytext); - g_insideBody=FALSE; - } - } - - /*-------- comments ---------------------------------------------------*/ -<Start,Declaration,DeclarationBinding>\n?{BS}"!>"|"!<" { // start comment line or comment block - if (yytext[0] == '\n') - { - g_contLineNr++; - yy_old_start = 0; - yy_my_start = 1; - yy_end = yyleng; - } - // Actually we should see if ! on position 6, can be continuation - // but the chance is very unlikely, so no effort to solve it here - yy_push_state(YY_START); - BEGIN(DocBlock); - docBlock=yytext; - } -<Declaration,DeclarationBinding>{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) - g_contLineNr++; - yy_old_start = 0; - yy_my_start = 1; - yy_end = yyleng; - // Actually we should see if ! on position 6, can be continuation - // but the chance is very unlikely, so no effort to solve it here - docBlock+=yytext; - } -<DocBlock>"\n" { // comment block ends at the end of this line - // remove special comment (default config) - g_contLineNr++; - if (Config_getBool(STRIP_CODE_COMMENTS)) - { - g_yyLineNr+=((QCString)docBlock).contains('\n'); - g_yyLineNr+=1; - endCodeLine(); - if (g_yyLineNr<g_inputLines) - { - startCodeLine(); - } - g_endComment=TRUE; - } - else // do not remove comment - { - startFontClass("comment"); - codifyLines(docBlock); - endFontClass(); - } - unput(*yytext); - g_contLineNr--; - yy_pop_state(); - YY_FTN_RESET - } - -<*>"!"[^><\n].*|"!"$ { // normal comment - if(YY_START == String) YY_FTN_REJECT; // ignore in strings - if (g_isFixedForm && yy_my_start == 6) YY_FTN_REJECT; - startFontClass("comment"); - codifyLines(yytext); - endFontClass(); - } - -<*>^[Cc*].* { // normal comment - if(! g_isFixedForm) YY_FTN_REJECT; - - startFontClass("comment"); - codifyLines(yytext); - endFontClass(); - } -<*>"assignment"/{BS}"("{BS}"="{BS}")" { - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } -<*>"operator"/{BS}"("[^)]*")" { - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } - - /*------ preprocessor --------------------------------------------*/ -<Start>"#".*\n { - if (g_isFixedForm && yy_my_start == 6) YY_FTN_REJECT; - g_contLineNr++; - startFontClass("preprocessor"); - codifyLines(yytext); - endFontClass(); - YY_FTN_RESET - } - /*------ variable references? -------------------------------------*/ - -<Start>"%"{BS}{ID} { // ignore references to elements - g_code->codify(yytext); - } -<Start>{ID} { - g_insideBody=TRUE; - generateLink(*g_code, yytext); - g_insideBody=FALSE; - } - /*------ strings --------------------------------------------------*/ -<String>\n { // string with \n inside - g_contLineNr++; - str+=yytext; - startFontClass("stringliteral"); - codifyLines(str); - endFontClass(); - str = ""; - YY_FTN_RESET - } -<String>\"|\' { // string ends with next quote without previous backspace - if(yytext[0]!=stringStartSymbol) YY_FTN_REJECT; // single vs double quote - str+=yytext; - startFontClass("stringliteral"); - codifyLines(str); - endFontClass(); - yy_pop_state(); - } -<String>. {str+=yytext;} - -<*>\"|\' { /* string starts */ - /* if(YY_START == StrIgnore) YY_FTN_REJECT; // ignore in simple comments */ - if (g_isFixedForm && yy_my_start == 6) YY_FTN_REJECT; - yy_push_state(YY_START); - stringStartSymbol=yytext[0]; // single or double quote - BEGIN(String); - str=yytext; - } - /*-----------------------------------------------------------------------------*/ - -<*>\n { - if (g_endComment) - { - g_endComment=FALSE; - } - else - { - codifyLines(yytext); - // comment cannot extend over the end of a line so should always be terminated at the end of the line. - if (g_currentFontClass && !strcmp(g_currentFontClass,"comment")) endFontClass(); - } - g_contLineNr++; - YY_FTN_RESET - } -<*>^{BS}"type"{BS}"=" { g_code->codify(yytext); } - -<*>. { - if (g_isFixedForm && yy_my_start > fixedCommentAfter) - { - //yy_push_state(YY_START); - //BEGIN(DocBlock); - //docBlock=yytext; - startFontClass("comment"); - codifyLines(yytext); - } - else - { - g_code->codify(yytext); - } - } -<*>{LOG_OPER} { // Fortran logical comparison keywords - g_code->codify(yytext); - } -<*><<EOF>> { - if (YY_START == DocBlock) { - if (!Config_getBool(STRIP_CODE_COMMENTS)) - { - startFontClass("comment"); - codifyLines(docBlock); - endFontClass(); - } - } - yyterminate(); - } -%% - -/*@ ---------------------------------------------------------------------------- - */ - /*===================================================================*/ -void resetFortranCodeParserState() {} - -bool recognizeFixedForm(const char* contents, FortranFormat format); /* prototype, implementation in fortranscanner.l */ -const char* prepassFixedForm(const char* contents, int *hasContLine); /* prototype, implementation in fortranscanner.l */ -static void checkContLines(const char *s) +static void checkContLines(yyscan_t yyscanner,const char *s) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; int numLines = 0; - int curLine = 0; int i = 0; const char *p = s; @@ -1312,117 +1383,157 @@ static void checkContLines(const char *s) p++; } - g_hasContLine = (int *) malloc((numLines) * sizeof(int)); + yyextra->hasContLine = (int *) malloc((numLines) * sizeof(int)); for (i = 0; i < numLines; i++) - g_hasContLine[i] = 0; - p = prepassFixedForm(s, g_hasContLine); - g_hasContLine[0] = 0; + yyextra->hasContLine[i] = 0; + p = prepassFixedForm(s, yyextra->hasContLine); + yyextra->hasContLine[0] = 0; } -void parseFortranCode(CodeOutputInterface &od,const char *,const QCString &s, +void parseFortranCode(CodeOutputInterface &od,const char *,const QCString &s, bool exBlock, const char *exName,FileDef *fd, - int startLine,int endLine,bool inlineFragment, - const MemberDef *,bool,const Definition *searchCtx, + int startLine,int endLine,bool inlineFragment, + const MemberDef *,bool,const Definition *searchCtx, bool collectXRefs, FortranFormat format) { //printf("***parseCode() exBlock=%d exName=%s fd=%p\n",exBlock,exName,fd); - if (s.isEmpty()) return; - printlex(yy_flex_debug, TRUE, __FILE__, fd ? fd->fileName().data(): NULL); - g_code = &od; - g_inputString = s; - g_inputPosition = 0; - g_isFixedForm = recognizeFixedForm((const char*)s,format); - g_contLineNr = 1; - g_hasContLine = NULL; - if (g_isFixedForm) + return; +} + +//--------------------------------------------------------- + +struct FortranCodeParser::Private +{ + yyscan_t yyscanner; + fortrancodeYY_state state; + FortranFormat format; +}; + +FortranCodeParser::FortranCodeParser(FortranFormat format) : p(std::make_unique<Private>()) +{ + p->format = format; + fortrancodeYYlex_init_extra(&p->state,&p->yyscanner); +#ifdef FLEX_DEBUG + fortrancodeYYset_debug(1,p->yyscanner); +#endif + resetCodeParserState(); + p->state.useMembers = new UseSDict; +} + +FortranCodeParser::~FortranCodeParser() +{ + delete p->state.useMembers; + fortrancodeYYlex_destroy(p->yyscanner); +} + +void FortranCodeParser::resetCodeParserState() +{ + struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner; + yyextra->currentDefinition = 0; + yyextra->currentMemberDef = 0; + yyextra->currentFontClass = 0; + yyextra->needsTermination = FALSE; + BEGIN( Start ); +} + +void FortranCodeParser::parseCode(CodeOutputInterface & codeOutIntf, + const char * scopeName, + const QCString & input, + SrcLangExt /*lang*/, + bool isExampleBlock, + const char * exampleName, + FileDef * fileDef, + int startLine, + int endLine, + bool inlineFragment, + const MemberDef *memberDef, + bool showLineNumbers, + const Definition *searchCtx, + bool collectXRefs + ) +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner; + //::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName, + // fileDef,startLine,endLine,inlineFragment,memberDef, + // showLineNumbers,searchCtx,collectXRefs,m_format); + // parseFortranCode(CodeOutputInterface &od,const char *,const QCString &s, + // bool exBlock, const char *exName,FileDef *fd, + // int startLine,int endLine,bool inlineFragment, + // const MemberDef *,bool,const Definition *searchCtx, + // bool collectXRefs, FortranFormat format) + if (input.isEmpty()) return; + printlex(yy_flex_debug, TRUE, __FILE__, fileDef ? fileDef->fileName().data(): NULL); + yyextra->code = &codeOutIntf; + yyextra->inputString = input; + yyextra->inputPosition = 0; + yyextra->isFixedForm = recognizeFixedForm(input,p->format); + yyextra->contLineNr = 1; + yyextra->hasContLine = NULL; + if (yyextra->isFixedForm) { - checkContLines(g_inputString); + checkContLines(yyscanner,yyextra->inputString); } - g_currentFontClass = 0; - g_needsTermination = FALSE; - g_searchCtx = searchCtx; - g_collectXRefs = collectXRefs; + yyextra->currentFontClass = 0; + yyextra->needsTermination = FALSE; + yyextra->searchCtx = searchCtx; + yyextra->collectXRefs = collectXRefs; if (startLine!=-1) - g_yyLineNr = startLine; + yyextra->yyLineNr = startLine; else - g_yyLineNr = 1; + yyextra->yyLineNr = 1; if (endLine!=-1) - g_inputLines = endLine+1; + yyextra->inputLines = endLine+1; else - g_inputLines = g_yyLineNr + countLines() - 1; + yyextra->inputLines = yyextra->yyLineNr + countLines(yyscanner) - 1; - g_exampleBlock = exBlock; - g_exampleName = exName; - g_sourceFileDef = fd; - if (exBlock && fd==0) + yyextra->exampleBlock = isExampleBlock; + yyextra->exampleName = exampleName; + yyextra->sourceFileDef = fileDef; + if (isExampleBlock && fileDef==0) { // create a dummy filedef for the example - g_sourceFileDef = createFileDef("",exName); + yyextra->sourceFileDef = createFileDef("",exampleName); } - if (g_sourceFileDef) + if (yyextra->sourceFileDef) { - setCurrentDoc("l00001"); + setCurrentDoc(yyscanner,"l00001"); } - g_currentDefinition = 0; - g_currentMemberDef = 0; - if (!g_exampleName.isEmpty()) + yyextra->currentDefinition = 0; + yyextra->currentMemberDef = 0; + if (!yyextra->exampleName.isEmpty()) { - g_exampleFile = convertNameToFile(g_exampleName+"-example"); + yyextra->exampleFile = convertNameToFile(yyextra->exampleName+"-example"); } - g_includeCodeFragment = inlineFragment; - startCodeLine(); - g_parmName.resize(0); - g_parmType.resize(0); - fortrancodeYYrestart( fortrancodeYYin ); + yyextra->includeCodeFragment = inlineFragment; + startCodeLine(yyscanner); + fortrancodeYYrestart(0, yyscanner); BEGIN( Start ); - fortrancodeYYlex(); - if (g_needsTermination) + fortrancodeYYlex(yyscanner); + if (yyextra->needsTermination) { - endFontClass(); - g_code->endCodeLine(); + endFontClass(yyscanner); + yyextra->code->endCodeLine(); } - if (exBlock && g_sourceFileDef) + if (isExampleBlock && yyextra->sourceFileDef) { // delete the temporary file definition used for this example - delete g_sourceFileDef; - g_sourceFileDef=0; + delete yyextra->sourceFileDef; + yyextra->sourceFileDef=0; } - if (g_hasContLine) free(g_hasContLine); - g_hasContLine = NULL; - printlex(yy_flex_debug, FALSE, __FILE__, fd ? fd->fileName().data(): NULL); - return; -} - -//--------------------------------------------------------- + if (yyextra->hasContLine) free(yyextra->hasContLine); + yyextra->hasContLine = NULL; -void FortranCodeParser::parseCode(CodeOutputInterface & codeOutIntf, - const char * scopeName, - const QCString & input, - SrcLangExt /*lang*/, - bool isExampleBlock, - const char * exampleName, - FileDef * fileDef, - int startLine, - int endLine, - bool inlineFragment, - const MemberDef *memberDef, - bool showLineNumbers, - const Definition *searchCtx, - bool collectXRefs - ) -{ - ::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName, - fileDef,startLine,endLine,inlineFragment,memberDef, - showLineNumbers,searchCtx,collectXRefs,m_format); -} + // write the tooltips + TooltipManager::instance().writeTooltips(codeOutIntf); -void FortranCodeParser::resetCodeParserState() -{ - ::resetFortranCodeParserState(); + printlex(yy_flex_debug, FALSE, __FILE__, fileDef ? fileDef->fileName().data(): NULL); } //--------------------------------------------------------- +#if USE_STATE2STRING #include "fortrancode.l.h" +#endif |