Lexcial analysis(词法分析)即将一个代码段中的字符或字符串(token
)进行分类。
what is token? a token class corresponds to a set of substrings
目的
词法分析完成后,分析器会将结果给后面的解析器(Parser
),解析器会根据不同的分类对不同类的token
做不一样的处理,因此需要词法分析。
分类
在自然语言中我们可以将词分为:名词、动词、形容词、副词等,而在编程语言中我们也可以进行分类,有如下几类(不同的编程语言分类中的内容有区别):
Identifier
——标识符(变量名或类型名)Keyword
——关键字,如if
、while
Number
——数字WhiteSpace
——空白字符,如\n
、\t
、空格等Operator
——操作符,如+
、-
、=
等Punctuation
——标点符号,如:
、;
、"
等,通常每个标点自己作为一类
流程
- 传入代码段
- 将代码段分成多个小字符串,每个字符串中的内容都隶属于上面的某一类
- 对每个字符串进行分类
- 得到多个
<类型, 字符串>
这样的<key,value>
对,传给后面的解析器Parser
。
一个简单的例子:
// C++
if (i == j)
z = 0;
else
z = 1;
将上面的代码段中的字符先按字符串形式写出:
if (i == j)\n\tz = 0;\nelse\n\tz = 1;
然后进行划分(用|
分隔):
|if| |(|i| |==| |j|)|\n|\t|z| |=| |0|;|\n|else|\n|\t|z| |=| |1|;|
最后再对每个子串分类,最后产生如<keyword, if>
这样的<key-value>
对。
难点:
- 一个字符串如何划分,可能会产生歧义:
cin >> a; // 1
foo<bar<int>> // 2
在1
和2
中都出现了>>
,但意义完全不同,在1
中它是输入流的一个操作符,而2
中却不应该看成一个整体。
- 如何划分,有时候还要看后面的内容是什么,如,
==
被看作是一个整体,但解析到第一个=
时,我们不知道是否应该就此停止,将这个=
单独作为一个token
,还是说将其和后面的内容结合,为了辨识这一点,需要看后面串的内容。
为了解决困难,需要引入正则语言(regression language)。
正则语言
设集合A
和B
为字符、字符串、数字的集合,空字符串集记作
ϵ
\epsilon
ϵ(
ϵ
=
{
\epsilon = \{
ϵ={“”
}
\}
},但不是空集),有如下计算:
Union
——并: A + B = { s ∣ s ∈ A 或 s ∈ B } A + B = \{s| s \in A 或 s \in B\} A+B={s∣s∈A或s∈B}。Concatenation
——连接: A B = { a b ∣ a ∈ A 且 b ∈ B } AB = \{ab| a \in A 且 b \in B\} AB={ab∣a∈A且b∈B}。Kleene closure
——Kleene
闭包: A ∗ = ∪ i ⩾ 0 A i A^* = \cup_{i \geqslant 0}A^i A∗=∪i⩾0Aiclosure
——闭包: A + = ∪ i ⩾ 1 A i A^+ = \cup_{i \geqslant 1}A^i A+=∪i⩾1Ai
设r
和s
为正则表达式,L(r)
和L(s)
分别为r
和s
所能表达的字符串集合,则:
- r ∣ s r|s r∣s 为一个正则表达式,其表示 L ( r ) ∪ L ( s ) L(r) \cup L(s) L(r)∪L(s)
- r s rs rs 为一个正则表达式,其表示 L ( r ) L ( s ) L(r)L(s) L(r)L(s)
- r ∗ r^* r∗为一个正则表达式,其表示 ( L ( r ) ) ∗ (L(r))^* (L(r))∗
用正则表达式表示:
- 数字:
0
∣
−
∗
[
1
−
9
]
[
0
−
9
]
∗
0 | -^*[1-9][0-9]^*
0∣−∗[1−9][0−9]∗
®L(s)$
- r ∗ r^* r∗为一个正则表达式,其表示 ( L ( r ) ) ∗ (L(r))^* (L(r))∗
用正则表达式表示:
- 数字: 0 ∣ − ∗ [ 1 − 9 ] [ 0 − 9 ] ∗ 0 | -^*[1-9][0-9]^* 0∣−∗[1−9][0−9]∗
- 以字母开头的字母、数字串: [ a − z A − Z ] + [ a − z A − z 0 − 9 ] ∗ [a-zA-Z]+[a-zA-z0-9]^* [a−zA−Z]+[a−zA−z0−9]∗