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_} { }
746 {ATTR_SPEC}. { /* update current modifierswhen it is an ATTR_SPEC and not a variable name */
748 QChar chr = yytext[(int)yyleng-1];
749 if (chr.isLetter() || chr.isDigit() || (chr == '_'))
751 yyColNr -= (int)yyleng;
756 QCString tmp = yytext;
757 tmp = tmp.left(tmp.length() - 1);
759 unput(yytext[(int)yyleng-1]);
760 currentModifiers |= (tmp);
763 "::" { /* end attribute list */
766 . { /* unknown attribute, consider variable name */
767 //cout<<"start variables, unput "<<*yytext<<endl;
775 <Variable>{ID} { /* parse variable declaration */
776 //cout << "5=========> got variable: " << argType << "::" << yytext << endl;
777 /* work around for bug in QCString.replace (QCString works) */
778 QCString name=yytext;
780 /* remember attributes for the symbol */
781 modifiers[current_root][name.lower()] |= currentModifiers;
785 if (!argType.isEmpty() && current_root->section!=Entry::FUNCTION_SEC)
786 { // new variable entry
788 current->section = Entry::VARIABLE_SEC;
789 current->name = argName;
790 current->type = argType;
791 current->fileName = yyFileName;
792 current->bodyLine = yyLineNr; // used for source reference
795 else if (!argType.isEmpty())
796 { // declaration of parameter list: add type for corr. parameter
797 parameter = getParameter(argName);
801 if (!argType.isNull()) parameter->type=argType.stripWhiteSpace();
802 if (!docBlock.isNull())
804 subrHandleCommentBlock(docBlock,TRUE);
807 // save, it may be function return type
810 modifiers[current_root][name.lower()].type = argType;
814 if ((current_root->name.lower() == argName.lower()) ||
815 (modifiers[current_root->parent()][current_root->name.lower()].returnName.lower() == argName.lower()))
817 int strt = current_root->type.find("function");
824 if (strt != 0) lft = current_root->type.left(strt).stripWhiteSpace();
825 if ((current_root->type.length() - strt - strlen("function"))!= 0)
827 rght = current_root->type.right(current_root->type.length() - strt - strlen("function")).stripWhiteSpace();
829 current_root->type = lft;
830 if (rght.length() > 0)
832 if (current_root->type.length() > 0) current_root->type += " ";
833 current_root->type += rght;
835 if (argType.stripWhiteSpace().length() > 0)
837 if (current_root->type.length() > 0) current_root->type += " ";
838 current_root->type += argType.stripWhiteSpace();
840 if (current_root->type.length() > 0) current_root->type += " ";
841 current_root->type += "function";
845 current_root->type += " " + argType.stripWhiteSpace();
847 current_root->type = current_root->type.stripWhiteSpace();
848 modifiers[current_root][name.lower()].type = current_root->type;
852 modifiers[current_root][name.lower()].type = argType;
855 // any accumulated doc for argument should be emptied,
856 // because it is handled other way and this doc can be
857 // unexpectedly passed to the next member.
858 current->doc.resize(0);
859 current->brief.resize(0);
862 <Variable>{ARGS} { /* dimension of the previous entry. */
863 QCString name(argName);
864 QCString attr("dimension");
866 modifiers[current_root][name.lower()] |= attr;
868 <Variable>{COMMA} { //printf("COMMA: %d<=..<=%d\n", yyColNr-(int)yyleng, yyColNr);
870 updateVariablePrepassComment(yyColNr-(int)yyleng, yyColNr);
872 <Variable>{BS}"=" { yy_push_state(YY_START);
874 initializerScope = initializerArrayScope = 0;
875 BEGIN(Initialization);
877 <Variable>"\n" { currentModifiers = SymbolModifiers();
878 yy_pop_state(); // end variable declaration list
882 <Variable>";".*"\n" { currentModifiers = SymbolModifiers();
883 yy_pop_state(); // end variable declaration list
885 inputStringSemi =(const char*)(QCString(" \n") + QCString(yytext+1)).data();
887 pushBuffer(inputStringSemi);
890 if (YY_START == Variable) REJECT; // Just be on the safe side
891 if (YY_START == String) REJECT; // ";" ignored in strings
892 if (YY_START == StrIgnore) REJECT; // ";" ignored in regular comments
893 inputStringSemi =(const char*)(QCString(" \n") + QCString(yytext+1)).data();
895 pushBuffer(inputStringSemi);
898 <Initialization,ArrayInitializer>"[" |
899 <Initialization,ArrayInitializer>"(/" { initializer+=yytext;
900 initializerArrayScope++;
901 BEGIN(ArrayInitializer); // initializer may contain comma
903 <ArrayInitializer>"]" |
904 <ArrayInitializer>"/)" { initializer+=yytext;
905 initializerArrayScope--;
906 if(initializerArrayScope<=0)
908 initializerArrayScope = 0; // just in case
909 BEGIN(Initialization);
912 <ArrayInitializer>. { initializer+=yytext; }
913 <Initialization>"(" { initializerScope++;
916 <Initialization>")" { initializerScope--;
919 <Initialization>{COMMA} { if (initializerScope == 0)
921 updateVariablePrepassComment(yyColNr-(int)yyleng, yyColNr);
922 yy_pop_state(); // end initialization
923 if (v_type == V_VARIABLE) last_entry->initializer= initializer;
928 <Initialization>"\n"|"!" { //|
929 yy_pop_state(); // end initialization
930 if (v_type == V_VARIABLE) last_entry->initializer= initializer;
934 <Initialization>. { initializer+=yytext; }
936 /*------ fortran subroutine/function handling ------------------------------------------------------------*/
937 /* Start is initial condition */
939 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{TYPE_SPEC}{BS}({PREFIX}{BS_})?/{SUBPROG}{BS_} {
940 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
942 addInterface("$interface$", ifType);
943 startScope(last_entry);
946 // TYPE_SPEC is for old function style function result
947 result = QCString(yytext).stripWhiteSpace().lower();
948 current->type = result;
949 yy_push_state(SubprogPrefix);
952 <SubprogPrefix>{BS}{SUBPROG}{BS_} {
953 // Fortran subroutine or function found
956 result=result.stripWhiteSpace();
957 addSubprogram(result);
961 <Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{SUBPROG}{BS_} {
962 // Fortran subroutine or function found
964 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
966 addInterface("$interface$", ifType);
967 startScope(last_entry);
970 result = QCString(yytext).stripWhiteSpace();
971 addSubprogram(result);
972 yy_push_state(Subprog);
975 <Subprog>{BS} { /* ignore white space */ }
976 <Subprog>{ID} { current->name = yytext;
977 //cout << "1a==========> got " << current->type << " " << yytext << " " << yyLineNr << endl;
978 modifiers[current_root][current->name.lower()].returnName = current->name.lower();
980 if (ifType == IF_ABSTRACT || ifType == IF_SPECIFIC)
982 current_root->name.replace(QRegExp("\\$interface\\$"), yytext);
985 BEGIN(Parameterlist);
987 <Parameterlist>"(" { current->args = "("; }
989 current->args += ")";
990 current->args = removeRedundantWhiteSpace(current->args);
992 startScope(last_entry);
995 <Parameterlist>{COMMA}|{BS} { current->args += yytext;
996 CommentInPrepass *c = locatePrepassComment(yyColNr-(int)yyleng, yyColNr);
998 if(current->argList->count()>0) {
999 current->argList->at(current->argList->count()-1)->docs = c->str;
1003 <Parameterlist>{ID} {
1004 //current->type not yet available
1005 QCString param = yytext;
1006 // std::cout << "3=========> got parameter " << param << std::endl;
1007 current->args += param;
1008 Argument *arg = new Argument;
1011 current->argList->append(arg);
1013 <Parameterlist>{NOARGS} {
1015 //printf("3=========> without parameterlist \n");
1016 //current->argList = ;
1018 startScope(last_entry);
1021 <SubprogBody>result{BS}\({BS}{ID} {
1025 result= result.right(result.length()-result.find("(")-1);
1026 result= result.stripWhiteSpace();
1027 modifiers[current_root->parent()][current_root->name.lower()].returnName = result;
1029 //cout << "=====> got result " << result << endl;
1032 /*---- documentation comments --------------------------------------------------------------------*/
1034 <Variable,SubprogBody,ModuleBody,TypedefBody,TypedefBodyContains>"!<" { /* backward docu comment */
1035 if (v_type != V_IGNORE) {
1036 current->docLine = yyLineNr;
1037 docBlockJavaStyle = FALSE;
1039 docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF");
1040 startCommentBlock(TRUE);
1041 yy_push_state(DocBackLine);
1045 /* handle out of place !< comment as a normal comment */
1046 if (YY_START == String) { yyColNr -= (int)yyleng; REJECT; } // "!" is ignored in strings
1047 // skip comment line (without docu comments "!>" "!<" )
1048 /* ignore further "!" and ignore comments in Strings */
1049 if ((YY_START != StrIgnore) && (YY_START != String))
1051 yy_push_state(YY_START);
1054 //fprintf(stderr,"start comment %d\n",yyLineNr);
1058 <DocBackLine>.* { // contents of current comment line
1061 <DocBackLine>"\n"{BS}"!"("<"|"!"+) { // comment block (next line is also comment line)
1062 docBlock+="\n"; // \n is necessary for lists
1065 <DocBackLine>"\n" { // comment block ends at the end of this line
1066 //cout <<"3=========> comment block : "<< docBlock << endl;
1069 if (v_type == V_VARIABLE)
1071 Entry *tmp_entry = current;
1072 current = last_entry; // temporarily switch to the previous entry
1073 handleCommentBlock(docBlock,TRUE);
1076 else if (v_type == V_PARAMETER)
1078 subrHandleCommentBlock(docBlock,TRUE);
1084 <Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains,TypedefBodyContains>"!>" {
1085 yy_push_state(YY_START);
1086 current->docLine = yyLineNr;
1087 docBlockJavaStyle = FALSE;
1088 if (YY_START==SubprogBody) docBlockInBody = TRUE;
1090 docBlockJavaStyle = Config_getBool("JAVADOC_AUTOBRIEF");
1091 startCommentBlock(TRUE);
1093 //cout << "start DocBlock " << endl;
1096 <DocBlock>.* { // contents of current comment line
1099 <DocBlock>"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line)
1100 docBlock+="\n"; // \n is necessary for lists
1103 <DocBlock>"\n" { // comment block ends at the end of this line
1104 //cout <<"3=========> comment block : "<< docBlock << endl;
1107 handleCommentBlock(docBlock,TRUE);
1111 /*-----Prototype parsing -------------------------------------------------------------------------*/
1112 <Prototype>{BS}{SUBPROG}{BS_} {
1113 BEGIN(PrototypeSubprog);
1115 <Prototype,PrototypeSubprog>{BS}{SCOPENAME}?{BS}{ID} {
1116 current->name = QCString(yytext).lower();
1117 current->name.stripWhiteSpace();
1118 BEGIN(PrototypeArgs);
1121 "("|")"|","|{BS_} { current->args += yytext; }
1122 {ID} { current->args += yytext;
1123 Argument *a = new Argument;
1124 a->name = QCString(yytext).lower();
1125 current->argList->append(a);
1129 /*------------------------------------------------------------------------------------------------*/
1133 //if (debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << debugStr << " state: " <<YY_START << endl;
1138 /*---- error: EOF in wrong state --------------------------------------------------------------------*/
1141 if (parsingPrototype) {
1144 } else if ( include_stack_ptr <= 0 ) {
1145 if (YY_START!=INITIAL && YY_START!=Start) {
1146 DBG_CTX((stderr,"==== Error: EOF reached in wrong state (end missing)"));
1154 <*>{LOG_OPER} { // Fortran logical comparison keywords
1158 //printf("I:%c\n", *yytext);
1159 } // ignore remaining text
1161 /**********************************************************************************/
1162 /**********************************************************************************/
1163 /**********************************************************************************/
1165 //----------------------------------------------------------------------------
1168 static void extractPrefix(QCString &text)
1170 int prefixIndex = 0;
1173 const char* pre[] = {"RECURSIVE","IMPURE","PURE","ELEMENTAL"};
1177 for(unsigned int i=0; i<4; i++)
1179 if((prefixIndex=text.find(pre[i], curIndex, FALSE))==0)
1181 text.remove(0,strlen(pre[i]));
1182 text.stripWhiteSpace();
1190 static void newLine() {
1192 yyLineNr+=lineCountPrepass;
1197 static CommentInPrepass* locatePrepassComment(int from, int to) {
1198 //printf("Locate %d-%d\n", from, to);
1199 for(uint i=0; i<comments.count(); i++) { // todo: optimize
1200 int c = comments.at(i)->column;
1201 //printf("Candidate %d\n", c);
1202 if (c>=from && c<=to) {
1203 // comment for previous variable or parameter
1204 return comments.at(i);
1210 static void updateVariablePrepassComment(int from, int to) {
1211 CommentInPrepass *c = locatePrepassComment(from, to);
1212 if (c!=NULL && v_type == V_VARIABLE) {
1213 last_entry->brief = c->str;
1214 } else if (c!=NULL && v_type == V_PARAMETER) {
1215 Argument *parameter = getParameter(argName);
1216 if (parameter) parameter->docs = c->str;
1220 static int getAmpersandAtTheStart(const char *buf, int length)
1222 for(int i=0; i<length; i++) {
1236 /* Returns ampersand index, comment start index or -1 if neither exist.*/
1237 static int getAmpOrExclAtTheEnd(const char *buf, int length)
1239 // Avoid ampersands in string and comments
1240 int parseState = Start;
1241 char quoteSymbol = 0;
1243 int commentIndex = -1;
1245 for(int i=0; i<length && parseState!=Comment; i++)
1247 // When in string, skip backslashes
1248 // Legacy code, not sure whether this is correct?
1249 if(parseState==String)
1251 if(buf[i]=='\\') i++;
1258 // Close string, if quote symbol matches.
1259 // Quote symbol is set iff parseState==String
1260 if(buf[i]==quoteSymbol)
1265 // Start new string, if not already in string or comment
1266 else if(parseState==Start)
1268 parseState = String;
1269 quoteSymbol = buf[i];
1271 ampIndex = -1; // invalidate prev ampersand
1274 // When in string or comment, ignore exclamation mark
1275 if(parseState==Start)
1277 parseState = Comment;
1281 case ' ': // ignore whitespace
1283 case '\n': // this may be at the end of line
1289 ampIndex = -1; // invalidate prev ampersand
1296 return commentIndex;
1299 /* Although comments at the end of continuation line are grabbed by this function,
1300 * we still do not know how to use them later in parsing.
1302 void truncatePrepass(int index)
1304 int length = inputStringPrepass.length();
1305 for (int i=index+1; i<length; i++) {
1306 if (inputStringPrepass[i]=='!' && i<length-1 && inputStringPrepass[i+1]=='<') { // save comment
1307 struct CommentInPrepass *c=new CommentInPrepass(index, inputStringPrepass.right(length-i-2));
1311 inputStringPrepass.truncate(index);
1314 // simplified way to know if this is fixed form
1315 // duplicate in fortrancode.l
1316 static bool recognizeFixedForm(const char* contents, FortranFormat format)
1319 bool skipLine=FALSE;
1321 if (format == FortranFormat_Fixed) return TRUE;
1322 if (format == FortranFormat_Free) return FALSE;
1327 switch(contents[i]) {
1339 if(column==1) return TRUE;
1343 if(column>1 && column<7) return FALSE;
1348 if(column==7) return TRUE;
1355 /* This function assumes that contents has at least size=length+1 */
1356 static void insertCharacter(char *contents, int length, int pos, char c)
1358 // shift tail by one character
1359 for(int i=length; i>pos; i--)
1360 contents[i]=contents[i-1];
1361 // set the character
1365 /* change comments and bring line continuation character to previous line */
1366 static const char* prepassFixedForm(const char* contents)
1369 int prevLineLength=0;
1370 int prevLineAmpOrExclIndex=-1;
1371 bool emptyLabel=TRUE;
1372 int newContentsSize = strlen(contents)+3; // \000, \n (when necessary) and one spare character (to avoid reallocation)
1373 char* newContents = (char*)malloc(newContentsSize);
1375 for(int i=0, j=0;;i++,j++) {
1376 if(j>=newContentsSize-1) { // check for one spare character, which may be eventually used below (by &)
1377 newContents = (char*)realloc(newContents, newContentsSize+1000);
1378 newContentsSize = newContentsSize+1000;
1382 char c = contents[i];
1385 prevLineLength=column;
1386 prevLineAmpOrExclIndex=getAmpOrExclAtTheEnd(&contents[i-prevLineLength+1], prevLineLength);
1395 newContents[j]='\000';
1396 newContentsSize = strlen(newContents);
1397 if (newContents[newContentsSize - 1] != '\n')
1399 // to be on the safe side
1400 newContents = (char*)realloc(newContents, newContentsSize+2);
1401 newContents[newContentsSize] = '\n';
1402 newContents[newContentsSize + 1] = '\000';
1418 if(column==6 && emptyLabel) { // continuation
1419 if (c != '0') { // 0 not allowed as continuatioin character, see f95 standard paragraph 3.3.2.3
1422 if(prevLineAmpOrExclIndex==-1) { // add & just before end of previous line
1423 insertCharacter(newContents, j+1, (j+1)-6-1, '&');
1425 } else { // add & just before end of previous line comment
1426 insertCharacter(newContents, j+1, (j+1)-6-prevLineLength+prevLineAmpOrExclIndex, '&');
1430 newContents[j]=c; // , just handle like space
1440 newContentsSize = strlen(newContents);
1441 if (newContents[newContentsSize - 1] != '\n')
1443 // to be on the safe side
1444 newContents = (char*)realloc(newContents, newContentsSize+2);
1445 newContents[newContentsSize] = '\n';
1446 newContents[newContentsSize + 1] = '\000';
1451 static void pushBuffer(QCString& buffer)
1453 if (include_stack_cnt <= include_stack_ptr)
1455 include_stack_cnt++;
1456 include_stack = (YY_BUFFER_STATE *)realloc(include_stack, include_stack_cnt * sizeof(YY_BUFFER_STATE));
1458 include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
1459 yy_switch_to_buffer(yy_scan_string(buffer));
1461 DBG_CTX((stderr, "--PUSH--%s", (const char *)buffer));
1465 static void popBuffer() {
1466 DBG_CTX((stderr, "--POP--"));
1467 include_stack_ptr --;
1468 yy_delete_buffer( YY_CURRENT_BUFFER );
1469 yy_switch_to_buffer( include_stack[include_stack_ptr] );
1472 /** used to copy entry to an interface module procedure */
1473 static void copyEntry(Entry *dest, Entry *src)
1475 dest->type = src->type;
1476 dest->fileName = src->fileName;
1477 dest->bodyLine = src->bodyLine;
1478 dest->args = src->args;
1479 dest->argList = new ArgumentList(*src->argList);
1480 dest->doc = src->doc;
1481 dest->brief = src->brief;
1484 /** fill empty interface module procedures with info from
1485 corresponding module subprogs
1486 @TODO: handle procedures in used modules
1488 void resolveModuleProcedures(QList<Entry> &moduleProcedures, Entry *current_root)
1490 if (moduleProcedures.isEmpty()) return;
1492 EntryListIterator eli1(moduleProcedures);
1493 // for all module procedures
1494 for (Entry *ce1; (ce1=eli1.current()); ++eli1)
1496 // check all entries in this module
1497 EntryListIterator eli2(*current_root->children());
1498 for (Entry *ce2; (ce2=eli2.current()); ++eli2)
1500 if (ce1->name == ce2->name)
1502 copyEntry(ce1, ce2);
1504 } // for procedures in current module
1505 } // for all interface module procedures
1506 moduleProcedures.clear();
1510 static bool isTypeName(QCString name)
1512 name = name.lower();
1513 return name=="integer" || name == "real" ||
1514 name=="complex" || name == "logical";
1518 /*! Extracts string which resides within parentheses of provided string. */
1519 static QCString extractFromParens(const QCString name)
1521 QCString extracted = name;
1522 int start = extracted.find("(");
1525 extracted.remove(0, start+1);
1527 int end = extracted.findRev(")");
1530 int length = extracted.length();
1531 extracted.remove(end, length);
1533 extracted = extracted.stripWhiteSpace();
1538 /*! Adds passed modifiers to these modifiers.*/
1539 SymbolModifiers& SymbolModifiers::operator|=(const SymbolModifiers &mdfs)
1541 if (mdfs.protection!=NONE_P) protection = mdfs.protection;
1542 if (mdfs.direction!=NONE_D) direction = mdfs.direction;
1543 optional |= mdfs.optional;
1544 if (!mdfs.dimension.isNull()) dimension = mdfs.dimension;
1545 allocatable |= mdfs.allocatable;
1546 external |= mdfs.external;
1547 intrinsic |= mdfs.intrinsic;
1548 protect |= mdfs.protect;
1549 parameter |= mdfs.parameter;
1550 pointer |= mdfs.pointer;
1551 target |= mdfs.target;
1553 deferred |= mdfs.deferred;
1554 nonoverridable |= mdfs.nonoverridable;
1555 nopass |= mdfs.nopass;
1557 passVar = mdfs.passVar;
1561 /*! Extracts and adds passed modifier to these modifiers.*/
1562 SymbolModifiers& SymbolModifiers::operator|=(QCString mdfString)
1564 mdfString = mdfString.lower();
1565 SymbolModifiers newMdf;
1567 if (mdfString.find("dimension")==0)
1569 newMdf.dimension=mdfString;
1571 else if (mdfString.contains("intent"))
1573 QCString tmp = extractFromParens(mdfString);
1574 bool isin = tmp.contains("in");
1575 bool isout = tmp.contains("out");
1576 if (isin && isout) newMdf.direction = SymbolModifiers::INOUT;
1577 else if (isin) newMdf.direction = SymbolModifiers::IN;
1578 else if (isout) newMdf.direction = SymbolModifiers::OUT;
1580 else if (mdfString=="public")
1582 newMdf.protection = SymbolModifiers::PUBLIC;
1584 else if (mdfString=="private")
1586 newMdf.protection = SymbolModifiers::PRIVATE;
1588 else if (mdfString=="protected")
1590 newMdf.protect = TRUE;
1592 else if (mdfString=="optional")
1594 newMdf.optional = TRUE;
1596 else if (mdfString=="allocatable")
1598 newMdf.allocatable = TRUE;
1600 else if (mdfString=="external")
1602 newMdf.external = TRUE;
1604 else if (mdfString=="intrinsic")
1606 newMdf.intrinsic = TRUE;
1608 else if (mdfString=="parameter")
1610 newMdf.parameter = TRUE;
1612 else if (mdfString=="pointer")
1614 newMdf.pointer = TRUE;
1616 else if (mdfString=="target")
1618 newMdf.target = TRUE;
1620 else if (mdfString=="save")
1624 else if (mdfString=="nopass")
1626 newMdf.nopass = TRUE;
1628 else if (mdfString=="deferred")
1630 newMdf.deferred = TRUE;
1632 else if (mdfString=="non_overridable")
1634 newMdf.nonoverridable = TRUE;
1636 else if (mdfString.contains("pass"))
1639 if (mdfString.contains("("))
1640 newMdf.passVar = extractFromParens(mdfString);
1642 newMdf.passVar = "";
1649 /*! For debugging purposes. */
1650 //ostream& operator<<(ostream& out, const SymbolModifiers& mdfs)
1652 // out<<mdfs.protection<<", "<<mdfs.direction<<", "<<mdfs.optional<<
1653 // ", "<<(mdfs.dimension.isNull() ? "" : mdfs.dimension.latin1())<<
1654 // ", "<<mdfs.allocatable<<", "<<mdfs.external<<", "<<mdfs.intrinsic;
1659 /*! Find argument with given name in \a subprog entry. */
1660 static Argument *findArgument(Entry* subprog, QCString name, bool byTypeName = FALSE)
1662 QCString cname(name.lower());
1663 for (unsigned int i=0; i<subprog->argList->count(); i++)
1665 Argument *arg = subprog->argList->at(i);
1666 if ((!byTypeName && arg->name.lower() == cname) ||
1667 (byTypeName && arg->type.lower() == cname)
1676 /*! Find function with given name in \a entry. */
1678 static Entry *findFunction(Entry* entry, QCString name)
1680 QCString cname(name.lower());
1682 EntryListIterator eli(*entry->children());
1684 for (;(ce=eli.current());++eli)
1686 if (ce->section != Entry::FUNCTION_SEC)
1689 if (ce->name.lower() == cname)
1697 /*! Apply modifiers stored in \a mdfs to the \a typeName string. */
1698 static QCString applyModifiers(QCString typeName, SymbolModifiers& mdfs)
1700 if (!mdfs.dimension.isNull())
1702 if (!typeName.isEmpty()) typeName += ", ";
1703 typeName += mdfs.dimension;
1705 if (mdfs.direction!=SymbolModifiers::NONE_D)
1707 if (!typeName.isEmpty()) typeName += ", ";
1708 typeName += directionStrs[mdfs.direction];
1712 if (!typeName.isEmpty()) typeName += ", ";
1713 typeName += "optional";
1715 if (mdfs.allocatable)
1717 if (!typeName.isEmpty()) typeName += ", ";
1718 typeName += "allocatable";
1722 if (!typeName.isEmpty()) typeName += ", ";
1723 typeName += "external";
1727 if (!typeName.isEmpty()) typeName += ", ";
1728 typeName += "intrinsic";
1732 if (!typeName.isEmpty()) typeName += ", ";
1733 typeName += "parameter";
1737 if (!typeName.isEmpty()) typeName += ", ";
1738 typeName += "pointer";
1742 if (!typeName.isEmpty()) typeName += ", ";
1743 typeName += "target";
1747 if (!typeName.isEmpty()) typeName += ", ";
1752 if (!typeName.isEmpty()) typeName += ", ";
1753 typeName += "deferred";
1755 if (mdfs.nonoverridable)
1757 if (!typeName.isEmpty()) typeName += ", ";
1758 typeName += "non_overridable";
1762 if (!typeName.isEmpty()) typeName += ", ";
1763 typeName += "nopass";
1767 if (!typeName.isEmpty()) typeName += ", ";
1769 if (!mdfs.passVar.isEmpty())
1770 typeName += "(" + mdfs.passVar + ")";
1772 if (mdfs.protection == SymbolModifiers::PUBLIC)
1774 if (!typeName.isEmpty()) typeName += ", ";
1775 typeName += "public";
1777 else if (mdfs.protection == SymbolModifiers::PRIVATE)
1779 if (!typeName.isEmpty()) typeName += ", ";
1780 typeName += "private";
1784 if (!typeName.isEmpty()) typeName += ", ";
1785 typeName += "protected";
1791 /*! Apply modifiers stored in \a mdfs to the \a arg argument. */
1792 static void applyModifiers(Argument *arg, SymbolModifiers& mdfs)
1794 QCString tmp = arg->type;
1795 arg->type = applyModifiers(tmp, mdfs);
1798 /*! Apply modifiers stored in \a mdfs to the \a ent entry. */
1799 static void applyModifiers(Entry *ent, SymbolModifiers& mdfs)
1801 QCString tmp = ent->type;
1802 ent->type = applyModifiers(tmp, mdfs);
1804 if (mdfs.protection == SymbolModifiers::PUBLIC)
1805 ent->protection = Public;
1806 else if (mdfs.protection == SymbolModifiers::PRIVATE)
1807 ent->protection = Private;
1810 /*! Starts the new scope in fortran program. Consider using this function when
1811 * starting module, interface, function or other program block.
1814 static void startScope(Entry *scope)
1816 //cout<<"start scope: "<<scope->name<<endl;
1817 current_root= scope; /* start substructure */
1819 QMap<QCString,SymbolModifiers> mdfMap;
1820 modifiers.insert(scope, mdfMap);
1823 /*! Ends scope in fortran program: may update subprogram arguments or module variable attributes.
1826 static bool endScope(Entry *scope, bool isGlobalRoot)
1828 //cout<<"end scope: "<<scope->name<<endl;
1829 if (current_root->parent() || isGlobalRoot)
1831 current_root= current_root->parent(); /* end substructure */
1835 fprintf(stderr,"parse error in end <scopename>");
1840 // update variables or subprogram arguments with modifiers
1841 QMap<QCString,SymbolModifiers>& mdfsMap = modifiers[scope];
1843 if (scope->section == Entry::FUNCTION_SEC)
1845 // iterate all symbol modifiers of the scope
1846 for (QMap<QCString,SymbolModifiers>::Iterator it=mdfsMap.begin(); it!=mdfsMap.end(); it++)
1848 //cout<<it.key()<<": "<<it.data()<<endl;
1849 Argument *arg = findArgument(scope, it.key());
1852 applyModifiers(arg, it.data());
1855 // find return type for function
1856 //cout<<"RETURN NAME "<<modifiers[current_root][scope->name.lower()].returnName<<endl;
1857 QCString returnName = modifiers[current_root][scope->name.lower()].returnName.lower();
1858 if (modifiers[scope].contains(returnName))
1860 scope->type = modifiers[scope][returnName].type; // returning type works
1861 applyModifiers(scope, modifiers[scope][returnName]); // returning array works
1865 if (scope->section == Entry::CLASS_SEC)
1866 { // was INTERFACE_SEC
1867 if (scope->parent()->section == Entry::FUNCTION_SEC)
1868 { // interface within function
1869 // iterate functions of interface and
1870 // try to find types for dummy(ie. argument) procedures.
1871 //cout<<"Search in "<<scope->name<<endl;
1872 EntryListIterator eli(*scope->children());
1876 for (;(ce=eli.current());++eli)
1879 if (ce->section != Entry::FUNCTION_SEC)
1882 Argument *arg = findArgument(scope->parent(), ce->name, TRUE);
1885 // set type of dummy procedure argument to interface
1886 arg->name = arg->type;
1887 arg->type = scope->name;
1889 if (ce->name.lower() == scope->name.lower()) found = TRUE;
1891 if ((count == 1) && found)
1893 // clear all modifiers of the scope
1894 modifiers.remove(scope);
1895 delete scope->parent()->removeSubEntry(scope);
1901 if (scope->section!=Entry::FUNCTION_SEC)
1902 { // not function section
1903 // iterate variables: get and apply modifiers
1904 EntryListIterator eli(*scope->children());
1906 for (;(ce=eli.current());++eli)
1908 if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC)
1911 //cout<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl;
1912 if (mdfsMap.contains(ce->name.lower()))
1913 applyModifiers(ce, mdfsMap[ce->name.lower()]);
1917 // clear all modifiers of the scope
1918 modifiers.remove(scope);
1924 //! Return full name of the entry. Sometimes we must combine several names recursively.
1925 static QCString getFullName(Entry *e)
1927 QCString name = e->name;
1928 if (e->section == Entry::CLASS_SEC // || e->section == Entry::INTERFACE_SEC
1929 || !e->parent() || e->parent()->name.isEmpty())
1932 return getFullName(e->parent())+"::"+name;
1936 static int yyread(char *buf,int max_size)
1940 while ( c < max_size && inputString[inputPosition] )
1942 *buf = inputString[inputPosition++] ;
1948 static void initParser()
1953 static void initEntry()
1957 current->protection = typeProtection;
1961 current->protection = defaultProtection;
1963 current->mtype = mtype;
1964 current->virt = virt;
1965 current->stat = gstat;
1966 current->lang = SrcLangExt_Fortran;
1967 initGroupInfo(current);
1971 adds current entry to current_root and creates new current
1973 static void addCurrentEntry(int case_insens)
1975 if (case_insens) current->name = current->name.lower();
1976 //printf("===Adding entry %s to %s\n", current->name.data(), current_root->name.data());
1977 current_root->addSubEntry(current);
1978 last_entry = current;
1979 current = new Entry ;
1983 static int max(int a, int b) {return a>b?a:b;}
1985 static void addModule(const char *name, bool isModule)
1987 DBG_CTX((stderr, "0=========> got module %s\n", name));
1990 current->section = Entry::CLASS_SEC;
1992 current->section = Entry::FUNCTION_SEC;
1996 current->name = name;
2000 QCString fname = yyFileName;
2001 int index = max(fname.findRev('/'), fname.findRev('\\'));
2002 fname = fname.right(fname.length()-index-1);
2003 fname = fname.prepend("__").append("__");
2004 current->name = fname;
2006 current->type = "program";
2007 current->fileName = yyFileName;
2008 current->bodyLine = yyLineNr; // used for source reference
2009 current->protection = Public ;
2011 startScope(last_entry);
2015 static void addSubprogram(const char *text)
2017 DBG_CTX((stderr,"1=========> got subprog, type: %s\n",text));
2018 subrCurrent.prepend(current);
2019 current->section = Entry::FUNCTION_SEC ;
2020 QCString subtype = text; subtype=subtype.lower().stripWhiteSpace();
2021 functionLine = (subtype.find("function") != -1);
2022 current->type += " " + subtype;
2023 current->type = current->type.stripWhiteSpace();
2024 current->fileName = yyFileName;
2025 current->bodyLine = yyLineNr; // used for source reference
2026 current->startLine = -1; // ??? what is startLine for?
2027 current->args.resize(0);
2028 current->argList->clear();
2032 /*! Adds interface to the root entry.
2033 * \note Code was brought to this procedure from the parser,
2034 * because there was/is idea to use it in several parts of the parser.
2036 static void addInterface(QCString name, InterfaceType type)
2038 if (YY_START == Start)
2041 yy_push_state(ModuleBody); //anon program
2044 current->section = Entry::CLASS_SEC; // was Entry::INTERFACE_SEC;
2045 current->spec = Entry::Interface;
2046 current->name = name;
2051 current->type = "abstract";
2055 current->type = "generic";
2064 /* if type is part of a module, mod name is necessary for output */
2065 if ((current_root) &&
2066 (current_root->section == Entry::CLASS_SEC ||
2067 current_root->section == Entry::NAMESPACE_SEC))
2069 current->name= current_root->name + "::" + current->name;
2072 current->fileName = yyFileName;
2073 current->bodyLine = yyLineNr;
2078 //-----------------------------------------------------------------------------
2080 /*! Get the argument \a name.
2082 static Argument* getParameter(const QCString &name)
2084 // std::cout<<"addFortranParameter(): "<<name<<" DOCS:"<<(docs.isNull()?QCString("null"):docs)<<std::endl;
2086 if (current_root->argList==0) return 0;
2087 ArgumentListIterator ali(*current_root->argList);
2089 for (ali.toFirst();(a=ali.current());++ali)
2091 if (a->name.lower()==name.lower())
2094 //printf("parameter found: %s\n",(const char*)name);
2101 //----------------------------------------------------------------------------
2102 static void startCommentBlock(bool brief)
2106 current->briefFile = yyFileName;
2107 current->briefLine = yyLineNr;
2111 current->docFile = yyFileName;
2112 current->docLine = yyLineNr;
2116 //----------------------------------------------------------------------------
2118 static void handleCommentBlock(const QCString &doc,bool brief)
2120 bool needsEntry = FALSE;
2121 static bool hideInBodyDocs = Config_getBool("HIDE_IN_BODY_DOCS");
2123 if (docBlockInBody && hideInBodyDocs)
2125 docBlockInBody = FALSE;
2128 DBG_CTX((stderr,"call parseCommentBlock [%s]\n",doc.data()));
2129 int lineNr = brief ? current->briefLine : current->docLine;
2130 while (parseCommentBlock(
2132 docBlockInBody ? subrCurrent.getFirst() : current,
2136 docBlockInBody ? FALSE : brief,
2137 docBlockInBody ? FALSE : docBlockJavaStyle,
2144 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2145 if (needsEntry) addCurrentEntry(0);
2147 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
2149 if (needsEntry) addCurrentEntry(0);
2150 docBlockInBody = FALSE;
2153 //----------------------------------------------------------------------------
2155 static void subrHandleCommentBlock(const QCString &doc,bool brief)
2158 Entry *tmp_entry = current;
2159 current = subrCurrent.getFirst(); // temporarily switch to the entry of the subroutine / function
2161 // Still in the specification section so no inbodyDocs yet, but parameter documentation
2162 current->inbodyDocs = "";
2164 if (docBlock.stripWhiteSpace().find("\\param") == 0)
2166 handleCommentBlock("\n\n"+doc,brief);
2168 else if (docBlock.stripWhiteSpace().find("@param") == 0)
2170 handleCommentBlock("\n\n"+doc,brief);
2174 int dir1 = modifiers[current_root][argName.lower()].direction;
2175 loc_doc = doc.stripWhiteSpace();
2176 if (loc_doc.lower().find(directionParam[SymbolModifiers::IN]) == 0)
2178 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2179 (directionParam[dir1] == directionParam[SymbolModifiers::IN]))
2181 handleCommentBlock(QCString("\n\n@param ") + directionParam[SymbolModifiers::IN] + " " +
2182 argName + " " + loc_doc.right(loc_doc.length()-strlen(directionParam[SymbolModifiers::IN])),brief);
2186 warn(yyFileName,yyLineNr, "inconsistency between intent attribute and documenation for variable: "+argName);
2187 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2188 argName + " " + doc,brief);
2191 else if (loc_doc.lower().find(directionParam[SymbolModifiers::OUT]) == 0)
2193 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2194 (directionParam[dir1] == directionParam[SymbolModifiers::OUT]))
2196 handleCommentBlock(QCString("\n\n@param ") + directionParam[SymbolModifiers::OUT] + " " +
2197 argName + " " + loc_doc.right(loc_doc.length()-strlen(directionParam[SymbolModifiers::OUT])),brief);
2201 warn(yyFileName,yyLineNr, "inconsistency between intent attribute and documenation for variable: "+argName);
2202 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2203 argName + " " + doc,brief);
2206 else if (loc_doc.lower().find(directionParam[SymbolModifiers::INOUT]) == 0)
2208 if ((directionParam[dir1] == directionParam[SymbolModifiers::NONE_D]) ||
2209 (directionParam[dir1] == directionParam[SymbolModifiers::INOUT]))
2211 handleCommentBlock(QCString("\n\n@param ") + directionParam[SymbolModifiers::INOUT] + " " +
2212 argName + " " + loc_doc.right(loc_doc.length()-strlen(directionParam[SymbolModifiers::INOUT])),brief);
2216 warn(yyFileName,yyLineNr, "inconsistency between intent attribute and documenation for variable: "+argName);
2217 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2218 argName + " " + doc,brief);
2223 handleCommentBlock(QCString("\n\n@param ") + directionParam[dir1] + " " +
2224 argName + " " + doc,brief);
2230 //----------------------------------------------------------------------------
2234 static void debugCompounds(Entry *rt) // print Entry structure (for debugging)
2237 printf("%d) debugCompounds(%s) line %d\n",level, rt->name.data(), rt->bodyLine);
2238 EntryListIterator eli(*rt->children());
2240 for (;(ce=eli.current());++eli)
2249 static void parseMain(const char *fileName,const char *fileBuf,Entry *rt, FortranFormat format)
2251 char *tmpBuf = NULL;
2254 defaultProtection = Public;
2255 inputString = fileBuf;
2257 inputStringPrepass = NULL;
2258 inputPositionPrepass = 0;
2260 //anonCount = 0; // don't reset per file
2266 inputFile.setName(fileName);
2267 if (inputFile.open(IO_ReadOnly))
2269 isFixedForm = recognizeFixedForm(fileBuf,format);
2273 msg("Prepassing fixed form of %s\n", fileName);
2274 //printf("---strlen=%d\n", strlen(fileBuf));
2275 //clock_t start=clock();
2277 inputString = prepassFixedForm(fileBuf);
2279 //clock_t end=clock();
2280 //printf("CPU time used=%f\n", ((double) (end-start))/CLOCKS_PER_SEC);
2282 else if (inputString[strlen(fileBuf)-1] != '\n')
2284 tmpBuf = (char *)malloc(strlen(fileBuf)+2);
2285 strcpy(tmpBuf,fileBuf);
2286 tmpBuf[strlen(fileBuf)]= '\n';
2287 tmpBuf[strlen(fileBuf)+1]= '\000';
2288 inputString = tmpBuf;
2292 yyFileName = fileName;
2293 msg("Parsing file %s...\n",yyFileName.data());
2295 startScope(rt); // implies current_root = rt
2297 groupEnterFile(yyFileName,yyLineNr);
2299 current = new Entry;
2300 current->lang = SrcLangExt_Fortran;
2301 current->name = yyFileName;
2302 current->section = Entry::SOURCE_SEC;
2303 current_root->addSubEntry(current);
2304 file_root = current;
2305 current = new Entry;
2306 current->lang = SrcLangExt_Fortran;
2308 fortranscannerYYrestart( fortranscannerYYin );
2313 fortranscannerYYlex();
2314 groupLeaveFile(yyFileName,yyLineNr);
2316 endScope(current_root, TRUE); // TRUE - global root
2318 //debugCompounds(rt); //debug
2320 rt->program.resize(0);
2321 delete current; current=0;
2322 moduleProcedures.clear();
2324 free((char*)tmpBuf);
2328 free((char*)inputString);
2336 //----------------------------------------------------------------------------
2338 void FortranLanguageScanner::parseInput(const char *fileName,
2339 const char *fileBuf,
2341 bool /*sameTranslationUnit*/,
2342 QStrList & /*filesInSameTranslationUnit*/)
2344 g_thisParser = this;
2346 printlex(yy_flex_debug, TRUE, __FILE__, fileName);
2348 ::parseMain(fileName,fileBuf,root,m_format);
2350 printlex(yy_flex_debug, FALSE, __FILE__, fileName);
2353 void FortranLanguageScanner::parseCode(CodeOutputInterface & codeOutIntf,
2354 const char * scopeName,
2355 const QCString & input,
2356 SrcLangExt /*lang*/,
2357 bool isExampleBlock,
2358 const char * exampleName,
2362 bool inlineFragment,
2363 MemberDef *memberDef,
2364 bool showLineNumbers,
2365 Definition *searchCtx,
2369 ::parseFortranCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName,
2370 fileDef,startLine,endLine,inlineFragment,memberDef,
2371 showLineNumbers,searchCtx,collectXRefs,m_format);
2374 bool FortranLanguageScanner::needsPreprocessing(const QCString &extension)
2376 return extension!=extension.lower(); // use preprocessor only for upper case extensions
2378 void FortranLanguageScanner::resetCodeParserState()
2380 ::resetFortranCodeParserState();
2383 void FortranLanguageScanner::parsePrototype(const char *text)
2385 QCString buffer = QCString(text);
2387 parsingPrototype = TRUE;
2389 fortranscannerYYlex();
2390 parsingPrototype = FALSE;
2394 static void scanner_abort()
2396 fprintf(stderr,"********************************************************************\n");
2397 fprintf(stderr,"Error in file %s line: %d, state: %d\n",yyFileName.data(),yyLineNr,YY_START);
2398 fprintf(stderr,"********************************************************************\n");
2400 EntryListIterator eli(*global_root->children());
2404 for (;(ce=eli.current());++eli)
2406 if (ce == file_root) start=TRUE;
2407 if (start) ce->reset();
2410 // dummy call to avoid compiler warning
2411 (void)yy_top_state();
2417 //----------------------------------------------------------------------------
2419 #if !defined(YY_FLEX_SUBMINOR_VERSION)
2420 //----------------------------------------------------------------------------
2421 extern "C" { // some bogus code to keep the compiler happy
2422 void fortranscannernerYYdummy() { yy_flex_realloc(0,0); }