4

I’m trying to use a command defined using \NewDocumentCommand in a tikzpicture environment and cannot resolve an error. Here’s a minimal example of what I’m trying to do:

\documentclass{article}

\usepackage{tikz}

\begin{document}

\ExplSyntaxOn
\NewDocumentCommand { \test } { m }
  {
    \tl_set:Nn \l_tmpa_tl { #1 }
    \int_eval:n { \l_tmpa_tl }
  }
\ExplSyntaxOff

\begin{tikzpicture}
  \draw (\test{0}, 0) -- (1, 1);
\end{tikzpicture}

\end{document}

Attempting to compile this (with LuaLaTeX) results in:

! Missing \endcsname inserted.
<to be read again> 
\__kernel_tl_set:Nx 
l.16   \draw (\test{0}, 0)
                         -- (1, 1);

A different error results if the token list is removed. Changing the code to—

\documentclass{article}

\usepackage{tikz}

\begin{document}

\ExplSyntaxOn
\NewDocumentCommand { \test } { m }
  {
    % \tl_set:Nn \l_tmpa_tl { #1 }
    \int_eval:n { #1 } % <-- This line is different.
  }
\ExplSyntaxOff

\begin{tikzpicture}
  \draw (\test{0}, 0) -- (1, 1);
\end{tikzpicture}

\end{document}

—results in:

! Missing number, treated as zero.
<to be read again> 
\relax 
l.16   \draw (\test{0}, 0)
                         -- (1, 1);

I’m hoping I’m missing something simple that can make TikZ work with commands from \NewDocumentCommand.

4
  • 1
    not really related to NewDocumentCommand or latex3, you would see the same with tex primitives, the argument has to expand do a node name so \node (a) you can not do assignments like \node (\def\foo{a}\foo) it has to be \def\foo{a} \node(\foo) Commented Jun 10 at 22:57
  • @DavidCarlisle Terribly sorry about this; I evidently minimized what I was working on into something different without realizing it. I’m actually trying to use a command in a path coordinate, and I forgot that \node is followed by a name, not a point on a path. I’ve updated the post for this, but I suspect it’s a similar issue (assignments can’t occur during path construction).
    – Nate
    Commented Jun 10 at 23:25
  • that's why pgfmath constructs all return a value in a \pgfresult macro to use later rather than returning a result "inline" as you typically need something that expands to a value. Commented Jun 10 at 23:36
  • You can e.g. trigger pgfmath evaluation in the specification of a coordinate by using braces. So you could use that here rather than \int_eval:n. pgfmath is slow, but maybe not slower than the shenanigans necessary to offload the calculation to TeX.
    – cfr
    Commented Jun 10 at 23:39

1 Answer 1

6

It's not obvious from your question why you want to do this exactly or whether this is the best way to do it. However, if you do wish to do it with one of the \NewDocumentCommand family, you could try using \NewExpandableDocumentCommand instead, as the basic problem in this particular case is that \test{0} isn't fully expandable and the pgf parser needs to see a number or node name or whatever right after the (.

\documentclass{article}

\usepackage{tikz}

\begin{document}
  
  \ExplSyntaxOn
  \NewExpandableDocumentCommand { \test } { m }
  {
    \int_eval:n { #1 } % <-- This line is different. % <- From what?
  }
  \ExplSyntaxOff
  
  \begin{tikzpicture}
    \draw (\test{0}, 0) -- (1, 1);
  \end{tikzpicture}
  
\end{document}

But, in this particular case, the obvious choice would be pgfmath, which can be evaluated inline as part of the ordinary parsing of the path. Just surround the stuff by braces/curly brackets to trigger the evaluation.

  \begin{tikzpicture}
    \draw ({int(3-3)}, 0) -- (1, 1);
  \end{tikzpicture}

If for whatever reason you want to avoid pgfmath, you could just use \inteval here.

  \begin{tikzpicture}
    \draw (\inteval{0}, 0) -- (1, 1);
  \end{tikzpicture}

But, in general, you cannot reliably count on slotting in TeX/LaTeX commands in the context of pgf/tikz because pgf has its own parser and various standard things are suspended or altered. Moreover, the parser depends on finding certain things in certain places in order to figure out what to do. Although the parser is quite flexible, most of that flexibility is designed around pgf/tikz elements, since that is generally what makes the most sense when the parser is active.

1
  • Thank you for your answer! What I was trying to write was a macro that takes a musical note name (like C4) and expands to a MIDI note number (60) in a picture where a unit of the x axis is effectively a note number. Based on your answer, it seems like this is very difficult to do in a tikzpicture environment. I ended up going in the other direction—a macro that takes a note number and expands to a note name. > lang-tex > \int_eval:n { #1 } % <-- This line is different. % <- From what? > In the first code example, \l_tmpa_tl is passed to \int_eval:n.
    – Nate
    Commented Jun 17 at 14:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .