几个macro_call问题向deepseek,提问和回答。macro_call注释、令牌是啥、参数如何压栈。
参数压栈时,会用到param_size全局变量,指示最大参数栈深度。如果不在配置文件中修改,默认值是60。一旦有CJK,栈深度很容易就超过100。MikTex在配置文件中写的是20000。
一、令牌
令牌(Token 是TeX引擎中最基本的数据单元,代表源代码中的原子元素。有时可这么认为,一个令牌类似汇编语言中的一条指令,处理tex时,就是一个令牌一个令牌不断执行。对令牌,它每条都是跳转指令,跳转到“link”指向的那个令牌。
typedef struct token_node {
halfword info; // 令牌类型和内容编码
halfword link; // 指向下一个令牌(形成链表)
} token_node;这是定义令牌伪代码。以下是luatex定义的令牌。
typedef struct smemory_word_ {
# ifdef WORDS_BIGENDIAN
halfword hhrh;
halfword hhlh;
# else
halfword hhlh; <-- 小端。采用是这里
halfword hhrh;
# endif
} smemory_word;
smemory_word *fixmem;smemory_word类似上面token_node,smemory_word还要用于定义其它结构,内中字段名用了更通用的hhlh、hhrh。
info letter_token G 0x1600047 = 0x1600000 + 0x47
info同时定义了令牌类型和内容编码。以0x1600047为例,由两部分组成,0x1600000表示此令牌类型是字母,0x47表示内容编码是字母“G”。
令牌链表示例。info值和最新源码对不上,只是让直观了解链表是啥样。
假设宏 \foo{bar} 被展开
// 在 fixmem 中的实际存储
索引 hhlh (info) hhrh (link) 说明
1000 0x8000+123 1002 \foo 控制序列
1002 0x2000+'{' 1004 左花括号 {
1004 char_token+'b' 1006 字符 'b'
1006 char_token+'a' 1008 字符 'a'
1008 char_token+'r' 1010 字符 'r'
1010 0x2000+'}' null 右花括号 }
二、macro_call
\documentclass{article}
\newcommand{\keyword}[2][\bfseries]{{#1#2}}
\begin{document}
\keyword{Grouping} by curly braces limits the
\keyword{scope} of \keyword[\itshape]{declareatios}.
\end{document}处理上面这段tex文本期间,会出现3次宏名是“\keyword”的macro_call调用。注意,期间也会出现宏名是“keyword”的macro_call,但以下令牌链表对应的是“\keyword”。
const char *macro_name = (const char *) str_string(cs_text(warning_index));
上面是得到宏名的方法,在macro_call进入时就执行。以下日志记录的是期间调用“get_token()”后,得到的cur_tok值。
#0 macro_call(): macro: \keyword, warning_index: 25518, ref_count: 102048, token(info: 0, link: 102281)
[1]cur_tok: 25165915(0x180005b) [
[1]cur_tok: 2097275(0x20007b) {
[2]cur_tok: 536896930(0x200065a2) 控制序列令牌(cs_token_flag(0x1FFFFFFF)),对应:\bfseries
[2]cur_tok: 4194429(0x40007d) }
[1]cur_tok: 25165917(0x180005d) ]
[1]cur_tok: 2097275(0x20007b) {
[2]cur_tok: 23068743(0x1600047) G
[2]cur_tok: 23068786(0x1600072) r
[2]cur_tok: 23068783(0x160006f) o
[2]cur_tok: 23068789(0x1600075) u
[2]cur_tok: 23068784(0x1600070) p
[2]cur_tok: 23068777(0x1600069) i
[2]cur_tok: 23068782(0x160006e) n
[2]cur_tok: 23068775(0x1600067) g
[2]cur_tok: 4194429(0x40007d) }上面这个令牌链表对应着tex中“\keyword{Grouping}”。do {...} while (token_info(r) != end_match_token);中会有两处调用get_token(),每行前肌的[1]或[2],表示这是第几处。
\newcommand{\keyword}[2][abc]{{#1#2}}0x200065a2对应“\bfseries”是我猜的。我把“\bfseries”换成“abc”后,0x200065a2就变成三个。其它的都没变。
[2]cur_tok: 23068769(0x1600061) a [2]cur_tok: 23068770(0x1600062) b [2]cur_tok: 23068771(0x1600063) c
以下对应tex文本:\keyword{scope}
#1 macro_call(): macro: \keyword, warning_index: 25518, ref_count: 102048, token(info: 0, link: 102281)
[1]cur_tok: 25165915(0x180005b) [
[1]cur_tok: 2097275(0x20007b) {
[2]cur_tok: 536896930(0x200065a2) 控制序列令牌,对应:\bfseries
[2]cur_tok: 4194429(0x40007d) }
[1]cur_tok: 25165917(0x180005d) ]
[1]cur_tok: 2097275(0x20007b) {
[2]cur_tok: 23068787(0x1600073) s
[2]cur_tok: 23068771(0x1600063) c
[2]cur_tok: 23068783(0x160006f) o
[2]cur_tok: 23068784(0x1600070) p
[2]cur_tok: 23068773(0x1600065) e
[2]cur_tok: 4194429(0x40007d) }以下对应tex文本:\keyword[\itshape]{declareatios}
#2 macro_call(): macro: \keyword, warning_index: 25518, ref_count: 102048, token(info: 0, link: 102281)
[1]cur_tok: 25165915(0x180005b) [
[1]cur_tok: 536884730(0x200035fa) 控制序列令牌,对应:\itshape
[1]cur_tok: 25165917(0x180005d) ]
[1]cur_tok: 2097275(0x20007b) {
[2]cur_tok: 23068772(0x1600064) d
[2]cur_tok: 23068773(0x1600065) e
[2]cur_tok: 23068771(0x1600063) c
[2]cur_tok: 23068780(0x160006c) l
[2]cur_tok: 23068769(0x1600061) a
[2]cur_tok: 23068786(0x1600072) r
[2]cur_tok: 23068773(0x1600065) e
[2]cur_tok: 23068769(0x1600061) a
[2]cur_tok: 23068788(0x1600074) t
[2]cur_tok: 23068777(0x1600069) i
[2]cur_tok: 23068783(0x160006f) o
[2]cur_tok: 23068787(0x1600073) s
[2]cur_tok: 4194429(0x40007d) }通过令牌链表可以看到,如果没提供可选参数,即用默认值,生成的修饰是“[{...}]”,否则是“[...]”。