1 /******************************************************************************
5 * Copyright (C) 1997-2015 by Dimitri van Heesch.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation under the terms of the GNU General Public License is hereby
9 * granted. No representations are made about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 * See the GNU General Public License for more details.
13 * Documents produced by Doxygen are derivative works derived from the
14 * input used in their production; they are not affected by this license.
17 /* This code is based on the work done by the MoxyPyDoxy team
18 * (Linda Leong, Mike Rivera, Kim Truong, and Gabriel Estrada)
19 * in Spring 2005 as part of CS 179E: Compiler Design Project
20 * at the University of California, Riverside; the course was
21 * taught by Peter H. Froehlich <phf@acm.org>.
24 %option never-interactive
25 %option prefix="pycodeYY"
30 #include <qvaluestack.h>
38 #include "outputlist.h"
40 #include "membername.h"
41 #include "searchindex.h"
44 #include "classlist.h"
46 #include "namespacedef.h"
49 // Toggle for some debugging info
50 //#define DBG_CTX(x) fprintf x
51 #define DBG_CTX(x) do { } while(0)
54 #define YY_NO_UNISTD_H 1
56 static ClassSDict g_codeClassSDict(17);
57 static QCString g_curClassName;
58 static QStrList g_curClassBases;
61 static CodeOutputInterface * g_code;
62 static const char * g_inputString; //!< the code fragment as text
63 static int g_inputPosition; //!< read offset during parsing
64 static const char * g_currentFontClass;
65 static bool g_needsTermination;
66 static Definition *g_searchCtx;
67 static bool g_collectXRefs;
68 static int g_inputLines; //!< number of line in the code fragment
69 static int g_yyLineNr; //!< current line number
70 static FileDef * g_sourceFileDef;
71 static Definition * g_currentDefinition;
72 static MemberDef * g_currentMemberDef;
73 static bool g_includeCodeFragment;
74 static QCString g_realScope;
75 //static bool g_insideBody;
76 static int g_bodyCurlyCount;
77 static bool g_searchingForBody;
78 static QCString g_classScope;
79 static int g_paramParens;
80 //static int g_anchorCount;
82 static bool g_exampleBlock;
83 static QCString g_exampleName;
85 static QCString g_type;
86 static QCString g_name;
88 static bool g_doubleStringIsDoc;
89 static bool g_doubleQuote;
90 static bool g_noSuiteFound;
91 static int g_stringContext;
93 static QValueStack<uint> g_indents; //!< Tracks indentation levels for scoping in python
95 static QCString g_docBlock; //!< contents of all lines of a documentation block
96 static bool g_endComment;
98 static void endFontClass();
99 static void adjustScopesAndSuites(unsigned indentLength);
102 /*! Represents a stack of variable to class mappings as found in the
103 * code. Each scope is enclosed in pushScope() and popScope() calls.
104 * Variables are added by calling addVariables() and one can search
105 * for variable using findVariable().
107 class PyVariableContext
110 static const ClassDef *dummyContext;
111 class Scope : public SDict<ClassDef>
114 Scope() : SDict<ClassDef>(17) {}
119 m_scopes.setAutoDelete(TRUE);
122 virtual ~PyVariableContext()
128 m_scopes.append(new Scope);
133 if (m_scopes.count()>0)
135 m_scopes.remove(m_scopes.count()-1);
142 m_globalScope.clear();
145 void clearExceptGlobal()
150 void addVariable(const QCString &type,const QCString &name);
151 ClassDef *findVariable(const QCString &name);
155 QList<Scope> m_scopes;
158 void PyVariableContext::addVariable(const QCString &type,const QCString &name)
160 //printf("PyVariableContext::addVariable(%s,%s)\n",type.data(),name.data());
161 QCString ltype = type.simplifyWhiteSpace();
162 QCString lname = name.simplifyWhiteSpace();
164 Scope *scope = m_scopes.count()==0 ? &m_globalScope : m_scopes.getLast();
167 (varType=g_codeClassSDict[ltype]) || // look for class definitions inside the code block
168 (varType=getResolvedClass(g_currentDefinition,g_sourceFileDef,ltype)) // look for global class definitions
171 scope->append(lname,varType); // add it to a list
175 if (m_scopes.count()>0) // for local variables add a dummy entry so the name
176 // is hidden to avoid FALSE links to global variables with the same name
177 // TODO: make this work for namespaces as well!
179 scope->append(lname,dummyContext);
184 ClassDef *PyVariableContext::findVariable(const QCString &name)
186 if (name.isEmpty()) return 0;
187 ClassDef *result = 0;
188 QListIterator<Scope> sli(m_scopes);
190 // search from inner to outer scope
191 for (sli.toLast();(scope=sli.current());--sli)
193 result = scope->find(name);
199 // nothing found -> also try the global scope
200 result=m_globalScope.find(name);
204 static PyVariableContext g_theVarContext;
205 const ClassDef *PyVariableContext::dummyContext = (ClassDef*)0x8;
212 Ctx() : name(g_name), type(g_type), cd(0) {}
220 m_classList.append(new Ctx);
221 m_classList.setAutoDelete(TRUE);
224 virtual ~PyCallContext() {}
226 void setClass(ClassDef *cd)
228 Ctx *ctx = m_classList.getLast();
236 m_classList.append(new Ctx);
241 if (m_classList.count()>1)
243 Ctx *ctx = m_classList.getLast();
249 m_classList.removeLast();
259 m_classList.append(new Ctx);
262 ClassDef *getClass() const
264 Ctx *ctx = m_classList.getLast();
273 QList<Ctx> m_classList;
276 static PyCallContext g_theCallContext;
279 /*! counts the number of lines in the input */
280 static int countLines()
282 const char *p=g_inputString;
288 if (c=='\n') count++;
290 if (p>g_inputString && *(p-1)!='\n')
291 { // last line does not end with a \n, so we add an extra
292 // line and explicitly terminate the line after parsing.
294 g_needsTermination=TRUE;
299 static void setCurrentDoc(const QCString &anchor)
301 if (Doxygen::searchIndex)
305 Doxygen::searchIndex->setCurrentDoc(g_searchCtx,g_searchCtx->anchor(),FALSE);
309 Doxygen::searchIndex->setCurrentDoc(g_sourceFileDef,anchor,TRUE);
314 static void addToSearchIndex(const char *text)
316 if (Doxygen::searchIndex)
318 Doxygen::searchIndex->addWord(text,FALSE);
323 static ClassDef *stripClassName(const char *s,Definition *d=g_currentDefinition)
329 while (extractClassNameFromType(type,pos,className,templSpec)!=-1)
331 QCString clName=className+templSpec;
334 if (!g_classScope.isEmpty())
336 cd=getResolvedClass(d,g_sourceFileDef,g_classScope+"::"+clName);
340 cd=getResolvedClass(d,g_sourceFileDef,clName);
353 /*! start a new line of code, inserting a line number if g_sourceFileDef
354 * is TRUE. If a definition starts at the current line, then the line
355 * number is linked to the documentation of that definition.
357 static void startCodeLine()
359 //if (g_currentFontClass) { g_code->endFontClass(); }
362 //QCString lineNumber,lineAnchor;
363 //lineNumber.sprintf("%05d",g_yyLineNr);
364 //lineAnchor.sprintf("l%05d",g_yyLineNr);
366 Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr);
367 //printf("startCodeLine %d d=%p\n",g_yyLineNr,d);
368 //g_code->startLineNumber();
370 if (!g_includeCodeFragment && d && d->isLinkableInProject())
372 g_currentDefinition = d;
373 g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr);
374 //g_insideBody = FALSE;
375 g_endComment = FALSE;
376 g_searchingForBody = TRUE;
377 g_realScope = d->name().copy();
378 g_classScope = d->name().copy();
379 //printf("Real scope: `%s'\n",g_realScope.data());
380 g_bodyCurlyCount = 0;
382 lineAnchor.sprintf("l%05d",g_yyLineNr);
383 if (g_currentMemberDef)
385 g_code->writeLineNumber(g_currentMemberDef->getReference(),
386 g_currentMemberDef->getOutputFileBase(),
387 g_currentMemberDef->anchor(),g_yyLineNr);
388 setCurrentDoc(lineAnchor);
392 g_code->writeLineNumber(d->getReference(),
393 d->getOutputFileBase(),
395 setCurrentDoc(lineAnchor);
400 //g_code->codify(lineNumber);
401 g_code->writeLineNumber(0,0,0,g_yyLineNr);
403 //g_code->endLineNumber();
405 g_code->startCodeLine(g_sourceFileDef);
406 if (g_currentFontClass)
408 g_code->startFontClass(g_currentFontClass);
412 static void codify(const char* text)
414 g_code->codify(text);
417 static void endCodeLine()
420 g_code->endCodeLine();
423 static void nextCodeLine()
425 const char *fc = g_currentFontClass;
427 if (g_yyLineNr<g_inputLines)
429 g_currentFontClass = fc;
435 /*! writes a link to a fragment \a text that may span multiple lines, inserting
436 * line numbers for each line. If \a text contains newlines, the link will be
437 * split into multiple links with the same destination, one for each line.
439 static void writeMultiLineCodeLink(CodeOutputInterface &ol,
443 static bool sourceTooltips = Config_getBool(SOURCE_TOOLTIPS);
444 TooltipManager::instance()->addTooltip(d);
445 QCString ref = d->getReference();
446 QCString file = d->getOutputFileBase();
447 QCString anchor = d->anchor();
449 if (!sourceTooltips) // fall back to simple "title" tooltips
451 tooltip = d->briefDescriptionAsTooltip();
454 char *p=(char *)text;
459 while ((c=*p++) && c!='\n') { }
464 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
465 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
470 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
471 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
477 static void startFontClass(const char *s)
479 // if font class is already set don't stop and start it.
480 // strcmp does not like null pointers as input.
481 if (!g_currentFontClass || !s || strcmp(g_currentFontClass,s))
484 g_code->startFontClass(s);
485 g_currentFontClass=s;
489 static void endFontClass()
491 if (g_currentFontClass)
493 g_code->endFontClass();
494 g_currentFontClass=0;
498 static void codifyLines(char *text)
500 //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text);
504 const char * tmp_currentFontClass = g_currentFontClass;
508 while ((c=*p++) && c!='\n') { }
515 if (g_yyLineNr<g_inputLines)
519 if (tmp_currentFontClass)
521 startFontClass(tmp_currentFontClass);
532 static void codifyLines(const QCString &str)
534 char *tmp= (char *)malloc(str.length()+1);
540 static bool getLinkInScope(const QCString &c, // scope
541 const QCString &m, // member
542 const char *memberText, // exact text
543 CodeOutputInterface &ol,
552 //printf("Trying `%s'::`%s'\n",c.data(),m.data());
553 if (getDefs(c,m,"()",md,cd,fd,nd,gd,FALSE,g_sourceFileDef) &&
557 //if (cd) d=cd; else if (nd) d=nd; else if (fd) d=fd; else d=gd;
559 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
560 md->getBodyDef() : md->getOuterScope();
561 //printf("Found! d=%s\n",d?d->name().data():"<none>");
562 if (md->getGroupDef()) d = md->getGroupDef();
563 if (d && d->isLinkable())
565 g_theCallContext.setClass(stripClassName(md->typeString(),md->getOuterScope()));
566 //printf("g_currentDefinition=%p g_currentMemberDef=%p\n",
567 // g_currentDefinition,g_currentMemberDef);
569 if (g_currentDefinition && g_currentMemberDef &&
570 md!=g_currentMemberDef && g_collectXRefs)
572 addDocCrossReference(g_currentMemberDef,md);
574 //printf("d->getReference()=`%s' d->getOutputBase()=`%s' name=`%s' member name=`%s'\n",d->getReference().data(),d->getOutputFileBase().data(),d->name().data(),md->name().data());
576 writeMultiLineCodeLink(ol,md, text ? text : memberText);
577 addToSearchIndex(text ? text : memberText);
584 static bool getLink(const char *className,
585 const char *memberName,
586 CodeOutputInterface &ol,
589 QCString m=removeRedundantWhiteSpace(memberName);
590 QCString c=className;
591 if (!getLinkInScope(c,m,memberName,ol,text))
593 if (!g_curClassName.isEmpty())
595 if (!c.isEmpty()) c.prepend("::");
596 c.prepend(g_curClassName);
597 return getLinkInScope(c,m,memberName,ol,text);
606 For a given string in the source code,
607 finds its class or global id and links to it.
609 static void generateClassOrGlobalLink(CodeOutputInterface &ol,char *clName,
612 QCString className=clName;
614 // Don't do anything for empty text
615 if (className.isEmpty()) return;
617 DBG_CTX((stderr,"generateClassOrGlobalLink(className=%s)\n",className.data()));
619 ClassDef *cd=0,*lcd=0; /** Class def that we may find */
620 MemberDef *md=0; /** Member def that we may find */
621 //bool isLocal=FALSE;
623 if ((lcd=g_theVarContext.findVariable(className))==0) // not a local variable
625 Definition *d = g_currentDefinition;
626 QCString scope = substitute(className,".","::");
628 cd = getResolvedClass(d,g_sourceFileDef,substitute(className,".","::"),&md);
630 DBG_CTX((stderr,"d=%s g_sourceFileDef=%s\n",
631 d?d->displayName().data():"<null>",
632 g_currentDefinition?g_currentDefinition->displayName().data():"<null>"));
633 DBG_CTX((stderr,"is found as a type %s\n",cd?cd->name().data():"<null>"));
635 if (cd==0 && md==0) // also see if it is variable or enum or enum value
637 NamespaceDef *nd = getResolvedNamespace(scope);
640 writeMultiLineCodeLink(ol,nd,clName);
641 addToSearchIndex(className);
644 else if (getLink(g_classScope,clName,ol,clName))
652 if (lcd!=PyVariableContext::dummyContext)
654 g_theCallContext.setClass(lcd);
657 DBG_CTX((stderr,"is a local variable cd=%p!\n",cd));
660 if (cd && cd->isLinkable()) // is it a linkable class
662 writeMultiLineCodeLink(ol,cd,clName);
663 addToSearchIndex(className);
666 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
667 md->getBodyDef() : md->getOuterScope();
668 if (md->getGroupDef()) d = md->getGroupDef();
669 if (d && d->isLinkable() && md->isLinkable() &&
670 g_currentMemberDef && g_collectXRefs)
672 addDocCrossReference(g_currentMemberDef,md);
676 else // not a class, maybe a global member
678 int scopeEnd = className.findRev(".");
679 if (scopeEnd!=-1 && !typeOnly) // name with explicit scope
681 QCString scope = substitute(className.left(scopeEnd),".","::");
682 QCString locName = className.right(className.length()-scopeEnd-1);
683 ClassDef *mcd = getClass(scope);
684 DBG_CTX((stderr,"scope=%s locName=%s mcd=%p\n",scope.data(),locName.data(),mcd));
687 MemberDef *md = mcd->getMemberByName(locName);
690 g_theCallContext.setClass(stripClassName(md->typeString(),md->getOuterScope()));
691 writeMultiLineCodeLink(ol,md,clName);
692 addToSearchIndex(className);
693 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
694 md->getBodyDef() : md->getOuterScope();
695 if (md->getGroupDef()) d = md->getGroupDef();
696 if (d && d->isLinkable() && md->isLinkable() &&
697 g_currentMemberDef && g_collectXRefs)
699 addDocCrossReference(g_currentMemberDef,md);
704 else // check namespace as well
706 NamespaceDef *mnd = getResolvedNamespace(scope);
709 MemberDef *md=mnd->getMemberByName(locName);
712 //printf("name=%s scope=%s\n",locName.data(),scope.data());
713 g_theCallContext.setClass(stripClassName(md->typeString(),md->getOuterScope()));
714 writeMultiLineCodeLink(ol,md,clName);
715 addToSearchIndex(className);
716 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
717 md->getBodyDef() : md->getOuterScope();
718 if (md->getGroupDef()) d = md->getGroupDef();
719 if (d && d->isLinkable() && md->isLinkable() &&
720 g_currentMemberDef && g_collectXRefs)
722 addDocCrossReference(g_currentMemberDef,md);
730 // nothing found, just write out the word
732 addToSearchIndex(clName);
737 As of June 1, this function seems to work
738 for file members, but scopes are not
739 being correctly tracked for classes
740 so it doesn't work for classes yet.
743 static void generateFunctionLink(CodeOutputInterface &ol,char *funcName)
745 //CodeClassDef *ccd=0;
747 QCString locScope=g_classScope.copy();
748 QCString locFunc=removeRedundantWhiteSpace(funcName);
749 DBG_CTX((stdout,"*** locScope=%s locFunc=%s\n",locScope.data(),locFunc.data()));
750 int i=locFunc.findRev("::");
753 locScope=locFunc.left(i);
754 locFunc=locFunc.right(locFunc.length()-i-2).stripWhiteSpace();
756 //printf("generateFunctionLink(%s) classScope=`%s'\n",locFunc.data(),locScope.data());
757 if (!locScope.isEmpty() && (ccd=g_codeClassSDict[locScope]))
759 //printf("using classScope %s\n",g_classScope.data());
760 if (ccd->baseClasses())
762 BaseClassListIterator bcli(*ccd->baseClasses());
763 for ( ; bcli.current() ; ++bcli)
765 if (getLink(bcli.current()->classDef->name(),locFunc,ol,funcName))
772 if (!getLink(locScope,locFunc,ol,funcName))
774 generateClassOrGlobalLink(ol,funcName);
779 static bool findMemberLink(CodeOutputInterface &ol,Definition *sym,const char *symName)
781 //printf("sym %s outerScope=%s equal=%d\n",
782 // sym->name().data(),sym->getOuterScope()->name().data(),
783 // sym->getOuterScope()==g_currentDefinition);
785 if (sym->getOuterScope() &&
786 sym->getOuterScope()->definitionType()==Definition::TypeClass &&
787 g_currentDefinition->definitionType()==Definition::TypeClass)
789 ClassDef *cd = (ClassDef*)sym->getOuterScope();
790 ClassDef *thisCd = (ClassDef *)g_currentDefinition;
791 if (sym->definitionType()==Definition::TypeMember)
793 if (g_currentMemberDef && g_collectXRefs)
795 addDocCrossReference(g_currentMemberDef,(MemberDef*)sym);
798 DBG_CTX((stderr,"cd=%s thisCd=%s\n",cd?cd->name().data():"<none>",thisCd?thisCd->name().data():"<none>"));
800 // TODO: find the nearest base class in case cd is a base class of
802 if (cd==thisCd || (thisCd && thisCd->isBaseClass(cd,TRUE)))
804 writeMultiLineCodeLink(ol,sym,symName);
811 static void findMemberLink(CodeOutputInterface &ol,char *symName)
813 //printf("Member reference: %s scope=%s member=%s\n",
815 // g_currentDefinition?g_currentDefinition->name().data():"<none>",
816 // g_currentMemberDef?g_currentMemberDef->name().data():"<none>"
818 if (g_currentDefinition)
820 DefinitionIntf *di = Doxygen::symbolMap->find(symName);
823 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multiple symbols
825 DefinitionListIterator dli(*(DefinitionList*)di);
827 for (dli.toFirst();(sym=dli.current());++dli)
829 if (findMemberLink(ol,sym,symName)) return;
832 else // single symbol
834 if (findMemberLink(ol,(Definition*)di,symName)) return;
838 //printf("sym %s not found\n",&yytext[5]);
843 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
845 static int yyread(char *buf,int max_size)
848 while( c < max_size && g_inputString[g_inputPosition] )
850 *buf = g_inputString[g_inputPosition++] ;
864 LETTER [A-Za-z\x80-\xFF]
865 NONEMPTY [A-Za-z0-9_\x80-\xFF]
866 EXPCHAR [#(){}\[\],:.%/\\=`*~|&<>!;+-]
867 NONEMPTYEXP [^ \t\n:]
868 PARAMNONEMPTY [^ \t\n():]
869 IDENTIFIER ({LETTER}|"_")({LETTER}|{DIGIT}|"_")*
870 BORDER ([^A-Za-z0-9])
875 TRIDOUBLEQUOTE "\"\"\""
876 LONGSTRINGCHAR [^\\"']
878 LONGSTRINGITEM ({LONGSTRINGCHAR}|{ESCAPESEQ})
879 SMALLQUOTE ("\"\""|"\""|"'"|"''")
880 LONGSTRINGBLOCK ({LONGSTRINGITEM}+|{SMALLQUOTE})
882 SHORTSTRING ("'"{SHORTSTRINGITEM}*"'"|'"'{SHORTSTRINGITEM}*'"')
883 SHORTSTRINGITEM ({SHORTSTRINGCHAR}|{ESCAPESEQ})
884 SHORTSTRINGCHAR [^\\\n"]
885 STRINGLITERAL {STRINGPREFIX}?( {SHORTSTRING} | {LONGSTRING})
886 STRINGPREFIX ("r"|"u"|"ur"|"R"|"U"|"UR"|"Ur"|"uR")
887 KEYWORD ("lambda"|"import"|"class"|"assert"|"as"|"from"|"global"|"def"|"True"|"False")
888 FLOWKW ("or"|"and"|"is"|"not"|"print"|"for"|"in"|"if"|"try"|"except"|"yield"|"raise"|"break"|"continue"|"pass"|"if"|"return"|"while"|"elif"|"else"|"finally")
889 QUOTES ("\""[^"]*"\"")
890 SINGLEQUOTES ("'"[^']*"'")
892 LONGINTEGER {INTEGER}("l"|"L")
893 INTEGER ({DECIMALINTEGER}|{OCTINTEGER}|{HEXINTEGER})
894 DECIMALINTEGER ({NONZERODIGIT}{DIGIT}*|"0")
895 OCTINTEGER "0"{OCTDIGIT}+
896 HEXINTEGER "0"("x"|"X"){HEXDIGIT}+
899 HEXDIGIT ({DIGIT}|[a-f]|[A-F])
900 FLOATNUMBER ({POINTFLOAT}|{EXPONENTFLOAT})
901 POINTFLOAT ({INTPART}?{FRACTION}|{INTPART}".")
902 EXPONENTFLOAT ({INTPART}|{POINTFLOAT}){EXPONENT}
905 EXPONENT ("e"|"E")("+"|"-")?{DIGIT}+
906 IMAGNUMBER ({FLOATNUMBER}|{INTPART})("j"|"J")
907 ATOM ({IDENTIFIER}|{LITERAL}|{ENCLOSURE})
908 ENCLOSURE ({PARENTH_FORM}|{LIST_DISPLAY}|{DICT_DISPLAY}|{STRING_CONVERSION})
909 LITERAL ({STRINGLITERAL}|{INTEGER}|{LONGINTEGER}|{FLOATNUMBER}|{IMAGNUMBER})
910 PARENTH_FORM "("{EXPRESSION_LIST}?")"
911 TEST ({AND_TEST}("or"{AND_TEST})*|{LAMBDA_FORM})
912 TESTLIST {TEST}( ","{TEST})*","?
913 LIST_DISPLAY "["{LISTMAKER}?"]"
914 LISTMAKER {EXPRESSION}({LIST_FOR}|(","{EXPRESSION})*","?)
915 LIST_ITER ({LIST_FOR}|{LIST_IF})
916 LIST_FOR "for"{EXPRESSION_LIST}"in"{TESTLIST}{LIST_ITER}?
917 LIST_IF "if"{TEST}{LIST_ITER}?
918 DICT_DISPLAY "\{"{KEY_DATUM_LIST}?"\}"
919 KEY_DATUM_LIST {KEY_DATUM}(","{KEY_DATUM})*","?
920 KEY_DATUM {EXPRESSION}":"{EXPRESSION}
921 STRING_CONVERSION "`"{EXPRESSION_LIST}"`"
922 PRIMARY ({ATOM}|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING}|{CALL})
923 ATTRIBUTEREF {PRIMARY}"."{IDENTIFIER}
924 SUBSCRIPTION {PRIMARY}"["{EXPRESSION_LIST}"]"
925 SLICING ({SIMPLE_SLICING}|{EXTENDED_SLICING})
926 SIMPLE_SLICING {PRIMARY}"["{SHORT_SLICE}"]"
927 EXTENDED_SLICING {PRIMARY}"["{SLICE_LIST}"]"
928 SLICE_LIST {SLICE_ITEM}(","{SLICE_ITEM})*","?
929 SLICE_ITEM ({EXPRESSION}|{PROPER_SLICE}|{ELLIPSIS})
930 PROPER_SLICE ({SHORT_SLICE}|{LONG_SLICE})
931 SHORT_SLICE {LOWER_BOUND}?":"{UPPER_BOUND}?
932 LONG_SLICE {SHORT_SLICE}":"{STRIDE}?
933 LOWER_BOUND {EXPRESSION}
934 UPPER_BOUND {EXPRESSION}
937 CALL {PRIMARY}"("({ARGUMENT_LIST}","?)?")"
938 ARGUMENT_LIST ({POSITIONAL_ARGUMENTS}(","{KEYWORD_ARGUMENTS})?(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|{KEYWORD_ARGUMENTS}(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|"*"{EXPRESSION}(",""**"{EXPRESSION})?|"**"{EXPRESSION})
939 POSITIONAL_ARGUMENTS {EXPRESSION}(","{EXPRESSION})*
940 KEYWORD_ARGUMENTS {KEYWORD_ITEM}(","{KEYWORD_ITEM})*
941 KEYWORD_ITEM {IDENTIFIER}"="{EXPRESSION}
942 POWER {PRIMARY}("**"{U_EXPR})?
943 U_EXPR ({POWER}|"-"{U_EXPR}|"+"{U_EXPR}|"\~"{U_EXPR})
944 M_EXPR ({U_EXPR}|{M_EXPR}"*"{U_EXPR}|{M_EXPR}"//"{U_EXPR}|{M_EXPR}"/"{U_EXPR}|{M_EXPR}"\%"{U_EXPR})
945 A_EXPR ({M_EXPR}|{A_EXPR}"+"{M_EXPR}|{A_EXPR}"-"{M_EXPR}
946 SHIFT_EXPR ({A_EXPR}|{SHIFT_EXPR}("<<"|">>"){A_EXPR})
947 AND_EXPR ({SHIFT_EXPR}|{AND_EXPR}"\;SPMamp;"{SHIFT_EXPR}
948 XOR_EXPR ({AND_EXPR}|{XOR_EXPR}"\textasciicircum"{AND_EXPR})
949 OR_EXPR ({XOR_EXPR}|{OR_EXPR}"|"{ XOR_EXPR})
951 COMPARISON {OR_EXPR}({COMP_OPERATOR}{OR_EXPR})*
952 COMP_OPERATOR ("<"|">"|"=="|">="|"<="|"<>"|"!="|"is""not"?|"not"?"in")
953 EXPRESSION ({OR_TEST}|{LAMBDA_FORM})
954 OR_TEST ({AND_TEST}|{OR_TEST}"or"{AND_TEST})
955 AND_TEST ({NOT_TEST}|{AND_TEST}"and"{NOT_TEST})
956 NOT_TEST ({COMPARISON}|"not"{NOT_TEST})
957 LAMBDA_FORM "lambda"{PARAMETER_LIST}?":"{EXPRESSION}
958 EXPRESSION_LIST {EXPRESSION}(","{EXPRESSION})*","?
959 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})
960 EXPRESSION_STMT {EXPRESSION_LIST}
961 ASSERT_STMT "assert"{EXPRESSION}(","{EXPRESSION})?
962 ASSIGNMENT_STMT ({TARGET_LIST}"=")+{EXPRESSION_LIST}
963 TARGET_LIST {TARGET}(","{TARGET})*","?
964 TARGET ({IDENTIFIER}|"("{TARGET_LIST}")"|"["{TARGET_LIST}"]"|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING})
979 %x SuiteCaptureIndent
995 startFontClass("keyword");
998 BEGIN( FunctionDec );
1002 startFontClass("keyword");
1008 startFontClass("keywordtype");
1012 "self."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER}"(" {
1014 findMemberLink(*g_code,&yytext[5]);
1016 "self."{IDENTIFIER}/"(" {
1018 findMemberLink(*g_code,&yytext[5]);
1020 "self."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER} {
1022 findMemberLink(*g_code,&yytext[5]);
1024 "self."{IDENTIFIER} {
1026 findMemberLink(*g_code,&yytext[5]);
1028 "cls."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER}"(" {
1030 findMemberLink(*g_code,&yytext[4]);
1032 "cls."{IDENTIFIER}/"(" {
1034 findMemberLink(*g_code,&yytext[4]);
1036 "cls."{IDENTIFIER}/"."({IDENTIFIER}".")*{IDENTIFIER} {
1038 findMemberLink(*g_code,&yytext[4]);
1040 "cls."{IDENTIFIER} {
1042 findMemberLink(*g_code,&yytext[4]);
1046 <ClassDec>{IDENTIFIER} {
1048 generateClassOrGlobalLink(*g_code,yytext);
1050 g_curClassName = yytext;
1051 g_curClassBases.clear();
1052 BEGIN( ClassInheritance );
1060 ({IDENTIFIER}".")*{IDENTIFIER} {
1063 // that ALL identifiers
1065 // are base classes;
1066 // it doesn't check to see
1067 // that the first parenthesis
1070 // This is bad - it should
1071 // probably be more strict
1072 // about what to accept.
1074 g_curClassBases.inSort(yytext);
1075 generateClassOrGlobalLink(*g_code,yytext);
1083 // be a one-line suite;
1084 // found counter-example
1087 // Push a class scope
1089 ClassDef *classDefToAdd = new ClassDef("<code>",1,1,g_curClassName,ClassDef::Class,0,0,FALSE);
1090 g_codeClassSDict.append(g_curClassName,classDefToAdd);
1091 char *s=g_curClassBases.first();
1094 ClassDef *baseDefToAdd;
1095 baseDefToAdd=g_codeClassSDict[s];
1097 // Try to find class in global
1099 if (baseDefToAdd==0)
1101 baseDefToAdd=getResolvedClass(g_currentDefinition,g_sourceFileDef,s);
1104 if (baseDefToAdd && baseDefToAdd!=classDefToAdd)
1106 classDefToAdd->insertBaseClass(baseDefToAdd,s,Public,Normal);
1109 s=g_curClassBases.next();
1112 // Reset class-parsing variables.
1113 g_curClassName.resize(0);
1114 g_curClassBases.clear();
1116 g_noSuiteFound = TRUE;
1117 BEGIN( SuiteStart );
1124 generateFunctionLink(*g_code,yytext);
1129 BEGIN( FunctionParams );
1135 // Parses delimiters
1139 ({IDENTIFIER}|{PARAMNONEMPTY}+) {
1148 codifyLines(yytext);
1155 // be a one-line suite;
1156 // found counter-example
1158 g_noSuiteFound = TRUE;
1159 BEGIN( SuiteStart );
1166 // Position-sensitive rules!
1167 // Must come AFTER keyword-triggered rules
1168 // Must come BEFORE identifier NONEMPTY-like rules
1169 // to syntax highlight.
1171 startFontClass("keyword");
1177 startFontClass("keywordflow");
1181 ({IDENTIFIER}".")*{IDENTIFIER}/"(" {
1182 generateClassOrGlobalLink(*g_code,yytext);
1184 ({IDENTIFIER}".")+{IDENTIFIER} {
1185 generateClassOrGlobalLink(*g_code,yytext,TRUE);
1187 {IDENTIFIER} { codify(yytext); }
1199 startFontClass("keyword");
1200 codifyLines(yytext);
1205 startFontClass("keyword");
1206 codifyLines(yytext);
1209 // No indentation necessary
1210 g_noSuiteFound = FALSE;
1214 startFontClass("keywordflow");
1215 codifyLines(yytext);
1218 // No indentation necessary
1219 g_noSuiteFound = FALSE;
1227 if (YY_START==SingleQuoteString ||
1228 YY_START==DoubleQuoteString ||
1229 YY_START==TripleString
1234 yy_push_state(YY_START);
1240 codifyLines(yytext);
1241 if ( g_noSuiteFound )
1243 // printf("New suite to capture! [%d]\n", g_yyLineNr);
1244 BEGIN ( SuiteCaptureIndent );
1249 <SuiteCaptureIndent>{
1251 // Blankline - ignore, keep looking for indentation.
1252 codifyLines(yytext);
1256 // This state lasts momentarily,
1257 // to check the indentation
1258 // level that is about to be
1260 codifyLines(yytext);
1261 g_indents.push(yyleng);
1262 // printf("Captured indent of %d [line %d]\n", yyleng, g_yyLineNr);
1269 {BB}/({NONEMPTY}|{EXPCHAR}) {
1270 // This implements poor
1271 // indendation-tracking;
1272 // should be improved.
1273 // (translate tabs to space, etc)
1274 codifyLines(yytext);
1275 adjustScopesAndSuites((int)yyleng);
1279 // If this ever succeeds,
1280 // it means that this is
1281 // a blank line, and
1283 codifyLines(yytext);
1286 ""/({NONEMPTY}|{EXPCHAR}) {
1287 // Default rule; matches
1288 // the empty string, assuming
1289 // real text starts here.
1290 // Just go straight to Body.
1291 adjustScopesAndSuites(0);
1297 codifyLines(yytext);
1298 BEGIN( SuiteMaintain );
1300 <Body>{IDENTIFIER} {
1304 codifyLines(yytext);
1307 <SingleQuoteString>{ // Single quoted string like 'That\'s a """nice""" string!'
1308 \\{B}\n { // line continuation
1309 codifyLines(yytext);
1311 \\. { // espaced char
1314 {STRINGPREFIX}?{TRIDOUBLEQUOTE} { // tripple double quotes
1317 "'" { // end of the string
1320 BEGIN(g_stringContext);
1322 [^"'\n\\]+ { // normal chars
1330 <DoubleQuoteString>{ // Double quoted string like "That's \"a '''nice'''\" string!"
1331 \\{B}\n { // line continuation
1332 codifyLines(yytext);
1334 \\. { // espaced char
1337 {STRINGPREFIX}?{TRISINGLEQUOTE} { // tripple single quotes
1340 "\"" { // end of the string
1343 BEGIN(g_stringContext);
1345 [^"'\n\\]+ { // normal chars
1357 if (g_doubleQuote==(yytext[0]=='"'))
1360 BEGIN(g_stringContext);
1364 codifyLines(yytext);
1367 codifyLines(yytext);
1375 <*>({NONEMPTY}|{EXPCHAR}|{BB}) { // This should go one character at a time.
1377 // printf("[pycode] '%s' [ state %d ] [line %d] no match\n",
1378 // yytext, YY_START, g_yyLineNr);
1385 <*>{STRINGPREFIX}?{TRISINGLEQUOTE} |
1386 <*>{STRINGPREFIX}?{TRIDOUBLEQUOTE} {
1387 startFontClass("stringliteral");
1388 g_stringContext=YY_START;
1389 g_doubleQuote=yytext[yyleng-1]=='"';
1391 BEGIN(TripleString);
1393 <*>{STRINGPREFIX}?"'" { // single quoted string
1394 startFontClass("stringliteral");
1395 g_stringContext=YY_START;
1397 BEGIN(SingleQuoteString);
1399 <*>{STRINGPREFIX}?"\"" { // double quoted string
1400 startFontClass("stringliteral");
1401 g_stringContext=YY_START;
1403 BEGIN(DoubleQuoteString);
1405 <DocBlock>.* { // contents of current comment line
1408 <DocBlock>"\n"{B}("#") { // comment block (next line is also comment line)
1411 <DocBlock>{NEWLINE} { // comment block ends at the end of this line
1412 // remove special comment (default config)
1413 if (Config_getBool(STRIP_CODE_COMMENTS))
1415 g_yyLineNr+=((QCString)g_docBlock).contains('\n');
1418 else // do not remove comment
1420 startFontClass("comment");
1421 codifyLines(g_docBlock);
1427 <*>{POUNDCOMMENT}.* {
1428 if (YY_START==SingleQuoteString ||
1429 YY_START==DoubleQuoteString ||
1430 YY_START==TripleString
1435 yy_push_state(YY_START);
1439 <*>"#".* { // normal comment
1440 if (YY_START==SingleQuoteString ||
1441 YY_START==DoubleQuoteString ||
1442 YY_START==TripleString
1447 startFontClass("comment");
1448 codifyLines(yytext);
1458 codifyLines(yytext);
1460 //printf("[pycode] %d NEWLINE [line %d] no match\n",
1461 // YY_START, g_yyLineNr);
1473 // printf("[pycode] '%s' [ state %d ] [line %d] no match\n",
1474 // yytext, YY_START, g_yyLineNr);
1481 if (YY_START == DocBlock) {
1482 if (!Config_getBool(STRIP_CODE_COMMENTS))
1484 startFontClass("comment");
1485 codifyLines(g_docBlock);
1493 /*@ ----------------------------------------------------------------------------
1496 void resetPythonCodeParserState()
1498 g_currentDefinition = 0;
1499 g_currentMemberDef = 0;
1500 g_doubleStringIsDoc = FALSE;
1507 Examines current stack of white-space indentations;
1508 re-syncs the parser with the correct scope.
1510 static void adjustScopesAndSuites(unsigned indentLength)
1513 if (!g_indents.isEmpty() && indentLength < g_indents.top())
1515 while (!g_indents.isEmpty() && indentLength < g_indents.top())
1517 // printf("Exited scope indent of [%d]\n", g_indents.top());
1518 g_indents.pop(); // Pop the old suite's indentation
1520 g_currentMemberDef=0;
1521 if (g_currentDefinition)
1522 g_currentDefinition=g_currentDefinition->getOuterScope();
1526 // Are there any remaining indentation levels for suites?
1527 if (!g_indents.isEmpty())
1537 void parsePythonCode(CodeOutputInterface &od,const char * /*className*/,
1538 const QCString &s,bool exBlock, const char *exName,
1539 FileDef *fd,int startLine,int endLine,bool inlineFragment,
1540 MemberDef *,bool,Definition *searchCtx,bool collectXRefs)
1543 //printf("***parseCode()\n");
1545 //--------------------------------------
1546 if (s.isEmpty()) return;
1547 printlex(yy_flex_debug, TRUE, __FILE__, fd ? fd->fileName().data(): NULL);
1548 TooltipManager::instance()->clearTooltips();
1551 g_inputPosition = 0;
1552 g_currentFontClass = 0;
1553 g_needsTermination = FALSE;
1554 g_searchCtx=searchCtx;
1555 g_collectXRefs=collectXRefs;
1557 g_yyLineNr = startLine;
1561 g_inputLines = endLine+1;
1563 g_inputLines = g_yyLineNr + countLines() - 1;
1566 g_exampleBlock = exBlock;
1567 g_exampleName = exName;
1568 g_sourceFileDef = fd;
1570 bool cleanupSourceDef = FALSE;
1571 if (exBlock && fd==0)
1573 // create a dummy filedef for the example
1574 g_sourceFileDef = new FileDef("",(exName?exName:"generated"));
1575 cleanupSourceDef = TRUE;
1577 if (g_sourceFileDef)
1579 setCurrentDoc("l00001");
1582 g_includeCodeFragment = inlineFragment;
1583 // Starts line 1 on the output
1586 pycodeYYrestart( pycodeYYin );
1590 if (!g_indents.isEmpty())
1592 // printf("Exited pysourceparser in inconsistent state!\n");
1595 if (g_needsTermination)
1601 TooltipManager::instance()->writeTooltips(*g_code);
1603 if (cleanupSourceDef)
1605 // delete the temporary file definition used for this example
1606 delete g_sourceFileDef;
1609 printlex(yy_flex_debug, FALSE, __FILE__, fd ? fd->fileName().data(): NULL);
1614 #if !defined(YY_FLEX_SUBMINOR_VERSION)
1615 extern "C" { // some bogus code to keep the compiler happy
1616 void pycodeYYdummy() { yy_flex_realloc(0,0); }
1618 #elif YY_FLEX_MAJOR_VERSION<=2 && YY_FLEX_MINOR_VERSION<=5 && YY_FLEX_SUBMINOR_VERSION<33
1619 #error "You seem to be using a version of flex newer than 2.5.4. These are currently incompatible with 2.5.4, and do NOT work with doxygen! Please use version 2.5.4 or expect things to be parsed wrongly! A bug report has been submitted (#732132)."