news 2026/5/25 15:36:59

别再死记硬背了!用Python一步步带你生成LR(1)分析表(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再死记硬背了!用Python一步步带你生成LR(1)分析表(附完整代码)

用Python实战解析LR(1)分析表生成:从理论到代码的完整指南

编译原理中的LR(1)分析算法常被视为"龙书"中最具挑战性的内容之一。许多计算机专业学生在学习时,往往陷入抽象概念的泥沼而难以自拔。本文将通过Python代码实现,带您一步步构建LR(1)分析表,让这些抽象概念变得触手可及。

1. 理解LR(1)分析的核心概念

LR(1)分析是自底向上语法分析中最强大的方法之一,它能够处理绝大多数上下文无关文法。与SLR(1)相比,LR(1)通过引入向前搜索符(lahead)显著提高了分析的精确度。

关键术语解析

  • 项目(Item): 形如A→α·β, a的表达式,其中·表示当前分析位置,a是向前搜索符
  • 闭包(Closure): 对项目集进行扩展的操作
  • GOTO函数: 状态转移函数,决定在遇到特定符号时转移到哪个状态

注意:LR(1)分析表由ACTION和GOTO两部分组成,前者处理终结符动作,后者处理非终结符转移

2. 准备工作:文法表示与数据结构设计

在开始编码前,我们需要设计合适的数据结构来表示文法和分析过程中的各种元素。

from collections import defaultdict class Grammar: def __init__(self): self.productions = defaultdict(list) # 产生式存储 self.non_terminals = set() # 非终结符集合 self.terminals = set() # 终结符集合 self.start_symbol = None # 开始符号 def add_production(self, head, body): """添加产生式""" self.productions[head].append(body) self.non_terminals.add(head) for symbol in body: if not symbol.isupper() and symbol != 'ε': self.terminals.add(symbol)

文法示例(我们将使用这个简单但足够展示特性的文法贯穿全文):

E → E + T | T T → T * F | F F → ( E ) | id

3. 构建LR(1)项目集规范族

项目集规范族的构造是LR(1)分析表生成的核心步骤,它包括闭包计算和状态转移两个主要操作。

3.1 拓广文法

首先需要对原文法进行拓广,添加一个新的开始符号S'和产生式S'→S。

def augment_grammar(grammar): """拓广文法""" augmented = Grammar() augmented.start_symbol = grammar.start_symbol + "'" augmented.add_production(augmented.start_symbol, [grammar.start_symbol]) for head in grammar.productions: for body in grammar.productions[head]: augmented.add_production(head, body) return augmented

3.2 计算闭包

闭包操作是LR(1)分析中最关键也最容易出错的环节。以下代码展示了如何正确计算带向前搜索符的项目集闭包。

def closure(items, grammar): """计算LR(1)项目集的闭包""" closure_set = set(items) changed = True while changed: changed = False temp_set = set(closure_set) for item in temp_set: head, body, pos, lookahead = item if pos < len(body) and body[pos] in grammar.non_terminals: B = body[pos] beta = body[pos+1:] if pos+1 < len(body) else [] first_beta = first_of_sequence(beta + [lookahead], grammar) for production in grammar.productions[B]: for b in first_beta: new_item = (B, production, 0, b) if new_item not in closure_set: closure_set.add(new_item) changed = True return frozenset(closure_set)

4. 生成LR(1)分析表

有了项目集规范族后,我们就可以构建分析表了。分析表包括ACTION和GOTO两部分。

4.1 构建ACTION表

ACTION表处理移进和规约操作:

def build_action_table(canonical_collection, grammar): """构建ACTION表""" action_table = defaultdict(dict) for i, state in enumerate(canonical_collection): for item in state: head, body, pos, lookahead = item # 移进项目 if pos < len(body) and body[pos] in grammar.terminals: symbol = body[pos] next_state = goto(state, symbol, grammar) if next_state in canonical_collection: j = canonical_collection.index(next_state) action_table[i][symbol] = ('shift', j) # 规约项目 elif pos == len(body): if head != grammar.start_symbol + "'": for a in [lookahead]: action_table[i][a] = ('reduce', (head, body)) else: action_table[i]['$'] = ('accept',) return action_table

4.2 构建GOTO表

GOTO表处理非终结符的转移:

def build_goto_table(canonical_collection, grammar): """构建GOTO表""" goto_table = defaultdict(dict) for i, state in enumerate(canonical_collection): for A in grammar.non_terminals: next_state = goto(state, A, grammar) if next_state in canonical_collection: j = canonical_collection.index(next_state) goto_table[i][A] = j return goto_table

5. 完整代码实现与测试

现在我们将所有部分组合起来,形成一个完整的LR(1)分析表生成器。

def generate_lr1_table(grammar): """生成完整的LR(1)分析表""" augmented = augment_grammar(grammar) start_item = (augmented.start_symbol, [grammar.start_symbol], 0, '$') canonical_collection = [closure({start_item}, augmented)] changed = True while changed: changed = False temp_collection = list(canonical_collection) for state in temp_collection: for symbol in augmented.terminals | augmented.non_terminals: next_state = goto(state, symbol, augmented) if next_state and next_state not in canonical_collection: canonical_collection.append(next_state) changed = True action_table = build_action_table(canonical_collection, augmented) goto_table = build_goto_table(canonical_collection, augmented) return action_table, goto_table

测试示例

# 定义示例文法 g = Grammar() g.start_symbol = 'E' g.add_production('E', ['E', '+', 'T']) g.add_production('E', ['T']) g.add_production('T', ['T', '*', 'F']) g.add_production('T', ['F']) g.add_production('F', ['(', 'E', ')']) g.add_production('F', ['id']) # 生成分析表 action, goto = generate_lr1_table(g) # 打印ACTION表 print("ACTION表:") for state in action: for symbol in action[state]: print(f"action[{state}][{symbol}] = {action[state][symbol]}")

6. 常见问题与调试技巧

在实现LR(1)分析器时,经常会遇到一些典型问题:

  1. 闭包计算不完整

    • 确保正确处理ε产生式
    • 检查FIRST集合计算是否正确
    • 验证向前搜索符的传播逻辑
  2. 分析表冲突

    • 检查是否为真正的LR(1)文法
    • 确认没有遗漏项目集中的项目
    • 验证GOTO函数是否正确实现
  3. 性能问题

    • 对大型文法,考虑使用更高效的数据结构
    • 缓存FIRST集合计算结果
    • 对重复状态进行哈希处理

调试建议:从简单文法开始,逐步增加复杂度,使用print语句输出中间结果验证每个步骤的正确性

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 15:34:39

终极艾尔登法环存档迁移指南:3分钟学会角色无损转移

终极艾尔登法环存档迁移指南&#xff1a;3分钟学会角色无损转移 【免费下载链接】EldenRingSaveCopier 项目地址: https://gitcode.com/gh_mirrors/el/EldenRingSaveCopier 还在为《艾尔登法环》存档迁移而烦恼吗&#xff1f;当游戏版本更新后&#xff0c;你辛辛苦苦培…

作者头像 李华
网站建设 2026/5/25 15:34:11

PHP与MySQL安全交互-防止SQL注入的终极指南

先跟大家说个真事儿。去年我一个朋友的电商网站被黑了&#xff0c;黑客拿走了两万多条用户订单数据&#xff0c;包括姓名、电话、收货地址。最后怎么进来的&#xff1f;就是登录框那里一个很简单的SQL注入漏洞。那天晚上他给我打电话的时候声音都是抖的。这事儿给我敲了警钟。其…

作者头像 李华
网站建设 2026/5/25 15:29:00

代付模式?

一、代付类型银行代付、第三方支付代付&#xff0c;支持批量转账。二、支持类型B2B、B2C、C2B、C2C 全场景资金划转。三、业务分类充值代付、提现代付、转账代付。四、应用场景返佣、薪资、理赔、提现、采购、缴费等。五、到账时效秒到、实时到账、72 小时到账。

作者头像 李华
网站建设 2026/5/25 15:22:38

PS 批量处理图片大小|2026 最新超详细步骤

在平面设计、电商运营、自媒体排版、日常办公场景中&#xff0c;我们经常需要批量调整数十张甚至上百张图片的尺寸、大小和分辨率。如果逐张手动修改&#xff0c;不仅耗时费力、效率极低&#xff0c;还容易出现尺寸不统一、画面拉伸变形、画质参差不齐等问题。为了解决大家的批…

作者头像 李华