9

I'm trying to write a macro (ideally an environment, but a command would do) that takes a piece of Asymptote code, displays it in a verbatim environment, and then runs it through an asy environment. Unfortunately, my every attempt at the latter seems to go wrong. Here's a MWE:

\documentclass{article}
\usepackage{asymptote}
\newcommand{\asycode}[1]{\begin{asy}
#1
\end{asy}}
\begin{document}
\asycode{label("$x^2 = x \cdot x$");}
\end{document}

If I attempt to compile this, I get the following:

Runaway argument?
! File ended while scanning use of \next.
<inserted text> 
                \par 
<*> problem.tex

? 

What am I doing wrong here, and can it be fixed in any remotely reasonable fashion?

[Note: I've tried a number of different permutations regarding line break placement, etc., and none of them seem to work.]

  • Try using the command-form of the asy environment. For example, \newcommand{\asycode}[1]{\asy #1\endasy}. – Werner Aug 06 '13 at 18:29
  • @Werner: That doesn't help. For that matter, even putting \asy and \endasy in the actual document does not work. As far as I can tell from the source, the environment keeps going with \next until it encounters the string \end{asy}. – Charles Staats Aug 06 '13 at 19:06
  • The problem is that the asy environment writes line by line; the line is absorbed using the end-of-line as delimiter; by hiding the environment in a macro, it's impossible to make the needed category code change. – egreg Aug 06 '13 at 20:03

1 Answers1

7

The problem is that the asy environment writes the output file line by line; each line is absorbed using the end-of-line as delimiter. By hiding the environment in a macro, it's impossible to make the needed category code change. When an argument to a macro has been read, category codes are assigned, so they can't be changed any more. In particular, the information about line breaks, which asy relies on, is irremediably lost

You can do by emulating the asy environment's working.

\documentclass{article}
\usepackage{asymptote}

\makeatletter
\newcommand\asycode[2][]{%
  \stepcounter{asy}%
  \setkeys{ASYkeys}{#1}%
  \ifASYattach
    \ASYinlinefalse
  \fi
  \ifx\asydir\empty\else
    \def\ASYprefix{\asydir/}%
  \fi
  \immediate\write\AsyPreStream{%
    \noexpand\InputIfFileExists{%
      \ASYprefix\noexpand\jobname-\the\c@asy.pre}{}{}%
  }
  \asy@write@graphic@header
  \immediate\write\AsyStream{\detokenize{#2}}% here asy does the writing
  \asy@finalise@stream
  \asy@input@graphic
}
\makeatother

\begin{document}
\asycode{label("$x^2 = x \cdot x$");}
\end{document}
egreg
  • 1,121,712
  • This certainly does what I wanted. But even including your comment above, I still don't entirely understand why my original MWE doesn't work. How exactly does hiding the environment in a macro prevent changing category codes? – Charles Staats Aug 06 '13 at 23:00
  • @CharlesStaats When an argument to a macro is read, category codes are assigned, so they can't be changed any more. In particular, the information about line breaks is irremediably lost. – egreg Aug 07 '13 at 07:46
  • @egreg: There is a problem. When your \asycode contains blank lines \par tokens appear in the asy file. Then Asymptote crashes. – Weißer Kater Jan 11 '20 at 16:35
  • @user125730 Sorry, but that's essentially unavoidable. – egreg Jan 11 '20 at 16:46
  • I found out that this is avoidable when I make \^^M active and define it as \newline before any \asycode. I add the line \catcode'\^^M=\active \let\^^M\newline. But when I add this to the definition of \asycode, it does not work any more. I don't understand that... Do you know the reason? Or maybe how to fix this? – Weißer Kater Jan 11 '20 at 18:45