1 /* -*- mode: fundamental; indent-tabs-mode: 1; -*- */
2 /*****************************************************************************
3 * Parser for Fortran90 F subset
5 * Copyright (C) by Anke Visser
6 * based on the work of Dimitri van Heesch.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation under the terms of the GNU General Public License is hereby
10 * granted. No representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or implied warranty.
12 * See the GNU General Public License for more details.
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
21 * - Consider using startScope(), endScope() functions with module, program,
22 * subroutine or any other scope in fortran program.
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.
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.
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]'
38 * - Must track yyextra->lineNr when using REJECT, unput() or similar commands.
40 %option never-interactive
41 %option case-insensitive
42 %option prefix="fortranscannerYY"
44 %option extra-type="struct fortranscannerYY_state *"
47 // forward declare yyscan_t to improve type safety
48 #define YY_TYPEDEF_YY_SCANNER_T
50 typedef yyguts_t *yyscan_t;
63 #include "fortranscanner.h"
71 #include "commentscan.h"
73 #include "arguments.h"
77 const int fixedCommentAfter = 72;
79 // Toggle for some debugging info
80 //#define DBG_CTX(x) fprintf x
81 #define DBG_CTX(x) do { } while(0)
84 #define YY_NO_UNISTD_H 1
86 enum ScanVar { V_IGNORE, V_VARIABLE, V_PARAMETER, V_RESULT};
87 enum InterfaceType { IF_NONE, IF_SPECIFIC, IF_GENERIC, IF_ABSTRACT };
89 // {{{ ----- Helper structs -----
90 //! Holds yyextra->modifiers (ie attributes) for one symbol (variable, function, etc)
91 struct SymbolModifiers
93 enum Protection {NONE_P, PUBLIC, PRIVATE};
94 enum Direction {NONE_D, IN, OUT, INOUT};
96 //! This is only used with function return value.
97 QCString type, returnName;
98 Protection protection;
115 bool volat; /* volatile is a reserved name */
116 bool value; /* volatile is a reserved name */
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(),
127 SymbolModifiers& operator|=(const SymbolModifiers &mdfs);
128 SymbolModifiers& operator|=(QCString mdfrString);
131 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs);
133 static const char *directionStrs[] =
135 "", "intent(in)", "intent(out)", "intent(inout)"
137 static const char *directionParam[] =
139 "", "[in]", "[out]", "[in,out]"
144 struct CommentInPrepass
148 CommentInPrepass(int col, QCString s) : column(col), str(s) {}
151 /* -----------------------------------------------------------------
156 struct fortranscannerYY_state
158 OutlineParserInterface * thisParser;
159 CommentScanner commentScanner;
160 const char * inputString;
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;
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
185 bool docBlockInBody = FALSE;
186 bool docBlockJavaStyle;
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()
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;
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);
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);
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);
240 static const char *stateToString(int state);
242 //-----------------------------------------------------------------------------
244 #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
245 #define YY_USER_ACTION yyextra->colNr+=(int)yyleng;
246 #define INVALID_ENTRY ((Entry*)0x8)
247 //-----------------------------------------------------------------------------
251 //-----------------------------------------------------------------------------
252 //-----------------------------------------------------------------------------
254 NOTIDSYM [^a-z_A-Z0-9]
256 ID [a-z_A-Z%]+{IDSYM}*
257 ID_ [a-z_A-Z%]*{IDSYM}*
259 LABELID [a-z_A-Z]+[a-z_A-Z0-9\-]*
260 SUBPROG (subroutine|function)
264 BT_ ([ \t]+|[ \t]*"(")
266 ARGS_L0 ("("[^)]*")")
267 ARGS_L1a [^()]*"("[^)]*")"[^)]*
268 ARGS_L1 ("("{ARGS_L1a}*")")
269 ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")"
270 ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2})
273 NUM_TYPE (complex|integer|logical|real)
274 LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.)
276 CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS}))
277 TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|ENUMERATOR|{CHAR}|TYPE{ARGS}|CLASS{ARGS}|PROCEDURE{ARGS}?)
279 INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")"
280 ATTR_SPEC (EXTERNAL|ALLOCATABLE|DIMENSION{ARGS}|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PROTECTED|PRIVATE|PUBLIC|SAVE|TARGET|NOPASS|PASS{ARGS}?|DEFERRED|NON_OVERRIDABLE|CONTIGUOUS|VOLATILE|VALUE)
281 ACCESS_SPEC (PRIVATE|PUBLIC)
282 LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}((,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})|(,{BS}NAME{BS}"="{BS}"'"(.*)"'"{BS}))?")"
283 /* Assume that attribute statements are almost the same as attributes. */
284 ATTR_STMT {ATTR_SPEC}|DIMENSION|{ACCESS_SPEC}
285 EXTERNAL_STMT (EXTERNAL)
288 PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?
289 SCOPENAME ({ID}{BS}"::"{BS})*
296 //---------------------------------------------------------------------------------
298 /** fortran parsing states */
303 %x SubprogBodyContains
309 %x ModuleBodyContains
317 %x TypedefBodyContains
327 /** comment parsing states */
333 /** prototype parsing */
340 /*-----------------------------------------------------------------------------------*/
342 <Prepass>^{BS}[&]*{BS}!.*\n { /* skip lines with just comment. Note code was in free format or has been converted to it */
343 yyextra->lineCountPrepass ++;
345 <Prepass>^{BS}\n { /* skip empty lines */
346 yyextra->lineCountPrepass ++;
348 <*>^.*\n { // prepass: look for line continuations
349 yyextra->functionLine = FALSE;
351 DBG_CTX((stderr, "---%s", yytext));
353 int indexStart = getAmpersandAtTheStart(yytext, (int)yyleng);
354 int indexEnd = getAmpOrExclAtTheEnd(yytext, (int)yyleng, '\0');
355 if (indexEnd>=0 && yytext[indexEnd]!='&') //we are only interested in amp
361 { // ----- no ampersand as line continuation
362 if (YY_START == Prepass)
363 { // last line in "continuation"
365 // Only take input after initial ampersand
366 yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1));
368 //printf("BUFFER:%s\n", (const char*)yyextra->inputStringPrepass);
369 pushBuffer(yyscanner,yyextra->inputStringPrepass);
371 yy_pop_state(yyscanner);
380 { // ----- line with continuation
381 if (YY_START != Prepass)
383 yyextra->comments.clear();
384 yyextra->inputStringPrepass=QCString();
385 yy_push_state(Prepass,yyscanner);
388 int length = yyextra->inputStringPrepass.length();
390 // Only take input after initial ampersand
391 yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1));
392 yyextra->lineCountPrepass ++;
394 // cut off & and remove following comment if present
395 truncatePrepass(yyscanner,length+indexEnd-(indexStart+1));
400 /*------ ignore strings that are not initialization strings */
401 <String>\"|\' { // string ends with next quote without previous backspace
402 if (yytext[0]!=yyextra->stringStartSymbol)
404 yyextra->colNr -= (int)yyleng;
406 } // single vs double quote
407 if (yy_top_state(yyscanner) == Initialization ||
408 yy_top_state(yyscanner) == ArrayInitializer)
410 yyextra->initializer+=yytext;
412 yy_pop_state(yyscanner);
414 <String>. { if (yy_top_state(yyscanner) == Initialization ||
415 yy_top_state(yyscanner) == ArrayInitializer)
417 yyextra->initializer+=yytext;
420 <*>\"|\' { /* string starts */
421 if (YY_START == StrIgnore)
422 { yyextra->colNr -= (int)yyleng;
424 }; // ignore in simple yyextra->comments
425 yy_push_state(YY_START,yyscanner);
426 if (yy_top_state(yyscanner) == Initialization ||
427 yy_top_state(yyscanner) == ArrayInitializer)
429 yyextra->initializer+=yytext;
431 yyextra->stringStartSymbol=yytext[0]; // single or double quote
435 /*------ ignore simple comment (not documentation yyextra->comments) */
437 <*>"!"/[^<>\n] { if (YY_START == String)
438 { yyextra->colNr -= (int)yyleng;
440 } // "!" is ignored in strings
441 // skip comment line (without docu yyextra->comments "!>" "!<" )
442 /* ignore further "!" and ignore yyextra->comments in Strings */
443 if ((YY_START != StrIgnore) && (YY_START != String))
445 yy_push_state(YY_START,yyscanner);
447 yyextra->debugStr="*!";
448 DBG_CTX((stderr,"start comment %d\n",yyextra->lineNr));
451 <StrIgnore>.?/\n { yy_pop_state(yyscanner); // comment ends with endline character
452 DBG_CTX((stderr,"end comment %d %s\n",yyextra->lineNr,qPrint(yyextra->debugStr)));
453 } // comment line ends
454 <StrIgnore>. { yyextra->debugStr+=yytext; }
457 /*------ use handling ------------------------------------------------------------*/
459 <Start,ModuleBody,SubprogBody>"use"{BS_} {
460 if (YY_START == Start)
462 addModule(yyscanner);
463 yy_push_state(ModuleBody,yyscanner); //anon program
465 yy_push_state(Use,yyscanner);
468 DBG_CTX((stderr,"using dir %s\n",yytext));
469 yyextra->current->name=yytext;
470 yyextra->current->name=yyextra->current->name.lower();
471 yyextra->current->fileName = yyextra->fileName;
472 yyextra->current->section=Entry::USINGDIR_SEC;
473 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
474 yyextra->current->lang = SrcLangExt_Fortran;
475 yy_pop_state(yyscanner);
478 yyextra->useModuleName=yytext;
479 yyextra->useModuleName=yyextra->useModuleName.lower();
481 <Use>,{BS}"ONLY" { BEGIN(UseOnly);
483 <UseOnly>{BS},{BS} {}
485 yyextra->current->name= yyextra->useModuleName+"::"+yytext;
486 yyextra->current->name=yyextra->current->name.lower();
487 yyextra->current->fileName = yyextra->fileName;
488 yyextra->current->section=Entry::USINGDECL_SEC;
489 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
490 yyextra->current->lang = SrcLangExt_Fortran;
495 yy_pop_state(yyscanner);
498 /* INTERFACE definitions */
499 <Start,ModuleBody,SubprogBody>{
500 ^{BS}interface{IDSYM}+ { /* variable with interface prefix */ }
501 ^{BS}interface { yyextra->ifType = IF_SPECIFIC;
502 yy_push_state(InterfaceBody,yyscanner);
503 // do not start a scope here, every
504 // interface body is a scope of its own
507 ^{BS}abstract{BS_}interface { yyextra->ifType = IF_ABSTRACT;
508 yy_push_state(InterfaceBody,yyscanner);
509 // do not start a scope here, every
510 // interface body is a scope of its own
513 ^{BS}interface{BS_}{ID}{ARGS}? { yyextra->ifType = IF_GENERIC;
514 yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
515 yy_push_state(InterfaceBody,yyscanner);
517 // extract generic name
518 QCString name = QCString(yytext).stripWhiteSpace();
519 name = name.right(name.length() - 9).stripWhiteSpace().lower();
520 addInterface(yyscanner,name, yyextra->ifType);
521 startScope(yyscanner,yyextra->last_entry.get());
525 <InterfaceBody>^{BS}end{BS}interface({BS_}{ID})? {
526 // end scope only if GENERIC interface
527 if (yyextra->ifType == IF_GENERIC)
529 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr - 1;
531 if (yyextra->ifType == IF_GENERIC && !endScope(yyscanner,yyextra->current_root))
535 yyextra->ifType = IF_NONE;
536 yy_pop_state(yyscanner);
538 <InterfaceBody>module{BS}procedure { yy_push_state(YY_START,yyscanner);
539 BEGIN(ModuleProcedure);
541 <ModuleProcedure>{ID} { if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
543 addInterface(yyscanner,yytext, yyextra->ifType);
544 startScope(yyscanner,yyextra->last_entry.get());
547 yyextra->current->section = Entry::FUNCTION_SEC ;
548 yyextra->current->name = yytext;
549 yyextra->moduleProcedures.push_back(yyextra->current);
550 addCurrentEntry(yyscanner,true);
552 <ModuleProcedure>"\n" { yyextra->colNr -= 1;
554 yy_pop_state(yyscanner);
558 /*-- Contains handling --*/
559 <Start>^{BS}{CONTAINS}/({BS}|\n|!|;) {
560 if (YY_START == Start)
562 addModule(yyscanner);
563 yy_push_state(ModuleBodyContains,yyscanner); //anon program
566 <ModuleBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(ModuleBodyContains); }
567 <SubprogBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(SubprogBodyContains); }
568 <TypedefBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(TypedefBodyContains); }
570 /*------ module handling ------------------------------------------------------------*/
571 <Start>block{BS}data{BS}{ID_} { //
572 yyextra->vtype = V_IGNORE;
573 yy_push_state(BlockData,yyscanner);
574 yyextra->defaultProtection = Public;
576 <Start>module|program{BS_} { //
577 yyextra->vtype = V_IGNORE;
578 if (yytext[0]=='m' || yytext[0]=='M')
580 yy_push_state(Module,yyscanner);
584 yy_push_state(Program,yyscanner);
586 yyextra->defaultProtection = Public;
588 <BlockData>^{BS}"end"({BS}(block{BS}data)({BS_}{ID})?)?{BS}/(\n|!|;) { // end block data
589 //if (!endScope(yyscanner,yyextra->current_root))
591 yyextra->defaultProtection = Public;
592 yy_pop_state(yyscanner);
594 <Start,ModuleBody,ModuleBodyContains>"end"({BS}(module|program)({BS_}{ID})?)?{BS}/(\n|!|;) { // end module
595 resolveModuleProcedures(yyscanner,yyextra->current_root);
596 if (!endScope(yyscanner,yyextra->current_root))
600 yyextra->defaultProtection = Public;
601 if (yyextra->global_scope)
603 if (yyextra->global_scope != INVALID_ENTRY)
605 yy_push_state(Start,yyscanner);
609 yy_pop_state(yyscanner); // cannot pop artrificial entry
614 yy_push_state(Start,yyscanner);
615 yyextra->global_scope = INVALID_ENTRY; // signal that the yyextra->global_scope has already been used.
619 addModule(yyscanner, QCString(yytext), TRUE);
623 addModule(yyscanner, QCString(yytext), FALSE);
627 /*------- access specification --------------------------------------------------------------------------*/
629 <ModuleBody>private/{BS}(\n|"!") { yyextra->defaultProtection = Private;
630 yyextra->current->protection = yyextra->defaultProtection ;
632 <ModuleBody>public/{BS}(\n|"!") { yyextra->defaultProtection = Public;
633 yyextra->current->protection = yyextra->defaultProtection ;
636 /*------- type definition -------------------------------------------------------------------------------*/
638 <Start,ModuleBody>^{BS}type/[^a-z0-9_] {
639 if (YY_START == Start)
641 addModule(yyscanner,QCString());
642 yy_push_state(ModuleBody,yyscanner); //anon program
645 yy_push_state(Typedef,yyscanner);
646 yyextra->current->protection = yyextra->defaultProtection;
647 yyextra->typeProtection = yyextra->defaultProtection;
648 yyextra->typeMode = true;
656 yyextra->current->spec |= Entry::AbstractClass;
659 QCString basename = extractFromParens(yytext).lower();
660 yyextra->current->extends.push_back(BaseInfo(basename, Public, Normal));
663 yyextra->current->protection = Public;
664 yyextra->typeProtection = Public;
667 yyextra->current->protection = Private;
668 yyextra->typeProtection = Private;
670 {LANGUAGE_BIND_SPEC} {
671 /* ignored for now */
673 {ID} { /* type name found */
674 yyextra->current->section = Entry::CLASS_SEC;
675 yyextra->current->spec |= Entry::Struct;
676 yyextra->current->name = yytext;
677 yyextra->current->fileName = yyextra->fileName;
678 yyextra->current->bodyLine = yyextra->lineNr;
679 yyextra->current->startLine = yyextra->lineNr;
681 /* if type is part of a module, mod name is necessary for output */
682 if (yyextra->current_root &&
683 (yyextra->current_root->section == Entry::CLASS_SEC ||
684 yyextra->current_root->section == Entry::NAMESPACE_SEC))
686 yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name;
689 addCurrentEntry(yyscanner,true);
690 startScope(yyscanner,yyextra->last_entry.get());
695 <TypedefBodyContains>{ /* Type Bound Procedures */
696 ^{BS}PROCEDURE{ARGS}? {
697 yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
700 yyextra->current->spec |= Entry::Final;
701 yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
704 yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
709 yyextra->currentModifiers |= QCString(yytext);
714 QCString name = yytext;
715 yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers;
716 yyextra->current->section = Entry::FUNCTION_SEC;
717 yyextra->current->name = name;
718 yyextra->current->fileName = yyextra->fileName;
719 yyextra->current->bodyLine = yyextra->lineNr;
720 yyextra->current->startLine = yyextra->lineNr;
721 addCurrentEntry(yyscanner,true);
723 {BS}"=>"[^(\n|\!)]* { /* Specific bindings come after the ID. */
724 QCString args = yytext;
725 yyextra->last_entry->args = args.lower();
728 yyextra->currentModifiers = SymbolModifiers();
730 yyextra->docBlock.resize(0);
735 <TypedefBody,TypedefBodyContains>{
736 ^{BS}"end"{BS}"type"({BS_}{ID})?{BS}/(\n|!|;) { /* end type definition */
737 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
738 if (!endScope(yyscanner,yyextra->current_root))
742 yyextra->typeMode = false;
743 yy_pop_state(yyscanner);
745 ^{BS}"end"{BS}/(\n|!|;) { /* incorrect end type definition */
746 warn(yyextra->fileName,yyextra->lineNr, "Found 'END' instead of 'END TYPE'");
747 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
748 if (!endScope(yyscanner,yyextra->current_root))
752 yyextra->typeMode = false;
753 yy_pop_state(yyscanner);
757 /*------- module/global/typedef variable ---------------------------------------------------*/
759 <SubprogBody,SubprogBodyContains>^{BS}[0-9]*{BS}"end"({BS}{SUBPROG}({BS_}{ID})?)?{BS}/(\n|!|;) {
761 // ABSTRACT and specific interfaces are stored
762 // in a scope of their own, even if multiple
763 // are group in one INTERFACE/END INTERFACE block.
765 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
767 endScope(yyscanner,yyextra->current_root);
768 yyextra->last_entry->endBodyLine = yyextra->lineNr - 1;
770 yyextra->current_root->endBodyLine = yyextra->lineNr - 1;
772 if (!endScope(yyscanner,yyextra->current_root))
776 yyextra->subrCurrent.pop_back();
777 yyextra->vtype = V_IGNORE;
778 yy_pop_state(yyscanner) ;
784 <Start,ModuleBody,TypedefBody,SubprogBody,Enum>{
785 ^{BS}{TYPE_SPEC}/{SEPARATE} {
786 yyextra->last_enum.reset();
787 if (YY_START == Enum)
789 yyextra->argType = "@"; // enum marker
793 yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower();
795 yyextra->current->bodyLine = yyextra->lineNr + 1;
796 yyextra->current->endBodyLine = yyextra->lineNr + yyextra->lineCountPrepass;
797 /* variable declaration starts */
798 if (YY_START == Start)
800 addModule(yyscanner);
801 yy_push_state(ModuleBody,yyscanner); //anon program
803 yy_push_state(AttributeList,yyscanner);
805 {EXTERNAL_STMT}/({BS}"::"|{BS_}{ID}) {
806 /* external can be a "type" or an attribute */
807 if (YY_START == Start)
809 addModule(yyscanner);
810 yy_push_state(ModuleBody,yyscanner); //anon program
812 QCString tmp = yytext;
813 yyextra->currentModifiers |= tmp.stripWhiteSpace();
814 yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower();
815 yy_push_state(AttributeList,yyscanner);
817 {ATTR_STMT}/{BS_}{ID} |
818 {ATTR_STMT}/{BS}"::" {
819 /* attribute statement starts */
820 DBG_CTX((stderr,"5=========> Attribute statement: %s\n", yytext));
821 QCString tmp = yytext;
822 yyextra->currentModifiers |= tmp.stripWhiteSpace();
824 yy_push_state(YY_START,yyscanner);
825 BEGIN( AttributeList ) ;
829 ^{BS}"type"{BS_}"is"/{BT_} {}
830 ^{BS}"type"{BS}"=" {}
831 ^{BS}"class"{BS_}"is"/{BT_} {}
832 ^{BS}"class"{BS_}"default" {}
837 {LANGUAGE_BIND_SPEC} {
838 yyextra->currentModifiers |= yytext;
840 {ATTR_SPEC}. { /* update yyextra->current yyextra->modifiers when it is an ATTR_SPEC and not a variable name */
841 /* buyyextra->625519 */
842 char chr = yytext[(int)yyleng-1];
845 yyextra->colNr -= (int)yyleng;
850 QCString tmp = yytext;
851 tmp = tmp.left(tmp.length() - 1);
853 unput(yytext[(int)yyleng-1]);
854 yyextra->currentModifiers |= (tmp);
857 "::" { /* end attribute list */
860 . { /* unknown attribute, consider variable name */
861 //cout<<"start variables, unput "<<*yytext<<endl;
869 <Variable>{ID} { /* parse variable declaration */
870 //cout << "5=========> got variable: " << yyextra->argType << "::" << yytext << endl;
871 /* work around for bug in QCString.replace (QCString works) */
872 QCString name=yytext;
874 /* remember attributes for the symbol */
875 yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers;
876 yyextra->argName= name;
878 yyextra->vtype= V_IGNORE;
879 if (!yyextra->argType.isEmpty() && yyextra->current_root->section!=Entry::FUNCTION_SEC)
880 { // new variable entry
881 yyextra->vtype = V_VARIABLE;
882 yyextra->current->section = Entry::VARIABLE_SEC;
883 yyextra->current->name = yyextra->argName;
884 yyextra->current->type = yyextra->argType;
885 yyextra->current->fileName = yyextra->fileName;
886 yyextra->current->bodyLine = yyextra->lineNr; // used for source reference
887 yyextra->current->startLine = yyextra->lineNr;
888 if (yyextra->argType == "@")
890 yyextra->current_root->copyToSubEntry(yyextra->current);
891 // add to the scope surrounding the enum (copy!)
892 yyextra->last_enum = yyextra->current;
893 yyextra->current_root->parent()->moveToSubEntryAndRefresh(yyextra->current);
894 initEntry(yyscanner);
898 addCurrentEntry(yyscanner,true);
901 else if (!yyextra->argType.isEmpty())
902 { // declaration of parameter list: add type for corr. parameter
903 Argument *parameter = getParameter(yyscanner,yyextra->argName);
906 yyextra->vtype= V_PARAMETER;
907 if (!yyextra->argType.isNull()) parameter->type=yyextra->argType.stripWhiteSpace();
908 if (!yyextra->docBlock.isNull())
910 subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
913 // save, it may be function return type
916 yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType;
920 if ((yyextra->current_root->name.lower() == yyextra->argName.lower()) ||
921 (yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName.lower() == yyextra->argName.lower()))
923 int strt = yyextra->current_root->type.find("function");
928 yyextra->vtype = V_RESULT;
931 if (strt != 0) lft = yyextra->current_root->type.left(strt).stripWhiteSpace();
932 if ((yyextra->current_root->type.length() - strt - strlen("function"))!= 0)
934 rght = yyextra->current_root->type.right(yyextra->current_root->type.length() - strt - (int)strlen("function")).stripWhiteSpace();
936 yyextra->current_root->type = lft;
937 if (rght.length() > 0)
939 if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
940 yyextra->current_root->type += rght;
942 if (yyextra->argType.stripWhiteSpace().length() > 0)
944 if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
945 yyextra->current_root->type += yyextra->argType.stripWhiteSpace();
947 if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
948 yyextra->current_root->type += "function";
949 if (!yyextra->docBlock.isNull())
951 subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE);
956 yyextra->current_root->type += " " + yyextra->argType.stripWhiteSpace();
958 yyextra->current_root->type = yyextra->current_root->type.stripWhiteSpace();
959 yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->current_root->type;
963 yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType;
966 // any accumulated doc for argument should be emptied,
967 // because it is handled other way and this doc can be
968 // unexpectedly passed to the next member.
969 yyextra->current->doc.resize(0);
970 yyextra->current->brief.resize(0);
973 <Variable>{ARGS} { /* dimension of the previous entry. */
974 QCString name(yyextra->argName);
975 QCString attr("dimension");
977 yyextra->modifiers[yyextra->current_root][name.lower().str()] |= attr;
979 <Variable>{COMMA} { //printf("COMMA: %d<=..<=%d\n", yyextra->colNr-(int)yyleng, yyextra->colNr);
981 updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
984 yy_push_state(YY_START,yyscanner);
985 yyextra->initializer="=";
986 yyextra->initializerScope = yyextra->initializerArrayScope = 0;
987 BEGIN(Initialization);
989 <Variable>"\n" { yyextra->currentModifiers = SymbolModifiers();
990 yy_pop_state(yyscanner); // end variable declaration list
992 yyextra->docBlock.resize(0);
994 <Variable>";".*"\n" { yyextra->currentModifiers = SymbolModifiers();
995 yy_pop_state(yyscanner); // end variable declaration list
996 yyextra->docBlock.resize(0);
997 yyextra->inputStringSemi = " \n"+QCString(yytext+1);
999 pushBuffer(yyscanner,yyextra->inputStringSemi);
1002 if (YY_START == Variable) REJECT; // Just be on the safe side
1003 if (YY_START == String) REJECT; // ";" ignored in strings
1004 if (YY_START == StrIgnore) REJECT; // ";" ignored in regular yyextra->comments
1005 yyextra->inputStringSemi = " \n"+QCString(yytext+1);
1007 pushBuffer(yyscanner,yyextra->inputStringSemi);
1010 <Initialization,ArrayInitializer>"[" |
1011 <Initialization,ArrayInitializer>"(/" { yyextra->initializer+=yytext;
1012 yyextra->initializerArrayScope++;
1013 BEGIN(ArrayInitializer); // initializer may contain comma
1015 <ArrayInitializer>"]" |
1016 <ArrayInitializer>"/)" { yyextra->initializer+=yytext;
1017 yyextra->initializerArrayScope--;
1018 if (yyextra->initializerArrayScope<=0)
1020 yyextra->initializerArrayScope = 0; // just in case
1021 BEGIN(Initialization);
1024 <ArrayInitializer>. { yyextra->initializer+=yytext; }
1025 <Initialization>"(" { yyextra->initializerScope++;
1026 yyextra->initializer+=yytext;
1028 <Initialization>")" { yyextra->initializerScope--;
1029 yyextra->initializer+=yytext;
1031 <Initialization>{COMMA} { if (yyextra->initializerScope == 0)
1033 updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1034 yy_pop_state(yyscanner); // end initialization
1035 if (yyextra->last_enum)
1037 yyextra->last_enum->initializer.str(yyextra->initializer.str());
1041 if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str());
1046 yyextra->initializer+=", ";
1049 <Initialization>"\n"|"!" { //|
1050 yy_pop_state(yyscanner); // end initialization
1051 if (yyextra->last_enum)
1053 yyextra->last_enum->initializer.str(yyextra->initializer.str());
1057 if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str());
1059 yyextra->colNr -= 1;
1062 <Initialization>. { yyextra->initializer+=yytext; }
1064 <*>{BS}"enum"{BS}","{BS}"bind"{BS}"("{BS}"c"{BS}")"{BS} {
1065 if (YY_START == Start)
1067 addModule(yyscanner);
1068 yy_push_state(ModuleBody,yyscanner); //anon program
1071 yy_push_state(Enum,yyscanner);
1072 yyextra->current->protection = yyextra->defaultProtection;
1073 yyextra->typeProtection = yyextra->defaultProtection;
1074 yyextra->typeMode = true;
1076 yyextra->current->spec |= Entry::Struct;
1077 yyextra->current->name.resize(0);
1078 yyextra->current->args.resize(0);
1079 yyextra->current->name.sprintf("@%d",yyextra->anonCount++);
1081 yyextra->current->section = Entry::ENUM_SEC;
1082 yyextra->current->fileName = yyextra->fileName;
1083 yyextra->current->startLine = yyextra->lineNr;
1084 yyextra->current->bodyLine = yyextra->lineNr;
1085 if ((yyextra->current_root) &&
1086 (yyextra->current_root->section == Entry::CLASS_SEC ||
1087 yyextra->current_root->section == Entry::NAMESPACE_SEC))
1089 yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name;
1092 addCurrentEntry(yyscanner,true);
1093 startScope(yyscanner,yyextra->last_entry.get());
1096 <Enum>"end"{BS}"enum" {
1097 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
1098 if (!endScope(yyscanner,yyextra->current_root))
1102 yyextra->typeMode = false;
1103 yy_pop_state(yyscanner);
1105 /*------ fortran subroutine/function handling ------------------------------------------------------------*/
1106 /* Start is initial condition */
1108 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{TYPE_SPEC}{BS}({PREFIX}{BS_})?/{SUBPROG}{BS_} {
1109 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1111 addInterface(yyscanner,"$interface$", yyextra->ifType);
1112 startScope(yyscanner,yyextra->last_entry.get());
1115 // TYPE_SPEC is for old function style function result
1116 QCString result = QCString(yytext).stripWhiteSpace().lower();
1117 yyextra->current->type = result;
1118 yy_push_state(SubprogPrefix,yyscanner);
1121 <SubprogPrefix>{BS}{SUBPROG}{BS_} {
1122 // Fortran subroutine or function found
1123 yyextra->vtype = V_IGNORE;
1124 QCString result=yytext;
1125 result=result.stripWhiteSpace();
1126 addSubprogram(yyscanner,result);
1128 yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
1129 yyextra->current->startLine = yyextra->lineNr;
1132 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{SUBPROG}{BS_} {
1133 // Fortran subroutine or function found
1134 yyextra->vtype = V_IGNORE;
1135 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1137 addInterface(yyscanner,"$interface$", yyextra->ifType);
1138 startScope(yyscanner,yyextra->last_entry.get());
1141 QCString result = QCString(yytext).stripWhiteSpace();
1142 addSubprogram(yyscanner,result);
1143 yy_push_state(Subprog,yyscanner);
1144 yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
1145 yyextra->current->startLine = yyextra->lineNr;
1148 <Subprog>{BS} { /* ignore white space */ }
1149 <Subprog>{ID} { yyextra->current->name = yytext;
1150 //cout << "1a==========> got " << yyextra->current->type << " " << yytext << " " << yyextra->lineNr << endl;
1151 yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()].returnName = yyextra->current->name.lower();
1153 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1155 yyextra->current_root->name = substitute(
1156 yyextra->current_root->name, "$interface$", yytext);
1159 BEGIN(Parameterlist);
1161 <Parameterlist>"(" { yyextra->current->args = "("; }
1162 <Parameterlist>")" {
1163 yyextra->current->args += ")";
1164 yyextra->current->args = removeRedundantWhiteSpace(yyextra->current->args);
1165 addCurrentEntry(yyscanner,true);
1166 startScope(yyscanner,yyextra->last_entry.get());
1169 <Parameterlist>{COMMA}|{BS} { yyextra->current->args += yytext;
1170 const CommentInPrepass *c = locatePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1173 if (!yyextra->current->argList.empty())
1175 yyextra->current->argList.back().docs = c->str;
1179 <Parameterlist>{ID} {
1180 //yyextra->current->type not yet available
1181 QCString param = yytext;
1182 // std::cout << "3=========> got parameter " << param << "\n";
1183 yyextra->current->args += param;
1186 yyextra->current->argList.push_back(arg);
1188 <Parameterlist>{NOARGS} {
1190 //printf("3=========> without parameterlist \n");
1191 addCurrentEntry(yyscanner,true);
1192 startScope(yyscanner,yyextra->last_entry.get());
1195 <SubprogBody>result{BS}\({BS}{ID} {
1196 if (yyextra->functionLine)
1198 QCString result= yytext;
1199 result= result.right(result.length()-result.find("(")-1);
1200 result= result.stripWhiteSpace();
1201 yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName = result;
1203 //cout << "=====> got result " << result << endl;
1206 /*---- documentation yyextra->comments --------------------------------------------------------------------*/
1208 <Variable,SubprogBody,ModuleBody,TypedefBody,TypedefBodyContains>"!<" { /* backward docu comment */
1209 if (yyextra->vtype != V_IGNORE)
1211 yyextra->current->docLine = yyextra->lineNr;
1212 yyextra->docBlockJavaStyle = FALSE;
1213 yyextra->docBlock.resize(0);
1214 yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF);
1215 startCommentBlock(yyscanner,TRUE);
1216 yy_push_state(DocBackLine,yyscanner);
1220 /* handle out of place !< comment as a normal comment */
1221 if (YY_START == String)
1223 yyextra->colNr -= (int)yyleng;
1225 } // "!" is ignored in strings
1226 // skip comment line (without docu yyextra->comments "!>" "!<" )
1227 /* ignore further "!" and ignore yyextra->comments in Strings */
1228 if ((YY_START != StrIgnore) && (YY_START != String))
1230 yy_push_state(YY_START,yyscanner);
1232 yyextra->debugStr="*!";
1236 <DocBackLine>.* { // contents of yyextra->current comment line
1237 yyextra->docBlock+=yytext;
1239 <DocBackLine>"\n"{BS}"!"("<"|"!"+) { // comment block (next line is also comment line)
1240 yyextra->docBlock+="\n"; // \n is necessary for lists
1243 <DocBackLine>"\n" { // comment block ends at the end of this line
1244 //cout <<"3=========> comment block : "<< yyextra->docBlock << endl;
1245 yyextra->colNr -= 1;
1247 if (yyextra->vtype == V_VARIABLE)
1249 std::shared_ptr<Entry> tmp_entry = yyextra->current;
1250 // temporarily switch to the previous entry
1251 if (yyextra->last_enum)
1253 yyextra->current = yyextra->last_enum;
1257 yyextra->current = yyextra->last_entry;
1259 handleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1261 yyextra->current = tmp_entry;
1263 else if (yyextra->vtype == V_PARAMETER)
1265 subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1267 else if (yyextra->vtype == V_RESULT)
1269 subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE);
1271 yy_pop_state(yyscanner);
1272 yyextra->docBlock.resize(0);
1275 <Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains,TypedefBodyContains,Enum>"!>" {
1276 yy_push_state(YY_START,yyscanner);
1277 yyextra->current->docLine = yyextra->lineNr;
1278 yyextra->docBlockJavaStyle = FALSE;
1279 if (YY_START==SubprogBody) yyextra->docBlockInBody = TRUE;
1280 yyextra->docBlock.resize(0);
1281 yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF);
1282 startCommentBlock(yyscanner,TRUE);
1284 //cout << "start DocBlock " << endl;
1287 <DocBlock>.* { // contents of yyextra->current comment line
1288 yyextra->docBlock+=yytext;
1290 <DocBlock>"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line)
1291 yyextra->docBlock+="\n"; // \n is necessary for lists
1294 <DocBlock>"\n" { // comment block ends at the end of this line
1295 //cout <<"3=========> comment block : "<< yyextra->docBlock << endl;
1296 yyextra->colNr -= 1;
1298 handleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1299 yy_pop_state(yyscanner);
1302 /*-----Prototype parsing -------------------------------------------------------------------------*/
1303 <Prototype>{BS}{SUBPROG}{BS_} {
1304 BEGIN(PrototypeSubprog);
1306 <Prototype,PrototypeSubprog>{BS}{SCOPENAME}?{BS}{ID} {
1307 yyextra->current->name = QCString(yytext).lower();
1308 yyextra->current->name.stripWhiteSpace();
1309 BEGIN(PrototypeArgs);
1312 "("|")"|","|{BS_} { yyextra->current->args += yytext; }
1313 {ID} { yyextra->current->args += yytext;
1315 a.name = QCString(yytext).lower();
1316 yyextra->current->argList.push_back(a);
1320 /*------------------------------------------------------------------------------------------------*/
1324 //if (yyextra->debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << yyextra->debugStr << " state: " <<YY_START << endl;
1325 yyextra->debugStr="";
1329 /*---- error: EOF in wrong state --------------------------------------------------------------------*/
1332 if (yyextra->parsingPrototype)
1336 else if ( yyextra->includeStackPtr <= 0 )
1338 if (YY_START!=INITIAL && YY_START!=Start)
1340 DBG_CTX((stderr,"==== Error: EOF reached in wrong state (end missing)"));
1341 scanner_abort(yyscanner);
1347 popBuffer(yyscanner);
1350 <*>{LOG_OPER} { // Fortran logical comparison keywords
1353 //yyextra->debugStr+=yytext;
1354 //printf("I:%c\n", *yytext);
1355 } // ignore remaining text
1357 /**********************************************************************************/
1358 /**********************************************************************************/
1359 /**********************************************************************************/
1361 //----------------------------------------------------------------------------
1363 static void newLine(yyscan_t yyscanner)
1365 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1367 yyextra->lineNr+=yyextra->lineCountPrepass;
1368 yyextra->lineCountPrepass=0;
1369 yyextra->comments.clear();
1372 static const CommentInPrepass *locatePrepassComment(yyscan_t yyscanner,int from, int to)
1374 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1375 //printf("Locate %d-%d\n", from, to);
1376 for (const auto &cip : yyextra->comments)
1379 //printf("Candidate %d\n", c);
1380 if (c>=from && c<=to)
1382 // comment for previous variable or parameter
1389 static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to)
1391 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1392 const CommentInPrepass *c = locatePrepassComment(yyscanner,from, to);
1393 if (c && yyextra->vtype == V_VARIABLE)
1395 yyextra->last_entry->brief = c->str;
1397 else if (c && yyextra->vtype == V_PARAMETER)
1399 Argument *parameter = getParameter(yyscanner,yyextra->argName);
1400 if (parameter) parameter->docs = c->str;
1404 static int getAmpersandAtTheStart(const char *buf, int length)
1406 for(int i=0; i<length; i++)
1422 /* Returns ampersand index, comment start index or -1 if neither exist.*/
1423 static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch)
1425 // Avoid ampersands in string and yyextra->comments
1426 int parseState = Start;
1427 char quoteSymbol = 0;
1429 int commentIndex = -1;
1431 if (ch != '\0') parseState = String;
1433 for(int i=0; i<length && parseState!=Comment; i++)
1435 // When in string, skip backslashes
1436 // Legacy code, not sure whether this is correct?
1437 if (parseState==String)
1439 if (buf[i]=='\\') i++;
1446 // Close string, if quote symbol matches.
1447 // Quote symbol is set iff parseState==String
1448 if (buf[i]==quoteSymbol)
1453 // Start new string, if not already in string or comment
1454 else if (parseState==Start)
1456 parseState = String;
1457 quoteSymbol = buf[i];
1459 ampIndex = -1; // invalidate prev ampersand
1462 // When in string or comment, ignore exclamation mark
1463 if (parseState==Start)
1465 parseState = Comment;
1469 case ' ': // ignore whitespace
1471 case '\n': // this may be at the end of line
1477 ampIndex = -1; // invalidate prev ampersand
1484 return commentIndex;
1487 /* Although yyextra->comments at the end of continuation line are grabbed by this function,
1488 * we still do not know how to use them later in parsing.
1490 void truncatePrepass(yyscan_t yyscanner,int index)
1492 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1493 int length = yyextra->inputStringPrepass.length();
1494 for (int i=index+1; i<length; i++) {
1495 if (yyextra->inputStringPrepass[i]=='!' && i<length-1 && yyextra->inputStringPrepass[i+1]=='<') { // save comment
1496 yyextra->comments.emplace_back(index, yyextra->inputStringPrepass.right(length-i-2));
1499 yyextra->inputStringPrepass.truncate(index);
1502 /* This function assumes that contents has at least size=length+1 */
1503 static void insertCharacter(char *contents, int length, int pos, char c)
1505 // shift tail by one character
1506 for(int i=length; i>pos; i--)
1507 contents[i]=contents[i-1];
1508 // set the character
1512 /* change yyextra->comments and bring line continuation character to previous line */
1513 /* also used to set continuation marks in case of fortran code usage, done here as it is quite complicated code */
1514 const char* prepassFixedForm(const char* contents, int *hasContLine)
1517 int prevLineLength=0;
1518 int prevLineAmpOrExclIndex=-1;
1520 char prevQuote = '\0';
1521 char thisQuote = '\0';
1522 bool emptyLabel=TRUE;
1523 bool commented=FALSE;
1524 bool inSingle=FALSE;
1525 bool inDouble=FALSE;
1526 bool inBackslash=FALSE;
1527 bool fullCommentLine=TRUE;
1528 bool artificialComment=FALSE;
1530 int newContentsSize = (int)strlen(contents)+3; // \000, \n (when necessary) and one spare character (to avoid reallocation)
1531 char* newContents = (char*)malloc(newContentsSize);
1537 char c = contents[i];
1538 if (artificialComment && c != '\n')
1540 if (c == '!' && spaces)
1542 newContents[j++] = c;
1543 artificialComment = FALSE;
1548 else if (c == ' ' || c == '\t') continue;
1558 if (j>=newContentsSize-3) { // check for spare characters, which may be eventually used below (by & and '! ')
1559 newContents = (char*)realloc(newContents, newContentsSize+1000);
1560 newContentsSize = newContentsSize+1000;
1565 if (!fullCommentLine)
1567 prevLineLength=column;
1568 prevLineAmpOrExclIndex=getAmpOrExclAtTheEnd(&contents[i-prevLineLength+1], prevLineLength,prevQuote);
1569 if (prevLineAmpOrExclIndex == -1) prevLineAmpOrExclIndex = column - 1;
1572 prevLineAmpOrExclIndex = -1;
1578 prevLineLength+=column;
1579 /* Even though a full comment line is not really a comment line it can be seen as one. An empty line is also seen as a comment line (small bonus) */
1582 hasContLine[curLine - 1] = 1;
1585 artificialComment=FALSE;
1587 fullCommentLine=TRUE;
1592 prevQuote = thisQuote;
1605 newContents[j]='\000';
1606 newContentsSize = (int)strlen(newContents);
1607 if (newContents[newContentsSize - 1] != '\n')
1609 // to be on the safe side
1610 newContents = (char*)realloc(newContents, newContentsSize+2);
1611 newContents[newContentsSize] = '\n';
1612 newContents[newContentsSize + 1] = '\000';
1618 if ((column <= fixedCommentAfter) && (column!=6) && !commented)
1620 // we have some special cases in respect to strings and escaped string characters
1621 fullCommentLine=FALSE;
1625 inBackslash = !inBackslash;
1632 inSingle = !inSingle;
1633 if (inSingle) thisQuote = c;
1634 else thisQuote = '\0';
1642 inDouble = !inDouble;
1643 if (inDouble) thisQuote = c;
1644 else thisQuote = '\0';
1649 inBackslash = FALSE;
1656 if ((column <= fixedCommentAfter) && (column!=6))
1664 else if ((c == '!') && !inDouble && !inSingle)
1671 if (!commented) fullCommentLine=FALSE;
1678 if (!commented && (column < 6) && ((c - '0') >= 0) && ((c - '0') <= 9))
1679 { // remove numbers, i.e. labels from first 5 positions.
1682 else if (column==6 && emptyLabel)
1684 if (!commented) fullCommentLine=FALSE;
1686 { // 0 not allowed as continuation character, see f95 standard paragraph 3.3.2.3
1689 if (prevLineAmpOrExclIndex==-1)
1690 { // add & just before end of previous line
1691 /* first line is not a continuation line in code, just in snippets etc. */
1692 if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-1, '&');
1696 { // add & just before end of previous line comment
1697 /* first line is not a continuation line in code, just in snippets etc. */
1698 if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-prevLineLength+prevLineAmpOrExclIndex+skipped, '&');
1704 hasContLine[curLine - 1] = 1;
1709 newContents[j]=c; // , just handle like space
1713 else if ((column > fixedCommentAfter) && !commented)
1715 // first non commented non blank character after position fixedCommentAfter
1722 // I'm not a possible start of doxygen comment
1724 artificialComment = TRUE;
1736 if (!commented) fullCommentLine=FALSE;
1749 newContentsSize = (int)strlen(newContents);
1750 if (newContents[newContentsSize - 1] != '\n')
1752 // to be on the safe side
1753 newContents = (char*)realloc(newContents, newContentsSize+2);
1754 newContents[newContentsSize] = '\n';
1755 newContents[newContentsSize + 1] = '\000';
1760 static void pushBuffer(yyscan_t yyscanner,const QCString &buffer)
1762 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1763 if (yyextra->includeStackCnt <= yyextra->includeStackPtr)
1765 yyextra->includeStackCnt++;
1766 yyextra->includeStack = (YY_BUFFER_STATE *)realloc(yyextra->includeStack, yyextra->includeStackCnt * sizeof(YY_BUFFER_STATE));
1768 yyextra->includeStack[yyextra->includeStackPtr++] = YY_CURRENT_BUFFER;
1769 yy_switch_to_buffer(yy_scan_string(buffer.data(),yyscanner),yyscanner);
1771 DBG_CTX((stderr, "--PUSH--%s", qPrint(buffer)));
1774 static void popBuffer(yyscan_t yyscanner)
1776 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1777 DBG_CTX((stderr, "--POP--"));
1778 yyextra->includeStackPtr --;
1779 yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
1780 yy_switch_to_buffer( yyextra->includeStack[yyextra->includeStackPtr], yyscanner );
1783 /** used to copy entry to an interface module procedure */
1784 static void copyEntry(std::shared_ptr<Entry> dest, const std::shared_ptr<Entry> &src)
1786 dest->type = src->type;
1787 dest->fileName = src->fileName;
1788 dest->startLine = src->startLine;
1789 dest->bodyLine = src->bodyLine;
1790 dest->endBodyLine = src->endBodyLine;
1791 dest->args = src->args;
1792 dest->argList = src->argList;
1793 dest->doc = src->doc;
1794 dest->brief = src->brief;
1797 /** fill empty interface module procedures with info from
1798 corresponding module subprogs
1800 TODO: handle procedures in used modules
1802 void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root)
1804 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1805 for (const auto &ce1 : yyextra->moduleProcedures)
1807 // check all entries in this module
1808 for (const auto &ce2 : current_root->children())
1810 if (ce1->name == ce2->name)
1812 copyEntry(ce1, ce2);
1814 } // for procedures in yyextra->current module
1815 } // for all interface module procedures
1816 yyextra->moduleProcedures.clear();
1819 /*! Extracts string which resides within parentheses of provided string. */
1820 static QCString extractFromParens(const QCString &name)
1822 QCString extracted = name;
1823 int start = extracted.find("(");
1826 extracted.remove(0, start+1);
1828 int end = extracted.findRev(")");
1831 int length = extracted.length();
1832 extracted.remove(end, length);
1834 extracted = extracted.stripWhiteSpace();
1839 /*! remove useless spaces from bind statement */
1840 static QCString extractBind(const QCString &name)
1842 QCString parensPart = extractFromParens(name);
1843 if (parensPart.length() == 1)
1850 parensPart = parensPart.mid(1).stripWhiteSpace();
1852 parensPart = parensPart.mid(1).stripWhiteSpace();
1854 parensPart = parensPart.mid(4).stripWhiteSpace();
1856 parensPart = parensPart.mid(1).stripWhiteSpace();
1858 return "bind(C, name=" + parensPart + ")";
1862 /*! Adds passed yyextra->modifiers to these yyextra->modifiers.*/
1863 SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs)
1865 if (mdfs.protection!=NONE_P) protection = mdfs.protection;
1866 if (mdfs.direction!=NONE_D) direction = mdfs.direction;
1867 optional |= mdfs.optional;
1868 if (!mdfs.dimension.isNull()) dimension = mdfs.dimension;
1869 allocatable |= mdfs.allocatable;
1870 external |= mdfs.external;
1871 intrinsic |= mdfs.intrinsic;
1872 protect |= mdfs.protect;
1873 parameter |= mdfs.parameter;
1874 pointer |= mdfs.pointer;
1875 target |= mdfs.target;
1877 deferred |= mdfs.deferred;
1878 nonoverridable |= mdfs.nonoverridable;
1879 nopass |= mdfs.nopass;
1881 passVar = mdfs.passVar;
1882 bindVar = mdfs.bindVar;
1883 contiguous |= mdfs.contiguous;
1884 volat |= mdfs.volat;
1885 value |= mdfs.value;
1889 /*! Extracts and adds passed modifier to these yyextra->modifiers.*/
1890 SymbolModifiers& SymbolModifiers::operator|=(QCString mdfStringArg)
1892 QCString mdfString = mdfStringArg.lower();
1893 SymbolModifiers newMdf;
1895 if (mdfString.find("dimension")==0)
1897 newMdf.dimension=mdfString;
1899 else if (mdfString.contains("intent"))
1901 QCString tmp = extractFromParens(mdfString);
1902 bool isin = tmp.contains("in");
1903 bool isout = tmp.contains("out");
1904 if (isin && isout) newMdf.direction = SymbolModifiers::INOUT;
1905 else if (isin) newMdf.direction = SymbolModifiers::IN;
1906 else if (isout) newMdf.direction = SymbolModifiers::OUT;
1908 else if (mdfString=="public")
1910 newMdf.protection = SymbolModifiers::PUBLIC;
1912 else if (mdfString=="private")
1914 newMdf.protection = SymbolModifiers::PRIVATE;
1916 else if (mdfString=="protected")
1918 newMdf.protect = TRUE;
1920 else if (mdfString=="optional")
1922 newMdf.optional = TRUE;
1924 else if (mdfString=="allocatable")
1926 newMdf.allocatable = TRUE;
1928 else if (mdfString=="external")
1930 newMdf.external = TRUE;
1932 else if (mdfString=="intrinsic")
1934 newMdf.intrinsic = TRUE;
1936 else if (mdfString=="parameter")
1938 newMdf.parameter = TRUE;
1940 else if (mdfString=="pointer")
1942 newMdf.pointer = TRUE;
1944 else if (mdfString=="target")
1946 newMdf.target = TRUE;
1948 else if (mdfString=="save")
1952 else if (mdfString=="nopass")
1954 newMdf.nopass = TRUE;
1956 else if (mdfString=="deferred")
1958 newMdf.deferred = TRUE;
1960 else if (mdfString=="non_overridable")
1962 newMdf.nonoverridable = TRUE;
1964 else if (mdfString=="contiguous")
1966 newMdf.contiguous = TRUE;
1968 else if (mdfString=="volatile")
1970 newMdf.volat = TRUE;
1972 else if (mdfString=="value")
1974 newMdf.value = TRUE;
1976 else if (mdfString.contains("pass"))
1979 if (mdfString.contains("("))
1980 newMdf.passVar = extractFromParens(mdfString);
1982 newMdf.passVar = "";
1984 else if (mdfString.startsWith("bind"))
1986 // we need here the original string as we want to don't want to have the lowercase name between the quotes of the name= part
1987 newMdf.bindVar = extractBind(mdfStringArg);
1994 /*! For debugging purposes. */
1995 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs)
1997 // out<<mdfs.protection<<", "<<mdfs.direction<<", "<<mdfs.optional<<
1998 // ", "<<(mdfs.dimension.isNull() ? "" : mdfs.dimension.latin1())<<
1999 // ", "<<mdfs.allocatable<<", "<<mdfs.external<<", "<<mdfs.intrinsic;
2004 /*! Find argument with given name in \a subprog entry. */
2005 static Argument *findArgument(Entry* subprog, QCString name, bool byTypeName = FALSE)
2007 QCString cname(name.lower());
2008 for (Argument &arg : subprog->argList)
2010 if ((!byTypeName && arg.name.lower() == cname) ||
2011 (byTypeName && arg.type.lower() == cname)
2021 /*! Apply yyextra->modifiers stored in \a mdfs to the \a typeName string. */
2022 static QCString applyModifiers(QCString typeName, const SymbolModifiers& mdfs)
2024 if (!mdfs.dimension.isNull())
2026 if (!typeName.isEmpty()) typeName += ", ";
2027 typeName += mdfs.dimension;
2029 if (mdfs.direction!=SymbolModifiers::NONE_D)
2031 if (!typeName.isEmpty()) typeName += ", ";
2032 typeName += directionStrs[mdfs.direction];
2036 if (!typeName.isEmpty()) typeName += ", ";
2037 typeName += "optional";
2039 if (mdfs.allocatable)
2041 if (!typeName.isEmpty()) typeName += ", ";
2042 typeName += "allocatable";
2046 if (!typeName.contains("external"))
2048 if (!typeName.isEmpty()) typeName += ", ";
2049 typeName += "external";
2054 if (!typeName.isEmpty()) typeName += ", ";
2055 typeName += "intrinsic";
2059 if (!typeName.isEmpty()) typeName += ", ";
2060 typeName += "parameter";
2064 if (!typeName.isEmpty()) typeName += ", ";
2065 typeName += "pointer";
2069 if (!typeName.isEmpty()) typeName += ", ";
2070 typeName += "target";
2074 if (!typeName.isEmpty()) typeName += ", ";
2079 if (!typeName.isEmpty()) typeName += ", ";
2080 typeName += "deferred";
2082 if (mdfs.nonoverridable)
2084 if (!typeName.isEmpty()) typeName += ", ";
2085 typeName += "non_overridable";
2089 if (!typeName.isEmpty()) typeName += ", ";
2090 typeName += "nopass";
2094 if (!typeName.isEmpty()) typeName += ", ";
2096 if (!mdfs.passVar.isEmpty())
2097 typeName += "(" + mdfs.passVar + ")";
2099 if (!mdfs.bindVar.isEmpty())
2101 if (!typeName.isEmpty()) typeName += ", ";
2102 typeName += mdfs.bindVar;
2104 if (mdfs.protection == SymbolModifiers::PUBLIC)
2106 if (!typeName.isEmpty()) typeName += ", ";
2107 typeName += "public";
2109 else if (mdfs.protection == SymbolModifiers::PRIVATE)
2111 if (!typeName.isEmpty()) typeName += ", ";
2112 typeName += "private";
2116 if (!typeName.isEmpty()) typeName += ", ";
2117 typeName += "protected";
2119 if (mdfs.contiguous)
2121 if (!typeName.isEmpty()) typeName += ", ";
2122 typeName += "contiguous";
2126 if (!typeName.isEmpty()) typeName += ", ";
2127 typeName += "volatile";
2131 if (!typeName.isEmpty()) typeName += ", ";
2132 typeName += "value";
2138 /*! Apply yyextra->modifiers stored in \a mdfs to the \a arg argument. */
2139 static void applyModifiers(Argument *arg, const SymbolModifiers& mdfs)
2141 QCString tmp = arg->type;
2142 arg->type = applyModifiers(tmp, mdfs);
2145 /*! Apply yyextra->modifiers stored in \a mdfs to the \a ent entry. */
2146 static void applyModifiers(Entry *ent, const SymbolModifiers& mdfs)
2148 QCString tmp = ent->type;
2149 ent->type = applyModifiers(tmp, mdfs);
2151 if (mdfs.protection == SymbolModifiers::PUBLIC)
2152 ent->protection = Public;
2153 else if (mdfs.protection == SymbolModifiers::PRIVATE)
2154 ent->protection = Private;
2157 /*! Starts the new scope in fortran program. Consider using this function when
2158 * starting module, interface, function or other program block.
2161 static void startScope(yyscan_t yyscanner,Entry *scope)
2163 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2164 //cout<<"start scope: "<<scope->name<<endl;
2165 yyextra->current_root= scope; /* start substructure */
2167 yyextra->modifiers.insert(std::make_pair(scope, std::map<std::string,SymbolModifiers>()));
2170 /*! Ends scope in fortran program: may update subprogram arguments or module variable attributes.
2173 static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot)
2175 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2176 if (yyextra->global_scope == scope)
2178 yyextra->global_scope = 0;
2181 if (yyextra->global_scope == INVALID_ENTRY)
2185 //cout<<"end scope: "<<scope->name<<endl;
2186 if (yyextra->current_root->parent() || isGlobalRoot)
2188 yyextra->current_root= yyextra->current_root->parent(); /* end substructure */
2190 else // if (yyextra->current_root != scope)
2192 fprintf(stderr,"parse error in end <scopename>\n");
2193 scanner_abort(yyscanner);
2197 // update variables or subprogram arguments with yyextra->modifiers
2198 std::map<std::string,SymbolModifiers>& mdfsMap = yyextra->modifiers[scope];
2200 if (scope->section == Entry::FUNCTION_SEC)
2202 // iterate all symbol yyextra->modifiers of the scope
2203 for (const auto &kv : mdfsMap)
2205 //cout<<it.key()<<": "<<qPrint(it)<<endl;
2206 Argument *arg = findArgument(scope, QCString(kv.first));
2210 applyModifiers(arg, kv.second);
2214 // find return type for function
2215 //cout<<"RETURN NAME "<<yyextra->modifiers[yyextra->current_root][scope->name.lower()].returnName<<endl;
2216 QCString returnName = yyextra->modifiers[yyextra->current_root][scope->name.lower().str()].returnName.lower();
2217 if (yyextra->modifiers[scope].find(returnName.str())!=yyextra->modifiers[scope].end())
2219 scope->type = yyextra->modifiers[scope][returnName.str()].type; // returning type works
2220 applyModifiers(scope, yyextra->modifiers[scope][returnName.str()]); // returning array works
2224 if (scope->section == Entry::CLASS_SEC)
2225 { // was INTERFACE_SEC
2226 if (scope->parent()->section == Entry::FUNCTION_SEC)
2227 { // interface within function
2228 // iterate functions of interface and
2229 // try to find types for dummy(ie. argument) procedures.
2230 //cout<<"Search in "<<scope->name<<endl;
2233 for (const auto &ce : scope->children())
2236 if (ce->section != Entry::FUNCTION_SEC)
2239 Argument *arg = findArgument(scope->parent(), ce->name, TRUE);
2242 // set type of dummy procedure argument to interface
2243 arg->name = arg->type;
2244 arg->type = scope->name;
2246 if (ce->name.lower() == scope->name.lower()) found = TRUE;
2248 if ((count == 1) && found)
2250 // clear all yyextra->modifiers of the scope
2251 yyextra->modifiers.erase(scope);
2252 scope->parent()->removeSubEntry(scope);
2258 if (scope->section!=Entry::FUNCTION_SEC)
2259 { // not function section
2260 // iterate variables: get and apply yyextra->modifiers
2261 for (const auto &ce : scope->children())
2263 if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC)
2266 //cout<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl;
2267 if (mdfsMap.find(ce->name.lower().str())!=mdfsMap.end())
2268 applyModifiers(ce.get(), mdfsMap[ce->name.lower().str()]);
2272 // clear all yyextra->modifiers of the scope
2273 yyextra->modifiers.erase(scope);
2278 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size)
2280 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2282 while ( c < max_size && yyextra->inputString[yyextra->inputPosition] )
2284 *buf = yyextra->inputString[yyextra->inputPosition++] ;
2290 static void initParser(yyscan_t yyscanner)
2292 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2293 yyextra->last_entry.reset();
2296 static void initEntry(yyscan_t yyscanner)
2298 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2299 if (yyextra->typeMode)
2301 yyextra->current->protection = yyextra->typeProtection;
2305 yyextra->current->protection = yyextra->defaultProtection;
2307 yyextra->current->mtype = Method;
2308 yyextra->current->virt = Normal;
2309 yyextra->current->stat = FALSE;
2310 yyextra->current->lang = SrcLangExt_Fortran;
2311 yyextra->commentScanner.initGroupInfo(yyextra->current.get());
2315 adds yyextra->current entry to yyextra->current_root and creates new yyextra->current
2317 static void addCurrentEntry(yyscan_t yyscanner,bool case_insens)
2319 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2320 if (case_insens) yyextra->current->name = yyextra->current->name.lower();
2321 //printf("===Adding entry %s to %s\n", qPrint(yyextra->current->name), qPrint(yyextra->current_root->name));
2322 yyextra->last_entry = yyextra->current;
2323 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
2324 initEntry(yyscanner);
2327 static void addModule(yyscan_t yyscanner,const QCString &name, bool isModule)
2329 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2330 DBG_CTX((stderr, "0=========> got module %s\n", qPrint(name)));
2333 yyextra->current->section = Entry::NAMESPACE_SEC;
2335 yyextra->current->section = Entry::FUNCTION_SEC;
2337 if (!name.isEmpty())
2339 yyextra->current->name = name;
2343 QCString fname = yyextra->fileName;
2344 int index = std::max(fname.findRev('/'), fname.findRev('\\'));
2345 fname = fname.right(fname.length()-index-1);
2346 fname = fname.prepend("__").append("__");
2347 yyextra->current->name = fname;
2349 yyextra->current->type = "program";
2350 yyextra->current->fileName = yyextra->fileName;
2351 yyextra->current->bodyLine = yyextra->lineNr; // used for source reference
2352 yyextra->current->startLine = yyextra->lineNr;
2353 yyextra->current->protection = Public ;
2354 addCurrentEntry(yyscanner,true);
2355 startScope(yyscanner,yyextra->last_entry.get());
2359 static void addSubprogram(yyscan_t yyscanner,const QCString &text)
2361 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2362 DBG_CTX((stderr,"1=========> got subprog, type: %s\n",qPrint(text)));
2363 yyextra->subrCurrent.push_back(yyextra->current);
2364 yyextra->current->section = Entry::FUNCTION_SEC ;
2365 QCString subtype = text; subtype=subtype.lower().stripWhiteSpace();
2366 yyextra->functionLine = (subtype.find("function") != -1);
2367 yyextra->current->type += " " + subtype;
2368 yyextra->current->type = yyextra->current->type.stripWhiteSpace();
2369 yyextra->current->fileName = yyextra->fileName;
2370 yyextra->current->bodyLine = yyextra->lineNr; // used for source reference start of body of routine
2371 yyextra->current->startLine = yyextra->lineNr; // used for source reference start of definition
2372 yyextra->current->args.resize(0);
2373 yyextra->current->argList.clear();
2374 yyextra->docBlock.resize(0);
2377 /*! Adds interface to the root entry.
2378 * \note Code was brought to this procedure from the parser,
2379 * because there was/is idea to use it in several parts of the parser.
2381 static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type)
2383 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2384 if (YY_START == Start)
2386 addModule(yyscanner);
2387 yy_push_state(ModuleBody,yyscanner); //anon program
2390 yyextra->current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC;
2391 yyextra->current->spec = Entry::Interface;
2392 yyextra->current->name = name;
2397 yyextra->current->type = "abstract";
2401 yyextra->current->type = "generic";
2407 yyextra->current->type = "";
2410 /* if type is part of a module, mod name is necessary for output */
2411 if ((yyextra->current_root) &&
2412 (yyextra->current_root->section == Entry::CLASS_SEC ||
2413 yyextra->current_root->section == Entry::NAMESPACE_SEC))
2415 yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name;
2418 yyextra->current->fileName = yyextra->fileName;
2419 yyextra->current->bodyLine = yyextra->lineNr;
2420 yyextra->current->startLine = yyextra->lineNr;
2421 addCurrentEntry(yyscanner,true);
2425 //-----------------------------------------------------------------------------
2427 /*! Get the argument \a name.
2429 static Argument *getParameter(yyscan_t yyscanner,const QCString &name)
2431 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2432 // std::cout<<"addFortranParameter(): "<<name<<" DOCS:"<<(docs.isNull()?QCString("null"):docs)<<"\n";
2434 for (Argument &a:yyextra->current_root->argList)
2436 if (a.name.lower()==name.lower())
2439 //printf("parameter found: %s\n",(const char*)name);
2446 //----------------------------------------------------------------------------
2447 static void startCommentBlock(yyscan_t yyscanner,bool brief)
2449 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2452 yyextra->current->briefFile = yyextra->fileName;
2453 yyextra->current->briefLine = yyextra->lineNr;
2457 yyextra->current->docFile = yyextra->fileName;
2458 yyextra->current->docLine = yyextra->lineNr;
2462 //----------------------------------------------------------------------------
2464 static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief)
2466 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2467 bool hideInBodyDocs = Config_getBool(HIDE_IN_BODY_DOCS);
2468 if (yyextra->docBlockInBody && hideInBodyDocs)
2470 yyextra->docBlockInBody = FALSE;
2473 DBG_CTX((stderr,"call parseCommentBlock [%s]\n",qPrint(doc)));
2474 int lineNr = brief ? yyextra->current->briefLine : yyextra->current->docLine;
2476 bool needsEntry = FALSE;
2477 Markdown markdown(yyextra->fileName,lineNr);
2478 QCString processedDoc = Config_getBool(MARKDOWN_SUPPORT) ? markdown.process(doc,lineNr) : doc;
2479 while (yyextra->commentScanner.parseCommentBlock(
2480 yyextra->thisParser,
2481 yyextra->docBlockInBody ? yyextra->subrCurrent.back().get() : yyextra->current.get(),
2482 processedDoc, // text
2483 yyextra->fileName, // file
2485 yyextra->docBlockInBody ? FALSE : brief,
2486 yyextra->docBlockInBody ? FALSE : yyextra->docBlockJavaStyle,
2487 yyextra->docBlockInBody,
2488 yyextra->defaultProtection,
2491 Config_getBool(MARKDOWN_SUPPORT)
2494 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2495 if (needsEntry) addCurrentEntry(yyscanner,false);
2497 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2499 if (needsEntry) addCurrentEntry(yyscanner,false);
2500 yyextra->docBlockInBody = FALSE;
2503 //----------------------------------------------------------------------------
2504 /// Handle parameter description as defined after the declaration of the parameter
2505 static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief)
2507 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2509 loc_doc = doc.stripWhiteSpace();
2511 std::shared_ptr<Entry> tmp_entry = yyextra->current;
2512 yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function
2514 // Still in the specification section so no inbodyDocs yet, but parameter documentation
2515 yyextra->current->inbodyDocs = "";
2517 // strip \\param or @param, so we can do some extra checking. We will add it later on again.
2518 if (!loc_doc.stripPrefix("\\param") &&
2519 !loc_doc.stripPrefix("@param")
2520 ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning
2521 loc_doc.stripWhiteSpace();
2523 // direction as defined with the declaration of the parameter
2524 int dir1 = yyextra->modifiers[yyextra->current_root][yyextra->argName.lower().str()].direction;
2525 // in description [in] is specified
2526 if (loc_doc.lower().find(directionParam[SymbolModifiers::IN]) == 0)
2528 // check if with the declaration intent(in) or nothing has been specified
2529 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2530 (directionParam[dir1] == directionParam[SymbolModifiers::IN]))
2533 loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::IN]));
2534 loc_doc.stripWhiteSpace();
2535 // in case of empty documentation or (now) just name, consider it as no documentation
2536 if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2538 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::IN] + " " +
2539 yyextra->argName + " " + loc_doc,brief);
2544 // something different specified, give warning and leave error.
2545 warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
2546 qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
2547 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2548 yyextra->argName + " " + loc_doc,brief);
2551 // analogous to the [in] case, here [out] direction specified
2552 else if (loc_doc.lower().find(directionParam[SymbolModifiers::OUT]) == 0)
2554 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2555 (directionParam[dir1] == directionParam[SymbolModifiers::OUT]))
2557 loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::OUT]));
2558 loc_doc.stripWhiteSpace();
2559 if (loc_doc.isEmpty() || (loc_doc.lower() == yyextra->argName.lower()))
2561 yyextra->current = tmp_entry;
2564 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::OUT] + " " +
2565 yyextra->argName + " " + loc_doc,brief);
2569 warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
2570 qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
2571 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2572 yyextra->argName + " " + loc_doc,brief);
2575 // analogous to the [in] case, here [in,out] direction specified
2576 else if (loc_doc.lower().find(directionParam[SymbolModifiers::INOUT]) == 0)
2578 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2579 (directionParam[dir1] == directionParam[SymbolModifiers::INOUT]))
2581 loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::INOUT]));
2582 loc_doc.stripWhiteSpace();
2583 if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2585 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::INOUT] + " " +
2586 yyextra->argName + " " + loc_doc,brief);
2591 warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
2592 qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
2593 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2594 yyextra->argName + " " + loc_doc,brief);
2597 // analogous to the [in] case; here no direction specified
2598 else if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2600 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
2601 yyextra->argName + " " + loc_doc,brief);
2604 // reset yyextra->current back to the part inside the routine
2605 yyextra->current = tmp_entry;
2607 //----------------------------------------------------------------------------
2608 /// Handle result description as defined after the declaration of the parameter
2609 static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief)
2611 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2613 loc_doc = doc.stripWhiteSpace();
2615 std::shared_ptr<Entry> tmp_entry = yyextra->current;
2616 yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function
2618 // Still in the specification section so no inbodyDocs yet, but parameter documentation
2619 yyextra->current->inbodyDocs = "";
2621 // strip \\returns or @returns. We will add it later on again.
2622 if (!loc_doc.stripPrefix("\\returns") &&
2623 !loc_doc.stripPrefix("\\return") &&
2624 !loc_doc.stripPrefix("@returns") &&
2625 !loc_doc.stripPrefix("@return")
2626 ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning
2627 loc_doc.stripWhiteSpace();
2629 if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
2631 handleCommentBlock(yyscanner,QCString("\n\n@returns ") + loc_doc,brief);
2634 // reset yyextra->current back to the part inside the routine
2635 yyextra->current = tmp_entry;
2638 //----------------------------------------------------------------------------
2640 static void parseMain(yyscan_t yyscanner, const QCString &fileName,const char *fileBuf,
2641 const std::shared_ptr<Entry> &rt, FortranFormat format)
2643 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2644 char *tmpBuf = nullptr;
2645 initParser(yyscanner);
2647 if (fileBuf==0 || fileBuf[0]=='\0') return;
2649 yyextra->defaultProtection = Public;
2650 yyextra->inputString = fileBuf;
2651 yyextra->inputPosition = 0;
2652 yyextra->inputStringPrepass = nullptr;
2653 yyextra->inputPositionPrepass = 0;
2655 //yyextra->anonCount = 0; // don't reset per file
2656 yyextra->current_root = rt.get();
2657 yyextra->global_root = rt;
2659 yyextra->isFixedForm = recognizeFixedForm(fileBuf,format);
2661 if (yyextra->isFixedForm)
2663 msg("Prepassing fixed form of %s\n", qPrint(fileName));
2664 //printf("---strlen=%d\n", strlen(fileBuf));
2665 //clock_t start=clock();
2667 //printf("Input fixed form string:\n%s\n", fileBuf);
2668 //printf("===========================\n");
2669 yyextra->inputString = prepassFixedForm(fileBuf, nullptr);
2670 Debug::print(Debug::FortranFixed2Free,0,"======== Fixed to Free format =========\n---- Input fixed form string ------- \n%s\n", fileBuf);
2671 Debug::print(Debug::FortranFixed2Free,0,"---- Resulting free form string ------- \n%s\n", yyextra->inputString);
2672 //printf("Resulting free form string:\n%s\n", yyextra->inputString);
2673 //printf("===========================\n");
2675 //clock_t end=clock();
2676 //printf("CPU time used=%f\n", ((double) (end-start))/CLOCKS_PER_SEC);
2678 else if (yyextra->inputString[strlen(fileBuf)-1] != '\n')
2680 tmpBuf = (char *)malloc(strlen(fileBuf)+2);
2681 strcpy(tmpBuf,fileBuf);
2682 tmpBuf[strlen(fileBuf)]= '\n';
2683 tmpBuf[strlen(fileBuf)+1]= '\000';
2684 yyextra->inputString = tmpBuf;
2687 yyextra->lineNr= 1 ;
2688 yyextra->fileName = fileName;
2689 msg("Parsing file %s...\n",qPrint(yyextra->fileName));
2691 yyextra->global_scope = rt.get();
2692 startScope(yyscanner,rt.get()); // implies yyextra->current_root = rt
2693 initParser(yyscanner);
2694 yyextra->commentScanner.enterFile(yyextra->fileName,yyextra->lineNr);
2696 // add entry for the file
2697 yyextra->current = std::make_shared<Entry>();
2698 yyextra->current->lang = SrcLangExt_Fortran;
2699 yyextra->current->name = yyextra->fileName;
2700 yyextra->current->section = Entry::SOURCE_SEC;
2701 yyextra->file_root = yyextra->current;
2702 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
2703 yyextra->current->lang = SrcLangExt_Fortran;
2705 fortranscannerYYrestart( 0, yyscanner );
2710 fortranscannerYYlex(yyscanner);
2711 yyextra->commentScanner.leaveFile(yyextra->fileName,yyextra->lineNr);
2713 if (yyextra->global_scope && yyextra->global_scope != INVALID_ENTRY)
2715 endScope(yyscanner,yyextra->current_root, TRUE); // TRUE - global root
2718 //debugCompounds(rt); //debug
2720 rt->program.str(std::string());
2721 //delete yyextra->current; yyextra->current=0;
2722 yyextra->moduleProcedures.clear();
2725 free((char*)tmpBuf);
2726 yyextra->inputString=NULL;
2728 if (yyextra->isFixedForm)
2730 free((char*)yyextra->inputString);
2731 yyextra->inputString=NULL;
2736 //----------------------------------------------------------------------------
2738 struct FortranOutlineParser::Private
2741 fortranscannerYY_state extra;
2742 FortranFormat format;
2743 Private(FortranFormat fmt) : format(fmt)
2745 fortranscannerYYlex_init_extra(&extra,&yyscanner);
2747 fortranscannerYYset_debug(1,yyscanner);
2752 fortranscannerYYlex_destroy(yyscanner);
2756 FortranOutlineParser::FortranOutlineParser(FortranFormat format)
2757 : p(std::make_unique<Private>(format))
2761 FortranOutlineParser::~FortranOutlineParser()
2765 void FortranOutlineParser::parseInput(const QCString &fileName,
2766 const char *fileBuf,
2767 const std::shared_ptr<Entry> &root,
2768 ClangTUParser * /*clangParser*/)
2770 struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
2771 yyextra->thisParser = this;
2773 printlex(yy_flex_debug, TRUE, __FILE__, qPrint(fileName));
2775 ::parseMain(p->yyscanner,fileName,fileBuf,root,p->format);
2777 printlex(yy_flex_debug, FALSE, __FILE__, qPrint(fileName));
2780 bool FortranOutlineParser::needsPreprocessing(const QCString &extension) const
2782 return extension!=extension.lower(); // use preprocessor only for upper case extensions
2785 void FortranOutlineParser::parsePrototype(const QCString &text)
2787 struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
2788 pushBuffer(p->yyscanner,text);
2789 yyextra->parsingPrototype = TRUE;
2791 fortranscannerYYlex(p->yyscanner);
2792 yyextra->parsingPrototype = FALSE;
2793 popBuffer(p->yyscanner);
2796 //----------------------------------------------------------------------------
2798 static void scanner_abort(yyscan_t yyscanner)
2800 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2801 fprintf(stderr,"********************************************************************\n");
2802 fprintf(stderr,"Error in file %s line: %d, state: %d(%s)\n",qPrint(yyextra->fileName),yyextra->lineNr,YY_START,stateToString(YY_START));
2803 fprintf(stderr,"********************************************************************\n");
2807 for (const auto &ce : yyextra->global_root->children())
2809 if (ce == yyextra->file_root) start=TRUE;
2810 if (start) ce->reset();
2813 // dummy call to avoid compiler warning
2814 (void)yy_top_state(yyscanner);
2820 //----------------------------------------------------------------------------
2822 #include "fortranscanner.l.h"