C++手写词法分析器需用状态机驱动,维护state变量和字符游标;next_token()每次返回有效Token,不漏读、不重读、不卡死;关键在正确处理标识符开头(字母/下划线)、数字识别、双字符运算符探查、字符回退及Token生命周期管理。

如何用C++手写一个能识别标识符、数字、括号和运算符的词法分析器
不需要第三方库,纯状态机驱动,核心就是维护一个state变量和字符流游标。关键不是“写全所有Token”,而是让next_token()每次返回一个有效的Token对象,且不漏读、不重读、不卡死。
常见错误是:遇到/就直接当除号,结果把//注释吞掉一半;或把123abc拆成123和abc两个Token,其实应该报错——因为123abc既不是合法数字也不是合法标识符。
- 状态转移必须覆盖所有输入字符,哪怕只是
default: state = STATE_ERROR; - 每个状态退出前,必须把当前字符“退回”(即
pos--),否则下一个next_token()会跳过它 - 数字状态里要区分
123、0xFF、3.14,但初版只处理十进制整数即可,避免过早复杂化
STATE_IDENTIFIER状态里为什么不能无条件接受下划线和字母数字
因为标识符不能以数字开头,但可以中间或结尾含数字。所以进入STATE_IDENTIFIER的前提,必须是**首字符为字母或下划线**;后续字符才允许是字母、数字或下划线。
容易踩的坑是:在STATE_START里看到'a'就跳进STATE_IDENTIFIER,但看到'1'也跳进去——这就放过了非法开头。
- 正确做法:
STATE_START中对isalpha(c) || c == '_'才转STATE_IDENTIFIER;对isdigit(c)则转STATE_NUMBER -
STATE_IDENTIFIER内部再用isalnum(c) || c == '_'判断是否继续 - 别忘了在状态退出时,把最后一个不满足条件的字符“回退”——比如读到
abc+,+不属于标识符,要退回去供下次处理
怎么处理双字符运算符比如==和=
不能读到第一个=就立即返回TOKEN_ASSIGN,得往前探一个字符:如果下一个也是=,就返回TOKEN_EQ;否则才是TOKEN_ASSIGN。
但探查不能破坏流位置,所以标准做法是:先消耗第一个=,然后peek()看下一个(不移动游标),再根据结果决定是否再消耗第二个。
- 推荐在
STATE_START中遇到'='时,立刻调用peek()(假设是input[pos]) - 若
peek() == '=',则pos += 2,返回TOKEN_EQ - 若不是,则
pos += 1,返回TOKEN_ASSIGN - 同理适用于
!=、&&、||等,但->、++这类需按实际语法需求取舍,不必一步到位
Token结构体里要不要存原始字符串指针
要,但必须小心生命周期。如果Token里存的是const char*指向输入缓冲区,那缓冲区被释放后Token就悬空了。
简单项目直接存std::string最安全;性能敏感场景可存std::string_view,但前提是保证输入字符串的生命周期长于所有Token对象。
- 初版建议用
std::string lexeme;字段,构造时用std::string(buf + start, pos - start)截取 - 不要用
char*手动管理内存,C++里没这个必要 - Token类型用enum class(如
enum class TokenType { TOKEN_NUMBER, TOKEN_IDENTIFIER, ... };),避免宏污染和隐式转换
状态机真正难的不是分支逻辑,而是边界:空格怎么跳、换行怎么计、EOF怎么判、错误怎么恢复。这些细节不写进循环体,词法分析器就会在真实脚本里崩得莫名其妙。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











