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