能否在Bison动作中控制规则匹配?DIMACS格式容错解析咨询
问题解答:Bison中实现容错式解析DIMACS格式
核心结论
不能通过Bison动作来指示规则是否匹配。Bison的语法分析基于规则的匹配优先逻辑,动作是规则成功匹配后执行的代码,无法回溯或否定已经完成的匹配。必须通过语法规则本身或错误恢复机制来实现跳过不支持行的需求。
两种场景的具体实现方案
场景一:Flex返回统一的DESCRIPTOR令牌
如果Flex中把所有行描述符都返回为DESCRIPTOR令牌(比如^[a-z] { return DESCRIPTOR; }),需要在Bison规则中先判断描述符的具体值,再决定是否处理:
- Flex补充规则:添加匹配行剩余内容的规则,方便跳过整行:
^[a-z] { yylval.c = yytext[0]; return DESCRIPTOR; } // 将字符存入yylval [^\n]*\n { return REST_OF_LINE; } [0-9]+ { yylval.i = atoi(yytext); return INTEGER; } [ \t]+ { /* 跳过空白字符 */ } ^c[^\n]*\n { /* 直接跳过注释行,不提交给Bison */ }
- Bison规则编写:区分支持的行和需要跳过的行:
%union { int i; char c; } %token <c> DESCRIPTOR %token <i> INTEGER %token REST_OF_LINE %% // 主解析规则:edge格式的文件内容 edge_file: 'p' "edge" '\n' edge_content ; edge_content: /* 空规则 */ | edge_content edge_supported_line | edge_content edge_unsupported_line ; // 处理支持的e行 edge_supported_line: DESCRIPTOR INTEGER INTEGER '\n' { if ($1 == 'e') { do_something($2, $3); // 执行e行的业务逻辑 } } ; // 跳过不支持的行 edge_unsupported_line: DESCRIPTOR REST_OF_LINE { fprintf(stderr, "Warning: 跳过不支持的行,起始字符为 '%c'\n", $1); } ; %%
场景二:Flex返回单个字符作为令牌
如果Flex直接返回描述符字符作为令牌(^[a-z] { return yytext[0]; }),可以更清晰地编写规则,同时用两种方式处理不支持的行:
方式1:显式定义不支持的描述符
^[a-z] { return yytext[0]; } [0-9]+ { yylval.i = atoi(yytext); return INTEGER; } [ \t]+ { /* 跳过空白字符 */ } ^c[^\n]*\n { /* 跳过注释行 */ } [^\n]*\n { return EOL; } // 匹配行尾
%union { int i; } %token <i> INTEGER %token EOL %% edge_file: 'p' "edge" EOL edge_content ; edge_content: /* 空规则 */ | edge_content edge_supported_line | edge_content edge_unsupported_line ; // 处理支持的e行 edge_supported_line: 'e' INTEGER INTEGER EOL { do_something($2, $3); } ; // 匹配所有不支持的描述符 edge_unsupported_line: UNSUPPORTED_DESCRIPTOR EOL { fprintf(stderr, "Warning: 跳过不支持的行,起始字符为 '%c'\n", $1); } | UNSUPPORTED_DESCRIPTOR .* EOL { // 处理带内容的不支持行 fprintf(stderr, "Warning: 跳过不支持的行,起始字符为 '%c'\n", $1); } ; // 定义所有非'e'的小写字母为不支持描述符 UNSUPPORTED_DESCRIPTOR: 'a'|'b'|'c'|'d'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z' ; %%
方式2:利用Bison错误恢复机制
这种方式更简洁,无需枚举所有不支持的字符:
edge_file: 'p' "edge" EOL edge_content ; edge_content: /* 空规则 */ | edge_content edge_supported_line | edge_content error EOL { yyerror("跳过无效行"); yyerrok; // 恢复解析状态,继续处理后续内容 } ; edge_supported_line: 'e' INTEGER INTEGER EOL { do_something($2, $3); } ;
当Bison遇到无法匹配的行(比如a开头的行),会触发error规则,跳过直到EOL,再通过yyerrok恢复解析流程。
最佳实践建议
- 优先选择场景二:Flex返回单个字符令牌,Bison规则更直观,维护成本更低。
- 注释行在Flex中直接跳过:不需要交给Bison处理,减少解析逻辑复杂度。
- 用
error规则做错误恢复:避免枚举所有不支持的字符,代码更简洁。
内容的提问来源于stack exchange,提问作者Szabolcs
相关产品推荐
相关产品推荐

