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