You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Pest/Rust中使用PUSH和POP匹配嵌套块的问题

解决LaTeX嵌套环境块的解析问题

我在解析LaTeX的一个小型定义子集时,搞不定嵌套的\begin{}和\end{}对的识别。比如下面这段LaTeX代码:

\begin{document}

Some text.

\begin{quote}
  A quote.
\end{quote}


\end{document}

我预期的解析树应该是嵌套的环境结构:

- env
-  - expression
-  - env
-  -  - expression

但实际解析出来,内部的\begin{quote}和\end{quote}被当成了普通命令,不是环境块:

- env
- - expression
- - command
- - expression
- - command

我的PEG语法规则如下:

// Top level rule is `document`.
document = { 
    SOI ~
    (section? ~ newline_char)* ~ section? ~
    EOI
}

section = {
    env_stmt |
    cmd_stmt |
    expression
}

// Expression grammar
expression   = { ( cmd_stmt | literal )* }
literal      = @{ char+ }
char         = @{ ASCII_ALPHANUMERIC | punctuation }
punctuation  = {
    "," | "." | ";" | "(" | ")" | "[" | "]" | "|" | "<" | ">" | ":" 
}

// Control Statement Grammar
cmd_stmt = { ctrl_character ~ name ~ cmd_stmt_opt? ~ "{" ~ expression ~ "}" }
cmd_stmt_opt = { "[" ~ name ~ "]" }

name                    = @{ ASCII_ALPHA+ }
COMMENT                 = _{ "%" ~ (!newline_char ~ ANY)* ~ newline_char }
WHITESPACE              = _{ " " }
newline_char            = _{"\n"}
ctrl_character          = _{ "\\" }

// Environment Grammar
env_stmt     = { env_begin ~ env_content ~ env_end }
env_content  = { (section? ~ newline_char)* }
env_begin    = @{ ctrl_character ~ "begin" ~ "{" ~ PUSH(name) ~ "}" }
env_end      = @{ ctrl_character ~ "end" ~ "{" ~ PEEK ~ "}" }

我猜是PUSH/PEEK的问题,但去掉它们后,只有内部块能被解析成环境,外部块反而变成了控制语句。


问题出在哪

  1. 普通命令的规则太宽:cmd_stmt里的name允许任意字母组合,\begin和\end会被误判成普通命令,而不是环境的专属开头/结尾。
  2. 栈的维护有问题:env_end只检查栈顶的环境名(PEEK),但没把它从栈里弹出来(POP),嵌套环境时栈的状态乱掉,后续匹配自然出错。

修正后的语法

// Top level rule is `document`.
document = { 
    SOI ~
    (section? ~ newline_char)* ~ section? ~
    EOI
}

section = {
    env_stmt |
    cmd_stmt |
    expression
}

// Expression grammar
expression   = { ( cmd_stmt | literal )* }
literal      = @{ char+ }
char         = @{ ASCII_ALPHANUMERIC | punctuation }
punctuation  = {
    "," | "." | ";" | "(" | ")" | "[" | "]" | "|" | "<" | ">" | ":" 
}

// Control Statement Grammar
// 禁止begin/end作为普通命令的名称
cmd_stmt = { ctrl_character ~ (name & !("begin" | "end")) ~ cmd_stmt_opt? ~ "{" ~ expression ~ "}" }
cmd_stmt_opt = { "[" ~ name ~ "]" }

name                    = @{ ASCII_ALPHA+ }
COMMENT                 = _{ "%" ~ (!newline_char ~ ANY)* ~ newline_char }
WHITESPACE              = _{ " " }
newline_char            = _{"\n"}
ctrl_character          = _{ "\\" }

// Environment Grammar
env_stmt     = { env_begin ~ env_content ~ env_end }
env_content  = { (section? ~ newline_char)* }
// 把环境名压入栈
env_begin    = @{ ctrl_character ~ "begin" ~ "{" ~ PUSH(name) ~ "}" }
// 匹配栈顶环境名并弹出
env_end      = @{ ctrl_character ~ "end" ~ "{" ~ POP ~ "}" }

修正说明

  1. 限制普通命令的名称:在cmd_stmt里加了& !("begin" | "end"),确保begin和end不会被当作普通命令的名字,这样\begin{...}和\end{...}只会被环境的规则匹配。
  2. 完善栈操作:把env_end里的PEEK改成POP,匹配栈顶环境名后立刻弹出,保证嵌套环境的栈状态正确:
    • 解析\begin{document}时,把document压栈;
    • 解析内部\begin{quote}时,把quote压栈;
    • 解析\end{quote}时,弹出栈顶的quote,栈顶回到document;
    • 最后解析\end{document}时,弹出document,栈清空。

这样就能正确识别嵌套的环境块,生成预期的解析树了。

内容的提问来源于stack exchange,提问作者pierre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 16:31:14