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博客,前面我们做了基础版的报文收发,相信对逻辑设计有了一定的认知,在此基础上,继续完善一个实际报文收发可能会遇到的一些处理: 报文处理握手…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...