The following MWE is a very much simplified version of a more complicated system I am trying to develop. As a result, the interface seems a bit weird, and I don’t expect anyone to like it in the current form, nor do I myself. However it demonstrates some expansion issues I am dealing with.
Let us say that I want to create a macro system for any interface of the form
⟨symbol⟩ ^ ⟨upper index⟩ _ ⟨lower index⟩ ( ⟨argument⟩ )
For this purpose, I create the command \newmathfunctioncommand{#1}
(with #1
being a command name without backslash). This defines a number of token lists to store the power, index, and argument in, as well as
the command \#1
with the interface
\#1 { ⟨symbol⟩ } [ ⟨upper index⟩ ] [ ⟨lower index⟩ ] { ⟨argument⟩ (optional argument) } [ ⟨output math function⟩ ]
This is supposed to print the line ⟨symbol⟩ ^ ⟨upper index⟩ _ ⟨lower index⟩ ( ⟨argument⟩ )
as above. Then it takes an additional optional argument ⟨output math function⟩
. This is supposed to contain the name of another math function command to wrap around the final value of the function \#1
, so that this itself becomes a math function.
In other words, it should allow interfaces like:
\newmathfunctioncommand{fun}
\newmathfunctioncommand{otherfun}
$\fun{f}[i][j]$ % Should output f^{i}_{j}
$\fun{f}[i][j]{x}$ % Should output f^{i}_{j}(x)
$\fun{f}[\fun{g}{x}]{t}$ % Should output f^{g(x)}(t)
$\fun{f}[i][j]{x}[otherfun]$ % Should output \otherfun{ f^{i}_{j}(x) }
$\fun{f}[i][j]{x}[otherfun]{t}$
% Should output \otherfun{ f^{i}_{j}(x) }{t},
% i.e., f^{i}_{j}(x)(t)
The purpose of this is to allow functions whose values are other functions, which is a well-known phenomenon in mathematics. However, this is the part of the system that I cannot get to work. The code that breaks is the one wrapped in the tag <code that does not work>
. A piece of code that does work is in the tags <code that does work>
. This working code implements everything except the output function system. Can someone fix this expansion issue?
Note that the reason for the use of \group_begin: ... \group_end:
is to allow expressions like \fun{f}[\fun{g}{x}]{t}
without causing infinite loops.
I am aware of some parts of the code which seem ridiculous in this MWE, like, output functions being set without a backslash. I ask you to keep this system, because they make much better sense in the more complicated setup I am really trying to create. Also, lots of stuff has been removed in this version of the code to keep it short, e.g., control of the size of parentheses.
\documentclass{article}
\usepackage{amsmath,xparse}
\ExplSyntaxOn
\tl_new:N\l_mathfunction_temp
\cs_set:Npn\g_mathfunction_identity:n#1 { #1 } % the identity function
\DeclareDocumentCommand\newmathfunctioncommand{m}
{
% This defines the command \⟨#1⟩. First we define some token lists
% to store the data of upper index, lower index etc. in.
\tl_new:c { g_mathfunction_#1_upper_index }
\tl_new:c { g_mathfunction_#1_lower_index }
\tl_new:c { g_mathfunction_#1_argument }
\tl_new:c { g_mathfunction_#1_output_function }
\tl_set:cn { g_mathfunction_#1_output_function } { g_mathfunction_identity:n }
% This will be the default output function if none else is given
\exp_args:Nc\DeclareDocumentCommand{#1}{moogo}{
\group_begin:
\IfNoValueTF { ##2 } % If ##2 is non-empty, set upper index token list
% to that value
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_upper_index } { \exp_not:n { ##2 } }
}
\IfNoValueTF { ##3 } % If ##3 is non-empty, set lower index token list
% to that value
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_lower_index } { \exp_not:n { ##3 } }
}
\IfNoValueTF { ##4 } % If ##4 is non-empty, set argument token list
% to that value
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_argument } {
\exp_not:n { ##4 }
}
}
\IfNoValueTF { ##5 } % If ##5 is non-empty, set output function
% token list to that value.
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_output_function } { \exp_not:n { ##5 } }
}
%
%
%
\tl_set:Nx\l_mathfunction_temp % Store output data in a temporary
% token list
{
\exp_not:n { ##1 }
\tl_if_empty:cTF { g_mathfunction_#1_upper_index }
{
% do nothing
}
{
\sp { \use:c { g_mathfunction_#1_upper_index } }
}
\tl_if_empty:cTF { g_mathfunction_#1_lower_index }
{
% do nothing
}
{
\sb { \use:c { g_mathfunction_#1_lower_index } }
}
\tl_if_empty:cTF { g_mathfunction_#1_argument }
{
% do nothing
}
{
(\use:c { g_mathfunction_#1_argument } )
}
}
%
% Now if the argument has a value, and only in this case, set
% the command \l_mathfunction_output_command:n to be equal
% to the output math function command.
%
\tl_if_empty:cTF { g_mathfunction_#1_argument }
{
\cs_set_eq:NN
\l_mathfunction_output_command:n
\g_mathfunction_identity:n
}
{
\exp_args:Nno\cs_set_eq:cc
{ l_mathfunction_output_command:n }
{ \use:c { g_mathfunction_#1_output_function } }
}
%
%
% Now finally it is time to render the actual output
%
% <code that does not work>
\exp_args:NNo
\exp_args:NNV
\group_end:
\l_mathfunction_output_command:n
{ \l_mathfunction_temp }
% </code that does not work>
%
%
% <code that does work>
% \exp_args:NV
% \group_end:
% \l_mathfunction_temp
% </code that does work>
}
}
\DeclareDocumentCommand\mathfunctionsetup{moogo}
% a command to setup the arguments
% globally.
{
\IfNoValueTF { #2 }
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_upper_index } { \exp_not:n { #2 } }
}
\IfNoValueTF { #3 }
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_lower_index } { \exp_not:n { #3 } }
}
\IfNoValueTF { #4 }
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_argument } {
\exp_not:n { #4 }
}
}
\IfNoValueTF { #5 }
{
% do nothing
}
{
\tl_set:cn { g_mathfunction_#1_output_function } { \exp_not:n { #5 } }
}
}
\ExplSyntaxOff
\begin{document}
\newmathfunctioncommand{fun}
\newmathfunctioncommand{otherfun}
$\fun{f}[i][j]$ % Should output f^{i}_{j}
$\fun{f}[i][j]{x}$ % Should output f^{i}_{j}(x)
$\fun{f}[\fun{g}{x}]{t}$ % Should output f^{g(x)}(t)
$\fun{f}[i][j]{x}[otherfun]$ % Should output \otherfun{ f^{i}_{j}(x) }
$\fun{f}[i][j]{x}[otherfun]{t}$
% Should output \otherfun{ f^{i}_{j}(x) }{t},
% i.e., f^{i}_{j}(x)(t)
\mathfunctionsetup{fun}[a][b][otherfun]
$\fun{g}{x}{t}$ % Should output \otherfun { g^{a}_{b}(x) } { t },
% i.e., g^{i}_{j}(x)(t)
\mathfunctionsetup{fun}[a][b][fun]
$\fun{g}{x}[p][q]{r}$ % Should output \fun { g^a_b(x) }[p][q]{r},
% i.e., g^{a}_{b}(x) ^{p}_{q}(r)
\end{document}
\function[keyvals]{argument}
. It is important to be able to omit the argument and render only the function. And braces are useful because they take care of any issues in expressions like\function{\function{x}+1}
. Otherwise, parentheses could also be used. However, as I said many times: The interface in the stated, simplified form is weird, yes, but the original version would make the MWE too long. I did not see any other way to formulate the question. The expansion issue I have is still pretty clear and concrete IMO.\fun{\fun{x}}{t}
from looping. I think you can forget about expansion and just put a system to stop that into the definition:\IfValueTF{##4}{ <ensure it loops one more time> } { <ensure it DOESN'T loop again> }
wouldn't that be enough?\function[keyval]{\function{x},\function{y}}
. This will first set up a\group_begin: ... \group_end:
, and in this group, the general setup function is applied tokeyval
. It is then important that this keyval assignment does not affect\function{x}
and\function{y}
.upper
sets the upper index, and the keyarg
sets the argument. Then\fun[upper=7]{x}
is really just short for\fun[upper=7,arg=x]
. Then I need to be able to say\fun[upper=\fun{t},arg=\fun{s}]
without creating a loop.