Doxygen
fortrancode.l
浏览该文件的文档.
1 /******************************************************************************
2  *
3  * Parser for syntax highlighting and references for Fortran90 F subset
4  *
5  * Copyright (C) by Anke Visser
6  * based on the work of Dimitri van Heesch.
7  * Copyright (C) 2020 by Dimitri van Heesch.
8  *
9  * Permission to use, copy, modify, and distribute this software and its
10  * documentation under the terms of the GNU General Public License is hereby
11  * granted. No representations are made about the suitability of this software
12  * for any purpose. It is provided "as is" without express or implied warranty.
13  * See the GNU General Public License for more details.
14  *
15  * Documents produced by Doxygen are derivative works derived from the
16  * input used in their production; they are not affected by this license.
17  *
18  */
19 
20 /**
21  @todo - continuation lines not always recognized
22  - merging of use-statements with same module name and different only-names
23  - rename part of use-statement
24  - links to interface functions
25  - references to variables
26 **/
27 %option never-interactive
28 %option case-insensitive
29 %option reentrant
30 %option prefix="fortrancodeYY"
31 %option extra-type="struct fortrancodeYY_state *"
32 %option noyy_top_state
33 %top{
34 #include <stdint.h>
35 // forward declare yyscan_t to improve type safety
36 #define YY_TYPEDEF_YY_SCANNER_T
37 struct yyguts_t;
38 typedef yyguts_t *yyscan_t;
39 }
40 
41 %{
42 
43 /*
44  * includes
45  */
46 #include <stdio.h>
47 #include <assert.h>
48 #include <ctype.h>
49 
50 #include "doxygen.h"
51 #include "message.h"
52 #include "outputlist.h"
53 #include "util.h"
54 #include "membername.h"
55 #include "defargs.h"
56 #include "config.h"
57 #include "groupdef.h"
58 #include "classlist.h"
59 #include "filedef.h"
60 #include "namespacedef.h"
61 #include "tooltip.h"
62 #include "fortrancode.h"
63 #include "fortranscanner.h"
64 #include "containers.h"
65 
66 const int fixedCommentAfter = 72;
67 
68 // Toggle for some debugging info
69 //#define DBG_CTX(x) fprintf x
70 #define DBG_CTX(x) do { } while(0)
71 
72 #define YY_NO_TOP_STATE 1
73 #define YY_NO_INPUT 1
74 #define YY_NO_UNISTD_H 1
75 
76 #define USE_STATE2STRING 0
77 
78 /*
79  * For fixed formatted code position 6 is of importance (continuation character).
80  * The following variables and macros keep track of the column number
81  * YY_USER_ACTION is always called for each scan action
82  * YY_FTN_RESET is used to handle end of lines and reset the column counter
83  * YY_FTN_REJECT resets the column counters when a pattern is rejected and thus rescanned.
84  */
85 int yy_old_start = 0;
86 int yy_my_start = 0;
87 int yy_end = 1;
88 #define YY_USER_ACTION {yy_old_start = yy_my_start; yy_my_start = yy_end; yy_end += static_cast<int>(yyleng);}
89 #define YY_FTN_RESET {yy_old_start = 0; yy_my_start = 0; yy_end = 1;}
90 #define YY_FTN_REJECT {yy_end = yy_my_start; yy_my_start = yy_old_start; REJECT;}
91 
92 //--------------------------------------------------------------------------------
93 
94 /**
95  data of an use-statement
96 */
97 class UseEntry
98 {
99  public:
100  QCString module; // just for debug
101  std::vector<QCString> onlyNames; /* entries of the ONLY-part */
102 };
103 
104 /**
105  module name -> list of ONLY/remote entries
106  (module name = name of the module, which can be accessed via use-directive)
107 */
108 class UseMap : public std::map<std::string,UseEntry>
109 {
110 };
111 
112 /**
113  Contains names of used modules and names of local variables.
114 */
115 class Scope
116 {
117  public:
118  std::vector<QCString> useNames; //!< contains names of used modules
119  StringUnorderedSet localVars; //!< contains names of local variables
120  StringUnorderedSet externalVars; //!< contains names of external entities
121 };
122 
123 /*===================================================================*/
124 /*
125  * statics
126  */
127 
128 struct fortrancodeYY_state
129 {
130  QCString docBlock; //!< contents of all lines of a documentation block
131  QCString currentModule=QCString(); //!< name of the current enclosing module
132  UseMap useMembers; //!< info about used modules
133  UseEntry useEntry; //!< current use statement info
134  std::vector<Scope> scopeStack;
135  bool isExternal = false;
136  QCString str=QCString(); //!< contents of fortran string
137 
138  CodeOutputInterface * code = 0;
139 
140  const char * inputString = 0; //!< the code fragment as text
141  yy_size_t inputPosition = 0; //!< read offset during parsing
142  int inputLines = 0; //!< number of line in the code fragment
143  int yyLineNr = 0; //!< current line number
144  int contLineNr = 0; //!< current, local, line number for continuation determination
145  int *hasContLine = 0; //!< signals whether or not a line has a continuation line (fixed source form)
146  bool needsTermination = false;
147  const Definition *searchCtx = 0;
148  bool collectXRefs = false;
149  bool isFixedForm = false;
150 
151  bool insideBody = false; //!< inside subprog/program body? => create links
152  const char * currentFontClass = 0;
153 
154  bool exampleBlock = false;
155  QCString exampleName;
156  QCString exampleFile;
157 
158  const FileDef * sourceFileDef = 0;
159  const Definition * currentDefinition = 0;
160  const MemberDef * currentMemberDef = 0;
161  bool includeCodeFragment = false;
162 
163  char stringStartSymbol = '\0'; // single or double quote
164 // count in variable declaration to filter out
165 // declared from referenced names
166  int bracketCount = 0;
167 
168 // signal when in type / class /procedure declaration
169  int inTypeDecl = 0;
170 
171  bool endComment = false;
172  TooltipManager tooltipManager;
173 };
174 
175 #if USE_STATE2STRING
176 static const char *stateToString(int state);
177 #endif
178 
179 static bool getFortranNamespaceDefs(const QCString &mname,
180  NamespaceDef *&cd);
181 static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName,
182  ClassDef *&cd, const UseMap &useMap);
183 
184 //----------------------------------------------------------------------------
185 
186 static void endFontClass(yyscan_t yyscanner);
187 static void startFontClass(yyscan_t yyscanner,const char *s);
188 static void setCurrentDoc(yyscan_t yyscanner,const QCString &anchor);
189 static void addToSearchIndex(yyscan_t yyscanner,const QCString &text);
190 static void startCodeLine(yyscan_t yyscanner);
191 static void endCodeLine(yyscan_t yyscanner);
192 static void nextCodeLine(yyscan_t yyscanner);
193 static void codifyLines(yyscan_t yyscanner,const QCString &text);
194 static void writeMultiLineCodeLink(yyscan_t yyscanner,CodeOutputInterface &ol,
195  Definition *d,const QCString &text);
196 static bool getGenericProcedureLink(yyscan_t yyscanner,const ClassDef *cd,
197  const QCString &memberText,
198  CodeOutputInterface &ol);
199 static bool getLink(yyscan_t yyscanner,const UseMap &useMap, // map with used modules
200  const QCString &memberText, // exact member text
201  CodeOutputInterface &ol,
202  const QCString &text);
203 static void generateLink(yyscan_t yyscanner,CodeOutputInterface &ol, const QCString &lname);
204 static void generateLink(yyscan_t yyscanner,CodeOutputInterface &ol, const char *lname);
205 static int countLines(yyscan_t yyscanner);
206 static void startScope(yyscan_t yyscanner);
207 static void endScope(yyscan_t yyscanner);
208 static void addUse(yyscan_t yyscanner,const QCString &moduleName);
209 static void addLocalVar(yyscan_t yyscanner,const QCString &varName);
210 static MemberDef *getFortranDefs(yyscan_t yyscanner,const QCString &memberName, const QCString &moduleName,
211  const UseMap &useMap);
212 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size);
213 
214 
215 //-------------------------------------------------------------------
216 
217 static std::mutex g_docCrossReferenceMutex;
218 static std::mutex g_countFlowKeywordsMutex;
219 
220 /* -----------------------------------------------------------------*/
221 #undef YY_INPUT
222 #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
223 
224 %}
225 
226 IDSYM [a-z_A-Z0-9]
227 ID [a-z_A-Z]+{IDSYM}*
228 SUBPROG (subroutine|function)
229 B [ \t]
230 BS [ \t]*
231 BS_ [ \t]+
232 COMMA {BS},{BS}
233 ARGS_L0 ("("[^)]*")")
234 ARGS_L1a [^()]*"("[^)]*")"[^)]*
235 ARGS_L1 ("("{ARGS_L1a}*")")
236 ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")"
237 ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2})
238 
239 NUM_TYPE (complex|integer|logical|real)
240 LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.)
241 KIND {ARGS}
242 CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS}))
243 TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|{CHAR}|TYPE|CLASS|PROCEDURE|ENUMERATOR)
244 
245 INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")"
246 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)
247 ACCESS_SPEC (PROTECTED|PRIVATE|PUBLIC)
248 /* Assume that attribute statements are almost the same as attributes. */
249 ATTR_STMT {ATTR_SPEC}|DIMENSION
250 FLOW (DO|SELECT|CASE|SELECT{BS}(CASE|TYPE)|WHERE|IF|THEN|ELSE|WHILE|FORALL|ELSEWHERE|ELSEIF|RETURN|CONTINUE|EXIT|GO{BS}TO)
251 COMMANDS (FORMAT|CONTAINS|MODULE{BS_}PROCEDURE|WRITE|READ|ALLOCATE|ALLOCATED|ASSOCIATED|PRESENT|DEALLOCATE|NULLIFY|SIZE|INQUIRE|OPEN|CLOSE|FLUSH|DATA|COMMON)
252 IGNORE (CALL)
253 PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?0
254 LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?")"
255 
256 /* | */
257 
258 %option noyywrap
259 %option stack
260 %option caseless
261 /*%option debug*/
262 
263 %x Start
264 %x SubCall
265 %x ClassName
266 %x Subprog
267 %x DocBlock
268 %x Use
269 %x UseOnly
270 %x Import
271 %x Declaration
272 %x DeclarationBinding
273 %x DeclContLine
274 %x String
275 %x Subprogend
276 
277 %%
278  /*==================================================================*/
279 
280  /*-------- ignore ------------------------------------------------------------*/
281 
282 <Start>{IGNORE}/{BS}"(" { // do not search keywords, intrinsics... TODO: complete list
283  codifyLines(yyscanner,yytext);
284  }
285  /*-------- inner construct ---------------------------------------------------*/
286 
287 <Start>{COMMANDS}/{BS}[,( \t\n] { // highlight
288  /* font class is defined e.g. in doxygen.css */
289  startFontClass(yyscanner,"keyword");
290  codifyLines(yyscanner,yytext);
291  endFontClass(yyscanner);
292  }
293 <Start>{FLOW}/{BS}[,( \t\n] {
294  if (yyextra->isFixedForm)
295  {
296  if ((yy_my_start == 1) && ((yytext[0] == 'c') || (yytext[0] == 'C'))) YY_FTN_REJECT;
297  }
298  if (yyextra->currentMemberDef && yyextra->currentMemberDef->isFunction())
299  {
300  std::lock_guard<std::mutex> lock(g_countFlowKeywordsMutex);
301  MemberDefMutable *mdm = toMemberDefMutable(yyextra->currentMemberDef);
302  if (mdm)
303  {
304  mdm->incrementFlowKeyWordCount();
305  }
306  }
307  /* font class is defined e.g. in doxygen.css */
308  startFontClass(yyscanner,"keywordflow");
309  codifyLines(yyscanner,yytext);
310  endFontClass(yyscanner);
311  }
312 <Start>{BS}(CASE|CLASS|TYPE){BS_}(IS|DEFAULT) {
313  startFontClass(yyscanner,"keywordflow");
314  codifyLines(yyscanner,yytext);
315  endFontClass(yyscanner);
316  }
317 <Start>{BS}"end"({BS}{FLOW})/[ \t\n] { // list is a bit long as not all have possible end
318  startFontClass(yyscanner,"keywordflow");
319  codifyLines(yyscanner,yytext);
320  endFontClass(yyscanner);
321  }
322 <Start>"implicit"{BS}("none"|{TYPE_SPEC}) {
323  startFontClass(yyscanner,"keywordtype");
324  codifyLines(yyscanner,yytext);
325  endFontClass(yyscanner);
326  }
327 <Start>^{BS}"namelist"/[/] { // Namelist specification
328  startFontClass(yyscanner,"keywordtype");
329  codifyLines(yyscanner,yytext);
330  endFontClass(yyscanner);
331  }
332  /*-------- use statement -------------------------------------------*/
333 <Start>"use"{BS_} {
334  startFontClass(yyscanner,"keywordtype");
335  codifyLines(yyscanner,yytext);
336  endFontClass(yyscanner);
337  yy_push_state(YY_START,yyscanner);
338  BEGIN(Use);
339  }
340 <Use>"ONLY" { // TODO: rename
341  startFontClass(yyscanner,"keywordtype");
342  codifyLines(yyscanner,yytext);
343  endFontClass(yyscanner);
344  yy_push_state(YY_START,yyscanner);
345  BEGIN(UseOnly);
346  }
347 <Use>{ID} {
348  QCString tmp(yytext);
349  tmp = tmp.lower();
350  yyextra->insideBody=TRUE;
351  generateLink(yyscanner,*yyextra->code, yytext);
352  yyextra->insideBody=FALSE;
353 
354  /* append module name to use dict */
355  yyextra->useEntry = UseEntry();
356  yyextra->useEntry.module = tmp;
357  yyextra->useMembers.insert(std::make_pair(tmp.str(), yyextra->useEntry));
358  addUse(yyscanner,tmp);
359  }
360 <Use,UseOnly,Import>{BS},{BS} { codifyLines(yyscanner,yytext); }
361 <UseOnly,Import>{BS}&{BS}"\n" { codifyLines(yyscanner,yytext);
362  yyextra->contLineNr++;
363  YY_FTN_RESET}
364 <UseOnly>{ID} {
365  QCString tmp(yytext);
366  tmp = tmp.lower();
367  yyextra->useEntry.onlyNames.push_back(tmp);
368  yyextra->insideBody=TRUE;
369  generateLink(yyscanner,*yyextra->code, yytext);
370  yyextra->insideBody=FALSE;
371  }
372 <Use,UseOnly,Import>"\n" {
373  unput(*yytext);
374  yy_pop_state(yyscanner);
375  YY_FTN_RESET
376  }
377 <*>"import"{BS}/"\n" |
378 <*>"import"{BS_} {
379  if(YY_START == String) YY_FTN_REJECT; // ignore in strings
380  startFontClass(yyscanner,"keywordtype");
381  codifyLines(yyscanner,yytext);
382  endFontClass(yyscanner);
383  yy_push_state(YY_START,yyscanner);
384  BEGIN(Import);
385  }
386 <Import>{ID} {
387  yyextra->insideBody=TRUE;
388  generateLink(yyscanner,*yyextra->code, yytext);
389  yyextra->insideBody=FALSE;
390  }
391 <Import>("ONLY"|"NONE"|"ALL") {
392  startFontClass(yyscanner,"keywordtype");
393  codifyLines(yyscanner,yytext);
394  endFontClass(yyscanner);
395  }
396  /*-------- fortran module -----------------------------------------*/
397 <Start>("block"{BS}"data"|"program"|"module"|"interface")/{BS_}|({COMMA}{ACCESS_SPEC})|\n { //
398  startScope(yyscanner);
399  startFontClass(yyscanner,"keyword");
400  codifyLines(yyscanner,yytext);
401  endFontClass(yyscanner);
402  yy_push_state(YY_START,yyscanner);
403  BEGIN(ClassName);
404  if (!qstricmp(yytext,"module")) yyextra->currentModule="module";
405  }
406 <Start>("enum")/{BS_}|{BS}{COMMA}{BS}{LANGUAGE_BIND_SPEC}|\n { //
407  startScope(yyscanner);
408  startFontClass(yyscanner,"keyword");
409  codifyLines(yyscanner,yytext);
410  endFontClass(yyscanner);
411  yy_push_state(YY_START,yyscanner);
412  BEGIN(ClassName);
413  }
414 <*>{LANGUAGE_BIND_SPEC} { //
415  if(YY_START == String) YY_FTN_REJECT; // ignore in strings
416  startFontClass(yyscanner,"keyword");
417  codifyLines(yyscanner,yytext);
418  endFontClass(yyscanner);
419  }
420 <Start>("type")/{BS_}|({COMMA}({ACCESS_SPEC}|ABSTRACT|EXTENDS))|\n { //
421  startScope(yyscanner);
422  startFontClass(yyscanner,"keyword");
423  codifyLines(yyscanner,yytext);
424  endFontClass(yyscanner);
425  yy_push_state(YY_START,yyscanner);
426  BEGIN(ClassName);
427  }
428 <ClassName>{ID} {
429  if (yyextra->currentModule == "module")
430  {
431  yyextra->currentModule=yytext;
432  yyextra->currentModule = yyextra->currentModule.lower();
433  }
434  generateLink(yyscanner,*yyextra->code,yytext);
435  yy_pop_state(yyscanner);
436  }
437 <ClassName>({ACCESS_SPEC}|ABSTRACT|EXTENDS)/[,:( ] { //| variable declaration
438  startFontClass(yyscanner,"keyword");
439  yyextra->code->codify(QCString(yytext));
440  endFontClass(yyscanner);
441  }
442 <ClassName>\n { // interface may be without name
443  yy_pop_state(yyscanner);
444  YY_FTN_REJECT;
445  }
446 <Start>^{BS}"end"({BS_}"enum").* {
447  YY_FTN_REJECT;
448  }
449 <Start>^{BS}"end"({BS_}"type").* {
450  YY_FTN_REJECT;
451  }
452 <Start>^{BS}"end"({BS_}"module").* { // just reset yyextra->currentModule, rest is done in following rule
453  yyextra->currentModule=0;
454  YY_FTN_REJECT;
455  }
456  /*-------- subprog definition -------------------------------------*/
457 <Start>({PREFIX}{BS_})?{TYPE_SPEC}{BS_}({PREFIX}{BS_})?{BS}/{SUBPROG}{BS_} { // TYPE_SPEC is for old function style function result
458  startFontClass(yyscanner,"keyword");
459  codifyLines(yyscanner,yytext);
460  endFontClass(yyscanner);
461  }
462 <Start>({PREFIX}{BS_})?{SUBPROG}{BS_} { // Fortran subroutine or function found
463  startFontClass(yyscanner,"keyword");
464  codifyLines(yyscanner,yytext);
465  endFontClass(yyscanner);
466  yy_push_state(YY_START,yyscanner);
467  BEGIN(Subprog);
468  }
469 <Subprog>{ID} { // subroutine/function name
470  DBG_CTX((stderr, "===> start subprogram %s\n", yytext));
471  startScope(yyscanner);
472  generateLink(yyscanner,*yyextra->code,yytext);
473  }
474 <Subprog>"result"/{BS}"("[^)]*")" {
475  startFontClass(yyscanner,"keyword");
476  codifyLines(yyscanner,yytext);
477  endFontClass(yyscanner);
478  }
479 <Subprog>"("[^)]*")" { // ignore rest of line
480  codifyLines(yyscanner,yytext);
481  }
482 <Subprog,Subprogend>"\n" { codifyLines(yyscanner,yytext);
483  yyextra->contLineNr++;
484  yy_pop_state(yyscanner);
485  YY_FTN_RESET
486  }
487 <Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface")?{BS} { // Fortran subroutine or function ends
488  //cout << "===> end function " << yytext << endl;
489  endScope(yyscanner);
490  startFontClass(yyscanner,"keyword");
491  codifyLines(yyscanner,yytext);
492  endFontClass(yyscanner);
493  yy_push_state(YY_START,yyscanner);
494  BEGIN(Subprogend);
495  }
496 <Subprogend>{ID}/{BS}(\n|!|;) {
497  generateLink(yyscanner,*yyextra->code,yytext);
498  yy_pop_state(yyscanner);
499  }
500 <Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface"){BS}/(\n|!|;) { // Fortran subroutine or function ends
501  //cout << "===> end function " << yytext << endl;
502  endScope(yyscanner);
503  startFontClass(yyscanner,"keyword");
504  codifyLines(yyscanner,yytext);
505  endFontClass(yyscanner);
506  }
507  /*-------- variable declaration ----------------------------------*/
508 <Start>^{BS}"real"/[,:( ] { // real is a bit tricky as it is a data type but also a function.
509  yy_push_state(YY_START,yyscanner);
510  BEGIN(Declaration);
511  startFontClass(yyscanner,"keywordtype");
512  yyextra->code->codify(QCString(yytext));
513  endFontClass(yyscanner);
514  }
515 <Start>{TYPE_SPEC}/[,:( ] {
516  QCString typ(yytext);
517  typ = removeRedundantWhiteSpace(typ.lower());
518  if (typ.startsWith("real")) YY_FTN_REJECT;
519  if (typ == "type" || typ == "class" || typ == "procedure") yyextra->inTypeDecl = 1;
520  yy_push_state(YY_START,yyscanner);
521  BEGIN(Declaration);
522  startFontClass(yyscanner,"keywordtype");
523  yyextra->code->codify(QCString(yytext));
524  endFontClass(yyscanner);
525  }
526 <Start>{ATTR_SPEC} {
527  if (QCString(yytext) == "external")
528  {
529  yy_push_state(YY_START,yyscanner);
530  BEGIN(Declaration);
531  yyextra->isExternal = true;
532  }
533  startFontClass(yyscanner,"keywordtype");
534  yyextra->code->codify(QCString(yytext));
535  endFontClass(yyscanner);
536  }
537 <Declaration>({TYPE_SPEC}|{ATTR_SPEC})/[,:( ] { //| variable declaration
538  if (QCString(yytext) == "external") yyextra->isExternal = true;
539  startFontClass(yyscanner,"keywordtype");
540  yyextra->code->codify(QCString(yytext));
541  endFontClass(yyscanner);
542  }
543 <Declaration>{ID} { // local var
544  if (yyextra->isFixedForm && yy_my_start == 1)
545  {
546  startFontClass(yyscanner,"comment");
547  yyextra->code->codify(QCString(yytext));
548  endFontClass(yyscanner);
549  }
550  else if (yyextra->currentMemberDef &&
551  ((yyextra->currentMemberDef->isFunction() && (yyextra->currentMemberDef->typeString()!=QCString("subroutine") || yyextra->inTypeDecl)) ||
552  yyextra->currentMemberDef->isVariable() || yyextra->currentMemberDef->isEnumValue()
553  )
554  )
555  {
556  generateLink(yyscanner,*yyextra->code, yytext);
557  }
558  else
559  {
560  yyextra->code->codify(QCString(yytext));
561  addLocalVar(yyscanner,QCString(yytext));
562  }
563  }
564 <Declaration>{BS}("=>"|"="){BS} { // Procedure binding
565  BEGIN(DeclarationBinding);
566  yyextra->code->codify(QCString(yytext));
567  }
568 <DeclarationBinding>{ID} { // Type bound procedure link
569  generateLink(yyscanner,*yyextra->code, yytext);
570  yy_pop_state(yyscanner);
571  }
572 <Declaration>[(] { // start of array or type / class specification
573  yyextra->bracketCount++;
574  yyextra->code->codify(QCString(yytext));
575  }
576 
577 <Declaration>[)] { // end array specification
578  yyextra->bracketCount--;
579  if (!yyextra->bracketCount) yyextra->inTypeDecl = 0;
580  yyextra->code->codify(QCString(yytext));
581  }
582 
583 <Declaration,DeclarationBinding>"&" { // continuation line
584  yyextra->code->codify(QCString(yytext));
585  if (!yyextra->isFixedForm)
586  {
587  yy_push_state(YY_START,yyscanner);
588  BEGIN(DeclContLine);
589  }
590  }
591 <DeclContLine>"\n" { // declaration not yet finished
592  yyextra->contLineNr++;
593  codifyLines(yyscanner,yytext);
594  yyextra->bracketCount = 0;
595  yy_pop_state(yyscanner);
596  YY_FTN_RESET
597  }
598 <Declaration,DeclarationBinding>"\n" { // end declaration line (?)
599  if (yyextra->endComment)
600  {
601  yyextra->endComment=FALSE;
602  }
603  else
604  {
605  codifyLines(yyscanner,yytext);
606  }
607  yyextra->bracketCount = 0;
608  yyextra->contLineNr++;
609  if (!(yyextra->hasContLine && yyextra->hasContLine[yyextra->contLineNr - 1]))
610  {
611  yyextra->isExternal = false;
612  yy_pop_state(yyscanner);
613  }
614  YY_FTN_RESET
615  }
616 
617  /*-------- subprog calls -----------------------------------------*/
618 
619 <Start>"call"{BS_} {
620  startFontClass(yyscanner,"keyword");
621  codifyLines(yyscanner,yytext);
622  endFontClass(yyscanner);
623  yy_push_state(YY_START,yyscanner);
624  BEGIN(SubCall);
625  }
626 <SubCall>{ID} { // subroutine call
627  yyextra->insideBody=TRUE;
628  generateLink(yyscanner,*yyextra->code, yytext);
629  yyextra->insideBody=FALSE;
630  yy_pop_state(yyscanner);
631  }
632 <Start>{ID}{BS}/"(" { // function call
633  if (yyextra->isFixedForm && yy_my_start == 6)
634  {
635  // fixed form continuation line
636  YY_FTN_REJECT;
637  }
638  else if (QCString(yytext).stripWhiteSpace().lower() == "type")
639  {
640  yy_push_state(YY_START,yyscanner);
641  BEGIN(Declaration);
642  startFontClass(yyscanner,"keywordtype");
643  yyextra->code->codify(QCString(yytext).stripWhiteSpace());
644  endFontClass(yyscanner);
645  yyextra->code->codify(QCString(yytext + 4));
646  }
647  else
648  {
649  yyextra->insideBody=TRUE;
650  generateLink(yyscanner,*yyextra->code,yytext);
651  yyextra->insideBody=FALSE;
652  }
653  }
654 
655  /*-------- comments ---------------------------------------------------*/
656 <Start,Declaration,DeclarationBinding>\n?{BS}"!>"|"!<" { // start comment line or comment block
657  if (yytext[0] == '\n')
658  {
659  yyextra->contLineNr++;
660  yy_old_start = 0;
661  yy_my_start = 1;
662  yy_end = static_cast<int>(yyleng);
663  }
664  // Actually we should see if ! on position 6, can be continuation
665  // but the chance is very unlikely, so no effort to solve it here
666  yy_push_state(YY_START,yyscanner);
667  BEGIN(DocBlock);
668  yyextra->docBlock=yytext;
669  }
670 <Declaration,DeclarationBinding>{BS}"!<" { // start comment line or comment block
671  yy_push_state(YY_START,yyscanner);
672  BEGIN(DocBlock);
673  yyextra->docBlock=yytext;
674  }
675 
676 <DocBlock>.* { // contents of current comment line
677  yyextra->docBlock+=yytext;
678  }
679 <DocBlock>"\n"{BS}("!>"|"!<"|"!!") { // comment block (next line is also comment line)
680  yyextra->contLineNr++;
681  yy_old_start = 0;
682  yy_my_start = 1;
683  yy_end = static_cast<int>(yyleng);
684  // Actually we should see if ! on position 6, can be continuation
685  // but the chance is very unlikely, so no effort to solve it here
686  yyextra->docBlock+=yytext;
687  }
688 <DocBlock>"\n" { // comment block ends at the end of this line
689  // remove special comment (default config)
690  yyextra->contLineNr++;
691  if (Config_getBool(STRIP_CODE_COMMENTS))
692  {
693  yyextra->yyLineNr+=((QCString)yyextra->docBlock).contains('\n');
694  yyextra->yyLineNr+=1;
695  nextCodeLine(yyscanner);
696  yyextra->endComment=TRUE;
697  }
698  else // do not remove comment
699  {
700  startFontClass(yyscanner,"comment");
701  codifyLines(yyscanner,yyextra->docBlock);
702  endFontClass(yyscanner);
703  }
704  unput(*yytext);
705  yyextra->contLineNr--;
706  yy_pop_state(yyscanner);
707  YY_FTN_RESET
708  }
709 
710 <*>"!"[^><\n].*|"!"$ { // normal comment
711  if(YY_START == String) YY_FTN_REJECT; // ignore in strings
712  if (yyextra->isFixedForm && yy_my_start == 6) YY_FTN_REJECT;
713  startFontClass(yyscanner,"comment");
714  codifyLines(yyscanner,yytext);
715  endFontClass(yyscanner);
716  }
717 
718 <*>^[Cc*].* { // normal comment
719  if(! yyextra->isFixedForm) YY_FTN_REJECT;
720 
721  startFontClass(yyscanner,"comment");
722  codifyLines(yyscanner,yytext);
723  endFontClass(yyscanner);
724  }
725 <*>"assignment"/{BS}"("{BS}"="{BS}")" {
726  if(YY_START == String) YY_FTN_REJECT; // ignore in strings
727  startFontClass(yyscanner,"keyword");
728  codifyLines(yyscanner,yytext);
729  endFontClass(yyscanner);
730  }
731 <*>"operator"/{BS}"("[^)]*")" {
732  if(YY_START == String) YY_FTN_REJECT; // ignore in strings
733  startFontClass(yyscanner,"keyword");
734  codifyLines(yyscanner,yytext);
735  endFontClass(yyscanner);
736  }
737 
738  /*------ preprocessor --------------------------------------------*/
739 <Start>"#".*\n {
740  if (yyextra->isFixedForm && yy_my_start == 6) YY_FTN_REJECT;
741  yyextra->contLineNr++;
742  startFontClass(yyscanner,"preprocessor");
743  codifyLines(yyscanner,yytext);
744  endFontClass(yyscanner);
745  YY_FTN_RESET
746  }
747  /*------ variable references? -------------------------------------*/
748 
749 <Start>"%"{BS}{ID} { // ignore references to elements
750  yyextra->code->codify(QCString(yytext));
751  }
752 <Start>{ID} {
753  yyextra->insideBody=TRUE;
754  generateLink(yyscanner,*yyextra->code, yytext);
755  yyextra->insideBody=FALSE;
756  }
757  /*------ strings --------------------------------------------------*/
758 <String>\n { // string with \n inside
759  yyextra->contLineNr++;
760  yyextra->str+=yytext;
761  startFontClass(yyscanner,"stringliteral");
762  codifyLines(yyscanner,yyextra->str);
763  endFontClass(yyscanner);
764  yyextra->str = "";
765  YY_FTN_RESET
766  }
767 <String>\"|\' { // string ends with next quote without previous backspace
768  if(yytext[0]!=yyextra->stringStartSymbol) YY_FTN_REJECT; // single vs double quote
769  yyextra->str+=yytext;
770  startFontClass(yyscanner,"stringliteral");
771  codifyLines(yyscanner,yyextra->str);
772  endFontClass(yyscanner);
773  yy_pop_state(yyscanner);
774  }
775 <String>. {yyextra->str+=yytext;}
776 
777 <*>\"|\' { /* string starts */
778  /* if(YY_START == StrIgnore) YY_FTN_REJECT; // ignore in simple comments */
779  if (yyextra->isFixedForm && yy_my_start == 6) YY_FTN_REJECT;
780  yy_push_state(YY_START,yyscanner);
781  yyextra->stringStartSymbol=yytext[0]; // single or double quote
782  BEGIN(String);
783  yyextra->str=yytext;
784  }
785  /*-----------------------------------------------------------------------------*/
786 
787 <*>\n {
788  if (yyextra->endComment)
789  {
790  yyextra->endComment=FALSE;
791  }
792  else
793  {
794  codifyLines(yyscanner,yytext);
795  // comment cannot extend over the end of a line so should always be terminated at the end of the line.
796  if (yyextra->currentFontClass && !strcmp(yyextra->currentFontClass,"comment")) endFontClass(yyscanner);
797  }
798  yyextra->contLineNr++;
799  YY_FTN_RESET
800  }
801 <*>^{BS}"type"{BS}"=" { yyextra->code->codify(QCString(yytext)); }
802 
803 <*>[\x80-\xFF]* { // keep utf8 characters together...
804  if (yyextra->isFixedForm && yy_my_start > fixedCommentAfter)
805  {
806  startFontClass(yyscanner,"comment");
807  codifyLines(yyscanner,yytext);
808  }
809  else
810  {
811  yyextra->code->codify(QCString(yytext));
812  }
813  }
814 <*>. {
815  if (yyextra->isFixedForm && yy_my_start > fixedCommentAfter)
816  {
817  //yy_push_state(YY_START,yyscanner);
818  //BEGIN(DocBlock);
819  //yyextra->docBlock=yytext;
820  startFontClass(yyscanner,"comment");
821  codifyLines(yyscanner,yytext);
822  }
823  else
824  {
825  yyextra->code->codify(QCString(yytext));
826  }
827  }
828 <*>{LOG_OPER} { // Fortran logical comparison keywords
829  yyextra->code->codify(QCString(yytext));
830  }
831 <*><<EOF>> {
832  if (YY_START == DocBlock) {
833  if (!Config_getBool(STRIP_CODE_COMMENTS))
834  {
835  startFontClass(yyscanner,"comment");
836  codifyLines(yyscanner,yyextra->docBlock);
837  endFontClass(yyscanner);
838  }
839  }
840  yyterminate();
841  }
842 %%
843 
844 /*@ ----------------------------------------------------------------------------
845  */
846 
847 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size)
848 {
849  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
850  yy_size_t inputPosition = yyextra->inputPosition;
851  const char *s = yyextra->inputString + inputPosition;
852  yy_size_t c=0;
853  while( c < max_size && *s)
854  {
855  *buf++ = *s++;
856  c++;
857  }
858  yyextra->inputPosition += c;
859  return c;
860 }
861 
862 static void endFontClass(yyscan_t yyscanner)
863 {
864  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
865  if (yyextra->currentFontClass)
866  {
867  yyextra->code->endFontClass();
868  yyextra->currentFontClass=0;
869  }
870 }
871 
872 static void startFontClass(yyscan_t yyscanner,const char *s)
873 {
874  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
875  // if font class is already set don't stop and start it.
876  // strcmp does not like null pointers as input.
877  if (!yyextra->currentFontClass || !s || strcmp(yyextra->currentFontClass,s))
878  {
879  endFontClass(yyscanner);
880  yyextra->code->startFontClass(QCString(s));
881  yyextra->currentFontClass=s;
882  }
883 }
884 
885 static void setCurrentDoc(yyscan_t yyscanner,const QCString &anchor)
886 {
887  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
888  if (Doxygen::searchIndex)
889  {
890  if (yyextra->searchCtx)
891  {
892  yyextra->code->setCurrentDoc(yyextra->searchCtx,yyextra->searchCtx->anchor(),FALSE);
893  }
894  else
895  {
896  yyextra->code->setCurrentDoc(yyextra->sourceFileDef,anchor,TRUE);
897  }
898  }
899 }
900 
901 static void addToSearchIndex(yyscan_t yyscanner,const QCString &text)
902 {
903  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
904  if (Doxygen::searchIndex)
905  {
906  yyextra->code->addWord(text,FALSE);
907  }
908 }
909 
910 /*! start a new line of code, inserting a line number if yyextra->sourceFileDef
911  * is TRUE. If a definition starts at the current line, then the line
912  * number is linked to the documentation of that definition.
913  */
914 static void startCodeLine(yyscan_t yyscanner)
915 {
916  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
917  if (yyextra->sourceFileDef)
918  {
919  //QCString lineNumber,lineAnchor;
920  //lineNumber.sprintf("%05d",yyextra->yyLineNr);
921  //lineAnchor.sprintf("l%05d",yyextra->yyLineNr);
922 
923  const Definition *d = yyextra->sourceFileDef->getSourceDefinition(yyextra->yyLineNr);
924  //printf("startCodeLine %d d=%s\n", yyextra->yyLineNr,d ? qPrint(d->name()) : "<null>");
925  if (!yyextra->includeCodeFragment && d)
926  {
927  yyextra->currentDefinition = d;
928  yyextra->currentMemberDef = yyextra->sourceFileDef->getSourceMember(yyextra->yyLineNr);
929  yyextra->insideBody = FALSE;
930  yyextra->endComment = FALSE;
931  QCString lineAnchor;
932  lineAnchor.sprintf("l%05d",yyextra->yyLineNr);
933  if (yyextra->currentMemberDef)
934  {
935  yyextra->code->writeLineNumber(yyextra->currentMemberDef->getReference(),
936  yyextra->currentMemberDef->getOutputFileBase(),
937  yyextra->currentMemberDef->anchor(),yyextra->yyLineNr,
938  !yyextra->includeCodeFragment);
939  setCurrentDoc(yyscanner,lineAnchor);
940  }
941  else if (d->isLinkableInProject())
942  {
943  yyextra->code->writeLineNumber(d->getReference(),
944  d->getOutputFileBase(),
945  QCString(),yyextra->yyLineNr,
946  !yyextra->includeCodeFragment);
947  setCurrentDoc(yyscanner,lineAnchor);
948  }
949  }
950  else
951  {
952  yyextra->code->writeLineNumber(QCString(),QCString(),QCString(),yyextra->yyLineNr,
953  !yyextra->includeCodeFragment);
954  }
955  }
956  yyextra->code->startCodeLine(yyextra->sourceFileDef);
957  if (yyextra->currentFontClass)
958  {
959  yyextra->code->startFontClass(QCString(yyextra->currentFontClass));
960  }
961 }
962 
963 
964 static void endCodeLine(yyscan_t yyscanner)
965 {
966  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
967  endFontClass(yyscanner);
968  yyextra->code->endCodeLine();
969 }
970 
971 static void nextCodeLine(yyscan_t yyscanner)
972 {
973  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
974  const char * fc = yyextra->currentFontClass;
975  endCodeLine(yyscanner);
976  if (yyextra->yyLineNr<yyextra->inputLines)
977  {
978  yyextra->currentFontClass = fc;
979  startCodeLine(yyscanner);
980  }
981 }
982 
983 /*! write a code fragment 'text' that may span multiple lines, inserting
984  * line numbers for each line.
985  */
986 static void codifyLines(yyscan_t yyscanner,const QCString &text)
987 {
988  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
989  //printf("codifyLines(%d,\"%s\")\n",yyextra->yyLineNr,text);
990  if (text.isEmpty()) return;
991  const char *p=text.data(),*sp=p;
992  char c;
993  bool done=FALSE;
994  while (!done)
995  {
996  sp=p;
997  while ((c=*p++) && c!='\n') { }
998  if (c=='\n')
999  {
1000  yyextra->yyLineNr++;
1001  int l = (int)(p-sp-1);
1002  char *tmp = (char*)malloc(l+1);
1003  memcpy(tmp,sp,l);
1004  tmp[l]='\0';
1005  yyextra->code->codify(QCString(tmp));
1006  free(tmp);
1007  nextCodeLine(yyscanner);
1008  }
1009  else
1010  {
1011  yyextra->code->codify(QCString(sp));
1012  done=TRUE;
1013  }
1014  }
1015 }
1016 
1017 /*! writes a link to a fragment \a text that may span multiple lines, inserting
1018  * line numbers for each line. If \a text contains newlines, the link will be
1019  * split into multiple links with the same destination, one for each line.
1020  */
1021 static void writeMultiLineCodeLink(yyscan_t yyscanner,CodeOutputInterface &ol,
1022  Definition *d,const QCString &text)
1023 {
1024  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1025  static bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS);
1026  yyextra->tooltipManager.addTooltip(ol,d);
1027  QCString ref = d->getReference();
1028  QCString file = d->getOutputFileBase();
1029  QCString anchor = d->anchor();
1030  QCString tooltip;
1031  if (!sourceTooltips) // fall back to simple "title" tooltips
1032  {
1033  tooltip = d->briefDescriptionAsTooltip();
1034  }
1035  bool done=FALSE;
1036  const char *p=text.data();
1037  while (!done)
1038  {
1039  const char *sp=p;
1040  char c;
1041  while ((c=*p++) && c!='\n') { }
1042  if (c=='\n')
1043  {
1044  yyextra->yyLineNr++;
1045  //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
1046  ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,QCString(sp,p-sp-1),tooltip);
1047  nextCodeLine(yyscanner);
1048  }
1049  else
1050  {
1051  //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
1052  ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,sp,tooltip);
1053  done=TRUE;
1054  }
1055  }
1056 }
1057 //-------------------------------------------------------------------------------
1058 /**
1059  searches for definition of a module (Namespace)
1060  @param mname the name of the module
1061  @param cd the entry, if found or null
1062  @returns true, if module is found
1063 */
1064 static bool getFortranNamespaceDefs(const QCString &mname,
1065  NamespaceDef *&cd)
1066 {
1067  if (mname.isEmpty()) return FALSE; /* empty name => nothing to link */
1068 
1069  // search for module
1070  if ((cd=Doxygen::namespaceLinkedMap->find(mname))) return TRUE;
1071 
1072  return FALSE;
1073 }
1074 //-------------------------------------------------------------------------------
1075 /**
1076  searches for definition of a type
1077  @param tname the name of the type
1078  @param moduleName name of enclosing module or null, if global entry
1079  @param cd the entry, if found or null
1080  @param useMap map of data of USE-statement
1081  @returns true, if type is found
1082 */
1083 static bool getFortranTypeDefs(const QCString &tname, const QCString &moduleName,
1084  ClassDef *&cd, const UseMap &useMap)
1085 {
1086  if (tname.isEmpty()) return FALSE; /* empty name => nothing to link */
1087 
1088  //cout << "=== search for type: " << tname << endl;
1089 
1090  // search for type
1091  if ((cd=Doxygen::classLinkedMap->find(tname)))
1092  {
1093  //cout << "=== type found in global module" << endl;
1094  return TRUE;
1095  }
1096  else if (!moduleName.isEmpty() && (cd= Doxygen::classLinkedMap->find(moduleName+"::"+tname)))
1097  {
1098  //cout << "=== type found in local module" << endl;
1099  return TRUE;
1100  }
1101  else
1102  {
1103  for (const auto &kv : useMap)
1104  {
1105  if ((cd= Doxygen::classLinkedMap->find(kv.second.module+"::"+tname)))
1106  {
1107  //cout << "=== type found in used module" << endl;
1108  return TRUE;
1109  }
1110  }
1111  }
1112 
1113  return FALSE;
1114 }
1115 
1116 /**
1117  searches for definition of function memberName
1118  @param yyscanner the scanner data to be used
1119  @param memberName the name of the function/variable
1120  @param moduleName name of enclosing module or null, if global entry
1121  @param useMap map of data of USE-statement
1122  @returns MemberDef pointer, if found, or nullptr otherwise
1123 */
1124 static MemberDef *getFortranDefs(yyscan_t yyscanner,const QCString &memberName, const QCString &moduleName,
1125  const UseMap &useMap)
1126 {
1127  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1128  if (memberName.isEmpty()) return nullptr; /* empty name => nothing to link */
1129 
1130  // look in local variables
1131  for (auto it = yyextra->scopeStack.rbegin(); it!=yyextra->scopeStack.rend(); ++it)
1132  {
1133  const Scope &scope = *it;
1134  std::string lowMemName = memberName.lower().str();
1135  if (scope.localVars .find(lowMemName)!=std::end(scope.localVars) && // local var
1136  scope.externalVars.find(lowMemName)==std::end(scope.externalVars)) // and not external
1137  {
1138  return nullptr;
1139  }
1140  }
1141 
1142  // search for function
1143  MemberName *mn = Doxygen::functionNameLinkedMap->find(memberName);
1144  if (!mn)
1145  {
1146  mn = Doxygen::memberNameLinkedMap->find(memberName);
1147  }
1148 
1149  if (mn) // name is known
1150  {
1151  // all found functions with given name
1152  for (const auto &md : *mn)
1153  {
1154  const FileDef *fd=md->getFileDef();
1155  const GroupDef *gd=md->getGroupDef();
1156  const ClassDef *cd=md->getClassDef();
1157 
1158  //cout << "found link with same name: " << fd->fileName() << " " << memberName;
1159  //if (md->getNamespaceDef() != 0) cout << " in namespace " << md->getNamespaceDef()->name();cout << endl;
1160 
1161  if ((gd && gd->isLinkable()) || (fd && fd->isLinkable()))
1162  {
1163  const NamespaceDef *nspace= md->getNamespaceDef();
1164 
1165  if (nspace == 0)
1166  { // found function in global scope
1167  if(cd == 0)
1168  { // Skip if bound to type
1169  return md.get();
1170  }
1171  }
1172  else if (moduleName == nspace->name())
1173  { // found in local scope
1174  return md.get();
1175  }
1176  else
1177  { // else search in used modules
1178  QCString usedModuleName= nspace->name();
1179  auto use_it = useMap.find(usedModuleName.str());
1180  if (use_it!=useMap.end())
1181  {
1182  const UseEntry &ue = use_it->second;
1183  // check if only-list exists and if current entry exists is this list
1184  if (ue.onlyNames.empty())
1185  {
1186  //cout << " found in module " << usedModuleName << " entry " << memberName << endl;
1187  return md.get(); // whole module used
1188  }
1189  else
1190  {
1191  for ( const auto &name : ue.onlyNames)
1192  {
1193  //cout << " search in only: " << usedModuleName << ":: " << memberName << "==" << (*it)<< endl;
1194  if (memberName == name)
1195  {
1196  return md.get(); // found in ONLY-part of use list
1197  }
1198  }
1199  }
1200  }
1201  }
1202  } // if linkable
1203  } // for
1204  }
1205  return nullptr;
1206 }
1207 
1208 /**
1209  gets the link to a generic procedure which depends not on the name, but on the parameter list
1210  @todo implementation
1211 */
1212 static bool getGenericProcedureLink(yyscan_t yyscanner,const ClassDef *cd,
1213  const QCString &memberText,
1214  CodeOutputInterface &ol)
1215 {
1216  (void)cd;
1217  (void)memberText;
1218  (void)ol;
1219  return FALSE;
1220 }
1221 
1222 static bool getLink(yyscan_t yyscanner,const UseMap &useMap, // dictionary with used modules
1223  const QCString &memberText, // exact member text
1224  CodeOutputInterface &ol,
1225  const QCString &text)
1226 {
1227  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1228  MemberDef *md=0;
1229  QCString memberName= removeRedundantWhiteSpace(memberText);
1230 
1231  if ((md=getFortranDefs(yyscanner,memberName, yyextra->currentModule, useMap)) && md->isLinkable())
1232  {
1233  if (md->isVariable() && (md->getLanguage()!=SrcLangExt_Fortran)) return FALSE; // Non Fortran variables aren't handled yet,
1234  // see also linkifyText in util.cpp
1235 
1236  const Definition *d = md->getOuterScope()==Doxygen::globalScope ?
1237  md->getBodyDef() : md->getOuterScope();
1238  if (md->getGroupDef()) d = md->getGroupDef();
1239  if (d && d->isLinkable())
1240  {
1241  if (yyextra->currentDefinition && yyextra->currentMemberDef &&
1242  yyextra->insideBody && yyextra->collectXRefs)
1243  {
1244  std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1245  addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(md));
1246  }
1247  writeMultiLineCodeLink(yyscanner,ol,md,!text.isEmpty() ? text : memberText);
1248  addToSearchIndex(yyscanner, !text.isEmpty() ? text : memberText);
1249  return TRUE;
1250  }
1251  }
1252  return FALSE;
1253 }
1254 
1255 
1256 static void generateLink(yyscan_t yyscanner,CodeOutputInterface &ol, const QCString &lname)
1257 {
1258  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1259  ClassDef *cd=0;
1260  NamespaceDef *nsd=0;
1261  QCString name = lname;
1262  name = removeRedundantWhiteSpace(name.lower());
1263 
1264  // check if lowercase lname is a linkable type or interface
1265  if ( (getFortranTypeDefs(name, yyextra->currentModule, cd, yyextra->useMembers)) && cd->isLinkable() )
1266  {
1267  if ( (cd->compoundType() == ClassDef::Class) && // was Entry::INTERFACE_SEC) &&
1268  (getGenericProcedureLink(yyscanner, cd, name, ol)) )
1269  {
1270  //cout << "=== generic procedure resolved" << endl;
1271  }
1272  else
1273  { // write type or interface link
1274  writeMultiLineCodeLink(yyscanner, ol,cd,name);
1275  addToSearchIndex(yyscanner, name);
1276  }
1277  }
1278  // check for module
1279  else if ( (getFortranNamespaceDefs(name, nsd)) && nsd->isLinkable() )
1280  { // write module link
1281  writeMultiLineCodeLink(yyscanner,ol,nsd,name);
1282  addToSearchIndex(yyscanner,name);
1283  }
1284  // check for function/variable
1285  else if (getLink(yyscanner,yyextra->useMembers, name, ol, name))
1286  {
1287  //cout << "=== found link for lowercase " << lname << endl;
1288  }
1289  else
1290  {
1291  // nothing found, just write out the word
1292  //startFontClass("charliteral"); //test
1293  codifyLines(yyscanner,name);
1294  //endFontClass(yyscanner); //test
1295  addToSearchIndex(yyscanner,name);
1296  }
1297 }
1298 
1299 static void generateLink(yyscan_t yyscanner,CodeOutputInterface &ol, const char *lname)
1300 {
1301  generateLink(yyscanner,ol,QCString(lname));
1302 }
1303 
1304 /*! counts the number of lines in the input */
1305 static int countLines(yyscan_t yyscanner)
1306 {
1307  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1308  const char *p=yyextra->inputString;
1309  char c;
1310  int count=1;
1311  while ((c=*p))
1312  {
1313  p++ ;
1314  if (c=='\n') count++;
1315  }
1316  if (p>yyextra->inputString && *(p-1)!='\n')
1317  { // last line does not end with a \n, so we add an extra
1318  // line and explicitly terminate the line after parsing.
1319  count++,
1320  yyextra->needsTermination=TRUE;
1321  }
1322  return count;
1323 }
1324 
1325 //----------------------------------------------------------------------------
1326 /** start scope */
1327 static void startScope(yyscan_t yyscanner)
1328 {
1329  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1330  DBG_CTX((stderr, "===> startScope %s",yytext));
1331  yyextra->scopeStack.push_back(Scope());
1332 }
1333 
1334 /** end scope */
1335 static void endScope(yyscan_t yyscanner)
1336 {
1337  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1338  DBG_CTX((stderr,"===> endScope %s",yytext));
1339  if (yyextra->scopeStack.empty())
1340  {
1341  DBG_CTX((stderr,"WARNING: fortrancode.l: stack empty!\n"));
1342  return;
1343  }
1344 
1345  Scope &scope = yyextra->scopeStack.back();
1346  for ( const auto &name : scope.useNames)
1347  {
1348  yyextra->useMembers.erase(name.str());
1349  }
1350  yyextra->scopeStack.pop_back();
1351 }
1352 
1353 static void addUse(yyscan_t yyscanner,const QCString &moduleName)
1354 {
1355  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1356  if (!yyextra->scopeStack.empty())
1357  yyextra->scopeStack.back().useNames.push_back(moduleName);
1358 }
1359 
1360 static void addLocalVar(yyscan_t yyscanner,const QCString &varName)
1361 {
1362  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1363  if (!yyextra->scopeStack.empty())
1364  {
1365  std::string lowVarName = varName.lower().str();
1366  yyextra->scopeStack.back().localVars.insert(lowVarName);
1367  if (yyextra->isExternal) yyextra->scopeStack.back().externalVars.insert(lowVarName);
1368  }
1369 }
1370 
1371 /*===================================================================*/
1372 
1373 
1374 static void checkContLines(yyscan_t yyscanner,const char *s)
1375 {
1376  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1377  int numLines = 0;
1378  int i = 0;
1379  const char *p = s;
1380 
1381  numLines = 2; // one for element 0, one in case no \n at end
1382  while (*p)
1383  {
1384  if (*p == '\n') numLines++;
1385  p++;
1386  }
1387 
1388  yyextra->hasContLine = (int *) malloc((numLines) * sizeof(int));
1389  for (i = 0; i < numLines; i++)
1390  yyextra->hasContLine[i] = 0;
1391  p = prepassFixedForm(s, yyextra->hasContLine);
1392  yyextra->hasContLine[0] = 0;
1393 }
1394 
1395 void parseFortranCode(CodeOutputInterface &od,const char *,const QCString &s,
1396  bool exBlock, const char *exName,const FileDef *fd,
1397  int startLine,int endLine,bool inlineFragment,
1398  const MemberDef *,bool,const Definition *searchCtx,
1399  bool collectXRefs, FortranFormat format)
1400 {
1401  //printf("***parseCode() exBlock=%d exName=%s fd=%p\n",exBlock,exName,fd);
1402 
1403  return;
1404 }
1405 
1406 //---------------------------------------------------------
1407 
1408 struct FortranCodeParser::Private
1409 {
1410  yyscan_t yyscanner;
1411  fortrancodeYY_state state;
1412  FortranFormat format;
1413 };
1414 
1415 FortranCodeParser::FortranCodeParser(FortranFormat format) : p(std::make_unique<Private>())
1416 {
1417  p->format = format;
1418  fortrancodeYYlex_init_extra(&p->state,&p->yyscanner);
1419 #ifdef FLEX_DEBUG
1420  fortrancodeYYset_debug(1,p->yyscanner);
1421 #endif
1422  resetCodeParserState();
1423 }
1424 
1425 FortranCodeParser::~FortranCodeParser()
1426 {
1427  fortrancodeYYlex_destroy(p->yyscanner);
1428 }
1429 
1430 void FortranCodeParser::resetCodeParserState()
1431 {
1432  struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
1433  yyextra->currentDefinition = 0;
1434  yyextra->currentMemberDef = 0;
1435  yyextra->currentFontClass = 0;
1436  yyextra->needsTermination = FALSE;
1437  BEGIN( Start );
1438 }
1439 
1440 void FortranCodeParser::parseCode(CodeOutputInterface & codeOutIntf,
1441  const QCString & scopeName,
1442  const QCString & input,
1443  SrcLangExt /*lang*/,
1444  bool isExampleBlock,
1445  const QCString & exampleName,
1446  const FileDef * fileDef,
1447  int startLine,
1448  int endLine,
1449  bool inlineFragment,
1450  const MemberDef *memberDef,
1451  bool showLineNumbers,
1452  const Definition *searchCtx,
1453  bool collectXRefs
1454  )
1455 {
1456  yyscan_t yyscanner = p->yyscanner;
1457  struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
1458  //::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName,
1459  // fileDef,startLine,endLine,inlineFragment,memberDef,
1460  // showLineNumbers,searchCtx,collectXRefs,m_format);
1461  // parseFortranCode(CodeOutputInterface &od,const char *,const QCString &s,
1462  // bool exBlock, const char *exName,FileDef *fd,
1463  // int startLine,int endLine,bool inlineFragment,
1464  // const MemberDef *,bool,const Definition *searchCtx,
1465  // bool collectXRefs, FortranFormat format)
1466  if (input.isEmpty()) return;
1467  printlex(yy_flex_debug, TRUE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL);
1468  yyextra->code = &codeOutIntf;
1469  yyextra->inputString = input.data();
1470  yyextra->inputPosition = 0;
1471  yyextra->isFixedForm = recognizeFixedForm(input,p->format);
1472  yyextra->contLineNr = 1;
1473  yyextra->hasContLine = NULL;
1474  if (yyextra->isFixedForm)
1475  {
1476  checkContLines(yyscanner,yyextra->inputString);
1477  }
1478  yyextra->currentFontClass = 0;
1479  yyextra->needsTermination = FALSE;
1480  yyextra->searchCtx = searchCtx;
1481  yyextra->collectXRefs = collectXRefs;
1482  if (startLine!=-1)
1483  yyextra->yyLineNr = startLine;
1484  else
1485  yyextra->yyLineNr = 1;
1486 
1487  if (endLine!=-1)
1488  yyextra->inputLines = endLine+1;
1489  else
1490  yyextra->inputLines = yyextra->yyLineNr + countLines(yyscanner) - 1;
1491 
1492  yyextra->exampleBlock = isExampleBlock;
1493  yyextra->exampleName = exampleName;
1494  yyextra->sourceFileDef = fileDef;
1495  if (isExampleBlock && fileDef==0)
1496  {
1497  // create a dummy filedef for the example
1498  yyextra->sourceFileDef = createFileDef(QCString(),exampleName);
1499  }
1500  if (yyextra->sourceFileDef)
1501  {
1502  setCurrentDoc(yyscanner,QCString("l00001"));
1503  }
1504  yyextra->currentDefinition = 0;
1505  yyextra->currentMemberDef = 0;
1506  if (!yyextra->exampleName.isEmpty())
1507  {
1508  yyextra->exampleFile = convertNameToFile(yyextra->exampleName+"-example");
1509  }
1510  yyextra->includeCodeFragment = inlineFragment;
1511  startCodeLine(yyscanner);
1512  fortrancodeYYrestart(0, yyscanner);
1513  BEGIN( Start );
1514  fortrancodeYYlex(yyscanner);
1515  if (yyextra->needsTermination)
1516  {
1517  endFontClass(yyscanner);
1518  yyextra->code->endCodeLine();
1519  }
1520  if (isExampleBlock && yyextra->sourceFileDef)
1521  {
1522  // delete the temporary file definition used for this example
1523  delete yyextra->sourceFileDef;
1524  yyextra->sourceFileDef=0;
1525  }
1526  if (yyextra->hasContLine) free(yyextra->hasContLine);
1527  yyextra->hasContLine = NULL;
1528 
1529  // write the tooltips
1530  yyextra->tooltipManager.writeTooltips(codeOutIntf);
1531 
1532  printlex(yy_flex_debug, FALSE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL);
1533 }
1534 
1535 //---------------------------------------------------------
1536 
1537 #if USE_STATE2STRING
1538 #include "fortrancode.l.h"
1539 #endif