I have a partial version of this macro working, without xparse
, but I needed to tweak it and it got too unwieldy, so I'm trying to use xparse
instead. It's admittedly a farfetched macro, but I'm trying to learn how xparse
works... Suppose I'm trying to typeset references to sentences and their variants, like so:
X1
X2*
X3**
X4'
X56''
where:
- There can be some number of stars (indicating malformed variants), the number of these is unknown a priori
- There can be some number of primes (indicating well-formed variants), the number of these is also unknown
- The number afterwards may be more than one digit. The number is terminated by a space, a comma, semicolon, or other punctuation. For example,
$(\X'3; \X**45)$
. X
could be any alphabetic string.- It should preferably work in both text and math modes.
I'd like to input these as \X**3
, or \X'4
. (I'm choosing to place the punctuation first, because it's easier to convince TeX to parse things that way...) Suppose I already have an internal macro to typeset the actual thing:
\newcommand\@X[2]{ % #1 is the decorations, #2 is the number ...
\mbox{\ifmmode\text{X#2#1}\else X#2#1\fi}
}
Now I'm trying to define the user visible macro. My first hunch was
\DeclareDocumentCommand\X{ s s t' t' m }{ stuff }
but:
- This only handles two stars and two primes
- Actually, I can't seem to make it work even with just those two stars and primes :( It's giving me errors about missing
$
signs, but the math-mode code I had in\@X
was working before... - The
m
argument is only grabbing the first digit, rather than the whole number token as my old version used to do.
Is there a way to program this using xparse
, or is this just too weird a macro to try to support?
Edited to add: I got the following definitions working last night. They work for only '
and numbers, but don't use xparse
.
\newcommand\X{\@ifnextchar'\@Xprime{\@X{}}}
\newcommand\@Xprime[1]{\@ifnextchar'{\def\temp{\@Xprime{#1{'}}}\ex\temp\@gobble}{\@Xdigit{#1}}}
\newcommand\@Xdigit[2]{%
\@ifnextchar1{\def\temp{\@Xdigit{#1}{{#2}1}}\ex\temp\@gobble}{%
\@ifnextchar2{\def\temp{\@Xdigit{#1}{{#2}2}}\ex\temp\@gobble}{%
\@ifnextchar3{\def\temp{\@Xdigit{#1}{{#2}3}}\ex\temp\@gobble}{%
\@ifnextchar4{\def\temp{\@Xdigit{#1}{{#2}4}}\ex\temp\@gobble}{%
\@ifnextchar5{\def\temp{\@Xdigit{#1}{{#2}5}}\ex\temp\@gobble}{%
\@ifnextchar6{\def\temp{\@Xdigit{#1}{{#2}6}}\ex\temp\@gobble}{%
\@ifnextchar7{\def\temp{\@Xdigit{#1}{{#2}7}}\ex\temp\@gobble}{%
\@ifnextchar8{\def\temp{\@Xdigit{#1}{{#2}8}}\ex\temp\@gobble}{%
\@ifnextchar9{\def\temp{\@Xdigit{#1}{{#2}9}}\ex\temp\@gobble}{%
\@ifnextchar0{\def\temp{\@Xdigit{#1}{{#2}0}}\ex\temp\@gobble}{%
\@X{#1}{#2}\xspace}%
}}}}}}}}}}
The idea was for \@Xprime
to scrape up all the primes, one at a time and in brace groups to prevent them from turning into double-quotes, and accumulate them into its argument. It then passes that string off to \@Xdigit
, which scrapes up all digits (digits don't have their own catcode, so I couldn't figure out any shorter test than this one...) and accumulates them into its second argument. (I didn't intend for each digit to be in a brace group, but got stuck on how to append a digit to #2
.) Finally, once it's got both the primes and the digits, it calls \@X
to typeset them, and then uses \xspace
to restore space handling.
I couldn't figure out an equivalent \@Xstar
, so to speak, because \@ifnextchar*
didn't seem to do what I meant, and I think \@ifstar
only gobbles spaces following a command, not following the arguments to a command.
Edited I've figured out the following version, which behaves correctly, but still doesn't use xparse
. It uses \futuredef
from etextools
, and has the advantage of being much more concise and understandable than the previous version... I'm still curious how this would work with 'xparse`.
\DeclareRobustCommand\X{\@ifstar\@Xstar\@Xnostar}
\newcommand\@Xstar{\futuredef[*]\@Xstars{\futuredef[1234567890]\@Xdigits{\@X{\@Xstars}{\@Xdigits}}}}
\newcommand\@Xnostar{\@ifnextchar'\@Xprime{\futuredef[1234567890]\@Xdigits{\@X{}{\@Xdigits}}}}
\newcommand\@Xprime{\futuredef[']\@Xprimes{\futuredef[1234567890]\@Xdigits{\@X{\@Xprimes}{\@Xdigits}}}}
\newcommand\@X[2]{\mbox{\ifmmode\text{X#2#1}\else X#2#1\fi}\xspace}
*
and'
is variable, and we don't know the maximum in advance? How is the end of the numerical part of the argument delimited??{\test}
, where?
is a mnemonic for "predicate", and\test
is used as "\ifthenelse{\test{char}}{grab next char and keep going}{argument is completed}
"? (Or something like that, appropriately latex3-ified...)\X**{23}
defined by an argument specl m
)?