Bison笔记

来源:互联网 发布:51单片机视频教程下载 编辑:程序博客网 时间:2024/06/07 00:04

Bison笔记

2016/10/21

1.语法结构

%{

C/C++头文件、全局文件、全局变量、类型定义

词法分析器yylex(采用lex进行词法分析)和错误打印函数

%}

Bison声明区间。定义之后用到的终结符、非终结符、操作符优先级

%%

Bison语法规则定义

%%

C/C++代码 需要定义prologue区域函数,或者其他代码,生成的c/c++文件会完全拷贝这份代码。

 

2.FAQ

终结符、非终结符定义

Token用于定义终结符 type定义非终结符 操作符也属于终结符

Left表示左关联运算符 right表示右关联运算符

%token NUM   

%nonassoc ‘<’ 表示该终结符无结合性 不能出现a<b<c

%left ‘+’ ‘-’ 左结合 后面接操作符 下方的操作符比上方的优先级高

%left ‘*’ ‘/’ 

%right NEG  NEG表示非

%right ‘^’

 

Bison声明区域

%union{

  Expressions* expressions;/*表达式集合*/

  Expression* expression;/*表达式*/

  char      name[32];

  double    num;

}

%token ASSIGN 258

%token<num> DOUBLE_CONST 259

%token<name> IDENTIFIER 260

%token IF 261 THEN 262 ELSE 263 FI 264

%token WHILE 265 LOOP 266 POOL 267

  

%type<expression> expr

%type<expressions> exprs

%type<expressions> exprs_no

 

%%

input:

/* empty */

| exprs

;

 

exprs:

error { $$ = 0;}

| exprs error

| expr ';'

{

  $$ = t_single_exprs($1);

  Execute($1);

}

| exprs expr ';'

{

  $$ = t_append_exprs($1, $2);

  Execute($2);

}

;

 

expr:

IDENTIFIER { $$ = t_id($1); }

| DOUBLE_CONST { $$ = t_num($1);}

| expr '+' expr { $$ = t_plus($1, $3); }

| expr '-' expr { $$ = t_sub($1, $3); }

| expr '*' expr { $$ = t_mul($1, $3); }

| expr '/' expr { $$ = t_div($1, $3); }

| '(' expr ')' { $$ = $2;}

| '{' exprs_no '}' { $$ = t_block($2);}

| expr '<' expr { $$ = t_less($1, $3); }

| expr '=' expr { $$ = t_eq($1, $3); }

| IDENTIFIER ASSIGN expr { $$ = t_assign($1, $3); }

| IF expr THEN expr  ELSE expr FI { $$ = t_if($2, $4, $6); }

| WHILE expr LOOP expr POOL { $$ = t_while($2, $4); }

;

 

exprs_no:

expr ';'

{

  $$ = t_single_exprs($1);

}

| exprs_no expr ';'

{

  $$ = t_append_exprs($1, $2);

}

;

%%

 

终结符和非终结符

终结符的类型通过"%token<类型名>终结符"这样的格式来确定

%type是指定非终结符的类型,用法和%token一样,不过不需要指定编号。我们可以发exprexpression类型。这里expr的意思是一个表达式,exprsexprs_no是多个表达式集合。

非终结符也可以通过%type<类型名>非终结符这种格式来表示。

两者区别是终结符相当于原子不可分,而非终结符相当于分子可分,可由终结符或非终结符归约而成。

终结符使用词法分析来识别,而非终结符使用的归约方法。

bison中词法分析需要由自己指定,比如示例中的yylex(),也可以采用flex定义词法分析规则,利用生成的词法分析代码来完成,比如在nessus使用的nasl语言使用的自己实现的词法分析,详见规则文件中的mylexyara中则使用的flex实现的词法分析。

 

Union结构

Union{

par_exp_t*      exp;

int             lt_integer;

}

Bison中默认将所有的语义值都定义为int类型,可以通过定义宏YYSTYPE来改变值的类型。如果有多个值类型,则需要通过在Bison声明中使用%union列举出所有的类型。

Union中的每一个项,都是一个语法规则的每一个非终结符.

其中par_exp_t用来描述被识别出的exp的信息,对应到c/c++代码中的类型。

 

可以这样定义此非终结符

%type exp

%type lt_integer

如果类型名称与非终结符名称不一致可使用如下方法

Union{

par_exp_t*      eee;

int             iii;

}

%type<eee> exp

%type<iii> lt_integer

在一条归约规则中,每个终结符或非终结符都对应一个c类型,如果没有指定则为int类型,具体的类型在union中定义,在每一条匹配规则中用$$$n表示该类型。

 

声明语法的开始符号

%start tiptop

这是告知bison, 这是语法最终需要规约的非终结符号。

 

示例

input:

/* empty */

| exprs

;

exprs:

error { $$ = 0;}

| exprs error

| expr ';'

{

  $$ = t_single_exprs($1);

  Execute($1);

}

| exprs expr ';'

{

  $$ = t_append_exprs($1, $2);

  Execute($2);

}

;

在一条规则中$$表示表达式的返回值,$1表示第一个终结符,依次类推。

从上面input可以看到输入为一个表达式集合,而exprs是由expr ';'exprs expr ';'组成。也就是说一个表达式集合,是由一个或多个表达式后跟';'组成

$$ = t_single_exprs($1);动作的意思是创建只有一个表达式expr的表达式集,赋值给exprs,此时只归约到一个表达式。

$$ = t_append_exprs($1, $2);动作的意思是把表达式expr加入到exprs集合里。Execute($1);的意思是执行这个表达式。这里执行的意思是计算这个表达式的语义值,输出结果。

 

归约过程

expr:

IDENTIFIER { $$ = t_id($1); }

| DOUBLE_CONST { $$ = t_num($1);}

| expr '+' expr { $$ = t_plus($1, $3); }

| expr '-' expr { $$ = t_sub($1, $3); }

| expr '*' expr { $$ = t_mul($1, $3); }

| expr '/' expr { $$ = t_div($1, $3); }

| '(' expr ')' { $$ = $2;}

| '{' exprs_no '}' { $$ = t_block($2);}

| expr '<' expr { $$ = t_less($1, $3); }

| expr '=' expr { $$ = t_eq($1, $3); }

| IDENTIFIER ASSIGN expr { $$ = t_assign($1, $3); }

| IF expr THEN expr  ELSE expr FI { $$ = t_if($2, $4, $6); }

| WHILE expr LOOP expr POOL { $$ = t_while($2, $4); }

;

'{' exprs_no '}' { $$ = t_block($2);}是类似cool语言的一个语法规则:一个表达式可以推出大括号包围的表达式集合。这个集合类似之前的exprs,区别是exprs_no不需要立即执行表达式的值。因为可能条件判断不符合,所以这段代码就不能执行。

 

3.语法规则分析

expr:

NUM { $$ = $1; }

| expr '+' expr { $$ = $1 + $3; }

| expr '-' expr { $$ = $1 - $3; }

;

例如这样一个语句:1+3-2。根据规则expr->NUM先归约expr+3-2,然后规约'+',继续归约终结符为expr+expr-2。因为左结合,根据expr->expr + expr,得expr-2。继续归约终结符的expr-expr,最后结果为expr,归约结束

 

移进归约分析

语法分析有自顶向下(LL、递归下降分析)和自底向上(LR、移进归约分析)两种方法。

Bison采用LALR分析方法,适用于上下文无关文法。

参考链接http://www.cppblog.com/woaidongmao/archive/2008/11/23/67635.aspx

 

操作符优先级

例如1-2*3,考虑到文法二义性可能会产生2种方式进行分析。

假定分析器已经看到了终结符'1''-''2';那么应该对它们归约到减法运算规则吗?这取决于下一个终结符。当然,若下一个终结符是')',就必须归约;此时移进是非法的,因为没有任何规则可以对序列'- 2 )'进行归约,也没有以这个序列开始的什么东西。但是如果下一个终结符是'*'或者'<',那么就需要做一个选择:移进或者归约,都可以让分析得以完成,但是却有不同的结果。

为了决定Bison应该怎么做,必须考虑这两个结果。若下一个终结符即操作符op被移进,那么必然是op首先做归约,然后才有机会让前面的减法操作符做归约。其结果就是(有效的)'1(2 op 3)'。另一方面,若在移进op之前先对减法做归约,那结果就是'(12) op 3'。很显然,这里移进或者规约的选择取决于减法操作符'-'与下一个操作符op之间的优先级:若op是乘法操作符'*',那么就选择移进;若是关系运算符'<'则应该选择规约。

 

左关联与右关联操作符

那么诸如'1 2 5'这样的输入又如何呢?是应该作为'(12)5'还是应该作为'1(25)'?对于大多数的操作符,我们倾向于前一种形式,称作左关联(left association)。后一种形式称作右关联(right association),对于赋值操作符来说是比较理想的。当堆栈中已经有'12'且预读终结符是'-',此时分析器选择移进还是归约与选择左关联还是右关联是一回事:移进将会进行右关联。

0 0
原创粉丝点击