[Tex/LaTex] How to treat the arguments of a macro as literal or verbatim text

macrosverbatim

I'm working on a package that allows Python code to be entered within certain commands and environments, saves the Python code to external files and executes it, and brings back the results via \input. Similar to SympyTeX and SageTeX, but with additional features.

As a trivial example, \pylab{print 1+1} would save the text string print 1+1 to a file, Python executes this file to obtain 2 and saves this in a second file, and this second file is brought back into the original document with \input.

This is a minimal example. (It will give errors after the first run, before Python is run to create infile.tex.)

\newcommand{\pylab}[1]{
    \newwrite\outputstream
    \immediate\openout\outputstream=outfile.py
    \immediate\write\outputstream{#1}
    \immediate\closeout\outputstream
    \input{infile.tex}
}

The problem is that in more complex cases, the argument for \pylab could contain special characters, for example \pylab{print 3%2} (modulus operator). What's the best way to deal with this? Ideally, my command would act like \verb, so that it would take anything but * as the switch, so as to be able to work around almost any special code characters. I've been reading about verbatim, catcodes, \gdef, etc., but I haven't been able to get anywhere with those yet.

SympyTeX and SageTeX use

\catcode`\%=12
\newcommand{\percent}{%}
\catcode`\%=14

to define a replacement for %, but I want to avoid that route if possible since it means that I can't just directly paste in valid Python code.

Best Answer

You can read the argument verbatim using the follow technique. However this keeps { and } normal so they need to be matched inside the argument.

\documentclass{article}

\makeatletter
\newwrite\@pyout
\newcommand{\pylab}{%
    \begingroup
    % deactivate special characters
    \let\do\@makeother
    \dospecials
    % change '{' and '}' back to normal
    \catcode`\{=1
    \catcode`\}=2
    \@pylab
}
\def\@pylab#1{%
    \endgroup
    \immediate\openout\@pyout=\jobname.py
    \immediate\write\@pyout{#1}%
    \immediate\closeout\@pyout
    \immediate\write18{python \jobname.py > \jobname-py.tex}%
    \input{\jobname-py}%
}

\makeatother


\begin{document}

\pylab{print 3%2}

\end{document}

If you want the behavior of \verb use the following code which is inspired by the actual \verb code.

\documentclass{article}

\makeatletter

\newwrite\@pyout

\newcommand{\pylab}[1]{%
    \begingroup
    \verb@eol@error
    \let\do\@makeother
    \dospecials
    \catcode`#1\active
    \lccode`\~`#1%
    \lowercase{\def\@@pylab##1~}{\endgroup\@pylab{##1}}%
    \@@pylab
}

\def\@pylab#1{%
    \immediate\openout\@pyout=\jobname.py
    \immediate\write\@pyout{#1}%
    \immediate\closeout\@pyout
    \immediate\write18{python \jobname.py > \jobname-py.tex}%
    \input{\jobname-py}%
}

\makeatother

\begin{document}

\pylab|print 3%2|

\end{document}

Update:

With the new version 2011/07/23 of my newverbs package you can simplify the \pylab macro to \newcommand{\pylab}{\begingroup\Collectverb{\@pylab}}.