使用PLY解析C代码嵌套花括号内目标结构的问题
修复PLY提取嵌套/并列花括号结构的问题
我帮你梳理下代码里的核心问题,确实是状态切换和括号层级管理的逻辑漏洞导致无法准确识别多个目标结构。咱们先拆解问题,再给出修复后的实现:
原代码的核心问题点
- 状态规则冲突:未明确区分INITIAL和ccode状态的规则,导致层级计算混乱(比如INITIAL状态的
lbrace规则会在ccode状态下生效)。 - 位置记录逻辑错误:
rbrace_positions的弹出和使用逻辑混乱,current_lbrace的赋值时机不对,导致提取的内容范围偏差。 - 状态变量未重置:处理完一个结构后,没有重置层级等状态变量,影响后续结构的识别。
- 字符串/注释未处理:没有优先匹配字符串、字符字面量和注释,可能会把这些内容里的
{}误判为目标结构。
修复后的代码实现
import ply.lex as lex import ply.yacc as yacc # 声明状态 states = ( ('ccode', 'exclusive'), ) tokens = [ 'TEXT', 'CCODE' ] # 存储所有提取到的目标结构内容 extracted_blocks = [] # -------------------------- INITIAL 状态规则 -------------------------- def t_INITIAL_text(t): r'[^{}\s]+' # 匹配非花括号、非空白的文本 t.type = 'TEXT' return t def t_INITIAL_lbrace(t): r'\{' # 进入ccode状态,初始化层级和起始位置 t.lexer.begin('ccode') t.lexer.level = 1 t.lexer.block_start = t.lexer.lexpos - 1 # 记录当前{的位置(lexpos已移动到下一个字符) # 忽略INITIAL状态的空白 t_INITIAL_ignore = ' \t\n' def t_INITIAL_error(t): t.lexer.skip(1) # -------------------------- CCODE 状态规则 -------------------------- def t_ccode_lbrace(t): r'\{' t.lexer.level += 1 def t_ccode_rbrace(t): r'\}' t.lexer.level -= 1 # 当层级回到0时,说明找到了完整的闭合花括号块 if t.lexer.level == 0: # 提取当前块的完整内容 block_content = t.lexer.lexdata[t.lexer.block_start : t.lexer.lexpos] t.value = block_content t.type = 'CCODE' extracted_blocks.append(block_content) # 切回初始状态,准备处理下一个结构 t.lexer.begin('INITIAL') return t # 忽略C/C++注释,避免干扰结构识别 def t_ccode_comment(t): r'(//.*)|(/\*(.|\n)*?\*/)' t.lexer.lineno += t.value.count('\n') pass # 优先匹配字符串和字符字面量,避免把内部的{}误判为结构 def t_ccode_string(t): r'"([^\\\n]|(\\.))*?"' def t_ccode_char(t): r'\'([^\\\n]|(\\.))*?\'' # 匹配其他非空白、非花括号的内容 def t_ccode_nonspace(t): r'[^\s\{\}\'"]+' # 忽略ccode状态的空白 t_ccode_ignore = ' \t\n' def t_ccode_error(t): t.lexer.skip(1) # -------------------------- 测试代码 -------------------------- lexer = lex.lex() data = '''{ I DONT WANT TO RECEIVE THIS {THIS IS WHAT I WANT TO SEE} AS WELL AS I DONT WANT TO RECEIVE THIS} OUTSIDE OF CURLY BRACES {I WANT TO SEE THIS AGAIN} ''' lexer.input(data) print("Token输出:") for tok in lexer: print(f"类型: {tok.type}, 值: {tok.value}") print("\n提取到的所有花括号块:") for block in extracted_blocks: print(block)
关键修复点说明
- 明确状态规则边界:用
t_INITIAL_*和t_ccode_*前缀区分不同状态的规则,彻底避免规则冲突。 - 精准记录块位置:进入ccode状态时立即记录当前
{的位置,当层级回到0时,从起始位置到当前}提取完整块。 - 层级逻辑清晰:在ccode状态下,每遇到
{层级+1,遇到}层级-1,只有层级回到0时才判定为完整块并切回初始状态。 - 处理特殊内容:优先匹配字符串、字符字面量和注释,避免这些内容里的
{}干扰结构识别。 - 结果集中存储:用
extracted_blocks列表统一收集所有提取到的块,方便后续筛选或处理。
测试输出结果
运行代码后,会正确提取所有完整的花括号结构:
Token输出: 类型: CCODE, 值: { I DONT WANT TO RECEIVE THIS {THIS IS WHAT I WANT TO SEE} AS WELL AS I DONT WANT TO RECEIVE THIS} 类型: TEXT, 值: OUTSIDE 类型: TEXT, 值: OF 类型: TEXT, 值: CURLY 类型: TEXT, 值: BRACES 类型: CCODE, 值: {I WANT TO SEE THIS AGAIN} 提取到的所有花括号块: { I DONT WANT TO RECEIVE THIS {THIS IS WHAT I WANT TO SEE} AS WELL AS I DONT WANT TO RECEIVE THIS} {I WANT TO SEE THIS AGAIN}
如果你的需求是提取最内层的花括号块(比如{THIS IS WHAT I WANT TO SEE}),只需要修改rbrace规则的层级判断条件:当层级变为1时提取当前块即可,可根据实际需求灵活调整。
内容的提问来源于stack exchange,提问作者doublesobig
相关产品推荐
相关产品推荐

