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

如何阻止Flex词法分析器拆分非法Token?

Flex扫描C语言子集时阻止非法序列拆分的解决方案

问题场景

当前使用Flex实现C语言子集的词法分析,对应的.l文件如下:

/*definitions*/
%{
    #include <stdio.h>
    int yylex(void);
%}
/*rules*/
%%
"int"                   {printf("TYPE_INT %s\n",yytext);}
"float"                 {printf("TYPE_FLOAT %s\n",yytext);}
"char"                  {printf("TYPE_CHAR %s\n",yytext);}
"bool"                  {printf("TYPE_BOOL %s\n",yytext);}
"while"                 {printf("WHILE %s\n",yytext);}
"do"                    {printf("DO %s\n",yytext);}
"for"                   {printf("FOR %s\n",yytext);}
"printf"                {printf("PRINTF %s\n",yytext);}
"scanf"                 {printf("SCANF %s\n",yytext);}
"if"                    {printf("IF %s\n",yytext);}
"elif"                  {printf("ELIF %s\n",yytext);}
"else if"               {printf("ELSE_IF %s\n",yytext);}
"else"                  {printf("ELSE %s\n",yytext);}
"true"                  {printf("TRUE %s\n",yytext);}
"false"                 {printf("FALSE %s\n",yytext);}
"void"                  {printf("VOID %s\n",yytext);}
"main"                  {printf("MAIN %s\n",yytext);}
"return"                {printf("RETURN %s\n",yytext);}
[a-zA-Z_][a-zA-Z0-9]*   {printf("IDENTIFIER %s\n",yytext);}
[-+]?(([1-9][0-9]*)|0)  {printf("INTEGER %s\n",yytext);}
[-+]?[0-9]+\.[0-9]+      {printf("FLOAT %s\n",yytext);}
\"[^\\"\n]*\"            {printf("STRING %s\n",yytext);}
","                     {printf("COMMA %s\n",yytext);}
";"                     {printf("SEMICOLON %s\n",yytext);}
"{"                     {printf("LEFT_BRACE %s\n",yytext);}
"}"                     {printf("RIGHT_BRACE %s\n",yytext);}
"("                     {printf("LEFT_PAREN %s\n",yytext);}
")"                     {printf("RIGHT_PAREN %s\n",yytext);}
"["                     {printf("LEFT_BRACKET %s\n",yytext);}
"]"                     {printf("RIGHT_BRACKET %s\n",yytext);}
"-"                     {printf("MINUS %s\n",yytext);}
"+"                     {printf("PLUS %s\n",yytext);}
"*"                     {printf("MULTIPLY %s\n",yytext);}
"/"                     {printf("DIVIDE %s\n",yytext);}
"\\"                    {printf("BACKSLASH %s\n",yytext);}
"%"                     {printf("MODULUS %s\n",yytext);}
"=="                    {printf("EQUALS %s\n",yytext);}
"!="                    {printf("NOT_EQUALS %s\n",yytext);}
"<"                     {printf("LESS_THAN %s\n",yytext);}
">"                     {printf("GREATER_THAN %s\n",yytext);}
"<="                    {printf("LESS_THAN_OR_EQUAL %s\n",yytext);}
">="                    {printf("GREATER_THAN_OR_EQUAL %s\n",yytext);}
"="                     {printf("ASSIGN %s\n",yytext);}
"&&"                    {printf("LOGICAL_AND %s\n",yytext);}
"||"                    {printf("LOGICAL_OR %s\n",yytext);}
"!"                     {printf("LOGICAL_NOT %s\n",yytext);}
[" "|\t|\n|\f|\v]       {printf("WHITESPACE\n");}
.                       {printf("UNRECOGNIZED_CHARACTER %s\n",yytext);}
%%

/*for when we use multiple input files*/
int yywrap(void){
    return 1;
}

/*main driver function that takes */
int main(int argc, char *argv[]){
    if(argc<2){
        printf("Usage: %s <input_file_name>\n",argv[0]);
        return 1;
    }
    FILE *fp = fopen(argv[1], "r");
    if(fp == NULL){
        printf("Error opening input file.\n");
        return 1;
    }
    yyin = fp;
    yylex();
    fclose(fp);
    return 0;
}

遇到的问题:输入90.s3和232a3这类非法序列时,Flex不会按最后一条规则识别为UNRECOGNIZED_CHARACTER,而是拆分成多个合法token:

  • 232a3被拆分为:
    INTEGER 232
    IDENTIFIER a3
    
  • 90.s3被拆分为:
    INTEGER 90
    UNRECOGNIZED_CHARACTER .
    IDENTIFIER s3
    

解决方案

Flex的匹配逻辑是优先选择最长的匹配文本;若多个规则匹配相同长度的文本,则选择最先定义的规则。要阻止非法序列被拆分,需要在现有合法规则之前,添加匹配这类非法序列的规则,让Flex优先识别它们为未识别字符。

修改后的.l文件规则部分如下(关键修改已标注):

/*rules*/
%%
// 新增:匹配数字后跟字母/下划线的非法序列
[-+]?[0-9]+[a-zA-Z_][a-zA-Z0-9]*   {printf("UNRECOGNIZED_CHARACTER %s\n", yytext);}
// 新增:匹配数字后跟点再跟字母/下划线的非法序列
[-+]?[0-9]+\.[a-zA-Z_][a-zA-Z0-9]* {printf("UNRECOGNIZED_CHARACTER %s\n", yytext);}

"int"                   {printf("TYPE_INT %s\n",yytext);}
"float"                 {printf("TYPE_FLOAT %s\n",yytext);}
"char"                  {printf("TYPE_CHAR %s\n",yytext);}
"bool"                  {printf("TYPE_BOOL %s\n",yytext);}
"while"                 {printf("WHILE %s\n",yytext);}
"do"                    {printf("DO %s\n",yytext);}
"for"                   {printf("FOR %s\n",yytext);}
"printf"                {printf("PRINTF %s\n",yytext);}
"scanf"                 {printf("SCANF %s\n",yytext);}
"if"                    {printf("IF %s\n",yytext);}
"elif"                  {printf("ELIF %s\n",yytext);}
"else if"               {printf("ELSE_IF %s\n",yytext);}
"else"                  {printf("ELSE %s\n",yytext);}
"true"                  {printf("TRUE %s\n",yytext);}
"false"                 {printf("FALSE %s\n",yytext);}
"void"                  {printf("VOID %s\n",yytext);}
"main"                  {printf("MAIN %s\n",yytext);}
"return"                {printf("RETURN %s\n",yytext);}
[a-zA-Z_][a-zA-Z0-9]*   {printf("IDENTIFIER %s\n",yytext);}
[-+]?(([1-9][0-9]*)|0)  {printf("INTEGER %s\n",yytext);}
[-+]?[0-9]+\.[0-9]+      {printf("FLOAT %s\n",yytext);}
\"[^\\"\n]*\"            {printf("STRING %s\n",yytext);}
","                     {printf("COMMA %s\n",yytext);}
";"                     {printf("SEMICOLON %s\n",yytext);}
"{"                     {printf("LEFT_BRACE %s\n",yytext);}
"}"                     {printf("RIGHT_BRACE %s\n",yytext);}
"("                     {printf("LEFT_PAREN %s\n",yytext);}
")"                     {printf("RIGHT_PAREN %s\n",yytext);}
"["                     {printf("LEFT_BRACKET %s\n",yytext);}
"]"                     {printf("RIGHT_BRACKET %s\n",yytext);}
"-"                     {printf("MINUS %s\n",yytext);}
"+"                     {printf("PLUS %s\n",yytext);}
"*"                     {printf("MULTIPLY %s\n",yytext);}
"/"                     {printf("DIVIDE %s\n",yytext);}
"\\"                    {printf("BACKSLASH %s\n",yytext);}
"%"                     {printf("MODULUS %s\n",yytext);}
"=="                    {printf("EQUALS %s\n",yytext);}
"!="                    {printf("NOT_EQUALS %s\n",yytext);}
"<"                     {printf("LESS_THAN %s\n",yytext);}
">"                     {printf("GREATER_THAN %s\n",yytext);}
"<="                    {printf("LESS_THAN_OR_EQUAL %s\n",yytext);}
">="                    {printf("GREATER_THAN_OR_EQUAL %s\n",yytext);}
"="                     {printf("ASSIGN %s\n",yytext);}
"&&"                    {printf("LOGICAL_AND %s\n",yytext);}
"||"                    {printf("LOGICAL_OR %s\n",yytext);}
"!"                     {printf("LOGICAL_NOT %s\n",yytext);}
[" "|\t|\n|\f|\v]       {printf("WHITESPACE\n");}
.                       {printf("UNRECOGNIZED_CHARACTER %s\n",yytext);}
%%

原理说明

新增的规则会优先匹配232a3、90.s3这类不符合C语言语法的非法序列,因为它们的匹配长度比拆分后的多个token更长(比如232a3长度为5,拆分后是3+2,最长匹配是5),同时这些规则定义在合法规则之前,所以Flex会优先选择这些规则,将整个非法序列识别为UNRECOGNIZED_CHARACTER,避免拆分。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 01:42:25