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

FLEX/YACC多行输入解析异常:换行符识别及文件EOF处理问题

基于FLEX/YACC的表达式解析器多行文件解析问题排查

问题描述

开发了一款基于FLEX/YACC的简单表达式解析器,可解析表达式并计算数值结果。单行输入运行正常,但处理多行文件时出现以下问题:

  • 初始版本无法匹配STMTS与'\n',提示“无效字符”;
  • 修改语法规则后,手动输入可正常运行,但读取文件时遇到EOF仍触发无效字符错误。

相关代码

Flex代码

//lex
%{
#include "kfloat.tab.h"
%}

%option nounput
%option noinput

%%
[ \t]   ;

[0-9]+  { yylval.d=atof(yytext); return NUMBER; }
"sqrt"  {return iSQRT; }
"log"   {return iLOG; }
"+"     { return OPLUS; } 
"-"     { return OMINUS; }
"*"     { return OMULT; } 
"/"     { return ODIV; } 
"("     { return LPAR; }
")"     { return RPAR; }
"\n"    { yylineno++; return yytext[0]; }
";"     { return EOL;}
.   return yytext[0];
%%

YACC代码

//yacc
%{
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define YYDEBUG 1
void yyerror(char *);
int yylex(void);
extern int yylineno;

%}
%union { double d;}
%token <d> NUMBER
%token EOL
%type <d> EXP STMT 
%left OPLUS OMINUS
%left OMULT ODIV
%left iLOG iSQRT 
%left LPAR RPAR

%%
STMTS : STMTS STMT EOL {printf("END of deriv :2 and MORE statements- now EOF and next line\n");} //edit
      | STMT EOL {printf("END of deriv :ONE statement - now EOF and next line\n");} //edit
      | STMTS '\n'  //edit
     ;
STMT : EXP
     |   
     ;
EXP  : EXP OPLUS EXP   {$$=$1+$3; printf("ENDdddd1111:  %f\n",$1);}
     | EXP OMINUS EXP  {$$=$1-$3;}
     | EXP OMULT EXP   {$$=$1*$3;}
     | EXP ODIV EXP    {if ($3==0) {yyerror("Zatim nelze delit nulou"); exit(0);} else {$$=$1/$3;}}
     | LPAR EXP RPAR   { ;}
     | iSQRT EXP       {$$=sqrt($2);}
     | iLOG EXP        {$$=log10($2);}
     | OPLUS EXP       {$$= $2;}
     | OMINUS EXP      {$$= -$2;}
     | NUMBER          {$$= $1;}
     ;

%%

/* lineno do erroru printf*/
int main(void)
{
#if YYDEBUG
  yydebug = 1;
#endif
  if(!yyparse ())
    printf("OK\n");
  return 0;
}
  
void yyerror (char *s)
{
   printf("Syntakticka chyba: %s na radku: %i\n",s,yylineno);
}

运行日志

修改前日志

Stack now 0 8 17
Reducing stack by rule 2 (line 28):
   $1 = nterm STMT ()
   $2 = token EOL ()
**-> $$ = nterm STMTS ()
Entering state 7
Stack now 0 7
Reading a token
Next token is token "invalid token" ()**
Error: popping nterm STMTS ()
Stack now 0
Cleanup: discarding lookahead token "invalid token" ()
Stack now 0
ENDdddd1111:  5.000000
ENDddd333:  10.000000
Syntakticka chyba: syntax error na radku: 1

修改后日志

// 读取文件时的日志
END of deriv :ONE statement - now EOF and next line
-> $$ = nterm STMTS ()
Entering state 7
Stack now 0 7
Reading a token
Next token is token "invalid token" ()   // 期望是'\n'
Syntaktick▒ chyba
Error: popping nterm STMTS ()
Stack now 0
Cleanup: discarding lookahead token "invalid token" ()
Stack now 0

// 手动输入相同内容时的日志
END of deriv :ONE statement - now EOF and next line
-> $$ = nterm STMTS ()
Entering state 7
Stack now 0 7
Reading a token
Next token is token '\n' ()
Shifting token '\n' ()
Entering state 16
Stack now 0 7 16
Reducing stack by rule 3 (line 26):
   $1 = nterm STMTS ()
   $2 = token '\n' ()
-> $$ = nterm STMTS ()
Entering state 7
Stack now 0 7
Reading a token

// 输入文件内容
(+12-9);
sqrt(16);

问题根源分析

  1. EOF处理缺失:读取文件到末尾时,Flex返回0(EOF标记)而非'\n',但当前STMTS规则未定义EOF作为合法终止条件,导致解析器将EOF识别为无效字符。
  2. 语法规则不完整:修改后的规则允许STMTS匹配换行,但未支持输入直接以EOF结束,而手动输入时用户会主动输入换行,这造成了两种输入场景的行为差异。
  3. EXP规则的逻辑漏洞:LPAR EXP RPAR规则未设置$$ = $2,导致括号包裹的表达式无法正确传递计算结果。

修复方案

1. 更新YACC语法规则,支持EOF终止

修改STMTS规则,添加空规则以接受EOF作为输入结束标记:

STMTS : STMTS STMT EOL {printf("END of deriv :2 and MORE statements- now EOF and next line\n");}
      | STMT EOL {printf("END of deriv :ONE statement - now EOF and next line\n");}
      | STMTS '\n'
      | /* 空规则,允许输入以EOF结束 */
     ;

2. 修复EXP规则的括号处理

修正LPAR EXP RPAR规则的语义动作,确保结果正确传递:

| LPAR EXP RPAR   { $$ = $2; }

3. 完善STMT规则(可选)

为STMT的EXP分支添加结果输出,空分支保留以支持空行:

STMT : EXP { printf("计算结果: %f\n", $1); }
     | /* 空行,无处理 */
     ;

验证效果

修改后,无论是手动输入多行内容,还是读取不带末尾换行的文件,解析器都能正常处理所有表达式,遇到EOF时会正常终止,不再触发“无效字符”错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:29:55