From cf0e414d83f34ebf877abbe43a15c350876669d4 Mon Sep 17 00:00:00 2001 From: dimitri Date: Wed, 15 Jun 2005 19:21:39 +0000 Subject: Release-1.4.3-20050615 --- Doxyfile | 4 +- INSTALL | 4 +- README | 4 +- VERSION | 2 +- doc/Doxyfile | 2 +- doc/doxygen.1 | 8 +- doc/features.doc | 11 +- doc/htmlcmds.doc | 7 +- doc/index.doc | 3 + doc/xmlcmds.doc | 90 +++ src/Makefile.in | 3 +- src/cmdmapper.cpp | 91 +-- src/cmdmapper.h | 127 +++-- src/code.h | 11 +- src/code.l | 45 +- src/commentcnv.l | 9 +- src/commentscan.l | 19 +- src/config.l | 2 +- src/defgen.cpp | 1 - src/definition.cpp | 25 +- src/definition.h | 4 + src/docparser.cpp | 541 ++++++++++++++++-- src/docparser.h | 24 +- src/doctokenizer.h | 5 +- src/doctokenizer.l | 180 +++--- src/dot.cpp | 1 - src/doxygen.cpp | 72 +-- src/doxygen.h | 2 + src/filedef.cpp | 14 +- src/htmldocvisitor.cpp | 30 +- src/htmldocvisitor.h | 12 +- src/htmlgen.cpp | 4 +- src/htmlgen.h | 2 +- src/index.cpp | 1 - src/latexdocvisitor.cpp | 31 +- src/latexdocvisitor.h | 9 +- src/latexgen.cpp | 4 +- src/latexgen.h | 2 +- src/libdoxygen.pro.in | 4 + src/libdoxygen.t | 6 + src/mandocvisitor.cpp | 28 +- src/mandocvisitor.h | 8 +- src/mangen.cpp | 4 +- src/mangen.h | 2 +- src/memberdef.cpp | 6 +- src/membergroup.cpp | 1 - src/outputgen.h | 6 +- src/outputlist.cpp | 7 +- src/parserintf.h | 62 ++- src/pycode.h | 39 ++ src/pycode.l | 1425 +++++++++++++++++++++++++++++++++++++++++++++++ src/pyscanner.h | 56 ++ src/pyscanner.l | 1000 +++++++++++++++++++++++++++++++++ src/rtfdocvisitor.cpp | 29 +- src/rtfdocvisitor.h | 8 +- src/rtfgen.cpp | 4 +- src/rtfgen.h | 2 +- src/scanner.h | 36 +- src/scanner.l | 69 ++- src/util.cpp | 18 +- src/xmldocvisitor.cpp | 28 +- src/xmldocvisitor.h | 8 +- src/xmlgen.cpp | 53 +- 63 files changed, 3809 insertions(+), 506 deletions(-) create mode 100644 doc/xmlcmds.doc create mode 100644 src/pycode.h create mode 100644 src/pycode.l create mode 100644 src/pyscanner.h create mode 100644 src/pyscanner.l diff --git a/Doxyfile b/Doxyfile index c67bf87..43ddaf8 100644 --- a/Doxyfile +++ b/Doxyfile @@ -92,7 +92,9 @@ EXCLUDE = src/code.cpp \ src/searchindex.cpp \ src/searchindex.h \ src/commentcnv.cpp \ - src/commentscan.cpp + src/commentscan.cpp \ + src/pycode.cpp \ + src/pyscanner.cpp EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXAMPLE_PATH = diff --git a/INSTALL b/INSTALL index 92dd978..af1b6c4 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ -DOXYGEN Version 1.4.3-20050530 +DOXYGEN Version 1.4.3-20050615 Please read the installation section of the manual (http://www.doxygen.org/install.html) for instructions. -------- -Dimitri van Heesch (30 May 2005) +Dimitri van Heesch (15 June 2005) diff --git a/README b/README index e9f0890..e9cb674 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -DOXYGEN Version 1.4.3_20050530 +DOXYGEN Version 1.4.3_20050615 Please read INSTALL for compilation instructions. @@ -17,4 +17,4 @@ to subscribe to the lists or to visit the archives. Enjoy, -Dimitri van Heesch (dimitri@stack.nl) (30 May 2005) +Dimitri van Heesch (dimitri@stack.nl) (15 June 2005) diff --git a/VERSION b/VERSION index cf9bc9a..c1ffc41 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.3-20050530 +1.4.3-20050615 diff --git a/doc/Doxyfile b/doc/Doxyfile index 8c0cedb..ee3eb96 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -36,7 +36,7 @@ INPUT = index.doc install.doc starting.doc docblocks.doc lists.doc \ autolink.doc output.doc external.doc faq.doc trouble.doc history.doc features.doc \ doxygen_usage.doc doxytag_usage.doc \ doxywizard_usage.doc installdox_usage.doc \ - config.doc commands.doc htmlcmds.doc language.doc \ + config.doc commands.doc htmlcmds.doc xmlcmds.doc language.doc \ perlmod.doc perlmod_tree.doc arch.doc FILE_PATTERNS = *.cpp *.h *.doc EXAMPLE_PATH = ../examples diff --git a/doc/doxygen.1 b/doc/doxygen.1 index 3736aaa..fc4ccd8 100644 --- a/doc/doxygen.1 +++ b/doc/doxygen.1 @@ -1,9 +1,9 @@ .TH DOXYGEN "1" "DATE" "doxygen VERSION" "User Commands" .SH NAME -doxygen \- manual page for doxygen VERSION +doxygen \- documentation system for various programming languages .SH DESCRIPTION -Doxygen version VERSION -Copyright Dimitri van Heesch 1997-2005 +Doxygen is a documentation system for C++, C, Java, Objective-C, IDL +(Corba and Microsoft flavors) and to some extent PHP, C#, and D. .PP You can use doxygen in a number of ways: .TP @@ -40,5 +40,7 @@ doxygen \fB\-e\fR rtf extensionsFile .PP If \fB\-s\fR is specified the comments in the config file will be omitted. If configName is omitted `Doxyfile' will be used as a default. +.SH AUTHOR +Doxygen version VERSION, Copyright Dimitri van Heesch 1997-2005 .SH SEE ALSO doxytag(1), doxywizard(1). diff --git a/doc/features.doc b/doc/features.doc index 7eefca7..e3c76ad 100644 --- a/doc/features.doc +++ b/doc/features.doc @@ -22,11 +22,11 @@ Plain text will do, but for more fancy or structured output HTML tags and/or some of doxygen's special commands can be used.
  • Supports C/C++, Java, (Corba and Microsoft) Java, - IDL, and to some extent C# and PHP sources. + IDL, C#, Objective-C and to some extent D and PHP sources.
  • Supports documentation of files, namespaces, classes, structs, unions, templates, variables, functions, typedefs, enums and defines. -
  • JavaDoc (1.1), Qt-Doc, and KDOC compatible. -
  • Automatically generates class diagrams in HTML (as clickable +
  • JavaDoc (1.1), Qt-Doc, and ECMA-334 (C# spec.) compatible. +
  • Automatically generates class and collaboration diagrams in HTML (as clickable image maps) and \f$\mbox{\LaTeX}\f$ (as Encapsulated PostScript images).
  • Uses the dot tool of the Graphviz tool kit to generate include dependency graphs, collaboration diagrams, and @@ -76,8 +76,9 @@
  • Can cope with large projects easily. -Although doxygen can be used in any C or C++ project, it was specifically -designed to be used for projects that make use of Troll Tech's +Although doxygen can be used in any C or C++ project, +initially it was specifically designed to be used for projects that make +use of Troll Tech's Qt toolkit. I have tried to make doxygen `Qt-compatible'. That is: Doxygen can read the documentation contained in the Qt source code and create a class browser that looks very similar to the diff --git a/doc/htmlcmds.doc b/doc/htmlcmds.doc index 1639a99..0c8cd3b 100644 --- a/doc/htmlcmds.doc +++ b/doc/htmlcmds.doc @@ -17,9 +17,10 @@ /*! \page htmlcmds HTML Commands Here is a list of all HTML commands that may be used inside the -documentation. Note that all attributes of a HTML tag are passed on to -the HTML output only (the HREF and NAME attributes for the A tag are the -only exception). +documentation. Note that although these HTML tags are translated to the +proper commands for outer formats other than HTML, all attributes +of a HTML tag are passed on to the HTML output only +(the HREF and NAME attributes for the A tag are the only exception). The third part provides information for developers: @@ -186,6 +188,7 @@ Thanks go to:
  • Tim Mensch for adding the todo command.
  • Christian Hammond for redesigning the web-site.
  • Ken Wong for providing the HTML tree view code. +
  • Talin for adding support for C# style comments with XML markup.
  • Petr Prikryl for coordinating the internationalisation support. All language maintainers for providing translations into many languages.
  • Gerald Steffens of E-trend diff --git a/doc/xmlcmds.doc b/doc/xmlcmds.doc new file mode 100644 index 0000000..f6440ef --- /dev/null +++ b/doc/xmlcmds.doc @@ -0,0 +1,90 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2005 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ +/*! \page xmlcmds XML Commands + +Doxygen supports most of the XML commands that are typically used in C# +code comments. The XML tags are defined in Appendix E of the +ECMA-334 +standard, which defines the C# language. Unfortunately, the specification is +not very precise and a number of the examples given are of poor quality. + +Here is the list of tags supported by doxygen: + + + +Here is an example of a typical piece of code using some of the above commands: + +\code +/// +/// A search engine. +/// +class Engine +{ + /// + /// The Search method takes a series of parameters to specify the search criterion + /// and returns a dataset containing the result set. + /// + /// the connection string to connect to the + /// database holding the content to search + /// The maximum number of rows to + /// return in the result set + /// The text that we are searching for + /// A DataSet instance containing the matching rows. It contains a maximum + /// number of rows specified by the maxRows parameter + public DataSet Search(string connectionString, int maxRows, int searchString) + { + DataSet ds = new DataSet(); + return ds; + } +} +\endcode + +*/ + diff --git a/src/Makefile.in b/src/Makefile.in index 45c958c..7bc71bb 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -47,6 +47,7 @@ clean: Makefile.libdoxygen Makefile.libdoxycfg Makefile.doxygen Makefile.doxytag distclean: clean -$(RM) scanner.cpp code.cpp config.cpp pre.cpp ce_lex.cpp \ ce_parse.cpp ce_parse.h doxytag.cpp tag.cpp commentscan.cpp \ - declinfo.cpp defargs.cpp commentcnv.cpp doctokenizer.cpp + declinfo.cpp defargs.cpp commentcnv.cpp doctokenizer.cpp \ + pycode.cpp pyscanner.cpp FORCE: diff --git a/src/cmdmapper.cpp b/src/cmdmapper.cpp index 51c41eb..dddfcfd 100644 --- a/src/cmdmapper.cpp +++ b/src/cmdmapper.cpp @@ -100,7 +100,6 @@ CommandMap cmdMap[] = { "$", CMD_DOLLAR }, { "#", CMD_HASH }, { "%", CMD_PERCENT }, - //{ "~", CMD_LANGSWITCH }, { "_internalref", CMD_INTERNALREF }, { "dot", CMD_DOT }, { "enddot", CMD_ENDDOT }, @@ -112,42 +111,6 @@ CommandMap cmdMap[] = //---------------------------------------------------------------------------- -int CmdMapper::map(const char *name) -{ - return instance()->find(name); -} - -void CmdMapper::freeInstance() -{ - delete m_instance; m_instance=0; -} - -CmdMapper *CmdMapper::instance() -{ - if (m_instance==0) m_instance = new CmdMapper; - return m_instance; -} - -CmdMapper::CmdMapper() : m_map(89) -{ - m_map.setAutoDelete(TRUE); - CommandMap *p = cmdMap; - while (p->cmdName) - { - m_map.insert(p->cmdName,new int(p->cmdId)); - p++; - } -} -int CmdMapper::find(const char *name) -{ - int *result = m_map.find(name); - if (result) return *result; else return CMD_UNKNOWN; -} - -CmdMapper *CmdMapper::m_instance=0; - -//---------------------------------------------------------------------------- - CommandMap htmlTagMap[] = { { "strong", HTML_BOLD }, @@ -188,43 +151,37 @@ CommandMap htmlTagMap[] = { "h6", HTML_H6 }, { "span", HTML_SPAN }, { "div", HTML_DIV }, + + { "c", XML_C }, + // { "code", XML_CODE }, <= ambigious is also a HTML tag + { "description",XML_DESCRIPTION }, + { "example", XML_EXAMPLE }, + { "exception", XML_EXCEPTION }, + { "include", XML_INCLUDE }, + { "item", XML_ITEM }, + { "list", XML_LIST }, + { "para", XML_PARA }, + { "param", XML_PARAM }, + { "paramref", XML_PARAMREF }, + { "permission", XML_PERMISSION }, + { "remarks", XML_REMARKS }, + { "returns", XML_RETURNS }, + { "see", XML_SEE }, + { "seealso", XML_SEEALSO }, + { "summary", XML_SUMMARY }, + { "value", XML_VALUE }, { 0, 0 } }; //---------------------------------------------------------------------------- -int HtmlTagMapper::map(const char *name) -{ - return instance()->find(name); -} - -void HtmlTagMapper::freeInstance() -{ - delete m_instance; m_instance=0; -} - -HtmlTagMapper *HtmlTagMapper::instance() -{ - if (m_instance==0) m_instance = new HtmlTagMapper; - return m_instance; -} +Mapper *Mappers::cmdMapper = new Mapper(cmdMap); +Mapper *Mappers::htmlTagMapper = new Mapper(htmlTagMap); -HtmlTagMapper::HtmlTagMapper() : m_map(89) +void Mappers::freeMappers() { - m_map.setAutoDelete(TRUE); - CommandMap *p = htmlTagMap; - while (p->cmdName) - { - m_map.insert(p->cmdName,new int(p->cmdId)); - p++; - } -} -int HtmlTagMapper::find(const char *name) -{ - int *result = m_map.find(name); - if (result) return *result; else return HTML_UNKNOWN; + delete cmdMapper; cmdMapper = 0; + delete htmlTagMapper; htmlTagMapper = 0; } -HtmlTagMapper *HtmlTagMapper::m_instance=0; -//---------------------------------------------------------------------------- diff --git a/src/cmdmapper.h b/src/cmdmapper.h index e5277f2..1c65e24 100644 --- a/src/cmdmapper.h +++ b/src/cmdmapper.h @@ -111,67 +111,92 @@ enum CommandType enum HtmlTagType { - HTML_UNKNOWN = 0, - HTML_CENTER = 1, - HTML_TABLE = 2, - HTML_CAPTION = 3, - HTML_SMALL = 4, - HTML_CODE = 5, - HTML_IMG = 6, - HTML_PRE = 7, - HTML_SUB = 8, - HTML_SUP = 9, - HTML_TR = 10, - HTML_TD = 11, - HTML_TH = 12, - HTML_OL = 13, - HTML_UL = 14, - HTML_LI = 15, - HTML_EMPHASIS = 16, - HTML_HR = 17, - HTML_DL = 18, - HTML_DT = 19, - HTML_DD = 20, - HTML_BR = 21, - HTML_A = 22, - HTML_BOLD = 23, - HTML_P = 24, - HTML_H1 = 25, - HTML_H2 = 26, - HTML_H3 = 27, - HTML_H4 = 28, - HTML_H5 = 29, - HTML_H6 = 30, - HTML_SPAN = 31, - HTML_DIV = 32 + HTML_UNKNOWN = 0, + HTML_CENTER = 1, + HTML_TABLE = 2, + HTML_CAPTION = 3, + HTML_SMALL = 4, + HTML_CODE = 5, + HTML_IMG = 6, + HTML_PRE = 7, + HTML_SUB = 8, + HTML_SUP = 9, + HTML_TR = 10, + HTML_TD = 11, + HTML_TH = 12, + HTML_OL = 13, + HTML_UL = 14, + HTML_LI = 15, + HTML_EMPHASIS = 16, + HTML_HR = 17, + HTML_DL = 18, + HTML_DT = 19, + HTML_DD = 20, + HTML_BR = 21, + HTML_A = 22, + HTML_BOLD = 23, + HTML_P = 24, + HTML_H1 = 25, + HTML_H2 = 26, + HTML_H3 = 27, + HTML_H4 = 28, + HTML_H5 = 29, + HTML_H6 = 30, + HTML_SPAN = 31, + HTML_DIV = 32, + + XML_CmdMask = 0x100, + + XML_C = XML_CmdMask + 0, + XML_CODE = XML_CmdMask + 1, + XML_DESCRIPTION= XML_CmdMask + 2, + XML_EXAMPLE = XML_CmdMask + 3, + XML_EXCEPTION = XML_CmdMask + 4, + XML_INCLUDE = XML_CmdMask + 5, + XML_ITEM = XML_CmdMask + 6, + XML_LIST = XML_CmdMask + 7, + XML_PARA = XML_CmdMask + 8, + XML_PARAM = XML_CmdMask + 9, + XML_PARAMREF = XML_CmdMask + 10, + XML_PERMISSION = XML_CmdMask + 11, + XML_REMARKS = XML_CmdMask + 12, + XML_RETURNS = XML_CmdMask + 13, + XML_SEE = XML_CmdMask + 14, + XML_SEEALSO = XML_CmdMask + 15, + XML_SUMMARY = XML_CmdMask + 16, + XML_VALUE = XML_CmdMask + 17 + }; -class CmdMapper +class Mapper { public: - static int map(const char *name); - static void freeInstance(); + int map(const char *n) + { + QCString name=n; + int *result; + return !name.isEmpty() && (result=m_map.find(name.lower())) ? *result: 0; + } + Mapper(const CommandMap *cm) : m_map(89) + { + m_map.setAutoDelete(TRUE); + const CommandMap *p = cm; + while (p->cmdName) + { + m_map.insert(p->cmdName,new int(p->cmdId)); + p++; + } + } private: - static CmdMapper *instance(); - CmdMapper(); - int find(const char *name); QDict m_map; - static CmdMapper *m_instance; }; -class HtmlTagMapper +struct Mappers { - public: - static int map(const char *name); - static void freeInstance(); - - private: - static HtmlTagMapper *instance(); - HtmlTagMapper(); - int find(const char *name); - QDict m_map; - static HtmlTagMapper *m_instance; + static void freeMappers(); + static Mapper *cmdMapper; + static Mapper *htmlTagMapper; }; diff --git a/src/code.h b/src/code.h index 5dd0c7c..cf91b91 100644 --- a/src/code.h +++ b/src/code.h @@ -21,13 +21,14 @@ #include "qtbc.h" #include -class BaseCodeDocInterface; +class CodeOutputInterface; class FileDef; class MemberDef; -extern void parseCode(BaseCodeDocInterface &,const char *,const QCString &, +extern void parseCCode(CodeOutputInterface &,const char *,const QCString &, bool ,const char *,FileDef *fd=0, - int startLine=-1,int endLine=-1,bool inlineFragment=FALSE); -extern void initParseCodeContext(); -extern void setParameterList(MemberDef *md); + int startLine=-1,int endLine=-1,bool inlineFragment=FALSE, + MemberDef *memberDef=0); +extern void resetCCodeParserState(); + #endif diff --git a/src/code.l b/src/code.l index 6f2c874..598b781 100644 --- a/src/code.l +++ b/src/code.l @@ -27,7 +27,6 @@ #include #include "qtbc.h" -#include "scanner.h" #include "entry.h" #include "doxygen.h" #include "message.h" @@ -50,7 +49,7 @@ * statics */ -static BaseCodeDocInterface * g_code; +static CodeOutputInterface * g_code; static ClassSDict g_codeClassSDict(17); static QCString g_curClassName; @@ -346,11 +345,8 @@ class CallContext void clear() { DBG_CTX((stderr,"** Clear call context\n")); - Ctx *ctx = m_classList.getLast(); - if (ctx) - { - ctx->cd=0; - } + m_classList.clear(); + m_classList.append(new Ctx); } ClassDef *getClass() const { @@ -549,7 +545,7 @@ static void codifyLines(char *text) * line numbers for each line. If \a text contains newlines, the link will be * split into multiple links with the same destination, one for each line. */ -static void writeMultiLineCodeLink(BaseCodeDocInterface &ol, +static void writeMultiLineCodeLink(CodeOutputInterface &ol, const char *ref,const char *file, const char *anchor,const char *text) { @@ -600,7 +596,7 @@ static void addParmType() g_parmName.resize(0) ; } -void setParameterList(MemberDef *md) +static void setParameterList(MemberDef *md) { g_classScope = md->getClassDef() ? md->getClassDef()->name().data() : ""; ArgumentList *al = md->argumentList(); @@ -614,7 +610,7 @@ void setParameterList(MemberDef *md) if (i!=-1) g_parmType = g_parmType.left(i); i = g_parmType.find('&'); if (i!=-1) g_parmType = g_parmType.left(i); - if (g_parmType.left(6)=="const ") g_parmType = g_parmType.right(g_parmType.length()-6); + g_parmType.stripPrefix("const "); g_parmType=g_parmType.stripWhiteSpace(); g_theVarContext.addVariable(g_parmType,g_parmName); a = al->next(); @@ -767,7 +763,7 @@ static void addDocCrossReference(MemberDef *src,MemberDef *dst) static bool getLinkInScope(const QCString &c, // scope const QCString &m, // member const char *memberText, // exact text - BaseCodeDocInterface &ol, + CodeOutputInterface &ol, const char *text ) { @@ -825,7 +821,7 @@ static bool getLinkInScope(const QCString &c, // scope static bool getLink(const char *className, const char *memberName, - BaseCodeDocInterface &ol, + CodeOutputInterface &ol, const char *text=0) { QCString m=removeRedundantWhiteSpace(memberName); @@ -843,7 +839,7 @@ static bool getLink(const char *className, return TRUE; } -static void generateClassOrGlobalLink(BaseCodeDocInterface &ol,char *clName, +static void generateClassOrGlobalLink(CodeOutputInterface &ol,char *clName, bool typeOnly=FALSE) { int i=0; @@ -963,7 +959,7 @@ static void generateClassOrGlobalLink(BaseCodeDocInterface &ol,char *clName, } } -static bool generateClassMemberLink(BaseCodeDocInterface &ol,ClassDef *mcd,const char *memName) +static bool generateClassMemberLink(CodeOutputInterface &ol,ClassDef *mcd,const char *memName) { if (mcd) { @@ -1025,7 +1021,7 @@ static bool generateClassMemberLink(BaseCodeDocInterface &ol,ClassDef *mcd,const return FALSE; } -static void generateMemberLink(BaseCodeDocInterface &ol,const QCString &varName, +static void generateMemberLink(CodeOutputInterface &ol,const QCString &varName, char *memName) { //printf("generateMemberLink(object=%s,mem=%s) classScope=%s\n", @@ -1120,7 +1116,7 @@ static void generateMemberLink(BaseCodeDocInterface &ol,const QCString &varName, return; } -static void generateFunctionLink(BaseCodeDocInterface &ol,char *funcName) +static void generateFunctionLink(CodeOutputInterface &ol,char *funcName) { //CodeClassDef *ccd=0; ClassDef *ccd=0; @@ -2412,7 +2408,7 @@ OPERATOR {ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP} g_code->codify(yytext); endFontClass(); } -[a-z_A-Z][:a-z_A-Z0-9]*({B}*"<"[^\n\<\>]*">")? { +[a-z_A-Z][:a-z_A-Z0-9]*({B}*"<"[^\n\[\](){}<>]*">")? { addParmType(); g_parmName=yytext; generateClassOrGlobalLink(*g_code,yytext,!g_insideBody); @@ -2812,7 +2808,7 @@ OPERATOR {ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP} } } <*>"//"[!/][^\n]*\n { // strip special one-line comment - if (YY_START==SkipComment) REJECT; + if (YY_START==SkipComment || YY_START==SkipString) REJECT; if (Config_getBool("STRIP_CODE_COMMENTS")) { char c[2]; c[0]='\n'; c[1]=0; @@ -2863,6 +2859,7 @@ OPERATOR {ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP} } } <*>"/*"[!*]/[^/*] { // special C comment block half way a line + if (YY_START==SkipString) REJECT; if (Config_getBool("STRIP_CODE_COMMENTS")) { g_lastSpecialCContext = YY_START; @@ -2880,7 +2877,9 @@ OPERATOR {ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP} BEGIN(SkipComment); } } -<*>"/*"("!"?)"*/" { if (!Config_getBool("STRIP_CODE_COMMENTS")) +<*>"/*"("!"?)"*/" { + if (YY_START==SkipString) REJECT; + if (!Config_getBool("STRIP_CODE_COMMENTS")) { startFontClass("comment"); g_code->codify(yytext); @@ -2984,7 +2983,7 @@ static void restoreObjCContext() } } -void initParseCodeContext() +void resetCCodeParserState() { //printf("***initParseCodeContext()\n"); g_theVarContext.clear(); @@ -2994,9 +2993,10 @@ void initParseCodeContext() g_anchorCount = 0; } -void parseCode(BaseCodeDocInterface &od,const char *className,const QCString &s, +void parseCCode(CodeOutputInterface &od,const char *className,const QCString &s, bool exBlock, const char *exName,FileDef *fd, - int startLine,int endLine,bool inlineFragment) + int startLine,int endLine,bool inlineFragment, + MemberDef *memberDef) { //printf("***parseCode()\n"); if (s.isEmpty()) return; @@ -3048,6 +3048,7 @@ void parseCode(BaseCodeDocInterface &od,const char *className,const QCString &s, g_args.resize(0); g_parmName.resize(0); g_parmType.resize(0); + if (memberDef) setParameterList(memberDef); codeYYrestart( codeYYin ); BEGIN( Body ); codeYYlex(); diff --git a/src/commentcnv.l b/src/commentcnv.l index d1c4ad7..c29e731 100644 --- a/src/commentcnv.l +++ b/src/commentcnv.l @@ -454,9 +454,16 @@ CHARLIT (("'"\\[0-7]{1,3}"'")|("'"\\."'")|("'"[^' \\\n]{1,4}"'")) [ \t]* [\\@]"cond"[ \t]*\n | . { // forgot section id? + bool oldSkip=g_skip; startCondSection(" "); // fake section id causing the section to be hidden unconditionally + if (g_condCtx==CComment && !oldSkip && g_skip) + { + //printf("** Adding terminator for comment!\n"); + ADDCHAR('*'); + ADDCHAR('/'); + } if (*yytext=='\n') g_lineNr++; - if (YY_START==CondLine) BEGIN(g_condCtx); + BEGIN(g_condCtx); } [\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias QCString *pValue=Doxygen::aliasDict[yytext+1]; diff --git a/src/commentscan.l b/src/commentscan.l index 859cc1b..9181776 100644 --- a/src/commentscan.l +++ b/src/commentscan.l @@ -42,7 +42,6 @@ #include "outputlist.h" #include "membergroup.h" #include "reflist.h" -#include "code.h" #include "debug.h" #include "parserintf.h" @@ -715,6 +714,7 @@ ID "$"?[a-z_A-Z][a-z_A-Z0-9]* LABELID [a-z_A-Z][a-z_A-Z0-9\-]* SCOPEID {ID}({ID}*{BN}*"::"{BN}*)*({ID}?) SCOPENAME "$"?(({ID}?{BN}*"::"{BN}*)*)((~{BN}*)?{ID}) +MAILADR [a-z_A-Z0-9.+\-]+"@"[a-z_A-Z0-9\-]+("."[a-z_A-Z0-9\-]+)+[a-z_A-Z0-9\-]+ %option noyywrap @@ -770,6 +770,9 @@ SCOPENAME "$"?(({ID}?{BN}*"::"{BN}*)*)((~{BN}*)?{ID}) * words and whitespace and other characters (#,?!, etc). * grouping commands (e.g. @{ and @}) * language switch (e.g. \~english or \~). + * mail adress (e.g. dimitri@stack.nl). + * quoted text, such as "foo@bar" + * XML commands, */ {CMD}{CMD}[a-z_A-Z]+{B}* { // escaped command @@ -778,6 +781,12 @@ SCOPENAME "$"?(({ID}?{BN}*"::"{BN}*)*)((~{BN}*)?{ID}) {CMD}{CMD}"~"[a-z_A-Z]* { // escaped command addOutput(yytext); } +{MAILADR} { // mail adress + addOutput(yytext); + } +"\""[^"\n]*"\"" { // quoted text + addOutput(yytext); + } ("\\"[a-z_A-Z]+)+"\\" { // directory (or chain of commands!) addOutput(yytext); } @@ -791,6 +800,14 @@ SCOPENAME "$"?(({ID}?{BN}*"::"{BN}*)*)((~{BN}*)?{ID}) // continue with the same input REJECT; } +"" { // start of a .NET XML style brief description + setOutput(OutputBrief); + } +""|"" { // start of a .NET XML style detailed description + setOutput(OutputDoc); + } +"" { // end of a brief or detailed description + } " adding new line\n"); - } - } -} -#endif - static void parseFiles(Entry *root) { - ParserInterface *defaultParser = new CLanguageScanner; - ParserManager *parserManager = new ParserManager(defaultParser); - - // register any additional parsers here... QCString *s=inputFiles.first(); while (s) @@ -7372,12 +7321,14 @@ static void parseFiles(Entry *root) QCString extension; int ei = fileName.findRev('.'); if (ei!=-1) extension=fileName.right(fileName.length()-ei); + ParserInterface *parser = Doxygen::parserManager->getParser(extension); QFileInfo fi(fileName); BufStr preBuf(fi.size()+4096); BufStr *bufPtr = &preBuf; - if (Config_getBool("ENABLE_PREPROCESSING")) + if (Config_getBool("ENABLE_PREPROCESSING") && + parser->needsPreprocessing(extension)) { msg("Preprocessing %s...\n",s->data()); preprocessFile(fileName,*bufPtr); @@ -7396,8 +7347,7 @@ static void parseFiles(Entry *root) convBuf.addChar('\0'); - ParserInterface *parser = parserManager->getParser(extension); - parser->parse(fileName,convBuf.data(),root); + parser->parseInput(fileName,convBuf.data(),root); s=inputFiles.next(); } @@ -7792,6 +7742,12 @@ void initDoxygen() Doxygen::runningTime.start(); initPreprocessor(); + ParserInterface *defaultParser = new CLanguageScanner; + Doxygen::parserManager = new ParserManager(defaultParser); + Doxygen::parserManager->registerParser(".py",new PythonLanguageScanner); + + // register any additional parsers here... + Doxygen::sectionDict.setAutoDelete(TRUE); Doxygen::inputNameList.setAutoDelete(TRUE); Doxygen::memberNameSDict.setAutoDelete(TRUE); @@ -7820,13 +7776,13 @@ void cleanUpDoxygen() delete Doxygen::exampleSDict; delete Doxygen::globalScope; delete Doxygen::xrefLists; + delete Doxygen::parserManager; cleanUpPreprocessor(); Config::deleteInstance(); QTextCodec::deleteAllCodecs(); delete theTranslator; delete outputList; - CmdMapper::freeInstance(); - HtmlTagMapper::freeInstance(); + Mappers::freeMappers(); //delete Doxygen::symbolMap; <- we cannot do this unless all static lists // (such as Doxygen::namespaceSDict) // with objects based on Definition are made diff --git a/src/doxygen.h b/src/doxygen.h index d67165c..dd6ae1f 100644 --- a/src/doxygen.h +++ b/src/doxygen.h @@ -42,6 +42,7 @@ class PageSDict; class PageDef; class SearchIndex; class DirDef; +class ParserManager; typedef QList StringList; typedef QDict FileDict; @@ -116,6 +117,7 @@ class Doxygen static QCache lookupCache; static DirSDict directories; static SDict dirRelations; + static ParserManager *parserManager; }; void initDoxygen(); diff --git a/src/filedef.cpp b/src/filedef.cpp index bcc04fe..351d83b 100644 --- a/src/filedef.cpp +++ b/src/filedef.cpp @@ -28,18 +28,18 @@ #include "outputlist.h" #include "dot.h" #include "message.h" -#include "code.h" #include "docparser.h" #include "ftvhelp.h" #include "searchindex.h" #include "htags.h" +#include "parserintf.h" #if defined(_MSC_VER) || defined(__BORLANDC__) #define popen _popen #define pclose _pclose #endif -class DevNullCodeDocInterface : public BaseCodeDocInterface +class DevNullCodeDocInterface : public CodeOutputInterface { public: virtual void codify(const char *) {} @@ -653,9 +653,10 @@ void FileDef::writeSource(OutputList &ol) ol.endTextLink(); } - initParseCodeContext(); + ParserInterface *pIntf = Doxygen::parserManager->getParser(getDefFileExtension()); + pIntf->resetCodeParserState(); ol.startCodeFragment(); - parseCode(ol,0, + pIntf->parseCode(ol,0, fileToString(absFilePath(),Config_getBool("FILTER_SOURCE_FILES")), FALSE,0,this ); @@ -667,7 +668,10 @@ void FileDef::writeSource(OutputList &ol) void FileDef::parseSource() { DevNullCodeDocInterface devNullIntf; - parseCode(devNullIntf,0, + ParserInterface *pIntf = Doxygen::parserManager->getParser(getDefFileExtension()); + pIntf->resetCodeParserState(); + pIntf->parseCode( + devNullIntf,0, fileToString(absFilePath(),Config_getBool("FILTER_SOURCE_FILES")), FALSE,0,this ); diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp index 370f6a7..a560dc3 100644 --- a/src/htmldocvisitor.cpp +++ b/src/htmldocvisitor.cpp @@ -22,11 +22,11 @@ #include "language.h" #include "doxygen.h" #include "outputgen.h" -#include "code.h" #include "dot.h" #include "message.h" #include "config.h" #include "htmlgen.h" +#include "parserintf.h" static const int NUM_HTML_LIST_TYPES = 4; @@ -49,8 +49,10 @@ static QString htmlAttribsToString(const HtmlAttribList &attribs) //------------------------------------------------------------------------- -HtmlDocVisitor::HtmlDocVisitor(QTextStream &t,BaseCodeDocInterface &ci) - : DocVisitor(DocVisitor_Html), m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE) +HtmlDocVisitor::HtmlDocVisitor(QTextStream &t,CodeOutputInterface &ci, + const char *langExt) + : DocVisitor(DocVisitor_Html), m_t(t), m_ci(ci), m_insidePre(FALSE), + m_hide(FALSE), m_langExt(langExt) { } @@ -196,7 +198,9 @@ void HtmlDocVisitor::visit(DocVerbatim *s) { case DocVerbatim::Code: // fall though m_t << PREFRAG_START; - parseCode(m_ci,s->context(),s->text().latin1(),s->isExample(),s->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,s->context(),s->text().latin1(), + s->isExample(),s->exampleFile()); m_t << PREFRAG_END; break; case DocVerbatim::Verbatim: @@ -253,7 +257,9 @@ void HtmlDocVisitor::visit(DocInclude *inc) { case DocInclude::Include: m_t << PREFRAG_START; - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(),inc->text().latin1(), + inc->isExample(),inc->exampleFile()); m_t << PREFRAG_END; break; case DocInclude::IncWithLines: @@ -261,7 +267,11 @@ void HtmlDocVisitor::visit(DocInclude *inc) m_t << PREFRAG_START; QFileInfo cfi( inc->file() ); FileDef fd( cfi.dirPath(), cfi.fileName() ); - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile(), &fd); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(), + inc->isExample(), + inc->exampleFile(), &fd); m_t << PREFRAG_END; } break; @@ -291,7 +301,13 @@ void HtmlDocVisitor::visit(DocIncOperator *op) if (op->type()!=DocIncOperator::Skip) { popEnabled(); - if (!m_hide) parseCode(m_ci,op->context(),op->text().latin1(),op->isExample(),op->exampleFile()); + if (!m_hide) + { + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,op->context(), + op->text().latin1(),op->isExample(), + op->exampleFile()); + } pushEnabled(); m_hide=TRUE; } diff --git a/src/htmldocvisitor.h b/src/htmldocvisitor.h index f452ba8..d83b41f 100644 --- a/src/htmldocvisitor.h +++ b/src/htmldocvisitor.h @@ -21,16 +21,17 @@ #include "docvisitor.h" #include +#include class QTextStream; -class BaseCodeDocInterface; +class CodeOutputInterface; class QString; /*! @brief Concrete visitor implementation for HTML output. */ class HtmlDocVisitor : public DocVisitor { public: - HtmlDocVisitor(QTextStream &t,BaseCodeDocInterface &ci); + HtmlDocVisitor(QTextStream &t,CodeOutputInterface &ci,const char *langExt); //-------------------------------------- // visitor functions for leaf nodes @@ -77,8 +78,6 @@ class HtmlDocVisitor : public DocVisitor void visitPost(DocHtmlList *) ; void visitPre(DocHtmlListItem *); void visitPost(DocHtmlListItem *); - //void visitPre(DocHtmlPre *); - //void visitPost(DocHtmlPre *); void visitPre(DocHtmlDescList *); void visitPost(DocHtmlDescList *); void visitPre(DocHtmlDescTitle *); @@ -111,8 +110,6 @@ class HtmlDocVisitor : public DocVisitor void visitPost(DocSecRefItem *); void visitPre(DocSecRefList *); void visitPost(DocSecRefList *); - //void visitPre(DocLanguage *); - //void visitPost(DocLanguage *); void visitPre(DocParamSect *); void visitPost(DocParamSect *); void visitPre(DocParamList *); @@ -147,10 +144,11 @@ class HtmlDocVisitor : public DocVisitor //-------------------------------------- QTextStream &m_t; - BaseCodeDocInterface &m_ci; + CodeOutputInterface &m_ci; bool m_insidePre; bool m_hide; QStack m_enabled; + QCString m_langExt; }; #endif diff --git a/src/htmlgen.cpp b/src/htmlgen.cpp index 2db2aff..cae67c0 100644 --- a/src/htmlgen.cpp +++ b/src/htmlgen.cpp @@ -1418,9 +1418,9 @@ void HtmlGenerator::endParamList() t << ""; } -void HtmlGenerator::printDoc(DocNode *n) +void HtmlGenerator::printDoc(DocNode *n,const char *langExt) { - HtmlDocVisitor *visitor = new HtmlDocVisitor(t,*this); + HtmlDocVisitor *visitor = new HtmlDocVisitor(t,*this,langExt); n->accept(visitor); delete visitor; } diff --git a/src/htmlgen.h b/src/htmlgen.h index f2f2364..567a166 100644 --- a/src/htmlgen.h +++ b/src/htmlgen.h @@ -45,7 +45,7 @@ class HtmlGenerator : public OutputGenerator bool isEnabled(OutputType o) { return (o==Html && active); } OutputGenerator *get(OutputType o) { return (o==Html) ? this : 0; } - void printDoc(DocNode *); + void printDoc(DocNode *,const char *); void startFile(const char *name,const char *manName,const char *title); void writeFooter(); diff --git a/src/index.cpp b/src/index.cpp index 4e78c72..10560f5 100644 --- a/src/index.cpp +++ b/src/index.cpp @@ -25,7 +25,6 @@ #include "message.h" #include "index.h" #include "doxygen.h" -#include "code.h" #include "config.h" #include "filedef.h" #include "outputlist.h" diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp index 719c357..a72dd65 100644 --- a/src/latexdocvisitor.cpp +++ b/src/latexdocvisitor.cpp @@ -21,10 +21,10 @@ #include "language.h" #include "doxygen.h" #include "outputgen.h" -#include "code.h" #include "dot.h" #include "util.h" #include "message.h" +#include "parserintf.h" static QString escapeLabelName(const char *s) { @@ -79,10 +79,11 @@ QString LatexDocVisitor::escapeMakeIndexChars(const char *s) } -LatexDocVisitor::LatexDocVisitor(QTextStream &t,BaseCodeDocInterface &ci, - bool insideTabbing) +LatexDocVisitor::LatexDocVisitor(QTextStream &t,CodeOutputInterface &ci, + const char *langExt,bool insideTabbing) : DocVisitor(DocVisitor_Latex), m_t(t), m_ci(ci), m_insidePre(FALSE), - m_insideItem(FALSE), m_hide(FALSE), m_insideTabbing(insideTabbing) + m_insideItem(FALSE), m_hide(FALSE), m_insideTabbing(insideTabbing), + m_langExt(langExt) { } @@ -251,7 +252,9 @@ void LatexDocVisitor::visit(DocVerbatim *s) { case DocVerbatim::Code: m_t << "\n\n\\footnotesize\\begin{verbatim}"; - parseCode(m_ci,s->context(),s->text().latin1(),s->isExample(),s->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,s->context(),s->text().latin1(), + s->isExample(),s->exampleFile()); m_t << "\\end{verbatim}\n\\normalsize" << endl; break; case DocVerbatim::Verbatim: @@ -316,13 +319,20 @@ void LatexDocVisitor::visit(DocInclude *inc) m_t << "\n\n\\footnotesize\\begin{verbatim}"; QFileInfo cfi( inc->file() ); FileDef fd( cfi.dirPath(), cfi.fileName() ); - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile(), &fd); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(), + inc->isExample(), + inc->exampleFile(), &fd); m_t << "\\end{verbatim}\n\\normalsize" << endl; } break; case DocInclude::Include: m_t << "\n\n\\footnotesize\\begin{verbatim}"; - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(),inc->isExample(), + inc->exampleFile()); m_t << "\\end{verbatim}\n\\normalsize" << endl; break; case DocInclude::DontInclude: @@ -350,7 +360,12 @@ void LatexDocVisitor::visit(DocIncOperator *op) if (op->type()!=DocIncOperator::Skip) { popEnabled(); - if (!m_hide) parseCode(m_ci,op->context(),op->text().latin1(),op->isExample(),op->exampleFile()); + if (!m_hide) + { + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,op->context(),op->text().latin1(), + op->isExample(),op->exampleFile()); + } pushEnabled(); m_hide=TRUE; } diff --git a/src/latexdocvisitor.h b/src/latexdocvisitor.h index 48e5b2c..fdebaf4 100644 --- a/src/latexdocvisitor.h +++ b/src/latexdocvisitor.h @@ -21,16 +21,18 @@ #include "docvisitor.h" #include +#include class QTextStream; -class BaseCodeDocInterface; +class CodeOutputInterface; class QString; /*! @brief Concrete visitor implementation for LaTeX output. */ class LatexDocVisitor : public DocVisitor { public: - LatexDocVisitor(QTextStream &t,BaseCodeDocInterface &ci,bool insideTabbing); + LatexDocVisitor(QTextStream &t,CodeOutputInterface &ci, + const char *langExt,bool insideTabbing); //-------------------------------------- // visitor functions for leaf nodes @@ -150,12 +152,13 @@ class LatexDocVisitor : public DocVisitor //-------------------------------------- QTextStream &m_t; - BaseCodeDocInterface &m_ci; + CodeOutputInterface &m_ci; bool m_insidePre; bool m_insideItem; bool m_hide; bool m_insideTabbing; QStack m_enabled; + QCString m_langExt; }; #endif diff --git a/src/latexgen.cpp b/src/latexgen.cpp index cf19c9b..7d5b79a 100644 --- a/src/latexgen.cpp +++ b/src/latexgen.cpp @@ -1542,9 +1542,9 @@ void LatexGenerator::endParamList() } -void LatexGenerator::printDoc(DocNode *n) +void LatexGenerator::printDoc(DocNode *n,const char *langExt) { - LatexDocVisitor *visitor = new LatexDocVisitor(t,*this,insideTabbing); + LatexDocVisitor *visitor = new LatexDocVisitor(t,*this,langExt,insideTabbing); n->accept(visitor); delete visitor; } diff --git a/src/latexgen.h b/src/latexgen.h index 1a48b1e..2d347ea 100644 --- a/src/latexgen.h +++ b/src/latexgen.h @@ -43,7 +43,7 @@ class LatexGenerator : public OutputGenerator bool isEnabled(OutputType o) { return (o==Latex && active); } OutputGenerator *get(OutputType o) { return (o==Latex) ? this : 0; } - void printDoc(DocNode *); + void printDoc(DocNode *,const char *); void startFile(const char *name,const char *manName,const char *title); void writeFooter() {} diff --git a/src/libdoxygen.pro.in b/src/libdoxygen.pro.in index 337e920..6f6d2ab 100644 --- a/src/libdoxygen.pro.in +++ b/src/libdoxygen.pro.in @@ -75,6 +75,8 @@ HEADERS = bufstr.h \ pngenc.h \ pre.h \ printdocvisitor.h \ + pycode.h \ + pyscanner.h \ qtbc.h \ reflist.h \ rtfdocvisitor.h \ @@ -176,6 +178,8 @@ SOURCES = ce_lex.cpp \ perlmodgen.cpp \ pngenc.cpp \ pre.cpp \ + pycode.cpp \ + pyscanner.cpp \ reflist.cpp \ rtfdocvisitor.cpp \ rtfgen.cpp \ diff --git a/src/libdoxygen.t b/src/libdoxygen.t index 042dee2..189684e 100644 --- a/src/libdoxygen.t +++ b/src/libdoxygen.t @@ -51,6 +51,12 @@ sub GenerateDep { #$ GenerateDep("code.cpp","code.l"); $(LEX) -PcodeYY -t code.l | $(INCBUFSIZE) >code.cpp +#$ GenerateDep("pyscanner.cpp","pyscanner.l"); + $(LEX) -PpyscanYY -t pyscanner.l | $(INCBUFSIZE) >pyscanner.cpp + +#$ GenerateDep("pycode.cpp","pycode.l"); + $(LEX) -PpycodeYY -t pycode.l | $(INCBUFSIZE) >pycode.cpp + #$ GenerateDep("pre.cpp","pre.l"); $(LEX) -PpreYY -t pre.l | $(INCBUFSIZE) >pre.cpp diff --git a/src/mandocvisitor.cpp b/src/mandocvisitor.cpp index e600709..93c5ec5 100644 --- a/src/mandocvisitor.cpp +++ b/src/mandocvisitor.cpp @@ -26,10 +26,12 @@ #include "util.h" #include "message.h" #include +#include "parserintf.h" -ManDocVisitor::ManDocVisitor(QTextStream &t,BaseCodeDocInterface &ci) +ManDocVisitor::ManDocVisitor(QTextStream &t,CodeOutputInterface &ci, + const char *langExt) : DocVisitor(DocVisitor_Man), m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE), m_firstCol(TRUE), - m_indent(0) + m_indent(0), m_langExt(langExt) { } @@ -186,7 +188,9 @@ void ManDocVisitor::visit(DocVerbatim *s) if (!m_firstCol) m_t << endl; m_t << ".PP" << endl; m_t << ".nf" << endl; - parseCode(m_ci,s->context(),s->text().latin1(),s->isExample(),s->exampleFile()); + Doxygen::parserManager->getParser(0/*TODO*/) + ->parseCode(m_ci,s->context(),s->text().latin1(), + s->isExample(),s->exampleFile()); if (!m_firstCol) m_t << endl; m_t << ".fi" << endl; m_t << ".PP" << endl; @@ -231,7 +235,11 @@ void ManDocVisitor::visit(DocInclude *inc) m_t << ".nf" << endl; QFileInfo cfi( inc->file() ); FileDef fd( cfi.dirPath(), cfi.fileName() ); - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile(), &fd); + Doxygen::parserManager->getParser(0/*TODO*/) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(), + inc->isExample(), + inc->exampleFile(), &fd); if (!m_firstCol) m_t << endl; m_t << ".fi" << endl; m_t << ".PP" << endl; @@ -242,7 +250,10 @@ void ManDocVisitor::visit(DocInclude *inc) if (!m_firstCol) m_t << endl; m_t << ".PP" << endl; m_t << ".nf" << endl; - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile()); + Doxygen::parserManager->getParser(0/*TODO*/) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(),inc->isExample(), + inc->exampleFile()); if (!m_firstCol) m_t << endl; m_t << ".fi" << endl; m_t << ".PP" << endl; @@ -283,7 +294,12 @@ void ManDocVisitor::visit(DocIncOperator *op) if (op->type()!=DocIncOperator::Skip) { popEnabled(); - if (!m_hide) parseCode(m_ci,op->context(),op->text().latin1(),op->isExample(),op->exampleFile()); + if (!m_hide) + { + Doxygen::parserManager->getParser(0/*TODO*/) + ->parseCode(m_ci,op->context(),op->text().latin1(), + op->isExample(),op->exampleFile()); + } pushEnabled(); m_hide=TRUE; } diff --git a/src/mandocvisitor.h b/src/mandocvisitor.h index 66fece9..aa1c54b 100644 --- a/src/mandocvisitor.h +++ b/src/mandocvisitor.h @@ -21,16 +21,17 @@ #include "docvisitor.h" #include +#include class QTextStream; -class BaseCodeDocInterface; +class CodeOutputInterface; class QString; /*! @brief Concrete visitor implementation for LaTeX output. */ class ManDocVisitor : public DocVisitor { public: - ManDocVisitor(QTextStream &t,BaseCodeDocInterface &ci); + ManDocVisitor(QTextStream &t,CodeOutputInterface &ci,const char *langExt); //-------------------------------------- // visitor functions for leaf nodes @@ -142,12 +143,13 @@ class ManDocVisitor : public DocVisitor //-------------------------------------- QTextStream &m_t; - BaseCodeDocInterface &m_ci; + CodeOutputInterface &m_ci; bool m_insidePre; bool m_hide; bool m_firstCol; int m_indent; QStack m_enabled; + QCString m_langExt; }; #endif diff --git a/src/mangen.cpp b/src/mangen.cpp index 2339208..4ef5c13 100644 --- a/src/mangen.cpp +++ b/src/mangen.cpp @@ -627,9 +627,9 @@ void ManGenerator::endParamList() { } -void ManGenerator::printDoc(DocNode *n) +void ManGenerator::printDoc(DocNode *n,const char *langExt) { - ManDocVisitor *visitor = new ManDocVisitor(t,*this); + ManDocVisitor *visitor = new ManDocVisitor(t,*this,langExt); n->accept(visitor); delete visitor; firstCol=FALSE; diff --git a/src/mangen.h b/src/mangen.h index 970579d..8d4d981 100644 --- a/src/mangen.h +++ b/src/mangen.h @@ -40,7 +40,7 @@ class ManGenerator : public OutputGenerator bool isEnabled(OutputType o) { return (o==Man && active); } OutputGenerator *get(OutputType o) { return (o==Man) ? this : 0; } - void printDoc(DocNode *); + void printDoc(DocNode *,const char *); static void init(); void startFile(const char *name,const char *manName,const char *title); diff --git a/src/memberdef.cpp b/src/memberdef.cpp index ee96ff1..a18e1fc 100644 --- a/src/memberdef.cpp +++ b/src/memberdef.cpp @@ -33,6 +33,7 @@ #include "docparser.h" #include "dot.h" #include "searchindex.h" +#include "parserintf.h" //----------------------------------------------------------------------------- @@ -1577,9 +1578,10 @@ void MemberDef::writeDocumentation(MemberList *ml,OutputList &ol, else ol.parseText(theTranslator->trInitialValue()); ol.endBold(); - initParseCodeContext(); + ParserInterface *pIntf = Doxygen::parserManager->getParser(getDefFileExtension()); + pIntf->resetCodeParserState(); ol.startCodeFragment(); - parseCode(ol,scopeName,init,FALSE,0); + pIntf->parseCode(ol,scopeName,init,FALSE,0); ol.endCodeFragment(); } diff --git a/src/membergroup.cpp b/src/membergroup.cpp index c2808cb..aa9e8bc 100644 --- a/src/membergroup.cpp +++ b/src/membergroup.cpp @@ -24,7 +24,6 @@ #include "namespacedef.h" #include "filedef.h" #include "language.h" -#include "scanner.h" #include "groupdef.h" #include "doxygen.h" #include "docparser.h" diff --git a/src/outputgen.h b/src/outputgen.h index c271835..1146f4d 100644 --- a/src/outputgen.h +++ b/src/outputgen.h @@ -39,7 +39,7 @@ class GroupDef; /*! \brief Output interface for code parser. */ -class BaseCodeDocInterface +class CodeOutputInterface { public: /*! Writes an ASCII string to the output. This function should keep @@ -78,7 +78,7 @@ class BaseCodeDocInterface * or a list of formats (see OutputList). This interface * contains functions that generate fragments of the output. */ -class BaseOutputDocInterface : public BaseCodeDocInterface +class BaseOutputDocInterface : public CodeOutputInterface { public: enum ParamListTypes { Param, RetVal, Exception }; @@ -272,7 +272,7 @@ class OutputGenerator : public BaseOutputDocInterface void pushGeneratorState(); void popGeneratorState(); - virtual void printDoc(DocNode *) = 0; + virtual void printDoc(DocNode *,const char *langExt) = 0; /////////////////////////////////////////////////////////////// // structural output interface diff --git a/src/outputlist.cpp b/src/outputlist.cpp index 2dff450..b6e5e75 100644 --- a/src/outputlist.cpp +++ b/src/outputlist.cpp @@ -26,6 +26,7 @@ #include "outputgen.h" #include "config.h" #include "message.h" +#include "definition.h" #include "docparser.h" @@ -163,7 +164,9 @@ void OutputList::parseDoc(const char *fileName,int startLine, og=outputs->first(); while (og) { - if (og->isEnabled()) og->printDoc(root); + //printf("og->printDoc(extension=%s)\n", + // ctx?ctx->getDefFileExtension().data():""); + if (og->isEnabled()) og->printDoc(root,ctx?ctx->getDefFileExtension():0); og=outputs->next(); } @@ -186,7 +189,7 @@ void OutputList::parseText(const QCString &textStr) og=outputs->first(); while (og) { - if (og->isEnabled()) og->printDoc(root); + if (og->isEnabled()) og->printDoc(root,0); og=outputs->next(); } diff --git a/src/parserintf.h b/src/parserintf.h index 39ff310..ac81a72 100644 --- a/src/parserintf.h +++ b/src/parserintf.h @@ -21,6 +21,9 @@ #include class Entry; +class FileDef; +class CodeOutputInterface; +class MemberDef; /** \brief Abstract interface for programming language parsers. * @@ -31,13 +34,58 @@ class Entry; class ParserInterface { public: - /** Parses a single file. + /** Parses a single input file with the goal to build an Entry tree. * @param[in] fileName The full name of the file. * @param[in] fileBuf The contents of the file (zero terminated). * @param[in,out] root The root of the tree of Entry *nodes * representing the information extracted from the file. */ - virtual void parse(const char *fileName,const char *fileBuf,Entry *root) = 0; + virtual void parseInput(const char *fileName, + const char *fileBuf, + Entry *root) = 0; + + /** Returns TRUE if the language identified by \a extension needs + * the C preprocessor to be run before feed the result to the input + * parser. + * @see parseInput() + */ + virtual bool needsPreprocessing(const QCString &extension) = 0; + + /** Parses a source file or fragment with the goal to produce + * highlighted and cross-referenced output. + * @param[in] codeOutIntf Abstract interface for writing the result. + * @param[in] scopeName Name of scope to which the code belongs. + * @param[in] input Actual code in the form of a string + * @param[in] isExampleBlock TRUE iff the code is part of an example. + * @param[in] exampleName Name of the example. + * @param[in] fileDef File definition to which the code + * is associated. + * @param[in] startLine Starting line in case of a code fragment. + * @param[in] endLine Ending line of the code fragment. + * @param[in] inlineFragment Code fragment that is to be shown inline + * as part of the documentation. + * @param[in] memberDef Member definition to which the code + * is associated (non null in case of an inline fragment + * for a member). + */ + virtual void parseCode(CodeOutputInterface &codeOutIntf, + const char *scopeName, + const QCString &input, + bool isExampleBlock, + const char *exampleName=0, + FileDef *fileDef=0, + int startLine=-1, + int endLine=-1, + bool inlineFragment=FALSE, + MemberDef *memberDef=0 + ) = 0; + + /** Resets the state of the code parser. + * Since multiple code fragments can together form a single example, an + * explicit function is used to reset the code parser state. + * @see parseCode() + */ + virtual void resetCodeParserState() = 0; /** Callback function called by the comment block scanner. * It provides a string \a text containing the prototype of a function @@ -60,6 +108,8 @@ class ParserInterface virtual void handleGroupEndCommand() = 0; }; +//----------------------------------------------------------------------------- + /** \brief Manages programming language parsers. * * This class manages the language parsers in the system. One can @@ -70,13 +120,13 @@ class ParserManager public: /** Creates the parser manager object. * @param defaultParser The default parser that is used when - * no explicit extension has been register for + * no explicit extension has been registered for * a given input file. */ ParserManager(ParserInterface *defaultParser) : m_defaultParser(defaultParser) {} - /** Registers a new parser. + /** Registers an additional parser. * @param[in] extension The file extension that will trigger * the use of this parser (e.g. ".py", or ".bas"). * @param[in] parser The parser that is to be used for the @@ -87,8 +137,8 @@ class ParserManager m_parsers.insert(extension,parser); } - /** Gets the interface to the parser associated with given \a extension, - * if there is no parser explicitly registered for the supplied extension, + /** Gets the interface to the parser associated with given \a extension. + * If there is no parser explicitly registered for the supplied extension, * the interface to the default parser will be returned. */ ParserInterface *getParser(const char *extension) diff --git a/src/pycode.h b/src/pycode.h new file mode 100644 index 0000000..5f2b614 --- /dev/null +++ b/src/pycode.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2005 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ +/* This code is based on the work done by the MoxyPyDoxy team + * (Linda Leong, Mike Rivera, Kim Truong, and Gabriel Estrada), executed + * as part of CS179e (Compiler design project) at the UC Riverside, + * under supervision of Peter H. Fröhlic. + */ + +#ifndef PYCODE_H +#define PYCODE_H + +#include "qtbc.h" +#include + +class CodeOutputInterface; +class FileDef; +class MemberDef; + +extern void parsePythonCode(CodeOutputInterface &,const char *,const QCString &, + bool ,const char *,FileDef *fd=0, + int startLine=-1,int endLine=-1,bool inlineFragment=FALSE, + MemberDef *memberDef=0); +extern void resetPythonCodeParserState(); + +#endif diff --git a/src/pycode.l b/src/pycode.l new file mode 100644 index 0000000..dca57ca --- /dev/null +++ b/src/pycode.l @@ -0,0 +1,1425 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2005 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ +/* This code is based on the work done by the MoxyPyDoxy team + * (Linda Leong, Mike Rivera, Kim Truong, and Gabriel Estrada), executed + * as part of CS179e (Compiler design project) at the UC Riverside, + * under supervision of Peter H. Fröhlic + */ + +%{ + +#include +#include + +#include "pycode.h" +#include "message.h" + +#include "scanner.h" +#include "entry.h" +#include "doxygen.h" +#include "outputlist.h" +#include "util.h" +#include "membername.h" +#include "searchindex.h" + +#define YY_NEVER_INTERACTIVE 1 + +static ClassSDict g_codeClassSDict(17); +static QCString g_curClassName; +static QStrList g_curClassBases; + + +static CodeOutputInterface * g_code; +static const char * g_inputString; //!< the code fragment as text +static int g_inputPosition; //!< read offset during parsing +static const char * g_currentFontClass; +static bool g_needsTermination; +static int g_inputLines; //!< number of line in the code fragment +static int g_yyLineNr; //!< current line number +static FileDef * g_sourceFileDef; +static Definition * g_currentDefinition; +static MemberDef * g_currentMemberDef; +static bool g_includeCodeFragment; +static QCString g_realScope; +static bool g_insideBody; +static int g_bodyCurlyCount; +static bool g_searchingForBody; +static QCString g_classScope; +static int g_paramParens; +//static int g_anchorCount; + +static bool g_exampleBlock; +static QCString g_exampleName; +static QCString g_exampleFile; + +static QCString g_type; +static QCString g_name; + +static bool g_doubleStringIsDoc; +static bool g_doubleQuote; +static int g_lastState; +static bool g_noSuiteFound; + +static QValueStack g_indents; //!< Tracks indentation levels for scoping in python + +static void endFontClass(); +static void adjustScopesAndSuites(unsigned indentLength); + + +/*! Represents a stack of variable to class mappings as found in the + * code. Each scope is enclosed in pushScope() and popScope() calls. + * Variables are added by calling addVariables() and one can search + * for variable using findVariable(). + */ +class PyVariableContext +{ + public: + static const ClassDef *dummyContext; + class Scope : public SDict + { + public: + Scope() : SDict(17) {} + }; + + PyVariableContext() + { + m_scopes.setAutoDelete(TRUE); + } + + virtual ~PyVariableContext() + { + } + + void pushScope() + { + m_scopes.append(new Scope); + } + + void popScope() + { + if (m_scopes.count()>0) + { + m_scopes.remove(m_scopes.count()-1); + } + } + + void clear() + { + m_scopes.clear(); + m_globalScope.clear(); + } + + void clearExceptGlobal() + { + m_scopes.clear(); + } + + void addVariable(const QCString &type,const QCString &name); + ClassDef *findVariable(const QCString &name); + + private: + Scope m_globalScope; + QList m_scopes; +}; + +void PyVariableContext::addVariable(const QCString &type,const QCString &name) +{ + //printf("PyVariableContext::addVariable(%s,%s)\n",type.data(),name.data()); + QCString ltype = type.simplifyWhiteSpace(); + QCString lname = name.simplifyWhiteSpace(); + + Scope *scope = m_scopes.count()==0 ? &m_globalScope : m_scopes.getLast(); + ClassDef *varType; + if ( + (varType=g_codeClassSDict[ltype]) || // look for class definitions inside the code block + (varType=getResolvedClass(g_currentDefinition,g_sourceFileDef,ltype)) // look for global class definitions + ) + { + scope->append(lname,varType); // add it to a list + } + else + { + if (m_scopes.count()>0) // for local variables add a dummy entry so the name + // is hidden to avoid FALSE links to global variables with the same name + // TODO: make this work for namespaces as well! + { + scope->append(lname,dummyContext); + } + } +} + +ClassDef *PyVariableContext::findVariable(const QCString &name) +{ + if (name.isEmpty()) return 0; + ClassDef *result = 0; + QListIterator sli(m_scopes); + Scope *scope; + // search from inner to outer scope + for (sli.toLast();(scope=sli.current());--sli) + { + result = scope->find(name); + if (result) + { + return result; + } + } + // nothing found -> also try the global scope + result=m_globalScope.find(name); + return result; +} + +static PyVariableContext g_theVarContext; +const ClassDef *PyVariableContext::dummyContext = (ClassDef*)0x8; + +class PyCallContext +{ + public: + struct Ctx + { + Ctx() : name(g_name), type(g_type), cd(0) {} + QCString name; + QCString type; + ClassDef *cd; + }; + + PyCallContext() + { + m_classList.append(new Ctx); + m_classList.setAutoDelete(TRUE); + } + + virtual ~PyCallContext() {} + + void setClass(ClassDef *cd) + { + Ctx *ctx = m_classList.getLast(); + if (ctx) + { + ctx->cd=cd; + } + } + void pushScope() + { + m_classList.append(new Ctx); + } + + void popScope() + { + if (m_classList.count()>1) + { + Ctx *ctx = m_classList.getLast(); + if (ctx) + { + g_name = ctx->name; + g_type = ctx->type; + } + m_classList.removeLast(); + } + else + { + } + } + + void clear() + { + m_classList.clear(); + m_classList.append(new Ctx); + } + + ClassDef *getClass() const + { + Ctx *ctx = m_classList.getLast(); + + if (ctx) + return ctx->cd; + else + return 0; + } + + private: + QList m_classList; +}; + +static PyCallContext g_theCallContext; + + +/*! counts the number of lines in the input */ +static int countLines() +{ + const char *p=g_inputString; + char c; + int count=1; + while ((c=*p)) + { + p++ ; + if (c=='\n') count++; + } + if (p>g_inputString && *(p-1)!='\n') + { // last line does not end with a \n, so we add an extra + // line and explicitly terminate the line after parsing. + count++, + g_needsTermination=TRUE; + } + return count; +} + +static void setCurrentDoc(const QCString &name,const QCString &base,const QCString &anchor="") +{ + static bool searchEngineEnabled=Config_getBool("SEARCHENGINE"); + if (searchEngineEnabled) + { + Doxygen::searchIndex->setCurrentDoc(name,base,anchor); + } +} + +static void addToSearchIndex(const char *text) +{ + static bool searchEngineEnabled=Config_getBool("SEARCHENGINE"); + if (searchEngineEnabled) + { + Doxygen::searchIndex->addWord(text,FALSE); + } +} + + +static ClassDef *stripClassName(const char *s) +{ + int pos=0; + QCString type = s; + QCString className; + QCString templSpec; + while (extractClassNameFromType(type,pos,className,templSpec)) + { + QCString clName=className+templSpec; + ClassDef *cd=0; + if (!g_classScope.isEmpty()) + { + cd=getResolvedClass(g_currentDefinition,g_sourceFileDef,g_classScope+"::"+clName); + } + if (cd==0) + { + cd=getResolvedClass(g_currentDefinition,g_sourceFileDef,clName); + } + //printf("stripClass trying `%s' = %p\n",clName.data(),cd); + if (cd) + { + return cd; + } + } + + return 0; +} + + + +/*! start a new line of code, inserting a line number if g_sourceFileDef + * is TRUE. If a definition starts at the current line, then the line + * number is linked to the documentation of that definition. + */ +static void startCodeLine() +{ + //if (g_currentFontClass) { g_code->endFontClass(); } + if (g_sourceFileDef) + { + //QCString lineNumber,lineAnchor; + //lineNumber.sprintf("%05d",g_yyLineNr); + //lineAnchor.sprintf("l%05d",g_yyLineNr); + + Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr); + //printf("startCodeLine %d d=%p\n",g_yyLineNr,d); + //g_code->startLineNumber(); + if (!g_includeCodeFragment && d && d->isLinkableInProject()) + { + g_currentDefinition = d; + g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr); + g_insideBody = FALSE; + g_searchingForBody = TRUE; + g_realScope = d->name().copy(); + //printf("Real scope: `%s'\n",g_realScope.data()); + g_bodyCurlyCount = 0; + QCString lineAnchor; + lineAnchor.sprintf("l%05d",g_yyLineNr); + if (g_currentMemberDef) + { + g_code->writeLineNumber(g_currentMemberDef->getReference(), + g_currentMemberDef->getOutputFileBase(), + g_currentMemberDef->anchor(),g_yyLineNr); + setCurrentDoc( + g_currentMemberDef->qualifiedName(), + g_sourceFileDef->getSourceFileBase(), + lineAnchor); + } + else + { + g_code->writeLineNumber(d->getReference(), + d->getOutputFileBase(), + 0,g_yyLineNr); + setCurrentDoc( + d->qualifiedName(), + g_sourceFileDef->getSourceFileBase(), + lineAnchor); + } + } + else + { + //g_code->codify(lineNumber); + g_code->writeLineNumber(0,0,0,g_yyLineNr); + } + //g_code->endLineNumber(); + } + g_code->startCodeLine(); + if (g_currentFontClass) + { + g_code->startFontClass(g_currentFontClass); + } +} + +static void codify(char* text) +{ + g_code->codify(text); +} + +static void endCodeLine() +{ + if (g_currentFontClass) { g_code->endFontClass(); } + g_code->endCodeLine(); +} + +/*! writes a link to a fragment \a text that may span multiple lines, inserting + * line numbers for each line. If \a text contains newlines, the link will be + * split into multiple links with the same destination, one for each line. + */ +static void writeMultiLineCodeLink(CodeOutputInterface &ol, + const char *ref,const char *file, + const char *anchor,const char *text) +{ + bool done=FALSE; + char *p=(char *)text; + while (!done) + { + char *sp=p; + char c; + while ((c=*p++) && c!='\n'); + if (c=='\n') + { + g_yyLineNr++; + *(p-1)='\0'; + //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); + ol.writeCodeLink(ref,file,anchor,sp); + endCodeLine(); + if (g_yyLineNrcodify(sp); + endCodeLine(); + if (g_yyLineNrcodify(sp); + done=TRUE; + } + } +} + +static void addDocCrossReference(MemberDef *src,MemberDef *dst) +{ + if (dst->isTypedef() || dst->isEnumerate()) return; // don't add types + //printf("addDocCrossReference src=%s,dst=%s\n",src->name().data(),dst->name().data()); + if (Config_getBool("REFERENCED_BY_RELATION") && + (src->isFunction() || src->isSlot()) + ) + { + dst->addSourceReferencedBy(src); + } + if ((Config_getBool("REFERENCES_RELATION") || Config_getBool("CALL_GRAPH")) && + (src->isFunction() || src->isSlot()) + ) + { + src->addSourceReferences(dst); + } + +} + + + +static bool getLinkInScope(const QCString &c, // scope + const QCString &m, // member + const char *memberText, // exact text + CodeOutputInterface &ol, + const char *text + ) +{ + MemberDef *md; + ClassDef *cd; + FileDef *fd; + NamespaceDef *nd; + GroupDef *gd; + //printf("Trying `%s'::`%s'\n",c.data(),m.data()); + if (getDefs(c,m,"()",md,cd,fd,nd,gd,FALSE,g_sourceFileDef) && + md->isLinkable()) + { + //printf("Found!\n"); + //Definition *d=0; + //if (cd) d=cd; else if (nd) d=nd; else if (fd) d=fd; else d=gd; + + Definition *d = md->getOuterScope()==Doxygen::globalScope ? + md->getBodyDef() : md->getOuterScope(); + if (md->getGroupDef()) d = md->getGroupDef(); + if (d && d->isLinkable()) + { + g_theCallContext.setClass(stripClassName(md->typeString())); + //printf("g_currentDefinition=%p g_currentMemberDef=%p g_insideBody=%d\n", + // g_currentDefinition,g_currentMemberDef,g_insideBody); + + if (g_currentDefinition && g_currentMemberDef && + md!=g_currentMemberDef && g_insideBody) + { + addDocCrossReference(g_currentMemberDef,md); + } + //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()); + + writeMultiLineCodeLink(ol,md->getReference(), + md->getOutputFileBase(), + md->anchor(), + text ? text : memberText); + addToSearchIndex(text ? text : memberText); + return TRUE; + } + } + return FALSE; +} + +static bool getLink(const char *className, + const char *memberName, + CodeOutputInterface &ol, + const char *text=0) +{ + QCString m=removeRedundantWhiteSpace(memberName); + QCString c=className; + if (!getLinkInScope(c,m,memberName,ol,text)) + { + if (!g_curClassName.isEmpty()) + { + if (!c.isEmpty()) c.prepend("::"); + c.prepend(g_curClassName); + return getLinkInScope(c,m,memberName,ol,text); + } + return FALSE; + } + return TRUE; +} + + +/* + For a given string in the source code, + finds its class or global id and links to it. + + As of June 1, '05, this ONLY finds classes +*/ +static void generateClassOrGlobalLink(CodeOutputInterface &ol,char *clName, + bool /*typeOnly*/=FALSE) +{ + QCString className=clName; + + // Don't do anything for empty text + if (className.isEmpty()) return; + + ClassDef *cd=0,*lcd=0; /** Class def that we may find */ + MemberDef *md=0; /** Member def that we may find */ + bool isLocal=FALSE; + + // printf("generateClassOrGlobalLink(className=%s)\n",className.data()); + + if ((lcd=g_theVarContext.findVariable(className))==0) // not a local variable + { + Definition *d = g_currentDefinition; + + cd = getResolvedClass(d,g_sourceFileDef,className,&md); + + //printf("d=%p g_sourceFileDef=%p\n",d,g_currentDefinition); + //printf("is found as a type %s\n",cd?cd->name().data():""); + + if (cd==0 && md==0) // also see if it is variable or enum or enum value + { + if (getLink(g_classScope,clName,ol,clName)) + { + return; + } + } + } + else + { + if (lcd!=PyVariableContext::dummyContext) + { + g_theCallContext.setClass(lcd); + } + isLocal=TRUE; + //fprintf(stderr,"is a local variable cd=%p!\n",cd); + } + + if (cd && cd->isLinkable()) // is it a linkable class + { + writeMultiLineCodeLink(ol,cd->getReference(),cd->getOutputFileBase(),0,clName); + addToSearchIndex(className); + if (md) + { + Definition *d = md->getOuterScope()==Doxygen::globalScope ? + md->getBodyDef() : md->getOuterScope(); + if (md->getGroupDef()) d = md->getGroupDef(); + if (d && d->isLinkable() && md->isLinkable() && g_currentMemberDef) + { + addDocCrossReference(g_currentMemberDef,md); + } + } + } + else // not a class, maybe a global member + { + + /* + This code requires a going-over in order to + make it work for Python + + //printf("class %s not linkable! cd=%p md=%p typeOnly=%d\n",clName,cd,md,typeOnly); + if (!isLocal && (md!=0 || (cd==0 && !typeOnly))) // not a class, see if it is a global enum/variable/typedef. + { + if (md==0) // not found as a typedef + { + md = setCallContextForVar(clName); + //printf("setCallContextForVar(%s) md=%p g_currentDefinition=%p\n",clName,md,g_currentDefinition); + if (md && g_currentDefinition) + { + //fprintf(stderr,"%s accessible from %s? %d md->getOuterScope=%s\n", + // md->name().data(),g_currentDefinition->name().data(), + // isAccessibleFrom(g_currentDefinition,g_sourceFileDef,md), + // md->getOuterScope()->name().data()); + } + + if (md && g_currentDefinition && + isAccessibleFrom(g_currentDefinition,g_sourceFileDef,md)==-1) + { + md=0; // variable not accessible + } + } + if (md) + { + //printf("is a global md=%p g_currentDefinition=%s\n",md,g_currentDefinition?g_currentDefinition->name().data():""); + if (md->isLinkable()) + { + writeMultiLineCodeLink(ol,md->getReference(),md->getOutputFileBase(),md->anchor(),clName); + addToSearchIndex(clName); + if (g_currentMemberDef) + { + addDocCrossReference(g_currentMemberDef,md); + } + return; + } + } + } + + */ + + // nothing found, just write out the word + codifyLines(clName); + addToSearchIndex(clName); + } +} + +/* + As of June 1, this function seems to work + for file members, but scopes are not + being correctly tracked for classes + so it doesn't work for classes yet. + +*/ +static void generateFunctionLink(CodeOutputInterface &ol,char *funcName) +{ + //CodeClassDef *ccd=0; + ClassDef *ccd=0; + QCString locScope=g_classScope.copy(); + QCString locFunc=removeRedundantWhiteSpace(funcName); + //fprintf(stdout,"*** locScope=%s locFunc=%s\n",locScope.data(),locFunc.data()); + int i=locFunc.findRev("::"); + if (i>0) + { + locScope=locFunc.left(i); + locFunc=locFunc.right(locFunc.length()-i-2).stripWhiteSpace(); + } + //printf("generateFunctionLink(%s) classScope=`%s'\n",locFunc.data(),locScope.data()); + if (!locScope.isEmpty() && (ccd=g_codeClassSDict[locScope])) + { + //printf("using classScope %s\n",g_classScope.data()); + BaseClassListIterator bcli(*ccd->baseClasses()); + for ( ; bcli.current() ; ++bcli) + { + if (getLink(bcli.current()->classDef->name(),locFunc,ol,funcName)) + { + return; + } + } + } + if (!getLink(locScope,locFunc,ol,funcName)) + { + generateClassOrGlobalLink(ol,funcName); + } + return; +} + +static void startFontClass(const char *s) +{ + endFontClass(); + g_code->startFontClass(s); + g_currentFontClass=s; +} + +static void endFontClass() +{ + if (g_currentFontClass) + { + g_code->endFontClass(); + g_currentFontClass=0; + } +} + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); + +static int yyread(char *buf,int max_size) +{ + int c=0; + while( c < max_size && g_inputString[g_inputPosition] ) + { + *buf = g_inputString[g_inputPosition++] ; + c++; buf++; + } + return c; +} + +%} + + +BB [ \t]+ +B [ \t]* +NEWLINE \n + +DIGIT [0-9] +LETTER [A-Za-z] +NONEMPTY [A-Za-z0-9_] +EXPCHAR [#(){}\[\],:.%/\\=`*~|&<>!;+-] +NONEMPTYEXP [^ \t\n:] +PARAMNONEMPTY [^ \t\n():] +IDENTIFIER ({LETTER}|"_")({LETTER}|{DIGIT}|"_")* +BORDER ([^A-Za-z0-9]) + +POUNDCOMMENT "#".* + +TRISINGLEQUOTE "'''" +TRIDOUBLEQUOTE "\"\"\"" +LONGSTRINGCHAR [^\\"'] +ESCAPESEQ ("\\")(.) +LONGSTRINGITEM ({LONGSTRINGCHAR}|{ESCAPESEQ}) +SMALLQUOTE ("\"\""|"\""|"'"|"''") +LONGSTRINGBLOCK ({LONGSTRINGITEM}+|{SMALLQUOTE}) + +SHORTSTRING ("'"{SHORTSTRINGITEM}*"'"|'"'{SHORTSTRINGITEM}*'"') +SHORTSTRINGITEM ({SHORTSTRINGCHAR}|{ESCAPESEQ}) +SHORTSTRINGCHAR [^\\\n"] +STRINGLITERAL {STRINGPREFIX}?( {SHORTSTRING} | {LONGSTRING}) +STRINGPREFIX ("r"|"u"|"ur"|"R"|"U"|"UR"|"Ur"|"uR") +KEYWORD ("lambda"|"import"|"class"|"assert"|"as"|"from"|"global"|"def"|"True"|"False") +FLOWKW ("or"|"and"|"is"|"not"|"print"|"for"|"in"|"if"|"try"|"except"|"yield"|"raise"|"break"|"continue"|"pass"|"if"|"return"|"while"|"elif"|"else"|"finally") +QUOTES ("\""[^"]*"\"") +SINGLEQUOTES ("'"[^']*"'") + +LONGINTEGER {INTEGER}("l"|"L") +INTEGER ({DECIMALINTEGER}|{OCTINTEGER}|{HEXINTEGER}) +DECIMALINTEGER ({NONZERODIGIT}{DIGIT}*|"0") +OCTINTEGER "0"{OCTDIGIT}+ +HEXINTEGER "0"("x"|"X"){HEXDIGIT}+ +NONZERODIGIT [1-9] +OCTDIGIT [0-7] +HEXDIGIT ({DIGIT}|[a-f]|[A-F]) +FLOATNUMBER ({POINTFLOAT}|{EXPONENTFLOAT}) +POINTFLOAT ({INTPART}?{FRACTION}|{INTPART}".") +EXPONENTFLOAT ({INTPART}|{POINTFLOAT}){EXPONENT} +INTPART {DIGIT}+ +FRACTION "."{DIGIT}+ +EXPONENT ("e"|"E")("+"|"-")?{DIGIT}+ +IMAGNUMBER ({FLOATNUMBER}|{INTPART})("j"|"J") +ATOM ({IDENTIFIER}|{LITERAL}|{ENCLOSURE}) +ENCLOSURE ({PARENTH_FORM}|{LIST_DISPLAY}|{DICT_DISPLAY}|{STRING_CONVERSION}) +LITERAL ({STRINGLITERAL}|{INTEGER}|{LONGINTEGER}|{FLOATNUMBER}|{IMAGNUMBER}) +PARENTH_FORM "("{EXPRESSION_LIST}?")" +TEST ({AND_TEST}("or"{AND_TEST})*|{LAMBDA_FORM}) +TESTLIST {TEST}( ","{TEST})*","? +LIST_DISPLAY "["{LISTMAKER}?"]" +LISTMAKER {EXPRESSION}({LIST_FOR}|(","{EXPRESSION})*","?) +LIST_ITER ({LIST_FOR}|{LIST_IF}) +LIST_FOR "for"{EXPRESSION_LIST}"in"{TESTLIST}{LIST_ITER}? +LIST_IF "if"{TEST}{LIST_ITER}? +DICT_DISPLAY "\{"{KEY_DATUM_LIST}?"\}" +KEY_DATUM_LIST {KEY_DATUM}(","{KEY_DATUM})*","? +KEY_DATUM {EXPRESSION}":"{EXPRESSION} +STRING_CONVERSION "`"{EXPRESSION_LIST}"`" +PRIMARY ({ATOM}|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING}|{CALL}) +ATTRIBUTEREF {PRIMARY}"."{IDENTIFIER} +SUBSCRIPTION {PRIMARY}"["{EXPRESSION_LIST}"]" +SLICING ({SIMPLE_SLICING}|{EXTENDED_SLICING}) +SIMPLE_SLICING {PRIMARY}"["{SHORT_SLICE}"]" +EXTENDED_SLICING {PRIMARY}"["{SLICE_LIST}"]" +SLICE_LIST {SLICE_ITEM}(","{SLICE_ITEM})*","? +SLICE_ITEM ({EXPRESSION}|{PROPER_SLICE}|{ELLIPSIS}) +PROPER_SLICE ({SHORT_SLICE}|{LONG_SLICE}) +SHORT_SLICE {LOWER_BOUND}?":"{UPPER_BOUND}? +LONG_SLICE {SHORT_SLICE}":"{STRIDE}? +LOWER_BOUND {EXPRESSION} +UPPER_BOUND {EXPRESSION} +STRIDE {EXPRESSION} +ELLIPSIS "..." +CALL {PRIMARY}"("({ARGUMENT_LIST}","?)?")" +ARGUMENT_LIST ({POSITIONAL_ARGUMENTS}(","{KEYWORD_ARGUMENTS})?(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|{KEYWORD_ARGUMENTS}(",""*"{EXPRESSION})?(",""**"{EXPRESSION})?|"*"{EXPRESSION}(",""**"{EXPRESSION})?|"**"{EXPRESSION}) +POSITIONAL_ARGUMENTS {EXPRESSION}(","{EXPRESSION})* +KEYWORD_ARGUMENTS {KEYWORD_ITEM}(","{KEYWORD_ITEM})* +KEYWORD_ITEM {IDENTIFIER}"="{EXPRESSION} +POWER {PRIMARY}("**"{U_EXPR})? +U_EXPR ({POWER}|"-"{U_EXPR}|"+"{U_EXPR}|"\~"{U_EXPR}) +M_EXPR ({U_EXPR}|{M_EXPR}"*"{U_EXPR}|{M_EXPR}"//"{U_EXPR}|{M_EXPR}"/"{U_EXPR}|{M_EXPR}"\%"{U_EXPR}) +A_EXPR ({M_EXPR}|{A_EXPR}"+"{M_EXPR}|{A_EXPR}"-"{M_EXPR} +SHIFT_EXPR ({A_EXPR}|{SHIFT_EXPR}("<<"|">>"){A_EXPR}) +AND_EXPR ({SHIFT_EXPR}|{AND_EXPR}"\;SPMamp;"{SHIFT_EXPR} +XOR_EXPR ({AND_EXPR}|{XOR_EXPR}"\textasciicircum"{AND_EXPR}) +OR_EXPR ({XOR_EXPR}|{OR_EXPR}"|"{ XOR_EXPR}) + +COMPARISON {OR_EXPR}({COMP_OPERATOR}{OR_EXPR})* +COMP_OPERATOR ("<"|">"|"=="|">="|"<="|"<>"|"!="|"is""not"?|"not"?"in") +EXPRESSION ({OR_TEST}|{LAMBDA_FORM}) +OR_TEST ({AND_TEST}|{OR_TEST}"or"{AND_TEST}) +AND_TEST ({NOT_TEST}|{AND_TEST}"and"{NOT_TEST}) +NOT_TEST ({COMPARISON}|"not"{NOT_TEST}) +LAMBDA_FORM "lambda"{PARAMETER_LIST}?":"{EXPRESSION} +EXPRESSION_LIST {EXPRESSION}(","{EXPRESSION})*","? +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}) +EXPRESSION_STMT {EXPRESSION_LIST} +ASSERT_STMT "assert"{EXPRESSION}(","{EXPRESSION})? +ASSIGNMENT_STMT ({TARGET_LIST}"=")+{EXPRESSION_LIST} +TARGET_LIST {TARGET}(","{TARGET})*","? +TARGET ({IDENTIFIER}|"("{TARGET_LIST}")"|"["{TARGET_LIST}"]"|{ATTRIBUTEREF}|{SUBSCRIPTION}|{SLICING}) + + +%option noyywrap +%option nounput + +%x Body + +%x BlockWord + +%x FunctionDec +%x FunctionParams + +%x ClassDec +%x ClassInheritance + +%x Suite +%x SuiteCaptureIndent +%x SuiteStart +%x SuiteMaintain +%x SuiteContinuing + +%x LongString + + +%% + +{ + {B}{POUNDCOMMENT} { + // This eats EVERYTHING + // except the newline + startFontClass("comment"); + codifyLines(yytext); + endFontClass(); + } + + {B}{STRINGPREFIX}?{TRIDOUBLEQUOTE}({LONGSTRINGBLOCK}?) { + // Some-what position sensitive; + // must come before NONEMPTY general + // rules. + + // Eventually, we should write some intelligent + // code here to figure out if this docstring + // should be deleted. + g_doubleStringIsDoc = TRUE; + + if ( g_doubleStringIsDoc ) + { + g_yyLineNr+=QCString(yytext).contains('\n'); + } + else + { + startFontClass("stringliteral"); + codifyLines(yytext); + g_yyLineNr++; + } + + g_lastState = YY_START; + g_doubleQuote = TRUE; + + BEGIN( LongString ); + } + + {B}{STRINGPREFIX}?{TRISINGLEQUOTE}({LONGSTRINGBLOCK}?) { + //startFontClass("stringliteral"); + //codifyLines(yytext); + + // Eventually, we should write some intelligent + // code here to figure out if this docstring + // should be deleted. + g_doubleStringIsDoc = TRUE; + + if ( g_doubleStringIsDoc ) + { + g_yyLineNr+=QCString(yytext).contains('\n'); + } + else + { + startFontClass("stringliteral"); + codifyLines(yytext); + g_yyLineNr++; + } + + g_lastState = YY_START; + g_doubleQuote = FALSE; + BEGIN( LongString ); + } + +} + +{ + {STRINGPREFIX}?({SINGLEQUOTES}|{QUOTES}) { + startFontClass("stringliteral"); + codifyLines(yytext); + endFontClass(); + } + + "#".* { + startFontClass("stringliteral"); + codifyLines(yytext); + endFontClass(); + } + +} + +{ + + {LONGSTRINGBLOCK} { + if ( g_doubleStringIsDoc ) + { + g_yyLineNr+=QCString(yytext).contains('\n'); + } + else + { + codifyLines(yytext); + } + } + + {TRIDOUBLEQUOTE} { + if ( ! g_doubleStringIsDoc ) + { + codify(yytext); + endFontClass(); + } + + if (g_doubleQuote) + { + g_doubleStringIsDoc = FALSE; + BEGIN( g_lastState ); + } + } + + {TRISINGLEQUOTE} { + if ( ! g_doubleStringIsDoc ) + { + codify(yytext); + endFontClass(); + } + + if (!g_doubleQuote) + { + g_doubleStringIsDoc = FALSE; + BEGIN( g_lastState ); + } + } +} + + +{ + "def"{BB} { + startFontClass("keyword"); + codify(yytext); + endFontClass(); + BEGIN( FunctionDec ); + } + + "class"{BB} { + startFontClass("keyword"); + codify(yytext); + endFontClass(); + BEGIN( ClassDec ); + } +} + +{IDENTIFIER} { + + generateClassOrGlobalLink(*g_code,yytext); + // codify(yytext); + g_curClassName = yytext; + g_curClassBases.clear(); + BEGIN( ClassInheritance ); + } + +{ + ({BB}|[(,)]) { + codify(yytext); + } + + {IDENTIFIER} { + // The parser + // is assuming + // that ALL identifiers + // in this state + // are base classes; + // it doesn't check to see + // that the first parenthesis + // has been seen. + + // This is bad - it should + // probably be more strict + // about what to accept. + + g_curClassBases.inSort(yytext); + generateClassOrGlobalLink(*g_code,yytext); + // codify(yytext); + } + + ":" { + codify(yytext); + + // Assume this will + // be a one-line suite; + // found counter-example + // in SuiteStart. + + // Push a class scope + + ClassDef *classDefToAdd = new ClassDef("",1,g_curClassName,ClassDef::Class,0,0,FALSE); + g_codeClassSDict.append(g_curClassName,classDefToAdd); + char *s=g_curClassBases.first(); + while (s) + { + ClassDef *baseDefToAdd; + baseDefToAdd=g_codeClassSDict[s]; + + // Try to find class in global + // scope + if (baseDefToAdd==0) + { + baseDefToAdd=getResolvedClass(g_currentDefinition,g_sourceFileDef,s); + } + + if (baseDefToAdd && baseDefToAdd!=classDefToAdd) + { + classDefToAdd->insertBaseClass(baseDefToAdd,s,Public,Normal); + } + + s=g_curClassBases.next(); + } + + // Reset class-parsing variables. + g_curClassName.resize(0); + g_curClassBases.clear(); + + g_noSuiteFound = TRUE; + BEGIN( SuiteStart ); + } +} + + +{ + {IDENTIFIER} { + generateFunctionLink(*g_code,yytext); + } + + {B}"(" { + codify(yytext); + BEGIN( FunctionParams ); + } +} + +{ + ({BB}|",") { + // Parses delimiters + codify(yytext); + } + + ({IDENTIFIER}|{PARAMNONEMPTY}+) { + codify(yytext); + } + + ")" { + codify(yytext); + } + + ":" { + codify(yytext); + + // Assume this will + // be a one-line suite; + // found counter-example + // in SuiteStart. + g_noSuiteFound = TRUE; + BEGIN( SuiteStart ); + } +} + +("if"|"while"|"for"|"else"|"elif") { + startFontClass("keywordflow"); + codify(yytext); + endFontClass(); + // printf("Entering Blockword on '%s' [%d]\n", yytext, g_yyLineNr); + + } + +{ + + {KEYWORD} { + // Position-sensitive rules! + // Must come AFTER keyword-triggered rules + // Must come BEFORE identifier NONEMPTY-like rules + // to syntax highlight. + + startFontClass("keyword"); + codify(yytext); + endFontClass(); + } + + {FLOWKW} { + startFontClass("keywordflow"); + codify(yytext); + endFontClass(); + } +} + +{ + + ":" { + codify(yytext); + // printf("Requires SuiteState for BlockWord [line %d]\n", g_yyLineNr); + + // Assume this will + // be a one-line suite; + // found counter-example + // in SuiteStart. + g_noSuiteFound = TRUE; + BEGIN( SuiteStart ); + } + + ({BB}+|{NONEMPTY}+|{EXPCHAR}) { // Position-sensitive! Must come AFTER + // key-word catching rules, so that syntax + // highlighting takes priority over this. + + // Match SPACE, IDENTIFIERS, or EXPchars. + codify(yytext); + } + +} + +{ + + {BB} { + codify(yytext); + } + + {KEYWORD} { + startFontClass("keyword"); + codifyLines(yytext); + endFontClass(); + + // No indentation necesary + g_noSuiteFound = FALSE; + } + + {FLOWKW} { + startFontClass("keywordflow"); + codifyLines(yytext); + endFontClass(); + + // No indentation necesary + g_noSuiteFound = FALSE; + } + + ({NONEMPTY}+|{EXPCHAR}+) { + codifyLines(yytext); + + // No indentation necesary + g_noSuiteFound = FALSE; + } + + + {POUNDCOMMENT} { + // This eats EVERYTHING + // except the newline + startFontClass("comment"); + codifyLines(yytext); + endFontClass(); + } + + {NEWLINE} { + codifyLines(yytext); + if ( g_noSuiteFound ) + { + // printf("New suite to capture! [%d]\n", g_yyLineNr); + BEGIN ( SuiteCaptureIndent ); + } + } +} + +{ + "\n"|({BB}"\n") { + // Blankline - ignore, keep looking for indentation. + codifyLines(yytext); + } + + {BB} { + // This state lasts momentarily, + // to check the indentation + // level that is about to be + // used. + codifyLines(yytext); + g_indents.push(yyleng); + // printf("Captured indent of %d [line %d]\n", yyleng, g_yyLineNr); + BEGIN( Suite ); + } +} + +{ + + {BB}/({NONEMPTY}|{EXPCHAR}) { + // This implements poor + // indendation-tracking; + // should be improved. + // (translate tabs to space, etc) + codifyLines(yytext); + adjustScopesAndSuites(yyleng); + } + + "\n"|({BB}"\n") { + // If this ever succeeds, + // it means that this is + // a blank line, and + // can be ignored. + codifyLines(yytext); + } + + ""/({NONEMPTY}|{EXPCHAR}) { + // Default rule; matches + // the empty string, assuming + // real text starts here. + // Just go straight to Body. + adjustScopesAndSuites(0); + } +} + + +{NEWLINE} { + codifyLines(yytext); + BEGIN( SuiteMaintain ); + } + +({NONEMPTY}+|{EXPCHAR}+|{BB}) { + codify(yytext); + } + +{NEWLINE} { + codifyLines(yytext); + } + +<*>({NONEMPTY}|{EXPCHAR}|{BB}) { // This should go one character at a time. + codify(yytext); + // printf("[pycode] '%s' [ state %d ] [line %d] no match\n", + // yytext, YY_START, g_yyLineNr); + + endFontClass(); + BEGIN(Body); + } + +<*>{NEWLINE} { + codifyLines(yytext); + //printf("[pycode] %d NEWLINE [line %d] no match\n", + // YY_START, g_yyLineNr); + + endFontClass(); + BEGIN(Body); + } + +<*>. { + codify(yytext); + // printf("[pycode] '%s' [ state %d ] [line %d] no match\n", + // yytext, YY_START, g_yyLineNr); + + endFontClass(); + BEGIN(Body); + } + +%% + +/*@ ---------------------------------------------------------------------------- + */ + +void resetPythonCodeParserState() +{ + g_currentDefinition = 0; + g_currentMemberDef = 0; + g_doubleStringIsDoc = FALSE; + g_paramParens = 0; + g_indents.clear(); + BEGIN( Body ); +} + +/*! + Examines current stack of white-space indentations; + re-syncs the parser with the correct scope. +*/ +static void adjustScopesAndSuites(unsigned indentLength) +{ + // States to pop + if (!g_indents.isEmpty() && indentLength < g_indents.top()) + { + while (!g_indents.isEmpty() && indentLength < g_indents.top()) + { + // printf("Exited scope indent of [%d]\n", g_indents.top()); + g_indents.pop(); // Pop the old suite's indentation + + g_currentMemberDef=0; + if (g_currentDefinition) + g_currentDefinition=g_currentDefinition->getOuterScope(); + } + } + + // Are there any remaining indentation levels for suites? + if (!g_indents.isEmpty()) + { + BEGIN( Suite ); + } + else + { + BEGIN( Body ); + } +} + +void parsePythonCode(CodeOutputInterface &od,const char *className, + const QCString &s,bool exBlock, const char *exName, + FileDef *fd,int startLine,int endLine,bool inlineFragment, + MemberDef *) +{ + + //printf("***parseCode()\n"); + + //--- some code to eliminate warnings--- + className = ""; + exBlock = FALSE; + exName = ""; + inlineFragment = ""; + //-------------------------------------- + if (s.isEmpty()) return; + g_code = &od; + g_inputString = s; + g_inputPosition = 0; + g_currentFontClass = 0; + g_needsTermination = FALSE; + if (endLine!=-1) + g_inputLines = endLine+1; + else + g_inputLines = countLines(); + + if (startLine!=-1) + g_yyLineNr = startLine; + else + g_yyLineNr = 1; + + g_exampleBlock = exBlock; + g_exampleName = exName; + g_sourceFileDef = fd; + + + // Starts line 1 on the output + startCodeLine(); + + pycodeYYrestart( pycodeYYin ); + + pycodeYYlex(); + + if (!g_indents.isEmpty()) + { + // printf("Exited pysourceparser in inconsistent state!\n"); + } + + if (g_needsTermination) + { + endFontClass(); + g_code->endCodeLine(); + } + return; +} + + +#if !defined(YY_FLEX_SUBMINOR_VERSION) +extern "C" { // some bogus code to keep the compiler happy + void pycodeYYdummy() { yy_flex_realloc(0,0); } +} +#else +#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)." +#endif + diff --git a/src/pyscanner.h b/src/pyscanner.h new file mode 100644 index 0000000..6b40ddf --- /dev/null +++ b/src/pyscanner.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2005 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ +/* This code is based on the work done by the MoxyPyDoxy team + * (Linda Leong, Mike Rivera, Kim Truong, and Gabriel Estrada), executed + * as part of CS179e (Compiler design project) at the UC Riverside, + * under supervision of Peter H. Fröhlic. + */ + +#ifndef PYSCANNER_H +#define PYSCANNER_H + +#include "parserintf.h" + +/** \brief Python Language parser using state-based lexical scanning. + * + * This is the Python language parser for doxygen. + */ +class PythonLanguageScanner : public ParserInterface +{ + public: + void parseInput(const char * fileName, + const char *fileBuf, + Entry *root); + bool needsPreprocessing(const QCString &extension); + void parseCode(CodeOutputInterface &codeOutIntf, + const char *scopeName, + const QCString &input, + bool isExampleBlock, + const char *exampleName=0, + FileDef *fileDef=0, + int startLine=-1, + int endLine=-1, + bool inlineFragment=FALSE, + MemberDef *memberDef=0 + ); + void resetCodeParserState(); + void parsePrototype(const char *text); + void handleGroupStartCommand(const char *header); + void handleGroupEndCommand(); +}; + +#endif diff --git a/src/pyscanner.l b/src/pyscanner.l new file mode 100644 index 0000000..f2ebb4c --- /dev/null +++ b/src/pyscanner.l @@ -0,0 +1,1000 @@ +/****************************************************************************** + * + * + * + * Copyright (C) 1997-2005 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ +/* This code is based on the work done by the MoxyPyDoxy team + * (Linda Leong, Mike Rivera, Kim Truong, and Gabriel Estrada), executed + * as part of CS179e (Compiler design project) at the UC Riverside, + * under supervision of Peter H. Fröhlic. + */ + +%{ + +/* + * includes + */ +#include +#include +#include +#include + +#include "qtbc.h" +#include +#include +#include +#include +#include + +#include "pyscanner.h" +#include "entry.h" +#include "message.h" +#include "config.h" +#include "doxygen.h" +#include "util.h" +#include "defargs.h" +#include "language.h" +#include "commentscan.h" +#include "pycode.h" + +#define YY_NEVER_INTERACTIVE 1 + +/* ----------------------------------------------------------------- + * + * statics + */ +static ParserInterface *g_thisParser; +static const char * inputString; +static int inputPosition; +static QFile inputFile; + +static Protection protection; +static Protection baseProt; + +int tabsize = 0; +static QStack spaceStack; + +static int sharpCount = 0 ; +static int roundCount = 0 ; +static int curlyCount = 0 ; +static int padCount = 0 ; +static QCString slString; +static Entry* current_root = 0 ; +static Entry* global_root = 0 ; +static Entry* current = 0 ; +static Entry* previous = 0 ; +static int yyLineNr = 1 ; +static int anonCount = 0 ; +static QCString yyFileName; +static MethodTypes mtype; +static bool gstat; +static Specifier virt; +static Specifier baseVirt; +static QCString msType,msName,msArgs; +static int memberGroupId = DOX_NOGROUP; +static QCString memberGroupHeader; +static QCString memberGroupDocs; +static bool isTypedef; +//static char afterDocTerminator; +static QCString sectionLabel; +static QCString sectionTitle; +//static SectionInfo::SectionType +// sectionType; +static QCString funcPtrType; +static QCString templateStr; +static QCString aliasName; +static QCString baseName; +static QCString formulaText; +static QCString formulaEnd; + +static QCString fullArgString; + +//static QCString *currentTemplateSpec; +static QStack autoGroupStack; +static Grouping lastDefGroup( "", Grouping::GROUPING_LOWEST ); + +static bool insideFormula; +static bool insideTryBlock=FALSE; +static bool insideCode; + +static int depthIf; +static QCString memberGroupRelates; +static QCString memberGroupInside; +static QCString xrefItemKey; +static QCString xrefItemTitle; +static QCString xrefListTitle; + +static QCString g_skipBlockName; +static QCString oldStyleArgType; +static QCString docBackup; +static QCString briefBackup; + +static int docBlockContext; +static QCString docBlock; +static QCString docBlockName; +static bool docBlockInBody; +static bool docBlockJavaStyle; +static bool docBrief; + +static bool g_doubleQuote; +static bool g_specialBlock; + +int g_indent = 0; +int class_indent = 0; +int classKeywordIndent = 0; + +//----------------------------------------------------------------------------- + + +static void initParser() +{ + sectionLabel.resize(0); + sectionTitle.resize(0); + baseName.resize(0); + formulaText.resize(0); + protection = Public; + baseProt = Public; + sharpCount = 0; + roundCount = 0; + curlyCount = 0; + memberGroupId = DOX_NOGROUP; + memberGroupRelates.resize(0); + memberGroupInside.resize(0); + mtype = Method; + gstat = FALSE; + virt = Normal; + baseVirt = Normal; + isTypedef = FALSE; + autoGroupStack.clear(); + insideTryBlock = FALSE; + autoGroupStack.setAutoDelete(TRUE); + lastDefGroup.groupname.resize(0); + insideFormula = FALSE; + insideCode=FALSE; + previous = 0; +} + +static void initEntry() +{ + //current->python = TRUE; + current->protection = protection ; + current->mtype = mtype; + current->virt = virt; + current->stat = gstat; + current->mGrpId = memberGroupId; + current->relates = memberGroupRelates.copy(); + current->inside = memberGroupInside.copy(); + current->objc = FALSE; //insideObjC; + current->parent = current_root; + if (!autoGroupStack.isEmpty()) + { + //printf("Appending group %s\n",autoGroupStack.top()->groupname.data()); + current->groups->append(new Grouping(*autoGroupStack.top())); + } +} + + +//----------------------------------------------------------------------------- + +static void lineCount() +{ + for( const char* c = yytext ; *c ; ++c ) + yyLineNr += (*c == '\n') ; +} + +#if 0 +// Appends the current-name to current-type; +// Destroys current-name. +// Destroys current->args and current->argList +static void addType( Entry* current ) +{ + uint tl=current->type.length(); + if ( tl>0 && !current->name.isEmpty() && current->type.at(tl-1)!='.') + { + current->type += ' ' ; + } + current->type += current->name ; + current->name.resize(0) ; + tl=current->type.length(); + if ( tl>0 && !current->args.isEmpty() && current->type.at(tl-1)!='.') + { + current->type += ' ' ; + } + current->type += current->args ; + current->args.resize(0) ; + current->argList->clear(); +} + +static QCString stripQuotes(const char *s) +{ + QCString name; + if (s==0 || *s==0) return name; + name=s; + if (name.at(0)=='"' && name.at(name.length()-1)=='"') + { + name=name.mid(1,name.length()-2); + } + return name; +} +#endif +//----------------------------------------------------------------- + +static void addMemberGroupDocs() +{ + memberGroupDocs=current->brief.stripWhiteSpace(); + current->doc = current->doc.stripWhiteSpace(); + if (!memberGroupDocs.isEmpty() && !current->doc.isEmpty()) + { + memberGroupDocs+="\n\n"; + } + memberGroupDocs+=current->doc; + MemberGroupInfo *info=Doxygen::memGrpInfoDict.find(memberGroupId); + if (info) + { + info->doc = memberGroupDocs; + info->docFile = yyFileName; + } + current->doc.resize(0); + current->brief.resize(0); +} + +//----------------------------------------------------------------- +static void startCommentBlock(bool brief) +{ + if (brief) + { + current->briefFile = yyFileName; + current->briefLine = yyLineNr; + } + else + { + current->docFile = yyFileName; + current->docLine = yyLineNr; + } +} + +/* +static void appendDocBlock() { + previous = current; + current_root->addSubEntry(current); + current = new Entry; + initEntry(); +} +*/ + +static void handleCommentBlock(const QCString &doc,bool brief) +{ + //printf("handleCommentBlock(doc=[%s] brief=%d docBlockInBody=%d\n", + // doc.data(),brief,docBlockInBody); + + // TODO: Fix me + docBlockInBody=FALSE; + + if (docBlockInBody && previous && !previous->doc.isEmpty()) + { + previous->doc=previous->doc.stripWhiteSpace()+"\n\n"; + } + + if (parseCommentBlock( + g_thisParser, + (docBlockInBody && previous) ? previous : current, + doc, // text + yyFileName, // file + brief ? current->briefLine : current->docLine, // line of block start + docBlockInBody ? FALSE : brief, + FALSE, // javadoc style + protection) + ) // need to start a new entry + { + // printf("adding node to nodelist..."); + if (current->section==Entry::MEMBERGRP_SEC) + { + addMemberGroupDocs(); + } + current_root->addSubEntry(current); + previous = current; + current = new Entry ; + initEntry(); + } + +} + +//----------------------------------------------------------------------------- +/* ----------------------------------------------------------------- */ +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); + +static int yyread(char *buf,int max_size) +{ + int c=0; + while ( c < max_size && inputString[inputPosition] ) + { + *buf = inputString[inputPosition++] ; + //printf("%d (%c)\n",*buf,*buf); + c++; buf++; + } + return c; +} + +%} + + /* start command character */ + + + +BB [ \t]+ +B [ \t]* +NEWLINE \n +BN [ \t\n] + +DIGIT [0-9] +LETTER [A-Za-z] +NONEMPTY [A-Za-z0-9_] +EXPCHAR [#(){}\[\],:.%/\\=`*~|&<>!;+-] +NONEMPTYEXP [^ \t\n:] +PARAMNONEMPTY [^ \t\n():] +IDENTIFIER ({LETTER}|"_")({LETTER}|{DIGIT}|"_")* +BORDER ([^A-Za-z0-9]) + +POUNDCOMMENT "#".* + +TRISINGLEQUOTE "'''" +TRIDOUBLEQUOTE "\"\"\"" +LONGSTRINGCHAR [^\\"'] +ESCAPESEQ ("\\")(.) +LONGSTRINGITEM ({LONGSTRINGCHAR}|{ESCAPESEQ}) +SMALLQUOTE ("\"\""|"\""|"'"|"''") +LONGSTRINGBLOCK ({LONGSTRINGITEM}+|{SMALLQUOTE}) + +SHORTSTRING ("'"{SHORTSTRINGITEM}*"'"|'"'{SHORTSTRINGITEM}*'"') +SHORTSTRINGITEM ({SHORTSTRINGCHAR}|{ESCAPESEQ}) +SHORTSTRINGCHAR [^\\\n"] +STRINGLITERAL {STRINGPREFIX}?( {SHORTSTRING} | {LONGSTRING}) +STRINGPREFIX ("r"|"u"|"ur"|"R"|"U"|"UR"|"Ur"|"uR") +KEYWORD ("lambda"|"import"|"class"|"assert"|"as"|"from"|"global"|"def"|"True"|"False") +FLOWKW ("or"|"and"|"is"|"not"|"print"|"for"|"in"|"if"|"try"|"except"|"yield"|"raise"|"break"|"continue"|"pass"|"if"|"return"|"while"|"elif"|"else"|"finally") +QUOTES ("\""[^"]*"\"") +SINGLEQUOTES ("'"[^']*"'") + +STARTDOCSYMS "##" + +%option noyywrap +%option nounput + + /* Main start state */ + +%x Body + + /* Mid-comment states */ + + /* %x FuncDoubleComment */ + /* %x ClassDoubleComment */ +%x TryClassDocString +%x MultiDoubleComment +%x SpecialComment + + /* Function states */ + +%x FunctionDec +%x FunctionParams + + /* Class states */ + +%x ClassDec +%x ClassInheritance +%x ClassCaptureIndent + + +%% + + /* ------------ Function recognition rules -------------- */ + +{ + + {IDENTIFIER} { + //found function name + if (current->type.isEmpty()) + { + current->type = "def"; + } + + current->name = yytext; + current->name = current->name.stripWhiteSpace(); + current->fileName = yyFileName; + } + + {B}"(" { + BEGIN( FunctionParams ); + } +} + +{ + ({BB}|",") { + } + + {IDENTIFIER} { // Name of parameter + lineCount(); + Argument *a = new Argument; + current->argList->append(a); + current->argList->getLast()->name = QCString(yytext).stripWhiteSpace(); + current->argList->getLast()->type = ""; + } + "="[^,)\n]+ { // default value + // TODO: this rule is too simple, need to be able to + // match things like =")" as well! + QCString defVal=&yytext[1]; + if (current->argList->getLast()) + { + current->argList->getLast()->defval=defVal.stripWhiteSpace(); + } + } + + ")" { // end of parameter list + } + + ":"{BN}* { + lineCount(); + + // Push the entry. + previous = current; + current_root->addSubEntry(current); + current = new Entry ; + initEntry(); + + BEGIN( Body ); + } + + {PARAMNONEMPTY} { // Default rule inside arguments. + } + +} + +{ + + "def"{BB} { + lineCount(); + current->fileName = yyFileName; + current->startLine = yyLineNr; + current->bodyLine = yyLineNr; + current->section = Entry::FUNCTION_SEC; + current->protection = protection = Public; + current->objc = FALSE; + current->virt = Normal; + current->stat = FALSE; + current->mtype = mtype = Method; + current->type.resize(0); + current->name.resize(0); + current->args.resize(0); + current->argList->clear(); + + // If this function has slipped out + // of the parent scope, jump out. + if ( g_indent == 0 || g_indent < class_indent ) + { + // printf("Function has slipped out of scope! (%d < %d)", g_indent, class_indent); + + class_indent = 0; + + if (current_root->parent) + { + current_root = current_root->parent; + } + else + { + // This is bad!!! + // printf("Warning: using global root because pointer to parent was lost\n"); + current_root = global_root; + } + + } + + BEGIN( FunctionDec ); + } + + + "class"{BB} { + lineCount() ; + current->section = Entry::CLASS_SEC; + current->argList->clear(); + current->type += "class" ; + current->fileName = yyFileName; + current->startLine = yyLineNr; + current->bodyLine = yyLineNr; + + // Reset scope - new class found. + // (nested classes not supported) + classKeywordIndent = g_indent; + current_root = global_root; + + BEGIN( ClassDec ) ; + } + + ^{BB} { // This is for capturing the current indentation + // of the current line. + g_indent = yyleng; + } + + [^\n] { + // This is the major default + // that should catch everything + // else in Body. + } + + {NEWLINE}+ { + lineCount(); + g_indent = 0; + } +} + +{ + {TRIDOUBLEQUOTE} { + if (g_doubleQuote) + { + if (g_specialBlock) + { + handleCommentBlock(docBlock, FALSE); + } + else + { + docBlock.resize(0); + } + BEGIN(docBlockContext); + } + else + { + docBlock += yytext; + } + } + + {TRISINGLEQUOTE} { + if (!g_doubleQuote) + { + if (g_specialBlock) + { + handleCommentBlock(docBlock, FALSE); + } + else + { + docBlock.resize(0); + } + BEGIN(docBlockContext); + } + else + { + docBlock += yytext; + } + } + + ({LONGSTRINGBLOCK}) { + lineCount(); + docBlock += yytext; + } +} + +{ + ^{B}"#"("#")* { // skip leading hashes + } + \n/{B}"#" { // continuation of the comment on the next line + docBlock+='\n'; + docBrief = FALSE; + startCommentBlock(FALSE); + yyLineNr++; + } + [^#\n]+ { // any other stuff + docBlock+=yytext; + } + \n { // new line that ends the comment + handleCommentBlock(docBlock, docBrief); + yyLineNr++; + BEGIN(docBlockContext); + } + . { // anything we missed + docBlock+=*yytext; + } +} + + /* ------------ Class rules -------------- */ + +{IDENTIFIER} { + if (current->type.isEmpty()) + { + current->type = "class"; + } + + current->section = Entry::CLASS_SEC; + current->name = yytext; + current->name = current->name.stripWhiteSpace(); + current->fileName = yyFileName; + docBlockContext = YY_START; + docBlockInBody = FALSE; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + + // Setting indentation to 0; this totally + // totally disallows nested classes. + // This is okay for now. + class_indent = 0; + + BEGIN(ClassInheritance); + } + +{ + ({BB}|[(,)]) { + } + + ":" { + //BEGIN(TryClassDocString); + BEGIN(ClassCaptureIndent); + } + + + {IDENTIFIER} { + current->extends->append( + new BaseInfo(yytext,Public,Normal) + ); + //Has base class-do stuff + } +} + + +{ + "\n"|({BB}"\n") { + // Blankline - ignore, keep looking for indentation. + lineCount(); + } + + {BB}/({NONEMPTY}|{EXPCHAR}) { + // Indentation level found! + // Pushback the class, and + // try to take over as the current root. + + // Add to tree + current_root->addSubEntry(current); + + if (yyleng >= classKeywordIndent) + { + // Take over the parent if this indentation + // is greater than the indentation + // of where the class started. + current->parent = current_root; + current_root = current; + previous = 0; + class_indent = yyleng; + + // printf("Found indent of %d on line %d, using it.\n", class_indent, yyLineNr); + } + else + { + // Otherwise, don't push deeper; + // this class's scope never started + // properly. + previous = current; + current->endBodyLine = yyLineNr; + // printf("Found indent, but its too small (%d < %d)", yyleng, classKeywordIndent); + } + + // Re-initialize current + current = new Entry ; + initEntry(); + + // Remember indentation level for later funcs + g_indent = yyleng; + BEGIN( Body ); + } + + ""/({NONEMPTY}|{EXPCHAR}) { + // Default rule; this is a syntax error + // (no indentation defined by user). + class_indent = 0; + + // Just pushback an empty class, and + // resume parsing the body. + previous = current; + current_root->addSubEntry(current); + current = new Entry ; + initEntry(); + + // printf("Failed to find indent - skipping!"); + BEGIN( Body ); + } +} + + + + /* ------------ End rules -------------- */ + +<*>{TRIDOUBLEQUOTE}("!")? { // start of a comment block + lineCount(); + docBlockContext = YY_START; + docBlockInBody = FALSE; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + g_doubleQuote = TRUE; + g_specialBlock = yytext[yyleng-1]=='!'; + startCommentBlock(FALSE); + BEGIN(MultiDoubleComment); + } + +<*>{TRISINGLEQUOTE}("!"?) { + lineCount(); + docBlockContext = YY_START; + docBlockInBody = FALSE; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + g_doubleQuote = FALSE; + g_specialBlock = yytext[yyleng-1]=='!'; + startCommentBlock(FALSE); + BEGIN(MultiDoubleComment); + } + +<*>{STARTDOCSYMS} { + docBlockContext = YY_START; + docBlockInBody = FALSE; + docBlockJavaStyle = TRUE; + docBrief = TRUE; + docBlock.resize(0); + startCommentBlock(TRUE); + BEGIN(SpecialComment); + } + + +<*>({NONEMPTY}|{EXPCHAR}|{BB}) { // This should go one character at a time. + // printf("[pyscanner] '%s' [ state %d ] [line %d] no match\n", + // yytext, YY_START, yyLineNr); + + } + +<*>{NEWLINE} { + //printf("[pyscanner] %d NEWLINE [line %d] no match\n", + // YY_START, yyLineNr); + + lineCount(); + BEGIN(Body); + } + +<*>. { + //printf("[pyscanner] '%s' [ state %d ] [line %d] no match\n", + // yytext, YY_START, yyLineNr); + + BEGIN(Body); + } + + +%% + +//---------------------------------------------------------------------------- + +static void parseCompounds(Entry *rt) +{ + //printf("parseCompounds(%s)\n",rt->name.data()); + EntryListIterator eli(*rt->sublist); + Entry *ce; + for (;(ce=eli.current());++eli) + { + if (!ce->program.isEmpty()) + { + //printf("-- %s ---------\n%s\n---------------\n", + // ce->name.data(),ce->program.data()); + // init scanner state + padCount=0; + depthIf = 0; + inputString = ce->program; + lastDefGroup.groupname.resize(0); + inputPosition = 0; + pyscanYYrestart( pyscanYYin ) ; + + BEGIN( Body ) ; + + current_root = ce ; + yyFileName = ce->fileName; + //setContext(); + yyLineNr = ce->startLine ; + //insideObjC = ce->objc; + //printf("---> Inner block starts at line %d objC=%d\n",yyLineNr,insideObjC); + //current->reset(); + if (current) delete current; + current = new Entry; + gstat = FALSE; + int ni=ce->name.findRev("::"); if (ni==-1) ni=0; else ni+=2; + // set default protection based on the compound type + if ( ce->section==Entry::CLASS_SEC ) // class + { + current->protection = protection = Public; + } + mtype = Method; + virt = Normal; + //printf("name=%s current->stat=%d gstat=%d\n",ce->name.data(),current->stat,gstat); + + memberGroupId = DOX_NOGROUP; + memberGroupRelates.resize(0); + memberGroupInside.resize(0); + + pyscanYYlex() ; + delete current; current=0; + ce->program.resize(0); + + if (depthIf>0) + { + warn(yyFileName,yyLineNr,"Documentation block ended in the middle of a conditional section!"); + } + } + parseCompounds(ce); + } +} + +//---------------------------------------------------------------------------- + +static void parseMain(const char *fileName,const char *fileBuf,Entry *rt) +{ + initParser(); + + inputString = fileBuf; + inputPosition = 0; + + anonCount = 0; + depthIf = 0; + protection = Public; + mtype = Method; + gstat = FALSE; + virt = Normal; + current_root = rt; + + global_root = rt; + inputFile.setName(fileName); + if (inputFile.open(IO_ReadOnly)) + { + yyLineNr= 1 ; + yyFileName = fileName; + //setContext(); + msg("Parsing file %s...\n",yyFileName.data()); + + current_root = rt ; + initParser(); + current = new Entry; + int sec=guessSection(yyFileName); + if (sec) + { + current->name = yyFileName; + current->section = sec; + current_root->addSubEntry(current); + current = new Entry; + } + + + + // Set the python flags + //current_root->python = TRUE; + //current->python = TRUE; + + current->reset(); + pyscanYYrestart( pyscanYYin ); + BEGIN( Body ); + + pyscanYYlex(); + //call ast visitor + if (depthIf>0) + { + warn(yyFileName,yyLineNr,"Documentation block ended in the middle of a conditional section!"); + } + + rt->program.resize(0); + delete current; current=0; + + parseCompounds(rt); + + inputFile.close(); + } + +} + +//---------------------------------------------------------------------------- + +static void parsePrototype(const QCString &text) +{ + //printf("**** parsePrototype(%s) begin\n",text.data()); + + const char *orgInputString; + int orgInputPosition; + YY_BUFFER_STATE orgState; + + // save scanner state + orgState = YY_CURRENT_BUFFER; + yy_switch_to_buffer(yy_create_buffer(pyscanYYin, YY_BUF_SIZE)); + orgInputString = inputString; + orgInputPosition = inputPosition; + + // set new string + inputString = text; + inputPosition = 0; + pyscanYYrestart( pyscanYYin ); + + BEGIN( Body ); + + pyscanYYlex(); + + current->name = current->name.stripWhiteSpace(); + if (current->section == Entry::MEMBERDOC_SEC && current->args.isEmpty()) + current->section = Entry::VARIABLEDOC_SEC; + + // restore original scanner state + yy_switch_to_buffer(orgState); + inputString = orgInputString; + inputPosition = orgInputPosition; + + //printf("**** parsePrototype end\n"); +} + +//---------------------------------------------------------------------------- + +void PythonLanguageScanner::parseInput(const char *fileName,const char *fileBuf,Entry *root) +{ + g_thisParser = this; + ::parseMain(fileName,fileBuf,root); + + // May print the AST for debugging purposes + // printAST(global_root); +} + +bool PythonLanguageScanner::needsPreprocessing(const QCString &) +{ + return FALSE; +} + +void PythonLanguageScanner::parseCode(CodeOutputInterface &codeOutIntf, + const char *scopeName, + const QCString &input, + bool isExampleBlock, + const char *exampleName, + FileDef *fileDef, + int startLine, + int endLine, + bool inlineFragment, + MemberDef *memberDef + ) +{ + ::parsePythonCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName, + fileDef,startLine,endLine,inlineFragment,memberDef); +} + +void PythonLanguageScanner::parsePrototype(const char *text) +{ + ::parsePrototype(text); + +} + +void PythonLanguageScanner::resetCodeParserState() +{ + ::resetPythonCodeParserState(); +} + +void PythonLanguageScanner::handleGroupStartCommand(const char * /*header*/) +{ + +} + +void PythonLanguageScanner::handleGroupEndCommand() +{ + +} + + +//---------------------------------------------------------------------------- + +#if !defined(YY_FLEX_SUBMINOR_VERSION) +//---------------------------------------------------------------------------- +extern "C" { // some bogus code to keep the compiler happy + void pyscannerYYdummy() { yy_flex_realloc(0,0); } +} +#endif + diff --git a/src/rtfdocvisitor.cpp b/src/rtfdocvisitor.cpp index e524800..85a020e 100644 --- a/src/rtfdocvisitor.cpp +++ b/src/rtfdocvisitor.cpp @@ -21,20 +21,21 @@ #include "language.h" #include "doxygen.h" #include "outputgen.h" -#include "code.h" #include "dot.h" #include "util.h" #include "rtfstyle.h" #include "message.h" #include +#include "parserintf.h" #define DBG_RTF(x) m_t << x //#define DBG_RTF(x) do {} while(0) -RTFDocVisitor::RTFDocVisitor(QTextStream &t,BaseCodeDocInterface &ci) +RTFDocVisitor::RTFDocVisitor(QTextStream &t,CodeOutputInterface &ci, + const char *langExt) : DocVisitor(DocVisitor_RTF), m_t(t), m_ci(ci), m_insidePre(FALSE), - m_hide(FALSE), m_indentLevel(0), m_lastIsPara(FALSE) + m_hide(FALSE), m_indentLevel(0), m_lastIsPara(FALSE), m_langExt(langExt) { } @@ -322,7 +323,9 @@ void RTFDocVisitor::visit(DocVerbatim *s) m_t << "{" << endl; m_t << "\\par" << endl; m_t << rtf_Style_Reset << getStyle("CodeExample"); - parseCode(m_ci,s->context(),s->text().latin1(),s->isExample(),s->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,s->context(),s->text().latin1(), + s->isExample(),s->exampleFile()); //m_t << "\\par" << endl; m_t << "}" << endl; break; @@ -401,7 +404,11 @@ void RTFDocVisitor::visit(DocInclude *inc) m_t << rtf_Style_Reset << getStyle("CodeExample"); QFileInfo cfi( inc->file() ); FileDef fd( cfi.dirPath(), cfi.fileName() ); - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile(), &fd); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(), + inc->isExample(), + inc->exampleFile(), &fd); m_t << "\\par" << endl; m_t << "}" << endl; } @@ -410,7 +417,10 @@ void RTFDocVisitor::visit(DocInclude *inc) m_t << "{" << endl; m_t << "\\par" << endl; m_t << rtf_Style_Reset << getStyle("CodeExample"); - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(),inc->isExample(), + inc->exampleFile()); m_t << "\\par" << endl; m_t << "}" << endl; break; @@ -449,7 +459,12 @@ void RTFDocVisitor::visit(DocIncOperator *op) if (op->type()!=DocIncOperator::Skip) { popEnabled(); - if (!m_hide) parseCode(m_ci,op->context(),op->text().latin1(),op->isExample(),op->exampleFile()); + if (!m_hide) + { + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,op->context(),op->text().latin1(), + op->isExample(),op->exampleFile()); + } pushEnabled(); m_hide=TRUE; } diff --git a/src/rtfdocvisitor.h b/src/rtfdocvisitor.h index 9508972..90175e3 100644 --- a/src/rtfdocvisitor.h +++ b/src/rtfdocvisitor.h @@ -21,16 +21,17 @@ #include "docvisitor.h" #include +#include class QTextStream; -class BaseCodeDocInterface; +class CodeOutputInterface; class QString; /*! @brief Concrete visitor implementation for RTF output. */ class RTFDocVisitor : public DocVisitor { public: - RTFDocVisitor(QTextStream &t,BaseCodeDocInterface &ci); + RTFDocVisitor(QTextStream &t,CodeOutputInterface &ci,const char *langExt); //-------------------------------------- // visitor functions for leaf nodes @@ -149,12 +150,13 @@ class RTFDocVisitor : public DocVisitor //-------------------------------------- QTextStream &m_t; - BaseCodeDocInterface &m_ci; + CodeOutputInterface &m_ci; bool m_insidePre; bool m_hide; int m_indentLevel; QStack m_enabled; bool m_lastIsPara; + QCString m_langExt; }; #endif diff --git a/src/rtfgen.cpp b/src/rtfgen.cpp index 47193ba..2f768ad 100644 --- a/src/rtfgen.cpp +++ b/src/rtfgen.cpp @@ -2483,9 +2483,9 @@ void RTFGenerator::endParamList() t << "}"; } -void RTFGenerator::printDoc(DocNode *n) +void RTFGenerator::printDoc(DocNode *n,const char *langExt) { - RTFDocVisitor *visitor = new RTFDocVisitor(t,*this); + RTFDocVisitor *visitor = new RTFDocVisitor(t,*this,langExt); n->accept(visitor); delete visitor; } diff --git a/src/rtfgen.h b/src/rtfgen.h index 0495da7..d17cad8 100644 --- a/src/rtfgen.h +++ b/src/rtfgen.h @@ -43,7 +43,7 @@ class RTFGenerator : public OutputGenerator bool isEnabled(OutputType o) { return (o==RTF && active); } OutputGenerator *get(OutputType o) { return (o==RTF) ? this : 0; } - void printDoc(DocNode *); + void printDoc(DocNode *,const char *); void startFile(const char *name,const char *manName,const char *title); void writeFooter() {} diff --git a/src/scanner.h b/src/scanner.h index 2dbbf4e..8432f6f 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -24,32 +24,30 @@ * * This is the language parser for doxygen. It is somewhat fuzzy and * supports C++ and various languages that are closely related to C++, - * such as C,C#,Objective-C,Java,PHP,and IDL. + * such as C, C#, Objective-C, Java, PHP, and IDL. */ class CLanguageScanner : public ParserInterface { public: - void parse(const char *fileName,const char *fileBuf,Entry *root); + void parseInput(const char *fileName, + const char *fileBuf, + Entry *root); + bool needsPreprocessing(const QCString &extension); + void parseCode(CodeOutputInterface &codeOutIntf, + const char *scopeName, + const QCString &input, + bool isExampleBlock, + const char *exampleName=0, + FileDef *fileDef=0, + int startLine=-1, + int endLine=-1, + bool inlineFragment=FALSE, + MemberDef *memberDef=0 + ); + void resetCodeParserState(); void parsePrototype(const char *text); void handleGroupStartCommand(const char *header); void handleGroupEndCommand(); }; -#if 0 - -#include "qtbc.h" - -class OutputList; -class Entry; - -// Public interface provided by the language scanner -void parseMain(Entry *,const char *fileName); - -// Internal callback interface for comment block scanner -void parsePrototype(const QCString &text); -void handleGroupStartCommand(const char *header); -void handleGroupEndCommand(); - -#endif - #endif diff --git a/src/scanner.l b/src/scanner.l index b93df38..ddd839b 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -41,6 +41,7 @@ #include "defargs.h" #include "language.h" #include "commentscan.h" +#include "code.h" #define YY_NEVER_INTERACTIVE 1 @@ -1843,6 +1844,42 @@ IDLATTR ("["[^\]]*"]"){BN}* handleGroupStartCommand(current->name); current = tmp; initEntry(); + if (yytext[1]=='/') + { + if (yytext[2]=='!' || yytext[2]=='/') + { + docBlockContext = YY_START; + docBlockInBody = FALSE; + docBlockJavaStyle = FALSE; + docBlock.resize(0); + docBlockTerm = 0; + startCommentBlock(TRUE); + BEGIN(DocLine); + } + else + { + lastCContext=YY_START; + BEGIN(SkipCxxComment); + } + } + else + { + if (yytext[2]=='!' || yytext[2]=='*') + { + docBlockContext = YY_START; + docBlockInBody = FALSE; + docBlock.resize(0); + docBlockJavaStyle = yytext[2]=='*' && Config_getBool("JAVADOC_AUTOBRIEF"); + docBlockTerm = 0; + startCommentBlock(FALSE); + BEGIN(DocBlock); + } + else + { + lastCContext=YY_START; + BEGIN(SkipComment); + } + } } "//"([!/]?){B}*{CMD}"}".*|"/*"([!*]?){B}*{CMD}"}".*"*/" { handleGroupEndCommand(); @@ -4540,12 +4577,42 @@ static void handleGroupEndCommand() //---------------------------------------------------------------------------- -void CLanguageScanner::parse(const char *fileName,const char *fileBuf,Entry *root) +void CLanguageScanner::parseInput(const char *fileName,const char *fileBuf,Entry *root) { g_thisParser = this; ::parseMain(fileName,fileBuf,root); } +void CLanguageScanner::parseCode(CodeOutputInterface & codeOutIntf, + const char * scopeName, + const QCString & input, + bool isExampleBlock, + const char * exampleName, + FileDef * fileDef, + int startLine, + int endLine, + bool inlineFragment, + MemberDef *memberDef + ) +{ + ::parseCCode(codeOutIntf,scopeName,input,isExampleBlock,exampleName, + fileDef,startLine,endLine,inlineFragment,memberDef); +} + +bool CLanguageScanner::needsPreprocessing(const QCString &extension) +{ + QCString fe=extension.lower(); + return + !( fe==".java" || fe==".as" || fe==".cs" || fe==".d" || fe==".php" || + fe==".php4" || fe==".inc" || fe==".phtml" || fe==".m" || fe==".mm" + ); +} + +void CLanguageScanner::resetCodeParserState() +{ + ::resetCCodeParserState(); +} + void CLanguageScanner::parsePrototype(const char *text) { ::parsePrototype(text); diff --git a/src/util.cpp b/src/util.cpp index 6728c56..679e0d3 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -2698,6 +2698,8 @@ static QCString getCanonicalTypeForIdentifier( symName=word; } + //printf("symName=%s templSpec=%s\n",symName.data(),templSpec.data()); + if (!symName.isEmpty() && !templSpec.isEmpty() && (defList=Doxygen::symbolMap->find(symName+templSpec)) && defList->count()==1) // word without scope but with template specs @@ -2733,6 +2735,10 @@ static QCString getCanonicalTypeForIdentifier( if (cd) // known type { result = cd->qualifiedNameWithTemplateParameters(); + if (cd->isTemplate()) + { + *tSpec=""; + } } else if (mType && mType->isEnumerate()) // an enum { @@ -2799,15 +2805,15 @@ static QCString extractCanonicalType(Definition *d,FileDef *fs,const Argument *a // then resolve any identifiers inside. { static QRegExp re("[a-z_A-Z][a-z_A-Z0-9]*"); - int p=0,l,i; + int tp=0,tl,ti; // for each identifier template specifier - while ((i=re.match(templSpec,p,&l))!=-1) + while ((ti=re.match(templSpec,tp,&tl))!=-1) { - canType += templSpec.mid(p,i-p); - canType += getCanonicalTypeForIdentifier(d,fs,word,0); - p=i+l; + canType += templSpec.mid(tp,ti-tp); + canType += getCanonicalTypeForIdentifier(d,fs,templSpec.mid(ti,tl),0); + tp=ti+tl; } - canType+=templSpec.right(templSpec.length()-p); + canType+=templSpec.right(templSpec.length()-tp); } pp=p; diff --git a/src/xmldocvisitor.cpp b/src/xmldocvisitor.cpp index 6ff9ac1..01e5a06 100644 --- a/src/xmldocvisitor.cpp +++ b/src/xmldocvisitor.cpp @@ -22,13 +22,13 @@ #include "doxygen.h" #include "outputgen.h" #include "xmlgen.h" -#include "code.h" #include "dot.h" #include "message.h" #include "util.h" #include +#include "parserintf.h" -XmlDocVisitor::XmlDocVisitor(QTextStream &t,BaseCodeDocInterface &ci) +XmlDocVisitor::XmlDocVisitor(QTextStream &t,CodeOutputInterface &ci) : DocVisitor(DocVisitor_XML), m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE) { } @@ -169,7 +169,9 @@ void XmlDocVisitor::visit(DocVerbatim *s) { case DocVerbatim::Code: // fall though m_t << ""; - parseCode(m_ci,s->context(),s->text().latin1(),s->isExample(),s->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,s->context(),s->text().latin1(), + s->isExample(),s->exampleFile()); m_t << ""; break; case DocVerbatim::Verbatim: @@ -219,13 +221,21 @@ void XmlDocVisitor::visit(DocInclude *inc) m_t << ""; QFileInfo cfi( inc->file() ); FileDef fd( cfi.dirPath(), cfi.fileName() ); - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile(), &fd); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(), + inc->isExample(), + inc->exampleFile(), &fd); m_t << ""; } break; case DocInclude::Include: m_t << ""; - parseCode(m_ci,inc->context(),inc->text().latin1(),inc->isExample(),inc->exampleFile()); + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,inc->context(), + inc->text().latin1(), + inc->isExample(), + inc->exampleFile()); m_t << ""; break; case DocInclude::DontInclude: @@ -259,7 +269,13 @@ void XmlDocVisitor::visit(DocIncOperator *op) if (op->type()!=DocIncOperator::Skip) { popEnabled(); - if (!m_hide) parseCode(m_ci,op->context(),op->text().latin1(),op->isExample(),op->exampleFile()); + if (!m_hide) + { + Doxygen::parserManager->getParser(m_langExt) + ->parseCode(m_ci,op->context(), + op->text().latin1(),op->isExample(), + op->exampleFile()); + } pushEnabled(); m_hide=TRUE; } diff --git a/src/xmldocvisitor.h b/src/xmldocvisitor.h index 4d77a3b..60e0820 100644 --- a/src/xmldocvisitor.h +++ b/src/xmldocvisitor.h @@ -21,16 +21,17 @@ #include "docvisitor.h" #include +#include class QTextStream; -class BaseCodeDocInterface; +class CodeOutputInterface; class QString; /*! @brief Concrete visitor implementation for XML output. */ class XmlDocVisitor : public DocVisitor { public: - XmlDocVisitor(QTextStream &t,BaseCodeDocInterface &ci); + XmlDocVisitor(QTextStream &t,CodeOutputInterface &ci); //-------------------------------------- // visitor functions for leaf nodes @@ -145,10 +146,11 @@ class XmlDocVisitor : public DocVisitor //-------------------------------------- QTextStream &m_t; - BaseCodeDocInterface &m_ci; + CodeOutputInterface &m_ci; bool m_insidePre; bool m_hide; QStack m_enabled; + QCString m_langExt; }; #endif diff --git a/src/xmlgen.cpp b/src/xmlgen.cpp index 8be6b44..63a5f44 100644 --- a/src/xmlgen.cpp +++ b/src/xmlgen.cpp @@ -28,13 +28,13 @@ #include "defargs.h" #include "outputgen.h" #include "dot.h" -#include "code.h" #include "pagedef.h" #include "filename.h" #include "version.h" #include "xmldocvisitor.h" #include "docparser.h" #include "language.h" +#include "parserintf.h" #include #include @@ -222,7 +222,7 @@ template class ValStack }; -class XMLCodeGenerator : public BaseCodeDocInterface +class XMLCodeGenerator : public CodeOutputInterface { public: @@ -443,14 +443,15 @@ static void writeXMLDocBlock(QTextStream &t, void writeXMLCodeBlock(QTextStream &t,FileDef *fd) { - initParseCodeContext(); + ParserInterface *pIntf=Doxygen::parserManager->getParser(fd->getDefFileExtension()); + pIntf->resetCodeParserState(); XMLCodeGenerator *xmlGen = new XMLCodeGenerator(t); - parseCode(*xmlGen, - 0, - fileToString(fd->absFilePath(),Config_getBool("FILTER_SOURCE_FILES")), - FALSE, - 0, - fd); + pIntf->parseCode(*xmlGen, + 0, + fileToString(fd->absFilePath(),Config_getBool("FILTER_SOURCE_FILES")), + FALSE, + 0, + fd); xmlGen->finish(); delete xmlGen; } @@ -563,9 +564,10 @@ static void generateXMLForMember(MemberDef *md,QTextStream &ti,QTextStream &t,De case Private: t << "private"; break; case Package: t << "package"; break; } - t << "\" static=\""; - if (md->isStatic()) t << "yes"; else t << "no"; + t << "\""; + t << " static=\""; + if (md->isStatic()) t << "yes"; else t << "no"; t << "\""; if (isFunc) @@ -573,22 +575,24 @@ static void generateXMLForMember(MemberDef *md,QTextStream &ti,QTextStream &t,De ArgumentList *al = md->argumentList(); t << " const=\""; if (al && al->constSpecifier) t << "yes"; else t << "no"; + t << "\""; - t << "\" explicit=\""; + t << " explicit=\""; if (md->isExplicit()) t << "yes"; else t << "no"; + t << "\""; - t << "\" inline=\""; + t << " inline=\""; if (md->isInline()) t << "yes"; else t << "no"; + t << "\""; - t << "\" virt=\""; - switch (md->virtualness()) - { - case Normal: t << "non-virtual"; break; - case Virtual: t << "virtual"; break; - case Pure: t << "pure-virtual"; break; - default: ASSERT(0); - } - + t << " virt=\""; + switch (md->virtualness()) + { + case Normal: t << "non-virtual"; break; + case Virtual: t << "virtual"; break; + case Pure: t << "pure-virtual"; break; + default: ASSERT(0); + } t << "\""; } @@ -598,15 +602,16 @@ static void generateXMLForMember(MemberDef *md,QTextStream &ti,QTextStream &t,De //t << " volatile=\""; //if (al && al->volatileSpecifier) t << "yes"; else t << "no"; - t << "\" mutable=\""; + t << " mutable=\""; if (md->isMutable()) t << "yes"; else t << "no"; - t << "\""; } else if (md->memberType() == MemberDef::Property) { t << " readable=\""; if (md->isReadable()) t << "yes"; else t << "no"; + t << "\""; + t << "\" writable=\""; if (md->isWritable()) t << "yes"; else t << "no"; t << "\""; -- cgit v0.12