Compiler - lexical analysis

无人久伴 提交于 2020-01-05 01:39:47
概念:
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
   
抽象语法树不包含具体的文法信息(或形式)
3. Syntax-Directed Translation
翻译模式: 参考Antlr中的Arbitrary Actions。Syntax-Directed: 文法符号与一个属性集合关联,产生式与一个语义规则集合关联,文法符号(属性集合)和语义规则集合构成Syntax-Directed定义,参考Antlr的Rewrite Rules
4. Nondeterministic finite automata NFA
a). 一个状态的有穷集合S; b). 一个输入符号集合Σ; c). 一个转换函数move,把状态和符号组成的二元组映射到状态集合; d). 状态s0是唯一的初始状态; d). 状态集合F是终止状态集合
5. Deterministic finite automata DFA
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

1. Transition Diagram及其实现
语法: 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();
        }
    }

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
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
示例将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如图:
   

4. 从正则表达式构造DFA
输入: 正则表达式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计算方法:
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图:
   
与图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"的转换规则; 删除从开始状态不可到达的状态;

最小化图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一样
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!