给定一个任意的标记列表(例如01abpt
),如何检查它是否可以分配给一个维度(例如\@tempdima
)?LaTeX\@defaultunits
不是为这种类型的测试而设计的。如果标记列表的形式为10pt
,那么很容易。但标记列表可以是任意的。例如,abpt
将导致“!缺失数字,视为零”,我们不能这样做
\@tempdima\dimexpr 0pt+abpt\relax
为了增加问题的严重性,我们可能(在考虑到的情况下)接受10ptABD
它是可维度的。
注意:未定义的控制序列被排除在标记列表之外。
关于数字测试的讨论这里,但这并不相关。
答案1
只是为了给出一些味道,这里是测试,检查第一个标记(在f
-expansion 之后)是否是\dimen
或,\skip
并且后面跟着一个小于 32768 的整数:
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\tl_new:N \l_ahmed_arg_tl
\tl_new:N \l_ahmed_first_tl
\tl_new:N \l_ahmed_rest_tl
\tl_new:N \l_ahmed_temp_tl
\seq_new:N \l_ahmed_extr_seq
\prg_new_protected_conditional:Npnn \ahmed_if_dimen:n #1 {T,F,TF}
{
\tl_set:Nf \l_ahmed_arg_tl { #1 }
\ahmed_check_primitive:
}
\cs_generate_variant:Nn \token_if_eq_meaning_p:NN {NV}
\cs_new_protected:Npn \ahmed_check_primitive:
{
\tl_set:Nx \l_ahmed_first_tl { \tl_head:V \l_ahmed_arg_tl }
\tl_set:Nx \l_ahmed_rest_tl { \tl_tail:V \l_ahmed_arg_tl }
\bool_if:nTF
{
\token_if_eq_meaning_p:NV \tex_dimen:D \l_ahmed_first_tl
||
\token_if_eq_meaning_p:NV \tex_skip:D \l_ahmed_first_tl
}
{ \ahmed_check_integer: }
{ \ahmed_check_def_token: }
}
\cs_generate_variant:Nn \regex_extract_once:nnN {nV}
\cs_new_protected:Npn \ahmed_check_integer:
{
\tl_set:Nf \l_ahmed_temp_tl { \l_ahmed_rest_tl }
\regex_extract_once:nVN { \A \d * } \l_ahmed_temp_tl \l_ahmed_extr_seq
\seq_if_empty:NTF \l_ahmed_extr_seq
{ \prg_return_false: }
{
\int_compare:nTF { \seq_item:Nn \l_ahmed_extr_seq {0} < 32768 }
{ \prg_return_true: }
{ \prg_return_false: }
}
}
\cs_new:Npn \ahmed_test:n #1
{
\ahmed_if_dimen:nTF { #1 }{ \typeout{YES} } { \typeout{NO} }
}
\ahmed_test:n {\dimen34abc}
\ahmed_test:n {\skip1234567}
\def\xyz{\dimen22}
\ahmed_test:n {\xyz}
\ahmed_test:n {aaa}
输出为
YES
NO
YES
! Undefined control sequence.
<argument> \ahmed_check_def_token:
l.53 \ahmed_test:n {aaa}
表明在第四种情况下,控制确实转移到了下一阶段。这实际上仍然是不完整的,因为后面的整数\dimen
或\skip
可能是一个“隐式数字”(例如计数寄存器或\chardef
令牌),因此需要检查。或者更糟的是,它可能嵌入在宏中:
\def\fake{2}
\dimen1\fake 1=1pt
将是一项合法的任务\dimen121
。
如果对令牌列表有更多的控制,例如我们确信它可以扩展为不可扩展令牌列表,检查可以更容易。
答案2
到目前为止,我发现以下测试足以满足我的需求。我考虑的唯一输入类型是以下类型
{1pt,1.5pt,...,10pt,13pt,15pt,...,21pt}
{1ex,2ex,...,10ex,13ex,15ex,...,21ex}
例如,它已由用户直接提供,用于在回调中进行解析。因此,catcode-12
不需要将标记作为输入的一部分,但很容易容纳此类标记。我还可以\dimendef'd
通过以下测试容纳寄存器:测试给定的控制序列是否是长度寄存器。在预期的应用程序中,我不需要测试\count'd
和\skip'd
寄存器。问题实际上出在内部尺寸上,但它们不应该成为上述用户输入类型的一部分。
\documentclass{article}
\makeatletter
% Also test for number (without unit)
\newif\ifisnumber
\def\ifbooltrue#1{%
\csname @\csname if#1\endcsname first\else second\fi oftwo\endcsname
}
\def\ifcmdcmp@#1#2\@nil{\expandafter#1}
\def\ifcmdcmp#1#2{%
\csname @\expandafter\expandafter\expandafter
\ifx\ifcmdcmp@#1\batchmode\@nil\@car#2\noboundary\@nil
first\else second\fi oftwo\endcsname
}
\def\findunit#1#2#3\@nil{%
% Check for a valid unit.
\in@{,#1,}{,\paperwidth,\paperheight,\textwidth,\textheight,%
\hsize,\vsize,\columnwidth,\linewidth,}%
\ifbooltrue{in@}{%
\@firstoftwo
}{%
\lowercase{\in@{,#1#2,}}{,em,ex,in,pt,pc,cm,mm,dd,cc,nd,nc,bp,sp,}%
\ifbooltrue{in@}%
}%
}
\def\catchremainder#1\@nil{\endgroup\def\remainder{#1}}
\def\ifdimensionable#1{%
\isnumberfalse\begingroup
\afterassignment\catchremainder
\@tempcnta0#1\relax\@nil
\ifcmdcmp\remainder\empty{%
\isnumbertrue\@secondoftwo
}{%
\expandafter\ifcmdcmp\expandafter{\remainder}\relax{%
\isnumbertrue\@secondoftwo
}{%
\expandafter\findunit\remainder xx\@nil
}%
}%
}
\makeatother
%\ifdimensionable{01}{\def\x{T}}{\def\x{F}}
%\ifdimensionable{01pt}{\def\x{T}}{\def\x{F}}
\ifdimensionable{01abpt}{\def\x{T}}{\def\x{F}}
%\ifdimensionable\paperwidth{\def\x{T}}{\def\x{F}}
% Is 'pt' 0pt or 1pt? Make it invalid input in revised code:
%\ifdimensionable{pt}{\def\x{T}}{\def\x{F}}
\begin{document}
x
\end{document}
答案3
任意令牌列表?
如何赶上永无止境的 eTeX 情况,例如
\newdimen\tempdima
\def\Collatz#1{%
\expandafter\Collatz
\expandafter{\number\numexpr#1\ifodd#1 *3+1\else/2\fi\relax}%
}%
%
\tempdima=\Collatz{2057}pt %
%
\bye
?
如何处理产生不平衡括号的问题,例如,
\tempdima=\iffalse{\fi}4pt
?
如何处理可能存在的\outer
-token?
(你可以这样做
\newdimen\tempdima
\outer\def\somenumber{17}
\tempdima=\somenumber pt
\showthe\tempdima
\bye
,但\somenumber
不能作为宏参数的一部分进行处理,因为宏会触发对可能传递给的内容的检查\tempdima
。)