用antlr解析odata filter條件表達式

這篇文章分享如何用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編程

  • 表達式的先後能夠加括號以提升優先級 (expression OR 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)。

  • 詞法定義規則須大寫打頭,語法定義規則須小寫打頭。

  • 從antlr 4.7開始,提供了對全部unicode的支持。關於這個,舉一個實際的例子:因爲.NET裏面的正則表達式\w能夠match不少國家的字符(具體有哪些,see https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions#WordCharacter),若是咱們須要odata filter條件表達式裏面的key也支持\w能夠match的字符,怎麼作呢?那得益於antlr對Unicode的支持,能夠像下面這樣定義key:
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的插件則沒有這功能。

  • 關於odata filter條件表達式的示例grammar文件,能夠參考https://github.com/huazailmh/ODataFilterParser。

References

Antlr basics:

Unicode support:

相關文章
相關標籤/搜索