1:整体处理被 ExpressRunner 托管,可以在这个类里开启一些运行时辅助功能,比如 是否使用Cache中的指令集、是否输出详细的执行指令信息 等等,。
首先调用这个方法 ExpressRunner#execute(Sting,IExpressContext):Object ,第一个参数是表达式,第二个是上下文。
2:然后调用 ExpressRunner#parseInstructionSet(String): InstructionSet,把表达式转为指令集。这个方法主要调用两个方法
ExpressParse#parse(String):ExpressNode
ExpressNode 属性有值有类型,还有左子节点集合和右子节点集合,可以理解为树的根节点。
先调用
ExpressParse#splitWords(String): Word[] 根据定义的 splitWord 进行解析把单词提取出来,分隔符号有:空格、换行、位操作、四则运算、Boolean运算符号、等于、注释符号等等,Word 有行号、列号、index、值属性。
再调用
ExpressParse#(transferWord2ExpressNode(String, Word[]): ExpressNode ,ExpressNode 是带有类型的返回值,这里会根据 Word 的值来判断是什么类型,比如 数字、字符串、boolean、关键字等。NodeTypeManager 中保存了很多关键字,像四则运算符就是关键字。根据 Word 的值和 类型构造 ExpressNode。
到这里还只是单词,还没有树的概念,下面会转为树。
QLPattern#findMatchStatementWithAddRootOptimizeStack(MatchParamsPack,QLPatternNode): QLMatchResult 这个递归方法,根据 pattern 和 nodetype 来进行解析成 QLMatchResultTree,再掉用 QLMatchResultTree#buildExpressNodeTree() 方法进行中序遍历,生成树。
3: 然后调用 ExpressRunner#createInstructionSet(ExpressNode, String): InstructionSet,根据 ExpressNode 中的 NodeType 找到 对应的 InstructionFactory 的实现类,比如 Block、Break、Cast、CallFunction、If、In 等等。
InstructionSet 是指令集的集合,不同的指令,有不同的 excute(RunEnvironment): void 实现方法,比如 GoTo、CloseNewArea、ClearDataStack、CallMacro、Operator。
4: 指令集合生成后,调用 InstructionSetRunner#executeOuter(ExpressRunner, InstructionSet, ExpressLoader,IExpressContext): Object ,调用 InstructionSet#excute(RunEnvironment, InstructionSetContext): CallResult ,调用 InstructionSet#executeInnerOriginInstruction(Runenvironment): void ,把结果生成到 RunEnvironment 中。最后调用 CallResult#getReturnValue(): Object 返回结果。
主要流程: 分隔表达式 -> 转为带有类型的词法 -> 解析词法生成可执行的树 -> 根据树中 nodetype 的pattern 生成 instruction -> 根据不同指令的类型去执行指令。
表达式示例: 10 * 10 + 1 + 2 * 3 + 5 * 2
模式匹配,即使用 定义好的表达式,去解析传入的字符串
比如这个表达式: “OP_LEVEL4:TYPE=OPERATOR,DEFINE=*|/|mod|%”,name 是 OP_LEVEL4,类型是 运算符,有五种定义,乘、除、mod、取余、和 instassof 。
定义的常量在 KeyWordDefine4Java.nodeTypedefines[] 中。
QLPatternNode.isTreeNode ,是否是根节点,上面的 * 乘法表达式 就是根节点。
QLPatternNode#splitChild() ,把子节点加入到 pattern 中,比如 * QLPatternNode 乘法运算符的 List children 子匹配中,存在 CONST_INTEGER 类型的 QLPatternNode。在递归的时候,会遍历乘法运算符的子 pattern,即整数匹配模式,使用整数匹配模式,匹配乘法前后的数值单词。
在 splitChild() 的时候,根据 nodeTypeDefines 同一个表达式的分隔符的定义,会确定 pattern 的模式。 同一级别的如 加减乘除 就是 OR 模式,而一些函数就是 AND 模式。
词法分析较为简单,根据传入的表达式内容,按照对应的分隔符进行分隔,然后匹配类型即可。
QLexpress 使用了 findMatchStatementWithAddRootOptimizeStack(MatchParamsPack, QLPatternNode, int point, int deep) 递归函数来进行树的构造。
看递归表达式,应该着眼于 每次递归变化后传递下去的 和 用来保存递归结果的部分。
递归表达式中的变量是 QLPatternNode,表达式中,根据 node 的三种 matchMode :DETAIL、AND、OR,进行处理。
OR:遍历 patter.children,寻找合适的词法解析格式。
AND: 也是遍历 pattern.children 但是最后有一些变量的处理和缓存处理
DETAIL:左括号、右括号、注释符、EOF 等使用
递归表达式较为复杂,最终函数的副作用,就是生成了一个树形结构 QLMatchResultTree。
然后在递归表达式的外层,即调用方,拿到树的根节点,即 ExpressNode。
遍历 ExpressNode 的左右子节点,设置其 parent 节点。
把 ExpressNode 转换为 InstructionSet。
createInstructionSetPrivate(InstructionSet, Stack, ExpressNode) ,根据 node 的 type,获取 不同子类 InstructionoFactory,调用 createInstrctionSet(ExpressRunner, InstructionSet) 方法。
跟节点的 ExpressNode 对应的 nodetypekind 是 block,即先调用 BolckInstructionFactory#createInstructionSet(ExpressRunner, InstructionSet),在 block 的 factory 方法中,会遍历所有 children 节点的 nodetypekind 对应的的 factory 方法 ,来创建 instructionSet。
node 获取 children 的顺序是先左边全部,再右边全部。
最终会 return 一个 instructionSet 对象,对象中有个属性是 Instruction[],用来存放 parse 的结果。
调用 InstructionSetRunner#execute(ExpressRunner, InstuctionSet, ExpressLoader, IExpressContext, List,…),先把参数解析封装到 context 中。
InstructionSetContext context = OperateDataCacheManager.fetchInstructionSetContext (
true,runner,aContext,loader,isSupportDynamicFieldName);
再获取 RunEnvironment,
RunEnvironment environmen = OperateDataCacheManager.fetRunEnvironment(set,
(InstructionSetContext) context, isTrace);
最终指令的执行,在 InstructionSet 对象中,
public void executeInnerOrigiInstruction(RunEnvironment environmen,List<String> errorList,Log aLog) throws Exception{
Instruction instruction =null;
while (environmen.programPoint < this.instructionList.length) {
QLExpressTimer.assertTimeOut();
instruction = this.instructionList[environmen.programPoint];
instruction.setLog(aLog);// 设置log
instruction.execute(environmen, errorList);
}
}
一个指令一个执行的执行。