# Parse and execute the next word as a command?

I would like to have a macro, let's call it \csword, that reads the next word in the input (i.e. sequence of letters a-zA-Z), and then executes that word as a macro. For example

\csword command
\csword command[and]{arguments}


should expand to something like

\command
\command[and]{arguments}


Following \csword by anything other than a letter should, say, throw an error.

In case I'm just too focused and trying to solve things the wrong way, this is the bigger picture of what I actually want to do. I want to define a macro which receives a number of options as a comma separated list. These options, however, might further receive some arguments or even optional arguments. In other words, I'm trying to turn

\myfancycommand{optionA, optionB[with]{args}, optionC{fun}, optionA}


Roughly into something like

\optionA\optionB[with]{args}\optionC{fun}\optionA


By the way, and this is important, I should be able to repeat “options” and their order of execution matters.

-
I edited my answer to fix a bug. – Harald Hanche-Olsen Oct 19 '10 at 15:17

Assuming @ is a letter:

\def\csword{\def\csword@{}\@csword}
\def\@csword{\futurelet\next\@@csword}
\def\@@csword{\ifcat a\noexpand\next
\expandafter\@@@csword
\else\expandafter\expandafter\csname\csword@\endcsname\fi}
\def\@@@csword#1{\edef\csword@{\csword@#1}\@csword}


Edit: Removed a \expandafter from line 3. It is unnecessary, and causes the macro to fail in cases like \csword foo\bar.

-
Seems to do the work! Thanks! – Juan A. Navarro Oct 19 '10 at 14:44

I would use one of the keyval-packages (keyval, xkeyval, pgfkeys ...) and define keys optionA etc so that you can use them like this:

 \myfancycommand{optionA, optionB=[with]{args}, optionC={fun}}

-
Thanks for the suggestion. I thought about that, but the = doesn't make a lot of sense (semantically) in my command so I would love to be able to get rid of it. Anyway, if there isn't anything better I might go with this option in the end. – Juan A. Navarro Oct 19 '10 at 14:17
Well keyval et al simplify things a lot as it has the parser for the comma-list encluded, removes spaces, protects the argument and so on. I don't think that it is worse the time to rewrite all these things for a small change of syntax. Also you shouldn't forget that people are used to key=value syntax. – Ulrike Fischer Oct 19 '10 at 14:41

Here is an expl3 variant of Harald's solution:

\usepackage{xparse}

\ExplSyntaxOn
\tl_new:N \l_csword_tl
\cs_new_protected_nopar:Npn \csword_aux_i: {
\peek_catcode:NTF \c_letter_token {
\csword_aux_ii:N
} {
\use:c { \l_csword_tl }
}
}
\cs_new_protected_nopar:Npn \csword_aux_ii:N #1 {
\tl_put_right:Nn \l_csword_tl { #1 }
\csword_aux_i:
}
\NewDocumentCommand \csword { } {
\tl_clear:N \l_csword_tl
\csword_aux_i:
}
\ExplSyntaxOff

-
I think it's slightly nicer to put the aux_ii macro inline in the original T branch? (One of the advantages of the TF syntax.) Also, since \csword is already \protected usually I just stick with \cs_new:Npn for readability — but Joseph and I differ on that point ;) – Will Robertson Oct 20 '10 at 5:32
@Will Robertson "I think it's slightly nicer to put the aux_ii macro inline in the original T branch?" How should that be possible? – Philipp Oct 20 '10 at 7:03
Uh, it wouldn't be! Forget I said anything :) – Will Robertson Oct 20 '10 at 7:07

On the off-chance that you'd like this to be expandable, here's another expl3 version. While this is longer than Harald's, I actually think it's more readable. On second thoughts: no, I think I'm just thinking that because I just wrote it :)

Slight downside: commands like f{o}o are executed as if they were foo. Which also means you can't have something like foo{A}, unfortunately. But foo{abc} is okay. Also, spaces mean nothing: foo foo is processed as \foofoo.

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
\cs_new:Npn \csword #1 {
\cs:w \csword_aux:n #1
}
\cs_new:Nn \csword_aux:n {
\tl_if_empty:nTF {#1}
{ \cs_end: } % e.g., '{}' in 'foo{}': end scanning
{
\tl_if_single:nTF {#1}
{
% e.g., 'o' in 'foo' or '{A}' in 'foo{A}' or '[' in 'foo[a]':
\token_if_letter:NTF #1
{ #1 \csword_aux:n } % e.g., 'o' in 'foo'         : keep scanning
{ \cs_end: #1 }      % e.g., '[' in 'foo[a]'      : end scanning
}
{ \cs_end: {#1} }      % e.g., '{abc}' in 'foo{abc}': end scanning
}
}
\ExplSyntaxOff

\begin{document}

\newcommand\foo[1][opt]{[foo: #1]}
\newcommand\baz[2][opt]{[baz: #1/#2]}

\typeout{
\csword foo
\csword f{o}o
\csword foo{A}
\csword foo{ABC}
\csword foo[1]
\csword baz{2}
\csword baz{22}
\csword baz[333]{222}
}

\end{document}


I just bench-marked this against Harald's solution, and it's five times slower. So it's really of academic use only :)

-
Hmm, interesting. I'll put expl3 on the list of stuff to learn some day. I could have made mine expandable too, but I decided against it because the shortcomings you mention are unavoidable then. I wonder though, if it could be done better with luatex? (Another item on my to-learn list …) – Harald Hanche-Olsen Oct 20 '10 at 7:23

ConTeXt has a macro \dowithwargument to work with word (w for word) arguments and \dowithpargument to work with par (p for par) arguments. However, it considers a word to be anything delimited by a space. So, if you are willing to type your arguments as

\csword optionA
\csword optionB [and]{argument} % Note the space after optionB


then you can define \csword as

\def\csword{\dowithwargument\getvalue}


Edit: Another option is to capture the space delimited phrase with \dowithwargument and then use LuaTeX to add a \ in front. So

\def\csword{\dowithwargument\docsword}
\def\docsword#1{\ctxlua{tex.sprint("\\#1")}}

-
I was going to suggest this with \def\csword #1 {\csname #1\endcsname} but decided the restrictions on the syntax were a bit limiting. – Will Robertson Oct 20 '10 at 6:12
Agreed. For completeness, \dowithwargument (and similarly \dowithpargument) works with both a word argument and a brace delimited argument; so both \csword optionA and \csword{optionA} work. That is a completely different use case then what the OP is looking for. – Aditya Oct 20 '10 at 15:07