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

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>
            欧美伦理视频网站| 久久久在线视频| 国产精品国产三级国产专播精品人 | 裸体一区二区| 老司机aⅴ在线精品导航| 亚洲国产视频一区| 亚洲精品在线电影| 国产精品亚洲一区| 久久这里只精品最新地址| 免费日韩av电影| 亚洲素人在线| 欧美一区二区观看视频| 1000部精品久久久久久久久| 最新国产拍偷乱拍精品| 国产精品女主播在线观看| 久久婷婷亚洲| 欧美午夜不卡视频| 美女免费视频一区| 欧美国产日韩一区二区在线观看| 亚洲欧美国产高清| 久久久最新网址| 亚洲一区二区三区国产| 欧美一区免费| 在线中文字幕不卡| 久久久久久久久久久成人| 亚洲视频一二三| 久久夜色精品国产亚洲aⅴ| 国内精品视频666| 久久精品国产亚洲5555| 亚洲乱码视频| 亚洲综合不卡| 亚洲人成人一区二区在线观看| 亚洲影院色在线观看免费| 亚洲欧洲综合另类| 午夜久久黄色| 亚洲综合色丁香婷婷六月图片| 免费在线观看一区二区| 久久gogo国模裸体人体| 欧美日韩在线高清| 欧美电影在线| 韩国福利一区| 亚洲中字在线| 午夜精品影院| 欧美日韩三区四区| 亚洲国产日韩欧美综合久久| 激情视频一区二区三区| 亚洲欧美在线磁力| 亚洲欧美清纯在线制服| 欧美日韩小视频| 亚洲人被黑人高潮完整版| 亚洲第一天堂av| 久久久久久97三级| 久久一区激情| 国内精品久久久久久久果冻传媒 | 米奇777在线欧美播放| 国产一区二区三区高清播放| 亚洲一区视频在线| 性欧美超级视频| 国产精品美腿一区在线看 | 欧美国产在线电影| 在线播放国产一区中文字幕剧情欧美 | 日韩视频一区二区在线观看| 亚洲另类自拍| 欧美日韩ab片| 一区二区三区波多野结衣在线观看| 野花国产精品入口| 欧美日韩三级一区二区| 亚洲一级免费视频| 欧美伊人精品成人久久综合97| 国产精品一区二区你懂得| 亚洲网站啪啪| 久久久久久久久久久久久久一区 | 亚洲最新视频在线| 国产精品大片免费观看| 亚洲午夜免费福利视频| 久久国产精品久久精品国产| 国产一区二区视频在线观看| 久久久精品免费视频| 欧美91福利在线观看| 亚洲精品欧美| 国产精品久久国产精麻豆99网站| 亚洲欧美国产va在线影院| 久久国产欧美精品| 亚洲国产美女精品久久久久∴| 欧美激情视频网站| 一道本一区二区| 久久国产精品免费一区| 久久久久久日产精品| 亚洲高清资源综合久久精品| 欧美激情精品| 在线综合欧美| 久久夜色精品国产欧美乱极品| 亚洲全黄一级网站| 国产精品va在线| 久久激情久久| 亚洲精品久久久久久久久久久久 | 亚洲精品一区二区三区蜜桃久| 欧美日韩精品一区| 久久国内精品自在自线400部| 欧美二区在线播放| 午夜影院日韩| 亚洲精品久久久久| 国产伦精品一区二区三区高清版| 久久影院午夜片一区| 中文av字幕一区| 欧美激情一区二区三区四区| 午夜精品福利视频| 亚洲精品美女在线观看| 国产综合网站| 国产精品国产三级国产aⅴ无密码| 久久久欧美一区二区| 亚洲一区在线观看免费观看电影高清| 免费成人性网站| 欧美资源在线观看| 亚洲午夜精品久久| 最新日韩av| 国产日韩综合一区二区性色av| 欧美精品18| 麻豆精品在线观看| 久久aⅴ国产欧美74aaa| 亚洲天堂久久| av成人激情| 亚洲精品美女久久7777777| 免费成人av在线看| 久久夜色精品国产欧美乱极品| 午夜精品一区二区三区四区| 亚洲视频狠狠| 亚洲视频你懂的| 在线视频你懂得一区| 亚洲伦理一区| 亚洲黄一区二区三区| 在线电影国产精品| 激情欧美国产欧美| 国内一区二区三区| 国产视频久久| 国产三级精品在线不卡| 国产精品制服诱惑| 国产酒店精品激情| 国产精品一区二区三区成人| 国产精品国产三级国产aⅴ入口| 欧美三区视频| 国产精品乱码一区二三区小蝌蚪 | 亚洲国产中文字幕在线观看| 国产综合久久久久久鬼色| 国产在线成人| 一色屋精品视频在线观看网站| 国产综合视频在线观看| 狠狠操狠狠色综合网| 精品99视频| 亚洲精品欧美日韩专区| 亚洲视频在线观看三级| 亚洲制服少妇| 久久精品亚洲| 欧美不卡在线视频| 亚洲国内精品在线| 国产精品99久久久久久人| 亚洲愉拍自拍另类高清精品| 午夜精品视频在线| 久久久精品动漫| 欧美国产精品久久| 美女在线一区二区| 久久这里只有| 最新成人在线| 亚洲摸下面视频| 久久国产精品99精品国产| 久久男人资源视频| 欧美国产精品专区| 国产精品一级| 亚洲国产另类精品专区| 亚洲一区三区视频在线观看| 久久精品一区二区国产| 亚洲国产精品国自产拍av秋霞| 日韩视频在线免费| 欧美在线免费观看亚洲| 欧美电影免费网站| 国产午夜精品久久久久久免费视| 亚洲国产99精品国自产| 亚洲在线中文字幕| 欧美xxxx在线观看| 亚洲午夜av| 蜜桃久久精品一区二区| 国产精品久久久久婷婷| 亚洲人成小说网站色在线| 欧美一区二区三区在线| 亚洲清纯自拍| 久久久久中文| 国产精品稀缺呦系列在线| 91久久午夜| 久久久一区二区| 亚洲天堂av在线免费| 欧美电影免费观看网站| 精品成人国产| 午夜精品一区二区三区四区| 亚洲日本无吗高清不卡| 久久久久在线| 国产婷婷色综合av蜜臀av | 韩国精品主播一区二区在线观看| 亚洲视频你懂的| 91久久极品少妇xxxxⅹ软件| 久久中文字幕一区|