#lex & yacc
项目github地址
##1. 背景
网上关于lex和yacc的介绍真的又老又少,而goyacc的更加少,最近需要解析sql,接触到这块,虽然最后发现其效率没有用手动写代码解析效率高而放弃使用,但是学会了这种快速构建文法解析的工具还是有所收获吧。
##2. 相关知识
增加一点内容篇幅,不想看的直接跳到3就ok
####2.1词法分析
词法分析的作用就是对一个文本或称为一串字符串(包括空格,换行,特殊符号等)进行内容提取。通过分析给每一个字段打上标记(TOKEN)。那么如何分析呢,以sql的查询语句为例
select * from userinfo where name = 'zhang' order by age
这个字符串有多少信息呢,首先,select,from, where, order, by 这些单词在从左到右扫描时与userinfo,age这些并无不同,但很明显这些单词是数据库的保留单词,也称为关键字。所以一般的我们需要一个关键字表,在扫描到单词时判断其是可变的字段还是关键字。其次对于 * ,=,‘zhang’,我们单独将其标记为特殊符号,操作符,字符串,就这样,我们对所有的可能情况进行识别给不同类型的字段以唯一的标识(TOKEN)一般为int。所以此法分析的作用就是将一系列的文本转换成TOKEN序列。
####2.2文法分析
对于词法分析后的TOKEN序列,我们需要知道他是否符合我们的文法规范,例如
select from a=1
像这样的句子不符合sql规范,我们需要明确的指出。文法分析其实就是一个有限状态自动机,当扫描到一个TOKEN时,我们根据不同的情况跳转到另一状态,直到终结状态。
####2.3语义分析
语义阶段与文法阶段往往可以同步进行,在sql文法分析的阶段我们便可以标明该语义树的各个部分,比如在扫描到select时,我们便可以确定这是一个select语句或者可能是错误语句,然后根据后续信息补全select语法树。
##3. goyacc
为什么用goyacc,因为influxdb本身是go语言写的,我需要解析的语法树与influxdb使用的语法树相同,并且不需要二次构建,所以使用goyacc。其实还有一个原因就是go比c好写,alloc和malloc写起来,真的难受。
####工具 goyacc
go git github.com/golang/tools/cmd/goyacc
go build
go intall
####3.1 lex与yacc工作流程
go提供goyacc用于语法分析。在我们写好一个yacc规则后,使用goyacc sql.y指令生成对用的go文件。其中包含两个重要的对象
type yyLexer interface {
Lex(lval *yySymType) int
Error(s string)
} type yyParser interface {
Parse(yyLexer) int
Lookahead() int
}
yyparser是yacc自动实现的,不需要我们操作,parser是入口函数,它会不停的调用Lex函数来获取TOKEN进行文法分析。我们需要自己实现一个Lex即实现yyLexer接口。
Lex实现
type Tokenizer struct {
query Query
scanner *Scanner
}
func (tkn *Tokenizer) Lex(lval *yySymType) int{
var typ int
var val string
for {
typ, _, val = tkn.scann