本文章根据一篇文章修改而成,原文章链接: http://t.csdnimg.cn/OzRhM
一、 实验目的
根据算符优先分析法,对表达式进行语法分析,使其能够判断一个表达式是否正确。通过算符优先分析方法的实现,加深对自下而上语法分析方法的理解。
二、 实验要求
1、输入文法。可以是如下算术表达式的文法(你可以根据需要适当改变):
E→E+T|E-T|T
T→T*F|T/F|F
F→(E)|i
2、对给定表达式进行分析,输出表达式正确与否的判断。
程序输入/输出示例:
输入:1+2;
输出:正确
输入:(1+2)/3+4-(5+6/7);
输出:正确
输入:((1-2)/3+4
输出:错误
输入:1+2-3+(*4/5)
输出:错误
三、实验步骤
1、参考数据结构
char *VN=0,*VT=0;//非终结符和终结符数组
char firstvt[N][N],lastvt[N][N],table[N][N];
typedef struct //符号对(P,a)
{
char Vn;
char Vt;
} VN_VT;
typedef struct //栈
{
VN_VT *top;
VN_VT *bollow;
int size;
}stack;
2、根据文法求FIRSTVT集和LASTVT集
给定一个上下文无关文法,根据算法设计一个程序,求文法中每个非终结符的FirstVT 集和LastVT 集。
算符描述如下:
/*求 FirstVT 集的算法*/
PROCEDURE insert(P,a);
IF not F[P,a] then
begin
F[P,a] = true; //(P,a)进栈
end;
Procedure FirstVT;
Begin
for 对每个非终结符 P和终结符 a do
F[P,a] = false
for 对每个形如 P a…或 P→Qa…的产生式 do
Insert(P,a)
while stack 非空
begin
栈顶项出栈,记为(Q,a)
for 对每条形如 P→Q…的产生式 do
insert(P,a)
end;
end.
同理,可构造计算LASTVT的算法。
3、构造算符优先分析表
依据文法和求出的相应FirstVT和 LastVT 集生成算符优先分析表。
算法描述如下:
for 每个形如 P->X1X2…Xn的产生式 do
for i =1 to n-1 do
begin
if Xi和Xi+1都是终结符 then
Xi = Xi+1
if i<= n-2, Xi和Xi+2 是终结符, 但Xi+1 为非终结符 then
Xi = Xi+2
if Xi为终结符, Xi+1为非终结符 then
for FirstVT 中的每个元素 a do
Xi < a ;
if Xi为非终结符, Xi+1为终结符 then
for LastVT 中的每个元素 a do
a > Xi+1 ;
end
4、构造总控程序
算法描述如下:
stack S;
k = 1; //符号栈S的使用深度
S[k] = ‘#’
REPEAT
把下一个输入符号读进a中;
If S[k]
VT then j = k else j = k-1;
While S[j] > a do
Begin
Repeat
Q = S[j];
if S[j-1]
VT then j = j-1 else j = j-2
until S[j] < Q;
把S[j+1]…S[k]归约为某个N,并输出归约为哪个符号;
K = j+1;
S[k] = N;
end of while
if S[j] < a or S[j] = a then
begin k = k+1; S[k] = a end
else error //调用出错诊察程序
until a = ‘#’
5、对给定的表达式,给出准确与否的分析过程
6、给出表达式的计算结果。(本步骤可选作)
总代码:
grammarElement = {}
terSymblo = ['#'] #终结符列表
non_ter = [] #非终结符
Start = 'E' #开始符号
allSymbol = [] # 所有符号
firstVT = {} # FIRSTVT集
lastVT = {} # lastVT集
formules = []
def data_input(): # 读取文法
with open("3.txt", 'r+', encoding="utf-8") as f:
temp = f.readlines()
for i in temp:
line = str(i.strip("\n")) #获取当前行的字符串内容,并移除其尾部的换行符
formules.append(line) #将当前文法规则添加到一个名为formules的列表中
if line[0] not in non_ter: #检查当前规则的首字符是否已经在non_ter(非终结符号)列表中
non_ter.append(line[0])
#在grammarElement字典中设置一个键值对。键是当前规则的首字符,值是该规则的右侧部分(从第五个字符开始)。
#如果这个键已经存在于字典中,那么它的值将被更新为当前规则的右侧部分。
grammarElement.setdefault(line[0], line[5:])
else:
grammarElement[line[0]] += "|" + line[5:]
for i in temp:
line = str(i.strip("\n")).replace(" -> ", "")
for j in line:
if j not in non_ter and j not in terSymblo:
terSymblo.append(j)
if 'ε' in terSymblo: terSymblo.remove('ε') #如果ε存在终结符列表中,则从列表中移除它
def get_fistVT(formule):
x = formule[0] #获取文法规则的首字符,并将其赋值给变量x
ind = non_ter.index(x) #查找x在non_ter列表中的索引
index = []
i = 5
if formule[i] in terSymblo and formule[i] not in firstVT[x]: # 首位为终结符 P->a...
firstVT[x] += formule[i]
elif formule[i] in non_ter: # 首位为非终结符 P->Q...
for f in firstVT[formule[i]]: #如果a属于FIRSTVT(Q)
if f not in firstVT[x]:
firstVT[x] += f
if i + 1 < len(formule): #非终结符后面一个字符
if formule[i + 1] in terSymblo and formule[i + 1] not in firstVT[x]: # P->Qa...
firstVT[x] += formule[i + 1]
def get_lastVT(formule):
x = formule[0]
i = len(formule) - 1
if formule[i] in terSymblo and formule[i] not in lastVT[x]: #P->...a
lastVT[x] += formule[i]
elif formule[i] in non_ter: #P->...Q
for f in lastVT[formule[i]]: #遍历LastVT(Q)
if f not in lastVT[x]: #若a属于LastVT(Q)
lastVT[x] += f
if formule[i - 1] in terSymblo and formule[i - 1] not in lastVT[x]: #P->...aQ
lastVT[x] += formule[i - 1]
def addtodict2(thedict, key_a, key_b, val): # 设置二维字典的函数
if key_a in thedict.keys():
thedict[key_a].update({key_b: val})
else:
thedict.update({key_a: {key_b: val}})
def analy(formule): #算符优先分析表
start = 5
end = len(formule) - 2
if start == end: return
for i in range(start, end): #每个形如 P->X1X2…Xn的产生式
if formule[i] in terSymblo and formule[i + 1] in terSymblo: #Xi和Xi+1都是终结符
addtodict2(data, formule[i], formule[i + 1], "=")
#Xi和Xi+2 是终结符, 但Xi+1 为非终结符
if formule[i] in terSymblo and formule[i + 1] in non_ter and formule[i + 2] in terSymblo:
addtodict2(data, formule[i], formule[i + 2], "=")
#Xi为终结符, Xi+1为非终结符 ...aP...
if formule[i] in terSymblo and formule[i + 1] in non_ter:
for j in firstVT[formule[i + 1]]: #FirstVT 中的每个元素 b
addtodict2(data, formule[i], j, "<") #a<b
#Xi为非终结符, Xi+1为终结符...Pb...
if formule[i] in non_ter and formule[i + 1] in terSymblo:
for j in lastVT[formule[i]]: #LastVT 中的每个元素 a
addtodict2(data, j, formule[i + 1], ">") #a>b
if formule[i + 1] in terSymblo and formule[i + 2] in non_ter:
for j in firstVT[formule[i + 2]]:
addtodict2(data, formule[i + 1], j, "<")
if formule[i + 1] in non_ter and formule[i + 2] in terSymblo:
for j in lastVT[formule[i + 1]]:
addtodict2(data, j, formule[i + 2], ">")
def reverseString(string):
return string[::-1] #选择了从末尾到开头的所有字符,从而得到了一个反向的字符串
# 初始化两个栈
def initStack(string):
# 分析栈,入栈#
analysisStack = "#"
# 当前输入串入栈,即string逆序入栈
currentStack = reverseString(string)
# 调用分析函数
toAnalyze(analysisStack, currentStack)
# 寻找分析栈最顶终结符元素,返回该元素及其下标
def findVTele(string):
ele = '\0'
ele_index = 0
for i in range(len(string)):
if (string[i] in terSymblo):
ele = string[i]
ele_index = i
return ele, ele_index
# 根据栈中内容进行分析,构造算符优先分析表
def toAnalyze(analysisStack, currentStack):
global analyzeResult
global analyzeStep
analyzeStep += 1
analysisStack_top, analysisStack_index = findVTele(analysisStack) # 分析栈最顶终结符元素及下标
currentStack_top = currentStack[-1] # 当前输入串栈顶
#relation = data[analysisStack_top][currentStack_top]
#根据分析栈顶和当前输入串栈顶,从data中获取对应的关系。
relation = data.get(analysisStack_top, {}).get(currentStack_top, None)
if relation == '<':
print(" {:^5} {:^15} {:^9} {:^15} {:^12} ".format(analyzeStep, analysisStack, relation,
reverseString(currentStack), '移进'))
#将当前输入串栈顶的元素添加到分析栈,并从当前输入串中移除栈顶元素。
analysisStack += currentStack_top
currentStack = currentStack[:-1]
toAnalyze(analysisStack, currentStack)
elif relation == '>':
print(" {:^5} {:^15} {:^9} {:^15} {:^12} ".format(analyzeStep, analysisStack, relation,
reverseString(currentStack), '归约'))
currenChar = analysisStack_top #初始化变量currenChar为分析栈顶的元素
temp_string = ""
for i in range(len(analysisStack) - 1, -1, -1): #从分析栈的最后一个元素开始往前循环(倒序)
if (analysisStack[i] >= 'A' and analysisStack[i] <= 'Z'):
temp_string = analysisStack[i] + temp_string #将当前元素加到temp_string的开头
continue
elif (data[analysisStack[i]][currenChar] == '<'):
break; #最左素短语,跳出
temp_string = analysisStack[i] + temp_string
currenChar = analysisStack[i]
if (temp_string in sentencePattern):
analysisStack = analysisStack[0:i + 1] #截取分析栈,使其只包含归约后的部分
analysisStack += 'N'
toAnalyze(analysisStack, currentStack)
else:
print("归约出错!待归约串为:", temp_string, "--->产生式右部无此句型!")
analyzeResult = False
return
elif (relation == '='):
if (analysisStack_top == '#' and currentStack_top == '#'):
print(" {:^5} {:^15} {:^9} {:^15} {:^12} ".format(analyzeStep, analysisStack, relation,
reverseString(currentStack), '完成'))
analyzeResult = True
return
else:
print(" {:^5} {:^15} {:^9} {:^15} {:^12} ".format(analyzeStep, analysisStack, relation,
reverseString(currentStack), '移进'))
#将当前输入串栈顶的元素添加到分析栈,并从当前输入串中移除栈顶元素
analysisStack += currentStack_top
currentStack = currentStack[:-1]
toAnalyze(analysisStack, currentStack)
elif (relation == None):
print(" {:^5} {:^15} {:^9} {:^15} {:^12} ".format(analyzeStep, analysisStack, 'None',
reverseString(currentStack), '报错'))
analyzeResult = False
return
data_input()
data = dict() #准备工作,创造字典
for i in non_ter:
firstVT.setdefault(i, "")
lastVT.setdefault(i, "")
for i in terSymblo:
for j in terSymblo:
addtodict2(data, i, j, '')
#print(data)
sym = non_ter + terSymblo #创建一个新的列表sym,它是将non_ter和terSymblo两个列表连接在一起的结果
#输出firstVT集合、lastVT集合:
for n in range(10):
for i in formules:
get_fistVT(i)
get_lastVT(i)
print("firstVT集合:")
for i in non_ter:
print(i+" : "+firstVT[i])
print("lastVT集合:")
for i in non_ter:
print(i+" : "+lastVT[i])
temp2 = Start +" -> #" +Start+"#"
formules.append(temp2) #在前后加上井号
for i in formules:
analy(i)
print("算符优先分析表")
for i in terSymblo:
print("\t" + i.ljust(4), end="")
print()
for i in terSymblo:
print(i.ljust(4), end="") #i.ljust(4):如果i的长度小于4,那么它将在i的左侧添加空格,使其总宽度为4。
for j in terSymblo:
if j in data[i]:
print(data[i][j].ljust(8), end="")
else:
print("\t\t", end="")
print()
sentencePattern = ["N+N", "N*N", "N/N", "(N)", "i","N^N","N,N","N-N","a"]
analyzeResult = False
analyzeStep = 0
print("请输入待分析的字符串:")
string = input()
string = string.replace(" ", "")
#将数字转化成字符i
string = string.replace('0', 'i').replace('1', 'i').replace('2', 'i').replace('3', 'i').replace('4', 'i').replace(
'5', 'i').replace('6', 'i').replace('7', 'i').replace('8', 'i').replace('9', 'i')
string+="#"
print(" {:^4} {:^13} {:^6} {:^12} {:^10} ".format('步骤', '分析栈', '优先关系', '当前输入串', '移进或归约'))
initStack(string)
if (analyzeResult):
print("该字符串是文法的合法句子。\n")
else:
print("该字符串不是文法的合法句子。\n")
测试用例:
E -> E+T
E -> E-T
E -> T
T -> T*F
T -> T/F
T -> F
F -> (E)
F -> i
测试结果: