问题描述
- 输入产生式文法,若符合LR0文法,则分析得到该文法的LR0分析表:Goto表和Action表。
算法设计
-
1、 判断该文法是否符合LR0文法:
- a) 规约-规约冲突
- i. 若该文法中有两条产生式的右部相同,则会产生“规约-规约”冲突,不符合LR0文法。
- b) 移进-规约冲突
- i. 若该文法中含有产生式能产生空 @ ,则会产生移进-规约,不符合LR0文法。
- ii. 若分析该文法时,构建自动机过程中,有某个节点中的所有产生式中,有任意两条产生式,一条需要移进,一条需要规约,则不符合LR0文法。
- a) 规约-规约冲突
-
2、 创建活前缀自动机
- a) 给出开始产生式作为自动机开始节点,以点 “ . ”为记号,记录推导的点
- b) 分析该节点的产生式是否为完整:
- i. 完整即为:” . “后不为非终结符。
- ii. 不完整即为:“ . ”后为非终结符。还可以根据该非终结符展开得到其他产生式。
- c) 由该节点分析其邻接节点
- i. 若还能接受输入,则可以产生新的节点
- ii. 若不能接受输入,则该节点应该为Reduce节点或Accept节点
-
- 若“ . ”前为结束符 # ,则为Accept节点
-
- 否则为Reduce节点
-
- d) 新产生的所有节点回到(b)步骤继续分析,直至不产生新的节点,算法结束。
- e) 案例:
-
i. 文法为:
-
ii. 开始产生式为S -> E$,所以开始节点为:
-
iii. 因为该节点产生式有” . ”后为非终结符,则代表产生式未完整,需要进一步展开,得到:
-
iv. 易于知道,该节点产生式还未完整,还可以展开,最后得到:
-
v. 分析该节点能接受的输入,从而得到其邻接节点,易于知道,其能接受的输入为“ E、T、int、( “四个输入,所以邻接节点数量为4个。
-
vi. 然后分析新产生节点的邻接节点即可,最后可得:
-
-
3、 由自动机结构构件Goto表和Action表
-
a) 首先给自动机每个节点标号,如下:
-
b) 根据标号的自动机建立Goto表和Action表,其中个标号代表两个表的相应行,Goto表中列头部的非终结符和终结符代表该节点接受的输入,空代表Error,Action表内容代表每个节点进行的相应操作:
-
-
4、 至此,LR0分析完成。
测试结果
源代码
import tkinter as tk
from tkinter import *
from tkinter.ttk import Treeview
import tkinter.messagebox
from tkinter import ttk
bg = "#F0F8FF" # 背景颜色
y = 1 # 行
product_list = {} # 产生式集合
terminal_char = []
# 窗口居中
def CenterWindow(window, width, height, bg=bg):
window.configure(bg=bg) # 背景颜色
# 设置窗口居中
screenwidth = window.winfo_screenwidth()
screenheight = window.winfo_screenheight()
alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
window.geometry(alignstr)
# 设置窗口大小不可改变
window.resizable(width=False, height=False)
# 判断产生式左边是否正确 规则:1、只能为大写字母,且数量为一个
def is_left_correct(str: str):
if len(str) == 0 or len(str) > 1:
tk.messagebox.showerror('提示', '左边只能为大写字母且数量是1,请检查!!')
return False
elif str >= 'A' and str <= 'Z':
return True
tk.messagebox.showerror('提示', '左边只能为大写字母且数量是1,请检查!!')
return False
# 判断产生式右边是否正确 规则:1、不能含空格2、不能含数字3、@后不能含任何元素
def is_right_correct(str: str):
if len(str) == 1 and str[0] == '@':
return True
for index in range(len(str)):
if str[index] == '@' and len(str) != 1:
tk.messagebox.showerror('提示', '@前后不能含任何元素,请检查!!')
return False
return True
# 双击列表行 修改产生式
tip = False # 是否不显示提示
def treevie_double_wclick(event, tree, e1, e2):
global y
if len(tree.selection()) == 0:
return ""
global tip, product_list
for item in tree.selection():
item_text = tree.item(item, "values")
tree.delete(item)
product_list[item_text[1]].remove(item_text[3])
if len(product_list[item_text[1]]) == 0:
product_list.pop(item_text[1])
e1.delete(0, "end")
e2.delete(0, "end")
e1.insert("end", item_text[1])
e2.insert("end", item_text[3])
# 重新排序
sort_tree(tree)
if not tip:
tip = tkinter.messagebox.askyesno('提示', '所选数据已返回,请修改后重新提交\n是否不再提示(是 or 否)?')
# 添加产生式按钮
def btn_add_data(e1, e2, tree):
global product_list
left = e1.get()
right = e2.get()
if len(left) == 0 or len(right) == 0:
tk.messagebox.showerror('提示', '不能空输入!!')
return ""
if not is_left_correct(left):
return ""
if not is_right_correct(right):
return ""
# 判断是否已存在该字典key值
# 存在key值
if left in product_list.keys():
# 若重复 提示重复
if right in product_list[left]:
tk.messagebox.showerror('提示', '不能输入重复的产生式!!')
return ""
# 若不重复 直接添加
else:
product_list[left].append(right)
# 不存在key值
# 添加列表
else:
product_list[left] = [right]
# 添加数据
add_data(left, right, tree)
sort_tree(tree)
e1.delete(0, "end")
e2.delete(0, "end")
# 给列表添加数据
def add_data(left, right, tree): # 添加数据
global y
tree.insert("", "end", values=(y, left, "-->", right))
y += 1
# 清空列表
def clear_tree(tree, clear_pro=False):
global product_list
item_list = tree.get_children()
for item in item_list:
tree.delete(item)
if clear_pro:
product_list.clear()
# print(product_list)
# 获取第二个参数
def take_second(elemt):
return elemt[1]
# 获取第一个参数
def take_first(elemt):
return elemt[0]
# 排序表格
def sort_tree(tree):
global y
y = 1
data_list = []
item_list = tree.get_children()
for item in item_list:
item_text = tree.item(item, "values")
data_list.append(item_text)
clear_tree(tree)
data_list.sort(key=take_second)
for item in data_list:
add_data(item[1], item[3], tree)
def create_tree(window, cols):
# 表格窗体容器
frame = Frame(window, bg=bg)
frame.pack()
ybar = Scrollbar(frame, orient='vertical') # 竖直滚动条
# 表格
style = ttk.Style(window)
# set ttk theme to "clam" which support the fieldbackground option
style.theme_use("clam")
style.configure("Treeview", background=bg,
fieldbackground=bg, foreground="green")
tree = Treeview(frame, show='headings', columns=cols, yscrollcommand=ybar.set, height=12, selectmode="browse")
ybar['command'] = tree.yview
tree.grid(row=0, column=0) # grid方案
ybar.grid(row=0, column=1, sticky='ns')
return tree
def create_tree_goto_action(window, cols_goto, cols_action, window_width):
# goto表格窗体容器
goto_frame = Frame(window, bg=bg)
goto_frame.place(x=10, y=21)
goto_ybar = Scrollbar(goto_frame, orient='vertical') # 竖直滚动条
# 表格
style = ttk.Style(window)
# set ttk theme to "clam" which support the fieldbackground option
style.theme_use("clam")
style.configure("Treeview", background=bg,
fieldbackground=bg, foreground="green")
tree_goto = Treeview(goto_frame, show='headings', columns=cols_goto, yscrollcommand=goto_ybar.set, height=24,
selectmode="browse")
goto_ybar['command'] = tree_goto.yview
tree_goto.grid(row=0, column=0) # grid方案
goto_ybar.grid(row=0, column=1, sticky='ns')
# action表格窗体容器
action_frame = Frame(window, bg=bg)
action_frame.place(x=window_width - 220, y=21)
action_ybar = Scrollbar(action_frame, orient='vertical') # 竖直滚动条
# 表格
tree_action = Treeview(action_frame, show='headings', columns=cols_action, yscrollcommand=action_ybar.set,
height=24, selectmode="browse")
action_ybar['command'] = tree_action.yview
tree_action.grid(row=0, column=0) # grid方案
action_ybar.grid(row=0, column=1, sticky='ns')
return [tree_goto, tree_action]
# 初始化窗口
def init_window():
window = tk.Tk()
window.title("LR0分析表")
CenterWindow(window, 400, 410, bg)
lable = tk.Label(window, text="产生式( 双击修改,重新写入 ),结束符为 #", bg=bg)
lable.pack()
lable2 = tk.Label(window, text="请不要写‘ | ’,产生式右边为单独整体,默认为空(@)", bg=bg)
lable2.pack()
cols = ["行", "左边", "箭头", "右边"] # 数据
tree = create_tree(window, cols) # 创建表格
tree.heading('行', text="行")
tree.heading('左边', text="左边")
tree.heading('箭头', text="箭头")
tree.heading('右边', text="右边")
tree.column('行', width=25, anchor=S) # 定义列
tree.column('左边', width=60, anchor=S) # 定义列
tree.column('箭头', width=40, anchor=S) # 定义列
tree.column('右边', width=150, anchor=S) # 定义列
tree.bind('<Double-Button-1>', lambda event: treevie_double_wclick(event, tree, e1, e2)) # 双击事件绑定
e1 = Entry(window, width=6, fg='green')
e1.place(x=80, y=320)
lable_jiantou = Label(window, text="-->", bg=bg)
lable_jiantou.place(x=140, y=320)
e2 = Entry(window, width=10, fg='green')
e2.place(x=175, y=320)
btn_add = Button(window, text="添加产生式", command=lambda: btn_add_data(e1, e2, tree), bg=bg)
btn_add.place(x=280, y=318)
LR0_btn = tk.Button(window, text="求LR0", activebackground="#40E0D0", bg=bg, command=LR0_table)
# SLR1_btn = tk.Button(window, text="求SLR(1)", activebackground="#40E0D0", bg=bg, command=SLR1_table)
btn_y = 350
btn_x = 80
btn_w = 70
btn_h = 40
LR0_btn.place(x=btn_x, y=btn_y, width=btn_w, height=btn_h)
# SLR1_btn.place(x=btn_x + 100, y=btn_y, width=btn_w + 15, height=btn_h)
btn_delete = tk.Button(window, text="清空表格", command=lambda: clear_tree(tree, True), bg=bg,
activebackground="#40E0D0")
btn_delete.place(x=btn_x + 200, y=btn_y, width=btn_w, height=btn_h)
'''测试数据'''
'''测试数据一''' # 符合LR(0)文法
add_data('E', 'TB#', tree)
add_data('B', '+TB', tree)
add_data('T', 'FC', tree)
add_data('C', '*FC', tree)
add_data('F', '(E)', tree)
add_data('F', 'i', tree)
sort_tree(tree)
product_list['B'] = ['+TB']
product_list['C'] = ['*FC']
product_list['E'] = ['TB#']
product_list['F'] = ['(E)', 'i']
product_list['T'] = ['FC']
'''测试数据二''' #
# add_data('S', 'AB', tree)
# add_data('A', 'Da', tree)
# add_data('B', 'cC', tree)
# add_data('C', 'aADC', tree)
# add_data('D', 'b', tree)
# sort_tree(tree)
# product_list['S'] = ['AB']
# product_list['A'] = ['Da']
# product_list['B'] = ['cC']
# product_list['C'] = ['aADC']
# product_list['D'] = ['b']
'''测试数据三''' #
# add_data('S', 'AB', tree)
# add_data('S', 'bC', tree)
# add_data('A', 'b', tree)
# add_data('B', 'aD', tree)
# add_data('C', 'AD', tree)
# add_data('C', 'b', tree)
# add_data('D', 'aS', tree)
# add_data('D', 'c', tree)
# sort_tree(tree)
# product_list['S'] = ['AB', 'bC']
# product_list['A'] = ['b']
# product_list['B'] = ['aD']
# product_list['C'] = ['AD', 'b']
# product_list['D'] = ['aS', 'c']
'''测试数据'''
window.mainloop()
# 关闭窗口
def close_window(window):
window.destroy()
# 判断字符类型
def type_char(c):
if c >= 'A' and c <= 'Z':
return 'N' # 非终结符
return 'T' # 终结符
# 创建终结符集合
def create_terminal():
global terminal_char, product_list
terminal_char.clear()
for key in product_list.keys():
for item in product_list[key]:
for index in range(len(item)):
if type_char(item[index]) == 'T' and item[index] not in terminal_char and item[index] != '@':
terminal_char.append(item[index])
if '#' not in terminal_char:
terminal_char.append('#')
node_dic = {} # 装全部节点 每个node_index 对应一个 node
class node:
def __init__(self, name):
self.next_node_dic = {} # 装可以进行的输入,有了这个输入就可以转换到另一个节点,即每个输入对应一个 key为输入 node(node的下标 node_index)为结果
self.this_node_product_dic = {} # 当前节点含有的产生式,含点(.)作为记号
self.name = str(name)
global node_dic, product_list
node_dic[name] = self
self.node_index = name
for key in product_list.keys():
self.this_node_product_dic[key] = []
# 添加下一个节点 每个key对应一个节点 状态
def add_next_node(self, key, index_node):
self.next_node_dic[key] = index_node
# 添加this_node_product_list当前节点含的产生式
def add_product(self, left, right):
self.this_node_product_dic[left].append(right)
# 返回节点的index,用于查找节点
def get_node_index(self):
return self.node_index
# 返回节点的this_node_product_dic
def get_this_node_product_dic(self):
return self.this_node_product_dic
# 返回节点的next_node_dic
def get_next_node_dic(self):
return self.next_node_dic
# 打印节点信息
def print_node(self):
print('---------------------------')
print("序号:", self.node_index)
print("产生式:")
for key in self.this_node_product_dic.keys():
for item in self.this_node_product_dic[key]:
print(key, " --> ", item)
print('--------------------------')
# 构建LR0自动机
node_num = 1
have_next_node = [] # 存储已被分析过邻接节点的节点
new_node = [] # 存储没有被分析过邻接节点的节点
def automaton_LR0(product_dic, start_char):
global node_num, have_next_node, new_node
node_left = [] # 该节点的产生式左部 待展开的非终结符
node_left.append(start_char) # 刚开始左部只有start_char
# 创建开始节点
start_node = node(node_num)
new_node.append(start_node)
node_num += 1
index = 0
while True:
if index == len(node_left): # 展开完成 退出展开
break
for item in product_dic[node_left[index]]: # 展开该非终结符的所有内容
start_node.add_product(node_left[index], "." + item) # 首位加点,作为 活前缀 记号
if type_char(item[0]) == 'N': # 若点下一个字符为 非终结符,则添加到待展开列表
if item[0] not in node_left: # 防止重复
node_left.append(item[0])
index += 1 # 扫描下一个非终结符
# print("分析序号:",start_node.node_index)
analyse_next_node(start_node, product_dic)
have_next_node.append(start_node) # 添加到已被分析的节点列表
new_node.remove(start_node) # 从new_node列表删除元素
while True:
# print(len(new_node))
if len(new_node) == 0:
break
# print("分析序号:", new_node[0].node_index)
# print( new_node[0].get_this_node_product_dic())
analyse_next_node(new_node[0], product_dic)
have_next_node.append(new_node[0]) # 添加到已被分析的节点列表
new_node.remove(new_node[0]) # 从new_node列表删除元素
return have_next_node
# 根据 father_node 找出与他连接的下一个节点
def analyse_next_node(father_node, product_dic):
global node_num, node_dic, have_next_node, new_node
father_node_product = father_node.get_this_node_product_dic() # 获取产生式
node_str_dic = {}
# 扫描产生式,以确定下一节点
for key in father_node_product.keys():
for item in father_node_product[key]:
item = str(item)
pos_point = item.find('.')
if pos_point == len(item) - 1: # 到达末尾,规约状态 没有下一个节点
pass
else: # 未到达末尾 可以产生下一个状态,移动点 . 即可(交换与点 . 相邻的两个字符)
str_temp = item[pos_point] + item[pos_point + 1]
str_temp = item.replace(str_temp, str_temp[::-1])
if str_temp[pos_point] not in node_str_dic.keys():
node_str_dic[str_temp[pos_point]] = []
node_str_dic[str_temp[pos_point]].append([key, str_temp])
# 开始分析father的邻接节点
for key in node_str_dic.keys():
node_temp = node(node_num) # 每个key对应一个节点
node_left = [] # 待展开的非终结符
for item in node_str_dic[key]:
node_temp.add_product(item[0], item[1]) # 添加初始产生式
item = str(item)
# 根据初始产生式,开始分析,是否展开点 . 相邻的非终结符
pos_point = item.find('.')
if len(item) - 1 != pos_point: # 没有到达末尾
if item[pos_point + 1] not in node_left and type_char(item[pos_point + 1]) == 'N':
node_left.append(item[pos_point + 1]) # 假如待展开的非终结符
index_node_left = 0
while True: # 展开node_temp所有的点相邻的 非终结符(node_left里所有的)
if index_node_left == len(node_left): # 所有非终结符已经展开 则该节点 简化完成
break
for item in product_dic[node_left[index_node_left]]: # 展开该非终结符的所有内容
node_temp.add_product(node_left[index_node_left], "." + item) # 首位加点,作为 活前缀 记号
if type_char(item[0]) == 'N': # 若点下一个字符为 非终结符,则添加到待展开列表
if item[0] not in node_left: # 防止重复
node_left.append(item[0])
index_node_left += 1 # 扫描下一个非终结符
# 判断该节点是否已存在,若重复内容,表示已存在,否则表示不存在,可以创建
node_temp_product = node_temp.get_this_node_product_dic()
should_node_num_add = True
for key_2 in node_dic.keys():
if key_2 != node_num:
# print(key,node_num)
node_pro = node_dic[key_2].get_this_node_product_dic() # 已有的产生式状态
if node_pro != node_temp_product:
# print(node_pro,"与",node_temp_product,"不相同")
continue
else:
# print(node_pro,"与",node_temp_product,"相同")
father_node.add_next_node(key, key_2) # 添加入father节点的下一节点字典中 指向已有的产生式状态
should_node_num_add = False # 重复,则跳出判断
break
if should_node_num_add:
node_num += 1 # node数量+1
father_node.add_next_node(key, node_temp.get_node_index()) # 添加入father节点的下一节点字典中
new_node.append(node_temp) # 添加到new_node里
# print("father为:")
# father_node.print_node()
# print("child为:")
# childs = father_node.get_next_node_dic()
# for key in childs.keys():
# print("输入 ",key,"得到child状态:")
# child = node_dic[childs[key]]
# child.print_node()
def tree_of_preview(window, node_index, frame_x, frame_y):
left = " "
right = " "
cols = [left, "序号" + node_index, right]
# 表格窗体容器
frame = Frame(window, bg=bg)
frame.place(x=frame_x, y=frame_y)
ybar = Scrollbar(frame, orient='vertical') # 竖直滚动条
# 表格
style = ttk.Style(window)
# set ttk theme to "clam" which support the fieldbackground option
style.theme_use("clam")
style.configure("Treeview", background=bg,
fieldbackground=bg, foreground="green")
tree = Treeview(frame, show='headings', columns=cols, yscrollcommand=ybar.set, height=5,
selectmode="browse")
ybar['command'] = tree.yview
tree.grid(row=0, column=0) # grid方案
ybar.grid(row=0, column=1, sticky='ns')
tree.heading(left, text=left)
tree.heading("序号" + node_index, text="序号" + node_index)
tree.heading(right, text=right)
tree.column(left, width=25, anchor=S) # 定义列
tree.column("序号" + node_index, width=60, anchor=S) # 定义列
tree.column(right, width=100, anchor='w') # 定义列
return tree
def preview_node(all_node_list: dict):
window = tk.Tk()
window.title("预览节点")
CenterWindow(window, 1280, 710, bg)
x_int = 210 # 每个卡片的间隔,横向间隔
y_int = 200 # 每个卡片的间隔,纵向间隔
now_x_y = [0,0]
tree_num = 0
for node_temp in all_node_list:#dict为每个index对应一个node
node_product_temp = node_temp.get_this_node_product_dic()#每个键值对应一个list
tree_temp = tree_of_preview(window,str(node_temp.get_node_index()),now_x_y[0],now_x_y[1])
now_x_y[0] += x_int
tree_num += 1
if tree_num == 6:
now_x_y[0] = 0
now_x_y[1] += y_int
tree_num = 0
for key_temp in node_product_temp.keys():
data = ()
for item in node_product_temp[key_temp]:#扫描每个item填入卡片
data += (key_temp,"-->",item,)
tree_temp.insert("", "end", values=data)
data = ()
#判断文法是否符合LR0文法
def is_correct_LR0(product_dic:dict):
all_item = []
#判断产生式是否能产生空
for key in product_dic.keys():
for item in product_dic[key]:
all_item.append(item)
if '@' == item:
tk.messagebox.showerror('提示', '产生式能产生空 @!!移进-规约冲突,不符合LR(0)文法!!')
return False
all_item.sort()
#判断是否两个右部相同
for index in range(len(all_item)-1):#
if all_item[index] == all_item[index+1]:
tk.messagebox.showerror('提示', '产生式两个右部相同产生:'+all_item[index]+',规约-规约冲突,不符合LR(0)文法!!!!')
return False
return True
# 得到LR0的action表和goro表
def LR0_table():
global terminal_char, product_list, node_dic,have_next_node
if not is_correct_LR0(product_list):
return ""
# 创建终结符集合
create_terminal()
cols_goto = ['行'] # 列数据
cols_action = ['行', 'Action'] # 列数据
window_width = 50
for item in terminal_char:
cols_goto.append(item)
window_width += 40
for key in product_list.keys():
cols_goto.append(key)
window_width += 40
window_width += 250
LR0_window = Tk()
LR0_window.title("LR0表")
CenterWindow(LR0_window, window_width, 600)
def init_LR0_window(label_temp, btn, E):
LR0_window.wm_attributes('-topmost', 0)
start_c = E.get()
#若开始的产生式没有结束符,则自动加上
for index in range(len(product_list[start_c])):
item_index = product_list[start_c][index]
if '#' not in item_index:
product_list[start_c][index] = product_list[start_c][index]+'#'
if start_c < 'A' or start_c > 'Z':
tk.messagebox.showerror('提示', '请输入大写字符!!')
LR0_window.wm_attributes('-topmost', 1)
return ""
elif start_c not in product_list.keys():
tk.messagebox.showerror('提示', '没有该产生式!!请检查!!')
LR0_window.wm_attributes('-topmost', 1)
return ""
label_temp.destroy()
btn.destroy()
E.destroy()
label = Label(LR0_window, text="LR(0) Goto表", bg=bg)
label.place(x=100, y=0)
label2 = Label(LR0_window, text="LR(0) Action表", bg=bg)
label2.place(x=window_width - 150, y=0)
close_btn = tk.Button(LR0_window, text="关闭", activebackground="#40E0D0", bg=bg,
command=lambda: close_window(LR0_window))
close_btn.place(x=220, y=550, width=100, height=50)
preView_btn = tk.Button(LR0_window, text="预览每个序号的节点内容", activebackground="#40E0D0", bg=bg,
command=lambda: preview_node(have_next_node))
preView_btn.place(x=420, y=550, width=200, height=50)
tree_goto_action = create_tree_goto_action(LR0_window, cols_goto, cols_action, window_width) # 创建表格
tree_goto = tree_goto_action[0]
tree_action = tree_goto_action[1]
for col in cols_goto:
tree_goto.heading(col, text=col)
tree_goto.column(col, width=40, anchor=S) # 定义列
tree_action.heading(cols_action[0], text=cols_action[0])
tree_action.column(cols_action[0], width=40, anchor=S) # 定义列
tree_action.heading(cols_action[1], text=cols_action[1])
tree_action.column(cols_action[1], width=150, anchor=S) # 定义列
all_node = automaton_LR0(product_list, start_c)
# 获取分析数据
for node_item in all_node:
data_goto = ()
data_action = ()
line = node_item.get_node_index()
data_goto += (line,)
data_action += (line,)
temp = tree_goto.insert("", "end", values=data_goto)
for key in node_item.get_next_node_dic().keys():
tree_goto.set(temp, column=key, value=node_item.get_next_node_dic()[key])
if node_item.get_next_node_dic() != {}: # 含下一个节点 要执行Shift操作
data_action += ('Shift',)
tree_action.insert("", "end", values=data_action)
# print(node_item.get_next_node_dic())
else: # 不含下一个操作,操作分为Reduce 或 Accept
is_Accept = False
node_item_pro = node_item.get_this_node_product_dic()
for key_pro in node_item_pro.keys():
if len(node_item_pro[key_pro]) == 0:
continue
if '#.' in node_item_pro[key_pro][0]:
is_Accept = True
str_reduce_or_accept = node_item_pro[key_pro][0].replace('.', '')
key_str = key_pro
if is_Accept:
data_action += ('Accept : ' + key_str + " --> " + str_reduce_or_accept,)
tree_action.insert("", "end", values=data_action)
else:
data_action += ('Reduce : ' + key_str + " --> " + str_reduce_or_accept,)
tree_action.insert("", "end", values=data_action)
label = Label(LR0_window, text="请输入开始字符:", bg=bg)
label.place(x=10, y=200)
comvalue = tkinter.StringVar() # 窗体自带的文本,新建一个值
start_char = ttk.Combobox(LR0_window, textvariable=comvalue, width=10) # 初始化下拉列表
N_T_char = () # 记录全部非终结符
for key in product_list.keys():
N_T_char += (key,)
start_char["values"] = N_T_char
start_char.current(0) # 选择第一个作为开始字符
start_char.place(x=120, y=200)
btn_sure = Button(LR0_window, text="确定", command=lambda: init_LR0_window(label, btn_sure, start_char), bg=bg,
activebackground="#40E0D0", width=4, height=2)
btn_sure.place(x=250, y=190)
if __name__ == "__main__":
init_window()