1 /******************************************************************************
3 * $Id: pycode.h,v 1.9 2001/03/19 19:27:39 root Exp $
5 * Copyright (C) 1997-2012 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>.
28 #include <qvaluestack.h>
36 #include "outputlist.h"
38 #include "membername.h"
39 #include "searchindex.h"
41 #define YY_NEVER_INTERACTIVE 1
44 static ClassSDict g_codeClassSDict(17);
45 static QCString g_curClassName;
46 static QStrList g_curClassBases;
49 static CodeOutputInterface * g_code;
50 static const char * g_inputString; //!< the code fragment as text
51 static int g_inputPosition; //!< read offset during parsing
52 static const char * g_currentFontClass;
53 static bool g_needsTermination;
54 static Definition *g_searchCtx;
55 static int g_inputLines; //!< number of line in the code fragment
56 static int g_yyLineNr; //!< current line number
57 static FileDef * g_sourceFileDef;
58 static Definition * g_currentDefinition;
59 static MemberDef * g_currentMemberDef;
60 static bool g_includeCodeFragment;
61 static QCString g_realScope;
62 //static bool g_insideBody;
63 static int g_bodyCurlyCount;
64 static bool g_searchingForBody;
65 static QCString g_classScope;
66 static int g_paramParens;
67 //static int g_anchorCount;
69 static bool g_exampleBlock;
70 static QCString g_exampleName;
71 static QCString g_exampleFile;
73 static QCString g_type;
74 static QCString g_name;
76 static bool g_doubleStringIsDoc;
77 static bool g_doubleQuote;
78 static bool g_noSuiteFound;
79 static int g_stringContext;
81 static QValueStack<uint> g_indents; //!< Tracks indentation levels for scoping in python
83 static void endFontClass();
84 static void adjustScopesAndSuites(unsigned indentLength);
87 /*! Represents a stack of variable to class mappings as found in the
88 * code. Each scope is enclosed in pushScope() and popScope() calls.
89 * Variables are added by calling addVariables() and one can search
90 * for variable using findVariable().
92 class PyVariableContext
95 static const ClassDef *dummyContext;
96 class Scope : public SDict<ClassDef>
99 Scope() : SDict<ClassDef>(17) {}
104 m_scopes.setAutoDelete(TRUE);
107 virtual ~PyVariableContext()
113 m_scopes.append(new Scope);
118 if (m_scopes.count()>0)
120 m_scopes.remove(m_scopes.count()-1);
127 m_globalScope.clear();
130 void clearExceptGlobal()
135 void addVariable(const QCString &type,const QCString &name);
136 ClassDef *findVariable(const QCString &name);
140 QList<Scope> m_scopes;
143 void PyVariableContext::addVariable(const QCString &type,const QCString &name)
145 //printf("PyVariableContext::addVariable(%s,%s)\n",type.data(),name.data());
146 QCString ltype = type.simplifyWhiteSpace();
147 QCString lname = name.simplifyWhiteSpace();
149 Scope *scope = m_scopes.count()==0 ? &m_globalScope : m_scopes.getLast();
152 (varType=g_codeClassSDict[ltype]) || // look for class definitions inside the code block
153 (varType=getResolvedClass(g_currentDefinition,g_sourceFileDef,ltype)) // look for global class definitions
156 scope->append(lname,varType); // add it to a list
160 if (m_scopes.count()>0) // for local variables add a dummy entry so the name
161 // is hidden to avoid FALSE links to global variables with the same name
162 // TODO: make this work for namespaces as well!
164 scope->append(lname,dummyContext);
169 ClassDef *PyVariableContext::findVariable(const QCString &name)
171 if (name.isEmpty()) return 0;
172 ClassDef *result = 0;
173 QListIterator<Scope> sli(m_scopes);
175 // search from inner to outer scope
176 for (sli.toLast();(scope=sli.current());--sli)
178 result = scope->find(name);
184 // nothing found -> also try the global scope
185 result=m_globalScope.find(name);
189 static PyVariableContext g_theVarContext;
190 const ClassDef *PyVariableContext::dummyContext = (ClassDef*)0x8;
197 Ctx() : name(g_name), type(g_type), cd(0) {}
205 m_classList.append(new Ctx);
206 m_classList.setAutoDelete(TRUE);
209 virtual ~PyCallContext() {}
211 void setClass(ClassDef *cd)
213 Ctx *ctx = m_classList.getLast();
221 m_classList.append(new Ctx);
226 if (m_classList.count()>1)
228 Ctx *ctx = m_classList.getLast();
234 m_classList.removeLast();
244 m_classList.append(new Ctx);
247 ClassDef *getClass() const
249 Ctx *ctx = m_classList.getLast();
258 QList<Ctx> m_classList;
261 static PyCallContext g_theCallContext;
264 /*! counts the number of lines in the input */
265 static int countLines()
267 const char *p=g_inputString;
273 if (c=='\n') count++;
275 if (p>g_inputString && *(p-1)!='\n')
276 { // last line does not end with a \n, so we add an extra
277 // line and explicitly terminate the line after parsing.
279 g_needsTermination=TRUE;
284 static void setCurrentDoc(const QCString &anchor)
286 if (Doxygen::searchIndex)
290 Doxygen::searchIndex->setCurrentDoc(g_searchCtx,g_searchCtx->anchor(),FALSE);
294 Doxygen::searchIndex->setCurrentDoc(g_sourceFileDef,anchor,TRUE);
299 static void addToSearchIndex(const char *text)
301 if (Doxygen::searchIndex)
303 Doxygen::searchIndex->addWord(text,FALSE);
308 static ClassDef *stripClassName(const char *s,Definition *d=g_currentDefinition)
314 while (extractClassNameFromType(type,pos,className,templSpec)!=-1)
316 QCString clName=className+templSpec;
319 if (!g_classScope.isEmpty())
321 cd=getResolvedClass(d,g_sourceFileDef,g_classScope+"::"+clName);
325 cd=getResolvedClass(d,g_sourceFileDef,clName);
338 /*! start a new line of code, inserting a line number if g_sourceFileDef
339 * is TRUE. If a definition starts at the current line, then the line
340 * number is linked to the documentation of that definition.
342 static void startCodeLine()
344 //if (g_currentFontClass) { g_code->endFontClass(); }
347 //QCString lineNumber,lineAnchor;
348 //lineNumber.sprintf("%05d",g_yyLineNr);
349 //lineAnchor.sprintf("l%05d",g_yyLineNr);
351 Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr);
352 //printf("startCodeLine %d d=%p\n",g_yyLineNr,d);
353 //g_code->startLineNumber();
354 if (!g_includeCodeFragment && d && d->isLinkableInProject())
356 g_currentDefinition = d;
357 g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr);
358 //g_insideBody = FALSE;
359 g_searchingForBody = TRUE;
360 g_realScope = d->name().copy();
361 g_classScope = d->name().copy();
362 //printf("Real scope: `%s'\n",g_realScope.data());
363 g_bodyCurlyCount = 0;
365 lineAnchor.sprintf("l%05d",g_yyLineNr);
366 if (g_currentMemberDef)
368 g_code->writeLineNumber(g_currentMemberDef->getReference(),
369 g_currentMemberDef->getOutputFileBase(),
370 g_currentMemberDef->anchor(),g_yyLineNr);
371 setCurrentDoc(lineAnchor);
375 g_code->writeLineNumber(d->getReference(),
376 d->getOutputFileBase(),
378 setCurrentDoc(lineAnchor);
383 //g_code->codify(lineNumber);
384 g_code->writeLineNumber(0,0,0,g_yyLineNr);
386 //g_code->endLineNumber();
388 g_code->startCodeLine(g_sourceFileDef);
389 if (g_currentFontClass)
391 g_code->startFontClass(g_currentFontClass);
395 static void codify(const char* text)
397 g_code->codify(text);
400 static void endCodeLine()
403 g_code->endCodeLine();
406 static void nextCodeLine()
408 const char *fc = g_currentFontClass;
410 if (g_yyLineNr<g_inputLines)
412 g_currentFontClass = fc;
418 /*! writes a link to a fragment \a text that may span multiple lines, inserting
419 * line numbers for each line. If \a text contains newlines, the link will be
420 * split into multiple links with the same destination, one for each line.
422 static void writeMultiLineCodeLink(CodeOutputInterface &ol,
423 const char *ref,const char *file,
424 const char *anchor,const char *text,
428 char *p=(char *)text;
433 while ((c=*p++) && c!='\n') { }
438 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
439 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
444 //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp);
445 ol.writeCodeLink(ref,file,anchor,sp,tooltip);
452 static void codifyLines(char *text)
454 //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text);
461 while ((c=*p++) && c!='\n') { }
477 static void addDocCrossReference(MemberDef *src,MemberDef *dst)
479 static bool referencedByRelation = Config_getBool("REFERENCED_BY_RELATION");
480 static bool callerGraph = Config_getBool("CALLER_GRAPH");
481 static bool referencesRelation = Config_getBool("REFERENCES_RELATION");
482 static bool callGraph = Config_getBool("CALL_GRAPH");
483 if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types
484 //printf("addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data());
485 if ((referencedByRelation || callerGraph) && (src->isFunction() || src->isSlot()))
487 dst->addSourceReferencedBy(src);
489 if ((referencesRelation || callGraph) && (src->isFunction() || src->isSlot()))
491 src->addSourceReferences(dst);
497 static bool getLinkInScope(const QCString &c, // scope
498 const QCString &m, // member
499 const char *memberText, // exact text
500 CodeOutputInterface &ol,
509 //printf("Trying `%s'::`%s'\n",c.data(),m.data());
510 if (getDefs(c,m,"()",md,cd,fd,nd,gd,FALSE,g_sourceFileDef) &&
514 //if (cd) d=cd; else if (nd) d=nd; else if (fd) d=fd; else d=gd;
516 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
517 md->getBodyDef() : md->getOuterScope();
518 //printf("Found! d=%s\n",d?d->name().data():"<none>");
519 if (md->getGroupDef()) d = md->getGroupDef();
520 if (d && d->isLinkable())
522 g_theCallContext.setClass(stripClassName(md->typeString(),md->getOuterScope()));
523 //printf("g_currentDefinition=%p g_currentMemberDef=%p\n",
524 // g_currentDefinition,g_currentMemberDef);
526 if (g_currentDefinition && g_currentMemberDef &&
527 md!=g_currentMemberDef)
529 addDocCrossReference(g_currentMemberDef,md);
531 //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());
533 writeMultiLineCodeLink(ol,md->getReference(),
534 md->getOutputFileBase(),
536 text ? text : memberText,
537 md->briefDescriptionAsTooltip());
538 addToSearchIndex(text ? text : memberText);
545 static bool getLink(const char *className,
546 const char *memberName,
547 CodeOutputInterface &ol,
550 QCString m=removeRedundantWhiteSpace(memberName);
551 QCString c=className;
552 if (!getLinkInScope(c,m,memberName,ol,text))
554 if (!g_curClassName.isEmpty())
556 if (!c.isEmpty()) c.prepend("::");
557 c.prepend(g_curClassName);
558 return getLinkInScope(c,m,memberName,ol,text);
567 For a given string in the source code,
568 finds its class or global id and links to it.
570 static void generateClassOrGlobalLink(CodeOutputInterface &ol,char *clName,
573 QCString className=clName;
575 // Don't do anything for empty text
576 if (className.isEmpty()) return;
578 //fprintf(stderr,"generateClassOrGlobalLink(className=%s)\n",className.data());
580 ClassDef *cd=0,*lcd=0; /** Class def that we may find */
581 MemberDef *md=0; /** Member def that we may find */
582 //bool isLocal=FALSE;
584 if ((lcd=g_theVarContext.findVariable(className))==0) // not a local variable
586 Definition *d = g_currentDefinition;
587 QCString scope = substitute(className,".","::");
589 cd = getResolvedClass(d,g_sourceFileDef,substitute(className,".","::"),&md);
591 //fprintf(stderr,"d=%s g_sourceFileDef=%s\n",
592 // d?d->displayName().data():"<null>",
593 // g_currentDefinition?g_currentDefinition->displayName().data():"<null>");
594 //fprintf(stderr,"is found as a type %s\n",cd?cd->name().data():"<null>");
596 if (cd==0 && md==0) // also see if it is variable or enum or enum value
598 NamespaceDef *nd = getResolvedNamespace(scope);
601 writeMultiLineCodeLink(ol,nd->getReference(),nd->getOutputFileBase(),nd->anchor(),clName,nd->briefDescriptionAsTooltip());
602 addToSearchIndex(className);
605 else if (getLink(g_classScope,clName,ol,clName))
613 if (lcd!=PyVariableContext::dummyContext)
615 g_theCallContext.setClass(lcd);
618 //fprintf(stderr,"is a local variable cd=%p!\n",cd);
621 if (cd && cd->isLinkable()) // is it a linkable class
623 writeMultiLineCodeLink(ol,cd->getReference(),cd->getOutputFileBase(),cd->anchor(),clName,cd->briefDescriptionAsTooltip());
624 addToSearchIndex(className);
627 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
628 md->getBodyDef() : md->getOuterScope();
629 if (md->getGroupDef()) d = md->getGroupDef();
630 if (d && d->isLinkable() && md->isLinkable() && g_currentMemberDef)
632 addDocCrossReference(g_currentMemberDef,md);
636 else // not a class, maybe a global member
638 int scopeEnd = className.findRev(".");
639 if (scopeEnd!=-1 && !typeOnly) // name with explicit scope
641 QCString scope = substitute(className.left(scopeEnd),".","::");
642 QCString locName = className.right(className.length()-scopeEnd-1);
643 ClassDef *mcd = getClass(scope);
644 //fprintf(stderr,"scope=%s locName=%s mcd=%p\n",scope.data(),locName.data(),mcd);
647 MemberDef *md = mcd->getMemberByName(locName);
650 g_theCallContext.setClass(stripClassName(md->typeString(),md->getOuterScope()));
651 writeMultiLineCodeLink(ol,md->getReference(),md->getOutputFileBase(),md->anchor(),clName,md->briefDescriptionAsTooltip());
652 addToSearchIndex(className);
653 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
654 md->getBodyDef() : md->getOuterScope();
655 if (md->getGroupDef()) d = md->getGroupDef();
656 if (d && d->isLinkable() && md->isLinkable() && g_currentMemberDef)
658 addDocCrossReference(g_currentMemberDef,md);
663 else // check namespace as well
665 NamespaceDef *mnd = getResolvedNamespace(scope);
668 MemberDef *md=mnd->getMemberByName(locName);
671 //printf("name=%s scope=%s\n",locName.data(),scope.data());
672 g_theCallContext.setClass(stripClassName(md->typeString(),md->getOuterScope()));
673 writeMultiLineCodeLink(ol,md->getReference(),md->getOutputFileBase(),md->anchor(),clName,md->briefDescriptionAsTooltip());
674 addToSearchIndex(className);
675 Definition *d = md->getOuterScope()==Doxygen::globalScope ?
676 md->getBodyDef() : md->getOuterScope();
677 if (md->getGroupDef()) d = md->getGroupDef();
678 if (d && d->isLinkable() && md->isLinkable() && g_currentMemberDef)
680 addDocCrossReference(g_currentMemberDef,md);
688 // nothing found, just write out the word
690 addToSearchIndex(clName);
695 As of June 1, this function seems to work
696 for file members, but scopes are not
697 being correctly tracked for classes
698 so it doesn't work for classes yet.
701 static void generateFunctionLink(CodeOutputInterface &ol,char *funcName)
703 //CodeClassDef *ccd=0;
705 QCString locScope=g_classScope.copy();
706 QCString locFunc=removeRedundantWhiteSpace(funcName);
707 //fprintf(stdout,"*** locScope=%s locFunc=%s\n",locScope.data(),locFunc.data());
708 int i=locFunc.findRev("::");
711 locScope=locFunc.left(i);
712 locFunc=locFunc.right(locFunc.length()-i-2).stripWhiteSpace();
714 //printf("generateFunctionLink(%s) classScope=`%s'\n",locFunc.data(),locScope.data());
715 if (!locScope.isEmpty() && (ccd=g_codeClassSDict[locScope]))
717 //printf("using classScope %s\n",g_classScope.data());
718 if (ccd->baseClasses())
720 BaseClassListIterator bcli(*ccd->baseClasses());
721 for ( ; bcli.current() ; ++bcli)
723 if (getLink(bcli.current()->classDef->name(),locFunc,ol,funcName))
730 if (!getLink(locScope,locFunc,ol,funcName))
732 generateClassOrGlobalLink(ol,funcName);
737 static bool findMemberLink(CodeOutputInterface &ol,Definition *sym,const char *symName)
739 //printf("sym %s outerScope=%s equal=%d\n",
740 // sym->name().data(),sym->getOuterScope()->name().data(),
741 // sym->getOuterScope()==g_currentDefinition);
743 if (sym->getOuterScope() &&
744 sym->getOuterScope()->definitionType()==Definition::TypeClass &&
745 g_currentDefinition->definitionType()==Definition::TypeClass)
747 ClassDef *cd = (ClassDef*)sym->getOuterScope();
748 ClassDef *thisCd = (ClassDef *)g_currentDefinition;
749 QCString anchor=sym->anchor();
750 if (sym->definitionType()==Definition::TypeMember)
752 if (g_currentMemberDef)
754 addDocCrossReference(g_currentMemberDef,(MemberDef*)sym);
757 //fprintf(stderr,"cd=%s thisCd=%s\n",cd?cd->name().data():"<none>",thisCd?thisCd->name().data():"<none>");
759 // TODO: find the nearest base class in case cd is a base class of
761 if (cd==thisCd || (thisCd && thisCd->isBaseClass(cd,TRUE)))
763 writeMultiLineCodeLink(ol,sym->getReference(),
764 sym->getOutputFileBase(),
767 sym->briefDescriptionAsTooltip());
774 static void findMemberLink(CodeOutputInterface &ol,char *symName)
776 //printf("Member reference: %s scope=%s member=%s\n",
778 // g_currentDefinition?g_currentDefinition->name().data():"<none>",
779 // g_currentMemberDef?g_currentMemberDef->name().data():"<none>"
781 if (g_currentDefinition)
783 DefinitionIntf *di = Doxygen::symbolMap->find(symName);
786 if (di->definitionType()==DefinitionIntf::TypeSymbolList) // multiple symbols
788 DefinitionListIterator dli(*(DefinitionList*)di);
790 for (dli.toFirst();(sym=dli.current());++dli)
792 if (findMemberLink(ol,sym,symName)) return;
795 else // single symbol
797 if (findMemberLink(ol,(Definition*)di,symName)) return;
801 //printf("sym %s not found\n",&yytext[5]);
805 static void startFontClass(const char *s)
808 g_code->startFontClass(s);
809 g_currentFontClass=s;
812 static void endFontClass()
814 if (g_currentFontClass)
816 g_code->endFontClass();
817 g_currentFontClass=0;
822 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
824 static int yyread(char *buf,int max_size)
827 while( c < max_size && g_inputString[g_inputPosition] )
829 *buf = g_inputString[g_inputPosition++] ;
844 NONEMPTY [A-Za-z0-9_]
845 EXPCHAR [#(){}\[\],:.%/\\=`*~|&<>!;+-]
846 NONEMPTYEXP [^ \t\n:]
847 PARAMNONEMPTY [^ \t\n():]
848 IDENTIFIER ({LETTER}|"_")({LETTER}|{DIGIT}|"_")*
849 BORDER ([^A-Za-z0-9])
854 TRIDOUBLEQUOTE "\"\"\""
855 LONGSTRINGCHAR [^\\"']
857 LONGSTRINGITEM ({LONGSTRINGCHAR}|{ESCAPESEQ})
858 SMALLQUOTE ("\"\""|"\""|"'"|"''")
859 LONGSTRINGBLOCK ({LONGSTRINGITEM}+|{SMALLQUOTE})
861 SHORTSTRING ("'"{SHORTSTRINGITEM}*"'"|'"'{SHORTSTRINGITEM}*'"')
862 SHORTSTRINGITEM ({SHORTSTRINGCHAR}|{ESCAPESEQ})
863 SHORTSTRINGCHAR [^\\\n"]
864 STRINGLITERAL {STRINGPREFIX}?( {SHORTSTRING} | {LONGSTRING})
865 STRINGPREFIX ("r"|"u"|"ur"|"R"|"U"|"UR"|"Ur"|"uR")
866 KEYWORD ("lambda"|"import"|"class"|"assert"|"as"|"from"|"global"|"def"|"True"|"False")
867 FLOWKW ("or"|"and"|"is"|"not"|"print"|"for"|"in"|"if"|"try"|"except"|"yield"|"raise"|"break"|"continue"|"pass"|"if"|"return"|"while"|"elif"|"else"|"finally")
868 QUOTES ("\""[^"]*"\"")
869 SINGLEQUOTES ("'"[^']*"'")
871 LONGINTEGER {INTEGER}("l"|"L")
872 INTEGER ({DECIMALINTEGER}|{OCTINTEGER}|{HEXINTEGER})
873 DECIMALINTEGER ({NONZERODIGIT}{DIGIT}*|"0")
874 OCTINTEGER "0"{OCTDIGIT}+
875 HEXINTEGER "0"("x"|"X"){HEXDIGIT}+
878 HEXDIGIT ({DIGIT}|[a-f]|[A-F])
879 FLOATNUMBER ({POINTFLOAT}|{EXPONENTFLOAT})
880 POINTFLOAT ({INTPART}?{FRACTION}|{INTPART}".")
881 EXPONENTFLOAT ({INTPART}|{POINTFLOAT}){EXPONENT}
884 EXPONENT ("e"|"E")("+"|"-")?{DIGIT}+
885 IMAGNUMBER ({FLOATNUMBER}|{INTPART})("j"|"J")
886 ATOM ({IDENTIFIER}|{LITERAL}|{ENCLOSURE})
887 ENCLOSURE ({PARENTH_FORM}|{LIST_DISPLAY}|{DICT_DISPLAY}|{STRING_CONVERSION})
888 LITERAL ({STRINGLITERAL}|{INTEGER}|{LONGINTEGER}|{FLOATNUMBER}|{IMAGNUMBER})
889 PARENTH_FORM "("{EXPRESSION_LIST}?")"
890 TEST ({AND_TEST}("or"{AND_TEST})*|{LAMBDA_FORM})
891 TESTLIST {TEST}( ","{TEST})*","?
892 LIST_DISPLAY "["{LISTMAKER}?"]"
893 LISTMAKER {EXPRESSION}({LIST_FOR}|(","{EXPRESSION})*","?)
894 LIST_ITER ({LIST_FOR}|{LIST_IF})
895 LIST_FOR "for"{EXPRESSION_LIST}"in"{TESTLIST}{LIST_ITER}?
896 LIST_IF "if"{TEST}{LIST_ITER}?
897 DICT_DISPLAY "\{"{KEY_DATUM_LIST}?"\}"
898 KEY_DATUM_LIST {KEY_DATUM}(","{KEY_DATUM})*","?
899 KEY_DATUM {EXPRESSION}":"{EXPRESSION}
900 STRING_CONVERSION "`"{EXPRESSION_LIST}"`"
901 PRIMARY ({ATOM}|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING}|{CALL})
902 ATTRIBUTEREF {PRIMARY}"."{IDENTIFIER}
903 SUBSCRIPTION {PRIMARY}"["{EXPRESSION_LIST}"]"
904 SLICING ({SIMPLE_SLICING}|{EXTENDED_SLICING})
905 SIMPLE_SLICING {PRIMARY}"["{SHORT_SLICE}"]"
906 EXTENDED_SLICING {PRIMARY}"["{SLICE_LIST}"]"
907 SLICE_LIST {SLICE_ITEM}(","{SLICE_ITEM})*","?
908 SLICE_ITEM ({EXPRESSION}|{PROPER_SLICE}|{ELLIPSIS})
909 PROPER_SLICE ({SHORT_SLICE}|{LONG_SLICE})
910 SHORT_SLICE {LOWER_BOUND}?":"{UPPER_BOUND}?
911 LONG_SLICE {SHORT_SLICE}":"{STRIDE}?
912 LOWER_BOUND {EXPRESSION}
913 UPPER_BOUND {EXPRESSION}
916 CALL {PRIMARY}"("({ARGUMENT_LIST}","?)?")"
917 ARGUMENT_LIST ({POSITIONAL_ARGUMENTS}(","{KEYWORD_ARGUMENTS})?(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|{KEYWORD_ARGUMENTS}(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|"*"{EXPRESSION}(",""**"{EXPRESSION})?|"**"{EXPRESSION})
918 POSITIONAL_ARGUMENTS {EXPRESSION}(","{EXPRESSION})*
919 KEYWORD_ARGUMENTS {KEYWORD_ITEM}(","{KEYWORD_ITEM})*
920 KEYWORD_ITEM {IDENTIFIER}"="{EXPRESSION}
921 POWER {PRIMARY}("**"{U_EXPR})?
922 U_EXPR ({POWER}|"-"{U_EXPR}|"+"{U_EXPR}|"\~"{U_EXPR})
923 M_EXPR ({U_EXPR}|{M_EXPR}"*"{U_EXPR}|{M_EXPR}"//"{U_EXPR}|{M_EXPR}"/"{U_EXPR}|{M_EXPR}"\%"{U_EXPR})
924 A_EXPR ({M_EXPR}|{A_EXPR}"+"{M_EXPR}|{A_EXPR}"-"{M_EXPR}
925 SHIFT_EXPR ({A_EXPR}|{SHIFT_EXPR}("<<"|">>"){A_EXPR})
926 AND_EXPR ({SHIFT_EXPR}|{AND_EXPR}"\;SPMamp;"{SHIFT_EXPR}
927 XOR_EXPR ({AND_EXPR}|{XOR_EXPR}"\textasciicircum"{AND_EXPR})
928 OR_EXPR ({XOR_EXPR}|{OR_EXPR}"|"{ XOR_EXPR})
930 COMPARISON {OR_EXPR}({COMP_OPERATOR}{OR_EXPR})*
931 COMP_OPERATOR ("<"|">"|"=="|">="|"<="|"<>"|"!="|"is""not"?|"not"?"in")
932 EXPRESSION ({OR_TEST}|{LAMBDA_FORM})
933 OR_TEST ({AND_TEST}|{OR_TEST}"or"{AND_TEST})
934 AND_TEST ({NOT_TEST}|{AND_TEST}"and"{NOT_TEST})
935 NOT_TEST ({COMPARISON}|"not"{NOT_TEST})
936 LAMBDA_FORM "lambda"{PARAMETER_LIST}?":"{EXPRESSION}
937 EXPRESSION_LIST {EXPRESSION}(","{EXPRESSION})*","?
938 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})
939 EXPRESSION_STMT {EXPRESSION_LIST}
940 ASSERT_STMT "assert"{EXPRESSION}(","{EXPRESSION})?
941 ASSIGNMENT_STMT ({TARGET_LIST}"=")+{EXPRESSION_LIST}
942 TARGET_LIST {TARGET}(","{TARGET})*","?
943 TARGET ({IDENTIFIER}|"("{TARGET_LIST}")"|"["{TARGET_LIST}"]"|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING})
958 %x SuiteCaptureIndent
973 startFontClass("keyword");
976 BEGIN( FunctionDec );
980 startFontClass("keyword");
986 startFontClass("keywordtype");
990 "self."{IDENTIFIER}/"(" {
992 findMemberLink(*g_code,&yytext[5]);
994 "self."{IDENTIFIER} {
996 findMemberLink(*g_code,&yytext[5]);
1000 <ClassDec>{IDENTIFIER} {
1002 generateClassOrGlobalLink(*g_code,yytext);
1004 g_curClassName = yytext;
1005 g_curClassBases.clear();
1006 BEGIN( ClassInheritance );
1014 ({IDENTIFIER}".")*{IDENTIFIER} {
1017 // that ALL identifiers
1019 // are base classes;
1020 // it doesn't check to see
1021 // that the first parenthesis
1024 // This is bad - it should
1025 // probably be more strict
1026 // about what to accept.
1028 g_curClassBases.inSort(yytext);
1029 generateClassOrGlobalLink(*g_code,yytext);
1037 // be a one-line suite;
1038 // found counter-example
1041 // Push a class scope
1043 ClassDef *classDefToAdd = new ClassDef("<code>",1,g_curClassName,ClassDef::Class,0,0,FALSE);
1044 g_codeClassSDict.append(g_curClassName,classDefToAdd);
1045 char *s=g_curClassBases.first();
1048 ClassDef *baseDefToAdd;
1049 baseDefToAdd=g_codeClassSDict[s];
1051 // Try to find class in global
1053 if (baseDefToAdd==0)
1055 baseDefToAdd=getResolvedClass(g_currentDefinition,g_sourceFileDef,s);
1058 if (baseDefToAdd && baseDefToAdd!=classDefToAdd)
1060 classDefToAdd->insertBaseClass(baseDefToAdd,s,Public,Normal);
1063 s=g_curClassBases.next();
1066 // Reset class-parsing variables.
1067 g_curClassName.resize(0);
1068 g_curClassBases.clear();
1070 g_noSuiteFound = TRUE;
1071 BEGIN( SuiteStart );
1078 generateFunctionLink(*g_code,yytext);
1083 BEGIN( FunctionParams );
1089 // Parses delimiters
1093 ({IDENTIFIER}|{PARAMNONEMPTY}+) {
1105 // be a one-line suite;
1106 // found counter-example
1108 g_noSuiteFound = TRUE;
1109 BEGIN( SuiteStart );
1116 // Position-sensitive rules!
1117 // Must come AFTER keyword-triggered rules
1118 // Must come BEFORE identifier NONEMPTY-like rules
1119 // to syntax highlight.
1121 startFontClass("keyword");
1127 startFontClass("keywordflow");
1131 ({IDENTIFIER}".")*{IDENTIFIER}/"(" {
1132 generateClassOrGlobalLink(*g_code,yytext);
1134 ({IDENTIFIER}".")+{IDENTIFIER} {
1135 generateClassOrGlobalLink(*g_code,yytext,TRUE);
1137 {IDENTIFIER} { codify(yytext); }
1149 startFontClass("keyword");
1150 codifyLines(yytext);
1155 startFontClass("keyword");
1156 codifyLines(yytext);
1159 // No indentation necesary
1160 g_noSuiteFound = FALSE;
1164 startFontClass("keywordflow");
1165 codifyLines(yytext);
1168 // No indentation necesary
1169 g_noSuiteFound = FALSE;
1177 // This eats EVERYTHING
1178 // except the newline
1179 startFontClass("comment");
1180 codifyLines(yytext);
1185 codifyLines(yytext);
1186 if ( g_noSuiteFound )
1188 // printf("New suite to capture! [%d]\n", g_yyLineNr);
1189 BEGIN ( SuiteCaptureIndent );
1194 <SuiteCaptureIndent>{
1196 // Blankline - ignore, keep looking for indentation.
1197 codifyLines(yytext);
1201 // This state lasts momentarily,
1202 // to check the indentation
1203 // level that is about to be
1205 codifyLines(yytext);
1206 g_indents.push(yyleng);
1207 // printf("Captured indent of %d [line %d]\n", yyleng, g_yyLineNr);
1214 {BB}/({NONEMPTY}|{EXPCHAR}) {
1215 // This implements poor
1216 // indendation-tracking;
1217 // should be improved.
1218 // (translate tabs to space, etc)
1219 codifyLines(yytext);
1220 adjustScopesAndSuites(yyleng);
1224 // If this ever succeeds,
1225 // it means that this is
1226 // a blank line, and
1228 codifyLines(yytext);
1231 ""/({NONEMPTY}|{EXPCHAR}) {
1232 // Default rule; matches
1233 // the empty string, assuming
1234 // real text starts here.
1235 // Just go straight to Body.
1236 adjustScopesAndSuites(0);
1242 codifyLines(yytext);
1243 BEGIN( SuiteMaintain );
1245 <Body>{IDENTIFIER} {
1249 codifyLines(yytext);
1252 <SingleQuoteString>{ // Single quoted string like 'That\'s a """nice""" string!'
1253 \\{B}\n { // line continuation
1254 codifyLines(yytext);
1256 \\. { // espaced char
1259 {STRINGPREFIX}?{TRIDOUBLEQUOTE} { // tripple double quotes
1262 "'" { // end of the string
1265 BEGIN(g_stringContext);
1267 [^"'\n\\]+ { // normal chars
1275 <DoubleQuoteString>{ // Double quoted string like "That's \"a '''nice'''\" string!"
1276 \\{B}\n { // line continuation
1277 codifyLines(yytext);
1279 \\. { // espaced char
1282 {STRINGPREFIX}?{TRISINGLEQUOTE} { // tripple single quotes
1285 "\"" { // end of the string
1288 BEGIN(g_stringContext);
1290 [^"'\n\\]+ { // normal chars
1302 if (g_doubleQuote==(yytext[0]=='"'))
1305 BEGIN(g_stringContext);
1309 codifyLines(yytext);
1312 codifyLines(yytext);
1320 <*>({NONEMPTY}|{EXPCHAR}|{BB}) { // This should go one character at a time.
1322 // printf("[pycode] '%s' [ state %d ] [line %d] no match\n",
1323 // yytext, YY_START, g_yyLineNr);
1330 <*>{STRINGPREFIX}?{TRISINGLEQUOTE} |
1331 <*>{STRINGPREFIX}?{TRIDOUBLEQUOTE} {
1332 startFontClass("stringliteral");
1333 g_stringContext=YY_START;
1334 g_doubleQuote=yytext[yyleng-1]=='"';
1336 BEGIN(TripleString);
1338 <*>{STRINGPREFIX}?"'" { // single quoted string
1339 startFontClass("stringliteral");
1340 g_stringContext=YY_START;
1342 BEGIN(SingleQuoteString);
1344 <*>{STRINGPREFIX}?"\"" { // double quoted string
1345 startFontClass("stringliteral");
1346 g_stringContext=YY_START;
1348 BEGIN(DoubleQuoteString);
1351 if (YY_START==SingleQuoteString ||
1352 YY_START==DoubleQuoteString ||
1353 YY_START==TripleString
1358 // This eats EVERYTHING
1359 // except the newline
1360 startFontClass("comment");
1361 codifyLines(yytext);
1365 codifyLines(yytext);
1366 //printf("[pycode] %d NEWLINE [line %d] no match\n",
1367 // YY_START, g_yyLineNr);
1379 // printf("[pycode] '%s' [ state %d ] [line %d] no match\n",
1380 // yytext, YY_START, g_yyLineNr);
1388 /*@ ----------------------------------------------------------------------------
1391 void resetPythonCodeParserState()
1393 g_currentDefinition = 0;
1394 g_currentMemberDef = 0;
1395 g_doubleStringIsDoc = FALSE;
1402 Examines current stack of white-space indentations;
1403 re-syncs the parser with the correct scope.
1405 static void adjustScopesAndSuites(unsigned indentLength)
1408 if (!g_indents.isEmpty() && indentLength < g_indents.top())
1410 while (!g_indents.isEmpty() && indentLength < g_indents.top())
1412 // printf("Exited scope indent of [%d]\n", g_indents.top());
1413 g_indents.pop(); // Pop the old suite's indentation
1415 g_currentMemberDef=0;
1416 if (g_currentDefinition)
1417 g_currentDefinition=g_currentDefinition->getOuterScope();
1421 // Are there any remaining indentation levels for suites?
1422 if (!g_indents.isEmpty())
1432 void parsePythonCode(CodeOutputInterface &od,const char * /*className*/,
1433 const QCString &s,bool exBlock, const char *exName,
1434 FileDef *fd,int startLine,int endLine,bool /*inlineFragment*/,
1435 MemberDef *,bool,Definition *searchCtx)
1438 //printf("***parseCode()\n");
1440 //--------------------------------------
1441 if (s.isEmpty()) return;
1444 g_inputPosition = 0;
1445 g_currentFontClass = 0;
1446 g_needsTermination = FALSE;
1447 g_searchCtx=searchCtx;
1449 g_inputLines = endLine+1;
1451 g_inputLines = countLines();
1454 g_yyLineNr = startLine;
1458 g_exampleBlock = exBlock;
1459 g_exampleName = exName;
1460 g_sourceFileDef = fd;
1462 bool cleanupSourceDef = FALSE;
1465 // create a dummy filedef for the example
1466 g_sourceFileDef = new FileDef("",(exName?exName:"generated"));
1467 cleanupSourceDef = TRUE;
1469 if (g_sourceFileDef)
1471 setCurrentDoc("l00001");
1474 // Starts line 1 on the output
1477 pycodeYYrestart( pycodeYYin );
1481 if (!g_indents.isEmpty())
1483 // printf("Exited pysourceparser in inconsistent state!\n");
1486 if (g_needsTermination)
1490 if (cleanupSourceDef)
1492 // delete the temporary file definition used for this example
1493 delete g_sourceFileDef;
1500 #if !defined(YY_FLEX_SUBMINOR_VERSION)
1501 extern "C" { // some bogus code to keep the compiler happy
1502 void pycodeYYdummy() { yy_flex_realloc(0,0); }
1504 #elif YY_FLEX_SUBMINOR_VERSION<33
1505 #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)."