SQLite3源程序分析之分析器的生成

xiaoxiao2021-02-28  16

SQLite3源程序分析之分析器的生成

1、概述

  Lemon是一个LALR(1)文法分析器生成工具,与bison和yacc类似,是一个可以独立于SQLite使用的开源的分析器生成工具。而且它使用与yacc(bison)不同的语法规则,可以减少编程时出现错误的机会。Lemon比yacc和bison更精致、更快,而且是可重入的,也是线程安全的——这对于支持多线程的程序是非常重要的。

  Lemon的主要功能就是根据上下文无关文法(CFG),生成支持该文法的分析器。程序的输入文件有两个:  (1) 语法规则文件;  (2) 分析器模板文件。  一般来说,语法规则是由程序员定义的;Lemon有一个适用于大多数应用程序的默认分析器模板。

  根据命令行选项,Lemon会生成以下一些文件:  (1) 分析器的C代码;  (2) 一个为每个终结符定义一个整型ID的头文件;  (3) 一个描述分析器状态的文件。

  语法规范文件以”.y”为后缀,如果语法规范文件为”gram.y”,则可以使用命令生成分析器lemon gram.y

1.1、分析器接口

  Lemon不会生成一个完整的、可以运行的程序。它仅仅生成一些实现分析器的子例程,然后由用户程序在适当的地方调用这些子例程,从而生成一个完整的分析器。

1.1.1、ParseAlloc  程序在使用Lemon生成的分析器之前,必须创建一个分析器。如下:

void *pParser = ParseAlloc( malloc );

  ParseAlloc为分析器分配空间,然后初始化它,返回一个指向分析器的指针。SQLite对应的函数为:

void *sqlite3ParserAlloc(void *(*mallocProc)(size_t))

  函数的参数为一个函数指针,并在函数内调用该指针指向的函数。如:

void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){ yyParser *pParser; pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); if( pParser ){ pParser->yyidx = -1; #ifdef YYTRACKMAXSTACKDEPTH pParser->yyidxMax = 0; #endif #if YYSTACKDEPTH<=0 pParser->yystack = NULL; pParser->yystksz = 0; yyGrowStack(pParser); #endif } return pParser; }

1.1.2、ParseFree  当程序不再使用分析器时,应该回收为其分配的内存。如下:

ParseFree(pParser, free);

  SQLite对应的函数如下:

void sqlite3ParserFree( void *p, /* The parser to be deleted */ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ yyParser *pParser = (yyParser*)p; if( pParser==0 ) return; while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); #if YYSTACKDEPTH<=0 free(pParser->yystack); #endif (*freeProc)((void*)pParser); }

1.1.3、Parse  Parse是Lemon生成的分析器的核心例程。在分析器调用ParseAlloc后,分词器就可以将切分的词传递给Parse,进行语法分析。SQLite对应的函数如下:

void sqlite3Parser( void *yyp,      /* The parser */ int yymajor,      /* The major token code number */ sqlite3ParserTOKENTYPE yyminor /* The value for the token */ sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */ )

  该函数由sqlite3RunParser调用:

int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg)

  sqlite3RunParser位于token.c文件中,它是进行SQL语句分析的入口,它调用sqlite3GetToken对SQL语句zSql进行分词,然后调用sqlite3Parser进行语法分析。而sqlite3Parser在语法规则发生规约时调用相应的opcode生成子例程,生成opcode。

1.2、与yacc和bison的不同之处

  Lemon与yacc和bison有一些不同的地方:  (1)在yacc和bison中,是分析器调用分词器。在Lemon,分词器调用分析器;  (2)Lemon不会使用全局变量。而yacc与bison在分析器与分词器之间使用全局变量;  (3)Lemon允许多个分析器同时运行,因为它是可重入的。而yacc与bison却不行。

2、输入文件语法

  Lemon的语法规则文件(grammar specification file)主要用于为分析器定义语法规则。此外,输入文件还包括其它一些有用的信息,使用的Lemon的大部分工作就是写语法文件。

2.1、终结符与非终结符(Terminals and Nonterminals)

  终结符通常是大写的字符串(数字或字母)。非终结符是小写的字符串(数字或字母)。

2.2、语法规则(Grammar Rules)

  每个语法规则由三部分构成:以非终结符开始,随后紧接着为“::=”,然后是终结符(或非终结)列表,规则以英文语句“.”结尾。如下:

expr ::= expr PLUS expr. expr ::= expr TIMES expr. expr ::= LPAREN expr RPAREN. expr ::= VALUE.

  上例中,有一个非终结符“expr”,和5个终结符:“PLUS”、“TIMES”、“LPAREN”、“RPAREN”、和“VALUE”。  与yacc和bison一样,Lemon允许为规则添加C代码块,并由分析器进行规则规约时调用。如下:

expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }

  为了使规则有用,语法动作(grammar actions)必须与相应的语法规则联系起来。在yacc和bison中,动作(action)中的“$$”代表左值,而“$1”、“$2”等则代表“::=”右边的位置相应为1、2等的终结符或非终结符的值。这是非常有用的,但是,却非常容易出错。例如:

expr -> expr PLUS expr { $$ = $1 + $3; };

  而Lemon通过为规则中的每个符号指定一个额外的符号名字(symbolic)达到相同的目的,然后在动作中使用这些符号名字。如下:

expr(A) ::= expr(B) PLUS expr(C). { A = B+C; } 

2.3、优先级规则(Precedence Rules)

  Lemon采用与yacc和bison相同的方法处理歧义性问题。移进—规约冲突,则选择移进;规约——规约冲突,则选择先出现的规则。  同样,Lemon也允许通过优先级规则来解决冲突。如下:

%left AND. %left OR. %nonassoc EQ NE GT GE LT LE. %left PLUS MINUS. %left TIMES DIVIDE MOD. %right EXP NOT.

2.4、特殊指示符(Special Directives)

  Lemon支持如下一些特殊指示符:

%code %default_destructor %default_type %destructor %extra_argument %include %left %name %nonassoc %parse_accept %parse_failure %right %stack_overflow %stack_size %start_symbol %syntax_error %token_destructor %token_prefix %token_type %type

  %code  %code表示将一段C/C++代码添加到输出文件的尾部。它主要用于包含一些动作例程(action routines)或者词法器的部分代码。

  

转载请注明原文地址: https://www.6miu.com/read-2629566.html

最新回复(0)