基于上一篇文章python 预备实验1文法的读入和输出 编译原理
还添加了重复表达式删除功能,提取左因子和消除左递归。
1、将一个可转换非LL(1)文法转换为LL(1)文法,要经过两个阶段,1)消除文
法左递归,2)提取左因子,消除回溯。
2、提取文法左因子算法:
1)对文法G的所有非终结符进行排序
2)按上述顺序对每一个非终结符Pi依次执行:
for( j=1;j< i-1; j++)
将Pj代入Pi的产生式(若可代入的话);消除关于Pi的直接左递归:
Pi -> Pi a | β ,其中β不以Pi开头,则修改产生式为:
Pi—>βPi'
Pi′—>a Pi'| e
3)化简上述所得文法。
3、提取左因子的算法:
A ----> δβ1 | δβ2 | … | δβn | y1 | y2 |…| ym (其中,每个y不以δ开头)
那么,可以把这些产生式改写成
A ----> δA' | y1 l y 2 | … | ymA ′ ----> β1 | β2 |…| βn
4、利用上述算法,实现构造一个LL(1)文法:
1)从文本文件 g.txt中读入文法,利用实验1的结果,存入实验1设计的数据结
构;2)设计函数remove_left _recursion()和remove_left_gene ()实现消除左递归和
提取左因子算法,分别对文法进行操作,消除文法中的左递归和提出左因子;
def Delete_Duplicate_Production(grammar): # 删除重复的产生式
global non_terminator, production
for i in grammar[non_terminator]: # 遍历非终结符
j = 0
lengi = len(grammar[production][i]) # 该非终结符产生式个数
while j < lengi - 1: # 每个产生式
k = j + 1
while k < lengi: # 遍历每个产生式后面的产生式
if grammar[production][i][j] == grammar[production][i][k]: # 碰到重复的了
grammar[production][i].pop(k) # 删除
lengi -= 1
else:
k += 1
j += 1
def Get_New_Non_Terminator(grammar, new_non_terminator): # 产生新的非终结符
global non_terminator, production
if new_non_terminator + '\'' in grammar[non_terminator]: # 加个'看看有没有用过
# 数字一般非终结符用不到(用到也无所谓,都把产生式部分分开了)
j = 0
while True: # 看看后面加个数字的有没有
if not new_non_terminator + str(j) in grammar[non_terminator]: # 找不到才出循环
new_non_terminator = new_non_terminator + str(j)
break
j += 1
else: # 没有加个'就行
new_non_terminator = new_non_terminator + '\''
# 到这里必有一个没用过的非终结符出来了(数字都不够你用?)找不到不出循环的
return new_non_terminator
def Remove_Left_Gene(grammar): # 提取左因子
global non_terminator, production
# 怕无限递归之类的,就不做隐式左因子了
for i in grammar[non_terminator]: # 遍历非终结符
lenmax = len(grammar[production][i]) # 该非终结符产生式个数
j = -1
while j < lenmax - 1: # 遍历该终结符的所有产生式(除最后一个)
j += 1
t = [1] # 自己和自己肯定一样
if not grammar[production][i][j]: # 产生式为空跳过
continue
for k in range(j + 1, lenmax): # 看看该产生式后面的产生式
# 第一个元素一致就给个1不然给个0
if not grammar[production][i][k]: # 为空跳过
t.append(0)
continue
if grammar[production][i][j][0] == grammar[production][i][k][0]:
t.append(1)
else:
t.append(0)
if sum(t) > 1: # 有重复的
nowlen = 1 # 当前重复位数,也是要比较的下标
same = 1
while same: # 一直往后看,直到不同
if nowlen >= len(grammar[production][i][j]): # 越界就停
same = 0
break
for k in range(1, len(t)): # t第一位就不用看了
# 这一位之前相同但现在不同
if t[k] == 1 and not grammar[production][i][j][nowlen] == grammar[production][i][j + k][nowlen]:
nowlen -= 1 # 退一位结束(后面会+1.所以-1)
same = 0
break
nowlen += 1 # 每轮加一位
new_non_terminator = Get_New_Non_Terminator(grammar, i) # 给一个新的非终结符
grammar[non_terminator].append(new_non_terminator)
grammar[production][new_non_terminator] = [grammar[production][i][j][nowlen:]] # 新的产生式
grammar[production][i][j] = grammar[production][i][j][:nowlen] + [new_non_terminator] # 改变第一个左因子产生式
for k in range(len(t) - 1, 0, -1): # 从后往前遍历
if t[k] == 1:
grammar[production][new_non_terminator].append((grammar[production][i].pop(j + k))[nowlen:])
lenmax -= 1
pass
def Remove_Left_Recursion(grammar): # 消除文法左递归
# ε
global non_terminator, production
# 将间接左递归变为直接左递归
for i in grammar[non_terminator]: # 遍历非终结符
j = 0 # 遍历产生式用
for ii in grammar[non_terminator]: # 遍历之前的非终结符
if ii == i: # 到一起就停
break
lengi = len(grammar[production][i]) # 替换前产生式个数
while j < lengi: # 根据非终结符遍历该终结符的产生式
if (not len(grammar[production][i][j]) == 0) and grammar[production][i][j][0] is ii: # 是之前的非终结符
g_p = grammar[production][i].pop(j) # 弹出该产生式
lengi -= 1 # 由于弹出产生式了,要退一位
for jj in grammar[production][ii]: # 遍历替代产生式
grammar[production][i].append(jj + g_p[1:]) # 新的产生式追加在后面
else:
j += 1
# 消除直接左递归
end_production = grammar[non_terminator][-1] # 最后一个非终结符,提前终止用,新增的非终结符不会有左递归
for i in grammar[non_terminator]: # 遍历非终结符
t = [] # 记录有无左递归
for j in grammar[production][i]: # 根据非终结符遍历该终结符的产生式
if len(j) == 0:
t.append(0)
else:
if j[0] == i:
t.append(1)
else:
t.append(0)
lent = len(t)
if sum(t): # 有左递归
if sum(t) == lent: # 全是有左递归的
print('消除直接左递归错误') # 报错并弹出错误部分
print(i + '->', end='') # 前部
for j in range(len(grammar[production][i])): # 根据非终结符遍历该终结符的产生式
if j > 0:
print('|', end='')
for k in grammar[production][i][j]:
print(k, end='')
print(';')
print(i, '的所有产生式都有左递归')
return
new_non_terminator = Get_New_Non_Terminator(grammar, i) # 给一个新的非终结符
grammar[non_terminator].append(new_non_terminator)
grammar[production][new_non_terminator] = [] # 新的产生式
j = 0
while j < lent: # 遍历所有产生式
if t[j] == 1: # 该部分有左递归
g_p = grammar[production][i].pop(j) # 弹出该产生式
t.pop(j)
lent -= 1 # 由于弹出产生式了,要退一位
if not g_p[1:] == []: # 怕s->s|;这样的产生式
grammar[production][new_non_terminator].append(g_p[1:] + [new_non_terminator])
else:
grammar[production][i][j].append(new_non_terminator)
j += 1
grammar[production][new_non_terminator].append([]) # 最后加的空
if end_production == i: # 遇到最后就停
break
Delete_Duplicate_Production(grammar)
def GetGrammar(grammar): # 输出文法
global non_terminator, production
for i in grammar[non_terminator]: # 遍历非终结符
print(i + '->', end='') # 前部
for j in range(len(grammar[production][i])): # 根据非终结符遍历该终结符的产生式
if j > 0:
print('|', end='')
for k in grammar[production][i][j]:
print(k, end='')
print(';')
def LongStr(sting, list_data): # 从列表中寻找匹配最长的项
maxlen = 0 # 最长的匹配字符串长度
maxstr = -1 # 匹配的最长字符串位置
for i in range(len(list_data)): # 遍历
if sting.startswith(list_data[i]): # 判断字符串是否以list[i]开头
leni = len(list_data[i])
if leni > maxlen: # 如果新匹配字符串比原来长,替换
maxlen = leni
maxstr = i
return maxlen, maxstr
def PrimaryTreatment(grammar): # 初步处理,使得产生式内部分开
global non_terminator, production
for i in grammar[non_terminator]: # 遍历非终结符
for j in range(len(grammar[production][i])): # 根据非终结符遍历该终结符的产生式
k = 0 # 所处位置指针
str_production = grammar[production][i][j][0] # 产生式字符串
new_production = [] # 准备存初步处理后的产生式
while k < len(str_production):
maxlen, maxstr = LongStr(str_production, grammar[non_terminator]) # 寻找匹配最长的终结符
if maxlen == 0: # 没找到
new_production.append(str_production[k]) # 分出一个终结符
k += 1
else: # 找到了
new_production.append(str_production[k:k + maxlen]) # 分出一个非终结符
k += maxlen
grammar[production][i][j] = new_production # 产生式替换
def OpenGrammar(file): # 从文件中打开(读取)文法。并作初级处理(产生式中各个非终结符和终结符分开)
global non_terminator, production
file = open(file) # 读取文件
non_terminator = '非终结符'
production = '产生式'
grammar = {non_terminator: [], production: {}}
while i := file.readline(): # 一行一行的读取,并赋值给i
for j in range(len(i)): # 遍历i中每一个字符
if i[j] == '-' and i[j + 1] == '>': # 分割前面的字符就是非终结符
if not i[0:j] in grammar[non_terminator]: # 该非终结符还没有记录
grammar[non_terminator].append(i[0:j]) # 加入进去
grammar[production][i[0:j]] = []
k = j + 2 # 直达产生式右部第一个字符
for l in range(len(i) + 1): # +1是为了处理最后一行又不带分号又不带回车的情况
# 这里由于用了断路特性即l == len(i)成立后不会运行后面的,从而不会产生数组越界报错
if l == len(i) or i[l] == ';' or i[l] == '\n' or i[l] == ';': # 往后找到最后一个,结束
grammar[production][i[0:j]].append([i[k:l]]) # 添加到后面
break
if i[l] == '|': # 遇到了中断
grammar[production][i[0:j]].append([i[k:l]]) # 添加到后面
k = l + 1 # 并且左边标记右移
break
file.close()
PrimaryTreatment(grammar)
Delete_Duplicate_Production(grammar)
return grammar
global non_terminator, production
file = 'g.txt' # 文件位置
grammar = OpenGrammar(file) # 读取文法
print("读取文法后------------------------")
GetGrammar(grammar) # 输出文法
print(grammar)
Remove_Left_Recursion(grammar) # 消除左递归
print("消除左递归后------------------------")
GetGrammar(grammar) # 输出文法
Remove_Left_Gene(grammar) # 提取左因子
print("提取左因子后------------------------")
GetGrammar(grammar) # 输出文法
编辑一个文本文文件g.txt,在文件中输入如下内容:
S->Qc|c;
Q->Rb|b;
R->Sa|a;
结果:
读取文法后------------------------
S->Qc|c;
Q->Rb|b;
R->Sa|a;
{'非终结符': ['S', 'Q', 'R'], '产生式': {'S': [['Q', 'c'], ['c']], 'Q': [['R', 'b'], ['b']], 'R': [['S', 'a'], ['a']]}}
消除左递归后------------------------
S->Qc|c;
Q->Rb|b;
R->aR'|caR'|bcaR';
R'->bcaR'|;
提取左因子后------------------------
S->Qc|c;
Q->Rb|b;
R->aR'|caR'|bcaR';
R'->bcaR'|;
同样支持这样的输入:
S->Qc|c|cc|da|Q1
Q->Rb|b|V
R->Sa|a|cV2
Q1->aa|bd
V2->d|Q2
Q2->a
V->V2|e|||
结果:
读取文法后------------------------
S->Qc|c|cc|da|Q1;
Q->Rb|b|V;
R->Sa|a|cV2;
Q1->aa|bd;
V2->d|Q2;
Q2->a;
V->V2|e|;
{'非终结符': ['S', 'Q', 'R', 'Q1', 'V2', 'Q2', 'V'], '产生式': {'S': [['Q', 'c'], ['c'], ['c', 'c'], ['d', 'a'], ['Q1']], 'Q': [['R', 'b'], ['b'], ['V']], 'R': [['S', 'a'], ['a'], ['c', 'V', '2']], 'Q1': [['a', 'a'], ['b', 'd']], 'V2': [['d'], ['Q2']], 'Q2': [['a']], 'V': [['V2'], ['e'], []]}}
消除左递归后------------------------
S->Qc|c|cc|da|Q1;
Q->Rb|b|V;
R->aR'|cV2R'|caR'|ccaR'|daaR'|Q1aR'|bcaR'|VcaR';
Q1->aa|bd;
V2->d|Q2;
Q2->a;
V->V2|e|;
R'->bcaR'|;
提取左因子后------------------------
S->Qc|cS'|da|Q1;
Q->Rb|b|V;
R->aR'|cR0|daaR'|Q1aR'|bcaR'|VcaR';
Q1->aa|bd;
V2->d|Q2;
Q2->a;
V->V2|e|;
R'->bcaR'|;
S'->|c;
R0->V2R'|caR'|aR';