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