规则引擎--QLExpress:普通表达式的运行

news2024/12/28 5:20:49

目录

    • QLExpress
    • 普通表达式执行
      • 解析并转化为ExpressNode
      • 语法解析,得到如下的语法树
      • 根据 ExpressNode 树生成指令树
      • 执行指令树得到结果
        • InstructionConstData 的指令执行
        • InstructionOperator的指令执行
      • 最后得到结果
    • 再看一个in表达式设置参数的执行

QLExpress

github: https://github.com/alibaba/QLExpress

优点:

  1. 线程安全,引擎运算过程中的产生的临时变量都是threadlocal类型。
  2. 高效执行,比较耗时的脚本编译过程可以缓存在本地机器,运行时的临时变量创建采用了缓冲池的技术,和groovy性能相当。
  3. 弱类型脚本语言,和groovy,javascript语法类似,虽然比强类型脚本语言要慢一些,但是使业务的灵活度大大增强。
  4. 安全控制,可以通过设置相关运行参数,预防死循环、高危系统api调用等情况。
  5. 代码精简,依赖最小,250k的jar包适合所有java的运行环境,在android系统的低端pos机也得到广泛运用。
  6. 支持高精度计算
    ————————————————
    版权声明:本文为CSDN博主「包子丹」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_37590206/article/details/108344988

运行架构图:
在这里插入图片描述

普通表达式执行

@Test
public void testDemo() throws Exception {
    String express = "10 * 10 + 1 + 2 * 3 + 5 * 2";
    ExpressRunner runner = new ExpressRunner();
    Object r = runner.execute(express, null, null, false, false);
    Assert.assertTrue("表达式计算", r.toString().equalsIgnoreCase("117"));
    System.out.println("表达式计算:" + express + " = " + r);
}

debug:
在这里插入图片描述

解析并转化为ExpressNode

在这里插入图片描述
在这里插入图片描述

语法解析,得到如下的语法树

在这里插入图片描述

10 * 10 + 1 + 2 * 3 + 5 * 2转化为类似后缀表达式(逆波兰式): 10 10 * 1 + 2 3 * + 5 2 * +

1:   STAT_BLOCK:STAT_BLOCK                                                         	STAT_BLOCK
2:      STAT_SEMICOLON:STAT_SEMICOLON	STAT_SEMICOLON
3:         +:+	+
4:            +:+	+
5:               +:+	+
6:                  *:*	*
7:                     10:CONST_INTEGER	CONST
7:                     10:CONST_INTEGER	CONST
6:                  1:CONST_INTEGER	CONST
5:               *:*	*
6:                  2:CONST_INTEGER	CONST
6:                  3:CONST_INTEGER	CONST
4:            *:*	*
5:               5:CONST_INTEGER	CONST
5:               2:CONST_INTEGER	CONST

根据 ExpressNode 树生成指令树

在这里插入图片描述

即按照后缀表达式(逆波兰式): 10 10 * 1 + 2 3 * + 5 2 * + 执行

1:LoadData 10
2:LoadData 10
3:OP : * OPNUMBER[2]
4:LoadData 1
5:OP : + OPNUMBER[2]
6:LoadData 2
7:LoadData 3
8:OP : * OPNUMBER[2]
9:OP : + OPNUMBER[2]
10:LoadData 5
11:LoadData 2
12:OP : * OPNUMBER[2]
13:OP : + OPNUMBER[2]

执行指令树得到结果

private Object executeReentrant(InstructionSet sets, IExpressContext<String, Object> iExpressContext,
       List<String> errorList, boolean isTrace, boolean isCatchException) throws Exception {
       try {
           int reentrantCount = threadReentrantCount.get() + 1;
           threadReentrantCount.set(reentrantCount);

           return reentrantCount > 1 ?
               // 线程重入
               InstructionSetRunner.execute(this, sets, this.loader, iExpressContext, errorList, isTrace,
                   isCatchException, true, false) :
               InstructionSetRunner.executeOuter(this, sets, this.loader, iExpressContext, errorList, isTrace,
                   isCatchException, false);
       } finally {
           threadReentrantCount.set(threadReentrantCount.get() - 1);
       }
   }

在这里插入图片描述
得到结果,具体执行过程是

InstructionConstData 的指令执行

 @Override
    public void execute(RunEnvironment environment, List<String> errorList) throws Exception {
        environment.push(this.operateData);
        environment.programPointAddOne();
    }

com.ql.util.express.RunEnvironment#push

public void push(OperateData data) {
       this.point++;
       if (this.point >= this.dataContainer.length) {
           ensureCapacity(this.point + 1);
       }
       this.dataContainer[point] = data;
   }

添加操作数

InstructionOperator的指令执行

com.ql.util.express.instruction.detail.InstructionOperator#execute

在这里插入图片描述

按照特定指令,取数执行

 @Override
    public void execute(RunEnvironment environment, List<String> errorList) throws Exception {
        InstructionSetContext instructionSetContext = environment.getContext();
        ArraySwap parameters = environment.popArray(this.opDataNumber);
        try {
            OperateData result = this.operator.execute(instructionSetContext, parameters, errorList);
            environment.push(result);
            environment.programPointAddOne();
        } catch (QLException e) {
            throw new QLException(getExceptionPrefix(), e);
        } catch (Throwable t) {
            throw new QLBizException(getExceptionPrefix(), t);
        }
    }

最后得到结果

com.ql.util.express.InstructionSetRunner#execute
在这里插入图片描述

特别说明:代码执行过程中会有各种缓存,避免指令的重复生成,可提高运行效率

再看一个in表达式设置参数的执行

  @Test
    public void testOperatorIn() throws Exception {
        String express1 = "2 in (2, 3) ";
        String express2 = "2 in a";
        String express3 = "2 in b";

        ExpressRunner runner = new ExpressRunner(true, true);
        DefaultContext<String, Object> context = new DefaultContext<>();
        int[] a = {1, 2, 3};
        context.put("a", a);
        List<Integer> b = new ArrayList<>();
        b.add(2);
        b.add(3);

        context.put("b", b);
        System.out.println(runner.execute(express1, context, null, false, false));
        System.out.println(runner.execute(express2, context, null, false, false));
        System.out.println(runner.execute(express3, context, null, false, false));
    }

对于表达式"2 in b" 解析为的语法树

1:   STAT_BLOCK:STAT_BLOCK                                                         	STAT_BLOCK
2:      STAT_SEMICOLON:STAT_SEMICOLON	STAT_SEMICOLON
3:         in:in	in
4:            2:CONST_INTEGER	CONST
4:            b:ID	ID

执行指令:
在这里插入图片描述

在执行in操作符时候,在这里插入图片描述

获取b参数则调用com.ql.util.express.instruction.opdata.OperateDataAttr#getObjectInner, 从context中获取值:
在这里插入图片描述
最后正确执行in指令,得到结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/630546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【matlab】matlab算法封装成工具包提供给程序调用

说明&#xff1a; 1、非进程通讯协议&#xff0c;无需在电脑上安装完整版的matlab开发环境。 2、本项目以C#为案例&#xff0c;调用的语言不限&#xff0c;操作流程基本相同。 一、准备工作 1、安装MATLABWebAppServerSetup集成开发环境 2、安装Visual stdio 2017集成开发环…

Openharmony添加编译自己应用

介绍一下Openharmony如何在庞大的编译构建系统中&#xff0c;增添自己想编译的内容。不定期更新~&#x1f438; gn官方文档&#xff1a; https://gn.googlesource.com/gn//main/docs/quick_start.md https://gn.googlesource.com/gn//master/docs/reference.md openharmony官…

Salesforce退出市场后类似的CRM系统有哪些

Salesforce退出中国市场后&#xff0c;对很多使用Salesforce的国内企业来说是一个不小的打击。他们需要寻找与Salesforce功能相当、具有良好口碑的CRM客户管理系统来替代。本文就为大家推荐五款类似Salesforce的CRM系统。 1、Zoho CRM Zoho CRM是一款SaaS云端CRM系统&#xf…

005: vue中el-upload 组件添加token的方法

第005个 查看专栏目录: 按照VUE知识点 ------ 按照element UI知识点 echarts&#xff0c;openlayers&#xff0c;cesium&#xff0c;leaflet&#xff0c;mapbox&#xff0c;d3&#xff0c;canvas 免费交流社区 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏…

谈谈嵌入式开发中签名校验和加解密作用的理解

1、前言 本博文不是讲解可信加签和固件加密的具体原理&#xff0c;而是谈谈实际嵌入式开发中&#xff0c;可信加签和固件加密的应用场景&#xff0c;可以帮助从事嵌入式开发的人员快速理解加签和加密的作用。 2、嵌入式开发中可信加签和固件加密介绍 (1)各家公司都有自己的可信…

操作受限的线性表——栈

本文主要内容&#xff1a;本文主要讲解栈的基本概念、基本操作和栈的顺序、链式实现。 目录 栈一、栈的基本概念1、基本概念2、基本操作 二、栈的顺序存储结构1、顺序栈的实现2、顺序栈的基本运算1&#xff09;初始化2&#xff09;判栈空3&#xff09;进栈4&#xff09;出栈5&a…

【环境配置】C/C++第三方库管理工具vcpkg安装和使用

一&#xff0c;vcpkg简介 vcpkg是微软公司开发的一个开源C包管理工具&#xff0c;它可以很方便的帮助您在 Windows、 Linux 和 MacOS 上下载&#xff0c;编译和安装C 第三方库。它具有自动解决依赖关系的能力&#xff0c;并且支持多种目标架构和平台。提供了超过1500个C库的预…

【Ubuntu系统内核更新与卸载】

【Ubuntu系统内核更新与卸载】 1. 前言2. 内核安装2.1 系统更新2.2 官网下载 3. 内核卸载3.1 需求分析3.2 卸载方法 1. 前言 我们在搭建环境时常常遇到内核版本不匹配的问题&#xff0c;需要我们安装新的内核版本&#xff1b;有时又会遇到在安装软件时报错boot空间已满无法安装…

Python爬取影评并进行情感分析和数据可视化

Python爬取影评并进行情感分析和数据可视化 文章目录 Python爬取影评并进行情感分析和数据可视化一、引言二、使用requestsBeautifulSoup进行影评的爬取1、分析界面元素2、编写代码 三、情感分析1、数据预处理2、情感分析3、数据可视化 一、引言 前几天出了《航海王&#xff1…

N - Cthulhu

第三次题组 [Cloned] - Virtual Judge (vjudge.net) 【题目描述】 一个具有 n 个顶点和 m 条边的无向图。现在&#xff0c;世界上最好的头脑即将确定这张图是否可以被视为克苏鲁。 为了简单起见&#xff0c;让我们假设克苏鲁从空间里看起来就像一个附有触手的球形身体。从形式…

sqlserver存储过程中使用临时表的问题

2023年6月6日08:52:15 因为最近接触的his系统一些存储过程做数据统计&#xff0c;一个存储过程就要使用1-3个临时表&#xff0c;这些存储过程是零几年的写得&#xff0c;和我们这个时代的写的存储过程习惯不太一样&#xff0c;就好奇为什么要使用这么多的临时表 临时表的基本概…

结构型设计模式05-组合模式

&#x1f9d1;‍&#x1f4bb;作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 组合模式 1、组合模式介绍 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分…

Generator-Evaluator重排模型在淘宝流式场景的实践

除了相关性&#xff0c;复杂信息流推荐场景还需要兼顾多样的业务需求&#xff0c;包括打散&#xff08;多样性&#xff09;&#xff0c;流量调控&#xff0c;多展示形态/多路供给融合等。传统推荐系统采用pipeline的形式&#xff0c;分步处理上述需求&#xff0c;缺少统筹优化&…

【博客650】irate适用于绘制细粒度灵敏图,但警惕用于告警

irate适用于绘制细粒度灵敏图&#xff0c;但警惕用于告警 1、irate解析 作用&#xff1a; irate(v range-vector) 函数用于计算区间向量的增长率&#xff0c;但是其反应出的是瞬时增长率。 原理&#xff1a; irate 函数是通过区间向量中最后两个两本数据来计算区间向量的增长…

C++表达式模板教程:从原理到应用的全面解析

C表达式模板教程 1. C表达式模板的引入 (Introduction to C Expression Templates)1.1 表达式模板的定义和作用 (Definition and Role of Expression Templates)1.2 表达式模板的历史和发展 (History and Development of Expression Templates)1.3 表达式模板在现代C中的地位 (…

java springboot VUE 在线学习平台系统开发mysql数据库web结构java编程计算机网页源码maven项目前后端分离

一、源码特点 springboot VUE 在线学习平台系统是一套完善的完整信息管理类型系统 前后端分离&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架 &#xff08;MVC模式开发&#xff09;&#xff0c;系统具有…

005Mybatis返回值(ResultMap 一对多,多对多)

属性 id 应该总是指定一个或多个可以唯一标识结果的属性。 虽然&#xff0c;即使不指定这个属性&#xff0c;MyBatis 仍然可以工作&#xff0c;但是会产生严重的性能问题。 只需要指定可以唯一标识结果的最少属性。显然&#xff0c;你可以选择主键&#xff08;复合主键也可以…

DevOps系列文章之 远程部署的一种方案

远程部署的一种方案 sshpass 一个简单、轻量级命令行工具&#xff0c;提供非交互式密码验证 原理 ssh 直接使用 TTY 访问&#xff0c;以确保密码是用户键盘输入的。 sshpass 在专门的 tty 中运行 ssh&#xff0c;以误导 ssh 相信它是从用户接收到的密码使用 sshpass 是不安…

深入理解HashMap源码

文章目录 HashMap简介源码分析关键参数获取数组下标put方法resize扩容过程jdk1.7的扩容实现jdk1.8的扩容实现 get()方法remove()方法 总结 关于HashMap&#xff0c;一直都是一个非常热门的话题&#xff0c;只要你出去面试&#xff0c;一定少不了它&#xff01; 本文主要结合 JD…

English Learning - L3 作业打卡 Lesson5 Day35 2023.6.8 周四

English Learning - L3 作业打卡 Lesson5 Day35 2023.6.8 周四 引言&#x1f349;句1: Publishers know that some people are self-conscious about what they read on public transport and so they put out different versions of a cover.成分划分弱读连读爆破语调 &#x…