形式语言与自动机大作业

形式语言与自动机

1.整体流程

读取并存储CFG

预处理

转化为Greibach范式

    public void changeToGreibach_total(String filename) {
        List<GrammarNode> list = new LinkedList<>();
        list = readRule(list,filename);  //读取文法
        showRules(list);
        removeUseless(list);   //消除无用符号
        showRules(list);
        removeE(list);  //消除空产生式
        showRules(list);
        removeUnitProduction(list);  //消除单一产生式
        removeUseless(list);
        showRules(list);
        System.out.println("消除直接左递归!");
        removeLeftRecursive(list);  //消除直接左递归
        showRules(list);
        greibachFirstStep(list);
        removeUseless(list);
        showRules(list);
        greibachSecondStep(list);
        showRules(list);
        writeToTxt(list);
    }

2.代码实现

2.1 数据结构

public class GrammarNode {
    private String left;
    private String right;
    
    public String getLeft() {
        return left;
    }

    public void setLeft(String left) {
        this.left = left;
    }

    public String getRight() {
        return right;
    }

    public void setRight(String right) {
        this.right = right;
    }
}

2.2 读取 CFG

  • 读取文件
    List<GrammarNode> readRule(List<GrammarNode> list, String filePath) {
        list.clear();
        try {
            String encoding = "utf-8";
            File file = new File(filePath);
            if (file.isFile() && file.exists()){//判断文件是否存在
                InputStreamReader read = new InputStreamReader(new FileInputStream(file),encoding);//考虑到编码格式
                BufferedReader bufferedReader = new BufferedReader(read);
                String lineTxt;
                while((lineTxt = bufferedReader.readLine()) != null){
                    //读文件的行readline,每次使用都读一行
                    String[] arr =lineTxt.split(" ");
                    GrammarNode node = new GrammarNode();
                    node.setLeft(arr[0]);
                    node.setRight(arr[2]);
                    list.add(node);
                }
                read.close();
            }else{
                System.out.println("找不到指定的文件");
            }
        }catch (Exception e){
            System.out.println("读取文件错误!");
            e.printStackTrace();
        }
        return list;
    }
  • 判断是否为上下文无关文法
private void judgeContextFreeGrammar(List<GrammarNode> list)

2.3 预处理 CFG

  • 消除文法中的空产生式

    • 从P中去掉单独非终结符直接产生单独空字的产生式,去掉后的产生式集记为P’

      removeE(List<GrammarNode> list)
      
    • 把第一步中去掉的产生式代入其他产生式,获得新的产生式,把新的产生式加入P’’

      replaceV(List<GrammarNode> list, char V, String[] production_right, Set<GrammarNode> set)
      
    • 把P’’ 并入 P’,再加入新的非终结符,假如为S’,再把S’推出S和S’推出空字加入P’

  • 消除文法的单一产生式

    找到单一产生式,在文法集中找到相应的文法代替右部

    private void removeUnitProduction(List<GrammarNode> list)
    
  • 消除文法中的无用非终结符和产生式

        private void removeUseless(List<GrammarNode> list){
            System.out.println("消除无用符号!");
            HashSet<Character> usefulGrammar = new HashSet<>();
            usefulGrammar = generate(list);
            delete(list,usefulGrammar);
            usefulGrammar = reachable(list);
            delete(list,usefulGrammar);
        }
        
        private HashSet<Character> generate(List<GrammarNode> list) //获得生成的字符
        private HashSet<Character> reachable(List<GrammarNode> list)  //获得可达的字符
        private void delete(List<GrammarNode> list,HashSet<Character> useful)  //删除无用字符
        
    
  • 消除直接左递归

     private void removeLeftRecursive(List<GrammarNode> list)
    

2.4 转化为Greibach范式

private void greibachFirstStep(List<GrammarNode> list)

private void greibachSecondStep(List<GrammarNode> list)
for (int i=0;i<V.num;i++)
     for(int j=0;j<v.num;j++) //v表示V的拷贝
        if(p中存在v[j] →a && a中的每个符号都属于N)
          {将v[j]从v中删除并加到N中,同时跳出内层循环}

Reach(S){
       if(P中存在S→a)
            {将a中的所有字符加入到M中,
                if(a中存在非终结符B)
                  Reach(B)}
      return }

for(int i=0;i<Q.num;i++){//Q是VUT的集合
     for(int j=0;j<N.num;j++){
         if(Q[i]不在N)
            将其加入到非“产生的”符号集N1}} 
//  消除全部非“可达的”符号
for(int i=0;i<N1.num;i++){
   for(int j=0;j<M.num;j++){
      if(N1[i]不在M)
         将其加入到无用符号集NM}}
//消除产生式和无用符号,因结构类似只写了一个
for(int i=0;i<P.num;i++){
    if(P[i]个产生式中含有NM符号集的元素)
        将该条产生式删除}}

		for(int i=0;i<V.num;i++)
           for(int j=0;j<V.num;j++){
              if(左部为V[i]的产生式右部所有符号都在V1)V[i]加入V1中,跳出内层循环;
		
        for(int i=0;i<P.num;i++)
          if(存在产生式B→a0B1...Bkak∈P,aj∈(VUT)*,BiV1)Bi或者空代替Bi,并将形成的产生式加入到P|中;
          else if(产生式左部属于V1)
                将ε产生式去除后将其加入P|;            
          else if(若有S→ε)
           引入S|→ε|SS|为新的开始符号;}


# 消除无用符号
将T加入产生符号集Nwhile(N中有新的符号加入)
    for(i=0;i<P.num;i++)
        if(产生式的右部都在N中)
        	将产生式的左部加入N中
        
# 消除无用符号
将S加入可达符号集Mwhile(M中有新的符号加入)
    for(i=0;i<P.num;i++)
        if(产生式的左部在M中)
        	将产生式的右部中的非终结符加入Mfor(i=0;i<P.num;i++)
        if(P[i]为单一产生式A->B,记录其右部部为B,删除该产生式)
            for(j=0;j<P.num;j++)
                if(P[j]的左部为B)B的右部部复制到A->B的右部
                    将新产生式加入到P

3. PPT

    public void changeToGreibach_total(String filename) {
        List<Grammar> list = new LinkedList<>();
        list = readRule(list,filename);  //读取文法
        removeUseless(list);   //消除无用符号
        removeE(list);  //消除空产生式
        removeOnlyProduction(list);  //消除单一产生式
        removeUseless(list);
        removeDirectLeftRecursion(list);  //消除直接左递归
        removeIndirectLeftRecursion(list);   //消除间接左递归
        removeUseless(list);
        getGreibach(list);  //转换为Greibach范式
        showRules(list); 
        writeToTxt(list);
    }
3.1 消除无用符号

​ 因为所有的终结符都是可产生的,所以先将所有的非终结符加入到可产生符号集N中。这里采用双层循环,外层循环的终止条件为可产生符号集中没有新的符号加入。内层循环每次遍历文法集,找到右部全部为可产生符号的产生式,如果其左部符号不属于可产生符号集中,我们就将其左部符号加入可产生符号集中。直到不满足外层循环的条件,这时的可达符号集中就是所有的可产生符号,我们对比原产生式集合,将不可产生的符号进行删去。

​ 消除主要采用递归的思想。从初始符号S开始,遍历产生式,找到以当前非终结符为左部的产生式,如果右部都为终结符,则将符号都加入到可达符号集中。如果有非终结符,则对此符号进行递归操作。

​ 第二种为非递归思想,与可产生类似,通过不断向可产生符号集中加入可产生的符号,直到没有新的可产生符号终止。

3.2 消除空产生式

​ 遍历文法找到空产生式,删除该产生式,然后记录左部符号。接着遍历文法,找到右部含有该非终结符的产生式,带入空形成新的产生式。将所有新产生式加入到原产生式集中,继续遍历,直到无空产生式。

3.3 消除单一产生式

​ 外层循环,遍历文法找到单一产生式,记录并删除,并记录右部符号。内层循环,遍历文法找到左部为该符号的所有产生式,带入外层记录的单一产生式,将新的产生式加入到产生式集合中。

3.4 消除直接和间接左递归

​ 直接左递归产生式如例所示,分析可以看出,此文法产生的语言必定是以b开头,后跟若干的a。也就是说文法若想停止生成,必然结束于第二条产生式。因此,我们可以将其替换为如下的产生式。

​ 消除间接左递归的方法,核心为转化为直接左递归然后消除。方法将文法中的所有非终结符以 Ai 的形式重新命名。然后要求左部的 i 的值要小于右部 i 的值。实际代码中,遍历所有产生式,对于右部第一个字母是大写字母就用该大写字母的产生式替换,找到是否有直接左递归,有的话就消除。

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值