青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標題前加-[轉貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數據加載中……

使用 Flex 和 Bison 更好地進行錯誤處理

  盡管使用 Flex Bison 生成程序非常簡單,但是要讓這些程序產生用戶友好的語法和語義錯誤消息卻很困難。本文將介紹 Flex Bison 的錯誤處理特性,并展示如何使用它們,然后詳細介紹它們的一些缺陷。

 

  簡介

 

  正如 UNIX? 開發人員所了解的那樣,Flex Bison 的功能非常強大,非常適合開發詞法和語法解析器,尤其是語言編譯器和解釋器。如果我們不熟悉它們所實現的工具 —— 分別是 Lex Yacc —— 可以參考一下本文 參考資料一節中有關 Flex Bison 文檔的鏈接,以及其他介紹這兩個程序的文章。

 

  本文介紹了更高級的一些主題:用來在編譯器和解釋器中更好地實現錯誤處理能力的特性和技術。為了展示這些技術,我使用了一個示例程序 ccalc,它基于 Bison 手冊中的計算機實現了一個增強的計算器。我們可以從本文后面下載 一節下載 ccalc 和相關文件。

 

  增強包括使用了很多變量。在 ccalc 中,變量是通過在初始化中首次使用時定義的,例如 a = 3。如果變量是在初始化之前使用的,那就會產生語義錯誤,使用值為 0 來創建這個變量,并打印一條消息。

 

  示例源文件

 

  示例源代碼中包括 7 個文件:

 

ccalc.c:主程序,以及一些進行輸入、輸出和錯誤處理的函數

ccalc.h:包括了對所有模塊的定義

cmath.c:數學函數

parse.yBison 使用的輸入文法

lex.lFlex 的輸入

makefile:簡單的 makefile

defs.txt:示例輸入文件

  這個程序接收兩個參數:

 

-debug:產生調試輸出

filename:輸入文件名;默認值為 defs.txt

  Bison 使用的設置

 

  為了處理變量名和實際值,Bison 的語義類型必須進行增強:

 

  清單 1. 更好的 Bison 語義類型

 

/* generate include-file with symbols and types */

%defines

/* a more advanced semantic type */

%union {

 double   value;

 char    *string;

}

 

  有些文法規則可以產生特定的語義類型,這需要像清單 2 中一樣對 Bison 進行聲明。要獲得一個可移植性更好的 Bison 文法版本,我們需要重新定義 +-*/() 符號。下面這個例子沒有使用左括號 (,而是使用了結束符符號 LBRACE,這是由詞法分析提供的。另外,操作符的優先順序也必須進行聲明。

 

  對于 Flex 來說,所生成的代碼通常都依賴于平臺所使用的代碼頁(codepage)。盡管我們可以使用其他代碼頁,但是必須要對輸入進行轉換。因此與 Bison 代碼不同,Flex 代碼尚不能進行移植。

 

  清單 2. Bison 聲明

 

/* terminal symbols */

%token <string>  IDENTIFIER

%token <value>  VALUE

%type <value>   expression

/* operator-precedence

* top-0: -

*   1: * /

*   2: + -

*/

%left ADD SUB

%left MULT DIV

%left NEG

%start program

 

  這段文法與 Bison 手冊非常類似,不同之處在于它使用了名字作為終端符號和標識符的簡寫形式。標識符是在賦值語句中進行定義和初始化的,并且可以在任何允許使用的地方使用。清單 3 給出了一個示例文法:

 

  清單 3. 示例 Bison 文法

 

program

  : statement SEMICOLON program

  | statement SEMICOLON

  | statement error SEMICOLON program

  ;

statement

  : IDENTIFIER ASSIGN expression

  | expression

  ;

expression

  : LBRACE expression RBRACE

  | SUB expression %prec NEG

  | expression ADD expression

  | expression SUB expression

  | expression MULT expression

  | expression DIV expression

  | VALUE

  | IDENTIFIER

  ;

 

  program 的第三個輸出讓這個分析程序可以獲得錯誤,從中搜索分號,然后繼續執行(通常錯誤對于解析器來說都是非常嚴重的)。

 

  為了讓這個例子更加有趣,規則體中的真正數學函數都是以單獨函數的形式實現的。在進行高級文法分析時,我們要盡量保證規則簡短,并使用函數來實現一些不會直接處理解析的過程:

 

  清單 4. 使用單獨的函數來實現數學規則

 

| expression DIV expression

 {

  $$ = ReduceDiv($1, $3);

 }

 

  最后,函數 yyerror() 必須要進行定義。這個函數是在所生成的解析器檢測到語法錯誤時調用的,它又會調用一個小函數 PrintError(),后者會打印增強的錯誤消息。詳細內容請參看源代碼。

 

  Flex 的設置

 

  Flex 所生成的詞法分析器必須要根據語義類型提供終止符號。清單 5 定義了空格、實際值、標識符和符號所使用的語法。

 

  清單 5. 示例 Flex 規則

 

[   

]+ {

  /* eat up whitespace */

  }

{DIGIT}+ {

  yylval.value = atof(yytext);

  return VALUE;

  }

{DIGIT}+"."{DIGIT}*    {

  yylval.value = atof(yytext);

  return VALUE;

  }

{DIGIT}+[eE]["+""-"]?{DIGIT}*    {

  yylval.value = atof(yytext);

  return VALUE;

  }

{DIGIT}+"."{DIGIT}*[eE]["+""-"]?{DIGIT}*    {

  yylval.value = atof(yytext);

  return VALUE;

  }

{ID}    {

  yylval.string = malloc(strlen(yytext)+1);

  strcpy(yylval.string, yytext);

  return IDENTIFIER;

  }

"+"    { return ADD; }

"-"    { return SUB; }

"*"    { return MULT; }

"/"    { return DIV; }

"("    { return LBRACE; }

")"    { return RBRACE; }

";"    { return SEMICOLON; }

"="    { return ASSIGN; }

 

  為了幫助調試,我們在程序運行的末尾把所有已知的變量及其當前內容都打印了出來。

 

  使用普通錯誤消息的例子

 

  使用下面的輸入(其中稍微進行了排版)來編譯并運行這個示例解析器程序 ccalc

 

  清單 6. 數學解析器的示例輸入

 

a = 3;

3 aa = a * 4;

b = aa / ( a - 3 );

 

  輸出結果如下所示:

 

  清單 7. 數學解析器的示例輸出

 

Error 'syntax error'

Error: reference to unknown variable 'aa'

division by zero!

final content of variables

  Name------------------ Value----------

  'a          ' 3

  'b          ' 3

  'aa         ' 0

 

  這個輸出結果并非非常有用,因為它并沒有顯示問題到底在什么地方。這在下一節中會進行介紹。

 

  擴展 Bison 可以更好地處理錯誤消息

 

  Bison 的最主要的特性在 Bison 手冊中隱藏的很深,就是它可以通過使用 YYERROR_VERBOSE 宏在產生語法錯誤的情況下生成更有意義的錯誤消息。

 

  普通的 'syntax error' 消息如下:

 

  Error 'syntax error, unexpected IDENTIFIER, expecting SEMICOLON'

 

  這條消息對于調試更為合適。

 

  更好的輸入函數

 

  使用原來的錯誤消息,很難判斷語義的錯誤。當然,這個例子非常容易修復,因為我們立即就可以找出有錯誤的那一行。在更加復雜的語法和對應輸入中,這可能并不簡單。讓我們編寫一個輸入函數來從文件中讀取相應的行。

 

  Flex 具有一個非常有用的宏 YY_INPUT,它負責為符號解釋讀入數據。我們可以在 YY_INPUT 宏中添加一個對 GetNextChar() 函數的調用,后者從文件中讀取數據,并保留了下一個要讀取的字符的位置信息。GetNextChar() 使用了一個緩沖區來存放一行輸入。這兩個變量保存了當前行號和該行中下一個字符的位置:

 

  清單 8. 更好的 Flex YY_INPUT

 

#define YY_INPUT(buf,result,max_size) {

  result = GetNextChar(buf, max_size);

  if ( result <= 0 )

   result = YY_NULL;

  }

 

  使用這個增強的錯誤打印函數 PrintError()(在前面討論過,它可以很好地顯示有問題的輸入行,完整的 PrintError() 源代碼請參看 示例源代碼),我們就具有了一個用戶友好的消息,它顯示了下一個字符的位置:

 

  清單 9. 更好的 Flex 錯誤:字符位置

 

    |....+....:....+....:....+....:....+....:....+....:....+

   1 |a = 3;

   2 |3 aa = a * 4;

...... !.....^

Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON

   3 |b = aa / ( a - 3 );

...... !.......^

Error: reference to unknown variable 'aa'

...... !.................^

Error: division by zero!

 

  這個示例函數可以從其他函數(例如 ReduceDiv())中進行調用,從而打印語義錯誤,例如 division by zero unknown identifiers

 

  如果我們希望標記一下最后使用的符號,就可以對 Flex 規則進行擴展,并修改錯誤的打印。函數 BeginToken() PrintError()(二者都可以在示例源代碼中找到)是關鍵:BeginToken() 是由每條規則進行調用的,這樣它就可以記住每個符號的開始和結束,每次打印錯誤時都會調用 PrintError()。這樣,我們就可以生成一條有用的消息了,例如:

 

  清單 10. 更好的 Flex 錯誤:表示確切的符號位置

 

   2 |3 aa = a * 4;

...... !..^^............

Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON

 

  缺點

 

  所生成的詞法解析器可能會在檢測到某個符號之前讀入多個字符。因此,這個過程不可能精確地顯示確切的位置。它最終取決于為 Flex 所提供的規則。規則越復雜,位置的精確程度就越低。這個例子中的規則可以由 Flex 通過提前查找一個字符來進行處理,這會讓位置的預測更加精確。

 

  Bison 的定位機制

 

  下面讓我們來看一下 division by zero 這個錯誤。最后一次符號讀取(結束括號)并不是這個錯誤的根源。表達式 (a-3) 的值就是 0。對于更好的錯誤消息來說,我們需要知道表達式的位置。要實現這種功能,我們可以在 YYLTYPE 類型的全局變量 yylloc 中提供這個符號的確切位置。使用宏 YYLLOC_DEFAULT(請參看 Bison 文檔 中默認的定義),Bison 可以計算出某個表達式的位置。

 

  記住,只有當您在文法中使用位置時才會定義類型。這是一個常見的錯誤。

 

  默認的位置類型 YYLTYPE 如清單 11 所示。我們可以對這個類型重新進行定義,使其包括更多信息,例如 Flex 所讀取的文件名。

 

  清單 11. 默認位置類型 YYLTYPE

 

typedef struct YYLTYPE

{

 int first_line;

 int first_column;

 int last_line;

 int last_column;

} YYLTYPE;

 

  在上一節中,我們看到了 BeginToken() 函數,它是在新符號開始時調用的。此時就應該存儲這個位置了。在我們的例子中,一個符號不能跨越多行,因此 first_line last_line 是相同的,它們都保存了當前的行號。其他屬性有符號的起點(first_column)和終點(last_column),這是通過符號的起點和長度計算出來的。

 

  要使用這個位置,我們必須對規則處理函數進行處理,如清單 12 所示。符號 $3 的位置是通過 @3 進行引用的。為了防止拷貝這個規則中的整個結構,我們生成了一個指針 &@3。這看起來可能有點奇怪,但卻是正確的。

 

  清單 12. 記住規則中的位置

 

| expression DIV expression

 {

  $$ = ReduceDiv($1, $3, &@3);

 }

 

  在處理函數中,我們獲得了一個指向保存了位置信息的 YYLTYPE 結構的指針,這樣可以生成一條很好的錯誤消息。

 

  清單 13. ReduceDiv 中使用保存的位置

 

extern

double ReduceDiv(double a, double b, YYLTYPE *bloc) {

 if ( b == 0 ) {

  PrintError("division by zero! Line %d:c%d to %d:c%d",

            bloc->first_line, bloc->first_column,

            bloc->last_line, bloc->last_column);

  return MAXFLOAT;

 }

 return a / b;

}

 

  現在錯誤消息可以幫助我們來定位問題了。除零操作錯誤在第 3 行的第 10 列到 18 列之間。

 

  清單 14. 更好的 ReduceDiv() 錯誤消息

 

    |....+....:....+....:....+....:....+....:....+....:....+

   1 |a = 3;

   2 |3 aa = a * 4;

...... !..^^...........

Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON

   3 |b = aa / ( a - 3 );

...... !....^^...............

Error: reference to unknown variable 'aa'

...... !.................^..

Error: division by zero! Line 3:10 to 3:18

final content of variables

  Name------------------ Value----------

  'a          ' 3

  'b          ' 3.40282e+38

  'aa         ' 0

 

 

  結束語

 

  Flex Bison 是用來解析文法的一對功能強大的組合。通過使用本文中介紹的技巧,我們可以構建更好的解釋器,它們可以生成像您自己喜歡的編譯器中一樣的有用的、容易理解的錯誤消息。

 

 

posted on 2008-11-22 22:29 肥仔 閱讀(1235) 評論(0)  編輯 收藏 引用 所屬分類: LEX & YACC

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            夜夜嗨av一区二区三区中文字幕 | 美女免费视频一区| avtt综合网| 一区二区三区日韩在线观看| 亚洲国产专区| 欧美大片免费久久精品三p| 免费不卡在线观看av| 亚洲精品免费在线| 香蕉久久a毛片| 欧美—级在线免费片| 欧美精品在线视频| 国产日韩精品一区| 91久久中文| 久久影视三级福利片| 亚洲高清不卡在线| 欧美诱惑福利视频| 欧美日韩www| 在线观看日产精品| 久久精品天堂| 欧美精品久久99| 伊人色综合久久天天| 亚洲美女视频在线观看| 久久久亚洲高清| 亚洲主播在线观看| 欧美午夜不卡| 亚洲综合色激情五月| 美日韩精品免费| 久久一区二区三区av| 国产精品视频yy9099| 亚洲欧美国产一区二区三区| 最新中文字幕一区二区三区| 美日韩精品视频| 欧美一区二区成人| 国产日韩欧美另类| 久久久久久久久久看片| 欧美在线观看一区二区| 影音先锋亚洲视频| 欧美激情精品久久久| 欧美大片在线看| 一本久久青青| 亚洲免费在线视频| 欧美日韩精品一本二本三本| 91久久精品国产91久久性色tv| 看片网站欧美日韩| 欧美α欧美αv大片| 亚洲一区二区三区午夜| 欧美一区二区视频观看视频| 影音先锋中文字幕一区| 亚洲特色特黄| 亚洲精品黄网在线观看| 亚洲自拍偷拍一区| 亚洲精品视频一区二区三区| 亚洲影院在线| 在线亚洲欧美视频| 久久婷婷色综合| 欧美一级大片在线观看| 欧美—级在线免费片| 久久精品一区二区三区不卡牛牛| 欧美精品 日韩| 欧美黄色影院| 伊人春色精品| 久久久精品一区二区三区| 亚洲一区久久久| 欧美精品黄色| 欧美国产成人精品| 尤物在线精品| 久久久精彩视频| 久久亚洲风情| 久久国产视频网| 欧美在线啊v一区| 国产精品一区二区你懂得| 亚洲国产日韩欧美在线动漫| 在线成人黄色| 欧美精品日韩一区| 在线亚洲观看| 久久精品99无色码中文字幕| 国产色产综合色产在线视频 | 国产区精品在线观看| 宅男精品视频| 可以看av的网站久久看| 亚洲青涩在线| 国产精品人人做人人爽| 久久国产精品免费一区| 欧美国产1区2区| 亚洲欧美视频在线观看视频| 国产精品高潮在线| 久久精品av麻豆的观看方式| 久久视频精品在线| 亚洲尤物在线| 亚洲欧洲一区二区三区在线观看 | 美国十次成人| 亚洲自拍电影| 亚洲激情啪啪| 国外视频精品毛片| 欧美日韩视频在线| 欧美91视频| 美日韩精品视频| 欧美一区二区三区精品| 亚洲久久一区二区| 亚洲国产精品视频| 麻豆av一区二区三区| 欧美一级午夜免费电影| 欧美日韩高清区| 六月天综合网| 老巨人导航500精品| 久久精品国产96久久久香蕉| 亚洲主播在线播放| 亚洲主播在线| 欧美一区二区三区在线免费观看| 亚洲午夜国产成人av电影男同| 99re成人精品视频| 99在线精品免费视频九九视| 亚洲欧洲综合另类在线| 一本大道久久精品懂色aⅴ| 小黄鸭精品密入口导航| 欧美高清成人| 亚洲一区在线看| 欧美午夜欧美| 亚洲一级黄色av| 亚洲欧美日韩高清| 欧美中文日韩| 免费成人av在线看| 欧美激情女人20p| 一区二区欧美亚洲| 亚洲综合999| 久久久久久久久岛国免费| 先锋影音久久| 欧美激情一区二区三区| 欧美日韩国产影院| 国产一区深夜福利| 一区二区三区免费观看| 午夜久久久久久久久久一区二区| 欧美激情第六页| 欧美综合77777色婷婷| 国产欧美69| 欧美综合77777色婷婷| 亚洲砖区区免费| 国产精品乱码人人做人人爱| 91久久综合| 99国产精品国产精品久久| 久久亚洲不卡| 在线精品视频在线观看高清| 久久久久久久欧美精品| 久久国产综合精品| 影音欧美亚洲| 国产精品视频最多的网站| 久久久精品国产免费观看同学| 亚洲黄色在线观看| 亚洲国产精品成人久久综合一区| 开元免费观看欧美电视剧网站| 欧美色大人视频| 亚洲国产精品女人久久久| 看片网站欧美日韩| 99re8这里有精品热视频免费| 亚洲主播在线观看| 噜噜噜91成人网| 国内揄拍国内精品少妇国语| 亚久久调教视频| 久久久久久久999| 日韩一级黄色片| 午夜精品久久久99热福利| 一区二区三区亚洲| 亚洲精品日韩久久| 国产日韩在线一区| 99riav国产精品| 亚洲激情女人| 美日韩丰满少妇在线观看| 欧美中文字幕第一页| 久久久之久亚州精品露出| 国产欧美高清| 久久av二区| 欧美α欧美αv大片| 伊人狠狠色丁香综合尤物| 欧美视频你懂的| 国内在线观看一区二区三区| 亚洲性视频h| 狂野欧美激情性xxxx| 欧美一区二区三区在线观看| 免费在线亚洲欧美| 欧美成人情趣视频| 国产日韩欧美一区在线| 正在播放亚洲| 亚洲尤物视频网| 欧美日韩一区二区在线观看视频 | 亚洲影视中文字幕| 久久亚洲电影| 女同性一区二区三区人了人一| 国产午夜精品美女毛片视频| 中文久久精品| 午夜精品视频在线| 国产农村妇女毛片精品久久莱园子 | 久久成人羞羞网站| 久久久久久久久久久一区 | 国产模特精品视频久久久久| 亚洲一区二区日本| 久久精品一区二区国产| 尤物精品国产第一福利三区 | 欧美不卡激情三级在线观看| 鲁大师影院一区二区三区| 亚洲大片精品永久免费|