用C++实现词法分析器的核心是清晰的状态机设计:定义START、IN_NUMBER、IN_IDENTIFIER等enum状态,每个状态仅处理当前字符并决定下一状态和是否产出token;用string_view避免拷贝但需确保源缓冲区生命周期足够长;关键字匹配应预加载unordered_set,运算符区分单双字符需状态暂存而非回溯;行号列号等位置信息必须保留。

直接上结论:用 C++ 写一个能跑通基础语法(数字、标识符、运算符、括号)的词法分析器,核心不是堆类和模板,而是把状态机逻辑写清楚、边界条件收干净——否则 123abc 会被错切成 123 和 abc,而它其实该报错或按语言规则处理为非法 token。
怎么设计状态机?从字符流到 token 的关键跳转
状态机不是为了炫技,是为了解决“当前读到的字符到底属于哪个 token”的歧义。比如 = 是赋值,== 是相等判断,=> 在某些语法里是 lambda 引导符。必须靠状态推进来区分。
典型状态包括:START(初始)、IN_NUMBER(正在读数字)、IN_IDENTIFIER(正在读变量名)、IN_COMMENT(遇到//后跳过)、IN_STRING(遇到"后收集内容),以及各种双字符运算符的中间态如 SEEK_EQUAL(刚读到=,等下一个字符确认是==还是=)。
实操建议:
- 用
enum class State { START, IN_NUMBER, ... };明确列出所有状态,避免 magic number - 每个状态只处理「当前字符」,决定「下一个状态」和「是否 emit token」,不回溯
- 在
START状态遇到空格/制表符/换行就跳过;遇到/就进SEEK_COMMENT或SEEK_DIVIDE,不能直接 emit/ - 字符串字面量要处理转义,比如
"a\nb"中的\n应转为单个换行符,这步放在IN_STRING状态里做,别留到 parser 阶段
std::string_view vs std::string:token 值该不该拷贝?
词法分析阶段生成的 token 大多是源码的子串(比如 while 就是输入中连续的 5 个字符),用 std::string_view 指向原始缓冲区最省开销。但前提是:源码字符串生命周期必须长于所有 token 的使用周期。
常见翻车点:
- 把文件内容读进局部
std::string buffer,然后构造Lexer lexer(buffer),再返回一堆Token{.text = std::string_view(...)}—— buffer 析构后,所有string_view指向野地址 - 对数字 token 做
std::stoi转换时,传入string_view会隐式转std::string,触发一次拷贝;不如在Token结构体里加int value字段,只在IN_NUMBER状态里边读边算 - 保留原始位置信息(行号、列号)比保留
string_view更重要;哪怕全用std::string存 token 文本,只要位置准,parser 出错时就能准确定位
哪些 token 类型必须显式定义?别依赖 ASCII 码硬判断
初学者常写 if (c == 43) token = PLUS;,这既难读又错:ASCII 43 是 + 没错,但一旦支持 Unicode 标识符(比如变量名用中文),你就得切到 UTF-8 解码逻辑,硬编码字符值立刻崩盘。
正确做法是把关键字和运算符列为常量表,在 START 后统一匹配:
if (c == 'i') {
if (match_keyword("if")) return Token{IF, pos};
if (match_keyword("int")) return Token{INT, pos};
goto identifier;
}
实操建议:
- 关键字列表(
"if","while","return")用std::unordered_set<:string_view>预加载,避免每次线性扫描 - 单字符运算符(
+,-,;)可查表,双字符的(,!=,&&)必须靠状态暂存,不能靠“读两个再判断”——因为&单独出现也是合法 token - 忽略大小写?别在 lexer 里做
std::tolower,应在 keyword 匹配前统一转小写,或初始化 set 时插小写字符串
最难缠的其实是行首缩进和换行处理:Python 靠缩进定义作用域,但它的 lexer 必须输出 INDENT/DEDENT token;而 C++ 风格语言虽不依赖缩进,却要确保 // 注释吞掉整行剩余内容,且 \r\n 和 \n 都算一行结束——这些细节不写进状态转移表,光靠 if-else 很快就失控。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











