第五届字节跳动青训营Class6笔记 | 青训营笔记

211 阅读4分钟

这是我参与「第五届青训营 」笔记创作活动的第6天

本节课主要讲解了规则引擎的设计和实现

规则引擎的本质就是我们自己定义一套语法,然后去解析用这套语法写的表达式,然后根据解析的内容执行表达式。这个过程其实就是编译和执行的过程。

1.认识规则引擎

image.png

image.png

开发人员维护规则引擎使得业务人员可以直接进行业务逻辑的实现

组成部分

数据输入 -> 规则理解 -> 规则执行

2.编译原理的基本概念

image.png

词法分析

词法分析是把源代码字符串转换为词法单元(Token)的过程

识别Token:有限自动机

有限自动机是一个状态机,它的状态数量是有限的,在任何一个状态,基于输入的字符,都能做一个确定的状态转换。

graph TD
A(Start)
B(符号)
C(数字)
D(参数)
A --> B
A --> C
A --> D

image.png

语法分析

语法分析就是在词法分析的基础上,识别表达式的语法结构的过程 image.png

抽象语法树

表达式的语法结构可以用树来表示,其每个节点(子树)是一个语法单元,这个单元的构成规则就叫“语法”。每个节点还可以有下级节点。

image.png

上下文无关语法

语言无需考虑上下文就可以判断正确性,可以使用巴科斯范式(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)

image.png

根据每个表达式的优先级连线构造树

image.png

类型检查

类型综合:根据子表达式的类型构造出父表达式的类型:string+string->string

编译时检查/运行时检查:类型检查可以发生在表达式的编译阶段或者执行时的阶段。编译时检查需要提前声明参数类型,运行时检查则根据参数输入的值的类型,在执行的过程中进行类型检查。

语法树执行

预先定义好每种操作符的执行逻辑,对抽象语法树进行后序遍历执行

3.实现规则引擎

  • Token类:存放数据类型(Kind模板类),值(interface{}接口),位置

  • Scanner类:存放扫描的位置(指针),当前字符,规则表达式字符串(rune, rune是int32类型的别名,在go语言中可以和byte一样存放字符,因为英文字符只有一个字节而汉语字符在go语言中占用三个字节,使用rune可以对各种字符一视同仁),方法中包含对一个Token的处理方法,可以读取每一个表达式并进行递归判断。

  • Parser类:识别错误的Token序列,便于之后构造语法树的过程

  • Builder类:存放定义好的优先级,和一个parser类指针,对于token不断进行递归下降。

  • Node类:存放节点类型,值,左右节点,操作方法

总结

本节课程讲解了规则引擎的全流程和设计算法,通过递归下降算法来构造抽象语法树,并且以一个实战项目为例子,实现了一个简单的规则引擎,了解了构建过程和注意事项,加深了对规则引擎的理解。

代码引用

qimengxingyuan/young_engine: 简单的规则引擎 (github.com)