说明:文法的空字符串以@表示
实现:大写字母和小写字母分别表示终结符号以及非终结符号。(只能识别字母)
用<>和不带<> 分别表示终结符号以及非终结符号
产生式可以用->,::=表示
代码的部分解释:
1.Vn,Vt,分别为集合类型,在统计终结符号集以及非终结符号集时会自动去重。
2.left,right,分别会统计每一条产生式的左部以及右部的字符串。
3.left_num,right_num:存储类型为元组,分别表示左部以及右部出现的终结符号,以及非终结符号的个数。
4.对以<>格式的产生式,以堆栈进行解析,因此,需要逆序入栈。
# -*- coding: utf-8 -*-
"""
Created on Sat Jun 15 20:48:32 2019
@author: Administrator
"""
import re
Vn=set();Vt=set();flag=-1;left=[];right=[];left_num=[];right_num=[]#二维元组,分别表示非终结符号以及终结符号的个数
class Stack(object):
"""栈"""
def __init__(self):
self.items = []
def is_empty(self):
"""判断是否为空"""
return self.items == []
def push(self, item):
"""加入元素"""
self.items.append(item)
def pop(self):
"""弹出元素"""
return self.items.pop()
def peek(self):
"""返回栈顶元素"""
return self.items[len(self.items)-1]
def size(self):
"""返回栈的大小"""
return len(self.items)
def jiexi1(s,f): #解析由<>定义的文法
stack=Stack()
vn_num=0;vt_num=0
for c in s[::-1]:#使用堆栈,因此为了保证顺序,逆序访问
if c=='>':
v=''
while stack.size()!=0:
v+=stack.peek()
stack.pop()
if v!='':
Vt.add(v)
vt_num+=1
continue
if c=='<':
v=''
while stack.size()!=0:
v+=stack.peek()
stack.pop()
Vn.add(v)
vn_num+=1
continue
stack.push(c)#非<>的元素入栈
v=''#保证最后一个非<>的元素可以被访问
while stack.size()!=0:
v+=stack.peek()
stack.pop()
if v!='':
Vt.add(v)
vt_num+=1
if(f==0):
left_num.append((vn_num,vt_num))
if(f==1):
right_num.append((vn_num,vt_num))
def jiexi2(s,f):#处理大学字母表示非终结符号,小写字母表示终结符号集的情况
vn_num=0;vt_num=0
for c in s:
if c.isupper():
Vn.add(c)
vn_num+=1;
if c.islower():
Vt.add(c)
vt_num+=1
if(f==0):
left_num.append((vn_num,vt_num))
if(f==1):
right_num.append((vn_num,vt_num))
def getLeftRight():
with open("C:/Users/Administrator/Desktop/新建文件夹/编译原理.txt","r") as f:
global lines
lines=f.readlines()
if(''.join(lines).find("<")!=-1):
global format_wenfa
format_wenfa=1
else:
format_wenfa=2
for line in lines:
s=line.strip().partition("::=")
if(s[2]==""):
s=line.strip().partition("->")
left.append(s[0]);right.append(s[2])
return left,right
def getVnVt():
getLeftRight() #判断采用哪一种解析方式
if format_wenfa==1:
for v in left: #这里v表示每一条产生式的左部
jiexi1(v,0)
for v in right:
jiexi1(v,1)
else:
for v in left:
jiexi2(v,0)
for v in right:
jiexi2(v,1)
def first():
for k,v in zip([k+v for k,v in left_num],[k+v for k,v in right_num]):#k,v分别表示左边和右边
if k>v or k==0 or v==0:
return -1
return 1
def second():
for (k,v) in left_num:#left 分别指所有文法的左部
if k!=1 or v!=0:
return -1
return 2
def third():#(Vn,Vt)
for k,v in left_num:#k,v分别表示终结符号集以及非终结符号集
if not (k==1 and v==0):
return -1
for k,v in right_num:
if not (k<=1 and v==1):
return -1;
return 3
def zero():
for k,v in left_num:#k,v分别表示非终结符号以及终结符号
if k==0 and v==0:
return -1
return 0
if __name__ == '__main__':
getVnVt()
flag=third()
if(flag==-1):
print("flag1",flag)
flag=second()
if(flag==-1):
print("flag2",flag)
flag=first()
if(flag==-1):
print("flag3",flag)
flag=zero()
if(flag!=-1):
print(flag,"型文法")
else:
print("不合格的文法构造")
print("left_num",left_num)
print("right_num",right_num)
print("left",left)
print("right",right)
print("Vn:",Vn)
print("Vt:",Vt)
print("P:",[line.strip() for line in lines])
print("S:",left[1])
由于对python的方法有遗忘,个人感觉可以写的更简单点,但这只是一个作业,所以没想着精细修改。如果大家有什么指教,请不要吝啬,另外,我感觉用Scala写的话代码量可能更少,可以试试。