编译原理实验 -- 文法分析

编译原理实验 – 文法分析

终结符 和 非终结符

  • 终结符

通常使用小写字母表示,例如 abcdef

  • 非终结符

通常使用大写字母表示,例如 ABCDEF

产生式

通俗来说,就是由终结符和非终结符组合成的一个式子,例如

A -> bad

Ab -> Acd

AB -> BA

文法类型

文法定义的形式-四元组(Vn,Vt,P,S): Vn为非终结符集,Vt 为终结符集,P为规则集,S为识别符|开始符,至少要在一个规则中作为左部出现,Vn ∩ Vt = ∅。根据对文法施加不同的限制,分成4种类型。

0型文法
  • 产生式 α->β
  • α中至少含有一个非终结符
  • 例如: Ac -> b ; Bcd -> Ab
1型文法
  • 产生式 α->β
  • 左边可以有多个字符,但必须有一个非终结符
  • α的长度要小于β,或者β为空
  • 例如 Abcd -> ; Ab -> bAB
2型文法
  • 产生式 α->β
  • 左边必须有且仅有一个非终结符,右边可以有多个但有限个字符
  • 例如:A -> ab ;B ->a
3型文法
  • 产生式 α->β
  • 左边必须有且仅有一个非终结符
  • 右边最多只能有两个字符,且必须一个终结符,一个非终结符
  • 所有产生式右侧终结符与非终结符顺序一样,才能算是3型文法
  • 当右侧只有一个字符时,必须是终结符
  • 例如: A -> b 或 A ->Ab 或A ->bA

总结:

0型文法包含于1型文法

1型文法包含于2型文法

2型文法包含于3型文法

代码(JAVA完成)

思路

使用JHashSet来记录每一个规则的文法类型,如下

/*
0 : 0型文法

1 : 1型文法

2 : 2型文法

3 : 右线性文法

-3 : 左线性文法

6 : 3型文法
*/

第一步,输入终结符和非终结符
 //产生式列表
static ArrayList<String> str = new ArrayList<>();
 //非终结符列表
static ArrayList<Character> vn = new ArrayList<>();
 //终结符列表
static ArrayList<Character> vp = new ArrayList<>();

//初始化符号列表,产生式列表
public static void init(){
    Scanner sc = new Scanner(System.in);
    //初始化非终结符列表
    System.out.println("请输入非终结符:");
    String vnChar = sc.nextLine();
    for (int i = 0; i < vnChar.length(); i++) {
        vn.add(vnChar.charAt(i));
    }
    //初始化终结符列表
    System.out.println("请输入终结符:");
    String vpChar = sc.nextLine();
    for (int i = 0; i < vpChar.length(); i++) {
        vp.add(vpChar.charAt(i));
    }
    //初始化产生式列表
    System.out.println("请输入产生式,输入“#”结束:");
    while (true) {
        String strC = sc.nextLine();
        if(strC.equals("#")) break;
        str.add(strC);
    }
}
第二步,判断规则的可执行性
for (String s: str) {
    //将产生式 按 ::= 分为两部分
    if(!s.contains("::=")){//不存在分隔符
        System.out.println("无效产生式"+s);
        return;
    }
    strC = s.split("::=");//分割成两部分
    if(!exist(strC)) {
        System.out.println("无效产生式 "+s);
        return;
    }
}

//先判断产生式是否有效
    public static boolean exist(String[] strings){
        if(strings.length>2||strings.length<1) return false ;//至少分成两部分
        for(String strC:strings){
            //所有字符,应该存在于终结符列表和非终结符列表
            for(char c : strC.toCharArray()){
                if(!vn.contains(c) && !vp.contains(c)) return false ;
            }
        }
        for (int i = 0; i < strings[0].length() ; i++) {
            //左侧至少有一个非终结符
            if(vn.contains(strings[0].charAt(i))) return true ;
        }
        return false ;
    }
根据文法类型的包含关系,逐级判断,并添加标记3—>2—>1—>0
//标记
static Set<Integer> mark = new HashSet<>();

//判断是不是3型文法
public static boolean threeGrammar(String[] strings){
    if(strings[0].length()==1 && strings.length>1){
        //满足左侧只有一个字符,且是非终结字符
        if(strings[1].length()<=2 && strings[1].length()>0 ){
            //满足右侧最多只有两个字符
            if(strings[1].length() == 1 && vp.contains(strings[1].charAt(0))){
                mark.add(6);
                return true  ;
            }
            if(vn.contains(strings[1].charAt(0)) && vp.contains(strings[1].charAt(1))){
                //满足左线性 -3代表左
                mark.add(-3);
                return true ;
            }
            if(vn.contains(strings[1].charAt(1)) && vp.contains(strings[1].charAt(0))){
                //满足右线性 3代表右
                mark.add(3);
                return true   ;
            }
        }
    }
    //代表非3型文法
    return false ;
}
//判断是不是2型文法
private static boolean twoGrammar(String[] strings) {
    if(strings[0].length()==1 ){
        mark.add(2);
        return true ;
    }
    return false ;
}
//判断是不是1型文法
private static boolean oneGrammar(String[] strings) {
    if(strings.length == 2) {
        if (strings[1].length() < strings[0].length()) return false;
    }
    mark.add(1);
    return true ;
}

我们已经判断了规则的可判断性,所以当不是321时,他只能是0

此时

mark.add(0);

完整代码

package com.day02;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

/**
 * @author : YWJ
 * @date : 2022/10/6 : 23:14
 */
public class Chomsky {
    //产生式列表
    static ArrayList<String> str = new ArrayList<>();
    //非终结符列表
    static ArrayList<Character> vn = new ArrayList<>();
    //终结符列表
    static ArrayList<Character> vp = new ArrayList<>();
    //标记
    static Set<Integer> mark = new HashSet<>();

    public static void main(String[] args) {
        //初始化列表
        init();
        //判断文法
        judge();
    }

    //初始化符号列表,产生式列表
    public static void init(){
        Scanner sc = new Scanner(System.in);
        //初始化非终结符列表
        System.out.println("请输入非终结符:");
        String vnChar = sc.nextLine();
        for (int i = 0; i < vnChar.length(); i++) {
            vn.add(vnChar.charAt(i));
        }
        //初始化终结符列表
        System.out.println("请输入终结符:");
        String vpChar = sc.nextLine();
        for (int i = 0; i < vpChar.length(); i++) {
            vp.add(vpChar.charAt(i));
        }
        //初始化产生式列表
        System.out.println("请输入产生式,输入“#”结束:");
        while (true) {
            String strC = sc.nextLine();
            if(strC.equals("#")) break;
            str.add(strC);
        }
    }

    //判断属于那种文法,并初始化标记列表
    public static void judge(){
        String[] strC ;
        for (String s: str) {
            //将产生式 按 ::= 分为两部分
            if(!s.contains("::=")){
                System.out.println("无效产生式"+s);
                return;
            }
            strC = s.split("::=");
            //先判断产生式是否有效
            if(!exist(strC)) {
                System.out.println("无效产生式 "+s);
                return;
            }
            //先判断是不是3型文法
            if(threeGrammar(strC)) continue;
            //判断是不是2型文法
            if(twoGrammar(strC)) continue;
            //判断是不是1型文法
            if(oneGrammar(strC)) continue;
            mark.add(0);
        }
        //给出最终结果
        res();
    }

    //判断是不是1型文法
    private static boolean oneGrammar(String[] strings) {
        if(strings.length == 2) {
            if (strings[1].length() < strings[0].length()) return false;
        }
        mark.add(1);
        return true ;
    }

    //判断是不是2型文法
    private static boolean twoGrammar(String[] strings) {
        if(strings[0].length()==1 ){
            mark.add(2);
            return true ;
        }
        return false ;
    }

    //判断是不是3型文法
    public static boolean threeGrammar(String[] strings){
        if(strings[0].length()==1 && strings.length>1){
            //满足左侧只有一个字符,且是非终结字符
            if(strings[1].length()<=2 && strings[1].length()>0 ){
                //满足右侧最多只有两个字符
                if(strings[1].length() == 1 && vp.contains(strings[1].charAt(0))){
                    mark.add(6);
                    return true  ;
                }
                if(vn.contains(strings[1].charAt(0)) && vp.contains(strings[1].charAt(1))){
                    //满足左线性 -3代表左
                    mark.add(-3);
                    return true ;
                }
                if(vn.contains(strings[1].charAt(1)) && vp.contains(strings[1].charAt(0))){
                    //满足右线性 3代表右
                    mark.add(3);
                    return true   ;
                }
            }
        }
        //代表非3型文法
        return false ;
    }

    //先判断产生式是否有效
    public static boolean exist(String[] strings){
        if(strings.length>2||strings.length<1) return false ;
        for(String strC:strings){
            for(char c : strC.toCharArray()){
                if(!vn.contains(c) && !vp.contains(c)) return false ;
            }
        }
        for (int i = 0; i < strings[0].length() ; i++) {
            if(vn.contains(strings[0].charAt(i))) return true ;
        }
        return false ;
    }

    //根据标记列表给出最终答案
    public static void res (){
        /*
        0 : 0型文法
        1 : 1型文法
        2 : 2型文法
        3 : 右线性文法
        -3 : 左线性文法
        6 : 3型文法
         */
        if(mark.contains(0)){
            System.out.println("0型文法");
            return;
        }
        if(mark.contains(1)){
            System.out.println("1型文法");
            return;
        }
        if(mark.contains(2)){
            System.out.println("2型文法");
            return;
        }
        if(mark.contains(3) && mark.contains(-3)){
            System.out.println("2型文法");
            return;
        }
        if(mark.contains(3)||mark.contains(-3)){
            System.out.println("3型文法");
            return;
        }
        System.out.println("3型文法");
    }
}
  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值