6

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.

1

5 Answers 5

10

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.

0
3

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}}
2
  • 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. Commented Oct 19, 2010 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. Commented Oct 19, 2010 at 14:41
2

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

1
  • 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 …) Commented Oct 20, 2010 at 7:23
1

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
3
  • 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 ;) Commented Oct 20, 2010 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
    Commented Oct 20, 2010 at 7:03
  • Uh, it wouldn't be! Forget I said anything :) Commented Oct 20, 2010 at 7:07
1

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")}}
2
  • I was going to suggest this with \def\csword #1 {\csname #1\endcsname} but decided the restrictions on the syntax were a bit limiting. Commented Oct 20, 2010 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
    Commented Oct 20, 2010 at 15:07

You must log in to answer this question.

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