/* -*- mode: fundamental; indent-tabs-mode: 1; -*- */ /***************************************************************************** * Parser for Fortran90 F subset * * Copyright (C) by Anke Visser * based on the work of Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. * */ /* Developer notes. * * - Consider using startScope(), endScope() functions with module, program, * subroutine or any other scope in fortran program. * * - Symbol yyextra->modifiers (attributes) are collected using SymbolModifiers |= operator during * substructure parsing. When substructure ends all yyextra->modifiers are applied to actual * entries in applyModifiers() functions. * * - How case insensitiveness should be handled in code? * On one side we have arg->name and entry->name, on another side modifierMap[name]. * In entries and arguments case is the same as in code, in modifier map case is lowered and * then it is compared to lowered entry/argument names. * * - Do not like constructs like aa{BS} or {BS}bb. Should try to handle blank space * with separate rule?: It seems it is often necessary, because we may parse something like * "functionA" or "MyInterface". So constructs like '(^|[ \t])interface({BS_}{ID})?/[ \t\n]' * are desired. * * - Must track yyextra->lineNr when using REJECT, unput() or similar commands. */ %option never-interactive %option case-insensitive %option prefix="fortranscannerYY" %option reentrant %option extra-type="struct fortranscannerYY_state *" %top{ #include // forward declare yyscan_t to improve type safety #define YY_TYPEDEF_YY_SCANNER_T struct yyguts_t; typedef yyguts_t *yyscan_t; } %{ #include #include #include #include #include #include #include "fortranscanner.h" #include "entry.h" #include "message.h" #include "config.h" #include "doxygen.h" #include "util.h" #include "defargs.h" #include "language.h" #include "commentscan.h" #include "pre.h" #include "arguments.h" #include "debug.h" #include "markdown.h" // Toggle for some debugging info //#define DBG_CTX(x) fprintf x #define DBG_CTX(x) do { } while(0) #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 enum ScanVar { V_IGNORE, V_VARIABLE, V_PARAMETER, V_RESULT}; enum InterfaceType { IF_NONE, IF_SPECIFIC, IF_GENERIC, IF_ABSTRACT }; // {{{ ----- Helper structs ----- //! Holds yyextra->modifiers (ie attributes) for one symbol (variable, function, etc) struct SymbolModifiers { enum Protection {NONE_P, PUBLIC, PRIVATE}; enum Direction {NONE_D, IN, OUT, INOUT}; //! This is only used with function return value. QCString type, returnName; Protection protection; Direction direction; bool optional; bool protect; QCString dimension; bool allocatable; bool external; bool intrinsic; bool parameter; bool pointer; bool target; bool save; bool deferred; bool nonoverridable; bool nopass; bool pass; bool contiguous; bool volat; /* volatile is a reserved name */ bool value; /* volatile is a reserved name */ QCString passVar; QCString bindVar; SymbolModifiers() : type(), returnName(), protection(NONE_P), direction(NONE_D), optional(FALSE), protect(FALSE), dimension(), allocatable(FALSE), external(FALSE), intrinsic(FALSE), parameter(FALSE), pointer(FALSE), target(FALSE), save(FALSE), deferred(FALSE), nonoverridable(FALSE), nopass(FALSE), pass(FALSE), contiguous(FALSE), volat(FALSE), value(FALSE), passVar(), bindVar() {} SymbolModifiers& operator|=(const SymbolModifiers &mdfs); SymbolModifiers& operator|=(QCString mdfrString); }; //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs); static const char *directionStrs[] = { "", "intent(in)", "intent(out)", "intent(inout)" }; static const char *directionParam[] = { "", "[in]", "[out]", "[in,out]" }; // }}} struct CommentInPrepass { int column; QCString str; CommentInPrepass(int col, QCString s) : column(col), str(s) {} }; /* ----------------------------------------------------------------- * * statics */ struct fortranscannerYY_state { OutlineParserInterface * thisParser; CommentScanner commentScanner; const char * inputString; int inputPosition; bool isFixedForm; QCString inputStringPrepass; ///< Input string for prepass of line cont. '&' QCString inputStringSemi; ///< Input string after command separator ';' unsigned int inputPositionPrepass; int lineCountPrepass = 0; EntryList subrCurrent; std::vector comments; YY_BUFFER_STATE * includeStack = NULL; int includeStackPtr = 0; int includeStackCnt = 0; QCString fileName; int lineNr = 1 ; int colNr = 0 ; Entry *current_root = 0; Entry *global_scope = 0; std::shared_ptr global_root; std::shared_ptr file_root; std::shared_ptr last_entry; std::shared_ptr last_enum; std::shared_ptr current; ScanVar vtype = V_IGNORE; // type of parsed variable EntryList moduleProcedures; // list of all interfaces which contain unresolved module procedures QCString docBlock; bool docBlockInBody = FALSE; bool docBlockJavaStyle; QCString debugStr; // Argument *parameter; // element of parameter list QCString argType; // fortran type of an argument of a parameter list QCString argName; // last identifier name in variable list QCString initializer; // initial value of a variable int initializerArrayScope; // number if nested array scopes in initializer int initializerScope; // number if nested function calls in initializer QCString useModuleName; // name of module in the use statement Protection defaultProtection; Protection typeProtection; bool typeMode = false; InterfaceType ifType = IF_NONE; bool functionLine = FALSE; char stringStartSymbol; // single or double quote bool parsingPrototype = FALSE; // see parsePrototype() //! Accumulated modifiers of current statement, eg variable declaration. SymbolModifiers currentModifiers; //! Holds program scope->symbol name->symbol modifiers. std::map > modifiers; int anonCount = 0 ; int fixedCommentAfter = 72; //! counter for the number of main programs in this file int mainPrograms = 0; }; //----------------------------------------------------------------------------- static int getAmpersandAtTheStart(const char *buf, int length); static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch); static QCString extractFromParens(const QCString &name); static QCString extractBind(const QCString &name); static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size); static void startCommentBlock(yyscan_t yyscanner,bool); static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief); static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief); static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief); static void addCurrentEntry(yyscan_t yyscanner,bool case_insens); static void addModule(yyscan_t yyscanner,const QCString &name=QCString(), bool isModule=FALSE); static void addSubprogram(yyscan_t yyscanner,const QCString &text); static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type); static Argument *getParameter(yyscan_t yyscanner,const QCString &name); static void scanner_abort(yyscan_t yyscanner); static inline void pop_state(yyscan_t yyscanner); static void startScope(yyscan_t yyscanner,Entry *scope); static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot=FALSE); static void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root); static void truncatePrepass(yyscan_t yyscanner,int index); static void pushBuffer(yyscan_t yyscanner,const QCString &buffer); static void popBuffer(yyscan_t yyscanner); static const CommentInPrepass* locatePrepassComment(yyscan_t yyscanner,int from, int to); static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to); static void newLine(yyscan_t yyscanner); static void initEntry(yyscan_t yyscanner); static const char *stateToString(int state); //----------------------------------------------------------------------------- #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size); // otherwise the filename would be the name of the converted file (*.cpp instead of *.l) static inline const char *getLexerFILE() {return __FILE__;} #include "doxygen_lex.h" #define YY_USER_ACTION yyextra->colNr+=(int)yyleng; #define INVALID_ENTRY ((Entry*)0x8) //----------------------------------------------------------------------------- %} //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IDSYM [a-z_A-Z0-9] NOTIDSYM [^a-z_A-Z0-9] SEPARATE [:, \t] ID [a-z_A-Z%]+{IDSYM}* ID_ [a-z_A-Z%]*{IDSYM}* PP_ID {ID} LABELID [a-z_A-Z]+[a-z_A-Z0-9\-]* SUBPROG (subroutine|function) B [ \t] BS [ \t]* BS_ [ \t]+ BT_ ([ \t]+|[ \t]*"(") COMMA {BS},{BS} ARGS_L0 ("("[^)]*")") ARGS_L1a [^()]*"("[^)]*")"[^)]* ARGS_L1 ("("{ARGS_L1a}*")") ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")" ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2}) NOARGS {BS}"\n" NUM_TYPE (complex|integer|logical|real) LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.) KIND {ARGS} CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS})) 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}?) INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")" 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) ACCESS_SPEC (PRIVATE|PUBLIC) LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}((,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})|(,{BS}NAME{BS}"="{BS}"'"(.*)"'"{BS}))?")" /* Assume that attribute statements are almost the same as attributes. */ ATTR_STMT {ATTR_SPEC}|DIMENSION EXTERNAL_STMT (EXTERNAL) CONTAINS CONTAINS PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)? SCOPENAME ({ID}{BS}"::"{BS})* %option noyywrap %option stack %option caseless /*%option debug */ //--------------------------------------------------------------------------------- /** fortran parsing states */ %x Subprog %x SubprogPrefix %x Parameterlist %x SubprogBody %x SubprogBodyContains %x Start %x Comment %x Module %x Program %x ModuleBody %x ModuleBodyContains %x AttributeList %x Variable %x Initialization %x ArrayInitializer %x Enum %x Typedef %x TypedefBody %x TypedefBodyContains %x InterfaceBody %x StrIgnore %x String %x Use %x UseOnly %x ModuleProcedure %x Prepass /** comment parsing states */ %x DocBlock %x DocBackLine %x BlockData /** prototype parsing */ %x Prototype %x PrototypeSubprog %x PrototypeArgs %% /*-----------------------------------------------------------------------------------*/ ^{BS}[&]*{BS}!.*\n { /* skip lines with just comment. Note code was in free format or has been converted to it */ yyextra->lineCountPrepass ++; } ^{BS}\n { /* skip empty lines */ yyextra->lineCountPrepass ++; } <*>^.*\n { // prepass: look for line continuations yyextra->functionLine = FALSE; DBG_CTX((stderr, "---%s", yytext)); int indexStart = getAmpersandAtTheStart(yytext, (int)yyleng); int indexEnd = getAmpOrExclAtTheEnd(yytext, (int)yyleng, '\0'); if (indexEnd>=0 && yytext[indexEnd]!='&') //we are only interested in amp { indexEnd=-1; } if (indexEnd<0) { // ----- no ampersand as line continuation if (YY_START == Prepass) { // last line in "continuation" // Only take input after initial ampersand yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1)); //printf("BUFFER:%s\n", (const char*)yyextra->inputStringPrepass); pushBuffer(yyscanner,yyextra->inputStringPrepass); yyextra->colNr = 0; pop_state(yyscanner); } else { // simple line yyextra->colNr = 0; REJECT; } } else { // ----- line with continuation if (YY_START != Prepass) { yyextra->comments.clear(); yyextra->inputStringPrepass=QCString(); yy_push_state(Prepass,yyscanner); } int length = yyextra->inputStringPrepass.length(); // Only take input after initial ampersand yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1)); yyextra->lineCountPrepass ++; // cut off & and remove following comment if present truncatePrepass(yyscanner,length+indexEnd-(indexStart+1)); } } /*------ ignore strings that are not initialization strings */ \"|\' { // string ends with next quote without previous backspace if (yytext[0]!=yyextra->stringStartSymbol) { yyextra->colNr -= (int)yyleng; REJECT; } // single vs double quote if (yy_top_state(yyscanner) == Initialization || yy_top_state(yyscanner) == ArrayInitializer) { yyextra->initializer+=yytext; } pop_state(yyscanner); } . { if (yy_top_state(yyscanner) == Initialization || yy_top_state(yyscanner) == ArrayInitializer) { yyextra->initializer+=yytext; } } <*>\"|\' { /* string starts */ if (YY_START == StrIgnore) { yyextra->colNr -= (int)yyleng; REJECT; }; // ignore in simple yyextra->comments yy_push_state(YY_START,yyscanner); if (yy_top_state(yyscanner) == Initialization || yy_top_state(yyscanner) == ArrayInitializer) { yyextra->initializer+=yytext; } yyextra->stringStartSymbol=yytext[0]; // single or double quote BEGIN(String); } /*------ ignore simple comment (not documentation yyextra->comments) */ <*>"!"/[^<>\n] { if (YY_START == String) { yyextra->colNr -= (int)yyleng; REJECT; } // "!" is ignored in strings // skip comment line (without docu yyextra->comments "!>" "!<" ) /* ignore further "!" and ignore yyextra->comments in Strings */ if ((YY_START != StrIgnore) && (YY_START != String)) { yy_push_state(YY_START,yyscanner); BEGIN(StrIgnore); yyextra->debugStr="*!"; DBG_CTX((stderr,"start comment %d\n",yyextra->lineNr)); } } .?/\n { pop_state(yyscanner); // comment ends with endline character DBG_CTX((stderr,"end comment %d %s\n",yyextra->lineNr,qPrint(yyextra->debugStr))); } // comment line ends . { yyextra->debugStr+=yytext; } /*------ use handling ------------------------------------------------------------*/ "use"{BS_} { if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } yy_push_state(Use,yyscanner); } {ID} { DBG_CTX((stderr,"using dir %s\n",yytext)); yyextra->current->name=yytext; yyextra->current->name=yyextra->current->name.lower(); yyextra->current->fileName = yyextra->fileName; yyextra->current->section=Entry::USINGDIR_SEC; yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current); yyextra->current->lang = SrcLangExt_Fortran; pop_state(yyscanner); } {ID}/, { yyextra->useModuleName=yytext; yyextra->useModuleName=yyextra->useModuleName.lower(); } ,{BS}"ONLY" { BEGIN(UseOnly); } {BS},{BS} {} {ID} { yyextra->current->name= yyextra->useModuleName+"::"+yytext; yyextra->current->name=yyextra->current->name.lower(); yyextra->current->fileName = yyextra->fileName; yyextra->current->section=Entry::USINGDECL_SEC; yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current); yyextra->current->lang = SrcLangExt_Fortran; } "\n" { yyextra->colNr -= 1; unput(*yytext); pop_state(yyscanner); } /* INTERFACE definitions */ { ^{BS}interface{IDSYM}+ { /* variable with interface prefix */ } ^{BS}interface { yyextra->ifType = IF_SPECIFIC; yy_push_state(InterfaceBody,yyscanner); // do not start a scope here, every // interface body is a scope of its own } ^{BS}abstract{BS_}interface { yyextra->ifType = IF_ABSTRACT; yy_push_state(InterfaceBody,yyscanner); // do not start a scope here, every // interface body is a scope of its own } ^{BS}interface{BS_}{ID}{ARGS}? { yyextra->ifType = IF_GENERIC; 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. yy_push_state(InterfaceBody,yyscanner); // extract generic name QCString name = QCString(yytext).stripWhiteSpace(); name = name.right(name.length() - 9).stripWhiteSpace().lower(); addInterface(yyscanner,name, yyextra->ifType); startScope(yyscanner,yyextra->last_entry.get()); } } ^{BS}end{BS}interface({BS_}{ID})? { // end scope only if GENERIC interface if (yyextra->ifType == IF_GENERIC) { yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr - 1; } if (yyextra->ifType == IF_GENERIC && !endScope(yyscanner,yyextra->current_root)) { yyterminate(); } yyextra->ifType = IF_NONE; pop_state(yyscanner); } module{BS}procedure { yy_push_state(YY_START,yyscanner); BEGIN(ModuleProcedure); } {ID} { QCString name = QCString(yytext).lower(); if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { addInterface(yyscanner,name, yyextra->ifType); startScope(yyscanner,yyextra->last_entry.get()); } yyextra->current->section = Entry::FUNCTION_SEC ; yyextra->current->name = name; yyextra->moduleProcedures.push_back(yyextra->current); addCurrentEntry(yyscanner,true); } "\n" { yyextra->colNr -= 1; unput(*yytext); pop_state(yyscanner); } . {} /*-- Contains handling --*/ ^{BS}{CONTAINS}/({BS}|\n|!|;) { if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBodyContains,yyscanner); //anon program } } ^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(ModuleBodyContains); } ^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(SubprogBodyContains); } ^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(TypedefBodyContains); } /*------ module handling ------------------------------------------------------------*/ block{BS}data{BS}{ID_} { // yyextra->vtype = V_IGNORE; yy_push_state(BlockData,yyscanner); yyextra->defaultProtection = Public; } module|program{BS_} { // yyextra->vtype = V_IGNORE; if (yytext[0]=='m' || yytext[0]=='M') { yy_push_state(Module,yyscanner); } else { yy_push_state(Program,yyscanner); } yyextra->defaultProtection = Public; } ^{BS}"end"({BS}(block{BS}data)({BS_}{ID})?)?{BS}/(\n|!|;) { // end block data //if (!endScope(yyscanner,yyextra->current_root)) // yyterminate(); yyextra->defaultProtection = Public; pop_state(yyscanner); } "end"({BS}(module|program)({BS_}{ID})?)?{BS}/(\n|!|;) { // end module resolveModuleProcedures(yyscanner,yyextra->current_root); if (!endScope(yyscanner,yyextra->current_root)) { yyterminate(); } yyextra->defaultProtection = Public; if (yyextra->global_scope) { if (yyextra->global_scope != INVALID_ENTRY) { yy_push_state(Start,yyscanner); } else { pop_state(yyscanner); // cannot pop artrificial entry } } else { yy_push_state(Start,yyscanner); yyextra->global_scope = INVALID_ENTRY; // signal that the yyextra->global_scope has already been used. } } {ID} { addModule(yyscanner, QCString(yytext), TRUE); BEGIN(ModuleBody); } {ID} { addModule(yyscanner, QCString(yytext), FALSE); BEGIN(ModuleBody); } /*------- access specification --------------------------------------------------------------------------*/ private/{BS}(\n|"!") { yyextra->defaultProtection = Private; yyextra->current->protection = yyextra->defaultProtection ; } public/{BS}(\n|"!") { yyextra->defaultProtection = Public; yyextra->current->protection = yyextra->defaultProtection ; } /*------- type definition -------------------------------------------------------------------------------*/ ^{BS}type{BS}"=" {} ^{BS}type/[^a-z0-9_] { if (YY_START == Start) { addModule(yyscanner,QCString()); yy_push_state(ModuleBody,yyscanner); //anon program } yy_push_state(Typedef,yyscanner); yyextra->current->protection = Package; // invalid in Fortran, replaced below yyextra->typeProtection = Public; yyextra->typeMode = true; } { {COMMA} {} {BS}"::"{BS} {} abstract { yyextra->current->spec |= Entry::AbstractClass; } extends{ARGS} { QCString basename = extractFromParens(yytext).lower(); yyextra->current->extends.push_back(BaseInfo(basename, Public, Normal)); } public { yyextra->current->protection = Public; } private { yyextra->current->protection = Private; } {LANGUAGE_BIND_SPEC} { /* ignored for now */ } {ID} { /* type name found */ yyextra->current->section = Entry::CLASS_SEC; yyextra->current->spec |= Entry::Struct; yyextra->current->name = yytext; yyextra->current->fileName = yyextra->fileName; yyextra->current->bodyLine = yyextra->lineNr; yyextra->current->startLine = yyextra->lineNr; /* if type is part of a module, mod name is necessary for output */ if (yyextra->current_root && (yyextra->current_root->section == Entry::CLASS_SEC || yyextra->current_root->section == Entry::NAMESPACE_SEC)) { yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name; } // set modifiers to allow adjusting public/private in surrounding module scope if( yyextra->current->protection == Package ) { yyextra->current->protection = yyextra->defaultProtection; } else if( yyextra->current->protection == Public ) { yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()] |= QCString("public"); } else if( yyextra->current->protection == Private ) { yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()] |= QCString("private"); } addCurrentEntry(yyscanner,true); startScope(yyscanner,yyextra->last_entry.get()); BEGIN(TypedefBody); } } { /* Type Bound Procedures */ ^{BS}PROCEDURE{ARGS}? { yyextra->current->type = QCString(yytext).simplifyWhiteSpace(); } ^{BS}final { yyextra->current->spec |= Entry::Final; yyextra->current->type = QCString(yytext).simplifyWhiteSpace(); } ^{BS}generic { yyextra->current->type = QCString(yytext).simplifyWhiteSpace(); } {COMMA} { } {ATTR_SPEC} { yyextra->currentModifiers |= QCString(yytext); } {BS}"::"{BS} { } {ID} { QCString name = yytext; yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers; yyextra->current->section = Entry::FUNCTION_SEC; yyextra->current->name = name; yyextra->current->fileName = yyextra->fileName; yyextra->current->bodyLine = yyextra->lineNr; yyextra->current->startLine = yyextra->lineNr; addCurrentEntry(yyscanner,true); } {BS}"=>"[^(\n|\!)]* { /* Specific bindings come after the ID. */ QCString args = yytext; yyextra->last_entry->args = args.lower(); } "\n" { yyextra->currentModifiers = SymbolModifiers(); newLine(yyscanner); yyextra->docBlock.resize(0); } } { ^{BS}"end"{BS}"type"({BS_}{ID})?{BS}/(\n|!|;) { /* end type definition */ yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr; if (!endScope(yyscanner,yyextra->current_root)) { yyterminate(); } yyextra->typeMode = false; pop_state(yyscanner); } ^{BS}"end"{BS}/(\n|!|;) { /* incorrect end type definition */ warn(yyextra->fileName,yyextra->lineNr, "Found 'END' instead of 'END TYPE'"); yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr; if (!endScope(yyscanner,yyextra->current_root)) { yyterminate(); } yyextra->typeMode = false; pop_state(yyscanner); } } /*------- module/global/typedef variable ---------------------------------------------------*/ ^{BS}[0-9]*{BS}"end"({BS}{SUBPROG}({BS_}{ID})?)?{BS}/(\n|!|;) { // // ABSTRACT and specific interfaces are stored // in a scope of their own, even if multiple // are group in one INTERFACE/END INTERFACE block. // if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { endScope(yyscanner,yyextra->current_root); yyextra->last_entry->endBodyLine = yyextra->lineNr - 1; } yyextra->current_root->endBodyLine = yyextra->lineNr - 1; if (!endScope(yyscanner,yyextra->current_root)) { yyterminate(); } yyextra->subrCurrent.pop_back(); yyextra->vtype = V_IGNORE; pop_state(yyscanner) ; } { {ID} { } } { ^{BS}{TYPE_SPEC}/{SEPARATE} { yyextra->last_enum.reset(); if (YY_START == Enum) { yyextra->argType = "@"; // enum marker } else { yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower(); } yyextra->current->bodyLine = yyextra->lineNr + 1; yyextra->current->endBodyLine = yyextra->lineNr + yyextra->lineCountPrepass; /* variable declaration starts */ if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } yy_push_state(AttributeList,yyscanner); } {EXTERNAL_STMT}/({BS}"::"|{BS_}{ID}) { /* external can be a "type" or an attribute */ if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } QCString tmp = yytext; yyextra->currentModifiers |= tmp.stripWhiteSpace(); yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower(); yy_push_state(AttributeList,yyscanner); } {ATTR_STMT}/{BS_}{ID} | {ATTR_STMT}/{BS}"::" { /* attribute statement starts */ DBG_CTX((stderr,"5=========> Attribute statement: %s\n", yytext)); if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } QCString tmp = yytext; yyextra->currentModifiers |= tmp.stripWhiteSpace(); yyextra->argType=""; yy_push_state(YY_START,yyscanner); BEGIN( AttributeList ) ; } "common" { if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } } {ID} { if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } } ^{BS}"type"{BS_}"is"/{BT_} {} ^{BS}"type"{BS}"=" {} ^{BS}"class"{BS_}"is"/{BT_} {} ^{BS}"class"{BS_}"default" {} } { {COMMA} {} {BS} {} {LANGUAGE_BIND_SPEC} { yyextra->currentModifiers |= yytext; } {ATTR_SPEC}. { /* update yyextra->current yyextra->modifiers when it is an ATTR_SPEC and not a variable name */ /* buyyextra->625519 */ char chr = yytext[(int)yyleng-1]; if (isId(chr)) { yyextra->colNr -= (int)yyleng; REJECT; } else { QCString tmp = yytext; tmp = tmp.left(tmp.length() - 1); yyextra->colNr -= 1; unput(yytext[(int)yyleng-1]); yyextra->currentModifiers |= (tmp); } } "::" { /* end attribute list */ BEGIN( Variable ); } . { /* unknown attribute, consider variable name */ //cout<<"start variables, unput "<<*yytext<colNr -= 1; unput(*yytext); BEGIN( Variable ); } } {BS} {} {ID} { /* parse variable declaration */ //cout << "5=========> got variable: " << yyextra->argType << "::" << yytext << endl; /* work around for bug in QCString.replace (QCString works) */ QCString name=yytext; name = name.lower(); /* if variable/type/etc is part of a module, mod name is necessary for output */ // get surrounding state int currentState = YY_START; yy_pop_state(yyscanner); int outerState = YY_START; yy_push_state(currentState,yyscanner); if( outerState == Start || outerState == ModuleBody ) { if ((yyextra->current_root) && (yyextra->current_root->section == Entry::CLASS_SEC || yyextra->current_root->section == Entry::NAMESPACE_SEC)) { name = yyextra->current_root->name + "::" + name; } } /* remember attributes for the symbol */ yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers; yyextra->argName= name; yyextra->vtype= V_IGNORE; if (!yyextra->argType.isEmpty() && yyextra->current_root->section!=Entry::FUNCTION_SEC) { // new variable entry yyextra->vtype = V_VARIABLE; yyextra->current->section = Entry::VARIABLE_SEC; yyextra->current->name = yyextra->argName; yyextra->current->type = yyextra->argType; yyextra->current->fileName = yyextra->fileName; yyextra->current->bodyLine = yyextra->lineNr; // used for source reference yyextra->current->startLine = yyextra->lineNr; if (yyextra->argType == "@") { yyextra->current_root->copyToSubEntry(yyextra->current); // add to the scope surrounding the enum (copy!) yyextra->last_enum = yyextra->current; yyextra->current_root->parent()->moveToSubEntryAndRefresh(yyextra->current); initEntry(yyscanner); } else { addCurrentEntry(yyscanner,true); } } else if (!yyextra->argType.isEmpty()) { // declaration of parameter list: add type for corr. parameter Argument *parameter = getParameter(yyscanner,yyextra->argName); if (parameter) { yyextra->vtype= V_PARAMETER; if (!yyextra->argType.isNull()) parameter->type=yyextra->argType.stripWhiteSpace(); if (!yyextra->docBlock.isNull()) { subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE); } } // save, it may be function return type if (parameter) { yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType; } else { if ((yyextra->current_root->name.lower() == yyextra->argName.lower()) || (yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName.lower() == yyextra->argName.lower())) { int strt = yyextra->current_root->type.find("function"); QCString lft; QCString rght; if (strt != -1) { yyextra->vtype = V_RESULT; lft = ""; rght = ""; if (strt != 0) lft = yyextra->current_root->type.left(strt).stripWhiteSpace(); if ((yyextra->current_root->type.length() - strt - strlen("function"))!= 0) { rght = yyextra->current_root->type.right(yyextra->current_root->type.length() - strt - (int)strlen("function")).stripWhiteSpace(); } yyextra->current_root->type = lft; if (rght.length() > 0) { if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " "; yyextra->current_root->type += rght; } if (yyextra->argType.stripWhiteSpace().length() > 0) { if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " "; yyextra->current_root->type += yyextra->argType.stripWhiteSpace(); } if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " "; yyextra->current_root->type += "function"; if (!yyextra->docBlock.isNull()) { subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE); } } else { yyextra->current_root->type += " " + yyextra->argType.stripWhiteSpace(); } yyextra->current_root->type = yyextra->current_root->type.stripWhiteSpace(); yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->current_root->type; } else { yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType; } } // any accumulated doc for argument should be emptied, // because it is handled other way and this doc can be // unexpectedly passed to the next member. yyextra->current->doc.resize(0); yyextra->current->brief.resize(0); } } {ARGS} { /* dimension of the previous entry. */ QCString name(yyextra->argName); QCString attr("dimension"); attr += yytext; yyextra->modifiers[yyextra->current_root][name.lower().str()] |= attr; } {COMMA} { //printf("COMMA: %d<=..<=%d\n", yyextra->colNr-(int)yyleng, yyextra->colNr); // locate !< comment updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr); } {BS}"=" { yy_push_state(YY_START,yyscanner); yyextra->initializer="="; yyextra->initializerScope = yyextra->initializerArrayScope = 0; BEGIN(Initialization); } "\n" { yyextra->currentModifiers = SymbolModifiers(); pop_state(yyscanner); // end variable declaration list newLine(yyscanner); yyextra->docBlock.resize(0); } ";".*"\n" { yyextra->currentModifiers = SymbolModifiers(); pop_state(yyscanner); // end variable declaration list yyextra->docBlock.resize(0); yyextra->inputStringSemi = " \n"+QCString(yytext+1); yyextra->lineNr--; pushBuffer(yyscanner,yyextra->inputStringSemi); } <*>";".*"\n" { if (YY_START == Variable) REJECT; // Just be on the safe side if (YY_START == String) REJECT; // ";" ignored in strings if (YY_START == StrIgnore) REJECT; // ";" ignored in regular yyextra->comments yyextra->inputStringSemi = " \n"+QCString(yytext+1); yyextra->lineNr--; pushBuffer(yyscanner,yyextra->inputStringSemi); } "[" | "(/" { yyextra->initializer+=yytext; yyextra->initializerArrayScope++; BEGIN(ArrayInitializer); // initializer may contain comma } "]" | "/)" { yyextra->initializer+=yytext; yyextra->initializerArrayScope--; if (yyextra->initializerArrayScope<=0) { yyextra->initializerArrayScope = 0; // just in case BEGIN(Initialization); } } . { yyextra->initializer+=yytext; } "(" { yyextra->initializerScope++; yyextra->initializer+=yytext; } ")" { yyextra->initializerScope--; yyextra->initializer+=yytext; } {COMMA} { if (yyextra->initializerScope == 0) { updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr); pop_state(yyscanner); // end initialization if (yyextra->last_enum) { yyextra->last_enum->initializer.str(yyextra->initializer.str()); } else { if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str()); } } else { yyextra->initializer+=", "; } } "\n"|"!" { //| pop_state(yyscanner); // end initialization if (yyextra->last_enum) { yyextra->last_enum->initializer.str(yyextra->initializer.str()); } else { if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str()); } yyextra->colNr -= 1; unput(*yytext); } . { yyextra->initializer+=yytext; } <*>{BS}"enum"{BS}","{BS}"bind"{BS}"("{BS}"c"{BS}")"{BS} { if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } yy_push_state(Enum,yyscanner); yyextra->current->protection = yyextra->defaultProtection; yyextra->typeProtection = yyextra->defaultProtection; yyextra->typeMode = true; yyextra->current->spec |= Entry::Struct; yyextra->current->name.resize(0); yyextra->current->args.resize(0); yyextra->current->name.sprintf("@%d",yyextra->anonCount++); yyextra->current->section = Entry::ENUM_SEC; yyextra->current->fileName = yyextra->fileName; yyextra->current->startLine = yyextra->lineNr; yyextra->current->bodyLine = yyextra->lineNr; if ((yyextra->current_root) && (yyextra->current_root->section == Entry::CLASS_SEC || yyextra->current_root->section == Entry::NAMESPACE_SEC)) { yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name; } addCurrentEntry(yyscanner,true); startScope(yyscanner,yyextra->last_entry.get()); BEGIN( Enum ) ; } "end"{BS}"enum" { yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr; if (!endScope(yyscanner,yyextra->current_root)) { yyterminate(); } yyextra->typeMode = false; pop_state(yyscanner); } /*------ fortran subroutine/function handling ------------------------------------------------------------*/ /* Start is initial condition */ ^{BS}({PREFIX}{BS_})?{TYPE_SPEC}{BS}({PREFIX}{BS_})?/{SUBPROG}{BS_} { if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { addInterface(yyscanner,"$interface$", yyextra->ifType); startScope(yyscanner,yyextra->last_entry.get()); } // TYPE_SPEC is for old function style function result QCString result = QCString(yytext).stripWhiteSpace().lower(); yyextra->current->type = result; yy_push_state(SubprogPrefix,yyscanner); } {BS}{SUBPROG}{BS_} { // Fortran subroutine or function found yyextra->vtype = V_IGNORE; QCString result=yytext; result=result.stripWhiteSpace(); addSubprogram(yyscanner,result); BEGIN(Subprog); 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. yyextra->current->startLine = yyextra->lineNr; } ^{BS}({PREFIX}{BS_})?{SUBPROG}{BS_} { // Fortran subroutine or function found yyextra->vtype = V_IGNORE; if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { addInterface(yyscanner,"$interface$", yyextra->ifType); startScope(yyscanner,yyextra->last_entry.get()); } QCString result = QCString(yytext).stripWhiteSpace(); addSubprogram(yyscanner,result); yy_push_state(Subprog,yyscanner); 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. yyextra->current->startLine = yyextra->lineNr; } {BS} { /* ignore white space */ } {ID} { yyextra->current->name = yytext; //cout << "1a==========> got " << yyextra->current->type << " " << yytext << " " << yyextra->lineNr << endl; QCString returnName = yyextra->current->name.lower(); /* if type is part of a module, mod name is necessary for output */ if ((yyextra->current_root) && (yyextra->current_root->section == Entry::CLASS_SEC || yyextra->current_root->section == Entry::NAMESPACE_SEC)) { yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name; } yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()].returnName = returnName; if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { yyextra->current_root->name = substitute( yyextra->current_root->name, "$interface$", QCString(yytext).lower()); } BEGIN(Parameterlist); } "(" { yyextra->current->args = "("; } ")" { yyextra->current->args += ")"; yyextra->current->args = removeRedundantWhiteSpace(yyextra->current->args); addCurrentEntry(yyscanner,true); startScope(yyscanner,yyextra->last_entry.get()); BEGIN(SubprogBody); } {COMMA}|{BS} { yyextra->current->args += yytext; const CommentInPrepass *c = locatePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr); if (c) { if (!yyextra->current->argList.empty()) { yyextra->current->argList.back().docs = c->str; } } } {ID} { //yyextra->current->type not yet available QCString param = yytext; // std::cout << "3=========> got parameter " << param << "\n"; yyextra->current->args += param; Argument arg; arg.name = param; yyextra->current->argList.push_back(arg); } {NOARGS} { newLine(yyscanner); //printf("3=========> without parameterlist \n"); addCurrentEntry(yyscanner,true); startScope(yyscanner,yyextra->last_entry.get()); BEGIN(SubprogBody); } result{BS}\({BS}{ID} { if (yyextra->functionLine) { QCString result= yytext; result= result.right(result.length()-result.find("(")-1); result= result.stripWhiteSpace(); yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName = result; } //cout << "=====> got result " << result << endl; } /*---- documentation yyextra->comments --------------------------------------------------------------------*/ "!<" { /* backward docu comment */ if (yyextra->vtype != V_IGNORE) { yyextra->current->docLine = yyextra->lineNr; yyextra->docBlockJavaStyle = FALSE; yyextra->docBlock.resize(0); yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF); startCommentBlock(yyscanner,TRUE); yy_push_state(DocBackLine,yyscanner); } else { /* handle out of place !< comment as a normal comment */ if (YY_START == String) { yyextra->colNr -= (int)yyleng; REJECT; } // "!" is ignored in strings // skip comment line (without docu yyextra->comments "!>" "!<" ) /* ignore further "!" and ignore yyextra->comments in Strings */ if ((YY_START != StrIgnore) && (YY_START != String)) { yy_push_state(YY_START,yyscanner); BEGIN(StrIgnore); yyextra->debugStr="*!"; } } } .* { // contents of yyextra->current comment line yyextra->docBlock+=yytext; } "\n"{BS}"!"("<"|"!"+) { // comment block (next line is also comment line) yyextra->docBlock+="\n"; // \n is necessary for lists newLine(yyscanner); } "\n" { // comment block ends at the end of this line //cout <<"3=========> comment block : "<< yyextra->docBlock << endl; yyextra->colNr -= 1; unput(*yytext); if (yyextra->vtype == V_VARIABLE) { std::shared_ptr tmp_entry = yyextra->current; // temporarily switch to the previous entry if (yyextra->last_enum) { yyextra->current = yyextra->last_enum; } else { yyextra->current = yyextra->last_entry; } handleCommentBlock(yyscanner,yyextra->docBlock,TRUE); // switch back yyextra->current = tmp_entry; } else if (yyextra->vtype == V_PARAMETER) { subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE); } else if (yyextra->vtype == V_RESULT) { subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE); } pop_state(yyscanner); yyextra->docBlock.resize(0); } "!>" { yy_push_state(YY_START,yyscanner); yyextra->current->docLine = yyextra->lineNr; yyextra->docBlockJavaStyle = FALSE; if (YY_START==SubprogBody) yyextra->docBlockInBody = TRUE; yyextra->docBlock.resize(0); yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF); startCommentBlock(yyscanner,TRUE); BEGIN(DocBlock); //cout << "start DocBlock " << endl; } .* { // contents of yyextra->current comment line yyextra->docBlock+=yytext; } "\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line) yyextra->docBlock+="\n"; // \n is necessary for lists newLine(yyscanner); } "\n" { // comment block ends at the end of this line //cout <<"3=========> comment block : "<< yyextra->docBlock << endl; yyextra->colNr -= 1; unput(*yytext); handleCommentBlock(yyscanner,yyextra->docBlock,TRUE); pop_state(yyscanner); } /*-----Prototype parsing -------------------------------------------------------------------------*/ {BS}{SUBPROG}{BS_} { BEGIN(PrototypeSubprog); } {BS}{SCOPENAME}?{BS}{ID} { yyextra->current->name = QCString(yytext).lower(); yyextra->current->name.stripWhiteSpace(); BEGIN(PrototypeArgs); } { "("|")"|","|{BS_} { yyextra->current->args += yytext; } {ID} { yyextra->current->args += yytext; Argument a; a.name = QCString(yytext).lower(); yyextra->current->argList.push_back(a); } } /*------------------------------------------------------------------------------------------------*/ <*>"\n" { newLine(yyscanner); //if (yyextra->debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << yyextra->debugStr << " state: " <debugStr=""; } /*---- error: EOF in wrong state --------------------------------------------------------------------*/ <*><> { if (yyextra->parsingPrototype) { yyterminate(); } else if ( yyextra->includeStackPtr <= 0 ) { if (YY_START!=INITIAL && YY_START!=Start) { DBG_CTX((stderr,"==== Error: EOF reached in wrong state (end missing)")); scanner_abort(yyscanner); } yyterminate(); } else { popBuffer(yyscanner); } } <*>{LOG_OPER} { // Fortran logical comparison keywords } <*>. { //yyextra->debugStr+=yytext; //printf("I:%c\n", *yytext); } // ignore remaining text /**********************************************************************************/ /**********************************************************************************/ /**********************************************************************************/ %% //---------------------------------------------------------------------------- static void newLine(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; yyextra->lineNr++; yyextra->lineNr+=yyextra->lineCountPrepass; yyextra->lineCountPrepass=0; yyextra->comments.clear(); } static const CommentInPrepass *locatePrepassComment(yyscan_t yyscanner,int from, int to) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; //printf("Locate %d-%d\n", from, to); for (const auto &cip : yyextra->comments) { // todo: optimize int c = cip.column; //printf("Candidate %d\n", c); if (c>=from && c<=to) { // comment for previous variable or parameter return &cip; } } return 0; } static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; const CommentInPrepass *c = locatePrepassComment(yyscanner,from, to); if (c && yyextra->vtype == V_VARIABLE) { yyextra->last_entry->brief = c->str; } else if (c && yyextra->vtype == V_PARAMETER) { Argument *parameter = getParameter(yyscanner,yyextra->argName); if (parameter) parameter->docs = c->str; } } static int getAmpersandAtTheStart(const char *buf, int length) { for(int i=0; icomments int parseState = Start; char quoteSymbol = 0; int ampIndex = -1; int commentIndex = -1; quoteSymbol = ch; if (ch != '\0') parseState = String; for(int i=0; i=0) return ampIndex; else return commentIndex; } /* Although yyextra->comments at the end of continuation line are grabbed by this function, * we still do not know how to use them later in parsing. */ void truncatePrepass(yyscan_t yyscanner,int index) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; int length = yyextra->inputStringPrepass.length(); for (int i=index+1; iinputStringPrepass[i]=='!' && iinputStringPrepass[i+1]=='<') { // save comment yyextra->comments.emplace_back(index, yyextra->inputStringPrepass.right(length-i-2)); } } yyextra->inputStringPrepass.truncate(index); } /* This function assumes that contents has at least size=length+1 */ static void insertCharacter(char *contents, int length, int pos, char c) { // shift tail by one character for(int i=length; i>pos; i--) contents[i]=contents[i-1]; // set the character contents[pos] = c; } /* change yyextra->comments and bring line continuation character to previous line */ /* also used to set continuation marks in case of fortran code usage, done here as it is quite complicated code */ const char* prepassFixedForm(const char* contents, int *hasContLine,int fixedCommentAfter) { int column=0; int prevLineLength=0; int prevLineAmpOrExclIndex=-1; int skipped = 0; char prevQuote = '\0'; char thisQuote = '\0'; bool emptyLabel=TRUE; bool commented=FALSE; bool inSingle=FALSE; bool inDouble=FALSE; bool inBackslash=FALSE; bool fullCommentLine=TRUE; bool artificialComment=FALSE; bool spaces=TRUE; int newContentsSize = (int)strlen(contents)+3; // \000, \n (when necessary) and one spare character (to avoid reallocation) char* newContents = (char*)malloc(newContentsSize); int curLine = 1; int j = -1; for(int i=0;;i++) { column++; char c = contents[i]; if (artificialComment && c != '\n') { if (c == '!' && spaces) { newContents[j++] = c; artificialComment = FALSE; spaces = FALSE; skipped = 0; continue; } else if (c == ' ' || c == '\t') continue; else { spaces = FALSE; skipped++; continue; } } j++; if (j>=newContentsSize-3) { // check for spare characters, which may be eventually used below (by & and '! ') newContents = (char*)realloc(newContents, newContentsSize+1000); newContentsSize = newContentsSize+1000; } switch(c) { case '\n': if (!fullCommentLine) { prevLineLength=column; prevLineAmpOrExclIndex=getAmpOrExclAtTheEnd(&contents[i-prevLineLength+1], prevLineLength,prevQuote); if (prevLineAmpOrExclIndex == -1) prevLineAmpOrExclIndex = column - 1; if (skipped) { prevLineAmpOrExclIndex = -1; skipped = 0; } } else { prevLineLength+=column; /* 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) */ if (hasContLine) { hasContLine[curLine - 1] = 1; } } artificialComment=FALSE; spaces=TRUE; fullCommentLine=TRUE; column=0; emptyLabel=TRUE; commented=FALSE; newContents[j]=c; prevQuote = thisQuote; curLine++; break; case ' ': case '\t': newContents[j]=c; break; case '\000': if (hasContLine) { free(newContents); return NULL; } newContents[j]='\000'; newContentsSize = (int)strlen(newContents); if (newContents[newContentsSize - 1] != '\n') { // to be on the safe side newContents = (char*)realloc(newContents, newContentsSize+2); newContents[newContentsSize] = '\n'; newContents[newContentsSize + 1] = '\000'; } return newContents; case '"': case '\'': case '\\': if ((column <= fixedCommentAfter) && (column!=6) && !commented) { // we have some special cases in respect to strings and escaped string characters fullCommentLine=FALSE; newContents[j]=c; if (c == '\\') { inBackslash = !inBackslash; break; } else if (c == '\'') { if (!inDouble) { inSingle = !inSingle; if (inSingle) thisQuote = c; else thisQuote = '\0'; } break; } else if (c == '"') { if (!inSingle) { inDouble = !inDouble; if (inDouble) thisQuote = c; else thisQuote = '\0'; } break; } } inBackslash = FALSE; // fallthrough case '#': case 'C': case 'c': case '*': case '!': if ((column <= fixedCommentAfter) && (column!=6)) { emptyLabel=FALSE; if (column==1) { newContents[j]='!'; commented = TRUE; } else if ((c == '!') && !inDouble && !inSingle) { newContents[j]=c; commented = TRUE; } else { if (!commented) fullCommentLine=FALSE; newContents[j]=c; } break; } // fallthrough default: if (!commented && (column < 6) && ((c - '0') >= 0) && ((c - '0') <= 9)) { // remove numbers, i.e. labels from first 5 positions. newContents[j]=' '; } else if (column==6 && emptyLabel) { // continuation if (!commented) fullCommentLine=FALSE; if (c != '0') { // 0 not allowed as continuation character, see f95 standard paragraph 3.3.2.3 newContents[j]=' '; if (prevLineAmpOrExclIndex==-1) { // add & just before end of previous line /* first line is not a continuation line in code, just in snippets etc. */ if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-1, '&'); j++; } else { // add & just before end of previous line comment /* first line is not a continuation line in code, just in snippets etc. */ if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-prevLineLength+prevLineAmpOrExclIndex+skipped, '&'); skipped = 0; j++; } if (hasContLine) { hasContLine[curLine - 1] = 1; } } else { newContents[j]=c; // , just handle like space } prevLineLength=0; } else if ((column > fixedCommentAfter) && !commented) { // first non commented non blank character after position fixedCommentAfter if (c == '&') { newContents[j]=' '; } else if (c != '!') { // I'm not a possible start of doxygen comment newContents[j]=' '; artificialComment = TRUE; spaces=TRUE; skipped = 0; } else { newContents[j]=c; commented = TRUE; } } else { if (!commented) fullCommentLine=FALSE; newContents[j]=c; emptyLabel=FALSE; } break; } } if (hasContLine) { free(newContents); return NULL; } newContentsSize = (int)strlen(newContents); if (newContents[newContentsSize - 1] != '\n') { // to be on the safe side newContents = (char*)realloc(newContents, newContentsSize+2); newContents[newContentsSize] = '\n'; newContents[newContentsSize + 1] = '\000'; } return newContents; } static void pushBuffer(yyscan_t yyscanner,const QCString &buffer) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (yyextra->includeStackCnt <= yyextra->includeStackPtr) { yyextra->includeStackCnt++; yyextra->includeStack = (YY_BUFFER_STATE *)realloc(yyextra->includeStack, yyextra->includeStackCnt * sizeof(YY_BUFFER_STATE)); } yyextra->includeStack[yyextra->includeStackPtr++] = YY_CURRENT_BUFFER; yy_switch_to_buffer(yy_scan_string(buffer.data(),yyscanner),yyscanner); DBG_CTX((stderr, "--PUSH--%s", qPrint(buffer))); } static void popBuffer(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; DBG_CTX((stderr, "--POP--")); yyextra->includeStackPtr --; yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner ); yy_switch_to_buffer( yyextra->includeStack[yyextra->includeStackPtr], yyscanner ); } /** used to copy entry to an interface module procedure */ static void copyEntry(std::shared_ptr dest, const std::shared_ptr &src) { dest->type = src->type; dest->fileName = src->fileName; dest->startLine = src->startLine; dest->bodyLine = src->bodyLine; dest->endBodyLine = src->endBodyLine; dest->args = src->args; dest->argList = src->argList; dest->doc = src->doc; dest->brief = src->brief; } /** fill empty interface module procedures with info from corresponding module subprogs TODO: handle procedures in used modules */ void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (yyextra->moduleProcedures.empty()) return; // build up map of available functions std::map> procMap; { for (const auto& cf: current_root->children()) { if (cf->section != Entry::FUNCTION_SEC) continue; // remove scope from name QCString name = cf->name; { int end = name.findRev(":"); if (end != -1) name.remove(0, end+1); } procMap.insert(std::make_pair(name.str(), cf)); } } // for all module procedures for (const auto& ce1: yyextra->moduleProcedures) { if (procMap.find(ce1->name.str())!=procMap.end()) { std::shared_ptr proc = procMap[ce1->name.str()]; copyEntry(ce1, proc); } } // for all interface module procedures yyextra->moduleProcedures.clear(); } /*! Extracts string which resides within parentheses of provided string. */ static QCString extractFromParens(const QCString &name) { QCString extracted = name; int start = extracted.find("("); if (start != -1) { extracted.remove(0, start+1); } int end = extracted.findRev(")"); if (end != -1) { int length = extracted.length(); extracted.remove(end, length); } extracted = extracted.stripWhiteSpace(); return extracted; } /*! remove useless spaces from bind statement */ static QCString extractBind(const QCString &name) { QCString parensPart = extractFromParens(name); if (parensPart.length() == 1) { return "bind(C)"; } else { //strip 'c' parensPart = parensPart.mid(1).stripWhiteSpace(); // strip ',' parensPart = parensPart.mid(1).stripWhiteSpace(); // name part parensPart = parensPart.mid(4).stripWhiteSpace(); // = part parensPart = parensPart.mid(1).stripWhiteSpace(); return "bind(C, name=" + parensPart + ")"; } } /*! Adds passed yyextra->modifiers to these yyextra->modifiers.*/ SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs) { if (mdfs.protection!=NONE_P) protection = mdfs.protection; if (mdfs.direction!=NONE_D) direction = mdfs.direction; optional |= mdfs.optional; if (!mdfs.dimension.isNull()) dimension = mdfs.dimension; allocatable |= mdfs.allocatable; external |= mdfs.external; intrinsic |= mdfs.intrinsic; protect |= mdfs.protect; parameter |= mdfs.parameter; pointer |= mdfs.pointer; target |= mdfs.target; save |= mdfs.save; deferred |= mdfs.deferred; nonoverridable |= mdfs.nonoverridable; nopass |= mdfs.nopass; pass |= mdfs.pass; passVar = mdfs.passVar; bindVar = mdfs.bindVar; contiguous |= mdfs.contiguous; volat |= mdfs.volat; value |= mdfs.value; return *this; } /*! Extracts and adds passed modifier to these yyextra->modifiers.*/ SymbolModifiers& SymbolModifiers::operator|=(QCString mdfStringArg) { QCString mdfString = mdfStringArg.lower(); SymbolModifiers newMdf; if (mdfString.find("dimension")==0) { newMdf.dimension=mdfString; } else if (mdfString.contains("intent")) { QCString tmp = extractFromParens(mdfString); bool isin = tmp.contains("in"); bool isout = tmp.contains("out"); if (isin && isout) newMdf.direction = SymbolModifiers::INOUT; else if (isin) newMdf.direction = SymbolModifiers::IN; else if (isout) newMdf.direction = SymbolModifiers::OUT; } else if (mdfString=="public") { newMdf.protection = SymbolModifiers::PUBLIC; } else if (mdfString=="private") { newMdf.protection = SymbolModifiers::PRIVATE; } else if (mdfString=="protected") { newMdf.protect = TRUE; } else if (mdfString=="optional") { newMdf.optional = TRUE; } else if (mdfString=="allocatable") { newMdf.allocatable = TRUE; } else if (mdfString=="external") { newMdf.external = TRUE; } else if (mdfString=="intrinsic") { newMdf.intrinsic = TRUE; } else if (mdfString=="parameter") { newMdf.parameter = TRUE; } else if (mdfString=="pointer") { newMdf.pointer = TRUE; } else if (mdfString=="target") { newMdf.target = TRUE; } else if (mdfString=="save") { newMdf.save = TRUE; } else if (mdfString=="nopass") { newMdf.nopass = TRUE; } else if (mdfString=="deferred") { newMdf.deferred = TRUE; } else if (mdfString=="non_overridable") { newMdf.nonoverridable = TRUE; } else if (mdfString=="contiguous") { newMdf.contiguous = TRUE; } else if (mdfString=="volatile") { newMdf.volat = TRUE; } else if (mdfString=="value") { newMdf.value = TRUE; } else if (mdfString.contains("pass")) { newMdf.pass = TRUE; if (mdfString.contains("(")) newMdf.passVar = extractFromParens(mdfString); else newMdf.passVar = ""; } else if (mdfString.startsWith("bind")) { // 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 newMdf.bindVar = extractBind(mdfStringArg); } (*this) |= newMdf; return *this; } /*! For debugging purposes. */ //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs) //{ // out<argList) { if ((!byTypeName && arg.name.lower() == cname) || (byTypeName && arg.type.lower() == cname) ) { return &arg; } } return 0; } /*! Apply yyextra->modifiers stored in \a mdfs to the \a typeName string. */ static QCString applyModifiers(QCString typeName, const SymbolModifiers& mdfs) { if (!mdfs.dimension.isNull()) { if (!typeName.isEmpty()) typeName += ", "; typeName += mdfs.dimension; } if (mdfs.direction!=SymbolModifiers::NONE_D) { if (!typeName.isEmpty()) typeName += ", "; typeName += directionStrs[mdfs.direction]; } if (mdfs.optional) { if (!typeName.isEmpty()) typeName += ", "; typeName += "optional"; } if (mdfs.allocatable) { if (!typeName.isEmpty()) typeName += ", "; typeName += "allocatable"; } if (mdfs.external) { if (!typeName.contains("external")) { if (!typeName.isEmpty()) typeName += ", "; typeName += "external"; } } if (mdfs.intrinsic) { if (!typeName.isEmpty()) typeName += ", "; typeName += "intrinsic"; } if (mdfs.parameter) { if (!typeName.isEmpty()) typeName += ", "; typeName += "parameter"; } if (mdfs.pointer) { if (!typeName.isEmpty()) typeName += ", "; typeName += "pointer"; } if (mdfs.target) { if (!typeName.isEmpty()) typeName += ", "; typeName += "target"; } if (mdfs.save) { if (!typeName.isEmpty()) typeName += ", "; typeName += "save"; } if (mdfs.deferred) { if (!typeName.isEmpty()) typeName += ", "; typeName += "deferred"; } if (mdfs.nonoverridable) { if (!typeName.isEmpty()) typeName += ", "; typeName += "non_overridable"; } if (mdfs.nopass) { if (!typeName.isEmpty()) typeName += ", "; typeName += "nopass"; } if (mdfs.pass) { if (!typeName.isEmpty()) typeName += ", "; typeName += "pass"; if (!mdfs.passVar.isEmpty()) typeName += "(" + mdfs.passVar + ")"; } if (!mdfs.bindVar.isEmpty()) { if (!typeName.isEmpty()) typeName += ", "; typeName += mdfs.bindVar; } if (mdfs.protection == SymbolModifiers::PUBLIC) { if (!typeName.isEmpty()) typeName += ", "; typeName += "public"; } else if (mdfs.protection == SymbolModifiers::PRIVATE) { if (!typeName.isEmpty()) typeName += ", "; typeName += "private"; } if (mdfs.protect) { if (!typeName.isEmpty()) typeName += ", "; typeName += "protected"; } if (mdfs.contiguous) { if (!typeName.isEmpty()) typeName += ", "; typeName += "contiguous"; } if (mdfs.volat) { if (!typeName.isEmpty()) typeName += ", "; typeName += "volatile"; } if (mdfs.value) { if (!typeName.isEmpty()) typeName += ", "; typeName += "value"; } return typeName; } /*! Apply yyextra->modifiers stored in \a mdfs to the \a arg argument. */ static void applyModifiers(Argument *arg, const SymbolModifiers& mdfs) { QCString tmp = arg->type; arg->type = applyModifiers(tmp, mdfs); } /*! Apply yyextra->modifiers stored in \a mdfs to the \a ent entry. */ static void applyModifiers(Entry *ent, const SymbolModifiers& mdfs) { QCString tmp = ent->type; ent->type = applyModifiers(tmp, mdfs); if (mdfs.protection == SymbolModifiers::PUBLIC) ent->protection = Public; else if (mdfs.protection == SymbolModifiers::PRIVATE) ent->protection = Private; } /*! Starts the new scope in fortran program. Consider using this function when * starting module, interface, function or other program block. * \see endScope() */ static void startScope(yyscan_t yyscanner,Entry *scope) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; //cout<<"start scope: "<name<current_root= scope; /* start substructure */ yyextra->modifiers.insert(std::make_pair(scope, std::map())); } /*! Ends scope in fortran program: may update subprogram arguments or module variable attributes. * \see startScope() */ static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (yyextra->global_scope == scope) { yyextra->global_scope = 0; return TRUE; } if (yyextra->global_scope == INVALID_ENTRY) { return TRUE; } //cout<<"end scope: "<name<current_root->parent() || isGlobalRoot) { yyextra->current_root= yyextra->current_root->parent(); /* end substructure */ } else // if (yyextra->current_root != scope) { fprintf(stderr,"parse error in end \n"); scanner_abort(yyscanner); return FALSE; } // update variables or subprogram arguments with yyextra->modifiers std::map& mdfsMap = yyextra->modifiers[scope]; if (scope->section == Entry::FUNCTION_SEC) { // iterate all symbol yyextra->modifiers of the scope for (const auto &kv : mdfsMap) { //cout<modifiers[yyextra->current_root][scope->name.lower()].returnName<modifiers[yyextra->current_root][scope->name.lower().str()].returnName.lower(); if (yyextra->modifiers[scope].find(returnName.str())!=yyextra->modifiers[scope].end()) { scope->type = yyextra->modifiers[scope][returnName.str()].type; // returning type works applyModifiers(scope, yyextra->modifiers[scope][returnName.str()]); // returning array works } } if (scope->section == Entry::CLASS_SEC) { // was INTERFACE_SEC if (scope->parent()->section == Entry::FUNCTION_SEC) { // interface within function // iterate functions of interface and // try to find types for dummy(ie. argument) procedures. //cout<<"Search in "<name<children()) { count++; if (ce->section != Entry::FUNCTION_SEC) continue; Argument *arg = findArgument(scope->parent(), ce->name, TRUE); if (arg != 0) { // set type of dummy procedure argument to interface arg->name = arg->type; arg->type = scope->name; } if (ce->name.lower() == scope->name.lower()) found = TRUE; } if ((count == 1) && found) { // clear all yyextra->modifiers of the scope yyextra->modifiers.erase(scope); scope->parent()->removeSubEntry(scope); scope = 0; return TRUE; } } } if (scope->section!=Entry::FUNCTION_SEC) { // not function section // iterate variables: get and apply yyextra->modifiers for (const auto &ce : scope->children()) { if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC && ce->section != Entry::CLASS_SEC && ce->section != Entry::FUNCTION_SEC) continue; //cout<name<<", "<name.lower())<name.lower().str())!=mdfsMap.end()) applyModifiers(ce.get(), mdfsMap[ce->name.lower().str()]); // remove prefix for variable names if (ce->section == Entry::VARIABLE_SEC || ce->section == Entry::FUNCTION_SEC) { int end = ce->name.findRev(":"); if (end != -1) ce->name.remove(0, end+1); } } } // clear all yyextra->modifiers of the scope yyextra->modifiers.erase(scope); return TRUE; } static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; yy_size_t c=0; while ( c < max_size && yyextra->inputString[yyextra->inputPosition] ) { *buf = yyextra->inputString[yyextra->inputPosition++] ; c++; buf++; } return c; } static void initParser(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; yyextra->last_entry.reset(); } static void initEntry(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (yyextra->typeMode) { yyextra->current->protection = yyextra->typeProtection; } else { yyextra->current->protection = yyextra->defaultProtection; } yyextra->current->mtype = Method; yyextra->current->virt = Normal; yyextra->current->stat = FALSE; yyextra->current->lang = SrcLangExt_Fortran; yyextra->commentScanner.initGroupInfo(yyextra->current.get()); } /** adds yyextra->current entry to yyextra->current_root and creates new yyextra->current */ static void addCurrentEntry(yyscan_t yyscanner,bool case_insens) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (case_insens) yyextra->current->name = yyextra->current->name.lower(); //printf("===Adding entry %s to %s\n", qPrint(yyextra->current->name), qPrint(yyextra->current_root->name)); yyextra->last_entry = yyextra->current; yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current); initEntry(yyscanner); } static void addModule(yyscan_t yyscanner,const QCString &name, bool isModule) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; DBG_CTX((stderr, "0=========> got module %s\n", qPrint(name))); if (isModule) yyextra->current->section = Entry::NAMESPACE_SEC; else yyextra->current->section = Entry::FUNCTION_SEC; if (!name.isEmpty()) { yyextra->current->name = name; } else { QCString fname = yyextra->fileName; int index = std::max(fname.findRev('/'), fname.findRev('\\')); fname = fname.right(fname.length()-index-1); if (yyextra->mainPrograms) fname += "__" + QCString().setNum(yyextra->mainPrograms); yyextra->mainPrograms++; fname = fname.prepend("__").append("__"); yyextra->current->name = substitute(fname, ".", "_"); } yyextra->current->type = "program"; yyextra->current->fileName = yyextra->fileName; yyextra->current->bodyLine = yyextra->lineNr; // used for source reference yyextra->current->startLine = yyextra->lineNr; yyextra->current->protection = Public ; addCurrentEntry(yyscanner,true); startScope(yyscanner,yyextra->last_entry.get()); } static void addSubprogram(yyscan_t yyscanner,const QCString &text) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; DBG_CTX((stderr,"1=========> got subprog, type: %s\n",qPrint(text))); yyextra->subrCurrent.push_back(yyextra->current); yyextra->current->section = Entry::FUNCTION_SEC ; QCString subtype = text; subtype=subtype.lower().stripWhiteSpace(); yyextra->functionLine = (subtype.find("function") != -1); yyextra->current->type += " " + subtype; yyextra->current->type = yyextra->current->type.stripWhiteSpace(); yyextra->current->fileName = yyextra->fileName; yyextra->current->bodyLine = yyextra->lineNr; // used for source reference start of body of routine yyextra->current->startLine = yyextra->lineNr; // used for source reference start of definition yyextra->current->args.resize(0); yyextra->current->argList.clear(); yyextra->docBlock.resize(0); } /*! Adds interface to the root entry. * \note Code was brought to this procedure from the parser, * because there was/is idea to use it in several parts of the parser. */ static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (YY_START == Start) { addModule(yyscanner); yy_push_state(ModuleBody,yyscanner); //anon program } yyextra->current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC; yyextra->current->spec = Entry::Interface; yyextra->current->name = name; switch (type) { case IF_ABSTRACT: yyextra->current->type = "abstract"; break; case IF_GENERIC: yyextra->current->type = "generic"; break; case IF_SPECIFIC: case IF_NONE: default: yyextra->current->type = ""; } /* if type is part of a module, mod name is necessary for output */ if ((yyextra->current_root) && (yyextra->current_root->section == Entry::CLASS_SEC || yyextra->current_root->section == Entry::NAMESPACE_SEC)) { yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name; } yyextra->current->fileName = yyextra->fileName; yyextra->current->bodyLine = yyextra->lineNr; yyextra->current->startLine = yyextra->lineNr; addCurrentEntry(yyscanner,true); } //----------------------------------------------------------------------------- /*! Get the argument \a name. */ static Argument *getParameter(yyscan_t yyscanner,const QCString &name) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; // std::cout<<"addFortranParameter(): "<current_root->argList) { if (a.name.lower()==name.lower()) { ret=&a; //printf("parameter found: %s\n",(const char*)name); break; } } // for return ret; } //---------------------------------------------------------------------------- static void startCommentBlock(yyscan_t yyscanner,bool brief) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (brief) { yyextra->current->briefFile = yyextra->fileName; yyextra->current->briefLine = yyextra->lineNr; } else { yyextra->current->docFile = yyextra->fileName; yyextra->current->docLine = yyextra->lineNr; } } //---------------------------------------------------------------------------- static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; bool hideInBodyDocs = Config_getBool(HIDE_IN_BODY_DOCS); if (yyextra->docBlockInBody && hideInBodyDocs) { yyextra->docBlockInBody = FALSE; return; } DBG_CTX((stderr,"call parseCommentBlock [%s]\n",qPrint(doc))); int lineNr = brief ? yyextra->current->briefLine : yyextra->current->docLine; int position=0; bool needsEntry = FALSE; Markdown markdown(yyextra->fileName,lineNr); QCString processedDoc = Config_getBool(MARKDOWN_SUPPORT) ? markdown.process(doc,lineNr) : doc; while (yyextra->commentScanner.parseCommentBlock( yyextra->thisParser, yyextra->docBlockInBody ? yyextra->subrCurrent.back().get() : yyextra->current.get(), processedDoc, // text yyextra->fileName, // file lineNr, yyextra->docBlockInBody ? FALSE : brief, yyextra->docBlockInBody ? FALSE : yyextra->docBlockJavaStyle, yyextra->docBlockInBody, yyextra->defaultProtection, position, needsEntry, Config_getBool(MARKDOWN_SUPPORT) )) { DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry)); if (needsEntry) addCurrentEntry(yyscanner,false); } DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry)); if (needsEntry) addCurrentEntry(yyscanner,false); yyextra->docBlockInBody = FALSE; } //---------------------------------------------------------------------------- /// Handle parameter description as defined after the declaration of the parameter static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; QCString loc_doc; loc_doc = doc.stripWhiteSpace(); std::shared_ptr tmp_entry = yyextra->current; yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function // Still in the specification section so no inbodyDocs yet, but parameter documentation yyextra->current->inbodyDocs = ""; // strip \\param or @param, so we can do some extra checking. We will add it later on again. if (!loc_doc.stripPrefix("\\param") && !loc_doc.stripPrefix("@param") ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning loc_doc.stripWhiteSpace(); // direction as defined with the declaration of the parameter int dir1 = yyextra->modifiers[yyextra->current_root][yyextra->argName.lower().str()].direction; // in description [in] is specified if (loc_doc.lower().find(directionParam[SymbolModifiers::IN]) == 0) { // check if with the declaration intent(in) or nothing has been specified if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) || (directionParam[dir1] == directionParam[SymbolModifiers::IN])) { // strip direction loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::IN])); loc_doc.stripWhiteSpace(); // in case of empty documentation or (now) just name, consider it as no documentation if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower())) { handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::IN] + " " + yyextra->argName + " " + loc_doc,brief); } } else { // something different specified, give warning and leave error. warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:", qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName)); handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " + yyextra->argName + " " + loc_doc,brief); } } // analogous to the [in] case, here [out] direction specified else if (loc_doc.lower().find(directionParam[SymbolModifiers::OUT]) == 0) { if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) || (directionParam[dir1] == directionParam[SymbolModifiers::OUT])) { loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::OUT])); loc_doc.stripWhiteSpace(); if (loc_doc.isEmpty() || (loc_doc.lower() == yyextra->argName.lower())) { yyextra->current = tmp_entry; return; } handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::OUT] + " " + yyextra->argName + " " + loc_doc,brief); } else { warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:", qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName)); handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " + yyextra->argName + " " + loc_doc,brief); } } // analogous to the [in] case, here [in,out] direction specified else if (loc_doc.lower().find(directionParam[SymbolModifiers::INOUT]) == 0) { if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) || (directionParam[dir1] == directionParam[SymbolModifiers::INOUT])) { loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::INOUT])); loc_doc.stripWhiteSpace(); if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower())) { handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::INOUT] + " " + yyextra->argName + " " + loc_doc,brief); } } else { warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:", qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName)); handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " + yyextra->argName + " " + loc_doc,brief); } } // analogous to the [in] case; here no direction specified else if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower())) { handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " + yyextra->argName + " " + loc_doc,brief); } // reset yyextra->current back to the part inside the routine yyextra->current = tmp_entry; } //---------------------------------------------------------------------------- /// Handle result description as defined after the declaration of the parameter static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; QCString loc_doc; loc_doc = doc.stripWhiteSpace(); std::shared_ptr tmp_entry = yyextra->current; yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function // Still in the specification section so no inbodyDocs yet, but parameter documentation yyextra->current->inbodyDocs = ""; // strip \\returns or @returns. We will add it later on again. if (!loc_doc.stripPrefix("\\returns") && !loc_doc.stripPrefix("\\return") && !loc_doc.stripPrefix("@returns") && !loc_doc.stripPrefix("@return") ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning loc_doc.stripWhiteSpace(); if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower())) { handleCommentBlock(yyscanner,QCString("\n\n@returns ") + loc_doc,brief); } // reset yyextra->current back to the part inside the routine yyextra->current = tmp_entry; } //---------------------------------------------------------------------------- static void parseMain(yyscan_t yyscanner, const QCString &fileName,const char *fileBuf, const std::shared_ptr &rt, FortranFormat format) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; char *tmpBuf = nullptr; initParser(yyscanner); if (fileBuf==0 || fileBuf[0]=='\0') return; yyextra->defaultProtection = Public; yyextra->inputString = fileBuf; yyextra->inputPosition = 0; yyextra->inputStringPrepass = nullptr; yyextra->inputPositionPrepass = 0; //yyextra->anonCount = 0; // don't reset per file yyextra->current_root = rt.get(); yyextra->global_root = rt; yyextra->isFixedForm = recognizeFixedForm(fileBuf,format); if (yyextra->isFixedForm) { yyextra->fixedCommentAfter = Config_getInt(FORTRAN_COMMENT_AFTER); msg("Prepassing fixed form of %s\n", qPrint(fileName)); //printf("---strlen=%d\n", strlen(fileBuf)); //clock_t start=clock(); //printf("Input fixed form string:\n%s\n", fileBuf); //printf("===========================\n"); yyextra->inputString = prepassFixedForm(fileBuf, nullptr,yyextra->fixedCommentAfter); Debug::print(Debug::FortranFixed2Free,0,"======== Fixed to Free format =========\n---- Input fixed form string ------- \n%s\n", fileBuf); Debug::print(Debug::FortranFixed2Free,0,"---- Resulting free form string ------- \n%s\n", yyextra->inputString); //printf("Resulting free form string:\n%s\n", yyextra->inputString); //printf("===========================\n"); //clock_t end=clock(); //printf("CPU time used=%f\n", ((double) (end-start))/CLOCKS_PER_SEC); } else if (yyextra->inputString[strlen(fileBuf)-1] != '\n') { tmpBuf = (char *)malloc(strlen(fileBuf)+2); strcpy(tmpBuf,fileBuf); tmpBuf[strlen(fileBuf)]= '\n'; tmpBuf[strlen(fileBuf)+1]= '\000'; yyextra->inputString = tmpBuf; } yyextra->lineNr= 1 ; yyextra->fileName = fileName; msg("Parsing file %s...\n",qPrint(yyextra->fileName)); yyextra->global_scope = rt.get(); startScope(yyscanner,rt.get()); // implies yyextra->current_root = rt initParser(yyscanner); yyextra->commentScanner.enterFile(yyextra->fileName,yyextra->lineNr); // add entry for the file yyextra->current = std::make_shared(); yyextra->current->lang = SrcLangExt_Fortran; yyextra->current->name = yyextra->fileName; yyextra->current->section = Entry::SOURCE_SEC; yyextra->file_root = yyextra->current; yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current); yyextra->current->lang = SrcLangExt_Fortran; fortranscannerYYrestart( 0, yyscanner ); { BEGIN( Start ); } fortranscannerYYlex(yyscanner); yyextra->commentScanner.leaveFile(yyextra->fileName,yyextra->lineNr); if (yyextra->global_scope && yyextra->global_scope != INVALID_ENTRY) { endScope(yyscanner,yyextra->current_root, TRUE); // TRUE - global root } //debugCompounds(rt); //debug rt->program.str(std::string()); //delete yyextra->current; yyextra->current=0; yyextra->moduleProcedures.clear(); if (tmpBuf) { free((char*)tmpBuf); yyextra->inputString=NULL; } if (yyextra->isFixedForm) { free((char*)yyextra->inputString); yyextra->inputString=NULL; } } //---------------------------------------------------------------------------- struct FortranOutlineParser::Private { yyscan_t yyscanner; fortranscannerYY_state extra; FortranFormat format; Private(FortranFormat fmt) : format(fmt) { fortranscannerYYlex_init_extra(&extra,&yyscanner); #ifdef FLEX_DEBUG fortranscannerYYset_debug(1,yyscanner); #endif } ~Private() { fortranscannerYYlex_destroy(yyscanner); } }; FortranOutlineParser::FortranOutlineParser(FortranFormat format) : p(std::make_unique(format)) { } FortranOutlineParser::~FortranOutlineParser() { } void FortranOutlineParser::parseInput(const QCString &fileName, const char *fileBuf, const std::shared_ptr &root, ClangTUParser * /*clangParser*/) { struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner; yyextra->thisParser = this; printlex(yy_flex_debug, TRUE, __FILE__, qPrint(fileName)); ::parseMain(p->yyscanner,fileName,fileBuf,root,p->format); printlex(yy_flex_debug, FALSE, __FILE__, qPrint(fileName)); } bool FortranOutlineParser::needsPreprocessing(const QCString &extension) const { return extension!=extension.lower(); // use preprocessor only for upper case extensions } void FortranOutlineParser::parsePrototype(const QCString &text) { struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner; pushBuffer(p->yyscanner,text); yyextra->parsingPrototype = TRUE; BEGIN(Prototype); fortranscannerYYlex(p->yyscanner); yyextra->parsingPrototype = FALSE; popBuffer(p->yyscanner); } //---------------------------------------------------------------------------- static void scanner_abort(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; fprintf(stderr,"********************************************************************\n"); fprintf(stderr,"Error in file %s line: %d, state: %d(%s)\n",qPrint(yyextra->fileName),yyextra->lineNr,YY_START,stateToString(YY_START)); fprintf(stderr,"********************************************************************\n"); bool start=FALSE; for (const auto &ce : yyextra->global_root->children()) { if (ce == yyextra->file_root) start=TRUE; if (start) ce->reset(); } // dummy call to avoid compiler warning (void)yy_top_state(yyscanner); return; //exit(-1); } static inline void pop_state(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if ( yyg->yy_start_stack_ptr <= 0 ) warn(yyextra->fileName,yyextra->lineNr,"Unexpected statement '%s'",yytext ); else yy_pop_state(yyscanner); } //---------------------------------------------------------------------------- #include "fortranscanner.l.h"