根据如下要求修改代码:二、实验要求
将实验 1-实验 3 的实验内容进行整合设计,实现对指定 C--语言源程序文本文件的读取,并能够对
该源程序中的中间分析结果,即 3 个实验的结果进行逐步展示。
程序实现要求为:
1、输入为 C--程序和对应的文法。
2、系统能够输出经过预处理后的源程序(去掉注释、换行、空格等)
3、能够将该源程序中所有的单词根据其所属类型(整数、保留字、运算符、标识符等。定义的 C--
语言中的标识符只能以字母或下划线开头)进行归类显示。
4、能够输出文法的非终结符的 FIRST 集和 FOLLOW 集,并以表的形式进行展示。
5、能够输出文法的 LL(1)分析表。
6、分析输入符号串(C—程序)是否为文法的句子,打印分析过程表。
7、设计 GUI 界面或其他可展示方式,完成上述 6 个要求。代码如下:import re
from collections import defaultdict, deque
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
class LoginWindow:
def __init__(self, root):
self.root = root
self.root.title("编译原理实验 - 登录")
self.root.geometry("400x300")
# 创建登录框架
login_frame = tk.Frame(root, padx=20, pady=20)
login_frame.pack(expand=True)
# 标题
title_label = tk.Label(login_frame, text="编译原理实验系统", font=("Arial", 16))
title_label.grid(row=0, column=0, columnspan=2, pady=10)
# 学号输入
tk.Label(login_frame, text="学号:").grid(row=1, column=0, sticky="w", pady=5)
self.student_id = tk.Entry(login_frame, width=30)
self.student_id.grid(row=1, column=1, pady=5)
# 姓名输入
tk.Label(login_frame, text="姓名:").grid(row=2, column=0, sticky="w", pady=5)
self.name = tk.Entry(login_frame, width=30)
self.name.grid(row=2, column=1, pady=5)
# 班级输入
tk.Label(login_frame, text="班级:").grid(row=3, column=0, sticky="w", pady=5)
self.class_name = tk.Entry(login_frame, width=30)
self.class_name.grid(row=3, column=1, pady=5)
# 登录按钮
login_button = tk.Button(login_frame, text="登录", command=self.login, width=10)
login_button.grid(row=4, column=0, columnspan=2, pady=20)
# 登录状态
self.status_label = tk.Label(login_frame, text="", fg="red")
self.status_label.grid(row=5, column=0, columnspan=2)
def login(self):
# 获取输入
student_id = self.student_id.get().strip()
name = self.name.get().strip()
class_name = self.class_name.get().strip()
# 验证输入
if not student_id or not name or not class_name:
self.status_label.config(text="请填写所有信息")
return
# 关闭登录窗口并启动主程序
self.root.withdraw()
main_window = tk.Toplevel(self.root)
main_window.protocol("WM_DELETE_WINDOW", lambda: self.on_main_close(main_window))
app = CompilerGUI(main_window, student_id, name, class_name)
def on_main_close(self, main_window):
main_window.destroy()
self.root.destroy()
# 词法分析器实现
token_specs = [
('NUMBER', r'\d+'),
('ID', r'[a-zA-Z_][a-zA-Z0-9_]*'),
('OP', r'[+\-*/]'),
('PAREN', r'[()]'),
('SKIP', r'[ \t\n]+')
]
token_re = '|'.join('(?P<%s>%s)' % pair for pair in token_specs)
def lex(code):
tokens = []
for match in re.finditer(token_re, code):
kind = match.lastgroup
value = match.group()
if kind == 'SKIP': continue
tokens.append((kind, value))
return tokens
# 文法处理模块
def eliminate_left_recursion(grammar):
new_rules = {}
for A in list(grammar.keys()):
alpha = [rule for rule in grammar[A] if rule and rule[0] == A]
if not alpha: continue
A_prime = A + "'"
new_rules[A] = [rule for rule in grammar[A] if not rule or rule[0] != A]
new_rules[A_prime] = [rule[1:] + [A_prime] for rule in alpha] + [['ε']]
return {**grammar, **new_rules}
# 集合计算模块
def compute_first(grammar):
first = defaultdict(set)
changed = True
while changed:
changed = False
for nt in grammar:
for production in grammar[nt]:
for symbol in production:
if symbol in grammar: # 非终结符
before = len(first[nt])
first[nt] |= first[symbol] - {'ε'}
if 'ε' not in first[symbol]:
break
if all(s in grammar and 'ε' in first[s] for s in production):
first[nt].add('ε')
if len(first[nt]) > before:
changed = True
else: # 终结符
if symbol != 'ε' and symbol not in first[nt]:
first[nt].add(symbol)
changed = True
break
return first
def compute_follow(grammar, first):
follow = defaultdict(set)
start_symbol = next(iter(grammar))
follow[start_symbol].add('$')
changed = True
while changed:
changed = False
for nt in grammar:
for production in grammar[nt]:
for i, symbol in enumerate(production):
if symbol not in grammar:
continue # 终结符不需要 Follow 集
for beta in production[i+1:]:
first_beta = compute_production_first([beta], first)
follow[symbol] |= first_beta - {'ε'}
if 'ε' not in first_beta:
break
else:
if 'ε' in compute_production_first(production[i+1:], first):
follow[symbol] |= follow[nt]
continue
if not production[i+1:] and 'ε' in compute_production_first(production[i+1:], first):
follow[symbol] |= follow[nt]
before = sum(len(follow[nt]) for nt in follow)
changed = False
return follow
# 辅助函数:计算产生式的 First 集
def compute_production_first(production, first):
result = set()
for symbol in production:
result |= first[symbol] - {'ε'}
if 'ε' not in first[symbol]:
break
else:
result.add('ε')
return result
# LL(1)分析表生成
def build_ll1_table(grammar, first, follow):
table = defaultdict(dict)
for nt in grammar:
for production in grammar[nt]:
first_set = compute_production_first(production, first)
for term in first_set - {'ε'}:
if term in table[nt]:
messagebox.showerror("错误", f"LL(1)冲突:{nt} 在终结符 {term} 上有多个产生式")
table[nt][term] = production
if 'ε' in first_set:
for term in follow[nt]:
if term in table[nt]:
messagebox.showerror("错误", f"LL(1)冲突:{nt} 在终结符 {term} 上有多个产生式")
table[nt][term] = production
return table
# GUI界面设计
class CompilerGUI:
def __init__(self, window, student_id, name, class_name):
self.window = window
self.window.title("编译原理实验")
self.grammar = {}
self.first = defaultdict(set)
self.follow = defaultdict(set)
self.ll1_table = defaultdict(dict)
# 显示用户信息
self.create_user_info_panel(student_id, name, class_name)
self.create_editor_panel()
self.create_analysis_panel()
self.create_result_view()
self.window.protocol("WM_DELETE_WINDOW", self.on_close)
def create_user_info_panel(self, student_id, name, class_name):
frame = tk.Frame(self.window)
frame.pack(fill=tk.X, padx=10, pady=5)
info_text = f"学号: {student_id} | 姓名: {name} | 班级: {class_name}"
info_label = tk.Label(frame, text=info_text, font=("Arial", 10))
info_label.pack(anchor="w")
logout_button = tk.Button(frame, text="退出登录", command=self.logout)
logout_button.pack(side="right")
def logout(self):
self.window.destroy()
root = tk.Tk()
login = LoginWindow(root)
root.mainloop()
def on_close(self):
self.window.destroy()
def create_editor_panel(self):
frame = tk.Frame(self.window)
frame.pack(fill=tk.BOTH, expand=True)
self.editor_label = tk.Label(frame, text="输入源代码")
self.editor_label.pack()
self.editor = tk.Text(frame, height=15, width=80)
self.editor.pack(fill=tk.BOTH, expand=True)
self.grammar_label = tk.Label(frame, text="输入文法")
self.grammar_label.pack()
self.grammar_input = tk.Text(frame, height=10, width=80)
self.grammar_input.pack(fill=tk.BOTH, expand=True)
self.analyze_btn = tk.Button(frame, text="开始分析", command=self.start_analysis)
self.analyze_btn.pack(pady=10)
def create_analysis_panel(self):
frame = tk.Frame(self.window)
frame.pack(fill=tk.BOTH, expand=True)
self.tokens_label = tk.Label(frame, text="Token流")
self.tokens_label.pack()
self.tokens_text = tk.Text(frame, height=10, width=80)
self.tokens_text.pack(fill=tk.BOTH, expand=True)
def create_result_view(self):
frame = tk.Frame(self.window)
frame.pack(fill=tk.BOTH, expand=True)
# FIRST集和 FOLLOW集表格
self.first_label = tk.Label(frame, text="FIRST集")
self.first_label.grid(row=0, column=0)
self.first_text = tk.Text(frame, height=10, width=40)
self.first_text.grid(row=1, column=0)
self.follow_label = tk.Label(frame, text="FOLLOW集")
self.follow_label.grid(row=0, column=1)
self.follow_text = tk.Text(frame, height=10, width=40)
self.follow_text.grid(row=1, column=1)
self.ll1_label = tk.Label(frame, text="LL(1)分析表")
self.ll1_label.grid(row=0, column=2)
self.ll1_text = tk.Text(frame, height=10, width=40)
self.ll1_text.grid(row=1, column=2)
# 分析过程表
self.analysis_label = tk.Label(frame, text="分析过程")
self.analysis_label.grid(row=2, column=0, columnspan=3)
self.analysis_table = ttk.Treeview(frame, columns=("步骤", "栈内容", "输入符号", "动作"), show="headings")
self.analysis_table.heading("步骤", text="步骤")
self.analysis_table.heading("栈内容", text="栈内容")
self.analysis_table.heading("输入符号", text="输入符号")
self.analysis_table.heading("动作", text="动作")
self.analysis_table.grid(row=3, column=0, columnspan=3, sticky="nsew")
# 配置列权重,使组件可以扩展
for i in range(3):
frame.columnconfigure(i, weight=1)
frame.rowconfigure(3, weight=1)
def start_analysis(self):
code = self.editor.get("1.0", tk.END)
grammar_text = self.grammar_input.get("1.0", tk.END)
# 解析文法
self.parse_grammar(grammar_text)
# 词法分析
tokens = lex(code)
self.display_tokens(tokens)
# 消除左递归
self.grammar = eliminate_left_recursion(self.grammar)
# 计算 FIRST 和 FOLLOW 集
self.first = compute_first(self.grammar)
self.follow = compute_follow(self.grammar, self.first)
self.display_first_follow()
# 生成 LL(1) 分析表
self.ll1_table = build_ll1_table(self.grammar, self.first, self.follow)
self.display_ll1_table()
# 分析输⼊符号串
self.analyze_input(tokens)
def parse_grammar(self, grammar_text):
self.grammar = {}
for line in grammar_text.splitlines():
if '->' in line:
lhs, rhs = line.split('->')
lhs = lhs.strip()
rhs = [r.strip().split() for r in rhs.split('|')]
self.grammar[lhs] = rhs
def display_tokens(self, tokens):
self.tokens_text.delete("1.0", tk.END)
for token in tokens:
self.tokens_text.insert(tk.END, f"{token[0]}\t{token[1]}\n")
def display_first_follow(self):
self.first_text.delete("1.0", tk.END)
self.follow_text.delete("1.0", tk.END)
for nt in self.first:
self.first_text.insert(tk.END, f"{nt}: {', '.join(self.first[nt])}\n")
for nt in self.follow:
self.follow_text.insert(tk.END, f"{nt}: {', '.join(self.follow[nt])}\n")
def display_ll1_table(self):
self.ll1_text.delete("1.0", tk.END)
for nt in self.ll1_table:
for term in self.ll1_table[nt]:
self.ll1_text.insert(tk.END, f"{nt} -> {term}: {self.ll1_table[nt][term]}\n")
def analyze_input(self, tokens):
stack = ['$', list(self.grammar.keys())[0]]
input_tokens = deque(tokens + [('$', '$')])
self.analysis_table.delete(*self.analysis_table.get_children())
step = 1
while stack:
current_nt = stack[-1]
current_token = input_tokens[0][0]
if current_nt == current_token:
stack.pop()
input_tokens.popleft()
self.analysis_table.insert("", tk.END, values=(step, stack.copy(), input_tokens.copy(), f"匹配 {current_token}"))
step += 1
if not stack:
self.analysis_table.insert("", tk.END, values=(step, stack.copy(), input_tokens.copy(), "分析成功!"))
break
else:
if current_nt in self.ll1_table and current_token in self.ll1_table[current_nt]:
production = self.ll1_table[current_nt][current_token]
stack.pop()
for symbol in reversed(production):
if symbol != 'ε':
stack.append(symbol)
self.analysis_table.insert("", tk.END, values=(step, stack.copy(), input_tokens.copy(), f"{current_nt} → {' '.join(production)}"))
step += 1
else:
self.analysis_table.insert("", tk.END, values=(step, stack.copy(), input_tokens.copy(), "分析失败!"))
break
if __name__ == "__main__":
root = tk.Tk()
login = LoginWindow(root)
root.mainloop()