diff options
63 files changed, 3809 insertions, 506 deletions
@@ -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 = @@ -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) @@ -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) @@ -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. <li>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. <li>Supports documentation of files, namespaces, classes, structs, unions, templates, variables, functions, typedefs, enums and defines. -<li>JavaDoc (1.1), Qt-Doc, and KDOC compatible. -<li>Automatically generates class diagrams in HTML (as clickable +<li>JavaDoc (1.1), Qt-Doc, and ECMA-334 (C# spec.) compatible. +<li>Automatically generates class and collaboration diagrams in HTML (as clickable image maps) and \f$\mbox{\LaTeX}\f$ (as Encapsulated PostScript images). <li>Uses the dot tool of the Graphviz tool kit to generate include dependency graphs, collaboration diagrams, and @@ -76,8 +76,9 @@ <li>Can cope with large projects easily. </UL> -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 <A HREF="http://www.trolltech.com/products/qt.html">Qt toolkit</A>. 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). <ul> <li><tt>\<A HREF="..."\></tt> Starts a HTML hyper-link (HTML only). diff --git a/doc/index.doc b/doc/index.doc index fe0cc75..6f7fa2a 100644 --- a/doc/index.doc +++ b/doc/index.doc @@ -97,6 +97,8 @@ The second part forms a reference manual: used within the documentation. <li>Section \ref htmlcmds shows an overview of the HTML commands that can be used within the documentation. +<li>Section \ref xmlcmds shows an overview of the XML commands that + can be used within the documentation. </ul> The third part provides information for developers: @@ -186,6 +188,7 @@ Thanks go to: <li>Tim Mensch for adding the todo command. <li>Christian Hammond for redesigning the web-site. <li>Ken Wong for providing the HTML tree view code. +<li>Talin for adding support for C# style comments with XML markup. <li>Petr Prikryl for coordinating the internationalisation support. All language maintainers for providing translations into many languages. <li>Gerald Steffens of <a href="http://www.e-trend.de">E-trend</a> 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 +<a href="http://www.ecma-international.org/publications/standards/Ecma-334.htm">ECMA-334</a> +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: + +<ul> +<li><tt>\<c\></tt> Identifies inline text that should be rendered as a + piece of code. Similar to using <tt>\<tt\></tt>text<tt>\</tt\></tt>. +<li><tt>\<code\></tt> Set one or more lines of source code or program output. + Note that this command behaves like <tt>\\code ... \\endcode</tt> + for C# code, but it behaves like the HTML equivalent + <tt>\<code\>...\</code\></tt> for other languages. +<li><tt>\<description\></tt> Part of a <tt>\<list\></tt> command, describes an item. +<li><tt>\<example\></tt> Marks a block of text as an example, ignored by doxygen. +<li><tt>\<exception cref="member"\></tt> Identifies the exception a + method can throw. +<li><tt>\<include\></tt> Can be used to import a piece of XML from an external + file. Ignored by doxygen at the moment. +<li><tt>\<item\></tt> List item. Can only be used inside a <tt>\<list\></tt> context. +<li><tt>\<list type="type"\></tt> Starts a list, supported types are <tt>bullet</tt> + or <tt>number</tt>. A list consists of a number of <tt>\<item\></tt> tags. +<li><tt>\<para\></tt> Identifies a paragraph of text. +<li><tt>\<param name="paramName"\></tt> Marks a piece of text as the documentation + for parameter "paramName". Similar to + using \ref cmdparam "\\param". +<li><tt>\<paramref name="paramName"\></tt> Refers to a parameter with name + "paramName". Similar to using \ref cmda "\\a". +<li><tt>\<permission\></tt> Identifies the security accessibility of a member. + Ignored by doygen. +<li><tt>\<remarks\></tt> Identifies the detailed description. +<li><tt>\<returns\></tt> Marks a piece of text as the return value of a + function or method. Similar to using \ref cmdreturn "\\return". +<li><tt>\<see cref="member"\></tt> Refers to a member. Similar to \ref cmdref "\\ref". +<li><tt>\<seealso cref="member"\></tt> Starts a "See also" section referring + to "member". Similar to using \ref cmdsa "\\sa" member. +<li><tt>\<summary\></tt> Identifies the brief description. + Similar to using \ref cmdbrief "\\brief". +<li><tt>\<value\></tt> Identifies a property. Ignored by doxygen. +</ul> + +Here is an example of a typical piece of code using some of the above commands: + +\code +/// <summary> +/// A search engine. +/// </summary> +class Engine +{ + /// <summary> + /// The Search method takes a series of parameters to specify the search criterion + /// and returns a dataset containing the result set. + /// </summary> + /// <param name="connectionString">the connection string to connect to the + /// database holding the content to search</param> + /// <param name="maxRows">The maximum number of rows to + /// return in the result set</param> + /// <param name="searchString">The text that we are searching for</param> + /// <returns>A DataSet instance containing the matching rows. It contains a maximum + /// number of rows specified by the maxRows parameter</returns> + 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 <code> 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<int> 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<int> m_map; - static HtmlTagMapper *m_instance; + static void freeMappers(); + static Mapper *cmdMapper; + static Mapper *htmlTagMapper; }; @@ -21,13 +21,14 @@ #include "qtbc.h" #include <stdio.h> -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 @@ -27,7 +27,6 @@ #include <qdir.h> #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(); } -<MemberCall2,FuncCall>[a-z_A-Z][:a-z_A-Z0-9]*({B}*"<"[^\n\<\>]*">")? { +<MemberCall2,FuncCall>[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}"'")) <CondLine>[ \t]* <CComment,ReadLine>[\\@]"cond"[ \t]*\n | <CondLine>. { // 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); } <CComment,ReadLine>[\\@][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, <summary></summary><remarks></remarks> */ <Comment>{CMD}{CMD}[a-z_A-Z]+{B}* { // escaped command @@ -778,6 +781,12 @@ SCOPENAME "$"?(({ID}?{BN}*"::"{BN}*)*)((~{BN}*)?{ID}) <Comment>{CMD}{CMD}"~"[a-z_A-Z]* { // escaped command addOutput(yytext); } +<Comment>{MAILADR} { // mail adress + addOutput(yytext); + } +<Comment>"\""[^"\n]*"\"" { // quoted text + addOutput(yytext); + } <Comment>("\\"[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; } +<Comment>"<summary>" { // start of a .NET XML style brief description + setOutput(OutputBrief); + } +<Comment>"<remarks>"|"</summary>" { // start of a .NET XML style detailed description + setOutput(OutputDoc); + } +<Comment>"</remarks>" { // end of a brief or detailed description + } <Comment>"<!--" { BEGIN(HtmlComment); } diff --git a/src/config.l b/src/config.l index bfe0e13..1e49a28 100644 --- a/src/config.l +++ b/src/config.l @@ -1722,7 +1722,7 @@ void Config::create() "SHOW_DIRECTORIES", "If the sources in your project are distributed over multiple directories \n" "then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy \n" - "in the documentation.\n", + "in the documentation. The default is YES.\n", TRUE ); cs = addString( "FILE_VERSION_FILTER", diff --git a/src/defgen.cpp b/src/defgen.cpp index b2776d8..bd65ec7 100644 --- a/src/defgen.cpp +++ b/src/defgen.cpp @@ -28,7 +28,6 @@ #include "defargs.h" #include "outputgen.h" #include "dot.h" -#include "code.h" #include <qdir.h> #include <qfile.h> diff --git a/src/definition.cpp b/src/definition.cpp index dd0fa5c..381159b 100644 --- a/src/definition.cpp +++ b/src/definition.cpp @@ -32,6 +32,7 @@ #include "pagedef.h" #include "section.h" #include "htags.h" +#include "parserintf.h" #if defined(_MSC_VER) || defined(__BORLANDC__) #define popen _popen @@ -81,6 +82,11 @@ Definition::Definition(const char *df,int dl, { //QCString ns; m_defFileName = df; + int lastDot = m_defFileName.findRev('.'); + if (lastDot!=-1) + { + m_defFileExt = m_defFileName.mid(lastDot); + } m_defLine = dl; m_name=name; if (m_name!="<globalScope>") @@ -459,12 +465,23 @@ void Definition::writeInlineCode(OutputList &ol,const char *scopeName) actualStart,actualEnd,codeFragment) ) { - initParseCodeContext(); + ParserInterface *pIntf = Doxygen::parserManager->getParser(m_defFileExt); + pIntf->resetCodeParserState(); //printf("Read:\n`%s'\n\n",codeFragment.data()); - if (definitionType()==TypeMember) setParameterList((MemberDef *)this); + MemberDef *thisMd = 0; + if (definitionType()==TypeMember) thisMd = (MemberDef *)this; ol.startCodeFragment(); - parseCode(ol,scopeName,codeFragment,FALSE,0, - m_bodyDef,actualStart,actualEnd,TRUE); + pIntf->parseCode(ol, // codeOutIntf + scopeName, // scope + codeFragment, // input + FALSE, // isExample + 0, // exampleName + m_bodyDef, // fileDef + actualStart, // startLine + actualEnd, // endLine + TRUE, // inlineFragment + thisMd // memberDef + ); ol.endCodeFragment(); ol.newParagraph(); } diff --git a/src/definition.h b/src/definition.h index c766652..496b368 100644 --- a/src/definition.h +++ b/src/definition.h @@ -117,6 +117,9 @@ class Definition /*! returns the file in which this definition was found */ QCString getDefFileName() const { return m_defFileName; } + /*! returns the file in which this definition was found */ + QCString getDefFileExtension() const { return m_defFileExt; } + /*! returns the line number at which the definition was found */ int getDefLine() const { return m_defLine; } @@ -207,6 +210,7 @@ class Definition // where the item was found QCString m_defFileName; int m_defLine; + QCString m_defFileExt; /*! The class, namespace in which this class is located */ diff --git a/src/docparser.cpp b/src/docparser.cpp index 740eba6..c5cabcd 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -688,7 +688,7 @@ static int handleStyleArgument(DocNode *parent,QList<DocNode> &children, g_token->name.data(),cmdName.data()); break; case TK_HTMLTAG: - if (insideLI(parent) && HtmlTagMapper::map(g_token->name) && g_token->endTag) + if (insideLI(parent) && Mappers::htmlTagMapper->map(g_token->name) && g_token->endTag) { // ignore </li> as the end of a style command continue; } @@ -969,7 +969,7 @@ reparsetoken: switch (tok) { case TK_COMMAND: - switch (CmdMapper::map(tokenName)) + switch (Mappers::cmdMapper->map(tokenName)) { case CMD_BSLASH: children.append(new DocSymbol(parent,DocSymbol::BSlash)); @@ -1124,7 +1124,7 @@ reparsetoken: break; case TK_HTMLTAG: { - switch (HtmlTagMapper::map(tokenName)) + switch (Mappers::htmlTagMapper->map(tokenName)) { case HTML_DIV: warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: found <div> tag in heading\n"); @@ -1812,7 +1812,7 @@ void DocSecRefList::parse() { if (tok==TK_COMMAND) { - switch (CmdMapper::map(g_token->name)) + switch (Mappers::cmdMapper->map(g_token->name)) { case CMD_SECREFITEM: { @@ -2067,7 +2067,7 @@ QString DocLink::parse(bool isJavaLink) switch (tok) { case TK_COMMAND: - switch (CmdMapper::map(g_token->name)) + switch (Mappers::cmdMapper->map(g_token->name)) { case CMD_ENDLINK: if (isJavaLink) @@ -2314,7 +2314,7 @@ int DocHtmlHeader::parse() break; case TK_HTMLTAG: { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_H1 && g_token->endTag) // found </h1> tag { if (m_level!=1) @@ -2438,7 +2438,7 @@ int DocHRef::parse() break; case TK_HTMLTAG: { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_A && g_token->endTag) // found </a> tag { goto endhref; @@ -2579,7 +2579,7 @@ int DocIndexEntry::parse() } break; case TK_COMMAND: - switch (CmdMapper::map(g_token->name)) + switch (Mappers::cmdMapper->map(g_token->name)) { case CMD_BSLASH: m_entry+='\\'; break; case CMD_AT: m_entry+='@'; break; @@ -2634,7 +2634,7 @@ int DocHtmlCaption::parse() break; case TK_HTMLTAG: { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_CAPTION && g_token->endTag) // found </caption> tag { retval = RetVal_OK; @@ -2686,7 +2686,7 @@ int DocHtmlCell::parse() retval=par->parse(); if (retval==TK_HTMLTAG) { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_TD && g_token->endTag) // found </dt> tag { retval=TK_NEWPARA; // ignore the tag @@ -2725,7 +2725,7 @@ int DocHtmlRow::parse() // should find a html tag now if (tok==TK_HTMLTAG) { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_TD && !g_token->endTag) // found <td> tag { } @@ -2789,7 +2789,7 @@ getrow: // should find a html tag now if (tok==TK_HTMLTAG) { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_TR && !g_token->endTag) // found <tr> tag { // no caption, just rows @@ -2888,7 +2888,7 @@ int DocHtmlDescTitle::parse() { QString cmdName=g_token->name; bool isJavaLink=FALSE; - switch (CmdMapper::map(cmdName)) + switch (Mappers::cmdMapper->map(cmdName)) { case CMD_REF: { @@ -2964,7 +2964,7 @@ int DocHtmlDescTitle::parse() break; case TK_HTMLTAG: { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_DD && !g_token->endTag) // found <dd> tag { retval = RetVal_DescData; @@ -3054,7 +3054,7 @@ int DocHtmlDescList::parse() // should find a html tag now if (tok==TK_HTMLTAG) { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_DT && !g_token->endTag) // found <dt> tag { // continue @@ -3137,6 +3137,40 @@ int DocHtmlListItem::parse() return retval; } +int DocHtmlListItem::parseXml() +{ + DBG(("DocHtmlListItem::parseXml() start\n")); + int retval=0; + g_nodeStack.push(this); + + // parse one or more paragraphs + bool isFirst=TRUE; + DocPara *par=0; + do + { + par = new DocPara(this); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + m_children.append(par); + retval=par->parse(); + if (retval==0) break; + + //printf("new item: retval=%x g_token->name=%s g_token->endTag=%d\n", + // retval,g_token->name.data(),g_token->endTag); + if (retval==RetVal_ListItem) + { + break; + } + } + while (retval!=RetVal_CloseXml); + + if (par) par->markLast(); + + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + DBG(("DocHtmlListItem::parseXml() end retval=%x\n",retval)); + return retval; +} + //--------------------------------------------------------------------------- int DocHtmlList::parse() @@ -3153,7 +3187,7 @@ int DocHtmlList::parse() // should find a html tag now if (tok==TK_HTMLTAG) { - int tagId=HtmlTagMapper::map(g_token->name); + int tagId=Mappers::htmlTagMapper->map(g_token->name); if (tagId==HTML_LI && !g_token->endTag) // found <li> tag { // ok, we can go on. @@ -3198,6 +3232,68 @@ endlist: return retval==RetVal_EndList ? RetVal_OK : retval; } +int DocHtmlList::parseXml() +{ + DBG(("DocHtmlList::parseXml() start\n")); + int retval=RetVal_OK; + int num=1; + g_nodeStack.push(this); + + // get next token + int tok=doctokenizerYYlex(); + // skip whitespace and paragraph breaks + while (tok==TK_WHITESPACE || tok==TK_NEWPARA) tok=doctokenizerYYlex(); + // should find a html tag now + if (tok==TK_HTMLTAG) + { + int tagId=Mappers::htmlTagMapper->map(g_token->name); + //printf("g_token->name=%s g_token->endTag=%d\n",g_token->name.data(),g_token->endTag); + if (tagId==XML_ITEM && !g_token->endTag) // found <item> tag + { + // ok, we can go on. + } + else // found some other tag + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected <item> tag but " + "found <%s> instead!",g_token->name.data()); + goto endlist; + } + } + else if (tok==0) // premature end of comment + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while looking" + " for a html list item"); + goto endlist; + } + else // token other than html token + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: expected <item> tag but found %s token instead!", + tokToString(tok)); + goto endlist; + } + + do + { + DocHtmlListItem *li=new DocHtmlListItem(this,g_token->attribs,num++); + m_children.append(li); + retval=li->parseXml(); + if (retval==0) break; + //printf("retval=%x g_token->name=%s\n",retval,g_token->name.data()); + } while (retval==RetVal_ListItem); + + if (retval==0) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected end of comment while inside <list type=\"%s\"> block", + m_type==Unordered ? "bullet" : "number"); + } + +endlist: + DBG(("DocHtmlList::parseXml() end retval=%x\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval==RetVal_EndList ? RetVal_OK : retval; +} + //--------------------------------------------------------------------------- int DocSimpleListItem::parse() @@ -3383,6 +3479,66 @@ int DocSimpleSect::parseRcs() return RetVal_OK; } +int DocSimpleSect::parseXml() +{ + DBG(("DocSimpleSect::parse() start\n")); + g_nodeStack.push(this); + + int retval = RetVal_OK; + for (;;) + { + // add new paragraph as child + DocPara *par = new DocPara(this); + if (m_children.isEmpty()) + { + par->markFirst(); + } + else + { + ASSERT(m_children.last()->kind()==DocNode::Kind_Para); + ((DocPara *)m_children.last())->markLast(FALSE); + } + par->markLast(); + m_children.append(par); + + // parse the contents of the paragraph + retval = par->parse(); + if (retval == 0) break; + if (retval == RetVal_CloseXml) + { + retval = RetVal_OK; + break; + } + } + + DBG(("DocSimpleSect::parseXml() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + +void DocSimpleSect::appendLinkWord(const QString &word) +{ + DocPara *p; + if (m_children.isEmpty() || m_children.last()->kind()!=DocNode::Kind_Para) + { + p = new DocPara(this); + m_children.append(p); + } + else + { + p = (DocPara *)m_children.last(); + + // Comma-seperate <seealso> links. + p->injectToken(TK_WORD,","); + p->injectToken(TK_WHITESPACE," "); + } + + g_inSeeBlock=TRUE; + p->injectToken(TK_LNKWORD,word); + g_inSeeBlock=FALSE; +} + //-------------------------------------------------------------------------- int DocParamList::parse(const QString &cmdName) @@ -3390,6 +3546,7 @@ int DocParamList::parse(const QString &cmdName) int retval=RetVal_OK; DBG(("DocParamList::parse() start\n")); g_nodeStack.push(this); + DocPara *par=0; int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) @@ -3425,9 +3582,11 @@ int DocParamList::parse(const QString &cmdName) } ASSERT(tok==TK_WHITESPACE); - retval = m_paragraph->parse(); - m_paragraph->markFirst(); - m_paragraph->markLast(); + par = new DocPara(this); + m_paragraphs.append(par); + retval = par->parse(); + par->markFirst(); + par->markLast(); endparamlist: DBG(("DocParamList::parse() end retval=%d\n",retval)); @@ -3436,9 +3595,76 @@ endparamlist: return retval; } +int DocParamList::parseXml(const QString ¶mName) +{ + int retval=RetVal_OK; + DBG(("DocParamList::parseXml() start\n")); + g_nodeStack.push(this); + + g_token->name = paramName; + if (m_type==DocParamSect::Param) + { + g_hasParamCommand=TRUE; + checkArgumentName(g_token->name,TRUE); + } + else if (m_type==DocParamSect::RetVal) + { + g_hasReturnCommand=TRUE; + checkArgumentName(g_token->name,FALSE); + } + + handleLinkedWord(this,m_params); + + do + { + DocPara *par = new DocPara(this); + retval = par->parse(); + if (par->isEmpty()) // avoid adding an empty paragraph for the whitespace + // after </para> and before </param> + { + delete par; + break; + } + else // append the paragraph to the list + { + if (m_paragraphs.isEmpty()) + { + par->markFirst(); + } + else + { + m_paragraphs.last()->markLast(FALSE); + } + par->markLast(); + m_paragraphs.append(par); + } + + if (retval == 0) break; + + } while (retval==RetVal_CloseXml && + Mappers::htmlTagMapper->map(g_token->name)!=XML_PARAM && + Mappers::htmlTagMapper->map(g_token->name)!=XML_EXCEPTION); + + + if (retval==0) /* premature end of comment block */ + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unterminated param or exception tag"); + } + else + { + retval=RetVal_OK; + } + + + DBG(("DocParamList::parse() end retval=%d\n",retval)); + DocNode *n=g_nodeStack.pop(); + ASSERT(n==this); + return retval; +} + //-------------------------------------------------------------------------- -int DocParamSect::parse(const QString &cmdName,Direction d) +int DocParamSect::parse(const QString &cmdName,bool xmlContext, Direction d) { int retval=RetVal_OK; DBG(("DocParamSect::parse() start\n")); @@ -3457,7 +3683,14 @@ int DocParamSect::parse(const QString &cmdName,Direction d) pl->markLast(); } m_children.append(pl); - retval = pl->parse(cmdName); + if (xmlContext) + { + retval = pl->parseXml(cmdName); + } + else + { + retval = pl->parse(cmdName); + } DBG(("DocParamSect::parse() end retval=%d\n",retval)); DocNode *n=g_nodeStack.pop(); @@ -3467,8 +3700,7 @@ int DocParamSect::parse(const QString &cmdName,Direction d) //-------------------------------------------------------------------------- - -int DocPara::handleSimpleSection(DocSimpleSect::Type t) +int DocPara::handleSimpleSection(DocSimpleSect::Type t, bool xmlContext) { DocSimpleSect *ss=0; if (!m_children.isEmpty() && // previous element @@ -3484,12 +3716,21 @@ int DocPara::handleSimpleSection(DocSimpleSect::Type t) ss=new DocSimpleSect(this,t); m_children.append(ss); } - int rv = ss->parse(t==DocSimpleSect::User); + int rv = RetVal_OK; + if (xmlContext) + { + return ss->parseXml(); + } + else + { + rv = ss->parse(t==DocSimpleSect::User); + } return (rv!=TK_NEWPARA) ? rv : RetVal_OK; } int DocPara::handleParamSection(const QString &cmdName, DocParamSect::Type t, + bool xmlContext=FALSE, int direction=DocParamSect::Unspecified) { DocParamSect *ps=0; @@ -3505,7 +3746,7 @@ int DocPara::handleParamSection(const QString &cmdName, ps=new DocParamSect(this,t); m_children.append(ps); } - int rv=ps->parse(cmdName,(DocParamSect::Direction)direction); + int rv=ps->parse(cmdName,xmlContext,(DocParamSect::Direction)direction); return (rv!=TK_NEWPARA) ? rv : RetVal_OK; } @@ -3777,11 +4018,36 @@ int DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level) return (retval==RetVal_OK) ? TK_NEWPARA : retval; } +// For XML tags whose content is stored in attributes rather than +// contained within the element, we need a way to inject the attribute +// text into the current paragraph. +bool DocPara::injectToken(int tok,const QString &tokText) +{ + g_token->name = tokText; + return defaultHandleToken(this,tok,m_children); +} + +int DocPara::handleStartCode() +{ + int retval = doctokenizerYYlex(); + // search for the first non-whitespace line, index is stored in li + int i=0,li=0,l=g_token->verb.length(); + while (i<l && g_token->verb.at(i)==' ' || g_token->verb.at(i)=='\n') + { + if (g_token->verb.at(i)=='\n') li=i+1; + i++; + } + m_children.append(new DocVerbatim(this,g_context,g_token->verb.mid(li),DocVerbatim::Code,g_isExample,g_exampleName)); + if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: code section ended without end marker"); + doctokenizerYYsetStatePara(); + return retval; +} + int DocPara::handleCommand(const QString &cmdName) { DBG(("handleCommand(%s)\n",cmdName.data())); int retval = RetVal_OK; - switch (CmdMapper::map(cmdName)) + switch (Mappers::cmdMapper->map(cmdName)) { case CMD_UNKNOWN: warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Found unknown command `\\%s'",cmdName.data()); @@ -3910,17 +4176,7 @@ int DocPara::handleCommand(const QString &cmdName) case CMD_STARTCODE: { doctokenizerYYsetStateCode(); - retval = doctokenizerYYlex(); - // search for the first non-whitespace line, index is stored in li - int i=0,li=0,l=g_token->verb.length(); - while (i<l && g_token->verb.at(i)==' ' || g_token->verb.at(i)=='\n') - { - if (g_token->verb.at(i)=='\n') li=i+1; - i++; - } - m_children.append(new DocVerbatim(this,g_context,g_token->verb.mid(li),DocVerbatim::Code,g_isExample,g_exampleName)); - if (retval==0) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: code section ended without end marker"); - doctokenizerYYsetStatePara(); + retval = handleStartCode(); } break; case CMD_HTMLONLY: @@ -3988,7 +4244,7 @@ int DocPara::handleCommand(const QString &cmdName) warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: unexpected command %s",g_token->name.data()); break; case CMD_PARAM: - retval = handleParamSection(cmdName,DocParamSect::Param,g_token->paramDir); + retval = handleParamSection(cmdName,DocParamSect::Param,FALSE,g_token->paramDir); break; case CMD_RETVAL: retval = handleParamSection(cmdName,DocParamSect::RetVal); @@ -4150,12 +4406,31 @@ int DocPara::handleCommand(const QString &cmdName) return retval; } +static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, + const char *attrName, + QString *result) +{ + + HtmlAttribListIterator li(tagHtmlAttribs); + HtmlAttrib *opt; + for (li.toFirst();(opt=li.current());++li) + { + if (opt->name==attrName) + { + *result = opt->value; + return TRUE; + } + } + return FALSE; +} int DocPara::handleHtmlStartTag(const QString &tagName,const HtmlAttribList &tagHtmlAttribs) { DBG(("handleHtmlStartTag(%s,%d)\n",tagName.data(),tagHtmlAttribs.count())); int retval=RetVal_OK; - int tagId = HtmlTagMapper::map(tagName); + int tagId = Mappers::htmlTagMapper->map(tagName); + if (g_token->emptyTag && !(tagId&XML_CmdMask) && tagId!=HTML_UNKNOWN) + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: HTML tags may not use the 'empty tag' XHTML syntax."); switch (tagId) { case HTML_UL: @@ -4186,7 +4461,16 @@ int DocPara::handleHtmlStartTag(const QString &tagName,const HtmlAttribList &tag handleStyleEnter(this,m_children,DocStyleChange::Bold,&g_token->attribs); break; case HTML_CODE: - handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs); + if (g_fileName.right(3)==".cs") + // for C# code we treat <code> as an XML tag + { + doctokenizerYYsetStateXmlCode(); + retval = handleStartCode(); + } + else // normal HTML markup + { + handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs); + } break; case HTML_EMPHASIS: handleStyleEnter(this,m_children,DocStyleChange::Italic,&g_token->attribs); @@ -4309,11 +4593,149 @@ int DocPara::handleHtmlStartTag(const QString &tagName,const HtmlAttribList &tag } } break; + + case XML_SUMMARY: + case XML_REMARKS: + case XML_VALUE: + case XML_PARA: + if (!m_children.isEmpty()) + { + retval = TK_NEWPARA; + } + break; + case XML_EXAMPLE: + case XML_DESCRIPTION: + break; + case XML_C: + handleStyleEnter(this,m_children,DocStyleChange::Code,&g_token->attribs); + break; + case XML_PARAM: + { + QString paramName; + if (findAttribute(tagHtmlAttribs,"name",¶mName)) + { + retval = handleParamSection(paramName,DocParamSect::Param,TRUE); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'name' attribute from <param> tag."); + } + } + break; + case XML_PARAMREF: + { + QString paramName; + if (findAttribute(tagHtmlAttribs,"name",¶mName)) + { + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,TRUE)); + retval=handleStyleArgument(this,m_children,paramName); + m_children.append(new DocStyleChange(this,g_nodeStack.count(),DocStyleChange::Italic,FALSE)); + if (retval!=TK_WORD) m_children.append(new DocWhiteSpace(this," ")); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'name' attribute from <paramref> tag."); + } + } + break; + case XML_EXCEPTION: + { + QString exceptName; + if (findAttribute(tagHtmlAttribs,"cref",&exceptName)) + { + retval = handleParamSection(exceptName,DocParamSect::Exception,TRUE); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'name' attribute from <exception> tag."); + } + } + break; + case XML_ITEM: + if (!insideUL(this) && !insideOL(this)) + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: lonely <item> tag found"); + } + else + { + retval=RetVal_ListItem; + } + break; + case XML_RETURNS: + retval = handleSimpleSection(DocSimpleSect::Return,TRUE); + g_hasReturnCommand=TRUE; + break; + case XML_SEE: + // I'm not sure if <see> is the same as <seealso> or if it + // should you link a member without producing a section. The + // C# specification is extremely vague about this (but what else + // can we expect from Microsoft...) + { + QString cref; + if (findAttribute(tagHtmlAttribs,"cref",&cref)) + { + DocRef *ref = new DocRef(this,cref); + m_children.append(ref); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'cref' attribute from <see> tag."); + } + } + break; + case XML_SEEALSO: + { + QString cref; + if (findAttribute(tagHtmlAttribs,"cref",&cref)) + { + // Look for an existing "see" section + DocSimpleSect *ss=0; + QListIterator<DocNode> cli(m_children); + DocNode *n; + for (cli.toFirst();(n=cli.current());++cli) + { + if (n->kind()==Kind_SimpleSect && ((DocSimpleSect *)n)->type()==DocSimpleSect::See) + { + ss = (DocSimpleSect *)n; + } + } + + if (!ss) // start new section + { + ss=new DocSimpleSect(this,DocSimpleSect::See); + m_children.append(ss); + } + + ss->appendLinkWord(cref); + retval = RetVal_OK; + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Missing 'cref' attribute from <seealso> tag."); + } + } + break; + case XML_LIST: + { + QString type; + DocHtmlList::Type listType = DocHtmlList::Unordered; + if (findAttribute(tagHtmlAttribs,"type",&type) && type=="number") + { + listType=DocHtmlList::Ordered; + } + DocHtmlList *list = new DocHtmlList(this,tagHtmlAttribs,listType); + m_children.append(list); + retval=list->parseXml(); + } + break; + case XML_INCLUDE: + case XML_PERMISSION: + // These tags are defined in .Net but are currently unsupported + break; case HTML_UNKNOWN: - warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported html tag <%s> found", tagName.data()); + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported xml/html tag <%s> found", tagName.data()); m_children.append(new DocWord(this, "<"+tagName+tagHtmlAttribs.toString()+">")); break; - break; default: // we should not get here! ASSERT(0); @@ -4325,7 +4747,7 @@ int DocPara::handleHtmlStartTag(const QString &tagName,const HtmlAttribList &tag int DocPara::handleHtmlEndTag(const QString &tagName) { DBG(("handleHtmlEndTag(%s)\n",tagName.data())); - int tagId = HtmlTagMapper::map(tagName); + int tagId = Mappers::htmlTagMapper->map(tagName); int retval=RetVal_OK; switch (tagId) { @@ -4450,8 +4872,31 @@ int DocPara::handleHtmlEndTag(const QString &tagName) //warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unexpected tag </a> found"); // ignore </a> tag (can be part of <a name=...></a> break; + + case XML_SUMMARY: + case XML_REMARKS: + case XML_PARA: + case XML_VALUE: + case XML_LIST: + case XML_EXAMPLE: + case XML_PARAM: + case XML_RETURNS: + case XML_SEEALSO: + case XML_EXCEPTION: + retval = RetVal_CloseXml; + break; + case XML_C: + handleStyleLeave(this,m_children,DocStyleChange::Code,"c"); + break; + case XML_ITEM: + case XML_INCLUDE: + case XML_PERMISSION: + case XML_DESCRIPTION: + case XML_PARAMREF: + // These tags are defined in .Net but are currently unsupported + break; case HTML_UNKNOWN: - warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported html tag </%s> found", tagName.data()); + warn_doc_error(g_fileName,doctokenizerYYlineno,"Warning: Unsupported xml/html tag </%s> found", tagName.data()); m_children.append(new DocWord(this,"</"+tagName+">")); break; default: @@ -4610,7 +5055,7 @@ reparsetoken: case TK_COMMAND: { // see if we have to start a simple section - int cmd = CmdMapper::map(g_token->name); + int cmd = Mappers::cmdMapper->map(g_token->name); DocNode *n=parent(); while (n && n->kind()!=DocNode::Kind_SimpleSect && @@ -4901,7 +5346,7 @@ void DocText::parse() } break; case TK_COMMAND: - switch (CmdMapper::map(g_token->name)) + switch (Mappers::cmdMapper->map(g_token->name)) { case CMD_BSLASH: m_children.append(new DocSymbol(this,DocSymbol::BSlash)); @@ -5173,8 +5618,8 @@ DocNode *validatingParseDoc(const char *fileName,int startLine, // TODO: These should be called at the end of the program. //doctokenizerYYcleanup(); - //CmdMapper::freeInstance(); - //HtmlTagMapper::freeInstance(); + //Mappers::cmdMapper->freeInstance(); + //Mappers::htmlTagMapper->freeInstance(); return root; } diff --git a/src/docparser.h b/src/docparser.h index e647e84..1a58614 100644 --- a/src/docparser.h +++ b/src/docparser.h @@ -885,6 +885,7 @@ class DocHtmlList : public CompAccept<DocHtmlList>, public DocNode void accept(DocVisitor *v) { CompAccept<DocHtmlList>::accept(this,v); } const HtmlAttribList &attribs() const { return m_attribs; } int parse(); + int parseXml(); private: DocNode * m_parent; @@ -909,6 +910,8 @@ class DocSimpleSect : public CompAccept<DocSimpleSect>, public DocNode void accept(DocVisitor *v); int parse(bool userTitle); int parseRcs(); + int parseXml(); + void appendLinkWord(const QString &word); private: DocNode * m_parent; @@ -930,7 +933,7 @@ class DocParamSect : public CompAccept<DocParamSect>, public DocNode }; DocParamSect(DocNode *parent,Type t) : m_parent(parent), m_type(t) {} - int parse(const QString &cmdName,Direction d); + int parse(const QString &cmdName,bool xmlContext,Direction d); Kind kind() const { return Kind_ParamSect; } Type type() const { return m_type; } DocNode *parent() const { return m_parent; } @@ -962,9 +965,10 @@ class DocPara : public CompAccept<DocPara>, public DocNode int handleCommand(const QString &cmdName); int handleHtmlStartTag(const QString &tagName,const HtmlAttribList &tagHtmlAttribs); int handleHtmlEndTag(const QString &tagName); - int handleSimpleSection(DocSimpleSect::Type t); + int handleSimpleSection(DocSimpleSect::Type t,bool xmlContext=FALSE); int handleXRefItem(); int handleParamSection(const QString &cmdName,DocParamSect::Type t, + bool xmlContext, int direction); void handleIncludeOperator(const QString &cmdName,DocIncOperator::Type t); void handleImage(const QString &cmdName); @@ -973,7 +977,9 @@ class DocPara : public CompAccept<DocPara>, public DocNode void handleLink(const QString &cmdName,bool isJavaLink); void handleRef(const QString &cmdName); void handleSection(const QString &cmdName); + int handleStartCode(); int handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level); + bool injectToken(int tok,const QString &tokText); //int handleLanguageSwitch(); private: @@ -989,11 +995,10 @@ class DocParamList : public DocNode public: DocParamList(DocNode *parent,DocParamSect::Type t,DocParamSect::Direction d) : m_parent(parent) , m_type(t), m_dir(d), m_isFirst(TRUE), m_isLast(TRUE) - { m_paragraph=new DocPara(this); } - virtual ~DocParamList() { delete m_paragraph; } + { m_paragraphs.setAutoDelete(TRUE); } + virtual ~DocParamList() { } Kind kind() const { return Kind_ParamList; } DocNode *parent() const { return m_parent; } - //const QStrList ¶meters() { return m_params; } const QList<DocNode> ¶meters() { return m_params; } DocParamSect::Type type() const { return m_type; } DocParamSect::Direction direction() const { return m_dir; } @@ -1004,15 +1009,17 @@ class DocParamList : public DocNode void accept(DocVisitor *v) { v->visitPre(this); - m_paragraph->accept(v); + QListIterator<DocPara> cli(m_paragraphs); + DocNode *n; + for (cli.toFirst();(n=cli.current());++cli) n->accept(v); v->visitPost(this); } int parse(const QString &cmdName); + int parseXml(const QString ¶mName); private: DocNode * m_parent; - DocPara * m_paragraph; - //QStrList m_params; + QList<DocPara> m_paragraphs; QList<DocNode> m_params; DocParamSect::Type m_type; DocParamSect::Direction m_dir; @@ -1078,6 +1085,7 @@ class DocHtmlListItem : public CompAccept<DocHtmlListItem>, public DocNode DocNode *parent() const { return m_parent; } void accept(DocVisitor *v) { CompAccept<DocHtmlListItem>::accept(this,v); } int parse(); + int parseXml(); private: DocNode * m_parent; diff --git a/src/doctokenizer.h b/src/doctokenizer.h index e7ee442..0959b49 100644 --- a/src/doctokenizer.h +++ b/src/doctokenizer.h @@ -57,7 +57,8 @@ enum Tokens RetVal_TableHCell = 0x1000E, RetVal_EndTable = 0x1000F, RetVal_Internal = 0x10010, - RetVal_SwitchLang = 0x10011 + RetVal_SwitchLang = 0x10011, + RetVal_CloseXml = 0x10012 }; struct TokenInfo @@ -92,6 +93,7 @@ struct TokenInfo // html tag HtmlAttribList attribs; bool endTag; + bool emptyTag; // whitespace QString chars; @@ -124,6 +126,7 @@ void doctokenizerYYsetStatePara(); void doctokenizerYYsetStateTitle(); void doctokenizerYYsetStateTitleAttrValue(); void doctokenizerYYsetStateCode(); +void doctokenizerYYsetStateXmlCode(); void doctokenizerYYsetStateHtmlOnly(); void doctokenizerYYsetStateManOnly(); void doctokenizerYYsetStateLatexOnly(); diff --git a/src/doctokenizer.l b/src/doctokenizer.l index 640c562..b2be204 100644 --- a/src/doctokenizer.l +++ b/src/doctokenizer.l @@ -136,72 +136,6 @@ static int computeIndent(const char *str,int length) return indent; } -/*! converts input string \a opt into a list of Html Attributes. Each - * attribute is a name, value pair. The result is stored in g_token->attribs - */ -static void parseHtmlAttribs(const char *att) -{ - //printf("parseHtmlAttribs(%s)\n",att); - QCString attribs=att; - int len = attribs.length(); - char c; - int i=0,startName,endName,startAttrib,endAttrib; - while (i<len) - { - c=attribs.at(i); - // skip spaces - while (i<len && c==' ') { c=attribs.at(++i); } - startName=i; - // search for end of name - while (i<len && c!=' ' && c!='=') { c=attribs.at(++i); } - endName=i; - HtmlAttrib opt; - opt.name = attribs.mid(startName,endName-startName).lower(); - // skip spaces - while (i<len && c==' ') { c=attribs.at(++i); } - if (attribs.at(i)=='=') // option has value - { - c=attribs.at(++i); - // skip spaces - while (i<len && c==' ') { c=attribs.at(++i); } - if (attribs.at(i)=='\'') // option '...' - { - c=attribs.at(++i); - startAttrib=i; - - // search for matching quote - while (i<len && c!='\'') { c=attribs.at(++i); } - endAttrib=i; - if (i<len) c=attribs.at(++i); - } - else if (attribs.at(i)=='"') // option "..." - { - c=attribs.at(++i); - startAttrib=i; - // search for matching quote - while (i<len && c!='"') { c=attribs.at(++i); } - endAttrib=i; - if (i<len) c=attribs.at(++i); - } - else // value without any quotes - { - startAttrib=i; - // search for separator - while (i<len && c!=' ') { c=attribs.at(++i); } - endAttrib=i; - if (i<len) c=attribs.at(++i); - } - opt.value = attribs.mid(startAttrib,endAttrib-startAttrib); - } - else // start next option - { - } - //printf("=====> Adding option name=<%s> value=<%s>\n", - // opt.name.data(),opt.value.data()); - g_token->attribs.append(&opt); - } -} - //-------------------------------------------------------------------------- static void processSection() @@ -231,27 +165,96 @@ static void processSection() static void handleHtmlTag() { - g_token->name = yytext; + QCString tagText=yytext; g_token->attribs.clear(); + g_token->endTag = FALSE; + g_token->emptyTag = FALSE; + + // Check for end tag int startNamePos=1; - if (g_token->name.at(1)=='/') startNamePos++; - int attSep=0; - while (attSep<yyleng && !isspace(yytext[attSep])) + if (tagText.at(1)=='/') { - attSep++; + g_token->endTag = TRUE; + startNamePos++; } - if (attSep!=yyleng) // tag has one or more options + + // Parse the name portion + int i = startNamePos; + for (i=startNamePos; i < yyleng; i++) { - parseHtmlAttribs(g_token->name.mid(attSep+1,g_token->name.length()-attSep-2)); - g_token->name=g_token->name.mid(startNamePos,attSep-1).lower(); + // Check for valid HTML/XML name chars (including namespaces) + char c = tagText.at(i); + if (!(isalnum(c) || c=='-' || c=='_' || c==':')) break; } - else // tag without options, strip brackets + g_token->name = tagText.mid(startNamePos,i-startNamePos); + + // Parse the attributes. Each attribute is a name, value pair + // The result is stored in g_token->attribs. + int startName,endName,startAttrib,endAttrib; + while (i<yyleng) { - g_token->name=g_token->name.mid(startNamePos,g_token->name.length()-startNamePos-1).lower(); + char c=tagText.at(i); + // skip spaces + while (i<yyleng && c==' ') { c=tagText.at(++i); } + // check for end of the tag + if (c == '>') break; + // Check for XML style "empty" tag. + if (c == '/') + { + g_token->emptyTag = TRUE; + break; + } + startName=i; + // search for end of name + while (i<yyleng && c!=' ' && c!='=') { c=tagText.at(++i); } + endName=i; + HtmlAttrib opt; + opt.name = tagText.mid(startName,endName-startName).lower(); + // skip spaces + while (i<yyleng && c==' ') { c=tagText.at(++i); } + if (tagText.at(i)=='=') // option has value + { + c=tagText.at(++i); + // skip spaces + while (i<yyleng && c==' ') { c=tagText.at(++i); } + if (tagText.at(i)=='\'') // option '...' + { + c=tagText.at(++i); + startAttrib=i; + + // search for matching quote + while (i<yyleng && c!='\'') { c=tagText.at(++i); } + endAttrib=i; + if (i<yyleng) c=tagText.at(++i); + } + else if (tagText.at(i)=='"') // option "..." + { + c=tagText.at(++i); + startAttrib=i; + // search for matching quote + while (i<yyleng && c!='"') { c=tagText.at(++i); } + endAttrib=i; + if (i<yyleng) c=tagText.at(++i); + } + else // value without any quotes + { + startAttrib=i; + // search for separator + while (i<yyleng && c!=' ') { c=tagText.at(++i); } + endAttrib=i; + if (i<yyleng) c=tagText.at(++i); + } + opt.value = tagText.mid(startAttrib,endAttrib-startAttrib); + } + else // start next option + { + } + //printf("=====> Adding option name=<%s> value=<%s>\n", + // opt.name.data(),opt.value.data()); + g_token->attribs.append(&opt); } - g_token->endTag = startNamePos==2; } - + static QString stripEmptyLines(const char *s) { int result=0,p=0; @@ -287,6 +290,7 @@ WS [ \t\r\n] NONWS [^ \t\r\n] BLANK [ \t\r] ID [a-z_A-Z][a-z_A-Z0-9]* +MAILADR [a-z_A-Z0-9.+-]+"@"[a-z_A-Z0-9-]+("."[a-z_A-Z0-9\-]+)+[a-z_A-Z0-9\-]+ OPTSTARS ("//"{BLANK}*)?"*"*{BLANK}* LISTITEM {BLANK}*{OPTSTARS}"-"("#")?{WS} ENDLIST {BLANK}*{OPTSTARS}"."{BLANK}*\n @@ -326,7 +330,7 @@ WORD1 "%"?{CHARWORD}+|"{"|"}"|("\""[^"\n]*"\"") WORD2 "."|","|"("|")"|"["|"]"|":"|";"|"\?" WORD1NQ "%"?{CHARWORDQ}+ WORD2NQ "."|","|"("|")"|"["|"]"|":"|";"|"\?" -HTMLTAG "<"(("/")?){ID}({WS}+{ATTRIB})*{WS}*">" +HTMLTAG "<"(("/")?){ID}({WS}+{ATTRIB})*{WS}*(("/")?)">" HTMLKEYL "strong"|"center"|"table"|"caption"|"small"|"code"|"dfn"|"var"|"img"|"pre"|"sub"|"sup"|"tr"|"td"|"th"|"ol"|"ul"|"li"|"tt"|"kbd"|"em"|"hr"|"dl"|"dt"|"dd"|"br"|"i"|"a"|"b"|"p" HTMLKEYU "STRONG"|"CENTER"|"TABLE"|"CAPTION"|"SMALL"|"CODE"|"DFN"|"VAR"|"IMG"|"PRE"|"SUB"|"SUP"|"TR"|"TD"|"TH"|"OL"|"UL"|"LI"|"TT"|"KBD"|"EM"|"HR"|"DL"|"DT"|"DD"|"BR"|"I"|"A"|"B"|"P" HTMLKEYW {HTMLKEYL}|{HTMLKEYU} @@ -344,6 +348,7 @@ REFWORD ("#"|"::")?({ID}("."|"#"|"::"|"-"))*({ID}(":")?){FUNCARG}? %x St_TitleA %x St_TitleV %x St_Code +%x St_XmlCode %x St_HtmlOnly %x St_ManOnly %x St_LatexOnly @@ -445,7 +450,7 @@ REFWORD ("#"|"::")?({ID}("."|"#"|"::"|"-"))*({ID}(":")?){FUNCARG}? g_token->isEMailAddr=FALSE; return TK_URL; } -<St_Para>[a-z_A-Z0-9.+-]+"@"[a-z_A-Z0-9-]+("."[a-z_A-Z0-9\-]+)+[a-z_A-Z0-9\-]+ { // Mail address +<St_Para>{MAILADR} { // Mail address g_token->name=yytext; g_token->isEMailAddr=TRUE; return TK_URL; @@ -539,9 +544,12 @@ REFWORD ("#"|"::")?({ID}("."|"#"|"::"|"-"))*({ID}(":")?){FUNCARG}? <St_Code>{WS}*{CMD}"endcode" { return RetVal_OK; } -<St_Code>[^\\@\n]+ | -<St_Code>\n | -<St_Code>. { +<St_XmlCode>{WS}*"</code>" { + return RetVal_OK; + } +<St_Code,St_XmlCode>[^\\@\n]+ | +<St_Code,St_XmlCode>\n | +<St_Code,St_XmlCode>. { g_token->verb+=yytext; } <St_HtmlOnly>{CMD}"endhtmlonly" { @@ -942,6 +950,12 @@ void doctokenizerYYsetStateCode() BEGIN(St_Code); } +void doctokenizerYYsetStateXmlCode() +{ + g_token->verb=""; + BEGIN(St_XmlCode); +} + void doctokenizerYYsetStateHtmlOnly() { g_token->verb=""; diff --git a/src/dot.cpp b/src/dot.cpp index 2adeeb6..7a1ec72 100644 --- a/src/dot.cpp +++ b/src/dot.cpp @@ -24,7 +24,6 @@ #include "util.h" #include "config.h" #include "language.h" -#include "scanner.h" #include "defargs.h" #include "docparser.h" #include "debug.h" diff --git a/src/doxygen.cpp b/src/doxygen.cpp index 2a073bf..d2b4303 100644 --- a/src/doxygen.cpp +++ b/src/doxygen.cpp @@ -36,7 +36,6 @@ #include "logos.h" #include "instdox.h" #include "message.h" -#include "code.h" #include "config.h" #include "util.h" #include "pre.h" @@ -67,6 +66,7 @@ #include "searchindex.h" #include "parserintf.h" #include "htags.h" +#include "pyscanner.h" #if defined(_MSC_VER) || defined(__BORLANDC__) #define popen _popen @@ -125,6 +125,7 @@ QDict<int> * Doxygen::htmlDirMap = 0; QCache<LookupInfo> Doxygen::lookupCache(20000,20000); DirSDict Doxygen::directories(17); SDict<DirRelation> Doxygen::dirRelations(257); +ParserManager *Doxygen::parserManager = 0; static StringList inputFiles; static StringDict excludeNameDict(1009); // sections @@ -7310,60 +7311,8 @@ static void copyStyleSheet() } } -#if 0 -static void readFiles(const QCString &tmpFile) -{ - QFile outFile(tmpFile); - if (outFile.open(IO_WriteOnly)) - { - QTextStream out(&outFile); - QCString *s=inputFiles.first(); - while (s) - { - QCString fileName=*s; - - //bool multiLineIsBrief = Config_getBool("MULTILINE_CPP_IS_BRIEF"); - - out << (char)6; - out << fileName; - out << (char)6; - out << '\n'; - QFileInfo fi(fileName); - BufStr preBuf(fi.size()+4096); - BufStr *bufPtr = &preBuf; - - if (Config_getBool("ENABLE_PREPROCESSING")) - { - msg("Preprocessing %s...\n",s->data()); - preprocessFile(fileName,*bufPtr); - } - else - { - msg("Reading %s...\n",s->data()); - copyAndFilterFile(fileName,*bufPtr); - } - - bufPtr->addChar('\n'); /* to prevent problems under Windows ? */ - - BufStr convBuf(bufPtr->curPos()+1024); - - convertCppComments(&preBuf,&convBuf,fileName); - - out.writeRawBytes(convBuf.data(),convBuf.curPos()); - - s=inputFiles.next(); - //printf("-------> 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<QCString> StringList; typedef QDict<FileDef> FileDict; @@ -116,6 +117,7 @@ class Doxygen static QCache<LookupInfo> lookupCache; static DirSDict directories; static SDict<DirRelation> 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 <qstack.h> +#include <qcstring.h> 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<bool> 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 << "</dl>"; } -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 <qstack.h> +#include <qcstring.h> 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<bool> 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 <qfileinfo.h> +#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 <qstack.h> +#include <qcstring.h> 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<bool> 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():"<null>"); + 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 <qdict.h> 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 <stdio.h> + +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 <stdio.h> +#include <qvaluestack.h> + +#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<uint> 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<ClassDef> + { + public: + Scope() : SDict<ClassDef>(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<Scope> 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<Scope> 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<Ctx> 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_yyLineNr<g_inputLines) + { + startCodeLine(); + } + } + else + { + //printf("writeCodeLink(%s,%s,%s,%s)\n",ref,file,anchor,sp); + ol.writeCodeLink(ref,file,anchor,sp); + done=TRUE; + } + } +} + + +static void codifyLines(char *text) +{ + //printf("codifyLines(%d,\"%s\")\n",g_yyLineNr,text); + char *p=text,*sp=p; + char c; + bool done=FALSE; + while (!done) + { + sp=p; + while ((c=*p++) && c!='\n'); + if (c=='\n') + { + g_yyLineNr++; + *(p-1)='\0'; + g_code->codify(sp); + endCodeLine(); + if (g_yyLineNr<g_inputLines) + { + // Re-enable sometime + startCodeLine(); + } + } + else + { + g_code->codify(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():"<null>"); + + 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():"<none>"); + 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 + + +%% + +<Body,Suite,SuiteCaptureIndent>{ + {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 ); + } + +} + +<Body,Suite,BlockWord>{ + {STRINGPREFIX}?({SINGLEQUOTES}|{QUOTES}) { + startFontClass("stringliteral"); + codifyLines(yytext); + endFontClass(); + } + + "#".* { + startFontClass("stringliteral"); + codifyLines(yytext); + endFontClass(); + } + +} + +<LongString>{ + + {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 ); + } + } +} + + +<Body,Suite>{ + "def"{BB} { + startFontClass("keyword"); + codify(yytext); + endFontClass(); + BEGIN( FunctionDec ); + } + + "class"{BB} { + startFontClass("keyword"); + codify(yytext); + endFontClass(); + BEGIN( ClassDec ); + } +} + +<ClassDec>{IDENTIFIER} { + + generateClassOrGlobalLink(*g_code,yytext); + // codify(yytext); + g_curClassName = yytext; + g_curClassBases.clear(); + BEGIN( ClassInheritance ); + } + +<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("<code>",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 ); + } +} + + +<FunctionDec>{ + {IDENTIFIER} { + generateFunctionLink(*g_code,yytext); + } + + {B}"(" { + codify(yytext); + BEGIN( FunctionParams ); + } +} + +<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 ); + } +} + +<Body,Suite>("if"|"while"|"for"|"else"|"elif") { + startFontClass("keywordflow"); + codify(yytext); + endFontClass(); + // printf("Entering Blockword on '%s' [%d]\n", yytext, g_yyLineNr); + + } + +<Body,Suite,BlockWord>{ + + {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(); + } +} + +<BlockWord>{ + + ":" { + 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); + } + +} + +<SuiteStart>{ + + {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 ); + } + } +} + +<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 ); + } +} + +<SuiteMaintain>{ + + {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); + } +} + + +<Suite>{NEWLINE} { + codifyLines(yytext); + BEGIN( SuiteMaintain ); + } + +<Body,Suite>({NONEMPTY}+|{EXPCHAR}+|{BB}) { + codify(yytext); + } + +<Body>{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 <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> + +#include "qtbc.h" +#include <qarray.h> +#include <qstack.h> +#include <qregexp.h> +#include <unistd.h> +#include <qfile.h> + +#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<int> 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<Grouping> 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 -------------- */ + +<FunctionDec>{ + + {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 ); + } +} + +<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. + } + +} + +<Body>{ + + "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; + } +} + +<MultiDoubleComment>{ + {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; + } +} + +<SpecialComment>{ + ^{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 -------------- */ + +<ClassDec>{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); + } + +<ClassInheritance>{ + ({BB}|[(,)]) { + } + + ":" { + //BEGIN(TryClassDocString); + BEGIN(ClassCaptureIndent); + } + + + {IDENTIFIER} { + current->extends->append( + new BaseInfo(yytext,Public,Normal) + ); + //Has base class-do stuff + } +} + + +<ClassCaptureIndent>{ + "\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 <qfileinfo.h> +#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 <qstack.h> +#include <qcstring.h> 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<bool> 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); + } + } } <FindMembers,FindFields,ReadInitializer>"//"([!/]?){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 <qfileinfo.h> +#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 << "<programlisting>"; - 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 << "</programlisting>"; break; case DocVerbatim::Verbatim: @@ -219,13 +221,21 @@ void XmlDocVisitor::visit(DocInclude *inc) m_t << "<programlisting>"; 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 << "</programlisting>"; } break; case DocInclude::Include: m_t << "<programlisting>"; - 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 << "</programlisting>"; 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 <qstack.h> +#include <qcstring.h> 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<bool> 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 <qdir.h> #include <qfile.h> @@ -222,7 +222,7 @@ template<class T> 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 << "\""; |