編譯原理--語法分析之LR分析法的簡單實現



清晰記得本次實驗在推了兩次項目集規范簇之后,發現文檔中給出的文法有錯誤,聯系老師得到改正后,遂順利完成。簡單記錄一下本次實驗的經歷,留作以后備用,若有錯誤之處,還請路過的博友不吝賜教。

實驗設計目標

構造LR(1)分析程序,利用它進行語法分析,判斷給出的符號串是否為該文法識別的句子。

實驗原理

整體思路:在總控程序的控制下,從左到右掃描輸入符號串,根據狀態棧中的棧頂狀態、符號棧中的棧頂字符和文法及當前輸入符號,按分析表完成相應的分析工作。

LR分析器由三個部分組成:

  • 總控程序,也可以稱為驅動程序。對所有的LR分析器總控程序都是相同的。
  • 分析表或分析函數,不同的文法分析表將不同,同一個文法采用的LR分析器不同時,分析表將不同,分析表又可以分為動作表(ACTION)和狀態轉換(GOTO)表兩個部分,它們都可用二維數組表示。
  • 分析棧,包括文法符號棧和相應的狀態棧,它們均是先進后出棧。
    • 分析器的動作就是由棧頂狀態和當前輸入符號所決定。

      GOTO[i,X]=j表示,規定當棧頂狀態為i,遇到當前文法符號為X時應轉向狀態j,X為非終結符。

      ACTION[i,a]規定了棧頂狀態為i時,遇到輸入符號a應執行的動作有四種可能:

    1.  移進:

      action[i,a]= Sj:狀態 j 移入到狀態棧,把 a 移入到文法符號棧,其中 i , j 表示狀態號。

    2. 歸約:

      action[i,a]=Rk:當在棧頂形成句柄時,則歸約為相應的非終結符A,即文法中有A->B的產生式,若B的長度為R(即|B|=R),則從狀態棧和文法符號棧中自頂向下去掉R個符號,即棧指針SP減去R,並把A移入文法符號棧內,j=GOTO[i,A]移進狀態棧,其中i為修改指針后的棧頂狀態。

    3. 接收Acc:

      當歸約到文法符號棧中只剩文法的開始符號S時,並且輸入符號串已結束即當前輸入符是'#',則為分析成功。

    4. 報錯:

      當遇到狀態棧頂為某一狀態下出現不該遇到的文法符號時,則報錯,說明輸入端不是該文法能接受的符號串。

實驗文法

 (0)S->E         

 (1)E->E+T

 (2)E->E-T        

 (3)E->T

 (4)T->F         

 (5)T->T*F

 (6)T->T/F      

 (7)F->(E)

 (8)F->i

實驗輸入、輸出

  1. 輸入數據:

      case1: i+i*i#

      case2: i+i*#

  2. 輸出結果:

實驗實現過程

  1. 首先就是禿頭三次的項目集規范簇(一開始打算手寫來着,畫滿一張A4紙之后, Visio好香啊……)

這里我是采用類似DFS深度遍歷的思想來畫的,對於一個輸入就按着他一個輸入分支往下畫,直到不能再往下擴展。便回溯將前邊的分支填充,所以畫出來的最后的圖片就出現了這樣一個特點,靠前的很多分支都是直接寫編號就可以了。當然也可以采用像BFS遍歷的思想來畫,個人感覺效果應該是一樣的。

  2. 那有了項目集規范簇,接下來就要構建相應的Action表和Goto表了,這里照着項目集規范簇來並不難,麻煩的是將其轉換為代碼中對應的表(建表時,眼都看花了……)

    • Action表

 

    • Goto表:

 

  3. 有了Action表和Goto表,剩下的就好辦了,為了能更好的理清思路,我又畫了LR(1)控制器的流程圖。

代碼實現部分

(待實驗統計完畢之后,在統一上傳一下XD)

2020/6/29

LR.h

#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
#include "LRTable.h"
#ifndef _LR_H
#define _LR_H

using namespace std;

class Analyser{
private:
    string str; //被分析的字符串
    int step;   //步驟
    int idx;    //當前分析符號的下標
    vector<char> sign;  //符號棧
    vector<int> status; //狀態棧

public:
    Analyser();
    ~Analyser();
    bool startAnalyse(string str);
    string transformStackToStr(int opt);
};

Analyser::Analyser()
{
    this->step = 1;
    this->idx = 0;
}

Analyser::~Analyser(){}

string Analyser::transformStackToStr(int opt)
{
    string str = "";
    //返回狀態棧
    if(opt == 0)
    {
        vector<int>::iterator iter = status.begin();
        bool isfirst = true;
        for(;iter != status.end();iter++)
        {
            if(isfirst)
            {
                isfirst = false;
                str += to_string(*iter);
            }
            else
            {
                str += "_"+to_string(*iter);
            }
        }
    }
    else//返回符號棧
    {
        vector<char>::iterator iter = sign.begin();
        for(; iter != sign.end(); iter++)
        {
            str += *iter;
        }
    }
    return str;
}


bool Analyser::startAnalyse(string nowStr)
{
    this->str = nowStr;
    //將初始狀態輸入到棧中
    sign.push_back('#');
    status.push_back(0);
    LRAnalyseTable table;

    //當前的狀態
    int nowStatus = 0;

    //初始說明
    cout<<setw(10)<<"步驟"<<setw(15)<<"狀態棧"<<setw(10)<<"符號棧"<<setw(15)<<"當前符號"<<setw(15)<<"剩余字符串"<<setw(20)<<"動作說明"<<endl;
    

    while(table.getAction(nowStatus, str[idx]) != 0)
    {
        
        int result = table.getAction(nowStatus, str[idx]);
        //輸出錯誤的情況信息
        if(result == -1)
        {
            printf("error! 在輸入符號: %c 處出現錯誤,符號坐標為: %d\n", str[idx], idx);
            return false;
        }

        if(result > 0)//移進操作
        {
            cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<str[idx]<<setw(15)<<str.substr(idx)<<setw(20)<<"Action["<<status.back()<<"]["<<str[idx]<<"]=S"<<result<<" 移進狀態"<<endl;
            status.push_back(result);
            sign.push_back(str[idx]);
            idx++;
        }
        else if(result <= -10)//規約操作
        {
            //還原歸約所用產生式的下標
            result += 10;
            result = -result;
            string rule = table.getGrammer(result);

            cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<str[idx]<<setw(15)<<str.substr(idx)<<setw(20)<<"R"<<result<<""+rule+"歸約"<<endl;
            //彈出產生式右端的符號和狀態
            step++;
            for(int i=rule.size()-1; rule[i]!='>' && i>=0; i--)
            {
                status.pop_back();
                sign.pop_back();
            }

            nowStatus = status.back();
            result = table.getGoto(nowStatus, rule[0]);
            cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<rule[0]<<setw(15)<<str.substr(idx)<<setw(20)<<"Goto["<<nowStatus<<"]["<<rule[0]<<"]="<<result<<" 狀態轉移"<<endl;
            status.push_back(result);
            sign.push_back(rule[0]);
        }
        //獲取當前狀態棧的狀態
        nowStatus = status.back();
        step++;
    }
    cout<<setw(10)<<step<<setw(15)<<transformStackToStr(0)<<setw(10)<<transformStackToStr(1)<<setw(15)<<str[idx]<<setw(15)<<str.substr(idx)<<setw(20)<<" 分析完成"<<endl;
    return true;
}

#endif

LRTable.h

#include <string>
#ifndef _LRTABLE_H
#define _LRTABLE_H

using namespace std;

class LRAnalyseTable
{
private:
    //產生式
    string grammer[15] = {"S->E", "E->E+T", "E->E-T", "E->T", "T->F", "T->T*F", "T->T/F","F->(E)", "F->i"};
    //終結符
    char terminalChar[10] = {'+','-','*', '/', '(', ')','i','#'};
    //非終結符
    char nonTerminalChar[10] = {'E','F','T'};
    //終結符的個數
    int numTerminalChar = 8;
    //非終結符的個數
    int numNonTerminalChar = 3;


    //初始化LR(1)分析表
    //action表
    int Action[50][8] = {
        {-1, -1, -1, -1, 6, -1, 23, -1},{2, 28, -1, -1, -1, -1, -1, 0},// 0 1
        {-1, -1, -1, -1, 6, -1, 23, -1},{-11, -11, 4, 24, -1, -1, -1, -11},//2 3
        {-1, -1, -1, -1, 6, -1, 23, -1},{-15, -15, -15, -15, -1, -1, -1 ,-15},//4 5
        {-1, -1, -1, -1, 13, -1, 20, -1},{9, 16, -1, -1, -1, 8, -1, -1},//6 7
        {-17, -17, -17, -17, -1, -1, -1, 7},{-1, -1, -1, -1, 13, -1, 20, -1},//8 9
        {-11, -11, -11, 18, -1, 1 ,-1, -1},{-1, -1, -1, -1, 13, -1, 20, -1},//10 11
        {-15, -15, -15, -15, -1, -15, -1, -1},{-1, -1, -1, -1, 13, -1, 20, -1},//12 13
        {9, 16, -1, -1, -1, 15, -1, -1},{-17, -17, -17, -17, -1, -17, -1, -1},//14 15
        {-1, -1, -1, -1, 13, -1, 20, -1},{-12, -12, 11, 18, -1, -12, -1, -1},//16 17
        {-1, -1, -1, -1, 13, -1, 20,-1},{-16, -16, -16, -16, -1, -16, -1, -1},//18 19
        {-18, -18, -18, -18, -1, -18, -1, -1},{-14, -14, -14, -14, -1, -14, -1, -1},//20 21
        {-13, -13, 11, 18, -1, -13, -1, -1},{-18, -18, -18, -18, -1, -1, -1, -18},//22 23
        {-1, -1, -1, -1, 6, 23, -1, -1},{-16, -16, -16, -16, -1, -1, -1, -16},//24 25
        {-14, -14, -14, -14, -1, -1, -1, -14},{-13, -13, 4, 24, -1, -1, -1, -13},//26 27
        {-1, -1, -1, -1, 6, -1, 23, -1}//28
    };
    //goto表
    int Goto[50][4] = {
        {1, 26, 27},{-1, -1, -1},//0 1
        {-1, 26, 3},{-1, -1, -1},//2 3
        {-1, 5, -1},{-1, -1, -1},//4 5
        {7, 21, 22},{-1, -1, -1},//6 7
        {-1, -1, -1},{-1, 21, 10},//8 9
        {-1, -1, -1},{-1, 12, -1},//10 11
        {-1, -1, -1},{14, 21, 22},//12 13
        {-1, -1, -1},{-1, -1, -1},//14 15
        {-1, 21, 17},{-1, -1, -1},//16 17
        {-1, 19, -1},{-1, -1, -1},//18 19
        {-1, -1, -1},{-1, -1, -1},//20 21
        {-1, -1, -1},{-1, -1, -1},//22 23
        {-1, 5, -1},{-1, -1, -1},//24 25
        {-1, -1, -1},{-1, -1, -1},//26 27
        {-1, 26, 3}//28
    };


public:
    LRAnalyseTable();
    ~LRAnalyseTable();
    int getTerminalIndex(char ch);
    int getNonTerminalIndex(char ch);
    int getAction(int status, char ch);
    int getGoto(int status, char ch);
    string getGrammer(int idx);
};

LRAnalyseTable::LRAnalyseTable(/* args */)
{

}

LRAnalyseTable::~LRAnalyseTable()
{

}

int LRAnalyseTable::getAction(int status, char ch)
{
    return Action[status][getTerminalIndex(ch)];
}

int LRAnalyseTable::getGoto(int status, char ch)
{
    return Goto[status][getNonTerminalIndex(ch)];
}

string LRAnalyseTable::getGrammer(int idx)
{
    return grammer[idx];
}

//獲取終結符的下標
int LRAnalyseTable::getTerminalIndex(char ch){
    for(int i=0; i<numTerminalChar; i++)
    {
        if(ch == terminalChar[i])
        {
            return i;
        }    
    }
    return -1;
}

//獲得非終結符的下標
int LRAnalyseTable::getNonTerminalIndex(char ch){
    for(int i=0; i<numNonTerminalChar; i++)
    {
        if(ch == nonTerminalChar[i])
        {
            return i;
        }    
    }
    return -1;
}


#endif

test.cpp

#include <iostream>
#include <string>
#include <stack>
#include <vector>
#include <iomanip>
#include "LR.h"

using namespace std;

int main(){
    freopen("in.txt","r", stdin);
    freopen("out.txt", "w", stdout);
    string str="";
    int cnt = 1;
    while(getline(cin, str))
    {
        str = str.substr(0, str.size()-1);
        cout<<"Case"<<cnt++<<": "<<str<<endl;
        Analyser analyse;
        if(analyse.startAnalyse(str)){
            cout<<"輸入符號串"<<str<<"為合法符號串"<<endl;
        }else{
            cout<<"輸入符號串"<<str<<"為非法符號串"<<endl;
        }
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM