Flex词法规则无法吞除CLI输入末尾\r\n换行符问题求解
问题根因
Flex的词法匹配遵循两个核心规则:① 多规则同时可匹配时,优先选匹配字符长度最长的规则;② 匹配长度相同时,优先选定义顺序更靠前的规则。你的配置失效有三个核心原因:
swallow规则定义冗余且优先级被覆盖
你写的swallow ([[:space:]]|\n)属于重复定义:POSIX标准的[[:space:]]字符类本身已经包含空格、制表符(\t)、回车(\r)、换行(\n)、垂直制表符、换页符所有空白类字符,额外加|\n没有任何作用。更关键的是你把全局空白吞噬规则放在了所有规则的最后,而any规则的贪婪匹配长度远大于单个空白字符,末尾的\r\n根本轮不到swallow处理,直接被更长的any匹配捕获。any正则设计存在逻辑错误
你定义的any [^[:space:]](.|\n)+是无边界贪婪匹配:一是规则中显式加了|\n,让原本不匹配换行的.可以匹配所有字符(包括\r、\n),会从第一个非空白字符开始一直吃到输入缓冲区的最末尾,自然会把输入结尾的回车、换行都包含进捕获结果;二是规则后缀用了+限定符,要求值长度至少为2个字符,单个字符的值(比如数字、单字符状态标记)根本无法被匹配,会触发Flex默认的未匹配字符打印逻辑。- 隐藏逻辑缺陷:
VALUE状态匹配完cmd_value后没有切回INITIAL初始状态,处理完一条命令后词法分析器会停留在VALUE状态,后续所有输入都会被当作值内容捕获,完全无法识别新命令。另外你定义了LOG专属状态但没有写任何匹配规则,进入该状态后会触发Flex默认规则,直接把未匹配字符打印到标准输出。
修正方案
- 修正宏定义,去掉冗余逻辑,给值匹配加上行尾边界,同时修复单字符值无法匹配的问题
/* 匹配1个及以上连续空白,减少规则触发次数,提升效率 */ swallow [[:space:]]+ /* 匹配值内容:从第一个非空白字符开始,到回车/换行前停止,不会捕获行尾\r\n,支持单字符值 */ any [^[:space:]][^\r\n]*
- 补全状态回退逻辑,在
VALUE状态匹配到值后切回初始状态 - 补全
LOG状态的兜底规则,避免进入状态后出现异常输出。
修正后关键代码段
%{ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "hmd.tab.h" #include "cmd.h" %} %option noyywrap nounput noinput %option reentrant bison-bridge %option debug digit [0-9] integer [+-]?{digit}+ uinteger {digit}+ real [+-]?({digit}+[.]{digit}*)|({digit}*[.]{digit}+) exp [+-]?({integer}|{real})[eE]-?{integer} alpha [[:alpha:]]+ /* 修正后的值匹配规则 */ any [^[:space:]][^\r\n]* /* 修正后的空白匹配规则 */ swallow [[:space:]]+ %x ZONE ZONE_ACTION ZONE_FIELD_SET ZONE_FIELD_GET VALUE SYSTEM %x DEVICE DEVICE_ACTION DEVICE_FIELD_SET DEVICE_FIELD_GET %x SENSOR SENSOR_SET SENSOR_GET SENSOR_VALUE %x LOG %% /* 其余原有业务规则保持不变,仅列修改部分 */ <VALUE>{any} { yylval->name = strdup(yytext); printf("VALUE '%s'\n", yytext); /* 匹配完值后切回初始状态,准备处理下一条命令 */ BEGIN(INITIAL); return(cmd_value); } /* LOG状态兜底规则,可后续替换为实际业务逻辑 */ <LOG>.|\n { BEGIN(INITIAL); } /* 全局空白吞噬规则 */ <*>{swallow} {}
注意:如果你确实需要支持多行值(值内容中允许包含换行),不要使用PCRE专属的非贪婪、环视语法(Flex不支持这类特性),建议增加显式续行标记(比如行尾加
\表示下一行是值的延续),否则无法区分值内部的换行和命令结束的终止换行。常规CLI场景下单行命令、值到行尾截止的逻辑用修正后的any规则即可满足需求。
内容的提问来源于stack exchange,提问作者Lev
相关产品推荐
相关产品推荐

