规则引擎| 青训营笔记

99 阅读2分钟

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

介绍

  • 目标:设计一个规则引擎,支持特定的词法、运算符、数据类型和优先级,并且支持基于以上预定义语法的规则表达式.
  • 应用场景: ①风控对抗 ②活动策略运营 ③数据分析和清洗
  • 组成部分:数据输入 -> 规则理解 ->规则执行

设计过程

理解:

  • 词法分析:将源代码字符串转换为词法单元(token)的过程

通过有限自动机来识别Token:是状态数量有限的一个状态机,在任何状态下,基于输入的字符,都能做一个确定的状态转换.

image.png

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进行语法展开,过程中可能遇到递归情况

输入输出:

  • ①类型检查:验证执行的结果是否为合适的数据类型,在抽象语法树中,通常会验证某节点的子节点的数据类型是否合法
  • 类型综合:根据子表达式的类型构造出父表达式的类型
  • 编译时检查 & 运行时检查
  • ②参数注入:在规则执行过程中,使用输入的参数值来计算语法树中的标识符节点值的过程

参考文献