macro_call

几个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)    }

通过令牌链表可以看到,如果没提供可选参数,即用默认值,生成的修饰是“[{...}]”,否则是“[...]”。

全部评论: 0

    写评论: