Я пытаюсь применить expl3
функцию к содержимому среды. В LaTeX2e это можно сделать с помощью \bgroup
и \egroup
. В expl3
что-то не работает.
Я ожидал, что следующий код выведет "AAA hey BBB". Вместо этого он выводит "AAA BBB hey".
\ExplSyntaxOn
\cs_new:Npn \bar:n #1 {
AAA #1 BBB
}
\NewDocumentEnvironment{foo}{}{
\bar:n \bgroup
}{
\egroup
}
\ExplSyntaxOff
\begin{document}
\begin{foo}
hey
\end{foo}
\end{document}
Есть ли у вас мысли о том, почему это происходит, и о возможных путях решения?
Редактировать
Вот мой текущий подход:
\cs_new:Npn \env_new:Nn #1#2 {
\tl_new:N #1
\tl_set:Nn #1 { #2 }
}
\cs_new:Npn \env_begin:N #1 {}
\cs_new:Npn \env_end:N #1 {}
\cs_new:Npn \env_capture_on: {
\global \let \env_begin:N \group_begin:
\global \let \env_end:N \group_end:
}
\cs_new:Npn \env_capture_off: {
\global\let\env_begin:N\env_original_begin:N
\global\let\env_end:N\env_original_end:N
}
\cs_new:Npn \env_original_begin:N #1 {
\env_capture_on:
\tl_use:N #1
\group_begin:
\env_capture_off:
}
\cs_new:Npn \env_original_end:N #1 {
}
\env_capture_off:
\cs_new:Npn \foo:n #1 {
AAA #1 BBB
}
\env_new:Nn \baf {
\foo:n
}
\env_begin:N \baf
hey
\env_end:N \baf
Это не сработает, поскольку \group_begin: и \group_end: не могут использоваться для захвата аргументов функции (т.е. \foo \group_begin: A \group_end:
работают иначе, чем \foo { A }
).
Есть ли способ временно заставить управляющую последовательность вести себя так, как если бы она была явной фигурной скобкой?
Решение
Я думаю, у меня есть решение. Как я уже упоминал в комментариях, можно объединить сопоставление с образцом с подсчетом числа \begin
s, чтобы захватить содержимое окружения. Для меня было немного удивительно, что этоименно такчто происходит в environ
, чуть нижеМНОГОнотации.
Поскольку я знаю, что это environ
не сработало в моем предполагаемом приложении, я решил переписать его максимально простым способом, чтобы я мог понять, где мой код дает сбой (теперь у меня есть представление о том, что пошло не так, но это не суть). Ниже вы можете найти реализацию в expl3
. Я не тестировал ее тщательно, но я считаю, что она работает.
Любые комментарии и отзывы будут высоко оценены.
\int_new:N \env_count
\cs_new:Npn \env_new:Nn #1#2 {
\cs_new:cpn { env_defined@ \cs_to_str:N #1 :nn } ##1##2 {
#2
}
}
\cs_new:Npn \env_countbegin:w #1\xbegin#2 {
\cs_if_free:NTF #2 {
\int_incr:N \env_count
\env_countbegin:w
} {
\cs_if_eq:NNTF #2 \xend {} {
\int_incr:N \env_count
\env_countbegin:w
}
}
}
\cs_new:Npn \env_collect:w #1#2#3#4\xend#5 {
\env_countbegin:w #4\xbegin\xend
\int_compare:nTF { \env_count = 0 } {
\use:c { env_defined@ \cs_to_str:N #1 :nn } { #3 #4 } { #2 }
}{
\int_decr:N \env_count
\env_collect:w {#1} {#2} { #3 #4 \xend{#5} }
}
}
\NewDocumentCommand \xbegin {mo} {
\int_zero:N \env_count
\env_collect:w {#1} {#2} {}
}
\NewDocumentCommand \xend {} {}
\env_new:Nn \foo {
AAA #1 BBB
}
\xbegin{\foo}
\xbegin{\foo}
hey
\xend{\foo}
\xend{\foo}
решение1
В нем естьникогдаможно было бы зацепить аргумент с \bgroup
и \egroup
. Версия
\newenvironment{foo}{%
\baz\bgroup
}{%
\egroup
}
\newcommand{\baz}[1]{AAA #1 BBB}
потерпит неудачу точно так же. Аргументом для \baz
будет \bgroup
, поэтому вывод
x\begin{foo}
key
\end{foo}y
было бы
xAAA<space><space>BBB<space>key<space>y
Один из неожиданных пробелов появляется в конце строки после \begin{foo}
, другой — в конце строки после key
.
Можно использовать environ
, как показал Кристиан Хапфер:
\usepackage{xparse,environ}
\ExplSyntaxOn
\NewEnviron{foo}
{
\jaeya_baz:V \BODY
}
\cs_new:Nn \jaeya_baz:n { AAA~#1~BBB }
\cs_generate_variant:Nn \jaeya_baz:n { V }
\ExplSyntaxOff
и вывод кода выше будет ожидаемым
xAAA<space>key<space}BBBy
Добавление функций, подобных \NewEnviron
представленным, xparse
должно быть в списке задач команды LaTeX.
решение2
Это способ с environ
and \NewEnviron
, перехватывающий \BODY
и применяющий его в среде-оболочке, определенной с помощью xparse
' s \NewDocumentEnvironment
, что означает, что все возможности из xparse
спецификаторов аргументов могут по-прежнему использоваться.
\documentclass{article}
\usepackage{environ}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Nn \bar:n {
AAA #1 BBB
}
\NewEnviron{foointernal}{
\bar:n {\BODY}
}
\ExplSyntaxOff
\let\origfoointernal\foointernal
\let\origendfoointernal\endfoointernal
\NewDocumentEnvironment{foo}{}{%
\origfoointernal%
}{%
\origendfoointernal%
}
\begin{document}
\begin{foo}
hey
\end{foo}
\begin{foo}
\blindtext
Hello World
\end{foo}
\end{document}
решение3
Вы знаете, что хотите использовать \bar:n
в среде, чтобы захватить содержимое. Так что просто заставьте его сканировать вперед \end
и вставить \end
снова в конце текста замены.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Npn \bar:n #1 \end {
AAA #1 BBB \end
}
\NewDocumentEnvironment{foo}{}{\bar:n}{}
\ExplSyntaxOff
\begin{document}
\begin{foo}
hey
\end{foo}
\end{document}
решение4
Честно говоря, я не знаю, будет ли это работать, когда весь пакет будет переписан, но теперь я хотя бы понимаю, что происходит. Есть несколько ограничений: определенные среды имеют только два параметра. #1 представляет содержимое среды; #2 представляет параметр в квадратных скобках, который в принципе может использоваться для хранения пар «ключ-значение» и, таким образом, преодолевать это ограничение.
Кроме того, мои среды работают с командами \xbegin{\foo} и \xend{\foo}. Базовая структура этих команд отличается от сред LaTeX, поэтому я решил использовать другие ключевые слова. В любом случае, я предполагаю, что этот подход можно расширить для применения к средам LaTeX.
Любые отзывы и комментарии по коду приветствуются и ценятся!
\documentclass[10pt]{article}
\usepackage{expl3}
\ExplSyntaxOn
\int_new:N \env_count
\cs_new:Npn \env_new:Nn #1#2 {
\cs_new:cpn { env_defined@ \cs_to_str:N #1 :nn } ##1##2 {
#2
}
}
\cs_new:Npn \env_countbegin:w #1\xbegin#2 {
\cs_if_free:NTF #2 {
\int_incr:N \env_count
\env_countbegin:w
} {
\cs_if_eq:NNTF #2 \xend {} {
\int_incr:N \env_count
\env_countbegin:w
}
}
}
\cs_new:Npn \env_collect:w #1#2#3#4\xend#5 {
\env_countbegin:w #4\xbegin\xend
\int_compare:nTF { \env_count = 0 } {
\use:c { env_defined@ \cs_to_str:N #1 :nn } { #3 #4 } { #2 }
}{
\int_decr:N \env_count
\env_collect:w {#1} {#2} { #3 #4 \xend{#5} }
}
}
\NewDocumentCommand \xbegin {mo} {
\int_zero:N \env_count
\env_collect:w {#1} {#2} {}
}
\NewDocumentCommand \xend {} {}
\env_new:Nn \foo {
AAA #1 BBB
}
\ExplSyntaxOff
\begin{document}
\xbegin{\foo}
\xbegin{\foo}
hey
\xend{\foo}
\xend{\foo}
\end{document}