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