Bison文件组成,主要分为3部分
%{
1. 序言 (Prologue)
声明全局标识符,定义数据类型、变量和宏,包含头文件,等。
%}
声明 (declarations)
声明终结符,非终结符,运算符的优先级,符号语义值的各种类型。
%%
2. 语法 (Grammar rules)
定义每一非终结符的语法规则。
%%
3. 结言 (Epilogue)
定义序言中声明的函数,以及剩余的所有程序。开发者定义自己的函数 ,格式与 C语言定义函数相同。
第一部分:
序言:
%{
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
char * parsetreeroot=NULL;
extern "C" int yylex();
extern "C" int yyparse();
extern "C" void yyerror(const char *s, ...);
char globalInputText[10000];
int globalReadOffset;
%}
声明
%
union
{
int
intval;
double
floatval;
char
* strval;
int
subtok;
}
union就是把所有使用到的数据类型、变量声明在一起,然后供%type 和%token 来具体声明每个标记对应的变量类型。
%token <strval>
NAME
%token <strval> STRING
%token <strval> INTNUM
token声明终结符的类型,终结符 (Terminal symbol)不可再分的符号,类似数据库中的关键字
%type <strval> stmt_root create_stmt para_list definition data_type pro_block pro_parameters declare_list set_list
%type <strval> assign_var pro_body pro_stmt_list sql_stmt expr
type用于声明非终结符的类型,非终结符 (Nonterminal symbol)可以展开为更小的结构。每一非终结符用一条规则来定义。
优先级定义:
为了避免过多的语法冲突,在这里定义一些语法优先级,从上到下优先级依次递增
%left OR
%left AND
%right NOT
%nonassoc IS ISNULL NOTNULL /* IS sets precedence for IS NULL, etc */
%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
left左结合,right右结合
第二部分
所有规则的定义
bison语法是由一系列的规则组成。每个规则由非终结符开始,然后是“:”和可能为空的符号、文字记号和动作的列表。 例如:
simple_select:
SELECT opt_all_clause opt_target_list
into_clause from_clause where_clause
group_clause having_clause window_clause
{
SelectStmt *n = makeNode(SelectStmt);
n->targetList = $3;
n->intoClause = $4;
n->fromClause = $5;
n->whereClause = $6;
n->groupClause = $7;
n->havingClause = $8;
n->windowClause = $9;
$$ = (Node *)n;
}
|
……
……
……
;
- “:”左边为非终结符。
- “:”右边为规则,由一系列的终结符或非终结符构成。
- “$$”,表示simple_select。
- “$number”,表示“:”右边符号,数字为几,则表示第几个符号。
- “|”,表示或,“simple_select”其他规则。
- { }是具体每项展开对应的用户编程 内容 ,然后以分号结束 。
移进与归约(压栈和出栈操作)
shift 移进
把输入流中的下一个 token 移入分析栈。
reduce 归约
归约分析栈顶中已识别的规则。当分析栈顶的符号序列匹配某规则右端时,用该规则的左端替换之。
当bison处理一个语法分析树时,会创建一组状态,每个状态对应一个或者多个分析过的规则中的可能的位置。当读到的记号不足以结束一条规则的时候,就会把这个记号压入一个内部堆栈,然后切换到新状态,这个过程叫做移进。当压入栈内的所有的语法符号已经等价于一个规则的右部时,就把这些符号全部弹出,把规则的左部压入栈。这个过程叫做规约。
例如:
statement :NAME '=' expression
expression :NUMBER '+' NUMBER
|NUMBER '-' NUMBER
具体例子:
fred=12+13
语法分析器一次移进一个记号。
堆栈:
fred
fred =
fred =12
fred=12 +
fred =12+13 把12+13 规约成expression,12+13弹出,expression压入
fred = expression 把fred = expression规约成statement fred = expression弹出,statement压入