Bison 基础
简单介绍
bison 是一个语法分析器,把用户输入的内容,根据在.y文件中事先定义好的规则,构建一棵语法分析树。(所谓的规则就是,匹配上对应字符之后,执行相应的动作.)
一个简单的语法例子和分析:
statement :NAME '=' expression
expression : NUMBER '+' NUMBER
| NUMBER '-' NUMBER
冒号(:) 用来间隔一条规则的左边和右边。statement等价于NAME ‘=’ expression。
在语法中规定大写字母和引号的内容为终结符,比如NAME '='两个字符不再有含义,已经终结。
expression是非终结符,根据第二行的规定,expression 等价为
两个数的加法或者两个数的减法。其中**竖线(|)**表示一个语法符号有两种等价方式。
NUMBER ‘+’ NUMBER NUMBER ‘-’ NUMBER均是终结符,语法解析结束。
移进规约分析
当bison处理一个语法分析树时,会创建一组状态,每个状态对应一个或者多个分析过的规则中的可能的位置。
当读到的记号不足以结束一条规则的时候,就会把这个记号压入一个内部堆栈,然后切换到新状态,这个过程叫做移进。
当压入栈内的所有的语法符号已经等价于一个规则的右部时,就把这些符号全部弹出,把规则的左部压入栈。这个过程叫做规约。
Bison组成部分
定义部分
%%
规则部分
%%
用户附加的C语言部分
第一部分为定义部分,此部分主要包括选项、文字块、注释、声明符号、语义值数据类型的集合、指定开始符号及其它声明等等。
文字块存在与%{和%}之间,它们将被原样拷贝到生成文件中。
/* 指定起始符号(start symbol)有时也称为目标符号(goal symbol) */
%start calclist
/* 声明tokens记号,以便于告诉bison在语法分析程序中记号的名称。通常这些记号总是使用大写字母,虽然bison本身并没有这个要求。 */
%token NUMBER
%{
/* 文字块,该部分的内容将直接复制到生成的代码文件的开头,
以便它们在使用yyparse定义之前使用。 */
#define _GNU_SOURCE
#include <stdio.h>
#include "ptypes.h"
%}
第二部分,主要是语法规则
%%
/* 空规则 -- 起始符号(start symbol)有时也称为目标符号(goal symbol) */
calclist:
/* 如果没有指定语义动作,bison将使用默认的动作: { $$ = $1; }*/
| calclist exp EOL { printf("- %d\n", $2); }
//EOL代表一个表达式的结束。像flex一样,大括号中的表示规则的动作
;
exp: factor // default $$ = $1
| exp ADD factor { $$ = $1 + $3; }
| exp SUB factor { $$ = $1 - $3; }
; // represent the termination of this rule.
factor: term // default $$ = $1
| factor MUL term { $$ = $1 * $3; }
| factor DIV term { $$ = $1 / $3; }
;
term: NUMBER // default $$ = $1
| ABS term { $$ = $2 >= 0? $2 : - $2; }
;
%%
第三部分,此部分的内容将直接逐字复制到生成的代码文件末尾。该部分主要用于对之前一些声明了的函数进行实现。
int main(int argc, char ** argv)
{
printf(">");
yyparse();
return 0;
}
yyerror(char *s)
{
fprintf(stderr,"error:%s\n",s);
}
左递归和右递归
当你编写一个递归规则时,你可以把递归的引用放在规则右部的左端或者右端,例如:
exprlist: exprlist ',' expr; /* 左递归 */
exprlist: expr ',' exprlist; /* 右递归 */
大多数情况下,你可以选择任意一种方式来编写语法。bison处理左递归要比处理右递归更有效率 。这是因为它的内部堆栈需要追踪到目前位置所有还处在分析中规则的全部符号。
YYINITDEPTH来控制语法分析器堆栈的长度,表明堆栈的初始大小,通常为200,也可以定义YYMAXDEPTH来设置堆栈长度的最大值,通常为1000
%{
#define YYMAXDEPTH 50000
%}