首先,在 TeX/LaTeX 中,作为宏参数的“字符串”的正确术语是什么?例如,\macro{somestring}
显然somestring
是标记、标记集、字符串、参数等。我将它称为字符串,因为我不知道该怎么称呼它。我不认为它是另一个宏,而是一种“惰性”文本。
我想要做的是解析一个代表 LaTeX 中的宏的字符串,但以一种非常简单的方式编码:
最好的描述方式是举个例子
后续字符串
x4y3[cgreen, t4[-3,4,a90]]
表示设置属性
x 位置 = 4
y 位置 = 3
颜色 = 绿色测试 = 4,x 偏移量为 -3,y 偏移量为 4,角度为 90
[ ]
= 可选(可选说明符的可选列表,无特定顺序)
它应该是非常不言自明地解释了我如何从压缩字符串到它所代表的属性。如果不解释,请盯着字符串几个小时。在知道指示符号是什么之后,您应该能够阅读字符串并准确说出它的含义。(这与 xparse 对其规范字符串的处理没有什么不同,只是我的稍微复杂一些)
现在最困难的部分是,我希望能够将该字符串解析为一系列设置数据的宏。
您可能已经猜到了,该字符串代表与在某个位置放置图形有关的内容。
如果你想解释一下
x4y3[cgreen, t4[-3,4,a90]]
让你感觉更好:在坐标 (4,3) 处放置一个绿色圆圈,其中有一段文字“4”,偏移量为 (-3,4),并旋转 90 度。
为了干净高效地完成此操作,我想创建为字符串中每个相应宏调用的宏
\def\x#1{}
\def\y#1{}
\def\c#1{}
\def\t#1#2{}
因此我们可以将字符串看作是简单的调用\x{-3}\y{4}\c{green}\t{4}{-3,4,a90}
因此,如您所见,字符串只是宏的“精简”版本。字符串只是带有可能的可选参数的命令值对。
人们几乎可以简单地进入并添加\'
s 和括号来获取命令。
例如,
伪代码:
parse_string{
foreach token in command-list
write '\'token'{'next_token'}'
}
但我想添加具有多个字符的命令/宏(最小的令牌大小优先)并执行其他操作,例如默认值。
无论如何,通常这在大多数程序编程语言中相对简单,但我确信我将花费接下来的几周时间尝试弄清楚如何在 TeX/LaTeX 中做到这一点,然后在 10k 行代码之后我可能会得到一个充满错误的版本。
答案1
首先,TeX 中没有我们在其他计算机语言中理解的字符串概念。TeX 使用标记和框来工作。然后,每个标记被映射到一个字形或一系列字形上,或者执行其他操作。那么如何解析如下所示的字符串并构建命令呢?
x4y3[cgreen, t4[-3,4,a90]]
与任何计算机语言一样,您需要先定义语法和 UI,您已经部分完成了,但让我们假设您希望逐个字母地执行此操作。在这种情况下,可以从内核LaTeX2e
(texdoc source2e 提供一般视图)或某些包(soul 包有一些很好的解析器)借用宏。让我们使用内核的宏@tfor
\makeatletter
\@tfor \i:= x4y3[cgreen, t4[-3,4,a90]]\do{%
\i
}
\makeatother
上面的代码逐个字母遍历标记列表并将其保存在变量中\i
。我已使用\i
来使它看起来更熟悉。但是\i
TeX 预定义了 note 作为无点 i。假设您的音乐包与无点 i 无关,您可以将其丢弃或使用其他变量,例如\next
。
您还可以使用以下方式进行解析分隔参数宏。
\def\amacro x#1y#2[#3]{
... do something with #1, #2 and #3
}
当您将上述宏调用为 时,和的\amacro x4y3[cgreen, t4[-3,4,a90]]
值分别为和,然后您可以使用相同的方法进一步解析它们。#1
#2
#3
4
3
cgreen, t4[-3,4,a90
现在您已经获得了参数的第一个值,您可以在宏中使用它们。让我们定义\x
、\y
宏(我已将它们重命名为 和\pos@x
,pos@y
以防\x
和\y
是在其他地方定义的。)
\def\pos@x#1{This is position x1}
这应该在这里,
\def\amacro x#1y#2[#3]{
\def\pos@x{#1}
\def\pos@y{#2}
.... parse further the #3
}
正如您所看到的,pos@x
和pos@y
宏在定义时从所有宏的母体中获取了它们的值。凭借极大的耐心和毅力,您将解析器一点一点地拼接在一起。说到耐心,我们尽量避免在这里发表激烈言论,例如“它应该非常不言自明”。如果您能修改或删除它,我将不胜感激。
答案2
您可以使用带有分隔参数文本的宏来解析字符串。一个简单的示例(如果输入不符合预期,则会产生编译器错误)如下:
\def\parsestuff x#1y#2[#3, t#4[#5,#6,#7]]{%
\setx{#1}%
\sety{#2}%
\setcolor{#3}%
\settest{#4}{#5}{#6}{#7}%
}
\parsestuff x4y3[cgreen, t4[-3,4,a90]]
您可能需要使用包装函数,以便将字符串括在括号中,例如:
\newcommand*\mywrapper[1]{\parsestuff #1}
\mywrapper{x4y3[cgreen, t4[-3,4,a90]]}
这可以改进,但我想你可以明白我的意思。请注意,分隔符必须以与宏定义期间相同的 catcode 出现在给定的确切形式中。例如,尾随字符]]
之间不能有空格。如果你想放宽这个条件,你需要分多个步骤解析字符串,其中内部宏读取内部[ ]
等。
也可以逐个标记解析输入字符串。我在我的tikz-timing
包中执行此操作,其中时间序列由字符给出并转换为 TikZ 代码。实际上,TikZ 本身会进行某种形式的字符串解析以实现其语法。另一个示例是我的ydoc
包,我在其中解析标记以格式化包/类文档中的 LaTeX 宏。两者都使用状态机来实现字符串解析器。如果您感兴趣,请查看代码。