QLExpress代码解读,运行原理解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 本文针对上图的功能详细图,进行逐个的简单介绍:代码入口、代码的主要逻辑和算法。调用代码实例//本文以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,也可以使用类的全路径,构造函数、静态方法、对象方法、类的字段、函数的不定参数调用统统搞定。
相关文章
|
21天前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
69 13
|
2月前
|
运维 持续交付 云计算
深入解析云计算中的微服务架构:原理、优势与实践
深入解析云计算中的微服务架构:原理、优势与实践
76 1
|
16天前
|
自然语言处理 搜索推荐 数据安全/隐私保护
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
鸿蒙登录页面设计展示了 HarmonyOS 5.0(Next)的未来美学理念,结合科技与艺术,为用户带来视觉盛宴。该页面使用 ArkTS 开发,支持个性化定制和无缝智能设备连接。代码解析涵盖了声明式 UI、状态管理、事件处理及路由导航等关键概念,帮助开发者快速上手 HarmonyOS 应用开发。通过这段代码,开发者可以了解如何构建交互式界面并实现跨设备协同工作,推动智能生态的发展。
122 10
鸿蒙登录页面好看的样式设计-HarmonyOS应用开发实战与ArkTS代码解析【HarmonyOS 5.0(Next)】
|
7天前
|
存储 物联网 大数据
探索阿里云 Flink 物化表:原理、优势与应用场景全解析
阿里云Flink的物化表是流批一体化平台中的关键特性,支持低延迟实时更新、灵活查询性能、无缝流批处理和高容错性。它广泛应用于电商、物联网和金融等领域,助力企业高效处理实时数据,提升业务决策能力。实践案例表明,物化表显著提高了交易欺诈损失率的控制和信贷审批效率,推动企业在数字化转型中取得竞争优势。
46 14
|
16天前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
69 1
|
19天前
|
数据挖掘 vr&ar C++
让UE自动运行Python脚本:实现与实例解析
本文介绍如何配置Unreal Engine(UE)以自动运行Python脚本,提高开发效率。通过安装Python、配置UE环境及使用第三方插件,实现Python与UE的集成。结合蓝图和C++示例,展示自动化任务处理、关卡生成及数据分析等应用场景。
88 5
|
1月前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
32 1
|
2月前
|
机器学习/深度学习 存储 人工智能
强化学习与深度强化学习:深入解析与代码实现
本书《强化学习与深度强化学习:深入解析与代码实现》系统地介绍了强化学习的基本概念、经典算法及其在深度学习框架下的应用。从强化学习的基础理论出发,逐步深入到Q学习、SARSA等经典算法,再到DQN、Actor-Critic等深度强化学习方法,结合Python代码示例,帮助读者理解并实践这些先进的算法。书中还探讨了强化学习在无人驾驶、游戏AI等领域的应用及面临的挑战,为读者提供了丰富的理论知识和实战经验。
74 5
|
2月前
|
存储 供应链 算法
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
63 0
|
2月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
71 0

推荐镜像

更多