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