Compiler - lexical analysis
概念:
1. Context-Free Grammar: a). 一个终结符集合。b). 一个非终结符集合。c). 一个产生式集合,产生式左部为一个非终结符,右部为终结符或非终结符序列。d). 一个初始状态。
2. Parse Tree(Concrete Syntax Tree), Abstract Syntax Tree(AST)
文法: list -> list+digit | list-digit | digit; digit -> 0|1|2|3|4|5|6|7|8|9
抽象语法树不包含具体的文法信息(或形式)
为了实现类似greedy效果,给出了3个状态图,图一匹配12.3E-4的形式,图二匹配12.3这样的形式,图三匹配123这样的形式。如果状态图一匹配失败,尝试继续匹配状态图二...
2. Thompson's Construction 从正则表达式构造NFA
输入: 字母表Σ上的一个正则表达式r
输出: 接受L(r)的NFA N
分析r并将其分解成最基本的子表达式,如果a在r中出现多次,需要为a的每次出现构造NFA
2.1 对ε构造NFA
2.2 对Σ中的每个符号a构造NFA
2.3 如果N(s)和N(t)是正则表达式s和t的NFA,则:
椭圆中N(s)和N(t)左边部分为开始状态,右边部分为终止状态集合;st的NFA中,N(s)的终止状态与N(t)的开始状态合并
正则表达式(s)的NFA为N(s)本身
例用上面的方法为 r->(a|b)*abb 构造的NFA N(r)为:
3. Subset Construction - 从NFA构造DFA
输入: 一个NFA N
输出: 一个接受同样语言的DFA D
ε-closure(T)算法:
Subset construction(s0为NFA的初始状态;Dstates为DFA中的状态集合;Dtrans为DFA中的转换规则集合):
示例将2.3中构造的NFA转换为DFA:
a). 标记为ε的叶节点n: nullable(n)=true; firstpos(n)=Φ;
b). 标记为位置i的叶节点n: nullable(n)=false; firstpos(n)={i};
c). n=c1|c2: nullable(n)=nullable(c1) or nullable(c2); firstpos(n)=fistpos(c1)∪firstpos(c2);
d). n=c1●c2: nullable(n)=nullable(c1) and nullable(c2); firstpos(n)= if nullable(c1) then firstpos(c1)∪firstpos(c2) else firstpos(c1);
e). n=(c1)*: nullable(n)=true; firstpos(n)=firstpos(c1);
followpos计算方法:
a). 如果n是cat-node,具有左子节点c1和右子节点c2,并且i是lastpos(c1)中的一个位置,则firstpos(c2)中的所有位置都在followpos(i)中
b). 如果n是star-node,并且i是lastpos(n)中的一个位置,则所有firstpos(n)中的位置都在followpos(i)中
以r -> (a|b)*abb为例,扩展正则表达式(r)#的语法树、firstpos、lastpos和followpos如下图:
创建有向图来表示followpos: 节点表示位置; 从i到j的有向边表示j在followpos(i)中
根据上面的算法计算DFA:
与图3.1比较,图4.4的DFA少了一个状态
5. 最小化DFA的状态数
理论上每一个正则集都可以由一个状态数最少的DFA识别,这个DFA是唯一的,开始状态为s0,接受状态集为F
输入: DFA M,状态集合为S,输入符号集合为Σ
输出: 一个DFA M',它和M接受同样的语言,且状态数最少
算法:
最小化图3.1的DFA状态数:
文法: list -> list+digit | list-digit | digit; digit -> 0|1|2|3|4|5|6|7|8|9
抽象语法树不包含具体的文法信息(或形式)
3. Syntax-Directed Translation
翻译模式: 参考Antlr中的Arbitrary Actions。Syntax-Directed: 文法符号与一个属性集合关联,产生式与一个语义规则集合关联,文法符号(属性集合)和语义规则集合构成Syntax-Directed定义,参考Antlr的Rewrite Rules
翻译模式: 参考Antlr中的Arbitrary Actions。Syntax-Directed: 文法符号与一个属性集合关联,产生式与一个语义规则集合关联,文法符号(属性集合)和语义规则集合构成Syntax-Directed定义,参考Antlr的Rewrite Rules
4. Nondeterministic finite automata NFA
a). 一个状态的有穷集合S; b). 一个输入符号集合Σ; c). 一个转换函数move,把状态和符号组成的二元组映射到状态集合; d). 状态s0是唯一的初始状态; d). 状态集合F是终止状态集合
a). 一个状态的有穷集合S; b). 一个输入符号集合Σ; c). 一个转换函数move,把状态和符号组成的二元组映射到状态集合; d). 状态s0是唯一的初始状态; d). 状态集合F是终止状态集合
5. Deterministic finite automata DFA
DFA是NFA的特例,DFA在任何状态下,对任一输入符号最多只有一个转换
a). 没有一个状态具有ε转换; b). 对每个状态s和输入符号a,最多只有一条标记为a的边离开s
DFA是NFA的特例,DFA在任何状态下,对任一输入符号最多只有一个转换
a). 没有一个状态具有ε转换; b). 对每个状态s和输入符号a,最多只有一条标记为a的边离开s
词法分析:
Token与Lexeme区别: Token相当于类型,lexeme相当于不同的实例。例如token为relation,lexeme可能包括<, >, <>, <=, >=等;例如token为id,lexeme可能包括源代码中出现的所有标识符
语法上的运算: 并集 L∪M; 连接 LM; 闭包L*; 正闭包L+。语法树上如果一个节点被标记为连接、或、闭包操作,则该节点分别简称为cat-node, or-node, star-node
语法上的运算: 并集 L∪M; 连接 LM; 闭包L*; 正闭包L+。语法树上如果一个节点被标记为连接、或、闭包操作,则该节点分别简称为cat-node, or-node, star-node
1. Transition Diagram及其实现
语法: num->digit+(.digit+)?(E+|-)?digit+)?
状态图:
语法: num->digit+(.digit+)?(E+|-)?digit+)?
状态图:
为了实现类似greedy效果,给出了3个状态图,图一匹配12.3E-4的形式,图二匹配12.3这样的形式,图三匹配123这样的形式。如果状态图一匹配失败,尝试继续匹配状态图二...
示例代码:
while (1)
{
switch (state)
{
case 0:
c = NextChar(); //c is lookahead char
if (c == BLANK || c == TAB || c == NEWLINE)
lexeme_beginning++; //the lexeme pointer (advance beginning of lexeme)
else if (IsDigit(c)) state = 1; //only digit char is accepted by state 0
else Fail();
break;
case 1:
c = NextChar();
if (IsDigit(c)) state = 1;
else if (c == '.') state = 2;
else if (c == 'E') state = 4;
else Fail();
break;
//... case 2--6 here
case 7:
Retract(1); //put the lookahead char back into the input stream
InstallNum(); //install the matched token into symbol table
return GetToken(); //return the matched token
case 8:
if (IsDigit(c)) state = 9;
else Fail();
break;
//... case 9--14 here
case 15:
Retract(1);
InstallNum();
return GetToken();
}
}
while (1)
{
switch (state)
{
case 0:
c = NextChar(); //c is lookahead char
if (c == BLANK || c == TAB || c == NEWLINE)
lexeme_beginning++; //the lexeme pointer (advance beginning of lexeme)
else if (IsDigit(c)) state = 1; //only digit char is accepted by state 0
else Fail();
break;
case 1:
c = NextChar();
if (IsDigit(c)) state = 1;
else if (c == '.') state = 2;
else if (c == 'E') state = 4;
else Fail();
break;
//... case 2--6 here
case 7:
Retract(1); //put the lookahead char back into the input stream
InstallNum(); //install the matched token into symbol table
return GetToken(); //return the matched token
case 8:
if (IsDigit(c)) state = 9;
else Fail();
break;
//... case 9--14 here
case 15:
Retract(1);
InstallNum();
return GetToken();
}
}
2. Thompson's Construction 从正则表达式构造NFA
输入: 字母表Σ上的一个正则表达式r
输出: 接受L(r)的NFA N
分析r并将其分解成最基本的子表达式,如果a在r中出现多次,需要为a的每次出现构造NFA
2.1 对ε构造NFA
2.2 对Σ中的每个符号a构造NFA
2.3 如果N(s)和N(t)是正则表达式s和t的NFA,则:
椭圆中N(s)和N(t)左边部分为开始状态,右边部分为终止状态集合;st的NFA中,N(s)的终止状态与N(t)的开始状态合并
正则表达式(s)的NFA为N(s)本身
例用上面的方法为 r->(a|b)*abb 构造的NFA N(r)为:
3. Subset Construction - 从NFA构造DFA
输入: 一个NFA N
输出: 一个接受同样语言的DFA D
ε-closure(T)算法:
push all states in T onto stack;
initialize ε-closure(T) to T;
while stack is not empty do begin
pop t, the top element, off of stack;
for each state u with an edge from t to u labeled ε do
if u is not in ε-closure(T) do begin
add u to ε-closure(T);
push u onto stack;
end
end
initialize ε-closure(T) to T;
while stack is not empty do begin
pop t, the top element, off of stack;
for each state u with an edge from t to u labeled ε do
if u is not in ε-closure(T) do begin
add u to ε-closure(T);
push u onto stack;
end
end
Subset construction(s0为NFA的初始状态;Dstates为DFA中的状态集合;Dtrans为DFA中的转换规则集合):
initially, ε-closure(s0) is the only state in Dstates and it is unmarked;
while there is an unmarked state T in Dstates do begin
mark T;
for each input symbol a do begin
U := ε-closure(move(T, a));
if U is not in Dstates then
add U as an unmarked state to Dstates;
Dtrans[T, a] := U;
end
end
while there is an unmarked state T in Dstates do begin
mark T;
for each input symbol a do begin
U := ε-closure(move(T, a));
if U is not in Dstates then
add U as an unmarked state to Dstates;
Dtrans[T, a] := U;
end
end
示例将2.3中构造的NFA转换为DFA:
A = ε-closure({ 0 }) = { 0,1,2,4,7 }
B = ε-closure(move(A, a)) = ε-closure({ 3,8 }) = { 1,2,4,6,7,8 }; Dtrans[A, a] = B;
C = ε-closure(move(A, b)) = ε-closure({ 4 }) = { 1,2,4,5,6,7 }; Dtrans[A, b] = C;
D' = ε-closure(move(B, a)) = ε-closure({ 3,8 }) = B; Dtrans[B, a] = B;
D = ε-closure(move(B, b)) = ε-closure({ 5,9 }) = { 1,2,4,5,6,7,9 }; Dtrans[B, b] = D;
继续这一过程,还会得到一个DFA状态以及剩余的转换规则集合
E = { 1,2,4,5,6,7,10 }; ...
得到的DFA如图:B = ε-closure(move(A, a)) = ε-closure({ 3,8 }) = { 1,2,4,6,7,8 }; Dtrans[A, a] = B;
C = ε-closure(move(A, b)) = ε-closure({ 4 }) = { 1,2,4,5,6,7 }; Dtrans[A, b] = C;
D' = ε-closure(move(B, a)) = ε-closure({ 3,8 }) = B; Dtrans[B, a] = B;
D = ε-closure(move(B, b)) = ε-closure({ 5,9 }) = { 1,2,4,5,6,7,9 }; Dtrans[B, b] = D;
继续这一过程,还会得到一个DFA状态以及剩余的转换规则集合
E = { 1,2,4,5,6,7,10 }; ...
4. 从正则表达式构造DFA
输入: 正则表达式r
输出: 识别L(r)的DFA D
算法: 构造扩展正则表达式(r)#的语法树T; 对T进行深度优先遍历计算函数nullable, firstpos, lastpos和followpos; 用下面的算法计算Dstates和Dtrans
输入: 正则表达式r
输出: 识别L(r)的DFA D
算法: 构造扩展正则表达式(r)#的语法树T; 对T进行深度优先遍历计算函数nullable, firstpos, lastpos和followpos; 用下面的算法计算Dstates和Dtrans
initially, the only unmarked state in Dstates, where root is the root of the syntax tree for (r)#;
while there is an unmarked state T in Dstates do begin
mark T;
for each input symbol a do begin
let U be the set of positions that are in followpos(p) for some position p in T, such that the symbol at position p is a;
if U is not empty and is not in Dstates then
add U as an unmarked state to Dstates;
Dtrans[T, a] := U;
end
end
在正则表达式r后面添加#符号,目的是使r具有唯一的结束标识; nullable, firstpos, lastpos计算方法:while there is an unmarked state T in Dstates do begin
mark T;
for each input symbol a do begin
let U be the set of positions that are in followpos(p) for some position p in T, such that the symbol at position p is a;
if U is not empty and is not in Dstates then
add U as an unmarked state to Dstates;
Dtrans[T, a] := U;
end
end
a). 标记为ε的叶节点n: nullable(n)=true; firstpos(n)=Φ;
b). 标记为位置i的叶节点n: nullable(n)=false; firstpos(n)={i};
c). n=c1|c2: nullable(n)=nullable(c1) or nullable(c2); firstpos(n)=fistpos(c1)∪firstpos(c2);
d). n=c1●c2: nullable(n)=nullable(c1) and nullable(c2); firstpos(n)= if nullable(c1) then firstpos(c1)∪firstpos(c2) else firstpos(c1);
e). n=(c1)*: nullable(n)=true; firstpos(n)=firstpos(c1);
followpos计算方法:
a). 如果n是cat-node,具有左子节点c1和右子节点c2,并且i是lastpos(c1)中的一个位置,则firstpos(c2)中的所有位置都在followpos(i)中
b). 如果n是star-node,并且i是lastpos(n)中的一个位置,则所有firstpos(n)中的位置都在followpos(i)中
以r -> (a|b)*abb为例,扩展正则表达式(r)#的语法树、firstpos、lastpos和followpos如下图:
创建有向图来表示followpos: 节点表示位置; 从i到j的有向边表示j在followpos(i)中
根据上面的算法计算DFA:
firstpos(root)=firstpos(6)={1,2,3}=A;
Dtrans[A, a]=followpos(1)∪followpos(3)={1,2,3,4}=B;
Dtrans[A, b]=followpos(2)={1,2,3}=A;
Dtrans[B, a]=followpos(1)∪followpos(3)=B;
Dtrans[B, b]=followpos(2)∪followpos(4)={1,2,3,5}=C;
Dtrans[C, a]=followpos(1)∪followpos(3)=B;
Dtrans[C, b]=followpos(2)∪followpos(5)={1,2,3,6}=D;
Dtrans[D, a]=followpos(1)∪followpos(3)=B;
Dtrans[D, b]=followpos(2)=A;
构造的DFA图:Dtrans[A, a]=followpos(1)∪followpos(3)={1,2,3,4}=B;
Dtrans[A, b]=followpos(2)={1,2,3}=A;
Dtrans[B, a]=followpos(1)∪followpos(3)=B;
Dtrans[B, b]=followpos(2)∪followpos(4)={1,2,3,5}=C;
Dtrans[C, a]=followpos(1)∪followpos(3)=B;
Dtrans[C, b]=followpos(2)∪followpos(5)={1,2,3,6}=D;
Dtrans[D, a]=followpos(1)∪followpos(3)=B;
Dtrans[D, b]=followpos(2)=A;
与图3.1比较,图4.4的DFA少了一个状态
5. 最小化DFA的状态数
理论上每一个正则集都可以由一个状态数最少的DFA识别,这个DFA是唯一的,开始状态为s0,接受状态集为F
输入: DFA M,状态集合为S,输入符号集合为Σ
输出: 一个DFA M',它和M接受同样的语言,且状态数最少
算法:
a). 使S中每个状态对Σ中的每个输入符号都有转换; 对那些不存在转换的,引入一个"dead state"d,即任何状态对Σ上的任意输入字符都可以转换到d
b). 构造具有两个组的状态集合的初始划分Ⅱ: 接受状态组F,非接受状态组S-F
c). 用下面的方法构造新的划分Ⅱnew
for Ⅱ中的每个组G do begin
当且仅当对任意输入符号a,G中的状态s和t在a上的转换都到达Ⅱ中的同一个组时,才把G化分成一个小组(不符合这个规则的话G必须再拆分);
用所有新形成的小组集合代替Ⅱnew中的小组集合;
end
d). 如果Ⅱnew=Ⅱ,令Ⅱfinal=Ⅱ,执行步骤e);否则令Ⅱ=Ⅱnew,重复步骤c)
e). 为Ⅱfinal构造转换规则集
f). 移除Ⅱfinal中的"dead state",取消从任何状态到"dead state"的转换规则; 删除从开始状态不可到达的状态;
b). 构造具有两个组的状态集合的初始划分Ⅱ: 接受状态组F,非接受状态组S-F
c). 用下面的方法构造新的划分Ⅱnew
for Ⅱ中的每个组G do begin
当且仅当对任意输入符号a,G中的状态s和t在a上的转换都到达Ⅱ中的同一个组时,才把G化分成一个小组(不符合这个规则的话G必须再拆分);
用所有新形成的小组集合代替Ⅱnew中的小组集合;
end
d). 如果Ⅱnew=Ⅱ,令Ⅱfinal=Ⅱ,执行步骤e);否则令Ⅱ=Ⅱnew,重复步骤c)
e). 为Ⅱfinal构造转换规则集
f). 移除Ⅱfinal中的"dead state",取消从任何状态到"dead state"的转换规则; 删除从开始状态不可到达的状态;
最小化图3.1的DFA状态数:
a). A, B, C, D, E对输入字符a, b都有转换,不需要添加"dead state"; 初始划分Ⅱ: {A, B, C, D}, {E}
b). {E}不可再划分,直接加入Ⅱnew ;
对于输入字符a,A, B, C, D都转换到状态B,符合要求;
对于输入字符b,A, B, C转换到的状态都位于本组内,但D将转换到E,因此将D划分出去,将新的分组{A, B, C}和{D}加入Ⅱnew ;
c). 继续b)的步骤,得到Ⅱfinal : {A, C}, {B}, {D}, {E},即最小化状态的DFA,与图4.4一样
b). {E}不可再划分,直接加入Ⅱnew ;
对于输入字符a,A, B, C, D都转换到状态B,符合要求;
对于输入字符b,A, B, C转换到的状态都位于本组内,但D将转换到E,因此将D划分出去,将新的分组{A, B, C}和{D}加入Ⅱnew ;
c). 继续b)的步骤,得到Ⅱfinal : {A, C}, {B}, {D}, {E},即最小化状态的DFA,与图4.4一样