tags:

views:

151

answers:

3

I'm cleaning up some old LaTeX documents describing an API. In the process I'm creating my own little style for describing the API, but I've bumped up against a bit that seems to force me into a violation of DRY.

To follow the style of the old document each function should be described in the following format:

<function signature>
<table describing each argument>
<description of return value>

Ideally I'd like to create an environment and a couple of commands to describe arguments and return type. Something like

\begin{function}{getQuestion}
\return{string}{String with the question.}
\argument{bar}{int}{The answer to the ultimate question.}
\argument{baz}{int}{Number of years you are willing to wait for the question.}
\end{function}

This should then produce something like

\textit{string} getQuestion ( \textit{int} bar, \textit{int} baz )

\begin{tabular}{lll}
bar & int & The answer to the ultimate question \\
baz & int & Number of years you are willing to wait for the question.

\textbf{Returns:} \textit{string} - String with the question.

But for this to work I think I need to collect all arguments (\argument) in some way so I can iterate over them twice.

In the past, when I've had to only collect one value I've used \newcommand and \renewcommand, but I don't see how I can use that to collect more than one value.

Any pointers on how I can approach this?

A: 

My LaTeX is quite rusty, but maybe this helps (Taken from http://web.mit.edu/annakot/MacData/afs/sipb/project/tex/doc/latex/base/usrguide/node18.html):

\newcommand{\example}[2][YYY]{Mandatory arg: #2; Optional arg: #1.}

This defines \example to be a command with two arguments, referred to as #1 and #2 in the {}--nothing new so far. But by adding a second optional argument to this \newcommand (the [YYY]) the first argument (#1) of the newly defined command \example is made optional with its default value being YYY.

Treb
All right, the first two answerers misunderstood the question. Time to fix the question :-)
Magnus
I see.... as I said, my `LaTeX` knowledge has not been put to practical use recently, but I can not remember any way to achieve what you want directly in `LaTeX`. Maybe you need to dig down into `TeX` for this purpose, remember that `LaTeX` is just a collection of macros... ;-)
Treb
A: 

Many of the usual internals (cross references, ToC, ToF, etc...) seem to write some intermediate results to the .aux file or some other holding bin and then create the actual output on a second compilation pass.

I'm hoping some expert will come along and give us the gory details...

dmckee
+5  A: 

Well, long story.

The most common way to collect some tokens and then process them, is by storing them inside either a so-called tokens register, or by adding the new tokens to an existing command. The first trick is adding them to an existing tokens register or command rather than overwriting the existing value. You can do this by using the low-level TeX primitive \expandafter. For commands, this would look like

\expandafter\def\expandafter\cmd\expandafter{\cmd <new contents here>}

The \expandafter tells TeX to temporarily ignore the next command (or token) and work on the one after it. (One level expansion to be precise.) For example,

\def\cmd{A}
\expandafter\def\expandafter\cmd\expandafter{\cmd <new contents here>}

is effectively

\def\cmd{A<new contents here>}

Tokens registers work a little differently, but the idea is the same:

\newtoks{\argumenttoks} % you must allocate a toks once, ever, before using it
...
\argumenttoks={A}% like \(re)newcommand\cmd{A}
\argumenttoks=\expandafter{\the\argumenttoks <new contents here>}

The difference is between both methods is 1) speed (toks are faster) and 2) toks allow TeX chars like #, and \def or \(re)newcommand don't like that, and 3) toks are considered pretty low-level, and there is finite number of them, which you must claim (through \newtoks).

We're half way there now. The next thing is: how can I read the list? The most common way to read a list, is to execute it, strangely enough. But to process each item with a command, you need to be able to add that command before each list element. The trick commonly used is to actually add that command when you're building the list:

\newcommand{\argument}[3]{%
  \argumenttoks=\expandafter{\the\argumenttoks \showarg{#1}{#2}{#3}}%
}

This way,

\argument{bar}{int}{The answer to the ultimate question.}
\argument{baz}{int}{Number of years you are willing to wait for the question.}

is stored as a list

\showarg{bar}{int}{The answer to the ultimate question.}
\showarg{baz}{int}{Number of years you are willing to wait for the question.}

So to use the list, you must execute it at the right location, and make sure \showarg does the right thing at that point.

\newtoks{\argumenttoks}
\newenvironment{function}[1]{%
   \argumenttoks={}% make sure it's empty
   % other stuff here
}{% \end{function}
  \textit{\storedreturntype} \storedfunctionname
  % now the arglist
  \let\showarg=\showargcommaseparated
  \let\argcomma=\empty % no comma at the start
  (\the\argumenttoks)
  ...
  % now the table
  \let\showarg=\showargtabular
  \let\argrowsep=\empty
  \begin{tabular}{lll}\the\argumenttoks\end{tabular}
  ...
}
\newcommand{\showargcommaseparated}[3]{%
  \argcomma % first time empty, second time a comma
  \def\argcomma{, }% next time
  \textit{#2} #1%
}
\newcommand{\showargtabular}[3]{%
  \argrowsep % first time empty, second time a \\
  #1 & #2 & #3
  \def\argrowsep{\\}% next time
}

If you're not familiar with low-level TeX: \def is like \newcommand that never complains (it works whether the command exists or not so it's ideal for overwriting a command you own), and \let copies the definition of one command to another, so you can switch the meaning of a command quickly, like I did with the \showarg command.

Note that a low-level LaTeX programmer would optimize this much further, but using self-modifying commands and such can be a bit mind boggling, and I think this has become difficult enough!

Ruben
Very clear description and it does the job perfectly.
Magnus