listings with scantokens

  • Follow


Hi,

I have written a macro which reads LaTeX code verbatim and stores it
in a macro (line endings are active ^^M, backslash is also active).
Later I like to display it using the great 'listings' package. What I
do so far is that I just write the code into
a temp file and use '\lstinputlisting' on it. Works like a charm.

However, I was wondering if I could reduce the I/O overhead by using
eTeXs '\scantokens' (or even a simple \expandafter if the catcodes
match) together with some of the listings macros.

I couldn't find anything about this exotic use. May someone with more
experience and insight into 'listings' can give me a hint?

Thanks,
Martin

PS:
Before anyone asks: No, I can't use listings directly on the original
code in my special case.
0
Reply martin.scharrer (98) 3/29/2010 11:26:47 PM

Martin Scharrer wrote:

> I have written a macro which reads LaTeX code verbatim and stores it
> in a macro (line endings are active ^^M, backslash is also active).

Like

\begingroup
\catcode^^M=13%
\gdef\macro{Line 1^^MLine 2^^MLine 3^^MLine n^^M}%
\endgroup%

?

Does the last line of code stored in a macro also end with active ^^M ?

You can "flush" the expansion of your macro into the body of
a lstlisting-environment which in turn is wrapped into
\scantokens.

Something like

\begingroup
\newlinechar=^^M %
\scantokens{%
  \endgroup%
  \begin{lstinputlisting}%
  <"flush" macro-expansion here>
  \end{lstinputlisting}%
}%

Depending on how your TeX-implementation uses to write ^^M (which
denotes line-endings) you may need to set \newlinechar=\endlinechar
in order to ensure that the write-faking part of \scantokens "starts a
new line" at each ^^M.  When the first \endgroup within \scantokens is
carried-out, the write-faking-part is over and the input-faking-part of
what was faked to be written and hereby broken into lines has already
begun...

The crucial point is the <"flush" macro-expansion here>-part. Depending
on how flexible you want the thing to be (e.g., also pass some optional 
argument as lstinputlisting's optional argument / e.g., error-message in
case the user didn't provide a macro-token / Robustness in conjunction
with optional-argument-checking) this might turn into a nice grouping-
and-expansion-orgy.

I might be willing to try to help with that. But if at all then only if
you answer my very first question of this posting.

Ulrich
0
Reply Ulrich 3/30/2010 2:25:49 PM


Martin Scharrer wrote:

> I have written a macro which reads LaTeX code verbatim and stores it
> in a macro (line endings are active ^^M, backslash is also active).

Like

\begingroup
\catcode^^M=13%
\gdef\macro{Line 1^^MLine 2^^MLine 3^^MLine n^^M}%
\endgroup%

?

Does the last line of code stored in a macro also end with active ^^M ?

You can "flush" the expansion of your macro into the body of
a lstlisting-environment which in turn is wrapped into
\scantokens.

Something like

\begingroup
\newlinechar=^^M %
\scantokens{%
  \endgroup%
  \begin{lstinputlisting}%
  <"flush" macro-expansion here>
  \end{lstinputlisting}%
}%

Depending on how your TeX-implementation uses to write ^^M (which
denotes line-endings) you may need to set \newlinechar=\endlinechar
in order to ensure that the write-faking part of \scantokens "starts a
new line" at each ^^M.  When the first \endgroup within \scantokens is
carried-out, the write-faking-part is over and the input-faking-part of
what was faked to be written and hereby broken into lines has already
begun...

The crucial point is the <"flush" macro-expansion here>-part. Depending
on how flexible you want the thing to be (e.g., also pass some optional 
argument as lstinputlisting's optional argument / e.g., error-message in
case the user didn't provide a macro-token / Robustness in conjunction
with optional-argument-checking) this might turn into a nice grouping-
and-expansion-orgy.

I might be willing to try to help with that. But if at all then only if
you answer my first and second question of this posting.

Ulrich
0
Reply Ulrich 3/30/2010 2:27:52 PM

I wrote: 
> Something like
> 
> \begingroup
> \newlinechar=^^M %

Of course the line above is error-prone.
Should be:

\newlinechar=`\^^M %

> \scantokens{%
>   \endgroup%
>   \begin{lstinputlisting}%
>   <"flush" macro-expansion here>
>   \end{lstinputlisting}%
> }%


Ulrich
0
Reply Ulrich 3/30/2010 3:58:32 PM

On Mar 30, 3:27 pm, Ulrich  D i e z <eu_angel...@web.de> wrote:
> Martin Scharrer wrote:
> > I have written a macro which reads LaTeX code verbatim and stores it
> > in a macro (line endings are active ^^M, backslash is also active).
>
> Like
>
> \begingroup
> \catcode^^M=13%
> \gdef\macro{Line 1^^MLine 2^^MLine 3^^MLine n^^M}%
> \endgroup%
> ?
Almost. Like this:
\begingroup
\catcode10=\active%
\catcode`\|=0 %
\catcode`\\=\active%
|gdef|macro{Line 1^^MLine 2^^MLine 3 with \macro^^MLine n}%
|endgroup%


>
> Does the last line of code stored in a macro also end with active ^^M ?
Not by default. This ^^M is part of the end-marker and is eaten away.
However, I could easily add it again if required.
I do a '\def\macro{#1}' where #1 is the verbatim text.
Changing this to '\def\macro{#1^^M}' while ^^M has the correct catcode
should do it.

>
> You can "flush" the expansion of your macro into the body of
> a lstlisting-environment which in turn is wrapped into
> \scantokens.
>
> Something like
>
> \begingroup
> \newlinechar=^^M %
> \scantokens{%
>   \endgroup%
>   \begin{lstinputlisting}%
>   <"flush" macro-expansion here>
>   \end{lstinputlisting}%
>
> }%
>
> Depending on how your TeX-implementation uses to write ^^M (which
> denotes line-endings) you may need to set \newlinechar=\endlinechar
> in order to ensure that the write-faking part of \scantokens "starts a
> new line" at each ^^M.  When the first \endgroup within \scantokens is
> carried-out, the write-faking-part is over and the input-faking-part of
> what was faked to be written and hereby broken into lines has already
> begun...
>
> The crucial point is the <"flush" macro-expansion here>-part. Depending
> on how flexible you want the thing to be (e.g., also pass some optional
> argument as lstinputlisting's optional argument / e.g., error-message in
> case the user didn't provide a macro-token / Robustness in conjunction
> with optional-argument-checking) this might turn into a nice grouping-
> and-expansion-orgy.
Thanks for that. It inspired me to play around and come to this "easy"
and "straight-forward" version.
The basic idea is the following:
1) \scantokens{<code>} is like \input{file-with-code} (more or less)
2) All what I need is a 'file' with:
\begin{lstlisting}
<code>
\end{lstlisting}
3) So instead trying to place the <code> with new catcodes set by
lstlisting inside the lstlisting ''environment'',
I just have to define a macro with holds a verbatim '\begin{lstlisting}
^^M' + the verbatim code + verbatim '^^M\end{lstlistings}'
and expand that inside \scantokens using \expandafter (  \expandafter
\scantokens\expandafter{\verbcodewithlistingsaround} )

Here a working example:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\documentclass{article}
\usepackage{listings}

\begingroup
\catcode`\^^M=\active% or other
\catcode`\|=0%
\catcode`\\=\active% or other
|gdef|verbcode{Line 1^^MLine 2^^MLine 3 with \macro^^MLine n}%
|endgroup%

\begingroup
% either
\catcode`\^^M=12\relax%
\newlinechar=`\^^M\relax%
% or \obeylines%
\xdef\verbcodewithlistingsaround{\string\begin{lstlisting}^^M
\expandafter\unexpanded\expandafter{\verbcode}^^M\string
\end{lstlisting}}%
\endgroup%

\begin{document}
\hrule
\expandafter\scantokens\expandafter{\verbcodewithlistingsaround}%
\hrule
\end{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

I think the usage of a toks register could improve the performance
here.


> I might be willing to try to help with that. But if at all then only if
> you answer my first and second question of this posting.
Thanks so much.

Martin

0
Reply Martin 3/30/2010 7:32:25 PM

Martin Scharrer wrote:

> The basic idea is the following:
> 1) \scantokens{<code>} is like \input{file-with-code} (more or less)

\scantokens is like writing the tokens from <code> unexpanded to file
and afterwards \input-ting that file. You need to obey all subtleties
related to writing tokens unexpanded to file.

> 2) All what I need is a 'file' with:
> \begin{lstlisting}
> <code>
> \end{lstlisting}
> 3) So instead trying to place the <code> with new catcodes set by
> lstlisting inside the lstlisting ''environment'',
> I just have to define a macro with holds a verbatim '\begin{lstlisting}
> ^^M' + the verbatim code + verbatim '^^M\end{lstlistings}'
> and expand that inside \scantokens using \expandafter (  \expandafter
> \scantokens\expandafter{\verbcodewithlistingsaround} )

Actually you don't need the \expandafter-token before the
\scantokens-token. If you omit that, TeX will "launch" the
\expandafter-token right behind the \scantokens-token
anyway.
That's because \scantokens takes as argument some so-called
<balanced-text>, that means "brace-balanced" stuff  which is 
wrapped into a  leading catcode-1-token and a trailing catcode-2-
character-token.
When a token (e.g., \scantokens, \detokenize, \toks/token-register-
assignment) requires <balanced-text> , TeX keeps expanding
following tokens until expansion yields either a catcode-1-token
which might be the start of the balanced text or some token from
which it is obvious that an error-message needs to be raised as 
no proper <balanced-text> follows.
You can use this fact in order to "fool" TeX into expanding other
stuff behind \scantokens via \expandafter as well:

  \scantokens\expandafter{\verbcodewithlistingsaround}

Before finding the catcode-1-token "{ " which is the begin of
\scantokens' <balanced-text>, TeX will expand the \expandafte
and thus also the  \verbcodewithlistingsaround.

> Here a working example:
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> \documentclass{article}
> \usepackage{listings}
> 
> \begingroup
> \catcode`\^^M=\active% or other
> \catcode`\|=0%
> \catcode`\\=\active% or other
> |gdef|verbcode{Line 1^^MLine 2^^MLine 3 with \macro^^MLine n}%
> |endgroup%
> 
> \begingroup
> % either
> \catcode`\^^M=12\relax%
> \newlinechar=`\^^M\relax%

The value of \newlinechar is relevant at times when it comes
to writing (or faking writing via \scantokens or \detokenize) because
only at such times character-tokens whose character-codes equal 
the value of \newlinechar will get replaced by the byte-sequence (or
whatever) which on your computer-platform corresponds a 
line-break/start-of-a-new-line within a text-file/ascii-file.

Within the group no \write or \scantokens or \detokenize or the
like influencable by the value of \newlinechar takes place.
Thus the \newlinechar-assignment is somewhat obsolete at this
place.

> % or \obeylines%
> \xdef\verbcodewithlistingsaround{\string\begin{lstlisting}^^M
> \expandafter\unexpanded\expandafter{\verbcode}^^M\string
> \end{lstlisting}}%
> \endgroup%

> \begin{document}
> \hrule
> \expandafter\scantokens\expandafter{\verbcodewithlistingsaround}%
> \hrule
> \end{document}
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> 
> I think the usage of a toks register could improve the performance
> here.

The point is: The assignment to \verbcodewithlistingsaround
is repeated whenever you want to make another listing from another
\verbcode-like-macro.

It is possible to define a set of macros which -_without_ performing
any additional assignments - will "take" some \verbcode-like-macro-
token as argument, expand that and pass that to \scantokens with a
lstlisting-environment wrapped around it.

> > I might be willing to try to help with that. But if at all then only if
> > you answer my first and second question of this posting.
> Thanks so much.

Okay - here is what I tried. I give no warranties as currently it
is 00:11 o'clock and it was 23:43 o'clock when I started
preparing this posting and by now I didn't have time for
extensive testing.

[ In case you use some web-interface like google-groups,
please make sure to view this posting in "original format"
before probably copying-'n-pasting the code. ]

In example 1,  \PassMacroExpansionToLstInputListing is the
toplevel-macro/user-level-macro. It takes an optional
argument and a mandatory argument. The mandatory argument
is to hold a single macro-token (like the \verbcode-macro of
your example) which expands to the verbatimized text of the
code-listing. The expansion of that macro will get wrapped
into a lstlisting-environment which in turn forms the argument
of a call to \scantokens. The optional argument - if provided -
will be the optional argument of the lstlisting-environment.

As the mandatory argument is to be a single macro-token,
you might wish to have some error-checking implemented in
order to have raised an error-message in case the argument is
not a single macro-token.

In example 1, I omitted such error-checking as it takes a lot
of code to find out without further assignments if a macro-
argument is a single macro-token:
- Instead of a single macro-token, the argument might be empty.
  You need to find out about emptiness of an argument.
- Instead of a single macro-token, the argument might be
  something wrapped into several pairs of braces/an additional
  pair of braces. You need to find out about braces.
- Instead of a single macro-token, the argument might start
  with a space-token. You need to find out about space-tokens.
- Instead of a single macro-token, the argument might consist
  of several tokens. You need to find out, e.g., by gobbling one
  token and checking if the remainder is empty.
- Instead of a single macro-token, the argument might consist of
  a single non-expandable token (whose \meaning might as
  well contain the phrase "macro" - I think such a thing is
  accomplishable, e.g. , via a \fontdef-assignment refering to
  a font with the phrase "macro" in its name)
- Instead of a single macro-token, the argument might consist
  of a single expandable primitive not containing the phrase
  "macro" in its \meaning.
  You need to find out both about expandability and the phrase
  "macro" in the \meaning.

The only difference between example 1 and example 2 is that
in example 2 I added code for such error-checking. But that
still is not perfect as the argument being a macro still does not
warrant the expansion of that macro to be suitable for forming
the "body" of a lstlisting-environment...

Ulrich


-----------------------------------------------------------------
Example 1 - without error-checking:
-----------------------------------------------------------------

\documentclass{article}
\usepackage{listings}
\makeatletter
%%---------------------------------------------------------------
%% \PassMacroExpansionToLstInputListing[<options>]{<macro-token>}
%%
%% yields:
%%
%%    \begingroup
%%    \newlinechar=`^^M %
%%    \noexpand\scantokens{%
%%      \endgroup%
%%      \begin{lstlisting}[<options>]
%%      <Expansion of macro-token>
%%      \end{lstlisting}
%%    }%
%%
%% It is assumed that <macro-token> holds tokens verbatimized
%% and with ^^M denoting line-breaks.
%%
%%---------------------------------------------------------------
\DeclareRobustCommand*\PassMacroExpansionToLstInputListing{%
  \kernel@ifnextchar[{\begingroup\lst@setcatcodes
                      \@PassMacroExpansionToLstInputListing}%
                     {\@@PassMacroExpansionToLstInputListing{}}%
}
\@ifdefinable\@PassMacroExpansionToLstInputListing{%
  \long\def\@PassMacroExpansionToLstInputListing[#1]{%
    \endgroup
    \@@PassMacroExpansionToLstInputListing{[{#1}]}%
  }%
}
\newcommand\@@PassMacroExpansionToLstInputListing[2]{%
  \expandafter\@@@PassMacroExpansionToLstInputListing
  \expandafter{#2}{#1}%
}
\begingroup
\catcode`\^^M=12 %
\newcommand*\@@@PassMacroExpansionToLstInputListing{%
  \endgroup%
  \long\edef\@@@PassMacroExpansionToLstInputListing##1##2{%
    \begingroup%
    \newlinechar=`\noexpand\^^M %
    \noexpand\scantokens{%
      \endgroup%
      \string\begin{lstlisting}##2^^M%
      ##1%
      \string\end{lstlisting}%
    }%
  }%
}%
\@@@PassMacroExpansionToLstInputListing%
\makeatother
%---------------------------------------------------------------
% Some demo-macro with active ^^M faking line-endings:
%---------------------------------------------------------------
\begingroup
\catcode`\^^M=13 %
\newcommand\macro{%
  \endgroup%
  \def\macro{Line A^^M%
             Line B^^M%
             Line C^^M%
             Line D^^M%
             Line E}%
}%
\macro%

\begin{document}

Start\PassMacroExpansionToLstInputListing{\macro}Stop

Start\PassMacroExpansionToLstInputListing[numbers=left, numberstyle=\tiny]{\macro}Stop

Start%
\PassMacroExpansionToLstInputListing[firstline=2, lastline=4, numbers=left, numberstyle=\tiny]{\macro}
Stop

\end{document}




-----------------------------------------------------------------
Example 2 - with error-checking:
-----------------------------------------------------------------

\documentclass{article}
\usepackage{listings}
\makeatletter
%%---------------------------------------------------------------
%% \PassMacroExpansionToLstInputListing[<options>]{<macro-token>}
%%
%% yields:
%%
%%    \begingroup
%%    \newlinechar=`^^M %
%%    \noexpand\scantokens{%
%%      \endgroup%
%%      \begin{lstlisting}[<options>]
%%      <Expansion of macro-token>
%%      \end{lstlisting}
%%    }%
%%
%% It is assumed that <macro-token> holds tokens verbatimized
%% and with ^^M denoting line-breaks.
%%
%%---------------------------------------------------------------
\DeclareRobustCommand*\PassMacroExpansionToLstInputListing{%
  \kernel@ifnextchar[{\begingroup\lst@setcatcodes
                      \@PassMacroExpansionToLstInputListing}%
                     {\@@PassMacroExpansionToLstInputListing{}}%
}
\@ifdefinable\@PassMacroExpansionToLstInputListing{%
  \long\def\@PassMacroExpansionToLstInputListing[#1]{%
    \endgroup
    \@@PassMacroExpansionToLstInputListing{[{#1}]}%
  }%
}
\newcommand\@@PassMacroExpansionToLstInputListing[2]{%
  \@ifArgIsSingleMacroToken{#2}%
  {%
    \expandafter\@@@PassMacroExpansionToLstInputListing
    \expandafter{#2}{#1}%
  }%
  {%
    \@latex@error{\string\PassMacroExpansionToLstInputListing's
    mandatory argument is\MessageBreak not a single macro token}%
    {\string\PassMacroExpansionToLstInputListing's mandatory
    argument must be a\MessageBreak single macro-token.}%
  }%
}
\begingroup
\catcode`\^^M=12 %
\newcommand*\@@@PassMacroExpansionToLstInputListing{%
  \endgroup%
  \long\edef\@@@PassMacroExpansionToLstInputListing##1##2{%
    \begingroup%
    \newlinechar=`\noexpand\^^M %
    \noexpand\scantokens{%
      \endgroup%
      \string\begin{lstlisting}##2^^M%
      ##1%
      \string\end{lstlisting}%
    }%
  }%
}%
\@@@PassMacroExpansionToLstInputListing%
%%---------------------------------------------------------------
%% Check if argument is a single macro-token:
%%---------------------------------------------------------------
\newcommand\@ifArgIsSingleMacroToken[1]{%
  \romannumeral\expandafter\expandafter\expandafter\@rmstop
  \csname @\@ifnull{#1}{second}{\@ifbrace{#1}{second}{%
  \@iffirstspace{#1}{second}{\expandafter\@ifnull\expandafter{%
  \@gobble#1}{\@ifexpandable{#1}{\@ifmacromeaning{#1}{first}%
  {second}}{second}}{second}}}}oftwo\endcsname
}
%%---------------------------------------------------------------
%% Stopper for \romannumeral-expansion:
%%...............................................................
\newcommand\@rmstop{0 }
%%---------------------------------------------------------------
%% Check if argument is blank / is null:
%%...............................................................
\newcommand\@ifnull[1]{%
  \romannumeral\expandafter\@firstofone\expandafter{\expandafter
  \expandafter\expandafter\@rmstop\csname @\expandafter\@gobble
  \string{\expandafter\@secondoftwo\expandafter{\expandafter{%
  \string#1}{}\expandafter\expandafter\expandafter\@gobble
  \expandafter\@gobble\string}{}\expandafter\expandafter
  \expandafter\@firstoftwo\expandafter\expandafter\expandafter{%
  \expandafter\@gobble\string}second}{first}oftwo\endcsname}%
}
%%---------------------------------------------------------------
%% Check if brace-balanced argument starts with a catcode-1-char:
%%...............................................................
\newcommand\@ifbrace[1]{%
  \romannumeral\expandafter\@firstofone\expandafter{%
               \expandafter\expandafter\expandafter\@rmstop
               \csname @%
  \expandafter\@gobble\string{%
  \expandafter\@gobble
  \expandafter{%
  \expandafter{%
  \string#1}%
    \expandafter\expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter\expandafter\expandafter
    \@firstoftwo\expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter\@gobble\expandafter
    \expandafter\expandafter\@gobble\expandafter\expandafter
    \expandafter{%
    \expandafter\string
    \expandafter}%
    \string}%
      \expandafter\@gobble\string}%
      \@secondoftwo
      {first}{second}oftwo\endcsname
  }%
}
%%---------------------------------------------------------------
%% Check if brace-balanced argument starts with a space-char:
%%...............................................................
\newcommand\@iffirstspace[1]{%
  \romannumeral\expandafter\expandafter
               \expandafter\@rmstop
  \csname @\@ifnull{#1}{second}{%
   \iffalse{\fi\@@iffirstspace.#1 something}%
  }oftwo\endcsname
}
\@ifdefinable\@@iffirstspace{%
  \long\def\@@iffirstspace#1 #2{%
    \expandafter\@ifnull\expandafter{\@gobble#1}{first}{second}%
    \expandafter\@gobble\expandafter{\iffalse}\fi%
  }%
}
%%---------------------------------------------------------------
%% Check if meaning of token contains the phrase "macro":
%%---------------------------------------------------------------
\@ifdefinable\@ifmacromeaning{%
  \edef\@ifmacromeaning{%
    \string m\string a\string c\string r\string o%
  }%
}
\@ifdefinable\@@macromeaning{%
  \expandafter\long\expandafter\def\expandafter\@@macromeaning
  \expandafter#\expandafter1\@ifmacromeaning{}%
}
\long\edef\@ifmacromeaning#1{%
  \noexpand\romannumeral\noexpand\expandafter\noexpand
  \expandafter\noexpand\expandafter\noexpand\@ifnull\noexpand
  \expandafter\noexpand\expandafter\noexpand\expandafter{%
  \noexpand\expandafter\noexpand\@@macromeaning\noexpand\meaning
  #1\@ifmacromeaning}{\noexpand\expandafter\noexpand\@rmstop
  \noexpand\@secondoftwo}{\noexpand\expandafter\noexpand\@rmstop
  \noexpand\@firstoftwo}%
}
%%---------------------------------------------------------------
%% Check if token is expandable:
%%---------------------------------------------------------------
\newcommand\@ifexpandable[1]{%
  \romannumeral\expandafter\@firstofone\expandafter{\expandafter
  \expandafter\expandafter\@rmstop\csname @\expandafter\ifx
  \noexpand#1#1second\else first\fi oftwo\endcsname}%
}
\makeatother
%---------------------------------------------------------------
% Some demo-macro with active ^^M faking line-endings:
%---------------------------------------------------------------
\begingroup
\catcode`\^^M=13 %
\newcommand\macro{%
  \endgroup%
  \def\macro{Line A^^M%
             Line B^^M%
             Line C^^M%
             Line D^^M%
             Line E}%
}%
\macro%

\begin{document}

%% Error-message-checking
% Start\PassMacroExpansionToLstInputListing{blebb}Stop

%% Error-message-checking
% Start\PassMacroExpansionToLstInputListing{\if}Stop

%% Error-message-checking
% \countdef\test5
% Start\PassMacroExpansionToLstInputListing{\test}Stop

%% Error-message-checking
% Start\PassMacroExpansionToLstInputListing{{\macro}}Stop

%% Error-message-checking
% Start\PassMacroExpansionToLstInputListing{}Stop

%% Error-message-checking
% Start\PassMacroExpansionToLstInputListing{ }Stop

%% Error-message-checking
% Start\PassMacroExpansionToLstInputListing{ \macro}Stop

Start\PassMacroExpansionToLstInputListing{\macro}Stop

Start\PassMacroExpansionToLstInputListing[numbers=left, numberstyle=\tiny]{\macro}Stop

Start%
\PassMacroExpansionToLstInputListing[firstline=2, lastline=4, numbers=left, numberstyle=\tiny]{\macro}
Stop

\end{document}
0
Reply Ulrich 3/30/2010 10:16:32 PM

On 31 Mrz., 00:16, Ulrich  D i e z <eu_angel...@web.de> wrote:
> Martin Scharrer wrote:
> > The basic idea is the following:
> > 1) \scantokens{<code>} is like \input{file-with-code} (more or less)
>
> \scantokens is like writing the tokens from <code> unexpanded to file
> and afterwards \input-ting that file. You need to obey all subtleties
> related to writing tokens unexpanded to file.
>
> > 2) All what I need is a 'file' with:
> > \begin{lstlisting}
> > <code>
> > \end{lstlisting}
> > 3) So instead trying to place the <code> with new catcodes set by
> > lstlisting inside the lstlisting ''environment'',
> > I just have to define a macro with holds a verbatim '\begin{lstlisting}
> > ^^M' + the verbatim code + verbatim '^^M\end{lstlistings}'
> > and expand that inside \scantokens using \expandafter ( =A0\expandafter
> > \scantokens\expandafter{\verbcodewithlistingsaround} )
>
> Actually you don't need the \expandafter-token before the
> \scantokens-token. If you omit that, TeX will "launch" the
> \expandafter-token right behind the \scantokens-token
> anyway.
> That's because \scantokens takes as argument some so-called
> <balanced-text>, that means "brace-balanced" stuff =A0which is
> wrapped into a =A0leading catcode-1-token and a trailing catcode-2-
> character-token.
> When a token (e.g., \scantokens, \detokenize, \toks/token-register-
> assignment) requires <balanced-text> , TeX keeps expanding
> following tokens until expansion yields either a catcode-1-token
> which might be the start of the balanced text or some token from
> which it is obvious that an error-message needs to be raised as
> no proper <balanced-text> follows.
> You can use this fact in order to "fool" TeX into expanding other
> stuff behind \scantokens via \expandafter as well:
>
> =A0 \scantokens\expandafter{\verbcodewithlistingsaround}
>
> Before finding the catcode-1-token "{ " which is the begin of
> \scantokens' <balanced-text>, TeX will expand the \expandafte
> and thus also the =A0\verbcodewithlistingsaround.
>
>
>
> > Here a working example:
> > %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> > \documentclass{article}
> > \usepackage{listings}
>
> > \begingroup
> > \catcode`\^^M=3D\active% or other
> > \catcode`\|=3D0%
> > \catcode`\\=3D\active% or other
> > |gdef|verbcode{Line 1^^MLine 2^^MLine 3 with \macro^^MLine n}%
> > |endgroup%
>
> > \begingroup
> > % either
> > \catcode`\^^M=3D12\relax%
> > \newlinechar=3D`\^^M\relax%
>
> The value of \newlinecharis relevant at times when it comes
> to writing (or faking writing via \scantokens or \detokenize) because
> only at such times character-tokens whose character-codes equal
> the value of \newlinecharwill get replaced by the byte-sequence (or
> whatever) which on your computer-platform corresponds a
> line-break/start-of-a-new-line within a text-file/ascii-file.
>
> Within the group no \writeor \scantokens or \detokenize or the
> like influencable by the value of \newlinechartakes place.
> Thus the \newlinechar-assignment is somewhat obsolete at this
> place.
>
> > % or \obeylines%
> > \xdef\verbcodewithlistingsaround{\string\begin{lstlisting}^^M
> > \expandafter\unexpanded\expandafter{\verbcode}^^M\string
> > \end{lstlisting}}%
> > \endgroup%
> > \begin{document}
> > \hrule
> > \expandafter\scantokens\expandafter{\verbcodewithlistingsaround}%
> > \hrule
> > \end{document}
> > %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>
> > I think the usage of a toks register could improve the performance
> > here.
>
> The point is: The assignment to \verbcodewithlistingsaround
> is repeated whenever you want to make another listing from another
> \verbcode-like-macro.
>
> It is possible to define a set of macros which -_without_ performing
> any additional assignments - will "take" some \verbcode-like-macro-
> token as argument, expand that and pass that to \scantokens with a
> lstlisting-environment wrapped around it.
>
> > > I might be willing to try to help with that. But if at all then only =
if
> > > you answer my first and second question of this posting.
> > Thanks so much.
>
> Okay - here is what I tried. I give no warranties as currently it
> is 00:11 o'clock and it was 23:43 o'clock when I started
> preparing this posting and by now I didn't have time for
> extensive testing.
>
> [ In case you use some web-interface like google-groups,
> please make sure to view this posting in "original format"
> before probably copying-'n-pasting the code. ]
>
> In example 1, =A0\PassMacroExpansionToLstInputListing is the
> toplevel-macro/user-level-macro. It takes an optional
> argument and a mandatory argument. The mandatory argument
> is to hold a single macro-token (like the \verbcode-macro of
> your example) which expands to the verbatimized text of the
> code-listing. The expansion of that macro will get wrapped
> into a lstlisting-environment which in turn forms the argument
> of a call to \scantokens. The optional argument - if provided -
> will be the optional argument of the lstlisting-environment.
>
> As the mandatory argument is to be a single macro-token,
> you might wish to have some error-checking implemented in
> order to have raised an error-message in case the argument is
> not a single macro-token.
>
> In example 1, I omitted such error-checking as it takes a lot
> of code to find out without further assignments if a macro-
> argument is a single macro-token:
> - Instead of a single macro-token, the argument might be empty.
> =A0 You need to find out about emptiness of an argument.
> - Instead of a single macro-token, the argument might be
> =A0 something wrapped into several pairs of braces/an additional
> =A0 pair of braces. You need to find out about braces.
> - Instead of a single macro-token, the argument might start
> =A0 with a space-token. You need to find out about space-tokens.
> - Instead of a single macro-token, the argument might consist
> =A0 of several tokens. You need to find out, e.g., by gobbling one
> =A0 token and checking if the remainder is empty.
> - Instead of a single macro-token, the argument might consist of
> =A0 a single non-expandable token (whose \meaning might as
> =A0 well contain the phrase "macro" - I think such a thing is
> =A0 accomplishable, e.g. , via a \fontdef-assignment refering to
> =A0 a font with the phrase "macro" in its name)
> - Instead of a single macro-token, the argument might consist
> =A0 of a single expandable primitive not containing the phrase
> =A0 "macro" in its \meaning.
> =A0 You need to find out both about expandability and the phrase
> =A0 "macro" in the \meaning.
>
> The only difference between example 1 and example 2 is that
> in example 2 I added code for such error-checking. But that
> still is not perfect as the argument being a macro still does not
> warrant the expansion of that macro to be suitable for forming
> the "body" of a lstlisting-environment...
>
> Ulrich
>
> -----------------------------------------------------------------
> Example 1 - without error-checking:
> -----------------------------------------------------------------
>
> \documentclass{article}
> \usepackage{listings}
> \makeatletter
> %%---------------------------------------------------------------
> %% \PassMacroExpansionToLstInputListing[<options>]{<macro-token>}
> %%
> %% yields:
> %%
> %% =A0 =A0\begingroup
> %% =A0 =A0\newlinechar=3D`^^M %
> %% =A0 =A0\noexpand\scantokens{%
> %% =A0 =A0 =A0\endgroup%
> %% =A0 =A0 =A0\begin{lstlisting}[<options>]
> %% =A0 =A0 =A0<Expansion of macro-token>
> %% =A0 =A0 =A0\end{lstlisting}
> %% =A0 =A0}%
> %%
> %% It is assumed that <macro-token> holds tokens verbatimized
> %% and with ^^M denoting line-breaks.
> %%
> %%---------------------------------------------------------------
> \DeclareRobustCommand*\PassMacroExpansionToLstInputListing{%
> =A0 \kernel@ifnextchar[{\begingroup\lst@setcatcodes
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \@PassMacroExpansionToLstInpu=
tListing}%
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{\@@PassMacroExpansionToLstInp=
utListing{}}%}
>
> \@ifdefinable\@PassMacroExpansionToLstInputListing{%
> =A0 \long\def\@PassMacroExpansionToLstInputListing[#1]{%
> =A0 =A0 \endgroup
> =A0 =A0 \@@PassMacroExpansionToLstInputListing{[{#1}]}%
> =A0 }%}
>
> \newcommand\@@PassMacroExpansionToLstInputListing[2]{%
> =A0 \expandafter\@@@PassMacroExpansionToLstInputListing
> =A0 \expandafter{#2}{#1}%}
>
> \begingroup
> \catcode`\^^M=3D12 %
> \newcommand*\@@@PassMacroExpansionToLstInputListing{%
> =A0 \endgroup%
> =A0 \long\edef\@@@PassMacroExpansionToLstInputListing##1##2{%
> =A0 =A0 \begingroup%
> =A0 =A0 \newlinechar=3D`\noexpand\^^M %
> =A0 =A0 \noexpand\scantokens{%
> =A0 =A0 =A0 \endgroup%
> =A0 =A0 =A0 \string\begin{lstlisting}##2^^M%
> =A0 =A0 =A0 ##1%
> =A0 =A0 =A0 \string\end{lstlisting}%
> =A0 =A0 }%
> =A0 }%}%
>
> \@@@PassMacroExpansionToLstInputListing%
> \makeatother
> %---------------------------------------------------------------
> % Some demo-macro with active ^^M faking line-endings:
> %---------------------------------------------------------------
> \begingroup
> \catcode`\^^M=3D13 %
> \newcommand\macro{%
> =A0 \endgroup%
> =A0 \def\macro{Line A^^M%
> =A0 =A0 =A0 =A0 =A0 =A0 =A0Line B^^M%
> =A0 =A0 =A0 =A0 =A0 =A0 =A0Line C^^M%
> =A0 =A0 =A0 =A0 =A0 =A0 =A0Line D^^M%
> =A0 =A0 =A0 =A0 =A0 =A0 =A0Line E}%}%
>
> \macro%
>
> \begin{document}
>
> Start\PassMacroExpansionToLstInputListing{\macro}Stop
>
> Start\PassMacroExpansionToLstInputListing[numbers=3Dleft, numberstyle=3D\=
tiny]{\macro}Stop
>
> Start%
> \PassMacroExpansionToLstInputListing[firstline=3D2, lastline=3D4, numbers=
=3Dleft, numberstyle=3D\tiny]{\macro}
> Stop
>
> \end{document}
>
> -----------------------------------------------------------------
> Example 2 - with error-checking:
> -----------------------------------------------------------------
>
> \documentclass{article}
> \usepackage{listings}
> \makeatletter
> %%---------------------------------------------------------------
> %% \PassMacroExpansionToLstInputListing[<options>]{<macro-token>}
> %%
> %% yields:
> %%
> %% =A0 =A0\begingroup
> %% =A0 =A0\newlinechar=3D`^^M %
> %% =A0 =A0\noexpand\scantokens{%
> %% =A0 =A0 =A0\endgroup%
> %% =A0 =A0 =A0\begin{lstlisting}[<options>]
> %% =A0 =A0 =A0<Expansion of macro-token>
> %% =A0 =A0 =A0\end{lstlisting}
> %% =A0 =A0}%
> %%
> %% It is assumed that <macro-token> holds tokens verbatimized
> %% and with ^^M denoting line-breaks.
> %%
> %%---------------------------------------------------------------
> \DeclareRobustCommand*\PassMacroExpansionToLstInputListing{%
> =A0 \kernel@ifnextchar[{\begingroup\lst@setcatcodes
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 \@PassMacroExpansionToLstInpu=
tListing}%
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{\@@PassMacroExpansionToLstInp=
utListing{}}%}
>
> \@ifdefinable\@PassMacroExpansionToLstInputListing{%
> ...
>
> Erfahren Sie mehr =BB

Thanks Ulrich for the code.
I was on a conference last week and didn't had time to have a closer
look on it yet.
I will come back on it when as soon I find the time to work through
it.

Martin
0
Reply Martin 4/11/2010 9:11:38 AM

6 Replies
127 Views

(page loaded in 0.136 seconds)

Similiar Articles:

7/25/2012 10:11:05 PM


Reply: