本身写个 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) ; ...

相关文章
相关标签/搜索