1. 程式人生 > >編譯原理實驗報告:自上而下語法分析

編譯原理實驗報告:自上而下語法分析

1. 實驗題目:自上而下語法分析

實驗目的

  1. 給出 PL/0 文法規範,要求編寫 PL/0 語言的語法分析程式。
  2. 通過設計、編制、除錯一個典型的自上而下語法分析程式,實現對詞法分析程式所提供的單詞序列進行語法檢查和結構分析,進一步掌握常用的語法分析方法。
  3. 選擇最有代表性的語法分析方法,如遞迴下降分析法、預測分析法;選擇對各種常見程式語言都具備的語法結構,如賦值語句,特別是表示式,作為分析物件。

實驗內容

  已給 PL/0 語言文法,構造表示式部分的語法分析器。
  分析物件〈算術表示式〉的 BNF 定義如下:
  <表示式> ::= [+|-]<項>{<加法運算子> <項>}
  <項> ::= <因子>{<乘法運算子> <因子>}
  <因子> ::= <識別符號>|<無符號整數>| ‘(’<表示式>‘)’
  <加法運算子> ::= +|-
  <乘法運算子> ::= *|/
  <關係運算符> ::= =|#|<|<=|>|>=

實驗要求

  1. 將實驗一“詞法分析”的輸出結果,作為表示式語法分析器的輸入,進行語法解析,對於語法正確的表示式,報告“語法正確法錯誤的表示式,報告“語法錯誤”, 指出錯誤原因。
  2. 把語法分析器設計成一個獨立一遍的過程。
  3. 採用遞迴下降分析法或者採用預測分析法實現語法分析。

輸入輸出

輸入
  PL/0 表示式,用實驗一的輸出形式作為輸入。例如: 對於 PL/0 表示式,(a+15)*b 用下列形式作為輸入:
   (lparen,( )
   (ident, a)
   (plus, + )
   (number, 15)
   (rparen,) )
   (times, * )
   (ident, b )
輸出


  對於語法正確的表示式,報告“語法正確”;
  對於語法錯誤的表示式,報告“語法錯誤”, 指出錯誤原因

2. 設計思想

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

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

可以得到其語法圖
表示式:
在這裡插入圖片描述

在這裡插入圖片描述
因子
在這裡插入圖片描述
計算FIRST集:
  FIRST(<表示式>)={ +, -, (, <識別符號>, <無符號整數> }
  FIRST(<因子>)={ <識別符號>, <無符號整數>, ( }
  FIRST(<項>)={ <識別符號>, <無符號整數>, ( }
  FIRST(<加法運算子>)={ +, - }
  FIRST(<乘法運算子>)={ *, / }

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

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<fstream>
#include<cstring>
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include<map>
#include<bits/stdc++.h>
using namespace std;
ifstream infile("F:\\編譯原理\\第二次實驗\\result.txt");
map<string,string> word;//應用map資料結構形成一個string->string的對應
std::map<string,string>::iterator it;//用來遍歷整個對應關係的迭代器
string str;//string變數進行字元識別
string sym; //指標

void expressionAnalysis();//表示式分析
void termAnaysis();//項分析
void factorAnalysis();//因子分析
int advance();

int conterr=0;//記錄錯誤
int lpnum=0;//記錄左括號
int found;//提取字串中指標的位置
int flag=0;//記錄往後移動一個指標SYM是否正確

int main(){
    flag=advance();
    if(flag){
        expressionAnalysis();
    }
    if(flag!=-1&&!conterr){
        cout<<"語法正確"<<endl;
    }
    return 0;
}

int advance(){//SYM的移動
    if(!getline(infile,str)){//從檔案中提取字元
        return 0;
    }
    found=str.find(',',0);
    if(found==-1){//當為error的時候,沒有‘,’
        conterr++;
        cout<<"語法錯誤 識別字符錯誤"<<endl;
        return -1;
    }
    sym=str.substr(1,found-1);
    cout<<sym<<endl;
    return 1;
}

void factorAnalysis(){
    if(sym=="ident"||sym=="number"){//如果是識別符號和無符號整數,指標就往後移動
        flag=advance();
        if(conterr){
            return;
		}
		if(lpnum==0&&sym=="rparen"){
            conterr++;
			cout<<"語法錯誤 ')'不匹配"<<endl;
			return;
        }
    }
    else if(sym=="lparen"){//如果是左括號,就要接下來判斷是否為表示式,指標往後移動
        lpnum++;
        cout<<lpnum<<endl;
        flag=advance();
        if(conterr){
            return;
		}
		if(flag==0){//當為最後一個標誌的時候,若沒有右括號匹配就錯誤
			conterr++;
			cout<<"語法錯誤 '('後缺少表示式"<<endl;
			return;
		}
        expressionAnalysis();
        if(conterr){
            return;
		}
        if(flag==0||sym!="rparen"){
			conterr++;
			cout<<"語法錯誤 表示式後面缺少')'"<<endl;
			return;
		}else{
		    lpnum--;
            flag=advance();
            if(conterr){
                return;
            }
            if(flag==0){
                return;
            }
		}
    }else{
		cout<<"語法錯誤 因子首部不為<識別符號>|<無符號整數>|'('"<<endl;
		conterr++;
		return;
	}
	return;
}

void termAnalysis(){
    factorAnalysis();
    if(conterr){
        return;
    }
	while((sym=="times")||(sym=="slash")){//當為'*'或'/'的時候,一直往後識別因子並迴圈
		flag=advance();
		if(conterr){
            return;
		}
		if(flag==0){
			conterr++;
			cout<<"語法錯誤 <乘法運算子>後缺因子"<<endl;
			return;
		}
		if(conterr){
            return;
		}
		factorAnalysis();
		if(conterr){
            return;
		}
	}
	return;
}

void expressionAnalysis(){
    if(conterr){
        return;
	}
	if((sym=="plus")||(sym=="minus")){//當為'*'或'/'的時候
		flag=advance();
		if(!conterr){
            return;
		}
		if(flag==0){
            cout<<"語法錯誤 <加法運算子>後缺項"<<endl;
            conterr++;
			return;
		}
	}
	termAnalysis();
	if(conterr){
        return;
	}
	while((sym=="plus")||(sym=="minus")){//當為'+'或'-'的時候,一直往後識別項並迴圈
		flag=advance();
		if(conterr){
            return;
		}
		if(flag==0){
            cout<<"語法錯誤 <加法運算子>後缺項"<<endl;
            conterr++;
			return;
		}
		termAnalysis();
		if(conterr){
            return;
		}
	}
	return;
}

5. 除錯資料

輸入:
在這裡插入圖片描述
在這裡插入圖片描述
輸出:
在這裡插入圖片描述