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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

词法分析(1)---词法分析的有关概念以及转换图

發布時間:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 词法分析(1)---词法分析的有关概念以及转换图 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

詞法分析(1)---詞法分析的有關概念以及轉換圖
2009-09-26 23:52:03 閱讀(9) 發表評論
  詞法分析是編譯的第一個階段,前面簡介中也談到過詞法分析器的任務就是:

  字符流------>詞法記號流

  這里詞法分析和語法分析會交錯進行,也就是說,詞法分析器不會讀取所有的詞法記號再使用語法分析器來處理,通常情況下,每取一個詞法記號,就送入語法分析器進行分析,圖解:

?

  詞法分析器是編譯器中與源程序直接接觸的部分,因此詞法分析器可以做諸如

  1). 去掉注釋,自動生成文檔(c#中的///注釋)

  2). 提供錯誤位置(可以通過記錄行號來提供),當字符流變成詞法記號流以后,就沒有了行的概念

  3). 完成預處理,比如宏定義

  1. 詞法記號,詞法單元(lexeme),模式

  模式是一種規則

  每個詞法單元都有一個特定記號

  比如 int a=3,這里 int,a,=,3都是詞法單元,每個詞法單元都屬于某個詞法記號,比如3就是"num"這個詞法記號的一個詞法單元,而模式規定了什么樣的字符串的詞法記號是什么樣的(模式是一種規則)

  某一特定模式規定了某個詞法記號下的一類詞法單元,比如:

  模式:用字母開頭的包含字母和數字的串

  上面模式的詞法記號:id(所有符合上面模式的字符串的記號都是id)

  詞法單元:a123 或者 aabc 等

  詞法記號舉例(簡稱為記號):

  1) 每個的關鍵字都有屬于自己的一個記號,比如關鍵字for,它可以使用記號for;關鍵字int,可以使用記號int

  2) 所有的關系運算符只有一個記號,比如 >=,<=都用記號relation

  3) 所有的標識符只有一個記號,比如a123,aab使用記號id

  4) 所有的常數只有一個記號,比如123,22,32.3,23E10使用記號num

  5) 所有的字符串只有一個記號,比如"123","ab1"使用記號literal

  在實際的編譯器設計中,詞法記號,一般用一個整形數字表示

  詞法記號的屬性:

  我們喜歡用<詞法記號, 屬性>這個二元組來描述一個詞法單元,比如,對于源代碼:position := initial + rate * 60

  對于詞法單元 +,我們可以使用 <add_op, '+'> 來表示。

  有些情況,更加復雜一點,比如對于 position,我們表示是這樣的,<id, 指向符號表中的position元素的指針>,詳細來說應該是這樣的,假定屬性是一個字符串,那么id將指向這樣一個字符串"position\0",我們把存放這個字符串的地方叫做符號表。有些時候,屬性是不必要的,比如 := ,表示賦值,我們可以使用 <assign_op,257> 這樣的表示這個詞法單元,不過這個顯得有些多于,因為assign_op和詞法單元是一對一的,也就是assign_op只對應了:=,所以額外信息(屬性)就顯得多余的了

  詞法錯誤:

  詞法分析器是很難(有些錯誤還是可以檢測)檢測錯誤的,因為詞法分析器的目的是產生詞法記號流,它沒有能力去分析程序結構,因此無法檢測到和程序結構有關的錯誤,比如:

  fi(a == b)

  詞法分析器不會找到這個錯誤,它認為 fi 是一個標識符,而不是一個關鍵字,只有在后面的階段中,這個錯誤才會被發現,這是一個與程序結構有關的錯誤

  詞法分析器,只能檢測到詞法單元上的問題,比如 12.ab ,作為一個詞法單元,卻不沒有對應的模式,那么就是產生一個錯誤。

  2. 正規式:

  前面說過模式是一種規則,為了使用,我們需要一種規范的方式來表達模式,這就是正規式

  1) 串和語言

  字符類(又叫字母表):關于字符的有限集合

  串:字符類上字符的有窮序列,串這個概念,具體來說是,某個字符類上的串

  串的長度:串中字符的個數,比如串 s = abc ,那么串的長度為3,用|s|表示串的長度

  空串:用 ε 表示

  語言:某字符類上的串的集合,屬于語言的串,成為語言的句子或字

  比如:{abc, a}這就是一個語言,abc和a就是句子。另外空集也是屬于語言

?

?

連接:x是串,y是串,x和y連接,結果就是 xy 這個串。假如 x 是串,x^3為 xxx。對于 x^n (n>=0),x^0 = ε

  語言的運算(假定L和M是語言):

  1. L U M = {s|s屬于L或者M},例如:

  L={1,2} M={3,4} 那么 L U M = {1,2,3,4}

  2. LM = {st|s屬于L且t屬于M},例如:

  L={a,b} M={1,2} 那么 LM = {a1,a2,b1,b2}  ML={1a,1b,2a,2b}

  3. L^n = LLL...LLL (n個L),例如:

  L={a,b} 那么 L^3 = {aaa,aab,aba,abb,baa,bab,bbb,bba}

  注意 n 可以為0,L^0 = {ε}

  4. L* = L^0 U L^1 U L^2 U L^3 U ...

  L*表示,語言L中,所有的句子(串)以任意數目任意順序組成的句子的集合,包括 ε,例如:

  {a,b}* = {ε,a,b,ab,ba,aab,aba,baa,bba,bab,abb,aaa,bbb...}

  L*叫做L的閉包

  5. L+ = L^1 U L^2 U L^3 U...

  L+表示,語言L中,所有的句子(串)以任意數目任意順序組成的句子的集合,但是不包括 ε

  L+中的句子和 L*中的句子相比少一個 ε

  那么,我們通過上面的知識就可以表示一個標識符了,我們知道一般語言規定標識符是由字母開頭,后接若干個字母或數字,我們可以這樣來表示: L={a-z A-Z} N={0-9},那么標識符就是 L(L U N)*

  2) 正規式

  正規式又叫正規表達式,正規式是模式得一種規范的表達形式,正規式描述了一個集合,這個集合是由串組成的,其實這個集合就是我們前面說過的語言,不過這里大家喜歡使用正規集這個術語。正規式 r 表示正規集L(r)

  正規式的運算:

  1. 閉包運算,運算優先級最高,(r)* 表示 (L(r))*

  2. 連接運算,運算優先集合低于閉包,(r)(s) 表示 (L(r))(L(s))

  3. 或運算,運算優先集合最低,(r) | (s) 表示 (L(r)) U (L(s))

  例如:

  a | b 表示集合(語言,正規集) {a,b}

  (a | b)(a | b) 表示集合(語言,正規集) {aa,ab,ba,bb}

  a* 表示由一切a字符組成的集合(語言,正規集),包括 ε

  (a | b) 表示由a,b組成的集合(語言,正規集),包括 ε

  等價的正規式:(a | b) = (b | a)

  正規式的代數性質:


1. r|s = s|r2. r|(s|t) = (r|s)|t3. (rs)t = r(st)4. r(s|t) = rs|rt5. εr = r6. r** = r*7. r* = (r|ε)*
  注意,rs != sr 因為連接運算是有順序的,記住并理解2個最基本的運算:a|b表示{a,b},ab表示{ab}

  3. 正規定義

  我們可以使用 名字 -> 正規式這種表示,來說明一個等價的代替,比如:


dight -> 0|1|2|3|4|5|6|7|8|9
  這里,我們就可以使用名字 digit 來代替后面的正規表達式

  我們可以對某個串集進行正規定義,比如我們對標識符集合進行正規定義:


letter -> A|B|...|Z|a|b|...|zdight -> 0|1|2|3|4|5|6|7|8|9id -> letter(letter|dight)*
  請通過上面的例子理解正規定義。

  在我們表達正規表達式的時候,可以使用一些符號使得表達簡化

  1) + ,表示一個或者多個實力,比如,a+ 表示 {a,aa,aaa,aaaa,...}。區別一下*,他們的關系是這里 r+ = r* | ε

  2) 字符組,[abc]表示a|b|c,還可以這樣表示[a-zA-Z]表示字母表中的字符

  4. 狀態轉換圖

  狀態轉換圖是對詞法分析器進行分析過程的描述,我們看一個判斷關系運算的狀態轉化圖:

?

  1) 圖中圓圈表示狀態

  2) 箭頭叫做邊。X狀態的邊,一般指的是由X狀態出發,指向其他狀態的邊

  3) 邊上的符號叫做標記

  如何來使用這個圖?假定輸入字符串是 <= ,那么識別開始時,發現 < 和狀態0與狀態1間的邊上的標記一樣,那么就進入1狀態,下一個輸入字符為=,將進入2狀態,識別結束,返回二元組<relop,LE>

  上圖中2,3,4,5,7,8狀態,他們表示識別了一個關系運算符,這個狀態叫做接受狀態

狀態4上面有一個*,表示說,輸入指針需要回移。所謂的輸入指針,就是指向輸入字符串中現在被讀入的字符的位置,4狀態會多讀取一個字符,所以需要回移,也就是要注意的是,識別完成之后,輸入指針指向的是被識別對象的最后一個字符,而不是待識別對象的第一個字符,這樣的規定在實現詞法分析器時,是有一定的意義,舉例說明:

  輸入字符串為: a>b

  識別的時候,從>開始,讀入下一個字符b時,進入4狀態,這個時候,輸入指針指向b,這時候需要回移

  我們在需要回移的狀態上加一個*

  每個狀態后面有一個return(relop,XX)這個是狀態的行為,這里具體來說就是返回一個二元組的行為,詞法分析器分析的結果就是得到二元組(詞法記號和屬性的二元組),這個二元組可以表示一個特定的字符串。其實上面的*,也是表示行為,也就是輸入指針回移的行為,我們可以看見,只有在接受狀態才會有行為出現

  對一門典型的語言來說狀態可能有幾百個

  5. 如何編寫一個詞法分析器

  1) 根據需要寫出正規定義

  2) 根據正規定義畫出轉換圖

  3) 根據轉換圖寫出詞法分析器

  這里詳細討論面向過程的語言來實現一個詞法分析器(比如c語言),并且主要討論的是第3步

  1) 我們需要一個 nextchar() 函數,取得緩存中下一個等待分析的字符,這個函數完成年2個任務

  1.       讓輸入指針向前移動一位

  2.       返回輸入指針指向的字符

  2) 定義一個變量 token_beginning,在每個狀態轉換圖開始的時候,記錄輸入指針的位置,定義forward變量作為輸入指針

  3) 狀態轉換圖被實現成為代碼之后,每個狀態都有屬于自己的一塊代碼,這些代碼按順序完成以下工作:

  1.       讀取一個字符,通過nextchar()函數

  2.       讀取的字符(標志),如果它和當前狀態的邊上的標記相同,那么狀態將轉換到邊所指向的狀態,具體實現只需要一個語句就是 state = xxx(xxx為目標狀態);如果當前狀態的所有邊的標記和這個讀取字符不一樣,那么表示沒有找到token(詞法記號),這時候需要調用 fail() 函數

  3.       fail() 函數完成這樣的功能:a.指針回移,完成 forward = token_beginning 的操作 b.找到適當的開始狀態(也就是尋找另外一個轉換圖的開始狀態)。假定所有的轉換圖都被嘗試過,并且無法匹配,這時候會調用一個發現錯誤的小程序,來報告錯誤

  4.       請不要隨意添加行為到各個狀態所持有的代碼中,應該以轉換圖中表示的行為為準

  4) 定義一個全局變量 lexical_value,用于保存一個指針,這個指針由 install_id() 和 install_num() 兩個函數中的一個返回

  5) 定義兩個整形變量 start,state,分別表示一個轉換圖的開始狀態和當前的狀態

  6) nexttoken(),這是詞法分析器的主程序,可以說,我們通過調用nexttoken()就完成了詞法分析,這個函數一定是這樣的格式:


while(1){   switch(state){   case xx:      ...   case yy:      ...   default:      ...   }}
  關于詳細的設計這里就不說了,舉例說明一個轉換圖如何轉換成為程序:

?

  這是一個識別浮點數的例子,看下面的代碼:


#include <stdio.h>#include <ctype.h>#include <string.h>char *nexttoken();char nextchar();void next();void back();char* gettoken();char cbuf[]="12.3*********klj12.2e2jj778";int forward = -1; int main(){    while(1){        printf("%s\n",nexttoken());        if(forward >= strlen(cbuf)-1){            getchar();            return 0;        }    }}int state;int start;char* nexttoken(){    char c;    state = 12;    while(1){        switch(state){        case 12:            c = nextchar();            start = forward;            if(isdigit(c)){                state = 13;            }else{                next();            }            break;        case 13:            c = nextchar();            if(isdigit(c))                state = 13;            else if(c == 'e'||c == 'E')                 state = 16;            else if(c == '.')                state = 14;            else                state = 19;            break;        case 14:            c = nextchar();            if(isdigit(c))                state = 15;            break;        case 15:            c = nextchar();            if(isdigit(c))                state = 15;            else if(c == 'e'|| c == 'E')                state = 16;            else                state = 19;            break;        case 16:            c = nextchar();            if(isdigit(c))                state = 18;            else if(c == '+' || c == '-')                state = 17;            break;        case 17:            c = nextchar();            if(isdigit(c))                state = 18;            break;        case 18:            c = nextchar();            if(isdigit(c))                state = 18;            else                state = 19;            break;        case 19:            back();            return gettoken();        }    }}char nextchar(){    forward ++;    return cbuf[forward];}void back(){    forward --;}void next(){    forward ++;}char token_buf[128];char* gettoken(){    int i,j=0;    for(i = start; i <= forward; i ++){        token_buf[j++] = cbuf[i];    }    token_buf[j] = '\0';    return token_buf;}


?

?

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的词法分析(1)---词法分析的有关概念以及转换图的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。