通常使用小写字母表示,例如 abcdef
通常使用大写字母表示,例如 ABCDEF
通俗来说,就是由终结符和非终结符组合成的一个式子,例如
A -> bad
Ab -> Acd
AB -> BA
文法定义的形式-四元组(Vn,Vt,P,S): Vn为非终结符集,Vt 为终结符集,P为规则集,S为识别符|开始符,至少要在一个规则中作为左部出现,Vn ∩ Vt = ∅。根据对文法施加不同的限制,分成4种类型。
总结:
0型文法包含于1型文法
1型文法包含于2型文法
2型文法包含于3型文法
使用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 ;
}
//标记
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型文法");
}
}