对scan.l的解析(一)

源码链接

https://www.gitlink.org.cn/Eao3piq4e/openGauss-server/tree/master/src%2Fcommon%2Fbackend%2Fparser%2Fscan.l

概述

        该文件的路径为src/common/backend/parser/scan.l,在这个文件中定义了词法结构。在计算机科学里面,Flex 是一个词法分析器产生程序( lexical analyzer generator),Flex 即Fast Lex,它常常与 Bison 语法分析器产生程序( parser generator )一起使用,Bison 即 GNU 版的 Yacc 。而 Flex 作为词法分析中一个十分有用的工具,在本篇博客我来介绍一下与之联系甚密的 scan.l 文件的用处及其基本构造。

解析

        在解析之前,我们先来了解一下 token 的意义,这对我们理解为何要用 Flex 生成一个词法分析器有着莫大意义。当 Flex 读进一个代表词法分析器规则的输入字符串流,然后输出以C语言编写的词法分析器源代码。利用这个词法分析器可以将一个文本中的单词提取出来,而这其实就是提取编程语言占用的各种保留字、操作符等等语言的元素,我们也将这些元素称为令牌(token)。词法分析器一般以函数(yylex()函数)的形式存在,供语法分析器调用。

        令牌可以是关键字、标识符、常量、字符串值,或者是一个符号。例如,下面的 SQL 语句包括五个令牌:                                                                                                                                     

//代码清单1
SELECE name,id FROM student;

这五个令牌分别是:

SELECTnameidFROMstudent;
token类型关键字标识符标识符关键字标识符终结符
描述命令列名列名条款表名结束语句

        对于一种编程语言来说,token 的类型是有限的。Flex 工具会帮我们生成一个 yylex() 函数,Bison 通过调用这个函数来得知拿到的 token 是什么类型的,而这,便是 scan.l 和 Flex 的关联之处。Flex 的输入文件一般会被命名成 .l 文件,通过 scan.l 我们得到输出的文件是 scan.cpp,yylex() 函数就在这个文件中。这个过程的结构图大概是这样的,yylex() 函数就是我们所说的词法分析器的主体了:

        接下来,我们再了解一下 scan.l 文件的基本结构。总地来说,scan.l 文件的结构为:

定义段
%%
规则段 
%%
C代码段

可以看到,scan.l 文件分为3个部分,每个部分间用顶行的 %% 分隔开。

定义段

        这一部分又包含了代码部分、选项部分、状态定义部分、模式定义部分。在代码部分中,代码是用 %top{...} 或者是 %{...%} 括起来的。

不管是 %top{...} 还是 %{...%} 括起来的内容都是C代码,经 Flex 处理后会将代码照搬到生成的 scan.cpp 中。但是它们二者括起来的内容会有一点小差别:

1、%top{...}是将其中的代码拷贝到 scan.cpp 中的顶部,这里我们可以加入一些注释或者包含一些头文件。

2、%{...%}中可以定义一些函数声明、结构体、类型等,还可以重定义一些Flex的宏,从而改变程序的规则。

对应在 scan.l 文件中的这一部分:

//代码清单2
%{
#include "postgres.h"
#include "knl/knl_variable.h"
#include <ctype.h>
#include <unistd.h>
······

#undef fprintf
#define fprintf(file, fmt, msg)  ereport(ERROR, (errmsg_internal("%s", msg)))
#define YYSTYPE core_YYSTYPE
······

static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner);
static void addlitchar(unsigned char ychar, core_yyscan_t yyscanner);
static char *litbufdup(core_yyscan_t yyscanner);
static char *litbuf_udeescape(unsigned char escape, core_yyscan_t yyscanner);
······
%}

这里我做了部分省略,但还是可以看出在这一部分出现了头文件、宏定义、函数声明等。

        对于选项部分,它使得我们不必在控制台使用 Flex 命令来挑选选项,而是在 Flex 的输入文件中通过 %option 语句就可以实现这一功能,对应在 scan.l 文件中的这一部分:

//代码清单3
%option reentrant
%option bison-bridge
%option bison-locations
%option 8bit
%option never-interactive
%option nodefault
%option noinput
%option nounput
%option noyywrap
%option noyyalloc
%option noyyrealloc
%option noyyfree
%option warn
%option prefix="core_yy"

这些选项具体的作用可以参照这篇文章:Flex介绍 - 知乎

        在状态定义部分,对应的源码为:

//代码清单4
%x xb
%x xc
%x xd
%x xh
%x xe
%x xq
%x xdolq
%x xui
%x xus
%x xeu

        模式定义部分由正则表达式构成,对应的源码为:

//代码清单5
space			[ \t\n\r\f]
horiz_space		[ \t\f]
newline			[\n\r]
non_newline		[^\n\r]
comment			("--"{non_newline}*)
whitespace		({space}+|{comment})
whitespace_only	({space}+)
special_whitespace		({space}+|{comment}{newline})
horiz_whitespace		({horiz_space}|{comment})
whitespace_with_newline	({horiz_whitespace}*{newline}{special_whitespace}*)
quote			'
quotestop		{quote}{whitespace}*
quotecontinue	{quote}{whitespace_with_newline}{quote}
quotefail		{quote}{whitespace}*"-"
xbstart			[bB]{quote}
xbinside		[^']*
······

规则段

        在这一部分,每一个规则分为模式/行为行C执行代码行,例如其中的一个规则:

//代码清单6
{comment}    {
				if (yyextra->include_ora_comment)
					{
						SET_YYLLOC();
						addlit(yytext, yyleng, yyscanner);
						yylval->str = litbufdup(yyscanner);
						return COMMENTSTRING;
					}
             }

其中 {comment} 即为模式/行为行,而后面用花括号括起来的即C执行代码行,并且在前面的定义部分中,我们可以找到 comment 的正则表达式,意为以 "--" 开头的语句是注释:

//代码清单7
non_newline		[^\n\r]
comment			("--"{non_newline}*)

scan.l 文件在这里放置的规则就是每个正则表达式要对应的动作,一般是返回一个 token,例如 comment 对应的动作是将注释文本添加到指定的缓冲区并返回 COMMENTSTRING,该部分的C执行代码被 Flex 拷贝到 scan.cpp 中。

C代码段

        该部分的C代码也被 Flex 拷贝到 scan.cpp 中,具体的函数行使特定的功能。附加一些源码:

//代码清单8
#undef yyextra
#define yyextra  (((struct yyguts_t *) yyscanner)->yyextra_r)

/* Likewise for a couple of other things we need. */
#undef yylloc
#define yylloc  (((struct yyguts_t *) yyscanner)->yylloc_r)
#undef yyleng
#define yyleng  (((struct yyguts_t *) yyscanner)->yyleng_r)

int scanner_errposition(int location, core_yyscan_t yyscanner)
{
	······
}

void scanner_yyerror(const char *message, core_yyscan_t yyscanner)
{
	······
}
······

总结

        我在这篇博客介绍了 scan.l 的作用、基本结构,以及它和 Flex 工具的联系,这给我们理解 gram.y 的作用以及它和 Bison 的联系起到了可供借鉴的作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔走的月光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值