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