news 2026/7/11 20:10:55

LR(0) 分析表构造实战:Python 实现 5 步自动生成算法(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LR(0) 分析表构造实战:Python 实现 5 步自动生成算法(附完整代码)

LR(0)分析表构造实战:Python实现5步自动生成算法(附完整代码)

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

在编译原理中,语法分析是编译器前端的关键环节。LR(0)分析法作为一种自底向上的分析方法,因其强大的分析能力和相对简单的构造逻辑,成为许多编译器实现的首选方案。

LR(0)名称的由来

  • L:从左到右扫描输入串(Left-to-right)
  • R:构造最右推导的逆过程(Rightmost derivation)
  • 0:分析时不需向前查看任何符号(零个lookahead符号)

项目集与活前缀是理解LR(0)分析的两个核心概念:

# 示例:项目表示例 production = "E → E + T" items = [ "E → · E + T", # 初始状态 "E → E · + T", # 部分匹配 "E → E + · T", # 继续匹配 "E → E + T ·" # 完全匹配 ]

活前缀是指规范句型的一个前缀,它不包含句柄之后的任何符号。在分析过程中,栈中的符号串始终构成活前缀,这是LR分析正确性的重要保证。

2. 构建LR(0)分析器的5个关键步骤

2.1 拓广文法

拓广文法是为了给分析过程定义一个明确的起点和终点。通过在原始文法中添加一个新的开始符号和产生式,我们可以确保分析器有唯一的接受状态。

def augment_grammar(grammar): """拓广文法示例""" start_symbol = grammar[0][0] new_grammar = [f"S' → {start_symbol}"] + grammar return new_grammar # 原始文法示例 original_grammar = [ "E → E + T", "E → T", "T → T * F", "T → F", "F → ( E )", "F → id" ] augmented_grammar = augment_grammar(original_grammar)

2.2 构造项目集闭包

项目集闭包是LR(0)分析的基础数据结构,它表示分析器在某一时刻可能处于的所有状态。

闭包运算规则

  1. 将初始项目加入闭包
  2. 对于闭包中形如A→α·Bβ的项目,将所有B→·γ的项目加入闭包
  3. 重复步骤2直到闭包不再扩大
def closure(items, grammar): """计算项目集闭包""" closure_set = set(items) changed = True while changed: changed = False for item in list(closure_set): dot_pos = item.index('·') if dot_pos + 1 < len(item) and item[dot_pos+1].isupper(): non_terminal = item[dot_pos+1] for prod in grammar: if prod.startswith(non_terminal + ' →'): new_item = f"{non_terminal} → ·{prod.split('→')[1].strip()}" if new_item not in closure_set: closure_set.add(new_item) changed = True return sorted(list(closure_set))

2.3 计算GOTO函数

GOTO函数定义了从一个项目集通过某个文法符号能够到达的新项目集。

def goto(items, symbol, grammar): """计算GOTO函数""" new_items = [] for item in items: dot_pos = item.index('·') if dot_pos + 1 < len(item) and item[dot_pos+1] == symbol: moved_item = item[:dot_pos] + symbol + '·' + item[dot_pos+2:] new_items.append(moved_item) return closure(new_items, grammar) if new_items else []

2.4 构造项目集规范族

通过系统地应用闭包和GOTO函数,我们可以构建出完整的项目集规范族(即DFA的所有状态)。

def build_canonical_collection(grammar): """构造项目集规范族""" start_item = closure([f"{grammar[0].split('→')[0].strip()} → ·{grammar[0].split('→')[1].strip()}"], grammar) C = [start_item] transitions = [] changed = True while changed: changed = False for i, items in enumerate(C): symbols = {item[item.index('·')+1] for item in items if item.index('·')+1 < len(item)} for symbol in symbols: new_items = goto(items, symbol, grammar) if new_items and new_items not in C: C.append(new_items) changed = True if new_items: transitions.append((i, symbol, C.index(new_items) if new_items in C else -1)) return C, transitions

2.5 构造LR(0)分析表

基于项目集规范族,我们可以构建LR(0)分析表,包括ACTION和GOTO两个子表。

状态ACTIONGOTO
ab
0s1s2
1
.........
def build_lr0_table(C, transitions, grammar): """构造LR(0)分析表""" action_table = {} goto_table = {} terminals = set() non_terminals = set() for prod in grammar: lhs, rhs = prod.split('→') non_terminals.add(lhs.strip()) terminals.update([c for c in rhs.strip() if c.islower() or not c.isalpha()]) terminals.discard('·') terminals.add('#') # 结束符号 for state, items in enumerate(C): # 处理移进和GOTO for src, symbol, dest in transitions: if src == state: if symbol in terminals: action_table[(state, symbol)] = f's{dest}' else: goto_table[(state, symbol)] = dest # 处理归约 for item in items: if item.endswith('·'): prod_num = grammar.index(item.replace('·', '').strip()) for term in terminals: if (state, term) not in action_table: action_table[(state, term)] = f'r{prod_num}' # 处理接受状态 if f"{grammar[0].split('→')[0].strip()} → {grammar[0].split('→')[1].strip()}·" in items: action_table[(state, '#')] = 'acc' return action_table, goto_table, terminals, non_terminals

3. Python实现完整代码

以下是完整的LR(0)分析表生成器实现,包含所有辅助函数和主流程:

class LR0ParserGenerator: def __init__(self, grammar): self.grammar = self.augment_grammar(grammar) self.C, self.transitions = self.build_canonical_collection() self.action_table, self.goto_table, self.terminals, self.non_terminals = self.build_lr0_table() def augment_grammar(self, grammar): start_symbol = grammar[0].split('→')[0].strip() return [f"S' → {start_symbol}"] + grammar def closure(self, items): closure_set = set(items) changed = True while changed: changed = False for item in list(closure_set): dot_pos = item.index('·') if dot_pos + 1 < len(item) and item[dot_pos+1].isupper(): non_terminal = item[dot_pos+1] for prod in self.grammar: if prod.startswith(non_terminal + ' →'): new_item = f"{non_terminal} → ·{prod.split('→')[1].strip()}" if new_item not in closure_set: closure_set.add(new_item) changed = True return sorted(list(closure_set)) def goto(self, items, symbol): new_items = [] for item in items: dot_pos = item.index('·') if dot_pos + 1 < len(item) and item[dot_pos+1] == symbol: moved_item = item[:dot_pos] + symbol + '·' + item[dot_pos+2:] new_items.append(moved_item) return self.closure(new_items) if new_items else [] def build_canonical_collection(self): start_item = self.closure([ f"{self.grammar[0].split('→')[0].strip()} → ·{self.grammar[0].split('→')[1].strip()}" ]) C = [start_item] transitions = [] changed = True while changed: changed = False for i, items in enumerate(C): symbols = {item[item.index('·')+1] for item in items if item.index('·')+1 < len(item)} for symbol in symbols: new_items = self.goto(items, symbol) if new_items and new_items not in C: C.append(new_items) changed = True if new_items: transitions.append((i, symbol, C.index(new_items) if new_items in C else -1)) return C, transitions def build_lr0_table(self): action_table = {} goto_table = {} terminals = set() non_terminals = set() for prod in self.grammar: lhs, rhs = prod.split('→') non_terminals.add(lhs.strip()) terminals.update([c for c in rhs.strip() if c.islower() or not c.isalpha()]) terminals.discard('·') terminals.add('#') for state, items in enumerate(self.C): # 处理移进和GOTO for src, symbol, dest in self.transitions: if src == state: if symbol in terminals: action_table[(state, symbol)] = f's{dest}' else: goto_table[(state, symbol)] = dest # 处理归约 for item in items: if item.endswith('·'): prod_num = self.grammar.index(item.replace('·', '').strip()) for term in terminals: if (state, term) not in action_table: action_table[(state, term)] = f'r{prod_num}' # 处理接受状态 if f"{self.grammar[0].split('→')[0].strip()} → {self.grammar[0].split('→')[1].strip()}·" in items: action_table[(state, '#')] = 'acc' return action_table, goto_table, terminals, non_terminals def print_tables(self): print("ACTION Table:") sorted_terms = sorted(self.terminals) print("State\t" + "\t".join(sorted_terms)) for state in range(len(self.C)): row = [str(state)] for term in sorted_terms: row.append(self.action_table.get((state, term), "")) print("\t".join(row)) print("\nGOTO Table:") sorted_non_terms = sorted(self.non_terminals - {"S'"}) print("State\t" + "\t".join(sorted_non_terms)) for state in range(len(self.C)): row = [str(state)] for nt in sorted_non_terms: row.append(str(self.goto_table.get((state, nt), ""))) print("\t".join(row)) # 使用示例 if __name__ == "__main__": grammar = [ "E → E + T", "E → T", "T → T * F", "T → F", "F → ( E )", "F → id" ] parser_gen = LR0ParserGenerator(grammar) parser_gen.print_tables()

4. 实战案例分析

让我们以一个简单的算术表达式文法为例,演示完整的LR(0)分析表构造过程:

示例文法

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

步骤1:拓广文法

0: S' → E 1: E → E + T 2: E → T 3: T → T * F 4: T → F 5: F → ( E ) 6: F → id

步骤2:构造初始项目集闭包

I0: S' → ·E E → ·E + T E → ·T T → ·T * F T → ·F F → ·( E ) F → ·id

步骤3:计算GOTO函数

GOTO(I0, E) = I1: S' → E· E → E· + T GOTO(I0, T) = I2: E → T· T → T· * F GOTO(I0, F) = I3: T → F· GOTO(I0, "(") = I4: F → (· E ) E → ·E + T E → ·T T → ·T * F T → ·F F → ·( E ) F → ·id GOTO(I0, id) = I5: F → id·

步骤4:继续扩展所有项目集重复上述过程,直到不再产生新的项目集。最终我们会得到12个项目集(I0-I11)。

步骤5:构造LR(0)分析表根据项目集规范族和GOTO函数,我们可以填充ACTION和GOTO表:

ACTION Table: State id + * ( ) # 0 s5 s4 1 s6 acc 2 r2 s7 r2 r2 r2 3 r4 r4 r4 r4 r4 4 s5 s4 5 r6 r6 r6 r6 r6 6 s5 s4 7 s5 s4 8 s6 s9 9 r5 r5 r5 r5 r5 10 r3 r3 r3 r3 r3 11 r1 r1 r1 r1 r1 GOTO Table: State E T F 0 1 2 3 1 2 3 4 8 2 3 5 6 10 3 7 11 8 9 10 11

5. 算法优化与工程实践

在实际工程实现中,我们还需要考虑以下优化点:

性能优化

  1. 使用更高效的数据结构(如字典)存储项目集
  2. 对项目集进行哈希处理,加快查找速度
  3. 并行计算闭包和GOTO函数

错误处理

def parse(input_string, action_table, goto_table, grammar): stack = [0] # 初始状态 input_string = input_string.split() + ['#'] pointer = 0 while True: state = stack[-1] current_symbol = input_string[pointer] action = action_table.get((state, current_symbol), "") if not action: raise SyntaxError(f"Syntax error at position {pointer}, unexpected symbol '{current_symbol}'") if action.startswith('s'): # 移进 new_state = int(action[1:]) stack.append(current_symbol) stack.append(new_state) pointer += 1 elif action.startswith('r'): # 归约 prod_num = int(action[1:]) lhs, rhs = grammar[prod_num].split('→') lhs = lhs.strip() rhs = rhs.strip() pop_len = 2 * len(rhs.split()) stack = stack[:-pop_len] state = stack[-1] stack.append(lhs) stack.append(goto_table[(state, lhs)]) elif action == 'acc': # 接受 return True else: raise SyntaxError("Invalid action during parsing")

可视化输出

def visualize_dfa(C, transitions): """使用Graphviz可视化DFA""" from graphviz import Digraph dot = Digraph() for i, items in enumerate(C): label = f"I{i}\n" + "\n".join(items) dot.node(str(i), label=label) for src, symbol, dest in transitions: dot.edge(str(src), str(dest), label=symbol) dot.render('lr0_dfa', view=True)

测试验证

# 测试用例 test_cases = [ ("id + id * id", True), ("( id + id ) * id", True), ("id + * id", False), ("id id", False) ] for test_input, expected in test_cases: try: result = parse(test_input, action_table, goto_table, grammar) assert result == expected print(f"Test passed: '{test_input}'") except (SyntaxError, AssertionError): print(f"Test failed: '{test_input}'")

通过以上实现,我们完成了一个完整的LR(0)分析表生成器。这个实现不仅能够自动构造分析表,还能验证输入串是否符合文法规则,为后续的编译器开发奠定了坚实基础。

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

STM32数字控制DC-DC升压转换系统设计与优化

1. 项目概述与核心器件选型这个项目本质上是一个数字控制的DC-DC升压转换系统&#xff0c;核心是通过STM32微控制器动态调节TPS61170升压芯片的工作状态。我在工业电源设计中多次采用这种架构&#xff0c;它的优势在于既能保留专用电源芯片的高效特性&#xff0c;又能通过MCU实…

作者头像 李华
网站建设 2026/7/11 20:10:40

Neo4j AuraDB 与 Docker 社区版部署:3 种方案成本与性能实测

Neo4j AuraDB 与 Docker 社区版部署&#xff1a;3 种方案成本与性能实测 在当今数据驱动的商业环境中&#xff0c;图形数据库正成为处理复杂关联数据的首选工具。作为全球领先的图形数据库解决方案&#xff0c;Neo4j 提供了多种部署选项以满足不同场景需求。本文将深入对比 Neo…

作者头像 李华
网站建设 2026/7/11 20:10:00

如何高效使用openeuler/integration-test?10分钟上手核心测试用例

如何高效使用openeuler/integration-test&#xff1f;10分钟上手核心测试用例 【免费下载链接】integration-test The repo contains test suits for system integration test 项目地址: https://gitcode.com/openeuler/integration-test 前往项目官网免费下载&#xff…

作者头像 李华
网站建设 2026/7/11 20:09:53

KASandbox核心组件深度解析:从CRI shim到编排服务的完整架构

KASandbox核心组件深度解析&#xff1a;从CRI shim到编排服务的完整架构 【免费下载链接】KASandbox This repository contains the core orchestration services, CRI multiplexing shim, and infrastructure components that power the KASandbox platform. 项目地址: http…

作者头像 李华
网站建设 2026/7/11 20:09:29

现代文件上传系统架构深度解析:WebUploader技术实现与性能优化

现代文件上传系统架构深度解析&#xff1a;WebUploader技术实现与性能优化 【免费下载链接】webuploader Its a new file uploader solution! 项目地址: https://gitcode.com/gh_mirrors/we/webuploader 在现代Web应用开发中&#xff0c;文件上传功能面临着诸多技术挑战…

作者头像 李华
网站建设 2026/7/11 20:07:35

CDMA 直接序列扩频实验箱实战:m序列与Gold序列扩频增益32对比实测

CDMA直接序列扩频实验箱实战&#xff1a;m序列与Gold序列扩频增益32对比实测在移动通信系统中&#xff0c;扩频技术是实现多址接入和抗干扰的核心手段。本文将基于实验箱平台&#xff0c;深入对比分析m序列与Gold序列在直接序列扩频&#xff08;DS-CDMA&#xff09;中的性能差异…

作者头像 李华