YACC消除右递归:小型DSL解析器if语句语法解析求助
嘿,看你在给小型DSL重写解析器时卡在if语句的解析上了,结合你给出的语法规则,我来分享几个实用的思路,帮你搞定这个难题!
首先,先把你给出的语法规则再明确下,避免解析时出现歧义:
- 合法场景:
- 一行可写多个命令,用
,分隔 if语句块内支持和顶层完全一致的命令格式(包括多命令逗号分隔)if语句结束后可直接跟普通命令,或者另一个if语句
- 一行可写多个命令,用
- 非法场景:
if expr { ... }之后直接跟无意义的内容(比如你例子里的...),属于格式错误
核心解析思路:递归下降+命令单元划分
对于这种有嵌套结构的DSL,递归下降解析是最直观也最容易实现的方案,核心是把代码拆成一个个独立的“命令单元”,然后分别处理:
1. 先明确「命令单元」的定义
不管是顶层还是if块内,所有可执行的内容都是由命令单元组成的:
- 普通命令(比如你定义的
command) - 完整的
if语句(从if开始到匹配的}结束的整个结构)
逗号和换行只是命令单元之间的分隔符,遇到它们时跳过即可,但要注意:if语句是一个不可拆分的完整单元,不能被逗号或换行打断。
2. 递归处理if块内部内容
因为if块内的语法和顶层完全一致,所以咱们可以复用同一个解析函数来处理{}内部的命令列表。比如写一个parse_command_list(end_token)函数,当解析if块时,就把end_token设为},这样函数会自动解析到对应的}就停止,完美处理嵌套。
3. 处理语句的连续衔接
当解析完一个if语句的}之后,要检查下一个token:
- 如果是
if:直接调用parse_if_statement()解析下一个if语句 - 如果是普通命令:解析成普通命令单元
- 如果是
,:跳过逗号,继续解析下一个命令单元 - 如果是其他无效token:抛出格式错误(比如你例子里的非法情况)
常见陷阱与解决方法
陷阱1:误把if语句拆成多个部分
比如遇到if后,还没解析完expr、{、内部块、}就因为逗号或换行停止了。
解决方法:写一个专门的parse_if_statement()函数,进入这个函数后,必须完整解析整个if结构才能退出,中途不能被分隔符打断。
陷阱2:逗号的歧义
比如if块内的逗号是分隔块内命令,而if语句后的逗号是分隔顶层命令单元。
解决方法:在解析命令单元时,先判断当前token是不是if——如果是,优先解析完整的if单元;否则解析普通命令,直到遇到分隔符或块结束符。
陷阱3:未匹配的{/}
这是嵌套结构解析的常见问题,比如用户输入了{但没写对应的}。
解决方法:在递归解析if块时,确保parse_command_list("}")必须找到对应的},如果到输入结束都没找到,就抛出“未匹配的{”错误。
伪代码示例(递归下降解析)
下面是核心逻辑的伪代码,你可以根据自己用的语言(比如Python、Java、Go)来实现:
# 解析整个程序 function parse_program(): command_list = [] while 还有未解析的token: current_token = 查看下一个token if current_token == "if": # 解析完整的if语句 command_list.append(parse_if_statement()) elif current_token == "," or current_token == "\n": # 跳过分隔符 消耗当前token else: # 解析普通命令 command_list.append(parse_command()) return command_list # 解析命令列表(支持指定结束符,比如if块的}) function parse_command_list(end_token = None): commands = [] while 还有未解析的token: current_token = 查看下一个token if end_token is not None and current_token == end_token: # 遇到结束符,消耗并退出 消耗当前token break elif current_token == "if": commands.append(parse_if_statement()) elif current_token == ",": 消耗当前token continue else: commands.append(parse_command()) return commands # 解析单个if语句 function parse_if_statement(): # 消耗"if" token 消耗当前token("if") # 解析表达式expr(这里需要你实现expr的解析逻辑) expr = parse_expression() # 消耗"{" 消耗当前token("{") # 解析if块内的命令列表,直到"}" inner_commands = parse_command_list("}") # 返回if语句对象(根据你的DSL定义来封装) return IfStatement(expr, inner_commands)
这个思路应该能覆盖你给出的所有语法场景,包括if块内的多命令、if语句后直接跟命令或另一个if的情况,同时也能检测到非法的格式错误。
内容的提问来源于stack exchange,提问作者vbstb

