编译原理课程设计 基于Java的LL(1)文法预测分析程序


1概述

语法分析器的任务在于,对词法分析器识别出的单词符号串进行识别分析,如程序中的表达式、说明语句等各类型语句,对于语法分析主要采取的是上下文无关文法进行描述的,且主要分为自上而下的语法分析以及自下而上的语法分析,而在自上而下的语法分析中存在回溯现象,为了避免回溯,在自上而下的语法分析方法中,主要采用LL(1)语法分析方法,LL(1)分析方法的主要特点为:文法不含左递归,文法中每一个非终结符的各个产生式的候选首符集两两不相交,对输入的字符,都能精确的选取候选式进行工作,进而避免回溯。

2需求分析

LL(1)预测分析器由分析表,符号栈,和总控程序组成。为了方便使用分析器,本程序通过从程序的主界面选择txt文本读取文法的形式,自动生成分析表,在分析表的界面,可以输入句子,判断句子符不符合该文法,并出现提示。使用者需要把文法写入到某个txt文本中,且要注意格式,格式不对会提示。本程序不提供LL(1)文法判断功能,所以使用者要自己判断所选择的文法是不是LL(1)文法,另外对文法格式也有要求,一个非终结符号的产生式,只能写在一行,如E->+TE|-TE,而不能分成E->+TE,E->-TE,且非终结符号只能为一个字符,即不能出现诸如E'的形式,该形式为两个字符。

预测分析器用Java语言编写,界面分为文件选择界面,分析表界面。为了构造分析表,加入了求解文法的first集合和follow集合的模块。除了图形界面,在控制台,程序还可以打印文法follow集合和first集合,以及分析表的预测分析步骤

3概要设计

3.1开发工具及平台

软件:应用eclipse集成开发工具,用Java语言编写预测分析器。

硬件:Intel CPU 2950m

操作系统:WIN10

3.2主要开发技术

3.2.1 Java编程语言

Java 是面向对象程序设计语言。Java 语言具有以下特性:简单性高效、面向对象、平台无关性、交互式特性、多线程机制、动态内存管理机制等多种特性。这些特性为程序员编写高质量的程序提供保障,也是 Java 在大数据时代和移动互联网时代能处于主流地位的基础。

3.2.2 Swing开发工具包

Swing是一个用于开发java应用程序用户界面的开发工具包,通过Swing工具包能够快速设计、编写java应用程序的用户界面,利用该开发工具包来设计、开发G文法的递归下降分析器的用户界面。

3.3设计原理

根据文法规则,生成每个非终结符的Follow集,Firs集。通过两个集合构造预测分析表。总控程序,根据分析表,输入串的当前符号,符号栈栈顶符号决定执行的动作。

FIRST集的算法思想:

如果产生式右部第一个字符为终结符,则将其计入左部first集,如果产生式右部第一个字符为非终结符,求该非终结符的first集,将该非终结符的非εfirst集计入左部的first集,若存在ε,则将指向产生式的指针右移,若不存在ε,则停止遍历该产生式,进入下一个产生式,若已经到达产生式的最右部的非终结符,则将ε加入左部的first集,处理数组中重复的first集中的终结符。

求FOLLOW集的算法思想:

  对于文法G的每个终结符A构造Follow(A)的办法是,连续使用下面的规则,直至每个Follow不在增大为止。

 1)对于文法的开始符号S,置#于Follow(S)中

 2)若 A->aBb是一个产生式,则把First(b)\{ε}加至Follow(B)中

 3)若A->aB是一个产生式,或A->aBb是一个产生式而b→ε,则把Follow(A)加至Follow(B)中。

构造分析表M的算法是:

 (1) 对文法G的每个产生式A->a执行第二步和第三步;

 (2) 对每个终结符a∈FIRST(a),把A->a加至M[A,a]中;

 (3) 若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;

 (4) 把所有无定义的M[A,a]标上出错标志。

 

3.4主要功能的模块结构

 

 

模块结构图

4.1程序主体设计

程序打开时的界面,功能主要是导入存放文法的txt文件,并将文件构造成文法,调用其他的类生成其First集,Follow集,再根据这两个集合构造分析表。把表传入分析表界面将其显示出来。

    操作过程如下图:

 

图 2 主界面

 

3 选择文件界面

4 显示打开的内容

 

5 分析表界面

 

6 输入句子 

 

7控制台显示的两个集合

  

 

 

 

8 控制台显示的分析步骤

4.2分析表构造

     分析表构造,首先要求出文法的First集和Follow集。由于Follow集需要求解First集,故而设计时Follow类中带有First集合。因此,生成表时只需传入Follow集。

     下面给出求解文法First集的流程图。

9 求解文法First集的流程图

 

4.3分析句子

以下是句子分析功能实现的详细代码:

public String analyseSentence(String sentence){

 ConsoleTable t = new ConsoleTable(4, true);

 t.appendRow();

 t.appendColum("------步 骤 ------").appendColum("--符号栈--------").appendColum("输入---------").appendColum("所用产生式");

        sentence+="#";

        TableCell ts=null;

        Stack<Character> stack=new Stack<Character>();

        stack.push('#');

        int index=0;

        stack.push(rules.getStart());

        Character temp=null;

        boolean flag=true;

        int count=0;

        //输出信息

        StringBuffer stackString=new StringBuffer();

        for(int i=0;i<stack.size();i++){

            stackString.append(stack.get(i));

        }

        t.appendRow();

        t.appendColum( count).appendColum(stackString).appendColum(sentence.substring(index)).appendColum(" ");

        if(rules.getTerminators().contains('#')){

        for(int i=0;i<rules.getTerminators().size();i++){

        if(rules.getTerminators().get(i)=='#')

        rules.getTerminators().remove(i);

        }

        }

        while(flag){

            count++;

            temp=(Character)stack.peek();

            if(rules.getTerminators().contains(temp)){

                if(temp==sentence.charAt(index)){

                    stack.pop();

                    index++;

                }else{

                    return "ERROR";

                }

                //输出信息

                stackString=new StringBuffer();

                for(int i=0;i<stack.size();i++){

                    stackString.append(stack.get(i));

                }

                t.appendRow();      t.appendColum(count).appendColum(stackString).appendColum(sentence.substring(index)).appendColum(" ");

            }else if(temp=='#'){

                if(temp==sentence.charAt(index)){

                    flag=false;

                }else {

                    return "ERROR";

                }

            }else if((ts=ifExistsCell(temp,sentence.charAt(index)))!=null){

                stack.pop();

                if(!ts.getRuleRight().equals("ε")){

                    for(int i=ts.getRuleRight().length()-1;i>=0;i--){

                        stack.push(ts.getRuleRight().charAt(i));

                    }

                }

 

                //输出信息

                stackString=new StringBuffer();

                for(int i=0;i<stack.size();i++){

                    stackString.append(stack.get(i));

                }

                t.appendRow();         t.appendColum(count).appendColum(stackString).appendColum(sentence.substring(index)).appendColum(ts.getRuleLeft()+"->"+ts.getRuleRight());

             //   System.out.println(count+" "+stackString+" "+sentence.substring(index)+ts.getRuleLeft()+"->"+ts.getRuleRight());

            }else{

                return "ERROR";

            }

        }

        System.out.println(t.toString());

        return "SUCCESS";

}

其算法如下

对于栈顶符号X和当前的输入符号,总控程序每次都执行下述三种可能的动作。

(1)X=a=‘#’,则宣布分析成功,停止分析。

(2)X=a!=‘#’,则把X弹出栈顶,让a指向下一个符号。

(3)X是一个非终结符,则查看分析表M。若M[A,a]中存放着关于X的一个产生式,那么,首先把X弹出栈顶,然后把产生式右部反序压入栈中,若右部符号为ε,则不用推什么东西进栈。把产生式的右部符号压入栈中时,同时做这个产生式相应的语义动作。M[A,a]存放出错标志,则报错,停止分析。

5程序测试

对于文法G,产生规则如下,使用设计表里的用例测试,结果如下图。

B->*FB|ε

T->FB

E->TA

F->(E)|i

A->+TA|ε

1 用例设计表

类别

用例

合法性

i+i+i

(i+i)*(i+i)

i

非法性

i+*i

i*(i

7

 

10 输入i+i+i,分析

 

11 输入(i+i)*(i+i),分析

 

12 输入i,分析

 

13 输入i+*i,分析

 

输入i*(i,分析

 

输入7,分析

整个程序参考了在网上找到的一个程序的源码。其中First集是自己做的,Follow集、构造分析表和分析句子模块参考了别人的代码。求解Follow集时重要的是消去递归中的死循环,构造分析表时要考虑填入哪个产生式。


  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值