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