状态机实现词法分析器需确保每个状态出口唯一且覆盖所有输入,避免漏字符或死循环;必须显式处理空格、注释、数字后缀等边缘情况,consume() 后必推进位置,peek() 未消费字符须可重读;ASCII 字符判断应硬编码而非依赖 locale。

直接用状态机实现 C++ 脚本词法分析器,核心不是写得“多漂亮”,而是让 next_token() 每次返回的 Token 类型和值都可预测、不漏字符、不卡死。关键在状态跳转边界是否收束,以及对空格、注释、数字后缀等边缘情况的显式处理。
状态机怎么设才不会漏字符或死循环
常见错误是把“读到非预期字符就回退”当万能解法,结果 peek() 和 consume() 同步错位,导致某个字符被跳过或重复读取。必须保证每个状态的出口唯一且覆盖所有输入可能。
- 初始状态
STATE_START遇空格/制表符/换行 → 继续跳过;遇/→ 进入注释判断;遇字母/下划线 → 进入标识符;遇数字 → 进入数字字面量;遇引号 → 进入字符串;其他一律视为单字符运算符(如+、==)并立即返回 - 进入
STATE_STRING后,只在遇到未转义的匹配引号时退出;中途遇到文件尾(EOF)必须报错,不能静默截断 - 所有状态里,一旦调用
consume(),就必须推进位置;未consume()的字符,下次peek()必须还能拿到
is_digit() 和 is_alpha() 为什么不能直接用 std::isdigit/std::isalpha
它们依赖当前 locale,比如某些 locale 下 std::isalpha('á') 返回 true,但你的脚本语言只认 ASCII 标识符。硬编码判断更安全:
parse_number()
- 十六进制整数字面量(
0x...)需在识别出0x后,强制要求后续至少一个合法十六进制字符,否则报错 - 浮点字面量中
.后可无数字(如3.),但e后必须有整数(如3e2合法,3e不合法) - 后缀必须紧贴数字结尾,中间不能有空格;
123 f是两个 token:123和标识符f
Token 类型枚举与字符串值怎么存才不拖慢性能
不要在每次生成 Token 时都做 std::string(substr) 拷贝。对于关键字(if、while)、运算符(==、+=)这类固定字符串,用 enum class TokenType 区分类型即可,值字段只存原始位置(int start, len)和所属源码指针;真正需要字符串内容时(比如报错或调试),再按需提取子串。
只有字符串字面量、标识符这类长度不可控的,才用 std::string 存值——但要避免重复构造,可用 std::string_view 做视图引用,前提是源字符串生命周期长于 Token 实例。
最易被忽略的是换行计数:如果脚本支持多行字符串或行连接符(\),line_num 更新必须和字符消费严格同步,否则错误位置提示会偏移。别指望靠 std::count 事后统计。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











