% This file should be typeset by % % lw2nw ext.nw > ext1.nw; % noweave -delay ext1.nw > ext.tex % latex ext.tex (2x) \documentclass[a4paper,10pt]{article} \usepackage{noweb} \input MANUAL.pagesize \input MANUAL.mac \setcounter{secnumdepth}{5} \setcounter{tocdepth}{5} \newcommand{\tc}[1]{$\backslash\mathit{#1}$} \begin{document} \title{The LEDA Tools for Manual Production and Documentation} \author{Evelyn Haak \and Kurt Mehlhorn \and Stefan N\"{a}her \and Michael Seel \and Christian Uhrig} \maketitle \tableofcontents \newpage \section{The Manual Page of the Documentation Tools} \subsection{How to use the manual production tools} LEDA offers several tools for manual production and documentation. The following diagram shows the different extraction and conversion processes possible from a documented and manual commented file to the different intermediate files for further processing. \begin{verbatim} __________ weave _______ LaTeX source | | ------- Lman -------- LaTeX source for manual page foo | ------- Ldoc -------- LaTeX source for full documentation | | ------- Fman -------- ASCII version of manual page | |__________ tangle ______ C/C++ program foo's ------- Mkman ------- LaTeX source of manual foo's ------- Mkdvi ------- dvi versions of all manual pages in directory MANUAL/DVI \end{verbatim} Lman produces manual pages from header files suitably augmented by so-called manual comments. Many examples can be found in the LEDA include directory (LEDAROOT/incl/LEDA). A manual comment starts with \verb-\*{\M- and ends with \verb-}*/-. Try \verb-Lman list- to see Lman in action. If it does not work the error is very likely to be one of the following (if not, you should refer to the LEDA installation guide): \begin{itemize} \item The environment variable LEDAROOT is not set to the root directory of the LEDA system. \item \$LEDAROOT/Manual/cmd is not part of your PATH. \item \$LEDAROOT/Manual/tex is not part of your TEXINPUTS. \end{itemize} \begin{verbatim} LEDAROOT | ___________________________________ | | | incl Manual DVI | _____|_______________ LEDA | | | | cmd noweb tex MANUAL \end{verbatim} shows the parts of the LEDAROOT directory relevant for manual production and documentation. The include directory contains all header files of the LEDA system Manual contains all the relevant commands and tex files. \begin{description} \item[Lman] can be applied to LEDA data types and user-defined data types. Write \verb-Lman T[.h] options- to apply it to the LEDA data type T and \verb-Lman T[.ext] options- to apply it to file T.ext in the working directory. The extensions lw (Lweb file), nw (noweb file), w (a Cweb-file), and h (h-file) are possible. The extensions are tried in this order and the working directory is searched before the LEDA include directory. The following options are available: [[size={12,11,10}]]\\ [[constref={no,yes}]]\\ [[partypes={no,yes}]]\\ [[usesubscripts={no,yes}]]\\ [[numbered={no,yes}]]\\ [[xdvi={yes,no}]]\\ [[warnings={yes,no}]]\\ [[informational={yes,no}]]\\ [[ack={yes,no}]]\\ [[latexruns={1,2,0}]]\\ [[filter={all, signatures, definition, types, creation, operations, implementation, example, opname}]]\\ [[pid={"",string}]]\\ Options are given in assignment syntax. There must be no blank on either side of the equality sign. The size-option selects the font-size. The option constref determines whether const-ref pairs are shown, the option partypes determines whether the type of a value parameter of an operator is suppressed if equal to the currently defined type, and the option usesubscripts determines whether indexed variables are typeset using subscripts. A manual page can be numbered or not and the call of xdvi can be suppressed. Lman may be asked to issue warnings and warnings may require a user action or not. We recommend to run Lman with \verb-warnings=yes- and \verb-ack=yes- in the early stages of designing a manual page. Lman usually runs \LaTeX\ twice in order to get cross-references correct. You may settle for a single run of \LaTeX. Lman makes use of a preprocessor called ext. The preprocessor writes its output on file /tmp/pid-ext.tex where pid is either the process identification of the preprocessor run or specified by the pid-option. Mkman makes use of the pid-option. You will probably never have to use it. There are four ways to specify options for Lman. You may either put them into the file itself by means of the Moptions command, or you can put them onto the command line, or you can put them into a configuration file Lman.cfg in either the working directory or the home directory. The Lman-utility can also be told to extract only part of a manual page by the filter-option. The options definition, creation, operations, implementation, and example cause extraction of the corresponding section of the manual, and the option signatures extracts the signatures of all operations but suppresses the descriptions. One can also extract a single operation by using its name, e.g., \verb-Lman stack filter=push- extracts the push operation of the stack type and \verb-Lman point filter=operator+- extracts the + operator of the point type (write arrop to inquire about the array operator and funop to inquire about the function operator). \item[Fman] is an alternative to Lman. It gives low quality ASCII output but works faster. \item[Mkman] is the tool to produce manuals like the LEDA manual. \item[Ldoc] is the utility that supports the production of full documentations. All remarks above apply equally well to Ldoc. Ldoc applies to Lweb, noweb, and Cweb files. For use with noweb and Lweb, the LEDA dialect of noweb, you must have installed noweb and for use with Cweb you must have installed Cweb. The subdirectories Cweb, noweb, and Lweb contain full documentations of parts of the LEDA system. Copy a file from one of these directories to your working directory and run Lman on it to see it in action. If it does not work check whether you have installed the proper literate programming system. \item[lweave] is the weave tool for Lweb. \end{description} The production of manual pages and full documentations proceeds in phases. \begin{description} \item[Lman] \begin{verbatim} XXX.[h|lw|nw|w] -----> XXX.tex -------> XXX.dvi ext.pl latex \end{verbatim} \item[lweave and lw2dvi] \begin{verbatim} XXX.lw ----------> XXX.tex -------> XXX.dvi lweave latex \end{verbatim} \item[Ldoc] \begin{verbatim} XXX.[lw|nw|w] ----> temp.[lw|nw|w] --------------> XXX.tex -------> XXX.dvi | ext.pl [l|no|c]weave | latex | ext.pl | | | XXX.man ----------------------------------------------- \input \end{verbatim} \item[Fman] \begin{verbatim} XXX.[h|lw|nw|w] ----------> ASCII Fman \end{verbatim} \end{description} We use standard tools for all but the first phase. The first phase is accomplished by the perl program \emph{ext.pl}. It realizes Lman, Ldoc and Fman in a very direct way. \subsection{How to create a Manpage} Starting from a C++ data type [[DT]] we give the documentation process and its syntactical elements in form of a context free grammar. Non-terminals are marked by \verb-<...>-. Terminals are pseudocode statements like [[text_text_text]] which represent pieces of latex text apart from the C++ language constructs which should be easy to locate. Optional syntactical elements are put in square brackets \verb-[...]-. \begin{verbatim} -> class DT { }; \end{verbatim} The previous picture should give you an impression about the possible sectioning of the implementation of [[DT]]. Note that several of the cited sections are optional and are not necessarily part of the data types manpage specification. We now handle the different sections. \subsubsection*{The Manpage Statement} \begin{verbatim} -> /*{\Manpage {DT} {T1,..,Tk} {short_description} {d}}*/ \end{verbatim} This comment is mandatory for the start of a manpage. It introduces the data type to the macro package and sets the main variable \verb-\Mname = DT-. Thus [[DT]] the data types name. [[T1,..,Tk]] is a list of template parameters which can be empty. the [[short_description]] appears in the manpage header and can be used as a latex reference. Finally \verb-\Mvar- is set to [[d]] as the default variable name of a class instance. \subsubsection*{The Mdefinition Statement} \begin{verbatim} -> /*{\Mdefinition specification_text}*/ \end{verbatim} This comment is somehow mandatory as a manpage should always contain an accurate specification of a data type. \subsubsection*{The Mtypes Statement} \begin{verbatim} -> /*{\Mtypes [W]}*/ \end{verbatim} This comment is optional. It should be placed in a public section of class [[DT]] where the user wants to specify local types in class scope. [[W]] is an optional width specifier in cm determining the width of the indendation of the following type specifications. \begin{verbatim} -> typedef TYPE1 LOCALTYPE1; /*{\Mtypedef typedef_documentation}*/ \end{verbatim} This triggers a documenation line containing the whole typedef statement followed by [[typedef_documentation]]. \begin{verbatim} -> typedef TYPE2 LOCALTYPE2; /*{\Mtypemember type_documentation}*/ \end{verbatim} This triggers a documenation line containing only the defined type [[LOCALTYPE2]] followed by [[type_documentation]]. \begin{verbatim} -> class LOCALTYPE3 { /*{\Mtypemember type_documentation}*/ // local class implemenation }; \end{verbatim} This triggers a documenation line containing only the defined type [[LOCALTYPE3]] followed by [[type_documentation]]. \begin{verbatim} -> enum LOCALTYPE4 { T1, ... , Tk }; /*{\Menum type_documentation}*/ \end{verbatim} This triggers a documenation line containing the defined enum type [[LOCALTYPE4]] followed by [[type_documentation]]. \begin{verbatim} -> \end{verbatim} \subsubsection*{The Mcreation Statement} \begin{verbatim} -> /*{\Mcreation [MVAR] [W]}*/ \end{verbatim} This comment is somehow mandatory as most classes have constructors which have to be documented. [[MVAR]] can be used to set \verb-\Mvar- to the string [[MVAR]]. [[W]] is an optional width specifier in cm determining the width of the indendation of the following construction operations. \begin{verbatim} -> DT(paramlist); /*{\Mcreate constructor_specification }*/ \end{verbatim} creates the documenation for the constructor [[DT(paramlist)]]. \begin{verbatim} -> ~DT(); /*{\Mdesctruct desctructor_specification }*/ \end{verbatim} creates the documenation for the destructor [[~DT()]]. \begin{verbatim} -> \end{verbatim} \subsubsection*{The Moperations Statement} \begin{verbatim} -> /*{\Moperations [W1] [W2]}*/ \end{verbatim} This comments starts the operations section of the manpage which usually is dominating the manpage. [[W1]] is an optional width specifier determining the indendation used for the return type of all following operations. [[W2]] is an optional width specifier determining the width of the additional indendation up to the textual specification block. In the following we give all possible kinds of manual comments documenting member operations and friend operations of a class. \begin{verbatim} -> | \end{verbatim} The following entries should be rather self explaining. The first section provides documentation elements concerning member operations. Top down we have standard named member operations, static operations, conversion operations, binary operators, unary operatos and the special selection operator known from arrays. \begin{verbatim} -> return_type operation_name(paramlist); /*{\Mop semantic_description }*/ | return_type static_operation_name(paramlist); /*{\Mstatic semantic_description }*/ | return_type conversion_operation_name(paramlist); /*{\Mconversion semantic_description }*/ | return_type operator binary_op_symbol (paramlist); /*{\Mbinop semantic_description }*/ | return_type operator unary_op_symbol (paramlist); /*{\Munop semantic_description }*/ | return_type operator [] (paramlist); /*{\Marrop semantic_description }*/ \end{verbatim} The next section gives documentation elements suitable for friends in classes or for global functions. \begin{verbatim} -> return_type friend_operation_name(paramlist); /*{\Mfunc semantic_description }*/ | return_type friend_operator binary_op_symbol (paramlist); /*{\Mbinopfunc semantic_description }*/ | return_type friend_operator unary_op_symbol (paramlist); /*{\Munopfunc semantic_description }*/ \end{verbatim} Finally there's a function object documentation element. \begin{verbatim} -> constructor(paramlist); /*{\Mfunobj{return_type} semantic_description }*/ \end{verbatim} \subsubsection*{The Mimplementation Statement} \begin{verbatim} -> /*{\Mimplementation implemenation_information }*/ \end{verbatim} This section can be used to give the user certain implemenation details like running time, space requirements or implementation specific informations which are helpfull for the user of the data type. \subsubsection*{The Mexample Statement} \begin{verbatim} -> /*{\Mexample (example_description [])* }*/ \end{verbatim} In this section you should provide a small example demonstrating the usage of [[DT]]. [[example_description]] is just regular latex description. \begin{verbatim} -> \begin{Mverb} verbatim_multi_line_example_code \end{Mverb} \end{verbatim} This is just an optional block structure enabling code citation within the manual comment. @ \subsection{A Manpage Example} This section gives a short example of a manpage following the above specifications. \begin{figure} {\footnotesize \begin{verbatim} #ifndef LEDA_STACK_H #define LEDA_STACK_H #include #include /*{\Manpage {stack} {E} {Stacks} {S}}*/ template class _CLASSTYPE stack : private SLIST { /*{\Mdefinition An instance |S| of the parameterized data type |\Mname| is a sequence of elements of data type |E|, called the element type of |S|. Insertions or deletions of elements take place only at one end of the sequence, called the top of |S|. The size of |S| is the length of the sequence, a stack of size zero is called the empty stack.}*/ public: /*{\Mcreation}*/ stack() {} /*{\Mcreate creates an instance |\Mvar| of type |\Mname| and initializes it to the empty stack.}*/ stack(const stack& S) : SLIST(S) {} ~stack() { clear(); } stack& operator=(const stack& S) { return (stack&)SLIST::operator=(S); } /*{\Moperations 2.5 4}*/ E top() const { return ACCESS(E,SLIST::head());} /*{\Mop returns the top element of |\Mvar|.\\ \precond $S$ is not empty.}*/ void push(E x) { SLIST::push(Copy(x)); } /*{\Mop adds $x$ as new top element to |\Mvar|.}*/ E pop() { E x=top(); SLIST::pop(); return x; } /*{\Mop deletes and returns the top element of |\Mvar|.\\ \precond $S$ is not empty.}*/ \*{\Mimplementation Stacks are implemented by singly linked linear lists. All operations take time $O(1)$.}*/ #endif \end{verbatim} }% end footnotesize \caption{The header file stack.h} \label{the header of stack} \end{figure} \begin{figure} {\footnotesize \begin{verbatim} \documentclass[12pt,a4paper]{article} \input MANUAL.pagesize \input MANUAL.mac \begin{document} \begin{manual} \section*{Stacks(stack)}\label{stack} \definition An instance $S$ of the parameterized data type \mbox{$\mathit{stack}$} is a sequence of elements of data type $E$, called the element type of $S$. Insertions or deletions of elements take place only at one end of the sequence, called the top of $S$. The size of $S$ is the length of the sequence, a stack of size zero is called the empty stack. \creation \create {\mbox{$\mathit{stack\}$}} {\mbox{$\mathit{S}$}} {creates an instance \mbox{$\mathit{S}$} of type \mbox{$\mathit{stack\}$} and initializes it to the empty stack. } \setlength{\typewidth}{2.5cm} \setlength{\callwidth}{4cm} \computewidths \operations \function {\mbox{$\mathit{E}$}} {\mbox{$\mathit{S.}$}top\mbox{$\mathit{()}$}} {returns the top element of \mbox{$\mathit{S}$}.\\ \precond $S$ is not empty. } \function {\mbox{$\mathit{void}$}} {\mbox{$\mathit{S.}$}push\mbox{$\mathit{(E\ x)}$}} {adds $x$ as new top element to \mbox{$\mathit{S}$}. } \function {\mbox{$\mathit{E}$}} {\mbox{$\mathit{S.}$}pop\mbox{$\mathit{()}$}} {deletes and returns the top element of \mbox{$\mathit{S}$}.\\ \precond $S$ is not empty. } \function {\mbox{$\mathit{int}$}} {\mbox{$\mathit{S.}$}empty\mbox{$\mathit{()}$}} {returns true if \mbox{$\mathit{S}$} is empty, false otherwise. } \end{manual} \end{document} \end{verbatim} }% end footnotesize \caption{The intermediate \TeX-file produced from stack.h} \label{intermediate tex-file} \end{figure} Figure \ref{intermediate tex-file} shows the intermediate \TeX-file produced by Lman for the data type stack. The TeX file basically contains one TeX-macro call for each manual command. It also has a short preamble and postamble. The preamble reads MANUAL.pagesize and MANUAL.mac, opens the document and enters the manual environment, and the postamble closes the manual environment and the document. The files MANUAL.pagesize and MANUAL.mac are both contained in the LEDAMAN directory; they contain the definition of the pagesize used for the LEDA manual and the definition of the manual macros respectively. Ldoc applied to the same file produces (in stack.man) the subfile starting with \verb-\begin{manual}- and ending with \verb-\end{manual}-. Ldoc also produces a temporary file temp.w which is obtained from the input file by deleting all manual comments. It applies cweave to this file to obtain XXX.tex. \newpage \section{Implementation of Toplevel Callers} \subsection{The ASCII View} <>= #!/bin/sh -f perl $LEDAROOT/Manual/cmd/ext.pl Fman $1 $2 @ \subsection{The \LaTeX\ View} <>= #!/bin/sh -f perl $LEDAROOT/Manual/cmd/ext.pl Lman "$@" <>= #!/bin/sh -f perl $LEDAROOT/Manual/cmd/ext.pl Ldoc "$@" <>= #!/bin/sh -f if [ "$1" = "" -o "$2" = "" ] then echo "" echo "Usage is" echo " lextract infile outfile [options]" echo "" echo "Extracts the manual comments from infile into outfile." exit fi infile=$1 outfile=$2 shift shift perl $LEDAROOT/Manual/cmd/ext.pl Mkman $infile outfile=$outfile "$@" <>= #!/bin/csh -f if ($1 == "") then set source = $LEDAROOT/incl/LEDA set ext = h else set source = $1 if ($2 == "") then set ext = h else set ext = $2 endif endif \rm -r -f extract mkdir extract echo Extracting manual pages ... echo " " foreach f ($source/*.$ext) echo $f lextract $f extract/`basename $f .$ext`.tex > Mkman.log end <>= #!/bin/sh #lroot=$LEDAROOT lroot=../.. # check for perl and latex echo " " path=`which perl` if [ -x "$path" ]; then echo using $path else echo Cannot execute perl. echo " " exit 0 fi path=`which noweave` if [ -x "$path" ]; then echo using $path else echo Cannot execute noweave. echo " " exit 0 fi path=`which latex` if [ -x "$path" ]; then echo using $path else echo Cannot execute latex. echo " " exit 0 fi source=$lroot/incl/LEDA dvidir=$lroot/Manual/DVI cd $dvidir cp $lroot/Manual/MANUAL/intro.tex ./Introduction.tex cp $lroot/Manual/MANUAL/Preface.tex . cp $lroot/Manual/MANUAL/Basics.tex . latex Introduction > /dev/null echo " " echo Updating xlman dvi-files in `pwd` ... echo " " if [ -x /usr/local/bin/grep ]; then grep_cmd=/usr/local/bin/grep elif [ -x /usr/bin/grep ]; then grep_cmd=/usr/bin/grep else grep_cmd=grep fi tcmd=`which test` if [ "$tcmd" = "" ]; then tcmd=test fi for f in $source/*.h; do if $grep_cmd -q Manpage $f; then dvi=`basename $f .h`.dvi if $tcmd ! $dvi -nt $f; then echo $dvi perl ../cmd/ext.pl Lman $f xdvi=no dvioutfile=$dvi >> mkdvi.log fi fi done rm -f *.log cd .. <>= #!/bin/sh # check for perl and latex echo " " if perl $LEDAROOT/Manual/cmd/ext.pl; then echo "perl ok" else echo Cannot execute perl. echo " " exit 1 fi if noweave < /dev/null > /dev/null; then echo "noweb ok" else echo Cannot execute noweave. echo " " exit 1 fi echo '\\documentclass{article}' > dummy.tex echo '\\begin{document} hw \\end{document}' >> dummy.tex echo '' >> dummy.tex if latex dummy.tex > /dev/null; then echo "latex ok" rm -rf dummy.* else echo Cannot execute latex. echo " " exit 1 fi source=$1 echo " " echo Constructing dvi-files for xlman in `pwd` ... echo " " if [ -x /usr/local/bin/grep ]; then grep_cmd=/usr/local/bin/grep elif [ -x /usr/bin/grep ]; then grep_cmd=/usr/bin/grep else grep_cmd=grep fi for f in $source/*.h; do if $grep_cmd -q Manpage $f; then ln -s $f . fi done for f in *.h; do fbase=`basename $f .h` fbase=`basename $fbase _decl` perl $LEDAROOT/Manual/cmd/ext.pl Lman $f xdvi=no dvioutfile=$fbase.dvi done rm -f *.h *.log @ \subsection{The HTML View} The command [[Mkhtml]] makes the html-version of the LEDA-manual. It can be called in any directory. It gets the necessary tex and header files via [[LEDAROOT]]. The tasks are done by the subprocesses [[Mktoplevel]] and [[Mklowlevel]] described below. Having set up the necessary tex-files we call [[latex MANUAL]] twice and then [[latex2html]] once. This will construct the html-version of the manual in the subdirectory [[MANUAL]]. <>= #!/bin/csh -f set verbose on \rm -r -f extract mkdir extract echo "Preparing all tex-files for LEDA types" echo " " Mklowlevel echo "Preparing top level of the Manual" echo " " Mktoplevel echo "calling latex MANUAL.tex" echo " " latex MANUAL.tex latex MANUAL.tex dvips MANUAL echo "please ensure that your path does not contain dots!!!" echo "calling latex2html MANUAL.tex" echo "" \rm -rf MANUAL latex2html $* -split 4 -local_icons -up_url 'http://www.mpi-sb.mpg.de/LEDA' -up_title 'LEDA home page' MANUAL.tex @ Mktoplevel prepares the top level of the manual. \begin{itemize} \item It copies all tex-files and subdirectory prog from the parent directory. \item It applies [[rm_dangerous_macros]] to all these files. latex2html insists that the command names of LaTeX commands consist of letters only. We used \verb-\<-, \verb-\>-, and \verb-\g++-. We remove them. \item We want the crossreferences to appear nicely in the html-document. latex2html replaces all \verb-\ref- by hyperlinks but an ordinary ref or pageref appears only as a box. Named links are nicer. [[handle_refs]] replaces occurrences of \verb-\ref{label}- by \verb-\htmlref{label'}{label}- where label' is obtained by quoting underscores. \end{itemize} <>= #!/bin/csh -f foreach f ($LEDAROOT/Manual/MANUAL/*.tex) set core = `basename $f .tex` echo "preparing $core for the html-manual" cp $f TEMP1 perl $LEDAROOT/Manual/cmd/rm_dangerous_macros.pl TEMP1 > TEMP2 perl $LEDAROOT/Manual/cmd/handle_refs.pl TEMP2 > $core.tex end \rm -r -f prog mkdir prog foreach f ($LEDAROOT/Manual/MANUAL/prog/*.prog) set core = `basename $f` echo "preparing $core for the html-manual" cp $f TEMP1 perl $LEDAROOT/Manual/cmd/rm_dangerous_macros.pl TEMP1 > TEMP2 perl $LEDAROOT/Manual/cmd/handle_refs.pl TEMP2 > prog/$core end \rm -f TEMP1 \rm -f TEMP2 @ latex2html gets confused by macros \verb-\<-, \verb-\>>-, and \verb-\g++-. I change them to Ltemplateless, Ltemplategreater, and gpp. I(MS) conjecture that the new version can handle them. :-) <>= #!/bin/sh -f mv $1.tex $1.temp perl $LEDAROOT/Manual/cmd/rm_dangerous_macros.pl $1.temp > $1.tex <>= $INPUT = $ARGV[0] && shift; open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; while () { s/\\/\\Ltemplategreater /g; s/\\g\+\+/\\gpp /g; s/\\'/^\\prime /g; print ; } @ I am handling refs and pagerefs. <>= #!/bin/csh -f foreach f (*.tex) echo "handling refs for $f" set core = `basename $f .tex` mv $core.tex $core.temp perl $LEDAROOT/Manual/cmd/handle_refs.pl $core.temp > $core.tex end perl $LEDAROOT/Manual/cmd/handle_refs.pl $1.temp > $1.tex <>= $INPUT = $ARGV[0] && shift; open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; while () { if (/\\ref\{([^\}]*)\}/) { $key = $1; $key1 = $key; $key1 =~ s/_/\\_/; s/\\ref\{$key\}/\\htmlref\{$key1\}\{$key\}/g; } if (/\\pageref\{([^\}]*)\}/) { $key = $1; $key1 = $key; $key1 =~ s/_/\\_/; s/\\pageref\{$key\}/\\htmlref\{$key1\}\{$key\}/g; } print; } @ Making the lower level is simple. We only need to call Lman in mode HTMLh2html to extract the appropriate Tex-file. We send the texfile through [[rm_dangerous_macros]] to be on the safe side. <>= #!/bin/csh -f set source = $LEDAROOT/incl/LEDA set ext = h cd extract foreach f ($source/*.$ext) echo "preparing tex-file (html) for $f" set core = `basename $f .$ext` perl $LEDAROOT/Manual/cmd/ext.pl Lman $f mode=HTMLh2html outfile=TEMP1 informational=no perl $LEDAROOT/Manual/cmd/rm_dangerous_macros.pl TEMP1 > $core.tex end cd .. @ The command [[HTMLman T]] makes the html version of the manual page for type T. The command looks for files [[T.lw]], [[T.nw]], [[T.w]], [[T.h]] in this order and produces a tex-file [[T.tex]]. The tex-file is sent through latex twice and then through latex2html. The entry node to the html manual page is [[T/T.html]]. <>= #!/bin/csh -f echo "extracting file $1.tex" echo " " perl $LEDAROOT/Manual/cmd/ext.pl Lman $1 mode=HTMLman outfile=TEMP1 perl $LEDAROOT/Manual/cmd/rm_dangerous_macros.pl TEMP1 > $1.tex \rm -f TEMP1 latex $1.tex latex $1.tex latex2html -font_size 12pt -split 0 -antialias -local_icons $1.tex # we require version > 98.1 echo "netscape -raise -remote openFile($1/$1.html)"; set here = `pwd`; netscape -raise -remote "openURL($here/$1/$1.html)" # we use netscape version > 4.0 <>= #!/bin/csh -f set verbose on echo "calling latex MANUAL.tex" echo " " latex MANUAL.tex latex MANUAL.tex \rm -r -f MANUAL latex2html -split 4 -local_icons -up_url 'http://www.mpi-sb.mpg.de/LEDA' -up_title 'LEDA home page' -no_reuse MANUAL.tex @ \section{The Main Engine: ext} \subsection{An Overview} Ext.pl operates in phases. The first phase processes the command line, takes care of initialization, and produces the preamble. The second phase does most of the work. It reads the input file and produces the body of a TeX-file. The third phase adds the postamble and calls cweave and/or latex. In Ldoc-mode the third phase also removes all With the option mode=Mkman ext carries out only the first two phases, the resulting file can then be included in some larger document, and with the option Fman ext produces ASCII-output on standard output. The production of ext's output is controlled by the so-called manual comments in the input file, see the chapter on Manual Production in the LEDA-book for further details. You must be familiar with that chapter in order to understand this program. The details of the behavior of ext are directed by options. The options are processed in the initialization phase of ext. <>= <> <> <
> <> <> @ \subsection{Command Line Processing and Initialization} <>= <> <> <> <> @ The first argument tells ext whether it works for Lman, Ldoc, Fman or Mkmanual. If ext receives only one argument it prints usage information for its first argument, see section \ref{usage}. We have a variable for each option. The variables are initialized to their default values. <>= $mode = $ARGV[0] && shift; $filearg = $ARGV[0] && shift; if (($mode eq "Mkman")) { $numbered = "yes"; } else { $numbered = "no";} # Usually manual pages are not numbered $ack = "yes"; # ext asks for acknowledgments when it is confused $constref = "no"; # const-& pairs are usually suppressed $partypes = "no"; # operators suppress argument types that are identical to Mname $usesubscripts = "no"; # usually we do not write indexed variables as subscripts $size = 12; # default size is 12pt $xdvi = "yes"; $warnings = "yes"; $informational = "yes"; $debugging = "no"; # default is to give warnings, to give progress report # and to show the result $filter = "all"; # usually we show everything $pid = ""; # we do not know the process id yet $latexruns=1; @substlist = (); # a list L1,R1,L2,R2,.... of all substitutions $delman = "yes"; # Ldoc usually removes manual comments $outfile = ""; # the default is that we determine the outfile $dvioutfile = ""; # only relevant for Mkdvi $section = "section"; # each manual page is a LaTeX section $nextwarning = "yes"; # default is to show the next warning $justset = "no"; # $nextwarning was not just set to no. $noweaveoptions = ""; # we start with no options. if ($mode eq "Lman" || $mode eq "Mkman" || $mode =~ /HTML/ || $mode eq "Fman" ) { $print_title = "yes"; } else { $print_title = "no"; } $HTMLopentabular = "no"; @ In Fman-mode there is only one further argument, the filter. In the other modes we look for options in the file \$mode.cfg in the home directory or in the working directory or on the command line. Later options take precedence. The variable \$pid is set to the process id of the current process (available in variable \$\$) if the user leaves it undefined. We use a derived variable \$showsem. <>= if ($mode eq "Fman") { if ($ARGV[0]) { $filter = $ARGV[0]; foreach $candidate ("all","signatures","definition","types", "creation","operations","implementation","example") { if ($filter eq $candidate) { goto filterfound; } } &print_info("Searching for operation $filter of type $filearg"); filterfound: } } else { foreach $path ("HOME","PWD") { if ($path eq "HOME") { $CFG = $ENV{$path} . "\/" . $mode . "\.cfg"; } else { $CFG = $mode . "\.cfg"; } if (-e $CFG) { open (CFG); &print_info("Reading $CFG"); while () { if (/^(\w+)=(.*)$/) { eval "\$$1 = \$2"; } } close (CFG); } } eval "\$$1 = \$2" while $ARGV[0] =~ /(\w+)=(.*)/ && shift; } if ($informational eq "no") { $dontshowinfo = '> /dev/null'; } if ($pid eq "") { $pid = $$; } $showsem = 1; # usually we show the semantics of functions if ($filter eq "signatures") { $filter = "operations"; $showsem = 0; } @ We set up the various input and output files. We set\\ \verb-$LEDAROOT- to the LEDA root directory \verb-$INCL- to the directory containing all LEDA header files,\\ \verb-$basename- to the file argument minus the extension (if any),\\ \verb-$INPUT- to the input file ( either [[\$basename.[lw|w|nw|h] ]] in the working directory or \verb-$basename.h- in the LEDAINCLUDE directory,\\ \verb-$kind- to the kind of the input file,\\ [[OUTPUT]] to /tmp/\$pid-ext.tex. If we are in Lman-mode, want to use xdvi, and the dvi-file exists already we simply call xdvi. This is for quick access to the LEDA manual. In HTML mode we need to read three files: \begin{verbatim} $LEDAROOT/Manual/noweb/texreplacementtable, $LEDAROOT/Manual/noweb/namereplacementable, and $LEDAROOT/Manual/noweb/refreplacementable \end{verbatim} The first file contains lines of the form\\ \verb-cmd xxx-\\ where \verb-\cmd- is a tex-command and [[xxx]] is any string not containing a backslash. Any occurence of \verb-\cmd- is replaced by [[xxx]] in the detex-phase of translation to HTML. If [[xxx]] is missing it is assumed to be [[cmd]]. The second file contains lines of the form\\ \verb-xxx yyy-\\ where [[xxx]] is any name which we want to tag in the LEDA manual and [[yyy.html]] is the html-file to which we want to make the link. If [[yyy]] is missing then it is assumed to be [[xxx]]. The third file contains lines of the form\\ \verb-xxx yyy-\\ where [[xxx]] is any label which we set in our manual and [[yyy.html]] is the html-file to which we want to make the link. <>= &print_info("$mode version 2.2b"); if (!($filearg)) { &print_usage($mode); } if ( $filearg eq "mancommands" ) { &print_mancommands($mode); } if ( $filearg eq "ltools" || $filearg eq "Lman" || $filearg eq "Ldoc") { &print_ltools($mode); } $LEDAROOT = $ENV{"LEDAROOT"}; $INCL = $ENV{"LEDAROOT"} . "/incl/LEDA"; if ($mode =~ /HTML/) { $INPUT = "$LEDAROOT/Manual/noweb/texreplacementtable"; open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; while () { chop; if (/^\s*$/) { next; } # skip empty lines s/\t/ /g; # subsitute a tab by a blank; some people write xxx\tyyy if (/^\s*(\S+)\s+(\S.*)$/) { $texcommand = $1; $replacement= $2; } else { /^\s*(\S+)\s*$/; $texcommand = $1; $replacement = $1; } $texreplacementtable{"$texcommand"} = $replacement; } close (INPUT); $INPUT = "$LEDAROOT/Manual/noweb/namereplacementtable"; open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; while () { chop; if (/^\s*$/) { next; } # skip empty lines s/\t/ /g; # subsitute a tab by a blank; some people write xxx\tyyy if (/^\s*(\S+)\s+(\S+)\s*$/) { $LEDAname = $1; $URL = $2; } else { /^\s*(\S+)\s*$/; $LEDAname = $1; $URL = $1; } $namereplacementtable{"$LEDAname"} = $URL; } close (INPUT); $unrecognizedtex = "unrecognizedtex.log"; open(UNRECOGNIZEDTEX,">>$unrecognizedtex"); $INPUT = "$LEDAROOT/Manual/noweb/refreplacementtable"; open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; while () { chop; if (/^\s*$/) { next; } # skip empty lines s/\t/ /g; # subsitute a tab by a blank; some people write xxx\tyyy if (/^\s*([ \w]*\w)\s*\&\s*(\w+)\s*$/) { $LEDAname = $1; $URL = $2; } else { /^\s*(\S+)\s*$/; $LEDAname = $1; $URL = $1; } $refreplacementtable{"$LEDAname"} = $URL; } close (INPUT); $unrecognizedref = "unrecognizedref.log"; open(UNRECOGNIZEDREF,">>$unrecognizedref"); } if ($filearg =~ /(.*)\.[whln]/) { $basename = $1; } else { $basename = $filearg; } if (-e ($basename . "\.w")) { $INPUT = $basename . "\.w" ; $kind = "Cweb"; $ext = "w"; goto DONE; } if (-e ($basename . "\.web")) { $INPUT = $basename . "\.web" ; $kind = "Cweb"; $ext = "web"; goto DONE; } if (-e ($basename . "\.lw")) { $INPUT = $basename . "\.lw" ; $kind = "Lweb";$ext = "lw"; goto DONE; } if (-e ($basename . "\.nw")) { $INPUT = $basename . "\.nw" ; $kind = "noweb"; $ext = "nw"; goto DONE; } if (-e ($basename . "\.h")) { $INPUT = $basename . "\.h" ; $kind = "h"; goto DONE; } $longname = $INCL . "/" . $basename; if (-e ($longname . "\.h")) { $INPUT = $longname . "\.h" ; $kind = "LEDAtype"; } if ($mode eq "Lman" && $xdvi eq "yes" && (-e "$LEDAROOT/Manual/DVI/$basename.dvi")) { &print_info(" Taking $LEDAROOT/Manual/DVI/$basename.dvi as a shortcut"); system("xdvi $LEDAROOT/Manual/DVI/$basename.dvi $dontshowinfo"); exit; } DONE: open (INPUT) || die "Error: Cannot find input file $basename.[w|lw|nw|h]: $!\n\n"; if ($mode ne "Fman") { if ($outfile eq "" || $mode eq "Ldoc") { $outfile = "/tmp/" . $pid . "-ext.tex"; } open(OUTPUT,">".$outfile); &print_info("Reading input file $INPUT to extract manual ..."); } @ Having opened all files we now construct the preamble. For Lman it has the form \begin{verbatim} \documentclass[$size pt,a4paper]{article} \input MANUAL.pagesize \input MANUAL.mac \begin{document} \begin{manual} \end{verbatim} For Ldoc and Mkman we only need to generate \verb-\begin{manual}-. <>= if ($mode eq "HTMLman" ) { print OUTPUT "\\documentclass[a4paper]{article}\n"; print OUTPUT "\\usepackage{html}\n"; print OUTPUT "\\input MANUAL.mac\n"; print OUTPUT "\\begin{document}\n"; } if ($mode eq "Lman") { print OUTPUT "\\documentclass\[" . $size . "pt,a4paper\]\{article\}\n\n"; print OUTPUT "\\input " . $ENV{"LEDAROOT"} . "/Manual/tex/MANUAL.pagesize\n\n"; print OUTPUT "\\input " . $ENV{"LEDAROOT"} . "/Manual/tex/MANUAL.mac\n\n"; print OUTPUT "\\begin\{document\}\n\n"; print OUTPUT "\\begin\{manual\}\n\n"; } if ($mode eq "Ldoc" || $mode eq "Mkman") { print OUTPUT "\\begin{manual}\n"; } @ \subsection{The Main Loop} We now come the core of ext. It is a single while loop that scans the input for manual commands. Whenever a manual command is encountered the appropiate output is produced. We need to recall some facts. The output actions of ext are directed by manual comments, i.e., comments starting with \verb-/*{\M- and ending with \verb-}*/-. Many of these comments refer to the preceeding code unit. A code unit is any maximal contiguous sequence of non-empty lines not containing any line belonging to an Mcomment. We call a line starting with \verb-/*{\M- an Mline. Three placeholders are relevant for manual production:\\ Mvar = the name of the canonical variable.\\ Mtype = the short type name, defined in the header of the manpage\\ Mname = the full type name, i.e., Mtype if there are no type parameters and Mtype$\langle$par\_list$\rangle$ if there are type parameters. The default value of all three placeholders is the empty string. <
>= $Mvar = ""; $Mtype = ""; $Mname = ""; main_loop: { # $_ is either undefined or the last line of an Mcomment <> # $_ is an Mline and variables $original_code_unit and $code_unit # contain the current code unit. <> # $Mcomment contains the manual command (without the brackets /*{\M and }*/ # $Mcommand contains the command (without the leading M) <> redo main_loop; } @ \subsubsection{Building a Code Unit} We build up a code unit by reading lines until we encounter an empty line or an Mline. We skip empty lines. If the first nonempty line is an Mline we have found a code unit that requires action. Otherwise, we discard the current code unit and use the non-empty line as the first line of our next attempt. Cweb inputs require some additional care. We ignore all lines starting with \verb-@s- or \verb-@f-. <>= # I advance $_ to a non-empty line (WEB-directives are output but count as # empty lines otherwise) or beyond the end of the file $_ = ; build_code_unit: while ($_ && (($_ =~ /^\s*$/) || ($_ =~ /^\@[sf]/))) { $_ = ; } if (! $_) { last main_loop; } # input exhausted $code_unit = ""; # $_ exists, is non_empty, and contains no @, and $code_unit is empty # The code_unit is either terminated by an Mcomment or an empty line # (empty line: only white space characters) or the end of the file while ($_ && !(/\/\*\{\\M/ || /^\s*$/)) { if ($_ =~ /\/\* *\{M/) { &print_warning("I encountered /*{M. Did you really mean it?"); } $code_unit .= $_; # append current line to code unit $_ = ; } # The current line either does not exist or is either empty or the # begin of an Mcomment. # Skip empty lines. while ( $_ && /^ *$/ ){ $_ = ; } # the current line is non-empty (if it exists). if (! $_) { last main_loop; } # input exhausted # the current line is non-empty. if (! (/\/\*\{\\M/)) { goto build_code_unit; # start new code unit. } @invisible_words = ('__exportC' , '__exportF' , '__exportD', '__typename'); foreach $invis_word (@invisible_words) { $code_unit =~ s/$invis_word//g; } $original_code_unit = $code_unit; @ \subsubsection{Reading a Manual Comment} The current line contains the begin of an Mcomment. I first remove the \verb-/*{\- and everything preceeding it and then process a command. Processing a command entails the following: Production of the required output and moving the current line to the last line of the manual comment. In order to make life simple I concatenate the comment into a single string. <>= s/^(.*)\/\*\{\\//; # remove begin comment and everything before it if ($1 =~ /\S/) { # issue warning if something non-white before comment &print_warning("ignored non-white stuff in front of begin comment"); } $Mcomment = ""; while ($_ && (! ( /\}\*\// ) ) ) { if (/\} *\*\//) { &print_warning("encountered } */ in manual comment. Did you mean }*/ ?"); } $Mcomment .= $_; $_ = ; } if (! $_) { die "Error: missing end comment\n"; } s/\}\*\/(.*)$/ /; # replace end comment and everything after it by a blank if ($1 =~ /\S/) { &print_warning("ignored non-white after Mcomment"); } $Mcomment .= $_; $original_comment = $Mcomment; # Mcomment contains the entire manual comment. We extract the command (the # maximal alphanumeric prefix). $Mcomment =~ /^(\w*)\W.*/; $command = $1; $Mcomment =~ s/$command *//; # remove command and succeeding blanks $command =~ s/^M//; # remove the M if (! ($command eq options)) { &apply_Msubst(*Mcomment); } # we replace all substitution strings in the comment as an exception # you can escape any string by a preceding backslash. @ \subsubsection{Processing a Manual Command} At this point we have completely read a manual comment. We have the command (without the leading M) available in \verb-$command- and we have the remainder of the comment available in \verb-$Mcomment-. We process commands in a big case statement with one case for each manual command. There is one command that requires special treatment, the Moptions command that turns off warnings for the next command. We use variables \$nextwarning and \$justset to deal with them. We initialize the first variable to yes and the second variable to no. Whenever we process a nextwarning=no in Moptions we set nextwarning to no and justset to true. The justset is changed back to no before the next command is processed. This makes sure that nextwarning applies exactly to the next command. pront\_warning does not print if nextwarning is no. <>= if ($nextwarning eq "no" && $justset eq "no") { $nextwarning = "yes";} $justset = "no"; switch: { if ($command eq 'options') { <> last switch; } if ($command eq 'subst') { <> last switch; } if ($command eq 'anpage') { <> last switch; } if ($command eq 'definition' || $command eq 'types' || $command eq 'creation' || $command eq 'operations' || $command eq 'implementation' || $command eq 'example') { <> last switch; } if ($command eq 'text' || $command eq "preamble") { <> last switch; } if ($command eq 'enum') { <> last switch; } if ($command eq 'typedef') { <> last switch; } if ($command eq 'typemember') { <> last switch; } <> &print_warning( "did not recognize command name M" . $command); } @ We now come to the various manual commands. They fall naturally into three groups: Moptions and Msubst produce no output, Manpage, Mdefinition, Mexample, Mimplementation, Mcreation, Moperations, Mtext and Mpreamble only deal with text, and all others need to extract code from the preceding code unit. @ \subsection{The Toplevel Manual Commands} \subsubsection{Moptions} The comment is a single options. We first remove leading and trailing white stuff (= all space characters) and then proceed as on command line. The filter variable in used in regular expressions and it is therefore necessary to quote all special characters. <>= $Mcomment =~ s/\s//g; $Mcomment =~ /^(\w+)=(.*)$/ ; if ($1 eq outfile) { if ($print_title eq "no") { # gives information if we are in Lman/Mkman mode # where we don't want to change the outfile but # just parse the manual comments in one file in # the given sequence close(OUTPUT); $outfile = $2; if ( $exisiting_outfiles{$output} == 1 ) { open OUTPUT, ">>$outfile"; } else { $existing_outfiles{$output} = 1; open OUTPUT, ">$outfile"; } } } else { eval "\$$1 = \$2"; } if ($nextwarning eq "no") {$justset = "yes";} @ \subsubsection{Msubst} The comment is a sequence of lines each containing either a pair w1 w2 or a pair w1 \# w2.. We put them into [[\$map]]. Note that the first line may be empty and contains only a carriage return or line feed. We do the following substitution strategy. We substitute the w1 by w2 in all [[code_unit]] and [[Mcomment]] just after they have been detected. If you want to keep a generally replaced string (as defined by Msubst) you can escape it with a backslash $\\$. This keeps the string and the backslash will be erased before prettyprinting. <>= while ($Mcomment =~ s/^([^\n\r]*)[\r\n]//) { $substline = $1; if ($substline =~ /^(.*)#(.*)$/ || $substline =~ /^ *(\S+) +(\S+) *$/) { $leftside = $1; $rightside = $2; &remove_enclosing_blanks($leftside); &remove_enclosing_blanks($rightside); push(@substlist,$leftside); push(@substlist,$rightside); } else { if ($substline =~ /\S/) { &print_warning("Msubst does not understand $substline"); } } } @ \subsubsection{Manpage} Mcomment is equal to $\{$type$\}\ \{$par\_list$\}\ \{$title$\}\ \{$varname$\}$ where the last argument is optional. We extract the parts, define the values of the placeholders, and produce the header line of the manual page. The header line is not produced if ext works for Ldoc. <>= if (! ($Mcomment =~ /\{([^\{\}]*)\}\s*\{([^\{\}]*)\}\s*\{([^\{\}]*)\}\s*\{([^\{\}]*)\}/ || $Mcomment =~ /\{([^\{\}]*)\}\s*\{([^\{\}]*)\}\s*\{([^\{\}]*)\}/ ) ) { &print_warning("Manpage expects either three or four arguments"); } $Mtype = $1; $par_list = $2; $title = $3; if ($4) { $Mvar = $4; } if ($par_list =~ /^ *$/) { $Mname = $Mtype; } else { # remove excessive blanks in parlist $par_list =~ s/ //g; $Mname = $Mtype."<".$par_list.">"; } if ($print_title eq "yes") { # we print \section*{title (type')}\label{type}\n where type' is obtained # from type by quoting underscores. # if numbered is true we supress the star if ($mode ne "Fman") { $Mtype1 = $Mtype; $Mtype1 =~ s/_/\\_/g; if ($numbered eq "yes" ) {$star = "";} else {$star = "*";} if ($mode =~ /HTML/) { &closetabular(); $star = ""; } print OUTPUT "\\$section" , $star, "{",$title," (",$Mtype1, ")}\\label{",$title,"}\n\\label{$Mtype}\n\n"; } else { print "$title ( $Mtype )\n\n"; } } @ \subsubsection{Mdefinition, Mcreation, Mtypes, Moperations, Mexample and Mimplementation} We come to the commands for the header lines of the various parts of a manual page. The body of these commands is either text or defines some widths and/or placeholders. We proceed in steps: \begin{enumerate} \item Mcreation: determine varname and declwidth. Set [[\$Mcomment]] to empty string. Moperations: determine typewidth and callwidth. Set [[\$Mcomment]] to empty string. All others: Do nothing \item Store [[\$Mcommand]] in [[\$currentsection]] \item Produce output. The output consists of a backslash, followed by the command name, followed (if there is text) by a newline followed by the text. The text is subject to placeholder substitution and conversion from C to LaTeX. This is the task of procedure [[convert_text]]. In Fman mode we only perform placeholder substitution. \end{enumerate} <>= if ($command eq "creation") { # Mcomment may contain a varname and/or a length. @params = split(' ',$Mcomment); # split at blanks foreach $i (0 .. $#params) { if ($params[$i] =~ /^[a-zA-Z]/) { $Mvar = $params[$i]; } # variable names start with a letter else { $a = $params[$i]; if ($a =~ /^ *$/) { &print_warning("something wrong in create"); } if ($a =~ /^[0-9\.]*$/) {$a = $a . 'cm';} print OUTPUT "\\setlength\{\\declwidth}\{" , $a, "\}\n" ; print OUTPUT "\\computewidths\n"; } } $Mcomment = ""; } if ($command eq "types") { # Mcomment may contain a length. @params = split(' ',$Mcomment); # split at blanks foreach $i (0 .. $#params) { $a = $params[$i]; if ($a =~ /^ *$/) { &print_warning("something wrong in create"); } if ($a =~ /^[0-9\.]*$/) { $a = $a . 'cm'; } print OUTPUT "\\setlength\{\\declwidth}\{" , $a, "\}\n" ; print OUTPUT "\\computewidths\n"; } $Mcomment = ""; } if ($command eq "operations") { # Mcomment is either empty or a b where a and b are lengths # If the lengths are without dimension then we add cm @params = split(' ',$Mcomment); # split at blanks foreach $i (0 .. $#params) { $a = $params[$i]; if ($a =~ /^[0-9\.]*$/) {$a = $a . 'cm';} if ($i == 0) { print OUTPUT "\\setlength\{\\typewidth\}\{" , $a, "\}\n" ;} else { print OUTPUT "\\setlength\{\\callwidth\}\{" , $a, "\}\n" ;} } print OUTPUT "\\computewidths\n"; $Mcomment = ""; } $currentsection = $command; if ($filter eq "all" || $filter eq $currentsection) { if ($mode =~ /HTML/) { $semantics = &substitute_for_placeholders($Mcomment); $semantics = &convert_HTML($semantics); if ( $semantics ne "" ) { $semantics .= "\n"; } if ($command eq "definition") { print OUTPUT "\\textbf{\\large Definition}\n\n" , $semantics; } elsif ($command eq "creation") { &closetabular(); print OUTPUT "\\textbf{\\large Creation}\n\n", $semantics; } elsif ($command eq "types") { &closetabular(); print OUTPUT "\\textbf{\\large Types}\n\n", $semantics; } elsif ($command eq "operations") { &closetabular(); print OUTPUT "\\textbf{\\large Operations}\n\n" , $semantics; } elsif ($command eq "implementation") { &closetabular(); print OUTPUT "\\textbf{\\large Implementation}\n\n" , $semantics; } elsif ($command eq "example") { &closetabular(); print OUTPUT "\\textbf{\\large Example}\n\n" , $semantics; } } elsif ($mode eq "Fman") { print $command , "\n____________________________\n\n" , &substitute_for_placeholders($Mcomment), "\n\n"; } else { # Lman, Ldoc here ! $output = "\\" . $command ; if ($Mcomment) { $output .= "\n" . &convert_text($Mcomment);} &print_unit($output); } } @ \subsubsection{Mtext and Mpreamble} This is basically the third step of the previous section. However, in Ldoc mode we produce no output for Mpreamble. In HTML mode we also have to be careful if the text appears in the creation section or operations sections. <>= if ($filter eq "all" || $filter eq $currentsection ) { if ($mode eq "Fman" || $mode =~ /HTML/) { if ($mode eq "Fman") { print &substitute_for_placeholders($Mcomment) , "\n\n"; } else { &closetabular(); &print_unit(&convert_HTML(&substitute_for_placeholders($Mcomment))); } } else { if ($command eq "text" || $mode ne "Ldoc") { &print_unit(&convert_text($Mcomment)); } } } @ It is natural to learn about functions [[convert_text]] and [[print_unit]] at this point. <>= sub print_unit{ local($text) = @_; # read argument into local variable if ( $text ne "" ) { $text .="\n\n"; } print OUTPUT $text; } @ \subsection{Converting Bodies of Manual Commands to \LaTeX} Many manual comments contain pieces of text. We prepare this text for output in a two step process. We first subject it to placeholder substitution (procedure [[substitute_for_placeholders]] and then to \CC to \LaTeX\ conversion (procedure [[web_to_latex]]. The result of this process is not for human consumption. We found that even some programs have difficulties with it because it may contain lines of 200 and more characters. Latex has no problems with this but cweave does. We therefore break the output at suitable blanks. <>= sub convert_text{ local($text) = @_; # read argument into local variable $text = &substitute_for_placeholders($text); $text = &web_to_latex($text); if ($kind eq "Cweb") { <> } $text; } <>= local($i, $remline, $outline, $lines , $word); @lines = split(/^/,$text); # split at new lines $text = ""; foreach $i (0 .. $#lines) { if (length($lines[$i]) > 80) { $outline = ""; $remline = $lines[$i]; while ($remline ) { if ($remline =~ /[\w\,] /) { $word = $` . $&; $remline = $'; } else { $word = $remline; $remline = ""; } # a word charachter or komma followed by a blank or the entire line if (length($outline) + length($word) < 80) { $outline .= $word; } else { if ($outline) { $text .= $outline . "\n"; } else { &print_warning("unable to break line, " . "might cause trouble with cweave\n$word\n\n"); } $outline = $word; } } $text .= $outline; # no newline here } else { $text .= $lines[$i]; } # no newline here } @ \subsubsection{Placeholder Substitution} We define a function that replaces all occurences of the placeholders Mvar, Mtype, and Mname by the values of the corresponding variable. Some users prefer to drop the M in all placeholders. We warn them. We also replace the keys in the substitution map. <>= sub substitute_for_placeholders{ local($string) = @_; $string =~ s/\\Mvar/$Mvar/g; $string =~ s/\\Mtype/$Mtype/g; $string =~ s/\\Mname/$Mname/g; if ($string =~ /\\var\W/ || $string =~ /Mvar/) {&print_warning("found an occurrence of \\var or an unslashed occurrence of Mvar. Did you mean \\Mvar?");} if ($string =~ /\\type\W/ || $string =~ /Mtype/) {&print_warning("found an occurrence of \\type or an unslashed occurrence of Mtype. Did you mean \\Mtype?");} if ($string =~ /\\nameW/ || $string =~ /Mname/) {&print_warning("found an occurrence of \\name or an unslashed occurrence of Mname. Did you mean \\Mname?");} $string; } sub apply_Msubst{ local(*alias) = @_; local($i) = 0; while ($i < @substlist) { $leftside = @substlist[$i]; $i++; $rightside = @substlist[$i]; $i++; $alias =~ s/^$leftside/$rightside/g; $alias =~ s/([^\\])$leftside/$1$rightside/g; # \name can be used as an escape symbol to keep name $alias =~ s/\\$leftside/$leftside/g; # when escaping \name we delete the \ for typesetting } } @ \subsubsection{Web to latex} [[web_to_latex]] takes any string and applies [[convert_line]] to each line of the string if the lines are outside our verbatim environment. If they are inside we just skip them. <>= sub web_to_latex { local($text) = @_; # read argument into local variable local($output) = ""; local($inverb) = 0; while ($text) { $text =~ /^([^\n]*\n)/; $text = $'; $currline = $1; if ( $currline =~ s/\\begin\{Mverb\}/\\begin\{verbatim\}/ ) { $inverb = 1; $output .= $currline; } elsif ( $currline =~ s/\\end\{Mverb\}/\\end\{verbatim\}/ ) { $inverb = 0; $output .= $currline; } elsif ( $inverb ) { $output .= $currline; } else { $output .= &convert_line($1); } } return $output; } <> @ \subsection{The Lowlevel Manual Commands} All commands to follow generate output for the current code unit. We therefore need to parse the current code unit. It is supposed to contain the definition or declaration of a single function or operator. The result of the parse will be made available in the following varables:\\ [[\$type]] contains the return type.\\ [[\$fname]] is the name of the function.\\ [[\$signature]] is what we usually set in the second column.\\ [[\$prefix]], [[\$funcname]], [[\$postfix]] are a detailed version of the signature. [[\$funcname]] is what appears in roman in the LEDA manual.\\ [[\$constructor]], [[\$static]], [[\$operator]], [[\$conversion]], [[\$destructor]] record if the code unit contains a special function.\\ We generate a manual entry in two steps. We first parse the code unit and thus determine the values of all variables below. In a second step we actually generate the entry. <>= $prefix = ""; $funcname = ""; $postfix = ""; $signature = ""; $type = ""; $fname = ""; $static = 0; $constructor = 0; $operator = 0; $conversion = 0; $destructor = 0; if (! $code_unit) { &print_warning("current code unit is empty"); last switch; } <> <> @ \subsubsection{Parsing the Code Unit} A (standard) function definition or declaration has the following form: A possibly empty list of qualifiers (static, extern, friend, inline, virtual) and a return type separated by blanks (if the return type is missing it is taken to be int)\\ the function name (a function name is a string over a-zA-Z\_0-9. (HOW ABOUT ::?) or it has the form operator opsymbol)\\ a (\\ a possibly empty list of parameters separated by comma\\ a )\\ the optional qualifier const\\ a semicolon or a compound statement\\ the initializer =0 ; (for pure virtual functions ).\\ \noindent Among the qualifiers only static has an effect on the output. \smallskip \noindent If the function is a constructor we may have a list of constructor calls after the closing bracket, i.e.,\\ a :\\ followed by a sequence of function calls seperated by comma. \smallskip \noindent A destructor has the format $\sim$Mtype(). \smallskip \noindent The syntax of type conversion functions is different, namely \verb-operator typname-. There are no brackets. A code unit is supposed to consist of a single function definition or declaration. We allow to stretch this rule a bit in order to allow a more natural mode of expression. \begin{itemize} \item The code unit may be a comment. In this case it must start with /* and end with */ both on separate lines. The first line may contain a text. \item The code unit may contain more than one function definition. This is either used for closely related functions or for functions that have a conditional definition. I remove the compiler directives (starting with \# followed by letters) and then extract the \emph{last} definition as follows. We search for a innermost matching pairs of parenthesis (pattern \verb-\{[^\{\}]*\}-) and replace it by a semicolon. We proceed until there are no pairs left. At this point we have a sequence of function definitions, each followed by a semicolon. The last function definition is the one after the next to last semicolon. \end{itemize} A code unit may contain Cweb-directives. We remove them first. <>= $code_unit =~ s/\@\+/ /g; # remove @+ $code_unit =~ s/\@\// /g; # remove @/ $code_unit =~ s/\@\|/ /g; # remove @| $code_unit =~ s/\@\#/ /g; # remove @# $code_unit =~ s/\@\;/ /g; # remove @; $code_unit =~ s/\@\,/ /g; # remove @, <>= <> if ($code_unit =~ /\/\*/) { # commented code units &print_warning("code unit contains a comment. ". "I remove the lines containing /* and */"); $code_unit =~ s/ *\/\*.*//; # remove first line $code_unit =~ s/ *\*\/ *//; # remove last line $original_code_unit = $code_unit; } $code_unit =~ s/\/\/.*\n//g; # remove C++ comment $code_unit =~ s/\t/ /g; # replace tab by blank $code_unit =~ s/ *\#.*\n/ /g; # remove all lines with compiler directives $code_unit =~ s/\n/ /g; # replace newline characters by blanks while ($code_unit =~ s/\{[^\{\}]*\}/\;/) {} # replaces code by a single semicolon. while ($code_unit =~ s/\;\s*\;/\;/) {} # replaces white space-separated ; by a single ; $code_unit =~ s/= *0 *; *$//; # removes = 0 ; $code_unit =~ s/\; *$//; # remove last semicolon if only followed by whitespace if ($code_unit =~ /\;([^\;]*)$/) { &print_warning("code unit contains several function definitions. " . "I extracted\n $1"); $code_unit = $1; } # At this point we have a single function definition in code_unit $code_unit =~ s/const *$//; # remove const qualifier $code_unit =~ s/::/doppeldoppel/g; # replace :: by doppeldoppel $code_unit =~ s/ *:.*$//; # remove initialization constructor call $code_unit =~ s/doppeldoppel/::/g; # reintroduce :: @ The code unit should now end with a ) maybe followed by blanks (if the function is a conversion function there will be no ) We next work at the beginning of the code unit. We first remove a template definition \verb-template< (class T,)* class S>- and then all qualifiers. If we find a static qualifier we record that fact. <<>>= $code_unit =~ s/template *<[ ,\w]*>// ; # remove template definition $code_unit =~ s/virtual//; # remove blanks virtual blanks $code_unit =~ s/friend//; # remove friend $code_unit =~ s/inline//; # remove inline $code_unit =~ s/extern//; # remove extern $static = ($code_unit =~ s/static//); #remove static and record &apply_Msubst(*code_unit); &print_debug("code unit at start = $code_unit"); #DEBUGOUT @ At this point we have the form\\ \begin{tabular}{ll} type fname ( parlist ) & for a standard function\\ type operator opsymbol (parlist ) & for the definition of an operator\\ Mtype ( parlist ) & for a constructor\\ operator type & for a conversion function.\\ $\sim$Mtype () & for a destructor.\\ \end{tabular} \noindent In either case we define signature as fname(parlist).\\ \noindent We collect information to distinguish cases. We have a conversion operator if there is no (), an operator if "operator" is present but we are not in the conversion case, a destructor if the code unit starts with ~, and a constructor or member function otherwise. <<>>= &print_debug("code unit before cleaning = $code_unit"); $conversion = ($code_unit =~ /operator\s*\w+\s*\(\s*\)/); if ($code_unit =~ /\( *\) *\(.*\)/) { $code_unit =~ s/\( *\) *\((.*)\)//; $par_list = $1; } else { $code_unit =~ s/\((.*)\)//; $par_list = $1; } $operator = (($code_unit =~ /operator/) && !$conversion); # symbol operator $destructor = ($code_unit =~ /^ *\~/); &remove_enclosing_blanks($code_unit); # I do anchored matches below &print_debug("code unit after first clean up = $code_unit"); &print_debug("par_list = $par_list"); extraction: { if ($destructor) { last extraction; } if ($conversion) { # a conversion function: operator type if (!($code_unit =~ s/ *operator *//)) { &print_warning("expected a conversion function"); } $fname = $code_unit; last extraction; } if ($operator) { # an operator: type operator opsymbol $code_unit =~ /^(.*) *operator *(.*)$/; $type = $1; $fname = $2; last extraction; } <> } @ The case of a standard function or constructor remains: a standard function is of the form type fname where fname is of the form (class name::)*name I postulate that fname contains no blank and hence fname is everything after the last blank note that type may be empty and that type may bracketed by const \& A constructor has the form $\sim$Mtype. Cweave taught me that life is more complex than this.\\ \verb+arraylinear_base()+\\ is apparently perfect \CC. I think it's sick but I have to handle it anyhow. If the code unit matches Mtype then I have a constructor. Otherwise the function name is the maximal suffix that consists only of word characters and colons. If this is everything the return type is integer. If it is not everything then the return type is the remainder (but without trailing blanks). <>= if ($code_unit eq $Mtype) { $constructor = 1; } else { # a function if ($code_unit =~ /^([:\w]+)$/ ) { $type = "int"; $fname = $code_unit; } else { $code_unit =~ /^(.*[^\:\w])([\:\w]+)$/ ; $type = $1; $fname = $2; while ($type =~ s/^ //) {} while ($type =~ s/ $//) {} # remove beginning and trailing blanks if (($type =~ /^\s*const/) && ($constref eq "no")) { # remove const & bracket $type =~ s/^\s*const//; $type =~ s/ *\& */ /; } $type =~ s/ *\& */\& /g; } } @ \subsubsection{Generating a Manual Entry} Now that we parsed the code unit we can proceed to generate output. We prepare type, fname, varname, and par\_list for output and define signature as fname(parlist). It is helpful to remove leading and trailing blanks from all relevant variables. <>= &remove_enclosing_blanks($type); &remove_enclosing_blanks($fname); &remove_enclosing_blanks($Mvar); &remove_enclosing_blanks($Mtype); &remove_enclosing_blanks($command); @ In par\_list we remove const \& pairs and rebuild par\_list with one blank after each comma. We also remove blanks before \&. How do I recognize a const \& pair? I look for an occurrence of const followed by a blank followed by anything followed by \&. Well, this is not quite right. I do not want a blank before the second comma in \verb-...,sortseq S,...- <<>>= @params = split(/,/,$par_list); # split at commas $par_list = ""; $i = 0; while ($i <= $#params) { $j = $i + 1; while ($params[$i] =~ / ($params[$i]=~tr/>/>/))) { # append all comma separated parameter parts together # as part of a template type list or comma separated # init parts, the second condition allows initialization # by bracketed expressions containing < or << $params[$i] .= "," . $params[$j]; $j++; } if (($params[$i] =~ /const .*\&/) && ($constref eq "no")) { $params[$i] =~ s/ *const //; $params[$i] =~ s/ *\& */ /; # replace blanks&blanks by a single blank # $params[$i] =~ s/\&//; simply remove & if not followed by a blank. } &remove_enclosing_blanks($params[$i]); $params[$i] =~ s/ *\& */\& /g; if ($i > 0) {$par_list .= "\, ";} $par_list .= $params[$i]; $i = $j; } $varname = $Mvar; $signature = $fname . "(" . $par_list . ")"; &print_debug("fname = $fname"); &print_debug("signature = $signature"); if ($command =~ /^opl?$/) { <> last switch; } if ($command =~ /^funcl?$/) { <> last switch; } if ($command =~ /^binopl?$/) { <> last switch; } if ($command =~ /^binopfuncl?$/) { <> last switch; } if ($command =~ /^arropl?$/) { <> last switch; } if ($command =~ /^funopl?$/) { <> last switch; } if ($command =~ /^funobjl?$/) { <> last switch; } if ($command =~ /^unopl?$/) { <> last switch; } if ($command =~ /^unopfuncl?$/) { <> last switch; } if ($command =~ /^staticl?$/) { <> last switch; } if ($command =~ /^conversion/) { <> last switch; } if ($command =~ /^create/) { <> last switch; } if ($command =~ /^destruct/) { <> last switch; } @ \subsubsection{Mop} <>= if ($operator || $constructor || $conversion || $static) { &print_warning("Mop applies only to member functions " . "and not to operators,..."); } $prefix = $Mvar . "\."; $funcname = $fname; $postfix = $par_list; if ($filter eq "all" || $filter eq $currentsection || ($funcname =~ /$filter/)) { &print_function(); } @ \subsubsection{Mfunc} <>= if ($operator || $constructor || $conversion || $static) { &print_warning("Mfunc applies only to functions " . "and not to operators,... "); } $prefix = ""; $funcname = $fname; $postfix = $par_list; if ($filter eq "all" || $filter eq $currentsection || ($funcname =~ /$filter/) ) { &print_function(); } @ \subsubsection{Binary Operators as Members} The operator name is available in fname, the first argument is Mvar, and the second argument is par\_list. Everything else is as below. <>= if (! $operator) { &print_warning("Mbinop applies only to operators"); } $Mreplace = $Mname . " "; if ($partypes eq "no") { while ($par_list =~ /$Mreplace/) { $par_list = $` . $';} } $signature = $Mvar. " " . $fname . " " . $par_list; if ($filter eq "all" || $filter eq $currentsection || $ filter eq ("operator" . $fname) ){ &print_function(); } @ \subsubsection{Binary Operators as Non-Members} fname contains the operator symbol and par\_list contains the arguments. We replace the comma in par\_list by fname surrounded by blanks. <>= if (! $operator) { &print_warning("Mbinopfunc applies only to operators"); } $Mreplace = $Mname . " "; if ($partypes eq "no") { while ($par_list =~ /$Mreplace/) { $par_list = $` . $'; } } $par_list =~ s/\, / $fname /; $signature = $par_list; if ($filter eq "all" || $filter eq $currentsection || $filter eq ("operator" . $fname) ){ &print_function(); } @ \subsubsection{Array Operator} par\_list contains a single element. <>= if (! $operator) { &print_warning("Marrop applies only to operators"); } $signature = $varname . "\[" . $par_list . "\]"; if ($filter eq "all" || $filter eq $currentsection || $filter eq "arrop" ) { &print_function(); } @ \subsubsection{Function Operator} <>= if (! $operator) { &print_warning("Mfunop applies only to operators"); } $signature = $varname . "\(" . $par_list . "\)"; if ($filter eq "all" || $filter eq $currentsection || $filter eq "funop" ) { &print_function(); } @ \subsubsection{Mfunobj} <>= if ($operator || $constructor || $conversion || $static) { &print_warning("Mfunobj applies only to function objects " . "and not to operators,..."); } $prefix = ""; $funcname = $fname; $postfix = $par_list; if ($filter eq "all" || $filter eq $currentsection || ($funcname =~ /$filter/) ) { &print_function("extract return type from Mcomment"); } @ \subsubsection{Unary Operator as Member} The operator is availabe in fname. \verb=++= and \verb=--= come in postfix and prefix. The postfix operators have an integer argument. new and delete have arguments and are prefix. \verb=->= is postfix. <>= if (! $operator) { &print_warning("Munop applies only to operators"); } unopcases: { if ($fname eq "->") { $signature = $varname . $fname; # -> is postfix last unopcases; } if ($fname eq "new" || $fname eq "delete" ) { $signature = $fname . ' ' . $varname; # new and delete are prefix last unopcases; } if ($par_list) { $signature = $varname . $fname; # postfix ++ and -- last unopcases; } $signature = $fname . $varname; # all others are prefix operators } if ($filter eq "all" || $filter eq $currentsection || $filter eq ("operator" . $fname )) { &print_function(); } @ \subsubsection{Unary Functions as Non-Members} The operator is availabe in fname. \verb=++= and \verb=--= come in postfix and prefix. The postfix operators have two arguments, new and delete have arguments and are prefix. \verb=->= is postfix . <>= if (! $operator) { &print_warning("Munop applies only to operators"); } unopfunccases: { if ($fname eq "->") { $signature = $varname . $fname; # -> is postfix last unopfunccases; } if ($fname eq "new" || $fname eq "delete" ) { $signature = $fname . ' ' . $varname; # new and delete are prefix last unopfunccases; } if ($par_list =~ /,/) { $signature = $varname . $fname; # postfix ++ and -- last unopfunccases; } $signature = $fname . $varname; # all others are prefix operators } if ($filter eq "all" || $filter eq $currentsection || $filter =~ /$fname/ || $filter eq ("operator" . $fname )) { &print_function(); } @ \subsubsection{Static Member Functions} For static member functions the fname is Mtype::fname. <>= if (!$static) { &print_warning("Mstatic applies only to static member functions"); } $prefix = $Mtype . "\:\:"; $funcname = $fname; $postfix = $par_list; if ($filter eq "all" || $filter eq $currentsection || ($fname =~ /$filter/)) { &print_function(); } @ \subsubsection{Conversion Operators} We have the type to be converted to in fname. A conversion call is fname(Mvar). <>= if (!$conversion) { &print_warning("Mconversion applies only to conversion operators"); } $funcname = $fname; $postfix = $Mvar; if ($filter eq "all" || $filter eq $currentsection ){ &print_function(); } @ \subsubsection{Constructors} Mcomment is Ltext at this point. A constructor call is Mtype(par\_list). <>= if (!$constructor) { &print_warning("Mcreate applies only to constructors"); } if ($filter eq "all" || $filter eq $currentsection ) { &print_constructor(); } @ \subsubsection{Destructors} Mcomment is Ltext at this point. A destructor call is $\sim$Mtype(). <>= if (!$destructor) { &print_warning("Mdestruct applies only to destructors"); } if ($filter eq "all" || $filter eq $currentsection ){ &print_destructor(); } @ \subsubsection{Member Types} In this case we want to document the existence of a type in the local scope. Such a type can be the result of a typedef or the definition of a local class. In the second case we only want to document the existence of a type. Which is stored in [[\$type]] to be output in [[print_typemember]]. <>= <> if ( $code_unit =~ s/^ *typedef// ) { $code_unit =~ s/typename//g; if ( $code_unit =~ /<.*>/ ) { $code_unit =~ s/^ *(.*\>\S*) +(\S*)//; # search for last > and then for end of type $type = $2; } else { $code_unit =~ s/^ *(\S*) +(\S*)//; $type = $2; } } elsif ( $code_unit =~ /(^class|.* +class) +(\w*)/ ) { $type = $2; # extract class name } else { &print_warning("Typemember should be a typedef or a class"); } &print_typemember(); @ \subsubsection{Typedef Documentation} We also want to offer a scheme for typedef documentation. This one actually shows the source type and the target type, for example in traits classes. Mcomment is Ltext at this point and [[\$code_unit]] contains the code. It should be of the form [[typedef type1 type2;]]. The parsing is almost trivial. Note that type1 could contain nested template parentesis and therefore also spaces. <>= <> if (!($code_unit =~ s/^ *typedef//)) { &print_warning("Typedef should start with keyword typedef"); } if ( $code_unit =~ /<.*>/ ) { $code_unit =~ s/^ *(.*>\S*) +(\S*)//; # search for last > and then for end of type $type = $1; $fname = $2; } else { $code_unit =~ s/^ *(\S*) +(\S*)//; $type = $1; $fname = $2; } &print_typedef(); <>= if ($code_unit =~ /\/\*/) { # commented code units &print_warning("code unit contains a comment. ". "I remove the lines containing /* and */."); $code_unit =~ s/ *\/\*//; # remove start comment $code_unit =~ s/ *\*\/ *//; # remove end comment $original_code_unit = $code_unit; } $code_unit =~ s/\t/ /g; # replace tabs by blanks $code_unit =~ s/\/\/.*//g; # remove trailing comment $code_unit =~ s/\n/ /g; # replace newline by blank $code_unit =~ s/; *$//; # replace; plus blanks at end @ \subsubsection{Enumeration Types} Mcomment is Ltext at this point and [[\$code_unit]] contains the code. It should be of the form [[enum type { t1, ..., tk}]]. The parsing is trivial. <>= <> if (!($code_unit =~ s/^ *enum//)) { &print_warning("Enum should start with keyword enum"); } $code_unit =~ s/^ *(\w*) *\{//; # remove blanks type blanks { $type = $1; $code_unit =~ s/\} *$//; # remove } at end $par_list = $code_unit; &remove_enclosing_blanks($par_list); $par_list =~ s/ *, */ , /g; &print_enum(); @ \subsection{Creating the Output File} \subsubsection{Printing a Function} [[print_function]] prints the manual entry for a function. It makes use of the global variables command, type, prefix, funcname , postfix, and Mcomment. We have \begin{verbatim} prefix funcname postfix signature return type Mop Mvar. fname par_list yes Mfunc fname par_list yes Mfunobj fname par_list yes Mstatic Mtype:: fname par_list yes Mconversion fname Mvar no Mbinop Mvar op par_list yes Mbinopfunc arg1 op arg2 yes Marrop Mvar[arg] yes Mfunop Mvar(par_list) yes Munop the right thing yes Munopfunc the right thing yes \end{verbatim} We output\\ \verb-\function {return type} {prefix funcname} {postfix} {Mcomment}- in the first four cases and \\ \verb-\operator {return type} {signature} {Mcomment}- in the other cases. There is a subtlety in the first case. We want funcname to be printed in roman. Thus web to latex conversion is only applied to the prefix and not to funcname. In funcname we only quote underscores. We need to apply the substitution map to all elements of funcname, prefix, \ldots. We use the following construction. We create a list [[@args]] of relevant quantities. We cycle through the list ann apply the substitution map to each element of the list. In HTML-mode we need to do two things. \begin{itemize} \item Add \verb-\htmlnormallink{typename}{URL}- for all typenames in the [[namereplacementtable]]. \item Perform HTML like quoting. Type name and signature consist only of alphanumeric characters, round brackets ( and ), angular brackets < and >, kommas, and ampersands (\&). \end{itemize} Typenames are maximal substrings of alphanumeric characters. <>= sub print_function{ @args = ("funcname","prefix","postfix","signature","type"); # foreach $arg (@args) { &apply_Msubst(*$arg); } # done earlier local($getreturntype) = @_; if ( "$getreturntype" ne "" ) { local($rtype) = $Mcomment; $rtype =~ s/\s*\{([^\}]*)\}(.|\n)*/$1/; $Mcomment =~ s/\s*\{[^\}]*\}(.*)/$1/; $type = $rtype; } if ($mode eq "Fman") { if ($funcname) {$signature = $prefix . $funcname . "(". $postfix. ")";} print $type , " ", $signature , "\n" ; if ($showsem == 1) { print &substitute_for_placeholders($Mcomment) , "\n\n"; } return; } if ($mode =~ /HTML/) { if ($funcname) { $signature = $prefix . $funcname . "(". $postfix. ")"; } local($semantics) = &substitute_for_placeholders($Mcomment); $semantics = &convert_HTML($semantics); local($shortsig) = 1; if (length($signature) > 25) { $shortsig = 0; } $type = &insert_URLs_and_quote($type); $signature = &insert_URLs_and_quote($signature); &opentabular(3); if ($shortsig == 1) { print OUTPUT $type , "\\ \\ \\ \\ \& ", $signature; print OUTPUT "\\ \\ \\ \\ \& " , $semantics , "\\\\ \n"; } else { print OUTPUT $type; print OUTPUT "\\ \\ \\ \\ \& \\multicolumn\{2\}\{l\}\{$signature\}\\\\ \n"; print OUTPUT " \& \& $semantics\\\\ \n"; } return; } $arg1 = ""; # conversion functions have no return type $arg4 = ""; # description may be empty if ($type) { $arg1 = &convert_M($type); } $arg2 = ""; if ($funcname) { if ($prefix) { $arg2 .= &convert_M($prefix);} $funcname =~ s/_/\\nspaceunderscore\\_/g; # quote and back up slightly $arg2 .= $funcname; if ($postfix =~ /^ *$/) {$arg3 = "";} else {$arg3 = &convert_M($postfix);} } else { $arg2 = &convert_M($signature); $arg3 = ""; } if ($Mcomment && $showsem == 1) {$arg4 = &convert_text($Mcomment);} local($text) = ""; if ($command =~ /^opl?/ || $command =~ /^funcl?/ || $command =~ /^funobjl?/ || $command =~ /^staticl?/ || $command =~ /^conversion?/) { if ($arg3 eq "") { $arg3 = "\$\\,\$"; } # small space $text = "\\function" .' {'. $arg1 . "\}\n\{". $arg2 . "\} \n\{". $arg3 . "\} \n\{" . $arg4 .'}'; } else { $text = "\\operator" .' {'. $arg1 . "\}\n\{". $arg2 ."\} \n\{" . $arg4 .'}'; } print OUTPUT $text,"\n\n"; } @ \subsubsection{Printing a Constructor} We output \verb-\create {Mname} {Mvar} {par_list} {Mcomment}- <>= sub print_constructor{ local($Mname_save) = $Mname; if (!$Mvar || !$Mname ) { &error_handler("You forgot to define either Mvar or Mname."); } if ($mode eq "Fman") { $signature = $Mname . " " . $Mvar; if ($par_list) { $signature .= "(" . $par_list. ")"; } print $signature , ";\n" , &substitute_for_placeholders($Mcomment) , "\n\n"; $Mname= $Mname_save; return; } if ($mode =~ /HTML/) { local($semantics) = &substitute_for_placeholders($Mcomment); $semantics = &convert_HTML($semantics); $sig1 = &insert_URLs_and_quote($Mname); $sig2 = $Mvar; if ($par_list) { $sig2 .= "(" . $par_list. ")"; } local($shortsig) = 1; if (length($sig2) > 25) { $shortsig = 0; } $sig2 = &insert_URLs_and_quote($sig2); &opentabular(3); if ($shortsig == 1) { print OUTPUT "$sig1 \& $sig2; \& " , $semantics , "\\\\ \n\n"; } else { print OUTPUT "$sig1 \& \\multicolumn\{2\}\{l\}\{$sig2;\}\\\\ \n"; print OUTPUT " \& \& $semantics\\\\ \n"; } $Mname= $Mname_save; return; } $arg1 = &convert_M($Mname); $arg2 = &convert_M($Mvar); if ($par_list) { $arg3 = &convert_M($par_list); } else { $arg3 = ""; } # empty arg list $arg4 = &convert_text($Mcomment); local($text) = "\\create" .' {'. $arg1 ."\}\n\{". $arg2 . "\} \n\{".$arg3 . "} \n\{".$arg4 .'}'; print OUTPUT $text,"\n\n"; $Mname = $Mname_save; } @ \subsubsection{Printing a Destructor} We output \verb-\destruct {Mname} {Mcomment}- <>= sub print_destructor{ if ($mode eq "Fman") { print "~", $Mname ,"()\n" , &substitute_for_placeholders($Mcomment) ,"\n\n"; } if ($mode =~ /HTML/) { local($semantics) = &substitute_for_placeholders($Mcomment); $semantics = &convert_HTML($semantics); print OUTPUT "~", $Mname ,"() \&" , $semantics ,"\\\\ \n\n"; } $Msubstname = $Mname; $arg1 = &convert_M($Msubstname); $arg2 = &convert_text($Mcomment); local($text) = "\\destruct" .' {'. $arg1 ."\}\n\{". $arg2 ."\}\n"; print OUTPUT $text,"\n\n"; } @ \subsubsection{Printing an Enum} [[\$type]] contains the type, [[\$par_list]] contains the alternatives, and [[\$Mcomment]] contains the text. We output \verb-\enum{type}{par_list}-. The typesetting is done via a latex macro. <>= sub print_enum{ @args = ("par_list","type"); if ($mode eq "Fman") { print "enum $type \{ $par_list \}\n" ; if ($showsem == 1) { print &substitute_for_placeholders($Mcomment) , "\n\n"; } return; } $arg1 = $type; $arg1 =~ s/_/\\_/g; # quote underscores $arg2 = &convert_M($par_list); $arg3 = ""; if ($Mcomment && $showsem == 1) {$arg3 = &convert_text($Mcomment);} $text = "\\enum\{$arg1\}\n\{$arg2\}\n\{$arg3\}\n\n"; print OUTPUT $text; } @ \subsubsection{Printing a Typedef} [[\$type]] contains the type, [[\$fname]] contains the new type, and [[\$Mcomment]] contains the text. We output \verb-\typedef{type}{fname}{Mcomment}- or \verb-\typemember{type}{Mcomment}- . The typesetting is done via a latex macro. <>= sub print_typedef{ if ($mode eq "Fman") { print "typedef $type $fname\n" ; if ($showsem == 1) { print &substitute_for_placeholders($Mcomment) , "\n\n"; } return; } if ($mode =~ /HTML/) { $signature = "typedef $type $fname "; local($semantics) = &substitute_for_placeholders($Mcomment); $semantics = &convert_HTML($semantics); local($shortsig) = 1; if (length($signature) > 25) { $shortsig = 0; } $signature = &insert_URLs_and_quote($signature); &opentabular(2); if ($shortsig == 1) { print OUTPUT $signature , "\\ \& \\ " , $semantics , "\\\\ \n"; } else { print OUTPUT "\\multicolumn\{2\}\{l\}\{$signature\}\\\\ \n"; print OUTPUT " \& $semantics\\\\ \n"; } return; } $arg1 = &convert_M($type); $arg2 = &convert_M($fname); $typedeftext = ""; if ($Mcomment && $showsem == 1) { $typedeftext = &convert_text($Mcomment); } $text = "\\typedef\{$arg1\}\{$arg2\}\n\{$typedeftext\}\n\n"; print OUTPUT $text; } sub print_typemember{ if ($mode eq "Fman") { print "$type\n" ; if ($showsem == 1) { print &substitute_for_placeholders($Mcomment) , "\n\n"; } return; } if ($mode =~ /HTML/) { $signature = "$type "; local($semantics) = &substitute_for_placeholders($Mcomment); $semantics = &convert_HTML($semantics); local($shortsig) = 1; if (length($signature) > 25) { $shortsig = 0; } $signature = &insert_URLs_and_quote($signature); &opentabular(2); if ($shortsig == 1) { print OUTPUT $signature, "\\ \& \\ " , $semantics , "\\\\ \n"; } else { print OUTPUT "\\multicolumn\{2\}\{l\}\{$signature\}\\\\ \n"; print OUTPUT " \& $semantics\\\\ \n"; } return; } $arg1 = &convert_M($type); $typedeftext = ""; if ($Mcomment && $showsem == 1) { $typedeftext = &convert_text($Mcomment); } $text = "\\typemember\{$arg1\}\n\{$typedeftext\}\n\n"; print OUTPUT $text; } @ \subsection{Postamble and Calling LaTeX and weave.} The core of the output is produced at this point. We next generate the postamble and then call weave and/or latex. <>= if ($mode eq "Fman" ) { exit; } if ($mode =~ /HTML/) { &closetabular(); } if ($mode eq "HTMLman") { &closetabular(); print OUTPUT "\\end{document}\n"; } if ($mode eq "Lman") { print OUTPUT "\\end\{manual\}\n"; print OUTPUT "\\end{document}\n"; close OUTPUT; $owd = $ENV{"PWD"}; if ($latexruns == 0) { exit; } chdir ("/tmp"); &print_info("Preparing manual page with LaTeX..."); if ($outfile ne "/tmp/$pid-ext.tex") { if ($outfile =~ /\//) # absolute path name { system("cp $outfile /tmp/$pid-ext.tex");} else { system("cp $owd/$outfile /tmp/$pid-ext.tex");} } system ("latex /tmp/" . $pid . "-ext.tex" . $dontshowinfo); if ($latexruns == 2) { system ("latex /tmp/" . $pid . "-ext.tex" . $dontshowinfo); } if ($xdvi eq "yes") { &print_info("Starting xdvi previewer..."); system ("xdvi -s 3 /tmp/" . $pid . "-ext.dvi" . $dontshowinfo); } elsif ($dvioutfile ne "") { &print_info("Copying dvi file into file $dvioutfile in working directory..."); system("cp /tmp/$pid-ext.dvi $owd/$dvioutfile"); } else { &print_info("Copying dvi file into file $basename.dvi in working directory..."); system("cp /tmp/$pid-ext.dvi $owd/$basename.dvi"); } system("rm -f /tmp/" . $pid . "-ext.*"); chdir ("$owd"); } if ($mode eq "Mkman") { print OUTPUT "\\end{manual}\n"; } if ($mode eq "Ldoc") { close OUTPUT; open OUTPUT , ">>/tmp/$pid-ext.tex"; print OUTPUT "\\end{manual}\n"; close(OUTPUT); &print_info(" Created $basename.man"); &print_info("Removing the manual comments (except for Mpreamble) from $INPUT..."); $tempfile = "/tmp/" . $pid . "-$basename"; if ($delman eq "refined") { system("ext_ldel $INPUT $tempfile.$ext"); &print_info(" Created $tempfile.$ext through ext_ldel"); } if ($delman eq "yes" || $kind eq "Cweb") { if ($delman eq "no") { &print_info("Option delman=no not implemented for Cweb");} system("ldel $INPUT $tempfile.$ext"); &print_info(" Created $tempfile.$ext through ldel"); } if ($delman eq "no" && $kind ne "Cweb") { system("cp $INPUT $tempfile.$ext"); &print_info(" Created $tempfile.$ext by copying"); } system("mv /tmp/$pid-ext.tex $basename.man"); # move manpage &print_info("Calling $kind weave on $tempfile.$ext..."); # cweave produces its output in the working directory # lweave produces its output in the directory of the input # noweave produces its output on standard output if ($kind eq "Cweb") { system( "cweave $tempfile.$ext"); system( "mv $pid-$basename.scn $basename.scn"); system( "mv $pid-$basename.idx $basename.idx"); system( "mv $pid-$basename.tex $basename.tex"); } elsif ($kind eq "Lweb") { system( "lweave $tempfile $noweaveoptions"); system( "mv $tempfile.tex $basename.tex"); system( "mv $tempfile.nw $basename.nw"); } elsif ($kind eq "noweb") { system("noweave -delay $noweaveoptions $tempfile.nw > $basename.tex"); } &print_info(" Created $basename.tex"); if ($latexruns == 0) {exit;} &print_info("Calling latex..."); system("latex $basename.tex $dontshowinfo"); if ($latexruns == 2) { system("latex $basename.tex $dontshowinfo"); } if ($xdvi eq "yes") { &print_info("Starting xdvi previewer..."); system("xdvi -s 3 $basename.dvi $dontshowinfo"); } system("rm -f /tmp/$pid*"); } @ In order to have access to the value of PWD we need to include the package pwd.pl. <>= require "pwd.pl"; &initpwd; @ \subsection{Warnings and Error Messages} Error\_handler and print\_warning prints the current line number, the error message, and the current code unit and manual comment. If desired, both asks for an acknowledgement. The output of the former functions cannot be turned off. Print\_info displays progress information if switched on. <>= sub error_handler{ local($text) = @_; # read argument into local variable print STDERR "A problem occured near line " , $. ,"\n"; print STDERR "ERROR: ",$text,"\n\n"; print STDERR "The current code unit is:\n"; print STDERR $original_code_unit, "\n"; print STDERR "The current manual comment is:\n"; print STDERR $original_comment, "\n\n"; if ($ack eq "yes") { print STDERR "* "; read(STDIN,$meaningless,1); } } sub print_warning { if ($warnings eq "no" || $nextwarning eq "no") {return;} local($text) = @_; # read argument into local variable print STDERR "A problem occured near line " , $. ,"\n"; print STDERR "WARNING: ",$text,"\n\n"; print STDERR "The current code unit is:\n\n"; print STDERR $original_code_unit, "\n"; print STDERR "The current manual comment is:\n\n"; print STDERR $original_comment, "\n\n"; if ($ack eq "yes") { print STDERR "* "; read(STDIN,$meaningless,1); } } sub print_info { if ($informational eq "yes") { local($text) = @_; # read argument into local variable print "$text\n\n"; } } sub print_debug { if ($debugging eq "yes") { local($text) = @_; # read argument into local variable print "DEBUG $text\n"; } } @ \subsection{Helpers and Subroutines} The function remove\_enclosing\_blanks takes a string and removes all leading and trailing whitespace characters. The perl notation for whitespace is \verb-\s-. It is equivalent to blank, newline, carriage return, tab, and form feed. \verb-\S- is any non-whitespace character. The pattern below relies on the fact that perl makes maximal matches, i.e., the first \verb-\s*- matches a maximal prefix of whitespace characters. <>= sub remove_enclosing_blanks{ if ($_[0] =~ /^ *$/) {$_[0] = "";} else { $_[0] =~ / *(.*[^ ]) *$/; $_[0] = $1; } } @ In HTML-mode we need function to open and close tabulators. We have a global variable [[\$HTMLopentabulator]] which tells us whether a tabulator is open. <>= sub opentabular{ local($cols) = @_; # this is either two, three or four if ($HTMLopentabular eq "no") { if ($cols == 2) { print OUTPUT "\\begin{tabular}{ll}\n"; } elsif ($cols == 3) { print OUTPUT "\\begin{tabular}{lll}\n"; } elsif ($cols == 4) { print OUTPUT "\\begin{tabular}{llll}\n"; } else { &error_handler("open_tabulator expects argument 2 or 3"); } $HTMLopentabular = "yes"; } } sub closetabular{ if ($HTMLopentabular eq "yes") { print OUTPUT "\\end{tabular}\n\n"; $HTMLopentabular = "no"; } } @ \subsection{Procedure convert\_line} [[convert_line]] takes any string and applies C to LaTeX conversion to the C-text contained in it. C-text may be enclosed between vertical bars, in Mcode\{\ldots\}, between \DLK\ and \DRK\ brackets and in Tcode\{\ldots\}. We find the brackets and perform the appropriate conversion. [[convert_M]] does the math-like conversion and [[convert_T]] does the typewriter-like conversion. <>= sub convert_line{ local($text) = @_; # read argument into local variable local($output) = ""; local($lastchar) = chop($text); if ($lastchar eq "\n" || $lastchar eq "\r") { } else { $text .= $lastchar; } #last line of text maybe without linefeed iteration: while ($text) { if ($text =~ /^([^\\\[\|]+)/) { # does not start with [ or | or \ $text = $'; $output .= $1; if ($text eq "") { last iteration; } } # text is nonempty and starts with [ or | or \ if ($text =~ /^\\Mcode(.)/) { $text = $'; $delimiter=$1; if (!($text =~ /\\$1/)) # \ to protext meta characters { &print_warning("Mcode extends beyond end of line"); } $text = $'; $output .= &convert_M($`); next iteration; } if ($text =~ /^\\Tcode(.)/) { $text = $'; $delimiter=$1; if (!($text =~ /\\$1/)) # as above { &print_warning("Tcode extends beyond end of line"); } $text = $'; $output .= &convert_T($`); next iteration; } # next we deal with | in its exceptional meanings if ($text =~ /^\\begin\{tabular\}/) { $output .= $text; last iteration; } if ($text =~ /^\\left\|/) { $text = $'; $output .= "\\left\|"; } if ($text =~ /^\\right\|/) { $text = $'; $output .= "\\right\|"; } if ($text =~/^\|\|/) { $text = $'; $output .= "\|\|"; } if ($text =~ /^\|/) { $text = $'; if (!($text =~ /\|/)) { &print_warning("odd number of |:\n|$text\n\n"); } $text = $'; $output .= &convert_M($`); next iteration; } if ($text =~ /^\[\[/) { $text = $'; if (!($text =~ /\]\]/)) { &print_warning("encountered [[ without matching ]]:[[$text\n\n"); } $code = $`; $text = $'; while ($code =~ /\]\]/) { $code = $`; $text = $' . "]]" . $text; } $output .= &convert_T($code);; next iteration; } # text does not start with a special symbol. Move first symbol to output. $text =~ /^(.)/; $output .= $1; $text = $'; } return $output . "\n"; } @ \subsection{Procedure convert\_M} Procedure \verb-convert_M- typesets \CC statements and returns \verb-\mbox{$\mathit{transformed text}$}-. It is important to observe that this function makes no decisions about spacing. They are made somewhere else. For example, we decided to have a blank after each comma in a parameter list, but no blanks in type parameter lists of templates. Also \& is sometimes a binary operator and sometimes a unary operator. The decisions above require more knowledge about the context than is available at this point. Hence all these decisions are made somewhere else. The input is transformed according to the following rules:\\ quote all occurrences of \verb-&,%,_, ,<,>-, replace all occurrences of \verb-~- by \verb-\tildeop-, all occurences of \verb-^- by \verb-\circumflexop- all occurrences of \verb-@<<- by \verb-\ll-, all occurrences of \verb->>- by \verb-\gg-, and all occurences of :: by \verb-\DD-. Some users like indexed variables to be written with subscripts. We provide this feature as an option. We want to apply this transformation only to variables consisting of a single character followed by a number. We therefore search for the pattern [a-zA-Z][0-9]+, where the pattern either occurs at the beginning of the string or after a nonword character. We print the transformed string as \verb-\mbox{$\mathit{transformed text}$}-. The purpose of the mbox-command is to put LaTeX into non-math mode at the beginning of the C-Text and let it return to the original mode at the end of the Ctext. The net effect of the mbox command is no effect if the Ctext appears in ordinary mode and to erase the enclosing \$s if the Ctext appears in math-mode. Change on April 4: The above strategy is to simple. I do not want to have the entire text in mathitalics, e.g., in \verb-diff + 1- only diff should be in math italics. I therefore change the strategy as follows. I output \verb-\mbox{$transformed text}$}-, where the transformed text contains all identifiers in mathitalics. I also added one more layer of bracketing because \verb-\Litem\mbox{ }- does not work. One more difficulty with C-text is that the transformed test is a single long string. After all, we quote blanks. This leads to difficulties with functions with long argument lists. I want to allow a line break after every comma and semicolon. To do so I simulate the occurrence of two vertical bars after every comma or semicolon, in other words I replace ,blank by $|\ |$. This is only implemented for commas at the moment. On March 6th, I made the following change. I want that indentifiers that consist of a single symbol are typeset as in math mode. Therefore I only put identifiers of more than one symbol into mathit. <>= # the following procedure is copied from ext.nw. Please make changes only here. # I still need a way to maintain consistency with ext. sub convert_M{ local($Ctext) = @_; # read argument into local variable # we first work on identifiers local($prefix) = ""; local($suffix) = $Ctext; local($ident) = ""; identloop: while ($suffix =~ /^(\W*)(\w.*)$/) { $prefix .= $1; $suffix = $2; if ( $suffix =~ /^(\w+)(\W.*)$/) { $ident = $1; $suffix = $2; } else { $ident = $suffix; $suffix = ""; } if ($prefix =~ /\\$/) # For things like \n { if ($ident ne "n") { &print_warning("huch:\\ in quoted code"); print STDERR $Ctext;} $prefix .= "L" . $ident; next identloop; } if ($ident =~ /^[0-9]*$/ ) # just a number { $prefix .= $ident; next identloop;} if (($usesubscripts eq "yes") && ($ident =~ /^([a-zA-Z])([0-9]+)$/)) { $ident = $1 ."\\underscore\{$2\}"; } else { if ( length($ident) > 1 ) { $ident = "\\mathit\{$ident\}"; } } $prefix .= $ident; } $Ctext = $prefix . $suffix; $Ctext =~ s/&/\\&/g; $Ctext =~ s/_/\\nspaceunderscore\\_/g; # small negative space before _ $Ctext =~ s/\./\\nspacedot\./g; # small negative space before . $Ctext =~ s/::/\\DP /g; $Ctext =~ s/<>/\\gg/g; $Ctext =~ s/ *<= */\\Lle/g; $Ctext =~ s/ *>= */\\Lge/g; $Ctext =~ s/ *== */\\Leq/g; $Ctext =~ s/ *-> */\\Larrow/g; $Ctext =~ s/ *\+ */+/g; # LaTeX takes care of the spacing $Ctext =~ s/ *- */-/g; $Ctext =~ s/ *\* +/\*/g; if ($Ctext =~ /<.*>/) { # I guess that I discovered template brackets. Therefore, I interpret # all brackets in the text as template brackets and I keep the spacing # in the input. $Ctext =~ s//\\Ltemplategreater/g; } else { # there are no template brackets. I interpret < and > as in Latex and # leave the spacing to LATEX $Ctext =~ s/ *< */ */>/g; } $Ctext =~ s/ *\+= */\\Lass\{\+\}/g; $Ctext =~ s/ *\-= */\\Lass\{\-\}/g; $Ctext =~ s/ *\*= */\\Lass\{\*\}/g; $Ctext =~ s/ *\/= */\\Lass\{\/\}/g; $Ctext =~ s/ *\\&= */\\Lass\{\\&\}/g; $Ctext =~ s/ *!= */\\Lass\{\!\}/g; $Ctext =~ s/ *\|= */\\Lass\{\|\}/g; # All blanks that are still in Ctext are to be preserved and # are hence quoted. $Ctext =~ s/ /\\ /g; $Ctext =~ s/\+\+/\\Dplus /g; $Ctext =~ s/\-\-/\\Dminus /g; # $Ctext =~ s/!=/\\Noteq /g; $Ctext =~ s/\\Lle/\\Lle /g; $Ctext =~ s/\\Lge/\\Lge /g; $Ctext =~ s/\\Leq/\\Leq /g; $Ctext =~ s/\\Ltemplateless/\\Ltemplateless /g; $Ctext =~ s/\\Ltemplategreater/\\Ltemplategreater /g; $Ctext =~ s/\\Larrow/\\Larrow /g; #if ($Ctext =~ /]*)>(.*)$/) # innermost template brackets #{ local($C1) = $1; # local($C2) = $2; # local($C3) = $3; # if ($C2 =~ /\\Lless/) # template arg is template # { $C2 = "\\ " . $C2; } # insert blank after < # if ($C3 =~ /^>/) # this > is followed by > # { $C3 = "\\ " . $C3; } #insert blank before > # $Ctext = $C1. "\\Lless " . $C2. "\\Lgreater " . $C3; # $Ctext =~ s/(\S)/\\Lgreater \\ $1/g; # $Ctext =~ s/>/\\Lgreater /g; #} #} $Ctext =~ s/%/\\%/g; $Ctext =~ s/\^/\\circumflexop /g; $Ctext =~ s/~/\\tildeop /g; $Ctext =~ s/'/\\Lrquote /g; $Ctext =~ s/`/\\Llquote /g; $Ctext =~ s/\(\)/\(\\;\)/g; # a little space for empty argument list $Ctext =~ s/\,\\ /\,\$\}\n\\mbox\{\$/g; # replace ,blank by ,$}newline\mbox{$ to allow line breaks $Ctext = "\\mbox\{\$" . $Ctext . "\$\}"; $Ctext; } @ \subsection{Procedure Convert\_T} We only need to quote all special characters and to change the font to typewriter. <>= # the following procedure is copied from ext.nw. Please make changes only here. # I still need way to maintain consistency with ext. sub convert_T{ local($Ctext) = @_; # read argument into local variable $Ctext =~ s/&/\\&/g; $Ctext =~ s/_/\\_/g; $Ctext =~ s/{/\\{/g; $Ctext =~ s/}/\\}/g; $Ctext =~ s/%/\\%/g; $Ctext =~ s/\^/\\circumflexop /g; $Ctext =~ s/~/\\Tildeop /g; return "\{\\tt " . $Ctext . "\}"; } @ \subsection{Procedure Convert\_HTML} We want to remove quoted code and mathmode from the input and we want to deal with labels. We first remove quoted code and then remove math-mode. Finally we deal with the labels. <>= sub convert_HTML{ local($Ctext) = @_; # read argument into local variable $Ctext =~ s/\\begin\{Mverb\}/\\begin\{verbatim\}/g; $Ctext =~ s/\\end\{Mverb\}/\\end\{verbatim\}/g; $Ctext = &remove_quoted_code($Ctext); $Ctext = &remove_math_mode($Ctext); $Ctext = &deal_with_refs($Ctext); return $Ctext; } @ \subsubsection{Removing Quoted Code in Conversion to HTML} I am reusing the code from procedure [[convert_line]]. [[convert_line]] takes any string and applies C to LaTeX conversion to the C-text contained in it. C-text may be enclosed between vertical bars, in Mcode\{\ldots\}, between \DLK\ and \DRK\ brackets and in Tcode\{\ldots\}. We find the brackets and perform the appropriate conversion. [[convert_M]] does the math-like conversion and [[convert_T]] does the typewriter-like conversion. The code can be simplified since we are aiming for conversion to plane ASCII and not to LaTex. Essentially I only have to recognize quoted code, to remove the brackets, and to quote some special characters (namely \_ and \&). In other word I am reusing the code but call [[quote_special_symbols_in_quoted_code_removal]] instead of [[convert_M]]. <>= sub remove_quoted_code{ local($text) = @_; # read argument into local variable local($output) = ""; $text =~ s/\n/LEDANEWLINE/g; iteration: while ($text) { if ($text =~ /^([^\\\[\|]+)/) # does not start with [ or | or \ { $text = $'; $output .= $1; if ($text eq "") { last iteration;} } # text is nonempty and starts with [ or | or \ if ($text =~ /^\\Mcode(.)/) { $text = $'; $delimiter=$1; if (!($text =~ /\\$1/)) # \ to protext meta characters {&print_warning("Mcode extends beyond end of line");} $text = $'; $output .= "e_special_symbols_in_quoted_code_removal($`); next iteration; } if ($text =~ /^\\Tcode(.)/) { $text = $'; $delimiter=$1; if (!($text =~ /\\$1/)) # as above {&print_warning("Tcode extends beyond end of line");} $text = $'; $output .= &convert_T($`); next iteration; } # next we deal with | in its exceptional meanings if ($text =~ /^\\begin\{tabular\}/) { $output .= $text; last iteration; } if ($text =~ /^\\left\|/) { $text = $'; $output .= "\\left\|"; } if ($text =~ /^\\right\|/) { $text = $'; $output .= "\\right\|"; } if ($text =~ /^\|/) { $text = $'; if (!($text =~ /\|/)) { &print_warning("odd number of |:\n|$text\n\n"); } $text = $'; $output .= "e_special_symbols_in_quoted_code_removal($`); next iteration; } if ($text =~ /^\[\[/) { $text = $'; if (!($text =~ /\]\]/)) { &print_warning("encountered [[ without matching ]]:[[$text\n\n"); } $code = $`; $text = $'; while ($code =~ /\]\]/) { $code = $`; $text = $' . "]]" . $text; } $output .= &convert_T($code);; next iteration; } # text does not start with a special symbol. Move first symbol to output. $text =~ /^(.)/; $output .= $1; $text = $'; } $output =~ s/LEDANEWLINE/\n/g; return $output ; } sub quote_special_symbols_in_quoted_code_removal{ local($text) = @_; $text =~ s/_/\\_/g; $text =~ s/&/\\&/g; return $text; } @ \subsubsection{Removing Math-Mode in Conversion to HTML} I am not touching any display math mode. Displayed math is started by \verb+\[+ and ended by \verb+\]+. So if the text contains displayed math I simply return it. This is to simplistic as the text may contain [[\\]] outside the displayed math. This is not allowed in a tabular. I leave it for manual treatment. Moreover, latex does not like displayed math in a tabular environment. My new strategy is to simpy delete displayed math and warn the user that he is supposed to edit the file manually. For mathmode we search for the first two occurrences of \$. We take the stuff between the dollars and try to convert it. If we do not succeed we put it back in and report our failure in file [[unrecognizedmath]]. We follow a very simple strategy to remove math. We first treat \verb+\Labs,\mbox,\Litem,\Lvert,\ + and then we apply all the replacements in the texreplacementtable. If this removes all occurrences of \verb+\+ we claim success. For the occurance of [[\\]] in the [[\$semantics]] string we follow the following rule. If we are to end within a tabular row which is the case in any creation, destruction, function comment we put in a rawhtml line break via the call to [[quote_special_symbols_in_text_mode]]. For all tabular structures which need the linebreak symbols like [[eqnarray]] and [[array]] we keep them in the string for the latex run over the manual page. <>= sub remove_math_mode{ local($Ctext) = @_; $Ctext =~ s/\n/LEDANEWLINE/gi; if ( $Ctext =~ /array/ || $Ctext =~ /tabular/ ) { local($unprocessed) = $Ctext; local($transformed) = ""; while ( $unprocessed =~ s/\\end\{([^\}]*)\}(.*)// ) { local($latexenv) = $1; local($rest) = $2; if ( $latexenv =~ /array/ || $latexenv =~ /tabular/ ) { local($lenv) = $latexenv; $lenv =~ s/\*/\\\*/; # escape * environments $unprocessed =~ s/\\begin\{$lenv\}(.*)//e; local($between) = $1; # now $unprocessed $between $rest is the whole line $between =~ s/\\\\/LEDAARRAYNL/gi; $transformed .= ($unprocessed . "\\begin{$latexenv}" . $between . "\\end{$latexenv}"); $unprocessed = $rest; } else { $transformed .= ($unprocessed . "\\end{$latexenv}"); $unprocessed = $rest; } if ( 0 ) { # debug on or off print "\nXXXXXXXXXXXXXXXXXXXX\n"; print $transformed; print "\nXXXXXXXXXXXXXXXXXXXX\n"; print $unprocessed; print "\nXXXXXXXXXXXXXXXXXXXX\n"; } } $Ctext = $transformed . $unprocessed; } if (0 || $Ctext =~ /\\\[/ || $Ctext =~ /\$\$/) { # Ctext contains displayed math. &print_warning("I encountered displayed mathematics. I replaced it by ordinary math mode"); $Ctext =~ s/\\\[/\$/g; $Ctext =~ s/\\\]/\$/g; $Ctext =~ s/\$\$/\$/g; } local($unprocessed) = $Ctext; $Ctext = ""; while ($unprocessed =~ /^([^\$]*)\$([^\$]*)\$(.*)$/) { # text contains two or more dollars $Ctext .= "e_special_symbols_in_text_mode($1); local($math) = $2; local($math_original) = $2; $unprocessed = $3; # lets first do the complicated stuff while ($math =~ s/\\Labs\{([^\}]*)\}/\|$1\|/) {} while ($math =~ s/\\mbox\{([^\}]*)\}/$1/) {} while ($math =~ s/\\Litem\{([^\}]*)\}/<$1>/) {} $math =~ s/\\Lvert/\|/g; $math =~ s/\\ / /g; # protected blank in math mode foreach $key (keys(%texreplacementtable)) { local($replacement) = $texreplacementtable{$key}; $math =~ s/\\$key/$replacement/g; } $math =~ s/\\mathit\{([^\}]*)\}/$1/g; # print unprocessed tex commands if ($math =~ /\\/) { # unrecognized tex-command print UNRECOGNIZEDTEX $math, "\n\n"; $Ctext .= "\$" . $math_original . "\$"; } else { $Ctext .= "e_special_symbols_in_math_mode($math); } } $Ctext .= "e_special_symbols_in_text_mode($unprocessed); $Ctext =~ s/LEDANEWLINE/\n/gi; $Ctext =~ s/LEDAARRAYNL/\\\\/gi; return $Ctext; } @ The following conversion also servers some transformation concerning HTML typesetting. We replace in case of HTML the complicated command [[\headerline]] by a simple [[\textbf]] which serves our purposes perfectly. <>= sub quote_special_symbols_in_text_mode { local($Ctext) = @_; # read argument into local variable # it remains to quote all special characters. # $Ctext =~ s/\\\\LEDANEWLINE/\\HTML\{P\}\{\}LEDANEWLINE/g; $Ctext =~ s/\\\\/\\HTML\{BR\}\{\}/g; $Ctext =~ s/\\headerline/\\textbf/g; # stating latex2html 98.1 we can enforce a linebreak return $Ctext; } sub quote_special_symbols_in_math_mode { local($Ctext) = @_; # read argument into local variable # it remains to quote all special characters. $Ctext =~ s/_/\\_/g; $Ctext =~ s/&/\\&/g; $Ctext =~ s/\^/\\circumflexop /g; return $Ctext; } @ [[deal_with_refs]] looks for \verb-\ref-. It replaces it by a \verb-\hyperref-. Hyperref takes four arguments. The last argument is the label. The first argument appears in the html-document. The text output contains the second argument followed by the numerical reference followed by the third argument. I set the second and the first argument to the empty string. Thus [[see section \verb-\ref{graphs}-]] generates [[see section graph]] in the html-document and generates [[see section number]] in the paper document. <>= sub deal_with_refs{ local($Ctext) = @_; # read argument into local variable while ($Ctext =~ /\\ref\{([^\}]*)\}/) { local($key) = $1; $key1 = $key; $key1 =~ s/_/\\_/g; $Ctext =~ s/\\ref\{$key\}/\\hyperref{$key1}{}{}{$key}/; } return $Ctext; } @ [[insert_URLs_and_quote]] is applied to return types and to signatures of functions. They consist of strings of alphanumeric characters (including \_), blanks, round brackets, angular brackets, square brackets, kommas, equality signs, star, dots, colon, and ampersands. For each maximal alphanumeric substring that is in the namereplacementtable we need to add an \verb-\addnormallink{typename}{URL}-. The split character [[&]] need to be quoted. <>= sub insert_URLs_and_quote{ local($Ctext) = @_; local(@fields) = split(/([ \(\)\<\>\,\&\.\:\[\]=\*])/,$Ctext); foreach $i (0 .. $#fields) { if ($fields[$i] =~ /^\w*$/) { # alphanumeric string local($key) = $fields[$i]; local($label) = $namereplacementtable{$key}; if ($label ne "" && $key ne $Mtype) { $key =~ s/_/\\_/g; $fields[$i] = "\\htmlref\{$key\}\{$label\}"; } else { $key =~ s/_/\\_/g; $fields[$i] = $key; } } else { # quote some characters if ($fields[$i] =~ /&/) { $fields[$i] = "\\$fields[$i]"; } if ($fields[$i] =~ //) { $fields[$i] = "\$>\$"; } $fields[$i] =~ s/\\n/\\Ln/g; # takes care of \n } } $Ctext = join("",@fields); return $Ctext; } @ \subsection{Usage Information} All utilities give manual information if called without arguments. <>= sub print_usage { if ($mode eq "Lman" || $mode eq "Ldoc") { print "Usage is $mode file [options] Options are given in assignment syntax variable=value. There must be no blank on either side of the equality sign. We list all variables and their possible values below. For each variable the default value of each option is given first. size={12,11,10} constref={no,yes} partypes={no,yes} numbered={no,yes} xdvi={yes,no} warnings={yes,no} informational={yes,no} ack={yes,no} usesubscripts={no,yes} latexruns={1,2,0} delman={yes,no} filter={all,signatures,definition,types,creation, operations,implementation,example,opname} $mode can be customized by putting options in a file $mode.cfg in either the home directory or the working directory. Call $mode ltools for more information on the tools usage. $mode mancommands for a short overview of usable manual commands.\n\n"; } if ($mode eq "Fman") { print "Usage is $mode file filter where the file name is of the form T.[h|w|lw] and T is either the name of a LEDA type, e.g., list, sortseq, or point, or the name of a user defined data type. The value of filter is one of { all, signatures, definition, types, creation, operations, implementation, example, opname } Call $mode ltools for more information on the tools usage. $mode mancommands for a short overview of usable manual commands.\n\n" ; } exit; } sub print_mancommands { if ($mode eq "Lman" || $mode eq "Ldoc" || $mode eq "Fman") { print "Manpage start header is: /*{\\Manpage {DT} {T1,..,Tk} {short_description} [MVAR]}*/ afterwards common variables are \\Mname = DT \\Mvar = MVAR Manual sections are: /*{\\Mdefinition specification_text}*/ /*{\\Mtypes [W]}*/ /*{\\Mtypedef typedef_documentation}*/ /*{\\Mtypemember type_documentation}*/ /*{\\Menum type_documentation}*/ /*{\\Mcreation [MVAR] [W]}*/ /*{\\Mcreate constructor_specification }*/ /*{\\Mdesctruct desctructor_specification }*/ /*{\\Moperations [W1] [W2]}*/ /*{\\Mop semantic_description }*/ /*{\\Mstatic semantic_description }*/ /*{\\Mconversion semantic_description }*/ /*{\\Mbinop semantic_description }*/ /*{\\Munop semantic_description }*/ /*{\\Marrop semantic_description }*/ /*{\\Mfunc semantic_description }*/ /*{\\Mfunop semantic_description }*/ /*{\\Mfunobj semantic_description }*/ /*{\\Mbinopfunc semantic_description }*/ /*{\\Munopfunc semantic_description }*/ /*{\\Mfunobj{return_type} semantic_description }*/ /*{\\Mimplementation implemenation_information }*/ /*{\\Mexample example_description }*/ Additional man commands /*{\\Mtext some_text }*/ /*{\\Moptions nextwarning=no }*/ /*{\\Msubst pattern replacement }*/ Common tex macros for man comments are \\setopdims[2], \\precond[1], \\headerline[1] Verbatim code in example section with environment \\begin|end{Mverb}.\n\n"; } exit; } sub print_ltools { $owd = $ENV{"PWD"}; chdir ("/tmp"); local($pid)=$$; if ($mode eq "Fman") { system("cat \$LEDAROOT/Manual/MANUAL/DocTools.tex"); } else { $outfile = "/tmp/" . $pid . "-ext.tex"; open(OUTPUT,">".$outfile); print OUTPUT "\\documentclass\[11pt,a4paper\]\{article\}\n\n"; print OUTPUT "\\usepackage\{html\}"; print OUTPUT "\\input " . $ENV{"LEDAROOT"} . "/Manual/tex/MANUAL.pagesize\n\n"; print OUTPUT "\\input " . $ENV{"LEDAROOT"} . "/Manual/tex/MANUAL.mac\n\n"; print OUTPUT "\\begin\{document\}\n\n"; close(OUTPUT); system("cat \$LEDAROOT/Manual/MANUAL/DocTools.tex >> $outfile"); open(OUTPUT,">>".$outfile); print OUTPUT "\\end\{document\}\n\n"; close(OUTPUT); system ("latex /tmp/$pid-ext.tex $dontshowinfo"); system ("xdvi /tmp/$pid-ext.dvi $dontshowinfo"); system ("rm -f /tmp/$pid-ext.*"); } chdir ("$owd"); exit; } @ \section{The \LaTeX-Macro Files} <>= %the following settings are adequate for European size a4 paper \textwidth 16cm \textheight 24 cm \topmargin -14mm \evensidemargin 3mm \oddsidemargin 3mm %the following settings are adequate for US legal size paper %\textwidth 16cm %\textheight 24 cm %\topmargin -14mm %\evensidemargin 3mm %\oddsidemargin 3mm \sloppy <>= <> <> <> <
> <> <> <> <> <> @ \subsection{The Manual Environment} The manual environment redefines some basic parameters of LaTeX, namely parskip, parindent, and baselineskip. parskip governs the vertical space between paragraphs and parindent governs indentation at the beginning of a paragraph. In LaTeX the first quantity is zero and the second quantity is non-zero. The TeX file of Figure \ref{intermediate Tex file} consists of many short paragraphs, each paragraph being basically a LaTeX command. We therefore want no indentation but we want paragraphs to be separated. baselineskip controls the vertical distance between lines. We increase its value slightly. When leaving the manual environment we set all values back to their original values. This requires no code in LaTeX. When used in Ldoc this has the effect of using the parameter settings below for the manual and to use the standard Cweb settings for the remainder of the document. <>= \newcounter{manctr} \newenvironment{manual}{ \setcounter{manctr}{1} \baselineskip 3.0ex %\spaceskip .4em plus .25em minus .25em %\xspaceskip .65em \parskip 11pt plus 1pt minus 1pt \parindent 0pt }{} @ \subsection{ \TeX-Macros that typeset functions, operators, constructors and destructors} We define and initialize some width parameters that are used in the commands that typeset manual entries and then turn to the actual typesetting. \subsection{Some width variables} We define several length variables. These variables define the width of the various columns in the layout of the creation and the operations sections. The separation between columns is colsep. The layout for the creation-section uses the variables createtextwidth and declwidth, where\\ createtextwidthwidth = textwidth - declwithwidth. We use two-column layout if the length of the declaration is at most declwidth minus colsep, and use two-row layout otherwise. The layout of the operations-section uses the variables typewidth, callwidth, longcallwidth, and descriptwidth, where\\ longcallwidth = textwidth - typewidth and\\ descriptwidth = textwidth - typewidth - callwidth We use three-column layout if the width of the call is at most callwidth and use two-row layout otherwise. Again we make sure that the colums are separated by colsep. Manual entries for variables and constructors are separated by entrysep and the two rows of an entry in two-row layout are separated by rowsep. The default values are set as percentages of textwidth or as relative length. I hope that this will allow to use different font sizes and page sizes. <>= \newlength{\rowsep} \setlength{\rowsep}{0.7ex} %separation in two-row layout \newlength{\entrysep} \setlength{\entrysep}{0.2ex} %additional space between entries \newlength{\colsep} \settowidth{\colsep}{\ } \newlength{\typewidth} \setlength{\typewidth}{0.15\textwidth} \newlength{\longtypewidth} \setlength{\longtypewidth}{0.4\textwidth} \newlength{\callwidth} \setlength{\callwidth}{.25\textwidth} \newlength{\declwidth} \setlength{\declwidth}{0.25\textwidth} \newlength{\longcallwidth} \newlength{\descriptwidth} \newlength{\createtextwidth} \newlength{\textminusdescriptwidth} \newlength{\typepluscallwidth} \newcommand{\computewidths}{ \setlength{\createtextwidth}{\textwidth} \addtolength{\createtextwidth}{-\declwidth} \setlength{\longcallwidth}{\textwidth} \addtolength{\longcallwidth}{-\typewidth} \setlength{\descriptwidth}{\textwidth} \addtolength{\descriptwidth}{-\typewidth} \addtolength{\descriptwidth}{-\callwidth} \setlength{\textminusdescriptwidth}{\textwidth} \addtolength{\textminusdescriptwidth}{-\descriptwidth} \setlength{\typepluscallwidth}{\typewidth} \addtolength{\typepluscallwidth}{\callwidth} } \newcommand{\setopdims}[2]{ \setlength{\typewidth}{#1} %\typewidth=#1cm \setlength{\callwidth}{#2} %\callwidth=#2cm \computewidths } \computewidths % initializes all widths @ \subsection{Operations} We have two macros to typeset entries for functions and operators:\verb-\function- and \verb-\operator-. The second is a simplified version of the first and thus we discuss \verb-\function-. It has four arguments, the return type, the fname, the parameter list and the description. We need to make layout decisions based on the length of the various parameters. The if-then package comes handy to program the case distinction. Both commands use the LaTeX parbox command extensively. In particular, in two-row layout I generate a parbox for each row and also put the two rows into a parbox. Since parboxes are symbols this has the effect of disallowing page breaks between the two rows. We start out by checking the equation \verb-\typewidth + \callwidth + \descriptwidth = \textwidth-. It is easy to violate it without noticing. This produces strange layouts. I have once spent hours to discover the error. Our layout strategy is as follows. Assume first that the actual width of the return type plus colsep is larger than typewidth. Consider \verb-#1 #2(#3)-. This is the return type plus the function call. If its width plus colsep is larger than the width alloted for the first two columns we use two row layout and otherwise we use one row layout. In the first case it may even be the case that the function call does not fit into a single line. We therefore compute the width of \verb-#1 #2(-. This is assumed to fit into a line. We put it down and use the remaining width for the parameter list plus ). <>= \usepackage{ifthen} \newlength{\actualcallwidth} \newlength{\actualtypewidth} \newlength{\actualtypepluscallwidth} \newlength{\fnamewidth} \newlength{\checkwidth} \newcommand{\returntype}{\,} \newcommand{\function}[4]{ \setlength{\checkwidth}{\typewidth} \renewcommand{\returntype}{#1} \addtolength{\checkwidth}{\callwidth} \addtolength{\checkwidth}{\descriptwidth} \ifthenelse{\lengthtest{\checkwidth = \textwidth}} {} {\typein{WARNING: The invariant \typewidth + \callwidth + \descriptwidth = \textwidth is violated. Did you change one of the quantities without calling \protect\computewidths? I do it for you. If the output looks okay and you changed textwidth after reading in Lweb.sty it is safe to ignore this warning. Type any character to proceed.} \computewidths} \settowidth{\actualcallwidth}{#2(#3)} \settowidth{\actualtypewidth}{#1} \addtolength{\actualcallwidth}{\colsep} \addtolength{\actualtypewidth}{\colsep} \ifthenelse{\actualtypewidth > \longtypewidth} {\parbox[t]{\textwidth}{#1}\\ \renewcommand{\returntype}{\ } \settowidth{\actualtypewidth}{\returntype} \addtolength{\actualcallwidth}{\colsep} } {} %empty else case \ifthenelse{\actualtypewidth > \typewidth} { \settowidth{\actualtypepluscallwidth}{#1\ #2(#3)} \addtolength{\actualtypepluscallwidth}{\colsep} \ifthenelse{\actualtypepluscallwidth > \typepluscallwidth} {% begin then1 \settowidth{\fnamewidth}{#1\ #2(}% \setlength{\parlistwidth}{\textwidth}% \addtolength{\parlistwidth}{-\fnamewidth}% \noindent \parbox[t]{\textwidth}{% \parbox[t]{\fnamewidth}{#1\ #2(}% \parbox[t]{\parlistwidth}{\raggedright \sloppy #3)}\vspace{\rowsep}\\% \hspace*{\typewidth}\hfill\parbox[t]{\descriptwidth} {\sloppy #4 }% }%end big parbox }%end then1 {%else descript on same line \parbox[t]{\typepluscallwidth}{#1\ #2(#3)}% \parbox[t]{\descriptwidth}{\sloppy #4 }% }% end else descript on same line }% end return type does not fit {% else return type fits \ifthenelse{\actualcallwidth > \callwidth} {\settowidth{\fnamewidth}{#2(}% \setlength{\parlistwidth}{\longcallwidth}% \addtolength{\parlistwidth}{-\fnamewidth}% \noindent \parbox[t]{\textwidth}{% \parbox[t]{\typewidth}{\returntype}\parbox[t]{\fnamewidth}{#2(}% \parbox[t]{\parlistwidth}{\raggedright \sloppy #3)}\vspace{\rowsep}\\% \hspace*{\typewidth}\hfill\parbox[t]{\descriptwidth}{\sloppy #4 }% }%end big parbox }%end then { \noindent\parbox[t]{\typewidth}{\fussy \returntype}% \parbox[t]{\callwidth}{\raggedright \sloppy #2(#3)}%\hspace{\colsep} \parbox[t]{\descriptwidth}{\sloppy #4}% }%end else }%end return type fits \vspace{\entrysep} \par } \newcommand{\operator}[3]{% \smallskip \settowidth{\actualcallwidth}{#2} \settowidth{\actualtypewidth}{#1} \addtolength{\actualcallwidth}{\colsep} \addtolength{\actualtypewidth}{\colsep} \ifthenelse{\actualtypewidth > \typewidth} {%\typeout{WARNING: #1 is too long to fit into the box provided %for the return type. I adopt another layout style. } %\typein{\\Type anything to acknowledge.} \settowidth{\actualtypepluscallwidth}{#1\ #2} \addtolength{\actualtypepluscallwidth}{\colsep} \ifthenelse{\actualtypepluscallwidth > \typepluscallwidth} {\parbox[t]{\textwidth}{% \parbox[t]{\textwidth}{#1\ #2}\vspace{\rowsep}\\% \hspace*{\textminusdescriptwidth}\parbox[t]{\descriptwidth}{\sloppy #3}% } %end big parbox } {%else descript on same line \parbox[t]{\typepluscallwidth}{#1\ #2} \parbox[t]{\descriptwidth}{\sloppy #3}% } } {%else return type fits \ifthenelse{\actualcallwidth > \callwidth} { \noindent \parbox[t]{\textwidth}{% \parbox[t]{\typewidth}{\fussy #1}\parbox[t]{\longcallwidth}{\raggedright \sloppy #2}\vspace{\rowsep}\\% \hspace*{\textminusdescriptwidth}\parbox[t]{\descriptwidth}{\sloppy #3}% } %end big parbox }%end then { \noindent\parbox[t]{\typewidth}{\fussy #1}%\hspace{\colsep} \parbox[t]{\callwidth}{\raggedright \sloppy #2}%\hspace{\colsep}% \parbox[t]{\descriptwidth}{\sloppy #3}% }%end else }%end outermost else \vspace{\entrysep} \par } @ \subsection{Constructors} create has four arguments, the typename, the variable name, the parameter list and the text. We always put the text into a parbox of width createtextwidth. We measure the length of the declaration \verb-#1 #2;- or \verb-#1 #2(#3);- and use two-layout if the declaration is short. We use two-row layout otherwise. See the section on functions for more details. <>= \usepackage{ifthen} \newlength{\actualdeclwidth} \newlength{\parlistwidth} \newcommand{\decl}{\,} % was empty before but creates trouble 2html \newcommand{\createtype}{\,} % was empty before but creates trouble 2html \newlength{\actualtypeplusnamewidth} \newlength{\createtypewidth} \newcommand{\create}[4]{ \setlength{\checkwidth}{\declwidth} \addtolength{\checkwidth}{\createtextwidth} \ifthenelse{\lengthtest{\checkwidth = \textwidth}} {} {\typein{WARNING: The invariant \declwidth + \createtextwidth = \textwidth is violated. Did you change one of the quantities without calling \protect\computewidths? I do it for you. If the output looks okay and you changed textwidth after reading Lweb.sty it is safe to ignore this warning. Type any character to proceed.} \computewidths} \ifthenelse{\equal{#3}{}}% {\renewcommand{\decl}{#1\ \ #2;}}% {\renewcommand{\decl}{#1\ \ #2(#3);}}%\typeout{decl is \decl} \settowidth{\createtypewidth}{#1} \ifthenelse{\createtypewidth > \longtypewidth} {\parbox[t]{\textwidth}{#1}\\ \ifthenelse{\equal{#3}{}}% {\renewcommand{\decl}{\hspace*{\typewidth}#2;}}% {\renewcommand{\decl}{\hspace*{\typewidth}#2(#3);}} }{} \settowidth{\actualdeclwidth}{\decl} \addtolength{\actualdeclwidth}{\colsep} \ifthenelse {\actualdeclwidth > \declwidth} {%\typeout{decl is long} \ifthenelse{\actualdeclwidth > \textwidth} {%\typeout{decl is very long} \settowidth{\actualtypeplusnamewidth}{#1\ \ #2(}% \setlength{\parlistwidth}{\textwidth}% \addtolength{\parlistwidth}{-\actualtypeplusnamewidth}% \parbox[t]{\textwidth}{% \parbox[t]{\actualtypeplusnamewidth}{#1\ \ #2(}% \parbox[t]{\parlistwidth}{\raggedright #3);}% \vspace{\rowsep}\\% \hspace*{1cm}\hfill\parbox[t]{\createtextwidth}{\sloppy #4 }% }%end parbox } {%\typeout{decl is long} \parbox[t]{\textwidth}{% \parbox[t]{\textwidth}{\decl}\vspace{\rowsep}\\% \hspace*{1cm}\hfill\parbox[t]{\createtextwidth}{\sloppy #4 }% }%end parbox } }%end then {%\typeout{decl is short} \parbox[t]{\declwidth}{\decl}\parbox[t]{\createtextwidth}{\sloppy #4 }% }%end else \vspace{\entrysep}\par } @ \subsection{Destructors} destruct has two arguments, the variable name followed by the text. We always put the text into a parbox of width createtextwidth. We measure the length of the destruction \verb-delete #1;- and use two-column layout if the destruction is short. We use two-row layout otherwise. <<>>= \usepackage{ifthen} \newlength{\actualdestructwidth} \newcommand{\destruct}[2]{ \settowidth{\actualdestructwidth}{$\sim$#1()} \ifthenelse {\actualdestructwidth > \declwidth} { \noindent \parbox[t]{\textwidth}{% \parbox[t]{\textwidth}{$\sim$#1()}\vspace{\rowsep}\\% \hspace*{1cm}\hfill\parbox[t]{\createtextwidth}{\sloppy #2}% } } {\noindent\parbox[t]{\declwidth}{$\sim$#1()}% \parbox[t]{\createtextwidth}{\sloppy #2}% } \vspace{\entrysep}\par } @ \subsection{Enumerations and Typedefs} For the enums we have three arguments, the type, the list of alternatives, and the text. We put the first two arguments centered on a line and add the text below. <>= \newlength{\actualenumwidth} \newcommand{\enum}[3]{% \settowidth{\actualenumwidth}{enum #1\ \{\ #2\ \}} \ifthenelse {\actualenumwidth > \declwidth}{ \noindent\parbox[t]{\textwidth}{% \parbox[t]{\textwidth}{enum #1\ \{\ #2\ \}}\vspace{\rowsep}\\% \hspace*{1cm}\hfill\parbox[t]{\createtextwidth}{\sloppy #3}% }}% else {\noindent\parbox[t]{\declwidth}{enum #1\ \{\ #2\ \}}% \parbox[t]{\createtextwidth}{\sloppy #3}% }\par} \newcommand{\typemember}[2]{% \settowidth{\actualenumwidth}{#1} \addtolength{\actualenumwidth}{\colsep} \ifthenelse {\actualenumwidth > \declwidth}{ \noindent\parbox[t]{\textwidth}{% \parbox[t]{\textwidth}{#1}\vspace{\rowsep}\\% \hspace*{1cm}\hfill\parbox[t]{\createtextwidth}{\sloppy #2}% }} {\noindent\parbox[t]{\declwidth}{#1}% \parbox[t]{\createtextwidth}{\sloppy #2}% }\par} \newcommand{\typedef}[3]{% \settowidth{\actualenumwidth}{typedef #1 #2} \ifthenelse {\actualenumwidth > \declwidth}{ \noindent\parbox[t]{\textwidth}{% \parbox[t]{\textwidth}{typedef #1 #2}\vspace{\rowsep}\\% \hspace*{1cm}\hfill\parbox[t]{\createtextwidth}{\sloppy #3}% }}% else {\noindent\parbox[t]{\declwidth}{typedef #1\ #2\ }% \parbox[t]{\createtextwidth}{\sloppy #3}% }\par} @ \subsection{Items and Accronyms} <>= \newcommand{\CC}{C\raise.06ex\hbox{\tt ++}} \newcommand{\CCC}{C\raise.08ex\hbox{\tt ++}} \newcommand{\gpp}{g\hbox{\tt ++\ }} \newcommand{\nat}{\hbox{\rm\vrule\kern-0.045em N}} \newcommand{\real}{\hbox{\rm\vrule\kern-0.035em R}} \newcommand{\sset}[1]{\{\hspace{0.05em}#1\hspace{0.05em} \}} \newcommand{\set}[2]{ \left\{\hspace{0.1em} #1 \mbox{ ; } #2 \hspace{0.1em} \right\}} \newcommand{\range}[2]{[#1 \, .. \, #2]} \newcommand{\precond}{{\it Precondition}: } \newcommand{\Labs}[1]{\hbox{$|\,#1\,|$}} \newcommand{\Lvert}{|} \newcommand{\Litem}[1]{\hbox{$\langle #1 \rangle$}} \newcommand{\Lchunk}[1]{$\langle${\it #1}$\rangle$} @ \subsection{Macros for Header Lines} The commands for header lines all have the same format. They typeset the headerline in bold-face and put some stuff around it to control page breaks and distances. They disallow a pagebreak after the header line and encourage pagebreaks before the header line. The empty line after the nopagebreak command is crucial. It ensures that the text following the command is not in the same paragraph as the expanded command. <
>= \newcommand{\headerline}[1]{ \smallskip\par \parbox[t]{\textwidth}{{\bf #1}}\par \nopagebreak } %empty line before bracket is crucial \newcommand{\definition}{ \setcounter{manctr}{1} \headerline{\arabic{manctr}. Definition} \stepcounter{manctr}} \newcommand{\creation}{ \headerline{\arabic{manctr}. Creation} \stepcounter{manctr}} \newcommand{\types}{ \headerline{\arabic{manctr}. Types} \stepcounter{manctr}} \newcommand{\operations}{ \headerline{\arabic{manctr}. Operations} \stepcounter{manctr}} \newcommand{\implementation}{ \headerline{\arabic{manctr}. Implementation} \stepcounter{manctr}} \newcommand{\example}{ \headerline{\arabic{manctr}. Example} \stepcounter{manctr}} @ \subsection{Special Symbols} And here are the corresponding \TeX\ commands. <>= \newcommand{\Lless}{\hbox{\tt <}} \newcommand{\Lgreater}{\hbox{\tt >}} \newcommand{\Ltemplateless}{\hbox{\tt <}} \newcommand{\Ltemplategreater}{\hbox{\tt >}} \newcommand{\<}{\hbox{\tt <}} \renewcommand{\>}{\hbox{\tt >}} \newcommand{\underscore}{_} \newcommand{\Lle}{\le} \newcommand{\Lge}{\ge} \newcommand{\Dplus}{{++}} \newcommand{\Dminus}{{--}} \newcommand{\Lass}[1]{\mathrel{{#1}{=}}} \newcommand{\Lminuseq}{{-}{=}} \newcommand{\Noteq}{{!}{=}} %\newcommand{\Leq}{\equiv} \newcommand{\Leq}{\mathbin {{=}{=}}} \newcommand{\Larrow}{\rightarrow} \newcommand{\tildeop}{{\sim}} \newcommand{\Tildeop}{{\char126}} \newcommand{\circumflexop}{\hbox{\^{}}} \newcommand{\DP}{\hspace{.1em}{::}\hspace{-.2em}} % symbol for :: \newcommand{\nspacedot}{\hspace{-0.09em}} \newcommand{\nspaceunderscore}{\hspace{-0.13em}} \newcommand{\Lrquote}{\mbox{'}} \newcommand{\Llquote}{\mbox{`}} \newcommand{\KLL}{@$\Lless\Lless$} % geaendert von \<\< \newcommand{\KGG}{@$\Lgreater\Lgreater$} \newcommand{\DLK}{{\tt [[}} \newcommand{\DRK}{{\tt ]]}} \newcommand{\Ln}{ \mbox{$\backslash${\tt n}} } @ \subsection{Old Macros} Some of the stuff that follows can be purged. <>= \newcommand{\Mdefinition}{{\bf $\backslash$ Mdefinition}\\} \newcommand{\Mcreation}{{\bf $\backslash$ Mcreation}\\} \newcommand{\Mtypes}{{\bf $\backslash$ Mtypes}\\} \newcommand{\Moperations}{{\bf $\backslash$ Moperations}\\} \newcommand{\Mimplementation}{{\bf $\backslash$ Mimplementation}\\} \newcommand{\Mexample}{{\bf $\backslash$ Mexample}\\} \newcommand{\Mcreate}{{\bf $\backslash$ Mcreate}\\} \newcommand{\Mop}{{\bf $\backslash$ Mop} \hspace{1cm}} \newcommand{\Mopl}{{\bf $\backslash$ Mopl} \hspace{1cm}} \newcommand{\Mbinop}{{\bf $\backslash$ Mbinop} \hspace{1cm}} \newcommand{\Marrop}{{\bf $\backslash$ Marrop} \hspace{1cm}} \newcommand{\Mfunop}{{\bf $\backslash$ Mfunop} \hspace{1cm}} \newcommand{\Munop}{{\bf $\backslash$ Munop} \hspace{1cm}} \newcommand{\Mfunc}{{\bf $\backslash$ Mfunc} \hspace{1cm}} \newcommand{\Mfuncl}{{\bf $\backslash$ Mfunc} \hspace{1cm}} \newcommand{\Mbinopfunc}{{\bf $\backslash$ Mbinopfunc} \hspace{1cm}} \newcommand{\Munopfunc}{{\bf $\backslash$ Munopfunc} \hspace{1cm}} \newcommand{\If}{{\bf if} } \newcommand{\Fi}{{\bf fi} } \newcommand{\Then}{{\bf then} } \newcommand{\Else}{{\bf else} } \newcommand{\Do}{{\bf do} } \newcommand{\Od}{{\bf od} } \newcommand{\For}{{\bf for} } \newcommand{\While}{{\bf while} } \newcommand{\Return}{{\bf return} } \newcommand{\Break}{{\bf return} } \newcommand{\Continue}{{\bf continue} } \newcommand{\Case}{{\bf case} } \newcommand{\Switch}{{\bf switch} } \newcommand{\Class}{{\bf class} } \newcommand{\Struct}{{\bf struct} } \newcommand{\Public}{{\bf public} } \newcommand{\Friend}{{\bf friend} } \newcommand{\Typedef}{{\bf typedef} } \newcommand{\n}{\backslash n} \newcommand{\co}{\mbox{//}} % LEDA macros: \newcommand{\Forall}{{\bf forall} } \newcommand{\Forallnodes}{{\bf forall\_nodes} } \newcommand{\Foralledges}{{\bf forall\_edges} } \newcommand{\Foralladjnodes}{{\bf forall\_adj\_nodes} } \newcommand{\Foralladjedges}{{\bf forall\_adj\_edges} } % verbatim \font\ttbig= cmtt10 scaled \magstephalf \chardef\other=12 \newcommand{\ttverbatim} { \parskip 5pt \catcode`\&=\other \catcode`\{=\other \catcode`\}=\other %\catcode'\\=\other \catcode`\$=\other \catcode`\&=\other \catcode`\#=\other \catcode`\%=\other \catcode`\~=\other \catcode`\_=\other \catcode`\^=\other \obeyspaces \obeylines} % usage: % %\begingroup % \ttbig % {\obeyspaces\gdef {\ }} % \ttverbatim % % ... % %\endgroup @ \section{Additional Scripts - Pre and Postprocessing} \subsection{Cleaning up Manual Comments - ldel and ext\_ldel} [[ldel infile outfile]] reads a file, deletes all manual comments from it (except Mpreamble comments) and writes the output to outfile. <>= #!/bin/sh -f perl $LEDAROOT/Manual/cmd/ldel.pl $1 $2 <>= <> $INPUT = $ARGV[0] && shift; $OUTPUT = $ARGV[0]; if ($OUTPUT eq "") { print " Usage is ldel infile outfile Removes manual comments from infile.\n"; exit; } open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; open(OUTPUT,">$OUTPUT"); while () { # The current line is either output or starts a manual comment $preamble = 0; if ( / *\/\*\{\\M/ || / *\/\*\{\\X/ ) { # current line contains the begin of a manual comment # we need to distinguish cases: if the manual comment is # Mpreamble then we proceed as in the manual extraction and # otherwise we simply delete the manual comment. if ( s/ *\/\*\{\\Mpreamble// ) { $Mcomment = $_; $preamble = 1; } # we scan lines until we find the end of a manual comment while ($_ && (! ( /\}\*\// ) ) ) { if ($preamble == 1) { $Mcomment .= $_; } $_ = ; } # the current line contains the end of a manual comment if ($preamble == 1) { s/\}\*\/ *//; $Mcomment .= $_; &print_unit(&convert_text($Mcomment)); } } elsif ( / *\/\*.*Mkillcomment/ ) { while ($_ && (! ( /\*\// ))) { $_ = ; } } else { print OUTPUT $_; } } close(OUTPUT); @ On November 5, 96 I extendend delman. I added the possiblility to leave the manual comments in the code and to typeset them using LaTeX. I use the following strategy. Whenever I see a comment of the form \verb-/*{\M ... }*/- or \verb-/*{\X ... }*/ which is followed by C-code I output \begin{verbatim} @ \semantics{ ... } <>= \end{verbatim} where chunk is the currently defined chunk and whenever I see a comment of the form above which is followed by non-C-text, i.e., by [[@ ]] or by [[<>=]], I output \begin{verbatim} @ \semantics{ ... } \end{verbatim} This is strategy is easily implemented by a finite automaton. I read the input file line by line. Whenever I encounter a chunk definition line [[<<....>>=]] I remember the line. Whenever I find a comment I read the comment in full. If it is a preamble comment I output it, if it is a text comment I ignore it and otherwise I output \verb-\semantics{...}-. I continue reading until I find the first non-empty line after the manual comment. If it begin with [[@ ]] or with [[<<...>>=]] I am in ground state again. If not I print the chunk definition line and continue. <>= perl $LEDAROOT/Manual/cmd/ext_ldel.pl $1 $2 <>= <> $INPUT = $ARGV[0] && shift; $OUTPUT = $ARGV[0]; if ($OUTPUT eq "") { print "usage ldel infile outfile removes manual comments from infile\n"; die; } $chunk_name = ""; open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; open(OUTPUT,">$OUTPUT"); while () { ground_state: $Mpreamble = 0; $Mignore = 0; $Manpage = 0; if (/\<\<.*\>\>=/) { # we read a chunk name and remember it $chunk_name = $_; print OUTPUT $_; next; } if ( / *\/\*\{\\M/ || / *\/\*\{\\X/) { # current line contains the begin of a manual comment # we determine whether we are dealing with a preamble comment # or a text comment # and we delete the header of the comment if ( /Mpreamble/ ) { $Mpreamble = 1; } if ( /Mtext/ || /Manpage/ || /Mdefinition/ || /Mtypes/ || /Moperations/ ||/Mimplementation/ || /Mcreation/ || /Moptions/ || /Msubst/ || /Mexample/ ) { $Mignore = 1; } if ( /Manpage/ ) { $Manpage = 1; $Manpageline = $_; while ($_ && (! ( /\}\*\// ) ) ) { $_ = ; chop $Manpageline; if (/\} +\*\//) { &print_warning("encountered } */ in manual comment. Did you mean }*/ ?");} $Manpageline .= $_; } if (! $_){ die "Error: missing end comment\n"; } $_ = $Manpageline; } s/ *\/\*\{\\\S*//; # remove the manual comment if ($Manpage == 1) { /\{([^\{\}]*)\}\s*\{([^\{\}]*)\}\s*\{([^\{\}]*)\}\s*\{([^\{\}]*)\}/; $Mtype = $1; $par_list = $2; $title = $3; $Mvar = $4; if ($par_list =~ /^ *$/) { $Mname = $Mtype; } else { # remove excessive blanks in parlist $par_list =~ s/ //g; $Mname = $Mtype."<".$par_list.">"; } $Manpage = 0; } $Mcomment = ""; # we scan lines until we find the end of a manual comment while ($_ && (! ( /\}\*\// ) ) ) { $Mcomment .= $_; $_ = ; } # the current line contains the end of a manual comment s/\}\*\/ *//; $Mcomment .= $_; if ($Mpreamble == 1) { &print_unit(&convert_text($Mcomment)); next; } if ($Mignore == 1) { next; } # no output # we are in the standard case and want to output the appropriate stuff print OUTPUT "\@ \\semantics{", &convert_text($Mcomment), "}\n"; $_ = ; while ($_ && /^\s*$/) { $_ = ; } # we are in the first non-empty line after the manual comment if (!(/^\@/ || /^\<\<.*\>\>=/)) # current chunk is continued { print OUTPUT $chunk_name, "\n"; } goto ground_state; } print OUTPUT $_; } close(OUTPUT); @ We also need a new manual macro. <>= \newlength{\setspacing} \newlength{\semwidth} \setlength{\semwidth}{\textwidth} \newlength{\semindent} \settowidth{\semindent}{{\bf Semantics}:\ } \addtolength{\semindent}{\parindent} \addtolength{\semwidth}{-\semindent} \newcommand{\semantics}[1]{ \nopagebreak%\hspace{\codemargin} {\bf Semantics}:\ \parbox[t]{\semwidth}{#1} \vspace{2ex} } @ \subsection{The lweave Command} Lweave converts an Lweb file into a tex file. It operates in three phases \begin{verbatim} foo.lw -----> foo.nw --------------> temp.tex --------> foo.tex lw2nw noweave -delay emptyline \end{verbatim} The first phase takes care of unnamed code sections and quoted code and the third phase takes care of empty lines in code. The second phase calls noweave. The option delay means that we generate the preamble and postamble for LaTeX ourselves. <>= #! /bin/sh if [ "$1" = "" ] then echo "" echo "Usage is" echo " lweave foo[.lw] [noweave_options]" echo "" echo "Converts an Lweb file foo.lw into a LaTeX file foo.tex." exit fi file=`basename $1 .lw` location=`dirname $1` name="$location/$file" if [ -f $name.lw ] then echo "Calling lw2nw on $name.lw ..." echo "" lw2nw $name.lw > $name.nw shift echo "Calling noweave -delay on $name.nw $* ..." echo "" noweave -delay "$@" $name.nw > $name.temp echo "Treating empty lines in code and taking care of pagebreaks in $name.tex ..." echo "" emptyline $name.temp > $name.tex else echo "Error: File $name.lw does not exist." echo "" exit fi <>= #! /bin/sh if [ "$1" = "" ] then echo "" echo "Usage is" echo " lw2dvi foo[.lw]" echo "" echo "Converts an Lweb file foo.lw into a dvi file foo.dvi." exit fi file=`basename $1 .lw` location=`dirname $1` name="$location/$file" if [ ! -f $name.lw ] then echo "Error: File $name.lw does not exist." echo "" exit fi lweave $1 echo "Calling latex..." echo "" latex $1.tex <>= perl $LEDAROOT/Manual/cmd/lw2nw.pl $1 <>= perl $LEDAROOT/Manual/cmd/emptyline.pl $1 @ [[lw2nw.pl]] has to deal with unnamed code sections and quoted code. An unnamed code section has the form \begin{verbatim} @c Zeile 1 . . . Zeile n This empty line may or may not exist @ No comes text... \end{verbatim} I replace it by \begin{verbatim} \begin{Lcode} [ [Zeile 1] ] . . . [ [Zeile n] ] \end{Lcode} Jetzt kommt Text... \end{verbatim} where Lcode is a new environment. Lcode uses the corresponding noweb environment and ensures proper spacing to the text above and below. Also I set codemargin to parindent. This ensures that code and text is indented the same. This must be in a style file. <>= <> <> <>= \usepackage{noweb} \setlength{\codemargin}{\parindent} <> \def\nwendcode{\endtrivlist \endgroup } \nwcodepenalty=0 \let\nwdocspar=\par%\filbreak \let\nowebsize=\small%selects small font for code %\nwcodetopsep = 3pt plus 1.2pt minus 1pt so stehts im Orginal \nwcodetopsep = 10pt plus .5pt minus .5pt <> \newcommand{\noeffect}{} \newenvironment{Lcode}{% \addtolength{\topsep}{-2.5ex}% \@begincode } {\endtrivlist \vspace{0.5ex} % Leerzeile ist wichtig?? } @ Let us see the details of [[lw2nw.pl]] In document chunks it applies [[convert_line]] to each line. In unnamed program chunks it converts @c to begin\{Lcode\} and inserts the end\{Lcode\} at the end of the code. If there is an empty line at the end of the code I simply put it there. If there is no empty line I add it to the preceding line. In either case this does not corrupt the line count. Named code sections are converted to noweb style, i.e. @\< is replaced by \<\< and @\> is replaced by \>\>. Also += is replaced by =. Noweave does not like \_ in junk names, although notangle handles them without any problems. I therefore quote underscores in junk names. <>= <> $INPUT = $ARGV[0] && shift; if ($INPUT eq "") { print " Usage is lw2nw file Converts foo.lw to foo.nw.\n"; exit; } open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; while () { if (/^\\begin{verbatim}/) { # print until I encounter end{verbatim} while (! /\\end{verbatim}/ ) { print $_; $_ = ; } print $_; $_ = ; } if (/^\@c/) # unnamed code section { # I read with one line lookahead because I want to remove empty lines # at the end of codechunks. $_ = ; $previous = "\\begin{Lcode}"; if (!$_) { die "Error: File terminated without @ \\end{document}";} while (!/^\@\s/) { $current = $_; print $previous . "\n"; chop($current); # der reine Code ohne newline if ($current =~ /\<\<(.*)\>\>/) # quote underscore in junk name { $junk_name = $1; $cleft = $` . "<<"; $cright = ">>" .$'; $junk_name =~ s/_/\\_/g; $current = $cleft .$junk_name . $cright; } $previous = "[[" .$current . "]]" ; $_ = ; if (!$_) { die "Error: File terminated without @ \\end{document}";} } # current line starts text after code if ($previous =~ /[^\s\[\]]/) { print $previous . "\\end{Lcode}\n";} else { print "\\end{Lcode}\n"; } } # current line starts with @space (if condition applied) if (/^\@[\(\<](.*)\@> *\+ *=/) # named code section: Cweb-style { $_ = "\<\<" . $1 ."\>\>=";} # convert to Noweb style if (/\<\<.*\>\>\+ *=/) {s/\+ *=/=/;} # correct common mistake: replace >>+ = by >>= if (/\<\<.*\>\>=/) { while (!/^\@\s/) { # I print line after treating applied occurrences # of Cweb-chunks s/\@\<(.*)\@\>/\<\<$1\>\>/; if (/\<\<(.*)\>\>/) # quote underscore in junk name { $junk_name = $1; $cleft = $` . "<<"; $cright = ">>" .$'; $junk_name =~ s/_/\\_/g; $_ = $cleft .$junk_name . $cright; } print; $_ = ; if (!$_) { die "Error: file terminated without @ \\end{document}";} } } # current line is a text line. print &convert_line($_); } close(); @ \subsection{Empty lines in code sections and page breaks} Empty lines in code chunks generate empty vertical space that I find too big. I solve the problem with a postprocessor that works on the file generated by noweave. Empty line occur in two forms. In unnamed code sections they appear as \verb-{\tt{}\_\_\_\_}-. Unnamed code chunks start with \verb-\begin{Lcode}-. In named code chunks they appear as empty lines. Named code chunks start with nwbegincode. In a first attempt I simply replaced empty lines by negative vertical space. This has an undesirable side effect. Within code chunks noweb puts LateX in observeline mode. In this mode each line is a paragraph and hence an empty line was converted into a paragraph of negative height. If such a paragraph falls on a line break it is always put on the old page. This causes problems with the textheight. For example footnotes were printed on top of the last line of text. In my new solution I replace an emptyline by \verb-\vspace{some positive value}-, which I make a prefix of the subsequent line. This has the additional effect that the space does not appear at the top of a page. It however corrupts the line count. I correct the line count by adding an appropriate number of lines containing only \% after the code chuck. Such lines have no effect with respect to LaTeX and restore the line count. I have also tried to replace empty lines in code by \verb-\vspace{some positive value}%-; this does not work because \% is not treated as a LaTeX comment in code sections. I have to be a bit careful where I insert the comment lines. I frequently define two code chucks right after another. I stay in code mode if the line containing nwendcode not also containes a nwbegincode. Noweb uses the following rule for page breaking. A code section together with the preceding document chunk are considered a unit. All attempts are made to keep them together. In other words, a unit is placed on a partially complete page only if it fits completely on the page. This rule generates a lot of pages that are only partially full. We need a more flexible rule for the book. Noweb implements this rule by issuing filbreak commands at appropriate places and by using a very high page breaking penalty within code sections. Filbreak commands are issued at the end of every code section and at the beginning of document chunks that start a new paragraph (i.e., start with @newline). It will not do to simply remove the filbreak command. Recall that every line of code is its own paragraph. Thus I will have many pagebreaks within code sections. I want no pagebreaks within the first three and the last three line of a code section. So I insert nobreak commands at these places. Also I do not discourage page breaks within code as much as noweb and therefore set penalty to lowpenalty. This does not work as I expected. Note that code is processed in obeylines environment. In this context each newline is an implicite \tc{par}-command. Norman Ramsey redefines this command as \begin{verbatim} \@@par \def\par{\leavevmode\null \@@par \penalty\nwcodepenalty}% \end{verbatim} i.e., a par-command expands to an old par command followed by a penalty as given by nwcodepenalty. I set this value at the end of every code line: to a very high value for the first three and last three lines and to a low value for all other lines. The values are given by \tc{Lhighpen}\ and \tc{Llowpen}. After some experiments I settled for values 10000 and -30. I redefine three commands in noweb.sty. First the standard value of nwcodepenalty is set to 0 instead of highpenalty. This allows line breaks within code. Secondly I remove the filbreak command form nwendcode and nwdocspar and set nwdocspar to par. <>= \def\nwendcode{\endtrivlist \endgroup } %\filbreak \nwcodepenalty=0 %\@highpenalty \let\nwdocspar=\par%\filbreak <>= $INPUT = $ARGV[0] && shift; if ($INPUT eq "") { print "Usage is emptyline filename\n"; exit; } open (INPUT) || die "Error: Can't find input file $INPUT: $!\n"; $anzahl = 0; while () { $codechunk = ""; if (/nwbegincode/) { $line = $_; chop($line); print $line,"\\nwcodepenalty=\\Lhighpen\n"; $_ = ; # $anzahl = 0; while (!/nwendcode/ || /nwbegincode/) { if (/\S/) {$codechunk .= $_;} else {$codechunk .= "\\vspace{\\Lemptyline}"; $anzahl++;} $_ = ; } } if (/begin{Lcode}/) { print; $_ = ; # $anzahl = 0; while (!/end{Lcode}/) { if (/^[\{\}\\ t]*$/) {$codechunk .= "\\vspace{\\Lemptyline}"; $anzahl++;} else {$codechunk .= $_;} $_ = ; } } if ($codechunk) { $nlines = ($codechunk =~ tr/\n/\n/); $lcount = 0; while ($codechunk =~ /^([^\n]*)\n/) { $line = $1; $lcount++; $codechunk = $'; if ($lcount <= 3 || ($lcount >= $nlines - 3 && $lcount < $nlines)) {print $line, "\\nwcodepenalty=\\Lhighpen\n";} else {print $line , "\\nwcodepenalty=\\Llowpen\n";} } print $codechunk; } print; # Prints text and nwendcode and end{Lcode} respectively. while ($anzahl > 0) {print "\%\n"; $anzahl--;} } close(); <>= \newlength{\Lemptyline} \setlength{\Lemptyline}{1ex} \newcount\Lhighpen \Lhighpen=10000 \newcount\Llowpen \Llowpen=-30 @ \section{Installation Remarks} To use this package you have to \begin{enumerate} \item set [[LEDAROOT]] to the root directory of your actual LEDA installation. \item extend your path by [[\$LEDAROOT/Manual/cmd]] \item extend your TEXINPUTS by [[\$LEDAROOT/Manual/tex]] \end{enumerate} The tools are based on \begin{itemize} \item Perl >5.0 \item Noweb \item LaTeX2HTML > 98.1 \end{itemize} \section{Changes} I record the changes made after the end of the trial phase. \begin{itemize} \item July 10, 96: xdvi=no and latexruns >= 1 implies that the produced dvi-file is copied into T.dvi in the working directory. This required to add one statement. \verb+cp /tmp/... $owd/$basename.dvi+. \item July 10, 96: \begin{verbatim} wir (Stefan und Christian) mussten um dynamic link libraries unter Windows zu erzeugen 3 Macros in die Header einfuegen: __exportC, __exportF, __exportD sie koennen bei Klassen, Funktionen und Variablen wie folgt auftauchen: class __exportC A { ... }; type __exportF func() type __exportD var; In der Unix-Version wollen wir die Macros vor Auslieferung herausfiltern, aber in unserer Arbeitsversion stehen sie drin, und insbesondere mit dem __exportF kommt Lman natuerlich nicht zurecht. \end{verbatim} I use the following solution. Whenever a code unit has been built I remove all words in the list [[invisible_words]] from the code unit. This is done in section [[determine code unit]]. The list is also defined there. \item July 11, 96: discovered that Fman handles the filter opname incorrectly. Corrected it. \item Every call of Lman starts from scratch. extracts the manual, calls latex, and finally xdvi. I introduce a directory /MANUAL/DVI where I store all dvi-files. The utility Mkdvi makes all dvi-files. Lman (with option xdvi=yes) first looks up whether the dvi-file exists and if so displays it. \item July 20, 96: I extended Fman to allow for approximate searching of opnames. I first do it in a very rudimentary way. I report any operation that contains the opname argument. \item July 23, 96: Michael Seel asks for three extensions. \begin{itemize} \item he wants to give options to noweave. I introduce noweave options. They can only be set in the Ldoc.cfg file. \item Msubst is not gereral enough. He wants be able to replace [[CGAL_Integer_vector]] by [[ivector]]. Also replacements in create are not made at all. The first request is easily handled. I assumed that both sides of the substitution consist of aphanumeric characters only (\verb+\w+). I changed this to non-space. This still does not allow blanks in either side. So the solution is not perfect. I discussed the matter with Michael and we agreed on the following syntax. On either write -verb-S T- or \verb-S#T-. In the former case S and T are not allowed to contain blanks. \item he wants me to remove all temporary files. \end{itemize} I performed all three changes. \item August 2nd, 96: I noticed that paremeter lists look strange if one of the types is templeted, e.g., [[edge_array]] rev will have no blank between [[>]] and [[rev]]. I made one change in [[M_convert]]. When it treats template brackets I insert a space after a [[>]]. This change handles parameter lists correctly but not quoted code. \item August 20th, 96: I introduced the command Menum for the definition of enumeration types. I want the layout enum Typename(in roman) { list of alternatives in typewriter } text of body of enum. \item September 15th, 96: Michael suggested two changes, this one and the next. [[> >]] in return type is treated incorrectly. I made two changes. When type is parsed I remove only beginning and trailing blanks but not interior blanks. [[convert_M]] uses a more refined strategy to typeset template delimiters. \item the warning uneven number of [[|]] is written into the tex-file and not on standard output. I changed this by using both CERR and CSTANDOUT. \item My correction for nested templates is incorrect. |stack S| removes the blank before S. I use the following heuristics. If a string submitted to [[convert_M]] contains [[<]] and [[>]] then all occurrences of such symbols are assumed to be template brackets. If it does not then they are assumed to be less or greater symbols. In the latter cases I let latex do the spacing. In the former case I keep the spacing in the input. I do not forsee a situation where I want to do both. Because one can always write something like [[$|stack S| < 5$]] instead of [[|stack S < 5|]]. And I guess there is no way that a string involving template brackets and less than sign automatically. \item [[const&]] removal does not always work. An example is sort in list, namely [[sort( int (*cmp)(const E&, const E&))]]. The first pair is not removed. The reason is that I insisted that the parameter starts with const. Together with the fact that I parse the parameter list only partially, i.e., do not discover that I deal with a nested parameter list this left the first const-ref pair undiscovered. I had to change two lines in [[@<>]]. \item remove empty lines introduces lines starting with \% in code section. I made a small change in emptyline.pl: A line containig nwendcode and nwbegincode does not move me out of codemode. \item Need a way to document private functions. Also need the possiblity to have manual comments nicely typeset in the implementation part of documents. This is solved. October 96 \item Need to produce html-type output. I talked to Joachim about the problem. He will look into it. This is solved. October 96. \item December 16th, 96. I forgot that a class can have more than one template argument. I added one line to correct this mistake (in the handling of code-unit). Now, I delete \verb-template *<[ ,\w]*> *-. \item have added a feature to produce very several outfiles \item changed the lauout of functions such that very long return types are handeled correctly. \item I added new formatting features for local typedefs in the class scope. There is a new section [[\Mtypes]] which introduces locally defined types. There are three commands which can be used there \verb-\Menum-, \verb-\Mtypedef- and \verb-\Mtypemember-. The first can be used to document enum types. The second documents a whole typedef as needed in traits classes and the third only declares the target type. \end{itemize} \section{Plans for changes} \begin{itemize} \item [[Fman two_tuple]] gives no output since the h-file has a name different from the type name. \item computewidths should store the old values. Then restorewidths should restore them. \end{itemize} \end{document}