2021-03-29 編譯原理簡介(一 詞法分析)

本文主要記錄一下編譯原理的學習路徑,由于龍書等著作已近給出了詳細介紹,本文只給出算法介紹;本文不會寫的特別嚴謹,主要是介紹算法。

詞法分析器的實現(xiàn)主要有幾個核心概念:
1,正則表達式
2,抽象語法樹
3,NFA(不確定有窮狀態(tài)機)
4, DFA(確定有窮狀態(tài)機)

1,正則表達式:
通俗理解:
可以看成一個符號序列,中間含若干確定運算符;最基礎的正則表達式就是字符;每個正則表達式唯一對應一個該正則表達式表達的語言,語言指的是以字符串序列為元素的確定集合;正則表達式的復合對應的語言集合的復合。
因此正則表達式在數(shù)學上可以看成是一系列集合運算;每個正則表達式就對應唯一一個集合,正則表達式的運算對應集合的運算,集合運算的結果仍然是集合

下文中用L(x)表示正則表達式x對應的語言。

定義:
1)給定兩個字符串s_1,s_2s_1s_2表示兩者前后相連所得字符串;
2)如果S_1是一個字符串序列的集合,S_2也是一個字符串序列的集合,則 S_1S_2表示S_1中任意一個字符串后接S_2中任意一個字符串所得的集合
3)S是一個字符串序列組成的集合,則S^i:= SSS...(共i個S),當i=0時,S^0定義為\epsilon(空串)
4)S^* : = S^0 \cup S \cup S^2 \cup S^3...

正則表達式官方定義:

  1. \epsilon是一個正則表達式,L(\epsilon) = \emptyset
  2. 給定符號集S,則S中每個元素x都是一個正則表達式,其對應語言為L(x)= {x}
    3)如果a是正則表達式,則(a)也是正則表達式,且L((a))=L(a)
  3. 如果(a),(b)是兩個正則表達式;則
    (a)|(b)是一個正則表達式, L((a)|(b))= L(a)\cup L(b)
    4)(a)(b)是一個正則表達式,L((a)(b)) = L(a)L(b)
    5)(r)^*是一個正則表達式,表示語言 L(r)^*

可以用以正則表達式定義的語言叫做正則集合

2,正則表達式的抽象語法樹
通俗解釋: 由于正則表達式只包含2元和1元運算符,因此正則表達式可以表示為一棵樹的形狀
舉例: (a|b)*abb 的抽象語法樹為:

                _
             /       \         
           _         b
         /    \
       _      b
     /    \
    *     a
    |
    (|)
   /   \
 a      b

其中用 _ 表示兩個正則表達式連接

3,NFA:
通俗解釋:可以看成一個不含重邊的有向圖,圖的有向邊上關聯(lián)著符號,同一個節(jié)點發(fā)出的多條邊上可能關聯(lián)相同的符號;
4,DFA:
基本同上,不過同一個節(jié)點發(fā)出的多條邊上的符號都不相同

下面基于以上概念快速實現(xiàn)詞法分析器:
一個DFA基本上等價于一個詞法分析器了,而輸入的詞法單元可以對應為一門語言,也就對應到一個正則表達式;

因此,實現(xiàn)詞法分析器的過程,也就是:
根據(jù)正則表達式 構建 DFA的過程,方法為:
正則表達式-> 抽象語法樹-> NFA->DFA

1,正則表達式 -> 抽象語法樹
這一步基本沒什么難度,手寫都可以,略去

2,抽象語法樹->NFA
給抽象語法樹的所有非\epsilon葉子節(jié)點標上數(shù)字(隨意標,不重復即可,不過一般從1,遞增標)這些數(shù)字稱為節(jié)點的位置,可同時也 稱為節(jié)點上符號的位置(一個符號允許對應多個位置)
計算 每個節(jié)點的三個參數(shù):
nullable, firstpos, lastpos ,
和每個位置 的 followpos
定義:
nullable(n) :是一個bool值,當且僅當 以n為根的子樹對應的正則表達式的語言中 包含 \epsilon
firstpos(n): 是一個位置集合,一個位置屬于該集合 當且僅當 以n為根的子樹對應正則表達式的語言中存在一個符號串滿足其首字母 按照這顆樹解析時 剛好解析到這個位置;
lastpos(n): 位置集合 ,一個位置屬于該集合 當且僅當 n為根的子樹對應的正則表達式的語言中存在一個符號串 滿足其尾字母 按照這顆樹解析時 剛好解析到這個位置;
followpos(p): 是一個和位置p相關的位置集合。 一個位置q屬于該集合當且僅當 L中的某個川 x=a_1a_2...a_n按照抽象語法樹解析時,p和某個a_i匹配而 qa_{i+1}匹配
算法:
每種類型對應的 nullabel 值 和 firstpos值 分別為:
\epsilon葉子節(jié)點: true , \emptyset
位置為i的葉子節(jié)點: false , {i}
or節(jié)點n=c_1|c_2: nullable(c_1) 或者 nullabel(c_2) ,firstpos(c_1) \cup firstpos(c_2)
cat-節(jié)點 n=c_1c_2: nullable(c_1) \quad and\quad nullable(c_2),
if(nullabel(c_1)) firstpos(c_1)\cup firstpos(c_2) else firstpos(c_1)
start-節(jié)點n=c^*: true, firstpos(c)
由對稱性容易推出 lastpos的公式,略過;
followpos計算法則如下:
1,如果n是 cat節(jié)點,左右子節(jié)點為c_1,c_2,則lastpos(c_1)中每個位置i ,firstpos(c_2)中所有都位于 followpos(i)
2,如果nstart節(jié)點,并且ilastpos(n)中的一個位置,firstpos(n)中的所有位置都位于 followpos(i)

按照以上方法,計算出nullabel,firstpos,lastpos,followpos之后,可以用一個有向圖 來表示 followpos, 每個位置對應圖中一個節(jié)點,位置i到位置j 存在一條有限邊當且僅當 j在followpos(i)中,
然后做三件事:
1把該圖firstpos所有位置設為 開始狀態(tài)
2每條i到j的有向邊上添加位置i上的符號
3 把和結尾#關聯(lián)的位置作為接受狀態(tài)
至此,得到 NFA

3,NFA -> DFA
之后利用以下偽代碼算法就可以計算出 DFA了:
初始化Dstates, 使之只包含未標記狀態(tài) firstpos(n_0)$ n_0為抽象語法樹根節(jié)點
while( Dstates 中存在未標記的狀態(tài)S){
標記S,
for( 每個輸入符號a){
U :=S中和a對應的所有位置p的followpos(p)的并集 (和a對應的意思NFA中該位置的節(jié)點有一個用a標記的發(fā)出邊)
if(U不在Dstates中)將U作為未標記態(tài)加入Dstates;
Dtran[S,a] = U;
}
}
注:其中Dran的就是待求DFA的狀態(tài)邊轉移關系,也就是狀態(tài)S通過標記了a的有向邊轉換到 U;

之后,還有一些優(yōu)化減少DFA節(jié)點數(shù)目,這里略過;

至此,我們快速的實現(xiàn)了一個詞法分析器。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容