Prevent xparse from stripping braces?
So I'm trying to implement a COOL
-style Sum
macro which is supposed to support the following syntax:
Sum{ldots} % -> sum ldots
Sum[i]{ldots} % -> sum_{i} ldots
Sum[i in I]{ldots} % -> sum_{i in I} ldots
Sum[i, 0, N]{ldots} % -> sum_{i=0}^{N} ldots
Sum[{i,j}, 0, N]{ldots} % -> sum_{i,j=0}^{N} ldots
Sum[{i,j,k}]{ldots} % -> sum_{i,j,k} ldots
The important bit is that I would like braces to protect commas in the first argument. The actual implementation is in expl3
with the user interface using xparse
. Unfortunately, xparse
appears to always strip braces inside arguments if they surround the whole argument such that this protection will not work in all cases, no matter what my internal code does. Looking at the docs, I couldn't find a way to prevent this.
I tried macros signatures of the forms
DeclareDocumentCommand Sum { s >{ SplitArgument{2}{,} } o m }
{ ... }
DeclareDocumentCommand Sum { s o m }
{ ... }
Am I missing something here, is there a (undocumented) way to make xparse not strip such braces short of circumventing xparse and implementing the interface manually (maybe using argument processors?)? If not, would this warrant a bug report/feature request at the latex github?
xparse
add a comment |
So I'm trying to implement a COOL
-style Sum
macro which is supposed to support the following syntax:
Sum{ldots} % -> sum ldots
Sum[i]{ldots} % -> sum_{i} ldots
Sum[i in I]{ldots} % -> sum_{i in I} ldots
Sum[i, 0, N]{ldots} % -> sum_{i=0}^{N} ldots
Sum[{i,j}, 0, N]{ldots} % -> sum_{i,j=0}^{N} ldots
Sum[{i,j,k}]{ldots} % -> sum_{i,j,k} ldots
The important bit is that I would like braces to protect commas in the first argument. The actual implementation is in expl3
with the user interface using xparse
. Unfortunately, xparse
appears to always strip braces inside arguments if they surround the whole argument such that this protection will not work in all cases, no matter what my internal code does. Looking at the docs, I couldn't find a way to prevent this.
I tried macros signatures of the forms
DeclareDocumentCommand Sum { s >{ SplitArgument{2}{,} } o m }
{ ... }
DeclareDocumentCommand Sum { s o m }
{ ... }
Am I missing something here, is there a (undocumented) way to make xparse not strip such braces short of circumventing xparse and implementing the interface manually (maybe using argument processors?)? If not, would this warrant a bug report/feature request at the latex github?
xparse
Not a solution: You could inputSum[{{i,j,k}}]{ldots}
.
– Skillmon
Dec 15 '18 at 13:57
3
Why not simply using;
as delimiter? You would need no braces.
– egreg
Dec 15 '18 at 14:03
Using;
would likely work for my usecases, I don't think I've ever used a semicolon in sum limits. Still, wrapping the argument in braces was the first and obvious (to me) thing I tried to protect arguments that contain the delimiter, and I was surprised that this didn't work. Maybe I'll go for ';' plus the workaround from the other answers (even though that might be overkill)
– Wisperwind
Dec 16 '18 at 13:05
add a comment |
So I'm trying to implement a COOL
-style Sum
macro which is supposed to support the following syntax:
Sum{ldots} % -> sum ldots
Sum[i]{ldots} % -> sum_{i} ldots
Sum[i in I]{ldots} % -> sum_{i in I} ldots
Sum[i, 0, N]{ldots} % -> sum_{i=0}^{N} ldots
Sum[{i,j}, 0, N]{ldots} % -> sum_{i,j=0}^{N} ldots
Sum[{i,j,k}]{ldots} % -> sum_{i,j,k} ldots
The important bit is that I would like braces to protect commas in the first argument. The actual implementation is in expl3
with the user interface using xparse
. Unfortunately, xparse
appears to always strip braces inside arguments if they surround the whole argument such that this protection will not work in all cases, no matter what my internal code does. Looking at the docs, I couldn't find a way to prevent this.
I tried macros signatures of the forms
DeclareDocumentCommand Sum { s >{ SplitArgument{2}{,} } o m }
{ ... }
DeclareDocumentCommand Sum { s o m }
{ ... }
Am I missing something here, is there a (undocumented) way to make xparse not strip such braces short of circumventing xparse and implementing the interface manually (maybe using argument processors?)? If not, would this warrant a bug report/feature request at the latex github?
xparse
So I'm trying to implement a COOL
-style Sum
macro which is supposed to support the following syntax:
Sum{ldots} % -> sum ldots
Sum[i]{ldots} % -> sum_{i} ldots
Sum[i in I]{ldots} % -> sum_{i in I} ldots
Sum[i, 0, N]{ldots} % -> sum_{i=0}^{N} ldots
Sum[{i,j}, 0, N]{ldots} % -> sum_{i,j=0}^{N} ldots
Sum[{i,j,k}]{ldots} % -> sum_{i,j,k} ldots
The important bit is that I would like braces to protect commas in the first argument. The actual implementation is in expl3
with the user interface using xparse
. Unfortunately, xparse
appears to always strip braces inside arguments if they surround the whole argument such that this protection will not work in all cases, no matter what my internal code does. Looking at the docs, I couldn't find a way to prevent this.
I tried macros signatures of the forms
DeclareDocumentCommand Sum { s >{ SplitArgument{2}{,} } o m }
{ ... }
DeclareDocumentCommand Sum { s o m }
{ ... }
Am I missing something here, is there a (undocumented) way to make xparse not strip such braces short of circumventing xparse and implementing the interface manually (maybe using argument processors?)? If not, would this warrant a bug report/feature request at the latex github?
xparse
xparse
asked Dec 15 '18 at 13:37
WisperwindWisperwind
1676
1676
Not a solution: You could inputSum[{{i,j,k}}]{ldots}
.
– Skillmon
Dec 15 '18 at 13:57
3
Why not simply using;
as delimiter? You would need no braces.
– egreg
Dec 15 '18 at 14:03
Using;
would likely work for my usecases, I don't think I've ever used a semicolon in sum limits. Still, wrapping the argument in braces was the first and obvious (to me) thing I tried to protect arguments that contain the delimiter, and I was surprised that this didn't work. Maybe I'll go for ';' plus the workaround from the other answers (even though that might be overkill)
– Wisperwind
Dec 16 '18 at 13:05
add a comment |
Not a solution: You could inputSum[{{i,j,k}}]{ldots}
.
– Skillmon
Dec 15 '18 at 13:57
3
Why not simply using;
as delimiter? You would need no braces.
– egreg
Dec 15 '18 at 14:03
Using;
would likely work for my usecases, I don't think I've ever used a semicolon in sum limits. Still, wrapping the argument in braces was the first and obvious (to me) thing I tried to protect arguments that contain the delimiter, and I was surprised that this didn't work. Maybe I'll go for ';' plus the workaround from the other answers (even though that might be overkill)
– Wisperwind
Dec 16 '18 at 13:05
Not a solution: You could input
Sum[{{i,j,k}}]{ldots}
.– Skillmon
Dec 15 '18 at 13:57
Not a solution: You could input
Sum[{{i,j,k}}]{ldots}
.– Skillmon
Dec 15 '18 at 13:57
3
3
Why not simply using
;
as delimiter? You would need no braces.– egreg
Dec 15 '18 at 14:03
Why not simply using
;
as delimiter? You would need no braces.– egreg
Dec 15 '18 at 14:03
Using
;
would likely work for my usecases, I don't think I've ever used a semicolon in sum limits. Still, wrapping the argument in braces was the first and obvious (to me) thing I tried to protect arguments that contain the delimiter, and I was surprised that this didn't work. Maybe I'll go for ';' plus the workaround from the other answers (even though that might be overkill)– Wisperwind
Dec 16 '18 at 13:05
Using
;
would likely work for my usecases, I don't think I've ever used a semicolon in sum limits. Still, wrapping the argument in braces was the first and obvious (to me) thing I tried to protect arguments that contain the delimiter, and I was surprised that this didn't work. Maybe I'll go for ';' plus the workaround from the other answers (even though that might be overkill)– Wisperwind
Dec 16 '18 at 13:05
add a comment |
3 Answers
3
active
oldest
votes
You can trick this by inserting another token after the opening bracket if there is one (proof of concept like answer). Replace Wisperwind_Sum_original_code:nnn
with your original code to typeset the sum (including the handling of the optional star with IfBooleanTF
).
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
clist_new:N l_Wisperwind_clist
NewDocumentCommand Sum { s t[ }
{
IfBooleanTF { #2 }
{ Sum_two { #1 } [ use_none:n {} }
{ Sum_two { #1 } }
}
NewDocumentCommand Sum_two { m O{ use_none:n {} } m }
{
Wisperwind_Sum_original_code:non { #1 } { #2 } { #3 }
}
cs_new:Npn Wisperwind_Sum_original_code:nnn #1 #2 #3
{
sum
clist_set:Nn l_Wisperwind_clist { #2 }
int_case:nn { clist_count:N l_Wisperwind_clist }
{
{ 1 } { sb { clist_item:Nn l_Wisperwind_clist { c_one_int } } }
{ 2 }
{
sb { clist_item:Nn l_Wisperwind_clist { c_one_int } }
sp { clist_item:Nn l_Wisperwind_clist { 2 } }
}
{ 3 }
{
sb
{
clist_item:Nn l_Wisperwind_clist { c_one_int }
=
clist_item:Nn l_Wisperwind_clist { 2 }
}
sp { clist_item:Nn l_Wisperwind_clist { 3 } }
}
}
#3
}
cs_generate_variant:Nn Wisperwind_Sum_original_code:nnn { non }
ExplSyntaxOff
begin{document}
$Sum{ldots}$ % -> sum ldots
$Sum[i]{ldots}$ % -> sum_{i} ldots
$Sum[i in I]{ldots}$ % -> sum_{i in I} ldots
$Sum[i, 0, N]{ldots}$ % -> sum_{i=0}^{N} ldots
$Sum[{i,j}, 0, N]{ldots}$ % -> sum_{i,j=0}^{N} ldots
$Sum[{i,j,k}]{ldots}$ % -> sum_{i,j,k} ldots
end{document}
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
add a comment |
Here using LaTeX2e (see below for Plain TeX):
documentclass{article}
usepackage{listofitems}
makeatletter
newcommandSum{@ifnextchar[{Sumaux[@gobble}{sum}}
makeatother
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
begin{document}
[Sum{ldots} ]% -> sum ldots
[Sum[i]{ldots} ]% -> sum_{i} ldots
[Sum[i in I]{ldots} ]% -> sum_{i in I} ldots
[Sum[i, 0, N]{ldots} ]% -> sum_{i=0}^{N} ldots
[Sum[{i,j}, 0, N]{ldots} ]% -> sum_{i,j=0}^{N} ldots
[Sum[{i,j,k}]{ldots} ]% -> sum_{i,j,k} ldots
end{document}
This approach can also be implemented in Plain TeX:
input listofitems
defgobble#1{}
defSum{futureletnextdoSum}
defdoSum{ifx[next%
expandafterSumauxexpandafter[expandaftergobbleelsesumfi}
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
$$Sum{dots} $$% -> sum ldots
$$Sum[i]{ldots} $$% -> sum_{i} ldots
$$Sum[i in I]{ldots} $$% -> sum_{i in I} ldots
$$Sum[i, 0, N]{ldots} $$% -> sum_{i=0}^{N} ldots
$$Sum[{i,j}, 0, N]{ldots} $$% -> sum_{i,j=0}^{N} ldots
$$Sum[{i,j,k}]{ldots} $$% -> sum_{i,j,k} ldots
bye
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
add a comment |
The brace stripping is quite deliberate as newcommand
-generated commands requires braces for the case
foo[{]}]{bar}
whereas xparse
-generated ones do not. Without brace stripping,
foo[{bar}]
and
foo[bar]
could be treated differently.
In your case, you have a comma list with one entry. The obvious solution is to provide a second, empty, entry
Sum[{i,j},]{ldots}
as this is a no-op.
1
I actually find this behavior unexpected AND annoying. I would preferfoo[{bar}]
andfoo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.
– Steven B. Segletes
Dec 15 '18 at 16:45
2
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created withnewcommand
behave this way (as well as those created withdeffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.
– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes As we are not changing the catcode of[
/]
, the only way to have a TeX<balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.
– Joseph Wright♦
Dec 15 '18 at 18:17
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to sayfoo[bar[baz]]{test}
does not require braces if defined through xparse, becausefoo[{]}]{bar}
does. Is the reason mostly historical in order to matchnewcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.
– Wisperwind
Dec 16 '18 at 12:51
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
|
show 1 more comment
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "85"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f465970%2fprevent-xparse-from-stripping-braces%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can trick this by inserting another token after the opening bracket if there is one (proof of concept like answer). Replace Wisperwind_Sum_original_code:nnn
with your original code to typeset the sum (including the handling of the optional star with IfBooleanTF
).
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
clist_new:N l_Wisperwind_clist
NewDocumentCommand Sum { s t[ }
{
IfBooleanTF { #2 }
{ Sum_two { #1 } [ use_none:n {} }
{ Sum_two { #1 } }
}
NewDocumentCommand Sum_two { m O{ use_none:n {} } m }
{
Wisperwind_Sum_original_code:non { #1 } { #2 } { #3 }
}
cs_new:Npn Wisperwind_Sum_original_code:nnn #1 #2 #3
{
sum
clist_set:Nn l_Wisperwind_clist { #2 }
int_case:nn { clist_count:N l_Wisperwind_clist }
{
{ 1 } { sb { clist_item:Nn l_Wisperwind_clist { c_one_int } } }
{ 2 }
{
sb { clist_item:Nn l_Wisperwind_clist { c_one_int } }
sp { clist_item:Nn l_Wisperwind_clist { 2 } }
}
{ 3 }
{
sb
{
clist_item:Nn l_Wisperwind_clist { c_one_int }
=
clist_item:Nn l_Wisperwind_clist { 2 }
}
sp { clist_item:Nn l_Wisperwind_clist { 3 } }
}
}
#3
}
cs_generate_variant:Nn Wisperwind_Sum_original_code:nnn { non }
ExplSyntaxOff
begin{document}
$Sum{ldots}$ % -> sum ldots
$Sum[i]{ldots}$ % -> sum_{i} ldots
$Sum[i in I]{ldots}$ % -> sum_{i in I} ldots
$Sum[i, 0, N]{ldots}$ % -> sum_{i=0}^{N} ldots
$Sum[{i,j}, 0, N]{ldots}$ % -> sum_{i,j=0}^{N} ldots
$Sum[{i,j,k}]{ldots}$ % -> sum_{i,j,k} ldots
end{document}
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
add a comment |
You can trick this by inserting another token after the opening bracket if there is one (proof of concept like answer). Replace Wisperwind_Sum_original_code:nnn
with your original code to typeset the sum (including the handling of the optional star with IfBooleanTF
).
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
clist_new:N l_Wisperwind_clist
NewDocumentCommand Sum { s t[ }
{
IfBooleanTF { #2 }
{ Sum_two { #1 } [ use_none:n {} }
{ Sum_two { #1 } }
}
NewDocumentCommand Sum_two { m O{ use_none:n {} } m }
{
Wisperwind_Sum_original_code:non { #1 } { #2 } { #3 }
}
cs_new:Npn Wisperwind_Sum_original_code:nnn #1 #2 #3
{
sum
clist_set:Nn l_Wisperwind_clist { #2 }
int_case:nn { clist_count:N l_Wisperwind_clist }
{
{ 1 } { sb { clist_item:Nn l_Wisperwind_clist { c_one_int } } }
{ 2 }
{
sb { clist_item:Nn l_Wisperwind_clist { c_one_int } }
sp { clist_item:Nn l_Wisperwind_clist { 2 } }
}
{ 3 }
{
sb
{
clist_item:Nn l_Wisperwind_clist { c_one_int }
=
clist_item:Nn l_Wisperwind_clist { 2 }
}
sp { clist_item:Nn l_Wisperwind_clist { 3 } }
}
}
#3
}
cs_generate_variant:Nn Wisperwind_Sum_original_code:nnn { non }
ExplSyntaxOff
begin{document}
$Sum{ldots}$ % -> sum ldots
$Sum[i]{ldots}$ % -> sum_{i} ldots
$Sum[i in I]{ldots}$ % -> sum_{i in I} ldots
$Sum[i, 0, N]{ldots}$ % -> sum_{i=0}^{N} ldots
$Sum[{i,j}, 0, N]{ldots}$ % -> sum_{i,j=0}^{N} ldots
$Sum[{i,j,k}]{ldots}$ % -> sum_{i,j,k} ldots
end{document}
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
add a comment |
You can trick this by inserting another token after the opening bracket if there is one (proof of concept like answer). Replace Wisperwind_Sum_original_code:nnn
with your original code to typeset the sum (including the handling of the optional star with IfBooleanTF
).
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
clist_new:N l_Wisperwind_clist
NewDocumentCommand Sum { s t[ }
{
IfBooleanTF { #2 }
{ Sum_two { #1 } [ use_none:n {} }
{ Sum_two { #1 } }
}
NewDocumentCommand Sum_two { m O{ use_none:n {} } m }
{
Wisperwind_Sum_original_code:non { #1 } { #2 } { #3 }
}
cs_new:Npn Wisperwind_Sum_original_code:nnn #1 #2 #3
{
sum
clist_set:Nn l_Wisperwind_clist { #2 }
int_case:nn { clist_count:N l_Wisperwind_clist }
{
{ 1 } { sb { clist_item:Nn l_Wisperwind_clist { c_one_int } } }
{ 2 }
{
sb { clist_item:Nn l_Wisperwind_clist { c_one_int } }
sp { clist_item:Nn l_Wisperwind_clist { 2 } }
}
{ 3 }
{
sb
{
clist_item:Nn l_Wisperwind_clist { c_one_int }
=
clist_item:Nn l_Wisperwind_clist { 2 }
}
sp { clist_item:Nn l_Wisperwind_clist { 3 } }
}
}
#3
}
cs_generate_variant:Nn Wisperwind_Sum_original_code:nnn { non }
ExplSyntaxOff
begin{document}
$Sum{ldots}$ % -> sum ldots
$Sum[i]{ldots}$ % -> sum_{i} ldots
$Sum[i in I]{ldots}$ % -> sum_{i in I} ldots
$Sum[i, 0, N]{ldots}$ % -> sum_{i=0}^{N} ldots
$Sum[{i,j}, 0, N]{ldots}$ % -> sum_{i,j=0}^{N} ldots
$Sum[{i,j,k}]{ldots}$ % -> sum_{i,j,k} ldots
end{document}
You can trick this by inserting another token after the opening bracket if there is one (proof of concept like answer). Replace Wisperwind_Sum_original_code:nnn
with your original code to typeset the sum (including the handling of the optional star with IfBooleanTF
).
documentclass{article}
usepackage{xparse}
ExplSyntaxOn
clist_new:N l_Wisperwind_clist
NewDocumentCommand Sum { s t[ }
{
IfBooleanTF { #2 }
{ Sum_two { #1 } [ use_none:n {} }
{ Sum_two { #1 } }
}
NewDocumentCommand Sum_two { m O{ use_none:n {} } m }
{
Wisperwind_Sum_original_code:non { #1 } { #2 } { #3 }
}
cs_new:Npn Wisperwind_Sum_original_code:nnn #1 #2 #3
{
sum
clist_set:Nn l_Wisperwind_clist { #2 }
int_case:nn { clist_count:N l_Wisperwind_clist }
{
{ 1 } { sb { clist_item:Nn l_Wisperwind_clist { c_one_int } } }
{ 2 }
{
sb { clist_item:Nn l_Wisperwind_clist { c_one_int } }
sp { clist_item:Nn l_Wisperwind_clist { 2 } }
}
{ 3 }
{
sb
{
clist_item:Nn l_Wisperwind_clist { c_one_int }
=
clist_item:Nn l_Wisperwind_clist { 2 }
}
sp { clist_item:Nn l_Wisperwind_clist { 3 } }
}
}
#3
}
cs_generate_variant:Nn Wisperwind_Sum_original_code:nnn { non }
ExplSyntaxOff
begin{document}
$Sum{ldots}$ % -> sum ldots
$Sum[i]{ldots}$ % -> sum_{i} ldots
$Sum[i in I]{ldots}$ % -> sum_{i in I} ldots
$Sum[i, 0, N]{ldots}$ % -> sum_{i=0}^{N} ldots
$Sum[{i,j}, 0, N]{ldots}$ % -> sum_{i,j=0}^{N} ldots
$Sum[{i,j,k}]{ldots}$ % -> sum_{i,j,k} ldots
end{document}
edited Dec 15 '18 at 16:40
answered Dec 15 '18 at 14:16
SkillmonSkillmon
21.3k11941
21.3k11941
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
add a comment |
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
Thanks for this suggestion (although this is pretty much the 'hack' I had hoped to avoid)! The solution I had in mind would have been closer to @steven-b-segletes answer, but your code preserves the advanced parsing of nested optional arguments that xparse offers.
– Wisperwind
Dec 16 '18 at 12:55
add a comment |
Here using LaTeX2e (see below for Plain TeX):
documentclass{article}
usepackage{listofitems}
makeatletter
newcommandSum{@ifnextchar[{Sumaux[@gobble}{sum}}
makeatother
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
begin{document}
[Sum{ldots} ]% -> sum ldots
[Sum[i]{ldots} ]% -> sum_{i} ldots
[Sum[i in I]{ldots} ]% -> sum_{i in I} ldots
[Sum[i, 0, N]{ldots} ]% -> sum_{i=0}^{N} ldots
[Sum[{i,j}, 0, N]{ldots} ]% -> sum_{i,j=0}^{N} ldots
[Sum[{i,j,k}]{ldots} ]% -> sum_{i,j,k} ldots
end{document}
This approach can also be implemented in Plain TeX:
input listofitems
defgobble#1{}
defSum{futureletnextdoSum}
defdoSum{ifx[next%
expandafterSumauxexpandafter[expandaftergobbleelsesumfi}
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
$$Sum{dots} $$% -> sum ldots
$$Sum[i]{ldots} $$% -> sum_{i} ldots
$$Sum[i in I]{ldots} $$% -> sum_{i in I} ldots
$$Sum[i, 0, N]{ldots} $$% -> sum_{i=0}^{N} ldots
$$Sum[{i,j}, 0, N]{ldots} $$% -> sum_{i,j=0}^{N} ldots
$$Sum[{i,j,k}]{ldots} $$% -> sum_{i,j,k} ldots
bye
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
add a comment |
Here using LaTeX2e (see below for Plain TeX):
documentclass{article}
usepackage{listofitems}
makeatletter
newcommandSum{@ifnextchar[{Sumaux[@gobble}{sum}}
makeatother
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
begin{document}
[Sum{ldots} ]% -> sum ldots
[Sum[i]{ldots} ]% -> sum_{i} ldots
[Sum[i in I]{ldots} ]% -> sum_{i in I} ldots
[Sum[i, 0, N]{ldots} ]% -> sum_{i=0}^{N} ldots
[Sum[{i,j}, 0, N]{ldots} ]% -> sum_{i,j=0}^{N} ldots
[Sum[{i,j,k}]{ldots} ]% -> sum_{i,j,k} ldots
end{document}
This approach can also be implemented in Plain TeX:
input listofitems
defgobble#1{}
defSum{futureletnextdoSum}
defdoSum{ifx[next%
expandafterSumauxexpandafter[expandaftergobbleelsesumfi}
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
$$Sum{dots} $$% -> sum ldots
$$Sum[i]{ldots} $$% -> sum_{i} ldots
$$Sum[i in I]{ldots} $$% -> sum_{i in I} ldots
$$Sum[i, 0, N]{ldots} $$% -> sum_{i=0}^{N} ldots
$$Sum[{i,j}, 0, N]{ldots} $$% -> sum_{i,j=0}^{N} ldots
$$Sum[{i,j,k}]{ldots} $$% -> sum_{i,j,k} ldots
bye
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
add a comment |
Here using LaTeX2e (see below for Plain TeX):
documentclass{article}
usepackage{listofitems}
makeatletter
newcommandSum{@ifnextchar[{Sumaux[@gobble}{sum}}
makeatother
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
begin{document}
[Sum{ldots} ]% -> sum ldots
[Sum[i]{ldots} ]% -> sum_{i} ldots
[Sum[i in I]{ldots} ]% -> sum_{i in I} ldots
[Sum[i, 0, N]{ldots} ]% -> sum_{i=0}^{N} ldots
[Sum[{i,j}, 0, N]{ldots} ]% -> sum_{i,j=0}^{N} ldots
[Sum[{i,j,k}]{ldots} ]% -> sum_{i,j,k} ldots
end{document}
This approach can also be implemented in Plain TeX:
input listofitems
defgobble#1{}
defSum{futureletnextdoSum}
defdoSum{ifx[next%
expandafterSumauxexpandafter[expandaftergobbleelsesumfi}
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
$$Sum{dots} $$% -> sum ldots
$$Sum[i]{ldots} $$% -> sum_{i} ldots
$$Sum[i in I]{ldots} $$% -> sum_{i in I} ldots
$$Sum[i, 0, N]{ldots} $$% -> sum_{i=0}^{N} ldots
$$Sum[{i,j}, 0, N]{ldots} $$% -> sum_{i,j=0}^{N} ldots
$$Sum[{i,j,k}]{ldots} $$% -> sum_{i,j,k} ldots
bye
Here using LaTeX2e (see below for Plain TeX):
documentclass{article}
usepackage{listofitems}
makeatletter
newcommandSum{@ifnextchar[{Sumaux[@gobble}{sum}}
makeatother
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
begin{document}
[Sum{ldots} ]% -> sum ldots
[Sum[i]{ldots} ]% -> sum_{i} ldots
[Sum[i in I]{ldots} ]% -> sum_{i in I} ldots
[Sum[i, 0, N]{ldots} ]% -> sum_{i=0}^{N} ldots
[Sum[{i,j}, 0, N]{ldots} ]% -> sum_{i,j=0}^{N} ldots
[Sum[{i,j,k}]{ldots} ]% -> sum_{i,j,k} ldots
end{document}
This approach can also be implemented in Plain TeX:
input listofitems
defgobble#1{}
defSum{futureletnextdoSum}
defdoSum{ifx[next%
expandafterSumauxexpandafter[expandaftergobbleelsesumfi}
defSumaux[#1]#2{
sum
setsepchar{,}
readlistsumargs{#1}
ifnumlistlensumargs>1relax
_{sumargs[1]=sumargs[2]}
ifnumlistlensumargs>2relax^sumargs[3]fi
else
_{sumargs[1]}
fi
#2
}
$$Sum{dots} $$% -> sum ldots
$$Sum[i]{ldots} $$% -> sum_{i} ldots
$$Sum[i in I]{ldots} $$% -> sum_{i in I} ldots
$$Sum[i, 0, N]{ldots} $$% -> sum_{i=0}^{N} ldots
$$Sum[{i,j}, 0, N]{ldots} $$% -> sum_{i,j=0}^{N} ldots
$$Sum[{i,j,k}]{ldots} $$% -> sum_{i,j,k} ldots
bye
edited Dec 15 '18 at 18:05
answered Dec 15 '18 at 14:55
Steven B. SegletesSteven B. Segletes
153k9193400
153k9193400
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
add a comment |
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
Thanks for the input, I'm writing this using expl3 + xparse, though (obviously I could translate your code to expl3) and for the reason mentioned in another comment will likely prefer @Skillmon 's suggestion
– Wisperwind
Dec 16 '18 at 12:57
add a comment |
The brace stripping is quite deliberate as newcommand
-generated commands requires braces for the case
foo[{]}]{bar}
whereas xparse
-generated ones do not. Without brace stripping,
foo[{bar}]
and
foo[bar]
could be treated differently.
In your case, you have a comma list with one entry. The obvious solution is to provide a second, empty, entry
Sum[{i,j},]{ldots}
as this is a no-op.
1
I actually find this behavior unexpected AND annoying. I would preferfoo[{bar}]
andfoo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.
– Steven B. Segletes
Dec 15 '18 at 16:45
2
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created withnewcommand
behave this way (as well as those created withdeffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.
– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes As we are not changing the catcode of[
/]
, the only way to have a TeX<balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.
– Joseph Wright♦
Dec 15 '18 at 18:17
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to sayfoo[bar[baz]]{test}
does not require braces if defined through xparse, becausefoo[{]}]{bar}
does. Is the reason mostly historical in order to matchnewcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.
– Wisperwind
Dec 16 '18 at 12:51
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
|
show 1 more comment
The brace stripping is quite deliberate as newcommand
-generated commands requires braces for the case
foo[{]}]{bar}
whereas xparse
-generated ones do not. Without brace stripping,
foo[{bar}]
and
foo[bar]
could be treated differently.
In your case, you have a comma list with one entry. The obvious solution is to provide a second, empty, entry
Sum[{i,j},]{ldots}
as this is a no-op.
1
I actually find this behavior unexpected AND annoying. I would preferfoo[{bar}]
andfoo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.
– Steven B. Segletes
Dec 15 '18 at 16:45
2
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created withnewcommand
behave this way (as well as those created withdeffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.
– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes As we are not changing the catcode of[
/]
, the only way to have a TeX<balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.
– Joseph Wright♦
Dec 15 '18 at 18:17
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to sayfoo[bar[baz]]{test}
does not require braces if defined through xparse, becausefoo[{]}]{bar}
does. Is the reason mostly historical in order to matchnewcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.
– Wisperwind
Dec 16 '18 at 12:51
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
|
show 1 more comment
The brace stripping is quite deliberate as newcommand
-generated commands requires braces for the case
foo[{]}]{bar}
whereas xparse
-generated ones do not. Without brace stripping,
foo[{bar}]
and
foo[bar]
could be treated differently.
In your case, you have a comma list with one entry. The obvious solution is to provide a second, empty, entry
Sum[{i,j},]{ldots}
as this is a no-op.
The brace stripping is quite deliberate as newcommand
-generated commands requires braces for the case
foo[{]}]{bar}
whereas xparse
-generated ones do not. Without brace stripping,
foo[{bar}]
and
foo[bar]
could be treated differently.
In your case, you have a comma list with one entry. The obvious solution is to provide a second, empty, entry
Sum[{i,j},]{ldots}
as this is a no-op.
answered Dec 15 '18 at 15:59
Joseph Wright♦Joseph Wright
202k21555882
202k21555882
1
I actually find this behavior unexpected AND annoying. I would preferfoo[{bar}]
andfoo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.
– Steven B. Segletes
Dec 15 '18 at 16:45
2
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created withnewcommand
behave this way (as well as those created withdeffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.
– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes As we are not changing the catcode of[
/]
, the only way to have a TeX<balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.
– Joseph Wright♦
Dec 15 '18 at 18:17
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to sayfoo[bar[baz]]{test}
does not require braces if defined through xparse, becausefoo[{]}]{bar}
does. Is the reason mostly historical in order to matchnewcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.
– Wisperwind
Dec 16 '18 at 12:51
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
|
show 1 more comment
1
I actually find this behavior unexpected AND annoying. I would preferfoo[{bar}]
andfoo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.
– Steven B. Segletes
Dec 15 '18 at 16:45
2
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created withnewcommand
behave this way (as well as those created withdeffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.
– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes As we are not changing the catcode of[
/]
, the only way to have a TeX<balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.
– Joseph Wright♦
Dec 15 '18 at 18:17
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to sayfoo[bar[baz]]{test}
does not require braces if defined through xparse, becausefoo[{]}]{bar}
does. Is the reason mostly historical in order to matchnewcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.
– Wisperwind
Dec 16 '18 at 12:51
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
1
1
I actually find this behavior unexpected AND annoying. I would prefer
foo[{bar}]
and foo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.– Steven B. Segletes
Dec 15 '18 at 16:45
I actually find this behavior unexpected AND annoying. I would prefer
foo[{bar}]
and foo[bar]
to be (potentially) treated differently. But I also see the need, as in the initial case you present, to embrace the bracket.– Steven B. Segletes
Dec 15 '18 at 16:45
2
2
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created with
newcommand
behave this way (as well as those created with deffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes I guess the reason is as (almost) always of historical nature. Since optional arguments created with
newcommand
behave this way (as well as those created with deffoo[#1]
) it is the expected behaviour for optional arguments, because it always was this way.– Skillmon
Dec 15 '18 at 16:47
@StevenB.Segletes As we are not changing the catcode of
[
/]
, the only way to have a TeX <balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.– Joseph Wright♦
Dec 15 '18 at 18:17
@StevenB.Segletes As we are not changing the catcode of
[
/]
, the only way to have a TeX <balanced text>
argument as an optional argument is to use a brace pair. Thus when the optional argument is exactly one balanced text we strip braces. It's the only way to handle stuff 'reasonably'.– Joseph Wright♦
Dec 15 '18 at 18:17
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to say
foo[bar[baz]]{test}
does not require braces if defined through xparse, because foo[{]}]{bar}
does. Is the reason mostly historical in order to match newcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.– Wisperwind
Dec 16 '18 at 12:51
@JosephWright I think I don't really understand what exactly wouldn't work without stripping braces. Wrt the first example, I guess you meant to say
foo[bar[baz]]{test}
does not require braces if defined through xparse, because foo[{]}]{bar}
does. Is the reason mostly historical in order to match newcommand
as @skillmon suggests? For most macros, I would expect additional braces not being stripped to not be an issue, thus this was also rather unexpected to me.– Wisperwind
Dec 16 '18 at 12:51
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
The workaround you suggest is what I'm currently doing, but I'd prefer not to have such quirks in the user interface.
– Wisperwind
Dec 16 '18 at 12:52
|
show 1 more comment
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f465970%2fprevent-xparse-from-stripping-braces%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Not a solution: You could input
Sum[{{i,j,k}}]{ldots}
.– Skillmon
Dec 15 '18 at 13:57
3
Why not simply using
;
as delimiter? You would need no braces.– egreg
Dec 15 '18 at 14:03
Using
;
would likely work for my usecases, I don't think I've ever used a semicolon in sum limits. Still, wrapping the argument in braces was the first and obvious (to me) thing I tried to protect arguments that contain the delimiter, and I was surprised that this didn't work. Maybe I'll go for ';' plus the workaround from the other answers (even though that might be overkill)– Wisperwind
Dec 16 '18 at 13:05