# Using \SplitArgument with r() arguments in xparse

I have been experimenting with using the xparse package for custom document macros with a Markdown-like syntax. For example, I used to write \cut{x}{P}{Q} to produce . With the help of xparse, I would like to instead write \cut(x)(P | Q) -- I find this Markdown-like syntax much easier to read in the document's source.

(In case you're not convinced of the benefit, also compare \send{y,x'}{x}{y,x'}{P}{Q} with \send (y,x')(x<y,x'> | P | Q) as a means of producing .)

The code that I'm currently using to define \cut is:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\cut}{r() >{\SplitArgument{1}{|}}r()}{%
(\nu #1)(\use_i:nn#2 \mid \use_ii:nn#2)%
}
\ExplSyntaxOff

\begin{document}
Example 1: $\cut(x)(P | Q)$ % Works great!

% Example 2: $\cut(x)(P | \cut(y)(Q_1 | Q_2))$ % Fails with error

Example 3: $\cut(x)(P | {\cut(y)(Q_1 | Q_2)})$ % Works, but slightly annoying
\end{document}


When \cuts are not nested (as in Example 1 above), then everything works perfectly. However, I sometimes need to nest \cuts (as in Example 2 above). Unfortunately, I then get an xparse/split-excess-tokens error: "Too many | tokens when trying to split argument."

Question: Is there any way to force xparse to treat r() as groups when splitting arguments so that this error is avoided? Alternatively, can I turn off or reduce this error to a warning by setting some xparse option?

Of course, I can add a group explicitly myself (as in Example 3 above), but this is somewhat annoying. Another compromise is to make the second argument to \cut an m-type argument and write \cut(x){P | \cut(y){Q_1 | Q_2}}, but I'd rather avoid this if possible.

• You could also just use \def\cut(#1)(#2|#3){(\nu#1)(#2\mid#3)} which allows you to use $\cut(x)(P | \cut(y)(Q_1 | Q_2))$. Spaces in the arguments #1 and #2 are not really a problem here. This doesn't use xparse and therefore doesn't answer the question, per se. – Werner Nov 1 '12 at 19:33
• I'm not convinced. ;-) However, the problem is that in the nested call, the second argument to \cut has two | tokens and LaTeX can't know which one to select as the splitting marker. If you use braces, this won't happen, because TeX never chooses something as an argument if it results in unbalanced braces. – egreg Nov 1 '12 at 20:32
• @egreg But, can I somehow have xparse/TeX "never choose something as an argument if it results in unbalanced" parentheses? In other words, can I make parentheses be treated as if they were braces? – Henry DeYoung Nov 1 '12 at 21:33
• @HenryDeYoung No. Braces are special, parentheses aren't. – egreg Nov 1 '12 at 21:36

When parsing an argument, xparse works very hard to allow [ ... ] or ( ... ) to work in the same way as { ... } without needing to change category codes. However, there are limitation to this when you want to post-process material. The input

\cut(x)(P | \cut(y)(Q_1 | Q_2))


can only be parsed correctly if we 'know' that \cut is making ( ... ) into an argument grouping pair as we try to parse the argument

P | \cut(y)(Q_1 | Q_2)


That works when you add in additional braces as it effectively hides the second | from the parsing step, but without the braces we'd need to be evaluating all of the argument to find where it actually ends, or make an arbitrary choice that we 'look up' the definition of each command to allow nested parsing. That's not how TeX (a macro expansion system) works, and programming it into xparse would be extremely hard and almost certainly not robust. (Changing the category code of ( ... ) would be a possible approach, but this runs into the need to retokenize material to deal with nested arguments, and that is always risky and not 100% reliable, so I would not recommend it.)

As Werner comments, what you could do is simply split at the first |, ignoring any others. Something like

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Npn \SplitOnce #1#2
{
\cs_set_protected:Npn \tmp:w ##1 #1 ##2 \q_stop
{ \tl_set:Nn \ProcessedArgument { {##1} {##2} } }
\tmp:w #2 \q_stop
}
\NewDocumentCommand{\cut}{r() >{\SplitOnce{|}}r()}{%
(\nu #1)(\use_i:nn#2 \mid \use_ii:nn#2)%
}
\ExplSyntaxOff

\begin{document}
Example 1: $\cut(x)(P | Q)$

Example 2: $\cut(x)(P | \cut(y)(Q_1 | Q_2))$

Example 3: $\cut(x)(P | {\cut(y)(Q_1 | Q_2)})$
\end{document}


should cover this.

• Of course $\cut(x)(\cut(y)(P_1 | P_2) | Q))$ wouldn't work. I remain of the idea that delimiting arguments with braces is just as readable and much more reliable. – egreg Nov 3 '12 at 10:17