Doxygen
Loading...
Searching...
No Matches
fortranscanner.l
Go to the documentation of this file.
1/* -*- mode: fundamental; indent-tabs-mode: 1; -*- */
2/*****************************************************************************
3 * Parser for Fortran90 F subset
4 *
5 * Copyright (C) by Anke Visser
6 * based on the work of Dimitri van Heesch.
7 *
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation under the terms of the GNU General Public License is hereby
10 * granted. No representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or implied warranty.
12 * See the GNU General Public License for more details.
13 *
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
16 *
17 */
18
19/* Developer notes.
20 *
21 * - Consider using startScope(), endScope() functions with module, program,
22 * subroutine or any other scope in fortran program.
23 *
24 * - Symbol yyextra->modifiers (attributes) are collected using SymbolModifiers |= operator during
25 * substructure parsing. When substructure ends all yyextra->modifiers are applied to actual
26 * entries in applyModifiers() functions.
27 *
28 * - How case insensitiveness should be handled in code?
29 * On one side we have arg->name and entry->name, on another side modifierMap[name].
30 * In entries and arguments case is the same as in code, in modifier map case is lowered and
31 * then it is compared to lowered entry/argument names.
32 *
33 * - Do not like constructs like aa{BS} or {BS}bb. Should try to handle blank space
34 * with separate rule?: It seems it is often necessary, because we may parse something like
35 * "functionA" or "MyInterface". So constructs like '(^|[ \t])interface({BS_}{ID})?/[ \t\n]'
36 * are desired.
37 *
38 * - Must track yyextra->lineNr when using REJECT, unput() or similar commands.
39 */
40%option never-interactive
41%option case-insensitive
42%option prefix="fortranscannerYY"
43%option reentrant
44%option extra-type="struct fortranscannerYY_state *"
45%top{
46#include <stdint.h>
47// forward declare yyscan_t to improve type safety
48#define YY_TYPEDEF_YY_SCANNER_T
49struct yyguts_t;
50typedef yyguts_t *yyscan_t;
yyguts_t * yyscan_t
Definition code.l:24
51}
52
53%{
54
55#include <map>
56#include <vector>
57#include <algorithm>
58
59#include <stdio.h>
60#include <stdlib.h>
61#include <assert.h>
62#include <ctype.h>
63
64#include "fortranscanner.h"
65#include "entry.h"
66#include "message.h"
67#include "config.h"
68#include "doxygen.h"
69#include "util.h"
70#include "defargs.h"
71#include "language.h"
72#include "commentscan.h"
73#include "pre.h"
74#include "arguments.h"
75#include "debug.h"
76#include "markdown.h"
77
78
79// Toggle for some debugging info
80//#define DBG_CTX(x) fprintf x
81#define DBG_CTX(x) do { } while(0)
82
83#define YY_NO_INPUT 1
84#define YY_NO_UNISTD_H 1
85
88
89// {{{ ----- Helper structs -----
90//! Holds yyextra->modifiers (ie attributes) for one symbol (variable, function, etc)
92{
95
96 //! This is only used with function return value.
97 QCString type, returnName;
102 QCString dimension;
108 bool target;
109 bool save;
112 bool nopass;
113 bool pass;
115 bool volat; /* volatile is a reserved name */
116 bool value; /* volatile is a reserved name */
117 QCString passVar;
118 QCString bindVar;
119
126
128 SymbolModifiers& operator|=(QCString mdfrString);
129};
130
131//ostream& operator<<(ostream& out, const SymbolModifiers& mdfs);
132
133static const char *directionStrs[] =
134{
135 "", "intent(in)", "intent(out)", "intent(inout)"
136};
137static const char *directionParam[] =
138{
139 "", "[in]", "[out]", "[in,out]"
140};
141
142// }}}
143
145{
146 CommentInPrepass(int col, const QCString &s) : column(col), str(s) {}
148 QCString str;
149};
150
151/* -----------------------------------------------------------------
152 *
153 * statics
154 */
155
157{
158 OutlineParserInterface * thisParser;
159 CommentScanner commentScanner;
160 const char * inputString;
163 QCString inputStringPrepass; ///< Input string for prepass of line cont. '&'
164 QCString inputStringSemi; ///< Input string after command separator ';'
168 std::vector<CommentInPrepass> comments;
169 YY_BUFFER_STATE * includeStack = nullptr;
172 QCString fileName;
173 int lineNr = 1 ;
174 int colNr = 0 ;
175 Entry *current_root = nullptr;
176 Entry *global_scope = nullptr;
177 std::shared_ptr<Entry> global_root;
178 std::shared_ptr<Entry> file_root;
179 std::shared_ptr<Entry> last_entry;
180 std::shared_ptr<Entry> last_enum;
181 std::shared_ptr<Entry> current;
182 ScanVar vtype = V_IGNORE; // type of parsed variable
183 EntryList moduleProcedures; // list of all interfaces which contain unresolved module procedures
184 QCString docBlock;
185 bool docBlockInBody = false;
187 QCString docBlockName;
188 QCString blockString;
190 QCString debugStr;
191 size_t fencedSize = 0;
192// Argument *parameter; // element of parameter list
193 QCString argType; // fortran type of an argument of a parameter list
194 QCString argName; // last identifier name in variable list
195 QCString initializer; // initial value of a variable
196 int initializerArrayScope; // number if nested array scopes in initializer
197 int initializerScope; // number if nested function calls in initializer
198 QCString useModuleName; // name of module in the use statement
201 bool typeMode = false;
203 bool functionLine = false;
204 char stringStartSymbol; // single or double quote
205 bool parsingPrototype = false; // see parsePrototype()
206
207//! Accumulated modifiers of current statement, eg variable declaration.
208 SymbolModifiers currentModifiers;
209//! Holds program scope->symbol name->symbol modifiers.
210 std::map<Entry*,std::map<std::string,SymbolModifiers> > modifiers;
211 int anonCount = 0 ;
212
214 //! counter for the number of main programs in this file
215 int mainPrograms = 0;
216 int curIndent = 0;
217};
218
219//-----------------------------------------------------------------------------
220static int getAmpersandAtTheStart(const char *buf, int length);
221static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch);
222static QCString extractFromParens(const QCString &name);
223static QCString extractBind(const QCString &name);
224
225
226static int yyread(yyscan_t yyscanner,char *buf,int max_size);
227static void startCommentBlock(yyscan_t yyscanner,bool);
228static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief);
229static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief);
230static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief);
231static void addCurrentEntry(yyscan_t yyscanner,bool case_insens);
232static void addModule(yyscan_t yyscanner,const QCString &name=QCString(), bool isModule=FALSE);
233static void addSubprogram(yyscan_t yyscanner,const QCString &text);
234static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type);
235static Argument *getParameter(yyscan_t yyscanner,const QCString &name);
236static void scanner_abort(yyscan_t yyscanner);
237static inline void pop_state(yyscan_t yyscanner);
238
239static void startScope(yyscan_t yyscanner,Entry *scope);
240static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot=FALSE);
241static void copyEntry(std::shared_ptr<Entry> dest, const std::shared_ptr<Entry> &src);
242static void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root);
243static void resolveTypeBoundProcedures(Entry *scope);
244static void truncatePrepass(yyscan_t yyscanner,int index);
245static void pushBuffer(yyscan_t yyscanner,const QCString &buffer);
246static void popBuffer(yyscan_t yyscanner);
247static const CommentInPrepass* locatePrepassComment(yyscan_t yyscanner,int from, int to);
248static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to);
249static void newLine(yyscan_t yyscanner);
250static void initEntry(yyscan_t yyscanner);
251
252static const char *stateToString(int state);
253static inline int computeIndent(const char *s);
254
255
256//-----------------------------------------------------------------------------
257#undef YY_INPUT
258#define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
259
260// otherwise the filename would be the name of the converted file (*.cpp instead of *.l)
261static inline const char *getLexerFILE() {return __FILE__;}
262#include "doxygen_lex.h"
263#define YY_USER_ACTION yyextra->colNr+=(int)yyleng;
264#define INVALID_ENTRY ((Entry*)0x8)
265
266
267//-----------------------------------------------------------------------------
268
Represents an unstructured piece of information, about an entity found in the sources.
Definition entry.h:116
This is an alternative implementation of QCString.
Definition qcstring.h:101
Interface for the comment block scanner.
std::vector< std::shared_ptr< Entry > > EntryList
Definition entry.h:269
static int getAmpersandAtTheStart(const char *buf, int length)
static const char * directionParam[]
static void resolveModuleProcedures(yyscan_t yyscanner, Entry *current_root)
fill empty interface module procedures with info from corresponding module subprogs
static void newLine(yyscan_t yyscanner)
static void addCurrentEntry(yyscan_t yyscanner, bool case_insens)
adds yyextra->current entry to yyextra->current_root and creates new yyextra->current
ScanVar
@ V_IGNORE
@ V_RESULT
@ V_VARIABLE
@ V_PARAMETER
static QCString extractFromParens(const QCString &name)
static int computeIndent(const char *s)
static void startCommentBlock(yyscan_t yyscanner, bool)
static void startScope(yyscan_t yyscanner, Entry *scope)
static void addModule(yyscan_t yyscanner, const QCString &name=QCString(), bool isModule=FALSE)
static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch)
static const CommentInPrepass * locatePrepassComment(yyscan_t yyscanner, int from, int to)
static bool endScope(yyscan_t yyscanner, Entry *scope, bool isGlobalRoot=FALSE)
static int yyread(yyscan_t yyscanner, char *buf, int max_size)
static const char * stateToString(int state)
static void popBuffer(yyscan_t yyscanner)
static void copyEntry(std::shared_ptr< Entry > dest, const std::shared_ptr< Entry > &src)
used to copy entry to an interface module procedure
static Argument * getParameter(yyscan_t yyscanner, const QCString &name)
static void subrHandleCommentBlock(yyscan_t yyscanner, const QCString &doc, bool brief)
Handle parameter description as defined after the declaration of the parameter.
static const char * directionStrs[]
static void subrHandleCommentBlockResult(yyscan_t yyscanner, const QCString &doc, bool brief)
Handle result description as defined after the declaration of the parameter.
static void scanner_abort(yyscan_t yyscanner)
static void pop_state(yyscan_t yyscanner)
static void resolveTypeBoundProcedures(Entry *scope)
static void updateVariablePrepassComment(yyscan_t yyscanner, int from, int to)
static void handleCommentBlock(yyscan_t yyscanner, const QCString &doc, bool brief)
static void pushBuffer(yyscan_t yyscanner, const QCString &buffer)
static void addSubprogram(yyscan_t yyscanner, const QCString &text)
static QCString extractBind(const QCString &name)
static void addInterface(yyscan_t yyscanner, QCString name, InterfaceType type)
static const char * getLexerFILE()
InterfaceType
@ IF_NONE
@ IF_GENERIC
@ IF_SPECIFIC
@ IF_ABSTRACT
static void truncatePrepass(yyscan_t yyscanner, int index)
static void initEntry(yyscan_t yyscanner)
#define FALSE
Definition qcstring.h:34
This class contains the information about the argument of a function or template.
Definition arguments.h:27
CommentInPrepass(int col, const QCString &s)
QCString type
This is only used with function return value.
Direction direction
SymbolModifiers & operator|=(const SymbolModifiers &mdfs)
Protection protection
std::map< Entry *, std::map< std::string, SymbolModifiers > > modifiers
Holds program scope->symbol name->symbol modifiers.
QCString inputStringSemi
Input string after command separator ';'.
QCString inputStringPrepass
Input string for prepass of line cont. '&'.
YY_BUFFER_STATE * includeStack
std::shared_ptr< Entry > last_enum
OutlineParserInterface * thisParser
SymbolModifiers currentModifiers
Accumulated modifiers of current statement, eg variable declaration.
unsigned int inputPositionPrepass
std::shared_ptr< Entry > global_root
std::vector< CommentInPrepass > comments
CommentScanner commentScanner
std::shared_ptr< Entry > last_entry
int mainPrograms
counter for the number of main programs in this file
std::shared_ptr< Entry > current
std::shared_ptr< Entry > file_root
Protection
Protection level of members.
Definition types.h:26
A bunch of utility functions.
269%}
270
271 //-----------------------------------------------------------------------------
272 //-----------------------------------------------------------------------------
273CMD ("\\"|"@")
274IDSYM [a-z_A-Z0-9]
275SEPARATE [:, \t]
276ID [a-z_A-Z%]+{IDSYM}*
277ID_ [a-z_A-Z%]*{IDSYM}*
278OPERATOR_ID (operator{BS}"("{BS}(\.[a-z_A-Z]+\.|"="|"/="|"//"|"=="|"<"|"<="|">"|">="|"+"|"*"|"**"|"/"|"-"){BS}")")
279SUBPROG (subroutine|function)
280B [ \t]
281BS [ \t]*
282BS_ [ \t]+
283BT_ ([ \t]+|[ \t]*"(")
284COMMA {BS},{BS}
285ARGS_L0 ("("[^)]*")")
286ARGS_L1a [^()]*"("[^)]*")"[^)]*
287ARGS_L1 ("("{ARGS_L1a}*")")
288ARGS_L2 "("({ARGS_L0}|[^()]|{ARGS_L1a}|{ARGS_L1})*")"
289ARGS {BS}({ARGS_L0}|{ARGS_L1}|{ARGS_L2})
290NOARGS {BS}"\n"
291
292PRE [pP][rR][eE]
293CODE [cC][oO][dD][eE]
294
295COMM "!"[!<>]
296NUM_TYPE (complex|integer|logical|real)
297LOG_OPER (\.and\.|\.eq\.|\.eqv\.|\.ge\.|\.gt\.|\.le\.|\.lt\.|\.ne\.|\.neqv\.|\.or\.|\.not\.)
298KIND {ARGS}
299CHAR (CHARACTER{ARGS}?|CHARACTER{BS}"*"({BS}[0-9]+|{ARGS}))
300TYPE_SPEC (({NUM_TYPE}({BS}"*"{BS}[0-9]+)?)|({NUM_TYPE}{KIND})|DOUBLE{BS}COMPLEX|DOUBLE{BS}PRECISION|ENUMERATOR|{CHAR}|TYPE{ARGS}|CLASS{ARGS}|PROCEDURE{ARGS}?)
301
302INTENT_SPEC intent{BS}"("{BS}(in|out|in{BS}out){BS}")"
303ATTR_SPEC (EXTERNAL|ALLOCATABLE|DIMENSION{ARGS}|{INTENT_SPEC}|INTRINSIC|OPTIONAL|PARAMETER|POINTER|PROTECTED|PRIVATE|PUBLIC|SAVE|TARGET|NOPASS|PASS{ARGS}?|DEFERRED|NON_OVERRIDABLE|CONTIGUOUS|VOLATILE|VALUE)
304LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}((,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})|(,{BS}NAME{BS}"="{BS}"'"(.*)"'"{BS}))?")"
305/* Assume that attribute statements are almost the same as attributes. */
306ATTR_STMT {ATTR_SPEC}|DIMENSION
307EXTERNAL_STMT (EXTERNAL)
308
309CONTAINS CONTAINS
310PREFIX ((NON_)?RECURSIVE{BS_}|IMPURE{BS_}|PURE{BS_}|ELEMENTAL{BS_}){0,4}((NON_)?RECURSIVE|IMPURE|PURE|ELEMENTAL)?
311SCOPENAME ({ID}{BS}"::"{BS})*
312
313LINENR {B}*[1-9][0-9]*
314FILEICHAR [a-z_A-Z0-9\x80-\xFF\\:\\\/\-\+=&#@]
315FILEECHAR [a-z_A-Z0-9\x80-\xFF\-\+=&#@]
316FILECHARS {FILEICHAR}*{FILEECHAR}+
317HFILEMASK {FILEICHAR}*("."{FILEICHAR}+)+{FILECHARS}*
318VFILEMASK {FILECHARS}("."{FILECHARS})*
319FILEMASK {VFILEMASK}|{HFILEMASK}
320
321%option noyywrap
322%option stack
323%option caseless
324/*%option debug */
325
326 //---------------------------------------------------------------------------------
327
328 /** fortran parsing states */
329%x Subprog
330%x SubprogPrefix
331%x Parameterlist
332%x SubprogBody
333%x SubprogBodyContains
334%x Start
335%x Comment
336%x Module
337%x Program
338%x ModuleBody
339%x ModuleBodyContains
340%x AttributeList
341%x FVariable
342%x Initialization
343%x ArrayInitializer
344%x FEnum
345%x Typedef
346%x TypedefBody
347%x TypedefBodyContains
348%x InterfaceBody
349%x StrIgnore
350%x String
351%x Use
352%x UseOnly
353%x ModuleProcedure
354
355%x Prepass
356
357 /** comment parsing states */
358%x DocBlock
359%x DocBackLine
360%x DocCopyBlock
361
362%x BlockData
363
364/** prototype parsing */
365%x Prototype
366%x PrototypeSubprog
367%x PrototypeArgs
368
370
371 /*-----------------------------------------------------------------------------------*/
372
373<Prepass>^{BS}[&]*{BS}!.*\n { /* skip lines with just comment. Note code was in free format or has been converted to it */
374 yyextra->lineCountPrepass ++;
375 }
376<Prepass>^{BS}\n { /* skip empty lines */
377 yyextra->lineCountPrepass ++;
378 }
379<*>^.*\n { // prepass: look for line continuations
380 yyextra->functionLine = FALSE;
381
382 DBG_CTX((stderr, "---%s", yytext));
383
384 int indexStart = getAmpersandAtTheStart(yytext, (int)yyleng);
385 int indexEnd = getAmpOrExclAtTheEnd(yytext, (int)yyleng, '\0');
386 if (indexEnd>=0 && yytext[indexEnd]!='&') //we are only interested in amp
387 {
388 indexEnd=-1;
389 }
390
391 if (indexEnd<0)
392 { // ----- no ampersand as line continuation
393 if (YY_START == Prepass)
394 { // last line in "continuation"
395
396 // Only take input after initial ampersand
397 yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1));
398
399 //printf("BUFFER:%s\n", (const char*)yyextra->inputStringPrepass);
400 pushBuffer(yyscanner,yyextra->inputStringPrepass);
401 yyextra->colNr = 0;
402 pop_state(yyscanner);
403 }
404 else
405 { // simple line
406 yyextra->colNr = 0;
407 REJECT;
408 }
409 }
410 else
411 { // ----- line with continuation
412 if (YY_START != Prepass)
413 {
414 yyextra->comments.clear();
415 yyextra->inputStringPrepass=QCString();
416 yy_push_state(Prepass,yyscanner);
417 }
418
419 size_t length = yyextra->inputStringPrepass.length();
420
421 // Only take input after initial ampersand
422 yyextra->inputStringPrepass+=(const char*)(yytext+(indexStart+1));
423 yyextra->lineCountPrepass ++;
424
425 // cut off & and remove following comment if present
426 truncatePrepass(yyscanner,static_cast<int>(length) + indexEnd - indexStart - 1);
427 }
428 }
#define DBG_CTX(x)
Definition code.l:73
static void pop_state(yyscan_t yyscanner)
429
430
431 /*------ ignore strings that are not initialization strings */
432<String>\"|\' { // string ends with next quote without previous backspace
433 if (yytext[0]!=yyextra->stringStartSymbol)
434 {
435 yyextra->colNr -= (int)yyleng;
436 REJECT;
437 } // single vs double quote
438 if (yy_top_state(yyscanner) == Initialization ||
439 yy_top_state(yyscanner) == ArrayInitializer)
440 {
441 yyextra->initializer+=yytext;
442 }
443 pop_state(yyscanner);
444 }
445<String>[\x80-\xFF]* |
446<String>. { if (yy_top_state(yyscanner) == Initialization ||
447 yy_top_state(yyscanner) == ArrayInitializer)
448 {
449 yyextra->initializer+=yytext;
450 }
451 }
452<*>\"|\' { /* string starts */
453 if (YY_START == StrIgnore)
454 { yyextra->colNr -= (int)yyleng;
455 REJECT;
456 }; // ignore in simple yyextra->comments
457 yy_push_state(YY_START,yyscanner);
458 if (yy_top_state(yyscanner) == Initialization ||
459 yy_top_state(yyscanner) == ArrayInitializer)
460 {
461 yyextra->initializer+=yytext;
462 }
463 yyextra->stringStartSymbol=yytext[0]; // single or double quote
464 BEGIN(String);
465 }
466
467 /*------ ignore simple comment (not documentation yyextra->comments) */
468
469<*>"!"/[^<>\n] { if (YY_START == String || YY_START == DocCopyBlock)
470 { yyextra->colNr -= (int)yyleng;
471 REJECT;
472 } // "!" is ignored in strings
473 // skip comment line (without docu yyextra->comments "!>" "!<" )
474 /* ignore further "!" and ignore yyextra->comments in Strings */
475 if ((YY_START != StrIgnore) && (YY_START != String))
476 {
477 yy_push_state(YY_START,yyscanner);
478 BEGIN(StrIgnore);
479 yyextra->debugStr="*!";
480 DBG_CTX((stderr,"start comment %d\n",yyextra->lineNr));
481 }
482 }
483<StrIgnore>.?/\n { pop_state(yyscanner); // comment ends with endline character
484 DBG_CTX((stderr,"end comment %d %s\n",yyextra->lineNr,qPrint(yyextra->debugStr)));
485 } // comment line ends
const char * qPrint(const char *s)
Definition qcstring.h:661
486<StrIgnore>[\x80-\xFF]* |
487<StrIgnore>. { yyextra->debugStr+=yytext; }
488
489
490 /*------ use handling ------------------------------------------------------------*/
491
492<Start,ModuleBody,SubprogBody>"use"{BS_} {
493 if (YY_START == Start)
494 {
495 addModule(yyscanner);
496 yy_push_state(ModuleBody,yyscanner); //anon program
497 }
498 yy_push_state(Use,yyscanner);
499 }
500<Use>{ID} {
501 DBG_CTX((stderr,"using dir %s\n",yytext));
502 yyextra->current->name=yytext;
503 yyextra->current->name=yyextra->current->name.lower();
504 yyextra->current->fileName = yyextra->fileName;
505 yyextra->current->section=EntryType::makeUsingDir();
506 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
507 yyextra->current->lang = SrcLangExt::Fortran;
508 pop_state(yyscanner);
509 }
@ Fortran
Definition types.h:53
510<Use>{ID}/, {
511 yyextra->useModuleName=yytext;
512 yyextra->useModuleName=yyextra->useModuleName.lower();
513 }
514<Use>,{BS}"ONLY" { BEGIN(UseOnly);
515 }
516<UseOnly>{BS},{BS} {}
517<UseOnly>{ID} {
518 yyextra->current->name= yyextra->useModuleName+"::"+yytext;
519 yyextra->current->name=yyextra->current->name.lower();
520 yyextra->current->fileName = yyextra->fileName;
521 yyextra->current->section=EntryType::makeUsingDecl();
522 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
523 yyextra->current->lang = SrcLangExt::Fortran;
524 }
525<Use,UseOnly>"\n" {
526 yyextra->colNr -= 1;
527 unput(*yytext);
528 pop_state(yyscanner);
529 }
530
531 /* INTERFACE definitions */
532<Start,ModuleBody,SubprogBody>{
533^{BS}interface{IDSYM}+ { /* variable with interface prefix */ }
534^{BS}interface { yyextra->ifType = IF_SPECIFIC;
535 yy_push_state(InterfaceBody,yyscanner);
536 // do not start a scope here, every
537 // interface body is a scope of its own
538 }
539
540^{BS}abstract{BS_}interface { yyextra->ifType = IF_ABSTRACT;
541 yy_push_state(InterfaceBody,yyscanner);
542 // do not start a scope here, every
543 // interface body is a scope of its own
544 }
545
546^{BS}interface{BS_}{ID}{ARGS}? { yyextra->ifType = IF_GENERIC;
547 yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
548 yy_push_state(InterfaceBody,yyscanner);
549
550 // extract generic name
551 QCString name = QCString(yytext).stripWhiteSpace();
552 name = name.right(name.length() - 9).stripWhiteSpace().lower();
553 addInterface(yyscanner,name, yyextra->ifType);
554 startScope(yyscanner,yyextra->last_entry.get());
555 }
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
QCString lower() const
Definition qcstring.h:234
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:245
QCString right(size_t len) const
Definition qcstring.h:219
static void startScope(yyscan_t yyscanner)
start scope
556}
557
558<InterfaceBody>^{BS}end{BS}interface({BS_}{ID})? {
559 // end scope only if GENERIC interface
560 if (yyextra->ifType == IF_GENERIC)
561 {
562 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr - 1;
563 }
564 if (yyextra->ifType == IF_GENERIC && !endScope(yyscanner,yyextra->current_root))
565 {
566 yyterminate();
567 }
568 yyextra->ifType = IF_NONE;
569 pop_state(yyscanner);
570 }
#define yyterminate()
static void endScope(yyscan_t yyscanner)
end scope
571<InterfaceBody>module{BS}procedure { yy_push_state(YY_START,yyscanner);
572 BEGIN(ModuleProcedure);
573 }
574<ModuleProcedure>{ID} { QCString name = QCString(yytext).lower();
575 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
576 {
577 addInterface(yyscanner,name, yyextra->ifType);
578 startScope(yyscanner,yyextra->last_entry.get());
579 }
580
581 yyextra->current->section = EntryType::makeFunction();
582 yyextra->current->name = name;
583 yyextra->moduleProcedures.push_back(yyextra->current);
584 addCurrentEntry(yyscanner,true);
585 }
586<ModuleProcedure>"\n" { yyextra->colNr -= 1;
587 unput(*yytext);
588 pop_state(yyscanner);
589 }
590<InterfaceBody>. {}
591
592 /*-- Contains handling --*/
593<Start>^{BS}{CONTAINS}/({BS}|\n|!|;) {
594 if (YY_START == Start)
595 {
596 addModule(yyscanner);
597 yy_push_state(ModuleBodyContains,yyscanner); //anon program
598 }
599 }
600<ModuleBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(ModuleBodyContains); }
601<SubprogBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(SubprogBodyContains); }
602<TypedefBody>^{BS}{CONTAINS}/({BS}|\n|!|;) { BEGIN(TypedefBodyContains); }
603
604 /*------ module handling ------------------------------------------------------------*/
605<Start>block{BS}data{BS}{ID_} { //
606 yyextra->vtype = V_IGNORE;
607 yy_push_state(BlockData,yyscanner);
608 yyextra->defaultProtection = Protection::Public;
609 }
@ Public
Definition types.h:26
610<Start>module|program{BS_} { //
611 yyextra->vtype = V_IGNORE;
612 if (yytext[0]=='m' || yytext[0]=='M')
613 {
614 yy_push_state(Module,yyscanner);
615 }
616 else
617 {
618 yy_push_state(Program,yyscanner);
619 }
620 yyextra->defaultProtection = Protection::Public;
621 }
622<BlockData>^{BS}"end"({BS}(block{BS}data)({BS_}{ID})?)?{BS}/(\n|!|;) { // end block data
623 //if (!endScope(yyscanner,yyextra->current_root))
624 // yyterminate();
625 yyextra->defaultProtection = Protection::Public;
626 pop_state(yyscanner);
627 }
628<Start,ModuleBody,ModuleBodyContains>"end"({BS}(module|program)({BS_}{ID})?)?{BS}/(\n|!|;) { // end module
629 resolveModuleProcedures(yyscanner,yyextra->current_root);
630 if (!endScope(yyscanner,yyextra->current_root))
631 {
632 yyterminate();
633 }
634 yyextra->defaultProtection = Protection::Public;
635 if (yyextra->global_scope)
636 {
637 if (yyextra->global_scope != INVALID_ENTRY)
638 {
639 yy_push_state(Start,yyscanner);
640 }
641 else
642 {
643 pop_state(yyscanner); // cannot pop artrificial entry
644 }
645 }
646 else
647 {
648 yy_push_state(Start,yyscanner);
649 yyextra->global_scope = INVALID_ENTRY; // signal that the yyextra->global_scope has already been used.
650 }
651 }
#define INVALID_ENTRY
652<Module>{ID} {
653 addModule(yyscanner, QCString(yytext), TRUE);
654 BEGIN(ModuleBody);
655 }
#define TRUE
Definition qcstring.h:37
656<Program>{ID} {
657 addModule(yyscanner, QCString(yytext), FALSE);
658 BEGIN(ModuleBody);
659 }
660
661 /*------- access specification --------------------------------------------------------------------------*/
662
663<ModuleBody,TypedefBody,TypedefBodyContains>private/{BS}(\n|"!") {
664 yyextra->defaultProtection = Protection::Private;
665 yyextra->current->protection = yyextra->defaultProtection ;
666 }
@ Private
Definition types.h:26
667<ModuleBody,TypedefBody,TypedefBodyContains>public/{BS}(\n|"!") {
668 yyextra->defaultProtection = Protection::Public;
669 yyextra->current->protection = yyextra->defaultProtection ;
670 }
671
672 /*------- type definition -------------------------------------------------------------------------------*/
673
674<ModuleBody>^{BS}type{BS}"=" {}
675<Start,ModuleBody>^{BS}type/[^a-z0-9_] {
676 if (YY_START == Start)
677 {
678 addModule(yyscanner,QCString());
679 yy_push_state(ModuleBody,yyscanner); //anon program
680 }
681
682 yy_push_state(Typedef,yyscanner);
683 yyextra->current->protection = Protection::Package; // invalid in Fortran, replaced below
684 yyextra->typeProtection = Protection::Public;
685 yyextra->typeMode = true;
686 }
@ Package
Definition types.h:26
687<Typedef>{
688{COMMA} {}
689
690{BS}"::"{BS} {}
691
692abstract {
693 yyextra->current->spec.setAbstractClass(true);
694 }
695extends{ARGS} {
696 QCString basename = extractFromParens(yytext).lower();
697 yyextra->current->extends.emplace_back(basename, Protection::Public, Specifier::Normal);
698 }
@ Normal
Definition types.h:29
699public {
700 yyextra->current->protection = Protection::Public;
701 }
702private {
703 yyextra->current->protection = Protection::Private;
704 }
705{LANGUAGE_BIND_SPEC} {
706 /* ignored for now */
707 }
708{ID} { /* type name found */
709 yyextra->current->section = EntryType::makeClass();
710 yyextra->current->spec.setStruct(true);
711 yyextra->current->name = yytext;
712 yyextra->current->fileName = yyextra->fileName;
713 yyextra->current->bodyLine = yyextra->lineNr;
714 yyextra->current->startLine = yyextra->lineNr;
715
716 /* if type is part of a module, mod name is necessary for output */
717 if (yyextra->current_root &&
718 (yyextra->current_root->section.isClass() ||
719 yyextra->current_root->section.isNamespace()))
720 {
721 yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name;
722 }
723
724 // set modifiers to allow adjusting public/private in surrounding module scope
725 if( yyextra->current->protection == Protection::Package )
726 {
727 yyextra->current->protection = yyextra->defaultProtection;
728 }
729 else if( yyextra->current->protection == Protection::Public )
730 {
731 yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()] |= QCString("public");
732 }
733 else if( yyextra->current->protection == Protection::Private )
734 {
735 yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()] |= QCString("private");
736 }
737
738 addCurrentEntry(yyscanner,true);
739 startScope(yyscanner,yyextra->last_entry.get());
740 BEGIN(TypedefBody);
741 }
742}
743
744<TypedefBodyContains>{ /* Type Bound Procedures */
745^{BS}PROCEDURE{ARGS}? {
746 yyextra->current->type = QCString(yytext).simplifyWhiteSpace().lower();
747 }
QCString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple whitespace cha...
Definition qcstring.cpp:185
748^{BS}final {
749 yyextra->current->spec.setFinal(true);
750 yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
751 }
752^{BS}generic {
753 yyextra->current->type = QCString(yytext).simplifyWhiteSpace();
754 }
755{COMMA} {
756 }
757{ATTR_SPEC} {
758 yyextra->currentModifiers |= QCString(yytext).stripWhiteSpace();
759 }
760{BS}"::"{BS} {
761 }
762{ID} {
763 QCString name = yytext;
764 yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers;
765 yyextra->current->section = EntryType::makeFunction();
766 yyextra->current->name = name;
767 // check for procedure(name)
768 if (yyextra->current->type.find('(') != -1)
769 {
770 yyextra->current->args = extractFromParens(yyextra->current->type).stripWhiteSpace();
771 }
772 else
773 {
774 yyextra->current->args = name.lower(); // target procedure name if no => is given
775 }
776 yyextra->current->fileName = yyextra->fileName;
777 yyextra->current->bodyLine = yyextra->lineNr;
778 yyextra->current->startLine = yyextra->lineNr;
779 addCurrentEntry(yyscanner,true);
780 }
const std::string & str() const
Definition qcstring.h:526
781{BS}"=>"[^(\n|\!)]* { /* Specific bindings come after the ID. */
782 QCString tmp = yytext;
783 int i = tmp.find("=>");
784 if (i!=-1)
785 {
786 tmp.remove(0, i+2);
787 }
788 tmp = tmp.simplifyWhiteSpace().lower();
789 if (yyextra->last_entry->type == "generic")
790 {
791 // duplicate entries for each overloaded variant
792 // (parse through medhod1,method2, methodN, ...
793 //printf("Parsing through %s for generic method %s.\n", tmp.data(), last_entry->name.data());
794 i = tmp.find(",");
795 while (i>0)
796 {
797 copyEntry(yyextra->current, yyextra->last_entry);
798 yyextra->current->name = yyextra->last_entry->name;
799 yyextra->current->section = EntryType::makeFunction();
800 yyextra->last_entry->args = tmp.left(i).stripWhiteSpace();
801 //printf("Found %s.\n", last_entry->args.data());
802 addCurrentEntry(yyscanner,true);
803 tmp = tmp.remove(0,i+1).stripWhiteSpace();
804 i = tmp.find(",");
805 }
806 }
807 //printf("Target function: %s\n", tmp.data());
808 yyextra->last_entry->args = tmp;
809 }
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & remove(size_t index, size_t len)
Definition qcstring.h:427
QCString left(size_t len) const
Definition qcstring.h:214
810"\n" {
811 yyextra->currentModifiers = SymbolModifiers();
812 newLine(yyscanner);
813 yyextra->docBlock.clear();
814 }
Holds yyextra->modifiers (ie attributes) for one symbol (variable, function, etc)
815}
816
817
818<TypedefBody,TypedefBodyContains>{
819^{BS}"end"{BS}"type"({BS_}{ID})?{BS}/(\n|!|;) { /* end type definition */
820 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
821 if (!endScope(yyscanner,yyextra->current_root))
822 {
823 yyterminate();
824 }
825 yyextra->typeMode = false;
826 pop_state(yyscanner);
827 }
828^{BS}"end"{BS}/(\n|!|;) { /* incorrect end type definition */
829 warn(yyextra->fileName,yyextra->lineNr, "Found 'END' instead of 'END TYPE'");
830 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
831 if (!endScope(yyscanner,yyextra->current_root))
832 {
833 yyterminate();
834 }
835 yyextra->typeMode = false;
836 pop_state(yyscanner);
837 }
#define warn(file, line, fmt,...)
Definition message.h:59
838}
839
840 /*------- module/global/typedef variable ---------------------------------------------------*/
841
842<SubprogBody,SubprogBodyContains>^{BS}[0-9]*{BS}"end"({BS}{SUBPROG}({BS_}{ID})?)?{BS}/(\n|!|;) {
843 //
844 // ABSTRACT and specific interfaces are stored
845 // in a scope of their own, even if multiple
846 // are group in one INTERFACE/END INTERFACE block.
847 //
848 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
849 {
850 endScope(yyscanner,yyextra->current_root);
851 yyextra->last_entry->endBodyLine = yyextra->lineNr - 1;
852 }
853 yyextra->current_root->endBodyLine = yyextra->lineNr - 1;
854
855 if (!endScope(yyscanner,yyextra->current_root))
856 {
857 yyterminate();
858 }
859 yyextra->subrCurrent.pop_back();
860 yyextra->vtype = V_IGNORE;
861 pop_state(yyscanner) ;
862 }
863<BlockData>{
864{ID} {
865 }
866}
867<Start,ModuleBody,TypedefBody,SubprogBody,FEnum>{
868^{BS}{TYPE_SPEC}/{SEPARATE} {
869 yyextra->last_enum.reset();
870 if (YY_START == FEnum)
871 {
872 yyextra->argType = "@"; // enum marker
873 }
874 else
875 {
876 yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower();
877 }
878 yyextra->current->bodyLine = yyextra->lineNr + 1;
879 yyextra->current->endBodyLine = yyextra->lineNr + yyextra->lineCountPrepass;
880 /* variable declaration starts */
881 if (YY_START == Start)
882 {
883 addModule(yyscanner);
884 yy_push_state(ModuleBody,yyscanner); //anon program
885 }
886 yy_push_state(AttributeList,yyscanner);
887 }
888{EXTERNAL_STMT}/({BS}"::"|{BS_}{ID}) {
889 /* external can be a "type" or an attribute */
890 if (YY_START == Start)
891 {
892 addModule(yyscanner);
893 yy_push_state(ModuleBody,yyscanner); //anon program
894 }
895 QCString tmp = yytext;
896 yyextra->currentModifiers |= tmp.stripWhiteSpace();
897 yyextra->argType = QCString(yytext).simplifyWhiteSpace().lower();
898 yy_push_state(AttributeList,yyscanner);
899 }
900{ATTR_STMT}/{BS_}{ID} |
901{ATTR_STMT}/{BS}"::" {
902 /* attribute statement starts */
903 DBG_CTX((stderr,"5=========> Attribute statement: %s\n", yytext));
904 if (YY_START == Start)
905 {
906 addModule(yyscanner);
907 yy_push_state(ModuleBody,yyscanner); //anon program
908 }
909 QCString tmp = yytext;
910 yyextra->currentModifiers |= tmp.stripWhiteSpace();
911 yyextra->argType="";
912 yy_push_state(YY_START,yyscanner);
913 BEGIN( AttributeList ) ;
914 }
915"common" {
916 if (YY_START == Start)
917 {
918 addModule(yyscanner);
919 yy_push_state(ModuleBody,yyscanner); //anon program
920 }
921 }
922{ID} {
923 if (YY_START == Start)
924 {
925 addModule(yyscanner);
926 yy_push_state(ModuleBody,yyscanner); //anon program
927 }
928 }
929^{BS}"type"{BS_}"is"/{BT_} {}
930^{BS}"type"{BS}"=" {}
931^{BS}"class"{BS_}"is"/{BT_} {}
932^{BS}"class"{BS_}"default" {}
933}
934<AttributeList>{
935{COMMA} {}
936{BS} {}
937{LANGUAGE_BIND_SPEC} {
938 yyextra->currentModifiers |= yytext;
939 }
940{ATTR_SPEC}. { /* update yyextra->current yyextra->modifiers when it is an ATTR_SPEC and not a variable name */
941 /* buyyextra->625519 */
942 char chr = yytext[(int)yyleng-1];
943 if (isId(chr))
944 {
945 yyextra->colNr -= (int)yyleng;
946 REJECT;
947 }
948 else
949 {
950 QCString tmp = yytext;
951 tmp = tmp.left(tmp.length() - 1);
952 yyextra->colNr -= 1;
953 unput(yytext[(int)yyleng-1]);
954 yyextra->currentModifiers |= (tmp);
955 }
956 }
bool isId(int c)
Definition util.h:202
957"::" { /* end attribute list */
958 BEGIN( FVariable );
959 }
960. { /* unknown attribute, consider variable name */
961 //cout<<"start variables, unput "<<*yytext<<endl;
962 yyextra->colNr -= 1;
963 unput(*yytext);
964 BEGIN( FVariable );
965 }
966}
967
968<FVariable>{BS} {}
969<FVariable>{OPERATOR_ID} { /* parse operator access statements "public :: operator(==)" */
970 QCString name = QCString(yytext).stripWhiteSpace().lower();
971 /* if variable/type/etc is part of a module, mod name is necessary for output */
972 // get surrounding state
973 int currentState = YY_START;
974 pop_state(yyscanner);
975 int outerState = YY_START;
976 yy_push_state(currentState,yyscanner);
977 if( outerState == Start || outerState == ModuleBody )
978 {
979 if ((yyextra->current_root) &&
980 (yyextra->current_root->section.isClass() ||
981 yyextra->current_root->section.isNamespace()))
982 {
983 name = yyextra->current_root->name + "::" + name;
984 }
985 }
986 /* remember attributes for the symbol */
987 yyextra->modifiers[yyextra->current_root][name.str()] |= yyextra->currentModifiers;
988 }
989<FVariable>{ID} { /* parse variable declaration */
990 //cout << "5=========> got variable: " << yyextra->argType << "::" << yytext << endl;
991 /* work around for bug in QCString.replace (QCString works) */
992 QCString name=yytext;
993 name = name.lower();
994 /* if variable/type/etc is part of a module, mod name is necessary for output */
995 if ((yyextra->current_root) && yyextra->current_root->section.isNamespace())
996 {
997 name = yyextra->current_root->name + "::" + name;
998 }
999 /* remember attributes for the symbol */
1000 yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers;
1001 yyextra->argName= name;
1002
1003 yyextra->vtype= V_IGNORE;
1004 if (!yyextra->argType.isEmpty() && !yyextra->current_root->section.isFunction())
1005 { // new variable entry
1006 yyextra->vtype = V_VARIABLE;
1007 yyextra->current->section = EntryType::makeVariable();
1008 yyextra->current->name = yyextra->argName;
1009 yyextra->current->type = yyextra->argType;
1010 yyextra->current->fileName = yyextra->fileName;
1011 yyextra->current->bodyLine = yyextra->lineNr; // used for source reference
1012 yyextra->current->startLine = yyextra->lineNr;
1013 if (yyextra->argType == "@")
1014 {
1015 yyextra->current_root->copyToSubEntry(yyextra->current);
1016 // add to the scope surrounding the enum (copy!)
1017 yyextra->last_enum = yyextra->current;
1018 yyextra->current_root->parent()->moveToSubEntryAndRefresh(yyextra->current);
1019 initEntry(yyscanner);
1020 }
1021 else
1022 {
1023 addCurrentEntry(yyscanner,true);
1024 }
1025 }
1026 else if (!yyextra->argType.isEmpty())
1027 { // declaration of parameter list: add type for corr. parameter
1028 Argument *parameter = getParameter(yyscanner,yyextra->argName);
1029 if (parameter)
1030 {
1031 yyextra->vtype= V_PARAMETER;
1032 if (!yyextra->argType.isEmpty()) parameter->type=yyextra->argType.stripWhiteSpace();
1033 if (!yyextra->docBlock.isEmpty())
1034 {
1035 subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1036 }
1037 }
1038 // save, it may be function return type
1039 if (parameter)
1040 {
1041 yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType;
1042 }
1043 else
1044 {
1045 if ((yyextra->current_root->name.lower() == yyextra->argName.lower()) ||
1046 (yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName.lower() == yyextra->argName.lower()))
1047 {
1048 int strt = yyextra->current_root->type.find("function");
1049 QCString lft;
1050 QCString rght;
1051 if (strt != -1)
1052 {
1053 yyextra->vtype = V_RESULT;
1054 lft = "";
1055 rght = "";
1056 if (strt != 0) lft = yyextra->current_root->type.left(strt).stripWhiteSpace();
1057 if ((yyextra->current_root->type.length() - strt - strlen("function"))!= 0)
1058 {
1059 rght = yyextra->current_root->type.right(yyextra->current_root->type.length() - strt - (int)strlen("function")).stripWhiteSpace();
1060 }
1061 yyextra->current_root->type = lft;
1062 if (rght.length() > 0)
1063 {
1064 if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
1065 yyextra->current_root->type += rght;
1066 }
1067 if (yyextra->argType.stripWhiteSpace().length() > 0)
1068 {
1069 if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
1070 yyextra->current_root->type += yyextra->argType.stripWhiteSpace();
1071 }
1072 if (yyextra->current_root->type.length() > 0) yyextra->current_root->type += " ";
1073 yyextra->current_root->type += "function";
1074 if (!yyextra->docBlock.isEmpty())
1075 {
1076 subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE);
1077 }
1078 }
1079 else
1080 {
1081 yyextra->current_root->type += " " + yyextra->argType.stripWhiteSpace();
1082 }
1083 yyextra->current_root->type = yyextra->current_root->type.stripWhiteSpace();
1084 yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->current_root->type;
1085 }
1086 else
1087 {
1088 yyextra->modifiers[yyextra->current_root][name.lower().str()].type = yyextra->argType;
1089 }
1090 }
1091 // any accumulated doc for argument should be emptied,
1092 // because it is handled other way and this doc can be
1093 // unexpectedly passed to the next member.
1094 yyextra->current->doc.clear();
1095 yyextra->current->brief.clear();
1096 }
1097 }
QCString type
Definition arguments.h:37
1098<FVariable>{ARGS} { /* dimension of the previous entry. */
1099 QCString name(yyextra->argName);
1100 QCString attr("dimension");
1101 attr += yytext;
1102 yyextra->modifiers[yyextra->current_root][name.lower().str()] |= attr;
1103 }
1104<FVariable>{COMMA} { //printf("COMMA: %d<=..<=%d\n", yyextra->colNr-(int)yyleng, yyextra->colNr);
1105 // locate !< comment
1106 updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1107 }
1108<FVariable>{BS}"=" {
1109 yy_push_state(YY_START,yyscanner);
1110 yyextra->initializer="=";
1111 yyextra->initializerScope = yyextra->initializerArrayScope = 0;
1112 BEGIN(Initialization);
1113 }
1114<FVariable>"\n" { yyextra->currentModifiers = SymbolModifiers();
1115 pop_state(yyscanner); // end variable declaration list
1116 newLine(yyscanner);
1117 yyextra->docBlock.clear();
1118 }
1119<FVariable>";".*"\n" { yyextra->currentModifiers = SymbolModifiers();
1120 pop_state(yyscanner); // end variable declaration list
1121 yyextra->docBlock.clear();
1122 yyextra->inputStringSemi = " \n"+QCString(yytext+1);
1123 yyextra->lineNr--;
1124 pushBuffer(yyscanner,yyextra->inputStringSemi);
1125 }
1126<*>";".*"\n" {
1127 if (YY_START == FVariable) REJECT; // Just be on the safe side
1128 if (YY_START == String) REJECT; // ";" ignored in strings
1129 if (YY_START == StrIgnore) REJECT; // ";" ignored in regular yyextra->comments
1130 if (YY_START == DocBlock) REJECT; // ";" ignored in documentation blocks
1131 yyextra->inputStringSemi = " \n"+QCString(yytext+1);
1132 yyextra->lineNr--;
1133 pushBuffer(yyscanner,yyextra->inputStringSemi);
1134 }
1135
1136<Initialization,ArrayInitializer>"[" |
1137<Initialization,ArrayInitializer>"(/" { yyextra->initializer+=yytext;
1138 yyextra->initializerArrayScope++;
1139 BEGIN(ArrayInitializer); // initializer may contain comma
1140 }
1141<ArrayInitializer>"]" |
1142<ArrayInitializer>"/)" { yyextra->initializer+=yytext;
1143 yyextra->initializerArrayScope--;
1144 if (yyextra->initializerArrayScope<=0)
1145 {
1146 yyextra->initializerArrayScope = 0; // just in case
1147 BEGIN(Initialization);
1148 }
1149 }
1150<ArrayInitializer>. { yyextra->initializer+=yytext; }
1151<Initialization>"(" { yyextra->initializerScope++;
1152 yyextra->initializer+=yytext;
1153 }
1154<Initialization>")" { yyextra->initializerScope--;
1155 yyextra->initializer+=yytext;
1156 }
1157<Initialization>{COMMA} { if (yyextra->initializerScope == 0)
1158 {
1159 updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1160 pop_state(yyscanner); // end initialization
1161 if (yyextra->last_enum)
1162 {
1163 yyextra->last_enum->initializer.str(yyextra->initializer.str());
1164 }
1165 else
1166 {
1167 if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str());
1168 }
1169 }
1170 else
1171 {
1172 yyextra->initializer+=", ";
1173 }
1174 }
1175<Initialization>"\n"|"!" { //|
1176 pop_state(yyscanner); // end initialization
1177 if (yyextra->last_enum)
1178 {
1179 yyextra->last_enum->initializer.str(yyextra->initializer.str());
1180 }
1181 else
1182 {
1183 if (yyextra->vtype == V_VARIABLE) yyextra->last_entry->initializer.str(yyextra->initializer.str());
1184 }
1185 yyextra->colNr -= 1;
1186 unput(*yytext);
1187 }
1188<Initialization>. { yyextra->initializer+=yytext; }
1189
1190<*>{BS}"enum"{BS}","{BS}"bind"{BS}"("{BS}"c"{BS}")"{BS} {
1191 if (YY_START == Start)
1192 {
1193 addModule(yyscanner);
1194 yy_push_state(ModuleBody,yyscanner); //anon program
1195 }
1196
1197 yy_push_state(FEnum,yyscanner);
1198 yyextra->current->protection = yyextra->defaultProtection;
1199 yyextra->typeProtection = yyextra->defaultProtection;
1200 yyextra->typeMode = true;
1201
1202 yyextra->current->spec.setStruct(true);
1203 yyextra->current->name.clear();
1204 yyextra->current->args.clear();
1205 yyextra->current->name.sprintf("@%d",yyextra->anonCount++);
1206
1207 yyextra->current->section = EntryType::makeEnum();
1208 yyextra->current->fileName = yyextra->fileName;
1209 yyextra->current->startLine = yyextra->lineNr;
1210 yyextra->current->bodyLine = yyextra->lineNr;
1211 if ((yyextra->current_root) &&
1212 (yyextra->current_root->section.isClass() ||
1213 yyextra->current_root->section.isNamespace()))
1214 {
1215 yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name;
1216 }
1217
1218 addCurrentEntry(yyscanner,true);
1219 startScope(yyscanner,yyextra->last_entry.get());
1220 BEGIN( FEnum ) ;
1221 }
1222<FEnum>"end"{BS}"enum" {
1223 yyextra->last_entry->parent()->endBodyLine = yyextra->lineNr;
1224 if (!endScope(yyscanner,yyextra->current_root))
1225 {
1226 yyterminate();
1227 }
1228 yyextra->typeMode = false;
1229 pop_state(yyscanner);
1230 }
1231 /*------ fortran subroutine/function handling ------------------------------------------------------------*/
1232 /* Start is initial condition */
1233
1234<Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{TYPE_SPEC}{BS}({PREFIX}{BS_})?/{SUBPROG}{BS_} {
1235 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1236 {
1237 addInterface(yyscanner,"$interface$", yyextra->ifType);
1238 startScope(yyscanner,yyextra->last_entry.get());
1239 }
1240
1241 // TYPE_SPEC is for old function style function result
1242 yyextra->current->type = QCString(yytext).stripWhiteSpace().lower();
1243 yy_push_state(SubprogPrefix,yyscanner);
1244 }
1245
1246<SubprogPrefix>{BS}{SUBPROG}{BS_} {
1247 // Fortran subroutine or function found
1248 yyextra->vtype = V_IGNORE;
1249 QCString result=yytext;
1250 result=result.stripWhiteSpace();
1251 addSubprogram(yyscanner,result);
1252 BEGIN(Subprog);
1253 yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
1254 yyextra->current->startLine = yyextra->lineNr;
1255 }
1256
1257<Start,ModuleBody,SubprogBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains>^{BS}({PREFIX}{BS_})?{SUBPROG}{BS_} {
1258 // Fortran subroutine or function found
1259 yyextra->vtype = V_IGNORE;
1260 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1261 {
1262 addInterface(yyscanner,"$interface$", yyextra->ifType);
1263 startScope(yyscanner,yyextra->last_entry.get());
1264 }
1265
1266 QCString result = QCString(yytext).stripWhiteSpace();
1267 addSubprogram(yyscanner,result);
1268 yy_push_state(Subprog,yyscanner);
1269 yyextra->current->bodyLine = yyextra->lineNr + yyextra->lineCountPrepass + 1; // we have to be at the line after the definition and we have to take continuation lines into account.
1270 yyextra->current->startLine = yyextra->lineNr;
1271 }
1272
1273<Subprog>{BS} { /* ignore white space */ }
1274<Subprog>{ID} { yyextra->current->name = yytext;
1275 //cout << "1a==========> got " << yyextra->current->type << " " << yytext << " " << yyextra->lineNr << endl;
1276 QCString returnName = yyextra->current->name.lower();
1277 /* if type is part of a module, mod name is necessary for output */
1278 if ((yyextra->current_root) &&
1279 (yyextra->current_root->section.isClass() ||
1280 yyextra->current_root->section.isNamespace()))
1281 {
1282 yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name;
1283 }
1284 yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()].returnName = std::move(returnName);
1285
1286 if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC)
1287 {
1288 yyextra->current_root->name = substitute(
1289 yyextra->current_root->name, "$interface$", QCString(yytext).lower());
1290 }
1291
1292 BEGIN(Parameterlist);
1293 }
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:477
1294<Parameterlist>"(" { yyextra->current->args = "("; }
1295<Parameterlist>")" {
1296 yyextra->current->args += ")";
1297 yyextra->current->args = removeRedundantWhiteSpace(yyextra->current->args);
1298 addCurrentEntry(yyscanner,true);
1299 startScope(yyscanner,yyextra->last_entry.get());
1300 BEGIN(SubprogBody);
1301 }
QCString removeRedundantWhiteSpace(const QCString &s)
Definition util.cpp:578
1302<Parameterlist>{COMMA}|{BS} { yyextra->current->args += yytext;
1303 const CommentInPrepass *c = locatePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr);
1304 if (c)
1305 {
1306 if (!yyextra->current->argList.empty())
1307 {
1308 yyextra->current->argList.back().docs = c->str;
1309 }
1310 }
1311 }
1312<Parameterlist>{ID} {
1313 //yyextra->current->type not yet available
1314 QCString param = QCString(yytext).lower();
1315 // std::cout << "3=========> got parameter " << param << "\n";
1316 yyextra->current->args += param;
1317 Argument arg;
1318 arg.name = param;
1319 arg.type = "";
1320 yyextra->current->argList.push_back(arg);
1321 }
QCString name
Definition arguments.h:39
1322<Parameterlist>{NOARGS} {
1323 newLine(yyscanner);
1324 //printf("3=========> without parameterlist \n");
1325 addCurrentEntry(yyscanner,true);
1326 startScope(yyscanner,yyextra->last_entry.get());
1327 BEGIN(SubprogBody);
1328 }
1329<SubprogBody>result{BS}\‍({BS}{ID} {
1330 if (yyextra->functionLine)
1331 {
1332 QCString result= yytext;
1333 result= result.right(result.length()-result.find("(")-1);
1334 result= result.stripWhiteSpace();
1335 yyextra->modifiers[yyextra->current_root->parent()][yyextra->current_root->name.lower().str()].returnName = result;
1336 }
1337 //cout << "=====> got result " << result << endl;
1338 }
1339
1340 /*---- documentation yyextra->comments --------------------------------------------------------------------*/
1341
1342<FVariable,SubprogBody,ModuleBody,TypedefBody,TypedefBodyContains>"!<" { /* backward docu comment */
1343 if (yyextra->vtype != V_IGNORE)
1344 {
1345 yyextra->current->docLine = yyextra->lineNr;
1346 yyextra->docBlockJavaStyle = FALSE;
1347 yyextra->docBlock.clear();
1348 yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF);
1349 startCommentBlock(yyscanner,FALSE);
1350 yy_push_state(DocBackLine,yyscanner);
1351 }
1352 else
1353 {
1354 /* handle out of place !< comment as a normal comment */
1355 if (YY_START == String)
1356 {
1357 yyextra->colNr -= (int)yyleng;
1358 REJECT;
1359 } // "!" is ignored in strings
1360 // skip comment line (without docu yyextra->comments "!>" "!<" )
1361 /* ignore further "!" and ignore yyextra->comments in Strings */
1362 if ((YY_START != StrIgnore) && (YY_START != String))
1363 {
1364 yy_push_state(YY_START,yyscanner);
1365 BEGIN(StrIgnore);
1366 yyextra->debugStr="*!";
1367 }
1368 }
1369 }
#define Config_getBool(name)
Definition config.h:33
1370<DocBackLine>.* { // contents of yyextra->current comment line
1371 yyextra->docBlock+=yytext;
1372 }
1373<DocBackLine>"\n"{BS}"!"("<"|"!"+) { // comment block (next line is also comment line)
1374 yyextra->docBlock+="\n"; // \n is necessary for lists
1375 newLine(yyscanner);
1376 }
1377<DocBackLine>"\n" { // comment block ends at the end of this line
1378 //cout <<"3=========> comment block : "<< yyextra->docBlock << endl;
1379 yyextra->colNr -= 1;
1380 unput(*yytext);
1381 if (yyextra->vtype == V_VARIABLE)
1382 {
1383 std::shared_ptr<Entry> tmp_entry = yyextra->current;
1384 // temporarily switch to the previous entry
1385 if (yyextra->last_enum)
1386 {
1387 yyextra->current = yyextra->last_enum;
1388 }
1389 else
1390 {
1391 yyextra->current = yyextra->last_entry;
1392 }
1393 handleCommentBlock(yyscanner,stripIndentation(yyextra->docBlock),TRUE);
1394 // switch back
1395 yyextra->current = std::move(tmp_entry);
1396 }
1397 else if (yyextra->vtype == V_PARAMETER)
1398 {
1399 subrHandleCommentBlock(yyscanner,yyextra->docBlock,TRUE);
1400 }
1401 else if (yyextra->vtype == V_RESULT)
1402 {
1403 subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE);
1404 }
1405 pop_state(yyscanner);
1406 yyextra->docBlock.clear();
1407 }
QCString stripIndentation(const QCString &s)
Definition util.cpp:6281
1408
1409<Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains,TypedefBodyContains,FEnum>^{BS} {
1410 yyextra->curIndent = computeIndent(yytext);
1411 }
static int computeIndent(const char *s)
1412<Start,SubprogBody,ModuleBody,TypedefBody,InterfaceBody,ModuleBodyContains,SubprogBodyContains,TypedefBodyContains,FEnum>"!>" {
1413 yy_push_state(YY_START,yyscanner);
1414 yyextra->current->docLine = yyextra->lineNr;
1415 yyextra->docBlockJavaStyle = FALSE;
1416 if (YY_START==SubprogBody) yyextra->docBlockInBody = TRUE;
1417 yyextra->docBlock.clear();
1418 yyextra->docBlockJavaStyle = Config_getBool(JAVADOC_AUTOBRIEF);
1419 startCommentBlock(yyscanner,FALSE);
1420 BEGIN(DocBlock);
1421 //cout << "start DocBlock " << endl;
1422 }
1423
1424
1425<DocBlock>({CMD}{CMD}){ID}/[^a-z_A-Z0-9] { // escaped command
1426 yyextra->docBlock += yytext;
1427 }
1428<DocBlock>{CMD}("f$"|"f["|"f{"|"f(") {
1429 yyextra->docBlock += yytext;
1430 yyextra->docBlockName=&yytext[1];
1431 if (yyextra->docBlockName.at(1)=='[')
1432 {
1433 yyextra->docBlockName.at(1)=']';
1434 }
1435 if (yyextra->docBlockName.at(1)=='{')
1436 {
1437 yyextra->docBlockName.at(1)='}';
1438 }
1439 if (yyextra->docBlockName.at(1)=='(')
1440 {
1441 yyextra->docBlockName.at(1)=')';
1442 }
1443 yyextra->fencedSize=0;
1444 yyextra->blockString=yytext;
1445 yyextra->blockLineNr=yyextra->lineNr;
1446 BEGIN(DocCopyBlock);
1447 }
1448<DocBlock>{CMD}"ifile"{B}+"\""[^\n\"]+"\"" {
1449 yyextra->fileName = &yytext[6];
1450 yyextra->fileName = yyextra->fileName.stripWhiteSpace();
1451 yyextra->fileName = yyextra->fileName.mid(1,yyextra->fileName.length()-2);
1452 yyextra->docBlock += yytext;
1453 }
1454<DocBlock>{CMD}"ifile"{B}+{FILEMASK} {
1455 yyextra->fileName = &yytext[6];
1456 yyextra->fileName = yyextra->fileName.stripWhiteSpace();
1457 yyextra->docBlock += yytext;
1458 }
1459<DocBlock>{CMD}"iline"{LINENR}/[\n\.] |
1460<DocBlock>{CMD}"iline"{LINENR}{B} {
1461 bool ok = false;
1462 int nr = QCString(&yytext[6]).toInt(&ok);
1463 if (!ok)
1464 {
1465 warn(yyextra->fileName,yyextra->lineNr,"Invalid line number '%s' for iline command",yytext);
1466 }
1467 else
1468 {
1469 yyextra->lineNr = nr;
1470 }
1471 yyextra->docBlock += yytext;
1472 }
int toInt(bool *ok=nullptr, int base=10) const
Definition qcstring.cpp:249
1473<DocBlock>{B}*"<"{PRE}">" {
1474 yyextra->docBlock += yytext;
1475 yyextra->docBlockName="<pre>";
1476 yyextra->fencedSize=0;
1477 yyextra->blockString=yytext;
1478 yyextra->blockLineNr=yyextra->lineNr;
1479 BEGIN(DocCopyBlock);
1480 }
1481<DocBlock>{B}*"<"<CODE>">" {
1482 yyextra->docBlock += yytext;
1483 yyextra->docBlockName="<code>";
1484 yyextra->fencedSize=0;
1485 yyextra->blockString=yytext;
1486 yyextra->blockLineNr=yyextra->lineNr;
1487 BEGIN(DocCopyBlock);
1488 }
1489<DocBlock>{CMD}"startuml"/[^a-z_A-Z0-9\-] { // verbatim command
1490 yyextra->docBlock += yytext;
1491 yyextra->docBlockName="uml";
1492 yyextra->fencedSize=0;
1493 yyextra->blockString=yytext;
1494 yyextra->blockLineNr=yyextra->lineNr;
1495 BEGIN(DocCopyBlock);
1496 }
1497<DocBlock>{CMD}("verbatim"|"iliteral"|"latexonly"|"htmlonly"|"xmlonly"|"manonly"|"rtfonly"|"docbookonly"|"dot"|"msc"|"code")/[^a-z_A-Z0-9\-] { // verbatim command
1498 yyextra->docBlock += yytext;
1499 yyextra->docBlockName=&yytext[1];
1500 yyextra->fencedSize=0;
1501 yyextra->blockString=yytext;
1502 yyextra->blockLineNr=yyextra->lineNr;
1503 BEGIN(DocCopyBlock);
1504 }
1505<DocBlock>"~~~"[~]* {
1506 QCString pat = yytext;
1507 yyextra->docBlock += pat;
1508 yyextra->docBlockName="~~~";
1509 yyextra->fencedSize=pat.length();
1510 yyextra->blockString=yytext;
1511 yyextra->blockLineNr=yyextra->lineNr;
1512 BEGIN(DocCopyBlock);
1513 }
1514<DocBlock>"```"[`]*/(".")?[a-zA-Z0-9#_-]+ |
1515<DocBlock>"```"[`]*/"{"[^}]+"}" |
1516<DocBlock>"```"[`]* {
1517 QCString pat = yytext;
1518 yyextra->docBlock += pat;
1519 yyextra->docBlockName="```";
1520 yyextra->fencedSize=pat.length();
1521 yyextra->blockString=yytext;
1522 yyextra->blockLineNr=yyextra->lineNr;
1523 BEGIN(DocCopyBlock);
1524 }
1525<DocBlock>"\\ilinebr "{BS} {
1526 QCString indent;
1527 int extraSpaces = std::max(0,static_cast<int>(yyleng-9-yyextra->curIndent-2));
1528 indent.fill(' ',extraSpaces);
1529 //printf("extraSpaces=%d\n",extraSpaces);
1530 yyextra->docBlock += "\\ilinebr ";
1531 yyextra->docBlock += indent;
1532 }
void fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:180
1533
1534<DocBlock>[^@*`~\/\\\n]+ { // any character that isn't special
1535 yyextra->docBlock += yytext;
1536 }
1537<DocBlock>"\n"{BS}"!"(">"|"!"+) { // comment block (next line is also comment line)
1538 yyextra->docBlock+="\n"; // \n is necessary for lists
1539 newLine(yyscanner);
1540 }
1541<DocBlock>"\n" { // comment block ends at the end of this line
1542 //cout <<"3=========> comment block : "<< yyextra->docBlock << endl;
1543 yyextra->colNr -= 1;
1544 unput(*yytext);
1545 handleCommentBlock(yyscanner,stripIndentation(yyextra->docBlock),TRUE);
1546 pop_state(yyscanner);
1547 }
1548<DocBlock>. { // command block
1549 yyextra->docBlock += *yytext;
1550 }
1551
1552 /* ---- Copy verbatim sections ------ */
1553
1554<DocCopyBlock>"</"{PRE}">" { // end of a <pre> block
1555 yyextra->docBlock += yytext;
1556 if (yyextra->docBlockName=="<pre>")
1557 {
1558 yyextra->docBlockName="";
1559 yyextra->blockString.clear();
1560 yyextra->blockLineNr=-1;
1561 BEGIN(DocBlock);
1562 }
1563 }
1564<DocCopyBlock>"</"{CODE}">" { // end of a <code> block
1565 yyextra->docBlock += yytext;
1566 if (yyextra->docBlockName=="<code>")
1567 {
1568 yyextra->docBlockName="";
1569 yyextra->blockString.clear();
1570 yyextra->blockLineNr=-1;
1571 BEGIN(DocBlock);
1572 }
1573 }
1574<DocCopyBlock>[\\@]("f$"|"f]"|"f}"|"f)") {
1575 yyextra->docBlock += yytext;
1576 if (yyextra->docBlockName==&yytext[1])
1577 {
1578 yyextra->docBlockName="";
1579 yyextra->blockString.clear();
1580 yyextra->blockLineNr=-1;
1581 BEGIN(DocBlock);
1582 }
1583 }
1584<DocCopyBlock>[\\@]("endverbatim"|"endiliteral"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endmanonly"|"endrtfonly"|"enddot"|"endmsc"|"enduml"|"endcode")/[^a-z_A-Z0-9] { // end of verbatim block
1585 yyextra->docBlock += yytext;
1586 if (&yytext[4]==yyextra->docBlockName)
1587 {
1588 yyextra->docBlockName="";
1589 yyextra->blockString.clear();
1590 yyextra->blockLineNr=-1;
1591 BEGIN(DocBlock);
1592 }
1593 }
1594
1595<DocCopyBlock>^{B}*{COMM} { // start of a comment line
1596 if (yyextra->docBlockName=="verbatim")
1597 {
1598 REJECT;
1599 }
1600 else
1601 {
1602 QCString indent;
1603 indent.fill(' ',computeIndent(yytext) + 2);
1604 yyextra->docBlock += indent;
1605 }
1606 }
1607<DocCopyBlock>"~~~"[~]* {
1608 QCString pat = yytext;
1609 yyextra->docBlock += pat;
1610 if (yyextra->docBlockName == "~~~" && yyextra->fencedSize==pat.length())
1611 {
1612 yyextra->blockString.clear();
1613 yyextra->blockLineNr=-1;
1614 BEGIN(DocBlock);
1615 }
1616 }
1617<DocCopyBlock>"```"[`]* {
1618 QCString pat = yytext;
1619 yyextra->docBlock += pat;
1620 if (yyextra->docBlockName == "```" && yyextra->fencedSize==pat.length())
1621 {
1622 yyextra->blockString.clear();
1623 yyextra->blockLineNr=-1;
1624 BEGIN(DocBlock);
1625 }
1626 }
1627
1628<DocCopyBlock>[^<@/\*\]!`~"\$\\\n]+ { // any character that is not special
1629 yyextra->docBlock += yytext;
1630 }
1631<DocCopyBlock>\n { // newline
1632 yyextra->docBlock += *yytext;
1633 newLine(yyscanner);
1634 }
1635<DocCopyBlock>. { // any other character
1636 yyextra->docBlock += *yytext;
1637 }
1638
1639 /*-----Prototype parsing -------------------------------------------------------------------------*/
1640<Prototype>{BS}{SUBPROG}{BS_} {
1641 BEGIN(PrototypeSubprog);
1642 }
1643<Prototype,PrototypeSubprog>{BS}{SCOPENAME}?{BS}{ID} {
1644 yyextra->current->name = QCString(yytext).lower();
1645 yyextra->current->name.stripWhiteSpace();
1646 BEGIN(PrototypeArgs);
1647 }
1648<PrototypeArgs>{
1649"("|")"|","|{BS_} { yyextra->current->args += yytext; }
1650{ID} { yyextra->current->args += yytext;
1651 Argument a;
1652 a.name = QCString(yytext).lower();
1653 yyextra->current->argList.push_back(a);
1654 }
1655}
1656
1657 /*------------------------------------------------------------------------------------------------*/
1658
1659<*>"\n" {
1660 newLine(yyscanner);
1661 //if (yyextra->debugStr.stripWhiteSpace().length() > 0) cout << "ignored text: " << yyextra->debugStr << " state: " <<YY_START << endl;
1662 yyextra->debugStr="";
1663 }
1664
1665
1666 /*---- error: EOF in wrong state --------------------------------------------------------------------*/
1667
1668<*><<EOF>> {
1669 if (yyextra->parsingPrototype)
1670 {
1671 yyterminate();
1672 }
1673 else if ( yyextra->includeStackPtr <= 0 )
1674 {
1675 if (YY_START!=INITIAL && YY_START!=Start)
1676 {
1677 DBG_CTX((stderr,"==== Error: EOF reached in wrong state (end missing)"));
1678 scanner_abort(yyscanner);
1679 }
1680 yyterminate();
1681 }
1682 else
1683 {
1684 popBuffer(yyscanner);
1685 }
1686 }
1687<*>{LOG_OPER} { // Fortran logical comparison keywords
1688 }
1689<*>. {
1690 //yyextra->debugStr+=yytext;
1691 //printf("I:%c\n", *yytext);
1692 } // ignore remaining text
1693
1694 /**********************************************************************************/
1695 /**********************************************************************************/
1696 /**********************************************************************************/
1697%%
1698//----------------------------------------------------------------------------
1699
1700static void newLine(yyscan_t yyscanner)
1701{
1702 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1703 yyextra->lineNr++;
1704 yyextra->lineNr+=yyextra->lineCountPrepass;
1705 yyextra->lineCountPrepass=0;
1706 yyextra->comments.clear();
1707}
1708
1709static inline int computeIndent(const char *s)
1710{
1711 int col=0;
1712 int tabSize=Config_getInt(TAB_SIZE);
1713 const char *p=s;
1714 char c = 0;
1715 while ((c=*p++))
1716 {
1717 if (c=='\t') col+=tabSize-(col%tabSize);
1718 else if (c=='\n') col=0;
1719 else col++;
1720 }
1721 return col;
1722}
1723
1724static const CommentInPrepass *locatePrepassComment(yyscan_t yyscanner,int from, int to)
1725{
1726 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1727 //printf("Locate %d-%d\n", from, to);
1728 for (const auto &cip : yyextra->comments)
1729 { // todo: optimize
1730 int c = cip.column;
1731 //printf("Candidate %d\n", c);
1732 if (c>=from && c<=to)
1733 {
1734 // comment for previous variable or parameter
1735 return &cip;
1736 }
1737 }
1738 return nullptr;
1739}
1740
1741static void updateVariablePrepassComment(yyscan_t yyscanner,int from, int to)
1742{
1743 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1744 const CommentInPrepass *c = locatePrepassComment(yyscanner,from, to);
1745 if (c && yyextra->vtype == V_VARIABLE)
1746 {
1747 yyextra->last_entry->brief = c->str;
1748 }
1749 else if (c && yyextra->vtype == V_PARAMETER)
1750 {
1751 Argument *parameter = getParameter(yyscanner,yyextra->argName);
1752 if (parameter) parameter->docs = c->str;
1753 }
1754}
1755
1756static int getAmpersandAtTheStart(const char *buf, int length)
1757{
1758 for(int i=0; i<length; i++)
1759 {
1760 switch(buf[i])
1761 {
1762 case ' ':
1763 case '\t':
1764 break;
1765 case '&':
1766 return i;
1767 default:
1768 return -1;
1769 }
1770 }
1771 return -1;
1772}
1773
1774/* Returns ampersand index, comment start index or -1 if neither exist.*/
1775static int getAmpOrExclAtTheEnd(const char *buf, int length, char ch)
1776{
1777 // Avoid ampersands in string and yyextra->comments
1778 int parseState = Start;
1779 char quoteSymbol = 0;
1780 int ampIndex = -1;
1781 int commentIndex = -1;
1782 quoteSymbol = ch;
1783 if (ch != '\0') parseState = String;
1784
1785 for(int i=0; i<length && parseState!=Comment; i++)
1786 {
1787 // When in string, skip backslashes
1788 // Legacy code, not sure whether this is correct?
1789 if (parseState==String)
1790 {
1791 if (buf[i]=='\\') i++;
1792 }
1793
1794 switch(buf[i])
1795 {
1796 case '\'':
1797 case '"':
1798 // Close string, if quote symbol matches.
1799 // Quote symbol is set iff parseState==String
1800 if (buf[i]==quoteSymbol)
1801 {
1802 parseState = Start;
1803 quoteSymbol = 0;
1804 }
1805 // Start new string, if not already in string or comment
1806 else if (parseState==Start)
1807 {
1808 parseState = String;
1809 quoteSymbol = buf[i];
1810 }
1811 ampIndex = -1; // invalidate prev ampersand
1812 break;
1813 case '!':
1814 // When in string or comment, ignore exclamation mark
1815 if (parseState==Start)
1816 {
1817 parseState = Comment;
1818 commentIndex = i;
1819 }
1820 break;
1821 case ' ': // ignore whitespace
1822 case '\t':
1823 case '\n': // this may be at the end of line
1824 break;
1825 case '&':
1826 ampIndex = i;
1827 break;
1828 default:
1829 ampIndex = -1; // invalidate prev ampersand
1830 }
1831 }
1832
1833 if (ampIndex>=0)
1834 return ampIndex;
1835 else
1836 return commentIndex;
1837}
1838
1839/* Although yyextra->comments at the end of continuation line are grabbed by this function,
1840* we still do not know how to use them later in parsing.
1841*/
1842void truncatePrepass(yyscan_t yyscanner,int index)
1843{
1844 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1845 size_t length = yyextra->inputStringPrepass.length();
1846 for (size_t i=index+1; i<length; i++) {
1847 if (yyextra->inputStringPrepass[i]=='!' && i<length-1 && yyextra->inputStringPrepass[i+1]=='<') { // save comment
1848 yyextra->comments.emplace_back(index, yyextra->inputStringPrepass.right(length-i-2));
1849 }
1850 }
1851 yyextra->inputStringPrepass.resize(index);
1852}
1853
1854/* This function assumes that contents has at least size=length+1 */
1855static void insertCharacter(char *contents, int length, int pos, char c)
1856{
1857 // shift tail by one character
1858 for(int i=length; i>pos; i--)
1859 contents[i]=contents[i-1];
1860 // set the character
1861 contents[pos] = c;
1862}
1863
1864/* change yyextra->comments and bring line continuation character to previous line */
1865/* also used to set continuation marks in case of fortran code usage, done here as it is quite complicated code */
1866const char* prepassFixedForm(const char* contents, int *hasContLine,int fixedCommentAfter)
1867{
1868 int column=0;
1869 int prevLineLength=0;
1870 int prevLineAmpOrExclIndex=-1;
1871 int skipped = 0;
1872 char prevQuote = '\0';
1873 char thisQuote = '\0';
1874 bool emptyLabel=TRUE;
1875 bool commented=FALSE;
1876 bool inSingle=FALSE;
1877 bool inDouble=FALSE;
1878 bool inBackslash=FALSE;
1879 bool fullCommentLine=TRUE;
1880 bool artificialComment=FALSE;
1881 bool spaces=TRUE;
1882 int newContentsSize = (int)strlen(contents)+3; // \000, \n (when necessary) and one spare character (to avoid reallocation)
1883 char* newContents = (char*)malloc(newContentsSize);
1884 int curLine = 1;
1885 size_t sizCont;
1886
1887 int j = -1;
1888 sizCont = strlen(contents);
1889 for(size_t i=0;i<sizCont;i++) {
1890 column++;
1891 char c = contents[i];
1892 if (artificialComment && c != '\n')
1893 {
1894 if (c == '!' && spaces)
1895 {
1896 newContents[j++] = c;
1897 artificialComment = FALSE;
1898 spaces = FALSE;
1899 skipped = 0;
1900 continue;
1901 }
1902 else if (c == ' ' || c == '\t') continue;
1903 else
1904 {
1905 spaces = FALSE;
1906 skipped++;
1907 continue;
1908 }
1909 }
1910
1911 j++;
1912 if (j>=newContentsSize-3) { // check for spare characters, which may be eventually used below (by & and '! ')
1913 newContents = (char*)realloc(newContents, newContentsSize+1000);
1914 newContentsSize = newContentsSize+1000;
1915 }
1916
1917 switch(c) {
1918 case '\n':
1919 if (!fullCommentLine)
1920 {
1921 prevLineLength=column;
1922 prevLineAmpOrExclIndex=getAmpOrExclAtTheEnd(&contents[i-prevLineLength+1], prevLineLength,prevQuote);
1923 if (prevLineAmpOrExclIndex == -1) prevLineAmpOrExclIndex = column - 1;
1924 if (skipped)
1925 {
1926 prevLineAmpOrExclIndex = -1;
1927 skipped = 0;
1928 }
1929 }
1930 else
1931 {
1932 prevLineLength+=column;
1933 /* Even though a full comment line is not really a comment line it can be seen as one. An empty line is also seen as a comment line (small bonus) */
1934 if (hasContLine)
1935 {
1936 hasContLine[curLine - 1] = 1;
1937 }
1938 }
1939 artificialComment=FALSE;
1940 spaces=TRUE;
1941 fullCommentLine=TRUE;
1942 column=0;
1943 emptyLabel=TRUE;
1944 commented=FALSE;
1945 newContents[j]=c;
1946 prevQuote = thisQuote;
1947 curLine++;
1948 break;
1949 case ' ':
1950 case '\t':
1951 newContents[j]=c;
1952 break;
1953 case '\000':
1954 if (hasContLine)
1955 {
1956 free(newContents);
1957 return nullptr;
1958 }
1959 newContents[j]='\000';
1960 newContentsSize = (int)strlen(newContents);
1961 if (newContents[newContentsSize - 1] != '\n')
1962 {
1963 // to be on the safe side
1964 newContents = (char*)realloc(newContents, newContentsSize+2);
1965 newContents[newContentsSize] = '\n';
1966 newContents[newContentsSize + 1] = '\000';
1967 }
1968 return newContents;
1969 case '"':
1970 case '\'':
1971 case '\\':
1972 if ((column <= fixedCommentAfter) && (column!=6) && !commented)
1973 {
1974 // we have some special cases in respect to strings and escaped string characters
1975 fullCommentLine=FALSE;
1976 newContents[j]=c;
1977 if (c == '\\')
1978 {
1979 inBackslash = !inBackslash;
1980 break;
1981 }
1982 else if (c == '\'')
1983 {
1984 if (!inDouble)
1985 {
1986 inSingle = !inSingle;
1987 if (inSingle) thisQuote = c;
1988 else thisQuote = '\0';
1989 }
1990 break;
1991 }
1992 else if (c == '"')
1993 {
1994 if (!inSingle)
1995 {
1996 inDouble = !inDouble;
1997 if (inDouble) thisQuote = c;
1998 else thisQuote = '\0';
1999 }
2000 break;
2001 }
2002 }
2003 inBackslash = FALSE;
2004 // fallthrough
2005 case '#':
2006 case 'C':
2007 case 'c':
2008 case '*':
2009 case '!':
2010 if ((column <= fixedCommentAfter) && (column!=6))
2011 {
2012 emptyLabel=FALSE;
2013 if (column==1)
2014 {
2015 newContents[j]='!';
2016 commented = TRUE;
2017 }
2018 else if ((c == '!') && !inDouble && !inSingle)
2019 {
2020 newContents[j]=c;
2021 commented = TRUE;
2022 }
2023 else
2024 {
2025 if (!commented) fullCommentLine=FALSE;
2026 newContents[j]=c;
2027 }
2028 break;
2029 }
2030 // fallthrough
2031 default:
2032 if (!commented && (column < 6) && ((c - '0') >= 0) && ((c - '0') <= 9))
2033 { // remove numbers, i.e. labels from first 5 positions.
2034 newContents[j]=' ';
2035 }
2036 else if (column==6 && emptyLabel)
2037 { // continuation
2038 if (!commented) fullCommentLine=FALSE;
2039 if (c != '0')
2040 { // 0 not allowed as continuation character, see f95 standard paragraph 3.3.2.3
2041 newContents[j]=' ';
2042
2043 if (prevLineAmpOrExclIndex==-1)
2044 { // add & just before end of previous line
2045 /* first line is not a continuation line in code, just in snippets etc. */
2046 if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-1, '&');
2047 j++;
2048 }
2049 else
2050 { // add & just before end of previous line comment
2051 /* first line is not a continuation line in code, just in snippets etc. */
2052 if (curLine != 1) insertCharacter(newContents, j+1, (j+1)-6-prevLineLength+prevLineAmpOrExclIndex+skipped, '&');
2053 skipped = 0;
2054 j++;
2055 }
2056 if (hasContLine)
2057 {
2058 hasContLine[curLine - 1] = 1;
2059 }
2060 }
2061 else
2062 {
2063 newContents[j]=c; // , just handle like space
2064 }
2065 prevLineLength=0;
2066 }
2067 else if ((column > fixedCommentAfter) && !commented)
2068 {
2069 // first non commented non blank character after position fixedCommentAfter
2070 if (c == '&')
2071 {
2072 newContents[j]=' ';
2073 }
2074 else if (c != '!')
2075 {
2076 // I'm not a possible start of doxygen comment
2077 newContents[j]=' ';
2078 artificialComment = TRUE;
2079 spaces=TRUE;
2080 skipped = 0;
2081 }
2082 else
2083 {
2084 newContents[j]=c;
2085 commented = TRUE;
2086 }
2087 }
2088 else
2089 {
2090 if (!commented) fullCommentLine=FALSE;
2091 newContents[j]=c;
2092 emptyLabel=FALSE;
2093 }
2094 break;
2095 }
2096 }
2097
2098 if (hasContLine)
2099 {
2100 free(newContents);
2101 return nullptr;
2102 }
2103
2104 if (j==-1) // contents was empty
2105 {
2106 newContents = (char*)realloc(newContents, 2);
2107 newContents[0] = '\n';
2108 newContents[1] = '\000';
2109 }
2110 else if (newContents[j] == '\n') // content ended with newline
2111 {
2112 newContents = (char*)realloc(newContents, j+2);
2113 newContents[j + 1] = '\000';
2114 }
2115 else // content did not end with a newline
2116 {
2117 newContents = (char*)realloc(newContents, j+3);
2118 newContents[j + 1] = '\n';
2119 newContents[j + 2] = '\000';
2120 }
2121 return newContents;
2122}
2123
2124static void pushBuffer(yyscan_t yyscanner,const QCString &buffer)
2125{
2126 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2127 if (yyextra->includeStackCnt <= yyextra->includeStackPtr)
2128 {
2129 yyextra->includeStackCnt++;
2130 yyextra->includeStack = (YY_BUFFER_STATE *)realloc(yyextra->includeStack, yyextra->includeStackCnt * sizeof(YY_BUFFER_STATE));
2131 }
2132 yyextra->includeStack[yyextra->includeStackPtr++] = YY_CURRENT_BUFFER;
2133 yy_switch_to_buffer(yy_scan_string(buffer.data(),yyscanner),yyscanner);
2134
2135 DBG_CTX((stderr, "--PUSH--%s", qPrint(buffer)));
2136}
2137
2138static void popBuffer(yyscan_t yyscanner)
2139{
2140 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2141 DBG_CTX((stderr, "--POP--"));
2142 yyextra->includeStackPtr --;
2143 yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner );
2144 yy_switch_to_buffer( yyextra->includeStack[yyextra->includeStackPtr], yyscanner );
2145}
2146
2147/** used to copy entry to an interface module procedure */
2148static void copyEntry(std::shared_ptr<Entry> dest, const std::shared_ptr<Entry> &src)
2149{
2150 dest->type = src->type;
2151 dest->fileName = src->fileName;
2152 dest->startLine = src->startLine;
2153 dest->bodyLine = src->bodyLine;
2154 dest->endBodyLine = src->endBodyLine;
2155 dest->args = src->args;
2156 dest->argList = src->argList;
2157 dest->doc = src->doc;
2158 dest->docLine = src->docLine;
2159 dest->docFile = src->docFile;
2160 dest->brief = src->brief;
2161 dest->briefLine= src->briefLine;
2162 dest->briefFile= src->briefFile;
2163}
2164
2165/** fill empty interface module procedures with info from
2166 corresponding module subprogs
2167
2168 TODO: handle procedures in used modules
2169*/
2170void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root)
2171{
2172 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2173 if (yyextra->moduleProcedures.empty()) return;
2174
2175 // build up map of available functions
2176 std::map<std::string,std::shared_ptr<Entry>> procMap;
2177 {
2178 for (const auto& cf: current_root->children())
2179 {
2180 if (!cf->section.isFunction())
2181 continue;
2182
2183 // remove scope from name
2184 QCString name = cf->name;
2185 {
2186 int end = name.findRev(":");
2187 if (end != -1)
2188 name.remove(0, end+1);
2189 }
2190
2191 procMap.emplace(name.str(), cf);
2192 }
2193 }
2194
2195
2196 // for all module procedures
2197 for (const auto& ce1: yyextra->moduleProcedures)
2198 {
2199 if (procMap.find(ce1->name.str())!=procMap.end())
2200 {
2201 std::shared_ptr<Entry> proc = procMap[ce1->name.str()];
2202 copyEntry(ce1, proc);
2203 }
2204 } // for all interface module procedures
2205 yyextra->moduleProcedures.clear();
2206}
2207
2208/*! Extracts string which resides within parentheses of provided string. */
2210{
2211 QCString extracted = name;
2212 int start = extracted.find("(");
2213 if (start != -1)
2214 {
2215 extracted.remove(0, start+1);
2216 }
2217 int end = extracted.findRev(")");
2218 if (end != -1)
2219 {
2220 size_t length = extracted.length();
2221 extracted.remove(end, length);
2222 }
2223 extracted = extracted.stripWhiteSpace();
2224
2225 return extracted;
2226}
2227
2228/*! remove useless spaces from bind statement */
2229static QCString extractBind(const QCString &name)
2230{
2231 QCString parensPart = extractFromParens(name);
2232 if (parensPart.length() == 1)
2233 {
2234 return "bind(C)";
2235 }
2236 else
2237 {
2238 //strip 'c'
2239 parensPart = parensPart.mid(1).stripWhiteSpace();
2240 // strip ','
2241 parensPart = parensPart.mid(1).stripWhiteSpace();
2242 // name part
2243 parensPart = parensPart.mid(4).stripWhiteSpace();
2244 // = part
2245 parensPart = parensPart.mid(1).stripWhiteSpace();
2246
2247 return "bind(C, name=" + parensPart + ")";
2248 }
2249}
2250
2251/*! Adds passed yyextra->modifiers to these yyextra->modifiers.*/
2253{
2254 if (mdfs.protection!=NONE_P) protection = mdfs.protection;
2255 if (mdfs.direction!=NONE_D) direction = mdfs.direction;
2256 optional |= mdfs.optional;
2257 if (!mdfs.dimension.isEmpty()) dimension = mdfs.dimension;
2258 allocatable |= mdfs.allocatable;
2259 external |= mdfs.external;
2260 intrinsic |= mdfs.intrinsic;
2261 protect |= mdfs.protect;
2262 parameter |= mdfs.parameter;
2263 pointer |= mdfs.pointer;
2264 target |= mdfs.target;
2265 save |= mdfs.save;
2266 deferred |= mdfs.deferred;
2268 nopass |= mdfs.nopass;
2269 pass |= mdfs.pass;
2270 passVar = mdfs.passVar;
2271 bindVar = mdfs.bindVar;
2272 contiguous |= mdfs.contiguous;
2273 volat |= mdfs.volat;
2274 value |= mdfs.value;
2275 return *this;
2276}
2277
2278/*! Extracts and adds passed modifier to these yyextra->modifiers.*/
2280{
2281 QCString mdfString = mdfStringArg.lower();
2282 SymbolModifiers newMdf;
2283
2284 if (mdfString.find("dimension")==0)
2285 {
2286 newMdf.dimension=mdfString;
2287 }
2288 else if (mdfString.contains("intent"))
2289 {
2290 QCString tmp = extractFromParens(mdfString);
2291 bool isin = tmp.contains("in")!=0;
2292 bool isout = tmp.contains("out")!=0;
2293 if (isin && isout) newMdf.direction = SymbolModifiers::INOUT;
2294 else if (isin) newMdf.direction = SymbolModifiers::IN;
2295 else if (isout) newMdf.direction = SymbolModifiers::OUT;
2296 }
2297 else if (mdfString=="public")
2298 {
2300 }
2301 else if (mdfString=="private")
2302 {
2304 }
2305 else if (mdfString=="protected")
2306 {
2307 newMdf.protect = TRUE;
2308 }
2309 else if (mdfString=="optional")
2310 {
2311 newMdf.optional = TRUE;
2312 }
2313 else if (mdfString=="allocatable")
2314 {
2315 newMdf.allocatable = TRUE;
2316 }
2317 else if (mdfString=="external")
2318 {
2319 newMdf.external = TRUE;
2320 }
2321 else if (mdfString=="intrinsic")
2322 {
2323 newMdf.intrinsic = TRUE;
2324 }
2325 else if (mdfString=="parameter")
2326 {
2327 newMdf.parameter = TRUE;
2328 }
2329 else if (mdfString=="pointer")
2330 {
2331 newMdf.pointer = TRUE;
2332 }
2333 else if (mdfString=="target")
2334 {
2335 newMdf.target = TRUE;
2336 }
2337 else if (mdfString=="save")
2338 {
2339 newMdf.save = TRUE;
2340 }
2341 else if (mdfString=="nopass")
2342 {
2343 newMdf.nopass = TRUE;
2344 }
2345 else if (mdfString=="deferred")
2346 {
2347 newMdf.deferred = TRUE;
2348 }
2349 else if (mdfString=="non_overridable")
2350 {
2351 newMdf.nonoverridable = TRUE;
2352 }
2353 else if (mdfString=="contiguous")
2354 {
2355 newMdf.contiguous = TRUE;
2356 }
2357 else if (mdfString=="volatile")
2358 {
2359 newMdf.volat = TRUE;
2360 }
2361 else if (mdfString=="value")
2362 {
2363 newMdf.value = TRUE;
2364 }
2365 else if (mdfString.contains("pass"))
2366 {
2367 newMdf.pass = TRUE;
2368 if (mdfString.contains("("))
2369 newMdf.passVar = extractFromParens(mdfString);
2370 else
2371 newMdf.passVar = "";
2372 }
2373 else if (mdfString.startsWith("bind"))
2374 {
2375 // we need here the original string as we want to don't want to have the lowercase name between the quotes of the name= part
2376 newMdf.bindVar = extractBind(mdfStringArg);
2377 }
2378
2379 (*this) |= newMdf;
2380 return *this;
2381}
2382
2383/*! For debugging purposes. */
2384//ostream& operator<<(ostream& out, const SymbolModifiers& mdfs)
2385//{
2386// out<<mdfs.protection<<", "<<mdfs.direction<<", "<<mdfs.optional<<
2387// ", "<<(mdfs.dimension.isEmpty() ? "" : mdfs.dimension.latin1())<<
2388// ", "<<mdfs.allocatable<<", "<<mdfs.external<<", "<<mdfs.intrinsic;
2389//
2390// return out;
2391//}
2392
2393/*! Find argument with given name in \a subprog entry. */
2394static Argument *findArgument(Entry* subprog, QCString name, bool byTypeName = FALSE)
2395{
2396 QCString cname(name.lower());
2397 for (Argument &arg : subprog->argList)
2398 {
2399 if ((!byTypeName && arg.name.lower() == cname) ||
2400 (byTypeName && arg.type.lower() == cname)
2401 )
2402 {
2403 return &arg;
2404 }
2405 }
2406 return nullptr;
2407}
2408
2409
2410/*! Apply yyextra->modifiers stored in \a mdfs to the \a typeName string. */
2412{
2413 if (!mdfs.dimension.isEmpty())
2414 {
2415 if (!typeName.isEmpty()) typeName += ", ";
2416 typeName += mdfs.dimension;
2417 }
2419 {
2420 if (!typeName.isEmpty()) typeName += ", ";
2421 typeName += directionStrs[mdfs.direction];
2422 }
2423 if (mdfs.optional)
2424 {
2425 if (!typeName.isEmpty()) typeName += ", ";
2426 typeName += "optional";
2427 }
2428 if (mdfs.allocatable)
2429 {
2430 if (!typeName.isEmpty()) typeName += ", ";
2431 typeName += "allocatable";
2432 }
2433 if (mdfs.external)
2434 {
2435 if (!typeName.contains("external"))
2436 {
2437 if (!typeName.isEmpty()) typeName += ", ";
2438 typeName += "external";
2439 }
2440 }
2441 if (mdfs.intrinsic)
2442 {
2443 if (!typeName.isEmpty()) typeName += ", ";
2444 typeName += "intrinsic";
2445 }
2446 if (mdfs.parameter)
2447 {
2448 if (!typeName.isEmpty()) typeName += ", ";
2449 typeName += "parameter";
2450 }
2451 if (mdfs.pointer)
2452 {
2453 if (!typeName.isEmpty()) typeName += ", ";
2454 typeName += "pointer";
2455 }
2456 if (mdfs.target)
2457 {
2458 if (!typeName.isEmpty()) typeName += ", ";
2459 typeName += "target";
2460 }
2461 if (mdfs.save)
2462 {
2463 if (!typeName.isEmpty()) typeName += ", ";
2464 typeName += "save";
2465 }
2466 if (mdfs.deferred)
2467 {
2468 if (!typeName.isEmpty()) typeName += ", ";
2469 typeName += "deferred";
2470 }
2471 if (mdfs.nonoverridable)
2472 {
2473 if (!typeName.isEmpty()) typeName += ", ";
2474 typeName += "non_overridable";
2475 }
2476 if (mdfs.nopass)
2477 {
2478 if (!typeName.isEmpty()) typeName += ", ";
2479 typeName += "nopass";
2480 }
2481 if (mdfs.pass)
2482 {
2483 if (!typeName.isEmpty()) typeName += ", ";
2484 typeName += "pass";
2485 if (!mdfs.passVar.isEmpty())
2486 typeName += "(" + mdfs.passVar + ")";
2487 }
2488 if (!mdfs.bindVar.isEmpty())
2489 {
2490 if (!typeName.isEmpty()) typeName += ", ";
2491 typeName += mdfs.bindVar;
2492 }
2494 {
2495 if (!typeName.isEmpty()) typeName += ", ";
2496 typeName += "public";
2497 }
2498 else if (mdfs.protection == SymbolModifiers::PRIVATE)
2499 {
2500 if (!typeName.isEmpty()) typeName += ", ";
2501 typeName += "private";
2502 }
2503 if (mdfs.protect)
2504 {
2505 if (!typeName.isEmpty()) typeName += ", ";
2506 typeName += "protected";
2507 }
2508 if (mdfs.contiguous)
2509 {
2510 if (!typeName.isEmpty()) typeName += ", ";
2511 typeName += "contiguous";
2512 }
2513 if (mdfs.volat)
2514 {
2515 if (!typeName.isEmpty()) typeName += ", ";
2516 typeName += "volatile";
2517 }
2518 if (mdfs.value)
2519 {
2520 if (!typeName.isEmpty()) typeName += ", ";
2521 typeName += "value";
2522 }
2523
2524 return typeName;
2525}
2526
2527/*! Apply yyextra->modifiers stored in \a mdfs to the \a arg argument. */
2528static void applyModifiers(Argument *arg, const SymbolModifiers& mdfs)
2529{
2530 arg->type = applyModifiers(arg->type, mdfs);
2531}
2532
2533/*! Apply yyextra->modifiers stored in \a mdfs to the \a ent entry. */
2534static void applyModifiers(Entry *ent, const SymbolModifiers& mdfs)
2535{
2536 ent->type = applyModifiers(ent->type, mdfs);
2537
2540 else if (mdfs.protection == SymbolModifiers::PRIVATE)
2542
2543 if (mdfs.nonoverridable)
2544 ent->spec.setFinal(true);
2545 if (mdfs.nopass)
2546 ent->isStatic = TRUE;
2547 if (mdfs.deferred)
2548 ent->virt = Specifier::Pure;
2549}
2550
2551/*! Starts the new scope in fortran program. Consider using this function when
2552 * starting module, interface, function or other program block.
2553 * \see endScope()
2554 */
2555static void startScope(yyscan_t yyscanner,Entry *scope)
2556{
2557 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2558 //cout<<"start scope: "<<scope->name<<endl;
2559 yyextra->current_root= scope; /* start substructure */
2560
2561 yyextra->modifiers.emplace(scope, std::map<std::string,SymbolModifiers>());
2562
2563 // create new current with possibly different defaults...
2564 yyextra->current = std::make_shared<Entry>();
2565 initEntry(yyscanner);
2566}
2567
2568/*! Ends scope in fortran program: may update subprogram arguments or module variable attributes.
2569 * \see startScope()
2570 */
2571static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot)
2572{
2573 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2574 if (yyextra->global_scope == scope)
2575 {
2576 yyextra->global_scope = nullptr;
2577 return TRUE;
2578 }
2579 if (yyextra->global_scope == INVALID_ENTRY)
2580 {
2581 return TRUE;
2582 }
2583 //cout<<"end scope: "<<scope->name<<endl;
2584 if (yyextra->current_root->parent() || isGlobalRoot)
2585 {
2586 yyextra->current_root= yyextra->current_root->parent(); /* end substructure */
2587 }
2588 else // if (yyextra->current_root != scope)
2589 {
2590 fprintf(stderr,"parse error in end <scopename>\n");
2591 scanner_abort(yyscanner);
2592 return FALSE;
2593 }
2594
2595 // create new current with possibly different defaults...
2596 yyextra->current = std::make_shared<Entry>();
2597 initEntry(yyscanner);
2598
2599 // update variables or subprogram arguments with yyextra->modifiers
2600 std::map<std::string,SymbolModifiers>& mdfsMap = yyextra->modifiers[scope];
2601
2602 if (scope->section.isFunction())
2603 {
2604 // iterate all symbol yyextra->modifiers of the scope
2605 for (const auto &kv : mdfsMap)
2606 {
2607 //cout<<it.key()<<": "<<qPrint(it)<<endl;
2608 Argument *arg = findArgument(scope, QCString(kv.first));
2609
2610 if (arg)
2611 {
2612 applyModifiers(arg, kv.second);
2613 }
2614 }
2615
2616 // find return type for function
2617 //cout<<"RETURN NAME "<<yyextra->modifiers[yyextra->current_root][scope->name.lower()].returnName<<endl;
2618 QCString returnName = yyextra->modifiers[yyextra->current_root][scope->name.lower().str()].returnName.lower();
2619 if (yyextra->modifiers[scope].find(returnName.str())!=yyextra->modifiers[scope].end())
2620 {
2621 scope->type = yyextra->modifiers[scope][returnName.str()].type; // returning type works
2622 applyModifiers(scope, yyextra->modifiers[scope][returnName.str()]); // returning array works
2623 }
2624
2625 }
2626 if (scope->section.isClass() && scope->spec.isInterface())
2627 { // was INTERFACE_SEC
2628 if (scope->parent() && scope->parent()->section.isFunction())
2629 { // interface within function
2630 // iterate functions of interface and
2631 // try to find types for dummy(ie. argument) procedures.
2632 //cout<<"Search in "<<scope->name<<endl;
2633 for (const auto &ce : scope->children())
2634 {
2635 if (!ce->section.isFunction())
2636 continue;
2637
2638 // remove prefix
2639 QCString name = ce->name.lower();
2640 int ii = name.findRev(":");
2641 if (ii != -1)
2642 {
2643 name.remove(0, ii+1);
2644 }
2645 Argument *arg = findArgument(scope->parent(), name);
2646 if (arg)
2647 {
2648 // set type of dummy procedure argument to interface
2649 arg->type = "external " + ce->type + "(";
2650 for (unsigned int i=0; i<ce->argList.size(); i++)
2651 {
2652 if (i > 0)
2653 {
2654 arg->type = arg->type + ", ";
2655 }
2656 const Argument &subarg = ce->argList.at(i);
2657 arg->type = arg->type + subarg.type + " " + subarg.name;
2658 }
2659 arg->type = arg->type + ")";
2660 arg->name = name;
2661 }
2662 }
2663 // clear all yyextra->modifiers of the scope
2664 yyextra->modifiers.erase(scope);
2665 scope->parent()->removeSubEntry(scope);
2666 scope = nullptr;
2667 return TRUE;
2668 }
2669 }
2670 if (!scope->section.isFunction())
2671 { // not function section
2672 // iterate variables: get and apply yyextra->modifiers
2673 for (const auto &ce : scope->children())
2674 {
2675 if (!ce->section.isVariable() && !ce->section.isFunction() && !ce->section.isClass())
2676 continue;
2677
2678 //cout<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl;
2679 if (mdfsMap.find(ce->name.lower().str())!=mdfsMap.end())
2680 applyModifiers(ce.get(), mdfsMap[ce->name.lower().str()]);
2681
2682 // remove prefix for variable names
2683 if (ce->section.isVariable() || ce->section.isFunction())
2684 {
2685 int end = ce->name.findRev(":");
2686 if (end != -1)
2687 ce->name.remove(0, end+1);
2688 }
2689 }
2690 }
2691
2692 // clear all yyextra->modifiers of the scope
2693 yyextra->modifiers.erase(scope);
2694
2695 // resolve procedures in types
2697
2698 return TRUE;
2699}
2700
2701/*! search for types with type bound procedures (e.g. methods)
2702 * and try to resolve their arguments
2703 */
2705{
2706 // map of all subroutines/functions
2707 bool procMapCreated = false;
2708 std::unordered_map<std::string,std::shared_ptr<Entry>> procMap;
2709
2710 // map of all abstract interfaces
2711 bool interfMapCreated = false;
2712 std::unordered_map<std::string,std::shared_ptr<Entry>> interfMap;
2713
2714 // iterate over all types
2715 for (const auto &ce: scope->children())
2716 {
2717 if (!ce->section.isClass())
2718 continue;
2719
2720 // handle non-"generic" non-"deferred" methods, copying the arguments from the implementation
2721 std::unordered_map<std::string,std::shared_ptr<Entry>> methodMap;
2722 for (auto &ct: ce->children())
2723 {
2724 if (!ct->section.isFunction())
2725 continue;
2726
2727 if (ct->type=="generic")
2728 continue;
2729
2730 if (ct->virt==Specifier::Pure)
2731 continue;
2732
2733 // set up the procMap
2734 if (!procMapCreated)
2735 {
2736 for (const auto &cf: scope->children())
2737 {
2738 if (cf->section.isFunction())
2739 {
2740 procMap.emplace(cf->name.str(), cf);
2741 }
2742 }
2743 procMapCreated = true;
2744 }
2745
2746 // found a (non-generic) method
2747 QCString implName = ct->args;
2748 if (procMap.find(implName.str())!=procMap.end())
2749 {
2750 std::shared_ptr<Entry> proc = procMap[implName.str()];
2751 ct->args = proc->args;
2752 ct->argList = ArgumentList(proc->argList);
2753 if (ct->brief.isEmpty())
2754 {
2755 ct->brief = proc->brief;
2756 ct->briefLine = proc->briefLine;
2757 ct->briefFile = proc->briefFile;
2758 }
2759 if (ct->doc.isEmpty())
2760 {
2761 ct->doc = proc->doc;
2762 ct->docLine = proc->docLine;
2763 ct->docFile = proc->docFile;
2764 }
2765 methodMap.emplace(ct->name.str(), ct);
2766 }
2767 }
2768
2769 // handle "deferred" methods (pure virtual functions), duplicating with arguments from the target abstract interface
2770 for (auto &ct: ce->children())
2771 {
2772 if (!ct->section.isFunction())
2773 continue;
2774
2775 if (ct->virt != Specifier::Pure)
2776 continue;
2777
2778 // set up the procMap
2779 if (!interfMapCreated)
2780 {
2781 for(const auto &cf: scope->children())
2782 {
2783 if (cf->section.isClass() && cf->spec.isInterface() && cf->type=="abstract")
2784 {
2785 std::shared_ptr<Entry> ci = cf->children().front();
2786 interfMap.emplace(ci->name.str(), ci);
2787 }
2788 }
2789 interfMapCreated = true;
2790 }
2791
2792 // found a (non-generic) method
2793 QCString implName = ct->args;
2794 if (interfMap.find(implName.str())!= interfMap.end() )
2795 {
2796 std::shared_ptr<Entry> proc = interfMap[implName.str()];
2797 ct->args = proc->args;
2798 ct->argList = ArgumentList(proc->argList);
2799 if (ct->brief.isEmpty())
2800 {
2801 ct->brief = proc->brief;
2802 ct->briefLine = proc->briefLine;
2803 ct->briefFile = proc->briefFile;
2804 }
2805 if (ct->doc.isEmpty())
2806 {
2807 ct->doc = proc->doc;
2808 ct->docLine = proc->docLine;
2809 ct->docFile = proc->docFile;
2810 }
2811
2812 methodMap.emplace(ct->name.str(), ct);
2813 }
2814 }
2815
2816 // handle "generic" methods (that is function overloading!), duplicating with arguments from the target method of the type
2817 {
2818 for (auto &ct: ce->children())
2819 {
2820 if (!ct->section.isFunction())
2821 continue;
2822
2823 if (ct->type!="generic")
2824 continue;
2825
2826 // found a generic method (already duplicated for each entry by the parser)
2827 QCString methodName = ct->args;
2828 if (methodMap.find(methodName.str()) != methodMap.end())
2829 {
2830 std::shared_ptr<Entry> method = methodMap[methodName.str()];
2831 ct->args = method->args;
2832 ct->argList = ArgumentList(method->argList);
2833 if (ct->brief.isEmpty())
2834 {
2835 ct->brief = method->brief;
2836 ct->briefLine = method->briefLine;
2837 ct->briefFile = method->briefFile;
2838 }
2839 if (ct->doc.isEmpty())
2840 {
2841 ct->doc = method->doc;
2842 ct->docLine = method->docLine;
2843 ct->docFile = method->docFile;
2844 }
2845 }
2846 }
2847 }
2848 }
2849}
2850
2851static int yyread(yyscan_t yyscanner,char *buf,int max_size)
2852{
2853 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2854 int c=0;
2855 while ( c < max_size && yyextra->inputString[yyextra->inputPosition] )
2856 {
2857 *buf = yyextra->inputString[yyextra->inputPosition++] ;
2858 c++; buf++;
2859 }
2860 return c;
2861}
2862
2863static void initParser(yyscan_t yyscanner)
2864{
2865 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2866 yyextra->last_entry.reset();
2867}
2868
2869static void initEntry(yyscan_t yyscanner)
2870{
2871 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2872 if (yyextra->typeMode)
2873 {
2874 yyextra->current->protection = yyextra->typeProtection;
2875 }
2876 else if (yyextra->current_root && yyextra->current_root->section.isClass() && yyextra->current_root->spec.isInterface())
2877 {
2878 yyextra->current->protection = Protection::Public;
2879 }
2880 else if (yyextra->current_root && yyextra->current_root->section.isFunction())
2881 {
2882 yyextra->current->protection = Protection::Private;
2883 }
2884 else
2885 {
2886 yyextra->current->protection = yyextra->defaultProtection;
2887 }
2888 yyextra->current->mtype = MethodTypes::Method;
2889 yyextra->current->virt = Specifier::Normal;
2890 yyextra->current->isStatic = false;
2891 yyextra->current->lang = SrcLangExt::Fortran;
2892 yyextra->commentScanner.initGroupInfo(yyextra->current.get());
2893}
2894
2895/**
2896 adds yyextra->current entry to yyextra->current_root and creates new yyextra->current
2897*/
2898static void addCurrentEntry(yyscan_t yyscanner,bool case_insens)
2899{
2900 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2901 if (case_insens) yyextra->current->name = yyextra->current->name.lower();
2902 //printf("===Adding entry %s to %s\n", qPrint(yyextra->current->name), qPrint(yyextra->current_root->name));
2903 yyextra->last_entry = yyextra->current;
2904 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
2905 initEntry(yyscanner);
2906}
2907
2908static void addModule(yyscan_t yyscanner,const QCString &name, bool isModule)
2909{
2910 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2911 DBG_CTX((stderr, "0=========> got module %s\n", qPrint(name)));
2912
2913 if (isModule)
2914 yyextra->current->section = EntryType::makeNamespace();
2915 else
2916 yyextra->current->section = EntryType::makeFunction();
2917
2918 if (!name.isEmpty())
2919 {
2920 yyextra->current->name = name;
2921 }
2922 else
2923 {
2924 QCString fname = yyextra->fileName;
2925 int index = std::max(fname.findRev('/'), fname.findRev('\\'));
2926 fname = fname.right(fname.length()-index-1);
2927 if (yyextra->mainPrograms) fname += "__" + QCString().setNum(yyextra->mainPrograms);
2928 yyextra->mainPrograms++;
2929 fname = fname.prepend("__").append("__");
2930 yyextra->current->name = substitute(fname, ".", "_");
2931 }
2932 yyextra->current->type = "program";
2933 yyextra->current->fileName = yyextra->fileName;
2934 yyextra->current->bodyLine = yyextra->lineNr; // used for source reference
2935 yyextra->current->startLine = yyextra->lineNr;
2936 yyextra->current->protection = Protection::Public ;
2937 addCurrentEntry(yyscanner,true);
2938 startScope(yyscanner,yyextra->last_entry.get());
2939}
2940
2941
2942static void addSubprogram(yyscan_t yyscanner,const QCString &text)
2943{
2944 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2945 DBG_CTX((stderr,"1=========> got subprog, type: %s\n",qPrint(text)));
2946 yyextra->subrCurrent.push_back(yyextra->current);
2947 yyextra->current->section = EntryType::makeFunction();
2948 QCString subtype = text; subtype=subtype.lower().stripWhiteSpace();
2949 yyextra->functionLine = (subtype.find("function") != -1);
2950 yyextra->current->type += " " + subtype;
2951 yyextra->current->type = yyextra->current->type.stripWhiteSpace();
2952 if (yyextra->ifType == IF_ABSTRACT)
2953 {
2954 yyextra->current->virt = Specifier::Virtual;
2955 }
2956 yyextra->current->fileName = yyextra->fileName;
2957 yyextra->current->bodyLine = yyextra->lineNr; // used for source reference start of body of routine
2958 yyextra->current->startLine = yyextra->lineNr; // used for source reference start of definition
2959 yyextra->current->args.clear();
2960 yyextra->current->argList.clear();
2961 yyextra->docBlock.clear();
2962}
2963
2964/*! Adds interface to the root entry.
2965 * \note Code was brought to this procedure from the parser,
2966 * because there was/is idea to use it in several parts of the parser.
2967 */
2968static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type)
2969{
2970 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
2971 if (YY_START == Start)
2972 {
2973 addModule(yyscanner);
2974 yy_push_state(ModuleBody,yyscanner); //anon program
2975 }
2976
2977 yyextra->current->section = EntryType::makeClass(); // was EntryType::Interface;
2978 yyextra->current->spec = TypeSpecifier().setInterface(true);
2979 yyextra->current->name = name;
2980
2981 switch (type)
2982 {
2983 case IF_ABSTRACT:
2984 yyextra->current->type = "abstract";
2985 break;
2986
2987 case IF_GENERIC:
2988 yyextra->current->type = "generic";
2989 break;
2990
2991 case IF_SPECIFIC:
2992 case IF_NONE:
2993 default:
2994 yyextra->current->type = "";
2995 }
2996
2997 /* if type is part of a module, mod name is necessary for output */
2998 if ((yyextra->current_root) &&
2999 (yyextra->current_root->section.isClass() ||
3000 yyextra->current_root->section.isNamespace()))
3001 {
3002 yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name;
3003 }
3004
3005 yyextra->current->fileName = yyextra->fileName;
3006 yyextra->current->bodyLine = yyextra->lineNr;
3007 yyextra->current->startLine = yyextra->lineNr;
3008 addCurrentEntry(yyscanner,true);
3009}
3010
3011
3012//-----------------------------------------------------------------------------
3013
3014/*! Get the argument \a name.
3015 */
3016static Argument *getParameter(yyscan_t yyscanner,const QCString &name)
3017{
3018 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3019 // std::cout<<"addFortranParameter(): "<<name<<" DOCS:"<<(docs.isEmpty()?QCString("null"):docs)<<"\n";
3020 Argument *ret = nullptr;
3021 for (Argument &a:yyextra->current_root->argList)
3022 {
3023 if (a.name.lower()==name.lower())
3024 {
3025 ret=&a;
3026 //printf("parameter found: %s\n",(const char*)name);
3027 break;
3028 }
3029 } // for
3030 return ret;
3031}
3032
3033 //----------------------------------------------------------------------------
3034static void startCommentBlock(yyscan_t yyscanner,bool brief)
3035{
3036 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3037 if (brief)
3038 {
3039 yyextra->current->briefFile = yyextra->fileName;
3040 yyextra->current->briefLine = yyextra->lineNr;
3041 }
3042 else
3043 {
3044 yyextra->current->docFile = yyextra->fileName;
3045 yyextra->current->docLine = yyextra->lineNr;
3046 }
3047}
3048
3049//----------------------------------------------------------------------------
3050
3051static void handleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief)
3052{
3053 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3054 bool hideInBodyDocs = Config_getBool(HIDE_IN_BODY_DOCS);
3055 if (yyextra->docBlockInBody && hideInBodyDocs)
3056 {
3057 yyextra->docBlockInBody = FALSE;
3058 return;
3059 }
3060 DBG_CTX((stderr,"call parseCommentBlock [%s]\n",qPrint(doc)));
3061 int lineNr = brief ? yyextra->current->briefLine : yyextra->current->docLine;
3062 int position=0;
3063 bool needsEntry = FALSE;
3064 Markdown markdown(yyextra->fileName,lineNr);
3065 GuardedSectionStack guards;
3066 QCString strippedDoc = stripIndentation(doc);
3067 QCString processedDoc = Config_getBool(MARKDOWN_SUPPORT) ? markdown.process(strippedDoc,lineNr) : strippedDoc;
3068 while (yyextra->commentScanner.parseCommentBlock(
3069 yyextra->thisParser,
3070 yyextra->docBlockInBody ? yyextra->subrCurrent.back().get() : yyextra->current.get(),
3071 processedDoc, // text
3072 yyextra->fileName, // file
3073 lineNr,
3074 yyextra->docBlockInBody ? FALSE : brief,
3075 yyextra->docBlockInBody ? FALSE : yyextra->docBlockJavaStyle,
3076 yyextra->docBlockInBody,
3077 yyextra->defaultProtection,
3078 position,
3079 needsEntry,
3080 Config_getBool(MARKDOWN_SUPPORT),
3081 &guards
3082 ))
3083 {
3084 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
3085 if (needsEntry) addCurrentEntry(yyscanner,false);
3086 }
3087 DBG_CTX((stderr,"parseCommentBlock position=%d [%s] needsEntry=%d\n",position,doc.data()+position,needsEntry));
3088
3089 if (needsEntry) addCurrentEntry(yyscanner,false);
3090 yyextra->docBlockInBody = FALSE;
3091}
3092
3093//----------------------------------------------------------------------------
3094/// Handle parameter description as defined after the declaration of the parameter
3095static void subrHandleCommentBlock(yyscan_t yyscanner,const QCString &doc,bool brief)
3096{
3097 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3098 QCString loc_doc;
3099 loc_doc = doc.stripWhiteSpace();
3100
3101 std::shared_ptr<Entry> tmp_entry = yyextra->current;
3102 yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function
3103
3104 // Still in the specification section so no inbodyDocs yet, but parameter documentation
3105 yyextra->current->inbodyDocs = "";
3106
3107 // strip \\param or @param, so we can do some extra checking. We will add it later on again.
3108 if (!loc_doc.stripPrefix("\\param") &&
3109 !loc_doc.stripPrefix("@param")
3110 ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning
3111 loc_doc.stripWhiteSpace();
3112
3113 // direction as defined with the declaration of the parameter
3114 int dir1 = yyextra->modifiers[yyextra->current_root][yyextra->argName.lower().str()].direction;
3115 // in description [in] is specified
3116 if (loc_doc.lower().find(directionParam[SymbolModifiers::IN]) == 0)
3117 {
3118 // check if with the declaration intent(in) or nothing has been specified
3121 {
3122 // strip direction
3123 loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::IN]));
3124 loc_doc.stripWhiteSpace();
3125 // in case of empty documentation or (now) just name, consider it as no documentation
3126 if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
3127 {
3128 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::IN] + " " +
3129 yyextra->argName + " " + loc_doc,brief);
3130 }
3131 }
3132 else
3133 {
3134 // something different specified, give warning and leave error.
3135 warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
3136 qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
3137 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
3138 yyextra->argName + " " + loc_doc,brief);
3139 }
3140 }
3141 // analogous to the [in] case, here [out] direction specified
3142 else if (loc_doc.lower().find(directionParam[SymbolModifiers::OUT]) == 0)
3143 {
3146 {
3147 loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::OUT]));
3148 loc_doc.stripWhiteSpace();
3149 if (loc_doc.isEmpty() || (loc_doc.lower() == yyextra->argName.lower()))
3150 {
3151 yyextra->current = tmp_entry;
3152 return;
3153 }
3154 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::OUT] + " " +
3155 yyextra->argName + " " + loc_doc,brief);
3156 }
3157 else
3158 {
3159 warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
3160 qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
3161 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
3162 yyextra->argName + " " + loc_doc,brief);
3163 }
3164 }
3165 // analogous to the [in] case, here [in,out] direction specified
3166 else if (loc_doc.lower().find(directionParam[SymbolModifiers::INOUT]) == 0)
3167 {
3170 {
3171 loc_doc = loc_doc.right(loc_doc.length()-(int)strlen(directionParam[SymbolModifiers::INOUT]));
3172 loc_doc.stripWhiteSpace();
3173 if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
3174 {
3175 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[SymbolModifiers::INOUT] + " " +
3176 yyextra->argName + " " + loc_doc,brief);
3177 }
3178 }
3179 else
3180 {
3181 warn(yyextra->fileName,yyextra->lineNr, "Routine: %s%s inconsistency between intent attribute and documentation for parameter %s:",
3182 qPrint(yyextra->current->name),qPrint(yyextra->current->args),qPrint(yyextra->argName));
3183 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
3184 yyextra->argName + " " + loc_doc,brief);
3185 }
3186 }
3187 // analogous to the [in] case; here no direction specified
3188 else if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
3189 {
3190 handleCommentBlock(yyscanner,QCString("\n\n@param ") + directionParam[dir1] + " " +
3191 yyextra->argName + " " + loc_doc,brief);
3192 }
3193
3194 // reset yyextra->current back to the part inside the routine
3195 yyextra->current = tmp_entry;
3196}
3197//----------------------------------------------------------------------------
3198/// Handle result description as defined after the declaration of the parameter
3199static void subrHandleCommentBlockResult(yyscan_t yyscanner,const QCString &doc,bool brief)
3200{
3201 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3202 QCString loc_doc;
3203 loc_doc = doc.stripWhiteSpace();
3204
3205 std::shared_ptr<Entry> tmp_entry = yyextra->current;
3206 yyextra->current = yyextra->subrCurrent.back(); // temporarily switch to the entry of the subroutine / function
3207
3208 // Still in the specification section so no inbodyDocs yet, but parameter documentation
3209 yyextra->current->inbodyDocs = "";
3210
3211 // strip \\returns or @returns. We will add it later on again.
3212 if (!loc_doc.stripPrefix("\\returns") &&
3213 !loc_doc.stripPrefix("\\return") &&
3214 !loc_doc.stripPrefix("@returns") &&
3215 !loc_doc.stripPrefix("@return")
3216 ) (void)loc_doc; // Do nothing work has been done by stripPrefix; (void)loc_doc: to overcome 'empty controlled statement' warning
3217 loc_doc.stripWhiteSpace();
3218
3219 if (!loc_doc.isEmpty() && (loc_doc.lower() != yyextra->argName.lower()))
3220 {
3221 handleCommentBlock(yyscanner,QCString("\n\n@returns ") + loc_doc,brief);
3222 }
3223
3224 // reset yyextra->current back to the part inside the routine
3225 yyextra->current = std::move(tmp_entry);
3226}
3227
3228//----------------------------------------------------------------------------
3229
3230static void parseMain(yyscan_t yyscanner, const QCString &fileName,const char *fileBuf,
3231 const std::shared_ptr<Entry> &rt, FortranFormat format)
3232{
3233 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3234 char *tmpBuf = nullptr;
3235 initParser(yyscanner);
3236
3237 if (fileBuf==nullptr || fileBuf[0]=='\0') return;
3238
3239 yyextra->defaultProtection = Protection::Public;
3240 yyextra->inputString = fileBuf;
3241 yyextra->inputPosition = 0;
3242 yyextra->inputStringPrepass = nullptr;
3243 yyextra->inputPositionPrepass = 0;
3244
3245 //yyextra->anonCount = 0; // don't reset per file
3246 yyextra->current_root = rt.get();
3247 yyextra->global_root = rt;
3248
3249 yyextra->isFixedForm = recognizeFixedForm(fileBuf,format);
3250
3251 if (yyextra->isFixedForm)
3252 {
3253 yyextra->fixedCommentAfter = Config_getInt(FORTRAN_COMMENT_AFTER);
3254 msg("Prepassing fixed form of %s\n", qPrint(fileName));
3255 //printf("---strlen=%d\n", strlen(fileBuf));
3256 //clock_t start=clock();
3257
3258 //printf("Input fixed form string:\n%s\n", fileBuf);
3259 //printf("===========================\n");
3260 yyextra->inputString = prepassFixedForm(fileBuf, nullptr,yyextra->fixedCommentAfter);
3261 Debug::print(Debug::FortranFixed2Free,0,"======== Fixed to Free format =========\n---- Input fixed form string ------- \n%s\n", fileBuf);
3262 Debug::print(Debug::FortranFixed2Free,0,"---- Resulting free form string ------- \n%s\n", yyextra->inputString);
3263 //printf("Resulting free form string:\n%s\n", yyextra->inputString);
3264 //printf("===========================\n");
3265
3266 //clock_t end=clock();
3267 //printf("CPU time used=%f\n", ((double) (end-start))/CLOCKS_PER_SEC);
3268 }
3269 else if (yyextra->inputString[strlen(fileBuf)-1] != '\n')
3270 {
3271 tmpBuf = (char *)malloc(strlen(fileBuf)+2);
3272 strcpy(tmpBuf,fileBuf);
3273 tmpBuf[strlen(fileBuf)]= '\n';
3274 tmpBuf[strlen(fileBuf)+1]= '\000';
3275 yyextra->inputString = tmpBuf;
3276 }
3277
3278 yyextra->lineNr= 1 ;
3279 yyextra->fileName = fileName;
3280 msg("Parsing file %s...\n",qPrint(yyextra->fileName));
3281
3282 yyextra->global_scope = rt.get();
3283 startScope(yyscanner,rt.get()); // implies yyextra->current_root = rt
3284 initParser(yyscanner);
3285 yyextra->commentScanner.enterFile(yyextra->fileName,yyextra->lineNr);
3286
3287 // add entry for the file
3288 yyextra->current = std::make_shared<Entry>();
3289 yyextra->current->lang = SrcLangExt::Fortran;
3290 yyextra->current->name = yyextra->fileName;
3291 yyextra->current->section = EntryType::makeSource();
3292 yyextra->file_root = yyextra->current;
3293 yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current);
3294 yyextra->current->lang = SrcLangExt::Fortran;
3295
3296 fortranscannerYYrestart( nullptr, yyscanner );
3297 {
3298 BEGIN( Start );
3299 }
3300
3301 fortranscannerYYlex(yyscanner);
3302 yyextra->commentScanner.leaveFile(yyextra->fileName,yyextra->lineNr);
3303
3304 if (yyextra->global_scope && yyextra->global_scope != INVALID_ENTRY)
3305 {
3306 endScope(yyscanner,yyextra->current_root, TRUE); // TRUE - global root
3307 }
3308
3309 //debugCompounds(rt); //debug
3310
3311 rt->program.str(std::string());
3312 //delete yyextra->current; yyextra->current=0;
3313 yyextra->moduleProcedures.clear();
3314 if (tmpBuf)
3315 {
3316 free((char*)tmpBuf);
3317 yyextra->inputString=nullptr;
3318 }
3319 if (yyextra->isFixedForm)
3320 {
3321 free((char*)yyextra->inputString);
3322 yyextra->inputString=nullptr;
3323 }
3324
3325}
3326
3327//----------------------------------------------------------------------------
3328
3330{
3332 fortranscannerYY_state extra;
3335 {
3336 fortranscannerYYlex_init_extra(&extra,&yyscanner);
3337#ifdef FLEX_DEBUG
3338 fortranscannerYYset_debug(Debug::isFlagSet(Debug::Lex_fortranscanner) ? 1 : 0,yyscanner);
3339#endif
3340 }
3341};
3342
3344 : p(std::make_unique<Private>(format))
3345{
3346}
3347
3349{
3350 fortranscannerYYlex_destroy(p->yyscanner);
3351}
3352
3354 const char *fileBuf,
3355 const std::shared_ptr<Entry> &root,
3356 ClangTUParser * /*clangParser*/)
3357{
3358 struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
3359 yyextra->thisParser = this;
3360
3361 DebugLex debugLex(Debug::Lex_fortranscanner, __FILE__, qPrint(fileName));
3362
3363 ::parseMain(p->yyscanner,fileName,fileBuf,root,p->format);
3364}
3365
3367{
3368 return extension!=extension.lower(); // use preprocessor only for upper case extensions
3369}
3370
3372{
3373 struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
3374 pushBuffer(p->yyscanner,text);
3375 yyextra->parsingPrototype = TRUE;
3376 BEGIN(Prototype);
3377 fortranscannerYYlex(p->yyscanner);
3378 yyextra->parsingPrototype = FALSE;
3379 popBuffer(p->yyscanner);
3380}
3381
3382//----------------------------------------------------------------------------
3383
3384static void scanner_abort(yyscan_t yyscanner)
3385{
3386 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3387 fprintf(stderr,"********************************************************************\n");
3388 if (yyextra->blockLineNr == -1)
3389 {
3390 fprintf(stderr,"Error in file %s line: %d, state: %d(%s)\n",
3391 qPrint(yyextra->fileName),yyextra->lineNr,YY_START,stateToString(YY_START));
3392 }
3393 else
3394 {
3395 fprintf(stderr,"Error in file %s line: %d, state: %d(%s), starting command: '%s' probable line reference: %d\n",
3396 qPrint(yyextra->fileName),yyextra->lineNr,YY_START,stateToString(YY_START),qPrint(yyextra->blockString),yyextra->blockLineNr);
3397 }
3398 fprintf(stderr,"********************************************************************\n");
3399
3400 bool start=FALSE;
3401
3402 for (const auto &ce : yyextra->global_root->children())
3403 {
3404 if (ce == yyextra->file_root) start=TRUE;
3405 if (start) ce->reset();
3406 }
3407
3408 // dummy call to avoid compiler warning
3409 (void)yy_top_state(yyscanner);
3410
3411 return;
3412 //exit(-1);
3413}
3414
3415static inline void pop_state(yyscan_t yyscanner)
3416{
3417 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
3418 if ( yyg->yy_start_stack_ptr <= 0 )
3419 warn(yyextra->fileName,yyextra->lineNr,"Unexpected statement '%s'",yytext );
3420 else
3421 yy_pop_state(yyscanner);
3422}
3423//----------------------------------------------------------------------------
3424
3425#include "fortranscanner.l.h"
This class represents an function or template argument list.
Definition arguments.h:60
Clang parser object for a single translation unit, which consists of a source file and the directly o...
Definition clangparser.h:25
@ Lex_fortranscanner
Definition debug.h:60
@ FortranFixed2Free
Definition debug.h:39
static void print(DebugMask mask, int prio, const char *fmt,...)
Definition debug.cpp:81
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:135
const std::vector< std::shared_ptr< Entry > > & children() const
Definition entry.h:139
Entry * parent() const
Definition entry.h:134
QCString type
member type
Definition entry.h:173
ArgumentList argList
member arguments as a list
Definition entry.h:194
QCString name
member name
Definition entry.h:174
EntryType section
entry type (see Sections);
Definition entry.h:172
Specifier virt
virtualness of the entry
Definition entry.h:191
Protection protection
class protection
Definition entry.h:180
bool isStatic
static ?
Definition entry.h:185
TypeSpecifier spec
class/member specifiers
Definition entry.h:182
void removeSubEntry(const Entry *e)
Definition entry.cpp:170
bool needsPreprocessing(const QCString &extension) const override
Returns TRUE if the language identified by extension needs the C preprocessor to be run before feed t...
std::unique_ptr< Private > p
void parsePrototype(const QCString &text) override
Callback function called by the comment block scanner.
~FortranOutlineParser() override
FortranOutlineParser(FortranFormat format=FortranFormat::Unknown)
void parseInput(const QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser) override
Parses a single input file with the goal to build an Entry tree.
Helper class to process markdown formatted text.
Definition markdown.h:32
QCString process(const QCString &input, int &startNewlines, bool fromParseInput=false)
QCString & prepend(const char *s)
Definition qcstring.h:407
bool startsWith(const char *s) const
Definition qcstring.h:492
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
QCString & setNum(short n)
Definition qcstring.h:444
QCString & append(char c)
Definition qcstring.h:381
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:91
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:159
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:143
bool stripPrefix(const QCString &prefix)
Definition qcstring.h:198
void clear()
Definition qcstring.h:169
Wrapper class for a number of boolean properties.
Definition types.h:492
static const char * stateToString(int state)
std::stack< GuardedSection > GuardedSectionStack
Definition commentscan.h:48
#define Config_getInt(name)
Definition config.h:34
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
const char * prepassFixedForm(const char *contents, int *hasContLine, int fixedCommentAfter)
#define DBG_CTX(x)
const char * prepassFixedForm(const char *contents, int *hasContLine, int fixedCommentAfter)
static void parseMain(yyscan_t yyscanner, const QCString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &rt, FortranFormat format)
static void initParser(yyscan_t yyscanner)
static QCString applyModifiers(QCString typeName, const SymbolModifiers &mdfs)
static Argument * findArgument(Entry *subprog, QCString name, bool byTypeName=FALSE)
static void insertCharacter(char *contents, int length, int pos, char c)
void msg(const char *fmt,...)
Definition message.cpp:98
QCString docs
Definition arguments.h:42
fortranscannerYY_state extra
@ Virtual
Definition types.h:29
@ Pure
Definition types.h:29
FortranFormat
Definition types.h:410
bool recognizeFixedForm(const QCString &contents, FortranFormat format)
Definition util.cpp:6670