Abstract
A literate programming tool combines code and documentation within a single document. Up to now these tools provided a very inflexible layout based on a specific documentation language. WebWeb gives control over the layout and is a generic approach to documentation language independence. It is based on the features offered by nuweb and implemented in C++ making strong use of the STL.
This is ver 2.6 of WebWeb. It now offers default formatting strings not only for HTML but also for LaTeX and provides a more flexible mechanism for overwriting this default formatting strings.
In addition to HTML CSS is now enabled to provide a very comfortable way of changing the basic layout.
@d, @o, @h, @c
@{
@<, @>, @$
@}, @+
STYLE
-file
weave
and tangle
, which are run on the web file.
tangle
aimed at producing a complete Pascal program,
ready for compilation. weave
was responsible for the generation of the documentation file which
then could be used as input to TeX.
@
(at-sign).
Therefore, a file without at-signs will be copied
unchanged. WebWeb commands are used to specify output files,
define macros, and delimit scraps. These are the basic
features of interest to the WebWeb tool - the remaining simply consists of text to be
copied to the documentation file.tag
.
@o
file-name flags scrap :
@d
macro-name scrap :
@h
macro-name scrap :
@d
but if invoked in a scrap, it will be only
expanded in the output file and not in the documentation. So scraps can be excluded
from the documentation. If invoked in normal text of a documentation section,
it will be expanded to the documentation. The macro definition itself will never be
documented. So it fulfils two tasks: First, it allows to hide part of the code
from the documentation and second, it represents the macro mechanism for text
in the documentation.
@c
macro-name scrap :
@{
anything @}
:
@<
macro-name @>
-l
:
#line
directives in the
output file. These are useful with C (and sometimes C++ and Fortran)
since they cause the compiler's error messages to
refer to the web file rather than the output file. Similarly, they
allow source debugging in terms of the web file.
-i
:
@@
@l language
@i filename
@f
@o
.
@m
@u
@+
.
it is alphabetically sorted.
@| text @|
text
will be displayed in the same font as the scraps.
@<scrapname@>
@h
.
@@
@<scrapname@>
@$ text @$
text
will be displayed in the same font as the documentation sections.
@+ string
string
in the user defined index. Must be followed by another
@+
or by @}
at the end of a scrap. @}
webweb
flags file-name
The file name must have the extension .ww
.html
.
For every included 'default' language the right extension is set.
The name of the documentation file can also be given as an option.-d
-o
-f
webweb -d -o ernie.ww
would simply scan the input and produce no output at all.-i
-N
doc-file-name
webweb [-h] [-help] [-i] [-f] [-d] [-o] [-s]
[-N docfileName] filename
iterator::operator++(),
iterator::operator==()
and iterator::operator*()
define the behavior
of the iterator. These iterators can not only be used with all container but also
with arrays and strings.wwguide.html
.
Therefore, I needed a kind of hidden scrap that had to be implemented.
Until the implementation was carried out, the user's guide was defined with
@d
as a 'normal' scrap and therefore appeared in the file webweb.html
.
After the creation of the hidden scrap I simply had to replace the
@d
with @h
. Now the users's guide was invisible in the
documentation of WebWeb but the file wwguide.html
was still created.The source is read and each chunk of text is stored in a STL-Container. In this first pass, all information needed for making the cross-references, the output-files etc, will also be stored in appropriate STL-containers. This gives the overall design of the program:
#include "webweb.h" <functions and routines:
11, 32, 33, 52, 64, 65, 66, 69> <read the input files:
18> <make the files:
51> <the main function:
9>
We maintain a header file. All global declarations, typedefs and preprocessor directives are put there.
<preprocessor directives:
96> <some typedefs:
3> <global declarations:
4, 5, 6, 7, 12, 15, 16, 19, 21, 26, 29, 30, 37, 39, 44, 53, 55, 63, 71, 73> <hidden declarations:
27, 34, 60>
typedef list< int > text_t; typedef map< string, text_t > map_t; typedef vector< string > buf_t; typedef map< string, string > opt_t; typedef vector< int > line_t; enum doc_t { NONE, HTML, LATEX, WfW }; doc_t docLang = NONE;
Scrap referenced in: 2
For each kind of scrap we maintain a STL-container. This seems adequate as we shall see when examine the creation of the documentation and the output files. A fourth container is used for the user-made index.
map_t scrap; map_t files; map_t hide; map_t merged;
Also defined in: , 5 ...
Scrap referenced in: 2
.txt
string begSStrLATEX = "{\\vspace*{-0.5ex} \\begin{flushleft}"; string endSStrLATEX = "\\end{flushleft} }"; //string headerStringLATEX = "{\\bf $\\langle$ scrapname, \\textit{scrapnumber} $\\rangle$ }"; string headerStringLATEX = "{{\\bf \\em scrapname}, \\textit{scrapnumber}\\\\[-0.5em] \\rule{7cm}{0.5pt}\\\\}"; string defStringLATEX = "{\\small \\it Also defined in: list of numbers }\\\\"; string refStringLATEX = "{\\small \\it Referenced in: list of numbers }\\\\"; string numStringLATEX = " scrapnumber"; string numSeparatorLATEX = ","; //string bodyStartStringLATEX = "\\vspace*{-2ex} \\begin{quote}"; //string bodyStartStringLATEX = "\\vspace*{-2ex} {\\small"; string bodyStartStringLATEX = "{\\footnotesize"; //string bodyEndStringLATEX = "\\end{quote} \\vspace*{-6ex} $\\diamond$ \\newline"; //string bodyEndStringLATEX = "} \\vspace*{-2ex} $\\diamond$ \\vspace*{2ex} \\newline"; string bodyEndStringLATEX = "} \\vspace*{-3ex} \\newline \\rule{7cm}{0.5pt}\\\\"; //string bodyEndStringLATEX = "} \\vspace*{-2ex} \\newline"; string macroCallNumLATEX = "} : {\\small list of numbers }"; string startMacroCallLATEX = "@ $\\langle$ {\\bf "; string endMacroCallLATEX = "$\\rangle$ \\verb@"; string toDocmodeStringLATEX = "@"; string toScrapmodeStringLATEX = "\\verb@"; string listStartFormatLATEX = "\\begin{description}"; string listEndFormatLATEX = "\\vspace*{2ex} \\end{description}"; string listEntryFormatLATEX = "\\item[scrapname: ] list of numbers \\vspace{-2ex}"; string ltLATEX = "$\\langle$"; string gtLATEX = "$\\rangle$";
Scrap referenced in: 2
Now, we list the appropriate settings used to format a documentation that is written in HTML. Note, that we make use of the comfortable Casscading Style Sheets.
string begSStrHTML = "<P CLASS=\"clsScrpStrt\"> </P>"; string endSStrHTML = "<P CLASS=\"clsScrpEnd\"> </P>"; string headerStringHTML = "<H4 CLASS=\"clsScrpHd\"><A CLASS=\"clsScrpHdA\" NAME=\"scrap scrapnumber\">« scrapname » scrapnumber</A></H4>"; string defStringHTML = "<P CLASS=\"clsScrpDf\">Also defined in: list of numbers </P>"; string refStringHTML = "<P CLASS=\"clsScrpRf\">Scrap referenced in: list of numbers </P>"; string numStringHTML = " <A CLASS=\"clsScrpNumHrf\" HREF=\"#scrap scrapnumber\">scrapnumber</A>"; string numSeparatorHTML = ","; string bodyStartStringHTML = ""; string bodyEndStringHTML = "</PRE><BR>"; string toScrapmodeStringHTML = "<PRE CLASS=\"clsScrpMd\">"; string toDocmodeStringHTML = "</PRE COLOR=\"Black\">"; string macroCallNumHTML = ":</CODE>list of numbers"; string startMacroCallHTML = "<<CODE CLASS=\"clsMcrCll\">"; string endMacroCallHTML = ">"; string listStartFormatHTML = "<TABLE CLASS=\"clsTbl\">"; string listEndFormatHTML = "</TABLE>"; string listEntryFormatHTML = "<TR><TD CLASS=\"clsTblNm\">scrapname : <TD CLASS=\"clsTblEntry\">list of numbers"; string ltHTML = "<"; string gtHTML = ">";
Scrap referenced in: 2
If these settings are not overwritten (which is indicated by the boolean variable
default_setting
)
in the web file by the tags @sc
,
(with c a character as listed in the user's guide)
they are mapped
to the current formatstrings i.e.
we work with these strings when generating the documentation.
string metaName = "scrapname"; string metaNumber = "scrapnumber"; string metaList = "list of numbers"; string lt = "<"; string gt = ">"; string empty_str =""; string* begSString; string* endSString; string* headerString; string* defString; string* refString; string* numString; string* numSeparator; string* bodyStartString; string* bodyEndString; string* macroCallNum; string* startMacroCall; string* endMacroCall; string* toDocmodeString; string* toScrapmodeString; string* listStartFormat; string* listEndFormat; string* listEntryFormat; bool default_setting = 1;
Also defined in: ... 6, 12 ...
Scrap referenced in: 2
It goes without saying that the right choice for the default settings depends on the 'default' documentation language that is used.
void init_formatting_strings() { if (wwdebug) wwdb("init_formatting_strings"); switch ( docLang ) { case HTML: { if ( default_name ) docfile = basename + ".html"; if ( ! begSString) begSString = &begSStrHTML; if ( ! endSString) endSString = &endSStrHTML; if ( ! headerString) headerString = &headerStringHTML; if ( ! defString ) defString = &defStringHTML; if ( ! refString ) refString = &refStringHTML; if ( ! numString ) numString = &numStringHTML; if ( ! numSeparator ) numSeparator = &numSeparatorHTML; if ( ! bodyStartString ) bodyStartString = &bodyStartStringHTML; if ( ! bodyEndString ) bodyEndString = &bodyEndStringHTML; if ( ! toDocmodeString ) toDocmodeString = &toDocmodeStringHTML; if ( ! toScrapmodeString ) toScrapmodeString = &toScrapmodeStringHTML; if ( ! macroCallNum ) macroCallNum = ¯oCallNumHTML; if ( ! startMacroCall ) startMacroCall = &startMacroCallHTML; if ( ! endMacroCall ) endMacroCall = &endMacroCallHTML; if ( ! listStartFormat ) listStartFormat = &listStartFormatHTML; if ( ! listEndFormat ) listEndFormat = &listEndFormatHTML; if ( ! listEntryFormat ) listEntryFormat = &listEntryFormatHTML; if ( ltHTML != lt && ltHTML!= "" ) lt = ltHTML; if ( gtHTML != gt && gtHTML!= "" ) gt = gtHTML; break; } case LATEX : { if ( default_name ) docfile = basename + ".tex"; if ( ! begSString) begSString = &begSStrLATEX; if ( ! endSString) endSString = &endSStrLATEX; if ( ! headerString) headerString = &headerStringLATEX; if ( ! defString ) defString = &defStringLATEX; if ( ! refString ) refString = &refStringLATEX; if ( ! numString ) numString = &numStringLATEX; if ( ! numSeparator ) numSeparator = &numSeparatorLATEX; if ( ! bodyStartString ) bodyStartString = &bodyStartStringLATEX; if ( ! bodyEndString ) bodyEndString = &bodyEndStringLATEX; if ( ! toDocmodeString ) toDocmodeString = &toDocmodeStringLATEX; if ( ! toScrapmodeString ) toScrapmodeString = &toScrapmodeStringLATEX; if ( ! macroCallNum ) macroCallNum = ¯oCallNumLATEX; if ( ! startMacroCall ) startMacroCall = &startMacroCallLATEX; if ( ! endMacroCall ) endMacroCall = &endMacroCallLATEX; if ( ! listStartFormat ) listStartFormat = &listStartFormatLATEX; if ( ! listEndFormat ) listEndFormat = &listEndFormatLATEX; if ( ! listEntryFormat ) listEntryFormat = &listEntryFormatLATEX; if ( ltLATEX != lt && ltLATEX!= "" ) lt = ltLATEX; if ( gtLATEX != gt && gtLATEX!= "" ) gt = gtLATEX; break; } case NONE: default: { docfile = basename + ".txt"; if ( ! begSString) begSString = &empty_str; if ( ! endSString) endSString = &empty_str; if ( ! headerString) headerString = &empty_str; if ( ! defString ) defString = &empty_str; if ( ! refString ) refString = &empty_str; if ( ! numString ) numString = &empty_str; if ( ! numSeparator ) numSeparator = &empty_str; if ( ! bodyStartString ) bodyStartString = &empty_str; if ( ! bodyEndString ) bodyEndString = &empty_str; if ( ! toDocmodeString ) toDocmodeString = &empty_str; if ( ! toScrapmodeString ) toScrapmodeString = &empty_str; if ( ! macroCallNum ) macroCallNum = &empty_str; if ( ! startMacroCall ) startMacroCall = &empty_str; if ( ! endMacroCall ) endMacroCall = &empty_str; if ( ! listStartFormat ) listStartFormat = &empty_str; if ( ! listEndFormat ) listEndFormat = &empty_str; if ( ! listEntryFormat ) listEntryFormat = &empty_str; break; } } if ( ! begSStrU.empty() ) begSString = &begSStrU; if ( ! endSStrU.empty() ) endSString = &endSStrU; if ( ! headStrU.empty() ) headerString = &headStrU; if ( ! defStrU.empty() ) defString = &defStrU; if ( ! refStrU.empty() ) refString = &refStrU; if ( ! numStrU.empty() ) numString = &numStrU; if ( ! numSepU.empty() ) numSeparator = &numSepU; if ( ! begBStrU.empty() ) bodyStartString = &begBStrU; if ( ! endBStrU.empty() ) bodyEndString = &endBStrU; if ( ! macroNumStrU.empty() ) macroCallNum = ¯oNumStrU; if ( ! begMStrU.empty() ) startMacroCall = &begMStrU; if ( ! endMStrU.empty() ) endMacroCall = &endMStrU; if ( ! begLStrU.empty() ) listStartFormat = &begLStrU; if ( ! endLStrU.empty() ) listEndFormat = &endLStrU; if ( ! listStrU.empty() ) listEntryFormat = &listStrU; }
Scrap referenced in: 69
main()
function
which makes the first and only pass over the web file itself.
Then, we will examine how the generation of the output files is done
and finally look at how the documentation is produced.
basename
to the basename of the web file.
Then, we evaluate the given command options.
After that, we call three routines named according to their basic tasks:read_source_file()
make_doc()
make_files()
int main(int argc, char * argv[]) {
<evaluate arguments:
10, 13, 14, 17>
read_source_file(argv[si]);
if (mkdoc)
make_doc();
if (mkout)
make_files();
return 0;
}
Scrap referenced in: 1
We assume that the name of the source file is the last given argument. So, at least one argument must be found. If not, WebWeb will exit sending an error message.
if (argc == 1) error("Usage: " + string(argv[0]) + " file", 0);
Also defined in: , 13 ...
Scrap referenced in: 9
error()
taking a string as argument. It copies the string to cerr
and exits with code 1.cout
.
void error(const string& s1, const int& part_no) { if ( part_no == 0 ) { cerr << s1; exit(1); } if ( lines.empty() ) compute_line_numbers(); cerr << "(" << actual_line_number() << "): " << s1 << endl; exit(1); } void warning(const string& s1, const int& part_no) { if ( part_no == 0 ) cerr << "WARNING: " << s1 << endl; else { if ( lines.empty() ) compute_line_numbers() ; cerr << "WARNING (" << actual_line_number() << "): " << s1 << endl; } } void wwdb(const string& s, string s2 = "") { cerr << "Funktion: \"" << s << s2 << "\"" << endl; }
Also defined in: , 32 ...
Scrap referenced in: 1
The name of the given file name is stored in the variable
source
which is of type string
.
If the source file name has the extension .ww
, the basename
will be stored in basename
. Both are defined in the header file.
string basename; string source;
Also defined in: ... 7, 15 ...
Scrap referenced in: 2
As most software does we provide an option to print out a commented list of the command line options.
version = "This is WebWeb, "; helpstr = version; helpstr += "\n \nThe command line options are:\n"; helpstr += " [-h] [-help] This text.\n"; helpstr += " [-d] Generate NO documentation but extract the files.\n"; helpstr += " [-o] Generate NO output files but make the documentation.\n"; helpstr += " [-i] For each file to create, check whether it already\n"; helpstr += " exists and if so, ask the user if WebWeb should\n"; helpstr += " overwrite it. (interactive)\n"; helpstr += " [-f] Force the overwriting of already existing files.\n"; helpstr += " [-s] Do not print the version when starting WebWeb. (silent)\n"; helpstr += " [-N fname] Write the documentation to file \'fname'\n";
Also defined in: ... 10, 14 ...
Scrap referenced in: 9
The basename is needed to construct the name of the documentation file. If no source file is given, WebWeb exits with an error message.
int si = argc - 1; source = argv[si]; if ( source == "-h" ) { cout << helpstr << endl; exit(1); } if ( source == "-help" ) { cout << helpstr << endl; exit(1); } size_t ppos = source.find('.'); if ( ppos == string::npos ) { basename = source; source = source + ".ww"; } else { if ( source.substr(ppos) != ".ww" ) error(" Wrong extension to filename. Must be <" + source + ".ww>", 0); basename = source.substr(0, ppos); }
Also defined in: ... 13, 17 ...
Scrap referenced in: 9
Then, we evaluate the arguments given after the command name. Therefore, we loop over the given options and set appropriate variables which are mostly of type
bool
and set them in the header file
to their default values.
bool wwdebug = 0; bool force = 0; bool interactive = 0; bool mkdoc = 1; bool mkout = 1; bool default_name = 1; bool css = 1; string docfile;
Also defined in: ... 12, 16 ...
Scrap referenced in: 2
If an invalid option is found, WebWeb will terminate and copy the string
usage
to error()
;
string usage; string version; string helpstr;
Also defined in: ... 15, 19 ...
Scrap referenced in: 2
bool silent = 0; string argument; usage = "USAGE: webweb [-h] [-help] [-o] [-d] [-i] [-f] [-s] [-N docfileName] sourcefile"; for ( int counter = 1; counter < si; counter++) { argument = argv[counter]; if ( argument == "-f" ) { force = 1; continue; } if ( argument == "-i" ) { interactive = 1; continue; } if ( argument == "-o" ) { mkout = 0; continue; } if ( argument == "-d" ) { mkdoc = 0; continue; } if ( argument == "-s" ) { silent = 1; continue; } if ( argument == "-debug" ) { wwdebug = 1; continue; } if ( argument == "-N" ) { docfile = argv[++counter]; default_name = 0; continue; } error("Wrong argument: " + argument + "\n" + usage, 0); } if ( ! silent ) cout << version << endl;
Also defined in: ... 14
Scrap referenced in: 9
main()
function
has shown, we read the source by calling the function read_source_file()
.
For this purpose, we make use of the comfortable class ifstream
which is a
realization of the template class basic_ifstream
.
It takes the name of the web file as an argument and tries to open it.
Then, we set the local variables include_file
and
incl_filename
.
We will turn to the meaning of this variables later on.
void read_source_file(const char_t* fileName) {
if (wwdebug) wwdb("read_ource_file");
state = doc;
ifstream is(fileName);
if ( ! is ) warning("Cannot open file " + string(fileName), 0);
// istream::pos_type firstPos = is.tellg();
// is.seekg(-1, ios::end);
// istream::pos_type lastPos = is.tellg();
// is.seekg(0, ios::beg);
// if ( (lastPos - firstPos) >= 10000 )
// bufferSize = (lastPos - firstPos);
// else
// bufferSize = 10000;
//cout << "FIRST: " << firstPos << "LAST: " << lastPos << endl;
bool include_file = 0;
string incl_filename;
if ( is ) {
<read the source:
20>
}
}
Scrap referenced in: 1
string line, line1; int bufferSize;
Also defined in: ... 16, 21 ...
Scrap referenced in: 2
We scan with getline over the web file and set the optional end-of-line character to @ which gives us the partitioning of the source. Then, we take a lookahead on the following character which indicates what to do. We store each read chunk of text in the STL-container buf which is a vector of strings. So, the contents of
buf
represents nearly the whole source,
except for comments and @
-signs, part of a tag, which are not copied.
<work on first line:
22> do { ++part_no; char_t c = tolower(static_cast< char_t >(is.peek())); <include a file:
49> <substitute double at-sign:
23> <skip comments:
24> <set documentation language:
42> <set formatting strings:
45> line = fake + line; fake = ""; buf.push_back(line); ++buf_no; if ( ! comment ) { <analyze the source line:
25> } } while ( getline(is, line, at_sign) );
Scrap referenced in: 18
Most variables are declared global.
typedef string::value_type char_t; const char_t at_sign = '@'; buf_t buf; int part_no = -1; int buf_no = -1; bool comment = 0; string fake;
Also defined in: ... 19, 26 ...
Scrap referenced in: 2
Since we assume that the first at_line of each source file given at the command line or with the
@i
tag belongs to the documentation, we get some
at_lines which do not start with a character special to WebWeb.
In order to simplify the generation of the documentation we want each
at_line to start with a special character.
Consequently, we add a fake
character, namely the z
.
getline(is, line, at_sign); line = 'z' + line;
Scrap referenced in: 20
If the lookahead gives us another @ we will call getline twice and store the read characters in line1:
while ( c == '@' ) { getline(is, line1, at_sign); getline(is, line1, at_sign); line += "@" + line1; c = tolower(static_cast< char_t >(is.peek())); }
Scrap referenced in: 20
To skip the comments we use the boolean variable
comment
.
It is set to 1 when is.peek
holds a c
while
scanning the input line.
if ( comment ) { if ( c == '}' ) comment = 0; continue; }
Scrap referenced in: 20
state
.
switch ( c ) {
<switch the lookahead:
28, 31, 35, 36, 38, 40, 41, 43, 46, 47, 48, 50>
default : {
if ( is.peek() != EOF )
error("Unexpected @. Perhaps you forgot to escape it?", buf_no );
break;
}
}
Scrap referenced in: 20
So we run a finite automaton through the setting of the variable state. This happens in dependence on the lookahead which can be viewed as the input token from the scanner
enum state_t { doc, header, body, macro, scrapmode, docmode, uindex, docdef, fdef }; state_t state = doc;
Also defined in: ... 21, 29 ...
Scrap referenced in: 2
bool isfile = 0; bool hidden = 0;
Also defined in: , 34 ...
Scrap referenced in: 2
@d, @o, @h, @c
case 'd': { if ( state == doc ) { state = header; map_ptr = &scrap; } else error("You cannot start a scrap definition here.", buf_no); break; } case 'o': { if ( state == doc ) { state = header; isfile = 1; map_ptr = &files; } else error("You cannot start a file definition here.", buf_no); break; } case 'h': { if ( state == doc ) { state = header; map_ptr = &hide; hidden = 1; } else error("You cannot start a hidden scrap definition here.", buf_no); break; } case 'c': { comment = 1; break; }
Also defined in: , 31 ...
Scrap referenced in: 25
As we want to store the references to the following at-line in the appropriate container until the end of the scrap is reached, we maintain a pointer to the
map_t
ype. This pointer
is always set to the appropriate container.
map_t* map_ptr;
Also defined in: ... 26, 30 ...
Scrap referenced in: 2
@{
@{
indicates the
end of a header and the beginning of the body. This means, the lookahead
is a }
. We will set the state
to body
and remove all leading and trailing spaces. def_nums
.
scrap_name holds the current scrapname and is valid while processing the whole scrap.
num
simply counts the scraps.
map_t def_nums; string scrap_name; int num = 0; string dL;
Also defined in: ... 29, 37 ...
Scrap referenced in: 2
This will only be the case if the scrap is not of kind
hidden
.
case '{': { if ( state == header ) { state = body; if ( isfile ) { scrap_name = grab_o_name(line); options[scrap_name] += opt; isfile = 0; } else scrap_name = grab_d_name(line); if ( ! hidden ) def_nums[ scrap_name ].push_back(++num); } else error("unexpected @{", buf_no); break; }
Also defined in: ... 28, 35 ...
Scrap referenced in: 25
As we see, two functions are called here:
grab_d_name
and grab_o_name
. Both remove the newlines, spaces,
and tabs at the beginning and at the end of each scrapname using the comfortable
functions of the string library of the STL:
string grab_d_name(const string& line) { if (wwdebug) wwdb("grab_d_name: ", line); s = line.find_first_not_of(" \t\n", 1); e = line.find_last_not_of(" \t\n"); return line.substr(s, e - s + 1); }
Also defined in: ... 11, 33 ...
Scrap referenced in: 1
grab_o_name
has a slightly broader effect.opt
. For every filename we provide
an entry in the container options
which will be evaluated when the
output files are written.
string grab_o_name(const string& line) { if (wwdebug) wwdb("grab_o_name", line); s = line.find_first_not_of(" \t\n", 1); e = line.find_first_of(" \t\n", s); opt = line.substr(e); return line.substr(s, e - s ); }
Also defined in: ... 32, 52 ...
Scrap referenced in: 1
opt_t options; string opt; size_t s; size_t e;
Also defined in: ... 27, 60 ...
Scrap referenced in: 2
@<, @>, @$
docmode
.<
from the lookahead. We then
set the state to macro
and store the current line index in the appropriate container.
case '<': { if ( state == body ) { (*map_ptr)[ scrap_name ].push_back(buf_no); state = macro; } if ( state == header ) error("Do not use macro extension in scrapheader: ", buf_no); break; }
Also defined in: ... 31, 36 ...
Scrap referenced in: 25
In this state the next lookahead must be a '
>
'.
If so, we push back the read line to
the appropriate container.
case '>': { if ( state == macro ) { (*map_ptr)[ scrap_name ].push_back(buf_no); state = body; } else { if ( state == body ) error("First invoke a macro call or escape this @>" , buf_no); } if ( ! hidden ) { macro_name = grab_d_name(line); ref_nums[ macro_name ].push_back(num); } break; }
Also defined in: ... 35, 38 ...
Scrap referenced in: 25
If the scrap is not a hidden scrap we will extract the name of the macro called and store the number of the current scrap in the container
ref_nums
.
map_t ref_nums; string macro_name;
Also defined in: ... 30, 39 ...
Scrap referenced in: 2
@}, @+
case '}': { switch (state) { case body : { (*map_ptr)[ scrap_name ].push_back(buf_no); break; } case uindex : { index[grab_d_name(line)]; break; } default : { error("Unexpected @}", buf_no - 1 ); } } state = doc; hidden = 0; break; }
Also defined in: ... 36, 40 ...
Scrap referenced in: 25
If the current line represents an entry to the user-defined list, we will put its name in the container
index
.
map_t index;
Also defined in: ... 37, 44 ...
Scrap referenced in: 2
Whenever the lookahead is a '+' we have to decide whether the current line has to be pushed to the user-defined index or not. Therefore, we set a new state: uindex.
case '+' : { if ( state == uindex ) { index[grab_d_name(line)]; break; } if ( state == body && ! hidden ) { (*map_ptr)[ scrap_name ].push_back(buf_no); state = uindex; break; } error("Unexpected @+", buf_no ); }
Also defined in: ... 38, 41 ...
Scrap referenced in: 25
@l
. If this is found in the lookahead, we switch to
state docdef
.
case 'l' : { if ( state == doc ) state = docdef; else error("Unexpected @l", buf_no ); break; }
Also defined in: ... 40, 43 ...
Scrap referenced in: 25
Having read the next line the following code will be executed.
docLang
to the appropriate value.
if ( state == docdef ) { state = doc; size_t a = line.find_first_not_of(" \t\n", 1); size_t b = line.find_first_of(" \t\n", a); dL = line.substr(a, b-a); line = line.substr(b); if ( dL == "HTML" || dL == "html" ) docLang = HTML; else { if ( dL == "LaTeX" || dL == "LATEX" || dL == "latex" ) docLang = LATEX; else warning(dL + ": Documentation language not valid \n No special formatting is done", 0) ; } }
Scrap referenced in: 20
If a user wants to specify his own formatting strings he shows this by giving the tag
@s
c where c is a character
as mentioned in the user's guide.
These strings may only be given when the state is set to doc
.fdef
, which means format definition.
case 's' : { if ( state == doc ) { state = fdef; continue; } else error("Unexpected @s", buf_no ); break; }
Also defined in: ... 41, 46 ...
Scrap referenced in: 25
We read the next line which must contain either the name of the desired documentation language or a string that contains formatting information. The strings controlling the formatting are stored in appropriate variables.
string begSStrU; string endSStrU; string headStrU; string defStrU; string refStrU; string numStrU; string numSepU; string begBStrU; string endBStrU; string macroNumStrU; string begMStrU; string endMStrU; string begLStrU; string endLStrU; string listStrU;
Also defined in: ... 39, 53 ...
Scrap referenced in: 2
Depending on the
state
we jump to the following code:
if ( state == fdef ) { if (line[0] == '}') { state = doc; } else { switch (line[0]) { case 's': { break; } case '{': { break; } case '}': { state = doc; break; } case 'S': { switch (line[1]) { case '{': { begSStrU = line.substr(2, (line.size() - 2)); break; } case '}': { endSStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@S" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'H': { switch (line[1]) { case '|': { headStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@H" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'B': { switch (line[1]) { case '{': { begBStrU = line.substr(2, (line.size() - 2)); break; } case '}': { endBStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@B" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'M': { switch (line[1]) { case '{': { begMStrU = line.substr(2, (line.size() - 2)); break; } case '}': { endMStrU = line.substr(2, (line.size() - 2)); break; } case '|': { macroNumStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@M" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'R': { switch (line[1]) { case '|': { refStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@R" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'D': { switch (line[1]) { case '|': { defStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@D" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'N': { switch (line[1]) { case '|': { numStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@N" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'C': { switch (line[1]) { case '|': { numSepU = line.substr(2, (line.size() - 2)); break; } default: { warning("@N" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'L': { switch (line[1]) { case '{': { begLStrU = line.substr(2, (line.size() - 2)); break; } case '}': { endLStrU = line.substr(2, (line.size() - 2)); break; } case '|': { listStrU = line.substr(2, (line.size() - 2)); break; } default: { warning("@L" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'l': { switch (line[1]) { case 't': { lt = line.substr(2, (line.size() - 2)); break; } default: { warning("@l" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } case 'g': { switch (line[1]) { case 't': { gt = line.substr(2, (line.size() - 2)); break; } default: { warning("@g" + line.substr(2) + "Unknown kind of formatting string", buf_no); break; } } break; } default: { warning("@s" + line.substr(1) + "Unknown kind of formatting string", buf_no); break; } } default_setting = 0; continue; } }
Scrap referenced in: 20
case '|' : { if ( state != doc ) error("Unexpected @|", buf_no ); break; }
Also defined in: ... 43, 47 ...
Scrap referenced in: 25
case '$' : { if ( state != body ) error("Unexpected @$", buf_no ); else (*map_ptr)[ scrap_name ].push_back(buf_no); break; }
Also defined in: ... 46, 48 ...
Scrap referenced in: 25
include_file
.
case 'i' : { if ( state == doc ) { include_file = 1; break; } error("Unexpected @i", buf_no ); break; }
Also defined in: ... 47, 50 ...
Scrap referenced in: 25
The other tasks mentioned above must be done after the next line is read.
if ( include_file ) { size_t s = line.find_first_not_of(" \t\n", 1); size_t e = line.find_first_of(" \t\n", s); incl_filename = line.substr(s, e-s); if ( mark.find(incl_filename) != mark.end() ) error(incl_filename + ": recursive call of this file" , buf_no); else mark.insert(incl_filename); string rline = line; read_source_file(incl_filename.c_str()); line = rline; mark.erase(incl_filename); include_file = 0; }
Scrap referenced in: 20
default
switch because there
the fake character is added.
case 'f' : case 'm' : case 'u' : break;
Also defined in: ... 48
Scrap referenced in: 25
make_files()
as already
mentioned. To do so we loop over the container files after we have merged the
content of scrap
and hide
.
void make_files() { if (wwdebug) wwdb("make_files"); //merge 'scrap' and 'hide' to copy ALL scraps into files std::string sname; for (map_t::iterator h_it = hide.begin(); h_it != hide.end(); ++h_it) { sname = h_it->first; if ( scrap.find(h_it->first) == scrap.end() ) { scrap[h_it->first] = h_it->second; } else { scrap[h_it->first].merge(h_it->second); } } for (map_t::iterator f_it = files.begin(); f_it != files.end(); ++f_it) { <evaluate output file options:
61> <prepare the #line directives:
62> mark.clear(); // const int bS = 100000; // char stringBuffer[bS]; // ostringstream os(stringBuffer, bS); ostringstream os; os << f_it->second; write_or_not(os, f_it->first); } }
Scrap referenced in: 1
and write the content of each file to an ostringstream. Therefore, we overload the operator
<<
.
The second operand is of type text_t
that is a list of
integers. These integers must be interpreted as indexes to buf
where all the lines
of the original source are stored. txt
and take for each integer the corresponding
line from buf
. buf
holds the information
about the sort of line.
ostream& operator << (ostream& os, const text_t& txt) {
if (wwdebug) wwdb("operator <<");
string scrap_name;
string blanks;
int ic;
for (text_t::const_iterator it = txt.begin(); it != txt.end(); ++it) {
switch ( buf[*it] [0] ) {
<switch in accordance with the first character:
54, 57>
};
}
return os;
}
Also defined in: ... 33, 64 ...
Scrap referenced in: 1
If a macro call occurs in the scrap, the line starts with '
<
'.
We extract the macro name and check whether this macro call
is recursive and causing an infinite loop. For this purpose we maintain
a set of scrap names.
set< string > mark;
Also defined in: ... 44, 55 ...
Scrap referenced in: 2
A scrap name is pushed in the set whenever it is called and it will be removed by closing the scrap. So, the first thing we do when we find a macro call is to test whether its name is already included in the set. If so, we give an error message and exit. If not we call the operator again with the index list of the macro.
case '<' : {
scrap_name = grab_d_name(buf[*it]);
if ( mkind ) {
<compute offset for indentation:
56>
}
if ( mark.find(scrap_name) != mark.end() )
error(scrap_name + ": recursive macro call" , buf_no);
else
mark.insert(scrap_name);
if ( scrap.find(scrap_name) != scrap.end() ) os << scrap[scrap_name];
else warning(scrap_name + ": Scrap never defined", buf_no);
if ( mkind )
ind_sum = ind_sum - ic;
mark.erase(scrap_name);
break;
}
Also defined in: , 57 ...
Scrap referenced in: 52
Here the offsets for the indentation of the expanded macros have to be computed. The line preceding the macro call gives the information we need. We only have to find the last newline in this string. The number of characters after this newline constitutes our offset. Therefore, we use the string member function
string::rfind()
taking as parameter the string to search for. This is a reverse search
as it starts at the end of the string in which the search string is to
be found. The result, if any, is an index pointing to the beginning
of the search string.ic
int ind_sum = 0; int offset;
Also defined in: ... 53, 63 ...
Scrap referenced in: 2
and add the computed offset when we expand the macro and substract this offset when we are at the end of this macro. This can comfortably be done because
offset
is local to the overloaded operator <<
.
size_t ipos = buf[(*it) -1].rfind('\n'); size_t b_size = buf[(*it) -1].size(); if ( ipos == string::npos ) ic = b_size + offset; else ic = b_size - ipos - 1; ind_sum = ind_sum + ic; offset = ic;
Scrap referenced in: 54
All lines beginning with another character must be printed to the ostream by removing the first character.
case '$' : case '{' : case '>' : { <print #line:
59> for (int i = 1; i < buf[*it].size(); ++i) { os << buf[*it][i]; <print indentation:
58> } break; } default : break;
Also defined in: ... 54
Scrap referenced in: 52
Whenever we print a newline and
mkind
is true,
the current offset must be printed.
if ( buf[*it][i] == '\n' && mkind ) { for ( int ici = 0; ici < ind_sum; ++ici ) os << " "; }
Scrap referenced in: 57
If the boolean variable
mkline
is true, the #line
directive
must be printed.
nextnumber = 1; if ( mkline && nextnumber ) { os << "\n#line " << lines[*it] << " \"" << source << "\"" << endl; nextnumber = everyline; }
Scrap referenced in: 57
Both boolean variables mentioned above
bool mkline = 0; bool mkind = 1; bool nextnumber; bool everyline = 0;
Also defined in: ... 34
Scrap referenced in: 2
are set by evaluating the options given with the output filename. As the description of the first pass has shown (6.2.3), these options are stored for each filename in the container
options
.
For this reason, we only search for the significant characters using again the string::find()
function. All other characters in the options string are ignored.
opt = options[f_it->first]; if ( opt.find("l") != string::npos ) mkline = 1; else mkline = 0; if ( opt.find("i") != string::npos ) mkind = 0; else mkind = 1;
Scrap referenced in: 51
The line numbers will only be computed if the option
-l
is given with one of the output files
and if the output files are to be generated.
This is in accordance with our objective to make the default behavior of WebWeb
as fast as possible.
if ( mkline && lines.empty() ) compute_line_numbers();
Scrap referenced in: 51
The computing is done as follows:
buf
we count the number of
newlines and store this number in the container lines
which is a vector of integers.
line_t lines; int line_no;
Also defined in: ... 55, 71 ...
Scrap referenced in: 2
This results in the following routine:
void compute_line_numbers() { if (wwdebug) wwdb("compute_line_numbers"); line_no = 1; size_t pos; for (buf_t::iterator b_it = buf.begin(); b_it != buf.end(); ++b_it) { lines.push_back(line_no); pos = (*b_it).find('\n'); while ( pos != string::npos ) { ++line_no; pos = (*b_it).find('\n', pos + 1); } } }
Also defined in: ... 52, 65 ...
Scrap referenced in: 1
In case of an error or a warning, the actual line number is computed by adding the number of newlines in the current
line
read by
getline
so far.
int actual_line_number() { if (wwdebug) wwdb("actual_line_number"); size_t pos = line.find('\n'); while ( pos != string::npos ) { ++line_no; pos = line.find('\n', pos + 1); } return(++line_no); }
Also defined in: ... 64, 66 ...
Scrap referenced in: 1
os
contains the whole content of the source file.
To write it physically to the file, we use the function write_or_not()
.
taking the ostringstream as a reference and the name of the outputfile
as parameters. After some tests it opens an output file stream ofstream ofs
and redirects the given ostringstream to it.
void write_or_not(ostringstream& ostst, const string& fname) { if (wwdebug) wwdb("write_or_not"); bool writefile = force; string line1; string line2; string answer; bool repeatit; bool gl1; bool gl2; <check whether file has changed:
68> if ( interactive ) { <ask whether user wants to overwrite:
67> } if ( writefile ) { ofstream ofs(fname.c_str()); if ( ! ofs.good() ) error("Cannot write to file " + fname, 0); ofs << ostst.str(); } }
Also defined in: ... 65, 69 ...
Scrap referenced in: 1
The tests are carried out to ensure that an existing file is only overwritten in the case it has changed and the user wants to overwrite it. This behavior is controlled with the command line options as listed in the How to use section of this document. So if the user has started WebWeb in the interactive-mode we would set the variable
writefile
in accordance with the user input.
This mode has higher priority than the force-mode displayed with
the variable force
which normally writes
the files without checking whether they have changed or not.
if ( writefile ) { cout << "File " << fname << " has changed. Overwrite? [y|n] :"; cin >> answer; } else { cout << "File " << fname << " has not changed. Overwrite anyway? [y|n] :"; cin >> answer; } if ( answer == "y" || answer == "yes" || answer == "Y" || answer == "YES" ) writefile = 1; else writefile = 0;
Scrap referenced in: 66
If no command option is given we will have to check whether the generated file already exists. For this purpose, we open the input file stream
ifs
on the generated file.
If this is possible, the file exists and we redirect the new generated
file that is still stored in the ostringstream to an istringstream.
Otherwise we simply write the content of the ostringstream to the file.
In the first case, we read both files parallel linewise as long as they are identical
or the end of one file is reached.
According to the result we set the variable writefile
which is used to make
the final decision on writing the file or not in
the function write_or_not()
.
if ( ! writefile ) { ifstream ifs(fname.c_str()); istringstream iostst(ostst.str()); repeatit = ifs.good(); if ( !repeatit ) writefile = 1; while ( repeatit ){ gl1 = getline(ifs, line1); gl2 = getline(iostst, line2); if ( !gl1 && !gl2 ) repeatit = 0; else { if ( (gl1 && gl2 && ( line1 != line2 )) || (!gl1 || !gl2) ){ repeatit = 0; writefile = 1; } } } }
Scrap referenced in: 66
<header_line(): returns scrap header:
90> <formatted_list(): returns formatted list of scrap numbers:
85> <ref_list(): returns Scrap referenced in: + scrap_numbers:
88> <def_list(): returns Also defined in: + scrap_numbers:
87> <macro_num_list(): returns def_list() formatted for using inside a scrap:
89> <print_formatted_list(): prints any kind of formatted list:
86> <init_formatting_strings(): part of language independence:
8> <print_line(): searchs in given line for user index and prints the line:
91> <make_doc(): The main-function for documentation generation:
72>
Also defined in: ... 66
Scrap referenced in: 1
We start describing these functions first by showing how
make_doc()
works. Then, we take a look at the other functions.
buf
.Based on the fact that the text is stored in the
same order as the source, we draw the concept how to generate the
documentation: buf
.switch
in accordance with the first character.ref_nums
. Since we do not want a number listed twice
or even more we have to remove them. In this context, we use list::unique
,
another comfortable STL function.
state
and allows
us to determine whether we are in a header, a scrap, or outside.
We initialize the state
to doc
.
visible
or not ,
we set a boolean variable.
This is used for hidden scraps.
init_formatting_strings(); for (map_t::iterator dit = ref_nums.begin(); dit != ref_nums.end(); ++dit) dit->second.unique(); string s; char_t c; state = doc; bool visible = 1;
Scrap referenced in: 72
The boolean variable
visible
indicates whether a
at-line has to be printed or not. It is true by default but is
set to false when a hidden scrap is found and set back to true when
the end of the scrap is reached.write_or_not
which was described
in detail in the section Write the file. So we can use the same mechanism
for controlling the overwriting of the documentation file and for the output files.
We write the lines not directly to the file but in an ostringstream.
Thus, we can use it later to test whether the generated documentation differs
from an existing one or not.
ostringstream ds(1000000);
Also defined in: ... 63, 73 ...
Scrap referenced in: 2
By taking these steps we get the following basic structure when we generate the documentation:
void make_doc() { if (wwdebug) wwdb("make_doc"); ds.seekp(0, ios::beg); <do initial tasks:
70> for (buf_t::iterator b_it = buf.begin(); b_it != buf.end(); ++b_it) { c = tolower(static_cast<char_t>((*b_it)[0])); if ( visible ) { switch ( c ) { <evaluate first character in at_line:
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84> } } else { if ( c == '}' ) { print_line(*b_it); state = doc; visible = 1; } } } write_or_not(ds, docfile); }
Scrap referenced in: 69
header_line
and give the arguments scrap name,
the current scrap number
int s_num = 0;
Also defined in: ... 71
Scrap referenced in: 2
and the
HeaderString
which contains the formatting
information.
case 'o' : { if ( state == doc ) { ds << *begSString; ds << header_line(scrap_name = grab_o_name(*b_it), ++s_num, *headerString); } break; } case 'd' : { if ( state == doc ) { ds << *begSString; ds << header_line(scrap_name = grab_d_name(*b_it), ++s_num, *headerString); } break; }
Also defined in: , 75 ...
Scrap referenced in: 72
Since we do not want to copy a hidden scrap to the documentation we set the variable
visible
to false in regard with the character h
.
case 'h' : { if ( state == doc ) visible = 0; break; }
Also defined in: ... 74, 76 ...
Scrap referenced in: 72
bodyStartString
whenever a scrap starts.
After that we print the rest of the at_line.
case '{' : { ds << *bodyStartString; ds << *toScrapmodeString; state = body; print_line(*b_it); break; }
Also defined in: ... 75, 77 ...
Scrap referenced in: 72
Of course, we must print the appropriate
bodyEndString
when we reach the end of a scrap.
case '}' : { if ( state == body ) { state = doc; ds << *toDocmodeString; ds << *bodyEndString; ds << def_list(scrap_name, s_num, *defString); ds << ref_list(scrap_name, s_num, *refString); ds << *endSString << endl; } print_line(*b_it); break; }
Also defined in: ... 76, 78 ...
Scrap referenced in: 72
macro_name
using again
the function grab_d_name()
.hide
. So we use the overloaded operator <<
again to print the whole extracted content of the macro.lt
which is set to the current representation. After the macro name the list of all
scraps defining this macro is printed.
case '<' : { macro_name = grab_d_name(*b_it); if (state == doc) { if ( hide.find(macro_name) != hide.end() ) ds << hide[macro_name]; else ds << *b_it; } if (state == body) { if ( scrap.find(macro_name) != scrap.end() ) ds << *startMacroCall << macro_name << macro_num_list(macro_name); } break; }
Also defined in: ... 77, 79 ...
Scrap referenced in: 72
At the end of a macro call we must print the >-sign if we are in a scrap and in both cases the rest of the at_line.
case '>' : { if ( state == body ) { if ( scrap.find(macro_name) != scrap.end() ) //if ( hide.find(macro_name) == hide.end() ) ds << *endMacroCall; } print_line(*b_it); break; }
Also defined in: ... 78, 80 ...
Scrap referenced in: 72
docmode
or body
depending on the fact whether we are at the beginning or at
the end of the docmode section.
After printing the formatting string we print the rest of the at-line.
case '$' : { if ( state == docmode ) { ds << *toScrapmodeString; state = body; } else { if ( state == body ) { ds << *toDocmodeString; state = docmode; } } print_line(*b_it); break; }
Also defined in: ... 79, 81 ...
Scrap referenced in: 72
If we are in the documentation and want to switch to the scrapmode we set the state to it and apply the same tasks as described above.
case '|' : { switch (state) { case doc: { ds << *toScrapmodeString; state = scrapmode; break; } case scrapmode: { ds << *toDocmodeString; state = doc; break; } default: { error("Switch between docmode and scrapmode is wrong placed", 0); break; } } print_line(*b_it); break; }
Also defined in: ... 80, 82 ...
Scrap referenced in: 72
print_formatted_list()
. Then
we print the rest of the at_line. The only difference consists of
the parameters we give when calling the function.
case 'f' : { print_formatted_list(files, def_nums); print_line(*b_it); break; } case 'm' : { print_formatted_list(scrap, def_nums); print_line(*b_it); break; } case 'u' : { print_formatted_list(index, index); print_line(*b_it); break; } case '+' : { break; }
Also defined in: ... 81, 83 ...
Scrap referenced in: 72
buf
when it has finished reading an include file.
Thus, this at-line starts with the character i
and we
must remove the filename.print_line()
removes the first character
from any given string, we remove one character less when we remove
the filename. So we do not need to alter the convention of calling
print_line()
in each case
.
case 'i' : { size_t a = (*b_it).find_first_not_of(" \t\n", 1); size_t b = (*b_it).find_first_of(" \t\n", a); print_line((*b_it).substr(b)); break; }
Also defined in: ... 82, 84 ...
Scrap referenced in: 72
z
' in front of any first line of any source file,
we have to remove it. Although it is not necessary to list this case because it would be
dealt with by the default
, we list it
here in order to contribute to a better understanding of the whole procedure.
case 'z' : { print_line(*b_it); break; } default : { print_line(*b_it); break; }
Also defined in: ... 83
Scrap referenced in: 72
make_doc()
with different arguments, some of them call others, and some of them are never called
directly from make_doc()
.locTxt
: Reference to a container of type list<int>
which formatted_list()
takes as reference.
These are def_nums
ref_nums
and index
.
s_num
: An integer which should be the current scrap number.
s_num
we replace the metaNumber
in the string numString
which holds the formatting information
and append this string to listOfNums
which is the return value.
In addition, we provide a numSeparator
(usually a comma)
which is copied between every two numbers.
string formatted_list(const text_t& locTxt, const int& s_num) { if (wwdebug) wwdb("formatted_list"); string listOfNums = ""; string tempString; size_t numPos; char_t number [5]; for (text_t::const_iterator it = locTxt.begin(); it != locTxt.end(); ++it) { if ( *it != s_num ) { if ( listOfNums != "" ) listOfNums += (*numSeparator); sprintf(number, "%d", *it); tempString = *numString; numPos = tempString.find(metaNumber); while ( numPos != string::npos ) { tempString.replace(numPos, metaNumber.length(), number); numPos = tempString.find(metaNumber); } listOfNums += tempString; } } return listOfNums; } string formatted_list2(const text_t& locTxt, const int& s_num) { if (wwdebug) wwdb("formatted_list"); // sort(logTxt.begin(), logTxt.end()); string listOfNums = ""; string listOfNums1 = ""; string listOfNums2 = ""; int counter1 = 0; int counter2= 0; string tempString; size_t numPos; char_t number [5]; bool flag1 = true; bool flag2 = true; for (text_t::const_iterator it = locTxt.begin(); it != locTxt.end(); ++it) { if ( *it == s_num ) { flag1 = false; continue; } if ( flag1 ) { listOfNums1 = "..."; sprintf(number, "%d", *it); tempString = *numString; numPos = tempString.find(metaNumber); while ( numPos != string::npos ) { tempString.replace(numPos, metaNumber.length(), number); numPos = tempString.find(metaNumber); } listOfNums1 += tempString; } if ( (! flag1) && (flag2) ) { listOfNums2 = (*numSeparator); sprintf(number, "%d", *it); tempString = *numString; numPos = tempString.find(metaNumber); while ( numPos != string::npos ) { tempString.replace(numPos, metaNumber.length(), number); numPos = tempString.find(metaNumber); } listOfNums2 += tempString; listOfNums2 += " ..."; flag2 = false; } } listOfNums = listOfNums1 + listOfNums2; return listOfNums; }
Scrap referenced in: 69
listStartFormat
.
Then, it prints for each entry in the first argument the list of references.
For this purpose, it takes the listEntryFormat
and replaces the metaName
with the current key from locMap
using the memberfunction string::replace().
Then, it replaces in the same string the metaList
with the returned string
from a call to formatted_list()
which is called with the third argument set to
empty because we want all entries from the index list.ds
,
the list environment is closed by the listEndFormat
.
void print_formatted_list(const map_t& locMap, map_t& ef_nums) { if (wwdebug) wwdb("print_formatted_list"); ds << *listStartFormat; string locEntryF; size_t namePos = listEntryFormat->find(metaName); size_t listPos; if ( namePos != string::npos ) { for ( map_t::const_iterator it = locMap.begin(); it != locMap.end(); ++it) { locEntryF = *listEntryFormat; locEntryF.replace(namePos, metaName.length(), it->first); listPos = locEntryF.find(metaList); if ( listPos != string::npos ) ds << locEntryF.replace(listPos, metaList.length(), formatted_list(ef_nums[it->first], 0)); } } ds << *listEndFormat; }
Scrap referenced in: 69
make_doc()
when a scrap has finished
and need three arguments:
s
: The name of the current scrap
s_num
: The number of the current scrap
[d|r]efStringLocal
: The string which holds the formatting information
and the text written before the list.
defStringLocal
is empty or there are not enough numbers
in the list, the empty string will be returned.
Otherwise the metaList
is replaced by the formatted_list()
of the appropriate integers.
string def_list(const string& s, const int& s_num, string defStringLocal) { if (wwdebug) wwdb("def_list"); size_t listPos; string rString; if ( (def_nums[s]).size() >= 2 && defStringLocal.length() != 0) { listPos = defStringLocal.find(metaList); if ( listPos != string::npos ) defStringLocal.replace(listPos, metaList.length(), formatted_list2(def_nums[s], s_num)); rString = defStringLocal; } return rString; }
Scrap referenced in: 69
The function
ref_list
does an equivalent task for the scraps calling the
current macro.
string ref_list(const string& s, const int& s_num, string refStringLocal) { if (wwdebug) wwdb("ref_list"); size_t listPos; string rString; if ( ! (ref_nums[s]).empty() && refStringLocal.length() != 0) { listPos = refStringLocal.find(metaList); if ( listPos != string::npos ) refStringLocal.replace(listPos, metaList.length(), formatted_list(ref_nums[s], s_num)); rString = refStringLocal; } return rString; }
Scrap referenced in: 69
macro_num_list()
returns the list appended to each
macroname which is called inside a scrap.def_list()
, structured in a simplier way, though,
since we want to print all numbers in the list and need only the current
scrapname as argument
string macro_num_list(const string& s) { if (wwdebug) wwdb("macro_num_list"); string tempString = *macroCallNum; size_t listPos = tempString.find(metaList); if ( listPos != string::npos ) tempString.replace(listPos, metaList.length(), formatted_list(def_nums[s], 0)); return tempString; }
Scrap referenced in: 69
header_line()
based on three parameters:
s
: The current scrapname
s_num
: The current scrapnumber
headerStringLocal
: The string which holds the formatting
information
metaName
with the
scrapname. Then, we replace all occurences of the metaNumber
with
the current scrapnumber by strongly referring to the STL.
Finally, we return the so manipulated string.
string header_line(const string& s, const int& s_num, string headerStringLocal) { if (wwdebug) wwdb("header_line"); if ( ! headerStringLocal.empty() ) { size_t namePos = headerStringLocal.find(metaName); while ( namePos != string::npos ) namePos = ( headerStringLocal.replace(namePos, metaName.length(), s) ).find(metaName); char_t number [5]; sprintf(number, "%d", s_num); size_t numPos = headerStringLocal.find(metaNumber); while ( numPos != string::npos ) numPos = (headerStringLocal.replace(numPos, metaNumber.length(), number)).find(metaNumber); } return headerStringLocal; }
Scrap referenced in: 69
print_line()
is
mainly used to remove the first character in an at_line.
void print_line(string s) { if (wwdebug) wwdb("print_line" + s); string::iterator pli = s.begin() +1; size_t spos; string tmp_string; string tmp_schar; s = s.substr(1, (s.length() - 1)); switch ( docLang ) { <CASE HTML:
92> <CASE LATEX:
93> default : { ds << s; } } <search for index:
94> }
Scrap referenced in: 69
Additionally, it provides some default actions for language specific tasks as replacing some special characters inside a scrap. For example, the < - sign which is to be replaced in a HTML-document by <
case HTML : { if ( (state == body) || (state == scrapmode) ) { spos = s.find('<'); while ( spos != string::npos ) { s.replace(spos, 1, lt); spos = s.find('<', spos); } spos = s.find('>'); while ( spos != string::npos ) { s.replace(spos, 1, gt); spos = s.find('<', spos); } } ds << s; break; }
Scrap referenced in: 91
In LaTeX the switching to documentation mode and back inside a scrap is a little bit more difficult. We can not use the simple
verbatim
environment here, because switching
to this environment and back implies a vskip and some more directives.
For that reason we put a simple \verb@
and @\\ around each line.
« CASE LATEX » 93
case LATEX : {
if ( (state == body) || (state == scrapmode) ) {
string latex_at = "@\\verb|@|\\verb@";
string latex_nl = "@\\newline \n \\verb@";
spos = s.find('@');
while ( spos != string::npos ) {
s.replace(spos, 1, latex_at);
spos = s.find('@', (spos + latex_at.length()));
}
spos = s.find('\n');
while ( spos != string::npos ) {
s.replace(spos, 1, latex_nl);
spos = s.find('\n', (spos + latex_nl.length()));
}
}
ds << s;
break;
}
Scrap referenced in: 91
If an user-defined index entry exists, we have to search for it in all lines
inside a scrap to find the references to all entries.
This is done by print_line()
.
For each entry in index
we use string::find()
to
test whether the index entry is in the current at_line.
If so, we push_back
the current scrapnumber
to the list <int>
which is the value of the current index entry.
« search for index » 94
size_t tmp_pos;
if ( state == body && ! index.empty() ) {
for ( map_t::iterator plm_it = index.begin(); plm_it != index.end(); ++plm_it) {
tmp_pos = s.find(plm_it->first);
if ( tmp_pos != string::npos )
plm_it->second.push_back(s_num);
}
}
Scrap referenced in: 91
7 Compiling
We compiled WebWeb using the Visual C++ compiler from Microsoft
under Windows NT.
We put the command line in a batch file
in order to prevent from typing this line during the development
of WebWeb again and again.
« mkweb.bat » 95
cl -GX -GR webweb.cpp
7.1 The Preprocessor Directives
As every C++ program uses library routines
we have to include them.
« preprocessor directives » 96
#include < iostream >
#include < fstream >
#include < sstream >
#include < string >
#include < vector >
#include < list >
#include < set >
#include < map >
#include < algorithm >
#ifndef __GNUC__
using namespace std;
#endif
Scrap referenced in: 2
8 The User's Guide
It always seemed useful to me having a compact reference manual when working with a new
tool. So I decided to make such a
special user's guide using some tables to struct the main
features.
You can find it in the file wwguide.html
in the directory where the file webweb.ww resides.
« wwguide.html » 97
<HTML>
<HEAD>
<Title>WebWeb</Title>
</HEAD>
<BODY>
</BODY>
Appendix A: List of files
mkweb.bat : 95 webstyles.css : 98 webweb.cpp : 1 webweb.h : 2 wwguide.html : 97
Appendix B: List of Macros
CASE HTML : 92 CASE LATEX : 93 analyze the source line : 25 ask whether user wants to overwrite : 67 check whether file has changed : 68 compute offset for indentation : 56 def_list(): returns Also defined in: + scrap_numbers : 87 do initial tasks : 70 evaluate arguments : 10, 13, 14, 17 evaluate first character in at_line : 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84 evaluate output file options : 61 formatted_list(): returns formatted list of scrap numbers : 85 functions and routines : 11, 32, 33, 52, 64, 65, 66, 69 global declarations : 4, 5, 6, 7, 12, 15, 16, 19, 21, 26, 29, 30, 37, 39, 44, 53, 55, 63, 71, 73 header_line(): returns scrap header : 90 hidden declarations : 27, 34, 60 include a file : 49 init_formatting_strings(): part of language independence : 8 macro_num_list(): returns def_list() formatted for using inside a scrap : 89 make the files : 51 make_doc(): The main-function for documentation generation : 72 prepare the #line directives : 62 preprocessor directives : 96 print #line : 59 print indentation : 58 print_formatted_list(): prints any kind of formatted list : 86 print_line(): searchs in given line for user index and prints the line : 91 read the input files : 18 read the source : 20 ref_list(): returns Scrap referenced in: + scrap_numbers : 88 search for index : 94 set documentation language : 42 set formatting strings : 45 skip comments : 24 some typedefs : 3 substitute double at-sign : 23 switch in accordance with the first character : 54, 57 switch the lookahead : 28, 31, 35, 36, 38, 40, 41, 43, 46, 47, 48, 50 the main function : 9 work on first line : 22
Appendix C: Differences between nuweb and WebWeb
These are the major differences between the literate programming tools nuweb and WebWeb:
- @c can be used for comments. The comment must be closed
with @}. So it is simple to comment out a whole scrap by changing
@d
, @o
, or @h
to @c
.
- A scrap introduced by @h (hide) and the macro calls referring
to it
will not be written to the documentation. The macro calls, however, will
be expanded in the output files.
- It is possible to use macros in the documentation sections, too.
They are called as usual
and defined by @h.
- Each index listed at the end of a scrap must be introduced by @+.
So it is possible to write all sorts of strings to the user-defined
index. This list must be followed by the @} closing the scrap.
- Input files must have the extension
.ww
.
- The default documentation language is HTML. Therefore the generated documentation
has the extension
.html
.
- Documentation text embraced by @| will be displayed in the same font
as the scraps.
- Scrap text embraced by @$ will be displayed in the same font as
the documentation text.
- It is possible to give the name of the documentation file to be generated.
This is done with the option
-N filename
.
- WebWeb checks each file that is to be written to disk whether it already
exists. If so, it will be only written if it has changed.
This behavior preserves the timestamp on files that have not changed
and may be used for projects using makefiles.
The option -f
forces the writing of the files even if
they have not changed. Using this option speeds up the running of WebWeb
as file changes will not be checked.
The option -i
provides an interactive mode.
Each output file is checked for changes. The user gets the result and will then
be asked whether to overwrite or not.
- WebWeb provides a mechanism to change the layout of the documentation by simply
changing one of the formatting strings.
Appendix D: Example
After all these rules for using WebWeb let us look at an example.
Consider the following short web file:
<HTML>
<HEAD>
<tITLE>WebWeb</TITLE>
</HEAD>
<BODY>
@l HTML
This is a short file to test the main features
of WebWeb.<BR>
First, let us define an output file:
@o sesame.cpp
@{
These are some declarations
@<a special functions@>
@<the main function@>
@}
Now, we want to describe the functionality of the main function
followed by a listing
@d the main function
@{void main(){
//some code
}
@}
Then, we make a detailed explanation concerning the superiority of sesame
compared to other programs and list the special function:
@d a special function
@{void special(ernie e, bert b){
// some code
@<more code@>
}
@}
Thereafter, we list more code
@d more code
@{ // some more code
@}
Finally, we comment the rest of the code,
give a listing,
@d more code
@{ // the rest
@}
and say bye.
</BODY>
Having run WebWeb on this file you get the following documentation:
This is a short file to test the main features of WebWeb.
First, let us define an output file:
« sesame.cpp » 1
These are some declarations
<a special function: 3>
<the main function: 2>
Now, we want to describe the functionality of the main function
followed by a listing
« the main function » 2void main(){
//some code
}
Scrap referenced in: 1
Then, we make a detailed explanation concerning the superiority of sesame
compared to other programs and list the special function:
« a special function » 3void special(ernie e, bert b){
// some code
<more code: 4 5>
}
Scrap referenced in: 1
Thereafter, we list more code
« more code » 4
// some more code
Also defined in: 5
Scrap referenced in: 3
Finally, we comment the rest of the code,
give a listing,
« more code » 5
// the rest
Also defined in: 4
Scrap referenced in: 3
and say bye.
At last a listing of the file sesame.cpp as it was generated by WebWeb:
These are some declarations
void special(ernie e, bert b){
// some code
// some more code
// the rest
}
void main(){
//some code
}
Appendix E: CSS - The STYLE
-file
We provide the following CLASS
es to ensure easy changing of the basic layout
of the scraps. They are in the file webstyles.css which is copied by WebWeb to the current
working directory.
« webstyles.css » 98
.clsScrpStrt {
}
.clsScrpEnd {
margin-top: 1em;
}
.clsScrpHd {
color: #660000;
font-weight: 700;
}
.clsScrpHdA {
}
.clsScrpMd {
margin-left: 3em;
margin-bottom: 1em;
color: #880000;
display: inline;
}
.clsMcrCll {
font-weight: 700;
}
.clsScrpDf {
margin-top: -1em;
font-style: italic;
font-size: small;
}
.clsScrpNumHrf {
text-decoration: none;
}
.clsScrpRf {
margin-top: -1em;
font-style: italic;
font-size: small;
}
.clsTbl { color: #000000;
margin-bottom: 2em;
}
.clsTblHrf { text-decoration: none;
font-weight: 500;
}
.clsTblNm { font-weight: 700;
color: #660000;
}
.clsTblEntry { font-weight: 500;
}
Appendix F: References
-
[1] Ulrich Breymann. Die C++ Standard Template Library.
- Addison-Wesley, 1996
-
[2] Preston Briggs. nuweb Version 0.87b. A Simple Literate Programming Tool.
- Submitted to IEEE Software, August 1992. Available through anonymous ftp:
Available through:
ftp://ftp.uni-stuttgart.de/pub/tex/web/nuweb/
-
[3] Nikos Drakos. All about LATEX2HTML, November 1996,
-
http://cbl.leeds.ac.uk/nikos/tex2html/doc/latex2html/latex2html.html
-
[4] Nicolai Josuttis. Die C++ Standardbibliothek.
- Addison-Wesley, 1996.
http://www.bredex.de/literatur.html
-
[5] Ira Pohl. C++ Distilled: a concise ANSI/ISO reference and style guide.
- Addison-Wesley, 1997.
-
[6] Silvio Levy and Donald E.Knuth.
CWEB
user manual: The CWEB system of
structured documentation.
-
Technical Report STAN-CS-83-977, Stanford University,
Oktober 1990.
-
[7] Donald E. Knuth. Literate programming.
-
The computer Journal, 27(2):97-111, May 1984
-
[8] Donald E. Knuth.
-
Homepage
-
[9] John D. Ramsdell. sweb v 2.0 Simple support for literate programming in Lisp, 1994.
- Available through:
ftp://ftp.uni-stuttgart.de/pub/tex/web/schemeweb/
-
[10] Norman Ramsey. Literate-programming tools need not be complex.
- Submitted to IEEE Software, August 1992. Available through:
ftp://ftp.uni-stuttgart.de/pub/tex/web/noweb/
-
[11] Lincoln D. Stein. How to Set Up and Maintain a WebSite.
- Addison-Wesley, 1997