編譯器設計-有限自動機

編譯器設計-有限自動機

Compiler Design - Finite Automata

有限自動機是一種狀態機,它以一串符號作爲輸入,並相應地改變其狀態。有限自動機是正則表達式的識別器。當正則表達式字符串被輸入到有限自動機中時,它會爲每個文本更改其狀態。如果輸入字符串成功處理並且自動機達到其最終狀態,則接受它,即剛剛輸入的字符串被認爲是當前語言的有效標記。

有限自動機的數學模型包括:

Finite
set of states (Q)
Finite
set of input symbols (Σ)
One
Start state (q0)
Set
of final states (qf)
Transition
function (δ)

設L(r)是有限自動機(FA)識別的正則語言。

狀態States:FA的狀態用圓圈表示。狀態名寫在圓圈裏。

開始狀態Start state:自動機開始的狀態,稱爲開始狀態。開始狀態有一個箭頭指向它。

中間狀態Intermediate states :所有中間狀態至少有兩個箭頭;一個指向外,另一個指向內(自己)。

最終狀態Final state:如果成功解析輸入字符串,則自動機應處於此狀態。最終狀態用雙圓表示。它可能有任意奇數個指向它的箭頭和偶數個指向它的箭頭。奇數箭頭的數目比偶數大一個,即奇數=偶數+1。

轉換Transition:當在輸入中找到所需的符號時,從一個狀態轉換到另一個狀態。在轉換時,自動機可以移動到下一個狀態,也可以保持在相同的狀態。從一個狀態到另一個狀態的移動顯示爲定向箭頭,箭頭指向目標狀態。如果自動機保持在相同的狀態,則會繪製一個從狀態指向自身的箭頭。

Example : We assume FA accepts any three digit binary value
ending in digit 1. FA = {Q(q0, qf), Σ(0,1), q0, qf, δ}
在這裏插入圖片描述
語法分析或分析是編譯器的第二個階段。在本章中,我們將學習構造解析器時使用的基本概念。

我們已經看到,詞法分析器可以藉助正則表達式和模式規則來識別標記。但是由於正則表達式的限制,詞法分析器無法檢查給定句子的語法。正則表達式無法檢查平衡標記,如括號。因此,這個階段使用上下文無關語法(CFG),這是由下推自動機識別的。

另一方面,CFG是正則語法的超集,如下所示:
在這裏插入圖片描述
這意味着每一種規則語法都是上下文無關的,但也存在一些超出規則語法範圍的問題。CFG是描述編程語言語法的有用工具。

上下文無關語法Context-Free Grammar

在本節中,我們將首先看到上下文無關語法的定義,並介紹用於解析技術的術語。
上下文無關語法有四個組成部分:

一組非終端(V)。非終結符是表示字符串集的語法變量。非終端定義字符串集,幫助定義由語法生成的語言。

一組標記,稱爲終端符號(∑)。終端是構成字符串的基本符號。

一組產品(P)。語法的產生指定了終端和非終端可以組合成字符串的方式。每個產品都由一個稱爲產品左側的非終端、一個箭頭和一系列令牌和/或終端(稱爲產品右側)組成。
其中一個非終端被指定爲開始符號;從那裏開始生產。

字符串是從開始符號派生的,方法是重複地將非終端(最初是開始符號)替換爲生產的右側(對於該非終端)。

例子

我們討論迴文語言的問題,它不能用正則表達式來描述。也就是說,L={w | w=wR}不是常規語言。但可以通過CFG來描述,如下所示:

G = ( V, Σ, P, S )

其中

V = { Q, Z, N }Σ = { 0, 1 }P = { Q → Z | Q → N | Q → ℇ | Z → 0Q0 | N → 1Q1 }S = { Q }

此語法描述迴文語言,如:1001、11100111、00100、1010101、11111等。

語法分析器syntax analyzer

語法分析器或解析器以令牌流的形式接受詞法分析器的輸入。解析器根據生產規則分析源代碼(令牌流),以檢測代碼中的任何錯誤。這個階段的輸出是一個解析樹。
在這裏插入圖片描述
這樣,解析器完成兩個任務,即解析代碼、查找錯誤和生成解析樹作爲階段的輸出。
即使程序中存在錯誤,解析器也需要解析整個代碼。解析器使用錯誤恢復策略,我們將在本章後面學習。

派生Derivation

派生基本上是一系列產生式規則,以便獲取輸入字符串。在解析過程中,我們對某些句子形式的輸入做出兩個決定:

決定要更換的非終端。

決定生產規則,用它來代替非終端。

要決定用生產規則替換哪個非終端,我們可以有兩個選擇。

最左端派生 Left-most Derivation

如果從左到右掃描並替換輸入的句子形式,則稱之爲最左派生。由最左邊派生出來的句子式叫做左邊的句子形式。

最右派生

如果我們Right-most Derivation

從右到左掃描並用產生式規則替換輸入,則稱爲最右派生。從最右邊派生出來的句子形式叫做右邊的句子形式。

例子

生產規則

E → E + EE → E * EE → id

Input string: id + id * id

The left-most derivation is:

E → E * EE → E + E * EE → id + E * EE → id + id * EE → id + id * id

請注意,最左邊的非終端總是先處理。

最正確的推導是:

E → E + EE → E + E * EE → E + E * idE → E + id * idE → id + id * id

Parse Tree

請注意,最左邊的非終端總是先處理。

解析樹

解析樹是派生的圖形描述。可以方便地看到字符串是如何從開始符號派生的。派生的開始符號將成爲解析樹的根。讓我們從上一個主題的一個例子來看這個問題。

我們取a+b*c的最左邊的導出

最左邊的推導是:

E → E * EE → E + E * EE → id + E * EE → id + id * EE → id + id * id
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在解析樹中

All
leaf nodes are terminals.
All
interior nodes are non-terminals.
In-order
traversal gives original input string.

所有葉節點都是終端。

所有內部節點都不是終端。

按順序遍歷給出原始輸入字符串。

按順序遍歷給出原始輸入字符串。解析樹描述運算符的關聯性和優先級。最深的子樹首先遍歷,因此該子樹中的運算符優先於父節點中的運算符。

模棱兩可

如果語法G對於至少一個字符串有多個解析樹(左派生或右派生),則稱它是不明確的。

Example

E → E + EE → E – EE → id

對於字符串id+id–id,上面的語法生成兩個解析樹:
在這裏插入圖片描述
由歧義語法生成的語言被稱爲天生的歧義。語法中的歧義不利於編譯器構造。沒有一種方法能夠自動檢測和消除歧義,但是可以通過重新編寫整個語法而不產生歧義,或者通過設置和遵循關聯性和優先約束來消除歧義。

關聯性Associativity

如果一個操作數的兩邊都有運算符,則運算符接受此操作數的那一邊由這些運算符的關聯性決定。如果操作是左關聯的,則操作數將由左運算符執行;如果操作是右關聯的,則右運算符將執行操作數。

例子

加法、乘法、減法和除法等運算是左關聯的。如果表達式包含:

id op id op id

it will be evaluated as:

(id op id) op id

例如,(id+id)+id

指數運算之類的運算是右關聯的,即同一表達式中的求值順序爲:

id op (id op id)

For example, id ^ (id ^ id)

優先Presedence

如果兩個不同的運算符共享同一個操作數,則由運算符的優先級決定哪個將成爲該操作數。也就是說,2+34可以有兩個不同的解析樹,一個對應於(2+3)4,另一個對應於2+(34)。通過設置運算符之間的優先級,可以很容易地解決此問題。如上例所示,數學上(乘法)優先於+(加法),因此表達式2+3*4將始終解釋爲:

2 + (3 * 4)

這些方法減少了語言或語法中的歧義。

左遞歸 Left Recursion

如果語法有任何非終端“A”,其派生包含“A”本身作爲最左邊的符號,則語法將變爲左遞歸。對於自頂向下的解析器來說,左遞歸語法被認爲是一個有問題的情況。自上而下的解析器從開始符號開始解析,開始符號本身是非終端的。因此,當解析器在派生過程中遇到同一個非終端時,就很難判斷何時停止解析左邊的非終端,從而進入無限循環。

Example:

(1) A => Aα | β (2) S => Aα | β A => Sd

(1) 是立即左遞歸的一個例子,其中A是任何非終端符號,α表示一個非終端字符串。
(2) 是間接左遞歸的一個例子。
在這裏插入圖片描述
自上而下的解析器將首先解析A,然後A將產生一個由A本身組成的字符串,解析器可能永遠進入一個循環。

刪除左遞歸

刪除左遞歸的一種方法是使用以下技術:

生產

A => Aα | β

is converted into following productions

A => βA’A’=> αA’ | ε這不會影響從語法派生的字符串,但會刪除立即左遞歸。
第二種方法是使用下面的算法,它應該消除所有直接和間接的左遞歸。START Arrange non-terminals in some order like A1, A2, A3,…, An for each i from 1 to n
{ for each j from 1 to i-1
{
replace each production of form Ai ⟹Aj𝜸
with Ai ⟹ δ1𝜸 | δ2𝜸 | δ3𝜸 |…| 𝜸
where Aj ⟹ δ1 | δ2|…| δn are current Aj productions
}
} e
liminate immediate left-recursion
END

Example

The production set

S => Aα | β A => Sd

after applying the above algorithm, should become

S => Aα | β A => Aαd | βd

and then, remove immediate left recursion using the first technique.

A => βdA’A’ => αdA’ | ε

Now none of the production has either direct or indirect left recursion.

左因子分解 Left Factoring
如果多個語法生成規則具有公共前綴字符串,則自上而下的解析器無法選擇應使用哪個生成來解析手頭的字符串。
例子
如果自頂向下的解析器遇到A ⟹ αβ | α𝜸 | …然後,由於兩個產品都從同一個終端(或非終端)開始,它無法確定要遵循哪個產品來分析字符串。爲了消除這種混亂,我們使用了一種稱爲左因子分解的技術。
左階乘轉換語法,使其對自頂向下的解析器有用。在這種技術中,我們爲每個公共前綴生成一個結果,其餘的派生結果由新的結果添加。
例子
以上作品可以寫成A => αA’A’=> β | 𝜸 | … 現在解析器每個前綴只有一個產品,這使得決策更容易。
第一組和第二組
解析器表構造的一個重要部分是創建第一個集合和後續集合。這些集合可以提供派生中任何終端的實際位置。這樣做是爲了創建解析表,其中用一些產生式規則替換T[A,T]=α的決定。
第一組
創建此集合是爲了知道非終端在第一個位置派生的終端符號。例如,α → t β即α在第一個位置導出t(終端)。所以,t∈第一(α)。
計算第一組的算法
看第一(α)組的定義:
如果α是終端,那麼首先(α)={α}。
如果α是非終端且α→ℇ是產物,則首先(α)={ℇ}。
如果α是非終端且α→𝜸1𝜸2𝜸3…𝜸n且任何第一個(𝜸)包含t,則t在第一個(α)中。
第一組可以看作:
在這裏插入圖片描述
跟隨集合Follow Set
同樣地,我們計算在產生式規則中,哪個終端符號緊跟非終端α。我們不考慮非終端可以生成什麼,而是看到在非終端生成之後的下一個終端符號是什麼。
計算跟隨集的算法:
如果α是開始符號,則FOLLOW()=$
如果α是非終端且具有產物α→a B,則除ℇ外,先(B)在後(a)中。
如果α是非終端的,並且具有產生α→a B,其中Bℇ,則FOLLOW(a)在FOLLOW(α)中。
Follow集可以看作:Follow(α)={t | Sαt}語法分析器的侷限性Limitations of Syntax Analyzers
語法分析器以標記的形式從詞彙分析器接收它們的輸入。詞法分析器負責語法分析器提供的令牌的有效性。語法分析器有以下缺點-
它無法確定令牌是否有效,
它無法確定令牌在使用前是否已聲明,
它無法確定令牌在使用前是否已初始化,
它無法確定對令牌類型執行的操作是否有效。
這些任務是由語義分析器完成的,我們將在語義分析中對其進行研究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章