編譯原理實驗報告:語義分析及中間代碼生成

1. 實驗題目:語義分析及中間代碼生成

實驗目的

  1. 通過上機實習,加深對語法制導翻譯原理的理解,掌握將語法分析所識別的語法範疇變換爲某種中間代碼的語義翻譯方法。
  2. 掌握目前普遍採用的語義分析方法──語法制導翻譯技術。
  3. 給出PL/0文法規範,要求在語法分析程序中添加語義處理,對於語法正確的表達式,輸出其中間代碼;對於語法正確的算術表達式,輸出其計算值。

實驗內容

  已給PL/0語言文法,在實驗二或實驗三的表達式語法分析程序裏,添加語義處理部分,輸出表達式的中間代碼,用四元式序列表示。

實驗要求

  1. 語義分析對象重點考慮經過語法分析後已是正確的語法範疇,本實驗重點是語義子程序。
  2. 在實驗二或實驗三“語法分析器”的裏面添加PL/0語言“表達式”部分的語義處理,輸出表達式的中間代碼,計算表達式的語義值。
  3. 中間代碼用四元式序列表示。

輸入輸出

  1. PL/0算術表達式的語義計算:
    輸入:
      PL/0算術表達式,例如: 2 + 3 * 5作爲輸入。
    輸出:
      17
  2. PL/0表達式的中間代碼表示
    輸入:
      PL/0表達式,例如: a *(b +c)
    輸出:
      ( + b c t1 )
      ( * a t1 t2 )

2. 設計思想

  本次實驗我採用的遞歸下降分析器的設計。
  遞歸下降分析法的原理是利用函數之間的遞歸調用來模擬語法樹自上而下的構建過程。從根節點出發,自頂向下爲輸入串中尋找一個最左匹配序列,建立一棵語法樹。在不含左遞歸和每個非終結符的所有候選終結首字符集都兩兩不相交條件下,我們就可能構造出一個不帶回溯的自頂向下的分析程序,這個分析程序是由一組遞歸過程(或函數)組成的,每個過程(或函數)對應文法的而一個非終結符。

語法:
<表達式> -> [+|-]<項>{<加法運算符> <項>}
<項> -><因子>{<乘法運算符> <因子>}
<因子> -> <標識符>|<無符號整數>|(<表達式>)
<加法運算符> -> +|-
<乘法運算符> -> *|/

計算FIRST集:
FIRST(<表達式>)={ +, -, (, <標識符>, <無符號整數> }
FIRST(<因子>)={ <標識符>, <無符號整數>, ( }
FIRST(<項>)={ <標識符>, <無符號整數>, ( }
FIRST(<加法運算符>)={ +, - }
FIRST(<乘法運算符>)={ *, / }

計算FOLLOW集:
FOLLOW(<表達式>)={ ) }
FOLLOW (<項>)={ +,- }
FOLLOW (<因子>)={ *,/ }
FOLLOW (<加法運算符>)={ <標識符>, <無符號整數>, ( }
FOLLOW (<乘法運算符>)={ <標識符>, <無符號整數>, ( }

可以改成如下拓廣文法:
<表達式>-><項> | <表達式>+<項> | <表達式>-<項>
<項>-><因子> | <項>*<因子> | <項>/<因子>
<因子>->(<表達式>) | <標識符> | <無符號整數>

將語法消除左遞歸可得(用E代表<表達式>,T代表<項>,F代表<因子>)
E->TE’
E’->ɛ|+TE’|-TE’
T->FT’
T’->ɛ|*FT’|/FT’
F->ident|(E)|number

屬性文法:是在上下文無關文法的基礎上爲每個文法符號(終結符或非終結符)配備若干個相關的“值”(稱爲屬性)。
屬性:代表與文法符號相關的信息,和變量一樣,可以進行計算和傳遞。
綜合屬性
用於“自下而上”傳遞信息
在語法樹中,一個結點的綜合屬性的值,由其子結點的屬性值確定
S—屬性文法:僅僅使用綜合屬性的屬性文法

語義規則: 屬性計算的過程即是語義處理的過程
對於文法的每一個產生式配備一組屬性的計算規則,則稱爲語義規則。
(1)終結符只有綜合屬性,它由詞法分析器提供
(2)非終結符既可以有綜合屬性也可以有繼承屬性,文法開始符號的所有繼承屬性作爲屬性計算前的初始值。
(3) 產生式右邊符號的繼承屬性和產生式左邊符號的綜合屬性都必須提供一個計算規則
(4) 產生式左邊符號的繼承屬性和產生式右邊符號的綜合屬性不由所給的產生式的屬性計算規則進行計算,它們由其它產生式的屬性規則計算

一遍掃描的處理方法: 與樹遍歷的屬性計算方法不同,一遍掃描的處理方法是在語法分析的同時計算屬性值,而不是語法分析構造語法樹之後進行屬性的計算,而且無需構造實際的語法樹。
因爲一遍掃描的處理方法與語法分析器的相互作用,它與下面兩個因素密切相關:
1.所採用的語法分析方法
2.屬性的計算次序

如下爲遞歸下降分析器的設計實現思想

  1. 對每個非終結符A構造一個函數過程,對A的每個繼承屬性設置一個形式參數,函數的返回值爲A的綜合屬性(作爲記錄,或指向記錄的一個指針,記錄中有若干域,每個屬性對應一個域)。
  2. 非終結符A 對應的函數過程中,根據當前的輸入符號決定哪個產生式候選。
  3. 每個產生式對應的代碼中,按照從左到右的次序,對於單詞符號(終結符),非終結符和語義分析分別做以下的工作。
    (1)對於帶有綜合屬性x的終結符X,把x的值存入爲X.x設置的變量中。然後產生一個匹配X的調用,並繼續讀入一個輸入符號。
    (2)對於每個非終結符號B,產生一個右邊帶有函數調用的賦值語句c=B(b1,b2,…,bk)
    (3)對於語義動作,把動作的代碼抄進分析器中,用代表屬性的變量來代替對應屬性的每一次引用。

3.算法流程

  1. 每一個非終結符對應於一個函數(子過程);
  2. 非終結符所對應的右側產生式爲函數體;
  3. 每遇到一個終結符,則需要判斷所輸入字符是否與之匹配,若匹配則讀取下一個,若不匹配,則進行出錯處理。
    算法過程:
PROCEDURE <表達式>:
BEGIN
	IF SYM=’+’ OR SYM=’-’ THEN
	BEGIN
		ADVANCE; <項>;
		WHILE SYM=’+’ OR SYM=’-’ DO
		BEGIN 
			ADVANCE; <項>; 
		END
	END 
	ELSE IF SYM=FIRST(<項>) THEN
	BEGIN
		<項>;
		WHILE SYM=’+’ OR SYM=’-’ DO
		BEGIN 
			ADVANCE; <項>; 
		END
	END 
	ELSE ERROR
END

PROCEDURE <項>:
BEGIN
	IF SYM=’*’ OR SYM=’/’ THEN
	BEGIN
		ADVANCE; <因子>;
		WHILE SYM=’*’ OR SYM=’/’ DO
		BEGIN 
			ADVANCE; <因子>; 
		END
	END 
	ELSE IF SYM=FIRST(<因子>) THEN
	BEGIN
		<因子>;
		WHILE SYM=’*’ OR SYM=’/’ DO
		BEGIN 
          ADVANCE; <因子>; 
        END
	END 
	ELSE ERROR
END

PROCEDURE <項>:
BEGIN
	IF SYM=’標識符’ OR SYM=<無符號整數> THEN
	BEGIN
ADVANCE;
	END  
	ELSE IF SYM=’(’ THEN
	BEGIN
		<表達式>
		IF SYM=’)’ THEN
		BEGIN
			ADVANCE;
		END
		ELSE ERROR
	END
	ELSE ERROR
END

PROGRAM PAESER
BEGIN
	ADVANCE;
	<表達式>;
	IF SYM<>’#’ THEN ERROR
END

因此要在如上的基礎上構造函數過程,函數中會包含計算屬性。

4. 源程序

#include<bits/stdc++.h>
using namespace std;
ifstream infile("F:\\編譯原理\\第四次實驗\\result.txt");//文件流
ifstream infile2("F:\\編譯原理\\第四次實驗\\analysis.txt");//文件流
ofstream outfile("F:\\編譯原理\\第四次實驗\\result.txt");//文件輸出流
map<string,string> word;//應用map數據結構形成一個string->string的對應
std::map<string,string>::iterator it;//用來遍歷整個對應關係的迭代器
string str;//讀入的字符串
string sym;//用來指示讀入的符號
string sym2;//用來指示讀入的符號
int count1=0,k=0,flag=0,conterr=0,lpnum=0;
string expressionAnalysis();//表達式分析,表達式的中間代碼表示
string termAnalysis();//項分析,表達式的中間代碼表示
string factorAnalysis();//因子分析,表達式的中間代碼表示
int expressionAnalysis2();//表達式分析,算數表達式的語義計算
int termAnalysis2();//項分析,算數表達式的語義計算
int factorAnalysis2();//因子分析,算數表達式的語義計算
struct quad{//定義四元式
     string result;
     string arg1;
     string arg2;
     string op;
};
struct quad quad[50];
void map_init(){//對應關係進行初始化,如下只包括了表達式的相關符號
    word["+"]="plus";
    word["-"]="minus";
    word["*"]="times";
    word["/"]="slash";
    word["="]="eql";
    word["("]="lparen";
    word[")"]="rparen";
}
void lexanalysis(){//詞法分析
    char ch;
    char a;
    string word1;//string變量識別單詞
    string str;//string變量進行字符識別
    ostringstream buf;
    while(buf&&infile2.get(ch)) buf.put(ch);//將文件中的字符讀出來
    str= buf.str();//將得到的字符儲存到string類型變量中
    int csize=str.length();
    for(int i=0;i<csize;i++){//對整個字符串進行遍歷
        while(str[i]==' '||str[i]=='\n') i++;//若最開始爲空格或換行符,則將指針的位置往後移
        if(isalpha(str[i])){//對標識符和基本字進行識別,調用庫函數isalpha()
            word1=str[i++];
            while(isalpha(str[i])||isdigit(str[i])){
                word1+=str[i++];
            }
            it=word.find(word1);
            if(it!=word.end()){//判斷是不是基本字,若爲基本字則進行輸出
                outfile<<"("<<word[word1]<<","<<word1<<")"<<endl;
            }
            else{//否則直接輸出
                outfile<<"(ident"<<","<<word1<<")"<<endl;
            }
            i--;
        }
        else if(isdigit(str[i])){//判斷是不是常數,調用庫函數isdigit()
            word1=str[i++];
            while(isdigit(str[i])){
                word1+=str[i++];
            }
             if(isalpha(str[i])){
                outfile<<"error"<<endl;
                break;
            }
            else{
                outfile<<"(number"<<","<<word1<<")"<<endl;
            }
            i--;
        }else{//對其他的基本字依次進行判斷
            word1=str[i];
            it=word.find(word1);
            if(it!=word.end()){
                outfile<<"("<<word[word1]<<","<<word1<<")"<<endl;
            }else{
                outfile<<"error"<<endl;
                break;
            }
        }
    }
    infile2.close();
}
int advance(){//用來讀入下一個符號
    int found1,found2;
    if(!getline(infile,str)){
        return 0;
    }
    found1=str.find(',',0);
    if(found1==-1){
        conterr++;
        cout<<"語法錯誤 識別字符錯誤"<<endl;
        return -1;
    }
    found2=str.length();
    sym=str.substr(1,found1-1);
    sym2=str.substr(found1+1,found2-found1-2);
    return 1;
}
string newtemp(){//產生新變量名t1,t2等
    char *p;
    char m[12];
    p=(char*)malloc(12);
    k++;
    itoa(k,m,10);
    strcpy(p+1,m);
    p[0]='t';
    string s;
    s=p;
    return s;
}
void emit(string op,string arg1,string arg2,string result){//產生四元式用於顯示
    quad[count1].op=op;
    quad[count1].arg1=arg1;
    quad[count1].arg2=arg2;
    quad[count1].result=result;
    count1++;
    return;
}
string expressionAnalysis(){//表達式的遞歸下降分析程序
    string op,arg1,arg2,result;
    if(conterr){
        return NULL;
	}
	arg1=termAnalysis();//通過項分析得到第一個參數的值
	if(conterr){
        return NULL;
	}
	while((sym=="plus")||(sym=="minus")){
        op=sym2;
		flag=advance();
		if(conterr){
            return NULL;
		}
		if(flag==0){
            cout<<"語法錯誤 <加法運算符>後缺項"<<endl;
            conterr++;
			return NULL;
		}
		arg2=termAnalysis();//通過項分析得到第二個參數的值
		if(conterr){
            return NULL;
		}
		result=newtemp();//產生中間變量名,相當於對結果進行存儲
		emit(op,arg1,arg2,result);//產生四元式,相當於進行加法或減法運算,得出結果
		arg1=result;//保存得到的結果
	}
	return arg1;//返回表達式最終得到的值
}
string termAnalysis(){//項的遞歸下降分析程序
    string op,arg1,arg2,result;
    arg1=factorAnalysis();//通過因子分析得到第一個參數的值
    if(conterr){
        return NULL;
    }
	while((sym=="times")||(sym=="slash")){
        op=sym2;
		flag=advance();
		if(conterr){
            return NULL;
		}
		if(flag==0){
			conterr++;
			cout<<"語法錯誤 <乘法運算符>後缺因子"<<endl;
			return NULL;
		}
		if(conterr){
            return NULL;
		}
		arg2=factorAnalysis();//通過因子分析得到第二個參數的值
		if(conterr){
            return NULL;
		}
		result=newtemp();//產生中間變量名,相當於對結果進行存儲
		emit(op,arg1,arg2,result);//產生四元式,相當於進行乘法或除法運算,得出結果
		arg1=result;//保存得到的結果
	}
	return arg1;//返回項最終得到的值
}
string factorAnalysis(){
    string arg;
    if(sym=="ident"){//如果是標識符,最終返回標識符的符號
        arg=sym2;
        flag=advance();
        if(conterr){
            return NULL;
		}
		if(lpnum==0&&sym=="rparen"){
            conterr++;
			cout<<"語法錯誤 ')'不匹配"<<endl;
			return NULL;
        }
    }
    else if(sym=="number"){//如果是無符號整數,最終返回相應的整數
        arg=sym2;
        flag=advance();
        if(conterr){
            return NULL;
		}
		if(lpnum==0&&sym=="rparen"){
            conterr++;
			cout<<"語法錯誤 ')'不匹配"<<endl;
			return NULL;
        }
    }
    else if(sym=="lparen"){//如果是左括號,則要進行右括號匹配,並判斷中間是不是表達式,並得出表達式的值進行返回
        lpnum++;
        flag=advance();
        if(conterr){
            return NULL;
		}
		if(flag==0){
			conterr++;
			cout<<"語法錯誤 '('後缺少表達式"<<endl;
			return NULL;
		}
        arg=expressionAnalysis();
        if(conterr){
            return NULL;
		}
        if(flag==0||sym!="rparen"){
			conterr++;
			cout<<"語法錯誤 表達式後面缺少')'"<<endl;
			return " ";
		}else{
		    lpnum--;
            flag=advance();
            if(conterr){
                return NULL;
            }
            if(flag==0){
                return arg;
            }
		}
    }else{
		cout<<"語法錯誤 因子首部不爲<標識符>|<無符號整數>|'('"<<endl;
		conterr++;
		return " ";
	}
	return arg;
}
int expressionAnalysis2(){//表達式的遞歸下降分析程序
    string op;
    int arg1,arg2,result;
    if(conterr){
        return 0;
	}
	arg1=termAnalysis2();//通過項分析得到第一個參數的值
	if(conterr){
        return 0;
	}
	while((sym=="plus")||(sym=="minus")){
        op=sym2;
		flag=advance();
		if(conterr){
            return 0;
		}
		if(flag==0){
            cout<<"語法錯誤 <加法運算符>後缺項"<<endl;
            conterr++;
			return 0;
		}
		arg2=termAnalysis2();//通過項分析得到第二個參數的值
		if(conterr){
            return 0;
		}
		if(op=="+"){//若是加法符號則進行加法運算,並對得到的結果進行保存
            result=arg1+arg2;
            arg1=result;
		}
        else{//若是減法符號則進行加法運算,並對得到的結果進行保存
            result=arg1-arg2;
            arg1=result;
        }
	}
	return arg1;//返回該表達式所代表的值
}
int termAnalysis2(){//項的遞歸下降分析程序
    string op;
    int arg1,arg2,result;
    arg1=factorAnalysis2();//通過因子分析得到第一個參數的值
    if(conterr){
        return 0;
    }
	while((sym=="times")||(sym=="slash")){
        op=sym2;
		flag=advance();
		if(conterr){
            return 0;
		}
		if(flag==0){
			conterr++;
			cout<<"語法錯誤 <乘法運算符>後缺因子"<<endl;
			return 0;
		}
		if(conterr){
            return 0;
		}
		arg2=factorAnalysis2();//通過因子分析得到第二個參數的值
		if(conterr){
            return 0;
		}
		if(op=="*"){//若是乘法符號則進行加法運算,並對得到的結果進行保存
            result=arg1*arg2;
            arg1=result;
		}
        else{//若是除法符號則進行加法運算,並對得到的結果進行保存
            if(arg2==0){
                conterr++;
                cout<<"除數不能爲0"<<endl;
                return 0;
            }
            result=arg1/arg2;
            arg1=result;
        }
	}
	return arg1;//返回該項所代表的值
}
int factorAnalysis2(){
    int arg;
    if(sym=="ident"){//算數表達式中不含有字母,否則無法進行運算
        cout<<"算術表達式中含有字母"<<endl;
		conterr++;
		return 0;
    }else if(sym=="number"){//若果是數字,則返回相應的值
        arg=atoi(sym2.c_str());
        flag=advance();
        if(conterr){
            return 0;
		}
		if(lpnum==0&&sym=="rparen"){
            conterr++;
			cout<<"語法錯誤 ')'不匹配"<<endl;
			return 0;
        }
    }
    else if(sym=="lparen"){//如果是左括號,則要進行右括號匹配,並判斷中間是不是表達式,並得出表達式的值進行返回
        lpnum++;
        flag=advance();
        if(conterr){
            return 0;
		}
		if(flag==0){
			conterr++;
			cout<<"語法錯誤 '('後缺少表達式"<<endl;
			return 0;
		}
        arg=expressionAnalysis2();//返回表達式的值
        if(conterr){
            return 0;
		}
        if(flag==0||sym!="rparen"){
			conterr++;
			cout<<"語法錯誤 表達式後面缺少')'"<<endl;
			return 0;
		}else{
		    lpnum--;
            flag=advance();
            if(conterr){
                return 0;
            }
            if(flag==0){
                return arg;
            }
		}
    }else{
		cout<<"語法錯誤 因子首部不爲<標識符>|<無符號整數>|'('"<<endl;
		conterr++;
		return 0;
	}
	return arg;//返回該因子所代表的值
}
int main(){
    int i=0,num,result;
    //開始詞法分析
    map_init();
    lexanalysis();
    //開始語法和語義分析
    cout<<"1.PL/0表達式的中間代碼表示"<<endl;
    cout<<"2.PL/0算術表達式的語義計算"<<endl;
    cout<<"請輸入類別號碼:";
    cin>>num;
    flag=advance();
    if(num==1){//PL/0表達式的中間代碼表示
        if(flag){
            expressionAnalysis();//開始進行表達式分析
        }
        if(flag!=-1&&!conterr){//若表達式正確則輸出表達式的中間代碼表示
            for(int i=0;i<count1;i++){
                cout<<'('<<quad[i].op<<','<<quad[i].arg1<<','<<quad[i].arg2<<','<<quad[i].result<<')'<<endl;;
            }
        }
    }else if(num==2){//PL/0算術表達式的語義計算
        if(flag){
            result=expressionAnalysis2();//開始進行表達式分析
        }
        if(flag!=-1&&!conterr){//若表達式正確則輸出表達式的值
            cout<<result<<endl;
        }
    }else{
       cout<<"error!"<<endl;
       return 0;
    }
    infile.close();
    return 0;
}

5. 調試數據

待輸入的文件流:
在這裏插入圖片描述
詞法分析結果:
在這裏插入圖片描述
最終得到的結果:
在這裏插入圖片描述
在這裏插入圖片描述

待輸入的文件流
在這裏插入圖片描述
詞法分析結果:
在這裏插入圖片描述
最終得到的結果:
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章