这是我参与「第五届青训营」笔记创作活动的第9天。
学习内容
今天学习规则引擎,设计并实现一个规则引擎——YoungEngine
设计目标
设计一个规则引擎,支持特定的词法、运算符、数据类型和优先级。并且支持基于以上预定义语法的规则表达式的编译和执行。
- 词法(合法Token)
- 数据类型
- 运算符
- 优先级
词法与语法
词法分析
- 参数:由字母数字下划线组成eg: _ab2、user_name
- 布尔值:true、false
- 字符串:“abcd”、 ‘abcd’、`abcd`
- 十进制float:123.5
- 一元运算符:+ -
- 二元运算符:+- * / % > < >= <= == !=
- 逻辑操作符:&& || !
- 括号:()
设计词法分析状态机
语法分析
优先级与语法树
优先级的表达
type precedence struct {
validKSymbols []eSymbol // 当前优先级的运算符类型
nextPrecedence *precedence // 更高优先级的
planner planner // 当前优先级的处理函数
}
语法树结构
- 一元运算符:左子树为空,右子树为右操作数
- 二元运算符:左子树为左操作数,右子树为右操作数
- 括号:左子树为空,右子树为内部表达式的AST
语法树执行
预先定义好每种操作符的执行逻辑。对抽象语法树进行后续遍历执行,即:
- 先执行左子树,得到左节点的值;
- 再执行右子树,得到有节点的值;
- 最后根据根节点的操作符执行得到根节点的值。
类型检查
- 检查时机:执行时检查
- 检查方法:在一个节点的左右子节点执行完成后,分别校验左右子节点的类型是否符合对应操作符的类型检查预设规则。
- ‘>’ 符号要求左右子节点的值都存在目为int或float。
- 符号要求左节点为空且右节点的值为bool。
YoungEngine项目实践
项目地址:https://github.com/qimengxingyuan/young_engine.git
项目结构
.
├── README.md
├── compiler.go
├── compiler_test.go
├── compiler
│ ├── lexical.go
│ ├── parser.go # 语法分析
│ ├── parser_test.go
│ ├── planner.go # 构建语法树
│ ├── scanner.go # 词法分析
│ └── scanner_test.go
├── executor
│ ├── ast.go # 抽象语法树定义
│ ├── operator.go # 语法树执行
│ ├── svg.go # 可视化打印语法树 - 辅助工具
│ ├── symbol.go # 符号定义
│ ├── type.go # 类型定义
│ └── type_checker.go # 类型检查
└── token
├── kind.go # token类型
├── kind_test.go
├── lexer.go # 词法定义
└── token.go # token定义