假设我想编写一个自定义解析器,它按逐字上下文读取字符,其中解析器参数应该分成几种类型的标记:
- 普通字符,即除了空格和
\
(以及}
限制参数的)之外的所有字符,应按原样打印; - 空格会被忽略;
- 通过 转义的字符
\
,也按原样打印;以及 - 任意 TeX 代码片段,散布在形式中
\{...}
。
所有标记都在每个字符都有 catcode 12 的上下文中解析。对于 TeX 片段,应暂时使用原始 catcode 表。此外,解析的参数字符串应从当前输入流中读取,因为它可能包含不匹配的括号。
下面的代码几乎满足了上述要求,唯一的限制是,TeX 片段只有在前面有一个额外的左括号(即 )时才能正确解析\{{...}
。出现此问题是因为解析器必须提前查看后面的字符,\
以确定它是转义字符还是 TeX 片段的开头,因此{
如果后面是平衡组,则会分配错误的 catcode。
有没有办法以某种方式{
备份标记流中缺失的部分,以便 TeX 的常规参数抓取器可用于收集代码片段?我真的很想避免使用额外的前缀来表示组的开始或重建平衡组解析器。
当前实施:
\documentclass{article}
\usepackage{l3cctab}
\usepackage{xcolor}
\ExplSyntaxOn
\cs_set:Npn \__foo_temp:w #1 #2 {
\exp_last_unbraced:NNf \cs_set_eq:NN #1 { \char_generate:nn { `#2 } { 12 } }
}
\__foo_temp:w \c__foo_space_char \ %
\__foo_temp:w \c__foo_lbrace_char \{
\__foo_temp:w \c__foo_rbrace_char \}
\__foo_temp:w \c__foo_backslash_char \\
\cctab_new:N \g__foo_orig_cctab
\cs_new_protected:Npn \foo_parse:w {
\cctab_gset:Nn \g__foo_orig_cctab { }
\cctab_begin:N \c_other_cctab
\__foo_parse_aux:w
}
\cs_new_protected:Npn \__foo_parse_aux:w #1 {
\token_if_eq_charcode:NNTF #1 \c__foo_lbrace_char
{ \__foo_parse_token:w }
{ \PackageError {foo} { Wrong~argument } }
}
\cs_new_protected:Npn \__foo_parse_token:w #1 {
\token_if_eq_charcode:NNTF #1 \c__foo_rbrace_char {
\__foo_parse_finish:
} {
\token_if_eq_charcode:NNTF #1 \c__foo_backslash_char {
\__foo_parse_esc_token:w
} {
% Print only if not a space
\token_if_eq_charcode:NNF #1 \c__foo_space_char
{ \texttt{#1} }
\__foo_parse_token:w
}
}
}
\cs_new_protected:Npn \__foo_parse_esc_token:w #1 {
\token_if_eq_charcode:NNTF #1 \c__foo_lbrace_char {
\cctab_begin:N \g__foo_orig_cctab
% >>> How to insert the missing opening brace here? <<<
\__foo_parse_balanced_group:n
} {
\texttt{#1}
\__foo_parse_token:w
}
}
\cs_new_protected:Npn \__foo_parse_balanced_group:n #1 {
\fbox { #1 }
\cctab_end:
\__foo_parse_token:w
}
\cs_new_protected:Npn \__foo_parse_finish: {
\cctab_end:
}
\ExplSyntaxOff
\begin{document}
\ExplSyntaxOn
\foo_parse:w{
$foo_\b\a\r_
\{{\textcolor{blue}{\LaTeX}} \}\}\}%{{{\\
} % ^----- extra brace here
\ExplSyntaxOff
\end{document}
答案1
当然是用 Dirty Tricks 了 ;-)
简而言之,你有:
\foo some text}
并且您想要\foo
抓取到的所有内容}
,因此的代码\foo
必须{
在那里放置一个。\foo
基本上必须是\fooaux{
。
但是 TeX 不允许你{
在那里出现不平衡的情况,所以你必须欺骗它相信括号是平衡的。你可以这样做:
\def\foo{%
\expandafter\fooaux\expandafter{\iffalse}\fi}
\def\fooaux#1{(#1)}
\foo some text}
输出将是(some text)
。
TeX 首先\foo
用其定义替换。然后,两个\expandafter
将触发\iffalse
,而这是一个假条件,它将吞噬匹配的所有内容\fi
,也就是}
。在这个过程中,两个\expandafter
和\iffalse}\fi
消失,只剩下\fooaux{
,然后与some text}
输入流中的左边匹配。
expl3
-在您的代码中确认:
\documentclass{article}
\usepackage{l3cctab}
\usepackage{xcolor}
\ExplSyntaxOn
\cs_set:Npn \__foo_temp:w #1 #2
{ \exp_last_unbraced:NNf \cs_new_eq:NN #1 { \char_generate:nn { `#2 } { 12 } } }
\__foo_temp:w \c__foo_space_char \ %
\__foo_temp:w \c__foo_lbrace_char \{
\__foo_temp:w \c__foo_rbrace_char \}
\__foo_temp:w \c__foo_backslash_char \\
\cctab_new:N \g__foo_orig_cctab
\cs_new_protected:Npn \foo_parse:w
{
\cctab_gset:Nn \g__foo_orig_cctab { }
\cctab_begin:N \c_other_cctab
\__foo_parse_aux:w
}
\cs_new_protected:Npn \__foo_parse_aux:w #1
{
\token_if_eq_charcode:NNTF #1 \c__foo_lbrace_char
{ \__foo_parse_token:w }
{ \PackageError {foo} { Wrong~argument } }
}
\cs_new_protected:Npn \__foo_parse_token:w #1
{
\token_if_eq_charcode:NNTF #1 \c__foo_rbrace_char
{ \__foo_parse_finish: }
{
\token_if_eq_charcode:NNTF #1 \c__foo_backslash_char
{ \__foo_parse_esc_token:w }
{
% Print only if not a space
\token_if_eq_charcode:NNF #1 \c__foo_space_char
{ \texttt{#1} }
\__foo_parse_token:w
}
}
}
\cs_new_protected:Npn \__foo_parse_esc_token:w #1
{
\token_if_eq_charcode:NNTF #1 \c__foo_lbrace_char
{
\cctab_begin:N \g__foo_orig_cctab
\exp_after:wN
\__foo_parse_balanced_group:n
% Brace hack: removes this V
\exp_after:wN { \if_false: } \fi:
}
{
\texttt{#1}
\__foo_parse_token:w
}
}
\cs_new_protected:Npn \__foo_parse_balanced_group:n #1
{
\fbox {#1}
\cctab_end:
\__foo_parse_token:w
}
\cs_new_protected:Npn \__foo_parse_finish:
{ \cctab_end: }
\ExplSyntaxOff
\begin{document}
\ExplSyntaxOn
\foo_parse:w{
$foo_\b\a\r_
\{\textcolor{blue}{\LaTeX}} \}\}\}%{{{\\
} % ^----- NO extra brace here :-)
\ExplSyntaxOff
\end{document}
输出: