這篇文章分享如何用antlr解析odata filter條件表達式。html
我最先接觸antlr,是在剛開始工做後不久,那次須要用antlr實現一個功能:把gemfire的OQL(object query language)翻譯成SQL語句,以便進行數據庫操做。其實,簡單講,antlr就是一個很是方便的詞法分析和語法分析的類庫,基於這個類庫,能夠很容易的實現不少場景,好比計算器算術表達式的解析、各類編程語言的解析等。git
印象很深入的記得,大學編譯原理的課程裏面就有相似的兩個練習,一個是實現計算器算術表達式的解析,一個是實現C-語言(C語言的簡化版)的解析,當時確定是須要本身手動實現,不能借助這些類庫,那如何作的呢?一個很關鍵的點是狀態機,在真正開始實現功能以前,須要根據具體問題的需求畫一個狀態機(我的以爲和狀態圖有些相似,或者說是狀態圖的一種形式),用狀態機來描述哪些字符連一塊兒能夠構成哪一種token,基於這個狀態機就能夠很方便的實現詞法解析。其實,狀態機在不少其它地方也有用途,好比:訂單的狀態變化,其實就能夠用狀態機來定義。github
除了上面提到的場景,還有兩個咱們平時常常碰到的場景:json解析和html在線編輯器,它們均可以用antlr來實現。正則表達式
具體odata filter條件表達式的定義能夠參考odata官方文檔,這裏爲了描述問題方便,簡化基本規則以下:數據庫
最小的表達式符合模式 key operator valueexpress
表達式和表達式能夠用邏輯運算符鏈接成一個新的表達式 expression AND expression編程
根據上面的規則,下面列舉幾個例子:json
1.$filter=Name eq 'John' //查詢全部name等於John的人c#
2.$filter=Name eq 'John' AND age ge 30 //查詢全部name等於John而且年齡大於等於30歲的人編程語言
3.$filter=(firstName eq 'John' OR firstName eq 'Bill') AND lastName eq 'Smith' //查詢全部名爲John或Bill,姓爲Smith的人
那麼,如何解析上面定義的規則呢?
首先,有一種方案:利用關鍵字(好比eq, AND等)來split這個filter string,在比較簡單的狀況下也許這個方案可行,可是若是有表達式嵌套的狀況(上面第三個例子),直接split string就會有些顯得力不從心。
其實,咱們能夠看到odata filter條件表達式和計算器的算術表達式有些相似,它們都是很是典型的詞法分析和語法分析案例,因此一樣能夠採用antlr來解析。
若是你們之前沒有接觸過antlr,網上有不少關於它的資料,你們能夠自行網上搜索(包括antlr官網https://www.antlr.org/)。下面僅分享一些我使用antlr(antlr 4)解析odata filter條件表達式的經驗總結:
antlr的簡單使用流程:定義grammar->生成對應語言(好比c#)的詞法和語法分析代碼->實現本身的Visitor遍歷抽象語法樹AST(abstract syntax tree)。
詞法定義規則須大寫打頭,語法定義規則須小寫打頭。
fragment VALID_ID_CHAR : [\p{General_Category=Other_Letter}\p{General_Category=Lowercase_Letter}\p{General_Category=Uppercase_Letter}\p{General_Category=Titlecase_Letter}\p{General_Category=Modifier_Letter}\p{General_Category=Nonspacing_Mark}\p{General_Category=Connector_Punctuation}\p{General_Category=Decimal_Number}] ;
從antlr 4.5開始,c#的runtime換成了Antlr4.Runtime.Standard;以前的版本是用Sam Harwell提供的一個Runtime。參考https://github.com/antlr/antlr4/tree/master/runtime/CSharp。
Intellij的antlr的插件提供了實時preview的功能,很是方便調試;VS的插件則沒有這功能。
Antlr basics:
Unicode support: