这是我参与「第五届青训营」伴学笔记创作活动的第 6 天
lecture6 规则引擎设计与实现
课程之前,大家需要根据项目工程,来完成环境的搭建和Demo的运行
项目地址: github.com/qimengxingy…
相信大家已经完成了Go环境的搭建,项目工程依赖了hertz框架,如果在之前的课程中完成了项目环境搭建可以直接复用。
项目环境:
- go语言环境搭建
- 需要安装docker环境
- 安装docker-compose工具
项目clone到本地后,可以执行测试脚本来测试环境的可用性。如果有错误欢迎百度和Google解决
git clone https://github.com/qimengxingyuan/young_engine.git
chmod a+x ./setup.sh
./setup.sh
脚本执行成功,则环境可以支持项目的执行
-
认识规则引擎
传统:修改活动规则=》if...else...
-
编译原理概念
编译原理:介绍编译和执行的相关知识,实际上是翻译
- 抽象语法树
- 上下无关语法
词法分析
把源代码字符串转换为词法单元(Token)的这个过程。
确定的有限自动机 DFA | Deterministic Finite Automaton
确定的有限自动机就是一个状态机,它的状态数量是有限的。该状态机在任何一个状态,基于输入的字符,都能做一个确定的状态转换。
语法分析
抽象语法树
BNF是描述上下文无关理论的一种具体方法,通过BNF可以实现上下文无关文法的具体化、公式化、科学化,是实现代码解析的必要条件。
<expr> ::= <expr> + <term>
| <expr> - <term>
| <term><term> ::= <term> * <factor>
| <term> / <factor>
| <factor><factor> ::= ( <expr> )
| Num
复制代码
BNF本质上就是树形分解,分解成一棵抽象语法树
- 每个产生式就是一个子树,在写编译器时,每个子树对应一个解析函数。
- 叶子节点叫做 终结符,非叶子节点叫做 非终结符。
递归下降算法 Recursive Descent Parsing
基本思路就是按照语法规则去匹配 Token 串。比如说,变量声明语句的规则如下:
varDecl : types Id varInitializer? ';' ; //变量声明
varInitializer : '=' exp ; //变量初始化exp : add ; //表达式
add : add '+' mul | mul; //加法表达式
mul : mul '*' pri | pri; //乘法表达式
pri : IntLiteral | Id | '(' exp ')' ; //基础表达式复制代码
如果写成产生式格式,是下面这样:
varDecl -> types Id varInitializer ';'
varInitializer -> '=' exp
varInitializer -> ε
exp -> add
add -> add + mul
add -> mul
mul -> mul * pri
mul -> pri
pri -> IntLiteral
pri -> Id
pri -> ( exp )
复制代码
而基于这个规则做解析的算法如下:
匹配一个数据类型(types)
匹配一个标识符(Id),作为变量名称
匹配初始化部分(varInitializer),而这会导致下降一层,使用一个新的语法规则:
匹配一个等号
匹配一个表达式(在这个步骤会导致多层下降:exp->add->mul->pri->IntLiteral)
创建一个varInitializer对应的AST节点并返回
如果没有成功地匹配初始化部分,则回溯,匹配ε,也就是没有初始化部分。
匹配一个分号
创建一个varDecl对应的AST节点并返回
int a = 2
-
对于一个非终结符,要从左到右依次匹配其产生式中的每个项,包括非终结符和终结符。
-
在匹配产生式右边的非终结符时,要下降一层,继续匹配该非终结符的产生式。
-
如果一个语法规则有多个可选的产生式,那么只要有一个产生式匹配成功就行。如果一个产生式匹配不成功,那就回退回来,尝试另一个产生式。这种回退过程,叫做回溯(Backtracking)。
-
设计规则引擎
工程地址:
根据以前学的,实现一个web版本的规则引擎
自己的实现提交issue或pr到公共的仓库