这是我参与「第五届青训营 」笔记创作活动的第6天
本节课主要讲解了规则引擎的设计和实现
规则引擎的本质就是我们自己定义一套语法,然后去解析用这套语法写的表达式,然后根据解析的内容执行表达式。这个过程其实就是编译和执行的过程。
1.认识规则引擎
开发人员维护规则引擎使得业务人员可以直接进行业务逻辑的实现
组成部分
数据输入 -> 规则理解 -> 规则执行
2.编译原理的基本概念
词法分析
词法分析是把源代码字符串转换为词法单元(Token)的过程
识别Token:有限自动机
有限自动机是一个状态机,它的状态数量是有限的,在任何一个状态,基于输入的字符,都能做一个确定的状态转换。
graph TD
A(Start)
B(符号)
C(数字)
D(参数)
A --> B
A --> C
A --> D
语法分析
语法分析就是在词法分析的基础上,识别表达式的语法结构的过程
抽象语法树
表达式的语法结构可以用树来表示,其每个节点(子树)是一个语法单元,这个单元的构成规则就叫“语法”。每个节点还可以有下级节点。
上下文无关语法
语言无需考虑上下文就可以判断正确性,可以使用巴科斯范式(BNF)来表达。
产生式:一个表达式可以由另外已知类型的表达式或符号推导产生
exp : add ;
add : add '+' mul | mul;
mul : mul '*' pri | pri;
pri : string | bool | number |identifer;
如基础表达式pri可以用string、bool、number、identifer等内置类型组成,乘法表达式mul可以由乘法表达式乘基础表达式或者基础表达式本身组成;解析这些表达式时递归解析。规定巴科斯范式的判断顺序可以规定运算符的优先级
递归下降算法
递归下降算法就是自顶向下构造语法树,递归得到可以理解的内容。
定义如下的巴科斯范式:
exp : log ;
log : log ('&&'|'||') cmp | cmp
cmp : cmp '>' add | add
add : add '+' mul | mul
mul : mul '*' pri | pri
pri : const | id | (exp)
price > 500 && ( isNewUser || userLevel > 5)
根据每个表达式的优先级连线构造树
类型检查
类型综合:根据子表达式的类型构造出父表达式的类型:string+string->string
编译时检查/运行时检查:类型检查可以发生在表达式的编译阶段或者执行时的阶段。编译时检查需要提前声明参数类型,运行时检查则根据参数输入的值的类型,在执行的过程中进行类型检查。
语法树执行
预先定义好每种操作符的执行逻辑,对抽象语法树进行后序遍历执行
3.实现规则引擎
-
Token类:存放数据类型(Kind模板类),值(interface{}接口),位置
-
Scanner类:存放扫描的位置(指针),当前字符,规则表达式字符串(rune, rune是int32类型的别名,在go语言中可以和byte一样存放字符,因为英文字符只有一个字节而汉语字符在go语言中占用三个字节,使用rune可以对各种字符一视同仁),方法中包含对一个Token的处理方法,可以读取每一个表达式并进行递归判断。
-
Parser类:识别错误的Token序列,便于之后构造语法树的过程
-
Builder类:存放定义好的优先级,和一个parser类指针,对于token不断进行递归下降。
-
Node类:存放节点类型,值,左右节点,操作方法
总结
本节课程讲解了规则引擎的全流程和设计算法,通过递归下降算法来构造抽象语法树,并且以一个实战项目为例子,实现了一个简单的规则引擎,了解了构建过程和注意事项,加深了对规则引擎的理解。