⾃⼰写个Drools⽂件语法检查⼯具——栈的应⽤之编译器检测语法错误
⼀、背景
当前⾃⼰开发的 Android 项⽬是⼀个智能推荐系统,⽤到 drools 规则引擎,于我来说是⼀个新知识点,以前都没听说过的东东,不过⽤起来也不算太难,经过⼀段时间学习,基本掌握。关于 drools 规则引擎的内容,后⾯再整理JBoss 官⽹上⾯有详细的⽂档,⽹上资料也⽐较多。学习 drools 规则引擎的传送门:
这⾥主要是因为⾃⼰使⽤ Android Studio 在编写 drools ⽂件时,没有了智能提⽰,IDE 不对语法进⾏检查了,出现了两次多写)的错误。这就跟⽤记事本写东西程序⼀样,慌的不⾏,所以⾃⼰写⼀个简单的语法检查的脚本。对 drools ⽂件进⾏⼀个初步的判断。
⼆、Dr o o ls 规则引擎简单介绍
⼆、Dro
D rools 规则引擎的使⽤场景
对于某些企业级应⽤,经常会有⼤量的、错综复杂的业务规则配置,⽤程序语⾔来描述,就形如:if-else或者switch-case等。像这种不同的条件,做不同的处理就是⼀种规则。⽤通俗易懂的结构来表⽰:当 XX
X 的时候,做 XXX 的事。理论上这样的问题都可以⽤规则引擎来解决。但是我们也不是说为了使⽤规则引擎去使⽤它,我们视具体业务逻辑⽽定,⼀般来说,条件(规则)⽐较复杂,情况种类⽐较多,条件可能会经常变化等,这时候,选择规则引擎去解决问题是⽐较明智的。
譬如随着企业管理者的决策变化,某些业务规则也会随之发⽣更改。对于我们开发⼈员来说,我们不得不⼀直处理软件中的各种复杂问题,需要将所有数据进⾏关联,还要尽可能快地⼀次性处理更多的数据,甚⾄还需要以快速的⽅式更新相关机制。
D rools 规则引擎的优点
Drools 规则引擎实现了将业务决策从应⽤程序中分离出来。
优点:
1、简化系统架构,优化应⽤
2、⽅便系统的整合,它们是独⽴的,允许不同背景的⼈进⾏合作
3、减少编写“硬代码”业务规则的成本和风险,每个规则控制所需的最⼩信息量
4、它们很容易更新,提⾼系统的可维护性,减⼩维护成本
D rools的基本⼯作⼯程
我们需要传递进去数据,⽤于规则的检查,调⽤外部接⼝,同时还可能获取规则执⾏完毕之后得到的结果
F act对象:
指传递给drools脚本的对象,是⼀个普通的javabean,原来javaBean对象的引⽤,可以对该对象进⾏读写操作,并调⽤该对象的⽅法。当⼀个java bean插⼊到working Memory(内存存储)中,规则使⽤的是原有对象的引⽤,规则通过对fact对象的读写,实现对应⽤数据的读写,对其中的属性,需要提供get和set⽅法,规则中可以动态的前往working memory中插⼊删除新的fact对象
D r l⽂件内容:
  例⼦:
    hello.drl⽂件如下:
stword
rule "test001"
  when
    //这⾥如果为空,则表⽰eval(true)
  then
    System.out.println("hello word");
end
D r ools的基础语法:
包路径,引⽤,规则体 (其中包路径和规则体是必须的)
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的类。不建议有条件判断。
三、dro o ls ⽂件的形式
三、dr o
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.SellPrice()));
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.SellPrice()));
end
和 java ⽂件类似,先声明包名,然后导⼊相关的类。⼀个规则由:
rule "xxx"
when
xxx
then
xxx
end
这样的形式构成。其中单⾏注释以//开头,多⾏注释形如/* */。
四、Dr o o ls ⽂件语法初步检查
四、Dro
⽬的
检测 .drl ⽂件中( )、{ }、" "是否成对出现。如果出现错误,指出错误⾏数。
思路
利⽤栈的数据结构来进⾏检测。
⾸先我们需要给出⼀个空栈,然后把待检测的代码中的字符⼀⼀⼊栈,在⼊栈的过程中,如果字符是⼀
个开放符号a(也就是我们的左括号),则把它压⼊栈中,如果是⼀个封闭符号(右括号)b,则此时先判断⼀下栈是否为空,如果为空的话,则报错(也就是待检测的代码中的括号不⼀⼀对应),如果栈不为空,则⽐较b和栈顶元素a,如果该封闭字符b和a字符匹配(也就是他们的括号能够匹配),则弹出栈顶元素,如果不匹配,则报错。当吧所有待检测的代码全部迭代完后,此时如果栈不为空,则报错。
上⾯的思路中,我们还要将注释中的符号排除在外。
步骤
1、读取 drl ⽂件内容为字符串。
2、通过正则匹配,将://替换为其它不受影响的字符或者字符串,避免误判断。
3、通过正则匹配,将//单⾏注释替换为空格。正则表达式为//.*
4、通过正则匹配,将/*和*/替换为不受影响的字符,如#。(纯粹是为了计算出错⾏数)
5、借助栈的数据结构,进⾏判断。
py t h on 脚本实现
# 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))python 正则表达式 空格
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.size()-2);
...