+-----------------------+ |
| Pike autodoc inlining | |
+-----------------------+ |
|
The autodoc extractor works either in C mode or in Pike mode. The |
reason why the two modes are not the same is that they perform two |
very different tasks. |
|
The C mode only has to find comments in the file, and ignores the |
surrounding code totally. |
|
The Pike mode, on the other hand, is supposed to be smarter and |
distill a lot of information from the Pike code that surrounds the |
comments. |
|
Both work at the file level. That makes it easy to use for example |
"make" to generate documentation for the source tree. Another benefit |
is that the generation will not have to reparse all of the tree if |
only one source file is changed. |
|
For Pike module trees, the extractor can recurse through the file tree |
on its own, but for C files, where the directory structure gives |
insufficient cues about what is what, there must be make targets set |
up manually. All generated XML files can then be merged together into |
the final Pike namespace. |
|
====================================================================== |
a) C files |
---------------------------------------------------------------------- |
|
In C files, the doc comments look like: |
|
/*! yadda yadda |
*! yadda yadda yadda |
*/ |
|
Note that only lines that start with *! count, so above are only two |
doc lines. Any whitespace before the leading *! is skipped, so that |
the lines can be indented freely. |
|
In the C files, no parsing of the surrounding code is done. The |
context lies completely in the doc comments themselves, and the target |
of the doc comment is determined by special meta keywords that are not |
really part of the doc blocks, but rather modifiers that tell which |
Pike entity the doc is about. |
|
/*! @module Foo |
*! ... doc for the Foo module ... |
*! ... */ |
|
/*! @decl int a() |
*! ... doc for the method Foo.a() ... |
*! .... */ |
|
/*! @class Bar |
*! ... doc for the class Foo.Bar ... |
*! ... */ |
|
/*! @decl mapping(string:string) userprefs() |
*! ... doc for the method Foo.Bar->userprefs() ... |
*! ... */ |
|
/*! @decl int a |
*! @decl int b |
*! ... doc for the variables Foo.Bar->a and Foo.Bar->b ... |
*! ... */ |
|
/*! @endclass */ |
|
/*! @endmodule */ |
|
The @module and @class too keywords are to work like segment |
directives in assembler source files. That is, you can have "@module |
foo" in several C files, if the module source is spread over multiple |
files. However, if you write doc for the module itself in several |
places, an error will be triggered. |
|
|
====================================================================== |
b) Pike files |
---------------------------------------------------------------------- |
|
Doc comments look like: |
|
//! yadda yadda yadda |
//! yadda yadda |
|
To be considered one doc block, the comments must be separated only by |
whitespace and _one_ "\n", that is they have to be on adjacent lines |
in the code. Each doc block in the Pike code has one or more targets; |
the Pike entities (modules, classes, variables etc.) that the doc |
block is documenting. The target of a doc comment is the coherent |
block of declarations adjacent to (immediately before or after) it in |
the code, without intervening blank lines. Examples: |
|
//! Doc for alpha |
int alpha() |
{ |
return 4711; |
} |
|
static int undocumented; |
|
//! Error! This doc block has no destination! |
|
int beta; |
//! Doc for beta |
|
//! Doc for gamma, delta, and epsilon |
int gamma, delta; |
float epsilon; |
|
//! Error here! |
int zeta; |
//! ambiguous which doc to associate with zeta. |
|
int eta; |
//! Error here too! ambiguous which variable is documented. |
int theta; |
|
//! Doc for two methods. This is so UGLY! We strongly recommend |
//! using the decl keywords instead to accomplish this effect. |
int methodOne() |
{ |
... |
} |
int methodTwo() |
{ |
... |
} |
|
//! However, it can be useful sometimes, for really short methods: |
int very_short() { return 4711; } |
int even_shorter() { return 0; } |
|
In Pike files, you can not use @class or @module to tell which module |
or class you are in. To document a class, you simply write: |
|
//! Doc for the class |
class CheeseMaker |
{ |
//! You can even document inherits! |
inherit Earth : earth; |
|
//! Doc for CheeseMaker->a() |
int a() |
{ |
... |
} |
|
void create(string s) { ... } |
} |
|
The parser will automatically identify a() as a member method of the |
class CheeseMaker, and will detect that Earth is inherited by |
CheeseMaker. If a class has no documentation comment, it's internals |
will not be examined, thus it is an error if a class contains |
documentation comments but is itself undocumented: |
|
class a() |
{ |
//! @decl foo |
//! ... doc for foo ... |
} |
|
A special inlining case is that of functions and classes. When documenting |
these, the doc comment can be put between the head of the function/class, |
and the opening "{", like this: |
|
class Roy |
//! Documentation for Roy |
{ |
.... |
} |
|
int un_randomize(int x) |
//! This function takes a random number, and transforms it into |
//! a predictable number. |
{ |
return x = 4711; |
} |
|
If a doc block is the first in a file, and it has no target, then it |
is treated as doc for the file (or rather: the module/class that the |
file compiles into) itself. In any other case it is an error to have a |
targetless doc block. A target can also be set with the @decl meta |
keyword. If a doc comment begins with some @decl keywords, these |
@decl's act just like real declarations standing next to the doc. |
Thus: |
|
//! @decl int a(int x) |
//! @decl int b(int x) |
//! Here is some doc for these functions.... |
|
is autodocwise equivalent to: |
|
//! Here is some doc for these functions.... |
int a(int x) |
{ |
..... |
} |
int b(int x) |
{ |
..... |
} |
|
In _one_ case it is legal to have both an adjacent declaration and |
the @decl keyword at the block beginning. That is when you document |
"polymorph" methods. Then the adjacent declaration must be a method, |
and all @decl's must be methods that have the same name as the real |
method: |
|
//! @decl float cube(float x) |
//! @decl int cube(int x) |
//! Gives x**3. |
//! @param x |
//! The number to cube. |
int|float cube(int|float x) |
{ |
.... |
} |
|
The real method prototype is discarded in favour to the @decl'ed |
variants, who will be shown in the documentation instead. |
|
One problem that is unsolved so far is how to handle #if .. #else .. |
#endif constructions. The approach so far has been to ignore |
preprocessor directives totally. For example, the parser does not |
handle: |
|
#ifdef MALE |
int bertil() |
#else |
int berit() |
#endif |
{ |
... body ... |
} |
|
It a portion of the code is unextractable because it contains too much |
preprocessor macros and stuff, you can make the extractor skip it by using |
@ignore: |
|
//! @ignore |
|
HERE_ARE_SOME_STRANGE_THINGS |
#ifdef A |
A |
#endif |
|
//! @endignore |
|
All @ignore-@endignore sections of the file are removed before any extraction |
is done, so they can cross class boundaries and the like. You can nest @ignore |
inside eachother. Another application for @ignore is to hide actual class |
boundaries from the extractor: |
|
//! @ignore |
class C { |
//! @endignore |
|
//! To the parser, this function appears to be on the top level |
int f() { ... } |
|
//! @ignore |
} |
//! @endignore |
|
|