擴展iQuery使其支持多種編程語言(四) – 兼編譯器的語法錯誤處理簡介css
iQuery是一個開源的自動化測試框架項目,有興趣的朋友能夠在這裏下載:https://github.com/vowei/iQuery/downloadshtml
源碼位置:https://github.com/vowei/iQueryjava
相關的使用文檔,請參看:git
開源類庫iQuery Android版使用說明
類jQuery selector的控件查詢iQuery開源類庫介紹
開源手機自動化測試框架iQuery入門教程(一)
開源手機自動化測試框架iQuery入門教程(二)
開源手機自動化測試框架iQuery入門教程(三)
在上一篇文章中,簡單介紹了iQuery解釋器的語義分析部分。github
ANTLR已經自帶了一些對詞法和語法錯誤的處理功能,當一行語句出現語法錯誤時,ANTLR會盡可能跳過出錯的一行代碼,恢復編譯和解釋功能,經過一個回調函數,咱們能夠向最終用戶顯示更細緻的錯誤提示。編程
通常來講,好的錯誤提示應該有如下幾個性質:
1. 須要指明錯誤的行號和列號,以便用戶快速在源代碼中定位出錯的那一行代碼,這個功能ANTLR會在調用咱們的回調函數時傳入這些信息。
2. 須要指明錯誤緣由,例如「不匹配的字符」這樣的錯誤信息顯然沒有「第1行,第25列: 沒有關閉的語句,指望']',當前碰到的是''<EOF>''!」這樣的信息更明確。
3. 須要指明致使出錯的文字,例如在編程中,針對使用一個未定義的變量的編程錯誤,固然須要在錯誤信息裏指出這個未定義的變量名。
4. 最好給出修復錯誤的建議。框架
在ANTLR裏,能夠在代碼裏定義一個getErrorMessage函數,以便ANTLR回調。在Java版本中,getErrorMessage函數的聲明形式以下(代碼實如今:https://github.com/vowei/iQuery/blob/master/java/iquery/iquery-core/src/main/java/cc/iqa/iquery/iQuery.g):
編程語言
1: public String getErrorMessage(RecognitionException e,
2: String[] tokenNames)
當ANTLR碰到詞法或者語法錯誤時,會拋出一個基類爲RecognitionException的異常,並傳遞給getErrorMessage函數,而第二個參數tokenNames就是致使詞法/語法錯誤時的源碼符號,getErrorMessage函數的返回值就是細化後的錯誤消息。函數
ANTLR針對不一樣的詞/語法錯誤會生成不一樣的RecognitionException的繼承類,在這些繼承類裏,分別定義了一些對細化錯誤消息有幫助的屬性。下面代碼是一個細化錯誤消息的例子,其中MismatchedTokenException表示一個未匹配的語法,在MismatchedTokenException的對象裏,能夠經過expecting字段獲取指望的詞法符號(原始代碼位置是:https://github.com/vowei/iQuery/blob/master/java/iquery/iquery-core/src/main/java/cc/iqa/iquery/ErrorMessageHelper.java )。測試
1: if (e instanceof MismatchedTokenException) {
2: MismatchedTokenException mte = (MismatchedTokenException) e;
3: String tokenName = "<unknown>";
4: if (mte.expecting == Token.EOF) {
5: tokenName = "EOF";
6: } else if (tokenNames != null) {
7: tokenName = tokenNames[mte.expecting];
8: } else {
9: tokenName = new String(new char[] {(char)mte.expecting});
10: }
11:
12: if ( e.token != null ) {
13: msg = String.format("%1$s: 沒有關閉的語句,指望%2$s,當前碰到的是'%3$s'!",
14: hdr, tokenName, recognizer.getTokenErrorDisplay(e.token));
15: } else {
16: msg = String.format("%1$s: 沒有關閉的語句,指望%2$s!",
17: hdr, tokenName);
18: }
19: }
JavaScript版本里getErrorMessage函數的聲明相似,處理方式也相似參考代碼:https://github.com/vowei/iQuery/blob/master/iOS/lib/iQuery.g 和 https://github.com/vowei/iQuery/blob/master/iOS/lib/error.js ):
1: function onMismatchedTokenException(mte, tokenNames, recognizer) {
2: debug("onMismatchedTokenException");
3:
4: var tokenName = "<unknown>";
5: if (mte.expecting == org.antlr.runtime.Token.EOF) {
6: tokenName = "EOF";
7: } else if (tokenNames != null) {
8: tokenName = tokenNames[mte.expecting];
9: } else {
10: debug("[onMismatchedTokenException] - mte.expecting: " + mte.expecting);
11: tokenName = mte.expecting;
12: }
13:
14: if (mte.token != null) {
15: return "沒有關閉的語句,指望" + tokenName + ",當前碰到的是'" + recognizer.getTokenErrorDisplay(mte.token) + "'!";
16: } else if (tokenName != undefined) {
17: return "沒有關閉的語句,指望" + tokenName + "!";
18: } else {
19: return "沒有關閉的語句!";
20: }
21: }
因爲詞法分析器(Lexer)和語法分析器(Parser)是兩個類,並且詞法和語法分析過程都有可能發生錯誤,所以須要分別在兩個分析器裏定義getErrorMessage函數,添加的方式很簡單,在antlr的語法定義.g文件裏,添加在@lexer::members和@parser::members代碼塊裏便可,例以下面是JavaScript版本的聲明方式:
1: @lexer::members {
2: _errors = [];
3: this.getErrorMessage = function(e, tokenNames)
4: {
5: var error = getErrorsHelper(e, null, tokenNames, this);
6:
7: if ( _errors != undefined && _errors != null ) {
8: _errors.push(error);
9: }
10:
11: return error;
12: }
13: }
14:
15: @parser::members {
16: _errors = [];
17: this.getErrorMessage = function(e, tokenNames)
18: {
19: var error = getErrorsHelper(e, this.input, tokenNames, this);
20:
21: if ( _errors != undefined && _errors != null ) {
22: _errors.push(error);
23: }
24:
25: return error;
26: }
27: }