Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
    
I edited my answer to fix a bug. –  Harald Hanche-Olsen Oct 19 '10 at 15:17
add comment

5 Answers

up vote 9 down vote accepted

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.

share|improve this answer
    
Seems to do the work! Thanks! –  Juan A. Navarro Oct 19 '10 at 14:44
add comment

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}}
share|improve this answer
    
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
add comment

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
share|improve this answer
    
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
add comment

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 :)

share|improve this answer
    
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
add comment

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")}}
share|improve this answer
    
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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.