编译原理实验4(自下而上语法分析(LR分析) )

  • 实验目的
  1. 给出 PL/0 文法规范,要求编写 PL/0 语言的语法分析程序。
  2. 通过设计、编制、调试一个典型的自上而下语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。
  3. 选择有代表性的语法分析方法,本次实验用LR文法分析;
  4. 选择对各种常见程序语言都具备的语法结构,如赋值语句,特别是表达式,作为分析对象。
  5. 提高自己的实践能力和解决问题的能力。
  • 实验原理
  1. 了解符已给 PL/0 语言文法,构造表达式部分的语法分析器。

分析对象〈算术表达式〉的 BNF 定义已给出。

  1. 将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,对于语法正确的表达式,输出“语法正确”;对于语法错误的表达式,输出“语法错误”,指出错误原因。

  • 实验内容

1.了解符已给 PL/0 语言文法,构造表达式部分的语法分析器。

分析对象〈算术表达式〉的 BNF 定义如下:

<表达式> ::= [+|-]<项>{<加法运算符> <项>}

 <项> ::= <因子>{<乘法运算符> <因子>}

<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’

<加法运算符> ::= +|-

<乘法运算符> ::= *|/

<关系运算符> ::= =|#|<|<=|>|>=

2.将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,对于语法正确的表达式,输出“语法正确”;对于语法错误的表达式,输出“语法错误”,指出错误原因。

3.输入:

4.输出:

  • 实验算法流程图
  1. 算法思想

下面主要介绍LR(0) 文法

(1)定义:文法 G 是 LR(0) 文法,当且仅当它的LR(0)FSM中的每个状态都满足:

①不同时含有移进项目和归约项目,即不存在移进-归约冲突。

②不含有两个以上归约项目,即不存在归约-归约冲突。

(2)LR(0)分析表的构造

ACTION 表项和 GOTO表项可按如下方法构造:

若项目A ->α • aβ属于 Ik 且 GO (Ik, a)= Ij, 期望字符a 为终结符,则置ACTION[k, a] =sj (j表示新状态Ij);

若项目A ->α • Aβ属于 Ik,且GO (Ik, A)= Ij,期望字符 A为非终结符,则置GOTO(k, A)=j (j表示文法中第j个产生式);

若项目A ->α •属于Ik, 那么对任何终结符a, 置ACTION[k, a]=rj;其中,假定A->α为文法G 的第j个产生式;

若项目S’ ->S • 属于Ik, 则置ACTION[k, #]为“acc”;

分析表中凡不能用上述规则填入信息的空白格均置上“出错标志”.

翻译一下:如果圆点不在项目k最后且圆点后的期待字符a为终结符,则ACTION[k, a] =sj (j表示新状态Ij);如果圆点不在项目k最后且圆点后的期待字符A为非终结符,则GOTO(k, A)=j (j表示文法中第j个产生式);如果圆点在项目k最后且k不是S’ ->S,那么对所有终结符a,ACTION[k, a]=rj (j表示文法中第j个产生式);

如果圆点在项目k最后且k是S’ ->S,则ACTION[k, #]为“acc”;

例子:

考虑文法G[S] :

S → (S) | a

相应的LR(0) FSM如下,构造其LR(0)分析表。

LR(0) FSM

从I0看,S‘->·S,期望字符是非终结符S,根据上面的规则2,得到GOTO(0,S)=1;

S‘->·(S),期望字符是终结符(,根据上面的规则1,得到ACTION(0,()=S2;

从I3看,S->a·,根据规则3,置ACTION[3, a]为r2;

从I1看,S‘->S·,根据规则4,置ACTION[1, #]为“acc”;

LR(0)分析表

  1. 设计思想

扩充的巴克斯范式

<表达式> ::= [+|-]<项>{<加法运算符> <项>}

<项> ::= <因子>{<乘法运算符> <因子>}

<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’

<加法运算符> ::= +|-

<乘法运算符> ::= *|/

普通的巴克斯范式

  为表示方便:

  表达式E、项X、因子Y、标识符b,无符号整数z,加法运算符A,乘法运算符C

E->AX|X|EAX

X->Y|XCY

Y->b|z|(E)

A->+|-

C->*|/

消除左递归

E->XE’|AXE’

E’->AXE’|ε

X->YX’

X’->CYX’|ε

Y->b|z|(E)

A->+|-

C->*|/

  1. LR分析算法描述

对给定的输入串,给出其分析过程及正确与否的判断

将S0移进状态栈,#移进符号栈,S为状态栈栈顶状态

begin

  a=getsym()  //读入第一个符号给a

  while(ACTION[S,a]!=acc)

If ACTION[S,a]=si   then

PUSH  i,a(分别进栈);输出进栈信息

a=getsym();//读入下一个符号给a

else  if ACTION[S,a] = rj (第j条产生式为A→β)  then

          输出归约信息

将状态栈和符号栈分别弹出|β|项; push(A);

将GOTO[S’,A]移进状态栈(S’为当前栈顶状态);

        else  error;

    输出分析结果,接受或出错

   End

  1. LR分析器模型

  1. 具体步骤

就1)移进【shift】

若ACTION[Si,a]=Sj,a为终结符,则把a移入文法符号栈,Sj移入状态栈;

(2)归约【reduce】

若ACTION[Si,a]=rj,a为终结符或#,则用第j个产生式(A->β)归约,将两个栈弹出∣β∣个元素,这时当前面临符号为第j个产生式左部的非终结符(A);若状态栈当前的栈顶状态为Sk,且GOTO[Sk,A]=j,则非终结符A移入符号栈,j移入状态栈;

(3)接受【accept】

若ACTION[Si,a]=acc,a为#,则为接受,表明分析成功;

(4)报错【error】

若ACTION[Si,a]=空白,则转向出错处理。

  1. 总控程序算法框架

  1. 算法流程图

整体算法流程图

LR算法流程图

  • 实验结果与分析
  1. 案例验证

输入一个文法进行验证

文法为:

这个文法之前手算过,所以可以和计算机的结果进行对比,判断实验结果是否正确。

  1. >E+T|T
  1. >T*F|F
  1. >(E)|i

用LR文法分析

得到该文法的项目

项目集规范族

识别活前缀的DFA

消除左递归后的产生式

first集

follow集

LR分析表

修改代码,按itc 要求,提交到itc中

成功编译,但有警告信息。

根据程序运行得到的结果可以看出:编写的代码正确。

  • 实验体会

1.本次实验是利用LR分析法进行语法分析,通过代码实现后更加了解了LR(K)分析方法是严格的从左向右扫描,和自底向上的语法分析方法。

2.LR分析法小结:

LR(0)构造的DFA,但是由LR(0)的DFA来构造分析表往往是不可行的,也就是说很少有语言是LR(0)的。

如果LR(0)的冲突可以根据当前输入的字符来解决,则构造的分析表为SLR分析表,SLR分析表跟LR(0)的dfa是相同的。

LR(1)构造的DFA考虑当前输入的字符,所以构造的DFA中存在的冲突比较少,也就是说它接受的语言就比较多。构造分析表时,不用额外的约束,因为它在构造dfa时就已经考虑了当前的输入字符。

考虑到LR(1)的dfa包含的状态太多,而且许多状态的不同仅仅是因为预测符的不同而已。通过合并不会产生冲突的状态,可以减少dfa的状态数,这就产生了LALR分析法。所以说LALR分析法只是对LR(1)的一种简化:通过合并同心集。

分析法的分析能力比较:LR(1)>LALR>SLR(1)>LR(0)。

3.实验心得

第四次编译原理的实验课,最大的收获是复习了hash函数的相关知识,从代码的层面深入的学习了符号表的建立与使用,同样提高了使用指针的熟练程度。

这次实验难度不大,主要是理解和掌握杂凑表(哈希表)的插入和查找等操作,以及对Symbol单链表,SymbolTable结构体的理解对编写代码尤为重要。

这是最后一次编译原理实验课,总结四次实验课的经验与教训:理解好项目给出的数据结构,以及演示模式程序执行的步骤和输出结果,熟练掌握数据结构的相关知识是完成代码的关键。

总的来说,通过四次实验课,既加深了我对编译原理课程重点内容的理解,又复习了数据结构的相关知识,提高了编写代码的能力,收获良多。

最后,特别感谢刘老师一直以来的耐心指导和同学的热心帮助!

  • 附录

  • 28
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!lr(0)语法分析器是一种自下而上语法分析器,它可以用于对文法进行自动分析和语法检查。在Java中,可以使用ANTLR框架来实现lr(0)语法分析器。ANTLR是一种高性能的语法分析器生成器,它可以根据语法文件自动生成语法分析器。以下是一个简单的lr(0)语法分析器的Java代码示例: ``` import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; public class MyParser { public static void main(String[] args) throws Exception { String input = "1+2*3"; ANTLRInputStream inputStream = new ANTLRInputStream(input); MyLexer lexer = new MyLexer(inputStream); CommonTokenStream tokens = new CommonTokenStream(lexer); MyParser parser = new MyParser(tokens); ParseTree tree = parser.expr(); System.out.println(tree.toStringTree(parser)); } } class MyLexer extends Lexer { public static int PLUS = 1; public static int MULT = 2; public static int INT = 3; public MyLexer(CharStream input) { super(input); } public Token nextToken() { if (_input.LA(1) == '+') { consume(); return new CommonToken(PLUS, "+"); } else if (_input.LA(1) == '*') { consume(); return new CommonToken(MULT, "*"); } else if (_input.LA(1) >= '0' && _input.LA(1) <= '9') { int start = _input.index(); while (_input.LA(1) >= '0' && _input.LA(1) <= '9') { consume(); } return new CommonToken(INT, _input.getText(start, _input.index() - 1)); } else { throw new RuntimeException("Invalid character: " + (char)_input.LA(1)); } } } class MyParser extends Parser { public MyParser(TokenStream input) { super(input); } public ParseTree expr() { ParseTree left = term(); while (true) { if (match(MyLexer.PLUS)) { ParseTree right = term(); left = new PlusExpr(left, right); } else { break; } } return left; } public ParseTree term() { ParseTree left = factor(); while (true) { if (match(MyLexer.MULT)) { ParseTree right = factor(); left = new MultExpr(left, right); } else { break; } } return left; } public ParseTree factor() { Token token = match(MyLexer.INT); return new IntExpr(Integer.parseInt(token.getText())); } public Token match(int type) { Token token = _input.LT(1); if (token.getType() == type) { consume(); return token; } else { throw new RuntimeException("Expecting " + getTokenName(type) + "; found " + token.getText()); } } } abstract class Expr extends RuleContext { public abstract int eval(); } class PlusExpr extends Expr { private ParseTree left; private ParseTree right; public PlusExpr(ParseTree left, ParseTree right) { this.left = left; this.right = right; } public int eval() { return ((Expr)left).eval() + ((Expr)right).eval(); } public String toString() { return "(" + left.toString() + " + " + right.toString() + ")"; } } class MultExpr extends Expr { private ParseTree left; private ParseTree right; public MultExpr(ParseTree left, ParseTree right) { this.left = left; this.right = right; } public int eval() { return ((Expr)left).eval() * ((Expr)right).eval(); } public String toString() { return "(" + left.toString() + " * " + right.toString() + ")"; } } class IntExpr extends Expr { private int value; public IntExpr(int value) { this.value = value; } public int eval() { return value; } public String toString() { return Integer.toString(value); } } ``` 这段代码实现了一个简单的四则运算解析器,可以识别表达式中的加号、乘号和整数,并计算表达式的值。您可以根据自己的需求修改代码来实现自己的lr(0)语法分析器

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值