Bison筆記

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'且預讀終結符是'-',此時分析器選擇移進還是歸約與選擇左關聯還是右關聯是一回事:移進將會進行右關聯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章