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

Lex/Yacc计算器无法处理末尾多余右括号的异常场景

解决Lex+Yacc计算器右括号冗余输入不报错问题

问题描述

我开发了一款支持基本算术运算及括号使用的Lex+Yacc计算器,多数场景可正常处理:有效输入返回整数结果,无效输入提示错误。但测试发现,输入如5)、99+22+33)或7)asdfghjk这类以多余右括号结尾的内容时,会被判定为有效并输出正确数值;而输入(5则会正常报错。

原Lex文件

/* ========== DECLARATIONS SECTION ========== */
%{
    #include <stdio.h>
    #include <stdlib.h>
    #include "y.tab.h"
    extern int yylval;
%}
/* ========================================== */

/* ========== SHORTHAND DEFINITIONS ========= */
/* ========================================== */

/* ================== RULES ================= */
%%
([1-9][0-9]*|0) { 
    yylval = atoi(yytext);
    return INT;
}

[+] {return PLUS;}
[-] {return MINUS;}
[*] {return MULT;}
[/] {return DIV;}
[(] {return LPAREN;}
[)] {return RPAREN;}
[ \t] {;}
[\n] {return 0;}
. {yyerror();}
%%
/* ========================================== */

/* ================ USER CODE =============== */
int yywrap() {
    return 1;
}
/* ========================================== */

原Yacc文件

/* ========== DECLARATIONS SECTION ========== */
%{
    #include <stdio.h>
    #include <stdlib.h>
%}

%token INT

%left PLUS MINUS
%left MULT DIV
%left UMINUS
%left LPAREN RPAREN
/* ========================================== */

/* ============ GRAMMAR SECTION ============= */
%%
ResultantExpression: E{
    printf("Result = %d\n", $$);

    return 0;
};


E: E PLUS T {$$ = $1 + $3;}
 | E MINUS T {$$ = $1 - $3;}
 | MINUS E %prec UMINUS {$$ = -$2;}
 | T {$$ = $1;}
 ;

T: T MULT F {$$ = $1 * $3;}
 | T DIV F {if ($3 == 0) {yyerror();} else {$$ = $1 / $3;}}
 | F {$$ = $1;}
 ;

F: INT {$$ = $1;}
 | LPAREN E RPAREN {$$ = ($2);}
 ; 
%%
/* ========================================== */

/* ============ USER CODE SECTION =========== */
int main() {
    printf("Enter the expression:\n");
    yyparse();

    return 0;
}

void yyerror() {
    printf("The entered expression is invalid!\n");
    exit(1);
}

/* ========================================== */

问题原因

当前Yacc的ResultantExpression规则在匹配到合法表达式E后直接返回0终止解析,不会检查后续是否存在多余的token(比如冗余的右括号)。而输入(5时,解析到LPAREN E后无法匹配RPAREN,触发语法错误,因此能正常报错。

解决方案

1. 修改Yacc语法规则

要求整个输入必须是完整表达式,且后续无多余token,需添加规则确保解析到文件结束(EOF):

/* ============ GRAMMAR SECTION ============= */
%%
ResultantExpression: E EOF {
    printf("Result = %d\n", $1);
    return 0;
};

// 其余规则保持不变
E: E PLUS T {$$ = $1 + $3;}
 | E MINUS T {$$ = $1 - $3;}
 | MINUS E %prec UMINUS {$$ = -$2;}
 | T {$$ = $1;}
 ;

T: T MULT F {$$ = $1 * $3;}
 | T DIV F {if ($3 == 0) {yyerror();} else {$$ = $1 / $3;}}
 | F {$$ = $1;}
 ;

F: INT {$$ = $1;}
 | LPAREN E RPAREN {$$ = ($2);}
 ; 
%%

同时在Yacc的声明部分添加%token EOF:

%token INT EOF

2. 调整Lex的换行处理

原Lex中[\n] {return 0;}会导致换行时直接返回0(相当于EOF),但此时后续的多余字符不会被扫描。修改为:

[\n] {return EOF;}

保留原本的.规则,确保多余字符触发错误:

. {yyerror();}

修改后的完整文件

修改后的Lex文件

/* ========== DECLARATIONS SECTION ========== */
%{
    #include <stdio.h>
    #include <stdlib.h>
    #include "y.tab.h"
    extern int yylval;
%}
/* ========================================== */

/* ========== SHORTHAND DEFINITIONS ========= */
/* ========================================== */

/* ================== RULES ================= */
%%
([1-9][0-9]*|0) { 
    yylval = atoi(yytext);
    return INT;
}

[+] {return PLUS;}
[-] {return MINUS;}
[*] {return MULT;}
[/] {return DIV;}
[(] {return LPAREN;}
[)] {return RPAREN;}
[ \t] {;}
[\n] {return EOF;}
. {yyerror();}
%%
/* ========================================== */

/* ================ USER CODE =============== */
int yywrap() {
    return 1;
}
/* ========================================== */

修改后的Yacc文件

/* ========== DECLARATIONS SECTION ========== */
%{
    #include <stdio.h>
    #include <stdlib.h>
%}

%token INT EOF

%left PLUS MINUS
%left MULT DIV
%left UMINUS
%left LPAREN RPAREN
/* ========================================== */

/* ============ GRAMMAR SECTION ============= */
%%
ResultantExpression: E EOF {
    printf("Result = %d\n", $1);
    return 0;
};

E: E PLUS T {$$ = $1 + $3;}
 | E MINUS T {$$ = $1 - $3;}
 | MINUS E %prec UMINUS {$$ = -$2;}
 | T {$$ = $1;}
 ;

T: T MULT F {$$ = $1 * $3;}
 | T DIV F {if ($3 == 0) {yyerror();} else {$$ = $1 / $3;}}
 | F {$$ = $1;}
 ;

F: INT {$$ = $1;}
 | LPAREN E RPAREN {$$ = ($2);}
 ; 
%%
/* ========================================== */

/* ============ USER CODE SECTION =========== */
int main() {
    printf("Enter the expression:\n");
    yyparse();

    return 0;
}

void yyerror() {
    printf("The entered expression is invalid!\n");
    exit(1);
}

/* ========================================== */

验证效果

修改后,输入5)、99+22+33)或7)asdfghjk时,多余的右括号或字符会触发yyerror(),提示表达式无效;输入合法表达式则正常返回结果,输入(5仍会正常报错。

内容的提问来源于stack exchange,提问作者Stephen Landaas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:45:41