1 /******************************************************************************
3 * Parser for syntax highlighting and references for Fortran90 F subset
5 * Copyright (C) by Anke Visser
6 * based on the work of Dimitri van Heesch.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation under the terms of the GNU General Public License is hereby
10 * granted. No representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or implied warranty.
12 * See the GNU General Public License for more details.
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
20 @todo - continutation lines not always recognized
21 - merging of use-statements with same module name and different only-names
22 - rename part of use-statement
23 - links to interface functions
24 - references to variables
26 %option never-interactive
27 %option case-insensitive
28 %option prefix="fortrancodeYY"
40 #include <qcstringlist.h>
44 #include "outputlist.h"
46 #include "membername.h"
47 #include "searchindex.h"
49 #include "memberlist.h"
52 #include "classlist.h"
54 #include "namespacedef.h"
56 #include "fortrancode.h"
58 // Toggle for some debugging info
59 //#define DBG_CTX(x) fprintf x
60 #define DBG_CTX(x) do { } while(0)
62 #define YY_NO_TOP_STATE 1
64 #define YY_NO_UNISTD_H 1
67 * For fixed formatted code position 6 is of importance (continuation character).
68 * The following variables and macros keep track of the column number
69 * YY_USER_ACTION is always called for each scan action
70 * YY_FTN_RESET is used to handle end of lines and reset the column counter
71 * YY_FTN_REJECT resets the column counters when a pattern is rejected and thus rescanned.
76 #define YY_USER_ACTION {yy_old_start = yy_my_start; yy_my_start = yy_end; yy_end += yyleng;}
77 #define YY_FTN_RESET {yy_old_start = 0; yy_my_start = 0; yy_end = 1;}
78 #define YY_FTN_REJECT {yy_end = yy_my_start; yy_my_start = yy_old_start; REJECT;}
80 //--------------------------------------------------------------------------------
83 data of an use-statement
88 QCString module; // just for debug
89 QCStringList onlyNames; /* entries of the ONLY-part */
93 module name -> list of ONLY/remote entries
94 (module name = name of the module, which can be accessed via use-directive)
96 class UseSDict : public SDict<UseEntry>
99 UseSDict() : SDict<UseEntry>(17) {}
103 Contains names of used modules and names of local variables.
108 QCStringList useNames; //!< contains names of used modules
109 QDict<void> localVars; //!< contains names of local variables
110 QDict<void> externalVars; //!< contains names of external entities
112 Scope() : localVars(7, FALSE /*caseSensitive*/), externalVars(7, FALSE /*caseSensitive*/) {}
115 /*===================================================================*/
120 static QCString docBlock; //!< contents of all lines of a documentation block
121 static QCString currentModule=0; //!< name of the current enclosing module
122 static QCString currentClass=0; //!< name of the current enclosing class
123 static UseSDict *useMembers= new UseSDict; //!< info about used modules
124 static UseEntry *useEntry = 0; //!< current use statement info
125 static QList<Scope> scopeStack;
126 static bool g_isExternal = false;
127 // static QCStringList *currentUseNames= new QCStringList; //! contains names of used modules of current program unit
128 static QCString str=""; //!> contents of fortran string
130 static CodeOutputInterface * g_code;
132 // TODO: is this still needed? if so, make it work
133 static QCString g_parmType;
134 static QCString g_parmName;
136 static const char * g_inputString; //!< the code fragment as text
137 static int g_inputPosition; //!< read offset during parsing
138 static int g_inputLines; //!< number of line in the code fragment
139 static int g_yyLineNr; //!< current line number
140 static int g_contLineNr; //!< current, local, line number for continuation determination
141 static int *g_hasContLine = NULL; //!< signals whether or not a line has a continuation line (fixed source form)
142 static bool g_needsTermination;
143 static Definition *g_searchCtx;
144 static bool g_collectXRefs;
145 static bool g_isFixedForm;
147 static bool g_insideBody; //!< inside subprog/program body? => create links
148 static const char * g_currentFontClass;
150 static bool g_exampleBlock;
151 static QCString g_exampleName;
152 static QCString g_exampleFile;
154 static FileDef * g_sourceFileDef;
155 static Definition * g_currentDefinition;
156 static MemberDef * g_currentMemberDef;
157 static bool g_includeCodeFragment;
159 static char stringStartSymbol; // single or double quote
160 // count in variable declaration to filter out
161 // declared from referenced names
162 static int bracketCount = 0;
164 // signal when in type / class /procedure declaration
165 static int inTypeDecl = 0;
167 static bool g_endComment;
169 static void endFontClass()
171 if (g_currentFontClass)
173 g_code->endFontClass();
174 g_currentFontClass=0;
178 static void startFontClass(const char *s)
180 // if font class is already set don't stop and start it.
181 // strcmp does not like null pointers as input.
182 if (!g_currentFontClass || !s || strcmp(g_currentFontClass,s))
185 g_code->startFontClass(s);
186 g_currentFontClass=s;
190 static void setCurrentDoc(const QCString &anchor)
192 if (Doxygen::searchIndex)
196 Doxygen::searchIndex->setCurrentDoc(g_searchCtx,g_searchCtx->anchor(),FALSE);
200 Doxygen::searchIndex->setCurrentDoc(g_sourceFileDef,anchor,TRUE);
205 static void addToSearchIndex(const char *text)
207 if (Doxygen::searchIndex)
209 Doxygen::searchIndex->addWord(text,FALSE);
213 /*! start a new line of code, inserting a line number if g_sourceFileDef
214 * is TRUE. If a definition starts at the current line, then the line
215 * number is linked to the documentation of that definition.
217 static void startCodeLine()
221 //QCString lineNumber,lineAnchor;
222 //lineNumber.sprintf("%05d",g_yyLineNr);
223 //lineAnchor.sprintf("l%05d",g_yyLineNr);
225 Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr);
226 //printf("startCodeLine %d d=%s\n", g_yyLineNr,d ? d->name().data() : "<null>");
227 if (!g_includeCodeFragment && d)
229 g_currentDefinition = d;
230 g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr);
231 g_insideBody = FALSE;
232 g_endComment = FALSE;
233 g_parmType.resize(0);
234 g_parmName.resize(0);
236 lineAnchor.sprintf("l%05d",g_yyLineNr);
237 if (g_currentMemberDef)
239 g_code->writeLineNumber(g_currentMemberDef->getReference(),
240 g_currentMemberDef->getOutputFileBase(),
241 g_currentMemberDef->anchor(),g_yyLineNr);
242 setCurrentDoc(lineAnchor);
244 else if (d->isLinkableInProject())
246 g_code->writeLineNumber(d->getReference(),
247 d->getOutputFileBase(),
249 setCurrentDoc(lineAnchor);
254 g_code->writeLineNumber(0,0,0,g_yyLineNr);
257 g_code->startCodeLine(g_sourceFileDef);
258 if (g_currentFontClass)
260 g_code->startFontClass(g_currentFontClass);
265 static void endFontClass();
266 static void endCodeLine()
269 g_code->endCodeLine();
272 /*! write a code fragment `text' that may span multiple lines, inserting
273 * line numbers for each line.
275 static void codifyLines(char *text)
277 //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text);
281 const char * tmp_currentFontClass = g_currentFontClass;
285 while ((c=*p++) && c!='\n') { }
292 if (g_yyLineNr<g_inputLines)
296 if (tmp_currentFontClass)
298 startFontClass(tmp_currentFontClass);
309 static void codifyLines(QCString str)
311 char *tmp= (char *) malloc(str.length()+1);
317 /*! writes a link to a fragment \a text that may span multiple lines, inserting
318 * line numbers for each line. If \a text contains newlines, the link will be
319 * split into multiple links with the same destination, one for each line.
321 static void writeMultiLineCodeLink(CodeOutputInterface &ol,
322 Definition *d,const char *text)
324 static bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS);
325 TooltipManager::instance()->addTooltip(d);
326 QCString ref = d->getReference();
327 QCString file = d->getOutputFileBase();
328 QCString anchor = d->anchor();
330 if (!sourceTooltips) // fall back to simple "title" tooltips
332 tooltip = d->briefDescriptionAsTooltip();
335 char *p=(char *)text;
340 while ((c=*p++) && c!='\n') { }
345 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
346 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
348 if (g_yyLineNr<g_inputLines)
355 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
356 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
361 //-------------------------------------------------------------------------------
363 searches for definition of a module (Namespace)
364 @param mname the name of the module
365 @param cd the entry, if found or null
366 @returns true, if module is found
368 static bool getFortranNamespaceDefs(const QCString &mname,
371 if (mname.isEmpty()) return FALSE; /* empty name => nothing to link */
374 if ((cd=Doxygen::namespaceSDict->find(mname))) return TRUE;
378 //-------------------------------------------------------------------------------
380 searches for definition of a type
381 @param tname the name of the type
382 @param moduleName name of enclosing module or null, if global entry
383 @param cd the entry, if found or null
384 @param useDict dictionary of data of USE-statement
385 @returns true, if type is found
387 static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName,
388 ClassDef *&cd, UseSDict *usedict=0)
390 if (tname.isEmpty()) return FALSE; /* empty name => nothing to link */
392 //cout << "=== search for type: " << tname << endl;
395 if ((cd=Doxygen::classSDict->find(tname)))
397 //cout << "=== type found in global module" << endl;
400 else if (moduleName && (cd= Doxygen::classSDict->find(moduleName+"::"+tname)))
402 //cout << "=== type found in local module" << endl;
408 for (UseSDict::Iterator di(*usedict); (use=di.current()); ++di)
410 if ((cd= Doxygen::classSDict->find(use->module+"::"+tname)))
412 //cout << "=== type found in used module" << endl;
422 searches for definition of function memberName
423 @param memberName the name of the function/variable
424 @param moduleName name of enclosing module or null, if global entry
425 @param md the entry, if found or null
426 @param usedict array of data of USE-statement
427 @returns true, if found
429 static bool getFortranDefs(const QCString &memberName, const QCString &moduleName,
430 MemberDef *&md, UseSDict *usedict=0)
432 if (memberName.isEmpty()) return FALSE; /* empty name => nothing to link */
434 // look in local variables
435 QListIterator<Scope> it(scopeStack);
437 for (it.toLast();(scope=it.current());--it)
439 if (scope->localVars.find(memberName) && (!scope->externalVars.find(memberName)))
443 // search for function
444 MemberName *mn = Doxygen::functionNameSDict->find(memberName);
447 mn = Doxygen::memberNameSDict->find(memberName);
450 if (mn) // name is known
452 MemberNameIterator mli(*mn);
453 for (mli.toFirst();(md=mli.current());++mli) // all found functions with given name
455 FileDef *fd=md->getFileDef();
456 GroupDef *gd=md->getGroupDef();
457 ClassDef *cd=md->getClassDef();
459 //cout << "found link with same name: " << fd->fileName() << " " << memberName;
460 //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl;
462 if ((gd && gd->isLinkable()) || (fd && fd->isLinkable()))
464 NamespaceDef *nspace= md->getNamespaceDef();
467 { // found function in global scope
468 if(cd == 0) { // Skip if bound to type
472 else if (moduleName == nspace->name())
473 { // found in local scope
477 { // else search in used modules
478 QCString moduleName= nspace->name();
479 UseEntry *ue= usedict->find(moduleName);
482 // check if only-list exists and if current entry exists is this list
483 QCStringList &only= ue->onlyNames;
486 //cout << " found in module " << moduleName << " entry " << memberName << endl;
487 return TRUE; // whole module used
491 for ( QCStringList::Iterator it = only.begin(); it != only.end(); ++it)
493 //cout << " search in only: " << moduleName << ":: " << memberName << "==" << (*it)<< endl;
494 if (memberName == *it)
496 return TRUE; // found in ONLY-part of use list
509 gets the link to a generic procedure which depends not on the name, but on the parameter list
512 static bool getGenericProcedureLink(const ClassDef *cd,
513 const char *memberText,
514 CodeOutputInterface &ol)
522 static bool getLink(UseSDict *usedict, // dictonary with used modules
523 const char *memberText, // exact member text
524 CodeOutputInterface &ol,
528 QCString memberName= removeRedundantWhiteSpace(memberText);
530 if (getFortranDefs(memberName, currentModule, md, usedict) && md->isLinkable())
532 if (md->isVariable() && (md->getLanguage()!=SrcLangExt_Fortran)) return FALSE; // Non Fortran variables aren't handled yet,
533 // see also linkifyText in util.cpp
535 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
536 md->getBodyDef() : md->getOuterScope();
537 if (md->getGroupDef()) d = md->getGroupDef();
538 if (d && d->isLinkable())
540 if (g_currentDefinition && g_currentMemberDef &&
541 md!=g_currentMemberDef && g_insideBody && g_collectXRefs)
543 addDocCrossReference(g_currentMemberDef,md);
545 writeMultiLineCodeLink(ol,md,text ? text : memberText);
546 addToSearchIndex(text ? text : memberText);
554 static void generateLink(CodeOutputInterface &ol, char *lname)
558 QCString tmp = lname;
559 tmp = removeRedundantWhiteSpace(tmp.lower());
561 // check if lowercase lname is a linkable type or interface
562 if ( (getFortranTypeDefs(tmp, currentModule, cd, useMembers)) && cd->isLinkable() )
564 if ( (cd->compoundType() == ClassDef::Class) && // was Entry::INTERFACE_SEC) &&
565 (getGenericProcedureLink(cd, tmp, ol)) )
567 //cout << "=== generic procedure resolved" << endl;
570 { // write type or interface link
571 writeMultiLineCodeLink(ol,cd,tmp);
572 addToSearchIndex(tmp.data());
576 else if ( (getFortranNamespaceDefs(tmp, nsd)) && nsd->isLinkable() )
577 { // write module link
578 writeMultiLineCodeLink(ol,nsd,tmp);
579 addToSearchIndex(tmp.data());
581 // check for function/variable
582 else if (getLink(useMembers, tmp, ol, tmp))
584 //cout << "=== found link for lowercase " << lname << endl;
588 // nothing found, just write out the word
589 //startFontClass("charliteral"); //test
591 //endFontClass(); //test
592 addToSearchIndex(tmp.data());
596 /*! counts the number of lines in the input */
597 static int countLines()
599 const char *p=g_inputString;
605 if (c=='\n') count++;
607 if (p>g_inputString && *(p-1)!='\n')
608 { // last line does not end with a \n, so we add an extra
609 // line and explicitly terminate the line after parsing.
611 g_needsTermination=TRUE;
616 //----------------------------------------------------------------------------
618 static void startScope()
620 DBG_CTX((stderr, "===> startScope %s",yytext));
621 Scope *scope = new Scope;
622 scopeStack.append(scope);
626 static void endScope()
628 DBG_CTX((stderr,"===> endScope %s",yytext));
629 if (scopeStack.isEmpty())
631 DBG_CTX((stderr,"WARNING: fortrancode.l: stack empty!\n"));
635 Scope *scope = scopeStack.getLast();
636 scopeStack.removeLast();
637 for ( QCStringList::Iterator it = scope->useNames.begin(); it != scope->useNames.end(); ++it)
639 useMembers->remove(*it);
644 static void addUse(const QCString &moduleName)
646 if (!scopeStack.isEmpty())
647 scopeStack.getLast()->useNames.append(moduleName);
650 static void addLocalVar(const QCString &varName)
652 if (!scopeStack.isEmpty())
654 scopeStack.getLast()->localVars.insert(varName, (void*)1);
655 if (g_isExternal) scopeStack.getLast()->externalVars.insert(varName, (void*)1);
659 //----------------------------------------------------------------------------
661 /* -----------------------------------------------------------------*/
663 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
665 static int yyread(char *buf,int max_size)
668 while( c < max_size && g_inputString[g_inputPosition] )
670 *buf = g_inputString[g_inputPosition++] ;
679 ID [a-z_A-Z]+{IDSYM}*
680 SUBPROG (subroutine|function)
685 ARGS_L0 ("("[^)]*")")
686 ARGS_L1a [^()]*"("[^)]*")"[^)]*
687 ARGS_L1 ("("{ARGS_L1a}*")")
688 ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")"
689 ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2})
691 NUM_TYPE (complex|integer|logical|real)
692 LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.)
694 CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS}))
695 TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|{CHAR}|TYPE|CLASS|PROCEDURE|ENUMERATOR)
697 INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")"
698 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)
699 ACCESS_SPEC (PROTECTED|PRIVATE|PUBLIC)
700 /* Assume that attribute statements are almost the same as attributes. */
701 ATTR_STMT {ATTR_SPEC}|DIMENSION
702 FLOW (DO|SELECT|CASE|SELECT{BS}(CASE|TYPE)|WHERE|IF|THEN|ELSE|WHILE|FORALL|ELSEWHERE|ELSEIF|RETURN|CONTINUE|EXIT|GO{BS}TO)
703 COMMANDS (FORMAT|CONTAINS|MODULE{BS_}PROCEDURE|WRITE|READ|ALLOCATE|ALLOCATED|ASSOCIATED|PRESENT|DEALLOCATE|NULLIFY|SIZE|INQUIRE|OPEN|CLOSE|FLUSH|DATA|COMMON)
705 PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?0
706 LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?")"
726 %x DeclarationBinding
733 /*==================================================================*/
735 /*-------- ignore ------------------------------------------------------------*/
737 <Start>{IGNORE}/{BS}"(" { // do not search keywords, intrinsics... TODO: complete list
740 /*-------- inner construct ---------------------------------------------------*/
742 <Start>{COMMANDS}/{BS}[,( \t\n] { // highlight
743 /* font class is defined e.g. in doxygen.css */
744 startFontClass("keyword");
748 <Start>{FLOW}/{BS}[,( \t\n] {
751 if ((yy_my_start == 1) && ((yytext[0] == 'c') || (yytext[0] == 'C'))) YY_FTN_REJECT;
753 /* font class is defined e.g. in doxygen.css */
754 startFontClass("keywordflow");
758 <Start>{BS}(CASE|CLASS|TYPE){BS_}(IS|DEFAULT) {
759 startFontClass("keywordflow");
763 <Start>{BS}"end"({BS}{FLOW})/[ \t\n] { // list is a bit long as not all have possible end
764 startFontClass("keywordflow");
768 <Start>"implicit"{BS}("none"|{TYPE_SPEC}) {
769 startFontClass("keywordtype");
773 <Start>^{BS}"namelist"/[//] { // Namelist specification
774 startFontClass("keywordtype");
778 /*-------- use statement -------------------------------------------*/
780 startFontClass("keywordtype");
783 yy_push_state(YY_START);
786 <Use>"ONLY" { // TODO: rename
787 startFontClass("keywordtype");
790 yy_push_state(YY_START);
794 QCString tmp = yytext;
797 generateLink(*g_code, yytext);
800 /* append module name to use dict */
801 useEntry = new UseEntry();
802 //useEntry->module = yytext;
803 //useMembers->append(yytext, useEntry);
805 useEntry->module = tmp;
806 useMembers->append(tmp, useEntry);
809 <Use,UseOnly,Import>{BS},{BS} { codifyLines(yytext); }
810 <UseOnly,Import>{BS}&{BS}"\n" { codifyLines(yytext);
814 QCString tmp = yytext;
816 useEntry->onlyNames.append(tmp);
818 generateLink(*g_code, yytext);
821 <Use,UseOnly,Import>"\n" {
823 yy_pop_state();YY_FTN_RESET
825 <*>"import"{BS}/"\n" |
827 startFontClass("keywordtype");
830 yy_push_state(YY_START);
835 generateLink(*g_code, yytext);
838 <Import>("ONLY"|"NONE"|"ALL") {
839 startFontClass("keywordtype");
843 /*-------- fortran module -----------------------------------------*/
844 <Start>("block"{BS}"data"|"program"|"module"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { //
846 startFontClass("keyword");
849 yy_push_state(YY_START);
851 if (!qstricmp(yytext,"module")) currentModule="module";
853 <Start>("enum")/{BS_}|{BS}{COMMA}{BS}{LANGUAGE_BIND_SPEC}|\n { //
855 startFontClass("keyword");
858 yy_push_state(YY_START);
860 currentClass="class";
862 <*>{LANGUAGE_BIND_SPEC} { //
863 startFontClass("keyword");
867 <Start>("type")/{BS_}|({COMMA}({ACCESS_SPEC}|ABSTRACT|EXTENDS))|\n { //
869 startFontClass("keyword");
872 yy_push_state(YY_START);
874 currentClass="class";
877 if (currentModule == "module")
879 currentModule=yytext;
880 currentModule = currentModule.lower();
882 generateLink(*g_code,yytext);
885 <ClassName>({ACCESS_SPEC}|ABSTRACT|EXTENDS)/[,:( ] { //| variable declaration
886 startFontClass("keyword");
887 g_code->codify(yytext);
890 <ClassName>\n { // interface may be without name
894 <Start>^{BS}"end"({BS_}"enum").* { // just reset currentClass, rest is done in following rule
898 <Start>^{BS}"end"({BS_}"type").* { // just reset currentClass, rest is done in following rule
902 <Start>^{BS}"end"({BS_}"module").* { // just reset currentModule, rest is done in following rule
906 /*-------- subprog definition -------------------------------------*/
907 <Start>({PREFIX}{BS_})?{TYPE_SPEC}{BS_}({PREFIX}{BS_})?{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result
908 startFontClass("keyword");
912 <Start>({PREFIX}{BS_})?{SUBPROG}{BS_} { // Fortran subroutine or function found
913 startFontClass("keyword");
916 yy_push_state(YY_START);
919 <Subprog>{ID} { // subroutine/function name
920 DBG_CTX((stderr, "===> start subprogram %s\n", yytext));
922 generateLink(*g_code,yytext);
924 <Subprog>"result"/{BS}"("[^)]*")" {
925 startFontClass("keyword");
929 <Subprog>"("[^)]*")" { // ignore rest of line
932 <Subprog,Subprogend>"\n" { codifyLines(yytext);
937 <Start>^{BS}"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface")?{BS} { // Fortran subroutine or function ends
938 //cout << "===> end function " << yytext << endl;
940 startFontClass("keyword");
943 yy_push_state(YY_START);
946 <Subprogend>{ID}/{BS}(\n|!) {
947 generateLink(*g_code,yytext);
950 <Start>^{BS}"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface"){BS}/(\n|!) { // Fortran subroutine or function ends
951 //cout << "===> end function " << yytext << endl;
953 startFontClass("keyword");
957 /*-------- variable declaration ----------------------------------*/
958 <Start>^{BS}"real"/[,:( ] { // real is a bit tricky as it is a data type but also a function.
959 yy_push_state(YY_START);
961 startFontClass("keywordtype");
962 g_code->codify(yytext);
965 <Start>{TYPE_SPEC}/[,:( ] {
966 QCString typ = yytext;
967 typ = removeRedundantWhiteSpace(typ.lower());
968 if (QString(typ).startsWith("real")) YY_FTN_REJECT;
969 if (typ == "type" || typ == "class" || typ == "procedure") inTypeDecl = 1;
970 yy_push_state(YY_START);
972 startFontClass("keywordtype");
973 g_code->codify(yytext);
977 if (QCString(yytext) == "external")
979 yy_push_state(YY_START);
983 startFontClass("keywordtype");
984 g_code->codify(yytext);
987 <Declaration>({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable declaration
988 if (QCString(yytext) == "external") g_isExternal = true;
989 startFontClass("keywordtype");
990 g_code->codify(yytext);
993 <Declaration>{ID} { // local var
994 if (g_isFixedForm && yy_my_start == 1)
996 startFontClass("comment");
997 g_code->codify(yytext);
1000 else if (g_currentMemberDef &&
1001 ((g_currentMemberDef->isFunction() && (g_currentMemberDef->typeString()!=QCString("subroutine") || inTypeDecl)) ||
1002 g_currentMemberDef->isVariable() || g_currentMemberDef->isEnumValue()
1006 generateLink(*g_code, yytext);
1010 g_code->codify(yytext);
1011 addLocalVar(yytext);
1014 <Declaration>{BS}("=>"|"="){BS} { // Procedure binding
1015 BEGIN(DeclarationBinding);
1016 g_code->codify(yytext);
1018 <DeclarationBinding>{ID} { // Type bound procedure link
1019 generateLink(*g_code, yytext);
1022 <Declaration>[(] { // start of array or type / class specification
1024 g_code->codify(yytext);
1027 <Declaration>[)] { // end array specification
1029 if (!bracketCount) inTypeDecl = 0;
1030 g_code->codify(yytext);
1033 <Declaration,DeclarationBinding>"&" { // continuation line
1034 g_code->codify(yytext);
1037 yy_push_state(YY_START);
1038 BEGIN(DeclContLine);
1041 <DeclContLine>"\n" { // declaration not yet finished
1043 codifyLines(yytext);
1048 <Declaration,DeclarationBinding>"\n" { // end declaration line (?)
1055 codifyLines(yytext);
1059 if (!(g_hasContLine && g_hasContLine[g_contLineNr - 1]))
1061 g_isExternal = false;
1067 /*-------- subprog calls -----------------------------------------*/
1069 <Start>"call"{BS_} {
1070 startFontClass("keyword");
1071 codifyLines(yytext);
1073 yy_push_state(YY_START);
1076 <SubCall>{ID} { // subroutine call
1078 generateLink(*g_code, yytext);
1082 <Start>{ID}{BS}/"(" { // function call
1083 if (g_isFixedForm && yy_my_start == 6)
1085 // fixed form continuation line
1088 else if (QCString(yytext).stripWhiteSpace().lower() == "type")
1090 yy_push_state(YY_START);
1092 startFontClass("keywordtype");
1093 g_code->codify(QCString(yytext).stripWhiteSpace());
1095 g_code->codify(yytext + 4);
1100 generateLink(*g_code, yytext);
1105 /*-------- comments ---------------------------------------------------*/
1106 <Start,Declaration,DeclarationBinding>\n?{BS}"!>"|"!<" { // start comment line or comment block
1107 if (yytext[0] == '\n')
1114 // Actually we should see if ! on position 6, can be continuation
1115 // but the chance is very unlikely, so no effort to solve it here
1116 yy_push_state(YY_START);
1120 <Declaration,DeclarationBinding>{BS}"!<" { // start comment line or comment block
1121 yy_push_state(YY_START);
1126 <DocBlock>.* { // contents of current comment line
1129 <DocBlock>"\n"{BS}("!>"|"!<"|"!!") { // comment block (next line is also comment line)
1134 // Actually we should see if ! on position 6, can be continuation
1135 // but the chance is very unlikely, so no effort to solve it here
1138 <DocBlock>"\n" { // comment block ends at the end of this line
1139 // remove special comment (default config)
1141 if (Config_getBool(STRIP_CODE_COMMENTS))
1143 g_yyLineNr+=((QCString)docBlock).contains('\n');
1146 if (g_yyLineNr<g_inputLines)
1152 else // do not remove comment
1154 startFontClass("comment");
1155 codifyLines(docBlock);
1164 <*>"!"[^><\n].*|"!"$ { // normal comment
1165 if(YY_START == String) YY_FTN_REJECT; // ignore in strings
1166 if (g_isFixedForm && yy_my_start == 6) YY_FTN_REJECT;
1167 startFontClass("comment");
1168 codifyLines(yytext);
1172 <*>^[Cc*].* { // normal comment
1173 if(! g_isFixedForm) YY_FTN_REJECT;
1175 startFontClass("comment");
1176 codifyLines(yytext);
1179 <*>"assignment"/{BS}"("{BS}"="{BS}")" {
1180 startFontClass("keyword");
1181 codifyLines(yytext);
1184 <*>"operator"/{BS}"("[^)]*")" {
1185 startFontClass("keyword");
1186 codifyLines(yytext);
1190 /*------ preprocessor --------------------------------------------*/
1192 if (g_isFixedForm && yy_my_start == 6) YY_FTN_REJECT;
1194 startFontClass("preprocessor");
1195 codifyLines(yytext);
1199 /*------ variable references? -------------------------------------*/
1201 <Start>"%"{BS}{ID} { // ignore references to elements
1202 g_code->codify(yytext);
1206 generateLink(*g_code, yytext);
1209 /*------ strings --------------------------------------------------*/
1210 <String>\n { // string with \n inside
1213 startFontClass("stringliteral");
1219 <String>\"|\' { // string ends with next quote without previous backspace
1220 if(yytext[0]!=stringStartSymbol) YY_FTN_REJECT; // single vs double quote
1222 startFontClass("stringliteral");
1227 <String>. {str+=yytext;}
1229 <*>\"|\' { /* string starts */
1230 /* if(YY_START == StrIgnore) YY_FTN_REJECT; // ignore in simple comments */
1231 if (g_isFixedForm && yy_my_start == 6) YY_FTN_REJECT;
1232 yy_push_state(YY_START);
1233 stringStartSymbol=yytext[0]; // single or double quote
1237 /*-----------------------------------------------------------------------------*/
1246 codifyLines(yytext);
1247 // comment cannot extend over the end of a line so should always be terminatd at the end of the line.
1248 if (g_currentFontClass && !strcmp(g_currentFontClass,"comment")) endFontClass();
1253 <*>^{BS}"type"{BS}"=" { g_code->codify(yytext); }
1256 if (g_isFixedForm && yy_my_start > fixedCommentAfter)
1258 //yy_push_state(YY_START);
1261 startFontClass("comment");
1262 codifyLines(yytext);
1266 g_code->codify(yytext);
1269 <*>{LOG_OPER} { // Fortran logical comparison keywords
1270 g_code->codify(yytext);
1273 if (YY_START == DocBlock) {
1274 if (!Config_getBool(STRIP_CODE_COMMENTS))
1276 startFontClass("comment");
1277 codifyLines(docBlock);
1285 /*@ ----------------------------------------------------------------------------
1288 /*===================================================================*/
1291 void resetFortranCodeParserState() {}
1293 bool recognizeFixedForm(const char* contents, FortranFormat format); /* prototype, implementation in fortranscanner.l */
1294 const char* prepassFixedForm(const char* contents, int *hasContLine); /* prototype, implementation in fortranscanner.l */
1295 static void checkContLines(const char *s)
1302 numLines = 2; // one for element 0, one in case no \n at end
1305 if (*p == '\n') numLines++;
1309 g_hasContLine = (int *) malloc((numLines) * sizeof(int));
1310 for (i = 0; i < numLines; i++)
1311 g_hasContLine[i] = 0;
1312 p = prepassFixedForm(s, g_hasContLine);
1313 g_hasContLine[0] = 0;
1316 void parseFortranCode(CodeOutputInterface &od,const char *className,const QCString &s,
1317 bool exBlock, const char *exName,FileDef *fd,
1318 int startLine,int endLine,bool inlineFragment,
1319 MemberDef *memberDef,bool,Definition *searchCtx,
1320 bool collectXRefs, FortranFormat format)
1322 //printf("***parseCode() exBlock=%d exName=%s fd=%p\n",exBlock,exName,fd);
1328 if (s.isEmpty()) return;
1329 printlex(yy_flex_debug, TRUE, __FILE__, fd ? fd->fileName().data(): NULL);
1330 TooltipManager::instance()->clearTooltips();
1333 g_inputPosition = 0;
1334 g_isFixedForm = recognizeFixedForm((const char*)s,format);
1336 g_hasContLine = NULL;
1339 checkContLines(g_inputString);
1341 g_currentFontClass = 0;
1342 g_needsTermination = FALSE;
1343 g_searchCtx = searchCtx;
1344 g_collectXRefs = collectXRefs;
1346 g_yyLineNr = startLine;
1351 g_inputLines = endLine+1;
1353 g_inputLines = g_yyLineNr + countLines() - 1;
1355 g_exampleBlock = exBlock;
1356 g_exampleName = exName;
1357 g_sourceFileDef = fd;
1358 if (exBlock && fd==0)
1360 // create a dummy filedef for the example
1361 g_sourceFileDef = new FileDef("",exName);
1363 if (g_sourceFileDef)
1365 setCurrentDoc("l00001");
1367 g_currentDefinition = 0;
1368 g_currentMemberDef = 0;
1369 if (!g_exampleName.isEmpty())
1371 g_exampleFile = convertNameToFile(g_exampleName+"-example");
1373 g_includeCodeFragment = inlineFragment;
1375 g_parmName.resize(0);
1376 g_parmType.resize(0);
1377 fortrancodeYYrestart( fortrancodeYYin );
1380 if (g_needsTermination)
1383 g_code->endCodeLine();
1387 TooltipManager::instance()->writeTooltips(*g_code);
1389 if (exBlock && g_sourceFileDef)
1391 // delete the temporary file definition used for this example
1392 delete g_sourceFileDef;
1395 if (g_hasContLine) free(g_hasContLine);
1396 g_hasContLine = NULL;
1397 printlex(yy_flex_debug, FALSE, __FILE__, fd ? fd->fileName().data(): NULL);
1401 #if !defined(YY_FLEX_SUBMINOR_VERSION)
1402 extern "C" { // some bogus code to keep the compiler happy
1403 void fortrancodeYYdummy() { yy_flex_realloc(0,0); }
1405 #elif YY_FLEX_MAJOR_VERSION<=2 && YY_FLEX_MINOR_VERSION<=5 && YY_FLEX_SUBMINOR_VERSION<33
1406 #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!"
1408 extern "C" { // some bogus code to keep the compiler happy
1409 void fortrancodeYYdummy() { yy_top_state(); }