Doxygen
pycode.l
浏览该文件的文档.
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2020 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 /* 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>.
20  */
21 
22 %option never-interactive
23 %option prefix="pycodeYY"
24 %option reentrant
25 %option extra-type="struct pycodeYY_state *"
26 %option noyy_top_state
27 %top{
28 #include <stdint.h>
29 // forward declare yyscan_t to improve type safety
30 #define YY_TYPEDEF_YY_SCANNER_T
31 struct yyguts_t;
32 typedef yyguts_t *yyscan_t;
33 }
34 
35 %{
36 
37 #include <vector>
38 #include <unordered_map>
39 #include <string>
40 #include <stack>
41 
42 #include <stdio.h>
43 
44 #include "pycode.h"
45 #include "message.h"
46 #include "scanner.h"
47 #include "entry.h"
48 #include "doxygen.h"
49 #include "outputlist.h"
50 #include "util.h"
51 #include "membername.h"
52 #include "searchindex.h"
53 #include "config.h"
54 #include "groupdef.h"
55 #include "classlist.h"
56 #include "filedef.h"
57 #include "namespacedef.h"
58 #include "tooltip.h"
59 #include "scopedtypevariant.h"
60 #include "symbolresolver.h"
61 
62 // Toggle for some debugging info
63 //#define DBG_CTX(x) fprintf x
64 #define DBG_CTX(x) do { } while(0)
65 
66 #define YY_NO_INPUT 1
67 #define YY_NO_UNISTD_H 1
68 
69 #define USE_STATE2STRING 0
70 
71 
72 struct pycodeYY_state
73 {
74  std::unordered_map< std::string, ScopedTypeVariant > codeClassMap;
75  QCString curClassName;
76  StringVector curClassBases;
77 
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;
91  QCString realScope;
92  int bodyCurlyCount = 0;
93  bool searchingForBody = FALSE;
94  QCString classScope;
95  int paramParens = 0;
96 
97  bool insideBody = false;
98  bool exampleBlock = FALSE;
99  QCString exampleName;
100 
101  QCString type;
102  QCString name;
103 
104  bool doubleStringIsDoc = FALSE;
105  bool doubleQuote = FALSE;
106  bool noSuiteFound = FALSE;
107  int stringContext = 0;
108 
109  std::stack<yy_size_t> indents; //!< Tracks indentation levels for scoping in python
110 
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;
117 };
118 
119 
120 #if USE_STATE2STRING
121 static const char *stateToString(int state);
122 #endif
123 
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);
150 
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);
153 #endif
154 
155 //-------------------------------------------------------------------
156 
157 static std::mutex g_searchIndexMutex;
158 static std::mutex g_docCrossReferenceMutex;
159 static std::mutex g_countFlowKeywordsMutex;
160 
161 //-------------------------------------------------------------------
162 
163 #undef YY_INPUT
164 #define YY_INPUT(buf,result,max_size) result=yyread(yyscanner,buf,max_size);
165 
166 %}
167 
168 
169 BB [ \t]+
170 B [ \t]*
171 NEWLINE \n
172 
173 DIGIT [0-9]
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])
183 
184 POUNDCOMMENT "##"
185 
186 TRISINGLEQUOTE "'''"
187 TRIDOUBLEQUOTE "\"\"\""
188 LONGSTRINGCHAR [^\\"']
189 ESCAPESEQ ("\\")(.)
190 LONGSTRINGITEM ({LONGSTRINGCHAR}|{ESCAPESEQ})
191 SMALLQUOTE ("\"\""|"\""|"'"|"''")
192 LONGSTRINGBLOCK ({LONGSTRINGITEM}|{SMALLQUOTE})
193 
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 ("'"[^']*"'")
203 
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}+
209 NONZERODIGIT [1-9]
210 OCTDIGIT [0-7]
211 HEXDIGIT ({DIGIT}|[a-f]|[A-F])
212 FLOATNUMBER ({POINTFLOAT}|{EXPONENTFLOAT})
213 POINTFLOAT ({INTPART}?{FRACTION}|{INTPART}".")
214 EXPONENTFLOAT ({INTPART}|{POINTFLOAT}){EXPONENT}
215 INTPART {DIGIT}+
216 FRACTION "."{DIGIT}+
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}
247 STRIDE {EXPRESSION}
248 ELLIPSIS "..."
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})
262 
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})
277 
278 %option noyywrap
279 %option stack
280 
281 %x Body
282 
283 %x FunctionDec
284 %x FunctionParams
285 
286 %x ClassDec
287 %x ClassInheritance
288 
289 %x Suite
290 %x SuiteCaptureIndent
291 %x SuiteStart
292 %x SuiteMaintain
293 
294 %x SingleQuoteString
295 %x DoubleQuoteString
296 %x TripleString
297 
298 %x DocBlock
299 %%
300 
301 <Body,Suite>{
302  "def"{BB} {
303  startFontClass(yyscanner,"keyword");
304  codify(yyscanner,yytext);
305  endFontClass(yyscanner);
306  BEGIN( FunctionDec );
307  }
308  "async"{BB}"def"{BB} {
309  startFontClass(yyscanner,"keyword");
310  codify(yyscanner,yytext);
311  endFontClass(yyscanner);
312  BEGIN( FunctionDec );
313  }
314 
315  "class"{BB} {
316  startFontClass(yyscanner,"keyword");
317  codify(yyscanner,yytext);
318  endFontClass(yyscanner);
319  BEGIN( ClassDec );
320  }
321  "None" {
322  startFontClass(yyscanner,"keywordtype");
323  codify(yyscanner,yytext);
324  endFontClass(yyscanner);
325  }
326  "self."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER}"(" {
327  codify(yyscanner,"self.");
328  findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
329  }
330  "self."{IDENTIFIER}/"(" {
331  codify(yyscanner,"self.");
332  findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
333  }
334  "self."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER} {
335  codify(yyscanner,"self.");
336  findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
337  }
338  "self."{IDENTIFIER} {
339  codify(yyscanner,"self.");
340  findMemberLink(yyscanner,*yyextra->code,&yytext[5]);
341  }
342  "cls."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER}"(" {
343  codify(yyscanner,"cls.");
344  findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
345  }
346  "cls."{IDENTIFIER}/"(" {
347  codify(yyscanner,"cls.");
348  findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
349  }
350  "cls."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER} {
351  codify(yyscanner,"cls.");
352  findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
353  }
354  "cls."{IDENTIFIER} {
355  codify(yyscanner,"cls.");
356  findMemberLink(yyscanner,*yyextra->code,&yytext[4]);
357  }
358  "@"{SCOPE}{CALLANY}? { // decorator
359  startFontClass(yyscanner,"preprocessor");
360  codify(yyscanner,yytext);
361  endFontClass(yyscanner);
362  }
363 }
364 
365 <ClassDec>{IDENTIFIER} {
366  generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext);
367  // codify(yyscanner,yytext);
368  yyextra->curClassName = yytext;
369  yyextra->curClassBases.clear();
370  BEGIN( ClassInheritance );
371  }
372 
373 <ClassInheritance>{
374  ({BB}|[(,)]) {
375  codify(yyscanner,yytext);
376  }
377 
378  ({IDENTIFIER}".")*{IDENTIFIER} {
379  // The parser
380  // is assuming
381  // that ALL identifiers
382  // in this state
383  // are base classes;
384  // it doesn't check to see
385  // that the first parenthesis
386  // has been seen.
387 
388  // This is bad - it should
389  // probably be more strict
390  // about what to accept.
391 
392  yyextra->curClassBases.push_back(yytext);
393  yyextra->insideBody = true;
394  generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext);
395  yyextra->insideBody = false;
396  // codify(yyscanner,yytext);
397  }
398 
399  ":" {
400  codify(yyscanner,yytext);
401 
402  // Assume this will
403  // be a one-line suite;
404  // found counter-example
405  // in SuiteStart.
406 
407  // Push a class scope
408 
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)
412  {
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())
417  {
418  baseDefToAdd = toClassDef(it->second.globalDef());
419  }
420  // Try to find class in global scope
421  if (baseDefToAdd==0)
422  {
423  baseDefToAdd=yyextra->symbolResolver.resolveClass(yyextra->currentDefinition,s.c_str());
424  }
425 
426  if (baseDefToAdd && baseDefToAdd->name()!=yyextra->curClassName)
427  {
428  var.localDef()->insertBaseClass(baseDefToAdd->name());
429  }
430  }
431  yyextra->codeClassMap.emplace(std::make_pair(yyextra->curClassName.str(),std::move(var)));
432 
433  // Reset class-parsing variables.
434  yyextra->curClassName.resize(0);
435  yyextra->curClassBases.clear();
436 
437  yyextra->noSuiteFound = TRUE;
438  BEGIN( SuiteStart );
439  }
440 }
441 
442 
443 <FunctionDec>{
444  {IDENTIFIER} {
445  generateFunctionLink(yyscanner,*yyextra->code,yytext);
446  }
447 
448  {B}"(" {
449  codify(yyscanner,yytext);
450  BEGIN( FunctionParams );
451  }
452 }
453 
454 <FunctionParams>{
455  ({BB}|",") {
456  // Parses delimiters
457  codify(yyscanner,yytext);
458  }
459 
460  ({IDENTIFIER}|{PARAMNONEMPTY}+) {
461  codify(yyscanner,yytext);
462  }
463 
464  ")" {
465  codify(yyscanner,yytext);
466  }
467 
468  "\n" {
469  codifyLines(yyscanner,yytext);
470  }
471 
472  ":" {
473  codify(yyscanner,yytext);
474 
475  // Assume this will
476  // be a one-line suite;
477  // found counter-example
478  // in SuiteStart.
479  yyextra->noSuiteFound = TRUE;
480  BEGIN( SuiteStart );
481  }
482 }
483 
484 <Body,Suite>{
485 
486  {KEYWORD} {
487  // Position-sensitive rules!
488  // Must come AFTER keyword-triggered rules
489  // Must come BEFORE identifier NONEMPTY-like rules
490  // to syntax highlight.
491 
492  startFontClass(yyscanner,"keyword");
493  codify(yyscanner,yytext);
494  endFontClass(yyscanner);
495  }
496 
497  {FLOWKW} {
498  if (yyextra->currentMemberDef && yyextra->currentMemberDef->isFunction())
499  {
500  std::lock_guard<std::mutex> lock(g_countFlowKeywordsMutex);
501  MemberDefMutable *mdm = toMemberDefMutable(yyextra->currentMemberDef);
502  if (mdm)
503  {
504  mdm->incrementFlowKeyWordCount();
505  }
506  }
507  startFontClass(yyscanner,"keywordflow");
508  codify(yyscanner,yytext);
509  endFontClass(yyscanner);
510  }
511  ({IDENTIFIER}".")*{IDENTIFIER}/"(" {
512  yyextra->insideBody = true;
513  generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext);
514  yyextra->insideBody = false;
515  }
516  ({IDENTIFIER}".")+{IDENTIFIER} {
517  yyextra->insideBody = true;
518  generateClassOrGlobalLink(yyscanner,*yyextra->code,yytext,TRUE);
519  yyextra->insideBody = false;
520  }
521  {IDENTIFIER} { codify(yyscanner,yytext); }
522 
523 }
524 
525 
526 
527 <SuiteStart>{
528 
529  {BB} {
530  codify(yyscanner,yytext);
531  }
532  "pass" {
533  startFontClass(yyscanner,"keyword");
534  codifyLines(yyscanner,yytext);
535  endFontClass(yyscanner);
536  BEGIN(Body);
537  }
538  {KEYWORD} {
539  startFontClass(yyscanner,"keyword");
540  codifyLines(yyscanner,yytext);
541  endFontClass(yyscanner);
542 
543  // No indentation necessary
544  yyextra->noSuiteFound = FALSE;
545  }
546 
547  {FLOWKW} {
548  if (yyextra->currentMemberDef && yyextra->currentMemberDef->isFunction())
549  {
550  std::lock_guard<std::mutex> lock(g_countFlowKeywordsMutex);
551  MemberDefMutable *mdm = toMemberDefMutable(yyextra->currentMemberDef);
552  if (mdm)
553  {
554  mdm->incrementFlowKeyWordCount();
555  }
556  }
557  startFontClass(yyscanner,"keywordflow");
558  codifyLines(yyscanner,yytext);
559  endFontClass(yyscanner);
560 
561  // No indentation necessary
562  yyextra->noSuiteFound = FALSE;
563  }
564  {IDENTIFIER} {
565  codify(yyscanner,yytext);
566  }
567 
568 
569  {POUNDCOMMENT} {
570  if (YY_START==SingleQuoteString ||
571  YY_START==DoubleQuoteString ||
572  YY_START==TripleString
573  )
574  {
575  REJECT;
576  }
577  yy_push_state(YY_START,yyscanner);
578  BEGIN(DocBlock);
579  yyextra->docBlock=yytext;
580  }
581 
582  {NEWLINE} {
583  codifyLines(yyscanner,yytext);
584  if ( yyextra->noSuiteFound )
585  {
586  // printf("New suite to capture! [%d]\n", yyextra->yyLineNr);
587  BEGIN ( SuiteCaptureIndent );
588  }
589  }
590 }
591 
592 <SuiteCaptureIndent>{
593  "\n"|({BB}"\n") {
594  // Blankline - ignore, keep looking for indentation.
595  codifyLines(yyscanner,yytext);
596  }
597 
598  {BB} {
599  // This state lasts momentarily,
600  // to check the indentation
601  // level that is about to be
602  // used.
603  codifyLines(yyscanner,yytext);
604  yyextra->indents.push(yyleng);
605  // printf("Captured indent of %d [line %d]\n", yyleng, yyextra->yyLineNr);
606  BEGIN( Suite );
607  }
608 }
609 
610 <SuiteMaintain>{
611 
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));
619  }
620 
621  "\n"|({BB}"\n") {
622  // If this ever succeeds,
623  // it means that this is
624  // a blank line, and
625  // can be ignored.
626  codifyLines(yyscanner,yytext);
627  }
628 
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);
635  }
636 }
637 
638 
639 <Suite>{NEWLINE} {
640  codifyLines(yyscanner,yytext);
641  BEGIN( SuiteMaintain );
642  }
643 <Body>{IDENTIFIER} {
644  codify(yyscanner,yytext);
645  }
646 <Body>{NEWLINE} {
647  codifyLines(yyscanner,yytext);
648  }
649 
650 <SingleQuoteString>{ // Single quoted string like 'That\'s a """nice""" string!'
651  \\{B}\n { // line continuation
652  codifyLines(yyscanner,yytext);
653  }
654  \\. { // escaped char
655  codify(yyscanner,yytext);
656  }
657  {STRINGPREFIX}?{TRIDOUBLEQUOTE} { // triple double quotes
658  codify(yyscanner,yytext);
659  }
660  "'" { // end of the string
661  codify(yyscanner,yytext);
662  endFontClass(yyscanner);
663  BEGIN(yyextra->stringContext);
664  }
665  [^"'\n\\]+ { // normal chars
666  codify(yyscanner,yytext);
667  }
668  . { // normal char
669  codify(yyscanner,yytext);
670  }
671 }
672 
673 <DoubleQuoteString>{ // Double quoted string like "That's \"a '''nice'''\" string!"
674  \\{B}\n { // line continuation
675  codifyLines(yyscanner,yytext);
676  }
677  \\. { // escaped char
678  codify(yyscanner,yytext);
679  }
680  {STRINGPREFIX}?{TRISINGLEQUOTE} { // triple single quotes
681  codify(yyscanner,yytext);
682  }
683  "\"" { // end of the string
684  codify(yyscanner,yytext);
685  endFontClass(yyscanner);
686  BEGIN(yyextra->stringContext);
687  }
688  [^"'\n\\]+ { // normal chars
689  codify(yyscanner,yytext);
690  }
691  . { // normal char
692  codify(yyscanner,yytext);
693  }
694 }
695 
696 <TripleString>{
697  {TRIDOUBLEQUOTE} |
698  {TRISINGLEQUOTE} {
699  codify(yyscanner,yytext);
700  if (yyextra->doubleQuote==(yytext[0]=='"'))
701  {
702  endFontClass(yyscanner);
703  BEGIN(yyextra->stringContext);
704  }
705  }
706  {LONGSTRINGBLOCK} {
707  codifyLines(yyscanner,yytext);
708  }
709  \n {
710  codifyLines(yyscanner,yytext);
711  }
712  . {
713  codify(yyscanner,yytext);
714  }
715 }
716 
717 
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);
724  BEGIN(TripleString);
725  }
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);
732  BEGIN(TripleString);
733  }
734 <*>{STRINGPREFIX}?"'" { // single quoted string
735  if (YY_START==SingleQuoteString ||
736  YY_START==DoubleQuoteString ||
737  YY_START==TripleString)
738  {
739  REJECT;
740  }
741  startFontClass(yyscanner,"stringliteral");
742  yyextra->stringContext=YY_START;
743  codify(yyscanner,yytext);
744  BEGIN(SingleQuoteString);
745  }
746 <*>{STRINGPREFIX}?"\"" { // double quoted string
747  if (YY_START==SingleQuoteString ||
748  YY_START==DoubleQuoteString ||
749  YY_START==TripleString)
750  {
751  REJECT;
752  }
753  startFontClass(yyscanner,"stringliteral");
754  yyextra->stringContext=YY_START;
755  codify(yyscanner,yytext);
756  BEGIN(DoubleQuoteString);
757  }
758 <DocBlock>.* { // contents of current comment line
759  yyextra->docBlock+=yytext;
760  }
761 <DocBlock>"\n"{B}("#") { // comment block (next line is also comment line)
762  yyextra->docBlock+=yytext;
763  }
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))
767  {
768  yyextra->yyLineNr+=((QCString)yyextra->docBlock).contains('\n');
769  yyextra->endComment=TRUE;
770  }
771  else // do not remove comment
772  {
773  startFontClass(yyscanner,"comment");
774  codifyLines(yyscanner,yyextra->docBlock);
775  endFontClass(yyscanner);
776  }
777  unput(*yytext);
778  yy_pop_state(yyscanner);
779  }
780 <*>{POUNDCOMMENT}.* {
781  if (YY_START==SingleQuoteString ||
782  YY_START==DoubleQuoteString ||
783  YY_START==TripleString)
784  {
785  REJECT;
786  }
787  yy_push_state(YY_START,yyscanner);
788  BEGIN(DocBlock);
789  yyextra->docBlock=yytext;
790  }
791 <*>"#".* { // normal comment
792  if (YY_START==SingleQuoteString ||
793  YY_START==DoubleQuoteString ||
794  YY_START==TripleString)
795  {
796  REJECT;
797  }
798  startFontClass(yyscanner,"comment");
799  codifyLines(yyscanner,yytext);
800  endFontClass(yyscanner);
801  }
802 <*>{NEWLINE} {
803  if (yyextra->endComment)
804  {
805  yyextra->endComment=FALSE;
806  }
807  else
808  {
809  codifyLines(yyscanner,yytext);
810  }
811  //printf("[pycode] %d NEWLINE [line %d] no match\n",
812  // YY_START, yyextra->yyLineNr);
813 
814  BEGIN(Body);
815  }
816 
817 <*>[ \t]+ {
818  codify(yyscanner,yytext);
819  BEGIN(Body);
820  }
821 <*>. {
822  codify(yyscanner,yytext);
823  // printf("[pycode] '%s' [ state %d ] [line %d] no match\n",
824  // yytext, YY_START, yyextra->yyLineNr);
825 
826  BEGIN(Body);
827  }
828 
829 <*><<EOF>> {
830  if (YY_START==DocBlock && !Config_getBool(STRIP_CODE_COMMENTS))
831  {
832  startFontClass(yyscanner,"comment");
833  codifyLines(yyscanner,yyextra->docBlock);
834  endFontClass(yyscanner);
835  }
836  yyterminate();
837  }
838 %%
839 
840 /*@ ----------------------------------------------------------------------------
841  */
842 
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)
845 {
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();
850 
851  auto it = yyextra->codeClassMap.find(ltype.str());
852  if (it!=yyextra->codeClassMap.end())
853  {
854  yyextra->theVarContext.addVariable(lname,std::move(it->second));
855  }
856  else
857  {
858  const ClassDef *varType = getResolvedClass(yyextra->currentDefinition,yyextra->sourceFileDef,ltype); // look for global class definitions
859  if (varType)
860  {
861  yyextra->theVarContext.addVariable(lname,ScopedTypeVariant(varType));
862  }
863  else
864  {
865  if (!yyextra->theVarContext.atGlobalScope()) // for local variable add a dummy entry to avoid linking to a global that is shadowed.
866  {
867  yyextra->theVarContext.addVariable(lname.str(),ScopedTypeVariant());
868  }
869  }
870  }
871 }
872 #endif
873 
874 //-------------------------------------------------------------------------------
875 
876 static yy_size_t yyread(yyscan_t yyscanner, char *buf,yy_size_t max_size)
877 {
878  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
879  yy_size_t inputPosition = yyextra->inputPosition;
880  const char *s = yyextra->inputString + inputPosition;
881  yy_size_t c=0;
882  while( c < max_size && *s )
883  {
884  *buf++ = *s++;
885  c++;
886  }
887  yyextra->inputPosition += c;
888  return c;
889 }
890 
891 //-------------------------------------------------------------------------------
892 
893 /*!
894  Examines current stack of white-space indentations;
895  re-syncs the parser with the correct scope.
896 */
897 static void adjustScopesAndSuites(yyscan_t yyscanner,unsigned indentLength)
898 {
899  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
900  // States to pop
901  if (!yyextra->indents.empty() && indentLength < yyextra->indents.top())
902  {
903  while (!yyextra->indents.empty() && indentLength < yyextra->indents.top())
904  {
905  // printf("Exited scope indent of [%d]\n", yyextra->indents.top());
906  yyextra->indents.pop(); // Pop the old suite's indentation
907 
908  yyextra->currentMemberDef=0;
909  if (yyextra->currentDefinition)
910  yyextra->currentDefinition=yyextra->currentDefinition->getOuterScope();
911  }
912  }
913 
914  // Are there any remaining indentation levels for suites?
915  if (!yyextra->indents.empty())
916  {
917  BEGIN( Suite );
918  }
919  else
920  {
921  BEGIN( Body );
922  }
923 }
924 
925 //-------------------------------------------------------------------------------
926 
927 /*! counts the number of lines in the input */
928 static int countLines(yyscan_t yyscanner)
929 {
930  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
931  const char *p=yyextra->inputString;
932  char c;
933  int count=1;
934  while ((c=*p))
935  {
936  p++;
937  if (c=='\n') count++;
938  }
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.
942  count++;
943  yyextra->needsTermination=TRUE;
944  }
945  return count;
946 }
947 
948 //-------------------------------------------------------------------------------
949 
950 static void setCurrentDoc(yyscan_t yyscanner, const QCString &anchor)
951 {
952  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
953  if (Doxygen::searchIndex)
954  {
955  std::lock_guard<std::mutex> lock(g_searchIndexMutex);
956  if (yyextra->searchCtx)
957  {
958  yyextra->code->setCurrentDoc(yyextra->searchCtx,yyextra->searchCtx->anchor(),FALSE);
959  }
960  else
961  {
962  yyextra->code->setCurrentDoc(yyextra->sourceFileDef,anchor,TRUE);
963  }
964  }
965 }
966 
967 //-------------------------------------------------------------------------------
968 
969 static void addToSearchIndex(yyscan_t yyscanner, const QCString &text)
970 {
971  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
972  if (Doxygen::searchIndex)
973  {
974  std::lock_guard<std::mutex> lock(g_searchIndexMutex);
975  yyextra->code->addWord(text,FALSE);
976  }
977 }
978 
979 //-------------------------------------------------------------------------------
980 
981 static const ClassDef *stripClassName(yyscan_t yyscanner,const QCString &s,Definition *d)
982 {
983  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
984  int pos=0;
985  QCString type = s;
986  QCString className;
987  QCString templSpec;
988  while (extractClassNameFromType(type,pos,className,templSpec)!=-1)
989  {
990  QCString clName=className+templSpec;
991  const ClassDef *cd=0;
992  if (!yyextra->classScope.isEmpty())
993  {
994  cd=yyextra->symbolResolver.resolveClass(d,yyextra->classScope+"::"+clName);
995  }
996  if (cd==0)
997  {
998  cd=yyextra->symbolResolver.resolveClass(d,clName);
999  }
1000  if (cd)
1001  {
1002  return cd;
1003  }
1004  }
1005 
1006  return 0;
1007 }
1008 
1009 //-------------------------------------------------------------------------------
1010 
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.
1014  */
1015 static void startCodeLine(yyscan_t yyscanner)
1016 {
1017  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1018  //if (yyextra->currentFontClass) { yyextra->code->endFontClass(yyscanner); }
1019  if (yyextra->sourceFileDef)
1020  {
1021  //QCString lineNumber,lineAnchor;
1022  //lineNumber.sprintf("%05d",yyextra->yyLineNr);
1023  //lineAnchor.sprintf("l%05d",yyextra->yyLineNr);
1024 
1025  const Definition *d = yyextra->sourceFileDef->getSourceDefinition(yyextra->yyLineNr);
1026  //printf("startCodeLine %d d=%p\n",yyextra->yyLineNr,d);
1027  //yyextra->code->startLineNumber();
1028 
1029  if (!yyextra->includeCodeFragment && d && d->isLinkableInProject())
1030  {
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)
1043  {
1044  yyextra->code->writeLineNumber(yyextra->currentMemberDef->getReference(),
1045  yyextra->currentMemberDef->getOutputFileBase(),
1046  yyextra->currentMemberDef->anchor(),yyextra->yyLineNr,
1047  !yyextra->includeCodeFragment);
1048  setCurrentDoc(yyscanner,lineAnchor);
1049  }
1050  else
1051  {
1052  yyextra->code->writeLineNumber(d->getReference(),
1053  d->getOutputFileBase(),
1054  QCString(),yyextra->yyLineNr,
1055  !yyextra->includeCodeFragment);
1056  setCurrentDoc(yyscanner,lineAnchor);
1057  }
1058  }
1059  else
1060  {
1061  yyextra->code->writeLineNumber(QCString(),QCString(),QCString(),yyextra->yyLineNr,
1062  !yyextra->includeCodeFragment);
1063  }
1064  }
1065  yyextra->code->startCodeLine(yyextra->sourceFileDef);
1066  if (yyextra->currentFontClass)
1067  {
1068  yyextra->code->startFontClass(yyextra->currentFontClass);
1069  }
1070 }
1071 
1072 //-------------------------------------------------------------------------------
1073 
1074 static void codify(yyscan_t yyscanner,const QCString &text)
1075 {
1076  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1077  yyextra->code->codify(text);
1078 }
1079 
1080 //-------------------------------------------------------------------------------
1081 
1082 static void endCodeLine(yyscan_t yyscanner)
1083 {
1084  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1085  endFontClass(yyscanner);
1086  yyextra->code->endCodeLine();
1087 }
1088 
1089 //-------------------------------------------------------------------------------
1090 
1091 static void nextCodeLine(yyscan_t yyscanner)
1092 {
1093  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1094  const char *fc = yyextra->currentFontClass;
1095  endCodeLine(yyscanner);
1096  if (yyextra->yyLineNr<yyextra->inputLines)
1097  {
1098  yyextra->currentFontClass = fc;
1099  startCodeLine(yyscanner);
1100  }
1101 }
1102 
1103 //-------------------------------------------------------------------------------
1104 
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.
1108  */
1109 static void writeMultiLineCodeLink(yyscan_t yyscanner,
1110  CodeOutputInterface &ol,
1111  const Definition *d,
1112  const QCString &text)
1113 {
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();
1121  QCString tooltip;
1122  if (!sourceTooltips) // fall back to simple "title" tooltips
1123  {
1124  tooltip = d->briefDescriptionAsTooltip();
1125  }
1126  bool done=FALSE;
1127  const char *p=text.data();
1128  while (!done)
1129  {
1130  const char *sp=p;
1131  char c;
1132  while ((c=*p++) && c!='\n') { }
1133  if (c=='\n')
1134  {
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);
1139  }
1140  else
1141  {
1142  //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
1143  ol.writeCodeLink(d->codeSymbolType(),ref,file,anchor,sp,tooltip);
1144  done=TRUE;
1145  }
1146  }
1147 }
1148 
1149 //-------------------------------------------------------------------------------
1150 
1151 static void startFontClass(yyscan_t yyscanner,const char *s)
1152 {
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))
1157  {
1158  endFontClass(yyscanner);
1159  yyextra->code->startFontClass(s);
1160  yyextra->currentFontClass=s;
1161  }
1162 }
1163 
1164 //-------------------------------------------------------------------------------
1165 
1166 static void endFontClass(yyscan_t yyscanner)
1167 {
1168  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1169  if (yyextra->currentFontClass)
1170  {
1171  yyextra->code->endFontClass();
1172  yyextra->currentFontClass=0;
1173  }
1174 }
1175 
1176 //-------------------------------------------------------------------------------
1177 
1178 static void codifyLines(yyscan_t yyscanner,const QCString &text)
1179 {
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;
1184  char c;
1185  bool done=FALSE;
1186  while (!done)
1187  {
1188  sp=p;
1189  while ((c=*p++) && c!='\n') { }
1190  if (c=='\n')
1191  {
1192  yyextra->yyLineNr++;
1193  int l = (int)(p-sp-1);
1194  char *tmp = (char*)malloc(l+1);
1195  memcpy(tmp,sp,l);
1196  tmp[l]='\0';
1197  yyextra->code->codify(tmp);
1198  free(tmp);
1199  nextCodeLine(yyscanner);
1200  }
1201  else
1202  {
1203  yyextra->code->codify(sp);
1204  done=TRUE;
1205  }
1206  }
1207 }
1208 
1209 //-------------------------------------------------------------------------------
1210 
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
1217  )
1218 {
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) &&
1227  md->isLinkable())
1228  {
1229  //Definition *d=0;
1230  //if (cd) d=cd; else if (nd) d=nd; else if (fd) d=fd; else d=gd;
1231 
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())
1237  {
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);
1241 
1242  if (yyextra->currentDefinition && yyextra->currentMemberDef && yyextra->collectXRefs && yyextra->insideBody)
1243  {
1244  std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1245  addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(md));
1246  }
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()));
1248 
1249  writeMultiLineCodeLink(yyscanner,ol,md, !text.isEmpty() ? text : memberText);
1250  addToSearchIndex(yyscanner,!text.isEmpty() ? text : memberText);
1251  return TRUE;
1252  }
1253  }
1254  return FALSE;
1255 }
1256 
1257 //-------------------------------------------------------------------------------
1258 
1259 static bool getLink(yyscan_t yyscanner,
1260  const QCString &className,
1261  const QCString &memberName,
1262  CodeOutputInterface &ol,
1263  const QCString &text)
1264 {
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))
1269  {
1270  if (!yyextra->curClassName.isEmpty())
1271  {
1272  if (!c.isEmpty()) c.prepend("::");
1273  c.prepend(yyextra->curClassName);
1274  return getLinkInScope(yyscanner,c,m,memberName,ol,text);
1275  }
1276  return FALSE;
1277  }
1278  return TRUE;
1279 }
1280 
1281 //-------------------------------------------------------------------------------
1282 
1283 /*
1284  For a given string in the source code,
1285  finds its class or global id and links to it.
1286 */
1287 static void generateClassOrGlobalLink(yyscan_t yyscanner,
1288  CodeOutputInterface &ol,
1289  const QCString &clName,
1290  bool typeOnly)
1291 {
1292  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1293  QCString className=clName;
1294 
1295  // Don't do anything for empty text
1296  if (className.isEmpty()) return;
1297 
1298  DBG_CTX((stderr,"generateClassOrGlobalLink(className=%s)\n",qPrint(className)));
1299 
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;
1304 
1305  if ((lcd=yyextra->theVarContext.findVariable(className))==0) // not a local variable
1306  {
1307  const Definition *d = yyextra->currentDefinition;
1308  QCString scope = substitute(className,".","::");
1309 
1310  cd = yyextra->symbolResolver.resolveClass(d,substitute(className,".","::"));
1311  md = yyextra->symbolResolver.getTypedef();
1312 
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>"));
1317 
1318  if (cd==0 && md==0) // also see if it is variable or enum or enum value
1319  {
1320  const NamespaceDef *nd = getResolvedNamespace(scope);
1321  if (nd)
1322  {
1323  writeMultiLineCodeLink(yyscanner,ol,nd,clName);
1324  addToSearchIndex(yyscanner,className);
1325  return;
1326  }
1327  else if (getLink(yyscanner,yyextra->classScope,clName,ol,clName))
1328  {
1329  return;
1330  }
1331  }
1332  }
1333  else
1334  {
1335  if (lcd->type()!=ScopedTypeVariant::Dummy)
1336  {
1337  yyextra->theCallContext.setScope(*lcd);
1338  }
1339  //isLocal=TRUE;
1340  DBG_CTX((stderr,"is a local variable cd=%p!\n",cd));
1341  }
1342 
1343  if (cd && cd->isLinkable()) // is it a linkable class
1344  {
1345  writeMultiLineCodeLink(yyscanner,ol,cd,clName);
1346  addToSearchIndex(yyscanner,className);
1347  if (md)
1348  {
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)
1354  {
1355  std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1356  addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(md));
1357  }
1358  }
1359  }
1360  else // not a class, maybe a global member
1361  {
1362  int scopeEnd = className.findRev(".");
1363  if (scopeEnd!=-1 && !typeOnly) // name with explicit scope
1364  {
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));
1369  if (mcd)
1370  {
1371  const MemberDef *mmd = mcd->getMemberByName(locName);
1372  if (mmd)
1373  {
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)
1382  {
1383  std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1384  addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(mmd));
1385  }
1386  return;
1387  }
1388  }
1389  else // check namespace as well
1390  {
1391  const NamespaceDef *mnd = getResolvedNamespace(scope);
1392  if (mnd)
1393  {
1394  const MemberDef *mmd=mnd->getMemberByName(locName);
1395  if (mmd)
1396  {
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)
1406  {
1407  std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1408  addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(mmd));
1409  }
1410  return;
1411  }
1412  }
1413  }
1414  }
1415 
1416  // nothing found, just write out the word
1417  codifyLines(yyscanner,clName);
1418  addToSearchIndex(yyscanner,clName);
1419  }
1420 }
1421 
1422 //-------------------------------------------------------------------------------
1423 
1424 /*
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.
1429 
1430 */
1431 static void generateFunctionLink(yyscan_t yyscanner,
1432  CodeOutputInterface &ol,
1433  const QCString &funcName)
1434 {
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("::");
1440  if (i>0)
1441  {
1442  locScope=locFunc.left(i);
1443  locFunc=locFunc.right(locFunc.length()-i-2).stripWhiteSpace();
1444  }
1445  //printf("generateFunctionLink(%s) classScope='%s'\n",qPrint(locFunc),qPrint(locScope));
1446  if (!locScope.isEmpty())
1447  {
1448  auto it = yyextra->codeClassMap.find(locScope.str());
1449  if (it!=yyextra->codeClassMap.end())
1450  {
1451  ScopedTypeVariant ccd = it->second;
1452  //printf("using classScope %s\n",qPrint(yyextra->classScope));
1453  if (ccd.localDef() && !ccd.localDef()->baseClasses().empty())
1454  {
1455  for (const auto &bcName : ccd.localDef()->baseClasses())
1456  {
1457  if (getLink(yyscanner,bcName,locFunc,ol,funcName))
1458  {
1459  return;
1460  }
1461  }
1462  }
1463  }
1464  }
1465  if (!getLink(yyscanner,locScope,locFunc,ol,funcName))
1466  {
1467  generateClassOrGlobalLink(yyscanner,ol,funcName);
1468  }
1469  return;
1470 }
1471 
1472 //-------------------------------------------------------------------------------
1473 
1474 static bool findMemberLink(yyscan_t yyscanner,
1475  CodeOutputInterface &ol,
1476  Definition *sym,
1477  const QCString &symName)
1478 {
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);
1483 
1484  if (sym->getOuterScope() &&
1485  sym->getOuterScope()->definitionType()==Definition::TypeClass &&
1486  yyextra->currentDefinition->definitionType()==Definition::TypeClass)
1487  {
1488  const ClassDef *cd = toClassDef(sym->getOuterScope());
1489  const ClassDef *thisCd = toClassDef(yyextra->currentDefinition);
1490  if (sym->definitionType()==Definition::TypeMember)
1491  {
1492  if (yyextra->currentMemberDef && yyextra->collectXRefs)
1493  {
1494  std::lock_guard<std::mutex> lock(g_docCrossReferenceMutex);
1495  addDocCrossReference(toMemberDefMutable(yyextra->currentMemberDef),toMemberDefMutable(toMemberDef(sym)));
1496  }
1497  }
1498  DBG_CTX((stderr,"cd=%s thisCd=%s\n",cd?qPrint(cd->name()):"<none>",thisCd?qPrint(thisCd->name()):"<none>"));
1499 
1500  // TODO: find the nearest base class in case cd is a base class of
1501  // thisCd
1502  if (cd==thisCd || (thisCd && thisCd->isBaseClass(cd,TRUE)))
1503  {
1504  writeMultiLineCodeLink(yyscanner,ol,sym,symName);
1505  return TRUE;
1506  }
1507  }
1508  return FALSE;
1509 }
1510 
1511 //-------------------------------------------------------------------------------
1512 
1513 static void findMemberLink(yyscan_t yyscanner,
1514  CodeOutputInterface &ol,
1515  const QCString &symName)
1516 {
1517  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1518  //printf("Member reference: %s scope=%s member=%s\n",
1519  // yytext,
1520  // yyextra->currentDefinition?qPrint(yyextra->currentDefinition->name()):"<none>",
1521  // yyextra->currentMemberDef?qPrint(yyextra->currentMemberDef->name()):"<none>"
1522  // );
1523  if (yyextra->currentDefinition)
1524  {
1525  auto range = Doxygen::symbolMap->find(symName);
1526  for (auto it = range.first; it!=range.second; ++it)
1527  {
1528  findMemberLink(yyscanner,ol,it->second,symName);
1529  }
1530  }
1531  //printf("sym %s not found\n",&yytext[5]);
1532  codify(yyscanner,symName);
1533 }
1534 
1535 
1536 //-------------------------------------------------------------------------------
1537 
1538 struct PythonCodeParser::Private
1539 {
1540  yyscan_t yyscanner;
1541  pycodeYY_state state;
1542 };
1543 
1544 PythonCodeParser::PythonCodeParser() : p(std::make_unique<Private>())
1545 {
1546  pycodeYYlex_init_extra(&p->state,&p->yyscanner);
1547 #ifdef FLEX_DEBUG
1548  pycodeYYset_debug(1,p->yyscanner);
1549 #endif
1550  resetCodeParserState();
1551 }
1552 
1553 PythonCodeParser::~PythonCodeParser()
1554 {
1555  pycodeYYlex_destroy(p->yyscanner);
1556 }
1557 
1558 void PythonCodeParser::resetCodeParserState()
1559 {
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();
1567  BEGIN( Body );
1568 }
1569 
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,
1577  int startLine,
1578  int endLine,
1579  bool inlineFragment,
1580  const MemberDef *memberDef,
1581  bool showLineNumbers,
1582  const Definition *searchCtx,
1583  bool collectXRefs
1584  )
1585 {
1586  yyscan_t yyscanner = p->yyscanner;
1587  struct yyguts_t *yyg = (struct yyguts_t*)yyscanner;
1588 
1589  //printf("***parseCode()\n");
1590 
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;
1600  if (startLine!=-1)
1601  yyextra->yyLineNr = startLine;
1602  else
1603  yyextra->yyLineNr = 1;
1604  if (endLine!=-1)
1605  yyextra->inputLines = endLine+1;
1606  else
1607  yyextra->inputLines = yyextra->yyLineNr + countLines(yyscanner) - 1;
1608 
1609 
1610  yyextra->exampleBlock = isExampleBlock;
1611  yyextra->exampleName = exampleName;
1612  yyextra->sourceFileDef = fileDef;
1613  yyextra->symbolResolver.setFileScope(fileDef);
1614 
1615  bool cleanupSourceDef = FALSE;
1616  if (yyextra->exampleBlock && fileDef==0)
1617  {
1618  // create a dummy filedef for the example
1619  yyextra->sourceFileDef = createFileDef("",(!exampleName.isEmpty()?qPrint(exampleName):"generated"));
1620  cleanupSourceDef = TRUE;
1621  }
1622  if (yyextra->sourceFileDef)
1623  {
1624  setCurrentDoc(yyscanner,"l00001");
1625  }
1626 
1627  yyextra->includeCodeFragment = inlineFragment;
1628  // Starts line 1 on the output
1629  startCodeLine(yyscanner);
1630 
1631  pycodeYYrestart(0,yyscanner);
1632 
1633  pycodeYYlex(yyscanner);
1634 
1635  if (!yyextra->indents.empty())
1636  {
1637  // printf("Exited pysourceparser in inconsistent state!\n");
1638  }
1639 
1640  if (yyextra->needsTermination)
1641  {
1642  endCodeLine(yyscanner);
1643  }
1644  if (cleanupSourceDef)
1645  {
1646  // delete the temporary file definition used for this example
1647  delete yyextra->sourceFileDef;
1648  yyextra->sourceFileDef=0;
1649  }
1650  // write the tooltips
1651  yyextra->tooltipManager.writeTooltips(codeOutIntf);
1652  printlex(yy_flex_debug, FALSE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL);
1653 }
1654 
1655 #if USE_STATE2STRING
1656 #include "pycode.l.h"
1657 #endif