1 /* -*- mode: fundamental; indent-tabs-mode: 1; -*- */
2 /*****************************************************************************
3 * Parser for Fortran90 F subset
5 * Copyright (C) by Anke Visser
6 * based on the work of Dimitri van Heesch.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation under the terms of the GNU General Public License is hereby
10 * granted. No representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or implied warranty.
12 * See the GNU General Public License for more details.
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
21 * - Consider using startScope(), endScope() functions with module, program,
22 * subroutine or any other scope in fortran program.
24 * - Symbol modifiers (attributes) are collected using SymbolModifiers |= operator during
25 * substructure parsing. When substructure ends all modifiers are applied to actual
26 * entries in applyModifiers() functions.
28 * - How case insensitiveness should be handled in code?
29 * On one side we have arg->name and entry->name, on another side modifierMap[name].
30 * In entries and arguments case is the same as in code, in modifier map case is lowered and
31 * then it is compared to lowered entry/argument names.
33 * - Do not like constructs like aa{BS} or {BS}bb. Should try to handle blank space
34 * with separate rule?: It seems it is often necessary, because we may parse something like
35 * "functionA" or "MyInterface". So constructs like `(^|[ \t])interface({BS_}{ID})?/[ \t\n]'
38 * - Must track yyLineNr when using REJECT, unput() or similar commands.
55 #include "fortranscanner.h"
63 #include "commentscan.h"
64 #include "fortrancode.h"
66 #include "arguments.h"
68 // Toggle for some debugging info
69 //#define DBG_CTX(x) fprintf x
70 #define DBG_CTX(x) do { } while(0)
72 #define YY_NEVER_INTERACTIVE 1
75 enum ScanVar { V_IGNORE, V_VARIABLE, V_PARAMETER};
76 enum InterfaceType { IF_NONE, IF_SPECIFIC, IF_GENERIC, IF_ABSTRACT };
78 // {{{ ----- Helper structs -----
79 //! Holds modifiers (ie attributes) for one symbol (variable, function, etc)
80 struct SymbolModifiers {
81 enum Protection {NONE_P, PUBLIC, PRIVATE};
82 enum Direction {NONE_D, IN, OUT, INOUT};
84 //!< This is only used with function return value.
85 QCString type, returnName;
86 Protection protection;
104 SymbolModifiers() : type(), returnName(), protection(NONE_P), direction(NONE_D),
105 optional(FALSE), protect(FALSE), dimension(), allocatable(FALSE),
106 external(FALSE), intrinsic(FALSE), parameter(FALSE),
107 pointer(FALSE), target(FALSE), save(FALSE), deferred(FALSE), nonoverridable(FALSE),
108 nopass(FALSE), pass(FALSE), passVar() {}
110 SymbolModifiers& operator|=(const SymbolModifiers &mdfs);
111 SymbolModifiers& operator|=(QCString mdfrString);
114 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs);
116 static const char *directionStrs[] =
118 "", "intent(in)", "intent(out)", "intent(inout)"
120 static const char *directionParam[] =
122 "", "[in]", "[out]", "[in,out]"
127 /* -----------------------------------------------------------------
131 static ParserInterface *g_thisParser;
132 static const char * inputString;
133 static int inputPosition;
134 static bool isFixedForm;
135 static QCString inputStringPrepass; ///< Input string for prepass of line cont. '&'
136 static QCString inputStringSemi; ///< Input string after command separetor ';'
137 static unsigned int inputPositionPrepass;
138 static int lineCountPrepass = 0;
140 static QList<Entry> subrCurrent;
142 struct CommentInPrepass {
145 CommentInPrepass(int column, QCString str) : column(column), str(str) {}
147 static QList<CommentInPrepass> comments;
149 YY_BUFFER_STATE *include_stack = NULL;
150 int include_stack_ptr = 0;
151 int include_stack_cnt = 0;
153 static QFile inputFile;
154 static QCString yyFileName;
155 static int yyLineNr = 1 ;
156 static int yyColNr = 0 ;
157 static Entry* current_root = 0 ;
158 static Entry* global_root = 0 ;
159 static Entry* file_root = 0 ;
160 static Entry* current = 0 ;
161 static Entry* last_entry = 0 ;
162 static ScanVar v_type = V_IGNORE; // type of parsed variable
163 static QList<Entry> moduleProcedures; // list of all interfaces which contain unresolved
165 static QCString docBlock;
166 static QCString docBlockName;
167 static bool docBlockInBody = FALSE;
168 static bool docBlockJavaStyle;
170 static MethodTypes mtype;
172 static Specifier virt;
174 static QCString debugStr;
175 static QCString result; // function result
176 static Argument *parameter; // element of parameter list
177 static QCString argType; // fortran type of an argument of a parameter list
178 static QCString argName; // last identifier name in variable list
179 static QCString initializer; // initial value of a variable
180 static int initializerArrayScope; // number if nested array scopes in initializer
181 static int initializerScope; // number if nested function calls in initializer
182 static QCString useModuleName; // name of module in the use statement
183 static Protection defaultProtection;
184 static Protection typeProtection;
185 static int typeMode = false;
186 static InterfaceType ifType = IF_NONE;
187 static bool functionLine = FALSE;
189 static char stringStartSymbol; // single or double quote
190 static bool parsingPrototype = FALSE; // see parsePrototype()
192 //! Accumulated modifiers of current statement, eg variable declaration.
193 static SymbolModifiers currentModifiers;
194 //! Holds program scope->symbol name->symbol modifiers.
195 static QMap<Entry*,QMap<QCString,SymbolModifiers> > modifiers;
197 //-----------------------------------------------------------------------------
199 static int yyread(char *buf,int max_size);
200 static void startCommentBlock(bool);
201 static void handleCommentBlock(const QCString &doc,bool brief);
202 static void subrHandleCommentBlock(const QCString &doc,bool brief);
203 static void addCurrentEntry(int case_insens);
204 static void addModule(const char *name, bool isModule=FALSE);
205 static void addSubprogram(const char *text);
206 static void addInterface(QCString name, InterfaceType type);
207 static Argument *getParameter(const QCString &name);
208 static void scanner_abort();
210 static void startScope(Entry *scope);
211 static bool endScope(Entry *scope, bool isGlobalRoot=FALSE);
212 //static bool isTypeName(QCString name);
213 static void resolveModuleProcedures(QList<Entry> &moduleProcedures, Entry *current_root);
214 static int getAmpersandAtTheStart(const char *buf, int length);
215 static int getAmpOrExclAtTheEnd(const char *buf, int length);
216 static void truncatePrepass(int index);
217 static void pushBuffer(QCString &buffer);
218 static void popBuffer();
219 //static void extractPrefix(QCString& text);
220 static QCString extractFromParens(const QCString name);
221 static CommentInPrepass* locatePrepassComment(int from, int to);
222 static void updateVariablePrepassComment(int from, int to);
223 static void newLine();
225 //-----------------------------------------------------------------------------
227 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
228 #define YY_USER_ACTION yyColNr+=(int)yyleng;
229 //-----------------------------------------------------------------------------
233 //-----------------------------------------------------------------------------
234 //-----------------------------------------------------------------------------
236 NOTIDSYM [^a-z_A-Z0-9]
238 ID [a-z_A-Z%]+{IDSYM}*
239 ID_ [a-z_A-Z%]*{IDSYM}*
241 LABELID [a-z_A-Z]+[a-z_A-Z0-9\-]*
242 SUBPROG (subroutine|function)
247 ARGS_L0 ("("[^)]*")")
248 ARGS_L1a [^()]*"("[^)]*")"[^)]*
249 ARGS_L1 ("("{ARGS_L1a}*")")
250 ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")"
251 ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2})
254 NUM_TYPE (complex|integer|logical|real)
255 LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.)
257 CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS}))
258 TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|{CHAR}|TYPE{ARGS}|CLASS{ARGS}|PROCEDURE{ARGS}?)
260 INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")"
261 ATTR_SPEC (ALLOCATABLE|DIMENSION{ARGS}|EXTERNAL|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PROTECTED|PRIVATE|PUBLIC|SAVE|TARGET|NOPASS|PASS{ARGS}?|DEFERRED|NON_OVERRIDABLE)
262 ACCESS_SPEC (PRIVATE|PUBLIC)
263 LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?")"
264 /* Assume that attribute statements are almost the same as attributes. */
265 ATTR_STMT {ATTR_SPEC}|DIMENSION|{ACCESS_SPEC}
268 PREFIX (RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,3}(RECURSIVE|IMPURE|PURE|ELEMENTAL)?
269 SCOPENAME ({ID}{BS}"::"{BS})*
276 //---------------------------------------------------------------------------------
278 /** fortran parsing states */
283 %x SubprogBodyContains
289 %x ModuleBodyContains
296 %x TypedefBodyContains
306 /** comment parsing states */
313 /** prototype parsing */
320 /*-----------------------------------------------------------------------------------*/
322 <*>^.*\n { // prepass: look for line continuations
323 functionLine = FALSE;
325 DBG_CTX((stderr, "---%s", yytext));
327 int indexStart = getAmpersandAtTheStart(yytext, (int)yyleng);
328 int indexEnd = getAmpOrExclAtTheEnd(yytext, (int)yyleng);
329 if (indexEnd>=0 && yytext[indexEnd]!='&') //we are only interested in amp
332 if(indexEnd<0){ // ----- no ampersand as line continuation
333 if(YY_START == Prepass) { // last line in "continuation"
335 // Only take input after initial ampersand
336 inputStringPrepass+=(const char*)(yytext+(indexStart+1));
338 //printf("BUFFER:%s\n", (const char*)inputStringPrepass);
339 pushBuffer(inputStringPrepass);
342 } else { // simple line
347 } else { // ----- line with continuation
348 if(YY_START != Prepass) {
349 comments.setAutoDelete(TRUE);
351 yy_push_state(Prepass);
354 int length = inputStringPrepass.length();
356 // Only take input after initial ampersand
357 inputStringPrepass+=(const char*)(yytext+(indexStart+1));
360 // cut off & and remove following comment if present
361 truncatePrepass(length+indexEnd-(indexStart+1));
367 /*------ ignore strings that are not initialization strings */
368 <*>"\\\\" { if (yy_top_state() == Initialization
369 || yy_top_state() == ArrayInitializer)
372 <*>"\\\""|\\\' { if (yy_top_state() == Initialization
373 || yy_top_state() == ArrayInitializer)
376 <String>\"|\' { // string ends with next quote without previous backspace
377 if (yytext[0]!=stringStartSymbol) { yyColNr -= (int)yyleng; REJECT; } // single vs double quote
378 if (yy_top_state() == Initialization
379 || yy_top_state() == ArrayInitializer)
383 <String>. { if (yy_top_state() == Initialization
384 || yy_top_state() == ArrayInitializer)
387 <*>\"|\' { /* string starts */
388 if (YY_START == StrIgnore) { yyColNr -= (int)yyleng; REJECT; }; // ignore in simple comments
389 yy_push_state(YY_START);
390 if (yy_top_state() == Initialization
391 || yy_top_state() == ArrayInitializer)
393 stringStartSymbol=yytext[0]; // single or double quote
397 /*------ ignore simple comment (not documentation comments) */
399 <*>"!"/[^<>\n] { if (YY_START == String) { yyColNr -= (int)yyleng; REJECT; } // "!" is ignored in strings
400 // skip comment line (without docu comments "!>" "!<" )
401 /* ignore further "!" and ignore comments in Strings */
402 if ((YY_START != StrIgnore) && (YY_START != String))
404 yy_push_state(YY_START);
407 DBG_CTX((stderr,"start comment %d\n",yyLineNr));
410 <StrIgnore>.?/\n { yy_pop_state(); // comment ends with endline character
411 DBG_CTX((stderr,"end comment %d %s\n",yyLineNr,debugStr.data()));
412 } // comment line ends
413 <StrIgnore>. { debugStr+=yytext; }
416 /*------ use handling ------------------------------------------------------------*/
418 <Start,ModuleBody,SubprogBody>"use"{BS_} {
419 if(YY_START == Start)
422 yy_push_state(ModuleBody); //anon program
427 DBG_CTX((stderr,"using dir %s\n",yytext));
428 current->name=yytext;
429 current->fileName = yyFileName;
430 current->section=Entry::USINGDIR_SEC;
431 current_root->addSubEntry(current);
433 current->lang = SrcLangExt_Fortran;
437 useModuleName=yytext;
439 <Use>,{BS}"ONLY" { BEGIN(UseOnly);
441 <UseOnly>{BS},{BS} {}
443 current->name= useModuleName+"::"+yytext;
444 current->fileName = yyFileName;
445 current->section=Entry::USINGDECL_SEC;
446 current_root->addSubEntry(current);
447 current = new Entry ;
448 current->lang = SrcLangExt_Fortran;
456 /* INTERFACE definitions */
457 <Start,ModuleBody,SubprogBody>{
458 ^{BS}interface{IDSYM}+ { /* variable with interface prefix */ }
459 ^{BS}interface { ifType = IF_SPECIFIC;
460 yy_push_state(InterfaceBody);
461 // do not start a scope here, every
462 // interface body is a scope of its own
465 ^{BS}abstract{BS_}interface { ifType = IF_ABSTRACT;
466 yy_push_state(InterfaceBody);
467 // do not start a scope here, every
468 // interface body is a scope of its own
471 ^{BS}interface{BS_}{ID}{ARGS}? { ifType = IF_GENERIC;
472 yy_push_state(InterfaceBody);
474 // extract generic name
475 QCString name = QCString(yytext).stripWhiteSpace();
476 name = name.right(name.length() - 9).stripWhiteSpace().lower();
477 addInterface(name, ifType);
478 startScope(last_entry);
482 <InterfaceBody>^{BS}end{BS}interface({BS_}{ID})? {
483 // end scope only if GENERIC interface
484 if (ifType == IF_GENERIC && !endScope(current_root))
490 <InterfaceBody>module{BS}procedure { yy_push_state(YY_START);
491 BEGIN(ModuleProcedure);
493 <ModuleProcedure>{ID} { if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
495 addInterface(yytext, ifType);
496 startScope(last_entry);
499 current->section = Entry::FUNCTION_SEC ;
500 current->name = yytext;
501 moduleProcedures.append(current);
504 <ModuleProcedure>"\n" { yyColNr -= 1;
510 /*-- Contains handling --*/
511 <Start>^{BS}{CONTAINS}/({BS}|\n|!) {
512 if(YY_START == Start)
515 yy_push_state(ModuleBodyContains); //anon program
518 <ModuleBody>^{BS}{CONTAINS}/({BS}|\n|!) { BEGIN(ModuleBodyContains); }
519 <SubprogBody>^{BS}{CONTAINS}/({BS}|\n|!) { BEGIN(SubprogBodyContains); }
520 <TypedefBody>^{BS}{CONTAINS}/({BS}|\n|!) { BEGIN(TypedefBodyContains); }
522 /*------ module handling ------------------------------------------------------------*/
523 <Start>block{BS}data{BS}{ID_} { //
525 yy_push_state(BlockData);
526 defaultProtection = Public;
528 <Start>module|program{BS_} { //
530 if(yytext[0]=='m' || yytext[0]=='M')
531 yy_push_state(Module);
533 yy_push_state(Program);
534 defaultProtection = Public;
536 <BlockData>^{BS}"end"({BS}(block{BS}data)({BS_}{ID})?)?{BS}/(\n|!) { // end block data
537 //if (!endScope(current_root))
539 defaultProtection = Public;
542 <Start,ModuleBody,ModuleBodyContains>^{BS}"end"({BS}(module|program)({BS_}{ID})?)?{BS}/(\n|!) { // end module
543 resolveModuleProcedures(moduleProcedures, current_root);
544 if (!endScope(current_root))
546 defaultProtection = Public;
550 addModule(yytext, TRUE);
555 addModule(yytext, FALSE);
559 /*------- access specification --------------------------------------------------------------------------*/
561 <ModuleBody>private/{BS}(\n|"!") { defaultProtection = Private;
562 current->protection = defaultProtection ;
564 <ModuleBody>public/{BS}(\n|"!") { defaultProtection = Public;
565 current->protection = defaultProtection ;
568 /*------- type definition -------------------------------------------------------------------------------*/
570 <Start,ModuleBody>^{BS}type/[^a-z0-9_] {
571 if(YY_START == Start)
574 yy_push_state(ModuleBody); //anon program
577 yy_push_state(Typedef);
578 current->protection = defaultProtection;
579 typeProtection = defaultProtection;
588 current->spec |= Entry::AbstractClass;
591 QCString basename = extractFromParens(yytext);
592 current->extends->append(new BaseInfo(basename, Public, Normal));
595 current->protection = Public;
596 typeProtection = Public;
599 current->protection = Private;
600 typeProtection = Private;
602 {LANGUAGE_BIND_SPEC} {
603 /* ignored for now */
605 {ID} { /* type name found */
606 current->section = Entry::CLASS_SEC;
607 current->spec |= Entry::Struct;
608 current->name = yytext;
609 current->fileName = yyFileName;
610 current->bodyLine = yyLineNr;
612 /* if type is part of a module, mod name is necessary for output */
613 if ((current_root) &&
614 (current_root->section == Entry::CLASS_SEC
615 || current_root->section == Entry::NAMESPACE_SEC))
617 current->name = current_root->name + "::" + current->name;
621 startScope(last_entry);
626 <TypedefBodyContains>{ /* Type Bound Procedures */
627 ^{BS}PROCEDURE{ARGS}? {
628 current->type = QCString(yytext).simplifyWhiteSpace();
631 current->spec |= Entry::Final;
632 current->type = QCString(yytext).simplifyWhiteSpace();
635 current->type = QCString(yytext).simplifyWhiteSpace();
640 currentModifiers |= QCString(yytext);
645 QCString name = yytext;
646 modifiers[current_root][name.lower()] |= currentModifiers;
647 current->section = Entry::FUNCTION_SEC;
648 current->name = name;
649 current->fileName = yyFileName;
650 current->bodyLine = yyLineNr;
653 {BS}"=>"[^(\n|\!)]* { /* Specific bindings come after the ID. */
654 last_entry->args = yytext;
657 currentModifiers = SymbolModifiers();
664 <TypedefBody,TypedefBodyContains>{
665 ^{BS}"end"{BS}"type"({BS_}{ID})?{BS}/(\n|!) { /* end type definition */
666 if (!endScope(current_root))
673 /*------- module/global/typedef variable ---------------------------------------------------*/
675 <SubprogBody,SubprogBodyContains>^{BS}[0-9]*{BS}"end"({BS}{SUBPROG}({BS_}{ID})?)?{BS}/(\n|!) {
677 // ABSTRACT and specific interfaces are stored
678 // in a scope of their own, even if multiple
679 // are group in one INTERFACE/END INTERFACE block.
681 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
682 endScope(current_root);
684 if (!endScope(current_root))
686 subrCurrent.remove(0u);
693 <Start,ModuleBody,TypedefBody,SubprogBody>{
694 ^{BS}{TYPE_SPEC}/{SEPARATE} {
695 /* variable declaration starts */
696 if(YY_START == Start)
699 yy_push_state(ModuleBody); //anon program
701 argType = QCString(yytext).simplifyWhiteSpace().lower();
702 yy_push_state(AttributeList);
704 /* Dimitri: macro expansion should already be done during preprocessing not here!
705 ^{BS}{PP_ID}{KIND}? { // check for preprocessor symbol expand to type
706 QCString str = yytext;
707 str = str.stripWhiteSpace();
708 //DefineDict* defines = getGlobalDefineDict();
710 int index = str.find("(");
712 name = str.left(index).stripWhiteSpace();
716 Define *define = 0; //(*defines)[name];
717 if (define != 0 && isTypeName(define->definition))
720 yy_push_state(AttributeList);
724 yyColNr -= (int)yyleng;
729 {ATTR_STMT}/{BS_}{ID} |
730 {ATTR_STMT}/{BS}"::" {
731 /* attribute statement starts */
732 DBG_CTX((stderr,"5=========> Attribute statement: %s\n", yytext));
733 QCString tmp = yytext;
734 currentModifiers |= tmp.stripWhiteSpace();
736 yy_push_state(YY_START);
737 BEGIN( AttributeList ) ;
741 ^{BS}"type"{BS_}"is"/{BS_} { }
742 ^{BS}"type"{BS}"=" { }
747 {ATTR_SPEC}. { /* update current modifierswhen it is an ATTR_SPEC and not a variable name */
749 QChar chr = yytext[(int)yyleng-1];
750 if (chr.isLetter() || chr.isDigit() || (chr == '_'))
752 yyColNr -= (int)yyleng;
757 QCString tmp = yytext;
758 tmp = tmp.left(tmp.length() - 1);
760 unput(yytext[(int)yyleng-1]);
761 currentModifiers |= (tmp);
764 "::" { /* end attribute list */
767 . { /* unknown attribute, consider variable name */
768 //cout<<"start variables, unput "<<*yytext<<endl;
776 <Variable>{ID} { /* parse variable declaration */
777 //cout << "5=========> got variable: " << argType << "::" << yytext << endl;
778 /* work around for bug in QCString.replace (QCString works) */
779 QCString name=yytext;
781 /* remember attributes for the symbol */
782 modifiers[current_root][name.lower()] |= currentModifiers;
786 if (!argType.isEmpty() && current_root->section!=Entry::FUNCTION_SEC)
787 { // new variable entry
789 current->section = Entry::VARIABLE_SEC;
790 current->name = argName;
791 current->type = argType;
792 current->fileName = yyFileName;
793 current->bodyLine = yyLineNr; // used for source reference
796 else if (!argType.isEmpty())
797 { // declaration of parameter list: add type for corr. parameter
798 parameter = getParameter(argName);
802 if (!argType.isNull()) parameter->type=argType.stripWhiteSpace();
803 if (!docBlock.isNull())
805 subrHandleCommentBlock(docBlock,TRUE);
808 // save, it may be function return type
811 modifiers[current_root][name.lower()].type = argType;
815 if ((current_root->name.lower() == argName.lower()) ||
816 (modifiers[current_root->parent()][current_root->name.lower()].returnName.lower() == argName.lower()))
818 int strt = current_root->type.find("function");
825 if (strt != 0) lft = current_root->type.left(strt).stripWhiteSpace();
826 if ((current_root->type.length() - strt - strlen("function"))!= 0)
828 rght = current_root->type.right(current_root->type.length() - strt - strlen("function")).stripWhiteSpace();
830 current_root->type = lft;
831 if (rght.length() > 0)
833 if (current_root->type.length() > 0) current_root->type += " ";
834 current_root->type += rght;
836 if (argType.stripWhiteSpace().length() > 0)
838 if (current_root->type.length() > 0) current_root->type += " ";
839 current_root->type += argType.stripWhiteSpace();
841 if (current_root->type.length() > 0) current_root->type += " ";
842 current_root->type += "function";
846 current_root->type += " " + argType.stripWhiteSpace();
848 current_root->type = current_root->type.stripWhiteSpace();
849 modifiers[current_root][name.lower()].type = current_root->type;
853 modifiers[current_root][name.lower()].type = argType;
856 // any accumulated doc for argument should be emptied,
857 // because it is handled other way and this doc can be
858 // unexpectedly passed to the next member.
859 current->doc.resize(0);
860 current->brief.resize(0);
863 <Variable>{ARGS} { /* dimension of the previous entry. */
864 QCString name(argName);
865 QCString attr("dimension");
867 modifiers[current_root][name.lower()] |= attr;
869 <Variable>{COMMA} { //printf("COMMA: %d<=..<=%d\n", yyColNr-(int)yyleng, yyColNr);
871 updateVariablePrepassComment(yyColNr-(int)yyleng, yyColNr);
873 <Variable>{BS}"=" { yy_push_state(YY_START);
875 initializerScope = initializerArrayScope = 0;
876 BEGIN(Initialization);
878 <Variable>"\n" { currentModifiers = SymbolModifiers();
879 yy_pop_state(); // end variable declaration list
883 <Variable>";".*"\n" { currentModifiers = SymbolModifiers();
884 yy_pop_state(); // end variable declaration list
886 inputStringSemi =(const char*)(QCString(" \n") + QCString(yytext+1)).data();
888 pushBuffer(inputStringSemi);
891 if (YY_START == Variable) REJECT; // Just be on the safe side
892 if (YY_START == String) REJECT; // ";" ignored in strings
893 if (YY_START == StrIgnore) REJECT; // ";" ignored in regular comments
894 inputStringSemi =(const char*)(QCString(" \n") + QCString(yytext+1)).data();
896 pushBuffer(inputStringSemi);
899 <Initialization,ArrayInitializer>"[" |
900 <Initialization,ArrayInitializer>"(/" { initializer+=yytext;
901 initializerArrayScope++;
902 BEGIN(ArrayInitializer); // initializer may contain comma
904 <ArrayInitializer>"]" |
905 <ArrayInitializer>"/)" { initializer+=yytext;
906 initializerArrayScope--;
907 if(initializerArrayScope<=0)
909 initializerArrayScope = 0; // just in case
910 BEGIN(Initialization);
913 <ArrayInitializer>. { initializer+=yytext; }
914 <Initialization>"(" { initializerScope++;
917 <Initialization>")" { initializerScope--;
920 <Initialization>{COMMA} { if (initializerScope == 0)
922 updateVariablePrepassComment(yyColNr-(int)yyleng, yyColNr);
923 yy_pop_state(); // end initialization
924 if (v_type == V_VARIABLE) last_entry->initializer= initializer;
929 <Initialization>"\n"|"!" { //|
930 yy_pop_state(); // end initialization
931 if (v_type == V_VARIABLE) last_entry->initializer= initializer;
935 <Initialization>. { initializer+=yytext; }
937 /*------ fortran subroutine/function handling ------------------------------------------------------------*/
938 /* Start is initial condition */
940 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{TYPE_SPEC}{BS}({PREFIX}{BS_})?/{SUBPROG}{BS_} {
941 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
943 addInterface("$interface$", ifType);
944 startScope(last_entry);
947 // TYPE_SPEC is for old function style function result
948 result = QCString(yytext).stripWhiteSpace().lower();
949 current->type = result;
950 yy_push_state(SubprogPrefix);
953 <SubprogPrefix>{BS}{SUBPROG}{BS_} {
954 // Fortran subroutine or function found
957 result=result.stripWhiteSpace();
958 addSubprogram(result);
962 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{SUBPROG}{BS_} {
963 // Fortran subroutine or function found
965 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
967 addInterface("$interface$", ifType);
968 startScope(last_entry);
971 result = QCString(yytext).stripWhiteSpace();
972 addSubprogram(result);
973 yy_push_state(Subprog);
976 <Subprog>{BS} { /* ignore white space */ }
977 <Subprog>{ID} { current->name = yytext;
978 //cout << "1a==========> got " << current->type << " " << yytext << " " << yyLineNr << endl;
979 modifiers[current_root][current->name.lower()].returnName = current->name.lower();
981 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
983 current_root->name.replace(QRegExp("\\$interface\\$"), yytext);
986 BEGIN(Parameterlist);
988 <Parameterlist>"(" { current->args = "("; }
990 current->args += ")";
991 current->args = removeRedundantWhiteSpace(current->args);
993 startScope(last_entry);
996 <Parameterlist>{COMMA}|{BS} { current->args += yytext;
997 CommentInPrepass *c = locatePrepassComment(yyColNr-(int)yyleng, yyColNr);
999 if(current->argList->count()>0) {
1000 current->argList->at(current->argList->count()-1)->docs = c->str;
1004 <Parameterlist>{ID} {
1005 //current->type not yet available
1006 QCString param = yytext;
1007 // std::cout << "3=========> got parameter " << param << std::endl;
1008 current->args += param;
1009 Argument *arg = new Argument;
1012 current->argList->append(arg);
1014 <Parameterlist>{NOARGS} {
1016 //printf("3=========> without parameterlist \n");
1017 //current->argList = ;
1019 startScope(last_entry);
1022 <SubprogBody>result{BS}\({BS}{ID} {
1026 result= result.right(result.length()-result.find("(")-1);
1027 result= result.stripWhiteSpace();
1028 modifiers[current_root->parent()][current_root->name.lower()].returnName = result;
1030 //cout << "=====> got result " << result << endl;
1033 /*---- documentation comments --------------------------------------------------------------------*/
1035 <Variable,SubprogBody,ModuleBody,TypedefBody,TypedefBodyContains>"!<" { /* backward docu comment */
1036 if (v_type != V_IGNORE) {
1037 current->docLine = yyLineNr;
1038 docBlockJavaStyle = FALSE;
1040 docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF");
1041 startCommentBlock(TRUE);
1042 yy_push_state(DocBackLine);
1046 /* handle out of place !< comment as a normal comment */
1047 if (YY_START == String) { yyColNr -= (int)yyleng; REJECT; } // "!" is ignored in strings
1048 // skip comment line (without docu comments "!>" "!<" )
1049 /* ignore further "!" and ignore comments in Strings */
1050 if ((YY_START != StrIgnore) && (YY_START != String))
1052 yy_push_state(YY_START);
1055 //fprintf(stderr,"start comment %d\n",yyLineNr);
1059 <DocBackLine>.* { // contents of current comment line
1062 <DocBackLine>"\n"{BS}"!"("<"|"!"+) { // comment block (next line is also comment line)
1063 docBlock+="\n"; // \n is necessary for lists
1066 <DocBackLine>"\n" { // comment block ends at the end of this line
1067 //cout <<"3=========> comment block : "<< docBlock << endl;
1070 if (v_type == V_VARIABLE)
1072 Entry *tmp_entry = current;
1073 current = last_entry; // temporarily switch to the previous entry
1074 handleCommentBlock(docBlock,TRUE);
1077 else if (v_type == V_PARAMETER)
1079 subrHandleCommentBlock(docBlock,TRUE);
1085 <Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains,TypedefBodyContains>"!>" {
1086 yy_push_state(YY_START);
1087 current->docLine = yyLineNr;
1088 docBlockJavaStyle = FALSE;
1089 if (YY_START==SubprogBody) docBlockInBody = TRUE;
1091 docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF");
1092 startCommentBlock(TRUE);
1094 //cout << "start DocBlock " << endl;
1097 <DocBlock>.* { // contents of current comment line
1100 <DocBlock>"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line)
1101 docBlock+="\n"; // \n is necessary for lists
1104 <DocBlock>"\n" { // comment block ends at the end of this line
1105 //cout <<"3=========> comment block : "<< docBlock << endl;
1108 handleCommentBlock(docBlock,TRUE);
1112 /*-----Prototype parsing -------------------------------------------------------------------------*/
1113 <Prototype>{BS}{SUBPROG}{BS_} {
1114 BEGIN(PrototypeSubprog);
1116 <Prototype,PrototypeSubprog>{BS}{SCOPENAME}?{BS}{ID} {
1117 current->name = QCString(yytext).lower();
1118 current->name.stripWhiteSpace();
1119 BEGIN(PrototypeArgs);
1122 "("|")"|","|{BS_} { current->args += yytext; }
1123 {ID} { current->args += yytext;
1124 Argument *a = new Argument;
1125 a->name = QCString(yytext).lower();
1126 current->argList->append(a);
1130 /*------------------------------------------------------------------------------------------------*/
1134 //if (debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << debugStr << " state: " <<YY_START << endl;
1139 /*---- error: EOF in wrong state --------------------------------------------------------------------*/
1142 if (parsingPrototype) {
1145 } else if ( include_stack_ptr <= 0 ) {
1146 if (YY_START!=INITIAL && YY_START!=Start) {
1147 DBG_CTX((stderr,"==== Error: EOF reached in wrong state (end missing)"));
1155 <*>{LOG_OPER} { // Fortran logical comparison keywords
1159 //printf("I:%c\n", *yytext);
1160 } // ignore remaining text
1162 /**********************************************************************************/
1163 /**********************************************************************************/
1164 /**********************************************************************************/
1166 //----------------------------------------------------------------------------
1169 static void extractPrefix(QCString &text)
1171 int prefixIndex = 0;
1174 const char* pre[] = {"RECURSIVE","IMPURE","PURE","ELEMENTAL"};
1178 for(unsigned int i=0; i<4; i++)
1180 if((prefixIndex=text.find(pre[i], curIndex, FALSE))==0)
1182 text.remove(0,strlen(pre[i]));
1183 text.stripWhiteSpace();
1191 static void newLine() {
1193 yyLineNr+=lineCountPrepass;
1198 static CommentInPrepass* locatePrepassComment(int from, int to) {
1199 //printf("Locate %d-%d\n", from, to);
1200 for(uint i=0; i<comments.count(); i++) { // todo: optimize
1201 int c = comments.at(i)->column;
1202 //printf("Candidate %d\n", c);
1203 if (c>=from && c<=to) {
1204 // comment for previous variable or parameter
1205 return comments.at(i);
1211 static void updateVariablePrepassComment(int from, int to) {
1212 CommentInPrepass *c = locatePrepassComment(from, to);
1213 if (c!=NULL && v_type == V_VARIABLE) {
1214 last_entry->brief = c->str;
1215 } else if (c!=NULL && v_type == V_PARAMETER) {
1216 Argument *parameter = getParameter(argName);
1217 if (parameter) parameter->docs = c->str;
1221 static int getAmpersandAtTheStart(const char *buf, int length)
1223 for(int i=0; i<length; i++) {
1237 /* Returns ampersand index, comment start index or -1 if neither exist.*/
1238 static int getAmpOrExclAtTheEnd(const char *buf, int length)
1240 // Avoid ampersands in string and comments
1241 int parseState = Start;
1242 char quoteSymbol = 0;
1244 int commentIndex = -1;
1246 for(int i=0; i<length && parseState!=Comment; i++)
1248 // When in string, skip backslashes
1249 // Legacy code, not sure whether this is correct?
1250 if(parseState==String)
1252 if(buf[i]=='\\') i++;
1259 // Close string, if quote symbol matches.
1260 // Quote symbol is set iff parseState==String
1261 if(buf[i]==quoteSymbol)
1266 // Start new string, if not already in string or comment
1267 else if(parseState==Start)
1269 parseState = String;
1270 quoteSymbol = buf[i];
1272 ampIndex = -1; // invalidate prev ampersand
1275 // When in string or comment, ignore exclamation mark
1276 if(parseState==Start)
1278 parseState = Comment;
1282 case ' ': // ignore whitespace
1284 case '\n': // this may be at the end of line
1290 ampIndex = -1; // invalidate prev ampersand
1297 return commentIndex;
1300 /* Although comments at the end of continuation line are grabbed by this function,
1301 * we still do not know how to use them later in parsing.
1303 void truncatePrepass(int index)
1305 int length = inputStringPrepass.length();
1306 for (int i=index+1; i<length; i++) {
1307 if (inputStringPrepass[i]=='!' && i<length-1 && inputStringPrepass[i+1]=='<') { // save comment
1308 struct CommentInPrepass *c=new CommentInPrepass(index, inputStringPrepass.right(length-i-2));
1312 inputStringPrepass.truncate(index);
1315 // simplified way to know if this is fixed form
1316 // duplicate in fortrancode.l
1317 static bool recognizeFixedForm(const char* contents, FortranFormat format)
1320 bool skipLine=FALSE;
1322 if (format == FortranFormat_Fixed) return TRUE;
1323 if (format == FortranFormat_Free) return FALSE;
1328 switch(contents[i]) {
1343 if(column==1) return TRUE;
1347 if(column>1 && column<7) return FALSE;
1352 if(column==7) return TRUE;
1359 /* This function assumes that contents has at least size=length+1 */
1360 static void insertCharacter(char *contents, int length, int pos, char c)
1362 // shift tail by one character
1363 for(int i=length; i>pos; i--)
1364 contents[i]=contents[i-1];
1365 // set the character
1369 /* change comments and bring line continuation character to previous line */
1370 static const char* prepassFixedForm(const char* contents)
1373 int prevLineLength=0;
1374 int prevLineAmpOrExclIndex=-1;
1375 bool emptyLabel=TRUE;
1376 int newContentsSize = strlen(contents)+3; // \000, \n (when necessary) and one spare character (to avoid reallocation)
1377 char* newContents = (char*)malloc(newContentsSize);
1379 for(int i=0, j=0;;i++,j++) {
1380 if(j>=newContentsSize-1) { // check for one spare character, which may be eventually used below (by &)
1381 newContents = (char*)realloc(newContents, newContentsSize+1000);
1382 newContentsSize = newContentsSize+1000;
1386 char c = contents[i];
1389 prevLineLength=column;
1390 prevLineAmpOrExclIndex=getAmpOrExclAtTheEnd(&contents[i-prevLineLength+1], prevLineLength);
1399 newContents[j]='\000';
1400 newContentsSize = strlen(newContents);
1401 if (newContents[newContentsSize - 1] != '\n')
1403 // to be on the safe side
1404 newContents = (char*)realloc(newContents, newContentsSize+2);
1405 newContents[newContentsSize] = '\n';
1406 newContents[newContentsSize + 1] = '\000';
1422 if(column==6 && emptyLabel) { // continuation
1423 if (c != '0') { // 0 not allowed as continuatioin character, see f95 standard paragraph 3.3.2.3
1426 if(prevLineAmpOrExclIndex==-1) { // add & just before end of previous line
1427 insertCharacter(newContents, j+1, (j+1)-6-1, '&');
1429 } else { // add & just before end of previous line comment
1430 insertCharacter(newContents, j+1, (j+1)-6-prevLineLength+prevLineAmpOrExclIndex, '&');
1434 newContents[j]=c; // , just handle like space
1444 newContentsSize = strlen(newContents);
1445 if (newContents[newContentsSize - 1] != '\n')
1447 // to be on the safe side
1448 newContents = (char*)realloc(newContents, newContentsSize+2);
1449 newContents[newContentsSize] = '\n';
1450 newContents[newContentsSize + 1] = '\000';
1455 static void pushBuffer(QCString& buffer)
1457 if (include_stack_cnt <= include_stack_ptr)
1459 include_stack_cnt++;
1460 include_stack = (YY_BUFFER_STATE *)realloc(include_stack, include_stack_cnt * sizeof(YY_BUFFER_STATE));
1462 include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
1463 yy_switch_to_buffer(yy_scan_string(buffer));
1465 DBG_CTX((stderr, "--PUSH--%s", (const char *)buffer));
1469 static void popBuffer() {
1470 DBG_CTX((stderr, "--POP--"));
1471 include_stack_ptr --;
1472 yy_delete_buffer( YY_CURRENT_BUFFER );
1473 yy_switch_to_buffer( include_stack[include_stack_ptr] );
1476 /** used to copy entry to an interface module procedure */
1477 static void copyEntry(Entry *dest, Entry *src)
1479 dest->type = src->type;
1480 dest->fileName = src->fileName;
1481 dest->bodyLine = src->bodyLine;
1482 dest->args = src->args;
1483 dest->argList = new ArgumentList(*src->argList);
1484 dest->doc = src->doc;
1485 dest->brief = src->brief;
1488 /** fill empty interface module procedures with info from
1489 corresponding module subprogs
1490 @TODO: handle procedures in used modules
1492 void resolveModuleProcedures(QList<Entry> &moduleProcedures, Entry *current_root)
1494 if (moduleProcedures.isEmpty()) return;
1496 EntryListIterator eli1(moduleProcedures);
1497 // for all module procedures
1498 for (Entry *ce1; (ce1=eli1.current()); ++eli1)
1500 // check all entries in this module
1501 EntryListIterator eli2(*current_root->children());
1502 for (Entry *ce2; (ce2=eli2.current()); ++eli2)
1504 if (ce1->name == ce2->name)
1506 copyEntry(ce1, ce2);
1508 } // for procedures in current module
1509 } // for all interface module procedures
1510 moduleProcedures.clear();
1514 static bool isTypeName(QCString name)
1516 name = name.lower();
1517 return name=="integer" || name == "real" ||
1518 name=="complex" || name == "logical";
1522 /*! Extracts string which resides within parentheses of provided string. */
1523 static QCString extractFromParens(const QCString name)
1525 QCString extracted = name;
1526 int start = extracted.find("(");
1529 extracted.remove(0, start+1);
1531 int end = extracted.findRev(")");
1534 int length = extracted.length();
1535 extracted.remove(end, length);
1537 extracted = extracted.stripWhiteSpace();
1542 /*! Adds passed modifiers to these modifiers.*/
1543 SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs)
1545 if (mdfs.protection!=NONE_P) protection = mdfs.protection;
1546 if (mdfs.direction!=NONE_D) direction = mdfs.direction;
1547 optional |= mdfs.optional;
1548 if (!mdfs.dimension.isNull()) dimension = mdfs.dimension;
1549 allocatable |= mdfs.allocatable;
1550 external |= mdfs.external;
1551 intrinsic |= mdfs.intrinsic;
1552 protect |= mdfs.protect;
1553 parameter |= mdfs.parameter;
1554 pointer |= mdfs.pointer;
1555 target |= mdfs.target;
1557 deferred |= mdfs.deferred;
1558 nonoverridable |= mdfs.nonoverridable;
1559 nopass |= mdfs.nopass;
1561 passVar = mdfs.passVar;
1565 /*! Extracts and adds passed modifier to these modifiers.*/
1566 SymbolModifiers& SymbolModifiers::operator|=(QCString mdfString)
1568 mdfString = mdfString.lower();
1569 SymbolModifiers newMdf;
1571 if (mdfString.find("dimension")==0)
1573 newMdf.dimension=mdfString;
1575 else if (mdfString.contains("intent"))
1577 QCString tmp = extractFromParens(mdfString);
1578 bool isin = tmp.contains("in");
1579 bool isout = tmp.contains("out");
1580 if (isin && isout) newMdf.direction = SymbolModifiers::INOUT;
1581 else if (isin) newMdf.direction = SymbolModifiers::IN;
1582 else if (isout) newMdf.direction = SymbolModifiers::OUT;
1584 else if (mdfString=="public")
1586 newMdf.protection = SymbolModifiers::PUBLIC;
1588 else if (mdfString=="private")
1590 newMdf.protection = SymbolModifiers::PRIVATE;
1592 else if (mdfString=="protected")
1594 newMdf.protect = TRUE;
1596 else if (mdfString=="optional")
1598 newMdf.optional = TRUE;
1600 else if (mdfString=="allocatable")
1602 newMdf.allocatable = TRUE;
1604 else if (mdfString=="external")
1606 newMdf.external = TRUE;
1608 else if (mdfString=="intrinsic")
1610 newMdf.intrinsic = TRUE;
1612 else if (mdfString=="parameter")
1614 newMdf.parameter = TRUE;
1616 else if (mdfString=="pointer")
1618 newMdf.pointer = TRUE;
1620 else if (mdfString=="target")
1622 newMdf.target = TRUE;
1624 else if (mdfString=="save")
1628 else if (mdfString=="nopass")
1630 newMdf.nopass = TRUE;
1632 else if (mdfString=="deferred")
1634 newMdf.deferred = TRUE;
1636 else if (mdfString=="non_overridable")
1638 newMdf.nonoverridable = TRUE;
1640 else if (mdfString.contains("pass"))
1643 if (mdfString.contains("("))
1644 newMdf.passVar = extractFromParens(mdfString);
1646 newMdf.passVar = "";
1653 /*! For debugging purposes. */
1654 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs)
1656 // out<<mdfs.protection<<", "<<mdfs.direction<<", "<<mdfs.optional<<
1657 // ", "<<(mdfs.dimension.isNull() ? "" : mdfs.dimension.latin1())<<
1658 // ", "<<mdfs.allocatable<<", "<<mdfs.external<<", "<<mdfs.intrinsic;
1663 /*! Find argument with given name in \a subprog entry. */
1664 static Argument *findArgument(Entry* subprog, QCString name, bool byTypeName = FALSE)
1666 QCString cname(name.lower());
1667 for (unsigned int i=0; i<subprog->argList->count(); i++)
1669 Argument *arg = subprog->argList->at(i);
1670 if ((!byTypeName && arg->name.lower() == cname) ||
1671 (byTypeName && arg->type.lower() == cname)
1680 /*! Find function with given name in \a entry. */
1682 static Entry *findFunction(Entry* entry, QCString name)
1684 QCString cname(name.lower());
1686 EntryListIterator eli(*entry->children());
1688 for (;(ce=eli.current());++eli)
1690 if (ce->section != Entry::FUNCTION_SEC)
1693 if (ce->name.lower() == cname)
1701 /*! Apply modifiers stored in \a mdfs to the \a typeName string. */
1702 static QCString applyModifiers(QCString typeName, SymbolModifiers& mdfs)
1704 if (!mdfs.dimension.isNull())
1706 if (!typeName.isEmpty()) typeName += ", ";
1707 typeName += mdfs.dimension;
1709 if (mdfs.direction!=SymbolModifiers::NONE_D)
1711 if (!typeName.isEmpty()) typeName += ", ";
1712 typeName += directionStrs[mdfs.direction];
1716 if (!typeName.isEmpty()) typeName += ", ";
1717 typeName += "optional";
1719 if (mdfs.allocatable)
1721 if (!typeName.isEmpty()) typeName += ", ";
1722 typeName += "allocatable";
1726 if (!typeName.isEmpty()) typeName += ", ";
1727 typeName += "external";
1731 if (!typeName.isEmpty()) typeName += ", ";
1732 typeName += "intrinsic";
1736 if (!typeName.isEmpty()) typeName += ", ";
1737 typeName += "parameter";
1741 if (!typeName.isEmpty()) typeName += ", ";
1742 typeName += "pointer";
1746 if (!typeName.isEmpty()) typeName += ", ";
1747 typeName += "target";
1751 if (!typeName.isEmpty()) typeName += ", ";
1756 if (!typeName.isEmpty()) typeName += ", ";
1757 typeName += "deferred";
1759 if (mdfs.nonoverridable)
1761 if (!typeName.isEmpty()) typeName += ", ";
1762 typeName += "non_overridable";
1766 if (!typeName.isEmpty()) typeName += ", ";
1767 typeName += "nopass";
1771 if (!typeName.isEmpty()) typeName += ", ";
1773 if (!mdfs.passVar.isEmpty())
1774 typeName += "(" + mdfs.passVar + ")";
1776 if (mdfs.protection == SymbolModifiers::PUBLIC)
1778 if (!typeName.isEmpty()) typeName += ", ";
1779 typeName += "public";
1781 else if (mdfs.protection == SymbolModifiers::PRIVATE)
1783 if (!typeName.isEmpty()) typeName += ", ";
1784 typeName += "private";
1788 if (!typeName.isEmpty()) typeName += ", ";
1789 typeName += "protected";
1795 /*! Apply modifiers stored in \a mdfs to the \a arg argument. */
1796 static void applyModifiers(Argument *arg, SymbolModifiers& mdfs)
1798 QCString tmp = arg->type;
1799 arg->type = applyModifiers(tmp, mdfs);
1802 /*! Apply modifiers stored in \a mdfs to the \a ent entry. */
1803 static void applyModifiers(Entry *ent, SymbolModifiers& mdfs)
1805 QCString tmp = ent->type;
1806 ent->type = applyModifiers(tmp, mdfs);
1808 if (mdfs.protection == SymbolModifiers::PUBLIC)
1809 ent->protection = Public;
1810 else if (mdfs.protection == SymbolModifiers::PRIVATE)
1811 ent->protection = Private;
1814 /*! Starts the new scope in fortran program. Consider using this function when
1815 * starting module, interface, function or other program block.
1818 static void startScope(Entry *scope)
1820 //cout<<"start scope: "<<scope->name<<endl;
1821 current_root= scope; /* start substructure */
1823 QMap<QCString,SymbolModifiers> mdfMap;
1824 modifiers.insert(scope, mdfMap);
1827 /*! Ends scope in fortran program: may update subprogram arguments or module variable attributes.
1830 static bool endScope(Entry *scope, bool isGlobalRoot)
1832 //cout<<"end scope: "<<scope->name<<endl;
1833 if (current_root->parent() || isGlobalRoot)
1835 current_root= current_root->parent(); /* end substructure */
1839 fprintf(stderr,"parse error in end <scopename>");
1844 // update variables or subprogram arguments with modifiers
1845 QMap<QCString,SymbolModifiers>& mdfsMap = modifiers[scope];
1847 if (scope->section == Entry::FUNCTION_SEC)
1849 // iterate all symbol modifiers of the scope
1850 for (QMap<QCString,SymbolModifiers>::Iterator it=mdfsMap.begin(); it!=mdfsMap.end(); it++)
1852 //cout<<it.key()<<": "<<it.data()<<endl;
1853 Argument *arg = findArgument(scope, it.key());
1856 applyModifiers(arg, it.data());
1859 // find return type for function
1860 //cout<<"RETURN NAME "<<modifiers[current_root][scope->name.lower()].returnName<<endl;
1861 QCString returnName = modifiers[current_root][scope->name.lower()].returnName.lower();
1862 if (modifiers[scope].contains(returnName))
1864 scope->type = modifiers[scope][returnName].type; // returning type works
1865 applyModifiers(scope, modifiers[scope][returnName]); // returning array works
1869 if (scope->section == Entry::CLASS_SEC)
1870 { // was INTERFACE_SEC
1871 if (scope->parent()->section == Entry::FUNCTION_SEC)
1872 { // interface within function
1873 // iterate functions of interface and
1874 // try to find types for dummy(ie. argument) procedures.
1875 //cout<<"Search in "<<scope->name<<endl;
1876 EntryListIterator eli(*scope->children());
1880 for (;(ce=eli.current());++eli)
1883 if (ce->section != Entry::FUNCTION_SEC)
1886 Argument *arg = findArgument(scope->parent(), ce->name, TRUE);
1889 // set type of dummy procedure argument to interface
1890 arg->name = arg->type;
1891 arg->type = scope->name;
1893 if (ce->name.lower() == scope->name.lower()) found = TRUE;
1895 if ((count == 1) && found)
1897 // clear all modifiers of the scope
1898 modifiers.remove(scope);
1899 delete scope->parent()->removeSubEntry(scope);
1905 if (scope->section!=Entry::FUNCTION_SEC)
1906 { // not function section
1907 // iterate variables: get and apply modifiers
1908 EntryListIterator eli(*scope->children());
1910 for (;(ce=eli.current());++eli)
1912 if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC)
1915 //cout<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl;
1916 if (mdfsMap.contains(ce->name.lower()))
1917 applyModifiers(ce, mdfsMap[ce->name.lower()]);
1921 // clear all modifiers of the scope
1922 modifiers.remove(scope);
1928 //! Return full name of the entry. Sometimes we must combine several names recursively.
1929 static QCString getFullName(Entry *e)
1931 QCString name = e->name;
1932 if (e->section == Entry::CLASS_SEC // || e->section == Entry::INTERFACE_SEC
1933 || !e->parent() || e->parent()->name.isEmpty())
1936 return getFullName(e->parent())+"::"+name;
1940 static int yyread(char *buf,int max_size)
1944 while ( c < max_size && inputString[inputPosition] )
1946 *buf = inputString[inputPosition++] ;
1952 static void initParser()
1957 static void initEntry()
1961 current->protection = typeProtection;
1965 current->protection = defaultProtection;
1967 current->mtype = mtype;
1968 current->virt = virt;
1969 current->stat = gstat;
1970 current->lang = SrcLangExt_Fortran;
1971 initGroupInfo(current);
1975 adds current entry to current_root and creates new current
1977 static void addCurrentEntry(int case_insens)
1979 if (case_insens) current->name = current->name.lower();
1980 //printf("===Adding entry %s to %s\n", current->name.data(), current_root->name.data());
1981 current_root->addSubEntry(current);
1982 last_entry = current;
1983 current = new Entry ;
1987 static int max(int a, int b) {return a>b?a:b;}
1989 static void addModule(const char *name, bool isModule)
1991 DBG_CTX((stderr, "0=========> got module %s\n", name));
1994 current->section = Entry::CLASS_SEC;
1996 current->section = Entry::FUNCTION_SEC;
2000 current->name = name;
2004 QCString fname = yyFileName;
2005 int index = max(fname.findRev('/'), fname.findRev('\\'));
2006 fname = fname.right(fname.length()-index-1);
2007 fname = fname.prepend("__").append("__");
2008 current->name = fname;
2010 current->type = "program";
2011 current->fileName = yyFileName;
2012 current->bodyLine = yyLineNr; // used for source reference
2013 current->protection = Public ;
2015 startScope(last_entry);
2019 static void addSubprogram(const char *text)
2021 DBG_CTX((stderr,"1=========> got subprog, type: %s\n",text));
2022 subrCurrent.prepend(current);
2023 current->section = Entry::FUNCTION_SEC ;
2024 QCString subtype = text; subtype=subtype.lower().stripWhiteSpace();
2025 functionLine = (subtype.find("function") != -1);
2026 current->type += " " + subtype;
2027 current->type = current->type.stripWhiteSpace();
2028 current->fileName = yyFileName;
2029 current->bodyLine = yyLineNr; // used for source reference
2030 current->startLine = -1; // ??? what is startLine for?
2031 current->args.resize(0);
2032 current->argList->clear();
2036 /*! Adds interface to the root entry.
2037 * \note Code was brought to this procedure from the parser,
2038 * because there was/is idea to use it in several parts of the parser.
2040 static void addInterface(QCString name, InterfaceType type)
2042 if (YY_START == Start)
2045 yy_push_state(ModuleBody); //anon program
2048 current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC;
2049 current->spec = Entry::Interface;
2050 current->name = name;
2055 current->type = "abstract";
2059 current->type = "generic";
2068 /* if type is part of a module, mod name is necessary for output */
2069 if ((current_root) &&
2070 (current_root->section == Entry::CLASS_SEC ||
2071 current_root->section == Entry::NAMESPACE_SEC))
2073 current->name= current_root->name + "::" + current->name;
2076 current->fileName = yyFileName;
2077 current->bodyLine = yyLineNr;
2082 //-----------------------------------------------------------------------------
2084 /*! Get the argument \a name.
2086 static Argument* getParameter(const QCString &name)
2088 // std::cout<<"addFortranParameter(): "<<name<<" DOCS:"<<(docs.isNull()?QCString("null"):docs)<<std::endl;
2090 if (current_root->argList==0) return 0;
2091 ArgumentListIterator ali(*current_root->argList);
2093 for (ali.toFirst();(a=ali.current());++ali)
2095 if (a->name.lower()==name.lower())
2098 //printf("parameter found: %s\n",(const char*)name);
2105 //----------------------------------------------------------------------------
2106 static void startCommentBlock(bool brief)
2110 current->briefFile = yyFileName;
2111 current->briefLine = yyLineNr;
2115 current->docFile = yyFileName;
2116 current->docLine = yyLineNr;
2120 //----------------------------------------------------------------------------
2122 static void handleCommentBlock(const QCString &doc,bool brief)
2124 bool needsEntry = FALSE;
2125 static bool hideInBodyDocs = Config_getBool("HIDE_IN_BODY_DOCS");
2127 if (docBlockInBody && hideInBodyDocs)
2129 docBlockInBody = FALSE;
2132 DBG_CTX((stderr,"call parseCommentBlock [%s]\n",doc.data()));
2133 int lineNr = brief ? current->briefLine : current->docLine;
2134 while (parseCommentBlock(
2136 docBlockInBody ? subrCurrent.getFirst() : current,
2140 docBlockInBody ? FALSE : brief,
2141 docBlockInBody ? FALSE : docBlockJavaStyle,
2148 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2149 if (needsEntry) addCurrentEntry(0);
2151 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2153 if (needsEntry) addCurrentEntry(0);
2154 docBlockInBody = FALSE;
2157 //----------------------------------------------------------------------------
2159 static void subrHandleCommentBlock(const QCString &doc,bool brief)
2162 Entry *tmp_entry = current;
2163 current = subrCurrent.getFirst(); // temporarily switch to the entry of the subroutine / function
2165 // Still in the specification section so no inbodyDocs yet, but parameter documentation
2166 current->inbodyDocs = "";
2168 if (docBlock.stripWhiteSpace().find("\\param") == 0)
2170 handleCommentBlock("\n\n"+doc,brief);
2172 else if (docBlock.stripWhiteSpace().find("@param") == 0)
2174 handleCommentBlock("\n\n"+doc,brief);
2178 int dir1 = modifiers[current_root][argName.lower()].direction;
2179 loc_doc = doc.stripWhiteSpace();
2180 if (loc_doc.lower().find(directionParam[SymbolModifiers::IN]) == 0)
2182 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2183 (directionParam[dir1] == directionParam[SymbolModifiers::IN]))
2185 handleCommentBlock(QCString("\n\n@param ") + directionParam[SymbolModifiers::IN] + " " +
2186 argName + " " + loc_doc.right(loc_doc.length()-strlen(directionParam[SymbolModifiers::IN])),brief);
2190 warn(yyFileName,yyLineNr, "inconsistency between intent attribute and documentation for variable: "+argName);
2191 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2192 argName + " " + doc,brief);
2195 else if (loc_doc.lower().find(directionParam[SymbolModifiers::OUT]) == 0)
2197 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2198 (directionParam[dir1] == directionParam[SymbolModifiers::OUT]))
2200 handleCommentBlock(QCString("\n\n@param ") + directionParam[SymbolModifiers::OUT] + " " +
2201 argName + " " + loc_doc.right(loc_doc.length()-strlen(directionParam[SymbolModifiers::OUT])),brief);
2205 warn(yyFileName,yyLineNr, "inconsistency between intent attribute and documentation for variable: "+argName);
2206 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2207 argName + " " + doc,brief);
2210 else if (loc_doc.lower().find(directionParam[SymbolModifiers::INOUT]) == 0)
2212 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2213 (directionParam[dir1] == directionParam[SymbolModifiers::INOUT]))
2215 handleCommentBlock(QCString("\n\n@param ") + directionParam[SymbolModifiers::INOUT] + " " +
2216 argName + " " + loc_doc.right(loc_doc.length()-strlen(directionParam[SymbolModifiers::INOUT])),brief);
2220 warn(yyFileName,yyLineNr, "inconsistency between intent attribute and documentation for variable: "+argName);
2221 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2222 argName + " " + doc,brief);
2227 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2228 argName + " " + doc,brief);
2234 //----------------------------------------------------------------------------
2238 static void debugCompounds(Entry *rt) // print Entry structure (for debugging)
2241 printf("%d) debugCompounds(%s) line %d\n",level, rt->name.data(), rt->bodyLine);
2242 EntryListIterator eli(*rt->children());
2244 for (;(ce=eli.current());++eli)
2253 static void parseMain(const char *fileName,const char *fileBuf,Entry *rt, FortranFormat format)
2255 char *tmpBuf = NULL;
2258 defaultProtection = Public;
2259 inputString = fileBuf;
2261 inputStringPrepass = NULL;
2262 inputPositionPrepass = 0;
2264 //anonCount = 0; // don't reset per file
2270 inputFile.setName(fileName);
2271 if (inputFile.open(IO_ReadOnly))
2273 isFixedForm = recognizeFixedForm(fileBuf,format);
2277 msg("Prepassing fixed form of %s\n", fileName);
2278 //printf("---strlen=%d\n", strlen(fileBuf));
2279 //clock_t start=clock();
2281 inputString = prepassFixedForm(fileBuf);
2283 //clock_t end=clock();
2284 //printf("CPU time used=%f\n", ((double) (end-start))/CLOCKS_PER_SEC);
2286 else if (inputString[strlen(fileBuf)-1] != '\n')
2288 tmpBuf = (char *)malloc(strlen(fileBuf)+2);
2289 strcpy(tmpBuf,fileBuf);
2290 tmpBuf[strlen(fileBuf)]= '\n';
2291 tmpBuf[strlen(fileBuf)+1]= '\000';
2292 inputString = tmpBuf;
2296 yyFileName = fileName;
2297 msg("Parsing file %s...\n",yyFileName.data());
2299 startScope(rt); // implies current_root = rt
2301 groupEnterFile(yyFileName,yyLineNr);
2303 current = new Entry;
2304 current->lang = SrcLangExt_Fortran;
2305 current->name = yyFileName;
2306 current->section = Entry::SOURCE_SEC;
2307 current_root->addSubEntry(current);
2308 file_root = current;
2309 current = new Entry;
2310 current->lang = SrcLangExt_Fortran;
2312 fortranscannerYYrestart( fortranscannerYYin );
2317 fortranscannerYYlex();
2318 groupLeaveFile(yyFileName,yyLineNr);
2320 endScope(current_root, TRUE); // TRUE - global root
2322 //debugCompounds(rt); //debug
2324 rt->program.resize(0);
2325 delete current; current=0;
2326 moduleProcedures.clear();
2328 free((char*)tmpBuf);
2332 free((char*)inputString);
2340 //----------------------------------------------------------------------------
2342 void FortranLanguageScanner::parseInput(const char *fileName,
2343 const char *fileBuf,
2345 bool /*sameTranslationUnit*/,
2346 QStrList & /*filesInSameTranslationUnit*/)
2348 g_thisParser = this;
2350 printlex(yy_flex_debug, TRUE, __FILE__, fileName);
2352 ::parseMain(fileName,fileBuf,root,m_format);
2354 printlex(yy_flex_debug, FALSE, __FILE__, fileName);
2357 void FortranLanguageScanner::parseCode(CodeOutputInterface & codeOutIntf,
2358 const char * scopeName,
2359 const QCString & input,
2360 SrcLangExt /*lang*/,
2361 bool isExampleBlock,
2362 const char * exampleName,
2366 bool inlineFragment,
2367 MemberDef *memberDef,
2368 bool showLineNumbers,
2369 Definition *searchCtx,
2373 ::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName,
2374 fileDef,startLine,endLine,inlineFragment,memberDef,
2375 showLineNumbers,searchCtx,collectXRefs,m_format);
2378 bool FortranLanguageScanner::needsPreprocessing(const QCString &extension)
2380 return extension!=extension.lower(); // use preprocessor only for upper case extensions
2382 void FortranLanguageScanner::resetCodeParserState()
2384 ::resetFortranCodeParserState();
2387 void FortranLanguageScanner::parsePrototype(const char *text)
2389 QCString buffer = QCString(text);
2391 parsingPrototype = TRUE;
2393 fortranscannerYYlex();
2394 parsingPrototype = FALSE;
2398 static void scanner_abort()
2400 fprintf(stderr,"********************************************************************\n");
2401 fprintf(stderr,"Error in file %s line: %d, state: %d\n",yyFileName.data(),yyLineNr,YY_START);
2402 fprintf(stderr,"********************************************************************\n");
2404 EntryListIterator eli(*global_root->children());
2408 for (;(ce=eli.current());++eli)
2410 if (ce == file_root) start=TRUE;
2411 if (start) ce->reset();
2414 // dummy call to avoid compiler warning
2415 (void)yy_top_state();
2421 //----------------------------------------------------------------------------
2423 #if !defined(YY_FLEX_SUBMINOR_VERSION)
2424 //----------------------------------------------------------------------------
2425 extern "C" { // some bogus code to keep the compiler happy
2426 void fortranscannernerYYdummy() { yy_flex_realloc(0,0); }