如何用YACC正确实现if语句?代码正确性核查求助
你的多分支if语句YACC实现的隐藏问题与修复方案
嘿,我仔细过了你的Lex和YACC代码,虽然表面上能运行,但确实藏着几个会导致逻辑错误的问题——尤其是多分支elseif的执行逻辑和变量处理部分。下面给你拆解问题并附上修复后的完整代码:
发现的核心问题
- 令牌定义错误:YACC代码中
$token _VAR是语法错误(应为%token),且Lex里根本没有对应_VAR的匹配规则。你实际需要的是匹配变量x/y,完全不需要这个冗余令牌。 - 多分支逻辑混乱:当前
else_condition的实现会导致所有满足条件的分支都会赋值z,而非仅第一个匹配的分支;而且else分支会无条件覆盖z的值,哪怕前面的if/elseif已经匹配成功,完全违背多分支if的“互斥执行”逻辑。 - Condition语义值错误:原规则中引用
_VAR的$3无法获取变量值(因为没有给_VAR绑定语义类型),必须直接读取全局变量x/y的实际值来做判断。 - 分支执行顺序错误:原if_condition的动作只处理了if分支的赋值,但else_condition的动作是直接修改z,导致if满足时else的赋值也会执行,彻底搞反了逻辑。
修复后的代码
Lex文件(lex.l)
%{ #include "y.tab.h" void yyerror (char *s); int yylex(); %} %% "print" {return PRINT;} "exit" {return EXIT_COMMAND;} "if" {return IF;} "elseif" {return ELSEIF;} "elif" {return ELSEIF;} "else" {return ELSE;} "(" {return LP;} ")" {return RP;} "{" {return GLP;} "}" {return GRP;} "==" {return EQEQ;} "=" {return EQ;} "!=" {return NEQ;} ";" {return SEMI;} "<" {return LT;} ">" {return GT;} "x" {return _X;} "y" {return _Y;} "z" {return _Z;} [-]?[0-9]+ {yylval.val = atoi(yytext); return NUMBER;} [ \t\n]+ ; /* 忽略空白字符 */ . {yyerror("Carattere non riconosciuto");} %%
YACC文件(yacc.y)
%{ void yyerror (char *s); int yylex(); #include <stdio.h> #include <stdlib.h> #include <string.h> // 全局变量,未初始化默认0 int x = 0, y = 0, z = 0; // 标记是否已经找到匹配的分支,确保互斥执行 int branch_matched = 0; %} %union { int val; } %token PRINT %token EXIT_COMMAND %token IF %token ELSEIF %token ELSE %token _X %token _Y %token _Z %token<val> NUMBER %token LP RP GLP GRP %token EQEQ EQ NEQ SEMI LT GT %type<val> condition %type<val> then %right EQ %start main_program %% main_program: | main_program rule ; rule: '\n' | init_variables | if_condition | printing | EXIT_COMMAND {printf("Uscita dal programma in corso...\n"); exit(0);} | error '\n' {yyerrok; printf("Riga ignorata a causa di errore\n");} /* 跳过错误行 */ ; printing: PRINT _X {printf("\nx=%d\n", x);} | PRINT _Y {printf("\ny=%d\n", y);} | PRINT _Z {printf("\nz=%d\n", z);} ; init_variables: _X EQ NUMBER SEMI {x = $3;} | _Y EQ NUMBER SEMI {y = $3;} ; // 重置分支标记,处理if分支,再递归处理else/elseif分支 if_condition: IF LP condition RP GLP then GRP { branch_matched = ($3 == 1); if (branch_matched) z = $6; } else_condition { printf("\nz=%d\n", z); // 重置标记,避免影响下一个if语句 branch_matched = 0; } ; // 多分支核心逻辑:仅当前面无匹配分支时,才检查当前条件 else_condition: /* 空:无else/elseif分支 */ | ELSEIF LP condition RP GLP then GRP { if (!branch_matched && $3 == 1) { z = $6; branch_matched = 1; } } else_condition | ELSE GLP then GRP { if (!branch_matched) { z = $3; } } ; condition: _X LT NUMBER {$$ = (x < $3) ? 1 : 0;} | _X GT NUMBER {$$ = (x > $3) ? 1 : 0;} | _X EQEQ NUMBER {$$ = (x == $3) ? 1 : 0;} | _X NEQ NUMBER {$$ = (x != $3) ? 1 : 0;} | _X LT _Y {$$ = (x < y) ? 1 : 0;} | _X GT _Y {$$ = (x > y) ? 1 : 0;} | _X EQEQ _Y {$$ = (x == y) ? 1 : 0;} | _X NEQ _Y {$$ = (x != y) ? 1 : 0;} | _Y LT NUMBER {$$ = (y < $3) ? 1 : 0;} | _Y GT NUMBER {$$ = (y > $3) ? 1 : 0;} | _Y EQEQ NUMBER {$$ = (y == $3) ? 1 : 0;} | _Y NEQ NUMBER {$$ = (y != $3) ? 1 : 0;} | _Y LT _X {$$ = (y < x) ? 1 : 0;} | _Y GT _X {$$ = (y > x) ? 1 : 0;} | _Y EQEQ _X {$$ = (y == x) ? 1 : 0;} | _Y NEQ _X {$$ = (y != x) ? 1 : 0;} ; then: _Z EQ NUMBER SEMI {$$ = $3;} ; %% void yyerror(char *s){ printf("ERRORE: %s\n", s); } int yywrap(){ return 1; } int main (void){ printf("Inserisci comandi (init x/y, if/elseif/else, print x/y/z, exit):\n"); return yyparse(); }
关键修复说明
- 新增分支匹配标记:用全局变量
branch_matched跟踪是否已有分支匹配,确保只有第一个满足条件的分支执行赋值,且else分支仅在无任何匹配时生效。 - 重构Condition规则:移除无效的
_VAR令牌,直接处理x/y之间的比较,正确读取全局变量值进行条件判断。 - 修正多分支逻辑:递归处理elseif分支,严格遵循“前面无匹配才检查当前条件”的规则;else分支仅在所有前面分支都不满足时执行。
- 优化错误处理:修正YACC语法错误,Lex中添加未知字符错误提示,优化错误行跳过逻辑,提升用户体验。
内容的提问来源于stack exchange,提问作者Fiorelo Odobashi
相关产品推荐
相关产品推荐

