扩展iQuery使其支持多种编程语言(二) – 兼编译器的语法分析简介
iQuery是一個開源的自動化測試框架項目,有興趣的朋友可以在這里下載:https://github.com/vowei/iQuery/downloads
源碼位置:https://github.com/vowei/iQuery
在上一篇文章中,簡單介紹了iQuery解析器的詞法分析部分,本文接著將語法分析部分解釋完畢,閱讀完本文后,應該可以將iQuery擴展到其他編程語言上。
下面是iQuery的完整語法(其實可以把它當作一個廣義的正則表達式對待):
https://github.com/vowei/iQuery/blob/master/iQuery.g
antlr支持EBNF語法,也就是說它支持可選和重復的元素,如:
上面的語法表示selectors由一到多個multi_selectors組成,因為在EBNF語法里,沒有類似正則表達式的“+”號操作符,所以使用“multi_selectors multi_selectors*”這種形式描述。
而:
1: selector 2: : selector_expression 3: ( 4: ('+' selector_expression) 5: | 6: ('~' selector_expression) 7: )? 8: ; .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }這個語法表示selector這個元素的組成部分,是selector_expression后面要么跟有“('+' selector_expression)”,要么跟有“('~' selector_expression)”,要么后面什么也不跟 -“?”號的作用。
在antlr里,語法定義的規則一般是,小寫字母組成的單詞是語法的組成部份,而標記(Token)都是由大寫字母組成,如:
1: selector_expression 2: : atom 3: | ':' indexop '(' INTEGER ')' 4: | ':' NOT '(' selectors ')' 5: | ':' HAS '(' selectors ')' 6: | ':' CONTAINS '(' QUOTED_STRING ')' 7: …. 8: ; .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }上面的語法里,大寫的字母例如“INTEGER”、“NOT”、“HAS”、“CONTAINS”和“QUOTED_STRING”都是標記(Token),這些標記都是從詞法分析器解析完輸入字符串之后產生的標記流中得到。而小寫字母組成的單詞例如“selector_expression”、“atom”、“indexop”和“selectors”都是語法的組成部份,這些元素的定義都可以在語法定義源文件中找到。
Antlr生成的語法解析器是一個LL(*)的編譯器,LL類型的編譯器是一個自頂向下進行語法分析的編譯器,這種編譯方式跟手寫編譯器進行語法解析的方式很像,因此相對于yacc等LR類型(自底向上)的編譯器更容易理解。
比如說,以“#ABC”這個iQuery查詢語句為例,antlr生成的解析器,也就是iQuery解釋器碰到這個字符串以后,它依照一個自頂向下的順序進行語法匹配:
1.??? 首先從“query”這個語法元素開始,由于“query”元素的兩個子元素只有第一個“selectors NEWLINE* EOF”成功匹配(這是因為第二個元素的開頭就是換行符,跟“#ABC”無法匹配),所以解析器再接著進入“selectors”的定義嘗試匹配。
2.??? “selectors”的定義里“multi_selectors”也僅僅是一個語法元素,里面沒有任何詞法標記(Token),因此解析器遞歸向下匹配語法元素,匹配順序如下:
query -> selectors -> multi_selectors -> selector -> selector_expression
3.??? 當匹配到selector_expression時,因為其可選的語法子句里有詞法標記,而且“# ELEMENT”可以完全匹配“#ABC”這個輸入字符串,這樣在selector_expression處就成功匹配了輸入字符串,并且“selector_expression”沒有更多的語法規則需要輸入字符串匹配,因此解析器退出“selector_expression”這個語法規則,發現上一層規則“selector”也沒有更多的語法規則需要匹配,遞歸上溯,直到“query”規則。
4.??? 在“query”規則里,匹配完“selector”子規則后,后一個是可選的換行符 - “NEWLINE*”,由于輸入字符串中沒有換行符,所以跳過這個規則,碰到最后一個規則“EOF”-表示字符串或者文件的結尾。因為在“selector_expression”里已經由“’#’ ELEMENT”匹配完整個字符串了,沒有更多的字符留下。到這里,語法解析器就認為執行了一次成功的匹配,而輸入字符串“#ABC”是一個合法的輸入。
比如說,針對“> :first [‘value’]”這個查詢,iQuery解析器依照自頂向下的方式進行匹配:
1.??? 首先匹配字符“>”,匹配順序是“query”-> “selectors” -> “multi_selectors”-> “'>’ selector”。
2.??? 匹配到“'>’ selector”這個規則時,因為“'>’”后面必須跟一個滿足“selector”規則的字符串。
3.??? 解析器繼續用“:first [‘value’]”試圖匹配“selector”這個規則,這時的匹配順序是“selector” -> “selector_expression”-> “’:’ FIRST”。
4.??? “’:’ FIRST”這個規則消化掉“:first”字符串,由于輸入的iQuery字符串還剩下“[‘value’]”,而“selector_expression”規則已經沒有多余的子規則了,解析器上溯,上溯的順序是:“selector_expression” -> “'>' selector”-> “multi_selectors”-> “selectors”。
5.??? 在“selectors”這個語法規則里,前面的匹配步驟只消化掉“multi_selectors multi_selectors*”里的第一個規則“multi_selectors”,還剩下第二個規則“multi_selectors*”沒有匹配,因此解析器使用輸入字符串剩下的字符匹配第二個規則。
6.??? 匹配的順序是:“multi_selectors” -> “selector” -> “multi_attributes” -> “'['”。
7.??? 在這次匹配過程中,由于剩下的字符串是“[‘value’]”(注意value周邊的單引號),沒有任何一個“multi_attributes”的子規則匹配這段字符串,而且也無法回溯,因此一個語法錯誤發生了,解析器會拋出一個語法錯誤的異常信息,這個異常信息有點晦澀,需要做二次處理才能讓iQuery使用者明白語法錯誤原因 – 語法錯誤的處理在后文會講到。
從上面關于語法匹配的描述可以看出,這個過程跟一個函數遞歸調用非常類似,實際上,對于語法定義文件中的每一個語法規則(例如“query”、“multi_selectors”等規則),antlr都會為其生成一個函數調用(例如函數query()、multi_selectors()等,而且antlr還提供了給生成的函數傳入參數,設置和獲取函數返回值的手段,參數的聲明語法跟指定語言的語法是一致的。
好了,語法方面的解釋就暫時寫到這里,下文講解Java版和JavaScript版的iQuery解析器的具體實現。
本文由知平軟件 施懿民編寫,請關注我們的微博。
轉載于:https://blog.51cto.com/4977661/980406
總結
以上是生活随笔為你收集整理的扩展iQuery使其支持多种编程语言(二) – 兼编译器的语法分析简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你的IT运维管理,是否只是“看起来很美”
- 下一篇: 微软雅黑的应用[补充中]