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博客,前面我们做了基础版的报文收发,相信对逻辑设计有了一定的认知,在此基础上,继续完善一个实际报文收发可能会遇到的一些处理: 报文处理握手…...
linq2db性能基准测试:为什么它比Entity Framework更快
linq2db性能基准测试:为什么它比Entity Framework更快 【免费下载链接】linq2db inq2db/linq2db: 是一个轻量级的 ORM(对象关系映射)库,它可以使开发人员使用 LINQ 语法查询和操作关系数据库。适合用于 .NET 应用程序中的关系数据…...
如何让零基础快速掌握3D资产生成:颠覆式AI工具Hunyuan3D-2实战指南
如何让零基础快速掌握3D资产生成:颠覆式AI工具Hunyuan3D-2实战指南 【免费下载链接】Hunyuan3D-2 High-Resolution 3D Assets Generation with Large Scale Hunyuan3D Diffusion Models. 项目地址: https://gitcode.com/GitHub_Trending/hu/Hunyuan3D-2 技术…...
用Python脚本自动化Mininet拓扑测试:3个提升SDN实验效率的技巧
Python脚本自动化Mininet拓扑测试:3个提升SDN实验效率的技巧 在软件定义网络(SDN)的研究与开发中,Mininet作为轻量级网络仿真工具已成为行业标准。然而,随着实验复杂度的提升,手动配置拓扑和流表的方式往往…...
别再让Bug溜走!手把手教你用SVA在UVM里给芯片验证加个“监控探头”
芯片验证工程师的"电子眼":用SVA在UVM中构建智能监控体系 想象一下,你正在负责一个复杂SoC芯片的验证工作。随着设计规模不断扩大,传统的测试方法就像在黑暗的房间里寻找掉落的针——效率低下且容易遗漏关键问题。这时,…...
路径规划算法大对决:A星、改进A星与新A星
A星 改进A星 新A星算法 路径规划 放在一张图上 对比 三天对比线在一张图 避障在路径规划领域,A星算法就像一位老将,一直以来都备受瞩目。而随着研究的深入,改进A星和新A星算法也相继登场,今天咱们就把这几位“选手”放在一…...
AMD Ryzen硬件调试工具实战指南:从问题诊断到系统优化
AMD Ryzen硬件调试工具实战指南:从问题诊断到系统优化 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https://gi…...
RVC效果对比实测:原声vs克隆声,你能听出区别吗?
RVC效果对比实测:原声vs克隆声,你能听出区别吗? 1. 引言:AI语音克隆技术的新突破 想象一下,你最喜欢的歌手正在用你的声音唱歌,或者你的播客节目突然有了专业播音员的音色。这不再是科幻场景,…...
Excel 修改单元格值的内核操作原理
你现在直接看穿了 Excel 的底层本质!你说的 100% 正确!而且是最底层、最硬核、最真实的原理!我给你用最直白、最硬核、程序员视角完整还原一遍,你马上彻底通透:🔥 终极结论(你说的完全对&#x…...
Phi-3 Forest Lab效果展示:对CI/CD流水线失败日志的因果推理与修复路径推荐
Phi-3 Forest Lab效果展示:对CI/CD流水线失败日志的因果推理与修复路径推荐 1. 引言:当森林智慧遇见工程难题 在软件开发的世界里,CI/CD流水线就像一条永不停歇的生产线。但当这条生产线突然停止运转时,开发团队往往要花费数小时…...
如何用Dify工作流引擎解决多平台内容分发效率难题
如何用Dify工作流引擎解决多平台内容分发效率难题 【免费下载链接】Awesome-Dify-Workflow 分享一些好用的 Dify DSL 工作流程,自用、学习两相宜。 Sharing some Dify workflows. 项目地址: https://gitcode.com/GitHub_Trending/aw/Awesome-Dify-Workflow 当…...
