Doxygen
Loading...
Searching...
No Matches
commentcnv.l
Go to the documentation of this file.
1/*****************************************************************************
2 *
3 * Copyright (C) 1997-2023 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16%{
17 // redefine buffer size (defaults to 16Kb) to avoid a 'flex scanner push-back overflow' error
18 #undef YY_BUF_SIZE
19 #define YY_BUF_SIZE 10*1024*1024 // 10Mb should be sufficient for a source file
20%}
21
22%option never-interactive
23%option prefix="commentcnvYY"
24%option reentrant
25%option extra-type="struct commentcnvYY_state *"
26%top{
27#include <stdint.h>
28// forward declare yyscan_t to improve type safety
29#define YY_TYPEDEF_YY_SCANNER_T
30struct yyguts_t;
31typedef yyguts_t *yyscan_t;
yyguts_t * yyscan_t
Definition code.l:24
32}
33
34%{
35
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <stack>
40#include <algorithm>
41#include <deque>
42#include <string_view>
43
44#include "debug.h"
45#include "message.h"
46#include "config.h"
47#include "doxygen.h"
48#include "util.h"
49#include "aliases.h"
50#include "condparser.h"
51#include "fileinfo.h"
52#include "stringutil.h"
53#include "regex.h"
54#include "section.h"
55
56#include <assert.h>
57
58#define YY_NO_INPUT 1
59#define YY_NO_UNISTD_H 1
60
62{
63 commentcnvYY_CondCtx(int line,const QCString &id,bool b)
64 : lineNr(line),sectionId(id), skip(b) {}
65 int lineNr;
67 bool skip;
68};
69
71{
72 CommentCtx(int line)
73 : lineNr(line) {}
74 int lineNr;
75};
76
93
95{
96 commentcnvYY_state(const std::string *i,std::string &o) : inBuf(i), outBuf(o) {}
97 const std::string *inBuf;
98 std::string &outBuf;
99 int inBufPos = 0;
100 int col = 0;
101 int blockHeadCol = 0; // column at which the start of a special comment block was found
102 int insertCommentCol = 0; // column at which an include or snippet command was found
104 int readLineCtx = 0;
105 int includeCtx = 0;
106 int raiseLevel = 0;
110 bool skip = FALSE;
112 int lineNr = 0;
113 int condCtx = 0;
114 std::stack<commentcnvYY_CondCtx> condStack;
115 std::stack<int> commentStack;
121 int charContext = 0;
122 int javaBlock = 0;
124 bool inVerbatim = false;
126 bool firstIncludeLine = false;
130 int blockCount = 0;
136 bool vhdl = FALSE; // for VHDL old style --! comment
137 SrcLangExt lang = SrcLangExt::Unknown;
138 bool isFixedForm = FALSE; // For Fortran
139 std::deque<std::unique_ptr<commentcnv_FileState>> includeStack;
140 std::vector<std::string> expandedAliases;
143};
144
145[[maybe_unused]] static const char *stateToString(int state);
146static inline int computeIndent(const char *s);
147
148static void replaceCommentMarker(yyscan_t yyscanner,std::string_view s);
149static inline void copyToOutput(yyscan_t yyscanner,std::string_view s);
150static inline void copyToOutput(yyscan_t yyscanner,const char *s,int len);
151static void startCondSection(yyscan_t yyscanner,const QCString &sectId);
152static void endCondSection(yyscan_t yyscanner);
153static void handleCondSectionId(yyscan_t yyscanner,const char *expression);
154static void replaceAliases(yyscan_t yyscanner,std::string_view s,bool replaceComment=false);
155static int yyread(yyscan_t yyscanner,char *buf,int max_size);
156static void replaceComment(yyscan_t yyscanner,int offset);
157static void clearCommentStack(yyscan_t yyscanner);
158static bool readIncludeFile(yyscan_t yyscanner,const QCString &inc,const QCString &blockId);
159static void insertCommentStart(yyscan_t yyscanner);
160static bool parseIncludeOptions(yyscan_t yyscanner,std::string_view s);
161
162#undef YY_INPUT
163#define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
164
165// otherwise the filename would be the name of the converted file (*.cpp instead of *.l)
166static inline const char *getLexerFILE() {return __FILE__;}
167#include "doxygen_lex.h"
168
This is an alternative implementation of QCString.
Definition qcstring.h:101
static void startCondSection(yyscan_t yyscanner, const QCString &sectId)
static void copyToOutput(yyscan_t yyscanner, std::string_view s)
static int computeIndent(const char *s)
static void handleCondSectionId(yyscan_t yyscanner, const char *expression)
static void endCondSection(yyscan_t yyscanner)
static void replaceComment(yyscan_t yyscanner, int offset)
static void insertCommentStart(yyscan_t yyscanner)
static int yyread(yyscan_t yyscanner, char *buf, int max_size)
static const char * stateToString(int state)
static void replaceCommentMarker(yyscan_t yyscanner, std::string_view s)
static void replaceAliases(yyscan_t yyscanner, std::string_view s, bool replaceComment=false)
copies string s with length len to the output, while replacing any alias commands found in the string...
static void clearCommentStack(yyscan_t yyscanner)
static const char * getLexerFILE()
Definition commentcnv.l:166
static bool readIncludeFile(yyscan_t yyscanner, const QCString &inc, const QCString &blockId)
static bool parseIncludeOptions(yyscan_t yyscanner, std::string_view s)
Token literal values and constants.
Definition CharStream.h:12
#define FALSE
Definition qcstring.h:34
Some helper functions for std::string.
CommentCtx(int line)
Definition commentcnv.l:72
std::string fileBuf
Definition commentcnv.l:86
YY_BUFFER_STATE bufState
Definition commentcnv.l:80
const std::string * oldFileBuf
Definition commentcnv.l:87
commentcnvYY_CondCtx(int line, const QCString &id, bool b)
Definition commentcnv.l:63
std::string & outBuf
Definition commentcnv.l:98
std::deque< std::unique_ptr< commentcnv_FileState > > includeStack
Definition commentcnv.l:139
QCString snippetFileName
Definition commentcnv.l:141
std::vector< std::string > expandedAliases
Definition commentcnv.l:140
std::stack< commentcnvYY_CondCtx > condStack
Definition commentcnv.l:114
const std::string * inBuf
Definition commentcnv.l:97
commentcnvYY_state(const std::string *i, std::string &o)
Definition commentcnv.l:96
std::stack< int > commentStack
Definition commentcnv.l:115
SrcLangExt
Definition types.h:207
A bunch of utility functions.
169%}
170
171MAILADDR ("mailto:")?[a-z_A-Z0-9\x80-\xff.+-]+"@"[a-z_A-Z0-9\x80-\xff-]+("."[a-z_A-Z0-9\x80-\xff\-]+)+[a-z_A-Z0-9\x80-\xff\-]+
172
173%option noyywrap
174
175%x Scan
176%x SkipString
177%x SkipVerbString
178%x SkipChar
179%x SkipLang
180%x SComment
181%x CComment
182%x CNComment
183%x Verbatim
184%x VerbatimCode
185%x ReadLine
186%x CopyLine
187%x CondLine
188%x ReadAliasArgs
189%x IncludeDoc
190%x SnippetDoc
191%x SnippetDocTag
192%x IncludeFile
193
194CMD [\\@]
195 //- start: NUMBER -------------------------------------------------------------------------
196 // Note same defines in code.l: keep in sync
197DECIMAL_INTEGER [1-9][0-9']*[0-9]?[uU]?[lL]?[lL]?
198HEXADECIMAL_INTEGER "0"[xX][0-9a-zA-Z']+[0-9a-zA-Z]?
199OCTAL_INTEGER "0"[0-7][0-7']+[0-7]?
200BINARY_INTEGER "0"[bB][01][01']*[01]?
201INTEGER_NUMBER {DECIMAL_INTEGER}|{HEXADECIMAL_INTEGER}|{OCTAL_INTEGER}|{BINARY_INTEGER}
202
203FP_SUF [fFlL]
204
205DIGIT_SEQ [0-9][0-9']*[0-9]?
206FRAC_CONST {DIGIT_SEQ}"."|{DIGIT_SEQ}?"."{DIGIT_SEQ}
207FP_EXP [eE][+-]?{DIGIT_SEQ}
208DEC_FP1 {FRAC_CONST}{FP_EXP}?{FP_SUF}?
209DEC_FP2 {DIGIT_SEQ}{FP_EXP}{FP_SUF}
210
211HEX_DIGIT_SEQ [0-9a-fA-F][0-9a-fA-F']*[0-9a-fA-F]?
212HEX_FRAC_CONST {HEX_DIGIT_SEQ}"."|{HEX_DIGIT_SEQ}?"."{HEX_DIGIT_SEQ}
213BIN_EXP [pP][+-]?{DIGIT_SEQ}
214HEX_FP1 "0"[xX]{HEX_FRAC_CONST}{BIN_EXP}{FP_SUF}?
215HEX_FP2 "0"[xX]{HEX_DIGIT_SEQ}{BIN_EXP}{FP_SUF}?
216
217FLOAT_DECIMAL {DEC_FP1}|{DEC_FP2}
218FLOAT_HEXADECIMAL {HEX_FP1}|{HEX_FP2}
219FLOAT_NUMBER {FLOAT_DECIMAL}|{FLOAT_HEXADECIMAL}
220NUMBER {INTEGER_NUMBER}|{FLOAT_NUMBER}
221
222FILEICHAR [a-z_A-Z0-9\x80-\xFF\\:\\\/\-\+=&#@]
223FILEECHAR [a-z_A-Z0-9\x80-\xFF\-\+=&#@]
224FILECHARS {FILEICHAR}*{FILEECHAR}+
225HFILEMASK {FILEICHAR}*("."{FILEICHAR}+)+{FILECHARS}*
226VFILEMASK {FILECHARS}("."{FILECHARS})*
227FILEMASK {VFILEMASK}|{HFILEMASK}
228
229B [ \t]
230
231OPTS "{"[^}]*"}"{B}*
232
233 //- end: NUMBER ---------------------------------------------------------------------------
234
235 // C start comment
236CCS "/\*"
237 // C end comment
238CCE "*\/"
239 // Cpp comment
240CPPC "/\/"
241
242 // Optional any character
243ANYopt .*
244 // Optional white space
245WSopt [ \t\r]*
246 // readline non special
247RLopt [^\\@<\n\*\/`]*
248RL [^\\@<\n\*\/`]+
249 // Optional slash
250SLASHopt [/]*
251
253
254<Scan>{NUMBER} { //Note similar code in code.l
255 if (yyextra->lang!=SrcLangExt::Cpp) REJECT;
256 copyToOutput(yyscanner,yytext,yyleng);
257 }
258<Scan>[^"'!\/\n\\#,\-=; \t@$]* { /* eat anything that is not " / , or \n */
259 copyToOutput(yyscanner,yytext,yyleng);
260 }
261<Scan>[,= ;\t] { /* eat , so we have a nice separator in long initialization lines */
262 copyToOutput(yyscanner,yytext,yyleng);
263 }
264<Scan>"'''"! |
265<Scan>"\"\"\""! { /* start of python long comment */
266 if (yyextra->lang!=SrcLangExt::Python)
267 {
268 REJECT;
269 }
270 else
271 {
272 yyextra->pythonDocString = TRUE;
273 yyextra->pythonDocStringChar = yytext[0];
274 yyextra->nestingCount=1;
275 clearCommentStack(yyscanner); /* to be on the save side */
276 copyToOutput(yyscanner,yytext,yyleng);
277 BEGIN(CComment);
278 yyextra->commentStack.push(yyextra->lineNr);
279 }
280 }
#define TRUE
Definition qcstring.h:37
281<Scan>"'''" |
282<Scan>"\"\"\"" { /* start of python long comment */
283 if (yyextra->lang!=SrcLangExt::Python)
284 {
285 REJECT;
286 }
287 else if (Config_getBool(PYTHON_DOCSTRING))
288 {
289 REJECT;
290 }
291 else
292 { /* handle as if """! */
293 yyextra->pythonDocString = TRUE;
294 yyextra->pythonDocStringChar = yytext[0];
295 yyextra->nestingCount=1;
296 clearCommentStack(yyscanner); /* to be on the save side */
297 copyToOutput(yyscanner,yytext,yyleng);
298 BEGIN(CComment);
299 yyextra->commentStack.push(yyextra->lineNr);
300 }
301 }
#define Config_getBool(name)
Definition config.h:33
302<Scan>{B}*![><!]/.*\n {
303 if (yyextra->lang!=SrcLangExt::Fortran)
304 {
305 REJECT;
306 }
307 else
308 {
309 yyextra->nestingCount=0; // Fortran doesn't have an end comment
310 clearCommentStack(yyscanner); /* to be on the save side */
311 yyextra->specialComment=true;
312 copyToOutput(yyscanner,yytext,yyleng);
313 yyextra->blockHeadCol=yyextra->col-2;
314 BEGIN(CComment);
315 yyextra->commentStack.push(yyextra->lineNr);
316 }
317 }
318<Scan>[Cc\*][><!]/.*\n {
319 if (yyextra->lang!=SrcLangExt::Fortran)
320 {
321 REJECT;
322 }
323 else
324 {
325 /* check for fixed format; we might have some conditional as part of multiline if like C<5 .and. & */
326 if (yyextra->isFixedForm && (yyextra->col == 0))
327 {
328 yyextra->nestingCount=0; // Fortran doesn't have an end comment
329 clearCommentStack(yyscanner); /* to be on the save side */
330 yyextra->specialComment=true;
331 copyToOutput(yyscanner,yytext,yyleng);
332 yyextra->blockHeadCol=yyextra->col-1;
333 BEGIN(CComment);
334 yyextra->commentStack.push(yyextra->lineNr);
335 }
336 else
337 {
338 REJECT;
339 }
340 }
341 }
342<Scan>!.*\n {
343 if (yyextra->lang!=SrcLangExt::Fortran)
344 {
345 REJECT;
346 }
347 else
348 {
349 copyToOutput(yyscanner,yytext,yyleng);
350 }
351 }
352<Scan>[Cc\*].*\n {
353 if (yyextra->lang!=SrcLangExt::Fortran)
354 {
355 REJECT;
356 }
357 else
358 {
359 if (yyextra->col == 0)
360 {
361 copyToOutput(yyscanner,yytext,yyleng);
362 }
363 else
364 {
365 REJECT;
366 }
367 }
368 }
369<Scan>[$]?"@\"" { /* start of an interpolated verbatim C# string */
370 if (yyextra->lang!=SrcLangExt::CSharp) REJECT
371 copyToOutput(yyscanner,yytext,yyleng);
372 yyextra->stringContext = YY_START;
373 BEGIN(SkipVerbString);
374 }
375<Scan>"\"" { /* start of a string */
376 copyToOutput(yyscanner,yytext,yyleng);
377 yyextra->stringContext = YY_START;
378 BEGIN(SkipString);
379 }
380<Scan>' {
381 copyToOutput(yyscanner,yytext,yyleng);
382 yyextra->charContext = YY_START;
383 if (yyextra->lang!=SrcLangExt::VHDL)
384 {
385 BEGIN(SkipChar);
386 }
387 }
388<Scan>\n { /* new line */
389 copyToOutput(yyscanner,yytext,yyleng);
390 }
391<Scan>{CPPC}[!/]/.*\n[ \t]*{CPPC}[!/][ \t]*{CMD}"}" { // see bug #8731, don't treat multiline C++ comment as detailed description
392 // if the next line is an end of group marker.
393 yyextra->inSpecialComment=true;
394 yyextra->blockHeadCol=yyextra->col+1;
395 yyextra->insertCppCommentMarker=true;
396 copyToOutput(yyscanner,yytext,yyleng);
397 yyextra->readLineCtx=YY_START;
398 BEGIN(ReadLine);
399 }
400<Scan>{CPPC}"!"/.*\n[ \t]*{CPPC}[\/!][^\/] | /* start C++ style special comment block */
401<Scan>({CPPC}"/"[/]*)/[^/].*\n[ \t]*{CPPC}[\/!][^\/] { /* start C++ style special comment block */
402 if (yyextra->mlBrief)
403 {
404 REJECT; // bail out if we do not need to convert
405 }
406 else
407 {
408 int i=3;
409 if (yytext[2]=='/')
410 {
411 while (i<(int)yyleng && yytext[i]=='/') i++;
412 }
413 yyextra->blockHeadCol=yyextra->col+1;
414 if (yytext[2] == '!')
415 {
416 copyToOutput(yyscanner,"/*!");
417 }
418 else
419 {
420 copyToOutput(yyscanner,"/**");
421 }
422 if (i<yyleng) replaceAliases(yyscanner,yytext+i);
423 yyextra->inSpecialComment=TRUE;
424 //BEGIN(SComment);
425 yyextra->readLineCtx=SComment;
426 BEGIN(ReadLine);
427 }
428 }
429<Scan>{CPPC}"##Documentation"{ANYopt}/\n { /* Start of Rational Rose ANSI C++ comment block */
430 if (yyextra->mlBrief) REJECT;
431 int i=17; //=strlen("//##Documentation");
432 yyextra->blockHeadCol=yyextra->col+1;
433 copyToOutput(yyscanner,"/**");
434 if (i<yyleng) replaceAliases(yyscanner,yytext+i);
435 yyextra->inRoseComment=TRUE;
436 BEGIN(SComment);
437 }
438<Scan>{CPPC}[!\/]/.*\n[ \t]*{CPPC}[|\/][ \t]*{CMD}"}" { // next line contains an end marker, see bug 752712
439 yyextra->inSpecialComment=yytext[2]=='/' || yytext[2]=='!';
440 if (yyextra->inSpecialComment)
441 {
442 yyextra->blockHeadCol=yyextra->col+1;
443 }
444 copyToOutput(yyscanner,yytext,yyleng);
445 yyextra->readLineCtx=YY_START;
446 BEGIN(ReadLine);
447 }
448<Scan>{CPPC}[!/]/.*\n { /* one line special C++ comment */
449 yyextra->inSpecialComment=true;
450 yyextra->blockHeadCol=yyextra->col+1;
451 yyextra->insertCppCommentMarker=true;
452 copyToOutput(yyscanner,yytext,yyleng);
453 yyextra->readLineCtx=YY_START;
454 BEGIN(ReadLine);
455 }
456<Scan>{CPPC}/.*\n { /* one line normal C++ comment */
457 yyextra->inSpecialComment=false;
458 copyToOutput(yyscanner,yytext,yyleng);
459 yyextra->readLineCtx=YY_START;
460 BEGIN(CopyLine);
461 }
462<Scan>{CCS}{CCE} { /* avoid matching next rule for empty C comment, see bug 711723 */
463 copyToOutput(yyscanner,yytext,yyleng);
464 }
465<Scan>{CCS}[*!]? { /* start of a C comment */
466 if (yyextra->lang==SrcLangExt::Python)
467 {
468 REJECT;
469 }
470 yyextra->specialComment=(int)yyleng==3;
471 yyextra->nestingCount=1;
472 clearCommentStack(yyscanner); /* to be on the save side */
473 copyToOutput(yyscanner,yytext,yyleng);
474 if (yyextra->specialComment)
475 {
476 yyextra->blockHeadCol=0;
477 BEGIN(CComment);
478 }
479 else
480 {
481 BEGIN(CNComment);
482 }
483 yyextra->commentStack.push(yyextra->lineNr);
484 }
485<Scan>"#"[^\n]*\n {
486 if (yyextra->lang!=SrcLangExt::PHP)
487 {
488 REJECT;
489 }
490 copyToOutput(yyscanner,yytext,yyleng);
491 }
492<Scan>"#"("#")? {
493 if (yyextra->lang!=SrcLangExt::Python)
494 {
495 REJECT;
496 }
497 else
498 {
499 yyextra->nestingCount=0; // Python doesn't have an end comment for #
500 clearCommentStack(yyscanner); /* to be on the save side */
501 yyextra->specialComment=(int)yyleng==2;
502 if (yyextra->specialComment)
503 {
504 yyextra->blockHeadCol=yyextra->col;
505 }
506 yyextra->commentStack.push(yyextra->lineNr);
507 copyToOutput(yyscanner,yytext,yyleng);
508 BEGIN(CComment);
509 }
510 }
511<Scan>"--"[^!][^\n]* {
512 if (yyextra->lang!=SrcLangExt::VHDL)
513 {
514 REJECT;
515 }
516 else
517 {
518 copyToOutput(yyscanner,yytext,yyleng);
519 }
520 }
521<Scan>"--!" {
522 if (yyextra->lang!=SrcLangExt::VHDL)
523 {
524 REJECT;
525 }
526 else
527 {
528 yyextra->specialComment=true;
529 yyextra->blockHeadCol=yyextra->col;
530 yyextra->vhdl = TRUE;
531 yyextra->nestingCount=0; // VHDL doesn't have an end comment
532 clearCommentStack(yyscanner); /* to be on the save side */
533 yyextra->commentStack.push(yyextra->lineNr);
534 copyToOutput(yyscanner,yytext,yyleng);
535 BEGIN(CComment);
536 }
537 }
538<Scan>{B}*![><!] {
539 if (yyextra->lang!=SrcLangExt::Fortran)
540 {
541 REJECT;
542 }
543 else
544 {
545 yyextra->nestingCount=0; // Fortran doesn't have an end comment
546 clearCommentStack(yyscanner); /* to be on the save side */
547 yyextra->specialComment=true;
548 yyextra->blockHeadCol=yyextra->col;
549 yyextra->commentStack.push(yyextra->lineNr);
550 copyToOutput(yyscanner,yytext,yyleng);
551 BEGIN(CComment);
552 }
553 }
554<CComment,CNComment,ReadLine,IncludeFile>{MAILADDR} |
555<CComment,CNComment,ReadLine,IncludeFile>"<"{MAILADDR}">" { // Mail address, to prevent seeing e.g x@code-factory.org as start of a code block
556 copyToOutput(yyscanner,yytext,yyleng);
557 }
558<CComment,IncludeFile>"{"[ \t]*"@code"/[ \t\n] {
559 copyToOutput(yyscanner,"@iliteral{code}");
560 yyextra->lastCommentContext = YY_START;
561 yyextra->javaBlock=1;
562 yyextra->blockName=QCString("end")+&yytext[1];
563 yyextra->inVerbatim=true;
564 BEGIN(VerbatimCode);
565 }
566<CComment,IncludeFile>"{"[ \t]*"@literal"/[ \t\n] {
567 copyToOutput(yyscanner,"@iliteral");
568 yyextra->lastCommentContext = YY_START;
569 yyextra->javaBlock=1;
570 yyextra->blockName=QCString("end")+&yytext[1];
571 yyextra->inVerbatim=true;
572 BEGIN(VerbatimCode);
573 }
574<CComment,ReadLine,IncludeFile>{CMD}"ilinebr"[ \t]+("```"[`]*|"~~~"[~]*) { /* start of markdown code block */
575 if (!Config_getBool(MARKDOWN_SUPPORT))
576 {
577 REJECT;
578 }
579 copyToOutput(yyscanner,yytext,yyleng);
580 yyextra->lastCommentContext = YY_START;
581 yyextra->javaBlock=0;
582 yyextra->blockName=QCString(yytext).stripWhiteSpace().right(3); // take the ``` or ~~~ part
583 yyextra->inVerbatim=true;
584 BEGIN(VerbatimCode);
585 }
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:245
QCString right(size_t len) const
Definition qcstring.h:219
586<CComment,ReadLine,IncludeFile>^[ \t]*("```"[`]*|"~~~"[~]*) { /* start of markdown code block */
587 if (!Config_getBool(MARKDOWN_SUPPORT))
588 {
589 REJECT;
590 }
591 copyToOutput(yyscanner,yytext,yyleng);
592 yyextra->lastCommentContext = YY_START;
593 yyextra->javaBlock=0;
594 yyextra->blockName=QCString(yytext).stripWhiteSpace().left(3); // take the ``` or ~~~ part
595 yyextra->inVerbatim=true;
596 BEGIN(VerbatimCode);
597 }
QCString left(size_t len) const
Definition qcstring.h:214
598<CComment,ReadLine,IncludeFile>{CMD}("dot"|"code"|"msc"|"startuml")/[^a-z_A-Z0-9] { /* start of a verbatim block */
599 copyToOutput(yyscanner,yytext,yyleng);
600 yyextra->lastCommentContext = YY_START;
601 yyextra->javaBlock=0;
602 if (qstrcmp(&yytext[1],"startuml")==0)
603 {
604 yyextra->blockName="enduml";
605 }
606 else
607 {
608 yyextra->blockName=QCString("end")+&yytext[1];
609 }
610 yyextra->inVerbatim=true;
611 BEGIN(VerbatimCode);
612 }
int qstrcmp(const char *str1, const char *str2)
Definition qcstring.h:69
613<CComment,ReadLine,IncludeFile>{CMD}("f$"|"f["|"f{"|"f(") {
614 copyToOutput(yyscanner,yytext,yyleng);
615 yyextra->blockName=&yytext[1];
616 if (yyextra->blockName.at(1)=='[')
617 {
618 yyextra->blockName.at(1)=']';
619 }
620 else if (yyextra->blockName.at(1)=='{')
621 {
622 yyextra->blockName.at(1)='}';
623 }
624 else if (yyextra->blockName.at(1)=='(')
625 {
626 yyextra->blockName.at(1)=')';
627 }
628 yyextra->lastCommentContext = YY_START;
629 yyextra->inVerbatim=true;
630 BEGIN(Verbatim);
631 }
632<CComment,ReadLine,IncludeFile>"<!--!" { /* HTML comment doxygen command*/
633 if (yyextra->inVerbatim) REJECT;
634 //copyToOutput(yyscanner," ",5);
635 yyextra->inHtmlDoxygenCommand=true;
636 }
637<CComment,ReadLine,IncludeFile>"-->" { /* potential end HTML comment doxygen command*/
638 if (yyextra->inHtmlDoxygenCommand)
639 {
640 yyextra->inHtmlDoxygenCommand=false;
641 }
642 else
643 {
644 copyToOutput(yyscanner,yytext,yyleng);
645 }
646 }
647<CComment,ReadLine,IncludeFile>"<!--" { /* HTML comment */
648 copyToOutput(yyscanner,yytext,yyleng);
649 yyextra->blockName="-->";
650 yyextra->lastCommentContext = YY_START;
651 yyextra->inVerbatim=true;
652 BEGIN(Verbatim);
653 }
654<CComment,ReadLine,IncludeFile>{CMD}("verbatim"|"iliteral"|"latexonly"|"htmlonly"|"xmlonly"|"docbookonly"|"rtfonly"|"manonly")/[^a-z_A-Z0-9] { /* start of a verbatim block */
655 copyToOutput(yyscanner,yytext,yyleng);
656 yyextra->blockName=QCString("end")+&yytext[1];
657 yyextra->lastCommentContext = YY_START;
658 yyextra->inVerbatim=true;
659 BEGIN(Verbatim);
660 }
661<Scan>"\\\"" { /* escaped double quote */
662 copyToOutput(yyscanner,yytext,yyleng);
663 }
664<Scan>"\\\\" { /* escaped backslash */
665 copyToOutput(yyscanner,yytext,yyleng);
666 }
667<Scan>. { /* any other character */
668 copyToOutput(yyscanner,yytext,yyleng);
669 }
670<Verbatim>{CMD}("endverbatim"|"endiliteral"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}"|"f)") { /* end of verbatim block */
671 copyToOutput(yyscanner,yytext,yyleng);
672 if (&yytext[1]==yyextra->blockName) // end of command or formula
673 {
674 yyextra->inVerbatim=false;
675 BEGIN(yyextra->lastCommentContext);
676 }
677 }
678<Verbatim>"-->" {
679 copyToOutput(yyscanner,yytext,yyleng);
680 if (yytext==yyextra->blockName)
681 {
682 yyextra->inVerbatim=false;
683 BEGIN(yyextra->lastCommentContext);
684 }
685 }
686<VerbatimCode>"{" {
687 if (yyextra->javaBlock==0)
688 {
689 REJECT;
690 }
691 else
692 {
693 yyextra->javaBlock++;
694 copyToOutput(yyscanner,yytext,yyleng);
695 }
696 }
697<VerbatimCode>"}" {
698 if (yyextra->javaBlock==0)
699 {
700 REJECT;
701 }
702 else
703 {
704 yyextra->javaBlock--;
705 if (yyextra->javaBlock==0)
706 {
707 copyToOutput(yyscanner," @endiliteral ");
708 yyextra->inVerbatim=false;
709 BEGIN(yyextra->lastCommentContext);
710 }
711 else
712 {
713 copyToOutput(yyscanner,yytext,yyleng);
714 }
715 }
716 }
717<VerbatimCode>("```"[`]*|"~~~"[~]*) { /* end of markdown code block */
718 copyToOutput(yyscanner,yytext,yyleng);
719 if (yytext[0]==yyextra->blockName[0])
720 {
721 yyextra->inVerbatim=false;
722 BEGIN(yyextra->lastCommentContext);
723 }
724 }
725<VerbatimCode>{CMD}("enddot"|"endcode"|"endmsc"|"enduml")/("{")? { /* end of verbatim block */
726 copyToOutput(yyscanner,yytext,yyleng);
727 if (&yytext[1]==yyextra->blockName)
728 {
729 yyextra->inVerbatim=false;
730 BEGIN(yyextra->lastCommentContext);
731 }
732 }
733<VerbatimCode>^[ \t]*{CPPC}[\!\/]? { /* skip leading comments */
734 if (!yyextra->inSpecialComment || yyextra->mlBrief)
735 {
736 copyToOutput(yyscanner,yytext,yyleng);
737 }
738 else
739 {
740 int l=0;
741 while (yytext[l]==' ' || yytext[l]=='\t')
742 {
743 l++;
744 }
745 copyToOutput(yyscanner,yytext,l);
746 if (yyleng-l==3) // ends with //! or ///
747 {
748 copyToOutput(yyscanner," * ");
749 }
750 else // ends with //
751 {
752 copyToOutput(yyscanner,"//");
753 }
754 }
755 }
756<Verbatim,VerbatimCode>[^`~@\/\-\\\n{}]* { /* any character not a backslash or new line or } */
757 copyToOutput(yyscanner,yytext,yyleng);
758 }
759<Verbatim,VerbatimCode>\n { /* new line in verbatim block */
760 copyToOutput(yyscanner,yytext,yyleng);
761 if (yyextra->lastCommentContext == IncludeFile)
762 {
763 insertCommentStart(yyscanner);
764 }
765 }
766<Verbatim>^[ \t]*{CPPC}[/!] {
767 if (yyextra->blockName=="enddot" || yyextra->blockName=="endmsc" || yyextra->blockName=="enduml" || yyextra->blockName.at(0)=='f')
768 {
769 // see bug 487871, strip /// from dot images and formulas.
770 int l=0;
771 while (yytext[l]==' ' || yytext[l]=='\t')
772 {
773 l++;
774 }
775 copyToOutput(yyscanner,yytext,l);
776 copyToOutput(yyscanner," ");
777 }
778 else // even slashes are verbatim (e.g. \verbatim, \code)
779 {
780 REJECT;
781 }
782 }
783<Verbatim,VerbatimCode>. { /* any other character */
784 copyToOutput(yyscanner,yytext,yyleng);
785 }
786<SkipString>\\. { /* escaped character in string */
787 if (yyextra->lang==SrcLangExt::Fortran || yyextra->lang==SrcLangExt::VHDL)
788 {
789 unput(yytext[1]);
790 copyToOutput(yyscanner,yytext,1);
791 }
792 else
793 {
794 copyToOutput(yyscanner,yytext,yyleng);
795 }
796 }
797<SkipString>"\"" { /* end of string */
798 copyToOutput(yyscanner,yytext,yyleng);
799 BEGIN(yyextra->stringContext);
800 }
801<SkipString>. { /* any other string character */
802 copyToOutput(yyscanner,yytext,yyleng);
803 }
804<SkipString>\n { /* new line inside string (illegal for some compilers) */
805 copyToOutput(yyscanner,yytext,yyleng);
806 }
807<SkipVerbString>[^"\n]+ {
808 copyToOutput(yyscanner,yytext,yyleng);
809 }
810<SkipVerbString>\"\" { // escaped quote
811 copyToOutput(yyscanner,yytext,yyleng);
812 }
813<SkipVerbString>"\"" { /* end of string */
814 copyToOutput(yyscanner,yytext,yyleng);
815 BEGIN(yyextra->stringContext);
816 }
817<SkipVerbString>. {
818 copyToOutput(yyscanner,yytext,yyleng);
819 }
820<SkipVerbString>\n {
821 copyToOutput(yyscanner,yytext,yyleng);
822 }
823<SkipChar>\\. { /* escaped character */
824 if (yyextra->lang==SrcLangExt::Fortran || yyextra->lang==SrcLangExt::VHDL)
825 {
826 unput(yytext[1]);
827 copyToOutput(yyscanner,yytext,1);
828 }
829 else
830 {
831 copyToOutput(yyscanner,yytext,yyleng);
832 }
833 }
834<SkipChar>' { /* end of character literal */
835 copyToOutput(yyscanner,yytext,yyleng);
836 BEGIN(yyextra->charContext);
837 }
838<SkipChar>. { /* any other string character */
839 copyToOutput(yyscanner,yytext,yyleng);
840 }
841<SkipChar>\n { /* new line character */
842 copyToOutput(yyscanner,yytext,yyleng);
843 }
844
845<CComment,CNComment>[^ `~<\\!@*\n{\"'\/-]* { /* anything that is not a '*' or command */
846 copyToOutput(yyscanner,yytext,yyleng);
847 }
848<CComment,CNComment>^{B}*"*"+[^*\/<\\@\n{\"]* { /* stars without slashes */
849 if (yyextra->lang==SrcLangExt::Markdown) REJECT;
850 yyextra->col = computeIndent(yytext);
851 if (yyextra->col>yyextra->blockHeadCol)
852 {
853 //printf("new blockHeadCol=%d\n",yyextra->blockHeadCol);
854 yyextra->blockHeadCol=yyextra->col;
855 }
856 copyToOutput(yyscanner,yytext,yyleng);
857 }
858<CComment>"'''" |
859<CComment>"\"\"\"" { /* end of Python docstring */
860 if (yyextra->lang!=SrcLangExt::Python)
861 {
862 REJECT;
863 }
864 else if (yyextra->pythonDocStringChar != yytext[0])
865 {
866 copyToOutput(yyscanner,yytext,yyleng);
867 }
868 else
869 {
870 yyextra->nestingCount--;
871 yyextra->pythonDocString = FALSE;
872 yyextra->pythonDocStringChar = '\0';
873 copyToOutput(yyscanner,yytext,yyleng);
874 BEGIN(Scan);
875 }
876 }
877<CComment,CNComment>\n { /* new line in comment */
878 copyToOutput(yyscanner,yytext,yyleng);
879 /* in case of Fortran always end of comment */
880 if (yyextra->lang==SrcLangExt::Fortran)
881 {
882 BEGIN(Scan);
883 }
884 }
885<CComment,CNComment>"/""/"+/"*/" { /* we are already in C-comment so not a start of a nested comment but
886 * just the end of the comment (the end part is handled later). */
887 copyToOutput(yyscanner,yytext,yyleng);
888 }
889<CComment,CNComment>"/"+"*" { /* nested C comment */
890 if (yyextra->lang==SrcLangExt::Python ||
891 yyextra->lang==SrcLangExt::Markdown)
892 {
893 REJECT;
894 }
895 yyextra->nestingCount++;
896 yyextra->commentStack.push(yyextra->lineNr);
897 copyToOutput(yyscanner,yytext,yyleng);
898 }
899<CComment,CNComment>^{B}*"*"+"/" |
900<CComment,CNComment>"*"+"/" { /* end of C comment */
901 if (yyextra->lang==SrcLangExt::Python ||
902 yyextra->lang==SrcLangExt::Markdown)
903 {
904 REJECT;
905 }
906 else
907 {
908 copyToOutput(yyscanner,yytext,yyleng);
909 yyextra->nestingCount--;
910 if (yyextra->nestingCount<=0)
911 {
912 BEGIN(Scan);
913 }
914 else
915 {
916 //yyextra->nestingCount--;
917 yyextra->commentStack.pop();
918 }
919 }
920 }
921 /* Python an VHDL share CComment,CNComment, so special attention for ending comments is required */
922<CComment,CNComment>"\n"/[ \t]*"#" {
923 if (yyextra->lang!=SrcLangExt::VHDL)
924 {
925 REJECT;
926 }
927 else
928 {
929 if (yyextra->vhdl) // inside --! comment
930 {
931 yyextra->vhdl = FALSE;
932 copyToOutput(yyscanner,yytext,yyleng);
933 BEGIN(Scan);
934 }
935 else // C-type comment
936 {
937 REJECT;
938 }
939 }
940 }
941<CComment,CNComment>"\n"/[ \t]*"-" {
942 if (yyextra->lang!=SrcLangExt::Python || yyextra->pythonDocString)
943 {
944 REJECT;
945 }
946 else
947 {
948 copyToOutput(yyscanner,yytext,yyleng);
949 BEGIN(Scan);
950 }
951 }
952<CComment,CNComment>"\n"/[ \t]*[^ \t#\-] {
953 if (yyextra->lang==SrcLangExt::Python)
954 {
955 if (yyextra->pythonDocString)
956 {
957 REJECT;
958 }
959 else
960 {
961 copyToOutput(yyscanner,yytext,yyleng);
962 BEGIN(Scan);
963 }
964 }
965 else if (yyextra->lang==SrcLangExt::VHDL)
966 {
967 if (yyextra->vhdl) // inside --! comment
968 {
969 yyextra->vhdl = FALSE;
970 copyToOutput(yyscanner,yytext,yyleng);
971 BEGIN(Scan);
972 }
973 else // C-type comment
974 {
975 REJECT;
976 }
977 }
978 else
979 {
980 REJECT;
981 }
982 }
983 /* removed for bug 674842 (bug was introduced in rev 768)
984<CComment,CNComment>"'" {
985 yyextra->charContext = YY_START;
986 copyToOutput(yyscanner,yytext,yyleng);
987 BEGIN(SkipChar);
988 }
989<CComment,CNComment>"\"" {
990 yyextra->stringContext = YY_START;
991 copyToOutput(yyscanner,yytext,yyleng);
992 BEGIN(SkipString);
993 }
994 */
995<CComment,CNComment>{CMD}"~"[a-z_A-Z-]* { // language switch command
996 if (yyextra->lang!=SrcLangExt::Markdown) REJECT;
997 QCString langId = QCString(yytext).mid(2);
998 if (!langId.isEmpty() &&
999 qstricmp(Config_getEnumAsString(OUTPUT_LANGUAGE),langId)!=0)
1000 { // enable language specific section
1001 if (!Config_isAvailableEnum(OUTPUT_LANGUAGE,langId))
1002 {
1003 warn(yyextra->fileName,yyextra->lineNr,
1004 "non supported language '{}' specified in '{}'",langId,QCString(yytext).stripWhiteSpace());
1005 }
1006 BEGIN(SkipLang);
1007 }
1008 }
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
#define Config_getEnumAsString(name)
Definition config.h:36
#define Config_isAvailableEnum(name, value)
Definition config.h:45
#define warn(file, line, fmt,...)
Definition message.h:97
int qstricmp(const char *s1, const char *s2)
Definition qcstring.cpp:442
std::string_view stripWhiteSpace(std::string_view s)
Given a string view s, returns a new, narrower view on that string, skipping over any leading or trai...
Definition stringutil.h:72
1009<CComment,CNComment>{CMD}{CMD} |
1010<CComment,CNComment>. {
1011 copyToOutput(yyscanner,yytext,yyleng);
1012 }
1013<SkipLang>{CMD}"~"[a-zA-Z-]* { /* language switch */
1014 QCString langId(&yytext[2]);
1015 if (!langId.isEmpty() && !Config_isAvailableEnum(OUTPUT_LANGUAGE,langId))
1016 {
1017 warn(yyextra->fileName,yyextra->lineNr,
1018 "non supported language '{}' specified in '{}'",langId,QCString(yytext).stripWhiteSpace());
1019 }
1020 else if (langId.isEmpty() ||
1021 qstricmp(Config_getEnumAsString(OUTPUT_LANGUAGE),langId)==0)
1022 { // enable language specific section
1023 BEGIN(CComment);
1024 }
1025 }
1026<SkipLang>[^*@\\\n]* { /* any character not a *, @, backslash or new line */
1027 }
1028<SkipLang>\n { /* new line in language block, needed for keeping track of line numbers */
1029 copyToOutput(yyscanner,yytext,yyleng);
1030 }
1031<SkipLang>. { /* any other character */
1032 }
1033<SComment>^[ \t]*{CPPC}"/"{SLASHopt}/\n {
1034 replaceComment(yyscanner,0);
1035 }
1036<SComment>\n[ \t]*{CPPC}"/"{SLASHopt}/\n {
1037 replaceComment(yyscanner,1);
1038 }
1039<SComment>^[ \t]*{CPPC}"/"[^\/\n]/.*\n {
1040 replaceComment(yyscanner,0);
1041 yyextra->readLineCtx=YY_START;
1042 BEGIN(ReadLine);
1043 }
1044<SComment>\n[ \t]*{CPPC}[\/!]("<")?[ \t]*{CMD}"}".*\n {
1045 /* See Bug 752712: end the multiline comment when finding a @} or \} command */
1046 copyToOutput(yyscanner," */");
1047 copyToOutput(yyscanner,yytext,yyleng);
1048 yyextra->inSpecialComment=false;
1049 yyextra->inRoseComment=false;
1050 BEGIN(Scan);
1051 }
1052<SComment>\n[ \t]*{CPPC}"/"[^\\@\/\n]/.*\n {
1053 replaceComment(yyscanner,1);
1054 yyextra->readLineCtx=YY_START;
1055 BEGIN(ReadLine);
1056 }
1057<SComment>^[ \t]*{CPPC}"!" | // just //!
1058<SComment>^[ \t]*{CPPC}"!<"/.*\n | // or //!< something
1059<SComment>^[ \t]*{CPPC}"!"[^<]/.*\n { // or //!something
1060 replaceComment(yyscanner,0);
1061 yyextra->readLineCtx=YY_START;
1062 BEGIN(ReadLine);
1063 }
1064<SComment>\n[ \t]*{CPPC}"!" |
1065<SComment>\n[ \t]*{CPPC}"!<"/.*\n |
1066<SComment>\n[ \t]*{CPPC}"!"[^<\n]/.*\n {
1067 replaceComment(yyscanner,1);
1068 yyextra->readLineCtx=YY_START;
1069 BEGIN(ReadLine);
1070 }
1071<SComment>^[ \t]*{CPPC}"##"/.*\n {
1072 if (!yyextra->inRoseComment)
1073 {
1074 REJECT;
1075 }
1076 else
1077 {
1078 replaceComment(yyscanner,0);
1079 yyextra->readLineCtx=YY_START;
1080 BEGIN(ReadLine);
1081 }
1082 }
1083<SComment>\n[ \t]*{CPPC}"##"/.*\n {
1084 if (!yyextra->inRoseComment)
1085 {
1086 REJECT;
1087 }
1088 else
1089 {
1090 replaceComment(yyscanner,1);
1091 yyextra->readLineCtx=YY_START;
1092 BEGIN(ReadLine);
1093 }
1094 }
1095<SComment>\n { /* end of special comment */
1096 copyToOutput(yyscanner," */");
1097 copyToOutput(yyscanner,yytext,yyleng);
1098 yyextra->inSpecialComment=FALSE;
1099 yyextra->inRoseComment=FALSE;
1100 yyextra->insertCppCommentMarker=false;
1101 yyextra->readLineCtx = Scan; // reset, otherwise there will be problems with:
1102 // static void handleCondSectionId
1103 BEGIN(Scan);
1104 }
1105<ReadLine>{CCS}"*" {
1106 copyToOutput(yyscanner,"/&zwj;**");
1107 }
1108<ReadLine>{CCE} {
1109 copyToOutput(yyscanner,"*&zwj;/");
1110 }
1111<ReadLine,CopyLine>"*" {
1112 copyToOutput(yyscanner,yytext,yyleng);
1113 }
1114<ReadLine,CopyLine>{RL} {
1115 copyToOutput(yyscanner,yytext,yyleng);
1116 }
1117<ReadLine,CopyLine>{RL}/{B}{CMD}"ilinebr"{B} {
1118 copyToOutput(yyscanner,yytext,yyleng);
1119 }
1120<ReadLine,CopyLine>{RLopt}/\n {
1121 copyToOutput(yyscanner,yytext,yyleng);
1122 yyextra->insertCppCommentMarker=false;
1123 BEGIN(yyextra->readLineCtx);
1124 }
1125<CComment,CNComment,ReadLine>"\<" { /* escaped html comment */
1126 copyToOutput(yyscanner,yytext,yyleng);
1127 }
1128<CComment,CNComment,ReadLine>{CMD}{CMD}[~a-z_A-Z][a-z_A-Z0-9]*[ \t]* { // escaped command
1129 copyToOutput(yyscanner,yytext,yyleng);
1130 }
1131
1132<CComment,ReadLine,IncludeFile>{CMD}("include"{OPTS}|"includedoc"{OPTS}*) {
1133 if (!parseIncludeOptions(yyscanner,std::string_view{yytext,static_cast<size_t>(yyleng)})) REJECT;
1134 yyextra->includeCtx = YY_START;
1135 yyextra->firstIncludeLine = true;
1136 yyextra->insertCommentCol = yyextra->col;
1137 if (!yyextra->insertCppCommentMarker && (yyextra->includeCtx==ReadLine || yyextra->includeCtx==IncludeFile))
1138 {
1139 yyextra->insertCppCommentMarker = yyextra->mlBrief;
1140 }
1141 //printf("blockHeadCol=%d insertCommentCol=%d\n",yyextra->blockHeadCol, yyextra->insertCommentCol);
1142 BEGIN(IncludeDoc);
1143 }
1144<CComment,ReadLine,IncludeFile>{CMD}("snippet"{OPTS}|"snippetdoc"{OPTS}*) {
1145 if (!parseIncludeOptions(yyscanner,std::string_view{yytext,static_cast<size_t>(yyleng)})) REJECT;
1146 yyextra->includeCtx = YY_START;
1147 yyextra->firstIncludeLine = true;
1148 yyextra->insertCommentCol = yyextra->col;
1149 if (!yyextra->insertCppCommentMarker && (yyextra->includeCtx==ReadLine || yyextra->includeCtx==IncludeFile))
1150 {
1151 yyextra->insertCppCommentMarker = yyextra->mlBrief;
1152 }
1153 //printf("blockHeadCol=%d insertCommentCol=%d\n",yyextra->blockHeadCol, yyextra->insertCommentCol);
1154 BEGIN(SnippetDoc);
1155 }
1156<IncludeDoc,SnippetDoc>{B}*
1157<IncludeDoc>{FILEMASK}|"\""[^\n\"]+"\"" {
1158 QCString fileName=yytext;
1159 if (yytext[0]=='"')
1160 {
1161 fileName=fileName.mid(1,fileName.length()-2); // strip quotes
1162 }
1163 if (readIncludeFile(yyscanner,fileName,""))
1164 {
1165 BEGIN(IncludeFile);
1166 }
1167 else
1168 {
1169 BEGIN(yyextra->includeCtx);
1170 }
1171 }
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
1172<SnippetDoc>({FILEMASK}|"\""[^\n\"]+"\""){B}+ {
1173 yyextra->snippetFileName=yytext;
1174 yyextra->snippetFileName=yyextra->snippetFileName.stripWhiteSpace();
1175 if (yyextra->snippetFileName == "this") yyextra->snippetFileName=yyextra->fileName;
1176 yyextra->snippetName = "";
1177 BEGIN(SnippetDocTag);
1178 }
1179<SnippetDocTag>[^\\@\n]+ {
1180 yyextra->snippetName += yytext;
1181 }
1182<SnippetDocTag>{CMD} {
1183 yyextra->snippetName += yytext;
1184 }
1185<SnippetDocTag>(\n|{CMD}"ilinebr") {
1186 for (int i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
1187 yyextra->snippetName = yyextra->snippetName.stripWhiteSpace();
1188 QCString blockId = "["+yyextra->snippetName+"]";
1189 if (readIncludeFile(yyscanner,yyextra->snippetFileName,blockId))
1190 {
1191 BEGIN(IncludeFile);
1192 }
1193 else
1194 {
1195 BEGIN(yyextra->includeCtx);
1196 }
1197 }
1198
1199<IncludeDoc,SnippetDoc>\n {
1200 copyToOutput(yyscanner,yytext,yyleng);
1201 insertCommentStart(yyscanner);
1202 // missing file name
1203 //warn(yyextra->fileName,yyextra->lineNr,"Found \\include{{doc}} command without valid file name argument");
1204 BEGIN(yyextra->includeCtx);
1205 }
1206<IncludeDoc,SnippetDoc>. { // invalid character
1207 copyToOutput(yyscanner,yytext,yyleng);
1208 BEGIN(yyextra->includeCtx);
1209 }
1210<CComment,ReadLine,IncludeFile>{CMD}"cond"/[^a-z_A-Z0-9] { // conditional section
1211 yyextra->condCtx = YY_START;
1212 BEGIN(CondLine);
1213 }
1214<CComment,ReadLine,IncludeFile>{CMD}"endcond"/[^a-z_A-Z0-9] { // end of conditional section
1215 bool oldSkip=yyextra->skip;
1216 endCondSection(yyscanner);
1217 if (YY_START==CComment && oldSkip && !yyextra->skip)
1218 {
1219 //printf("** Adding start of comment!\n");
1220 if (yyextra->lang!=SrcLangExt::Python &&
1221 yyextra->lang!=SrcLangExt::VHDL &&
1222 yyextra->lang!=SrcLangExt::Markdown &&
1223 yyextra->lang!=SrcLangExt::Fortran)
1224 {
1225 yyextra->outBuf+='/';
1226 yyextra->outBuf+='*';
1227 yyextra->col+=2;
1228 if (yyextra->specialComment)
1229 {
1230 yyextra->outBuf+='*';
1231 yyextra->col++;
1232 }
1233 }
1234 }
1235 }
1236<CondLine>[!()&| \ta-z_A-Z0-9.\-]+ {
1237 handleCondSectionId(yyscanner,yytext);
1238 }
1239<CComment,ReadLine,IncludeFile>{CMD}"cond"{WSopt}/\n {
1240 yyextra->condCtx=YY_START;
1241 handleCondSectionId(yyscanner," "); // fake section id causing the section to be hidden unconditionally
1242 }
1243<CondLine>\n |
1244<CondLine>. { // forgot section id?
1245 handleCondSectionId(yyscanner," "); // fake section id causing the section to be hidden unconditionally
1246 if (*yytext=='\n') { copyToOutput(yyscanner,"\n");}
1247 }
1248<CComment,ReadLine,IncludeFile,Verbatim,VerbatimCode>{CMD}[a-z_A-Z][a-z_A-Z0-9-]* { // expand alias without arguments
1249 replaceAliases(yyscanner,yytext,YY_START==ReadLine && yyextra->readLineCtx==SComment);
1250 }
1251<CComment,ReadLine,IncludeFile,Verbatim,VerbatimCode>{B}?{CMD}"ilinebr"{B}{CMD}"ialias{" { // expand alias with arguments
1252 yyextra->lastBlockContext=YY_START;
1253 yyextra->blockCount=1;
1254 int extraSpace = (yytext[0]==' '? 1:0);
1255 yyextra->aliasString=yytext+9+extraSpace;
1256 yyextra->aliasCmd=yytext+9+extraSpace;
1257 yyextra->lastEscaped=0;
1258 BEGIN( ReadAliasArgs );
1259 }
1260<CComment,ReadLine,IncludeFile,Verbatim,VerbatimCode>{CMD}[a-z_A-Z][a-z_A-Z0-9-]*"{" { // expand alias with arguments
1261 yyextra->lastBlockContext=YY_START;
1262 yyextra->blockCount=1;
1263 yyextra->aliasString=yytext;
1264 yyextra->aliasCmd=yytext;
1265 yyextra->lastEscaped=0;
1266 BEGIN( ReadAliasArgs );
1267 }
1268<ReadAliasArgs>^[ \t]*"*" { // skip leading *
1269 }
1270<ReadAliasArgs>^[ \t]*{CPPC}[/!]/[^\n]* { // skip leading special comments (see bug 618079)
1271 }
1272<ReadAliasArgs>[^{}\n\\\*]+ {
1273 yyextra->aliasString+=yytext;
1274 yyextra->lastEscaped=FALSE;
1275 }
1276<ReadAliasArgs>"\\" {
1277 if (yyextra->lastEscaped) yyextra->lastEscaped=FALSE;
1278 else yyextra->lastEscaped=TRUE;
1279 yyextra->aliasString+=yytext;
1280 }
1281<ReadAliasArgs>{CMD}("endverbatim"|"endiliteral"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"f$"|"f]"|"f}"|"f)") { /* end of verbatim block */
1282 yyextra->aliasString+=yytext;
1283 if (yyextra->inVerbatim && &yytext[1]==yyextra->blockName)
1284 // For verbatim sections we do not support matching end block markers inside
1285 // alias arguments. Instead this will end the verbatim block.
1286 // This is needed to support an alias definition
1287 // like startalign=" \latexonly\noalign{\endlatexonly" were otherwise
1288 // the scanner would try to find a matching closing brace for noalign and then
1289 // skip over the \endlatexonly command.
1290 {
1291 copyToOutput(yyscanner,yyextra->aliasString.view());
1292 yyextra->inVerbatim=false;
1293 BEGIN(yyextra->lastCommentContext);
1294 }
1295 }
1296<ReadAliasArgs>\n {
1297 yyextra->aliasString+=yytext;
1298 yyextra->lastEscaped=FALSE;
1299 if (yyextra->inVerbatim) // for verbatim sections we do not support multi-line
1300 // alias arguments.
1301 {
1302 copyToOutput(yyscanner,yyextra->aliasString.view());
1303 BEGIN( yyextra->lastBlockContext );
1304 }
1305 }
1306<ReadAliasArgs>"{" {
1307 yyextra->aliasString+=yytext;
1308 if (!yyextra->lastEscaped) yyextra->blockCount++;
1309 yyextra->lastEscaped=FALSE;
1310 }
1311<ReadAliasArgs>"}" {
1312 yyextra->aliasString+=yytext;
1313 if (!yyextra->lastEscaped) yyextra->blockCount--;
1314 if (yyextra->blockCount==0)
1315 {
1316 replaceAliases(yyscanner,yyextra->aliasString.view(),
1317 yyextra->lastBlockContext==ReadLine && yyextra->readLineCtx==SComment);
1318 BEGIN( yyextra->lastBlockContext );
1319 }
1320 yyextra->lastEscaped=FALSE;
1321 }
1322<ReadAliasArgs>. {
1323 yyextra->aliasString+=yytext;
1324 yyextra->lastEscaped=FALSE;
1325 }
1326<CopyLine>. {
1327 copyToOutput(yyscanner,yytext,yyleng);
1328 }
1329<CopyLine>\n {
1330 copyToOutput(yyscanner,yytext,yyleng);
1331 yyextra->insertCppCommentMarker=false;
1332 BEGIN(yyextra->readLineCtx);
1333 }
1334<ReadLine>``` {
1335 copyToOutput(yyscanner,yytext,yyleng);
1336 }
1337<ReadLine>`[^`]+` {
1338 copyToOutput(yyscanner,yytext,yyleng);
1339 }
1340<ReadLine>{CMD}{CMD} |
1341<ReadLine>. {
1342 copyToOutput(yyscanner,yytext,yyleng);
1343 }
1344<IncludeFile>. {
1345 copyToOutput(yyscanner,yytext,yyleng);
1346 }
1347<IncludeFile>\n {
1348 copyToOutput(yyscanner,yytext,yyleng);
1349 insertCommentStart(yyscanner);
1350 }
1351<*>. {
1352 copyToOutput(yyscanner,yytext,yyleng);
1353 }
1354<<EOF>> {
1355 if (YY_START == ReadAliasArgs)
1356 {
1357 warn(yyextra->fileName,yyextra->lineNr,
1358 "Reached end of file while still searching closing '}}' of an alias argument (probable start: '{}')",
1359 yyextra->aliasCmd);
1360 }
1361 if (yyextra->includeStack.empty())
1362 {
1363 yyextra->insertCppCommentMarker=false;
1364 yyterminate();
1365 }
1366 else // switch back to parent file
1367 {
1368 std::unique_ptr<commentcnv_FileState> &fs = yyextra->includeStack.back();
1369 YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
1370 yy_switch_to_buffer(fs->bufState, yyscanner);
1371 yy_delete_buffer(oldBuf, yyscanner);
1372 BEGIN(fs->oldState);
1373 yyextra->fileName = fs->oldFileName;
1374 yyextra->lineNr = fs->oldLineNr;
1375 yyextra->inBuf = fs->oldFileBuf;
1376 yyextra->inBufPos = fs->oldFileBufPos;
1377 yyextra->includeCtx = fs->oldIncludeCtx;
1378 QCString lineStr= " \\ifile \""+yyextra->fileName+"\" \\iline "+QCString().setNum(yyextra->lineNr)+" ";
1379 if (fs->oldRaiseLvl!=yyextra->raiseLevel)
1380 {
1381 lineStr+="\\iraise " + std::to_string(fs->oldRaiseLvl)+ " ";
1382 }
1383 if (fs->oldRaiseLbl!=yyextra->raiseLabel)
1384 {
1385 lineStr+="\\iprefix \"" + fs->oldRaiseLbl + "\" ";
1386 }
1387 lineStr+="\\ilinebr ";
1388 yyextra->raiseLevel = fs->oldRaiseLvl;
1389 yyextra->raiseLabel = fs->oldRaiseLbl;
1390 copyToOutput(yyscanner,lineStr.view());
1391 yyextra->includeStack.pop_back();
1392 //printf("<<EOF>> switch back to %s line %d inbufPos=%d outbufPos=%d\n",
1393 // qPrint(yyextra->fileName),yyextra->lineNr,yyextra->inBufPos,yyextra->outBuf.curPos());
1394 }
1395 }
QCString & setNum(short n)
Definition qcstring.h:444
std::string_view view() const
Definition qcstring.h:161
#define yyterminate()
1396 /*
1397<*>\n { fprintf(stderr,"Lex scanner %s (%s) default rule newline for state %s.\n", __FILE__, qPrint(yyextra->fileName),stateToString(YY_START));}
1398 */
1399%%
1400
1401static bool parseIncludeOptions(yyscan_t yyscanner,std::string_view s)
1402{
1403 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1404
1405 //printf("parseIncludeOptions=%s\n",qPrint(QCString(s)));
1406 size_t optIdxStart = s.find('{');
1407 size_t optIdxEnd = optIdxStart!=std::string::npos ? s.find("}",optIdxStart+1) : std::string::npos;
1408 std::string cmdName;
1409 StringVector optList;
1410 if (optIdxStart == std::string::npos) // no options
1411 {
1412 cmdName = stripWhiteSpace(s.substr(1)); // to remove {CMD}
1413 }
1414 else // options present
1415 {
1416 cmdName = stripWhiteSpace(s.substr(1,optIdxStart-1)); // to remove {CMD}
1417 optList = split(std::string{s.substr(optIdxStart+1,optIdxEnd-optIdxStart-1)},",");
1418 }
1419 bool isDoc = cmdName=="includedoc" || cmdName=="snippetdoc";
1420 for (const auto &opt : optList)
1421 {
1422 if (stripWhiteSpace(opt)==std::string_view{"doc"})
1423 {
1424 isDoc=true;
1425 break;
1426 }
1427 }
1428
1429 if (isDoc)
1430 {
1431 for (const auto &opt : optList)
1432 {
1433 std::string_view locOpt = stripWhiteSpace(opt);
1434 size_t posEqual = locOpt.find('=');
1435 std::string_view option = posEqual!=std::string::npos ? stripWhiteSpace(locOpt.substr(0,posEqual)) : locOpt;
1436 std::string_view value = posEqual!=std::string::npos ? stripWhiteSpace(locOpt.substr(posEqual+1)) : std::string_view();
1437
1438 if (option==std::string_view{"doc"} && value.empty())
1439 {
1440 }
1441 else if (option==std::string_view{"raise"} && !value.empty())
1442 {
1443 yyextra->raiseIncrement = atoi(value.data());
1444 if (yyextra->raiseLevel+yyextra->raiseIncrement>=SectionType::MaxLevel) // check range
1445 {
1446 warn(yyextra->fileName,yyextra->lineNr,"Raising section level from {} to {}, exceeds allowed range [0-{}], adjusting",
1447 yyextra->raiseLevel,yyextra->raiseLevel+yyextra->raiseIncrement,SectionType::MaxLevel-1);
1448 yyextra->raiseIncrement = std::max(0,SectionType::MaxLevel-1-yyextra->raiseLevel);
1449 }
1450 }
1451 else if (option==std::string_view{"prefix"} && !value.empty())
1452 {
1453 yyextra->raisePrefix = value;
1454 }
1455 else
1456 {
1457 warn(yyextra->fileName,yyextra->lineNr,"Unsupported option '{}' for {} command",option, cmdName);
1458 }
1459 }
1460 }
1461
1462 return isDoc;
1463}
1464
1465
1466static void replaceCommentMarker(yyscan_t yyscanner,std::string_view s)
1467{
1468 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1469 if (s.empty()) return;
1470 size_t p = 0;
1471 size_t len = s.length();
1472 char c = 0;
1473 // copy leading blanks
1474 while (p<len && (c=s[p]) && (c==' ' || c=='\t' || c=='\n'))
1475 {
1476 yyextra->outBuf+=c;
1477 if (c=='\n') { yyextra->lineNr++; yyextra->col=0; } else { yyextra->col++; }
1478 p++;
1479 }
1480 // replace start of comment marker by blanks and the last character by a *
1481 int blanks=0;
1482 while (p<len && (c=s[p]) && (c=='/' || c=='!' || c=='#'))
1483 {
1484 blanks++;
1485 p++;
1486 if (p<len && s[p]=='<') // comment-after-item marker
1487 {
1488 blanks++;
1489 p++;
1490 }
1491 if (c=='!') // end after first !
1492 {
1493 break;
1494 }
1495 }
1496 if (blanks>0)
1497 {
1498 while (blanks>2)
1499 {
1500 yyextra->outBuf+=' ';
1501 yyextra->col++;
1502 blanks--;
1503 }
1504 if (blanks>1) { yyextra->outBuf+='*'; yyextra->col++; }
1505 yyextra->outBuf+=' ';
1506 yyextra->col++;
1507 }
1508 // copy comment line to output
1509 yyextra->outBuf+=s.substr(p);
1510 yyextra->col+=s.substr(p).length();
1511}
1512
1513static inline int computeIndent(const char *s)
1514{
1515 int col=0;
1516 int tabSize=Config_getInt(TAB_SIZE);
1517 const char *p=s;
1518 char c = 0;
1519 while ((c=*p++))
1520 {
1521 if (c==' ') col++;
1522 else if (c=='\t') col+=tabSize-(col%tabSize);
1523 else break;
1524 }
1525 return col;
1526}
1527
1528static inline void copyToOutput(yyscan_t yyscanner,std::string_view s)
1529{
1530 int tabSize=Config_getInt(TAB_SIZE);
1531 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1532 size_t len = s.length();
1533 if (yyextra->skip) // only add newlines.
1534 {
1535 for (size_t i=0;i<len;i++)
1536 {
1537 switch(s[i])
1538 {
1539 case '\n':
1540 yyextra->outBuf+='\n';
1541 yyextra->lineNr++;
1542 yyextra->col=0;
1543 break;
1544 case '\t':
1545 yyextra->col+=tabSize-(yyextra->col%tabSize);
1546 break;
1547 default:
1548 yyextra->col++;
1549 break;
1550 }
1551 }
1552 }
1553 else if (len>0)
1554 {
1555 yyextra->outBuf+=s;
1556 for (size_t i=0;i<len;i++)
1557 {
1558 switch (s[i])
1559 {
1560 case '\n': yyextra->col=0;
1561 //fprintf(stderr,"---> copy %d\n",g_lineNr);
1562 yyextra->lineNr++; break;
1563 case '\t': yyextra->col+=tabSize-(yyextra->col%tabSize); break;
1564 default: yyextra->col++; break;
1565 }
1566 }
1567 }
1568}
1569
1570static inline void copyToOutput(yyscan_t yyscanner,const char *s,int len)
1571{
1572 copyToOutput(yyscanner,std::string_view{s,(size_t)len});
1573}
1574
1575static void clearCommentStack(yyscan_t yyscanner)
1576{
1577 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1578 while (!yyextra->commentStack.empty()) yyextra->commentStack.pop();
1579}
1580
1581static void startCondSection(yyscan_t yyscanner,const QCString &sectId)
1582{
1583 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1584 //printf("startCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
1585 CondParser prs;
1586 bool expResult = prs.parse(yyextra->fileName,yyextra->lineNr,sectId);
1587 yyextra->condStack.emplace(yyextra->lineNr,sectId,yyextra->skip);
1588 if (!expResult) // not enabled
1589 {
1590 yyextra->skip=TRUE;
1591 }
1592}
1593
1594static void endCondSection(yyscan_t yyscanner)
1595{
1596 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1597 if (yyextra->condStack.empty())
1598 {
1599 warn(yyextra->fileName,yyextra->lineNr,"Found \\endcond command without matching \\cond");
1600 yyextra->skip=FALSE;
1601 }
1602 else
1603 {
1604 const commentcnvYY_CondCtx &ctx = yyextra->condStack.top();
1605 yyextra->skip=ctx.skip;
1606 yyextra->condStack.pop();
1607 }
1608 //printf("endCondSection: skip=%d stack=%d\n",g_skip,g_condStack.count());
1609}
1610
1611static void handleCondSectionId(yyscan_t yyscanner,const char *expression)
1612{
1613 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1614 bool oldSkip=yyextra->skip;
1615 startCondSection(yyscanner,QCString(expression));
1616 if ((yyextra->condCtx==CComment || yyextra->readLineCtx==SComment) &&
1617 !oldSkip && yyextra->skip)
1618 {
1619 if (yyextra->lang!=SrcLangExt::Python &&
1620 yyextra->lang!=SrcLangExt::VHDL &&
1621 yyextra->lang!=SrcLangExt::Markdown &&
1622 yyextra->lang!=SrcLangExt::Fortran)
1623 {
1624 yyextra->outBuf+='*';
1625 yyextra->outBuf+='/';
1626 yyextra->col+=2;
1627 }
1628 }
1629 if (yyextra->readLineCtx==SComment)
1630 {
1631 BEGIN(SComment);
1632 }
1633 else
1634 {
1635 BEGIN(yyextra->condCtx);
1636 }
1637}
1638
1639/** Returns the section of text, in between a pair of markers.
1640 * Full lines are returned, excluding the lines on which the markers appear.
1641 * \sa routine lineBlock
1642 */
1643static QCString extractBlock(const QCString &text,const QCString &marker,int &blockPos)
1644{
1645 QCString result;
1646 int p=0,i=-1;
1647 bool found=FALSE;
1648
1649 // find the character positions of the markers
1650 int m1 = text.find(marker);
1651 if (m1==-1) return result;
1652 int m2 = text.find(marker,m1+static_cast<int>(marker.length()));
1653 if (m2==-1) return result;
1654
1655 // find start and end line positions for the markers
1656 int l1=-1,l2=-1;
1657 while (!found && (i=text.find('\n',p))!=-1)
1658 {
1659 found = (p<=m1 && m1<i); // found the line with the start marker
1660 p=i+1;
1661 }
1662 l1=p;
1663 blockPos=p;
1664 int lp=i;
1665 if (found)
1666 {
1667 while ((i=text.find('\n',p))!=-1)
1668 {
1669 if (p<=m2 && m2<i) // found the line with the end marker
1670 {
1671 l2=p;
1672 break;
1673 }
1674 p=i+1;
1675 lp=i;
1676 }
1677 }
1678 if (l2==-1) // marker at last line without newline (see bug706874)
1679 {
1680 l2=lp;
1681 }
1682 return l2>l1 ? text.mid(l1,l2-l1) : QCString();
1683}
1684
1685static void insertCommentStart(yyscan_t yyscanner)
1686{
1687 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1688 int startCol=yyextra->blockHeadCol;
1689 int contentCol=yyextra->insertCommentCol;
1690 int markerSpace=contentCol-startCol;
1691 //printf("insertCommentStart startCol=%d contentCol=%d mlBrief=%d insertCppCommentMarker=%d\n",
1692 // yyextra->blockHeadCol,yyextra->insertCommentCol,yyextra->mlBrief,yyextra->insertCppCommentMarker);
1693 std::string marker;
1694 if (yyextra->lang==SrcLangExt::Python) // need to insert # or space
1695 {
1696 if (yyextra->pythonDocString) // """! style comment
1697 {
1698 marker=" ";
1699 }
1700 else
1701 {
1702 marker="# ";
1703 }
1704 }
1705 else if (yyextra->lang==SrcLangExt::Fortran) // need to insert !!
1706 {
1707 marker="!! ";
1708 }
1709 else if (yyextra->lang==SrcLangExt::Markdown)
1710 {
1711 marker=" ";
1712 }
1713 else if (yyextra->insertCppCommentMarker) // need to insert ///
1714 {
1715 marker="/// ";
1716 if (startCol>0)
1717 {
1718 // insert `///` instead of '* '
1719 startCol--;
1720 markerSpace++;
1721 }
1722 }
1723 else // need to insert *
1724 {
1725 marker="* ";
1726 }
1727 int i=0;
1728 for (;i<startCol;i++)
1729 {
1730 copyToOutput(yyscanner," ");
1731 }
1732 if (static_cast<int>(marker.length())<=markerSpace && !yyextra->firstIncludeLine)
1733 {
1734 copyToOutput(yyscanner,marker);
1735 i+=marker.length();
1736 }
1737 for (;i<contentCol;i++)
1738 {
1739 copyToOutput(yyscanner," ");
1740 }
1741 yyextra->firstIncludeLine = false;
1742}
1743
1744static bool readIncludeFile(yyscan_t yyscanner,const QCString &inc,const QCString &blockId)
1745{
1746 //printf("readIncludeFile(inc=%s,blockId=%s)\n",qPrint(inc),qPrint(blockId));
1747 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1748 bool ambig = false;
1749 QCString absFileName = findFilePath(inc,ambig);
1750 FileInfo fi(absFileName.str());
1751 if (!absFileName.isEmpty() && fi.exists() && fi.isFile())
1752 {
1753 if (ambig)
1754 {
1755 warn_doc_error(yyextra->fileName,yyextra->lineNr,"included file name '{}' is ambiguous"
1756 "Possible candidates:\n{}",inc, showFileDefMatches(Doxygen::exampleNameLinkedMap,inc));
1757 }
1758 bool alreadyProcessed = std::any_of(
1759 yyextra->includeStack.begin(),
1760 yyextra->includeStack.end(),
1761 [&absFileName,&blockId](const auto &lfs)
1762 { return lfs->fileName==absFileName && lfs->blockId==blockId; }
1763 );
1764
1765 if (alreadyProcessed)
1766 {
1767 if (!blockId.isEmpty())
1768 {
1769 warn_doc_error(yyextra->fileName,yyextra->lineNr,"recursive usage of '\\snippet{{doc}}' block with name '{}' and file name '{}', skipping",
1770 blockId,absFileName);
1771 }
1772 else
1773 {
1774 warn_doc_error(yyextra->fileName,yyextra->lineNr,"recursive usage of '\\include{{doc}}' with file name '{}', skipping", absFileName);
1775 }
1776 return false;
1777 }
1778
1779 auto fs = std::make_unique<commentcnv_FileState>();
1780 if (!readInputFile(absFileName,fs->fileBuf,false))
1781 {
1782 warn_doc_error(yyextra->fileName,yyextra->lineNr,"\\{}{{doc}} file '{}' could not be read",blockId.isEmpty()?"include":"snippet",absFileName);
1783 fs.reset();
1784 return false;
1785 }
1786 int lineNr=1;
1787 if (!blockId.isEmpty())
1788 {
1789 QCString incText { fs->fileBuf };
1790 int count = incText.contains(blockId.data());
1791 if (count!=2)
1792 {
1793 warn_doc_error(yyextra->fileName,yyextra->lineNr,"block marked with {} for \\snippet{{doc}} should appear twice in file {}, found it {:d} times, skipping",
1794 blockId,absFileName,count);
1795 return false;
1796 }
1797 lineNr = lineBlock(incText, blockId);
1798 int blockPos = 0;
1799 incText = extractBlock(incText, blockId, blockPos);
1800 fs->fileBuf.clear();
1801 if (!incText.isEmpty())
1802 {
1803 fs->fileBuf.append(incText.str());
1804 }
1805 }
1806 int oldRaiseLevel = yyextra->raiseLevel;
1807 QCString oldRaiseLabel = yyextra->raiseLabel;
1808 yyextra->raiseLevel+=yyextra->raiseIncrement;
1809 yyextra->raiseLabel+=yyextra->raisePrefix;
1810 QCString lineStr=" \\ifile \""+absFileName+"\" \\iline " + std::to_string(lineNr)+" ";
1811 if (yyextra->raiseLevel>0)
1812 {
1813 lineStr+="\\iraise " + std::to_string(yyextra->raiseLevel)+" ";
1814 }
1815 if (!yyextra->raiseLabel.isEmpty())
1816 {
1817 lineStr+="\\iprefix \"" + yyextra->raiseLabel + "\" ";
1818 }
1819 lineStr+="\\ilinebr ";
1820 copyToOutput(yyscanner,lineStr.view());
1821
1822 fs->fileName = absFileName;
1823 fs->bufState = YY_CURRENT_BUFFER;
1824 fs->oldLineNr = yyextra->lineNr;
1825 fs->oldFileName = yyextra->fileName;
1826 fs->oldState = yyextra->includeCtx;
1827 fs->oldFileBuf = yyextra->inBuf;
1828 fs->oldFileBufPos = yyextra->inBufPos;
1829 fs->oldIncludeCtx = yyextra->includeCtx;
1830 fs->oldRaiseLvl = oldRaiseLevel;
1831 fs->oldRaiseLbl = oldRaiseLabel;
1832 fs->blockId = blockId;
1833 yy_switch_to_buffer(yy_create_buffer(nullptr, YY_BUF_SIZE, yyscanner),yyscanner);
1834 yyextra->fileName = absFileName;
1835 yyextra->lineNr = lineNr;
1836 yyextra->inBuf = &fs->fileBuf;
1837 yyextra->inBufPos = 0;
1838 yyextra->includeStack.push_back(std::move(fs));
1839 insertCommentStart(yyscanner);
1840 //printf("switched to %s\n",qPrint(fileName));
1841 }
1842 else
1843 {
1844 warn_doc_error(yyextra->fileName,yyextra->lineNr,"\\{}{{doc}} file '{}' not found",blockId.isEmpty()?"include":"snippet",inc);
1845 return false;
1846 }
1847 return true;
1848}
1849
1850/** copies string \a s with length \a len to the output, while
1851 * replacing any alias commands found in the string.
1852 */
1853static void replaceAliases(yyscan_t yyscanner,std::string_view s,bool replaceComment)
1854{
1855 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1856 if (s.empty()) return;
1857 size_t pos = s.find('{');
1858 std::string cmd { s.substr(1, pos!=std::string::npos ? pos-1 : s.length()-1) };
1859 auto &expAlias = yyextra->expandedAliases;
1860 if (std::find(expAlias.begin(),expAlias.end(),cmd)!=std::end(expAlias))
1861 {
1862 copyToOutput(yyscanner,s);
1863 return; // prevent recursive expansion
1864 }
1865 else if (cmd=="ialias")
1866 {
1867 if (s.length()>cmd.length()+3) // \cmd{value}
1868 {
1869 std::string value { s.substr(cmd.length()+2,s.length()-cmd.length()-3) };
1870 //printf("removing value '%s'\n",qPrint(value));
1871 expAlias.erase(std::remove(expAlias.begin(),expAlias.end(),value),expAlias.end());
1872 }
1873 return;
1874 }
1875 std::string result = resolveAliasCmd(s);
1876 if (yyextra->inVerbatim) // inside verbatim blocks (like @code...@endcode) only expand aliases if
1877 // their expansion contains the matching end block marker.
1878 {
1879 std::string blk = yyextra->blockName.str();
1880 assert(!blk.empty());
1881 bool isNamedCommand=isId(blk[0]); // true for e.g. @endcode, false for e.g. ~~~
1882 size_t i=0,p=0;
1883 bool found=false;
1884 while ((i=result.find(blk,p))!=std::string::npos && !found) // for each match of blk in result
1885 {
1886 found = !isNamedCommand || // e.g. '~~~' or '-->'
1887 (i>0 && (result[i-1]=='\\' || result[i-1]=='@') && !isId(result[i+blk.length()])); // e.g. '@endcode' but not ~endcode or @endcodex
1888 p = i+yyextra->blockName.length();
1889 }
1890 //printf("blk=%s result=%s found=%d\n",qPrint(blk),qPrint(result),found);
1891 if (!found) // treat alias as part of the verbatim block
1892 {
1893 copyToOutput(yyscanner,s);
1894 return;
1895 }
1896 }
1897 //printf("replaceAliases(%s)->'%s' replaceComment=%d\n",qPrint(s),qPrint(result),replaceComment);
1898 if (result!=s)
1899 {
1900 if (replaceComment) // In case we are replacing a multiline /// comment by a C style comment
1901 // and we have new lines in the alias argument, we need to place back a /// for each new line
1902 // to prevent breaking the multiline comment into multiple C style comments
1903 {
1904 result = substituteStringView(result,"\n","\n///");
1905 }
1906 expAlias.push_back(cmd);
1907 // add a ialias command to allow expansion of cmd again
1908 result += " \\ilinebr \\ialias{";
1909 result += cmd;
1910 result += "}";
1911 for (int i=(int)result.length()-1; i>=0; i--)
1912 {
1913 unput(result[i]);
1914 }
1915 }
1916 else
1917 {
1918 copyToOutput(yyscanner,result);
1919 }
1920}
1921
1922
1923static int yyread(yyscan_t yyscanner,char *buf,int max_size)
1924{
1925 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1926 int bytesInBuf = static_cast<int>(yyextra->inBuf->size())-yyextra->inBufPos;
1927 int bytesToCopy = std::min(max_size,bytesInBuf);
1928 memcpy(buf,yyextra->inBuf->data()+yyextra->inBufPos,bytesToCopy);
1929 yyextra->inBufPos+=bytesToCopy;
1930 return bytesToCopy;
1931}
1932
1933static void replaceComment(yyscan_t yyscanner,int offset)
1934{
1935 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1936 if (yyextra->mlBrief || yyextra->skip)
1937 {
1938 copyToOutput(yyscanner,yytext,yyleng);
1939 }
1940 else
1941 {
1942 int i=computeIndent(&yytext[offset]);
1943 //printf("i=%d blockHeadCol=%d\n",i,yyextra->blockHeadCol);
1944 if (i==yyextra->blockHeadCol || i+1==yyextra->blockHeadCol)
1945 {
1946 replaceCommentMarker(yyscanner,std::string_view(yytext,yyleng));
1947 }
1948 else
1949 {
1950 copyToOutput(yyscanner," */");
1951 for (i=(int)yyleng-1;i>=0;i--) unput(yytext[i]);
1952 yyextra->inSpecialComment=FALSE;
1953 BEGIN(Scan);
1954 }
1955 }
1956}
1957
1958/*! This function does three things:
1959 * -# It converts multi-line C++ style comment blocks (that are aligned)
1960 * to C style comment blocks (if MULTILINE_CPP_IS_BRIEF is set to NO).
1961 * -# It replaces aliases with their definition (see ALIASES)
1962 * -# It handles conditional sections (cond...endcond blocks)
1963 */
1964void convertCppComments(const std::string &inBuf,std::string &outBuf,const std::string &fn)
1965{
1966 QCString fileName { fn };
1967 yyscan_t yyscanner;
1968 commentcnvYY_state extra(&inBuf,outBuf);
1969 commentcnvYYlex_init_extra(&extra,&yyscanner);
1970#ifdef FLEX_DEBUG
1971 commentcnvYYset_debug(Debug::isFlagSet(Debug::Lex_commentcnv)?1:0,yyscanner);
1972#endif
1973 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1974 //printf("convertCppComments(%s)\n",qPrint(fileName));
1975 yyextra->inBufPos = 0;
1976 yyextra->col = 0;
1977 yyextra->mlBrief = Config_getBool(MULTILINE_CPP_IS_BRIEF);
1978 yyextra->skip = FALSE;
1979 yyextra->fileName = fileName;
1980 yyextra->lang = getLanguageFromFileName(fileName);
1981 yyextra->pythonDocString = FALSE;
1982 yyextra->lineNr = 1;
1983 yyextra->raiseLevel = 0;
1984 yyextra->raiseLabel = "";
1985 yyextra->raiseIncrement = 0;
1986 yyextra->raisePrefix = "";
1987 yyextra->insertCppCommentMarker=false;
1988 yyextra->expandedAliases.clear();
1989 while (!yyextra->condStack.empty()) yyextra->condStack.pop();
1990 clearCommentStack(yyscanner);
1991 yyextra->vhdl = FALSE;
1992
1993 DebugLex debugLex(Debug::Lex_commentcnv,__FILE__, qPrint(fileName));
1994 yyextra->isFixedForm = FALSE;
1995 if (yyextra->lang==SrcLangExt::Fortran)
1996 {
1998 yyextra->isFixedForm = recognizeFixedForm(QCString(inBuf),fmt);
1999 }
2000
2001 if (yyextra->lang==SrcLangExt::Markdown)
2002 {
2003 yyextra->nestingCount=0;
2004 BEGIN(CComment);
2005 yyextra->commentStack.push(yyextra->lineNr);
2006 }
2007 else
2008 {
2009 BEGIN(Scan);
2010 }
2011 yylex(yyscanner);
2012 while (!yyextra->condStack.empty())
2013 {
2014 const commentcnvYY_CondCtx &ctx = yyextra->condStack.top();
2015 QCString sectionInfo(" ");
2016 if (ctx.sectionId!=" ") sectionInfo.sprintf(" with label '%s' ",ctx.sectionId.stripWhiteSpace().data());
2017 warn(yyextra->fileName,ctx.lineNr,"Conditional section{}does not have "
2018 "a corresponding \\endcond command within this file.",sectionInfo.data());
2019 yyextra->condStack.pop();
2020 }
2021 if (yyextra->nestingCount>0 && yyextra->lang!=SrcLangExt::Markdown && yyextra->lang!=SrcLangExt::Fortran)
2022 {
2023 QCString lines;
2024 bool first = true;
2025 while (!yyextra->commentStack.empty())
2026 {
2027 int lineNr = yyextra->commentStack.top();
2028 if (!first) lines += ", ";
2029 lines += QCString().setNum(lineNr);
2030 first = false;
2031 yyextra->commentStack.pop();
2032 }
2033 warn(yyextra->fileName,yyextra->lineNr,"Reached end of file while still inside a (nested) comment. "
2034 "Nesting level {} (possible line reference(s): {})",yyextra->nestingCount,lines);
2035 }
2036 yyextra->nestingCount = 0;
2038 {
2039 Debug::print(Debug::CommentCnv,0,"-----------\nCommentCnv: {}\n"
2040 "output=[\n{}]\n-----------\n",fileName,yyextra->outBuf
2041 );
2042 }
2043 commentcnvYYlex_destroy(yyscanner);
2044}
2045
2046
2047//----------------------------------------------------------------------------
2048
2049#include "commentcnv.l.h"
std::string resolveAliasCmd(std::string_view aliasCmd)
Definition aliases.cpp:504
Copyright (C) 1997-2015 by Dimitri van Heesch.
Definition condparser.h:28
bool parse(const QCString &fileName, int lineNr, const QCString &expr)
Copyright (C) 1997-2015 by Dimitri van Heesch.
@ Lex_commentcnv
Definition debug.h:53
@ CommentCnv
Definition debug.h:31
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
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:103
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
bool isFile() const
Definition fileinfo.cpp:63
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
const std::string & str() const
Definition qcstring.h:537
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:159
int contains(char c, bool cs=TRUE) const
Definition qcstring.cpp:143
static constexpr int MaxLevel
Definition section.h:39
static int yyread(yyscan_t yyscanner, char *buf, int max_size)
Definition code.l:3995
void convertCppComments(const std::string &inBuf, std::string &outBuf, const std::string &fn)
Converts the comments in a file.
static QCString extractBlock(const QCString &text, const QCString &marker, int &blockPos)
Returns the section of text, in between a pair of markers.
#define YY_BUF_SIZE
Definition commentcnv.l:19
#define Config_getInt(name)
Definition config.h:34
int yylex(void)
std::vector< std::string > StringVector
Definition containers.h:33
#define warn_doc_error(file, line, fmt,...)
Definition message.h:112
Definition message.h:144
const char * qPrint(const char *s)
Definition qcstring.h:672
std::string substituteStringView(std::string_view s, std::string_view toReplace, std::string_view replaceWith)
Returns a new string where occurrences of substring toReplace in string s are replaced by string repl...
Definition stringutil.h:50
FortranFormat
Definition types.h:572
QCString findFilePath(const QCString &file, bool &ambig)
Definition util.cpp:3499
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5718
int lineBlock(const QCString &text, const QCString &marker)
Returns the line number of the line following the line with the marker.
Definition util.cpp:6383
bool readInputFile(const QCString &fileName, std::string &contents, bool filter, bool isSourceCode)
read a file name fileName and optionally filter and transcode it
Definition util.cpp:6047
bool recognizeFixedForm(const QCString &contents, FortranFormat format)
Definition util.cpp:6854
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3542
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:7129
FortranFormat convertFileNameFortranParserCode(QCString fn)
Definition util.cpp:6907
bool isId(int c)
Definition util.h:208