導航:首頁 > 源碼編譯 > 編譯原理實驗二心得體會

編譯原理實驗二心得體會

發布時間:2024-01-17 12:22:22

編譯原理筆記9:語法分析樹、語法樹、二義性的消除

語法分析樹和語法樹不是一種東西 。習慣上,我們把前者叫做「具體語法樹」,其能夠體現推導的過程;後者叫做「抽象語法樹」,其不體現過程,只關心最後的結果。

語法分析樹是語言推導過程的圖形化表示方法。這種表示方法反映了語言的實質以及語言的推導過程。

定義:對於 CFG G 的句型,分析樹被定義為具有下述性質的一棵樹:

推導,有最左推導和最右推導,這兩種推導方式在推導過程中的分析樹可能不同,但因最終得到的句子是相同的,所以最終的分析樹是一樣的。

分析樹能反映句型的推導過程,也能反映句型的結構。然而實際上,我們往往不關心推導的過程,而只關心推導的結果。因此,我們要對 分析樹 進行改造,得到 語法樹 。語法樹中全是終結符,沒有非終結符。而且語法樹中沒有括弧

定義:

說白了,語法樹這玩意,就一句話: 葉子全是操作數,內部全是操作符 ,樹里沒有非終結符也不能有括弧。

語法樹要表達的東西,是操作符(運算)作用於操作數(運算對象)

舉倆例子吧:

【例】: -(id+id) 的語法樹:

【例】:-id+id 的語法樹:

顯然,我們從上面這兩個語法樹中,直接就能觀察出來它們的運算順序。

【例】:句型 if C then s1 else s2

二義性問題:一個句子可能對應多於一棵語法樹。

【例】: 設文法 G: E → E+E | E*E | (E) | -E | id

則,句子 id+id*id、id+id+id 可能的分析樹有:

在該例中,雖然 id+id+id 的 「+」 的結合性無論左右都不會影響結果。但萬一,萬一「+」的含義變成了「減法」,那麼左結合和右結合就會引起很大的問題了。

我們在這里講的「二義性」的「義」並非語義——我們現在在學習的內容是「語法分析器」,尚未到需要研究語言背後含義的階段。

我們現在講的「二義性」指的是一個句子對應多種分析樹。

二義性的體現,是文法對同一句子有不止一棵分析樹。這種問題由【句子產生過程中的某些推導有多於一種選擇】引起。懸空 else 問題就可以很好地體現這種【超過一種選擇】帶來的二義性問題,示例如下。

看下面這么個例子。。

(其實,我感覺這個其實比較像是「說話大喘氣」帶來的理解歧義問題。。。)上面的產生式中並沒體現出來該咋算分一塊,所以兩種完全不同的句子結構都是合法的。

二義性問題是有救的,大概有以下這三種辦法:

這些辦法的核心,其實都是將優先順序和結合性說明白。

核心:把優先順序和結合性說明白

既然要說明白,那就不能讓一個非終結符可以直接在當次推導中能推出會帶來優先順序和結合性歧義的東西。(對分析樹的一個內部節點,不會有出現在其下面的分支是相同的非終結符的情況。如果有得選,那就有得歧義了。沒得選才能確定地一路走到黑)

改寫為非二義文法的二義文法大概有下面這幾個特點:

改寫的關鍵步驟:

【例】改寫下面的二義文法為非二義文法。圖右側是要達成的優先順序和結合性

改寫的核心其實就兩句話:

所以能夠得到非終結符與運算的對應關系(因為不同的運算有不同的優先順序,我們想要引入多個優先順序就要引入多個新的非終結符。這樣每個非終結符就可以負責一個優先順序的運算符號,也就是說新的非終結符是與運算有關系的了。因此這里搞出來了「對應關系」四個字)如下:

優先順序由低到高分別是 +、 、-,而距離開始符號越近,優先順序越低。因此在這里的排序也可以+ -順序。每個符號對應一層的非終結符。根據所需要的結合性,則可確定是左遞歸還是右遞歸,以確定新的產生式長什麼樣子

【例】:規定優先順序和結合性,寫出改寫的非二義文法

我們已經掌握了一種叫做【改寫】的工具,能讓我們消除二義性。接下來我們就要用這個工具來嘗試搞搞懸空 else 問題!

懸空 else 問題出現的原因是 then 數量多於 else,讓 else 有多個可以結合的 then。在二義文法中,由於選哪兩個 then、else 配對都可以,故會引起出現二義的情況。在這里,我們規定 else 右結合,即與左邊最靠近的 then 結合。

為改寫此文法,可以將 S 分為完全匹配(MS)和不完全匹配(UMS)兩類。在 MS 中體現 then、else 個數相等即匹配且右結合;在UMS 中 then、else 不匹配,體現 else 右結合。

【例】:用改寫後的文法寫一個條件語句

經過檢查,無法再根據文法寫出其他分析樹,故已經消除了二義性

雖然二義文法會導致二義性,但是其並非一無是處。其有兩個顯著的優點:

在 Yacc 中,我們可以直接指定優先順序、結合性而無需自己重寫文法。

left 表示左結合,right 表示右結合。越往下的算符優先順序越高。

嗯就這么簡單。。。

我們其實可以把語言本身定義成沒有優先順序和結合性的。。然後所有的優先、結合都交由括弧進行控制,哪個先算就加括弧。把一個過程的結束用明確的標志標記出來。

比如在 Ada 中:

在 Pascal 中,給表達式加括弧:

Ⅱ 編譯原理實驗報告

#include<stdio.h>
void main()
{

int m=0,n=0,n1=0,n2=0,n3=0,zg,fzg,flag;
int bz[7]=;/*狀態改變控制,1 表示可以改變狀態zt值,0 表示不可以*/
int zt[7]=;/*狀態值,2表示未定狀態,1表示 是,0表示 否*/

char temp[100]="\0";/*用於求first集*/
char z[7];/*非總結符*/
char z1[7];/*總結符*/
char z2[7]="\0";/*gs[]文法中出現的標記個數的輔助字元 01234*/
char gs[100]="\0";/*文法,按順序排成字元串*/

printf("請依次輸入非終結符(不超過7個):");
gets(z);
while(z[m]!='\0')

fzg=m;//zg是非終結符個數

while(n<m)
//生成01234輔助字元
printf("您輸入了:");
puts(z);
fflush(stdin);

printf("請依次輸入終結符(不超過7個):");
gets(z1);
while(z1[n1]!='\0')

zg=n1;
printf("您輸入了:");
puts(z1);
fflush(stdin);

printf("按照正確格式輸入所有文法(總長度不超過100格式如下):");
printf("如果文法為(字元'k'表示空):\n");
printf("S-->AB S-->bC A-->k A-->b\n");
printf("輸入:0SAB0SbC1Ak1Ab\n");
printf(" (注:數字01234表示第一二三四個非終結符)\n");

gets(gs);
fflush(stdin);
printf("您輸入了:");
puts(gs);
m=0;
//對於輸入文法字元串的轉換,將每個文法式左部去除
while(gs[m]!='\0')
{
n=m;
if(gs[m]>='0'&&gs[m]<='9')
{
m++;
while(gs[m]!='\0')
{
gs[m]=gs[m+1];
m++;
}
//gs[m-1]='\0';
}
m=++n;
}

m=0;

//puts(gs);

/*情況一,直接判定是 形如: (A-->k) */
while(gs[m]!='\0')
{
if(gs[m]=='k')
{
zt[gs[m-1]-48]=1;
bz[gs[m-1]-48]=0;
}
m++;
}

/*情況二,直接判定--否 形如: (D-->aS ,D-->c) */
for(n=0;n<fzg;n++)
{
if(bz[n]==1)
{
m=0;
n2=0;
while(gs[m]!='\0')
{
if(z2[n]==gs[m])
{
if(gs[m+1]>=z1[0]&&gs[m+1]<=z1[n1-1])
zt[n]=0;
else //gs[m+1] 是非終結符n2做標記
}
//跳出循環,無法解決該情況,推到下面情況三
m++;
}
if(n2!=99) //完成所有掃描,未出現非終結符,得出結論zt[n]=0.bz[n]=0不允許再改變zt[n]
}
}

/*情況三,最終判定*/
do
{
flag=0;
for(n=0;n<fzg;n++)
{
if(bz[n]==1) //未得到判定
{ m=0;
while(gs[m]!='\0')
{
if(gs[m]==z2[n]) //判定gs[m]是輔助字元0123
{
m++;
while(gs[m]>='A'&&gs[m]<='Z')
{

n1=0;
for(n2=0;n2<fzg;n2++) //循環查找是gs[m]哪個非終結符
{
if(gs[m]==z[n2])
{
if(zt[n2]==1) //這個非終結符能推出空
zt[n]=1;
else if(bz[n2]==1) //這個非終結符 現在 不能推出空,但它的狀態可改即它最終結果還未判定

else
//設 m1 做標記供下一if參考
break; //找到gs[m]是哪個非終結符,for循環完成任務,可以結束
}

}
if(n1==99) break;
m++;
}
}
m++;
}
if(zt[n]==1) bz[n]=0;
if(bz[n]==0) flag=1;//對應for下的第一個if(zt[n]==2)
}

}
}while(flag);

printf("結果是:\n");

for(m=0;m<5;m++)
{
switch(zt[m])
{
case 0:printf("%c---否\n",z[m]);break;
case 1:printf("%c---是\n",z[m]);break;
case 2:printf("%c---未定\n",z[m]);break;
}

}
/*
puts(gs);
puts(zt);
puts(z);
puts(z1);
puts(z2);
printf("%d,,,%d",fzg,zg);
*/

//下面求first集
//下面求first集

for(n=0;n<fzg;n++)

m=0;n=0;n1=0;n2=0;
while(gs[n]>='0'&&gs[n]<='9')
{
for(;m<fzg;m++)
{
if(n2!=m)
n1=0; //m=n2用於第二次以後的for循環中還原上次m的值

if(gs[n]==z2[m])
{
while(gs[n+1]>'9')
{
if(n1==0)
//如果是第一個直接保存

//不是第一個,先與字元數組中其它字元比較,沒相同的才保存
else if(gs[n]>='a'&&gs[n]<='z'&&gs[n+1]>='A'&&gs[n+1]<='Z') //gs[n]是終結符 且 gs[n+1]是非終結符
;//什麼也不做,程序繼續n++,掃描下一個gs[n]

else
{
for(n3=0;n3<=n1;n3++)
{
if(temp[m*13+n3]==gs[n+1])
break;
}

if(n3>n1) //for循環結束是因為n3而不是break

}
n++;
}
break; //break位於if(gs[n]==z2[m]),對於gs[n]已找到z2[m]完成任務跳出for循環
}
}
n2=m; //存放該for循環中m的值
n++;
}
//進一步處理集除去非終結符
m=0;n=0;n1=0;n2=0;
for(m=0;m<fzg;m++)
{
if(flag!=m)
n1=0; //m=flag用於第二次以後的for循環中還原上次m的值

while(temp[m*13+n1]!='\0')
{
while(temp[m*13+n1]>='A'&&temp[m*13+n1]<='Z') //搜索非終結符
{
for(n=0;n<fzg;n++) //確定是哪個非終結符
{if(temp[m*13+n1]==z[n])
break;
}
while(temp[m*13+n1]!='\0') //從temp[n*13+n1]開始每個字元依次往前移動一

n1--;
while(temp[n*13+n2]!='\0') //把z[n]對應的first加入temp[m*13+n1]這個first中,每個字元依次加在最後
{
for(n3=0;n3<n1;n3++) //循環判定是否有相同的字元
{
if(temp[m*13+n3]==temp[n*13+n2])
break;
}
if(temp[n*13+n2]=='k'&&zt[m]==0) //那些不能推出 空,但是因為要加入 其他非終結符的first集 而可能含有 空
n2++;
else if(n3>=n1) //for循環結束是因為n3而不是break ,即無相同字元

else n2++;
}

n1=0;
n2=0;
}

n1++;
}
flag=m; //存放該for循環中m的值
}

//非終結符的first集輸出
m=0;n1=0;
for(m=0;m<fzg;m++)
{
n1=0;
printf("非終結符 %c 的first集是: ",z[m]);
while(temp[m*13+n1]!='\0')
{
printf("%c",temp[m*13+n1]);
n1++;
}
printf("\n");
}

}

Ⅲ 編譯原理問題

你是長春理工大學的吧?
前幾天我們剛寫完,一樣的.

Ⅳ 編譯原理實驗二 LL(1)分析法

通過完成預測分析法的語法分析程序,了解預測分析法和遞歸子程序法的區別和聯系。使學生了解語法分析的功能,掌握語法分析程序設計的原理和構造方法,訓練學生掌握開發應用程序的基本方法。有利於提高學生的專業素質,為培養適應社會多方面需要的能力。

根據某一文法編制調試 LL(1)分析程序,以便對任意輸入的符號串進行分析。
構造預測分析表,並利用分析表和一個棧來實現對上述程序設計語言的分析程序。
分析法的功能是利用LL(1)控製程序根據顯示棧棧頂內容、向前看符號以及LL(1)分析表,對輸入符號串自上而下的分析過程。

對文法 的句子進行不含回溯的自上向下語法分析的充分必要條件是:
(1)文法不含左遞歸;
(2)對於文法中的每一個非終結符 的各個產生式的候選首符集兩兩不相交,即,若

Follow集合構造:
對於文法 的每個非終結符 構造 的演算法是,連續使用下面的規則,直至每個 不再增大為止:

僅給出核心部分
(1) GrammerSymbol.java

(2) GrammerSymbols.java

(3) Grammer.java

(4) LL1Grammer.java

Ⅳ 【編譯原理】第二章:語言和文法



上述文法 表示,該文法由終結符集合 ,非終結符集合 ,產生式集合 ,以及開始符號 構成。
而產生式 表示,一個表達式(Expression) ,可以由一個標識符(Identifier) 、或者兩個表達式由加號 或乘號 連接、或者另一個表達式用括弧包裹( )構成。

約定 :在不引起歧義的情況下,可以只寫產生式。如以上文法可以簡寫為:

產生式

可以簡寫為:

如上例中,

可以簡寫為:

給定文法 ,如果有 ,那麼可以將符號串 重寫 為 ,記作 ,這個過程稱為 推導
如上例中, 可以推導出 或 或 等等。

如果 ,
可以記作 ,則稱為 經過n步推導出 ,記作 。

推導的反過程稱為 歸約

如果 ,則稱 是 的一個 句型(sentential form )。

由文法 的開始符號 推導出的所有句子構成的集合稱為 文法G生成的語言 ,記作 。
即:


文法

表示什麼呢?
代表小寫字母;
代表數字;
表示若干個字母和數字構成的字元串;
說明 是一個字母、或者是字母開頭的字元串。
那麼這個文法表示的即是,以字母開頭的、非空的字元串,即標識符的構成方式。

並、連接、冪、克林閉包、正閉包。
如上例表示為:

中必須包含一個 非終結符


產生式一般形式:
即上式中只有當上下文滿足 與 時,才能進行從 到 的推導。

上下文有關文法不包含空產生式( )。


產生式的一般形式:
即產生式左邊都是非終結符。

右線性文法
左線性文法
以上都成為正則文法。
即產生式的右側只能有一個終結符,且所有終結符只能在同一側。

例:(右線性文法)

以上文法滿足右線性文法。
以上文法生成一個以字母開頭的字母數字串(標識符)。
以上文法等價於 上下文無關文法

正則文法能描述程序設計語言中的多數單詞。

正則文法能描述程序設計語言中的多數單詞,但不能表示句子構造,所以用到最多的是CFG。

根節點 表示文法開始符號S;
內部節點 表示對產生式 的應用;該節點的標號是產生式左部,子節點從左到右表示了產生式的右部;
葉節點 (又稱邊緣)既可以是非終結符也可以是終結符。

給定一個句型,其分析樹的每一棵子樹的邊緣稱為該句型的一個 短語
如果子樹高度為2,那麼這棵子樹的邊緣稱為該句型的一個 直接短語

直接短語一定是某產生式的右部,但反之不一定。

如果一個文法可以為某個句子生成 多棵分析樹 ,則稱這個文法是 二義性的

二義性原因:多個if只有一個else;
消岐規則:每個else只與最近的if匹配。

閱讀全文

與編譯原理實驗二心得體會相關的資料

熱點內容
設備部門如何做好伺服器 瀏覽:847
androido下載 瀏覽:476
神奇高量戰法副圖源碼 瀏覽:828
匯編語言設計凱撒密碼加密器 瀏覽:390
主次梁加密是加在哪裡 瀏覽:662
模板匹配演算法matlab 瀏覽:823
外地程序員去北京 瀏覽:22
安卓機換蘋果12如何轉移數據 瀏覽:418
互聯網ntp伺服器地址及埠 瀏覽:613
pdf到word轉換器 瀏覽:267
飛行解壓素材 瀏覽:498
51單片機指令用背嗎 瀏覽:936
unityai演算法 瀏覽:834
我的世界ice伺服器如何打開pvp 瀏覽:975
c語言編程如何做標記 瀏覽:884
python數據分析實戰pdf 瀏覽:985
u盤插入文件夾 瀏覽:918
華為amd雲伺服器 瀏覽:497
漢化編程卡是什麼意思 瀏覽:128
python學習pdf 瀏覽:316