首先明确引入规则引擎的目的是, 从 中解放出来。规则引擎可依据不同项目进行选型,本次主要分享bsp中使用到的govaluate规则引擎。
其输入为规则表达式和k-v键值对条件对象,通过规则引擎执行表达式,得到表达式的结果。
Abstract Syntax Tree简称AST,中文叫做抽象语法树。
govaluate首先将表达式构建出一颗ast。举个例子:比如规则表达式为 1+foo+4*boo>0
构建树时的流程图如下:
parserToken后,我们可以得到一堆token:
检查小括号是否成对出现
check token之间是否符合预设规则,核心是函数getLexerStateForToken
check当前的token是否是上一个token的合法值,合法值是预设的,比如NUMERIC的合法值是后面这些:
优化token,主要是编译一下正则
构建ast、优化ast为avl tree、预计算。
planStages这个大步骤内部大概分成了planTokens、reorderStages、elideLiterals这三个小步骤
给定一个规划器,创建一个函数来评估运算符的特定优先级,并将其链接到其他递归解析更高优先级的函数。
它用func做不同运算符的优先级计算,原理是func接收struct作为参数,而参数中的next为这个函数连接的下一个优先级的func。
其中核心函数为planPrecedenceLevel
这个func优先级打印出来是这样的:
有了运算符优先级之后,对于具体的节点,会继续看节点类型,比如是func,accesser还是valueType,valueType的节点对于不同的详细类型也有不同策略,比如数字节点会构建一个Node,而小括号节点会直接parser下一个token来构建优先级更高的树。
如valueType构建Node在planValue里具体体现为:
对于不同的运算符,在这个函数链上会下沉构建出优先级比较高的节点,保证符合数学计算的规律。
planToken执行完后,会变成这样一颗树:
例子体现为
这里主要把ast重排序,让ast由普通tree变成avl tree(Adelson-Velsky Landis Tree 自平衡二叉查找树)
重排序的过程是把相同优先级的节点进行旋转,第一步是交换左右节点:
第二步是LL左旋:
这样就平衡了
这个步骤是看叶子节点是否为LITERAL(本例可以暂且理解为数字类型的值),遍历整个树中的所有运算符,省略两边都是LITERAL的运算符。比如这棵树:
在这个阶段,各个子节点会进行dfs(Depth First Search 深度优先搜索)预计算。
各符号对应的算子如下:
至此第一阶段的逻辑梳理完毕,即ast构建完成。
Evaluate的主要功能即把k-v键值对条件对象填入ast,进行计算,得到一个interface类型的结果。
govaluate所有数字类型都是被解析为float64进行计算的,这么玩写代码爽了,但是当你用1+2+9做表达式时,可能会得到一个类型为fload64的interface{}结果。
比如这段代码:
理论上结果应该含有转义符,实际上结果是: