日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

词法分析原理 Lexical Analysis

發(fā)布時(shí)間:2023/12/13 综合教程 31 生活家
生活随笔 收集整理的這篇文章主要介紹了 词法分析原理 Lexical Analysis 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Regex, DFA, NFA 算法理論

算法1:根據(jù)Regex構(gòu)建NFA - McNaughton-Yamada-Thompson Construction

輸入:字母表∑上的一個(gè)正則表達(dá)式r。
輸出:一個(gè)接受L(r)的NFA N
方法:首先對(duì)r進(jìn)行語(yǔ)法分析,分解出組成它的子表達(dá)式。構(gòu)建NFA的規(guī)則分為基本規(guī)則和歸納規(guī)則。
基本規(guī)則:處理不包含運(yùn)算符的子表達(dá)式。
  對(duì)于表達(dá)式ε,構(gòu)造如下NFA:[ start ]---->[ i ]--(ε)-->[[ f ]]。其中i和f都是新?tīng)顟B(tài),分別為起始狀態(tài)和接受狀態(tài)。
  對(duì)于表達(dá)式a,構(gòu)造如下NFA:[ start ]---->[ i ]--(a)-->[[ f ]]。其中i和f都是新?tīng)顟B(tài),分別為起始狀態(tài)和接受狀態(tài)。
  對(duì)于每次ε或a作為r的子表達(dá)式出現(xiàn),都會(huì)用新?tīng)顟B(tài)構(gòu)建一個(gè)獨(dú)立的NFA。
歸納規(guī)則:根據(jù)給定表達(dá)式r的直接子表達(dá)式的NFA構(gòu)造總NFA。
  假設(shè)正則表達(dá)式s和t的NFA分別為N(s)和N(t),可如圖根據(jù)三種情況構(gòu)建r的DFA。
  

算法2:根據(jù)NFA構(gòu)建等價(jià)DFA - 子集構(gòu)造算法 - The Subset Construction

輸入:一個(gè)NFA N
輸出:一個(gè)等價(jià)的DFA D
方法:DFA的每個(gè)狀態(tài)是一個(gè)NFA的狀態(tài)集合,構(gòu)造DFA轉(zhuǎn)換方程Dtran,使得DFA并行地模擬NFA遇到給定輸入串時(shí)可能執(zhí)行的所有動(dòng)作。首先定義如下操作,其中s表示NFA的單個(gè)狀態(tài),T表示NFA的一個(gè)狀態(tài)集合。

ε-closure(s): 能夠從NFA狀態(tài)s開(kāi)始,只通過(guò)ε轉(zhuǎn)換到達(dá)的NFA狀態(tài)集合。
ε-closure(T): 能夠從NFA的狀態(tài)集T中某個(gè)狀態(tài)s開(kāi)始,只通過(guò)ε轉(zhuǎn)換到達(dá)的NFA狀態(tài)集合。
move(T, a):能夠從NFA的狀態(tài)集T中某個(gè)狀態(tài)s開(kāi)始,通過(guò)標(biāo)記為a的轉(zhuǎn)換到達(dá)的NFA狀態(tài)集合。

算法:初始狀態(tài)可能是ε-closure(s0)中的任意狀態(tài),其中s0是NFA的起始狀態(tài)。讀入輸入符號(hào)a后,NFA可以立即移動(dòng)到move(T, a)中任何狀態(tài),同樣可以進(jìn)一步移動(dòng)到ε-closure(move(T, a))中的任何狀態(tài)。

子集構(gòu)造法:
一開(kāi)始,ε-closure(s0)是Dstates中的唯一狀態(tài),且未標(biāo)記; while (在Dstates中有一個(gè)為標(biāo)記狀態(tài)T) { 標(biāo)記T; for (每個(gè)字母表中符號(hào)a) { U = ε-closure(move(T, a)); if (U不在Dstates中) 將未標(biāo)記的U加入到Dstates中; Dtran[T, a] = U; } }
計(jì)算ε-closure(T):
將T的所有狀態(tài)壓入棧stack中, 將ε-closure(T)初始化為T(mén);
while (stack不為空) {
    t = stack.pop;
    for (每個(gè)滿(mǎn)足如下條件的u: 從t出發(fā)有一個(gè)標(biāo)號(hào)為的轉(zhuǎn)換到達(dá)狀態(tài)u)
        if (u不在ε-closure(T)中) {
            將u加入到ε-closure(T)中;
            將u壓入棧中;
        }
}

算法3:模擬NFA執(zhí)行過(guò)程

輸入:一個(gè)以eof結(jié)尾的輸入串x,一個(gè)NFA N,開(kāi)始狀態(tài)為s0,接受狀態(tài)集為F,轉(zhuǎn)換函數(shù)為move。
輸出:接受串x則返回yes,否則返回no。
方法:保存一個(gè)當(dāng)前狀態(tài)集合S,即可以從開(kāi)始狀態(tài)沿著標(biāo)號(hào)到當(dāng)前已讀入的輸入部分的路徑到達(dá)狀態(tài)的集合。如果c是函數(shù)nextChar()讀到的下一個(gè)輸入字符,那么就首先計(jì)算move(S, c),然后通過(guò)ε-closure()求閉包。

模擬NFA執(zhí)行過(guò)程:
S = ε-closure(s0);
c = nextChar();
while (c != eof) {
    S = ε-closure(move(S, c));
    c = nextChar();
}
if (S ∩ F != ?) return yes;
else return no;

算法4:直接通過(guò)Regex創(chuàng)建DFA

重要狀態(tài):如果一個(gè)NFA狀態(tài)有離開(kāi)轉(zhuǎn)換,且都不是基于ε的轉(zhuǎn)換,則該狀態(tài)為重要狀態(tài)。NFA重要狀態(tài)直接對(duì)應(yīng)于regex中符號(hào)的位置。

過(guò)程函數(shù):這些函數(shù)基于增廣正則表達(dá)式(r)#所構(gòu)成的抽象語(yǔ)法樹(shù)。
  1) nullable(n),該節(jié)點(diǎn)所代表的子表達(dá)式對(duì)應(yīng)的語(yǔ)句包含ε語(yǔ)句,則為真。
  2) firstpos(n),可以出現(xiàn)在該節(jié)點(diǎn)表達(dá)的語(yǔ)句的第一個(gè)位置的符號(hào)。
  3) lastpos(n),可以出現(xiàn)在該節(jié)點(diǎn)表達(dá)的語(yǔ)句的最后一個(gè)位置的符號(hào)。
  4) followpos(p),可以出現(xiàn)在p所代表的語(yǔ)句的后面的第一個(gè)字符。
a) 計(jì)算nullable,firstpos,lastpos
  可以根據(jù)語(yǔ)法樹(shù),自底向上遞歸獲得。
b) 計(jì)算followpos
  只有兩種情況會(huì)使得一個(gè)regex的某個(gè)位置跟在另一個(gè)位置之后:
  1) 如果n是一個(gè)cat結(jié)點(diǎn),左右子節(jié)點(diǎn)為c1、c2,那么對(duì)于lastpos(c1)中的每個(gè)位置i,followpos(i) = firstpos(c2);
  2) 如果n是star結(jié)點(diǎn),對(duì)于lastpos(n)中的每個(gè)位置i,followpos(i) = firstpos(n)。

輸入:一個(gè)regex r
輸出:一個(gè)識(shí)別L(r)的DFA D
方法:
  1) 根據(jù)擴(kuò)展正則表達(dá)式(r)#構(gòu)造語(yǔ)法樹(shù)T。
  2) 計(jì)算函數(shù)nullable(), firstpos(), lastpos()和followpos()。
  3) 根據(jù)如下算法構(gòu)造:

從Regex構(gòu)造DFA:
初始化Dstates, 使其只包含未標(biāo)記狀態(tài)集firstpos(n0), 其中n0是T的根節(jié)點(diǎn);
while (Dstates存在未標(biāo)記狀態(tài)集S) {
    標(biāo)記S;
    for (每個(gè)輸入符號(hào)a) {
        令U為S中和a對(duì)應(yīng)的所有位置p的followpos(p)的并集;
        if (U不在Dstates中)
            將未標(biāo)記的U加入Dstates;
        Dtran[S, a] = U;
    }
}

算法5:最小化DFA - Hopcroft's Algorithm

DFA的等價(jià)狀態(tài):對(duì)于任意輸入串產(chǎn)生同樣狀態(tài)。
方法:首先粗劃分為兩組p0 = Daccept,p1 = {D - Daccpet}。對(duì)于ps中的狀態(tài)di和dj,他們必須滿(mǎn)足?c∈Σ,δ(i, c) = x,δ(j, c) = y 且 dx, dy ∈ pt。為了劃分P,該算法檢查每一個(gè)p∈P以及每一個(gè)c∈Σ。如果c劃分p,該算法就將p劃分為兩個(gè)子集并添加至T中。
創(chuàng)建DFA:根據(jù)等價(jià)狀態(tài)分組,對(duì)于每一組狀態(tài)集p∈P,在DFA中對(duì)應(yīng)建立一個(gè)狀態(tài)。對(duì)于dj∈pl,dk∈pm,且δ(dj, c) = dk,我們就創(chuàng)建兩個(gè)狀態(tài)分別對(duì)應(yīng)pl和pm,且δ(pl, c) = pm

DFA等價(jià)狀態(tài)劃分:
T = {Da, {D - Da}};
P = ?;
while (P != T) {
    P = T;
    T = ?;
    for (each set p ∈ P)
        T = T ∪ Split(p);
}
Split(S) {
    for (each c ∈ Σ) {
        if (c splits S into s1 and s2)
            then return {s1, s2};
    }
    return S;
}

算法6:直接從NFA構(gòu)建最小化DFA- Brzozowski's Algorithm

應(yīng)當(dāng)注意到在子集構(gòu)建DFA時(shí),不會(huì)有相同前綴出現(xiàn)。該算法運(yùn)用了這一特性,直接從NFA構(gòu)建最小化DFA。

對(duì)于給定NFA N
  reverse(N): 將NFA倒轉(zhuǎn),將所有原接受狀態(tài)連接至新的起始狀態(tài)。
  reachable(N): 所有從起始狀態(tài)可以到達(dá)的狀態(tài)。
  subset(N): 子集構(gòu)建法獲得的DFA。

于是可得NFA N對(duì)應(yīng)的最小化DFA為:
  reachable(subset(reverse( reachable(subset(reverse(n))) )))

其中,內(nèi)部的subset(reverse(n))精簡(jiǎn)了NFA的后綴,reachable()棄掉了所有無(wú)用的狀態(tài)。然后外部的三個(gè)函數(shù)再一次精簡(jiǎn)了NFA的前綴,并棄掉其余無(wú)用狀態(tài),最終構(gòu)建最小化NFA。

將DFA轉(zhuǎn)化為代碼實(shí)現(xiàn)

實(shí)現(xiàn)1:表驅(qū)動(dòng)分析器 - Table-Driben Scanner

方法:將轉(zhuǎn)換函數(shù)輸入到一個(gè)對(duì)應(yīng)的二維數(shù)組中,scan過(guò)程中通過(guò)查表來(lái)模擬DFA行為。還可以提供另外一個(gè)表用來(lái)將輸入字符分類(lèi),實(shí)現(xiàn)壓縮轉(zhuǎn)換表。

Table-Driven Scanner:
Nextword() {    /* initalization */ state = S0; lexeme = ""; stack.clear(); stack.push(bad); /* scanning loop to model the DFA's behavior */ while (state != Serror) { char = Nextchar(); lexeme += char; if (state ∈ Saccept) stack.clear(); stack.push(state); category = CharCat[char];  // 表1: 字符分類(lèi)表, e.g. digit, letter, punc, space state = δ[state, cat];    // 表2: 轉(zhuǎn)換表, 包含每個(gè)狀態(tài)下對(duì)應(yīng)不同輸入的轉(zhuǎn)換狀態(tài) } /* roll back loop in case the DFA overshoots the end of the token */ while (state ? Saccept && state != bad) { stack.pop(); truncate lexeme; RollBack(); }    /* interprets and reports the result */ if (state ∈ Saccept) return Type[state]; else return invalid; }

實(shí)現(xiàn)2:直接編碼分析器 - Direct-Coded Scanners

方法:直接將轉(zhuǎn)換表的行為編入代碼,節(jié)省查表時(shí)間。

Sinit: lexeme = "";
         stack.clear();
         stack.push(bad);
         goto S0;
S0:    Nextchar(char);
         lexeme += char;
         if (state ∈ Saccept)
             stack.clear();
         stack.push(state);
         if (char == 'r')
             goto S1;
         else goto Sout;
S1:    ...
S2:    ...
Sout: while (state ? Saccept && state != bad) {
             state = stack.pop();
             truncate lexeme;
             RollBack();
         }
         if (state ∈ Saccept)
              then return Type[state];
         return invalid;

實(shí)現(xiàn)3:手工構(gòu)建分析器 - hand-Coded Scanner

方法:利用雙重緩沖技術(shù),通過(guò)指針高效讀取輸入字符。

initialization:
Input = 0;
Fence = 0;
fill Buffer[0 : n];

implementing NextChar():
Char = Buffer[Input];
Input = (Input + 1) mod 2n;
if (Input mod n == 0) {
    fill Buffer[Input : Input + n - 1];
    Fence = (Input + n) mod 2n;
}
return Char;

implementing RollBack():
if (Input == Fence)
    then signal roll back error;
Input = (Input - 1) mod 2n;

總結(jié)

以上是生活随笔為你收集整理的词法分析原理 Lexical Analysis的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。