1 /******************************************************************************
3 * Copyright (C) 1997-2020 by Dimitri van Heesch.
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.
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
15 /* This code is based on the work done by the MoxyPyDoxy team
16 * (Linda Leong, Mike Rivera, Kim Truong, and Gabriel Estrada)
17 * in Spring 2005 as part of CS 179E: Compiler Design Project
18 * at the University of California, Riverside; the course was
19 * taught by Peter H. Froehlich <phf@acm.org>.
22 %option never-interactive
23 %option prefix="pycodeYY"
25 %option extra-type="struct pycodeYY_state *"
26 %option noyy_top_state
29 // forward declare yyscan_t to improve type safety
30 #define YY_TYPEDEF_YY_SCANNER_T
32 typedef yyguts_t *yyscan_t;
38 #include <unordered_map>
49 #include "outputlist.h"
51 #include "membername.h"
52 #include "searchindex.h"
55 #include "classlist.h"
57 #include "namespacedef.h"
59 #include "scopedtypevariant.h"
60 #include "symbolresolver.h"
62 // Toggle for some debugging info
63 //#define DBG_CTX(x) fprintf x
64 #define DBG_CTX(x) do { } while(0)
67 #define YY_NO_UNISTD_H 1
69 #define USE_STATE2STRING 0
74 std::unordered_map< std::string, ScopedTypeVariant > codeClassMap;
75 QCString curClassName;
76 StringVector curClassBases;
78 CodeOutputInterface * code = 0;
79 const char * inputString = 0; //!< the code fragment as text
80 yy_size_t inputPosition = 0; //!< read offset during parsing
81 const char * currentFontClass = 0;
82 bool needsTermination = FALSE;
83 const Definition *searchCtx = 0;
84 bool collectXRefs = FALSE;
85 int inputLines = 0; //!< number of line in the code fragment
86 int yyLineNr = 0; //!< current line number
87 const FileDef * sourceFileDef = 0;
88 const Definition * currentDefinition = 0;
89 const MemberDef * currentMemberDef = 0;
90 bool includeCodeFragment = FALSE;
92 int bodyCurlyCount = 0;
93 bool searchingForBody = FALSE;
97 bool insideBody = false;
98 bool exampleBlock = FALSE;
104 bool doubleStringIsDoc = FALSE;
105 bool doubleQuote = FALSE;
106 bool noSuiteFound = FALSE;
107 int stringContext = 0;
109 std::stack<yy_size_t> indents; //!< Tracks indentation levels for scoping in python
111 QCString docBlock; //!< contents of all lines of a documentation block
112 bool endComment = FALSE;
113 VariableContext theVarContext;
114 CallContext theCallContext;
115 SymbolResolver symbolResolver;
116 TooltipManager tooltipManager;
121 static const char *stateToString(int state);
124 static void startCodeLine(yyscan_t yyscanner);
125 static int countLines(yyscan_t yyscanner);
126 static void setCurrentDoc(yyscan_t yyscanner, const QCString &anchor);
127 static void addToSearchIndex(yyscan_t yyscanner, const QCString &text);
128 static const ClassDef *stripClassName(yyscan_t yyscanner,const QCString &s,Definition *d);
129 static void codify(yyscan_t yyscanner,const QCString &text);
130 static void endCodeLine(yyscan_t yyscanner);
131 static void nextCodeLine(yyscan_t yyscanner);
132 static void writeMultiLineCodeLink(yyscan_t yyscanner, CodeOutputInterface &ol, const Definition *d, const QCString &text);
133 static void startFontClass(yyscan_t yyscanner,const char *s);
134 static void endFontClass(yyscan_t yyscanner);
135 static void codifyLines(yyscan_t yyscanner,const QCString &text);
136 static bool getLinkInScope(yyscan_t yyscanner, const QCString &c, const QCString &m,
137 const QCString &memberText, CodeOutputInterface &ol, const QCString &text);
138 static bool getLink(yyscan_t yyscanner, const QCString &className, const QCString &memberName,
139 CodeOutputInterface &ol, const QCString &text=QCString());
140 static void generateClassOrGlobalLink(yyscan_t yyscanner, CodeOutputInterface &ol,
141 const QCString &clName, bool typeOnly=FALSE);
142 static void generateFunctionLink(yyscan_t yyscanner, CodeOutputInterface &ol,
143 const QCString &funcName);
144 static bool findMemberLink(yyscan_t yyscanner, CodeOutputInterface &ol,
145 Definition *sym, const QCString &symName);
146 static void findMemberLink(yyscan_t yyscanner, CodeOutputInterface &ol,
147 const QCString &symName);
148 static void adjustScopesAndSuites(yyscan_t yyscanner,unsigned indentLength);
149 static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size);
151 #if 0 // TODO: call me to store local variables and get better syntax highlighting, see code.l
152 static void addVariable(yyscan_t yyscanner, QCString type, QCString name);
155 //-------------------------------------------------------------------
157 static std::mutex g_searchIndexMutex;
158 static std::mutex g_docCrossReferenceMutex;
159 static std::mutex g_countFlowKeywordsMutex;
161 //-------------------------------------------------------------------
164 #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
174 LETTER [A-Za-z\x80-\xFF]
175 NONEMPTY [A-Za-z0-9_\x80-\xFF]
176 EXPCHAR [#(){}\[\],:.%/\\=`*~|&<>!;+-]
177 NONEMPTYEXP [^ \t\n:]
178 PARAMNONEMPTY [^ \t\n():]
179 IDENTIFIER ({LETTER}|"_")({LETTER}|{DIGIT}|"_")*
180 SCOPE {IDENTIFIER}("."{IDENTIFIER})*
181 CALLANY "("[^)\n]*")"
182 BORDER ([^A-Za-z0-9])
187 TRIDOUBLEQUOTE "\"\"\""
188 LONGSTRINGCHAR [^\\"']
190 LONGSTRINGITEM ({LONGSTRINGCHAR}|{ESCAPESEQ})
191 SMALLQUOTE ("\"\""|"\""|"'"|"''")
192 LONGSTRINGBLOCK ({LONGSTRINGITEM}|{SMALLQUOTE})
194 SHORTSTRING ("'"{SHORTSTRINGITEM}*"'"|'"'{SHORTSTRINGITEM}*'"')
195 SHORTSTRINGITEM ({SHORTSTRINGCHAR}|{ESCAPESEQ})
196 SHORTSTRINGCHAR [^\\\n"]
197 STRINGLITERAL {STRINGPREFIX}?( {SHORTSTRING} | {LONGSTRING})
198 STRINGPREFIX ("r"|"u"|"ur"|"R"|"U"|"UR"|"Ur"|"uR")
199 KEYWORD ("lambda"|"import"|"class"|"assert"|"with"|"as"|"from"|"global"|"async"|"def"|"True"|"False")
200 FLOWKW ("or"|"and"|"is"|"not"|"print"|"for"|"in"|"if"|"try"|"except"|"yield"|"raise"|"break"|"continue"|"pass"|"if"|"return"|"while"|"elif"|"else"|"finally")
201 QUOTES ("\""[^"]*"\"")
202 SINGLEQUOTES ("'"[^']*"'")
204 LONGINTEGER {INTEGER}("l"|"L")
205 INTEGER ({DECIMALINTEGER}|{OCTINTEGER}|{HEXINTEGER})
206 DECIMALINTEGER ({NONZERODIGIT}{DIGIT}*|"0")
207 OCTINTEGER "0"{OCTDIGIT}+
208 HEXINTEGER "0"("x"|"X"){HEXDIGIT}+
211 HEXDIGIT ({DIGIT}|[a-f]|[A-F])
212 FLOATNUMBER ({POINTFLOAT}|{EXPONENTFLOAT})
213 POINTFLOAT ({INTPART}?{FRACTION}|{INTPART}".")
214 EXPONENTFLOAT ({INTPART}|{POINTFLOAT}){EXPONENT}
217 EXPONENT ("e"|"E")("+"|"-")?{DIGIT}+
218 IMAGNUMBER ({FLOATNUMBER}|{INTPART})("j"|"J")
219 ATOM ({IDENTIFIER}|{LITERAL}|{ENCLOSURE})
220 ENCLOSURE ({PARENTH_FORM}|{LIST_DISPLAY}|{DICT_DISPLAY}|{STRING_CONVERSION})
221 LITERAL ({STRINGLITERAL}|{INTEGER}|{LONGINTEGER}|{FLOATNUMBER}|{IMAGNUMBER})
222 PARENTH_FORM "("{EXPRESSION_LIST}?")"
223 TEST ({AND_TEST}("or"{AND_TEST})*|{LAMBDA_FORM})
224 TESTLIST {TEST}( ","{TEST})*","?
225 LIST_DISPLAY "["{LISTMAKER}?"]"
226 LISTMAKER {EXPRESSION}({LIST_FOR}|(","{EXPRESSION})*","?)
227 LIST_ITER ({LIST_FOR}|{LIST_IF})
228 LIST_FOR "for"{EXPRESSION_LIST}"in"{TESTLIST}{LIST_ITER}?
229 LIST_IF "if"{TEST}{LIST_ITER}?
230 DICT_DISPLAY "\{"{KEY_DATUM_LIST}?"\}"
231 KEY_DATUM_LIST {KEY_DATUM}(","{KEY_DATUM})*","?
232 KEY_DATUM {EXPRESSION}":"{EXPRESSION}
233 STRING_CONVERSION "`"{EXPRESSION_LIST}"`"
234 PRIMARY ({ATOM}|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING}|{CALL})
235 ATTRIBUTEREF {PRIMARY}"."{IDENTIFIER}
236 SUBSCRIPTION {PRIMARY}"["{EXPRESSION_LIST}"]"
237 SLICING ({SIMPLE_SLICING}|{EXTENDED_SLICING})
238 SIMPLE_SLICING {PRIMARY}"["{SHORT_SLICE}"]"
239 EXTENDED_SLICING {PRIMARY}"["{SLICE_LIST}"]"
240 SLICE_LIST {SLICE_ITEM}(","{SLICE_ITEM})*","?
241 SLICE_ITEM ({EXPRESSION}|{PROPER_SLICE}|{ELLIPSIS})
242 PROPER_SLICE ({SHORT_SLICE}|{LONG_SLICE})
243 SHORT_SLICE {LOWER_BOUND}?":"{UPPER_BOUND}?
244 LONG_SLICE {SHORT_SLICE}":"{STRIDE}?
245 LOWER_BOUND {EXPRESSION}
246 UPPER_BOUND {EXPRESSION}
249 CALL {PRIMARY}"("({ARGUMENT_LIST}","?)?")"
250 ARGUMENT_LIST ({POSITIONAL_ARGUMENTS}(","{KEYWORD_ARGUMENTS})?(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|{KEYWORD_ARGUMENTS}(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|"*"{EXPRESSION}(",""**"{EXPRESSION})?|"**"{EXPRESSION})
251 POSITIONAL_ARGUMENTS {EXPRESSION}(","{EXPRESSION})*
252 KEYWORD_ARGUMENTS {KEYWORD_ITEM}(","{KEYWORD_ITEM})*
253 KEYWORD_ITEM {IDENTIFIER}"="{EXPRESSION}
254 POWER {PRIMARY}("**"{U_EXPR})?
255 U_EXPR ({POWER}|"-"{U_EXPR}|"+"{U_EXPR}|"\~"{U_EXPR})
256 M_EXPR ({U_EXPR}|{M_EXPR}"*"{U_EXPR}|{M_EXPR}"/""/"{U_EXPR}|{M_EXPR}"/"{U_EXPR}|{M_EXPR}"\%"{U_EXPR})
257 A_EXPR ({M_EXPR}|{A_EXPR}"+"{M_EXPR}|{A_EXPR}"-"{M_EXPR}
258 SHIFT_EXPR ({A_EXPR}|{SHIFT_EXPR}("<<"|">>"){A_EXPR})
259 AND_EXPR ({SHIFT_EXPR}|{AND_EXPR}"\;SPMamp;"{SHIFT_EXPR}
260 XOR_EXPR ({AND_EXPR}|{XOR_EXPR}"\textasciicircum"{AND_EXPR})
261 OR_EXPR ({XOR_EXPR}|{OR_EXPR}"|"{ XOR_EXPR})
263 COMPARISON {OR_EXPR}({COMP_OPERATOR}{OR_EXPR})*
264 COMP_OPERATOR ("<"|">"|"=="|">="|"<="|"<>"|"!="|"is""not"?|"not"?"in")
265 EXPRESSION ({OR_TEST}|{LAMBDA_FORM})
266 OR_TEST ({AND_TEST}|{OR_TEST}"or"{AND_TEST})
267 AND_TEST ({NOT_TEST}|{AND_TEST}"and"{NOT_TEST})
268 NOT_TEST ({COMPARISON}|"not"{NOT_TEST})
269 LAMBDA_FORM "lambda"{PARAMETER_LIST}?":"{EXPRESSION}
270 EXPRESSION_LIST {EXPRESSION}(","{EXPRESSION})*","?
271 SIMPLE_STMT ({EXPRESSION_STMT}|{ASSERT_STMT}|{ASSIGNMENT_STMT}|{AUGMENTED_ASSIGNMENT_STMT}|{PASS_STMT}|{DEL_STMT}|{PRINT_STMT}|{RETURN_STMT}|{YIELD_STMT}|{RAISE_STMT}|{BREAK_STMT}|{CONTINUE_STMT}|{IMPORT_STMT}|{GLOBAL_STMT}|{EXEC_STMT})
272 EXPRESSION_STMT {EXPRESSION_LIST}
273 ASSERT_STMT "assert"{EXPRESSION}(","{EXPRESSION})?
274 ASSIGNMENT_STMT ({TARGET_LIST}"=")+{EXPRESSION_LIST}
275 TARGET_LIST {TARGET}(","{TARGET})*","?
276 TARGET ({IDENTIFIER}|"("{TARGET_LIST}")"|"["{TARGET_LIST}"]"|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING})
290 %x SuiteCaptureIndent
303 startFontClass(yyscanner,"keyword");
304 codify(yyscanner,yytext);
305 endFontClass(yyscanner);
306 BEGIN( FunctionDec );
308 "async"{BB}"def"{BB} {
309 startFontClass(yyscanner,"keyword");
310 codify(yyscanner,yytext);
311 endFontClass(yyscanner);
312 BEGIN( FunctionDec );
316 startFontClass(yyscanner,"keyword");
317 codify(yyscanner,yytext);
318 endFontClass(yyscanner);
322 startFontClass(yyscanner,"keywordtype");
323 codify(yyscanner,yytext);
324 endFontClass(yyscanner);
326 "self."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER}"(" {
327 codify(yyscanner,"self.");
328 findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
330 "self."{IDENTIFIER}/"(" {
331 codify(yyscanner,"self.");
332 findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
334 "self."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER} {
335 codify(yyscanner,"self.");
336 findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
338 "self."{IDENTIFIER} {
339 codify(yyscanner,"self.");
340 findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
342 "cls."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER}"(" {
343 codify(yyscanner,"cls.");
344 findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
346 "cls."{IDENTIFIER}/"(" {
347 codify(yyscanner,"cls.");
348 findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
350 "cls."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER} {
351 codify(yyscanner,"cls.");
352 findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
355 codify(yyscanner,"cls.");
356 findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
358 "@"{SCOPE}{CALLANY}? { // decorator
359 startFontClass(yyscanner,"preprocessor");
360 codify(yyscanner,yytext);
361 endFontClass(yyscanner);
365 <ClassDec>{IDENTIFIER} {
366 generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext);
367 // codify(yyscanner,yytext);
368 yyextra->curClassName = yytext;
369 yyextra->curClassBases.clear();
370 BEGIN( ClassInheritance );
375 codify(yyscanner,yytext);
378 ({IDENTIFIER}".")*{IDENTIFIER} {
381 // that ALL identifiers
384 // it doesn't check to see
385 // that the first parenthesis
388 // This is bad - it should
389 // probably be more strict
390 // about what to accept.
392 yyextra->curClassBases.push_back(yytext);
393 yyextra->insideBody = true;
394 generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext);
395 yyextra->insideBody = false;
396 // codify(yyscanner,yytext);
400 codify(yyscanner,yytext);
403 // be a one-line suite;
404 // found counter-example
407 // Push a class scope
409 std::unique_ptr<ClassDef> classDefToAdd { createClassDef("<code>",1,1,yyextra->curClassName,ClassDef::Class,QCString(),QCString(),FALSE) };
410 ScopedTypeVariant var(yyextra->curClassName);
411 for (const auto &s : yyextra->curClassBases)
413 const ClassDef *baseDefToAdd = 0;
414 // find class in the local scope
415 auto it = yyextra->codeClassMap.find(s);
416 if (it != yyextra->codeClassMap.end())
418 baseDefToAdd = toClassDef(it->second.globalDef());
420 // Try to find class in global scope
423 baseDefToAdd=yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,s.c_str());
426 if (baseDefToAdd && baseDefToAdd->name()!=yyextra->curClassName)
428 var.localDef()->insertBaseClass(baseDefToAdd->name());
431 yyextra->codeClassMap.emplace(std::make_pair(yyextra->curClassName.str(),std::move(var)));
433 // Reset class-parsing variables.
434 yyextra->curClassName.resize(0);
435 yyextra->curClassBases.clear();
437 yyextra->noSuiteFound = TRUE;
445 generateFunctionLink(yyscanner,*yyextra->code,yytext);
449 codify(yyscanner,yytext);
450 BEGIN( FunctionParams );
457 codify(yyscanner,yytext);
460 ({IDENTIFIER}|{PARAMNONEMPTY}+) {
461 codify(yyscanner,yytext);
465 codify(yyscanner,yytext);
469 codifyLines(yyscanner,yytext);
473 codify(yyscanner,yytext);
476 // be a one-line suite;
477 // found counter-example
479 yyextra->noSuiteFound = TRUE;
487 // Position-sensitive rules!
488 // Must come AFTER keyword-triggered rules
489 // Must come BEFORE identifier NONEMPTY-like rules
490 // to syntax highlight.
492 startFontClass(yyscanner,"keyword");
493 codify(yyscanner,yytext);
494 endFontClass(yyscanner);
498 if (yyextra->currentMemberDef && yyextra->currentMemberDef->isFunction())
500 std::lock_guard<std::mutex> lock(g_countFlowKeywordsMutex);
501 MemberDefMutable *mdm = toMemberDefMutable(yyextra->currentMemberDef);
504 mdm->incrementFlowKeyWordCount();
507 startFontClass(yyscanner,"keywordflow");
508 codify(yyscanner,yytext);
509 endFontClass(yyscanner);
511 ({IDENTIFIER}".")*{IDENTIFIER}/"(" {
512 yyextra->insideBody = true;
513 generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext);
514 yyextra->insideBody = false;
516 ({IDENTIFIER}".")+{IDENTIFIER} {
517 yyextra->insideBody = true;
518 generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext,TRUE);
519 yyextra->insideBody = false;
521 {IDENTIFIER} { codify(yyscanner,yytext); }
530 codify(yyscanner,yytext);
533 startFontClass(yyscanner,"keyword");
534 codifyLines(yyscanner,yytext);
535 endFontClass(yyscanner);
539 startFontClass(yyscanner,"keyword");
540 codifyLines(yyscanner,yytext);
541 endFontClass(yyscanner);
543 // No indentation necessary
544 yyextra->noSuiteFound = FALSE;
548 if (yyextra->currentMemberDef && yyextra->currentMemberDef->isFunction())
550 std::lock_guard<std::mutex> lock(g_countFlowKeywordsMutex);
551 MemberDefMutable *mdm = toMemberDefMutable(yyextra->currentMemberDef);
554 mdm->incrementFlowKeyWordCount();
557 startFontClass(yyscanner,"keywordflow");
558 codifyLines(yyscanner,yytext);
559 endFontClass(yyscanner);
561 // No indentation necessary
562 yyextra->noSuiteFound = FALSE;
565 codify(yyscanner,yytext);
570 if (YY_START==SingleQuoteString ||
571 YY_START==DoubleQuoteString ||
572 YY_START==TripleString
577 yy_push_state(YY_START,yyscanner);
579 yyextra->docBlock=yytext;
583 codifyLines(yyscanner,yytext);
584 if ( yyextra->noSuiteFound )
586 // printf("New suite to capture! [%d]\n", yyextra->yyLineNr);
587 BEGIN ( SuiteCaptureIndent );
592 <SuiteCaptureIndent>{
594 // Blankline - ignore, keep looking for indentation.
595 codifyLines(yyscanner,yytext);
599 // This state lasts momentarily,
600 // to check the indentation
601 // level that is about to be
603 codifyLines(yyscanner,yytext);
604 yyextra->indents.push(yyleng);
605 // printf("Captured indent of %d [line %d]\n", yyleng, yyextra->yyLineNr);
612 {BB}/({NONEMPTY}|{EXPCHAR}) {
613 // This implements poor
614 // indentation-tracking;
615 // should be improved.
616 // (translate tabs to space, etc)
617 codifyLines(yyscanner,yytext);
618 adjustScopesAndSuites(yyscanner,static_cast<int>(yyleng));
622 // If this ever succeeds,
623 // it means that this is
626 codifyLines(yyscanner,yytext);
629 ""/({NONEMPTY}|{EXPCHAR}) {
630 // Default rule; matches
631 // the empty string, assuming
632 // real text starts here.
633 // Just go straight to Body.
634 adjustScopesAndSuites(yyscanner,0);
640 codifyLines(yyscanner,yytext);
641 BEGIN( SuiteMaintain );
644 codify(yyscanner,yytext);
647 codifyLines(yyscanner,yytext);
650 <SingleQuoteString>{ // Single quoted string like 'That\'s a """nice""" string!'
651 \\{B}\n { // line continuation
652 codifyLines(yyscanner,yytext);
654 \\. { // escaped char
655 codify(yyscanner,yytext);
657 {STRINGPREFIX}?{TRIDOUBLEQUOTE} { // triple double quotes
658 codify(yyscanner,yytext);
660 "'" { // end of the string
661 codify(yyscanner,yytext);
662 endFontClass(yyscanner);
663 BEGIN(yyextra->stringContext);
665 [^"'\n\\]+ { // normal chars
666 codify(yyscanner,yytext);
669 codify(yyscanner,yytext);
673 <DoubleQuoteString>{ // Double quoted string like "That's \"a '''nice'''\" string!"
674 \\{B}\n { // line continuation
675 codifyLines(yyscanner,yytext);
677 \\. { // escaped char
678 codify(yyscanner,yytext);
680 {STRINGPREFIX}?{TRISINGLEQUOTE} { // triple single quotes
681 codify(yyscanner,yytext);
683 "\"" { // end of the string
684 codify(yyscanner,yytext);
685 endFontClass(yyscanner);
686 BEGIN(yyextra->stringContext);
688 [^"'\n\\]+ { // normal chars
689 codify(yyscanner,yytext);
692 codify(yyscanner,yytext);
699 codify(yyscanner,yytext);
700 if (yyextra->doubleQuote==(yytext[0]=='"'))
702 endFontClass(yyscanner);
703 BEGIN(yyextra->stringContext);
707 codifyLines(yyscanner,yytext);
710 codifyLines(yyscanner,yytext);
713 codify(yyscanner,yytext);
718 <*>{STRINGPREFIX}?{TRISINGLEQUOTE} {
719 if (YY_START==SingleQuoteString) REJECT;
720 startFontClass(yyscanner,"stringliteral");
721 yyextra->stringContext=YY_START;
722 yyextra->doubleQuote=yytext[yyleng-1]=='"';
723 codify(yyscanner,yytext);
726 <*>{STRINGPREFIX}?{TRIDOUBLEQUOTE} {
727 if (YY_START==DoubleQuoteString) REJECT;
728 startFontClass(yyscanner,"stringliteral");
729 yyextra->stringContext=YY_START;
730 yyextra->doubleQuote=yytext[yyleng-1]=='"';
731 codify(yyscanner,yytext);
734 <*>{STRINGPREFIX}?"'" { // single quoted string
735 if (YY_START==SingleQuoteString ||
736 YY_START==DoubleQuoteString ||
737 YY_START==TripleString)
741 startFontClass(yyscanner,"stringliteral");
742 yyextra->stringContext=YY_START;
743 codify(yyscanner,yytext);
744 BEGIN(SingleQuoteString);
746 <*>{STRINGPREFIX}?"\"" { // double quoted string
747 if (YY_START==SingleQuoteString ||
748 YY_START==DoubleQuoteString ||
749 YY_START==TripleString)
753 startFontClass(yyscanner,"stringliteral");
754 yyextra->stringContext=YY_START;
755 codify(yyscanner,yytext);
756 BEGIN(DoubleQuoteString);
758 <DocBlock>.* { // contents of current comment line
759 yyextra->docBlock+=yytext;
761 <DocBlock>"\n"{B}("#") { // comment block (next line is also comment line)
762 yyextra->docBlock+=yytext;
764 <DocBlock>{NEWLINE} { // comment block ends at the end of this line
765 // remove special comment (default config)
766 if (Config_getBool(STRIP_CODE_COMMENTS))
768 yyextra->yyLineNr+=((QCString)yyextra->docBlock).contains('\n');
769 yyextra->endComment=TRUE;
771 else // do not remove comment
773 startFontClass(yyscanner,"comment");
774 codifyLines(yyscanner,yyextra->docBlock);
775 endFontClass(yyscanner);
778 yy_pop_state(yyscanner);
780 <*>{POUNDCOMMENT}.* {
781 if (YY_START==SingleQuoteString ||
782 YY_START==DoubleQuoteString ||
783 YY_START==TripleString)
787 yy_push_state(YY_START,yyscanner);
789 yyextra->docBlock=yytext;
791 <*>"#".* { // normal comment
792 if (YY_START==SingleQuoteString ||
793 YY_START==DoubleQuoteString ||
794 YY_START==TripleString)
798 startFontClass(yyscanner,"comment");
799 codifyLines(yyscanner,yytext);
800 endFontClass(yyscanner);
803 if (yyextra->endComment)
805 yyextra->endComment=FALSE;
809 codifyLines(yyscanner,yytext);
811 //printf("[pycode] %d NEWLINE [line %d] no match\n",
812 // YY_START, yyextra->yyLineNr);
818 codify(yyscanner,yytext);
822 codify(yyscanner,yytext);
823 // printf("[pycode] '%s' [ state %d ] [line %d] no match\n",
824 // yytext, YY_START, yyextra->yyLineNr);
830 if (YY_START==DocBlock && !Config_getBool(STRIP_CODE_COMMENTS))
832 startFontClass(yyscanner,"comment");
833 codifyLines(yyscanner,yyextra->docBlock);
834 endFontClass(yyscanner);
840 /*@ ----------------------------------------------------------------------------
843 #if 0 // TODO: call me to store local variables and get better syntax highlighting, see code.l
844 static void addVariable(yyscan_t yyscanner, QCString type, QCString name)
846 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
847 //printf("PyVariableContext::addVariable(%s,%s)\n",qPrint(type),qPrint(name));
848 QCString ltype = type.simplifyWhiteSpace();
849 QCString lname = name.simplifyWhiteSpace();
851 auto it = yyextra->codeClassMap.find(ltype.str());
852 if (it!=yyextra->codeClassMap.end())
854 yyextra->theVarContext.addVariable(lname,std::move(it->second));
858 const ClassDef *varType = getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,ltype); // look for global class definitions
861 yyextra->theVarContext.addVariable(lname,ScopedTypeVariant(varType));
865 if (!yyextra->theVarContext.atGlobalScope()) // for local variable add a dummy entry to avoid linking to a global that is shadowed.
867 yyextra->theVarContext.addVariable(lname.str(),ScopedTypeVariant());
874 //-------------------------------------------------------------------------------
876 static yy_size_t yyread(yyscan_t yyscanner, char *buf,yy_size_t max_size)
878 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
879 yy_size_t inputPosition = yyextra->inputPosition;
880 const char *s = yyextra->inputString + inputPosition;
882 while( c < max_size && *s )
887 yyextra->inputPosition += c;
891 //-------------------------------------------------------------------------------
894 Examines current stack of white-space indentations;
895 re-syncs the parser with the correct scope.
897 static void adjustScopesAndSuites(yyscan_t yyscanner,unsigned indentLength)
899 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
901 if (!yyextra->indents.empty() && indentLength < yyextra->indents.top())
903 while (!yyextra->indents.empty() && indentLength < yyextra->indents.top())
905 // printf("Exited scope indent of [%d]\n", yyextra->indents.top());
906 yyextra->indents.pop(); // Pop the old suite's indentation
908 yyextra->currentMemberDef=0;
909 if (yyextra->currentDefinition)
910 yyextra->currentDefinition=yyextra->currentDefinition->getOuterScope();
914 // Are there any remaining indentation levels for suites?
915 if (!yyextra->indents.empty())
925 //-------------------------------------------------------------------------------
927 /*! counts the number of lines in the input */
928 static int countLines(yyscan_t yyscanner)
930 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
931 const char *p=yyextra->inputString;
937 if (c=='\n') count++;
939 if (p>yyextra->inputString && *(p-1)!='\n')
940 { // last line does not end with a \n, so we add an extra
941 // line and explicitly terminate the line after parsing.
943 yyextra->needsTermination=TRUE;
948 //-------------------------------------------------------------------------------
950 static void setCurrentDoc(yyscan_t yyscanner, const QCString &anchor)
952 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
953 if (Doxygen::searchIndex)
955 std::lock_guard<std::mutex> lock(g_searchIndexMutex);
956 if (yyextra->searchCtx)
958 yyextra->code->setCurrentDoc(yyextra->searchCtx,yyextra->searchCtx->anchor(),FALSE);
962 yyextra->code->setCurrentDoc(yyextra->sourceFileDef,anchor,TRUE);
967 //-------------------------------------------------------------------------------
969 static void addToSearchIndex(yyscan_t yyscanner, const QCString &text)
971 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
972 if (Doxygen::searchIndex)
974 std::lock_guard<std::mutex> lock(g_searchIndexMutex);
975 yyextra->code->addWord(text,FALSE);
979 //-------------------------------------------------------------------------------
981 static const ClassDef *stripClassName(yyscan_t yyscanner,const QCString &s,Definition *d)
983 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
988 while (extractClassNameFromType(type,pos,className,templSpec)!=-1)
990 QCString clName=className+templSpec;
991 const ClassDef *cd=0;
992 if (!yyextra->classScope.isEmpty())
994 cd=yyextra->symbolResolver.resolveClass(d,yyextra->classScope+"::"+clName);
998 cd=yyextra->symbolResolver.resolveClass(d,clName);
1009 //-------------------------------------------------------------------------------
1011 /*! start a new line of code, inserting a line number if yyextra->sourceFileDef
1012 * is TRUE. If a definition starts at the current line, then the line
1013 * number is linked to the documentation of that definition.
1015 static void startCodeLine(yyscan_t yyscanner)
1017 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1018 //if (yyextra->currentFontClass) { yyextra->code->endFontClass(yyscanner); }
1019 if (yyextra->sourceFileDef)
1021 //QCString lineNumber,lineAnchor;
1022 //lineNumber.sprintf("%05d",yyextra->yyLineNr);
1023 //lineAnchor.sprintf("l%05d",yyextra->yyLineNr);
1025 const Definition *d = yyextra->sourceFileDef->getSourceDefinition(yyextra->yyLineNr);
1026 //printf("startCodeLine %d d=%p\n",yyextra->yyLineNr,d);
1027 //yyextra->code->startLineNumber();
1029 if (!yyextra->includeCodeFragment && d && d->isLinkableInProject())
1031 yyextra->currentDefinition = d;
1032 yyextra->currentMemberDef = yyextra->sourceFileDef->getSourceMember(yyextra->yyLineNr);
1033 yyextra->insideBody = false;
1034 yyextra->endComment = FALSE;
1035 yyextra->searchingForBody = TRUE;
1036 yyextra->realScope = d->name();
1037 yyextra->classScope = d->name();
1038 //printf("Real scope: '%s'\n",qPrint(yyextra->realScope));
1039 yyextra->bodyCurlyCount = 0;
1040 QCString lineAnchor;
1041 lineAnchor.sprintf("l%05d",yyextra->yyLineNr);
1042 if (yyextra->currentMemberDef)
1044 yyextra->code->writeLineNumber(yyextra->currentMemberDef->getReference(),
1045 yyextra->currentMemberDef->getOutputFileBase(),
1046 yyextra->currentMemberDef->anchor(),yyextra->yyLineNr,
1047 !yyextra->includeCodeFragment);
1048 setCurrentDoc(yyscanner,lineAnchor);
1052 yyextra->code->writeLineNumber(d->getReference(),
1053 d->getOutputFileBase(),
1054 QCString(),yyextra->yyLineNr,
1055 !yyextra->includeCodeFragment);
1056 setCurrentDoc(yyscanner,lineAnchor);
1061 yyextra->code->writeLineNumber(QCString(),QCString(),QCString(),yyextra->yyLineNr,
1062 !yyextra->includeCodeFragment);
1065 yyextra->code->startCodeLine(yyextra->sourceFileDef);
1066 if (yyextra->currentFontClass)
1068 yyextra->code->startFontClass(yyextra->currentFontClass);
1072 //-------------------------------------------------------------------------------
1074 static void codify(yyscan_t yyscanner,const QCString &text)
1076 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1077 yyextra->code->codify(text);
1080 //-------------------------------------------------------------------------------
1082 static void endCodeLine(yyscan_t yyscanner)
1084 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1085 endFontClass(yyscanner);
1086 yyextra->code->endCodeLine();
1089 //-------------------------------------------------------------------------------
1091 static void nextCodeLine(yyscan_t yyscanner)
1093 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1094 const char *fc = yyextra->currentFontClass;
1095 endCodeLine(yyscanner);
1096 if (yyextra->yyLineNr<yyextra->inputLines)
1098 yyextra->currentFontClass = fc;
1099 startCodeLine(yyscanner);
1103 //-------------------------------------------------------------------------------
1105 /*! writes a link to a fragment \a text that may span multiple lines, inserting
1106 * line numbers for each line. If \a text contains newlines, the link will be
1107 * split into multiple links with the same destination, one for each line.
1109 static void writeMultiLineCodeLink(yyscan_t yyscanner,
1110 CodeOutputInterface &ol,
1111 const Definition *d,
1112 const QCString &text)
1114 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1115 if (text.isEmpty()) return;
1116 bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS);
1117 yyextra->tooltipManager.addTooltip(ol,d);
1118 QCString ref = d->getReference();
1119 QCString file = d->getOutputFileBase();
1120 QCString anchor = d->anchor();
1122 if (!sourceTooltips) // fall back to simple "title" tooltips
1124 tooltip = d->briefDescriptionAsTooltip();
1127 const char *p=text.data();
1132 while ((c=*p++) && c!='\n') { }
1135 yyextra->yyLineNr++;
1136 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
1137 ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,QCString(sp,p-sp-1),tooltip);
1138 nextCodeLine(yyscanner);
1142 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
1143 ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,sp,tooltip);
1149 //-------------------------------------------------------------------------------
1151 static void startFontClass(yyscan_t yyscanner,const char *s)
1153 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1154 // if font class is already set don't stop and start it.
1155 // strcmp does not like null pointers as input.
1156 if (!yyextra->currentFontClass || !s || strcmp(yyextra->currentFontClass,s))
1158 endFontClass(yyscanner);
1159 yyextra->code->startFontClass(s);
1160 yyextra->currentFontClass=s;
1164 //-------------------------------------------------------------------------------
1166 static void endFontClass(yyscan_t yyscanner)
1168 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1169 if (yyextra->currentFontClass)
1171 yyextra->code->endFontClass();
1172 yyextra->currentFontClass=0;
1176 //-------------------------------------------------------------------------------
1178 static void codifyLines(yyscan_t yyscanner,const QCString &text)
1180 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1181 if (text.isEmpty()) return;
1182 //printf("codifyLines(%d,\"%s\")\n",yyextra->yyLineNr,text);
1183 const char *p=text.data(),*sp=p;
1189 while ((c=*p++) && c!='\n') { }
1192 yyextra->yyLineNr++;
1193 int l = (int)(p-sp-1);
1194 char *tmp = (char*)malloc(l+1);
1197 yyextra->code->codify(tmp);
1199 nextCodeLine(yyscanner);
1203 yyextra->code->codify(sp);
1209 //-------------------------------------------------------------------------------
1211 static bool getLinkInScope(yyscan_t yyscanner,
1212 const QCString &c, // scope
1213 const QCString &m, // member
1214 const QCString &memberText, // exact text
1215 CodeOutputInterface &ol,
1216 const QCString &text
1219 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1220 const MemberDef *md = 0;
1221 const ClassDef *cd = 0;
1222 const FileDef *fd = 0;
1223 const NamespaceDef *nd = 0;
1224 const GroupDef *gd = 0;
1225 //printf("Trying '%s'::'%s'\n",qPrint(c),qPrint(m));
1226 if (getDefs(c,m,"()",md,cd,fd,nd,gd,FALSE,yyextra->sourceFileDef) &&
1230 //if (cd) d=cd; else if (nd) d=nd; else if (fd) d=fd; else d=gd;
1232 const Definition *d = md->getOuterScope()==Doxygen::globalScope ?
1233 md->getBodyDef() : md->getOuterScope();
1234 //printf("Found! d=%s\n",d?qPrint(d->name()):"<none>");
1235 if (md->getGroupDef()) d = md->getGroupDef();
1236 if (d && d->isLinkable())
1238 yyextra->theCallContext.setScope(ScopedTypeVariant(stripClassName(yyscanner,md->typeString(),md->getOuterScope())));
1239 //printf("yyextra->currentDefinition=%p yyextra->currentMemberDef=%p\n",
1240 // yyextra->currentDefinition,yyextra->currentMemberDef);
1242 if (yyextra->currentDefinition && yyextra->currentMemberDef && yyextra->collectXRefs && yyextra->insideBody)
1244 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1245 addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(md));
1247 //printf("d->getReference()='%s' d->getOutputBase()='%s' name='%s' member name='%s'\n",qPrint(d->getReference()),qPrint(d->getOutputFileBase()),qPrint(d->name()),qPrint(md->name()));
1249 writeMultiLineCodeLink(yyscanner,ol,md, !text.isEmpty() ? text : memberText);
1250 addToSearchIndex(yyscanner,!text.isEmpty() ? text : memberText);
1257 //-------------------------------------------------------------------------------
1259 static bool getLink(yyscan_t yyscanner,
1260 const QCString &className,
1261 const QCString &memberName,
1262 CodeOutputInterface &ol,
1263 const QCString &text)
1265 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1266 QCString m=removeRedundantWhiteSpace(memberName);
1267 QCString c=className;
1268 if (!getLinkInScope(yyscanner,c,m,memberName,ol,text))
1270 if (!yyextra->curClassName.isEmpty())
1272 if (!c.isEmpty()) c.prepend("::");
1273 c.prepend(yyextra->curClassName);
1274 return getLinkInScope(yyscanner,c,m,memberName,ol,text);
1281 //-------------------------------------------------------------------------------
1284 For a given string in the source code,
1285 finds its class or global id and links to it.
1287 static void generateClassOrGlobalLink(yyscan_t yyscanner,
1288 CodeOutputInterface &ol,
1289 const QCString &clName,
1292 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1293 QCString className=clName;
1295 // Don't do anything for empty text
1296 if (className.isEmpty()) return;
1298 DBG_CTX((stderr,"generateClassOrGlobalLink(className=%s)\n",qPrint(className)));
1300 const ScopedTypeVariant *lcd = 0;
1301 const ClassDef *cd=0; // Class def that we may find
1302 const MemberDef *md=0; // Member def that we may find
1303 //bool isLocal=FALSE;
1305 if ((lcd=yyextra->theVarContext.findVariable(className))==0) // not a local variable
1307 const Definition *d = yyextra->currentDefinition;
1308 QCString scope = substitute(className,".","::");
1310 cd = yyextra->symbolResolver.resolveClass(d,substitute(className,".","::"));
1311 md = yyextra->symbolResolver.getTypedef();
1313 DBG_CTX((stderr,"d=%s yyextra->sourceFileDef=%s\n",
1314 d?qPrint(d->displayName()):"<null>",
1315 yyextra->currentDefinition?qPrint(yyextra->currentDefinition->displayName()):"<null>"));
1316 DBG_CTX((stderr,"is found as a type %s\n",cd?qPrint(cd->name()):"<null>"));
1318 if (cd==0 && md==0) // also see if it is variable or enum or enum value
1320 const NamespaceDef *nd = getResolvedNamespace(scope);
1323 writeMultiLineCodeLink(yyscanner,ol,nd,clName);
1324 addToSearchIndex(yyscanner,className);
1327 else if (getLink(yyscanner,yyextra->classScope,clName,ol,clName))
1335 if (lcd->type()!=ScopedTypeVariant::Dummy)
1337 yyextra->theCallContext.setScope(*lcd);
1340 DBG_CTX((stderr,"is a local variable cd=%p!\n",cd));
1343 if (cd && cd->isLinkable()) // is it a linkable class
1345 writeMultiLineCodeLink(yyscanner,ol,cd,clName);
1346 addToSearchIndex(yyscanner,className);
1349 const Definition *d = md->getOuterScope()==Doxygen::globalScope ?
1350 md->getBodyDef() : md->getOuterScope();
1351 if (md->getGroupDef()) d = md->getGroupDef();
1352 if (d && d->isLinkable() && md->isLinkable() &&
1353 yyextra->currentMemberDef && yyextra->collectXRefs && yyextra->insideBody)
1355 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1356 addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(md));
1360 else // not a class, maybe a global member
1362 int scopeEnd = className.findRev(".");
1363 if (scopeEnd!=-1 && !typeOnly) // name with explicit scope
1365 QCString scope = substitute(className.left(scopeEnd),".","::");
1366 QCString locName = className.right(className.length()-scopeEnd-1);
1367 ClassDef *mcd = getClass(scope);
1368 DBG_CTX((stderr,"scope=%s locName=%s mcd=%p\n",qPrint(scope),qPrint(locName),mcd));
1371 const MemberDef *mmd = mcd->getMemberByName(locName);
1374 yyextra->theCallContext.setScope(ScopedTypeVariant(stripClassName(yyscanner,mmd->typeString(),mmd->getOuterScope())));
1375 writeMultiLineCodeLink(yyscanner,ol,mmd,clName);
1376 addToSearchIndex(yyscanner,className);
1377 const Definition *d = mmd->getOuterScope()==Doxygen::globalScope ?
1378 mmd->getBodyDef() : mmd->getOuterScope();
1379 if (mmd->getGroupDef()) d = mmd->getGroupDef();
1380 if (d && d->isLinkable() && mmd->isLinkable() &&
1381 yyextra->currentMemberDef && yyextra->collectXRefs && yyextra->insideBody)
1383 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1384 addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(mmd));
1389 else // check namespace as well
1391 const NamespaceDef *mnd = getResolvedNamespace(scope);
1394 const MemberDef *mmd=mnd->getMemberByName(locName);
1397 //printf("name=%s scope=%s\n",qPrint(locName),qPrint(scope));
1398 yyextra->theCallContext.setScope(ScopedTypeVariant(stripClassName(yyscanner,mmd->typeString(),mmd->getOuterScope())));
1399 writeMultiLineCodeLink(yyscanner,ol,mmd,clName);
1400 addToSearchIndex(yyscanner,className);
1401 const Definition *d = mmd->getOuterScope()==Doxygen::globalScope ?
1402 mmd->getBodyDef() : mmd->getOuterScope();
1403 if (mmd->getGroupDef()) d = mmd->getGroupDef();
1404 if (d && d->isLinkable() && mmd->isLinkable() &&
1405 yyextra->currentMemberDef && yyextra->collectXRefs && yyextra->insideBody)
1407 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1408 addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(mmd));
1416 // nothing found, just write out the word
1417 codifyLines(yyscanner,clName);
1418 addToSearchIndex(yyscanner,clName);
1422 //-------------------------------------------------------------------------------
1425 As of June 1, this function seems to work
1426 for file members, but scopes are not
1427 being correctly tracked for classes
1428 so it doesn't work for classes yet.
1431 static void generateFunctionLink(yyscan_t yyscanner,
1432 CodeOutputInterface &ol,
1433 const QCString &funcName)
1435 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1436 QCString locScope=yyextra->classScope;
1437 QCString locFunc=removeRedundantWhiteSpace(funcName);
1438 DBG_CTX((stdout,"*** locScope=%s locFunc=%s\n",qPrint(locScope),qPrint(locFunc)));
1439 int i=locFunc.findRev("::");
1442 locScope=locFunc.left(i);
1443 locFunc=locFunc.right(locFunc.length()-i-2).stripWhiteSpace();
1445 //printf("generateFunctionLink(%s) classScope='%s'\n",qPrint(locFunc),qPrint(locScope));
1446 if (!locScope.isEmpty())
1448 auto it = yyextra->codeClassMap.find(locScope.str());
1449 if (it!=yyextra->codeClassMap.end())
1451 ScopedTypeVariant ccd = it->second;
1452 //printf("using classScope %s\n",qPrint(yyextra->classScope));
1453 if (ccd.localDef() && !ccd.localDef()->baseClasses().empty())
1455 for (const auto &bcName : ccd.localDef()->baseClasses())
1457 if (getLink(yyscanner,bcName,locFunc,ol,funcName))
1465 if (!getLink(yyscanner,locScope,locFunc,ol,funcName))
1467 generateClassOrGlobalLink(yyscanner,ol,funcName);
1472 //-------------------------------------------------------------------------------
1474 static bool findMemberLink(yyscan_t yyscanner,
1475 CodeOutputInterface &ol,
1477 const QCString &symName)
1479 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1480 //printf("sym %s outerScope=%s equal=%d\n",
1481 // qPrint(sym->name()),qPrint(sym->getOuterScope()->name()),
1482 // sym->getOuterScope()==yyextra->currentDefinition);
1484 if (sym->getOuterScope() &&
1485 sym->getOuterScope()->definitionType()==Definition::TypeClass &&
1486 yyextra->currentDefinition->definitionType()==Definition::TypeClass)
1488 const ClassDef *cd = toClassDef(sym->getOuterScope());
1489 const ClassDef *thisCd = toClassDef(yyextra->currentDefinition);
1490 if (sym->definitionType()==Definition::TypeMember)
1492 if (yyextra->currentMemberDef && yyextra->collectXRefs)
1494 std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1495 addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(toMemberDef(sym)));
1498 DBG_CTX((stderr,"cd=%s thisCd=%s\n",cd?qPrint(cd->name()):"<none>",thisCd?qPrint(thisCd->name()):"<none>"));
1500 // TODO: find the nearest base class in case cd is a base class of
1502 if (cd==thisCd || (thisCd && thisCd->isBaseClass(cd,TRUE)))
1504 writeMultiLineCodeLink(yyscanner,ol,sym,symName);
1511 //-------------------------------------------------------------------------------
1513 static void findMemberLink(yyscan_t yyscanner,
1514 CodeOutputInterface &ol,
1515 const QCString &symName)
1517 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1518 //printf("Member reference: %s scope=%s member=%s\n",
1520 // yyextra->currentDefinition?qPrint(yyextra->currentDefinition->name()):"<none>",
1521 // yyextra->currentMemberDef?qPrint(yyextra->currentMemberDef->name()):"<none>"
1523 if (yyextra->currentDefinition)
1525 auto range = Doxygen::symbolMap->find(symName);
1526 for (auto it = range.first; it!=range.second; ++it)
1528 findMemberLink(yyscanner,ol,it->second,symName);
1531 //printf("sym %s not found\n",&yytext[5]);
1532 codify(yyscanner,symName);
1536 //-------------------------------------------------------------------------------
1538 struct PythonCodeParser::Private
1541 pycodeYY_state state;
1544 PythonCodeParser::PythonCodeParser() : p(std::make_unique<Private>())
1546 pycodeYYlex_init_extra(&p->state,&p->yyscanner);
1548 pycodeYYset_debug(1,p->yyscanner);
1550 resetCodeParserState();
1553 PythonCodeParser::~PythonCodeParser()
1555 pycodeYYlex_destroy(p->yyscanner);
1558 void PythonCodeParser::resetCodeParserState()
1560 struct yyguts_t *yyg = (struct yyguts_t*)p->yyscanner;
1561 yyextra->codeClassMap.clear();
1562 yyextra->currentDefinition = 0;
1563 yyextra->currentMemberDef = 0;
1564 yyextra->doubleStringIsDoc = FALSE;
1565 yyextra->paramParens = 0;
1566 while (!yyextra->indents.empty()) yyextra->indents.pop();
1570 void PythonCodeParser::parseCode(CodeOutputInterface &codeOutIntf,
1571 const QCString &scopeName,
1572 const QCString &input,
1573 SrcLangExt /*lang*/,
1574 bool isExampleBlock,
1575 const QCString &exampleName,
1576 const FileDef *fileDef,
1579 bool inlineFragment,
1580 const MemberDef *memberDef,
1581 bool showLineNumbers,
1582 const Definition *searchCtx,
1586 yyscan_t yyscanner = p->yyscanner;
1587 struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1589 //printf("***parseCode()\n");
1591 if (input.isEmpty()) return;
1592 printlex(yy_flex_debug, TRUE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL);
1593 yyextra->code = &codeOutIntf;
1594 yyextra->inputString = input.data();
1595 yyextra->inputPosition = 0;
1596 yyextra->currentFontClass = 0;
1597 yyextra->needsTermination = FALSE;
1598 yyextra->searchCtx=searchCtx;
1599 yyextra->collectXRefs=collectXRefs;
1601 yyextra->yyLineNr = startLine;
1603 yyextra->yyLineNr = 1;
1605 yyextra->inputLines = endLine+1;
1607 yyextra->inputLines = yyextra->yyLineNr + countLines(yyscanner) - 1;
1610 yyextra->exampleBlock = isExampleBlock;
1611 yyextra->exampleName = exampleName;
1612 yyextra->sourceFileDef = fileDef;
1613 yyextra->symbolResolver.setFileScope(fileDef);
1615 bool cleanupSourceDef = FALSE;
1616 if (yyextra->exampleBlock && fileDef==0)
1618 // create a dummy filedef for the example
1619 yyextra->sourceFileDef = createFileDef("",(!exampleName.isEmpty()?qPrint(exampleName):"generated"));
1620 cleanupSourceDef = TRUE;
1622 if (yyextra->sourceFileDef)
1624 setCurrentDoc(yyscanner,"l00001");
1627 yyextra->includeCodeFragment = inlineFragment;
1628 // Starts line 1 on the output
1629 startCodeLine(yyscanner);
1631 pycodeYYrestart(0,yyscanner);
1633 pycodeYYlex(yyscanner);
1635 if (!yyextra->indents.empty())
1637 // printf("Exited pysourceparser in inconsistent state!\n");
1640 if (yyextra->needsTermination)
1642 endCodeLine(yyscanner);
1644 if (cleanupSourceDef)
1646 // delete the temporary file definition used for this example
1647 delete yyextra->sourceFileDef;
1648 yyextra->sourceFileDef=0;
1650 // write the tooltips
1651 yyextra->tooltipManager.writeTooltips(codeOutIntf);
1652 printlex(yy_flex_debug, FALSE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL);
1655 #if USE_STATE2STRING
1656 #include "pycode.l.h"