標籤(空格分隔): 未分類node
Lex
分析器生成工具驅動程序
就是模擬這些自動機的代碼,使用自動機肯定下一個詞法單元.詞法分析是編譯的第一階段.程序員
getNextToken
所指示的調用使得詞法單元從它的輸入不斷讀取字符,直到識別到下一個詞素爲止.把編譯階段的分析部分化爲詞法分析和語法分析階段有以下幾個緣由:正則表達式
三個相關但有區別的術語.
算法
在不少程序設計語言中,下面類別覆蓋了大多數詞法單元vim
標識符
的屬性值是一個指向符號表中該標識符條目的指針.若是沒有其餘組件幫助,詞法分析器很難發現源代碼的錯誤.數組
好比:安全
fi(a==f(x))
恐慌模式
:全部詞法單元都沒法和剩餘輸入的某個前綴相匹配時的策略
<float> <id, limitedSquaare> <(> <id, x> <)> <{> <float> <id, x> <return> <(> <id, x> <op,"<="> <num, -10.0> <op, "||"> <id, x> <op, ">="> <num, 10.0> <)> <op, "?"> <num, 100> <op, ":"> <id, x> <op, "*"> <id, x> <}>
<text, "Here is a photo of"> <nodestart, b> <text, "my house"> <nodeend, b> <nodestart, p> <selfendnode, img> <selfendnode, br> <text, "see"> <nodestart, a> <text, "More Picture"> <nodeend, a> <text, "if you liked that one."> <nodeend, p>
討論幾種能夠加快源程序讀入速度的方法.數據結構
-
,=
,<
多是->
,==
,<=
這樣雙字符運算符的開始.哨兵標記
來節約檢查緩衝區末端的時間.因爲在編譯一個大型程序須要處理大量的字符,處理這些字符須要不少時間,由此開發了一些特殊的緩衝技術來減小用於處理單個輸入字符的時間開銷.一種重要機制是利用兩個交替讀入的緩衝區.閉包
lexemeBegin指針
:該指針指向當前詞素的開始處.當前咱們正試圖肯定這個詞素的結尾.forward指針
:它一直向前掃,直到發現某個模式被匹配到爲止
forward
指針將指向該詞素結尾的字符.
lexemeBegin
指針指向剛找到詞素後的第一個位置forward
指針也會前移一個位置forward
指針前移超過緩衝區末尾(哨兵標記優化的地方)
forward
指針指向這個新載入符的頭部.思考以前的有兩步操做編輯器
在緩衝區末尾擴展一個絕對不會使用的符號,叫作哨兵(sentinel)
字符,一個天然的選擇是eof
.
這一節咱們將研究正則表達式的形式化表示方法
在3.7節中 咱們將學到如何能將正則表達式轉換爲可以識別詞法單元的自動機,並由此創建一個詞法分析樹.
字母表(alphabet)
是一個有限的符號集合.
{0,1}
,ASCII
,Unicode
.串(string)
是該字母表符號的有窮序列.語言(language)
是某個給定字符表上任意的可數的串的集合.
字符串的指數運算
在詞法分析,最重要的語言上的運算是並
,鏈接
,和閉包運算
.
正則表達式能夠由較小的正則表達式按照以下規則遞歸地構建.
某個字母表Σ上的正則表達式以及這些表達式所表示的語言
根據優先級丟掉括號
*
是最高級,而且是左結合.|
的遊戲級最低,也是左結合.(a)|((b)*(c))
改寫爲a|b*c
例子:
能夠用同一個正則表達式定義的語言叫作正則集合(regular set)
若是兩個正則表達式r
和s
表示的語言相同的語言,則稱二者等價,記作r=s
.
正則表達式遵照必定的代數定律
正則定義(regular definition)
是具備以下形式的定義序列:di
都是一個新符號,不在Σ中,而且各不相同.ri
是字母表`Σ U {d1,d2,...di-1}上的正則表達式.除了以上的運算,在現代像Lex
這樣的實用Unix程序都有對正則的擴展
+
+
表示一個正則表達式及其語言的正閉包.(r)+
意思爲 (L(r))+
+
與*
有相同的優先級?
?
的意思是零個和一個出現.r?
等價於r|空集
?
與+
與*
有相同的運算集[]
a1 | a2 |...|an
能夠縮寫爲[a1a2...an]
[a1-an]
[1-9]
,[a-z]
第三題 \/\*([^*"]*|".*"|\*+[^/])*\*\/
第四題 先解決比較簡單的{0,1,2} SB解法 0?1?2?|0?2?1?|1?0?2?|1?2?0?|2?0?1?|2?1?0?| 正確解法 want -> 0|A?0?1(A0?1|01)*A?0?|A0? A -> 0?2(02)* 證實以下面的圖
step3
5-7太難 第八題: b*(a+b?)* 第九題: b* | b*a+ | b*a+ba*
###Lex的擴展方法:
###如何引用這些被使用的符號
#3.4 詞法單元的識別
狀態轉換圖
狀態轉換圖
有一組被稱爲狀態
的結點和圓圈.咱們能夠用兩種方法處理像標識符的保留字.
爲每一個關鍵字創建單獨的狀態轉換圖
有幾種方法根據一組狀態圖構造出詞法分析器.
根據這個狀態圖,寫出getRelop()
函數
fail()
具體操做依賴於全局恢復策略
forward
指針重置爲lexemeBegin
的值fail()
啓動一個錯誤糾正步驟.狀態8,帶有*
,輸入指針會回退,c放回輸入流
retract()
完成狀態圖的執行
介紹一個名爲Lex
的工具,在最近的實現中也稱爲Flex
.
Lex語言
,工具自己是Lex編譯器
.a.out
一般是語法分析器調用的子例程yylval
中
模式 {動做}
Pi
匹配的前綴.Ai
.
Ai
會返回語法分析器yylval
傳遞詞素附加信息
%{ %}
處理名爲ID
的詞法單元的時候
某些時候,咱們但願僅僅詞素後面跟隨特定字符,才能和模式匹配.
/
以後跟隨表示一個附加的模式.揭示Lex如何將輸入程序轉換爲詞法分析器,核心在於有窮自動機
這些自動機本質和轉換狀態圖相似,但也有如下不一樣
肯定和不肯定的能識別的語言的集合是相同的,剛好也是正則表達式能識別的集合,這個集合的語言叫作正則語言
一個NFA由如下部分組成
S
Σ
,即輸入字母表.
S
中的s0
被稱爲開始狀態S
的一個子集F
被稱爲接受狀態跟轉換圖有如下區別
(a|b)*abb
的NFA轉換圖
一個NFA接受輸入字符串x,**當且僅當對應的轉換圖中存在一條開始狀態到某個接受狀態的路徑,使得路徑中各條變上的標號組成字符串x.
注意:路徑的ε
被忽略
咱們能夠用L(A)
表示自動機A接受的語言
子集構造法
的技術給出一個直接模擬NFA的算法
子集構造法的基本思想:是讓構造獲得的DFA的每一個狀態對應於NFA的一個狀態集合.
DFA在讀入輸入a1a2...an
以後到達的狀態對應於相應NFA從開始狀態出發,沿着以a1a2...an
爲標號走到的路徑可以到達狀態的集合.
DFA
狀態數多是NFA
狀態數的指數,此時試圖實現這個DNA有點困難.
輸入: 一個NFA N
輸出: 一個接受一樣語言的 DFA D
方法: 咱們的算法爲D
構造一個轉換表Dtran
.
D的每一個狀態是一個NFA
狀態集合,咱們將構造Dtran
,使得D "並行的" 模擬N在遇到一個給定輸入串可能執行的全部動做.
給出一些基本操做 s
表示N的單個狀態,T表示一個狀態集.
算法代碼(有ACM功底很容易懂):
D的開始狀態是ε-closure(s0)
,D的接受狀態是全部至少包含N
的一個接受狀態的狀態集合,
ε-closure(T)
代碼,一個簡單的深搜而已
許多的文本編輯器使用的策略是根據一個正則表達式構造出相應的NFA
,而後使用相似於on the fly
(邊構造邊)的子集構造法來模擬這個NFA的執行.
也相似廣搜,一步一步把全部狀況跑一遍.
須要如下數據結構
oldStates
存放 "當前狀態集合"newStates
存放 "下一個狀態集合"一個以NFA狀態爲下標的布爾數組 alreadyOn
,指示那個狀態已經在newStates
.
一個二維數組 move[s,a]
,保存了這個NFA的轉換表,是一個鄰接表(由於轉換表單元格有多個元素).
O(k(m+n))
複雜度...那就是吧如今給出一個算法,它能夠將任何正則表達式轉爲接受相同語言的NFA
基本規則
r=s|t
r=st
r=s*
幾個有趣的性質
子集構造法 : O(|r|^2 * DFA狀態數)
DFA狀態數通常是|r|
模擬算法: O(|r|*|x|)
Lex
這樣生成工具的體系NFA
和DFA
的方法,後者實質上就是Lex
的實現方法
本節給出三個算法,用於實現和優化根據正則表達式構建的模式匹配器
O(nlogn)
若是一個NFA狀態有一個標號非ε
的離開轉換,則稱這個狀態是重要狀態(important state)
ε-closure(move(T,a))
,它只使用了集合T中的重要狀態.s
是重要狀態時,move(s,a)
纔多是非空的.(r)#
,使得本來的接受狀態變爲重要狀態,使得在構造過程當中,不要考慮接受狀態.
(r)#
又叫擴展的正則變道時使用抽象語法樹表示擴展的正則表達式.
a
有1和3nullable
,firstpos
,lastpos
和followpos
.
(r)#
下進行的.ε
返回真
q
在 followpos(p)
中當且僅當存在L((r)#)
中的某個串x=a1a2...an
,使得咱們在解釋爲何x屬於L((r)#)
時,能夠將x中某個ai
能夠和位置p匹配,且將位置ai+1和位置q匹配只有兩種狀況,正則表達式某個位置會跟在另外一個位置以後
cat
節點,且左右節點是c1
,c2
.
lastpos(c1)
中的每一個位置i
,firstpos(c2)
中的全部位置都在followpos(i)
中.lastpos(n)
中的一個位置,那麼firstpos(n)
中的全部位置都在followpos(i)
中.
(r)#
構造出一顆抽象語法樹T
nullable
,firstpos
,lastpos
,followpos
D
的狀態集Dstates
和D
的轉換函數Dtran
(數電中有說起)
任何正則語言都一個惟一的(不計同構)狀態數目最少的DFA.,從任意一個接受相同語言的DFA出發,經過分組合並等價的狀態,咱們老是構建這個狀態數最少的DFA.
該算法首先建立輸入DFA的狀態的集合的劃分.
DFA狀態最小化的工做原理是將一個DFA的狀態集合分劃成多個組,每一個組中的各個狀態之間相互不可區分.
以後就相似於縮點的算法.
例子版本:
轉換鏈表壓縮
一種方式,即有數組的便攜,又有鏈表壓縮的默認狀態.
如何保存base值比較好