你写下一行代码:
total = price * 0.13
在你眼里,这是三个名字、两个符号、一个数字。但在编译器眼里,它一开始什么都不是——只是十四个字符排成的一列:t、o、t、a、l、空格、=……像一长串没断句的电报。
编译器干活的第一步,就是把这串电码切成段、贴上标签。这一步叫词法分析(lexical analysis,也叫 tokenization:照着一套规则,把字符流切成一个个有意义的最小单位),切出来的每一段就是一个 token。Martin Fowler 在《领域特定语言》里给它的定义干净利落:每个 token 是一个小对象,只有两个属性——类型(type)和内容(payload)。上面那行代码切完,大概是这样一排:
- 类型「标识符」,内容
total - 类型「赋值号」,内容
= - 类型「标识符」,内容
price - 类型「乘号」,内容
* - 类型「数字」,内容
0.13
为什么非要先切一刀
你可能会问:编译器直接读字符不行吗,为什么非要先过一遍这道刀?
因为不切断句子,后面的活没法干。设想你要分析「price 乘 0.13」的结构,就得先知道哪些字符算一伙的:空格和乘号是边界,p-r-i-c-e 五个字母要抱成一团当一个整体看。词法分析就是把「字符的河流」冻成「符号的冰块」——下游的语法分析(parsing,检查这些符号按语法能不能组成合法结构的那一步)只需要面对一小盒冰块,而不用在河里游泳。
大白话:这就像你听别人说话。对方嘴里出来的是一条连续的气流,但你大脑会先把气流切成一个一个词,然后才谈得上理解句子。词法分析器就是编译器的「耳朵」:先切词,再理解。
两个属性里的讲究
Fowler 点出了一个很细的差别:对关键词来说,内容不重要,类型就是一切;对标识符来说,内容才是命根子。
什么意思?if 这个词,编译器只在乎「它是个 if」——至于拼写本身,确认是 if 之后就没用了,类型「if 关键字」携带了全部信息。但 price 不一样:它是个标识符没错,可到底是 price 还是 prize,天差地别——后面算钱的时候要用到它指向的那个变量,内容必须原样带走。
所以 token 在这里是个行李牌:类型是牌子(「这是易碎品」「这是托运件」),决定后面的人怎么处置它;内容是箱子,有的箱子是空的(光靠牌子办事),有的箱子装着真金白银。
从「凭证」到「特征」:这一跳扔了什么
现在对照一下第三章的通行证。安全 token 代表的是「你已通过验证」这件事实;词法 token 代表的是「这段字符是什么」这个判断。替身还是那个替身——一个站出来代表别的东西的小对象——但正主换了:从「权利」换成了「类别」。
有意思的是,游戏厅的三条件仍然都在,只是变了形:
- 可铸造 → 词法规则定义了哪些字符组合是合法 token(
0.13是,0.1.3不是); - 可流通 → token 从词法分析器交给语法分析器,一站站往下传;
- 可兑现 → 下游见到「数字」类型,就真的按数字处理,不再回头看原始字符。
还有那个老原因:语法分析器不用再逐字辨认「p 后面是 r」,只看 token 的类型牌——验证替身,不验证正主,在这里变成了「处理类别,不处理字符」。
本章留下的问题
词法分析的切法是规则驱动的:人先定好「字母开头的一串叫标识符」,机器照章执行,黑白分明。但轮到自然语言——中文没有空格,英文有词形变化,「unbelievable」算不算一个词?——规则开始左支右绌。
下一章和再下一章,我们将看到两次大胆的换招:搜索引擎用语言学的刀来切,大模型干脆用统计学的刀来切。同一个 token,刀法截然不同。