5

In order to learn better about handling dynamic arguments and "blocks" like what you pass to a command/environment, I would like to try to write a wrapper function around \NewDocumentEnvironment and \NewDocumentCommand that does pretty much exactly what they do. However I would like the API to be:

\myenvironment{foo}{<args>}{startblock}{endblock}
\mycommand{foo}{<args>}{block}

i.e. even though the API is \NewDocumentCommand{\foo} with the backslash \foo, I would like to make it foo.

Here is the MWE for the system:

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\begin{document}

\NewDocumentCommand{\mycommand}{mmm} {
  \NewDocumentCommand{#1}{#2}{#3}
}

\mycommand{myenvironment}{mmmO{}} {
  \NewDocumentEnvironment{#1}{#2}{#3}{#4}
}

\myenvironment{foo}{m}{
  \typeout{#1}
}{}

\begin{foo}{abc}

\end{foo}

\end{document}

The things I am not sure about are:

  1. Passing through the arguments like \NewDocumentEnvironment{#1}{#2}{#3}{#4}
  2. Handling "blocks", since all I've ever dealt with in passing arguments is simple one-liners/strings/key-val pairs.
  3. How to handle the foo to \foo transformation when passing through.

Wondering how to get this to work using the above definitions for \myenvironment and \mycommand:

\begin{foo}{abc}

\end{foo}
  • I doubt that you get much with this 'chain' loading here. And you don't need \ExplSyntaxOn if you're using xparse macros alone – user31729 Mar 25 '18 at 20:21
  • There are a lot of spurious spaces in your macros which are just not notified because of using \ExplSyntaxOn. – user31729 Mar 25 '18 at 20:27
  • 1
    Off-Topic: You have asked a lot of questions in the meantime, and some of them have answers, but are not accepted. Please go through your list of questions and consider to accept the answers – user31729 Mar 25 '18 at 20:35
  • @ChristianHupfer will do – Lance Pollard Mar 25 '18 at 20:42
3

Avoid doing general definitions inside document. And remember to issue \ExplSyntaxOff when you're done with the code part.

You could make \NewDocumentCommand to accept a string instead of a control sequence. The question is: why? It just obfuscates code, generally speaking.

Anyway, \exp_args:Nc is the construct to use. It tells LaTeX to build a control sequence from the braced argument following the next token and then it disappears. It's the same, in “oldstyle” TeX programming as

\expandafter\token\csname string\endcsname

Note also that your \myenvironment has an optional final argument, so it should be given in brackets [] rather than in braces {}.

\documentclass[a4paper]{article}

\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\mycommand}{mmm}
 {
  \exp_args:Nc \NewDocumentCommand{#1}{#2}{#3}
 }

\mycommand{myenvironment}{mmmO{}}
 {
  \NewDocumentEnvironment{#1}{#2}{#3}{#4}
 }

\ExplSyntaxOff

\myenvironment{foo}{m}
 {%
  \typeout{#1}%
 }

\mycommand{baz}{m}{X#1X}

\begin{document}

\baz{Y}

\begin{foo}{abc}
Something inside
\end{foo}

\end{document}
  • I guess I need to learn the meaning of "control sequence", don't understand that one yet. – Lance Pollard Mar 25 '18 at 22:29
  • @LancePollard \section is a control sequence, foo isn't. – egreg Mar 25 '18 at 22:29
  • Was looking how to do this with LaTeX3 features, thanks for pointing out the \exp_args:Nc and \expandafter\token\csname string\endcsname equivalence. – Lance Pollard Mar 25 '18 at 22:31
  • @LancePollard expl3 and xparse are meant to provide consistent interfaces for programming. Stretching them like doing \mycommand isn't good for users. – egreg Mar 25 '18 at 22:31
  • actually this doesn't work, getting ! LaTeX Error: \begin{document} ended by \end{thebibliography} if changing to \myenvironment{foo}{m}{\begin{thebibliography}{9}}{\end{thebibliography}}. This does work however: \expandafter\NewDocumentCommand\expandafter{\csname#1\endcsname}{#2}{#3} not sure why latex3 way doesn't. – Lance Pollard Mar 27 '18 at 1:45
4

I don't see the point why you would want to tokenize the arguments, just to pass them on. Instead, just curry the \NewDocumentCommand.

\documentclass{article}

\usepackage{xparse}

\newcommand\mycommand[1]{\expandafter\NewDocumentCommand\csname#1\endcsname}
\newcommand\myenvironment{\NewDocumentEnvironment}

\begin{document}

\mycommand{hello}{}{world}

\hello

\myenvironment{foo}{m}{
  \typeout{#1}
}{}

\begin{foo}{abc}

\end{foo}

\end{document}
  • Didn't realize you could do this, thanks! – Lance Pollard Mar 25 '18 at 22:09
  • Wondering how you knew to do the \expandafter. – Lance Pollard Mar 25 '18 at 22:10
  • 3
    @LancePollard Actually this partial macro application (also called currying) is an often overlooked technique which can make a lot of macro code much simpler. Perhaps I'll write a TUGboat article on this and other functional programming techniques TeX. – Henri Menke Mar 25 '18 at 22:31
  • Yes please, do it! – gigabytes Mar 26 '18 at 1:53
2
\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\NewDocumentCommand{\mycommand}{ m m +m } {
  \expandafter\NewDocumentCommand\expandafter{\csname#1\endcsname}{#2}{#3}
}

\mycommand{myenvironment} { m m +m +m } {
  \NewDocumentEnvironment{#1}{#2}{#3}{#4}
}

\myenvironment{foo}{ m }{
  \typeout{#1}
}{}

\begin{document}


\begin{foo}{abc}

\end{foo}

\end{document}
2

The \mycommand macro should be defined as

\NewDocumentCommand{\mycommand}{mm+m} {%
  \expandafter\NewDocumentCommand\csname #1\endcsname{#2}{#3}%
}

since the #1 must become the argument name, so the typical \expandafter\NewDocumentCommand\csname #1\endcsname.

The 3rd. argument 'must' be +m in order to allow for the definition code of \foo, which is more likely to contain just more than one paragraph.

The same is true for the 3rd and 4th argument of \myenvironment.

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{xparse}


\begin{document}

\NewDocumentCommand{\mycommand}{mm+m} {%
  \expandafter\NewDocumentCommand\csname #1\endcsname{#2}{#3}%
}

\mycommand{myenvironment}{mm+m+O{}} {%
  \NewDocumentEnvironment{#1}{#2}{#3}{#4}
}

\myenvironment{foo}{m}{%
  \typeout{#1}
}{}

\begin{foo}{abc}

\end{foo}

\mycommand{foobar}{om}{%
  \IfValueT{#1}{Yes, there is an opt. argument: #1}

  Mandatory Argument: #2

}

\foobar{Hello World}

\foobar[And now for something completely different]{Hello World}

\end{document}

enter image description here

  • @Skillmon: I am not finished yet and the \myenvironment code is not changed from the O.P. (yet) – user31729 Mar 25 '18 at 20:24
  • Wondering how you knew to do the \expandafter\NewDocumentCommand\csname #1\endcsname , I have not seen that before. I am new to "expansion", haven't used it yet at all. – Lance Pollard Mar 25 '18 at 22:15
  • 1
    @LancePollard: If you would have asked 5 years ago I would not have known about \expandafter. And I could not have answered your question. You have to construct the command sequence name (note, the name, not the command sequence definition!), i.e. \foo first, before \NewDocumentCommand can grab this name and define it as a sequence, so you have to 'jump' over \NewDocumentCommand with \expandafter, letting \csname ...\endcsname come into action, this marks a valid command sequence name then and \NewDocumentCommand does the rest. The same would be true for \newcommand, anyway – user31729 Mar 25 '18 at 22:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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