符號表編譯原理
① 編譯原理中的詞法分析器的輸入與輸出是什麼
編譯原理中的詞法分析器的輸入是源程序,輸出是識別的記號流。
詞法分析器編制一個讀單詞的程序,從輸入的源程序中,識別出各個具有獨立意義的單詞,即基本保留字、標識符、常數、運算符和分隔符五大類。並依次輸出各個單詞的內部編碼及單詞符號自身值。(遇到錯誤時可顯示「Error」,然後跳過錯誤部分繼續顯示)。
(1)符號表編譯原理擴展閱讀
詞法分析器的作用:
1、與符號表進行交互,存儲和讀取符號表中的標識符的信息。
2、讀入源程序的輸入字元,將他們組成詞素,生成並輸出一個詞法單元序列,每個詞法單元序列對應一個於一個詞素。
3、過濾掉程序中的注釋和空白。
4、將編譯器生成的錯誤消息與源程序的位置聯系起。
② 編譯原理與什麼課程有聯系
與人工智慧、機器學習、操作系統、編譯器等都有關系,屬於計算機專業的核心課程,需要扎實掌握;
③ 編譯原理對符號表進行操作有哪些
//----------------------------符號表---------------------------------------
//預定義
struct snode;
struct stable;
//符號表結點
struct snode
{
string text; //符號名稱
string type; //符號類型
union {int ival;double rval;}value; //值------------
int offset; //偏移量
snode *nextn; //指向下一個節點
stable *header; //指向下屬符號表的表頭
};
//符號表表頭
struct stable
{
stable *previous; //指向先前創建的符號表表頭
snode *firstnode; //指向第一個結點
stable *ifnoelements;//如果此表為空,則用它指向下一個表頭
};
//當前表頭
stable *currtab;
//建立新表,返回表頭指針
//參數:當前的節點的表頭
stable *mktable(stable *previous)
{
stable *newtable =new stable;
newtable->previous=previous;
newtable->ifnoelements=0;
newtable->firstnode=0;
if(previous->firstnode==0)
{
previous->ifnoelements=newtable;
}
else
{
snode* ininode=previous->firstnode;
while(ininode->nextn!=0)
{
ininode=ininode->nextn;
}
ininode->header=newtable;
}
currtab=newtable;
return newtable;
}
//在node指向的符號表中為text建立一個新表項,返回新建立的結點
//參數:node為當前的節點的表頭,text名稱,type類型,offset偏移
snode *enter(stable *table,string text,string type,int offset,double value)
{
//創建節點
snode *newnode = new snode;
newnode->text=text;
newnode->type=type;
newnode->offset=offset;
newnode->nextn=0;
newnode->header=0;
if(type=="int")
{
newnode->value.ival=value;
}
else if(type=="real")
{
newnode->value.rval=value;
}
//判斷此表是否無元素
if(currtab->firstnode==0)
{
currtab->firstnode=newnode;
currtab->ifnoelements=0;
}
else
{
snode* addnode=currtab->firstnode;
while(addnode->nextn!=0)
{
addnode=addnode->nextn;
}
addnode->nextn=newnode;
}
return newnode;
}
//初始化符號表,返回表頭節點
void inittab()
{
stable *initable = new stable;
initable->firstnode=0;
initable->previous=0;
initable->ifnoelements=0;
currtab=initable;
}
//查找指針,表示結果
snode *searchresult;
//查找變數,返回指向該變數的指針
//查找變數,返回指向該變數的指針
snode* search(string name)
{
//檢查表是否空
bool isempty=true;
stable* checktab=currtab;
if(checktab->firstnode!=0)
{isempty=false;}
while(checktab->previous!=0)
{
if(checktab->firstnode!=0)
{isempty=false;}
checktab=checktab->previous;
}
if(checktab->firstnode!=0)
{isempty=false;}
if(isempty)
{
return 0;
}
snode* lastnode;
if(currtab->firstnode==0)
{
//移到非空的表頭
stable* notnullhead=currtab;
while(notnullhead->firstnode==0)
{
notnullhead=notnullhead->previous;
}
snode* tmpnode=notnullhead->firstnode;
//移到最後的元素
while(tmpnode->nextn!=0)
{
tmpnode=tmpnode->nextn;
}
lastnode=tmpnode;
}
else
{
lastnode=currtab->firstnode;
while(lastnode->nextn!=0)
{
lastnode=lastnode->nextn;
}
}
//移到表頭
stable* fronttab=currtab;
while(fronttab->previous!=0)
{
fronttab=fronttab->previous;
}
snode* nownode=0;
while(nownode!=lastnode)
{
while(fronttab->ifnoelements!=0)
{
fronttab=fronttab->ifnoelements;
}
nownode=fronttab->firstnode;
while(nownode->nextn!=0)
{
if(nownode->text==name)
{
searchresult=nownode;
return searchresult;
}
nownode=nownode->nextn;
}
if(nownode->text==name)
{
searchresult=nownode;
return searchresult;
}
fronttab=nownode->header;
}
if(nownode->text==name)
{
searchresult=nownode;
return searchresult;
}
return 0;
}
//消毀符號表
void delNode()
{
//more codes here......
}
④ 編譯原理全部的名詞解釋
書上有別那麼懶!.
編譯過程的六個階段:詞法分析,語法分析,語義分析,中間代碼生成,代碼優化,目標代碼生成
解釋程序:把某種語言的源程序轉換成等價的另一種語言程序——目標語言程序,然後再執行目標程序.解釋方式是接受某高級語言的一個語句輸入,進行解釋並控制計算機執行,馬上得到這句的執行結果,然後再接受下一句.
編譯程序:就是指這樣一種程序,通過它能夠將用高級語言編寫的源程序轉換成與之在邏輯上等價的低級語言形式的目標程序(機器語言程序或匯編語言程序).
解釋程序和編譯程序的根本區別:是否生成目標代碼
句子的二義性(這里的二義性是指語法結構上的.):文法G[S]的一個句子如果能找到兩種不同的最左推導(或最右推導),或者存在兩棵不同的語法樹,則稱這個句子是二義性的.
文法的二義性:一個文法如果包含二義性的句子,則這個文法是二義文法,否則是無二義文法.
LL(1)的含義:(LL(1)文法是無二義的; LL(1)文法不含左遞歸)
第1個L:從左到右掃描輸入串 第2個L:生成的是最左推導
1 :向右看1個輸入符號便可決定選擇哪個產生式
某些非LL(1)文法到LL(1)文法的等價變換: 1. 提取公因子 2. 消除左遞歸
文法符號的屬性:單詞的含義,即與文法符號相關的一些信息.如,類型、值、存儲地址等.
一個屬性文法(attribute grammar)是一個三元組A=(G, V, F)
G:上下文無關文法.
V:屬性的有窮集.每個屬性與文法的一個終結符或非終結符相連.屬性與變數一樣,可以進行計算和傳遞.
F:關於屬性的斷言或謂詞(一組屬性的計算規則)的有窮集.斷言或語義規則與一個產生式相聯,只引用該產生式左端或右端的終結符或非終結符相聯的屬性.
綜合屬性:若產生式左部的單非終結符A的屬性值由右部各非終結符的屬性值決定,則A的屬性稱為綜合屬
繼承屬性:若產生式右部符號B的屬性值是根據左部非終結符的屬性值或者右部其它符號的屬性值決定的,則B的屬性為繼承屬性.
(1)非終結符既可有綜合屬性也可有繼承屬性,但文法開始符號沒有繼承屬性.
(2) 終結符只有綜合屬性,沒有繼承屬性,它們由詞法程序提供.
在計算時: 綜合屬性沿屬性語法樹向上傳遞;繼承屬性沿屬性語法樹向下傳遞.
語法制導翻譯:是指在語法分析過程中,完成附加在所使用的產生式上的語義規則描述的動作.
語法制導翻譯實現:對單詞符號串進行語法分析,構造語法分析樹,然後根據需要構造屬性依賴圖,遍歷語法樹並在語法樹的各結點處按語義規則進行計算.
中間代碼(中間語言)
1、是復雜性介於源程序語言和機器語言的一種表示形式.
2、一般,快速編譯程序直接生成目標代碼.
3、為了使編譯程序結構在邏輯上更為簡單明確,常採用中間代碼,這樣可以將與機器相關的某些實現細節置於代碼生成階段仔細處理,並且可以在中間代碼一級進行優化工作,使得代碼優化比較容易實現.
何謂中間代碼:源程序的一種內部表示,不依賴目標機的結構,易於代碼的機械生成.
為何要轉換成中間代碼:(1)邏輯結構清楚;利於不同目標機上實現同一種語言.
(2)便於移植,便於修改,便於進行與機器無關的優化.
中間代碼的幾種形式:逆波蘭記號 ,三元式和樹形表示 ,四元式
符號表的一般形式:一張符號表的的組成包括兩項,即名字欄和信息欄.
信息欄包含許多子欄和標志位,用來記錄相應名字和種種不同屬性,名字欄也稱主欄.主欄的內容稱為關鍵字(key word).
符號表的功能:(1)收集符號屬性 (2) 上下文語義的合法性檢查的依據: 檢查標識符屬性在上下文中的一致性和合法性.(3)作為目標代碼生成階段地址分配的依據
符號的主要屬性及作用:
1. 符號名 2. 符號的類型 (整型、實型、字元串型等))3. 符號的存儲類別(公共、私有)
4. 符號的作用域及可視性 (全局、局部) 5. 符號變數的存儲分配信息 (靜態存儲區、動態存儲區)
存儲分配方案策略:靜態存儲分配;動態存儲分配:棧式、 堆式.
靜態存儲分配
1、基本策略
在編譯時就安排好目標程序運行時的全部數據空間,並能確定每個數據項的單元地址.
2、適用的分配對象:子程序的目標代碼段;全局數據目標(全局變數)
3、靜態存儲分配的要求:不允許遞歸調用,不含有可變數組.
FORTRAN程序是段結構,不允許遞歸,數據名大小、性質固定. 是典型的靜態分配
動態存儲分配
1、如果一個程序設計語言允許遞歸過程、可變數組或允許用戶自由申請和釋放空間,那麼,就需要採用動態存儲管理技術.
2、兩種動態存儲分配方式:棧式,堆式
棧式動態存儲分配
分配策略:將整個程序的數據空間設計為一個棧.
【例】在具有遞歸結構的語言程序中,每當調用一個過程時,它所需的數據空間就分配在棧頂,每當過程工作結束時就釋放這部分空間.
過程所需的數據空間包括兩部分
一部分是生存期在本過程這次活動中的數據對象.如局部變數、參數單元、臨時變數等;
另一部分則是用以管理過程活動的記錄信息(連接數據).
活動記錄(AR)
一個過程的一次執行所需要的信息使用一個連續的存儲區來管理,這個區 (塊)叫做一個活動記錄.
構成
1、臨時工作單元;2、局部變數;3、機器狀態信息;4、存取鏈;
5、控制鏈;6、實參;7、返回地址
什麼是代碼優化
所謂優化,就是對代碼進行等價變換,使得變換後的代碼運行結果與變換前代碼運行結果相同,而運行速度加快或佔用存儲空間減少.
優化原則:等價原則:經過優化後不應改變程序運行的結果.
有效原則:使優化後所產生的目標代碼運行時間較短,佔用的存儲空間較小.
合算原則:以盡可能低的代價取得較好的優化效果.
常見的優化技術
(1) 刪除多餘運算(刪除公共子表達式) (2) 代碼外提 +刪除歸納變數+ (3)強度削弱; (4)變換循環控制條件 (5)合並已知量與復寫傳播 (6)刪除無用賦值
基本塊定義
程序中只有一個入口和一個出口的一段順序執行的語句序列,稱為程序的一個基本塊.
給我分數啊.
⑤ 正在學習編譯原理,語法圖應該用什麼軟體畫會比較簡單一點
一般用YACC畫,不過建議用GOLD PARSER。畫起來最快,不要編程。
步驟:
1)寫出正確的文法。
2)在測試窗口中輸入要分析的字元串。
⑥ 編譯原理中pl/0符號表中oddsym是代表什麼符號
判斷一個表達式的結果是否為奇數,若為奇數返回真
⑦ 編譯程序包括哪幾個主要組成部分
數據結構 分析和綜合時所用的主要數據結構,包括符號表、常數表和中間語言程序。符號表由源程序中所用的標識符連同它們的屬性組成,其中屬性包括種類(如變數、數組、結構、函數、過程等)、類型(如整型、實型、字元串、復型、標號等),以及目標程序所需的其他信息。常數表由源程序中用的常數組成,其中包括常數的機內表示,以及分配給它們的目標程序地址。中間語言程序是將源程序翻譯為目標程序前引入的一種中間形式的程序,其表示形式的選擇取決於編譯程序以後如何使用和加工它。常用的中間語言形式有波蘭表示、三元組、四元組以及間接三元組等。
分析部分 源程序的分析是經過詞法分析、語法分析和語義分析三個步驟實現的。詞法分析由詞法分析程序(又稱為掃描程序)完成,其任務是識別單詞(即標識符、常數、保留字,以及各種運算符、標點符號等)、造符號表和常數表,以及將源程序換碼為編譯程序易於分析和加工的內部形式。語法分析程序是編譯程序的核心部分,其主要任務是根據語言的語法規則,檢查源程序是否合乎語法。如不合乎語法,則輸出語法出錯信息;如合乎語法,則分解源程序的語法結構,構造中間語言形式的內部程序。語法分析的目的是掌握單詞是怎樣組成語句的,以及語句又是如何組成程序的。語義分析程序是進一步檢查合法程序結構的語義正確性,其目的是保證標識符和常數的正確使用,把必要的信息收集和保存到符號表或中間語言程序中,並進行相應的語義處理。
綜合部分 綜合階段必須根據符號表和中間語言程序產生出目標程序,其主要工作包括代碼優化、存儲分配和代碼生成。代碼優化是通過重排和改變程序中的某些操作,以產生更加有效的目標程序。存儲分配的任務是為程序和數據分配運行時的存儲單元。代碼生成的主要任務是產生與中間語言程序符等價的目標程序,順序加工中間語言程序,並利用符號表和常數表中的信息生成一系列的匯編語言或機器語言指令。
結構編譯過程分為分析和綜合兩個部分,並進一步劃分為詞法分析、語法分析、 語義分析、 代碼優化、存儲分配和代碼生成等六個相繼的邏輯步驟。這六個步驟只表示編譯程序各部分之間的邏輯聯系,而不是時間關系。編譯過程既可以按照這六個邏輯步驟順序地執行,也可以按照平行互鎖方式去執行。在確定編譯程序的具體結構時,常常分若干遍實現。對於源程序或中間語言程序,從頭到尾掃視一次並實現所規定的工作稱作一遍。每一遍可以完成一個或相連幾個邏輯步驟的工作。例如,可以把詞法分析作為第一遍;語法分析和語義分析作為第二遍;代碼優化和存儲分配作為第三遍;代碼生成作為第四遍。反之,為了適應較小的存儲空間或提高目標程序質量,也可以把一個邏輯步驟的工作分為幾遍去執行。例如,代碼優化可劃分為代碼優化准備工作和實際代碼優化兩遍進行。
一個編譯程序是否分遍,以及如何分遍,根據具體情況而定。其判別標准可以是存儲容量的大小、源語言的繁簡、解題范圍的寬窄,以及設計、編制人員的多少等。分遍的好處是各遍功能獨立單純、相互聯系簡單、邏輯結構清晰、優化准備工作充分。缺點是各遍之中不可避免地要有些重復的部分,而且遍和遍之間要有交接工作,因之增加了編譯程序的長度和編譯時間。
一遍編譯程序是一種極端情況,整個編譯程序同時駐留在內存,彼此之間採用調用轉接方式連接在一起(圖2)。當語法分析程序需要新符號時,它就調用詞法分析程序;當它識別出某一語法結構時,它就調用語義分析程序。語義分析程序對識別出的結構進行語義檢查,並調用「存儲分配」和「代碼生成」程序生成相應的目標語言指令。
隨著程序設計語言在形式化、結構化、直觀化和智能化等方面的發展,作為實現相應語言功能的編譯程序,也正向自動程序設計的目標發展,以便提供理想的程序設計工具。
參考書目
陳火旺、錢家驊、孫永強編:《編譯原理》,國防工業出版社,北京,1980
⑧ token 編譯原理
token= '';
s=getchar();
getbe();
switch(s){
case 'a':
case 'b':
…
case 'z':
while(letter( )‖digit( ))
{
concatenation( );
getchar( );
}
retract( ); *掃描指針回退一個字元*/
c=reserve( );
if (c==0){
buildlist( ); /*將標識符登錄到符號表中*/
return(id,指向id的符號表入口指針);
}else{
return(保留字碼,null);
}
break;
case '0':
case '1':
…
case '9':
while (digit( )){
concatenation( );
getchar( );
}
retract();
buildlist( );/*將常數登錄到常數表中*/
return(num,num的常數表入口指針);
break;
case '+':
return('+',null);
break;
case '−':
return('−',null);
break;
case '*':
return('*',null);
break;
case '<':
getchar( );
if (character== '=')
return(relop,LE);
else{
retract();
return(relop,LT);
}
break;
case '=':
getchar( );
if(character== '=')
return (relop, EQ);
else{
retract( );
return('=', null);
}
break;
case ';':
return(';', null);
break;
default:
error( );