QLExpress代码解读,运行原理解析
简介: 本文针对上图的功能详细图,进行逐个的简单介绍:代码入口、代码的主要逻辑和算法。 调用代码实例 //本文以helloworld案例,开启了两个打印日志的参数,实际使用通常不建议打开。 boolean printParseLog = true;//语法分析日志开关 boolean printExecu…
本文针对上图的功能详细图,进行逐个的简单介绍:代码入口、代码的主要逻辑和算法。
调用代码实例
//本文以helloworld案例,开启了两个打印日志的参数,实际使用通常不建议打开。
boolean printParseLog = true;//语法分析日志开关
boolean printExecuteLog = true;//每一条指令执行开关
ExpressRunner runner = new ExpressRunner(false, printParseLog);
IExpressContext<String, Object> context = new DefaultContext<String, Object>();
String script = "sum=0;for(i=0;i<10;i=i+1){sum=sum+i;};return sum;";
Object result = runner.execute(script, context, null,true, printExecuteLog);
System.out.println(result);
//结果输出:45
一、自上而下debug代码
1、整体调用代码入口
// ExpressRunner.java
/*** 执行一段文本* @param expressString 程序文本* @param context 执行上下文* @param errorList 输出的错误信息List* @param isCache 是否使用Cache中的指令集* @param isTrace 是否输出详细的执行指令信息* @param aLog 输出的log* @return* @throws Exception*/
public Object execute(String expressString, IExpressContext<String,Object> context,List<String> errorList, boolean isCache, boolean isTrace, Log aLog)throws Exception {InstructionSet parseResult = null;if (isCache == true) {parseResult = expressInstructionSetCache.get(expressString);if (parseResult == null) {synchronized (expressInstructionSetCache) {parseResult = expressInstructionSetCache.get(expressString);if (parseResult == null) {parseResult = this.parseInstructionSet(expressString);expressInstructionSetCache.put(expressString,parseResult);}}}} else {parseResult = this.parseInstructionSet(expressString);}return InstructionSetRunner.executeOuter(this,parseResult,this.loader,context, errorList,isTrace,false,aLog,false);
}
(1)两个步骤
综上所述:ExpressRunner.execute()实质分成两个步骤
//(1)编译成指令集过程:string -> InstructionSet
parseResult = this.parseInstructionSet(expressString);//(2)指令集执行过程:InstructionSet + context ->Object
InstructionSetRunner.executeOuter(this,parseResult,this.loader,context, errorList,isTrace,false,aLog,false)
(2)指令集缓存
只要调用 ExpressRunner.execute() 的isCache的入参,
parseInstructionSet调用完的结果就会被缓存在ExpressRunner实例的内部对象里:
private Map expressInstructionSetCache
2、脚本编译过程:compile
//ExpressRunner.java
/*** 解析一段文本,生成指令集合* @param text* @return* @throws Exception*/public InstructionSet parseInstructionSet(String text){//(1)token分解Word[] words = WordSplit.parse(this.nodeTypeManager.splitWord,express);//(2)token解析List<ExpressNode> tempList = this.transferWord2ExpressNode(rootExpressPackage,words,selfDefineClass,true);//(3)匹配AST语法树QLMatchResult result = QLPattern.findMatchStatement(this.nodeTypeManager, this.nodeTypeManager.findNodeType("PROGRAM").getPatternNode(), tempList,0);result.getMatchs().get(0).buildExpressNodeTree();ExpressNode root =(ExpressNode)result.getMatchs().get(0).getRef();resetParent(root,null);//(4)生成指令集合InstructionSet result = createInstructionSet(root, "main");}
(0)输入文本:
sum=0;for(i=0;i<10;i=i+1){sum=sum+i;};return sum;
(1)token分解:
分解为Word[]:“sum”,”=“,”0“,”;“,“for”,“(”,“i”,…
(2)token解析:
Word[]转化为List《ExpressNode》:每一个word变得有意义:常量、变量、符号、分割符号
(3)匹配AST语法树:
根据KeyWordDefine4Java.java定义的推导文法匹配成一棵AST(抽象语法树)ExpressNode
(4)生成指令集合
1:LoadAttr:sum
2:LoadData 0
3:OP : = OPNUMBER[2]
4:openNewArea
5:clearDataStack
6:LoadAttr:i
7:LoadData 0
8:OP : = OPNUMBER[2]
9:clearDataStack
10:LoadAttr:i
11:LoadData 10
12:OP : < OPNUMBER[2]
13:GoToIf[false,isPop=true] +13
......
29:return [value]
3、脚本执行过程:execute
//ExpressRunner.java
/*** 批量执行指令集合,指令集间可以共享 变量和函数
*/public static Object execute(ExpressRunner runner,InstructionSet sets,ExpressLoader loader,IExpressContext<String,Object> aContext, List<String> errorList,boolean isTrace,boolean isCatchException,boolean isReturnLastData,Log aLog,boolean isSupportDynamicFieldName)throws Exception {//缓存池中创建context对象InstructionSetContext context = OperateDataCacheManager.fetchInstructionSetContext (true,runner,aContext,loader,isSupportDynamicFieldName);//缓存池中获取RunEnvironment对象RunEnvironment environmen = OperateDataCacheManager.fetRunEnvironment(set,(InstructionSetContext) context, isTrace);try {//执行每一条指令CallResult tempResult = set.excute(environmen, context, errorList,isReturnLastData, aLog);if (tempResult.isExit() == true) {result = tempResult.getReturnValue();}} catch (Exception e) {//...}return result;}
}
二、使用归纳总结
QLExpress属于弱类型脚本语言,一般不推荐声明局部变量的类型。语法编译阶段不会做类型校验,也不会做方法的参数校验,所以很灵活。
QLExpress的这套自定义的指令集属于解析执行,RunEnvironment中通过programPoint函数指针的跳转来实现每条指令的逐个计算,通过dataContainer作为栈来存储中间计算结果。
QLExpress定义的指令类型比较少,粒度比较粗,但是足够完成所有的语法功能。
QLExpress整个运算过程是通过threadLocal来保证线程安全的。
QLExpress的脚本编译过程比较耗时,但是是上下文无关的,所以同一个脚本运行缓存之后性能提升非常明显。
QLExpress指令计算运算过程中,基本不会new新对象,是通过缓存池技术来尽量减少资源的消耗。
QLExpress的宏,function,Operator,变量是非常开放的,名字可以为中文字符,也可以随意扩展,还可以通过脚本静态分析出包含哪些变量、函数,很方便的进行二次业务开发。
脚本调用classLoader资源的时候可以import,也可以使用类的全路径,构造函数、静态方法、对象方法、类的字段、函数的不定参数调用统统搞定。
本文来自
https://developer.aliyun.com/article/700198
相关文章:
QLExpress代码解读,运行原理解析
简介: 本文针对上图的功能详细图,进行逐个的简单介绍:代码入口、代码的主要逻辑和算法。 调用代码实例 //本文以helloworld案例,开启了两个打印日志的参数,实际使用通常不建议打开。 boolean printParseLog true;//语法分析日志开…...
M1 Mac创建虚拟环境遇到的问题
报错信息 PackagesNotFoundError: The following packages are not available from current channels: python3.7 Current channels: https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/osx-arm64 https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/noarch htt…...
flutter 与H5交互
主要是flutter内嵌H5页面,之后就是两者之间的交互 flutter:webview_flutter 4.2.2 H5: uniapp 1、flutter向H5传参 //在flutter 中的web页面,可在onPageFinished中向H5进行传参onPageFinished: (String url) async {WebViewCont…...

【Java 基础篇】Java类型通配符:解密泛型的神秘面纱
在Java中,类型通配符(Type Wildcard)是泛型的重要概念之一。它使得我们能够更加灵活地处理泛型类型,使代码更通用且可复用。本文将深入探讨Java类型通配符的用法、语法和最佳实践。 什么是类型通配符? 类型通配符是一…...
《极客时间:如何成为学习高手》【方法论】
本篇博客是学习过程中的笔记整理和个人思考。原文链接:https://time.geekbang.org/column/intro/100081501?tabcatalog 底层逻辑01|如何减少对学习的排斥和厌恶心理,使其变得相对愉悦?02|学会这 4 点,你也…...
如何处理ChatGPT在文本生成中的语法错误和不合理性?
ChatGPT是一种强大的自然语言处理模型,但它并不是完美的,有时会产生语法错误或不合理的文本。这些问题可能会影响模型生成的内容的质量和可信度。在处理ChatGPT中的语法错误和不合理性时,有许多方法和策略可以采用,以下是一些详细…...
GitHub常用命令
1. 将本文件夹初始化为一个本地git仓库 git init 2. 将github的远程克隆到本地 git clone XXX 3. 添加所有文件到暂存区 git add . 4. 删除工作区文件 git rm [file] 5. 提交 git commit -m "提交信息(比如:my first commit fileÿ…...

【Linux学习笔记】 - 常用指令学习及其验证(上)
前言:本文主要记录对Linux常用指令的使用验证。环境为阿里云服务器CentOS 7.9。关于环境如何搭建等问题,大家可到同平台等各大资源网进行搜索学习,本文不再赘述。 由于本人对Linux学习程度尚且较浅,本文仅介绍验证常用指令的常用…...

火山引擎边缘云助力智能科技赋予生活更多新意
当下,先进的科学技术使得我们的日常生活变得快捷、舒适。大到上百层智能大厦、高端公共场所、社会智能基础设施,小到智能家居监控、指纹密码锁等,在这个充满想象力的时代,科技以更加智能化的方式改变和守护我们的生活。 引入智能…...
【无标题】CTreeCtrl更改-/+展开按钮颜色
#pragma once #include <list>// CMyTreeCtrlclass CMyTreeCtrl : public CTreeCtrl {private:std::list<std::...

【深度学习】 Python 和 NumPy 系列教程(十九):Matplotlib详解:2、3d绘图类型(5)3D等高线图(3D Contour Plot)
目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图(3D Line Plot) 2. 3D散点图(3D Scatter Plot) 3. 3D条形图(3D Bar Plot) 4. 3D曲面图…...
Java ZGC 算法调优
ZGC 是一种专门的垃圾收集器,专注于管理大型堆并最大限度地减少 Java 应用程序中的暂停。它解决了内存密集型工作负载和一致的响应时间至关重要的场景中垃圾收集的挑战。利用并发处理能力和先进算法,ZGC 为优化 Java 应用程序的性能提供了有效的解决方案…...

【海思SS626 | 开发环境】编译整个SDK全过程以及问题汇总
目录 一、概述二、解压SDK,打补丁三、安装交叉编译工具✨3.1 安装 aarch64-mix410-linux.tgz✨3.2 安装 cc-riscv32-cfg11-musl-20220523-elf.tar.gz✨3.3 检查工具链版本,打印版本则表示安装成功 四、安装软件包✨4.1 安装软件包✨4.2 安装mtd-utils的依…...

83 # 静态服务中间件 koa-static 的使用以及实现
静态服务中间件:koa-static 中间件可以决定是否向下执行,如果自己可以处理,那么直接处理完毕结束,如果自己处理不了,next 方法会继续向下执行 新建 public 文件夹,里面添加 index.html、style.css 文件 …...

带讲解的自行车租赁系统,可做毕设/课设
适合人群: 马上毕业/需要毕设的同学 技术栈: 前后端分离 前端使用: Vue Element 后端使用: SpringBoot Mysql8.0 Mybatis 支付宝支付 功能截图: 分为管理员端和 普通用户端 和 维修人员端 阿里大佬亲讲 免费看地址: 见评论区...
mysql指令
1.删除表: drop table table_name; 2.查询表字段: select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAMEtable_name; 参考链接 3.切换数据库: use database_name 4.查看当前数据库所有表 show tables;...

【C语言】每日一题(半月斩)——day2
目录 一.选择题 1、以下程序段的输出结果是( ) 2、若有以下程序,则运行后的输出结果是( ) 3、如下函数的 f(1) 的值为( ) 4、下面3段程序代码的效果一样吗( ) 5、对于下面的说法,正确的是…...
电脑如何查看代理服务器IP?
许多人在使用互联网时可能会遇到需要使用代理服务器的情况。但是,你知道如何在电脑上查看代理服务器IP吗?本文将为您分享简单易懂的方法,帮助您轻松了解代理设置的秘密! 代理服务器在网络世界中担任着重要的角色,它可…...

【C++11】{}初始化、std::initializer_list、decltype、STL新增容器
文章目录 1. C11简介2. 统一的列表初始化2.1 {}初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype 4. nullptr5. 范围for循环6. 智能指针7. C11STL中的一些变化8. 演示代码 1. C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1…...

【FPGA项目】进阶版沙盘演练——报文收发(报文处理、CDC、CRC)
前言 书接上文【FPGA项目】沙盘演练——基础版报文收发_子墨祭的博客-CSDN博客,前面我们做了基础版的报文收发,相信对逻辑设计有了一定的认知,在此基础上,继续完善一个实际报文收发可能会遇到的一些处理: 报文处理握手…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...

【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...