java实现算符优先文法ll1文法

实验要求:

1.[实验项目]

实现LL(1)分析中控制程序(表驱动程序);完成以下描述赋值语句的LL(1)文法的LL(1)分析过程。

G[E]:

E →E+T∣E-T∣T

T→T*F∣T/F∣F

F→(E)∣i

2.[设计说明]

终结符号i 为用户定义的简单变量,即标识符的定义。

3.[设计要求]

(1)构造该算符优先文法的优先关系矩阵或优先函数;

(2)输入串应是词法分析的输出二元式序列,即某算术表达式“专题
1”的输出结果。输出为输入串是否为该文法定义的算术表达式的判断结果。

(3)算符优先分析过程应能发现输入串出错。

(4)设计两个测试用例(尽可能完备,正确和出错),并给出测试结果;

(5)考虑编写程序根据算符优先文法构造算符优先关系矩阵,并添加到你的算符优先分析程序中。

实验过程:

1、算符优先分析程序设计说明
1.1设计要求:

(1)构造该算符优先文法的优先关系矩阵或优先函数;

(2)输入串应是词法分析的输出二元式序列,即某算术表达式“专题
1”的输出结果。输出为输入串是否为该文法定义的算术表达式的判断结果。

(3)算符优先分析过程应能发现输入串出错。

(4)设计两个测试用例(尽可能完备,正确和出错),并给出测试结果;

(5)考虑编写程序根据算符优先文法构造算符优先关系矩阵,并添加到你的算符优先分析程序中。

1.2设计说明:

(1)该语言大小写不敏感;

(2)字母为a-zA-Z,数字为0-9;

(3)对文法进行扩充和改造;

(4)根据FIRSTVT集和LASTVE集的构造规则,构造出文法的FIRSTVT集和LASTVT集

(5)根据FIRSTVT集和LASTVT集构造算符优先矩阵;

(6)根据分析规则,构造算符优先矩阵分析器。

3、程序功能描述

(1)、能够录入一个.tys文件中的二元式内容;二元式内容为表达式

(2)、根据.tys文件内容进行算符优先分析,可识别

(3)、根据输入的二元式内容进行分析语法分析,并打印结果;

(4)、打印分析过程(分析栈和保留串)和错误提示;

(5)、根据文法构造FIRSTVT集和LASTVT集;

(6)、根据构造FIRSTVT集和LASTVT集,构造出算符优先矩阵分析表;

(7)、算符优先矩阵分析器。

4、主要的数据结构描述
4.1主要使用的java数据结构类型
4.1.1 List

//终结符号集

private List<Character> Vt = new ArrayList<Character>();

//非终结符号集

private List<Character> Vn = new ArrayList<Character>();

使用List保存文法的非终结符号和终结符号,一旦文法给定,非终结符号集合和终结符号集合也就确定,所以对Vt、Vn的操作一般是查找元素和遍历元素。

List添加元素

//设置非终结符号

Vn.add(‘S’);

Vn.add(‘E’);

Vn.add(‘T’);

Vn.add(‘F’);

//设置终结符号

Vt.add(’#’);

Vt.add(’+’);

Vt.add(’-’);

Vt.add(’*’);

Vt.add(’/’);

Vt.add(’(’);

Vt.add(’)’);

Vt.add(‘i’);

使用add()方法即可添加元素,上面的程序保存了Vt、Vn集合。

list中是否包含某个元素

方法:.contains(Object o); 返回true或者false

例如下面在程序中使用到的:

Vn.contains(nArryStr[i].charAt(0))&&Vt.contains(nArryStr[i].charAt(1))

list获取长度:

Vt.size()

使用size()即可返回List的长度

list中查看(判断)元素的索引

注意:.indexOf(); 和 lastIndexOf()的不同;

List<String> names=new ArrayList<>();

names.add(“刘备”); //索引为0

names.add(“关羽”); //索引为1

names.add(“张飞”); //索引为2

names.add(“刘备”); //索引为3

names.add(“张飞”); //索引为4

System.out.println(names.indexOf(“刘备”));

System.out.println(names.lastIndexOf(“刘备”));

System.out.println(names.indexOf(“张飞”));

System.out.println(names.lastIndexOf(“张飞”));

根据元素索引位置进行的判断
if (names.indexOf("刘备")==0) {
    System.out.println("刘备在这里");
}else if (names.lastIndexOf("刘备")==3) {
    System.out.println("刘备在那里");
}else {
    System.out.println("刘备到底在哪里?");
}

在程序中使用OPGtable[Vt.indexOf(Vt1)][Vt.indexOf(Vt2)]

判断list是否为空

//空则返回true,非空则返回false


if (person.isEmpty()) {
    System.out.println("空的");
}else {
    System.out.println("不是空的");
}

4.1.2 Map
HashMap

最常用的Map,它根据键的HashCode
值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为
Null。非同步的。

常用API
clear()从 Map 中删除所有映射
remove(Object key)从 Map 中删除键和关联的值
put(Object key, Object value)将指定值与指定键相关联
putAll(Map t)将指定 Map 中的所有映射复制到此 map
entrySet()返回 Map 中所包含映射的 Set 视图。Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素
keySet()返回 Map 中所包含键的 Set 视图。删除 Set 中的元素还将删除 Map 中相应的映射(键和值)
values()返回 map 中所包含值的 Collection 视图。删除 Collection 中的元素还将删除 Map 中相应的映射(键和值)
get(Object key)返回与指定键关联的值
containsKey(Object key)如果 Map 包含指定键的映射,则返回 true
containsValue(Object value)如果此 Map 将一个或多个键映射到指定值,则返回 true
isEmpty()如果 Map 不包含键-值映射,则返回 true
size()返回 Map 中的键-值映射的数目

在程序中使用了MAP:

//文法

private Map<String, String> grammar = new HashMap<String, String>();

//FIRSTVT集

private Map<Character,Set<Character>> FIRSTVT = new
HashMap<Character,Set<Character>>();

//LASTVT集

private Map<Character,Set<Character>> LASTVT = new
HashMap<Character,Set<Character>>();

使用map保存文法,和FIRSTVT,LASTVT。

Map添加元素

grammar.put(“S”, “#E#”);

grammar.put(“E”, “E+T|E-T|T”);

grammar.put(“T”, “T*F|T/F|F”);

grammar.put(“F”, “(E)|i”);

上面程序将题目要求的文法保存到map对象grammar中

Map的遍历

		grammar.forEach((k,v)->{
			String []nArryStr = v.split("\\|");
			for(int i=0 ;i<nArryStr.length;i++)
				
			{
				//System.out.println(nArryStr[i]);
				if(Vt.contains(nArryStr[i].charAt(0)))
				{
					char b= nArryStr[i].charAt(0);
					FIRSTVT.get(k.charAt(0)).add(b);
				}
				if(nArryStr[i].length()>=2)
				if(Vn.contains(nArryStr[i].charAt(0))&&Vt.contains(nArryStr[i].charAt(1)))
				{
					char b=nArryStr[i].charAt(1);
					FIRSTVT.get(k.charAt(0)).add(b);
				}
			}
			
			
		});

使用forEach方法,参数(k,v)

其中k为每个values的索引值,通过forEach的方法,遍历map的所有元素,就返回每个元素的key和values,并赋给参数(k,v)。

Map获取元素

Map获取元素一般根据key值获取对应的values:

例如:

LASTVT.get(k.charAt(0))

结果返回的是,key对应的values,而且values是声明map时设置的类型对应,

比如:

private Map<Character,Set<Character>> LASTVT = new
HashMap<Character,Set<Character>>();

key的类型为Character

values的类型为Set<Character>

所以通过LASTVT.get(key)获得values时,得到类型为Set<Character>。

4.2 二元式文件结构

二元式文件通过专题1的词法分析程序得到:

其中一个测试用例为:

(1,a)
(4,+)
(1,b)
(10,*)
(3,()
(1,c)
(5,-)
(2,34)
(3,))
(11,/)
(1,num5)

二元式文件内容被录入到

br = new BufferedReader(new InputStreamReader(new FileInputStream(fp.getName())));
			String erYuanShi = "";
			while((erYuanShi=br.readLine())!=null) {
				//截取符号串
				String substr=erYuanShi.substring(erYuanShi.indexOf("(") + 1, erYuanShi.lastIndexOf(","));
				if(substr.equals("1")||substr.equals("2"))
				{		
					s+="i";
				}
				else 
				{
					s+=erYuanShi.substring(erYuanShi.indexOf(",") + 1, erYuanShi.lastIndexOf(")"));
				}

			}

二元式文件的录入和专题2一样,InputStream是一个Java
List列表的一个对象,list列表是一系列的String类型的字符串,具体的操作:

br = new BufferedReader(new InputStreamReader(new FileInputStream(fp.getName())));
			String erYuanShi = "";
			while((erYuanShi=br.readLine())!=null) {
				//截取符号串
				InputStream.add(erYuanShi.substring(erYuanShi.indexOf(",") + 1, erYuanShi.lastIndexOf(")")));
			}
			InputStream.add("#");  //末尾添加#号

br为一个文件的读入流,通过使用br.readLine()方法读入二元式文件当前行内容并返回给String类型的变量erYuanShi,然后每一行的内容类似为(1,num1)的形式,但是我们需要就是num1,所以通过erYuanShi.substring(erYuanShi.indexOf(",")+ 1,
erYuanShi.lastIndexOf(")"))方法将num1截取下来,放入List列表对象中,继续读文件,直到读取结束。这样就将二元式文件的内容读取到了字符串s中。1
和2表示标识符和数字在符号表中的序号,也即1表示当前二元式的实际内容为标识符,2表示为数字,所以如果是标识符和数字时,就将内容转换为i保存到s中。

最后得到字符串s类似为: i+i*(i-i)/i

但是实际算术表达式为:a+a*(b-c)/2。

也就是将用户定义的标识符和数字都转为i。

4.5 FIRSTVT 集

private Map<Character,Set<Character>> FIRSTVT = new HashMap<Character,Set<Character>>();

根据构造规则:

U∈Vn

FIRSTVT(U)=

{b∣U=+>b…, 或U =+> Vb…, b∈Vt,V∈Vn }

则形如W→…aU…的规则 a < b

b ∈ FIRSTVT(U)

在这里插入图片描述

编写代码:

//遍历文法。进行非终结符号的FIRSTVT的初始化
		grammar.forEach((k,v)->{
			String []nArryStr = v.split("\\|");
			for(int i=0 ;i<nArryStr.length;i++)
				
			{
				//System.out.println(nArryStr[i]);
				if(Vt.contains(nArryStr[i].charAt(0)))
				{
					char b= nArryStr[i].charAt(0);
					FIRSTVT.get(k.charAt(0)).add(b);
				}
				if(nArryStr[i].length()>=2)
				if(Vn.contains(nArryStr[i].charAt(0))&&Vt.contains(nArryStr[i].charAt(1)))
				{
					char b=nArryStr[i].charAt(1);
					FIRSTVT.get(k.charAt(0)).add(b);
				}
			}
			
			
		});
		
		do 
		{
			FIRSTVTAllNochange=1;
			grammar.forEach((k,v)->{
				String []nArryStr = v.split("\\|");
				for(int i=0 ;i<nArryStr.length;i++)
					
				{
					char U=k.charAt(0);
					char V=nArryStr[i].charAt(0);
					if(Vn.contains(U)&&U!=V&&Vn.contains(V))
					{
						
							FIRSTVT.get(V).forEach(values->{
								if(!FIRSTVT.get(U).contains(values))
								{
								//	System.out.println(values);
								 FIRSTVT.get(U).add(values);
								 FIRSTVTAllNochange = 0;
								}
							
							});
							
						
					}
					
				}
				
				
			});
			if(FIRSTVTAllNochange==1)
				break;
		}while(true);

获得FIRSTVT集:

S:[#]
T:[(, i, *, /]
E:[(, i, *, +, -, /]
F:[(, i]

4.6 LASTVT 集

//LASTVT集 private Map<Character,Set<Character>> LASTVT = new
HashMap<Character,Set<Character>>();

LASTVT构造规则:

U∈Vn

LASTVT(U)=

{a∣U=+>…a, 或U =+>…aV, a∈Vt, V ∈Vn }

则形如W→…Ub…的规则 a > b

a ∈LASTVT(U)

编写代码:

//遍历文法。进行非终结符号的LASTVT的初始化
		grammar.forEach((k,v)->{
			String []nArryStr = v.split("\\|");
			for(int i=0 ;i<nArryStr.length;i++)
				
			{
				//System.out.println(nArryStr[i]);
				int len= nArryStr[i].length();
				if(Vt.contains(nArryStr[i].charAt(len-1)))
				{
					char b= nArryStr[i].charAt(len-1);
					LASTVT.get(k.charAt(0)).add(b);
				}
				if(nArryStr[i].length()>=2)
				if(Vt.contains(nArryStr[i].charAt(len-2))&&Vn.contains(nArryStr[i].charAt(len-1)))
				{
					char b=nArryStr[i].charAt(len-2);
					LASTVT.get(k.charAt(0)).add(b);
				}
			}
			
			
		});
		
		do 
		{
			FIRSTVTAllNochange=1;
			grammar.forEach((k,v)->{
				String []nArryStr = v.split("\\|");
				for(int i=0 ;i<nArryStr.length;i++)
					
				{
					int len =nArryStr[i].length();
					char U=k.charAt(0);
					char V=nArryStr[i].charAt(len-1);
					if(Vn.contains(U)&&U!=V&&Vn.contains(V))
					{
						
						LASTVT.get(V).forEach(values->{
								if(!LASTVT.get(U).contains(values))
								{
									//System.out.println(values);
									LASTVT.get(U).add(values);
								 FIRSTVTAllNochange = 0;
								}
							
							});
							
						
					}
					
				}
				
				
			});
			if(FIRSTVTAllNochange==1)
				break;
		}while(true);

最后得到LASTVT集:

S:[#]

T:[), i, *, /]

E:[), i, *, +, -, /]

F:[), i]

4.7 算符优先矩阵

/算符优先权关系表
/**
* 在OPGtable中,用-1,0,1,2表示优先权关系
* 0 表示优先关系等于
* 1 表示优先关系小于
* 2 表示优先关系大于
* -1 表示不存在优先权关系
* *
*/
private int [][]OPGtable = new int [N][N];

在这里插入图片描述

构造规则:

编写代码:

/**
		 * 在OPGtable中,用-1,0,1,2表示优先权关系
		 * 0 表示优先关系等于
		 * 1 表示优先关系小于
		 * 2 表示优先关系大于
		 * -1 表示不存在优先权关系
		 * *
		 */
		grammar.forEach((k,v)->{
			String []nArryStr = v.split("\\|");
			
			for(int i=0 ;i<nArryStr.length;i++)			
			{		
				String ruleRight=nArryStr[i];
				int  len = ruleRight.length();
				if(len>=2)
				{
					for(int i1= 0 ;i1<len-1 ;i1++)
					{
						char X1=ruleRight.charAt(i1);
						char X2=ruleRight.charAt(i1+1);
						
						if(Vt.contains(X1)&&Vt.contains(X2))				
							OPGtable[Vt.indexOf(X1)][Vt.indexOf(X2)]=0;//0 表示优先关系等于
						if(Vt.contains(X1)&&Vn.contains(X2))
						{
							FIRSTVT.get(X2).forEach(values->{
								OPGtable[Vt.indexOf(X1)][Vt.indexOf(values)]=1;//1 表示优先关系小于
							});
						}
						if(Vn.contains(X1)&&Vt.contains(X2))				
						{
							LASTVT.get(X1).forEach(values->{
								OPGtable[Vt.indexOf(values)][Vt.indexOf(X2)]=2;//2 表示优先关系大于
							});
						}
						if(len>=3&&i1<len-2)
						{
							char X3=ruleRight.charAt(i1+2);
							if(Vt.contains(X1)&&Vn.contains(X2)&&Vt.contains(X3))
							{	
								OPGtable[Vt.indexOf(X1)][Vt.indexOf(X3)]=0; //0 表示优先关系等于
							}
						}
							
							
					}
					
				}
				
			}
			
			
		});

最终的算符优先矩阵:

在这里插入图片描述

5、程序结构描述
5.1Java 主类:OPGMain

在这里插入图片描述

5.1.1全局变量
变量类型变量名称变量作用
private List<Character>Vt保存终结符号集,需要手动设置
Private List<Character>Vn保存非终结符号集。需要手动设置
private Map<Character, String>grammar保存文法,需要手动设置
private Map<Character,Set<Character>>FIRSTVT保存非终结符号的FIRSTVT集,由函数生成内容
private Map<Character,Set<Character>>LASTVT保存非终结符号的LASTVT集,有相应的生成函数生成其内容
private Map<String,Set<Character>>OPGtable//算符优先权关系矩阵 /** * 在OPGtable中,用-1,0,1,2表示优先权关系 * 0 表示优先关系等于 * 1 表示优先关系小于 * 2 表示优先关系大于 * -1 表示不存在优先权关系 * * */
5.1.2函数
类型返回值函数名功能
publicOPGMain构造函数,初始化文法,Vt和Vn
publicvoidCreate_FIRSTVT构造FIRSTVT集
publicvoidCreate_LASTVT输出LASTVT集
publicvoidCreate_OPGTable构造算符优先关系矩阵
publicvoidOPGanalysis算符优先文法分析器
publicintget_PriorityRelationship获得两个终结符号的优先关系
publicstatic voidmain类的主函数,实例化主类,并传入中间文件。
./media/image5.png

5.1.3函数调用关系图
在这里插入图片描述

绿色虚线为调用

6、程序测试
6.1 正确用例

测试用例为二元式文件结构部分的用例

(1,a)
(4,+)
(1,b)
(10,*)
(3,()
(1,c)
(5,-)
(2,34)
(3,))
(11,/)
(1,num5)

结果为:

在这里插入图片描述

在这里插入图片描述

6.2 错误用例

通过修改测试用例1使其不符合赋值语句语法:

6.2.1缺少操作符

在这里插入图片描述

6.2.2缺少 ‘)’符号

在这里插入图片描述
源代码:https://github.com/Topdu/Compilation-principle/tree/master/16281002-杜永坤-专题4

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值