Flex和Bison逻辑运算符语法错误排查求助
问题分析与解决方案
你遇到的语法错误和编译冲突主要来自冗余的语法规则、错误的运算符优先级/结合性设置,以及部分逻辑与需求不符的问题。下面一步步解决:
1. 消除语法规则冗余(解决Reduce/Reduce冲突)
你定义了expr和smallexprs两个几乎完全重复的非终结符,这是导致reduce/reduce冲突的核心原因。Bison无法确定某些情况下应该归约到哪个非终结符,所以需要合并这两个规则,只保留expr:
- 删除
smallexprs相关的所有规则,把原来smallexprs的内容合并到expr中。
2. 修正运算符优先级与结合性(解决Shift/Reduce冲突)
当前的优先级设置逻辑混乱,比如逻辑运算符AND/OR被标记为%nonassoc(无结合性),但它们应该是左结合,且优先级顺序要符合常规:算术运算符 > 比较运算符(>、<、==) > 逻辑非(NOT) > 逻辑与(AND) > 逻辑或(OR),同时赋值运算符=优先级最低。
修改后的优先级设置如下:
%right '=' // 最低优先级:赋值 %left OR // 逻辑或:左结合,优先级低于AND %left AND // 逻辑与:左结合 %nonassoc NOT // 逻辑非:单目运算符,无结合性 %nonassoc EQ GT LT // 比较运算符:无结合性(a==b==c无意义) %left '+' '-' // 加减法:左结合 %left '*' '/' // 乘除法:左结合 %nonassoc UMINUS // 单目负号:最高优先级 %nonassoc IF // IF优先级,用于解决悬空else %nonassoc ELSE // ELSE优先级高于IF,确保匹配最近的IF
3. 调整比较运算的返回值以匹配需求
你的需求是:a > b 返回0、a < b 返回1,但当前代码直接使用C的布尔值(真为1,假为0),和需求相反。需要修改比较规则的返回逻辑:
expr: expr GT expr { $$ = ($1 > $3) ? 0 : ($1 < $3) ? 1 : 2; } // 假设a==b返回2,可按需调整 | expr LT expr { $$ = ($1 < $3) ? 1 : ($1 > $3) ? 0 : 2; } | expr EQ expr { $$ = ($1 == $3) ? 2 : ($1 > $3) ? 0 : 1; } // 相等返回2,可自定义
4. 修正逻辑运算符的语法规则
合并规则后,逻辑运算符的规则直接基于expr,同时注意逻辑运算的返回值要和你的比较结果兼容(比如1表示真,0表示假):
expr: expr AND expr { $$ = ($1 == 1 && $3 == 1) ? 1 : 0; } | expr OR expr { $$ = ($1 == 1 || $3 == 1) ? 1 : 0; } | NOT expr { $$ = ($2 == 1) ? 0 : 1; }
5. 调整IF语句的规则
原来的IF规则是IF expr expr expr(IF 条件 真分支 假分支),合并后修改为:
expr: IF expr expr expr %prec IF { $$ = ($2 == 1) ? $3 : $4; }
添加%prec IF确保优先级正确,避免和其他规则冲突。
修改后的完整calc.y文件
%{ #include "calc.h" #include <string.h> #include <math.h> #include <stdio.h> #include <stdlib.h> int yylex(); int yyparse(); %} %union { double dval; struct symtab *symp; } %token <symp> NAME %token <dval> NUMBER %token EQ %token GT %token LT %token OR %token AND %token NOT %token IF %token ELSE // 修正后的优先级与结合性 %right '=' // 最低优先级:赋值 %left OR // 逻辑或:左结合 %left AND // 逻辑与:左结合 %nonassoc NOT // 逻辑非:单目运算符 %nonassoc EQ GT LT // 比较运算符 %left '+' '-' // 加减法 %left '*' '/' // 乘除法 %nonassoc UMINUS // 单目负号:最高优先级 %nonassoc IF // IF优先级 %nonassoc ELSE // ELSE优先级高于IF %type <dval> expr %% session: /* empty */ | session toplevel '\n' ; toplevel: expr { printf("%g\n\n>> ", $1); } | /* empty */ | '?' { printHelp(); printf("\n>> "); } | '.' { printf("Exiting 331 calc\n"); exit(1); } ; expr: NUMBER { $$ = $1; } | NAME { $$ = $1->value; } | NAME '=' expr { $1->value = $3; $$ = $3; } | IF expr expr expr %prec IF { $$ = ($2 == 1) ? $3 : $4; } | expr AND expr { $$ = ($1 == 1 && $3 == 1) ? 1 : 0; } | expr OR expr { $$ = ($1 == 1 || $3 == 1) ? 1 : 0; } | NOT expr { $$ = ($2 == 1) ? 0 : 1; } | expr EQ expr { $$ = ($1 == $3) ? 2 : ($1 > $3) ? 0 : 1; } | expr GT expr { $$ = ($1 > $3) ? 0 : ($1 < $3) ? 1 : 2; } | expr LT expr { $$ = ($1 < $3) ? 1 : ($1 > $3) ? 0 : 2; } | expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; } | '~' expr %prec UMINUS { $$ = -$2; } | '(' expr ')' { $$ = $2; } ; %% struct symtab * symlook(char *s) { char *p; struct symtab *sp; for(sp = symtab; sp < &symtab[SYMBOLTABLESIZE]; sp++) { if (sp->name && !strcmp(sp->name, s)) return sp; if (!sp->name) { sp->name = strdup(s); return sp; } } yyerror("The symbol table is full, sorry...\n"); exit(1); } void printHelp() { printf("Enter an expression in infix notation followed by a newline.\n"); printf("Operators include +, -, *, /, =, >, <, ==, and, or, not.\n"); printf("IF syntax: IF [condition] [true-expr] [false-expr]\n"); printf("Assign variables with =, type ? for help, . to exit.\n"); } void yyerror(char *msg) { printf("%s \n" , msg); } int yywrap(){return 1;} int main() { printf("331 Calculator\n(type ? for help and . to exit)\n\n>> "); yyparse(); return 0; }
修改后的calc.l文件(仅调整注释和规则,核心无变化)
%{ #include "y.tab.h" #include "calc.h" #include <math.h> %} D [0-9] A [a-zA-Z] AD [a-zA-Z0-9] %% ({D}+|({D}*\.{D}+)([eE][-+]?{D}+)?) {yylval.dval = atof(yytext); return NUMBER;} if {return IF;} or {return OR;} and {return AND;} not {return NOT;} {A}{AD}* {struct symtab *sp = symlook(yytext); yylval.symp = sp; return NAME;} "==" {return EQ;} ">" {return GT;} "<" {return LT;} [ \t] ; \n { return yytext[0]; } . { return yytext[0]; } %%
测试验证
编译后测试你的示例:
>> a = 5 5 >> b = 8 8 >> a < b && a < b 1 >> a > b 0 >> a == b 2 >> IF a<b 10 20 10
此时语法错误消失,编译时的shift/reduce和reduce/reduce冲突也会被消除。
内容的提问来源于stack exchange,提问作者EthanP
相关产品推荐
相关产品推荐

