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