词法分析器应采用状态机区分in_code/in_string/in_comment状态,仅在in_code跳过空白,遇/后预读判断注释或运算符;递归下降需消除左递归,改用迭代处理同级运算符并显式编码优先级;作用域用vector栈管理,变量查找从栈顶向下扫描且声明只写入栈顶;ast须分层体现优先级,如+节点右子树必须为term节点。

词法分析器怎么写才不卡在空格和注释上
直接手撸 Tokenizer 时,最常崩在没统一处理空白符和行内注释。比如 // 后面跟换行、/* ... */ 跨行、还有字符串里嵌套的 //,全当成注释就错了。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用状态机驱动,区分
IN_CODE/IN_STRING/IN_COMMENT三种主状态,字符串里的"//"不触发注释逻辑 - 跳过空白只在
IN_CODE状态做;遇到/先 peek 下一个字符:是/就吞掉整行,是*就进多行注释状态,否则当除法运算符 - 别用
std::stringstream按空格切——它吃掉换行又不报错,导致后续行号错乱;改用std::string::find_first_not_of(" \t\n\r")定位 token 起点
递归下降解析器怎么避免左递归崩溃
写 parseExpression() 时照着“表达式 → 项 + 表达式”硬套文法,立刻栈溢出。C++ 没尾调用优化,无限递归不是报错,是直接段错误。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 把左递归改造成迭代:用 while 循环处理同级运算符(如
+、-),每次循环只 consume 一个操作符+下一个项 - 优先级必须显式编码:先调
parseTerm()(处理*//),再在parseExpression()里检查是否为+或-,而不是让parseExpression()自己调自己 - 每个解析函数返回
std::unique_ptr<node></node>,别传引用或裸指针——临时对象生命周期一乱,AST 节点就 dangling
变量作用域怎么管才不会覆盖全局变量
int x = 1; { int x = 2; print(x); } 输出 2 没问题,但退出块后继续用 x 却拿到 1,说明作用域链没连对。常见错误是所有变量都往全局 std::map<:string value></:string> 里塞,压根没嵌套。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
std::vector<:unordered_map value>></:unordered_map>当作用域栈,push_back({})进新块,pop_back()出块 - 查找变量时从栈顶往下 scan,但声明变量只写入栈顶 map;别在查找失败时自动 fallback 到全局——那是 JS 的坑,C++ 解释器该严格报
undefined identifier 'x' - 函数参数也得进当前作用域,且必须在函数体执行前绑定,否则闭包捕获时值是未初始化的垃圾
为什么 eval("1 + 2 * 3") 返回 9 而不是 7
这说明运算符优先级没生效,所有二元操作都当成了左结合、同级处理。根本原因在于 AST 构建阶段没按优先级分层,而是把 + 和 * 都塞进同一个节点类型里。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- AST 节点类型要分层:
BinaryOpNode存操作符,但父节点结构体现结合性——比如+节点的右子树必须是Term节点(含*//),不能是另一个+ - 求值时别用 switch 堆积所有运算符逻辑;按节点类型 dispatch:
evalAddNode()专门处理加法,内部确保左右子树已求值完毕 - 测试用例必须覆盖混合优先级:
eval("a = 1 + 2 * 3; a - 4 / 2"),结果应是5,不是3或6
真正麻烦的是错误恢复——词法错一个字符,后面几十行全报错。先保证单行语法正确能跑通,再考虑报错位置精准和继续解析。其他都是后续迭代的事。










