部分灵感来自这个问题,我对这个问题很感兴趣,我们是否可以(假设)切换到一种新的 LaTeX 语法,其中括号括起来的参数是强制性的。也就是说,不再是\frac12
; 总是\frac{1}{2}
。在许多方面,这种语法会更好,造成的混乱也会少得多。
无论如何,我想到的第一个(非常丑陋的)解决方案是滥用g
-type 参数(见下文)。什么才是最好的/最强大的解决方案?
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\foo{g}{%
\IfValueTF{#1}{%
foo(#1)%
}{%
\PackageError{foo}{Missing braces around argument}%
{This is the newest trend in LaTeX syntax}%
}%
}
\begin{document}
\foo{bar} % prints foo(bar)
\foo1 % issues an error
\end{document}
答案1
如果滚动浏览错误,错误恢复会有点残酷,但是......
\foo 1
给出:
! Package foo Error: unexpected text before brace.
\documentclass{article}
\begin{document}
\long\def\foo#1#{%
\if\relax\detokenize{#1}\relax\expandafter\foox
\else\PackageError{foo}{unexpected text before brace}{you are doomed}\fi}
\def\foox#1{foo(#1)}
\foo{bar}
\foo1
\end{document}
答案2
使用令牌循环。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\newcommand\abortfoo{\tcpush{\empty\endfoo}}
\xtokcycleenvironment\foo
{Bad syntax (unbraced character)\abortfoo}
{\addcytoks{\fooaux{##1}}\abortfoo}
{Bad syntax (unbraced control sequence)\abortfoo}
{Bad syntax (unbraced space)\abortfoo}
{\stripgroupingtrue}
{}
\newcommand\fooaux[1]{Foo argument ``\detokenize{#1}''}
\begin{document}
\foo A
\foo\today
\foo\tcsptoken
\foo{X}
\foo{\today}
\end{document}
答案3
另一种自动给出缺失{
错误并且不会浪费整个文档去寻找的机制{
是
\documentclass{article}
\def\foo{\afterassignment\foox\toks0 }
\def\foox{foo(\the\toks0)}
\begin{document}
\foo{bar}
\foo1
\end{document}
这使
! Missing { inserted.
<to be read again>
1
l.10 \foo1
答案4
除了检测 catcode-1-左括号和 catcode-2-右括号的所需类型(显式/隐式/特定字符代码)的问题之外,另一个问题可能是检测左括号和右括号的正确嵌套。
(我想到的是 Lua 扩展,但据我所知,当通过 Lua 代码向前看时,TeX 引擎的字符标记的类别代码将不会被考虑在内。)
一种在扩展上下文中也能 100% 可靠地运行的机制,用于检测标记流中的下一个标记是否是类别代码 1(组开头)的(显式)字符标记,可能是字符代码 123({
) ),如果不是,则仅发出定制的错误消息(而不发出其他任何消息),这需要 100% 可靠的可扩展前瞻标记流的下一个标记。
这在传统的 TeX 引擎中是不可能的。
通过 -notation使用{
-delimited 参数可能会导致在没有明确说明的#{
情况下,生成有关与您定制的错误消息之前/之后的定义不匹配的内容的 TeX 错误消息。{1
基于抓取的方法⟨一般文本⟩倾向于(\toks0=...
} 不可扩展,或(\scantokens
等)不保留 catcode-régime,或(\uppercase
/ \lowercase
)不保留字符代码。除此之外,⟨一般文本⟩之前的左括号标记⟨平衡文本⟩可以是显式的或隐式的,并且在扫描时不会抑制扩展⟨一般文本⟩的左括号标记。
基于\@ifnextchar
或\let
或的方法\futurelet
基于赋值,因此不可扩展。如果可扩展性不是您感兴趣的,那么例如使用\futurelet
,遵循其微妙之处,您可以让 TeX 提前查看下一个标记的含义,如果它表示 catcode-1 标记,则可能在通过参数处理宏检查后续标记的(字符代码)之前应用\string
或,\meaning
然后再进行任何在正确的 catcode 机制下重新插入字符串化标记的技巧。如果\escapechar
当前具有负值,则可能会出现区分显式字符标记与控制符号标记/单字母控制字标记的问题。可能会出现区分显式字符标记与让其相等的活动吊坠的问题。
在你的问题中你提到需要括号的 TeX 基本语法其中
\def\seq{abcdef}%
\uppercase\seq %
出现问题是因为在\upppercase
扫描⟨一般文本⟩\seq
以作为“起点”的左括号标记扩展不会产生以开头的标记序列⟨填料⟩后面跟着一个左括号标记(可能是隐式的,也可能是显式的),后面跟着⟨平衡文本⟩ 随后是⟨右括号⟩(必须明确)。
虽然无法以可扩展且 100% 可靠的方式检测 TeX 口中产生的标记流的下一个标记是否明确 ,但可以在扩展阶段(即在 TeX 的喉咙中)进行测试,如果一组来自已经抓取的宏参数(抓取过程中已删除最外层括号)的标记形成{1
⟨平衡文本⟩嵌套在明确的catcode 1/2 的字符标记:
% A TeX-engine is needed which brings along the \expanded-primitive.
\tt
\hyphenchar\font=`\-\relax
\emergencystretch 3em
\frenchspacing
\parindent=0pt
\chardef\stopromannumeral=`\^^00
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
%%-----------------------------------------------------------------------------
%% Check whether argument forms <balanced text> that is nested
%% between a pair of matching explicit catcode1/2-character-
%% tokens:
%%.............................................................................
%% \CheckWhetherNestedInExplicitBraces{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is nested between
%% whatsoever explicit braces>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not nested between
%% whatsoever explicit braces>}%
\long\def\CheckWhetherNestedInExplicitBraces#1{%
\romannumeral\expandafter\secondoftwo\expandafter{\expandafter{\string#1.}%
\expandafter\firstoftwo\expandafter{\expandafter\secondoftwo\string}%
\expandafter\secondoftwo\string{\expandafter\expandafter\expandafter
\secondoftwo\expandafter\expandafter\expandafter{\expandafter\expandafter
\expandafter{\expandafter\string\firstoftwo{}#1}\expandafter\secondoftwo
\string}\expandafter\firstoftwo\expandafter{\expandafter\secondoftwo\string}%
\expandafter\stopromannumeral\secondoftwo}{\expandafter\stopromannumeral
\firstoftwo}}{\expandafter\stopromannumeral\secondoftwo}%
}%
01. \CheckWhetherNestedInExplicitBraces{A{B}C}{Nested in explicit braces}{Not nested in explicit braces}%
02. \CheckWhetherNestedInExplicitBraces{}{Nested in explicit braces}{Not nested in explicit braces}%
03. \CheckWhetherNestedInExplicitBraces{ }{Nested in explicit braces}{Not nested in explicit braces}%
04. \CheckWhetherNestedInExplicitBraces{{A}BC}{Nested in explicit braces}{Not nested in explicit braces}%
05. \CheckWhetherNestedInExplicitBraces{ABC}{Nested in explicit braces}{Not nested in explicit braces}%
06. \CheckWhetherNestedInExplicitBraces{{A}{B}{C}}{Nested in explicit braces}{Not nested in explicit braces}%
07. \CheckWhetherNestedInExplicitBraces{{ABC} }{Nested in explicit braces}{Not nested in explicit braces}%
08. \CheckWhetherNestedInExplicitBraces{ {ABC} }{Nested in explicit braces}{Not nested in explicit braces}%
09. \CheckWhetherNestedInExplicitBraces{ {ABC}}{Nested in explicit braces}{Not nested in explicit braces}%
10. \CheckWhetherNestedInExplicitBraces{{ABC}}{Nested in explicit braces}{Not nested in explicit braces}%
\noindent\hrulefill\null
11.
\def\seq{{abcdef}}%
\expandafter\CheckWhetherNestedInExplicitBraces\expandafter{\expanded{\seq}}{%
\uppercase\seq
}{%
%\errmessage
{Here could be a customized error-message about things not being nested between
explicit brace tokens (although with \string\uppercase\space an implicit left
brace would do as well.)}%
%Or try one of the following:
%\uppercase\expandafter{\seq}%
%\uppercase\expandafter{\expanded{\seq}}%
}%
\noindent\hrulefill\null
12.
\def\seq{abcdef}%
\expandafter\CheckWhetherNestedInExplicitBraces\expandafter{\expanded{\seq}}{%
\uppercase\seq
}{%
%\errmessage
{Here could be a customized error-message about things not being nested between
explicit brace tokens (although with \string\uppercase\space an implicit left
brace would do as well.)}%
%Or try one of the following:
%\uppercase\expandafter{\seq}%
%\uppercase\expandafter{\expanded{\seq}}%
}%
\bye