Flex 根据将输入文本映射到 C 或 C++ 动作的规则生成扫描器。扫描器通常向解析器提供令牌,但也可以转换文本或验证正则语言格式。Flex 手册 定义了生成的接口以及各选项的行为。
规范结构 #
.l 文件包含三个由 %% 分隔的部分:定义、规则和用户代码。定义部分包含 C 声明、命名正则表达式和开始条件。规则部分将模式与动作配对。最后一个部分提供辅助函数,并在需要时提供驱动程序。
%option noyywrap
%{
#include "parser.h"
%}
identifier [[:alpha:]_][[:alnum:]_]*
%%
[[:space:]]+ ;
{identifier} return IDENTIFIER;
"+" return '+';
. return INVALID;
%%Flex 选择与输入文本中最长部分匹配的规则。当多个规则匹配相同长度时,选择书写在前面的规则。默认规则会将未匹配的字符复制到标准输出;当必须报告未匹配输入时,应使用显式的兜底规则或 -s/%option nodefault。
正则表达式与扫描器状态 #
Flex 模式描述正则语言。命名定义可以减少重复,字符类用于表达与区域设置相关的分组,带引号的文本用于匹配字面字符串。当语言令牌需要按字面使用元字符时,请对其进行转义或加引号。
开始条件可以划分规则集。使用 %s 声明包含式条件,使用 %x 声明排他式条件;使用 <STATE> 为规则添加限定。
%x COMMENT
%%
"/*" BEGIN(COMMENT);
<COMMENT>"*/" BEGIN(INITIAL);
<COMMENT>\n ++line;
<COMMENT>. ;
<COMMENT><<EOF>> return UNTERMINATED_COMMENT;包含式条件会保留未限定的规则,而排他式条件只运行针对该条件或所有状态显式限定的规则。除非使用 %option nodefault 或显式兜底规则替换,否则 Flex 生成的默认规则在每个状态中都保持有效。使用 BEGIN 更改状态;当动作需要检查当前状态时,使用 YY_START。对于可能提前结束输入的状态,应处理 <<EOF>>。应将字符串、注释、缩进或词法模式建模为显式状态,而不是试图在一个模式中表达上下文相关的结构。
生成的 API、缓冲区与可重入性 #
默认情况下,Flex 生成 yylex() 以及 yytext、yyleng 和输入缓冲区状态等全局变量。%option prefix="project_" 会重命名生成的公共符号,从而允许在一个程序中使用多个扫描器。%option outfile="scanner.c" 选择生成的文件;命令行选项也可以提供相同的配置。
扫描器通过缓冲区读取输入。yyrestart(FILE*) 会重新开始输入,yy_scan_string 或 yy_scan_bytes 会扫描调用方提供的内存。使用 yy_scan_* 函数创建的缓冲区应通过 yy_delete_buffer 删除;当嵌套输入需要在受管理的缓冲区之间切换时,使用 yy_switch_to_buffer。
%option reentrant 会创建扫描器句柄 (yyscan_t),并使生成的 API 接受该句柄。使用 yylex_init/yylex_destroy(或 _extra 变体)初始化和销毁句柄,然后通过 yyextra 而不是全局变量传递每个扫描器的状态。当解析器或线程必须彼此独立地运行时,应使用此模式。
解析器集成与诊断 #
与 Bison 集成时,应配置匹配的令牌声明,并包含生成的解析器头文件。%option bison-bridge 通过扫描器接口传递语义值;%option bison-locations 还会传递位置信息。必须为每个消耗的字符(包括空白和注释)更新位置,否则诊断信息会偏离源代码。
应区分词法分析错误与解析器语法错误。扫描器应返回或报告带有源代码范围的无效令牌;解析器应在语法上下文中描述意外令牌,并且只在有意设置的同步点进行恢复。
评论
在 GitHub 上阅读或评论(发表评论需要登录)