You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:38:26