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