这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
介绍
- 目标:设计一个规则引擎,支持特定的词法、运算符、数据类型和优先级,并且支持基于以上预定义语法的规则表达式.
- 应用场景: ①风控对抗 ②活动策略运营 ③数据分析和清洗
- 组成部分:数据输入 -> 规则理解 ->规则执行
设计过程
理解:
- 词法分析:将源代码字符串转换为词法单元(token)的过程
通过有限自动机来识别Token:是状态数量有限的一个状态机,在任何状态下,基于输入的字符,都能做一个确定的状态转换.
GO-FSM 提供了一种轻量级有限状态机
package main
import (
"fmt"
"github.com/looplab/fsm"
)
func main() {
myFsm:=fsm.NewFSM(
"closed", //初始化
fsm.Events{ //事件
{Name:"开",Src:[]string{"closed"},Dst:"open"},
{Name:"关",Src:[]string{"open"},Dst:"closed"},
},
fsm.Callbacks{},//回调
)
fmt.Println("初始化的当前状态为:",myFsm.Current())
err:=myFsm.Event("开")
if err!=nil {
fmt.Println(err)
return
}
fmt.Println("执行了“开”,当前状态为:",myFsm.Current())
err=myFsm.Event("关")
if err!=nil {
fmt.Println(err)
return
}
fmt.Println("执行了“关”,当前状态为:",myFsm.Current())
fmt.Println()
}
- 语法分析:在词法分析基础上识别表达式的语法结构
其实就是要识别操作符,以及支持递归的操作符的左操作数和右操作数.
type precedence struct {
validSymbols []Symbol // 当前优先级支持的运算符类型
nextPrecedence *precedence // 更高优先级
planner planner // 当前优先级的处理函数
}
执行:
- ①抽象语法树:表达式抽象语法结构的树状表示,对于一个表达式,抽象语法树一定是唯一确定的
如 price > 500 && (isNewUser || userLevel > 5) 变成一个二叉树.
- 执行:预先定义好每种操作符的执行逻辑, 对抽象语法树进行后续遍历即可
- 上下文无关语法:巴科斯范式(BNF)
- 产生式:一个表达式可以由另外一直类型的表达式或者符合推导产生
- ②递归下降算法:自顶向下构造语法树,不断对Token进行语法展开,过程中可能遇到递归情况
输入输出:
- ①类型检查:验证执行的结果是否为合适的数据类型,在抽象语法树中,通常会验证某节点的子节点的数据类型是否合法
- 类型综合:根据子表达式的类型构造出父表达式的类型
- 编译时检查 & 运行时检查
- ②参数注入:在规则执行过程中,使用输入的参数值来计算语法树中的标识符节点值的过程