概念:
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
![](https://i-blog.csdnimg.cn/blog_migrate/a9400a3b9176c889a6bfd923662cf172.jpeg)
抽象语法树不包含具体的文法信息(或形式)
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+)?
状态图:
![](https://i-blog.csdnimg.cn/blog_migrate/14be6837b757a603046d1a56308d1a49.jpeg)
为了实现类似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
![](https://i-blog.csdnimg.cn/blog_migrate/bbb650244a9b31d0b7cbe270126f2d08.jpeg)
2.3 如果N(s)和N(t)是正则表达式s和t的NFA,则:
![](https://i-blog.csdnimg.cn/blog_migrate/307cffad8c284adb9a97eb525d1e52e9.jpeg)
椭圆中N(s)和N(t)左边部分为开始状态,右边部分为终止状态集合;st的NFA中,N(s)的终止状态与N(t)的开始状态合并
正则表达式(s)的NFA为N(s)本身
例用上面的方法为 r->(a|b)*abb 构造的NFA N(r)为:
![](https://i-blog.csdnimg.cn/blog_migrate/de2fcc01f3818de95fd7138f81c6e037.jpeg)
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 }; ...
![](https://i-blog.csdnimg.cn/blog_migrate/e480cb83f959186c51bdbad0cb72eb4a.jpeg)
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如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/2e231c6828c5e8a2dbdd46271768523b.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/d750e38cb72d07852cb9d2318ff1d688.jpeg)
创建有向图来表示followpos: 节点表示位置; 从i到j的有向边表示j在followpos(i)中
![](https://i-blog.csdnimg.cn/blog_migrate/825ae2b46b383580647ce7a5c2f1d7fc.jpeg)
根据上面的算法计算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;
![](https://i-blog.csdnimg.cn/blog_migrate/408bf2578830503bae174eb4af087c4d.jpeg)
与图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一样