- 专栏内容:postgresql内核源码分析
- 个人主页:我的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
目录
前言
词法分析与语法分析的关系
工具介绍
flex的介绍
bison的介绍
flex的使用
举例
flex文件的结构
flex与bison协同工作
语法分析
bsion文件结构
语法规则
二义性的处理
结尾
在数据库内核中,语法解析和词法分析已经非常成熟,本文来介绍一下它使用的基本知识点,后面介绍数据库内容时,也能更好的理解。
本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。
一般的编程语言,如SQL的分析,分为:词法分析->语法分析->语义分析Semantic Analysis->语法树(Abstract Syntax Tree)
词法分析就是把输入的语句,按定义的模式规则分解成词语或符号,也叫token;
语法分析就是把有序的token输入,按约定的语句分隔符分析是否是完成语句,以及分析token组成的语句是否符合语法规则;
语义分析就是把语句的行为进行分析,得出它需要做什么,输入那些参数或约束,是否符合语义的规则;
其中有两个重要的工具来协助完成词法和语法分析,它们是:
flex词法分析(lexical analysis 或称为scanning)
bison是语法分析(systax analysis 或称为parsing)
flex的前身是Lex, Lex是1975年由Mike Lesk和当时尚在AT&T实习的Eric Schmidt共同完成的),是一个词法分析器的生成程序,可以单独使用也可以与Johnson的yacc协同工作。大概在1987年,Berkeley实验室的Vern Paxson用C重新写了Lex,并命名为FLex(the Fast Lexical Analyzer Generator),基于伯克利许可证。
flex将模式处理为内部格式(确定性有穷自动机,Deterministic Fininte Automation,DFA),速度非常快;
bison的前身是yacc。yacc是由贝尔实验室的S.C.Johnson基于Knuth大神的LR语法分析理论,于1975~1978年写成。大约1985年,UC Berkeley 的研究生Bob Corbett使用改进的内部算法实现了伯克利yacc,来自FSF的Richard Stallman改写了伯克利yacc并将其用于GNU项目,添加了很多特性,形成了今天的GNU Bison。bison现在作为FSF的项目被维护,基于GNU公共许可证发布。
原来lex/yacc组合,可以由flex/bsion组合来替代。
下面是一个统计行数,单词,字符的程序
%{
int chars = 0;
int words = 0;
int lines = 0;
%}
%%
[^ \t\n\r\f\v]+ { words++; chars += strlen(yytext); }
\n { lines ++; chars++; }
. { chars++; }
%%
int main(int argc, char *argv[])
{
yylex();
printf("lines:%8d\n words:%8d\n chars:%8d\n",lines,words,chars);
}
int yywrap(void) {
return 1;
}
其中第一行模式,不是列出的空白字符,多个字符组成的字符串;
第二行模式,遇到换行时统计行数和字符;
第三行,其它的字符,统计字符;这里是上面匹配之外的。
[senllang@localhost demo04]$ flex charCount.l
[senllang@localhost demo04]$ gcc lex.yy.c
[senllang@localhost demo04]$ ./a.out
sdflk chain
234 df dsf
lines: 2
words: 5
chars: 23
结束时按Ctrl+D,就会输出结果。
flex文件是嵌套c代码,它的文件(*.l)内容分三大部分
第一部分是声明和选项设置;在%{和}%间的会原样被拷到C程序中;
第二部分是一系列模式和动作;模式必须在每行的开头,因为flex认为有空格的都是行为,会被原样拷到c程序中;由%%和%%包围;这里的c代码是用{}括住的一行或多行;
第三部分C代码,会被拷到生的c文件中,通常是一些与动作代码相关的例程;
其中模式规则,使用正则表达式(regular expression, regex或regexp表示)。
在flex的yylex调用中,识别到一个标识符后,以记号的形式返回;再调用时,会以当前位置继续分析;对于空白等可以忽略,不返回时,yylex就继续工作;
利用以上特点,flex就可以与其它程序进行协作;
语法分析树,具用优先级;记号转换成语法分析树的规则,常用的语言是上下文无关文法(Contex-Free Grammar,CFG),在语言界也被称为短语结构文法(Phrase-Structure Grammar)或3型语言(Type-3 language)
上下文无关文法的格式就是BNF范式(BackusNaur form,BNF)。
bison的规则就是基于BNF范式,但简化了一点输入。
::= |
+
::= NUMBER |
* NUMBER
每一行就是一条规则,用来说明如何创建语法分析树的分支。在BNF里,::=被读作“是”,或者“变成”,|读作“或者”,是创建同类分支的另一种方式。
规则左边的名称是语法符号(symbol)。也就是说,所有记号都被认为是语法符号,但有一些语法符号不是记号。
有效的BNF总是带有递归性,规则会直接或间接的指向自身,这些简单的规则被递归的使用来匹配任何极端复杂的加法和乘法序列。
bison程序包含了与flex相同的三部分结构,
第一部分声明部分,声明也会被原样拷到C代码中,用%{和}%来声明。随后是%token记号声明,告诉语法分析程序记号名称。记号通常用大写,没有声明为记号的语法符号必须出现在至少一条规则左边。
第二部分规则部分;bison不是用::=,而是用:=,同时行间隔并不明显,分号被用来表示规则的结构。同样,C的动作代码在每条规则之后用{}包起来。第一条规则的左边的语法符号是语法起始符号(start symbol),当然其它规则,也可以拥有相同的起始符号。
第三部分是C代码部分。
在bison语法中,目标符号的值,也就是左边语法符号的值用$$来表示,右边语法符号的语义值依次用$1,$2...直到规则结束;当语法分析器返回时,值保存在yyval中。
第一条一般用常见的双规则递归定义来定义一个表达式;
对于有优先级的语法符号,需要定义多条规则来分别处理,通过前后顺序来决定优先级,否则就会产生二义性或不确定性。
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!