有限自动机到正规文法转换器v1.0

news2025/6/12 17:44:04

1 项目简介

这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典算法精心打造,能够高效且准确地处理用户输入的任意有限自动机,并将其转换为与之等价的正规文法。这一转换器不仅为用户提供了便捷的工具,还为理解和研究自动机与正规文法之间的关系提供了有力的支持,是进行相关学习和研究的得力助手。

2 功能特点

🖥️ “专业GUI界面”: 多选项卡设计,类似专业分析器生成器

🔄 “智能转换”: 自动将FA转换为等价的正规文法

✅ “完整验证”: 输入验证、FA完整性检查

📊 “可视化展示”: FA结构图、转换表、语言特征分析

💾 “文件操作“: 支持保存/加载FA定义、导出结果

🎯 ”示例库“: 内置多个经典FA示例

⚡ ”一键打包“: 支持打包为独立exe文件

🌐 ”跨平台“: 支持Windows、Linux、macOS

import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext, filedialog
import json
from collections import defaultdict
import os
import sys

class FAToGrammarConverter:
    def __init__(self, root):
        self.root = root
        self.root.title("有限自动机到正规文法转换器 v1.0")
        self.root.geometry("1200x800")
        self.root.minsize(1000, 700)
        
        # 设置图标(如果存在)
        try:
            self.root.iconbitmap('icon.ico')
        except:
            pass
        
        # 存储FA信息
        self.states = set()
        self.alphabet = set()
        self.transitions = defaultdict(list)
        self.start_state = ""
        self.final_states = set()
        
        self.create_gui()
        self.create_menu()
    
    def create_menu(self):
        """创建菜单栏"""
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
        file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_separator()
        file_menu.add_command(label="导出结果", command=self.export_result)
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.root.quit)
        
        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="编辑", menu=edit_menu)
        edit_menu.add_command(label="清空输入", command=self.clear_input)
        edit_menu.add_command(label="加载示例", command=self.load_example)
        
        # 工具菜单
        tools_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="工具", menu=tools_menu)
        tools_menu.add_command(label="验证FA", command=self.validate_fa)
        tools_menu.add_command(label="转换为正规文法", command=self.convert_fa_to_grammar)
        
        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="帮助", menu=help_menu)
        help_menu.add_command(label="使用说明", command=self.show_help)
        help_menu.add_command(label="关于", command=self.show_about)
        
        # 绑定快捷键
        self.root.bind_all("<Control-n>", lambda e: self.new_file())
        self.root.bind_all("<Control-o>", lambda e: self.open_file())
        self.root.bind_all("<Control-s>", lambda e: self.save_file())
    
    def create_gui(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)
        main_frame.rowconfigure(1, weight=1)
        
        # 创建Notebook(选项卡)
        notebook = ttk.Notebook(main_frame)
        notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        
        # 输入选项卡
        input_frame = ttk.Frame(notebook, padding="10")
        notebook.add(input_frame, text="输入有限自动机")
        
        # 结果选项卡
        result_frame = ttk.Frame(notebook, padding="10")
        notebook.add(result_frame, text="转换结果")
        
        # 可视化选项卡
        visual_frame = ttk.Frame(notebook, padding="10")
        notebook.add(visual_frame, text="可视化")
        
        self.create_input_tab(input_frame)
        self.create_result_tab(result_frame)
        self.create_visual_tab(visual_frame)
        
        # 状态栏
        self.status_bar = ttk.Label(main_frame, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
    
    def create_input_tab(self, parent):
        """创建输入选项卡"""
        # 标题
        title_label = ttk.Label(parent, text="有限自动机输入界面", 
                               font=("Microsoft YaHei", 16, "bold"))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
        
        # 左侧输入区域
        left_frame = ttk.LabelFrame(parent, text="FA定义", padding="10")
        left_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10))
        left_frame.columnconfigure(1, weight=1)
        
        # 状态集合
        ttk.Label(left_frame, text="状态集合 Q:", font=("Microsoft YaHei", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.states_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))
        self.states_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)
        ttk.Label(left_frame, text="用逗号分隔,如: q0,q1,q2", 
                 font=("Microsoft YaHei", 9), foreground="gray").grid(row=0, column=2, sticky=tk.W, padx=(10, 0))
        
        # 字母表
        ttk.Label(left_frame, text="字母表 Σ:", font=("Microsoft YaHei", 10, "bold")).grid(row=1, column=0, sticky=tk.W, pady=5)
        self.alphabet_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))
        self.alphabet_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)
        ttk.Label(left_frame, text="用逗号分隔,如: a,b,c", 
                 font=("Microsoft YaHei", 9), foreground="gray").grid(row=1, column=2, sticky=tk.W, padx=(10, 0))
        
        # 转换函数
        ttk.Label(left_frame, text="转换函数 δ:", font=("Microsoft YaHei", 10, "bold")).grid(row=2, column=0, sticky=tk.W, pady=5)
        self.transitions_text = scrolledtext.ScrolledText(left_frame, height=8, width=40, font=("Consolas", 10))
        self.transitions_text.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)
        ttk.Label(left_frame, text="每行一个转换\n格式: 起始状态,输入符号,目标状态", 
                 font=("Microsoft YaHei", 9), foreground="gray").grid(row=2, column=2, sticky=tk.W, padx=(10, 0))
        
        # 初始状态
        ttk.Label(left_frame, text="初始状态 q₀:", font=("Microsoft YaHei", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=5)
        self.start_state_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))
        self.start_state_entry.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)
        
        # 终结状态
        ttk.Label(left_frame, text="终结状态 F:", font=("Microsoft YaHei", 10, "bold")).grid(row=4, column=0, sticky=tk.W, pady=5)
        self.final_states_entry = ttk.Entry(left_frame, width=40, font=("Consolas", 10))
        self.final_states_entry.grid(row=4, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=5)
        ttk.Label(left_frame, text="用逗号分隔", 
                 font=("Microsoft YaHei", 9), foreground="gray").grid(row=4, column=2, sticky=tk.W, padx=(10, 0))
        
        # 右侧按钮区域
        right_frame = ttk.LabelFrame(parent, text="操作", padding="10")
        right_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(10, 0))
        
        # 按钮
        ttk.Button(right_frame, text="加载示例", command=self.load_example, width=15).grid(row=0, column=0, pady=5, sticky=tk.W+tk.E)
        ttk.Button(right_frame, text="验证FA", command=self.validate_fa, width=15).grid(row=1, column=0, pady=5, sticky=tk.W+tk.E)
        ttk.Button(right_frame, text="转换为正规文法", command=self.convert_fa_to_grammar, 
                  style="Accent.TButton", width=15).grid(row=2, column=0, pady=10, sticky=tk.W+tk.E)
        ttk.Button(right_frame, text="清空输入", command=self.clear_input, width=15).grid(row=3, column=0, pady=5, sticky=tk.W+tk.E)
        
        # 示例列表
        ttk.Label(right_frame, text="示例库:", font=("Microsoft YaHei", 10, "bold")).grid(row=4, column=0, sticky=tk.W, pady=(20, 5))
        self.example_listbox = tk.Listbox(right_frame, height=6, font=("Microsoft YaHei", 9))
        self.example_listbox.grid(row=5, column=0, sticky=(tk.W, tk.E), pady=5)
        self.example_listbox.insert(0, "识别以'a'开头的字符串")
        self.example_listbox.insert(1, "识别偶数个'a'")
        self.example_listbox.insert(2, "识别包含'ab'的字符串")
        self.example_listbox.bind("<Double-Button-1>", self.load_selected_example)
        
        # 配置权重
        parent.columnconfigure(0, weight=2)
        parent.columnconfigure(1, weight=1)
        parent.rowconfigure(1, weight=1)
    
    def create_result_tab(self, parent):
        """创建结果选项卡"""
        # 结果显示区域
        result_frame = ttk.LabelFrame(parent, text="转换结果 - 正规文法", padding="10")
        result_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        result_frame.columnconfigure(0, weight=1)
        result_frame.rowconfigure(0, weight=1)
        
        self.result_text = scrolledtext.ScrolledText(result_frame, height=25, width=100, 
                                                   font=("Consolas", 11))
        self.result_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 按钮区域
        button_frame = ttk.Frame(parent)
        button_frame.grid(row=1, column=0, pady=(10, 0))
        
        ttk.Button(button_frame, text="复制结果", command=self.copy_result).grid(row=0, column=0, padx=5)
        ttk.Button(button_frame, text="保存结果", command=self.save_result).grid(row=0, column=1, padx=5)
        ttk.Button(button_frame, text="清空结果", command=self.clear_result).grid(row=0, column=2, padx=5)
        
        parent.columnconfigure(0, weight=1)
        parent.rowconfigure(0, weight=1)
    
    def create_visual_tab(self, parent):
        """创建可视化选项卡"""
        # 可视化区域
        visual_frame = ttk.LabelFrame(parent, text="FA结构可视化", padding="10")
        visual_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        visual_frame.columnconfigure(0, weight=1)
        visual_frame.rowconfigure(0, weight=1)
        
        self.visual_text = scrolledtext.ScrolledText(visual_frame, height=25, width=100, 
                                                   font=("Consolas", 10))
        self.visual_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        parent.columnconfigure(0, weight=1)
        parent.rowconfigure(0, weight=1)
    
    def load_selected_example(self, event):
        """加载选中的示例"""
        selection = self.example_listbox.curselection()
        if selection:
            index = selection[0]
            if index == 0:
                self.load_example()
            elif index == 1:
                self.load_even_a_example()
            elif index == 2:
                self.load_contains_ab_example()
    
    def load_example(self):
        """加载示例FA - 识别以'a'开头的字符串"""
        self.clear_input()
        self.states_entry.insert(0, "q0,q1,q2")
        self.alphabet_entry.insert(0, "a,b")
        self.transitions_text.insert(1.0, "q0,a,q1\nq1,a,q1\nq1,b,q1\nq0,b,q2\nq2,a,q2\nq2,b,q2")
        self.start_state_entry.insert(0, "q0")
        self.final_states_entry.insert(0, "q1")
        self.update_status("已加载示例:识别以'a'开头的字符串")
    
    def load_even_a_example(self):
        """加载示例FA - 识别偶数个'a'"""
        self.clear_input()
        self.states_entry.insert(0, "q0,q1")
        self.alphabet_entry.insert(0, "a,b")
        self.transitions_text.insert(1.0, "q0,a,q1\nq1,a,q0\nq0,b,q0\nq1,b,q1")
        self.start_state_entry.insert(0, "q0")
        self.final_states_entry.insert(0, "q0")
        self.update_status("已加载示例:识别偶数个'a'")
    
    def load_contains_ab_example(self):
        """加载示例FA - 识别包含'ab'的字符串"""
        self.clear_input()
        self.states_entry.insert(0, "q0,q1,q2")
        self.alphabet_entry.insert(0, "a,b")
        self.transitions_text.insert(1.0, "q0,a,q1\nq0,b,q0\nq1,a,q1\nq1,b,q2\nq2,a,q2\nq2,b,q2")
        self.start_state_entry.insert(0, "q0")
        self.final_states_entry.insert(0, "q2")
        self.update_status("已加载示例:识别包含'ab'的字符串")
    
    def clear_input(self):
        """清空所有输入"""
        self.states_entry.delete(0, tk.END)
        self.alphabet_entry.delete(0, tk.END)
        self.transitions_text.delete(1.0, tk.END)
        self.start_state_entry.delete(0, tk.END)
        self.final_states_entry.delete(0, tk.END)
        self.update_status("已清空输入")
    
    def update_status(self, message):
        """更新状态栏"""
        self.status_bar.config(text=message)
    
    def parse_input(self):
        """解析用户输入的FA"""
        try:
            # 解析状态集合
            states_str = self.states_entry.get().strip()
            if not states_str:
                raise ValueError("状态集合不能为空")
            self.states = set(s.strip() for s in states_str.split(','))
            
            # 解析字母表
            alphabet_str = self.alphabet_entry.get().strip()
            if not alphabet_str:
                raise ValueError("字母表不能为空")
            self.alphabet = set(s.strip() for s in alphabet_str.split(','))
            
            # 解析转换函数
            transitions_str = self.transitions_text.get(1.0, tk.END).strip()
            if not transitions_str:
                raise ValueError("转换函数不能为空")
            
            self.transitions = defaultdict(list)
            for line in transitions_str.split('\n'):
                line = line.strip()
                if line:
                    parts = [p.strip() for p in line.split(',')]
                    if len(parts) != 3:
                        raise ValueError(f"转换函数格式错误: {line}")
                    from_state, symbol, to_state = parts
                    if from_state not in self.states:
                        raise ValueError(f"状态 {from_state} 不在状态集合中")
                    if to_state not in self.states:
                        raise ValueError(f"状态 {to_state} 不在状态集合中")
                    if symbol not in self.alphabet:
                        raise ValueError(f"符号 {symbol} 不在字母表中")
                    self.transitions[(from_state, symbol)].append(to_state)
            
            # 解析初始状态
            self.start_state = self.start_state_entry.get().strip()
            if not self.start_state:
                raise ValueError("初始状态不能为空")
            if self.start_state not in self.states:
                raise ValueError("初始状态不在状态集合中")
            
            # 解析终结状态
            final_states_str = self.final_states_entry.get().strip()
            if not final_states_str:
                raise ValueError("终结状态不能为空")
            self.final_states = set(s.strip() for s in final_states_str.split(','))
            for state in self.final_states:
                if state not in self.states:
                    raise ValueError(f"终结状态 {state} 不在状态集合中")
            
            return True
        except ValueError as e:
            messagebox.showerror("输入错误", str(e))
            self.update_status(f"错误: {str(e)}")
            return False
    
    def validate_fa(self):
        """验证FA的完整性和正确性"""
        if not self.parse_input():
            return False
        
        try:
            # 检查是否所有状态都可达
            reachable_states = set()
            stack = [self.start_state]
            reachable_states.add(self.start_state)
            
            while stack:
                current = stack.pop()
                for symbol in self.alphabet:
                    if (current, symbol) in self.transitions:
                        for next_state in self.transitions[(current, symbol)]:
                            if next_state not in reachable_states:
                                reachable_states.add(next_state)
                                stack.append(next_state)
            
            unreachable = self.states - reachable_states
            
            # 显示验证结果
            result = ["FA验证结果:", "=" * 30]
            result.append(f"状态总数: {len(self.states)}")
            result.append(f"字母表大小: {len(self.alphabet)}")
            result.append(f"转换函数数量: {sum(len(v) for v in self.transitions.values())}")
            result.append(f"可达状态: {len(reachable_states)}")
            
            if unreachable:
                result.append(f"不可达状态: {', '.join(unreachable)}")
                result.append("警告: 存在不可达状态!")
            else:
                result.append("✓ 所有状态都可达")
            
            # 检查FA类型
            is_dfa = all(len(transitions) <= 1 for transitions in self.transitions.values())
            result.append(f"FA类型: {'DFA (确定有限自动机)' if is_dfa else 'NFA (非确定有限自动机)'}")
            
            messagebox.showinfo("验证结果", "\n".join(result))
            self.update_status("FA验证完成")
            return True
            
        except Exception as e:
            messagebox.showerror("验证错误", f"验证过程中发生错误: {str(e)}")
            self.update_status(f"验证失败: {str(e)}")
            return False
    
    def convert_fa_to_grammar(self):
        """将FA转换为正规文法"""
        if not self.parse_input():
            return
        
        try:
            # 构造正规文法
            grammar = self.fa_to_regular_grammar()
            self.display_grammar(grammar)
            self.display_visualization()
            self.update_status("转换完成")
        except Exception as e:
            messagebox.showerror("转换错误", f"转换过程中发生错误: {str(e)}")
            self.update_status(f"转换失败: {str(e)}")
    
    def fa_to_regular_grammar(self):
        """FA到正规文法的转换算法"""
        productions = []
        
        # 对于每个转换 δ(q, a) = p,创建产生式 q → ap
        for (from_state, symbol), to_states in self.transitions.items():
            for to_state in to_states:
                production = f"{from_state} → {symbol}{to_state}"
                productions.append(production)
        
        # 对于每个终结状态 f ∈ F,创建产生式 f → ε
        for final_state in self.final_states:
            production = f"{final_state} → ε"
            productions.append(production)
        
        grammar = {
            'non_terminals': self.states,
            'terminals': self.alphabet,
            'start_symbol': self.start_state,
            'productions': productions
        }
        
        return grammar
    
    def display_grammar(self, grammar):
        """显示转换后的正规文法"""
        result = []
        result.append("╔" + "═" * 60 + "╗")
        result.append("║" + "转换结果:正规文法 G = (N, T, P, S)".center(60) + "║")
        result.append("╚" + "═" * 60 + "╝")
        result.append("")
        
        # 非终结符集合
        result.append("【非终结符集合】")
        result.append(f"N = {{{', '.join(sorted(grammar['non_terminals']))}}}")
        result.append("")
        
        # 终结符集合
        result.append("【终结符集合】")
        result.append(f"T = {{{', '.join(sorted(grammar['terminals']))}}}")
        result.append("")
        
        # 开始符号
        result.append("【开始符号】")
        result.append(f"S = {grammar['start_symbol']}")
        result.append("")
        
        # 产生式集合
        result.append("【产生式集合】")
        result.append("P = {")
        for i, production in enumerate(grammar['productions']):
            prefix = "    " if i < len(grammar['productions']) - 1 else "    "
            suffix = "," if i < len(grammar['productions']) - 1 else ""
            result.append(f"{prefix}{production}{suffix}")
        result.append("}")
        result.append("")
        
        # 转换算法说明
        result.append("╔" + "═" * 60 + "╗")
        result.append("║" + "转换算法说明".center(60) + "║")
        result.append("╚" + "═" * 60 + "╝")
        result.append("")
        result.append("算法步骤:")
        result.append("1. 对于FA中的每个转换 δ(q, a) = p,")
        result.append("   创建产生式 q → ap")
        result.append("")
        result.append("2. 对于FA中的每个终结状态 f ∈ F,")
        result.append("   创建产生式 f → ε")
        result.append("")
        result.append("3. FA的初始状态成为文法的开始符号")
        result.append("")
        result.append("✓ 此正规文法生成的语言与原FA识别的语言相同")
        result.append("✓ 转换保持了语言的等价性")
        
        # 显示结果
        self.result_text.delete(1.0, tk.END)
        self.result_text.insert(1.0, '\n'.join(result))
    
    def display_visualization(self):
        """显示FA的可视化表示"""
        visual = []
        visual.append("╔" + "═" * 60 + "╗")
        visual.append("║" + "有限自动机结构可视化".center(60) + "║")
        visual.append("╚" + "═" * 60 + "╝")
        visual.append("")
        
        # 状态图表示
        visual.append("【状态转换图】")
        visual.append("")
        
        # 绘制状态
        for state in sorted(self.states):
            state_type = ""
            if state == self.start_state:
                state_type += "[初始]"
            if state in self.final_states:
                state_type += "[终结]"
            visual.append(f"状态 {state} {state_type}")
            
            # 显示从该状态出发的转换
            for symbol in sorted(self.alphabet):
                if (state, symbol) in self.transitions:
                    for target in self.transitions[(state, symbol)]:
                        visual.append(f"  --{symbol}--> {target}")
            visual.append("")
        
        # 转换表
        visual.append("【转换表】")
        visual.append("δ" + " " * 8 + "|" + " ".join(f"{s:>8}" for s in sorted(self.alphabet)))
        visual.append("-" * (10 + 9 * len(self.alphabet)))
        
        for state in sorted(self.states):
            row = f"{state:<8} |"
            for symbol in sorted(self.alphabet):
                if (state, symbol) in self.transitions:
                    targets = ",".join(self.transitions[(state, symbol)])
                    row += f"{targets:>8} "
                else:
                    row += f"{'∅':>8} "
            visual.append(row)
        
        visual.append("")
        visual.append("【语言描述】")
        visual.append("此FA识别的正规语言L(FA)的特征:")
        
        # 简单的语言特征分析
        if len(self.final_states) == 1 and self.start_state in self.final_states:
            visual.append("- 包含空字符串ε")
        
        # 检查是否存在循环
        has_loops = any(state in self.transitions.get((state, symbol), []) 
                       for state in self.states for symbol in self.alphabet)
        if has_loops:
            visual.append("- 可以生成无限长度的字符串")
        
        visual.append(f"- 字母表: {{{', '.join(sorted(self.alphabet))}}}")
        visual.append(f"- 状态数: {len(self.states)}")
        
        self.visual_text.delete(1.0, tk.END)
        self.visual_text.insert(1.0, '\n'.join(visual))
    
    def copy_result(self):
        """复制结果到剪贴板"""
        self.root.clipboard_clear()
        self.root.clipboard_append(self.result_text.get(1.0, tk.END))
        self.update_status("结果已复制到剪贴板")
    
    def save_result(self):
        """保存结果到文件"""
        filename = filedialog.asksaveasfilename(
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        if filename:
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(self.result_text.get(1.0, tk.END))
                self.update_status(f"结果已保存到: {filename}")
            except Exception as e:
                messagebox.showerror("保存错误", f"无法保存文件: {str(e)}")
    
    def clear_result(self):
        """清空结果"""
        self.result_text.delete(1.0, tk.END)
        self.visual_text.delete(1.0, tk.END)
        self.update_status("结果已清空")
    
    def new_file(self):
        """新建文件"""
        self.clear_input()
        self.clear_result()
        self.update_status("新建文件")
    
    def open_file(self):
        """打开文件"""
        filename = filedialog.askopenfilename(
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        if filename:
            try:
                with open(filename, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                
                self.clear_input()
                self.states_entry.insert(0, ','.join(data.get('states', [])))
                self.alphabet_entry.insert(0, ','.join(data.get('alphabet', [])))
                self.start_state_entry.insert(0, data.get('start_state', ''))
                self.final_states_entry.insert(0, ','.join(data.get('final_states', [])))
                
                transitions_text = '\n'.join([f"{t[0]},{t[1]},{t[2]}" for t in data.get('transitions', [])])
                self.transitions_text.insert(1.0, transitions_text)
                
                self.update_status(f"已打开文件: {filename}")
            except Exception as e:
                messagebox.showerror("打开错误", f"无法打开文件: {str(e)}")
    
    def save_file(self):
        """保存文件"""
        if not self.parse_input():
            return
        
        filename = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )
        if filename:
            try:
                transitions = []
                for (from_state, symbol), to_states in self.transitions.items():
                    for to_state in to_states:
                        transitions.append([from_state, symbol, to_state])
                
                data = {
                    'states': list(self.states),
                    'alphabet': list(self.alphabet),
                    'transitions': transitions,
                    'start_state': self.start_state,
                    'final_states': list(self.final_states)
                }
                
                with open(filename, 'w', encoding='utf-8') as f:
                    json.dump(data, f, ensure_ascii=False, indent=2)
                
                self.update_status(f"已保存到: {filename}")
            except Exception as e:
                messagebox.showerror("保存错误", f"无法保存文件: {str(e)}")
    
    def export_result(self):
        """导出转换结果"""
        if not self.result_text.get(1.0, tk.END).strip():
            messagebox.showwarning("导出警告", "没有可导出的结果")
            return
        
        filename = filedialog.asksaveasfilename(
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        if filename:
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(self.result_text.get(1.0, tk.END))
                    f.write("\n" + "="*60 + "\n")
                    f.write("可视化信息:\n")
                    f.write(self.visual_text.get(1.0, tk.END))
                
                self.update_status(f"结果已导出到: {filename}")
            except Exception as e:
                messagebox.showerror("导出错误", f"无法导出文件: {str(e)}")
    
    def show_help(self):
        """显示帮助信息"""
        help_text = """
有限自动机到正规文法转换器 - 使用说明

1. 输入有限自动机:
   • 状态集合:输入所有状态,用逗号分隔
   • 字母表:输入所有输入符号,用逗号分隔
   • 转换函数:每行一个转换,格式为"起始状态,输入符号,目标状态"
   • 初始状态:输入初始状态
   • 终结状态:输入所有终结状态,用逗号分隔

2. 操作步骤:
   • 点击"加载示例"可以快速加载预设的FA
   • 点击"验证FA"检查输入的FA是否正确
   • 点击"转换为正规文法"执行转换
   • 在"转换结果"选项卡查看结果
   • 在"可视化"选项卡查看FA结构

3. 文件操作:
   • 支持保存/打开FA定义文件(JSON格式)
   • 支持导出转换结果(文本格式)

4. 快捷键:
   • Ctrl+N:新建
   • Ctrl+O:打开
   • Ctrl+S:保存

5. 注意事项:
   • 状态名和符号不能包含逗号
   • 支持确定和非确定有限自动机
   • 转换后的正规文法与原FA等价
        """
        messagebox.showinfo("使用说明", help_text)
    
    def show_about(self):
        """显示关于信息"""
        about_text = """
有限自动机到正规文法转换器 v1.0

        """
        messagebox.showinfo("关于", about_text)

def main():
    root = tk.Tk()
    
    # 设置样式
    style = ttk.Style()
    style.theme_use('clam')
    
    # 配置颜色主题
    style.configure('Accent.TButton', background='#0078D4', foreground='white')
    
    app = FAToGrammarConverter(root)
    
    # 设置窗口居中
    root.update_idletasks()
    x = (root.winfo_screenwidth() // 2) - (root.winfo_width() // 2)
    y = (root.winfo_screenheight() // 2) - (root.winfo_height() // 2)
    root.geometry(f"+{x}+{y}")
    
    root.mainloop()

if __name__ == "__main__":
    main() 

安装依赖

pip install -r requirements.txt

# 有限自动机到正规文法转换器依赖
# Python 3.6+ 兼容

# GUI框架 (通常随Python安装)
# tkinter - 内置模块

# 打包工具
pyinstaller>=5.0.0

# 其他依赖
setuptools>=40.0.0 

```bash

# 运行构建脚本

build.bat

# 或手动构建

pip install pyinstaller

pyinstaller --onefile --windowed --name="FA转换器" fa_to_grammar_converter.py

# 运行exe文件

dist\FA转换器.exe

# 设置执行权限

chmod +x build.sh

# 运行构建脚本

./build.sh

# 运行可执行文件

./dist/FA转换器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407426.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格