C++如何实现简单的脚本解释器?(词法+语法分析)

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

C++如何实现简单的脚本解释器?(词法+语法分析)

词法分析器怎么写才不卡在空格和注释上

直接手撸 Tokenizer 时,最常崩在没统一处理空白符和行内注释。比如 // 后面跟换行、/* ... */ 跨行、还有字符串里嵌套的 //,全当成注释就错了。

实操建议:

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

  • 用状态机驱动,区分 IN_CODE / IN_STRING / IN_COMMENT 三种主状态,字符串里的 "//" 不触发注释逻辑
  • 跳过空白只在 IN_CODE 状态做;遇到 / 先 peek 下一个字符:是 / 就吞掉整行,是 * 就进多行注释状态,否则当除法运算符
  • 别用 std::stringstream 按空格切——它吃掉换行又不报错,导致后续行号错乱;改用 std::string::find_first_not_of(" tnr") 定位 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,不是 36

真正麻烦的是错误恢复——词法错一个字符,后面几十行全报错。先保证单行语法正确能跑通,再考虑报错位置精准和继续解析。其他都是后续迭代的事。