编译原理--语法分析之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;
}

 

posted @ 2020-06-27 10:09  sykline  阅读(5292)  评论(0编辑  收藏  举报