0

0

C++如何实现递归下降解析器?(手写语法分析)

冰火之心

冰火之心

发布时间:2026-02-22 17:25:03

|

245人浏览过

|

来源于php中文网

原创

递归下降解析器是一组相互调用的c++函数,每个函数对应一条语法规则,共享一个封装token流的类,通过前瞻一个token并手动推进位置来实现解析。

c++如何实现递归下降解析器?(手写语法分析)

递归下降解析器的核心结构长什么样

递归下降不是某种库或框架,它就是一组相互调用的 C++ 函数,每个函数对应一条语法规则。比如 expr() 解析表达式,term() 解析项,factor() 解析因子——这些函数名直接映射 BNF 中的非终结符。

关键在于:每个函数只向前看一个 token(即“前瞻一个”),靠这个 token 决定走哪条分支。这意味着你必须手动维护一个 current_token,并在每次匹配成功后调用 next_token() 推进。

常见错误现象是函数没推进 token 就返回,导致无限循环或跳过输入;或者推进了但没检查是否到结尾,引发越界访问。

  • 必须用一个类封装 token 流,包含 m_tokensm_pos,所有解析函数共享同一实例
  • 每个函数开头先检查 current_token.type == expected_type,不匹配就报错或回退(如果支持回溯)
  • 匹配成功后立刻调用 consume()(即 m_pos++),不能靠调用方负责
Token current_token() const { 
  return m_pos < m_tokens.size() ? m_tokens[m_pos] : Token{EOF}; 
}
void consume() { ++m_pos; }

如何处理左递归导致的无限调用

C++ 里直接写 expr → expr '+' term 这种左递归规则,expr() 会无终止地调用自己,栈溢出是分分钟的事。

立即学习C++免费学习笔记(深入)”;

这不是语法设计失误,而是递归下降的硬约束:它天然不支持直接左递归。必须重写文法,把左递归转成右递归 + 循环。

典型做法是把加减运算提出来,用迭代方式处理:

  • 原规则 expr → term | expr '+' term | expr '-' term → 改成 expr → term { ('+' | '-') term }
  • 对应代码中,expr() 先调一次 term() 得到初始值,再用 while 循环反复检查 current_token.type == PLUS || MINUS
  • 每次匹配操作符后 consume,再调 term(),然后做实际运算

这样既避免栈爆炸,又保持了左结合性。别试图在 C++ 里模拟回溯或用 std::function 缓存状态来绕过——性能差、逻辑乱、调试难。

AI Home Tab
AI Home Tab

把你喜欢的AI放到首页

下载

什么时候该用 std::variant 而不是继承+虚函数

如果你的 AST 节点类型不多(比如 BinaryOpNumberIdentifier),用 std::variant 更轻量、更安全。

继承方案看似“面向对象”,但容易踩坑:忘了定义虚析构函数、误用裸指针管理生命周期、RTTI 开销不可控。而 std::variant 强制你在访问前做模式匹配,编译期就能拦住未覆盖的 case。

使用场景很明确:AST 构建完成后要遍历执行或打印,且节点种类稳定(少于 10 种)。这时 std::visit 配合 lambda 是最直白的写法。

  • 不要用 dynamic_cast 做运行时类型判断,那是给多态接口用的,不是给 AST 的
  • 如果节点需要携带位置信息(line, col),直接塞进每个 struct 里,别搞“基类带位置字段”那一套
  • std::variant 的构造开销几乎为零,但注意别在循环里反复拷贝大 variant
using Expr = std::variant<Number, BinaryOp, Identifier>;
Expr parse_expr() { /* ... */ }

错误恢复怎么做才不至于一崩到底

递归下降默认是“悲观式”解析:一个 token 错,后面全乱。但用户写代码时打错一个括号,你不该直接退出,而应尝试跳过坏 token、同步到下一个合理起点(比如 ;})。

关键是定义“同步集”(synchronizing set):对每个函数,明确哪些 token 可以作为恢复锚点。比如 stmt() 的同步集通常是 SEMIRBRACEIFWHILE

  • 不要一遇到错误就 throw —— 异常打断控制流,让恢复逻辑难以插入
  • 在每个顶层函数(如 parse_function())入口加保护:若 current_token 是明显非法值(如 INVALID),先调用 skip_to_sync({SEMI, RBRACE})
  • skip_to_sync 就是 while 循环:只要 current_token.type 不在集合里,就 consume()

性能影响很小,但用户体验差别巨大。没人想每次少打个逗号就得重输整段。

真正麻烦的是错误嵌套:比如在 expr() 里出错,跳出去后又在 stmt() 里撞上另一个错。这时候同步集得有层级感——外层比内层更宽松。这点容易被忽略,结果是报错位置飘忽、信息错位。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

826

2023.08.22

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

103

2023.09.25

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

57

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

60

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

24

2025.11.27

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6409

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

837

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1087

2023.12.21

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

1030

2026.02.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 10万人学习

C 教程
C 教程

共75课时 | 4.9万人学习

C++教程
C++教程

共115课时 | 18.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号