Doxygen
fortranscanner.l
浏览该文件的文档.
1 /* -*- mode: fundamental; indent-tabs-mode: 1; -*- */
2 /*****************************************************************************
3  * Parser for Fortran90 F subset
4  *
5  * Copyright (C) by Anke Visser
6  * based on the work of Dimitri van Heesch.
7  *
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.
13  *
14  * Documents produced by Doxygen are derivative works derived from the
15  * input used in their production; they are not affected by this license.
16  *
17  */
18 
19 /* Developer notes.
20  *
21  * - Consider using startScope(), endScope() functions with module, program,
22  * subroutine or any other scope in fortran program.
23  *
24  * - Symbol yyextra->modifiers (attributes) are collected using SymbolModifiers |= operator during
25  * substructure parsing. When substructure ends all yyextra->modifiers are applied to actual
26  * entries in applyModifiers() functions.
27  *
28  * - How case insensitiveness should be handled in code?
29  * On one side we have arg->name and entry->name, on another side modifierMap[name].
30  * In entries and arguments case is the same as in code, in modifier map case is lowered and
31  * then it is compared to lowered entry/argument names.
32  *
33  * - Do not like constructs like aa{BS} or {BS}bb. Should try to handle blank space
34  * with separate rule?: It seems it is often necessary, because we may parse something like
35  * "functionA" or "MyInterface". So constructs like '(^|[ \t])interface({BS_}{ID})?/[ \t\n]'
36  * are desired.
37  *
38  * - Must track yyextra->lineNr when using REJECT, unput() or similar commands.
39  */
40 %option never-interactive
41 %option case-insensitive
42 %option prefix="fortranscannerYY"
43 %option reentrant
44 %option extra-type="struct fortranscannerYY_state *"
45 %top{
46 #include <stdint.h>
47 // forward declare yyscan_t to improve type safety
48 #define YY_TYPEDEF_YY_SCANNER_T
49 struct yyguts_t;
50 typedef yyguts_t *yyscan_t;
51 }
52 
53 %{
54 
55 #include <map>
56 #include <vector>
57 
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <assert.h>
61 #include <ctype.h>
62 
63 #include "fortranscanner.h"
64 #include "entry.h"
65 #include "message.h"
66 #include "config.h"
67 #include "doxygen.h"
68 #include "util.h"
69 #include "defargs.h"
70 #include "language.h"
71 #include "commentscan.h"
72 #include "pre.h"
73 #include "arguments.h"
74 #include "debug.h"
75 #include "markdown.h"
76 
77 const int fixedCommentAfter = 72;
78 
79 // Toggle for some debugging info
80 //#define DBG_CTX(x) fprintf x
81 #define DBG_CTX(x) do { } while(0)
82 
83 #define YY_NO_INPUT 1
84 #define YY_NO_UNISTD_H 1
85 
86 enum ScanVar { V_IGNORE, V_VARIABLE, V_PARAMETER, V_RESULT};
87 enum InterfaceType { IF_NONE, IF_SPECIFIC, IF_GENERIC, IF_ABSTRACT };
88 
89 // {{{ ----- Helper structs -----
90 //! Holds yyextra->modifiers (ie attributes) for one symbol (variable, function, etc)
91 struct SymbolModifiers
92 {
93  enum Protection {NONE_P, PUBLIC, PRIVATE};
94  enum Direction {NONE_D, IN, OUT, INOUT};
95 
96  //! This is only used with function return value.
97  QCString type, returnName;
98  Protection protection;
99  Direction direction;
100  bool optional;
101  bool protect;
102  QCString dimension;
103  bool allocatable;
104  bool external;
105  bool intrinsic;
106  bool parameter;
107  bool pointer;
108  bool target;
109  bool save;
110  bool deferred;
111  bool nonoverridable;
112  bool nopass;
113  bool pass;
114  bool contiguous;
115  bool volat; /* volatile is a reserved name */
116  bool value; /* volatile is a reserved name */
117  QCString passVar;
118  QCString bindVar;
119 
120  SymbolModifiers() : type(), returnName(), protection(NONE_P), direction(NONE_D),
121  optional(FALSE), protect(FALSE), dimension(), allocatable(FALSE),
122  external(FALSE), intrinsic(FALSE), parameter(FALSE),
123  pointer(FALSE), target(FALSE), save(FALSE), deferred(FALSE), nonoverridable(FALSE),
124  nopass(FALSE), pass(FALSE), contiguous(FALSE), volat(FALSE), value(FALSE), passVar(),
125  bindVar() {}
126 
127  SymbolModifiers& operator|=(const SymbolModifiers &mdfs);
128  SymbolModifiers& operator|=(QCString mdfrString);
129 };
130 
131 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs);
132 
133 static const char *directionStrs[] =
134 {
135  "", "intent(in)", "intent(out)", "intent(inout)"
136 };
137 static const char *directionParam[] =
138 {
139  "", "[in]", "[out]", "[in,out]"
140 };
141 
142 // }}}
143 
144 struct CommentInPrepass
145 {
146  int column;
147  QCString str;
148  CommentInPrepass(int col, QCString s) : column(col), str(s) {}
149 };
150 
151 /* -----------------------------------------------------------------
152  *
153  * statics
154  */
155 
156 struct fortranscannerYY_state
157 {
158  OutlineParserInterface * thisParser;
159  CommentScanner commentScanner;
160  const char * inputString;
161  int inputPosition;
162  bool isFixedForm;
163  QCString inputStringPrepass; ///< Input string for prepass of line cont. '&'
164  QCString inputStringSemi; ///< Input string after command separator ';'
165  unsigned int inputPositionPrepass;
166  int lineCountPrepass = 0;
167  EntryList subrCurrent;
168  std::vector<CommentInPrepass> comments;
169  YY_BUFFER_STATE * includeStack = NULL;
170  int includeStackPtr = 0;
171  int includeStackCnt = 0;
172  QCString fileName;
173  int lineNr = 1 ;
174  int colNr = 0 ;
175  Entry *current_root = 0;
176  Entry *global_scope = 0;
177  std::shared_ptr<Entry> global_root;
178  std::shared_ptr<Entry> file_root;
179  std::shared_ptr<Entry> last_entry;
180  std::shared_ptr<Entry> last_enum;
181  std::shared_ptr<Entry> current;
182  ScanVar vtype = V_IGNORE; // type of parsed variable
183  EntryList moduleProcedures; // list of all interfaces which contain unresolved module procedures
184  QCString docBlock;
185  bool docBlockInBody = FALSE;
186  bool docBlockJavaStyle;
187  QCString debugStr;
188 // Argument *parameter; // element of parameter list
189  QCString argType; // fortran type of an argument of a parameter list
190  QCString argName; // last identifier name in variable list
191  QCString initializer; // initial value of a variable
192  int initializerArrayScope; // number if nested array scopes in initializer
193  int initializerScope; // number if nested function calls in initializer
194  QCString useModuleName; // name of module in the use statement
195  Protection defaultProtection;
196  Protection typeProtection;
197  bool typeMode = false;
198  InterfaceType ifType = IF_NONE;
199  bool functionLine = FALSE;
200  char stringStartSymbol; // single or double quote
201  bool parsingPrototype = FALSE; // see parsePrototype()
202 
203 //! Accumulated modifiers of current statement, eg variable declaration.
204  SymbolModifiers currentModifiers;
205 //! Holds program scope->symbol name->symbol modifiers.
206  std::map<Entry*,std::map<std::string,SymbolModifiers> > modifiers;
207  int anonCount = 0 ;
208 };
209 
210 //-----------------------------------------------------------------------------
211 static int getAmpersandAtTheStart(const char *buf, int length);
212 static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch);
213 static QCString extractFromParens(const QCString &name);
214 static QCString extractBind(const QCString &name);
215 
216 
217 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size);
218 static void startCommentBlock(yyscan_t yyscanner,bool);
219 static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief);
220 static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief);
221 static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief);
222 static void addCurrentEntry(yyscan_t yyscanner,bool case_insens);
223 static void addModule(yyscan_t yyscanner,const QCString &name=QCString(), bool isModule=FALSE);
224 static void addSubprogram(yyscan_t yyscanner,const QCString &text);
225 static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type);
226 static Argument *getParameter(yyscan_t yyscanner,const QCString &name);
227 static void scanner_abort(yyscan_t yyscanner);
228 
229 static void startScope(yyscan_t yyscanner,Entry *scope);
230 static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot=FALSE);
231 static void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root);
232 static void truncatePrepass(yyscan_t yyscanner,int index);
233 static void pushBuffer(yyscan_t yyscanner,const QCString &buffer);
234 static void popBuffer(yyscan_t yyscanner);
235 static const CommentInPrepass* locatePrepassComment(yyscan_t yyscanner,int from, int to);
236 static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to);
237 static void newLine(yyscan_t yyscanner);
238 static void initEntry(yyscan_t yyscanner);
239 
240 static const char *stateToString(int state);
241 
242 //-----------------------------------------------------------------------------
243 #undef YY_INPUT
244 #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
245 #define YY_USER_ACTION yyextra->colNr+=(int)yyleng;
246 #define INVALID_ENTRY ((Entry*)0x8)
247 //-----------------------------------------------------------------------------
248 
249 %}
250 
251  //-----------------------------------------------------------------------------
252  //-----------------------------------------------------------------------------
253 IDSYM [a-z_A-Z0-9]
254 NOTIDSYM [^a-z_A-Z0-9]
255 SEPARATE [:, \t]
256 ID [a-z_A-Z%]+{IDSYM}*
257 ID_ [a-z_A-Z%]*{IDSYM}*
258 PP_ID {ID}
259 LABELID [a-z_A-Z]+[a-z_A-Z0-9\-]*
260 SUBPROG (subroutine|function)
261 B [ \t]
262 BS [ \t]*
263 BS_ [ \t]+
264 BT_ ([ \t]+|[ \t]*"(")
265 COMMA {BS},{BS}
266 ARGS_L0 ("("[^)]*")")
267 ARGS_L1a [^()]*"("[^)]*")"[^)]*
268 ARGS_L1 ("("{ARGS_L1a}*")")
269 ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")"
270 ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2})
271 NOARGS {BS}"\n"
272 
273 NUM_TYPE (complex|integer|logical|real)
274 LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.)
275 KIND {ARGS}
276 CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS}))
277 TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|ENUMERATOR|{CHAR}|TYPE{ARGS}|CLASS{ARGS}|PROCEDURE{ARGS}?)
278 
279 INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")"
280 ATTR_SPEC (EXTERNAL|ALLOCATABLE|DIMENSION{ARGS}|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PROTECTED|PRIVATE|PUBLIC|SAVE|TARGET|NOPASS|PASS{ARGS}?|DEFERRED|NON_OVERRIDABLE|CONTIGUOUS|VOLATILE|VALUE)
281 ACCESS_SPEC (PRIVATE|PUBLIC)
282 LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}((,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})|(,{BS}NAME{BS}"="{BS}"'"(.*)"'"{BS}))?")"
283 /* Assume that attribute statements are almost the same as attributes. */
284 ATTR_STMT {ATTR_SPEC}|DIMENSION|{ACCESS_SPEC}
285 EXTERNAL_STMT (EXTERNAL)
286 
287 CONTAINS CONTAINS
288 PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?
289 SCOPENAME ({ID}{BS}"::"{BS})*
290 
291 %option noyywrap
292 %option stack
293 %option caseless
294 /*%option debug */
295 
296  //---------------------------------------------------------------------------------
297 
298  /** fortran parsing states */
299 %x Subprog
300 %x SubprogPrefix
301 %x Parameterlist
302 %x SubprogBody
303 %x SubprogBodyContains
304 %x Start
305 %x Comment
306 %x Module
307 %x Program
308 %x ModuleBody
309 %x ModuleBodyContains
310 %x AttributeList
311 %x Variable
312 %x Initialization
313 %x ArrayInitializer
314 %x Enum
315 %x Typedef
316 %x TypedefBody
317 %x TypedefBodyContains
318 %x InterfaceBody
319 %x StrIgnore
320 %x String
321 %x Use
322 %x UseOnly
323 %x ModuleProcedure
324 
325 %x Prepass
326 
327  /** comment parsing states */
328 %x DocBlock
329 %x DocBackLine
330 
331 %x BlockData
332 
333 /** prototype parsing */
334 %x Prototype
335 %x PrototypeSubprog
336 %x PrototypeArgs
337 
338 %%
339 
340  /*-----------------------------------------------------------------------------------*/
341 
342 <Prepass>^{BS}[&]*{BS}!.*\n { /* skip lines with just comment. Note code was in free format or has been converted to it */
343  yyextra->lineCountPrepass ++;
344  }
345 <Prepass>^{BS}\n { /* skip empty lines */
346  yyextra->lineCountPrepass ++;
347  }
348 <*>^.*\n { // prepass: look for line continuations
349  yyextra->functionLine = FALSE;
350 
351  DBG_CTX((stderr, "---%s", yytext));
352 
353  int indexStart = getAmpersandAtTheStart(yytext, (int)yyleng);
354  int indexEnd = getAmpOrExclAtTheEnd(yytext, (int)yyleng, '\0');
355  if (indexEnd>=0 && yytext[indexEnd]!='&') //we are only interested in amp
356  {
357  indexEnd=-1;
358  }
359 
360  if (indexEnd<0)
361  { // ----- no ampersand as line continuation
362  if (YY_START == Prepass)
363  { // last line in "continuation"
364 
365  // Only take input after initial ampersand
366  yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1));
367 
368  //printf("BUFFER:%s\n", (const char*)yyextra->inputStringPrepass);
369  pushBuffer(yyscanner,yyextra->inputStringPrepass);
370  yyextra->colNr = 0;
371  yy_pop_state(yyscanner);
372  }
373  else
374  { // simple line
375  yyextra->colNr = 0;
376  REJECT;
377  }
378  }
379  else
380  { // ----- line with continuation
381  if (YY_START != Prepass)
382  {
383  yyextra->comments.clear();
384  yyextra->inputStringPrepass=QCString();
385  yy_push_state(Prepass,yyscanner);
386  }
387 
388  int length = yyextra->inputStringPrepass.length();
389 
390  // Only take input after initial ampersand
391  yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1));
392  yyextra->lineCountPrepass ++;
393 
394  // cut off & and remove following comment if present
395  truncatePrepass(yyscanner,length+indexEnd-(indexStart+1));
396  }
397  }
398 
399 
400  /*------ ignore strings that are not initialization strings */
401 <String>\"|\' { // string ends with next quote without previous backspace
402  if (yytext[0]!=yyextra->stringStartSymbol)
403  {
404  yyextra->colNr -= (int)yyleng;
405  REJECT;
406  } // single vs double quote
407  if (yy_top_state(yyscanner) == Initialization ||
408  yy_top_state(yyscanner) == ArrayInitializer)
409  {
410  yyextra->initializer+=yytext;
411  }
412  yy_pop_state(yyscanner);
413  }
414 <String>. { if (yy_top_state(yyscanner) == Initialization ||
415  yy_top_state(yyscanner) == ArrayInitializer)
416  {
417  yyextra->initializer+=yytext;
418  }
419  }
420 <*>\"|\' { /* string starts */
421  if (YY_START == StrIgnore)
422  { yyextra->colNr -= (int)yyleng;
423  REJECT;
424  }; // ignore in simple yyextra->comments
425  yy_push_state(YY_START,yyscanner);
426  if (yy_top_state(yyscanner) == Initialization ||
427  yy_top_state(yyscanner) == ArrayInitializer)
428  {
429  yyextra->initializer+=yytext;
430  }
431  yyextra->stringStartSymbol=yytext[0]; // single or double quote
432  BEGIN(String);
433  }
434 
435  /*------ ignore simple comment (not documentation yyextra->comments) */
436 
437 <*>"!"/[^<>\n] { if (YY_START == String)
438  { yyextra->colNr -= (int)yyleng;
439  REJECT;
440  } // "!" is ignored in strings
441  // skip comment line (without docu yyextra->comments "!>" "!<" )
442  /* ignore further "!" and ignore yyextra->comments in Strings */
443  if ((YY_START != StrIgnore) && (YY_START != String))
444  {
445  yy_push_state(YY_START,yyscanner);
446  BEGIN(StrIgnore);
447  yyextra->debugStr="*!";
448  DBG_CTX((stderr,"start comment %d\n",yyextra->lineNr));
449  }
450  }
451 <StrIgnore>.?/\n { yy_pop_state(yyscanner); // comment ends with endline character
452  DBG_CTX((stderr,"end comment %d %s\n",yyextra->lineNr,qPrint(yyextra->debugStr)));
453  } // comment line ends
454 <StrIgnore>. { yyextra->debugStr+=yytext; }
455 
456 
457  /*------ use handling ------------------------------------------------------------*/
458 
459 <Start,ModuleBody,SubprogBody>"use"{BS_} {
460  if (YY_START == Start)
461  {
462  addModule(yyscanner);
463  yy_push_state(ModuleBody,yyscanner); //anon program
464  }
465  yy_push_state(Use,yyscanner);
466  }
467 <Use>{ID} {
468  DBG_CTX((stderr,"using dir %s\n",yytext));
469  yyextra->current->name=yytext;
470  yyextra->current->name=yyextra->current->name.lower();
471  yyextra->current->fileName = yyextra->fileName;
472  yyextra->current->section=Entry::USINGDIR_SEC;
473  yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
474  yyextra->current->lang = SrcLangExt_Fortran;
475  yy_pop_state(yyscanner);
476  }
477 <Use>{ID}/, {
478  yyextra->useModuleName=yytext;
479  yyextra->useModuleName=yyextra->useModuleName.lower();
480  }
481 <Use>,{BS}"ONLY" { BEGIN(UseOnly);
482  }
483 <UseOnly>{BS},{BS} {}
484 <UseOnly>{ID} {
485  yyextra->current->name= yyextra->useModuleName+"::"+yytext;
486  yyextra->current->name=yyextra->current->name.lower();
487  yyextra->current->fileName = yyextra->fileName;
488  yyextra->current->section=Entry::USINGDECL_SEC;
489  yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
490  yyextra->current->lang = SrcLangExt_Fortran;
491  }
492 <Use,UseOnly>"\n" {
493  yyextra->colNr -= 1;
494  unput(*yytext);
495  yy_pop_state(yyscanner);
496  }
497 
498  /* INTERFACE definitions */
499 <Start,ModuleBody,SubprogBody>{
500 ^{BS}interface{IDSYM}+ { /* variable with interface prefix */ }
501 ^{BS}interface { yyextra->ifType = IF_SPECIFIC;
502  yy_push_state(InterfaceBody,yyscanner);
503  // do not start a scope here, every
504  // interface body is a scope of its own
505  }
506 
507 ^{BS}abstract{BS_}interface { yyextra->ifType = IF_ABSTRACT;
508  yy_push_state(InterfaceBody,yyscanner);
509  // do not start a scope here, every
510  // interface body is a scope of its own
511  }
512 
513 ^{BS}interface{BS_}{ID}{ARGS}? { yyextra->ifType = IF_GENERIC;
514  yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
515  yy_push_state(InterfaceBody,yyscanner);
516 
517  // extract generic name
518  QCString name = QCString(yytext).stripWhiteSpace();
519  name = name.right(name.length() - 9).stripWhiteSpace().lower();
520  addInterface(yyscanner,name, yyextra->ifType);
521  startScope(yyscanner,yyextra->last_entry.get());
522  }
523 }
524 
525 <InterfaceBody>^{BS}end{BS}interface({BS_}{ID})? {
526  // end scope only if GENERIC interface
527  if (yyextra->ifType == IF_GENERIC)
528  {
529  yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr - 1;
530  }
531  if (yyextra->ifType == IF_GENERIC && !endScope(yyscanner,yyextra->current_root))
532  {
533  yyterminate();
534  }
535  yyextra->ifType = IF_NONE;
536  yy_pop_state(yyscanner);
537  }
538 <InterfaceBody>module{BS}procedure { yy_push_state(YY_START,yyscanner);
539  BEGIN(ModuleProcedure);
540  }
541 <ModuleProcedure>{ID} { if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
542  {
543  addInterface(yyscanner,yytext, yyextra->ifType);
544  startScope(yyscanner,yyextra->last_entry.get());
545  }
546 
547  yyextra->current->section = Entry::FUNCTION_SEC ;
548  yyextra->current->name = yytext;
549  yyextra->moduleProcedures.push_back(yyextra->current);
550  addCurrentEntry(yyscanner,true);
551  }
552 <ModuleProcedure>"\n" { yyextra->colNr -= 1;
553  unput(*yytext);
554  yy_pop_state(yyscanner);
555  }
556 <InterfaceBody>. {}
557 
558  /*-- Contains handling --*/
559 <Start>^{BS}{CONTAINS}/({BS}|\n|!|;) {
560  if (YY_START == Start)
561  {
562  addModule(yyscanner);
563  yy_push_state(ModuleBodyContains,yyscanner); //anon program
564  }
565  }
566 <ModuleBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(ModuleBodyContains); }
567 <SubprogBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(SubprogBodyContains); }
568 <TypedefBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(TypedefBodyContains); }
569 
570  /*------ module handling ------------------------------------------------------------*/
571 <Start>block{BS}data{BS}{ID_} { //
572  yyextra->vtype = V_IGNORE;
573  yy_push_state(BlockData,yyscanner);
574  yyextra->defaultProtection = Public;
575  }
576 <Start>module|program{BS_} { //
577  yyextra->vtype = V_IGNORE;
578  if (yytext[0]=='m' || yytext[0]=='M')
579  {
580  yy_push_state(Module,yyscanner);
581  }
582  else
583  {
584  yy_push_state(Program,yyscanner);
585  }
586  yyextra->defaultProtection = Public;
587  }
588 <BlockData>^{BS}"end"({BS}(block{BS}data)({BS_}{ID})?)?{BS}/(\n|!|;) { // end block data
589  //if (!endScope(yyscanner,yyextra->current_root))
590  // yyterminate();
591  yyextra->defaultProtection = Public;
592  yy_pop_state(yyscanner);
593  }
594 <Start,ModuleBody,ModuleBodyContains>"end"({BS}(module|program)({BS_}{ID})?)?{BS}/(\n|!|;) { // end module
595  resolveModuleProcedures(yyscanner,yyextra->current_root);
596  if (!endScope(yyscanner,yyextra->current_root))
597  {
598  yyterminate();
599  }
600  yyextra->defaultProtection = Public;
601  if (yyextra->global_scope)
602  {
603  if (yyextra->global_scope != INVALID_ENTRY)
604  {
605  yy_push_state(Start,yyscanner);
606  }
607  else
608  {
609  yy_pop_state(yyscanner); // cannot pop artrificial entry
610  }
611  }
612  else
613  {
614  yy_push_state(Start,yyscanner);
615  yyextra->global_scope = INVALID_ENTRY; // signal that the yyextra->global_scope has already been used.
616  }
617  }
618 <Module>{ID} {
619  addModule(yyscanner, QCString(yytext), TRUE);
620  BEGIN(ModuleBody);
621  }
622 <Program>{ID} {
623  addModule(yyscanner, QCString(yytext), FALSE);
624  BEGIN(ModuleBody);
625  }
626 
627  /*------- access specification --------------------------------------------------------------------------*/
628 
629 <ModuleBody>private/{BS}(\n|"!") { yyextra->defaultProtection = Private;
630  yyextra->current->protection = yyextra->defaultProtection ;
631  }
632 <ModuleBody>public/{BS}(\n|"!") { yyextra->defaultProtection = Public;
633  yyextra->current->protection = yyextra->defaultProtection ;
634  }
635 
636  /*------- type definition -------------------------------------------------------------------------------*/
637 
638 <Start,ModuleBody>^{BS}type/[^a-z0-9_] {
639  if (YY_START == Start)
640  {
641  addModule(yyscanner,QCString());
642  yy_push_state(ModuleBody,yyscanner); //anon program
643  }
644 
645  yy_push_state(Typedef,yyscanner);
646  yyextra->current->protection = yyextra->defaultProtection;
647  yyextra->typeProtection = yyextra->defaultProtection;
648  yyextra->typeMode = true;
649  }
650 <Typedef>{
651 {COMMA} {}
652 
653 {BS}"::"{BS} {}
654 
655 abstract {
656  yyextra->current->spec |= Entry::AbstractClass;
657  }
658 extends{ARGS} {
659  QCString basename = extractFromParens(yytext).lower();
660  yyextra->current->extends.push_back(BaseInfo(basename, Public, Normal));
661  }
662 public {
663  yyextra->current->protection = Public;
664  yyextra->typeProtection = Public;
665  }
666 private {
667  yyextra->current->protection = Private;
668  yyextra->typeProtection = Private;
669  }
670 {LANGUAGE_BIND_SPEC} {
671  /* ignored for now */
672  }
673 {ID} { /* type name found */
674  yyextra->current->section = Entry::CLASS_SEC;
675  yyextra->current->spec |= Entry::Struct;
676  yyextra->current->name = yytext;
677  yyextra->current->fileName = yyextra->fileName;
678  yyextra->current->bodyLine = yyextra->lineNr;
679  yyextra->current->startLine = yyextra->lineNr;
680 
681  /* if type is part of a module, mod name is necessary for output */
682  if (yyextra->current_root &&
683  (yyextra->current_root->section == Entry::CLASS_SEC ||
684  yyextra->current_root->section == Entry::NAMESPACE_SEC))
685  {
686  yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name;
687  }
688 
689  addCurrentEntry(yyscanner,true);
690  startScope(yyscanner,yyextra->last_entry.get());
691  BEGIN(TypedefBody);
692  }
693 }
694 
695 <TypedefBodyContains>{ /* Type Bound Procedures */
696 ^{BS}PROCEDURE{ARGS}? {
697  yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
698  }
699 ^{BS}final {
700  yyextra->current->spec |= Entry::Final;
701  yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
702  }
703 ^{BS}generic {
704  yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
705  }
706 {COMMA} {
707  }
708 {ATTR_SPEC} {
709  yyextra->currentModifiers |= QCString(yytext);
710  }
711 {BS}"::"{BS} {
712  }
713 {ID} {
714  QCString name = yytext;
715  yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers;
716  yyextra->current->section = Entry::FUNCTION_SEC;
717  yyextra->current->name = name;
718  yyextra->current->fileName = yyextra->fileName;
719  yyextra->current->bodyLine = yyextra->lineNr;
720  yyextra->current->startLine = yyextra->lineNr;
721  addCurrentEntry(yyscanner,true);
722  }
723 {BS}"=>"[^(\n|\!)]* { /* Specific bindings come after the ID. */
724  QCString args = yytext;
725  yyextra->last_entry->args = args.lower();
726  }
727 "\n" {
728  yyextra->currentModifiers = SymbolModifiers();
729  newLine(yyscanner);
730  yyextra->docBlock.resize(0);
731  }
732 }
733 
734 
735 <TypedefBody,TypedefBodyContains>{
736 ^{BS}"end"{BS}"type"({BS_}{ID})?{BS}/(\n|!|;) { /* end type definition */
737  yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
738  if (!endScope(yyscanner,yyextra->current_root))
739  {
740  yyterminate();
741  }
742  yyextra->typeMode = false;
743  yy_pop_state(yyscanner);
744  }
745 ^{BS}"end"{BS}/(\n|!|;) { /* incorrect end type definition */
746  warn(yyextra->fileName,yyextra->lineNr, "Found 'END' instead of 'END TYPE'");
747  yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
748  if (!endScope(yyscanner,yyextra->current_root))
749  {
750  yyterminate();
751  }
752  yyextra->typeMode = false;
753  yy_pop_state(yyscanner);
754  }
755 }
756 
757  /*------- module/global/typedef variable ---------------------------------------------------*/
758 
759 <SubprogBody,SubprogBodyContains>^{BS}[0-9]*{BS}"end"({BS}{SUBPROG}({BS_}{ID})?)?{BS}/(\n|!|;) {
760  //
761  // ABSTRACT and specific interfaces are stored
762  // in a scope of their own, even if multiple
763  // are group in one INTERFACE/END INTERFACE block.
764  //
765  if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
766  {
767  endScope(yyscanner,yyextra->current_root);
768  yyextra->last_entry->endBodyLine = yyextra->lineNr - 1;
769  }
770  yyextra->current_root->endBodyLine = yyextra->lineNr - 1;
771 
772  if (!endScope(yyscanner,yyextra->current_root))
773  {
774  yyterminate();
775  }
776  yyextra->subrCurrent.pop_back();
777  yyextra->vtype = V_IGNORE;
778  yy_pop_state(yyscanner) ;
779  }
780 <BlockData>{
781 {ID} {
782  }
783 }
784 <Start,ModuleBody,TypedefBody,SubprogBody,Enum>{
785 ^{BS}{TYPE_SPEC}/{SEPARATE} {
786  yyextra->last_enum.reset();
787  if (YY_START == Enum)
788  {
789  yyextra->argType = "@"; // enum marker
790  }
791  else
792  {
793  yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower();
794  }
795  yyextra->current->bodyLine = yyextra->lineNr + 1;
796  yyextra->current->endBodyLine = yyextra->lineNr + yyextra->lineCountPrepass;
797  /* variable declaration starts */
798  if (YY_START == Start)
799  {
800  addModule(yyscanner);
801  yy_push_state(ModuleBody,yyscanner); //anon program
802  }
803  yy_push_state(AttributeList,yyscanner);
804  }
805 {EXTERNAL_STMT}/({BS}"::"|{BS_}{ID}) {
806  /* external can be a "type" or an attribute */
807  if (YY_START == Start)
808  {
809  addModule(yyscanner);
810  yy_push_state(ModuleBody,yyscanner); //anon program
811  }
812  QCString tmp = yytext;
813  yyextra->currentModifiers |= tmp.stripWhiteSpace();
814  yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower();
815  yy_push_state(AttributeList,yyscanner);
816  }
817 {ATTR_STMT}/{BS_}{ID} |
818 {ATTR_STMT}/{BS}"::" {
819  /* attribute statement starts */
820  DBG_CTX((stderr,"5=========> Attribute statement: %s\n", yytext));
821  QCString tmp = yytext;
822  yyextra->currentModifiers |= tmp.stripWhiteSpace();
823  yyextra->argType="";
824  yy_push_state(YY_START,yyscanner);
825  BEGIN( AttributeList ) ;
826  }
827 {ID} {
828  }
829 ^{BS}"type"{BS_}"is"/{BT_} {}
830 ^{BS}"type"{BS}"=" {}
831 ^{BS}"class"{BS_}"is"/{BT_} {}
832 ^{BS}"class"{BS_}"default" {}
833 }
834 <AttributeList>{
835 {COMMA} {}
836 {BS} {}
837 {LANGUAGE_BIND_SPEC} {
838  yyextra->currentModifiers |= yytext;
839  }
840 {ATTR_SPEC}. { /* update yyextra->current yyextra->modifiers when it is an ATTR_SPEC and not a variable name */
841  /* buyyextra->625519 */
842  char chr = yytext[(int)yyleng-1];
843  if (isId(chr))
844  {
845  yyextra->colNr -= (int)yyleng;
846  REJECT;
847  }
848  else
849  {
850  QCString tmp = yytext;
851  tmp = tmp.left(tmp.length() - 1);
852  yyextra->colNr -= 1;
853  unput(yytext[(int)yyleng-1]);
854  yyextra->currentModifiers |= (tmp);
855  }
856  }
857 "::" { /* end attribute list */
858  BEGIN( Variable );
859  }
860 . { /* unknown attribute, consider variable name */
861  //cout<<"start variables, unput "<<*yytext<<endl;
862  yyextra->colNr -= 1;
863  unput(*yytext);
864  BEGIN( Variable );
865  }
866 }
867 
868 <Variable>{BS} {}
869 <Variable>{ID} { /* parse variable declaration */
870  //cout << "5=========> got variable: " << yyextra->argType << "::" << yytext << endl;
871  /* work around for bug in QCString.replace (QCString works) */
872  QCString name=yytext;
873  name = name.lower();
874  /* remember attributes for the symbol */
875  yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers;
876  yyextra->argName= name;
877 
878  yyextra->vtype= V_IGNORE;
879  if (!yyextra->argType.isEmpty() && yyextra->current_root->section!=Entry::FUNCTION_SEC)
880  { // new variable entry
881  yyextra->vtype = V_VARIABLE;
882  yyextra->current->section = Entry::VARIABLE_SEC;
883  yyextra->current->name = yyextra->argName;
884  yyextra->current->type = yyextra->argType;
885  yyextra->current->fileName = yyextra->fileName;
886  yyextra->current->bodyLine = yyextra->lineNr; // used for source reference
887  yyextra->current->startLine = yyextra->lineNr;
888  if (yyextra->argType == "@")
889  {
890  yyextra->current_root->copyToSubEntry(yyextra->current);
891  // add to the scope surrounding the enum (copy!)
892  yyextra->last_enum = yyextra->current;
893  yyextra->current_root->parent()->moveToSubEntryAndRefresh(yyextra->current);
894  initEntry(yyscanner);
895  }
896  else
897  {
898  addCurrentEntry(yyscanner,true);
899  }
900  }
901  else if (!yyextra->argType.isEmpty())
902  { // declaration of parameter list: add type for corr. parameter
903  Argument *parameter = getParameter(yyscanner,yyextra->argName);
904  if (parameter)
905  {
906  yyextra->vtype= V_PARAMETER;
907  if (!yyextra->argType.isNull()) parameter->type=yyextra->argType.stripWhiteSpace();
908  if (!yyextra->docBlock.isNull())
909  {
910  subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
911  }
912  }
913  // save, it may be function return type
914  if (parameter)
915  {
916  yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType;
917  }
918  else
919  {
920  if ((yyextra->current_root->name.lower() == yyextra->argName.lower()) ||
921  (yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName.lower() == yyextra->argName.lower()))
922  {
923  int strt = yyextra->current_root->type.find("function");
924  QCString lft;
925  QCString rght;
926  if (strt != -1)
927  {
928  yyextra->vtype = V_RESULT;
929  lft = "";
930  rght = "";
931  if (strt != 0) lft = yyextra->current_root->type.left(strt).stripWhiteSpace();
932  if ((yyextra->current_root->type.length() - strt - strlen("function"))!= 0)
933  {
934  rght = yyextra->current_root->type.right(yyextra->current_root->type.length() - strt - (int)strlen("function")).stripWhiteSpace();
935  }
936  yyextra->current_root->type = lft;
937  if (rght.length() > 0)
938  {
939  if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
940  yyextra->current_root->type += rght;
941  }
942  if (yyextra->argType.stripWhiteSpace().length() > 0)
943  {
944  if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
945  yyextra->current_root->type += yyextra->argType.stripWhiteSpace();
946  }
947  if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
948  yyextra->current_root->type += "function";
949  if (!yyextra->docBlock.isNull())
950  {
951  subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE);
952  }
953  }
954  else
955  {
956  yyextra->current_root->type += " " + yyextra->argType.stripWhiteSpace();
957  }
958  yyextra->current_root->type = yyextra->current_root->type.stripWhiteSpace();
959  yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->current_root->type;
960  }
961  else
962  {
963  yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType;
964  }
965  }
966  // any accumulated doc for argument should be emptied,
967  // because it is handled other way and this doc can be
968  // unexpectedly passed to the next member.
969  yyextra->current->doc.resize(0);
970  yyextra->current->brief.resize(0);
971  }
972  }
973 <Variable>{ARGS} { /* dimension of the previous entry. */
974  QCString name(yyextra->argName);
975  QCString attr("dimension");
976  attr += yytext;
977  yyextra->modifiers[yyextra->current_root][name.lower().str()] |= attr;
978  }
979 <Variable>{COMMA} { //printf("COMMA: %d<=..<=%d\n", yyextra->colNr-(int)yyleng, yyextra->colNr);
980  // locate !< comment
981  updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
982  }
983 <Variable>{BS}"=" {
984  yy_push_state(YY_START,yyscanner);
985  yyextra->initializer="=";
986  yyextra->initializerScope = yyextra->initializerArrayScope = 0;
987  BEGIN(Initialization);
988  }
989 <Variable>"\n" { yyextra->currentModifiers = SymbolModifiers();
990  yy_pop_state(yyscanner); // end variable declaration list
991  newLine(yyscanner);
992  yyextra->docBlock.resize(0);
993  }
994 <Variable>";".*"\n" { yyextra->currentModifiers = SymbolModifiers();
995  yy_pop_state(yyscanner); // end variable declaration list
996  yyextra->docBlock.resize(0);
997  yyextra->inputStringSemi = " \n"+QCString(yytext+1);
998  yyextra->lineNr--;
999  pushBuffer(yyscanner,yyextra->inputStringSemi);
1000  }
1001 <*>";".*"\n" {
1002  if (YY_START == Variable) REJECT; // Just be on the safe side
1003  if (YY_START == String) REJECT; // ";" ignored in strings
1004  if (YY_START == StrIgnore) REJECT; // ";" ignored in regular yyextra->comments
1005  yyextra->inputStringSemi = " \n"+QCString(yytext+1);
1006  yyextra->lineNr--;
1007  pushBuffer(yyscanner,yyextra->inputStringSemi);
1008  }
1009 
1010 <Initialization,ArrayInitializer>"[" |
1011 <Initialization,ArrayInitializer>"(/" { yyextra->initializer+=yytext;
1012  yyextra->initializerArrayScope++;
1013  BEGIN(ArrayInitializer); // initializer may contain comma
1014  }
1015 <ArrayInitializer>"]" |
1016 <ArrayInitializer>"/)" { yyextra->initializer+=yytext;
1017  yyextra->initializerArrayScope--;
1018  if (yyextra->initializerArrayScope<=0)
1019  {
1020  yyextra->initializerArrayScope = 0; // just in case
1021  BEGIN(Initialization);
1022  }
1023  }
1024 <ArrayInitializer>. { yyextra->initializer+=yytext; }
1025 <Initialization>"(" { yyextra->initializerScope++;
1026  yyextra->initializer+=yytext;
1027  }
1028 <Initialization>")" { yyextra->initializerScope--;
1029  yyextra->initializer+=yytext;
1030  }
1031 <Initialization>{COMMA} { if (yyextra->initializerScope == 0)
1032  {
1033  updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1034  yy_pop_state(yyscanner); // end initialization
1035  if (yyextra->last_enum)
1036  {
1037  yyextra->last_enum->initializer.str(yyextra->initializer.str());
1038  }
1039  else
1040  {
1041  if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str());
1042  }
1043  }
1044  else
1045  {
1046  yyextra->initializer+=", ";
1047  }
1048  }
1049 <Initialization>"\n"|"!" { //|
1050  yy_pop_state(yyscanner); // end initialization
1051  if (yyextra->last_enum)
1052  {
1053  yyextra->last_enum->initializer.str(yyextra->initializer.str());
1054  }
1055  else
1056  {
1057  if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str());
1058  }
1059  yyextra->colNr -= 1;
1060  unput(*yytext);
1061  }
1062 <Initialization>. { yyextra->initializer+=yytext; }
1063 
1064 <*>{BS}"enum"{BS}","{BS}"bind"{BS}"("{BS}"c"{BS}")"{BS} {
1065  if (YY_START == Start)
1066  {
1067  addModule(yyscanner);
1068  yy_push_state(ModuleBody,yyscanner); //anon program
1069  }
1070 
1071  yy_push_state(Enum,yyscanner);
1072  yyextra->current->protection = yyextra->defaultProtection;
1073  yyextra->typeProtection = yyextra->defaultProtection;
1074  yyextra->typeMode = true;
1075 
1076  yyextra->current->spec |= Entry::Struct;
1077  yyextra->current->name.resize(0);
1078  yyextra->current->args.resize(0);
1079  yyextra->current->name.sprintf("@%d",yyextra->anonCount++);
1080 
1081  yyextra->current->section = Entry::ENUM_SEC;
1082  yyextra->current->fileName = yyextra->fileName;
1083  yyextra->current->startLine = yyextra->lineNr;
1084  yyextra->current->bodyLine = yyextra->lineNr;
1085  if ((yyextra->current_root) &&
1086  (yyextra->current_root->section == Entry::CLASS_SEC ||
1087  yyextra->current_root->section == Entry::NAMESPACE_SEC))
1088  {
1089  yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name;
1090  }
1091 
1092  addCurrentEntry(yyscanner,true);
1093  startScope(yyscanner,yyextra->last_entry.get());
1094  BEGIN( Enum ) ;
1095  }
1096 <Enum>"end"{BS}"enum" {
1097  yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
1098  if (!endScope(yyscanner,yyextra->current_root))
1099  {
1100  yyterminate();
1101  }
1102  yyextra->typeMode = false;
1103  yy_pop_state(yyscanner);
1104  }
1105  /*------ fortran subroutine/function handling ------------------------------------------------------------*/
1106  /* Start is initial condition */
1107 
1108 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{TYPE_SPEC}{BS}({PREFIX}{BS_})?/{SUBPROG}{BS_} {
1109  if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1110  {
1111  addInterface(yyscanner,"$interface$", yyextra->ifType);
1112  startScope(yyscanner,yyextra->last_entry.get());
1113  }
1114 
1115  // TYPE_SPEC is for old function style function result
1116  QCString result = QCString(yytext).stripWhiteSpace().lower();
1117  yyextra->current->type = result;
1118  yy_push_state(SubprogPrefix,yyscanner);
1119  }
1120 
1121 <SubprogPrefix>{BS}{SUBPROG}{BS_} {
1122  // Fortran subroutine or function found
1123  yyextra->vtype = V_IGNORE;
1124  QCString result=yytext;
1125  result=result.stripWhiteSpace();
1126  addSubprogram(yyscanner,result);
1127  BEGIN(Subprog);
1128  yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
1129  yyextra->current->startLine = yyextra->lineNr;
1130  }
1131 
1132 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{SUBPROG}{BS_} {
1133  // Fortran subroutine or function found
1134  yyextra->vtype = V_IGNORE;
1135  if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1136  {
1137  addInterface(yyscanner,"$interface$", yyextra->ifType);
1138  startScope(yyscanner,yyextra->last_entry.get());
1139  }
1140 
1141  QCString result = QCString(yytext).stripWhiteSpace();
1142  addSubprogram(yyscanner,result);
1143  yy_push_state(Subprog,yyscanner);
1144  yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
1145  yyextra->current->startLine = yyextra->lineNr;
1146  }
1147 
1148 <Subprog>{BS} { /* ignore white space */ }
1149 <Subprog>{ID} { yyextra->current->name = yytext;
1150  //cout << "1a==========> got " << yyextra->current->type << " " << yytext << " " << yyextra->lineNr << endl;
1151  yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()].returnName = yyextra->current->name.lower();
1152 
1153  if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1154  {
1155  yyextra->current_root->name = substitute(
1156  yyextra->current_root->name, "$interface$", yytext);
1157  }
1158 
1159  BEGIN(Parameterlist);
1160  }
1161 <Parameterlist>"(" { yyextra->current->args = "("; }
1162 <Parameterlist>")" {
1163  yyextra->current->args += ")";
1164  yyextra->current->args = removeRedundantWhiteSpace(yyextra->current->args);
1165  addCurrentEntry(yyscanner,true);
1166  startScope(yyscanner,yyextra->last_entry.get());
1167  BEGIN(SubprogBody);
1168  }
1169 <Parameterlist>{COMMA}|{BS} { yyextra->current->args += yytext;
1170  const CommentInPrepass *c = locatePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1171  if (c)
1172  {
1173  if (!yyextra->current->argList.empty())
1174  {
1175  yyextra->current->argList.back().docs = c->str;
1176  }
1177  }
1178  }
1179 <Parameterlist>{ID} {
1180  //yyextra->current->type not yet available
1181  QCString param = yytext;
1182  // std::cout << "3=========> got parameter " << param << "\n";
1183  yyextra->current->args += param;
1184  Argument arg;
1185  arg.name = param;
1186  yyextra->current->argList.push_back(arg);
1187  }
1188 <Parameterlist>{NOARGS} {
1189  newLine(yyscanner);
1190  //printf("3=========> without parameterlist \n");
1191  addCurrentEntry(yyscanner,true);
1192  startScope(yyscanner,yyextra->last_entry.get());
1193  BEGIN(SubprogBody);
1194  }
1195 <SubprogBody>result{BS}\({BS}{ID} {
1196  if (yyextra->functionLine)
1197  {
1198  QCString result= yytext;
1199  result= result.right(result.length()-result.find("(")-1);
1200  result= result.stripWhiteSpace();
1201  yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName = result;
1202  }
1203  //cout << "=====> got result " << result << endl;
1204  }
1205 
1206  /*---- documentation yyextra->comments --------------------------------------------------------------------*/
1207 
1208 <Variable,SubprogBody,ModuleBody,TypedefBody,TypedefBodyContains>"!<" { /* backward docu comment */
1209  if (yyextra->vtype != V_IGNORE)
1210  {
1211  yyextra->current->docLine = yyextra->lineNr;
1212  yyextra->docBlockJavaStyle = FALSE;
1213  yyextra->docBlock.resize(0);
1214  yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF);
1215  startCommentBlock(yyscanner,TRUE);
1216  yy_push_state(DocBackLine,yyscanner);
1217  }
1218  else
1219  {
1220  /* handle out of place !< comment as a normal comment */
1221  if (YY_START == String)
1222  {
1223  yyextra->colNr -= (int)yyleng;
1224  REJECT;
1225  } // "!" is ignored in strings
1226  // skip comment line (without docu yyextra->comments "!>" "!<" )
1227  /* ignore further "!" and ignore yyextra->comments in Strings */
1228  if ((YY_START != StrIgnore) && (YY_START != String))
1229  {
1230  yy_push_state(YY_START,yyscanner);
1231  BEGIN(StrIgnore);
1232  yyextra->debugStr="*!";
1233  }
1234  }
1235  }
1236 <DocBackLine>.* { // contents of yyextra->current comment line
1237  yyextra->docBlock+=yytext;
1238  }
1239 <DocBackLine>"\n"{BS}"!"("<"|"!"+) { // comment block (next line is also comment line)
1240  yyextra->docBlock+="\n"; // \n is necessary for lists
1241  newLine(yyscanner);
1242  }
1243 <DocBackLine>"\n" { // comment block ends at the end of this line
1244  //cout <<"3=========> comment block : "<< yyextra->docBlock << endl;
1245  yyextra->colNr -= 1;
1246  unput(*yytext);
1247  if (yyextra->vtype == V_VARIABLE)
1248  {
1249  std::shared_ptr<Entry> tmp_entry = yyextra->current;
1250  // temporarily switch to the previous entry
1251  if (yyextra->last_enum)
1252  {
1253  yyextra->current = yyextra->last_enum;
1254  }
1255  else
1256  {
1257  yyextra->current = yyextra->last_entry;
1258  }
1259  handleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1260  // switch back
1261  yyextra->current = tmp_entry;
1262  }
1263  else if (yyextra->vtype == V_PARAMETER)
1264  {
1265  subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1266  }
1267  else if (yyextra->vtype == V_RESULT)
1268  {
1269  subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE);
1270  }
1271  yy_pop_state(yyscanner);
1272  yyextra->docBlock.resize(0);
1273  }
1274 
1275 <Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains,TypedefBodyContains,Enum>"!>" {
1276  yy_push_state(YY_START,yyscanner);
1277  yyextra->current->docLine = yyextra->lineNr;
1278  yyextra->docBlockJavaStyle = FALSE;
1279  if (YY_START==SubprogBody) yyextra->docBlockInBody = TRUE;
1280  yyextra->docBlock.resize(0);
1281  yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF);
1282  startCommentBlock(yyscanner,TRUE);
1283  BEGIN(DocBlock);
1284  //cout << "start DocBlock " << endl;
1285  }
1286 
1287 <DocBlock>.* { // contents of yyextra->current comment line
1288  yyextra->docBlock+=yytext;
1289  }
1290 <DocBlock>"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line)
1291  yyextra->docBlock+="\n"; // \n is necessary for lists
1292  newLine(yyscanner);
1293  }
1294 <DocBlock>"\n" { // comment block ends at the end of this line
1295  //cout <<"3=========> comment block : "<< yyextra->docBlock << endl;
1296  yyextra->colNr -= 1;
1297  unput(*yytext);
1298  handleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1299  yy_pop_state(yyscanner);
1300  }
1301 
1302  /*-----Prototype parsing -------------------------------------------------------------------------*/
1303 <Prototype>{BS}{SUBPROG}{BS_} {
1304  BEGIN(PrototypeSubprog);
1305  }
1306 <Prototype,PrototypeSubprog>{BS}{SCOPENAME}?{BS}{ID} {
1307  yyextra->current->name = QCString(yytext).lower();
1308  yyextra->current->name.stripWhiteSpace();
1309  BEGIN(PrototypeArgs);
1310  }
1311 <PrototypeArgs>{
1312 "("|")"|","|{BS_} { yyextra->current->args += yytext; }
1313 {ID} { yyextra->current->args += yytext;
1314  Argument a;
1315  a.name = QCString(yytext).lower();
1316  yyextra->current->argList.push_back(a);
1317  }
1318 }
1319 
1320  /*------------------------------------------------------------------------------------------------*/
1321 
1322 <*>"\n" {
1323  newLine(yyscanner);
1324  //if (yyextra->debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << yyextra->debugStr << " state: " <<YY_START << endl;
1325  yyextra->debugStr="";
1326  }
1327 
1328 
1329  /*---- error: EOF in wrong state --------------------------------------------------------------------*/
1330 
1331 <*><<EOF>> {
1332  if (yyextra->parsingPrototype)
1333  {
1334  yyterminate();
1335  }
1336  else if ( yyextra->includeStackPtr <= 0 )
1337  {
1338  if (YY_START!=INITIAL && YY_START!=Start)
1339  {
1340  DBG_CTX((stderr,"==== Error: EOF reached in wrong state (end missing)"));
1341  scanner_abort(yyscanner);
1342  }
1343  yyterminate();
1344  }
1345  else
1346  {
1347  popBuffer(yyscanner);
1348  }
1349  }
1350 <*>{LOG_OPER} { // Fortran logical comparison keywords
1351  }
1352 <*>. {
1353  //yyextra->debugStr+=yytext;
1354  //printf("I:%c\n", *yytext);
1355  } // ignore remaining text
1356 
1357  /**********************************************************************************/
1358  /**********************************************************************************/
1359  /**********************************************************************************/
1360 %%
1361 //----------------------------------------------------------------------------
1362 
1363 static void newLine(yyscan_t yyscanner)
1364 {
1365  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1366  yyextra->lineNr++;
1367  yyextra->lineNr+=yyextra->lineCountPrepass;
1368  yyextra->lineCountPrepass=0;
1369  yyextra->comments.clear();
1370 }
1371 
1372 static const CommentInPrepass *locatePrepassComment(yyscan_t yyscanner,int from, int to)
1373 {
1374  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1375  //printf("Locate %d-%d\n", from, to);
1376  for (const auto &cip : yyextra->comments)
1377  { // todo: optimize
1378  int c = cip.column;
1379  //printf("Candidate %d\n", c);
1380  if (c>=from && c<=to)
1381  {
1382  // comment for previous variable or parameter
1383  return &cip;
1384  }
1385  }
1386  return 0;
1387 }
1388 
1389 static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to)
1390 {
1391  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1392  const CommentInPrepass *c = locatePrepassComment(yyscanner,from, to);
1393  if (c && yyextra->vtype == V_VARIABLE)
1394  {
1395  yyextra->last_entry->brief = c->str;
1396  }
1397  else if (c && yyextra->vtype == V_PARAMETER)
1398  {
1399  Argument *parameter = getParameter(yyscanner,yyextra->argName);
1400  if (parameter) parameter->docs = c->str;
1401  }
1402 }
1403 
1404 static int getAmpersandAtTheStart(const char *buf, int length)
1405 {
1406  for(int i=0; i<length; i++)
1407  {
1408  switch(buf[i])
1409  {
1410  case ' ':
1411  case '\t':
1412  break;
1413  case '&':
1414  return i;
1415  default:
1416  return -1;
1417  }
1418  }
1419  return -1;
1420 }
1421 
1422 /* Returns ampersand index, comment start index or -1 if neither exist.*/
1423 static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch)
1424 {
1425  // Avoid ampersands in string and yyextra->comments
1426  int parseState = Start;
1427  char quoteSymbol = 0;
1428  int ampIndex = -1;
1429  int commentIndex = -1;
1430  quoteSymbol = ch;
1431  if (ch != '\0') parseState = String;
1432 
1433  for(int i=0; i<length && parseState!=Comment; i++)
1434  {
1435  // When in string, skip backslashes
1436  // Legacy code, not sure whether this is correct?
1437  if (parseState==String)
1438  {
1439  if (buf[i]=='\\') i++;
1440  }
1441 
1442  switch(buf[i])
1443  {
1444  case '\'':
1445  case '"':
1446  // Close string, if quote symbol matches.
1447  // Quote symbol is set iff parseState==String
1448  if (buf[i]==quoteSymbol)
1449  {
1450  parseState = Start;
1451  quoteSymbol = 0;
1452  }
1453  // Start new string, if not already in string or comment
1454  else if (parseState==Start)
1455  {
1456  parseState = String;
1457  quoteSymbol = buf[i];
1458  }
1459  ampIndex = -1; // invalidate prev ampersand
1460  break;
1461  case '!':
1462  // When in string or comment, ignore exclamation mark
1463  if (parseState==Start)
1464  {
1465  parseState = Comment;
1466  commentIndex = i;
1467  }
1468  break;
1469  case ' ': // ignore whitespace
1470  case '\t':
1471  case '\n': // this may be at the end of line
1472  break;
1473  case '&':
1474  ampIndex = i;
1475  break;
1476  default:
1477  ampIndex = -1; // invalidate prev ampersand
1478  }
1479  }
1480 
1481  if (ampIndex>=0)
1482  return ampIndex;
1483  else
1484  return commentIndex;
1485 }
1486 
1487 /* Although yyextra->comments at the end of continuation line are grabbed by this function,
1488 * we still do not know how to use them later in parsing.
1489 */
1490 void truncatePrepass(yyscan_t yyscanner,int index)
1491 {
1492  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1493  int length = yyextra->inputStringPrepass.length();
1494  for (int i=index+1; i<length; i++) {
1495  if (yyextra->inputStringPrepass[i]=='!' && i<length-1 && yyextra->inputStringPrepass[i+1]=='<') { // save comment
1496  yyextra->comments.emplace_back(index, yyextra->inputStringPrepass.right(length-i-2));
1497  }
1498  }
1499  yyextra->inputStringPrepass.truncate(index);
1500 }
1501 
1502 /* This function assumes that contents has at least size=length+1 */
1503 static void insertCharacter(char *contents, int length, int pos, char c)
1504 {
1505  // shift tail by one character
1506  for(int i=length; i>pos; i--)
1507  contents[i]=contents[i-1];
1508  // set the character
1509  contents[pos] = c;
1510 }
1511 
1512 /* change yyextra->comments and bring line continuation character to previous line */
1513 /* also used to set continuation marks in case of fortran code usage, done here as it is quite complicated code */
1514 const char* prepassFixedForm(const char* contents, int *hasContLine)
1515 {
1516  int column=0;
1517  int prevLineLength=0;
1518  int prevLineAmpOrExclIndex=-1;
1519  int skipped = 0;
1520  char prevQuote = '\0';
1521  char thisQuote = '\0';
1522  bool emptyLabel=TRUE;
1523  bool commented=FALSE;
1524  bool inSingle=FALSE;
1525  bool inDouble=FALSE;
1526  bool inBackslash=FALSE;
1527  bool fullCommentLine=TRUE;
1528  bool artificialComment=FALSE;
1529  bool spaces=TRUE;
1530  int newContentsSize = (int)strlen(contents)+3; // \000, \n (when necessary) and one spare character (to avoid reallocation)
1531  char* newContents = (char*)malloc(newContentsSize);
1532  int curLine = 1;
1533 
1534  int j = -1;
1535  for(int i=0;;i++) {
1536  column++;
1537  char c = contents[i];
1538  if (artificialComment && c != '\n')
1539  {
1540  if (c == '!' && spaces)
1541  {
1542  newContents[j++] = c;
1543  artificialComment = FALSE;
1544  spaces = FALSE;
1545  skipped = 0;
1546  continue;
1547  }
1548  else if (c == ' ' || c == '\t') continue;
1549  else
1550  {
1551  spaces = FALSE;
1552  skipped++;
1553  continue;
1554  }
1555  }
1556 
1557  j++;
1558  if (j>=newContentsSize-3) { // check for spare characters, which may be eventually used below (by & and '! ')
1559  newContents = (char*)realloc(newContents, newContentsSize+1000);
1560  newContentsSize = newContentsSize+1000;
1561  }
1562 
1563  switch(c) {
1564  case '\n':
1565  if (!fullCommentLine)
1566  {
1567  prevLineLength=column;
1568  prevLineAmpOrExclIndex=getAmpOrExclAtTheEnd(&contents[i-prevLineLength+1], prevLineLength,prevQuote);
1569  if (prevLineAmpOrExclIndex == -1) prevLineAmpOrExclIndex = column - 1;
1570  if (skipped)
1571  {
1572  prevLineAmpOrExclIndex = -1;
1573  skipped = 0;
1574  }
1575  }
1576  else
1577  {
1578  prevLineLength+=column;
1579  /* Even though a full comment line is not really a comment line it can be seen as one. An empty line is also seen as a comment line (small bonus) */
1580  if (hasContLine)
1581  {
1582  hasContLine[curLine - 1] = 1;
1583  }
1584  }
1585  artificialComment=FALSE;
1586  spaces=TRUE;
1587  fullCommentLine=TRUE;
1588  column=0;
1589  emptyLabel=TRUE;
1590  commented=FALSE;
1591  newContents[j]=c;
1592  prevQuote = thisQuote;
1593  curLine++;
1594  break;
1595  case ' ':
1596  case '\t':
1597  newContents[j]=c;
1598  break;
1599  case '\000':
1600  if (hasContLine)
1601  {
1602  free(newContents);
1603  return NULL;
1604  }
1605  newContents[j]='\000';
1606  newContentsSize = (int)strlen(newContents);
1607  if (newContents[newContentsSize - 1] != '\n')
1608  {
1609  // to be on the safe side
1610  newContents = (char*)realloc(newContents, newContentsSize+2);
1611  newContents[newContentsSize] = '\n';
1612  newContents[newContentsSize + 1] = '\000';
1613  }
1614  return newContents;
1615  case '"':
1616  case '\'':
1617  case '\\':
1618  if ((column <= fixedCommentAfter) && (column!=6) && !commented)
1619  {
1620  // we have some special cases in respect to strings and escaped string characters
1621  fullCommentLine=FALSE;
1622  newContents[j]=c;
1623  if (c == '\\')
1624  {
1625  inBackslash = !inBackslash;
1626  break;
1627  }
1628  else if (c == '\'')
1629  {
1630  if (!inDouble)
1631  {
1632  inSingle = !inSingle;
1633  if (inSingle) thisQuote = c;
1634  else thisQuote = '\0';
1635  }
1636  break;
1637  }
1638  else if (c == '"')
1639  {
1640  if (!inSingle)
1641  {
1642  inDouble = !inDouble;
1643  if (inDouble) thisQuote = c;
1644  else thisQuote = '\0';
1645  }
1646  break;
1647  }
1648  }
1649  inBackslash = FALSE;
1650  // fallthrough
1651  case '#':
1652  case 'C':
1653  case 'c':
1654  case '*':
1655  case '!':
1656  if ((column <= fixedCommentAfter) && (column!=6))
1657  {
1658  emptyLabel=FALSE;
1659  if (column==1)
1660  {
1661  newContents[j]='!';
1662  commented = TRUE;
1663  }
1664  else if ((c == '!') && !inDouble && !inSingle)
1665  {
1666  newContents[j]=c;
1667  commented = TRUE;
1668  }
1669  else
1670  {
1671  if (!commented) fullCommentLine=FALSE;
1672  newContents[j]=c;
1673  }
1674  break;
1675  }
1676  // fallthrough
1677  default:
1678  if (!commented && (column < 6) && ((c - '0') >= 0) && ((c - '0') <= 9))
1679  { // remove numbers, i.e. labels from first 5 positions.
1680  newContents[j]=' ';
1681  }
1682  else if (column==6 && emptyLabel)
1683  { // continuation
1684  if (!commented) fullCommentLine=FALSE;
1685  if (c != '0')
1686  { // 0 not allowed as continuation character, see f95 standard paragraph 3.3.2.3
1687  newContents[j]=' ';
1688 
1689  if (prevLineAmpOrExclIndex==-1)
1690  { // add & just before end of previous line
1691  /* first line is not a continuation line in code, just in snippets etc. */
1692  if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-1, '&');
1693  j++;
1694  }
1695  else
1696  { // add & just before end of previous line comment
1697  /* first line is not a continuation line in code, just in snippets etc. */
1698  if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-prevLineLength+prevLineAmpOrExclIndex+skipped, '&');
1699  skipped = 0;
1700  j++;
1701  }
1702  if (hasContLine)
1703  {
1704  hasContLine[curLine - 1] = 1;
1705  }
1706  }
1707  else
1708  {
1709  newContents[j]=c; // , just handle like space
1710  }
1711  prevLineLength=0;
1712  }
1713  else if ((column > fixedCommentAfter) && !commented)
1714  {
1715  // first non commented non blank character after position fixedCommentAfter
1716  if (c == '&')
1717  {
1718  newContents[j]=' ';
1719  }
1720  else if (c != '!')
1721  {
1722  // I'm not a possible start of doxygen comment
1723  newContents[j]=' ';
1724  artificialComment = TRUE;
1725  spaces=TRUE;
1726  skipped = 0;
1727  }
1728  else
1729  {
1730  newContents[j]=c;
1731  commented = TRUE;
1732  }
1733  }
1734  else
1735  {
1736  if (!commented) fullCommentLine=FALSE;
1737  newContents[j]=c;
1738  emptyLabel=FALSE;
1739  }
1740  break;
1741  }
1742  }
1743 
1744  if (hasContLine)
1745  {
1746  free(newContents);
1747  return NULL;
1748  }
1749  newContentsSize = (int)strlen(newContents);
1750  if (newContents[newContentsSize - 1] != '\n')
1751  {
1752  // to be on the safe side
1753  newContents = (char*)realloc(newContents, newContentsSize+2);
1754  newContents[newContentsSize] = '\n';
1755  newContents[newContentsSize + 1] = '\000';
1756  }
1757  return newContents;
1758 }
1759 
1760 static void pushBuffer(yyscan_t yyscanner,const QCString &buffer)
1761 {
1762  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1763  if (yyextra->includeStackCnt <= yyextra->includeStackPtr)
1764  {
1765  yyextra->includeStackCnt++;
1766  yyextra->includeStack = (YY_BUFFER_STATE *)realloc(yyextra->includeStack, yyextra->includeStackCnt * sizeof(YY_BUFFER_STATE));
1767  }
1768  yyextra->includeStack[yyextra->includeStackPtr++] = YY_CURRENT_BUFFER;
1769  yy_switch_to_buffer(yy_scan_string(buffer.data(),yyscanner),yyscanner);
1770 
1771  DBG_CTX((stderr, "--PUSH--%s", qPrint(buffer)));
1772 }
1773 
1774 static void popBuffer(yyscan_t yyscanner)
1775 {
1776  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1777  DBG_CTX((stderr, "--POP--"));
1778  yyextra->includeStackPtr --;
1779  yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
1780  yy_switch_to_buffer( yyextra->includeStack[yyextra->includeStackPtr], yyscanner );
1781 }
1782 
1783 /** used to copy entry to an interface module procedure */
1784 static void copyEntry(std::shared_ptr<Entry> dest, const std::shared_ptr<Entry> &src)
1785 {
1786  dest->type = src->type;
1787  dest->fileName = src->fileName;
1788  dest->startLine = src->startLine;
1789  dest->bodyLine = src->bodyLine;
1790  dest->endBodyLine = src->endBodyLine;
1791  dest->args = src->args;
1792  dest->argList = src->argList;
1793  dest->doc = src->doc;
1794  dest->brief = src->brief;
1795 }
1796 
1797 /** fill empty interface module procedures with info from
1798  corresponding module subprogs
1799 
1800  TODO: handle procedures in used modules
1801 */
1802 void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root)
1803 {
1804  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1805  for (const auto &ce1 : yyextra->moduleProcedures)
1806  {
1807  // check all entries in this module
1808  for (const auto &ce2 : current_root->children())
1809  {
1810  if (ce1->name == ce2->name)
1811  {
1812  copyEntry(ce1, ce2);
1813  }
1814  } // for procedures in yyextra->current module
1815  } // for all interface module procedures
1816  yyextra->moduleProcedures.clear();
1817 }
1818 
1819 /*! Extracts string which resides within parentheses of provided string. */
1820 static QCString extractFromParens(const QCString &name)
1821 {
1822  QCString extracted = name;
1823  int start = extracted.find("(");
1824  if (start != -1)
1825  {
1826  extracted.remove(0, start+1);
1827  }
1828  int end = extracted.findRev(")");
1829  if (end != -1)
1830  {
1831  int length = extracted.length();
1832  extracted.remove(end, length);
1833  }
1834  extracted = extracted.stripWhiteSpace();
1835 
1836  return extracted;
1837 }
1838 
1839 /*! remove useless spaces from bind statement */
1840 static QCString extractBind(const QCString &name)
1841 {
1842  QCString parensPart = extractFromParens(name);
1843  if (parensPart.length() == 1)
1844  {
1845  return "bind(C)";
1846  }
1847  else
1848  {
1849  //strip 'c'
1850  parensPart = parensPart.mid(1).stripWhiteSpace();
1851  // strip ','
1852  parensPart = parensPart.mid(1).stripWhiteSpace();
1853  // name part
1854  parensPart = parensPart.mid(4).stripWhiteSpace();
1855  // = part
1856  parensPart = parensPart.mid(1).stripWhiteSpace();
1857 
1858  return "bind(C, name=" + parensPart + ")";
1859  }
1860 }
1861 
1862 /*! Adds passed yyextra->modifiers to these yyextra->modifiers.*/
1863 SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs)
1864 {
1865  if (mdfs.protection!=NONE_P) protection = mdfs.protection;
1866  if (mdfs.direction!=NONE_D) direction = mdfs.direction;
1867  optional |= mdfs.optional;
1868  if (!mdfs.dimension.isNull()) dimension = mdfs.dimension;
1869  allocatable |= mdfs.allocatable;
1870  external |= mdfs.external;
1871  intrinsic |= mdfs.intrinsic;
1872  protect |= mdfs.protect;
1873  parameter |= mdfs.parameter;
1874  pointer |= mdfs.pointer;
1875  target |= mdfs.target;
1876  save |= mdfs.save;
1877  deferred |= mdfs.deferred;
1878  nonoverridable |= mdfs.nonoverridable;
1879  nopass |= mdfs.nopass;
1880  pass |= mdfs.pass;
1881  passVar = mdfs.passVar;
1882  bindVar = mdfs.bindVar;
1883  contiguous |= mdfs.contiguous;
1884  volat |= mdfs.volat;
1885  value |= mdfs.value;
1886  return *this;
1887 }
1888 
1889 /*! Extracts and adds passed modifier to these yyextra->modifiers.*/
1890 SymbolModifiers& SymbolModifiers::operator|=(QCString mdfStringArg)
1891 {
1892  QCString mdfString = mdfStringArg.lower();
1893  SymbolModifiers newMdf;
1894 
1895  if (mdfString.find("dimension")==0)
1896  {
1897  newMdf.dimension=mdfString;
1898  }
1899  else if (mdfString.contains("intent"))
1900  {
1901  QCString tmp = extractFromParens(mdfString);
1902  bool isin = tmp.contains("in");
1903  bool isout = tmp.contains("out");
1904  if (isin && isout) newMdf.direction = SymbolModifiers::INOUT;
1905  else if (isin) newMdf.direction = SymbolModifiers::IN;
1906  else if (isout) newMdf.direction = SymbolModifiers::OUT;
1907  }
1908  else if (mdfString=="public")
1909  {
1910  newMdf.protection = SymbolModifiers::PUBLIC;
1911  }
1912  else if (mdfString=="private")
1913  {
1914  newMdf.protection = SymbolModifiers::PRIVATE;
1915  }
1916  else if (mdfString=="protected")
1917  {
1918  newMdf.protect = TRUE;
1919  }
1920  else if (mdfString=="optional")
1921  {
1922  newMdf.optional = TRUE;
1923  }
1924  else if (mdfString=="allocatable")
1925  {
1926  newMdf.allocatable = TRUE;
1927  }
1928  else if (mdfString=="external")
1929  {
1930  newMdf.external = TRUE;
1931  }
1932  else if (mdfString=="intrinsic")
1933  {
1934  newMdf.intrinsic = TRUE;
1935  }
1936  else if (mdfString=="parameter")
1937  {
1938  newMdf.parameter = TRUE;
1939  }
1940  else if (mdfString=="pointer")
1941  {
1942  newMdf.pointer = TRUE;
1943  }
1944  else if (mdfString=="target")
1945  {
1946  newMdf.target = TRUE;
1947  }
1948  else if (mdfString=="save")
1949  {
1950  newMdf.save = TRUE;
1951  }
1952  else if (mdfString=="nopass")
1953  {
1954  newMdf.nopass = TRUE;
1955  }
1956  else if (mdfString=="deferred")
1957  {
1958  newMdf.deferred = TRUE;
1959  }
1960  else if (mdfString=="non_overridable")
1961  {
1962  newMdf.nonoverridable = TRUE;
1963  }
1964  else if (mdfString=="contiguous")
1965  {
1966  newMdf.contiguous = TRUE;
1967  }
1968  else if (mdfString=="volatile")
1969  {
1970  newMdf.volat = TRUE;
1971  }
1972  else if (mdfString=="value")
1973  {
1974  newMdf.value = TRUE;
1975  }
1976  else if (mdfString.contains("pass"))
1977  {
1978  newMdf.pass = TRUE;
1979  if (mdfString.contains("("))
1980  newMdf.passVar = extractFromParens(mdfString);
1981  else
1982  newMdf.passVar = "";
1983  }
1984  else if (mdfString.startsWith("bind"))
1985  {
1986  // we need here the original string as we want to don't want to have the lowercase name between the quotes of the name= part
1987  newMdf.bindVar = extractBind(mdfStringArg);
1988  }
1989 
1990  (*this) |= newMdf;
1991  return *this;
1992 }
1993 
1994 /*! For debugging purposes. */
1995 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs)
1996 //{
1997 // out<<mdfs.protection<<", "<<mdfs.direction<<", "<<mdfs.optional<<
1998 // ", "<<(mdfs.dimension.isNull() ? "" : mdfs.dimension.latin1())<<
1999 // ", "<<mdfs.allocatable<<", "<<mdfs.external<<", "<<mdfs.intrinsic;
2000 //
2001 // return out;
2002 //}
2003 
2004 /*! Find argument with given name in \a subprog entry. */
2005 static Argument *findArgument(Entry* subprog, QCString name, bool byTypeName = FALSE)
2006 {
2007  QCString cname(name.lower());
2008  for (Argument &arg : subprog->argList)
2009  {
2010  if ((!byTypeName && arg.name.lower() == cname) ||
2011  (byTypeName && arg.type.lower() == cname)
2012  )
2013  {
2014  return &arg;
2015  }
2016  }
2017  return 0;
2018 }
2019 
2020 
2021 /*! Apply yyextra->modifiers stored in \a mdfs to the \a typeName string. */
2022 static QCString applyModifiers(QCString typeName, const SymbolModifiers& mdfs)
2023 {
2024  if (!mdfs.dimension.isNull())
2025  {
2026  if (!typeName.isEmpty()) typeName += ", ";
2027  typeName += mdfs.dimension;
2028  }
2029  if (mdfs.direction!=SymbolModifiers::NONE_D)
2030  {
2031  if (!typeName.isEmpty()) typeName += ", ";
2032  typeName += directionStrs[mdfs.direction];
2033  }
2034  if (mdfs.optional)
2035  {
2036  if (!typeName.isEmpty()) typeName += ", ";
2037  typeName += "optional";
2038  }
2039  if (mdfs.allocatable)
2040  {
2041  if (!typeName.isEmpty()) typeName += ", ";
2042  typeName += "allocatable";
2043  }
2044  if (mdfs.external)
2045  {
2046  if (!typeName.contains("external"))
2047  {
2048  if (!typeName.isEmpty()) typeName += ", ";
2049  typeName += "external";
2050  }
2051  }
2052  if (mdfs.intrinsic)
2053  {
2054  if (!typeName.isEmpty()) typeName += ", ";
2055  typeName += "intrinsic";
2056  }
2057  if (mdfs.parameter)
2058  {
2059  if (!typeName.isEmpty()) typeName += ", ";
2060  typeName += "parameter";
2061  }
2062  if (mdfs.pointer)
2063  {
2064  if (!typeName.isEmpty()) typeName += ", ";
2065  typeName += "pointer";
2066  }
2067  if (mdfs.target)
2068  {
2069  if (!typeName.isEmpty()) typeName += ", ";
2070  typeName += "target";
2071  }
2072  if (mdfs.save)
2073  {
2074  if (!typeName.isEmpty()) typeName += ", ";
2075  typeName += "save";
2076  }
2077  if (mdfs.deferred)
2078  {
2079  if (!typeName.isEmpty()) typeName += ", ";
2080  typeName += "deferred";
2081  }
2082  if (mdfs.nonoverridable)
2083  {
2084  if (!typeName.isEmpty()) typeName += ", ";
2085  typeName += "non_overridable";
2086  }
2087  if (mdfs.nopass)
2088  {
2089  if (!typeName.isEmpty()) typeName += ", ";
2090  typeName += "nopass";
2091  }
2092  if (mdfs.pass)
2093  {
2094  if (!typeName.isEmpty()) typeName += ", ";
2095  typeName += "pass";
2096  if (!mdfs.passVar.isEmpty())
2097  typeName += "(" + mdfs.passVar + ")";
2098  }
2099  if (!mdfs.bindVar.isEmpty())
2100  {
2101  if (!typeName.isEmpty()) typeName += ", ";
2102  typeName += mdfs.bindVar;
2103  }
2104  if (mdfs.protection == SymbolModifiers::PUBLIC)
2105  {
2106  if (!typeName.isEmpty()) typeName += ", ";
2107  typeName += "public";
2108  }
2109  else if (mdfs.protection == SymbolModifiers::PRIVATE)
2110  {
2111  if (!typeName.isEmpty()) typeName += ", ";
2112  typeName += "private";
2113  }
2114  if (mdfs.protect)
2115  {
2116  if (!typeName.isEmpty()) typeName += ", ";
2117  typeName += "protected";
2118  }
2119  if (mdfs.contiguous)
2120  {
2121  if (!typeName.isEmpty()) typeName += ", ";
2122  typeName += "contiguous";
2123  }
2124  if (mdfs.volat)
2125  {
2126  if (!typeName.isEmpty()) typeName += ", ";
2127  typeName += "volatile";
2128  }
2129  if (mdfs.value)
2130  {
2131  if (!typeName.isEmpty()) typeName += ", ";
2132  typeName += "value";
2133  }
2134 
2135  return typeName;
2136 }
2137 
2138 /*! Apply yyextra->modifiers stored in \a mdfs to the \a arg argument. */
2139 static void applyModifiers(Argument *arg, const SymbolModifiers& mdfs)
2140 {
2141  QCString tmp = arg->type;
2142  arg->type = applyModifiers(tmp, mdfs);
2143 }
2144 
2145 /*! Apply yyextra->modifiers stored in \a mdfs to the \a ent entry. */
2146 static void applyModifiers(Entry *ent, const SymbolModifiers& mdfs)
2147 {
2148  QCString tmp = ent->type;
2149  ent->type = applyModifiers(tmp, mdfs);
2150 
2151  if (mdfs.protection == SymbolModifiers::PUBLIC)
2152  ent->protection = Public;
2153  else if (mdfs.protection == SymbolModifiers::PRIVATE)
2154  ent->protection = Private;
2155 }
2156 
2157 /*! Starts the new scope in fortran program. Consider using this function when
2158  * starting module, interface, function or other program block.
2159  * \see endScope()
2160  */
2161 static void startScope(yyscan_t yyscanner,Entry *scope)
2162 {
2163  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2164  //cout<<"start scope: "<<scope->name<<endl;
2165  yyextra->current_root= scope; /* start substructure */
2166 
2167  yyextra->modifiers.insert(std::make_pair(scope, std::map<std::string,SymbolModifiers>()));
2168 }
2169 
2170 /*! Ends scope in fortran program: may update subprogram arguments or module variable attributes.
2171  * \see startScope()
2172  */
2173 static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot)
2174 {
2175  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2176  if (yyextra->global_scope == scope)
2177  {
2178  yyextra->global_scope = 0;
2179  return TRUE;
2180  }
2181  if (yyextra->global_scope == INVALID_ENTRY)
2182  {
2183  return TRUE;
2184  }
2185  //cout<<"end scope: "<<scope->name<<endl;
2186  if (yyextra->current_root->parent() || isGlobalRoot)
2187  {
2188  yyextra->current_root= yyextra->current_root->parent(); /* end substructure */
2189  }
2190  else // if (yyextra->current_root != scope)
2191  {
2192  fprintf(stderr,"parse error in end <scopename>\n");
2193  scanner_abort(yyscanner);
2194  return FALSE;
2195  }
2196 
2197  // update variables or subprogram arguments with yyextra->modifiers
2198  std::map<std::string,SymbolModifiers>& mdfsMap = yyextra->modifiers[scope];
2199 
2200  if (scope->section == Entry::FUNCTION_SEC)
2201  {
2202  // iterate all symbol yyextra->modifiers of the scope
2203  for (const auto &kv : mdfsMap)
2204  {
2205  //cout<<it.key()<<": "<<qPrint(it)<<endl;
2206  Argument *arg = findArgument(scope, QCString(kv.first));
2207 
2208  if (arg)
2209  {
2210  applyModifiers(arg, kv.second);
2211  }
2212  }
2213 
2214  // find return type for function
2215  //cout<<"RETURN NAME "<<yyextra->modifiers[yyextra->current_root][scope->name.lower()].returnName<<endl;
2216  QCString returnName = yyextra->modifiers[yyextra->current_root][scope->name.lower().str()].returnName.lower();
2217  if (yyextra->modifiers[scope].find(returnName.str())!=yyextra->modifiers[scope].end())
2218  {
2219  scope->type = yyextra->modifiers[scope][returnName.str()].type; // returning type works
2220  applyModifiers(scope, yyextra->modifiers[scope][returnName.str()]); // returning array works
2221  }
2222 
2223  }
2224  if (scope->section == Entry::CLASS_SEC)
2225  { // was INTERFACE_SEC
2226  if (scope->parent()->section == Entry::FUNCTION_SEC)
2227  { // interface within function
2228  // iterate functions of interface and
2229  // try to find types for dummy(ie. argument) procedures.
2230  //cout<<"Search in "<<scope->name<<endl;
2231  int count = 0;
2232  int found = FALSE;
2233  for (const auto &ce : scope->children())
2234  {
2235  count++;
2236  if (ce->section != Entry::FUNCTION_SEC)
2237  continue;
2238 
2239  Argument *arg = findArgument(scope->parent(), ce->name, TRUE);
2240  if (arg != 0)
2241  {
2242  // set type of dummy procedure argument to interface
2243  arg->name = arg->type;
2244  arg->type = scope->name;
2245  }
2246  if (ce->name.lower() == scope->name.lower()) found = TRUE;
2247  }
2248  if ((count == 1) && found)
2249  {
2250  // clear all yyextra->modifiers of the scope
2251  yyextra->modifiers.erase(scope);
2252  scope->parent()->removeSubEntry(scope);
2253  scope = 0;
2254  return TRUE;
2255  }
2256  }
2257  }
2258  if (scope->section!=Entry::FUNCTION_SEC)
2259  { // not function section
2260  // iterate variables: get and apply yyextra->modifiers
2261  for (const auto &ce : scope->children())
2262  {
2263  if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC)
2264  continue;
2265 
2266  //cout<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl;
2267  if (mdfsMap.find(ce->name.lower().str())!=mdfsMap.end())
2268  applyModifiers(ce.get(), mdfsMap[ce->name.lower().str()]);
2269  }
2270  }
2271 
2272  // clear all yyextra->modifiers of the scope
2273  yyextra->modifiers.erase(scope);
2274 
2275  return TRUE;
2276 }
2277 
2278 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size)
2279 {
2280  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2281  yy_size_t c=0;
2282  while ( c < max_size && yyextra->inputString[yyextra->inputPosition] )
2283  {
2284  *buf = yyextra->inputString[yyextra->inputPosition++] ;
2285  c++; buf++;
2286  }
2287  return c;
2288 }
2289 
2290 static void initParser(yyscan_t yyscanner)
2291 {
2292  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2293  yyextra->last_entry.reset();
2294 }
2295 
2296 static void initEntry(yyscan_t yyscanner)
2297 {
2298  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2299  if (yyextra->typeMode)
2300  {
2301  yyextra->current->protection = yyextra->typeProtection;
2302  }
2303  else
2304  {
2305  yyextra->current->protection = yyextra->defaultProtection;
2306  }
2307  yyextra->current->mtype = Method;
2308  yyextra->current->virt = Normal;
2309  yyextra->current->stat = FALSE;
2310  yyextra->current->lang = SrcLangExt_Fortran;
2311  yyextra->commentScanner.initGroupInfo(yyextra->current.get());
2312 }
2313 
2314 /**
2315  adds yyextra->current entry to yyextra->current_root and creates new yyextra->current
2316 */
2317 static void addCurrentEntry(yyscan_t yyscanner,bool case_insens)
2318 {
2319  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2320  if (case_insens) yyextra->current->name = yyextra->current->name.lower();
2321  //printf("===Adding entry %s to %s\n", qPrint(yyextra->current->name), qPrint(yyextra->current_root->name));
2322  yyextra->last_entry = yyextra->current;
2323  yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
2324  initEntry(yyscanner);
2325 }
2326 
2327 static void addModule(yyscan_t yyscanner,const QCString &name, bool isModule)
2328 {
2329  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2330  DBG_CTX((stderr, "0=========> got module %s\n", qPrint(name)));
2331 
2332  if (isModule)
2333  yyextra->current->section = Entry::NAMESPACE_SEC;
2334  else
2335  yyextra->current->section = Entry::FUNCTION_SEC;
2336 
2337  if (!name.isEmpty())
2338  {
2339  yyextra->current->name = name;
2340  }
2341  else
2342  {
2343  QCString fname = yyextra->fileName;
2344  int index = std::max(fname.findRev('/'), fname.findRev('\\'));
2345  fname = fname.right(fname.length()-index-1);
2346  fname = fname.prepend("__").append("__");
2347  yyextra->current->name = fname;
2348  }
2349  yyextra->current->type = "program";
2350  yyextra->current->fileName = yyextra->fileName;
2351  yyextra->current->bodyLine = yyextra->lineNr; // used for source reference
2352  yyextra->current->startLine = yyextra->lineNr;
2353  yyextra->current->protection = Public ;
2354  addCurrentEntry(yyscanner,true);
2355  startScope(yyscanner,yyextra->last_entry.get());
2356 }
2357 
2358 
2359 static void addSubprogram(yyscan_t yyscanner,const QCString &text)
2360 {
2361  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2362  DBG_CTX((stderr,"1=========> got subprog, type: %s\n",qPrint(text)));
2363  yyextra->subrCurrent.push_back(yyextra->current);
2364  yyextra->current->section = Entry::FUNCTION_SEC ;
2365  QCString subtype = text; subtype=subtype.lower().stripWhiteSpace();
2366  yyextra->functionLine = (subtype.find("function") != -1);
2367  yyextra->current->type += " " + subtype;
2368  yyextra->current->type = yyextra->current->type.stripWhiteSpace();
2369  yyextra->current->fileName = yyextra->fileName;
2370  yyextra->current->bodyLine = yyextra->lineNr; // used for source reference start of body of routine
2371  yyextra->current->startLine = yyextra->lineNr; // used for source reference start of definition
2372  yyextra->current->args.resize(0);
2373  yyextra->current->argList.clear();
2374  yyextra->docBlock.resize(0);
2375 }
2376 
2377 /*! Adds interface to the root entry.
2378  * \note Code was brought to this procedure from the parser,
2379  * because there was/is idea to use it in several parts of the parser.
2380  */
2381 static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type)
2382 {
2383  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2384  if (YY_START == Start)
2385  {
2386  addModule(yyscanner);
2387  yy_push_state(ModuleBody,yyscanner); //anon program
2388  }
2389 
2390  yyextra->current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC;
2391  yyextra->current->spec = Entry::Interface;
2392  yyextra->current->name = name;
2393 
2394  switch (type)
2395  {
2396  case IF_ABSTRACT:
2397  yyextra->current->type = "abstract";
2398  break;
2399 
2400  case IF_GENERIC:
2401  yyextra->current->type = "generic";
2402  break;
2403 
2404  case IF_SPECIFIC:
2405  case IF_NONE:
2406  default:
2407  yyextra->current->type = "";
2408  }
2409 
2410  /* if type is part of a module, mod name is necessary for output */
2411  if ((yyextra->current_root) &&
2412  (yyextra->current_root->section == Entry::CLASS_SEC ||
2413  yyextra->current_root->section == Entry::NAMESPACE_SEC))
2414  {
2415  yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name;
2416  }
2417 
2418  yyextra->current->fileName = yyextra->fileName;
2419  yyextra->current->bodyLine = yyextra->lineNr;
2420  yyextra->current->startLine = yyextra->lineNr;
2421  addCurrentEntry(yyscanner,true);
2422 }
2423 
2424 
2425 //-----------------------------------------------------------------------------
2426 
2427 /*! Get the argument \a name.
2428  */
2429 static Argument *getParameter(yyscan_t yyscanner,const QCString &name)
2430 {
2431  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2432  // std::cout<<"addFortranParameter(): "<<name<<" DOCS:"<<(docs.isNull()?QCString("null"):docs)<<"\n";
2433  Argument *ret = 0;
2434  for (Argument &a:yyextra->current_root->argList)
2435  {
2436  if (a.name.lower()==name.lower())
2437  {
2438  ret=&a;
2439  //printf("parameter found: %s\n",(const char*)name);
2440  break;
2441  }
2442  } // for
2443  return ret;
2444 }
2445 
2446  //----------------------------------------------------------------------------
2447 static void startCommentBlock(yyscan_t yyscanner,bool brief)
2448 {
2449  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2450  if (brief)
2451  {
2452  yyextra->current->briefFile = yyextra->fileName;
2453  yyextra->current->briefLine = yyextra->lineNr;
2454  }
2455  else
2456  {
2457  yyextra->current->docFile = yyextra->fileName;
2458  yyextra->current->docLine = yyextra->lineNr;
2459  }
2460 }
2461 
2462 //----------------------------------------------------------------------------
2463 
2464 static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief)
2465 {
2466  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2467  bool hideInBodyDocs = Config_getBool(HIDE_IN_BODY_DOCS);
2468  if (yyextra->docBlockInBody && hideInBodyDocs)
2469  {
2470  yyextra->docBlockInBody = FALSE;
2471  return;
2472  }
2473  DBG_CTX((stderr,"call parseCommentBlock [%s]\n",qPrint(doc)));
2474  int lineNr = brief ? yyextra->current->briefLine : yyextra->current->docLine;
2475  int position=0;
2476  bool needsEntry = FALSE;
2477  Markdown markdown(yyextra->fileName,lineNr);
2478  QCString processedDoc = Config_getBool(MARKDOWN_SUPPORT) ? markdown.process(doc,lineNr) : doc;
2479  while (yyextra->commentScanner.parseCommentBlock(
2480  yyextra->thisParser,
2481  yyextra->docBlockInBody ? yyextra->subrCurrent.back().get() : yyextra->current.get(),
2482  processedDoc, // text
2483  yyextra->fileName, // file
2484  lineNr,
2485  yyextra->docBlockInBody ? FALSE : brief,
2486  yyextra->docBlockInBody ? FALSE : yyextra->docBlockJavaStyle,
2487  yyextra->docBlockInBody,
2488  yyextra->defaultProtection,
2489  position,
2490  needsEntry,
2491  Config_getBool(MARKDOWN_SUPPORT)
2492  ))
2493  {
2494  DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2495  if (needsEntry) addCurrentEntry(yyscanner,false);
2496  }
2497  DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2498 
2499  if (needsEntry) addCurrentEntry(yyscanner,false);
2500  yyextra->docBlockInBody = FALSE;
2501 }
2502 
2503 //----------------------------------------------------------------------------
2504 /// Handle parameter description as defined after the declaration of the parameter
2505 static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief)
2506 {
2507  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2508  QCString loc_doc;
2509  loc_doc = doc.stripWhiteSpace();
2510 
2511  std::shared_ptr<Entry> tmp_entry = yyextra->current;
2512  yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function
2513 
2514  // Still in the specification section so no inbodyDocs yet, but parameter documentation
2515  yyextra->current->inbodyDocs = "";
2516 
2517  // strip \\param or @param, so we can do some extra checking. We will add it later on again.
2518  if (!loc_doc.stripPrefix("\\param") &&
2519  !loc_doc.stripPrefix("@param")
2520  ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning
2521  loc_doc.stripWhiteSpace();
2522 
2523  // direction as defined with the declaration of the parameter
2524  int dir1 = yyextra->modifiers[yyextra->current_root][yyextra->argName.lower().str()].direction;
2525  // in description [in] is specified
2526  if (loc_doc.lower().find(directionParam[SymbolModifiers::IN]) == 0)
2527  {
2528  // check if with the declaration intent(in) or nothing has been specified
2529  if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2530  (directionParam[dir1] == directionParam[SymbolModifiers::IN]))
2531  {
2532  // strip direction
2533  loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::IN]));
2534  loc_doc.stripWhiteSpace();
2535  // in case of empty documentation or (now) just name, consider it as no documentation
2536  if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2537  {
2538  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::IN] + " " +
2539  yyextra->argName + " " + loc_doc,brief);
2540  }
2541  }
2542  else
2543  {
2544  // something different specified, give warning and leave error.
2545  warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
2546  qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
2547  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2548  yyextra->argName + " " + loc_doc,brief);
2549  }
2550  }
2551  // analogous to the [in] case, here [out] direction specified
2552  else if (loc_doc.lower().find(directionParam[SymbolModifiers::OUT]) == 0)
2553  {
2554  if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2555  (directionParam[dir1] == directionParam[SymbolModifiers::OUT]))
2556  {
2557  loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::OUT]));
2558  loc_doc.stripWhiteSpace();
2559  if (loc_doc.isEmpty() || (loc_doc.lower() == yyextra->argName.lower()))
2560  {
2561  yyextra->current = tmp_entry;
2562  return;
2563  }
2564  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::OUT] + " " +
2565  yyextra->argName + " " + loc_doc,brief);
2566  }
2567  else
2568  {
2569  warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
2570  qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
2571  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2572  yyextra->argName + " " + loc_doc,brief);
2573  }
2574  }
2575  // analogous to the [in] case, here [in,out] direction specified
2576  else if (loc_doc.lower().find(directionParam[SymbolModifiers::INOUT]) == 0)
2577  {
2578  if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2579  (directionParam[dir1] == directionParam[SymbolModifiers::INOUT]))
2580  {
2581  loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::INOUT]));
2582  loc_doc.stripWhiteSpace();
2583  if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2584  {
2585  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::INOUT] + " " +
2586  yyextra->argName + " " + loc_doc,brief);
2587  }
2588  }
2589  else
2590  {
2591  warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
2592  qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
2593  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2594  yyextra->argName + " " + loc_doc,brief);
2595  }
2596  }
2597  // analogous to the [in] case; here no direction specified
2598  else if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2599  {
2600  handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2601  yyextra->argName + " " + loc_doc,brief);
2602  }
2603 
2604  // reset yyextra->current back to the part inside the routine
2605  yyextra->current = tmp_entry;
2606 }
2607 //----------------------------------------------------------------------------
2608 /// Handle result description as defined after the declaration of the parameter
2609 static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief)
2610 {
2611  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2612  QCString loc_doc;
2613  loc_doc = doc.stripWhiteSpace();
2614 
2615  std::shared_ptr<Entry> tmp_entry = yyextra->current;
2616  yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function
2617 
2618  // Still in the specification section so no inbodyDocs yet, but parameter documentation
2619  yyextra->current->inbodyDocs = "";
2620 
2621  // strip \\returns or @returns. We will add it later on again.
2622  if (!loc_doc.stripPrefix("\\returns") &&
2623  !loc_doc.stripPrefix("\\return") &&
2624  !loc_doc.stripPrefix("@returns") &&
2625  !loc_doc.stripPrefix("@return")
2626  ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning
2627  loc_doc.stripWhiteSpace();
2628 
2629  if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2630  {
2631  handleCommentBlock(yyscanner,QCString("\n\n@returns ") + loc_doc,brief);
2632  }
2633 
2634  // reset yyextra->current back to the part inside the routine
2635  yyextra->current = tmp_entry;
2636 }
2637 
2638 //----------------------------------------------------------------------------
2639 
2640 static void parseMain(yyscan_t yyscanner, const QCString &fileName,const char *fileBuf,
2641  const std::shared_ptr<Entry> &rt, FortranFormat format)
2642 {
2643  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2644  char *tmpBuf = nullptr;
2645  initParser(yyscanner);
2646 
2647  if (fileBuf==0 || fileBuf[0]=='\0') return;
2648 
2649  yyextra->defaultProtection = Public;
2650  yyextra->inputString = fileBuf;
2651  yyextra->inputPosition = 0;
2652  yyextra->inputStringPrepass = nullptr;
2653  yyextra->inputPositionPrepass = 0;
2654 
2655  //yyextra->anonCount = 0; // don't reset per file
2656  yyextra->current_root = rt.get();
2657  yyextra->global_root = rt;
2658 
2659  yyextra->isFixedForm = recognizeFixedForm(fileBuf,format);
2660 
2661  if (yyextra->isFixedForm)
2662  {
2663  msg("Prepassing fixed form of %s\n", qPrint(fileName));
2664  //printf("---strlen=%d\n", strlen(fileBuf));
2665  //clock_t start=clock();
2666 
2667  //printf("Input fixed form string:\n%s\n", fileBuf);
2668  //printf("===========================\n");
2669  yyextra->inputString = prepassFixedForm(fileBuf, nullptr);
2670  Debug::print(Debug::FortranFixed2Free,0,"======== Fixed to Free format =========\n---- Input fixed form string ------- \n%s\n", fileBuf);
2671  Debug::print(Debug::FortranFixed2Free,0,"---- Resulting free form string ------- \n%s\n", yyextra->inputString);
2672  //printf("Resulting free form string:\n%s\n", yyextra->inputString);
2673  //printf("===========================\n");
2674 
2675  //clock_t end=clock();
2676  //printf("CPU time used=%f\n", ((double) (end-start))/CLOCKS_PER_SEC);
2677  }
2678  else if (yyextra->inputString[strlen(fileBuf)-1] != '\n')
2679  {
2680  tmpBuf = (char *)malloc(strlen(fileBuf)+2);
2681  strcpy(tmpBuf,fileBuf);
2682  tmpBuf[strlen(fileBuf)]= '\n';
2683  tmpBuf[strlen(fileBuf)+1]= '\000';
2684  yyextra->inputString = tmpBuf;
2685  }
2686 
2687  yyextra->lineNr= 1 ;
2688  yyextra->fileName = fileName;
2689  msg("Parsing file %s...\n",qPrint(yyextra->fileName));
2690 
2691  yyextra->global_scope = rt.get();
2692  startScope(yyscanner,rt.get()); // implies yyextra->current_root = rt
2693  initParser(yyscanner);
2694  yyextra->commentScanner.enterFile(yyextra->fileName,yyextra->lineNr);
2695 
2696  // add entry for the file
2697  yyextra->current = std::make_shared<Entry>();
2698  yyextra->current->lang = SrcLangExt_Fortran;
2699  yyextra->current->name = yyextra->fileName;
2700  yyextra->current->section = Entry::SOURCE_SEC;
2701  yyextra->file_root = yyextra->current;
2702  yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
2703  yyextra->current->lang = SrcLangExt_Fortran;
2704 
2705  fortranscannerYYrestart( 0, yyscanner );
2706  {
2707  BEGIN( Start );
2708  }
2709 
2710  fortranscannerYYlex(yyscanner);
2711  yyextra->commentScanner.leaveFile(yyextra->fileName,yyextra->lineNr);
2712 
2713  if (yyextra->global_scope && yyextra->global_scope != INVALID_ENTRY)
2714  {
2715  endScope(yyscanner,yyextra->current_root, TRUE); // TRUE - global root
2716  }
2717 
2718  //debugCompounds(rt); //debug
2719 
2720  rt->program.str(std::string());
2721  //delete yyextra->current; yyextra->current=0;
2722  yyextra->moduleProcedures.clear();
2723  if (tmpBuf)
2724  {
2725  free((char*)tmpBuf);
2726  yyextra->inputString=NULL;
2727  }
2728  if (yyextra->isFixedForm)
2729  {
2730  free((char*)yyextra->inputString);
2731  yyextra->inputString=NULL;
2732  }
2733 
2734 }
2735 
2736 //----------------------------------------------------------------------------
2737 
2738 struct FortranOutlineParser::Private
2739 {
2740  yyscan_t yyscanner;
2741  fortranscannerYY_state extra;
2742  FortranFormat format;
2743  Private(FortranFormat fmt) : format(fmt)
2744  {
2745  fortranscannerYYlex_init_extra(&extra,&yyscanner);
2746 #ifdef FLEX_DEBUG
2747  fortranscannerYYset_debug(1,yyscanner);
2748 #endif
2749  }
2750  ~Private()
2751  {
2752  fortranscannerYYlex_destroy(yyscanner);
2753  }
2754 };
2755 
2756 FortranOutlineParser::FortranOutlineParser(FortranFormat format)
2757  : p(std::make_unique<Private>(format))
2758 {
2759 }
2760 
2761 FortranOutlineParser::~FortranOutlineParser()
2762 {
2763 }
2764 
2765 void FortranOutlineParser::parseInput(const QCString &fileName,
2766  const char *fileBuf,
2767  const std::shared_ptr<Entry> &root,
2768  ClangTUParser * /*clangParser*/)
2769 {
2770  struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
2771  yyextra->thisParser = this;
2772 
2773  printlex(yy_flex_debug, TRUE, __FILE__, qPrint(fileName));
2774 
2775  ::parseMain(p->yyscanner,fileName,fileBuf,root,p->format);
2776 
2777  printlex(yy_flex_debug, FALSE, __FILE__, qPrint(fileName));
2778 }
2779 
2780 bool FortranOutlineParser::needsPreprocessing(const QCString &extension) const
2781 {
2782  return extension!=extension.lower(); // use preprocessor only for upper case extensions
2783 }
2784 
2785 void FortranOutlineParser::parsePrototype(const QCString &text)
2786 {
2787  struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
2788  pushBuffer(p->yyscanner,text);
2789  yyextra->parsingPrototype = TRUE;
2790  BEGIN(Prototype);
2791  fortranscannerYYlex(p->yyscanner);
2792  yyextra->parsingPrototype = FALSE;
2793  popBuffer(p->yyscanner);
2794 }
2795 
2796 //----------------------------------------------------------------------------
2797 
2798 static void scanner_abort(yyscan_t yyscanner)
2799 {
2800  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2801  fprintf(stderr,"********************************************************************\n");
2802  fprintf(stderr,"Error in file %s line: %d, state: %d(%s)\n",qPrint(yyextra->fileName),yyextra->lineNr,YY_START,stateToString(YY_START));
2803  fprintf(stderr,"********************************************************************\n");
2804 
2805  bool start=FALSE;
2806 
2807  for (const auto &ce : yyextra->global_root->children())
2808  {
2809  if (ce == yyextra->file_root) start=TRUE;
2810  if (start) ce->reset();
2811  }
2812 
2813  // dummy call to avoid compiler warning
2814  (void)yy_top_state(yyscanner);
2815 
2816  return;
2817  //exit(-1);
2818 }
2819 
2820 //----------------------------------------------------------------------------
2821 
2822 #include "fortranscanner.l.h"