编译原理LR语法分析器的设计与实现

Posted BkbK-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译原理LR语法分析器的设计与实现相关的知识,希望对你有一定的参考价值。

LR语法分析器的设计与实现

本文为当时编译原理实验作业,要求用设计的思想完成,小题大做,仅供参考

文章目录

实验要求

实现功能

1)使用LR(1)分析方法构造识别活前缀的DFA;
2)构造文法的分析表(Action表和Goto表);
3)构造LR语法分析器的总控程序;
3)输入文法(语言语法结构的文法描述存储在文本文件中);
4)输出文法的项目集簇(标准输出设备);
5)输出识别活前缀的DFA(标准输出设备);
6)输出文法的Action表和Goto表(标准输出设备);
7)对给定的输入串(存储在文本文件中),输出其是否该文法正确句子的判断,并输出文本形式的分析过程。

输入输出

文件结构:
1)非终结符个数;
2)所有非终结符,空格分隔;
3)终结符个数;
4)所有终结符,空格分隔;
5)规则个数;
6)所有规则,每行一个规则,规则输入格式:左部,右部符号数,右部符号,空格分隔;
7)开始符号。
8)待分析的符号串。

样例

描述:

文法G =(VN,VT,P,S)其中:
	VN =  S', S 
	VT =  a, (, ) 
	P =  	S' -> S
     		S -> ( S )
S -> a

	S = S’

输入:

4
S' S L R 

3
= * i

6
S' -> S
S -> L = R
S -> R
L -> * R 
L -> i
R -> L

S'

* i = i #

输出:

 CFG=(VN,VT,P,S)
  VN: S' S L R
  VT: = * i
  Production:
     0: S' -> S
     1: S -> L = R
     2: S -> R
     3: L -> * R
     4: L -> i
     5: R -> L
  StartSymbol: S'

[LR(1) item set cluster]
  I0 :
        S' -> . S, #
        S -> . L = R, #
        S -> . R, #
        L -> . * R, #
        L -> . * R, =
        L -> . i, #
        L -> . i, =
        R -> . L, #
  I1 :
        S' -> S ., #
  I2 :
        S -> L . = R, #
        R -> L ., #
  I3 :
        S -> R ., #
  I4 :
        L -> . * R, #
        L -> . * R, =
        L -> * . R, #
        L -> * . R, =
        L -> . i, #
        L -> . i, =
        R -> . L, #
        R -> . L, =
  I5 :
        L -> i ., #
        L -> i ., =
  I6 :
        L -> * R ., #
        L -> * R ., =
  I7 :
        R -> L ., #
        R -> L ., =
  I8 :
        S -> L = . R, #
        L -> . * R, #
        L -> . i, #
        R -> . L, #
  I9 :
        S -> L = R ., #
  I10:
        L -> . * R, #
        L -> * . R, #
        L -> . i, #
        R -> . L, #
  I11:
        L -> i ., #
  I12:
        R -> L ., #
  I13:
        L -> * R ., #

[LR(0) analytical table]
  Action:
         #    =    *    i
    0             s4   s5
    1  acc               
    2   r5   s8          
    3   r2               
    4             s4   s5
    5   r4   r4          
    6   r3   r3          
    7   r5   r5          
    8            s10  s11
    9   r1               
   10            s10  s11
   11   r4               
   12   r5               
   13   r3               
  Goto:
        S   L   R
    0   1   2   3
    1            
    2            
    3            
    4       7   6
    5            
    6            
    7            
    8      12   9
    9            
   10      12  13
   11            
   12            
   13            

文法是 LR(1) 文法!

[parsing]
栈顶         输入     查表 动作                                  注
------------+--------+----+-------------------------------------+-----------
  0 #        *        s4   进栈  4 *
  4 *        i        s5   进栈  5 i
  5 i        =        r4   出栈 1 个符号和状态  进栈  7 L        L -> i
  7 L        =        r5   出栈 1 个符号和状态  进栈  6 R        R -> L
  6 R        =        r3   出栈 2 个符号和状态  进栈  2 L        L -> * R
  2 L        =        s8   进栈  8 =
  8 =        i        s11  进栈 11 i
 11 i        #        r4   出栈 1 个符号和状态  进栈 12 L        L -> i
 12 L        #        r5   出栈 1 个符号和状态  进栈  9 R        R -> L
  9 R        #        r1   出栈 3 个符号和状态  进栈  1 S        S -> L = R
  1 S        #        acc  成功接收!
------------+--------+----+-------------------------------------+-----------
 end!

一、LR语法分析器问题定义

1.1 语法分析器定义

语法分析器(Parser)通常是作为编译器或解释器的组件出现的,它的作用是进行语法检查、并构建由输入的单词组成的数据结构(一般是语法分析树、抽象语法树等层次化的数据结构)。语法分析器通常使用一个独立的词法分析器从输入字符流中分离出一个个的“单词”,并将单词流作为其输入。实际开发中,语法分析器可以手工编写,也可以使用工具(半)自动生成。

1.2 自上而下语法分析原理

(1)自上而下语法分析基本思想

  • 将输入符号按从左到右顺序依次移入文法符号的栈栈中,边移入边分析;
  • 当栈顶符号串形成某条规则右部时就进行一次归约,即用该规则左部非终结符替换相应规则右部符号串。重复这一过程直到整个输入串分析完毕。
  • 最终若栈中剩下句子右界符“#”和文法的开始符号,则所分析的输入符号串是文法的正确句子。否则,就不是的正确句子,报告错误。

(2)自上而下语法分析常用方法
LR(0)、LR(1)、SLR、LALR等。

二、LR语法分析器需求陈述

  • 使用LR(0)、LR(1)分析方法构造识别活前缀的DFA;
  • 构造文法的分析表(Action表和Goto表);
  • 构造LR语法分析器的总控程序;
  • 输入文法(语言语法结构的文法描述存储在文本文件中);
  • 输出文法的项目集簇(标准输出设备);
  • 输出识别活前缀的DFA(标准输出设备);
  • 输出文法的Action表和Goto表(标准输出设备);
  • 对给定的输入串(存储在文本文件中),输出其是否该文法正确句子的判断,并输出文本形式的分析过程。

三、LR语法分析器分析模型

3.1 LR语法分析器领域模型

3.1.1 确定LR法分析器类与对象

从语法分析器的问题定义中筛选出可能作为对象的候选对象。
根据编译程序使用的基本概念,将所有概念实体作为候选对象,下图是根据概念之间的组成关系总结出的所有候选对象。

3.1.2 绘制领域模型(对象模型)

生成对象模型的方法:

  • 对候选对象确定初步关联,筛选出必要关联和必要的类;
  • 确定各个对象的属性;
  • 绘制出各个类之间的联系。

初步的对象模型(领域模型):

3.2 LR语法分析器动态模型
3.2.1 编写系统状态脚本
以下给出了文法分析系统的正常和异常情况脚本
(1)文法分析系统正常情况脚本:

(2)文法分析系统异常情况脚本:

3.2.2绘制事件跟踪图

文法分析系统的主要事件有两个:
(1)文法处理事件:
用户通过文件或中端输入文法,文法系统接收后初始化文法进行文法分析,分析后生成预测分析表,最后将文法的组成信息和分析信息打印在终端上或输出在文件中。

(2)文法分析事件:
用户通过文件或中端输入待分析串,文法系统接收后初对照预测分析表进行文法分析,分析后生成移进归约序列,最后将字符串的文法分析信息打印在终端上或输出在文件中。

3.2.3 绘制状态图

状态图描绘事件与对象之间的关系。将事件跟踪图中两个事件整合绘制系统状态图如下:

3.3 LR语法分析器功能模型

3.3.1 绘制基本系统模型图

基本的系统模型图指明了目标系统的边界,绘制语法分析器基本系统模型图如下:

3.3.2 绘制功能级数据流图

把基本系统模型中单一的处理框分解成若干个处理框,以描述系统加工,变换数据的基本功能,得到以下LR文法分析系统的功能级数据流图:

3.3.3 用例建模

(1)用例文本
LR文法分析系统的使用者主要有两类:文法处理使用者、文法分析使用者;
文法处理分为LR0文法处理和LR1文法处理;
文法分析分为LR0文法分析和LR1文法处分析。

(2)LR文法分析用例图:

四、LR语法分析器设计模型

4.1 LR法分析器对象模型

4.1.1 完善对象模型

利用继承机制共享公共性质,对系统中的类加以组织:

  • 终结符和非终结符继承符号类
  • LR0文法类和LR1文法类及其相关的类继承LR文法及其相关的类。

4.1.2 绘制类图

4.2 主要算法

4.2.1 LR(0)项目集的闭包算法

LR0项目集闭包算法:

  • 输入:LR0项目集。
  • 输出:LR0项目集的闭包。
function CLOSURE (I)
begin
    J:= I;
    repeat for J 中的每个项目A →α·Bβ和规则 B→γ
        若 B→·γ不在 J 中 Do
            将 B→·γ加到J中
         until 再没有项目加到 J 中
 return J
end;

4.2.2 LR(1)项目集的闭包算法

LR1项目集闭包算法:

  • 输入:LR1项目集。
  • 输出:LR1项目集的闭包。
function CLOSURE (I)
begin
  J:= I;
 repeat for J 中的每个项目 A→α·Bβ,a和产生式 B→γ;
        α,β,γ∈V*;b∈FIRST(βa),
        若 B→·γ, b 不在 J 中
        Do 
            将 B→·γ, b 加到 J 中
         until 再没有项目加到J中
 return J
end.

4.2.3 计算GO函数和LR0项目集规范族

计算GO函数和LR0项目集规范族算法:

  • 输入:LR0项目集。
  • 输出:GO函数和LR0项目集规范族。
C=I0 ,I1 , ... ,In 
procedure itemsets(G’);
begin C :=  CLOSURE (S’→·S)
repeat
  for C 中每一项目集 I 和每一文法符号 x 
        do
            if GO(I,x) 非空且不属于C
         then 把 GO(I,x) 放入 C 中
        until C 不再增大
end;

4.2.4 计算GO函数和LR1项目集规范族

计算GO函数和LR1项目集规范族算法:

  • 输入:LR1项目集。
  • 输出:GO函数和LR1项目集规范族。
C=I0 ,I1 , ... ,In 
procedure itemsets(G’);
Begain
    C:= Closure( S′→·S,#) ;
    Repeat For C 中的每个项目集 I 和每个符号 X Do
        If Go(I,X)非空且不属于 C  
        Then 把 Go(I,X)加入 C 中
    Until C 不再增大
End

4.2.5 分析输入串

LR分析程序算法:

  • 输入:待分析符号串。
  • 输出:LR文法分析结果。
初始化,初始状态 S0 在分析栈栈顶;
输入串 W# 的第一个符号读入 a 中。
while (ACTION[S,a]!=acc) 

    if (ACTION[S,a]==Si)
    
        将 Si 和 a 进栈,将下一个输入符号读入 a ;
    
    else if (ACTION[S,a]==rj) 
    
        用第 j 条规则 A→x 规约;
        将 |x|个状态和|a|个文法符号退栈;
        假设当前栈顶状态为 S’,将 A 和GOTO[S’,A]=S”进栈;
    
    else if (ACTION[S,a]==ERROR) 
        error();

五、LR语法分析器系统实现

5.1 文法类及其子类的定义

5.1.1上下文无关文法类(基类)

5.1.2 LR0文法类(子类)

5.1.3LR1文法类(子类)

5.2 分析类的定义

5.2.1 文法分析类(父类)

5.2.2 LR分析类(子类)

六、系统测试

6.1测试主程序

6.2 测试数据及测试结果

6.2.1测试样例

测试样例一:

测试样例二:

6.2.2 测试结果

样例一分析结果如下:

*i=i#   分析过程:
[parsing]
栈顶         输入     查表 动作                                  注
------------+--------+----+-------------------------------------+------
 0 #            *      s2  进栈  2  *
 2 *            i      s3  进栈  3  i
 3 i            =      r4  出栈  1 个符号和状态 进栈:7  L       L -> i
 7 L            =      r5  出栈  1 个符号和状态 进栈:6  R       R -> L
 6 R            =      r3  出栈  2 个符号和状态 进栈:5  L       L -> * R
 5 L            =      s8  进栈  8  =
 8 =            i      s10 进栈  10 i
10 i            #      r4  出栈  1 个符号和状态 进栈:12 L       L -> i
12 L            #      r5  出栈  1 个符号和状态 进栈:11 R       R -> L
11 R            #      r1  出栈  3 个符号和状态 进栈:1  S       S -> L = R
 1 S            #      acc 成功接收!
------------+--------+----+-------------------------------------+------
 end!
文法分析正确!


样例二分析结果如下:

abab#   分析过程:
[parsing]
栈顶         输入     查表 动作                                  注
------------+--------+----+-------------------------------------+------
 0 #            a      s3  进栈  3  a
 3 a            b      s4  进栈  4  b
 4 b            a      r3  出栈  1 个符号和状态 进栈:8  B       B -> b
 8 B            a      r2  出栈  2 个符号和状态 进栈:2  B       B -> a B
 2 B            a      s6  进栈  6  a
 6 a            b      s7  进栈  7  b
 7 b            #      r3  出栈  1 个符号和状态 进栈:9  B       B -> b
 9 B            #      r2  出栈  2 个符号和状态 进栈:5  B       B -> a B
 5 B            #      r1  出栈  2 个符号和状态 进栈:1  S       S -> B B
 1 S            #      acc 成功接收!
------------+--------+----+-------------------------------------+------
 end!
文法分析正确!

附录(cpp源代码):

#include <bits/stdc++.h>
using namespace std;

// seta=seta U setb seta未改变返回值为false,否则为true
bool insertSet(set<string> &seta, set<string> setb)

    int asize = seta.size();
    for (auto i : setb)
    
        seta.insert(i);
    
    return seta.size() != asize;


struct Reflect

    int first;
    string sign;
    int next;
;

struct Formula

    string left;
    vector<string> right;
    Formula() 
    Formula(string l, vector<string> r)
    
        left = l;
        right = r;
    
    bool operator<(Formula it) const //重载<,放入map.key
    
        if (left != it.left)
            return (left < it.left);
        else
        
            if (right.size() != it.right.size())
                return right.size() < it.right.size();
            else
            
                for (int i = 0; i < right.size(); i++)
                
                    if (right[i] != it.right[i])
                        return right[i] < it.right[i];
                
            
        
        return false;
    
    bool operator==(Formula f) const //重载==比较
    
        return (left == f.left && right == f.right);
    
;

struct item

    Formula formula;    //产生式
    int dot;            //点的位置,-1表示到达最后
    set<string> symbol; //展望串
    item() 
    item(Formula f, int d)
    
        formula = f;
        dot = d;
    

    bool operator<(item it) const //重载<,放入set
    
        if (symbol != it.symbol)
        
            return symbol < it.symbol;
        
        if (dot != it.dot)
            return dot < it.dot;
        return formula < it.formula;
    
    bool operator==(item it) const //重载==比较
    
        return (formula == it.formula && dot == it.dot && symbol == it.symbol);
    
;

class CFG

protected:
    unordered_set<string> VT;     //终结符集
    unordered_set<string> VN;     //非终结符集
    string startSymbol;           //开始符号
    map<int, Formula> production; //产生式集

public:
    CFG() 
    ~CFG() 

    //是终结符
    bool isVT(string str)
    
        return VT.find(str) != VT.end();
    

    //是非终结符
    bool isVN(string str)
    
        return VN.find(str) != VN.end();
    

    string getStartSymbol()
    
        return startSymbol;
    
    void setStartSymbol(string str)
    
        startSymbol = str;
    
    unordered_set<string> getVT()
    
        return VT;
    
    void setVT(unordered_set<string> vt)
    
        VT = vt;
    
    unordered_set<string> getVN()
    
        return VN;
    
    void setVN(unordered_set<string> vn)
    
        VN = vn;
    
    map<int, Formula> getProduction()
    
        return production;
    
    void setProduction(map<int, Formula> pr)
    
        production = pr;
    

    //重载>>
    friend istream &operator>>(istream &in, CFG &cfg)
    
        int num;
        in >> num;
        for (int i = 0; i < num; i++)
        

            string s;
            in >> s;
            cfg.VN.insert(s);
        
        in >> num;
        for (int i = 0; i < num; i++)
        
            string s;
            in >> s;
            cfg.VT.insert(s);
        
        in >> num;
        getchar();
        for (int i = 0; i < num; i++)
        
            string str;
            string s;
            char ch;
            vector<string> vct;
            in >> s;
            in >> str;

            while (true)
            
                ch = in.get();
                if (ch != ' ')
                    break;
                in >> str;
                vct.push_back(str);
            
            Formula f(s, vct);
            cfg.production[i] = f;
        
        string s;
        in >> s;
        cfg.setStartSymbol(s);

        return in;
    
    //重载<<
    friend ostream &operator<<(ostream &os, CFG &cfg)
    
        os << " CFG=(VN,VT,P,S)";
        os << "\\n  VN:";
        for (auto i : cfg.VN)
        
            os << ' ' << i;
        
        os << "\\n  VT:";
        for (auto i : cfg.VT)
        
            os << ' ' << i;
        
        os << "\\n  Production:" << endl;
        for (auto pr : cfg.production)
        
            os << "     " << pr.first << ':' << ' ' << pr.second.left << " ->";
            for (auto j : pr.second.right)
            
                os << ' ' << j;
            
            os << endl;
        
        os << "  StartSymbol:";
        os << ' ' << cfg.startSymbol << endl;
        return os;
    
;

class CFG_LR1 : public CFG

private:
    map<string, set<string>> firstSet; // first集
    map<int, set<item>> itemset;       //项目集
    vector<Reflect> GOfuction;         // GO映射
public:
    CFG_LR1();
    ~CFG_LR1();
    map<int, set<item>> getItemset();
    vector<Reflect> getGOfuction();
    //计算First集合
    void culculateFirstSet();
    //建立项目集
    void createItem();
    //输出项目集
    void outPutItem();
    //求闭包
    void Closure(set<item> &ist);
;

CFG_LR1::CFG_LR1() 

CFG_LR1::~CFG_LR1() 

vector<Reflect> CFG_LR1::getGOfuction()

    return GOfuction;


map<int, set<item>> CFG_LR1::getItemset()

    return itemset;


void CFG_LR1::culculateFirstSet()

    //初始化空集合
    set<string> us;
    for (auto i : getVN())
    
        firstSet[i] = us;
    
    //遍历表达式
    while (true)
    
        bool changeFlag = false;
        for (auto pr : getProduction())
        
            //如果X->ε,把ε加入first(X)
            if (pr.second.right[0] == "ε")
            
                if (firstSet[pr.second.left].find("ε") != firstSet[pr.second.left].end())
                    continue;
                firstSet[pr.second.left].insert("ε");
                changeFlag = true;
            
            else // X->Y1Y2...
            
                for (int i = 0; i < pr.second.right.size(); i++)
                
                    string symbol = pr.second.right[i];
                    if (getVT().find(symbol) != getVT().end())
                    
                        if (firstSet[pr.second.left].find(symbol) != firstSet[pr.second.left].end())
                            break;
                        firstSet[pr.second.left].insert(symbol)

以上是关于编译原理LR语法分析器的设计与实现的主要内容,如果未能解决你的问题,请参考以下文章

编译原理LR语法分析器的设计与实现

编译原理--语法分析之LR分析法的简单实现

编译原理 LL语法分析器的设计与实现

编译原理 LL语法分析器的设计与实现

编译原理 LL语法分析器的设计与实现

编译原理 LL语法分析器的设计与实现