本身寫個 Drools 文件語法檢查工具——棧的應用之編譯器檢測語法錯誤

1、背景

當前本身開發的 Android 項目是一個智能推薦系統,用到 drools 規則引擎,於我來講是一個新知識點,之前都沒據說過的東東,不過用起來也不算太難,通過一段時間學習,基本掌握。關於 drools 規則引擎的內容,後面再整理JBoss 官網上面有詳細的文檔,網上資料也比較多。學習 drools 規則引擎的傳送門:html

Drools 官網首頁: https://www.drools.org/
Drools 官方文檔: https://docs.jboss.org/drools/release/7.12.0.Final/drools-docs/html_single/index.htmljava

這裏主要是由於本身使用 Android Studio 在編寫 drools 文件時,沒有了智能提示,IDE 不對語法進行檢查了,出現了兩次多寫 ) 的錯誤。這就跟用記事本寫東西程序同樣,慌的不行,因此本身寫一個簡單的語法檢查的腳本。對 drools 文件進行一個初步的判斷。python

2、Drools 規則引擎簡單介紹

Drools 規則引擎的使用場景

對於某些企業級應用,常常會有大量的、錯綜複雜的業務規則配置,用程序語言來描述,就形如:if-else 或者 switch-case等。像這種不一樣的條件,作不一樣的處理就是一種規則。用通俗易懂的結構來表示:當 XXX 的時候,作 XXX 的事。理論上這樣的問題均可以用規則引擎來解決。可是咱們也不是說爲了使用規則引擎去使用它,咱們視具體業務邏輯而定,通常來講,條件(規則)比較複雜,狀況種類比較多,條件可能會常常變化等,這時候,選擇規則引擎去解決問題是比較明智的。
譬如隨着企業管理者的決策變化,某些業務規則也會隨之發生更改。對於咱們開發人員來講,咱們不得不一直處理軟件中的各類複雜問題,須要將全部數據進行關聯,還要儘量快地一次性處理更多的數據,甚至還須要以快速的方式更新相關機制。正則表達式

Drools 規則引擎的優勢

Drools 規則引擎實現了將業務決策從應用程序中分離出來。
優勢:
一、簡化系統架構,優化應用
二、方便系統的整合,它們是獨立的,容許不一樣背景的人進行合做
三、減小編寫「硬代碼」業務規則的成本和風險,每一個規則控制所需的最小信息量
四、它們很容易更新,提升系統的可維護性,減少維護成本數據結構

Drools的基本工做工程

咱們須要傳遞進去數據,用於規則的檢查,調用外部接口,同時還可能獲取規則執行完畢以後獲得的結果架構

Fact對象:

指傳遞給drools腳本的對象,是一個普通的javabean,原來javaBean對象的引用,能夠對該對象進行讀寫操做,並調用該對象的方法。當一個java bean插入到working Memory(內存存儲)中,規則使用的是原有對象的引用,規則經過對fact對象的讀寫,實現對應用數據的讀寫,對其中的屬性,須要提供get和set方法,規則中能夠動態的前往working memory中插入刪除新的fact對象app

Drl文件內容:

  例子:
    hello.drl文件以下:ide

package rules.testword
rule "test001"
  when 
    //這裏若是爲空,則表示eval(true)
  then
    System.out.println("hello word");
end

Drools的基礎語法:

包路徑,引用,規則體 (其中包路徑和規則體是必須的)
package:
包路徑,該路徑是邏輯路徑(能夠隨便寫,可是不能不寫,最好和文件目錄同名,以(.)的方式隔開),規則文件中永遠是第一行。
rule:
規則體,以rule開頭,以end結尾,每一個文件能夠包含多個rule ,規則體分爲3個部分:LHS,RHS,屬性 三大部分。
LHS:
(Left Hand Side),條件部分,在一個規則當中「when」和「then」中間的部分就是LHS部分,在LHS當中,能夠包含0~N個條件,若是 LHS 爲空的話,那麼引擎會自動添加一個eval(true)的條件,因爲該條件老是返回true,因此LHS爲空的規則老是返回true。
RHS:
(Right Hand Side),在一個規則中「then」後面的部分就是RHS,只有在LHS的全部條件都知足的狀況下,RHS部分纔會執行。RHS部分是規則真正作事情的部分,知足條件觸發動做的操做部分,在RHS可使用LHS部分當中的定義的綁定變量名,設置的全局變量、或者是直接編寫的java代碼,可使用import的類。不建議有條件判斷。工具

3、drools 文件的形式

Drools是一款基於Java的開源規則引擎,因此 Drools 文件語法徹底兼容 java 語法,以 .drl 爲後綴。大體形式以下:學習

package droolsexample

// list any import classes here.
import com.sample.ItemCity;
import java.math.BigDecimal;

// declare any global variables here
dialect "java"

/*
  規則1
*/
rule "Pune Medicine Item"
   when
      item : ItemCity (purchaseCity == ItemCity.City.PUNE,
                       typeofItem == ItemCity.Type.MEDICINES)
   
   then
      BigDecimal tax = new BigDecimal(0.0);
      item.setLocalTax(tax.multiply(item.getSellPrice()));
end


/**
  規則2
*/
rule "Pune Groceries Item"
   
   when
      item : ItemCity(purchaseCity == ItemCity.City.PUNE,
                      typeofItem == ItemCity.Type.GROCERIES)
   
   then
      BigDecimal tax = new BigDecimal(2.0);
      item.setLocalTax(tax.multiply(item.getSellPrice()));
end

和 java 文件相似,先聲明包名,而後導入相關的類。一個規則由:

rule "xxx"
    when
        xxx
    then
        xxx
end

這樣的形式構成。其中單行註釋以 // 開頭,多行註釋形如 /* */

4、Drools 文件語法初步檢查

目的

檢測 .drl 文件中 ( ){ }" " 是否成對出現。若是出現錯誤,指出錯誤行數。

思路

利用 的數據結構來進行檢測。

首先咱們須要給出一個空棧,而後把待檢測的代碼中的字符一一入棧,在入棧的過程當中,若是字符是一個開放符號a(也就是咱們的左括號),則把它壓入棧中,若是是一個封閉符號(右括號)b,則此時先判斷一下棧是否爲空,若是爲空的話,則報錯(也就是待檢測的代碼中的括號不一一對應),若是棧不爲空,則比較b和棧頂元素a,若是該封閉字符b和a字符匹配(也就是他們的括號可以匹配),則彈出棧頂元素,若是不匹配,則報錯。當吧全部待檢測的代碼所有迭代完後,此時若是棧不爲空,則報錯。

上面的思路中,咱們還要將註釋中的符號排除在外。

步驟

一、讀取 drl 文件內容爲字符串。
二、經過正則匹配,將 :// 替換爲其它不受影響的字符或者字符串 ,避免誤判斷。
三、經過正則匹配,將 // 單行註釋替換爲空格。正則表達式爲 //.*
四、經過正則匹配,將 /**/ 替換爲不受影響的字符,如 #。(純粹是爲了計算出錯行數)
五、藉助棧的數據結構,進行判斷。

python 腳本實現

# coding=utf-8
import re


def remove_annotation(file_path):
    with open(file_path, "r", encoding="UTF-8") as f:
        text = f.read()
        re_uri = "://"
        text1 = re.sub(re_uri, "_____", text)
        re_single = "//.*"
        text2 = re.sub(re_single, " ", text1)
        re_multi1 = "/\*"
        text3 = re.sub(re_multi1, "#", text2)
        re_multi2 = "\*/"
        result = re.sub(re_multi2, "#", text3)
        return result


def check_syntax(text):
    try:
        clist = []
        row_num = 1
        for c in text:
            if c == '\n':
                row_num = row_num + 1
            if not clist or (clist[-2] != '\"' and clist[-2] != '#'):
                if c == '\"':
                    clist.append(c)
                    clist.append(row_num)
                if c == '#':
                    clist.append(c)
                    clist.append(row_num)
                if c == '(':
                    clist.append(c)
                    clist.append(row_num)
                if c == '{':
                    clist.append(c)
                    clist.append(row_num)
                if c == ')':
                    if clist[-2] == '(':
                        clist.pop()
                        clist.pop()
                    else:
                        print("多餘的 ) , 可能錯誤行數爲 " + str(row_num))
                        return -1
                if c == '}':
                    if clist[-2] == '{':
                        clist.pop()
                        clist.pop()
                    else:
                        print("多餘的 } , 可能錯誤行數爲 " + str(row_num))
                        return -1
            else:
                if c == '\"':
                    if clist[-2] == '\"':
                        clist.pop()
                        clist.pop()
                if c == '#':
                    if clist[-2] == '#':
                        clist.pop()
                        clist.pop()
        if clist:
            print("存在多餘的 ( 或者 { 或者 \" 可能的錯誤行數爲:" + str(clist[-1]))
            return -2
        else:
            print("語法檢查初步正確!")
            return 0
    except IndexError:
        print("存在多餘的 ) 或者 } 或者 \" 可能的錯誤行數爲: " + str(row_num))
        return -1
    except Exception as e:
        print(e)
        print("其它錯誤!")


drl_path = input("請輸入 drools 文件路徑,可拖拽:")
content = remove_annotation(drl_path)
check_syntax(content)

演示實例:

drools 文件就取用上面的例子,命名爲 test.drl ,如圖:

上面的腳本保存爲 check_drl.py 文件。

結果以下:

下面將故意將 drools 文件第 40 行增長一個 ) ,以下圖:

再次測試以下圖:

結語

本文簡單介紹了一下 Drools 規則引擎的使用場景以及 drool 文件的簡單語法檢測,主要是利用了棧數據結構後進先出的思想。原本是計劃用 java 來寫這個工具的,後來想了一下,仍是以爲 python 比較實在,有不少優點,列舉一二:
python 的列表 list 直接能夠代替 java 的 Stack<E>
python 結合正則表達式,處理字符串更方便;
python 獲取 list 倒數第二個元素,能夠直接使用 list[-2] ,java 可能須要 stack.get(stack.size()-2) ; ...

相關文章
相關標籤/搜索