# 项目结构
本文档介绍了您从GitHub拷贝下来的项目结构,方便您更好的了解每个模块的作用,以便进行二次开发
# 根模块
位于/src/main/java/io/github/mczzcs/下
对应JAVA包路径
io.github.mczzcs
该目录下主要有三个类
- CompileManager : 编译管理器,负责调取ConsoleModel类的各种参数状态并控制编译器编译脚本
- ConsoleModel : 命令行处理器,负责接受解析后的命令行参数,并进行对应输出或启动编译管理器
- Main : 程序入口点,OpenEX从这里启动,该类也存储了所有关键字与保留字
# 编译器 - 前端
位于io.github.mczzcs.compile包下
该目录下主要有5个类
# Compiler
代表一个OpenEX标准前端编译器,负责调控各个解析模块,将脚本解析成执行节点
位于io.github.mczzcs.compile.Compiler下
# 成员变量
- String filename; 存储编译的脚本名称
- ArrayList<ASTNode> bcs; 存储脚本编译后的执行节点
编译期间该列表为空 - ArrayList<String> libname;
include语句符号表,存储已经导入的库名,方便函数调用表达式解析 - ArrayList<String> value_names; 全局变量符号表,存储脚本中定义的全局变量
- ArrayList<String> array_names; 全局数组符号表,协助全局变量符号表参与解析变量属性
# ExprssionParsing
OpenEX的新一代混合型表达式解析器,其架构最初在OpenEXAST版本中开发出来,一直沿用至今
其内部采用中转后缀表达式解析方式,对非双目运算符解析能力较差,但可以解析布尔表达式与算术表达式混合型的表达式
# 成员方法
- transitSuffix ; 初步进行
Token串的中转后缀解析,返回一个解析后的Token串 - calculate ; 将解析后的
Token串进一步解析成执行节点
# LexicalAnalysus
OpenEX标准词法解析器,其架构最初在OpenEX:JavaEdition版本中开发出来,经不断升级一直沿用至今
其内部采用自动机方式逐个抽取源代码字符,并分割成
Token词素后组合成列表返回
# 成员方法
- getTokens ; 返回解析后的
Token列表 privatelex ; 返回单个字符解析后的Token词素
# 其他
- Token ; 代表一个词素
- TokenX ; 负责表达式中间缓存,与词法分析阶段无关
# 编译器 - 语法分析器
# Parser
位于io.github.mczzcs.compile.parser.Parser中
同样采用自动机方式逐个提取词素并拼接成语句,然后识别语句类型并传入对应子分析器进行详细解析
# SubParser
位于io.github.mczzcs.compile.parser.Parser.SubParser中
Parser类的子类,通常用于分割语句块,与Parser架构相差无几,但其更多用于函数体和语句体的解析
# 编译器 - 语法/语义混合分析器
位于io.github.mczzcs.compile.parser中
对应语句解析后将返回继承于ASTNode的运行时执行节点
所有混合分析器都实现
BaseParser接口的eval方法
| 类名 | 功能 | 备注 |
|---|---|---|
| BackParser | 对循环内的break语句进行解析 | - |
| BufParser | 表达式解析缓存 | 无解析功能 |
| ContinueParser | 对循环内continue语句进行解析 | - |
| ElseIfParser | 对elif语句的解析 | - |
| ExpParser | 对表达式进行解析 | 解析功能由ExpressionParser实现 |
| ForParser | 对for语句进行解析 | - |
| FunctionParser | 对function语句的定义部分进行解析 | - |
| FunctionXParser | 对function语句的函数体部分进行解析 | - |
| IfParser | 对if语句进行解析 | - |
| IncludeParser | 对include语句进行解析 | - |
| InvokeParser | 对函数调用表达式进行解析 | - |
| ReturnParser | 对return语句进行解析 | - |
| ValueParser | 对变量定义语句进行解析 | - |
| WhileParser | 对while语句进行解析 | - |
# O1优化引擎
位于io.github.mczzcs.compiler.OptimizationExecutor
OpenEX开启o1优化模式时候会对一些纯值表达式(不包含任何函数调用以及变量引用)直接在编译阶段进行求值,由该类进行求值计算
# O1优化模式
该功能在Pro v0.1.6版本加入,通过命令行参数-o1开启
OpenEX的O1优化模式会对您的脚本进行以下编译期优化:
- 对纯值表达式进行求值运算
- 删除循环末尾的
continue语句 - 删除空操作执行节点
注意
O1优化模式会消耗大量编译时间以提高运行时的效率,如果您是即时解释执行,尽量不要开启O1优化模式,该模式仅适用于用于长时间运行的场景或StamonVM编译模式
# 执行节点
位于io.github.mczzcs.exe.code中
OpenEX前端编译器的输出结果,该结构可以被OpenEX运行时识别并执行,或由StamonVM_IR编译器进一步交叉编译成StamonVM字节码
所有执行节点都实现
io.github.mczzcs.exe.code.ASTNode接口
# 操作数执行节点
该类型的执行节点都位于io.github.mczzcs.exe.code.opcode下
该类型的节点大部分是由表达式转换后的结果,其功能可以控制操作栈和实现运算
计算结果将压回操作栈,表达式求值功能以此实现
| 类名 | 功能 | 备注 |
|---|---|---|
| AddMovNode | 将栈顶两元素相加后的值赋值到第二个元素 | - |
| AddNode | 将栈顶两元素相加 | - |
| AndNode | 将栈顶两元素与比较 | - |
| BigEquNode | 将栈顶两元素进行大于等于比较 | - |
| BigNode | 将栈顶两元素进行大于比较 | - |
| DivMovNode | 将栈顶两元素相除后赋值到第二个元素 | - |
| DivNode | 将栈顶两元素相除 | - |
| DivXMovNode | 将栈顶两元素取余后赋值到第二个元素 | - |
| DivXNode | 将栈顶两元素取余 | - |
| EquNode | 将栈顶两元素比较 | - |
| LessEquNode | 将栈顶两元素小于等于比较 | - |
| LessNode | 将栈顶两元素小于比较 | - |
| MovNode | 将栈顶第一个元素的值赋值到第二个元素 | - |
| MulMovNode | 将栈顶两元素相乘后的值赋值到第二个元素 | - |
| MulNode | 将栈顶两元素相乘 | - |
| NotNode | 将栈顶元素取反 | - |
| OrNode | 将栈顶两元素或比较 | - |
| PushNode | 将指定元素压入操作栈 | - |
| SubMovNode | 将栈顶两元素相减后的值赋值到第二个元素 | - |
| SubNode | 将栈顶两元素相减 | - |
# 结构体执行节点
该类型的执行节点都位于io.github.mczzcs.exe.code.struct下
该类型的节点大部分是由语句转换后的结果,通常具有1个或多个子节点
| 类名 | 功能 | 备注 |
|---|---|---|
| GroupASTNode | 多个执行节点的集合 | 表达式或代码块解析后的结果 |
| InvokeASTNode | 调用指定函数 | - |
| LoadArrayNode | 向指定变量池开辟一块数组类型内存区域 | - |
| LoadValueNode | 向指定变量吃开辟一块变量类型内存区域 | - |
| MovVarNode | 重赋值指定变量 | - |
| NulASTNode | 空操作 | - |
| ReturnNode | 弹出调用栈栈顶栈帧,并将返回值压入上一级栈帧的操作栈 | - |
# 运行时 - 核心
# 执行引擎
位于io.github.mczzcs.exe.core.Executor
可以识别相应执行节点并执行对应操作
在旧版中其内部通常包含一个操作栈,但是新版中栈帧操作栈替代了该功能
# 运行库加载器
位于io.github.mczzcs.exe.core.LibraryLoader
加载位于io.github.mczzcs.exe.lib下的运行库或扩展库
详细运行库接口文档请前往 Runtime API
# 栈帧
其有三种类型
- LoaderStackFrame : 加载器栈帧,作为脚本根节点或线程
run起始 - RuntimeStackFrame : 代表运行库函数的调用栈栈帧
- StackFrame : 代表用户脚本所定义函数的调用栈栈帧
异常
当脚本代码发生运行时异常并没有对应的异常处理程序正确对其进行处理时,在输出异常类型等基本信息的同时,新版OpenEX还会打印调用栈结构,如下所示
CallStackStruct:
at <脚本文件名> loader.boot (Script Loader)
at <脚本文件名> 脚本调用ID.用户定义函数 (User Script)
...
at <脚本文件名> 扩展库调用ID,扩展库函数名 (Runtime Function)
2
3
4
5
实际调用栈结构可能会依照脚本代码结构有所差异,请以控制台实际打印信息为准
# 运行时 - 线程管理器
位于io.github.mczzcs.exe.thread
是OpenEX脚本多线程和异步执行功能的实现
# ThreadManager
线程管理,调度的实现. 也存储全部用户脚本定义的全局变量和函数
# ThreadTask
线程调度单元,表示OpenEX的一条线程,受ThreadManager管理