├── src ├── bin-include │ ├── file.st │ ├── string.st │ ├── native.st │ ├── std.st │ ├── stdio.st │ ├── stddef.st │ ├── system.st │ └── math.st ├── exception │ ├── codegen │ │ ├── bufferstream.json │ │ ├── sfn.json │ │ ├── consttabreader.json │ │ ├── tokenfilereader.json │ │ ├── astirreader.json │ │ ├── astir.json │ │ ├── objectmanager.json │ │ ├── astfilereader.json │ │ ├── astrunner.json │ │ ├── codegen.py │ │ └── compiler.json │ ├── BufferStreamException.cpp │ ├── SFNException.cpp │ ├── AstIrReaderException.cpp │ ├── ConstTabReaderException.cpp │ ├── TokenFileReaderException.cpp │ ├── AstIrException.cpp │ ├── ObjectManagerException.cpp │ ├── AstFileReaderException.cpp │ ├── AstRunnerException.cpp │ └── CompilerException.cpp ├── data_type │ ├── NullType.cpp │ ├── StringType.cpp │ ├── Variable.cpp │ ├── ClassType.cpp │ ├── ObjectType.cpp │ ├── MethodType.cpp │ ├── SequenceType.cpp │ ├── NumberType.cpp │ └── DataType.hpp ├── config │ ├── VmConfig.hpp │ ├── CompilerConfig.hpp │ └── StamonConfig.hpp ├── ast │ ├── SfnAst.cpp │ ├── Ast.hpp │ ├── LeafAst.cpp │ ├── CodeLogicAst.cpp │ └── ExprAst.cpp ├── action │ ├── ConstTabWriter.cpp │ ├── AstIrWriter.cpp │ ├── TokenFileWriter.cpp │ ├── AstIrReader.cpp │ ├── ConstTabReader.cpp │ ├── TokenFileReader.cpp │ └── AstFileWriter.cpp └── compiler │ ├── ebnf.txt │ ├── Compiler.hpp │ └── Token.cpp ├── .gitattributes ├── demos ├── demo.st ├── demo.stvc ├── demo1.stvc ├── demo2.stvc ├── demo2.st └── demo1.st ├── logo ├── logo.png ├── mascot_v2.2.10.jpg ├── mascot_v2.2.11.jpg └── mascot_v2.2.9.jpg ├── .gitignore ├── doc ├── 工作日志 │ ├── 20230727.md │ ├── 20250614.md │ ├── 20250412.md │ ├── 20230813.md │ ├── 20250716.md │ ├── 20240714.md │ ├── 20250404.md │ ├── 20240707.md │ ├── 20240715.md │ ├── 20250620.md │ ├── 20240730.md │ ├── 20240712.md │ ├── 20250406.md │ ├── 20250723.md │ ├── 20250318.md │ ├── 20240727.md │ ├── 20240908.md │ ├── 20240519.md │ ├── 20250729.md │ ├── 20250606.md │ ├── 20250302.md │ ├── 20240626.md │ ├── 20241029.md │ ├── 20241202.md │ ├── 20250328.md │ ├── 20240801.md │ ├── 20250322.md │ ├── 20250607.md │ ├── 20250722.md │ ├── 20241004.md │ ├── 20250308.md │ ├── 20250222.md │ ├── 20250303.md │ ├── 20250525.md │ ├── 20230916.md │ ├── 20240821.md │ ├── 20250228.md │ ├── 20240624.md │ ├── 20250423.md │ ├── 20250402.md │ ├── 20240205.md │ ├── 20230825.md │ ├── 20250127.md │ ├── 20240207.md │ ├── 20231202.md │ ├── 20240131.md │ ├── 20240616.md │ ├── 20250808.md │ └── 20250713.md ├── 移植接口开发指导.md ├── 词法单元文件编码文档.md ├── 语法分析器文档.md ├── 词法分析器文档.md ├── 编译器开发文档.md ├── 分步行动机制文档.md ├── 代码速览指南.md ├── 抽象语法树文件编码文档.md ├── 对象管理器文档.md ├── 发行版使用指南.md ├── AstRunner开发文档.md ├── SFN机制文档.md ├── 发行版编译指南.md ├── AST-IR编码文件文档.md ├── STVC-TAC二进制编码规范.md └── CITP设计文档.md ├── include ├── interface │ ├── IBasicIo.hpp │ ├── IStack.hpp │ ├── IFileWriter.hpp │ ├── IFileReader.hpp │ ├── IMemoryPool.hpp │ ├── IHashMap.hpp │ ├── IBasicPlatform.hpp │ ├── IEasySmartPtr.hpp │ ├── StamonLib.hpp │ ├── IArrayList.hpp │ └── Exception.hpp └── stdc_implemented │ ├── Stack.hpp │ ├── FileWriter.hpp │ ├── BasicIo.hpp │ ├── BasicPlatform.hpp │ ├── FileReader.hpp │ ├── EasySmartPtr.hpp │ ├── MemoryPool.hpp │ └── ArrayList.hpp ├── MOSPP ├── xmake.lua ├── README.md ├── Makefile ├── .github └── workflows │ └── makefile.yml ├── test └── test.cpp ├── CMakeLists.txt └── .clang-format /src/bin-include/file.st: -------------------------------------------------------------------------------- 1 | import stddef; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.st linguist-language=stamon 2 | -------------------------------------------------------------------------------- /demos/demo.st: -------------------------------------------------------------------------------- 1 | import std; 2 | 3 | puts("Hello world!\n"); -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/logo/logo.png -------------------------------------------------------------------------------- /demos/demo.stvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/demos/demo.stvc -------------------------------------------------------------------------------- /demos/demo1.stvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/demos/demo1.stvc -------------------------------------------------------------------------------- /demos/demo2.stvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/demos/demo2.stvc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | bin 3 | build 4 | CMakeFiles 5 | CMakeCache.txt 6 | .xmake 7 | .vscode -------------------------------------------------------------------------------- /logo/mascot_v2.2.10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/logo/mascot_v2.2.10.jpg -------------------------------------------------------------------------------- /logo/mascot_v2.2.11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/logo/mascot_v2.2.11.jpg -------------------------------------------------------------------------------- /logo/mascot_v2.2.9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CLimber-Rong/stamon/HEAD/logo/mascot_v2.2.9.jpg -------------------------------------------------------------------------------- /src/bin-include/string.st: -------------------------------------------------------------------------------- 1 | import stddef; 2 | 3 | func str(s) { 4 | return native("str", s); 5 | } -------------------------------------------------------------------------------- /doc/工作日志/20230727.md: -------------------------------------------------------------------------------- 1 | # 2023/07/27 工作日志 2 | 3 | 1. 完成了对AST节点的设计 4 | 2. 创建了基本的目录(后续可能会更改) 5 | 3. 接下来准备编写AST的C++代码实现 -------------------------------------------------------------------------------- /src/bin-include/native.st: -------------------------------------------------------------------------------- 1 | import stddef; 2 | 3 | func native(port, arg) { 4 | sfn port, arg; 5 | return arg; 6 | } -------------------------------------------------------------------------------- /doc/工作日志/20250614.md: -------------------------------------------------------------------------------- 1 | # 2025/06/14 工作日志 2 | 3 | 本次更新不会发布新版本。 4 | 5 | ### 整合了Lexer的代码 6 | 7 | 以往的getLineTok函数太臃肿了,我将不同的Token分析功能封装到了不同的函数。 -------------------------------------------------------------------------------- /src/bin-include/std.st: -------------------------------------------------------------------------------- 1 | //标准库汇总 2 | 3 | import stddef; 4 | import native; 5 | import math; 6 | import stdio; 7 | import system; 8 | import string; 9 | import file; -------------------------------------------------------------------------------- /doc/工作日志/20250412.md: -------------------------------------------------------------------------------- 1 | # 2025/04/12 工作日志 2 | 3 | 本次更新没有建立版本。 4 | 本次更新修复了一个问题 5 | 6 | ### 修复了一个问题 7 | 8 | 我未在``Parser.cpp``中引用``CompilerConfig.hpp``,这使得代码在Makefile中正常构建,但在CMake中报错。 -------------------------------------------------------------------------------- /doc/工作日志/20230813.md: -------------------------------------------------------------------------------- 1 | # 2023/08/13 工作日志 2 | 3 | 本次提交修复了一些漏洞,修复内容如下: 4 | 5 | 1. 我原本通过整合移除了Quark节点,但是并没有移除干净(文档里还保留了Quark节点的介绍,代码里也写到了Quark节点的基本定义)。现在Quark节点已经被我移除干净了 6 | 2. 我漏掉了NULL节点。现在我在文档中加入了NULL节点,也编写了NULL节点的代码。 -------------------------------------------------------------------------------- /doc/工作日志/20250716.md: -------------------------------------------------------------------------------- 1 | # 2025/07/16 工作日志 2 | 3 | 本次更新不会发布新版本。 4 | 5 | ### 整理了Lexer.cpp 6 | 7 | 将Token的定义部分单独提取出来,整理为``Token.cpp``。这样,对于一些只需要Token而不需要Lexer的代码来说,不需要引用整个``Lexer.cpp``,而是只需引用``Token.cpp``,提升了可读性,减少了代码的耦合。 -------------------------------------------------------------------------------- /doc/工作日志/20240714.md: -------------------------------------------------------------------------------- 1 | # 2024/07/14 工作日志 2 | 3 | 本次并没有提交新功能。 4 | 5 | ### 修复代码漏洞,增强代码安全性 6 | 7 | 修复了SFN,增强了``LineReader``的安全性 8 | 9 | ### 接下来要做的事 10 | 11 | 1. 支持文件处理库 12 | 2. 支持编译为平面字节码 13 | 3. 编写词法分析的保存功能 14 | 4. 编写AST的O1优化器 15 | 5. 完善标准库 16 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20250404.md: -------------------------------------------------------------------------------- 1 | # 2025/04/04 工作日志 2 | 3 | 本次更新的内部版本为``2.4.44``。 4 | 本次更新不会发布``2.4.44``。 5 | 6 | ### 修复了一些漏洞 7 | 8 | 修复了以下问题: 9 | * ``Parser.cpp``:在分析多层引用路径时会出错 10 | * ``Main.cpp``:在编译时额外指定引用路径会出错 11 | * ``ObjectManager.cpp``:误引用了``strie.h``和``stack.h`` -------------------------------------------------------------------------------- /doc/工作日志/20240707.md: -------------------------------------------------------------------------------- 1 | # 2024/07/07 工作日志 2 | 3 | 本次并没有提交新功能。 4 | 5 | ### 重构了标准库 6 | 7 | 将调用sfn的代码封装成了``native``函数,并且定义了File类的架构。 8 | 9 | ### 接下来要做的事 10 | 11 | 1. 支持文件处理库 12 | 2. 支持编译为平面字节码 13 | 3. 编写词法分析的保存功能 14 | 4. 编写AST的O1优化器 15 | 5. 完善标准库 16 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20240715.md: -------------------------------------------------------------------------------- 1 | # 2024/07/15 工作日志 2 | 3 | 本次并没有提交新功能。 4 | 5 | ### 撤回了部分2024/7/14的更改 6 | 7 | 修复了LineReader,解决了无法读取文件的问题。 8 | 9 | ### 接下来要做的事 10 | 11 | 1. 支持文件处理库 12 | 2. 支持编译为平面字节码 13 | 3. 编写词法分析的保存功能 14 | 4. 编写AST的O1优化器 15 | 5. 完善标准库 16 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20250620.md: -------------------------------------------------------------------------------- 1 | # 2025/06/20 工作日志 2 | 3 | 本次更新的内部版本为``2.4.48``。 4 | 本次更新不会发布新版本。 5 | 6 | ### 提升文件解码器安全性 7 | 8 | 将``AstIr.cpp``中的``ir::AstLeaf``迁移至``ast::AstLeaf``。 9 | 10 | 将``AstIr.cpp``和``AstFileReader.cpp``中有关平面节点转为树状结构的代码进行了安全性升级。对于一些异常情况(例如多余的结尾单元)进行判断。 -------------------------------------------------------------------------------- /doc/工作日志/20240730.md: -------------------------------------------------------------------------------- 1 | # 2024/07/30 工作日志 2 | 3 | 本次修复了程序的漏洞,正式发行了2.4.7。 4 | 5 | ### 修复了String漏洞 6 | 7 | 由于漏洞逻辑过于复杂,因此把String.hpp重写了。经测试后无问题。 8 | 9 | ### 接下来要做的事 10 | 11 | 1. 支持文件处理库 12 | 2. 支持编译为平面字节码 13 | 3. 编写词法分析的保存功能 14 | 4. 编写AST的O1优化器 15 | 5. 完善标准库 16 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20240712.md: -------------------------------------------------------------------------------- 1 | # 2024/07/12 工作日志 2 | 3 | 本次并没有提交新功能。 4 | 5 | ### 将SFN涉及到平台的函数封装到了依赖库 6 | 7 | ``SFN.cpp``将不再直接调用标准C库,而是调用``stmlib.hpp``中的封装后的接口。 8 | 9 | ### 接下来要做的事 10 | 11 | 1. 支持文件处理库 12 | 2. 支持编译为平面字节码 13 | 3. 编写词法分析的保存功能 14 | 4. 编写AST的O1优化器 15 | 5. 完善标准库 16 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20250406.md: -------------------------------------------------------------------------------- 1 | # 2025/04/06 工作日志 2 | 3 | 本次更新不会修改任何版本。 4 | 5 | ### 对报错信息进行调整 6 | 7 | 细化报错信息,让用户可以更具体的知道错误所在的位置以及修复方法。同时整理了一些遗漏的报错信息。 8 | 9 | ### 修改了命名规范,微调了代码 10 | 11 | 对于``Lexer.cpp``中对Token编号的命名进行了微调,``Compiler.cpp``也进行了微调。 12 | 13 | ### 提交新的文档 14 | 15 | 上传了``Stamon错误及警告信息修复指南.md``,该指南能给用户提供错误信息的解释和修复方向。 -------------------------------------------------------------------------------- /src/exception/codegen/bufferstream.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BufferStreamException.cpp", 3 | "error": [ 4 | { 5 | "type": "DataSizeError", 6 | "arg": [], 7 | "msg": ["the size of data is too small"], 8 | "doc": "数据在读取过程中过早读完,即数据过小,此错误一般发生在文件解码过程中。" 9 | } 10 | ], 11 | "warning": [] 12 | } 13 | -------------------------------------------------------------------------------- /src/bin-include/stdio.st: -------------------------------------------------------------------------------- 1 | //标准输入输出库 2 | 3 | import stddef; 4 | import math; 5 | import string; 6 | 7 | func puts(s) { 8 | native("print", s); 9 | } 10 | 11 | func print(n) { 12 | puts(str(n)); 13 | } 14 | 15 | func println(n) { 16 | puts(str(n)); 17 | puts("\n"); 18 | } 19 | 20 | func input() { 21 | return native("input", null); 22 | } -------------------------------------------------------------------------------- /doc/工作日志/20250723.md: -------------------------------------------------------------------------------- 1 | # 2025/07/23 工作日志 2 | 3 | 本次更新不修改版本。 4 | 本次更新对未来的路线做了调整。 5 | 6 | ### 重新调整分步行动机制 7 | 8 | 分步行动机制被调整后重新引入,现在,分步行动机制支持对一个文件的连续处理,但不支持对多个文件的联动处理。一句话,分步行动机制从树状的算法变成了线性的算法。 9 | 10 | 新的分步行动机制文档已更新。 11 | 12 | ### 引入IOStream.cpp 13 | 14 | 接下来,我会大幅度调整``src/action``中的源码。为了让文件编解码器既能在文件中读写又能在内存等其他更令人意想不到的地方读写,我封装了一层IOStream。目前,``IOStream.cpp``尚未完善,我会在后续的更新中进行完善或修改。 -------------------------------------------------------------------------------- /doc/工作日志/20250318.md: -------------------------------------------------------------------------------- 1 | # 2025/03/18 工作日志 2 | 3 | 本次提交没有发布新功能。 4 | 本次提交规范化了日志和文档。 5 | 6 | ### 规范了日志和文档 7 | 8 | 我规范化了工作日志的格式,并将工作日志中与技术无关的“后记”部分统一迁移至``工作日志后记集.md``。 9 | 我还删除了文档当中一些不合适的内容。 10 | 11 | ### 接下来要做的事 12 | 13 | 1. 将``TokenFileWriter``和``TokenFileReader``进行调试 14 | 2. 为重构虚拟机做准备工作 15 | 3. 实现分步行动机制 16 | 4. 开发Stamon的安装工具 17 | 5. 推进Stamon包管理器的开发 18 | 6. 推进斯塔蒙的开发 19 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /doc/工作日志/20240727.md: -------------------------------------------------------------------------------- 1 | # 2024/07/27 工作日志 2 | 3 | 本次将发布2.4.7发行版。 4 | 5 | 本次更新来源于[GusemFowage](https://github.com/GusemFowage),感谢! 6 | 7 | ### 更新了部分标准库 8 | 9 | 主要重构了``String.hpp``,解决了可能出现的内存泄露的情况。并且添加了部分新的功能。 10 | 新建了TypeDef.hpp,这是一个类型定义库,这让Stamon的跨平台性更强。 11 | 12 | ### 接下来要做的事 13 | 14 | 1. 支持文件处理库 15 | 2. 支持编译为平面字节码 16 | 3. 编写词法分析的保存功能 17 | 4. 编写AST的O1优化器 18 | 5. 完善标准库 19 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20240908.md: -------------------------------------------------------------------------------- 1 | # 2024/09/08 工作日志 2 | 3 | 本次修复了项目的许多漏洞,正式发行了2.4.25。 4 | 5 | ### 修复了大量漏洞 6 | 7 | 本次修复的漏洞有: 8 | 9 | * 修复了运行时报错不显示报错文件和行号的漏洞 10 | * 将String的初始化参数设为常量 11 | * 修改LICENSE中的年份和版权所有者 12 | * 修复了新建长度为负数的数列会内存泄漏的漏洞 13 | * 修复了0为除数无法报错的漏洞 14 | 15 | 16 | ### 接下来要做的事 17 | 18 | 1. 支持文件处理库 19 | 2. 支持编译为平面字节码 20 | 3. 编写词法分析的保存功能 21 | 4. 编写AST的O1优化器 22 | 5. 完善标准库 23 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /src/exception/codegen/sfn.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SFNException.cpp", 3 | "error": [ 4 | { 5 | "type": "SFNError", 6 | "arg": ["msg"], 7 | "msg": ["$msg"], 8 | "doc": "SFN产生的综合性错误。" 9 | } 10 | ], 11 | "warning": [ 12 | { 13 | "type": "SFNWarning", 14 | "arg": ["msg"], 15 | "msg": ["$msg"], 16 | "doc": "SFN产生的综合性警告。" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /doc/工作日志/20240519.md: -------------------------------------------------------------------------------- 1 | # 2024/05/19 工作日志 2 | 3 | 这是一个小更新。 4 | 5 | ### 修复了漏洞 6 | 7 | 重构了``LineReader``类,并且补充了``String``类的``substring``方法。 8 | 确保了Stamon能够在其他平台上运行。 9 | 10 | ### 更新了路径搜索功能 11 | 12 | 以前的环境变量所存储的路径末尾不能有分隔符,但是现在可以了。 13 | 14 | ### 封装了版本号 15 | 16 | 我将他们封装成了宏,他们是``STAMON_VER_X``,``STAMON_VER_Y``,``STAMON_VER_Z``。 17 | 这样以后更新版本号只需修改这些宏即可。 18 | 19 | ### 推送了发行版 20 | 21 | ~~当前版本2.2.9。~~ 22 | 截至22时09分,当前版本2.2.10。 -------------------------------------------------------------------------------- /doc/工作日志/20250729.md: -------------------------------------------------------------------------------- 1 | # 2025/07/29 工作日志 2 | 3 | 本次更新不修改版本。 4 | 5 | ### 处理过时内容 6 | 7 | 为了兼容以往代码而作的妥协设计已经被移除。被移除的设计包括但不限于: 8 | 9 | * 测试用例 10 | * ``Parser``等主体类的兼容用函数 11 | * 过时的文档 12 | 13 | ### 规范化代码 14 | 15 | 整理了一些不整齐的命名。我可能会在后续补充详细的编码规范。 16 | 17 | ### 重新整理文档 18 | 19 | 随着一些接口的变化,有些文档已经不再适用,我大幅度更新了文档当中的接口和对应的介绍。 20 | 21 | 除了普通的文档变化以外,还有如下显著变化: 22 | 23 | * ``编译发行版指南.md``改名为``发行版编译指南.md`` 24 | * 新增``SFN机制文档.md`` 25 | -------------------------------------------------------------------------------- /include/interface/IBasicIo.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IBasicIo.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 11:45 6 | Description: 基本IO接口 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "String.hpp" 12 | 13 | namespace stamon { 14 | 15 | void platform_exit(int code); //退出 16 | int platform_system(String cmd); //调用系统命令 17 | String platform_input(); //输入 18 | int platform_print(String s); //输出 19 | 20 | } -------------------------------------------------------------------------------- /src/data_type/NullType.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name:NullType.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 14/08/23 10:10 6 | Description: 空值数据类型定义 7 | */ 8 | 9 | #pragma once 10 | 11 | #include"DataType.hpp" 12 | 13 | namespace stamon::datatype { 14 | class NullType : public DataType { 15 | public: 16 | NullType() : DataType(NullTypeID) {} 17 | virtual ~NullType() = default; 18 | }; 19 | } //namespace stamon::datatype -------------------------------------------------------------------------------- /src/bin-include/stddef.st: -------------------------------------------------------------------------------- 1 | // 为了防止互相引用的文件出现“未命名变量”的情况 2 | // 我们借鉴了C++的前置定义 3 | // 利用def先定义函数名,接着进行具体声明 4 | 5 | //数学库定义 6 | def abs; 7 | def all; 8 | def range; 9 | def range_ab; 10 | def int; 11 | def len; 12 | 13 | //IO库定义 14 | def puts; 15 | def print; 16 | def println; 17 | def input; 18 | 19 | //平台库定义 20 | def Stamon; 21 | def System; 22 | 23 | //字符串库定义 24 | def str; 25 | 26 | //外部调用库定义 27 | def native; 28 | 29 | //文件处理库定义 30 | def File; 31 | def open; -------------------------------------------------------------------------------- /src/exception/codegen/consttabreader.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ConstTabReaderException.cpp", 3 | "error": [ 4 | { 5 | "type": "ConstantsError", 6 | "arg": [], 7 | "msg": ["unknown constants"], 8 | "doc": "该文件存在未知的常量,请检查编码文件或重新编译。" 9 | }, 10 | { 11 | "type": "FormatError", 12 | "arg": [], 13 | "msg": ["not stvc"], 14 | "doc": "常量表的格式错误,请检查编码文件或重新编译。" 15 | } 16 | ], 17 | "warning": [] 18 | } 19 | -------------------------------------------------------------------------------- /src/exception/codegen/tokenfilereader.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TokenFileReaderException.cpp", 3 | "error": [ 4 | { 5 | "type": "FormatError", 6 | "arg": [], 7 | "msg": ["non-standardized token-file"], 8 | "doc": "文件格式综合错误,请检查编码文件或重新编码。" 9 | }, 10 | { 11 | "type": "TokenError", 12 | "arg": [], 13 | "msg": ["unknown token"], 14 | "doc": "该文件出现未知的Token,请检查编码文件或重新编码。" 15 | } 16 | ], 17 | "warning": [] 18 | } 19 | -------------------------------------------------------------------------------- /src/exception/codegen/astirreader.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AstIrReaderException.cpp", 3 | "error": [ 4 | { 5 | "type": "FormatError", 6 | "arg": [], 7 | "msg": ["not stvc"], 8 | "doc": "该文件的魔数不是0xABDB,由此推断该文件不是AST-IR字节码,请检查编码文件或重新编译。" 9 | }, 10 | { 11 | "type": "NodeError", 12 | "arg": [], 13 | "msg": ["unknown ast-ir node"], 14 | "doc": "检测到未知的AST-IR单元,请检查编码文件或重新编译。" 15 | } 16 | ], 17 | "warning": [] 18 | } 19 | -------------------------------------------------------------------------------- /doc/工作日志/20250606.md: -------------------------------------------------------------------------------- 1 | # 2025/06/06 工作日志 2 | 3 | 本次更新发行``2.4.47``。 4 | 本次更新修复了新功能的漏洞。 5 | 6 | ### 修复了新功能的漏洞 7 | 8 | 主要修复了``AstFileReader.cpp``,``AstFileWriter.cpp``,``TokenFileReader.cpp``,``TokenFileWriter.cpp``。 9 | 这四个文件经过我的逐一测试已经没有大问题了。 10 | 11 | 在此分享一种有关文件编解码的测试方法:先将数据用writer写入,然后再用reader读取,将读取到的数据再用writer写入到新的文件。利用写入->读入->写入的方式,会生成两个文件,如果这两个文件经过对比一模一样,那么大致可以确定写入器和读入器的正确性了。 12 | 13 | ### 调整了部分代码 14 | 15 | 主要对一些数据结构和基本定义进行了修改。对于``Lexer.cpp``,将字符串转义功能进行了封装。我认为``Lexer.cpp``有待进一步封装。 -------------------------------------------------------------------------------- /src/exception/BufferStreamException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: BufferStreamException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::bufferstream { 14 | inline STMInfo DataSizeError(String position) { 15 | return STMInfo(String("bufferstream"), String("DataSizeError"), String("the size of data is too small"), position); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /doc/工作日志/20250302.md: -------------------------------------------------------------------------------- 1 | # 2025/03/02 工作日志 2 | 3 | 本次更新不改变内部版本。 4 | 本次更新整合了Makefile,删去了已经过时的预处理器代码。 5 | 6 | ### 整合了Makefile 7 | 8 | 将编译参数整合到一起,降低了耦合。 9 | 10 | ### 删去了已经过时的预处理代码 11 | 12 | 关于预处理器的工作日志,可以参阅``工作日志/20240207.md``和``工作日志/20240510.md``。 13 | 14 | ### 接下来要做的事 15 | 16 | 1. 将``TokenFileWriter``和``TokenFileReader``进行对接调试 17 | 2. 将``STVCReader.cpp``和``STVCWriter.cpp``迁移到``action``目录下,并将其改名为``AstIrReader.cpp``和``AstIrWriter.cpp`` 18 | 3. 为重构虚拟机做准备工作 19 | 4. 开发Stamon的安装工具 20 | 5. 推进Stamon包管理器的开发 21 | 6. 推进斯塔蒙的开发 22 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /doc/工作日志/20240626.md: -------------------------------------------------------------------------------- 1 | # 2024/06/26 工作日志 2 | 3 | 本次主要修复了Makefile,更新了SFN,并且编写了平面字节码的规范。 4 | 5 | ### 修复Makefile 6 | 7 | 考虑到用户可能在编译前不会提前创建``bin``文件夹,并且新建``bin/include``目录。故在Makefile里编写了相应的实现。 8 | 9 | ### 更新了SFN 10 | 11 | 本更新来源于[冻橘](https://github.com/MikanAffine)的提议,感谢! 12 | 13 | 本次更新将端口号从整型改为了字符串型。这样调用起来更直观,方便。在后续我也可能重构SFN。 14 | 15 | **注意:本次更新之后的虚拟机可能会和之前的版本所编译出的程序产生不兼容的问题。** 16 | 17 | ### 编写了平面字节码的规范 18 | 19 | 见``STVC-TAC规范.md``。 20 | 21 | ### 接下来要做的事 22 | 23 | 1. 支持编译为平面字节码 24 | 2. 编写词法分析的保存功能 25 | 3. 编写AST的O1优化器 26 | 4. 完善标准库 27 | 5. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20241029.md: -------------------------------------------------------------------------------- 1 | # 2024/10/29 工作日志 2 | 3 | 本次提交支持了自定义程序的语言环境,并且整理封装了一些复杂的代码逻辑,正式发行2.4.29。 4 | 5 | ### 支持自定义程序的语言环境 6 | 7 | 从此,Stamon可以通过指定语言环境,来输出中文及其他除英文以外的语言了。具体的参数使用方法见``发行版使用指南``。 8 | 9 | ### 封装了Stamon.hpp 10 | 11 | 在以前,读取STVC文件使用的是``STVCWriter``,而写入STVC文件是在``Stamon.hpp``当中直接实现的,这种现象带来的大量的代码冗余。 12 | 我将写入STVC文件的代码逻辑封装到了``STVCWriter.cpp``中,现在,源码逻辑更加对称和简洁了。 13 | 14 | 接下来,我将会进一步封装``Stamon.hpp``,使得Stamon的编译过程可以更加细化(甚至做到分布编译)。 15 | 16 | ### 新增了一些文档 17 | 18 | 首先是新增了``代码速览指南.md``,用于向开发者澄清此项目的一些误区,也提升了项目的教程性。 19 | 20 | 接着是一些平面字节码的规范,我将会开始慢慢推进平面字节码的工作进程。 -------------------------------------------------------------------------------- /doc/工作日志/20241202.md: -------------------------------------------------------------------------------- 1 | # 2024/12/02 工作日志 2 | 3 | 本次发布了2.4.31版本。 4 | 5 | ### 好久不见! 6 | 7 | 由于其他原因,外加学业繁忙,最近的更新以维护为主。 8 | 9 | 我预计在寒假开始编写一些新功能。 10 | 11 | ### 更新了大量文档 12 | 13 | 我正在为新功能编写一些规范文档。 14 | 15 | ### 新增了XMake系统 16 | 17 | 至此,Stamon已经支持了三种编译方法。 18 | 19 | XMake构建方法已经更新到``编译发行版指南``。 20 | 21 | ### 修复了一些漏洞 22 | 23 | 本次修复的问题有: 24 | 25 | * 输出警告信息后程序自动退出 26 | * 溢出的整数数值全部变为-1 27 | 28 | 目前,在版本号的存储上显现出一些“非致命”的问题,我想要在STVC-TAC标准中解决它。 29 | 30 | ### 接下来要做的事 31 | 32 | 1. 支持文件处理库 33 | 2. 支持编译为平面字节码 34 | 3. 编写词法分析的保存功能 35 | 4. 编写AST的O1优化器 36 | 5. 完善标准库 37 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /src/config/VmConfig.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: VmConfig.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 08/03/25 18:35 6 | Description: 此文件用于存储虚拟机的相关配置信息 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "StamonConfig.hpp" 12 | 13 | namespace stamon::vm::config { 14 | 15 | // 警告的重要程度 16 | int WarningLevel = stamon::config::StamonWarningSafeLevel_JustWarn; 17 | // 是否启用垃圾回收 18 | bool isGC = true; 19 | // 虚拟机对象占用的内存限制 20 | int MemLimit = 16 * 1024 * 1024; 21 | // 内存池的缓存大小 22 | int PoolCacheSize = 16 * 1024 * 1024; 23 | 24 | } // namespace stamon::vm::config -------------------------------------------------------------------------------- /doc/工作日志/20250328.md: -------------------------------------------------------------------------------- 1 | # 2025/03/28 工作日志 2 | 3 | 本次更新的内部版本为``2.4.42``。 4 | 本次更新并不会发行``2.4.42``。 5 | 6 | ### 整合了文档 7 | 8 | 我修正了一些文档的用词,让文档变得更加规范。 9 | 10 | ### 微调了Stamon语言库 11 | 12 | 经过再三思考,我认为System.version和System.typeof的命名是不够恰当的,因此我把这两个函数封装到了``Stamon``对象当中。 13 | 14 | ### 修复了一个致命漏洞 15 | 16 | 在``TypeCalculator.cpp``中,有一个表达式由于未妥善使用括号,导致优先级不符合预期,现已修复。 17 | 除此之外我也删去了一些调试时遗留的注释代码。 18 | 19 | ### 接下来要做的事 20 | 21 | 1. 将``TokenFileWriter``和``TokenFileReader``进行调试 22 | 2. 为重构虚拟机做准备工作 23 | 3. 实现分步行动机制 24 | 4. 开发Stamon的安装工具 25 | 5. 推进Stamon包管理器的开发 26 | 6. 推进斯塔蒙的开发 27 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /doc/工作日志/20240801.md: -------------------------------------------------------------------------------- 1 | # 2024/07/30 工作日志 2 | 3 | 本次修复了程序的漏洞,提升了程序安全性,正式发行了2.4.8。 4 | 5 | ### 修复了SFN的漏洞,提升了安全性 6 | 7 | 在执行以下Stamon代码时出现了内存泄漏: 8 | 9 | ```stamon 10 | import std; 11 | def a = {1,2,3}; 12 | print(a); 13 | print(a); 14 | ``` 15 | 16 | 问题出在``SFN.cpp``里的``DataType2String``函数,在调用该函数后,数列a里的内容被释放了,导致了一系列的事故,从而造成内存泄露。 17 | 18 | 本次更新还让SFN在被调用时,先检查端口是否有对应的功能,如果有,则继续调用;否则报错。 19 | 20 | ### 更新了Stamon标准库 21 | 22 | 避免了程序启动时自动调用SFN来获取版本号的情况。 23 | 24 | ### 接下来要做的事 25 | 26 | 1. 支持文件处理库 27 | 2. 支持编译为平面字节码 28 | 3. 编写词法分析的保存功能 29 | 4. 编写AST的O1优化器 30 | 5. 完善标准库 31 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20250322.md: -------------------------------------------------------------------------------- 1 | # 2025/03/22 工作日志 2 | 3 | 本次更新发行2.4.40。 4 | 本次更新调整了依赖库和SFN机制,修复了一个致命的漏洞。 5 | 6 | ### 调整了依赖库和SFN机制 7 | 8 | 将依赖库接口的``printf``和``scanf``改为实现起来更为简单的``print``和``input``。现在开发者不实现格式字符串,也能移植Stamon了。 9 | 除此之外,删除了早期测试用的,现在已经废弃的``printNum``SFN端口,并且将一些分支判断语句改为更简单明了的switch语句。 10 | 11 | ### 修复了一个漏洞 12 | 13 | Stamon在初始化一个继承类时会崩溃,经调查发现是因为传参时直接将``Variable*``指针转换成``ClassType*``指针。目前该漏洞以修复。 14 | 15 | ### 接下来要做的事 16 | 17 | 1. 将``TokenFileWriter``和``TokenFileReader``进行调试 18 | 2. 为重构虚拟机做准备工作 19 | 3. 实现分步行动机制 20 | 4. 开发Stamon的安装工具 21 | 5. 推进Stamon包管理器的开发 22 | 6. 推进斯塔蒙的开发 23 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /doc/工作日志/20250607.md: -------------------------------------------------------------------------------- 1 | # 2025/06/07 工作日志 2 | 3 | 本次更新不会修改版本。 4 | 5 | ### 修改了AstNode的getType()的底层运作方式 6 | 7 | 无论是``DataType``,还是``AstNode``,抑或是``Token``,都需要调用方法来获取他们的具体类型,以进行详细的辨认。 8 | 9 | 但是,用现在的眼光看,``AstNode``的getType()在设计时是欠缺更多思考的——利用虚函数重载的方式来实现。 10 | 11 | 这样做有两点问题: 12 | 1. 调用虚函数需要一定的开销 13 | 2. 在初始化语法树时需要一个一个类型判断过去并new出对应的节点,即使对应的节点没有任何特有的数据。即每个节点都需要特判 14 | 15 | 理想的解决方法应该是:在父类设立一个整数变量``id``,子类在创建时初始化这个id。在初始化语法树时,对于没有任何特有数据的节点,可以直接创建父类,然后修改id。 16 | 17 | 目前,我已经把这种理想的解决方法应用到了Ast中。 18 | 19 | ### 删除了AstLeftPostfix节点 20 | 21 | 用现在的眼光看,该节点是设计之初过度思考的产物。``AstLeftPostfix``和``AstPostfix``别无二致,因此我删去了他。 -------------------------------------------------------------------------------- /doc/移植接口开发指导.md: -------------------------------------------------------------------------------- 1 | # 移植接口开发指导 2 | 3 | 移植接口的定义位于``include/interface``目录中,本项目的接口定义设计参考了``C#``的接口类设计。因此文件名和类名的开头都会带上一个字母``I``,这是``Interface``的缩写。由于有些底层库不需要任何修改即可移植,因此也被归类到``include/interface``目录中 4 | 5 | Stamon的移植接口类设计采用一套自创的设计方法——**CITP**(**Curiously Iterating Template Pattern,奇异递推模板模式**)。详细的设计原理和思路指导见``CITP设计文档.md``。利用CITP强大的接口约束表达力,开发者在按要求实现对应接口后,可以非常轻松的将自己的实现对接到项目中。 6 | 7 | 移植接口的实现也会被保存到``include``目录下。 8 | 9 | 移植接口可以有多种实现方式,目前被收录的官方实现如下: 10 | 11 | |目录名(默认``include``目录下)|备注| 12 | |:-|:-| 13 | |``stdc_implemented``|标准C实现| 14 | 15 | 开发者在移植过程中,可以挑选并接入最佳的现成移植实现,也可以自己动手实现。 16 | 17 | 欢迎更多开发者贡献在不同平台上的移植接口的实现。 -------------------------------------------------------------------------------- /src/exception/SFNException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: SFNException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::sfn { 14 | inline STMInfo SFNError(String position, String msg) { 15 | return STMInfo(String("sfn"), String("SFNError"), msg, position); 16 | } 17 | 18 | inline STMInfo SFNWarning(String position, String msg) { 19 | return STMInfo(String("sfn"), String("SFNWarning"), msg, position); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/config/CompilerConfig.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: CompilerConfig.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 08/03/25 18:19 6 | Description: 此文件用于存储编译器的相关配置信息 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "StamonConfig.hpp" 12 | #include "String.hpp" 13 | 14 | namespace stamon::c::config { 15 | 16 | // 警告的重要程度 17 | int WarningLevel = stamon::config::StamonWarningSafeLevel_JustWarn; 18 | // 默认的目标文件名 19 | String DefaultObjectFileName = String("a.stvc"); 20 | // 是否支持import 21 | bool isSupportImport = true; 22 | // 是否剥削调试信息 23 | bool isStrip = false; 24 | 25 | } // namespace stamon::c::config -------------------------------------------------------------------------------- /src/data_type/StringType.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: StringType.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 14/08/23 10:10 6 | Description: 字符串类型的定义 7 | */ 8 | 9 | #pragma once 10 | 11 | #include"DataType.hpp" 12 | #include"String.hpp" 13 | 14 | namespace stamon::datatype { 15 | class StringType : public DataType { 16 | String val; 17 | public: 18 | StringType(const String& value) : DataType(StringTypeID) { 19 | val = value; 20 | } 21 | virtual String getVal() const { 22 | return val; 23 | } 24 | virtual ~StringType() = default; 25 | }; 26 | } //namespace stamon::datatype -------------------------------------------------------------------------------- /doc/工作日志/20250722.md: -------------------------------------------------------------------------------- 1 | # 2025/07/22 工作日志 2 | 3 | 本次更新的内部版本为2.4.50。 4 | 本次更新不发行新版本。 5 | 本次更新是非兼容的。 6 | 本次更新新增了stamon的语法,并对分步行动机制进行了调整。 7 | 8 | ### 对分步行动机制的调整 9 | 10 | 在进行了大量的,深度的思考之后,我认为分步行动机制本质上是在创造一个新的构建系统,难度不亚于重新从头创造一门编程语言。况且将一整个构建系统内嵌到一个编程语言工具链当中是不必要的,因而分步行动机制暂时被搁置。 11 | 12 | 我参考了``gcc``对构建系统的做法。决定引入链接器:将源码分开来编译,并用链接器合成为一份字节码。 13 | 14 | 这意味着我需要新增一个外部定义的语法 15 | 16 | ### 新增外部定义语法 17 | 18 | 语法规则为``"extern" identifier { "," identifier } ";"``。该语法和未赋值的``def``语法类似。 19 | 20 | 编译器会将其记录到作用域当中,但不生成任何赋值代码。用户需要在链接时将具有赋值代码的文件链接在一起。如果未链接,则会在虚拟机阶段报错。 21 | 22 | 事实上,这个设计还是不完美——外部标识符未赋值的情况可以在编译期发现,我可能会在后续进行补充。 23 | 24 | 该语法我将会同步到高亮插件和语法教程。 -------------------------------------------------------------------------------- /src/ast/SfnAst.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: SfnAst.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 01/08/23 19:55 6 | Description: SFN节点的基本定义 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "Ast.hpp" 12 | #include "String.hpp" 13 | 14 | namespace stamon::ast { 15 | class AstSFN : public AstNode { 16 | public: 17 | AstSFN() 18 | : AstNode(AstSFNType) { 19 | } 20 | AstSFN(AstIdentifier *port, AstIdentifier *result) 21 | : AstNode(AstSFNType) { 22 | children->add((AstNode *) port); 23 | children->add((AstNode *) result); 24 | } 25 | virtual int getType() { 26 | return AstSFNType; 27 | } 28 | }; 29 | } // namespace stamon::ast -------------------------------------------------------------------------------- /src/data_type/Variable.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: Variable.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 01/12/23 19:52 6 | Description: 变量类,Variable类存储着一个DataType*数据(也就是值) 7 | * 当需要给每个变量(包括数组元素等)赋值时,只需改变DataType*数据即可 8 | */ 9 | 10 | #pragma once 11 | 12 | #include"DataType.hpp" 13 | 14 | namespace stamon::datatype { 15 | class Variable { 16 | public: 17 | DataType* data; 18 | Variable() {} 19 | Variable(const Variable& right) { 20 | data = right.data; 21 | } 22 | Variable(DataType* dt) { 23 | data = dt; 24 | } 25 | virtual ~Variable() = default; 26 | }; 27 | } //namespace stamon::datatype -------------------------------------------------------------------------------- /src/exception/AstIrReaderException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: AstIrReaderException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::astirreader { 14 | inline STMInfo FormatError(String position) { 15 | return STMInfo(String("astirreader"), String("FormatError"), String("not stvc"), position); 16 | } 17 | 18 | inline STMInfo NodeError(String position) { 19 | return STMInfo(String("astirreader"), String("NodeError"), String("unknown ast-ir node"), position); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/bin-include/system.st: -------------------------------------------------------------------------------- 1 | //系统类 2 | 3 | import stddef; 4 | import math; 5 | 6 | Stamon = class { 7 | 8 | func typeof(self, v) { 9 | //给定对象,返回其类型 10 | return native("typeof", v); 11 | } 12 | 13 | func version(self) { 14 | //返回版本号 15 | return native("version", null); 16 | } 17 | 18 | }.new; 19 | 20 | System = class { 21 | 22 | func throw(self, s) { 23 | //抛出异常,同时让虚拟机退出 24 | return native("throw", s); 25 | } 26 | 27 | func system(self, s) { 28 | return native("system", s); 29 | } 30 | 31 | func exit(self, n) { 32 | native("exit", int(n)); 33 | } 34 | 35 | }.new; -------------------------------------------------------------------------------- /doc/工作日志/20241004.md: -------------------------------------------------------------------------------- 1 | # 2024/10/04 工作日志 2 | 3 | 本次修复了项目的一些漏洞,新增了警告系统,正式发行2.4.28。 4 | 5 | ### 修复了一些漏洞 6 | 7 | 本次修复的漏洞有: 8 | 9 | * 修复了浮点数在字节码中的存储错误 10 | * 防止整数大到溢出而引起的误会 11 | 12 | ### 新增警告系统 13 | 14 | 这是很多编程语言都应该具备的系统。 15 | 16 | 用户可以在``build``、``run``、``strip``指令当中,用参数指定警告程度。 17 | 18 | 警告程度分以下几种: 19 | 20 | * ``--IgnoreWarning``:忽略警告 21 | * ``--JustWarn``:只做警告 22 | * ``--FatalWarning``:致命警告(将警告也当作致命错误对待) 23 | 24 | 用户可以通过指定警告程度来决定代码的安全程度。 25 | 26 | ### 微调了Makefile 27 | 28 | 开发者可以使用``make zip_release``来进一步压缩可执行文件(相应的,可执行文件的运行速度也会变慢)。 29 | 30 | 31 | ### 接下来要做的事 32 | 33 | 1. 支持文件处理库 34 | 2. 支持编译为平面字节码 35 | 3. 编写词法分析的保存功能 36 | 4. 编写AST的O1优化器 37 | 5. 完善标准库 38 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /src/exception/ConstTabReaderException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ConstTabReaderException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::consttabreader { 14 | inline STMInfo ConstantsError(String position) { 15 | return STMInfo(String("consttabreader"), String("ConstantsError"), String("unknown constants"), position); 16 | } 17 | 18 | inline STMInfo FormatError(String position) { 19 | return STMInfo(String("consttabreader"), String("FormatError"), String("not stvc"), position); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /doc/工作日志/20250308.md: -------------------------------------------------------------------------------- 1 | # 2025/03/08 工作日志 2 | 3 | 本次没有提交新功能。 4 | 5 | ### 整理了项目异常信息的代码 6 | 7 | 将编译端和AstRunner的异常信息生成函数整理在了``src/error``目录下,并为每种异常信息写了注解。 8 | 此举意在整合冗余代码,方便后续编写有关异常信息解决方案的文档。 9 | 顺便修复了异常信息的语病。 10 | 11 | ### 整理了程序运行参数的默认值 12 | 13 | 将这些散落在代码各处的默认值整合到了``src/config``目录下,其中``src/config/StamonConfig.hpp``是这个目录的领导性文件,引用此文件即可引用该目录下的所有源码。 14 | 15 | ### 整理了Main.cpp的代码 16 | 17 | main函数应该是简洁的,因此我把参数解析的代码封装到了源码底部。 18 | 19 | ### 整理了代码 20 | 21 | 为了确保文档的时间顺序性,我将``20240510.md``和``20241029.md``稍作修改。 22 | 23 | ### 接下来要做的事 24 | 25 | 1. 将``TokenFileWriter``和``TokenFileReader``进行调试 26 | 2. 为重构虚拟机做准备工作 27 | 3. 实现分步行动机制 28 | 4. 开发Stamon的安装工具 29 | 5. 推进Stamon包管理器的开发 30 | 6. 推进斯塔蒙的开发 31 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /src/exception/TokenFileReaderException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: TokenFileReaderException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::tokenfilereader { 14 | inline STMInfo FormatError(String position) { 15 | return STMInfo(String("tokenfilereader"), String("FormatError"), String("non-standardized token-file"), position); 16 | } 17 | 18 | inline STMInfo TokenError(String position) { 19 | return STMInfo(String("tokenfilereader"), String("TokenError"), String("unknown token"), position); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/data_type/ClassType.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ClassType.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 14/08/23 11:54 6 | Description: 7 | * 在STVM中,类本身也是一个值,它对应的数据类型就是ClassType 8 | * 这里就写上了ClassType的定义 9 | */ 10 | 11 | #pragma once 12 | 13 | #include"DataType.hpp" 14 | #include"Ast.hpp" 15 | 16 | namespace stamon::datatype { 17 | class ClassType : public DataType { 18 | ast::AstAnonClass* val; 19 | public: 20 | ClassType(ast::AstAnonClass* value) : DataType(ClassTypeID) { 21 | val = value; 22 | } 23 | virtual ast::AstAnonClass* getVal() const { 24 | return val; 25 | } 26 | virtual ~ClassType() = default; 27 | }; 28 | } //namespace stamon::datatype -------------------------------------------------------------------------------- /doc/工作日志/20250222.md: -------------------------------------------------------------------------------- 1 | # 2025/02/22 工作日志 2 | 3 | 本次更新的内部版本为``2.4.37``。 4 | 本次更新并不会发行``2.4.37``。 5 | 6 | 本次提交修改了STVC-TAC的规范,修复了依赖库的内存泄漏问题。 7 | 8 | ### 修改了STVC-TAC的规范 9 | 10 | 在正式重构虚拟机之前,编写一篇好的规范非常重要。 11 | 今后这篇规范还会持续更新,直到我认为足够成熟为止。 12 | 13 | ### 重构了依赖库的C语言标准库实现 14 | 15 | 我将ArrayList的引用构造改写为了拷贝构造,使其不再出现内存泄漏。 16 | 与此同时,我也运用了引用计数法,用智能指针封装了``ByteMap``的``STRIE*``。又用``ByteMap``代替了``StringMap``和``NumberMap``的内部实现,减少了冗余代码,使其不再出现内存泄漏。 17 | 18 | ### 接下来要做的事 19 | 20 | 1. 将``TokenFileWriter``和``TokenFileReader``进行对接调试 21 | 2. 将``STVCReader.cpp``和``STVCWriter.cpp``迁移到``action``目录下,并将其改名为``AstIrReader.cpp``和``AstIrWriter.cpp`` 22 | 3. 为重构虚拟机做准备工作 23 | 4. 开发Stamon的安装工具 24 | 5. 推进Stamon包管理器的开发 25 | 6. 推进斯塔蒙的开发 26 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /MOSPP: -------------------------------------------------------------------------------- 1 | 开源项目保护宣言(Manifesto of Open Source Project Protection, MOSPP) 2 | 3 | 第一版,2024年9月 4 | https://CLimber-Rong.github.io/resource/mospp/mospp_zh-cn.txt 5 | 6 | 开源精神的根本主旨是推动学术研究更稳定的发展,而非仅供人盗用,批判和破坏。 7 | 本宣言仅作项目的基本说明,并不具有相当的法律约束力,我们呼吁更多人遵守此宣言,社会公众也能分发此宣言。 8 | 我们在此呼吁开发者: 9 | 10 | 11 | 1. 遵守相应的学术道德 12 | 开发者自然被赋予了开源许可证所规定的基本权利,但我们呼吁更多有学术道德的开发者尊重项目作者。这不是道德绑架,开发者有权忽略这种建议。 13 | 14 | 2. 开源项目作者为开发者提供更多自由权利,但这从来不代表作者有义务为开发者服务 15 | 开发者具有一定自由权利。相应的,作者也能自主决定自己想干什么,项目将会怎样发展。 16 | 17 | 3. 开源项目作者不必接受其他开发者提出的破坏性意见 18 | 破坏性包括但不限于:对项目作者未来规划的破坏性,对项目的基本主旨的破坏性,对社区良好环境的破坏性。 19 | 这与第二点类似,但不同的是:作者可以在无为开发者服务义务的情况下听取用户反馈意见,但破坏性意见是难以容忍的。 20 | 21 | 22 | 开源项目作者可以把此宣言附属在项目当中,以进行道德声明。同时作者也应根据许可证来检查宣言与许可证是否存在冲突。如有争议,按中文原版解释。 23 | 愿开源世界有美好的明天! -------------------------------------------------------------------------------- /doc/工作日志/20250303.md: -------------------------------------------------------------------------------- 1 | # 2025/03/03 工作日志 2 | 3 | 本次更新的内部版本为``2.4.38``。 4 | 本次更新并不会发行``2.4.38``。 5 | 本次更新迁移了部分文件,修复了一个不致命的错误。 6 | 7 | ### 迁移了AstIR的读入写入源码 8 | 9 | 将``STVCReader.cpp``改名为``AstIrReader.cpp``,并迁移至``src/action``目录下。 10 | 将``STVCWriter.cpp``改名为``AstIrWriter.cpp``,并迁移至``src/action``目录下。 11 | 12 | 相应文档对``STVCReader``和``STVCWriter``的称呼也被调整。 13 | 14 | 此举动旨在为后续的STVC-TAC读入写入源码留足命名的空间。 15 | 16 | ### 修复了一个不致命的错误 17 | 18 | 错误位于``Stamon.hpp``。 19 | 在初始化AST的头节点时,忘记初始化其文件名和对应行号,导致每次生成的字节码都不相同。 20 | 尽管头节点不会报错,不影响使用,但是我还是修复了他。 21 | 22 | ### 接下来要做的事 23 | 24 | 1. 将``TokenFileWriter``和``TokenFileReader``进行调试 25 | 2. 为重构虚拟机做准备工作 26 | 3. 实现分步行动机制 27 | 4. 开发Stamon的安装工具 28 | 5. 推进Stamon包管理器的开发 29 | 6. 推进斯塔蒙的开发 30 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /include/interface/IStack.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IStack.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 16:45 6 | Description: 栈接口 7 | */ 8 | 9 | #pragma once 10 | 11 | namespace stamon { 12 | 13 | template class IStack : public Impl { 14 | // 栈,拷贝传递 15 | public: 16 | void clear() { 17 | // 清空 18 | Impl::clear(); 19 | } 20 | bool empty() { 21 | // 是否为空 22 | return Impl::empty(); 23 | } 24 | int size() { 25 | // 元素个数 26 | return Impl::size(); 27 | } 28 | T *peek() { 29 | // 获取栈顶元素 30 | return Impl::peek(); 31 | } 32 | void push(T *data) { 33 | // 入栈 34 | Impl::push(data); 35 | } 36 | T *pop() { 37 | // 出栈 38 | return Impl::pop(); 39 | } 40 | }; 41 | 42 | } // namespace stamon -------------------------------------------------------------------------------- /include/interface/IFileWriter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IFileWriter.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 16:19 6 | Description: 文件写入类接口 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BasicPlatform.hpp" 12 | #include "Exception.hpp" 13 | #include "StamonLib.hpp" 14 | #include "String.hpp" 15 | 16 | namespace stamon { 17 | 18 | template class IFileWriter : public Impl { 19 | //文件写入器,引用传递 20 | public: 21 | IFileWriter(STMException *e, String filename) 22 | : Impl(e, move(filename)) { 23 | // 打开名为filename的文件 24 | } 25 | void write(byte b) { 26 | // 写入一个字节 27 | return Impl::write(move(b)); 28 | } 29 | void close() { 30 | // 关闭文件 31 | return Impl::close(); 32 | } 33 | }; 34 | 35 | } // namespace stamon -------------------------------------------------------------------------------- /doc/词法单元文件编码文档.md: -------------------------------------------------------------------------------- 1 | # 词法单元文件编码文档 2 | 3 | ### 前言 4 | 5 | 词法单元文件里存储着一系列词法单元,一些应用程序可以将自己的语言解析成Stamon的词法单元格式,保存在文件,剩下的语法分析等流程交给Stamon的编译器即可。 6 | 7 | ### 格式 8 | 9 | 词法单元文件的编码格式相对简单。 10 | 11 | 一个词法单元文件结构由以下几种规则构成: 12 | 13 | * 每个词法单元文件的开头必须要有两字节的二进制码:0xABDC,此魔数用于区分于其他文件 14 | * 词法单元文件中的数值存储采用大端存储(以Python的大端存储为标准) 15 | * 词法单元标识是用于区分词法单元的数字标记,以``src/compiler/Lexer.cpp``当中的``TOKEN_TYPE``枚举为准,特殊的,``TokenEOL(换行)``被规定为-1 16 | * 每个词法单元由一字节的**词法单元标识**和若干字节的**词法单元数据**组成 17 | * **一些词法单元只有词法单元标识,不含有词法单元数据**(即一些词法单元只占一字节) 18 | 19 | 我们来特别看看拥有词法单元数据的词法单元(以及词法单元数据的构成): 20 | 21 | * 标识符单元(TokenIden):其词法单元由**四字节的标识符长度**和**字节长度为标识符长度的标识符内容**组成。 22 | * 整数单元(TokenInt):其词法单元由**四字节的整数数值**组成。 23 | * 双精度浮点数单元(TokenDouble):其词法单元由**八字节的浮点数值**组成 24 | * 字符串单元(TokenString):其词法单元由**四字节的字符长度**和**字节长度为字符串长度的字符串内容**组成。 -------------------------------------------------------------------------------- /src/bin-include/math.st: -------------------------------------------------------------------------------- 1 | import stddef; 2 | import native; 3 | 4 | func abs(n) { 5 | if n<0: return -n; 6 | return n; 7 | } 8 | 9 | func all(list) { 10 | for i in list { 11 | if !i { 12 | return false; 13 | } 14 | } 15 | return true; 16 | } 17 | 18 | func range(n) { 19 | def list = [n]; 20 | def i = 0; 21 | while i=x { 9 | j-=1; 10 | } 11 | if i
6 | 本次提交的测试是STVM自创建以来的第一次测试。 7 |

8 | 本次测试的代码位于src/test目录当中的stvm_test.cpp,为了编译stvm_test.cpp,我在根目录下编写了Makefile。 9 |

10 | 11 | 12 | 其中,Makefile的用法是: 13 | 1. make clean:删除可执行文件 14 | 2. make build:编译可执行文件 15 | 3. make zip:压缩可执行文件 16 | 4. make run:编译并运行可执行文件 17 | 18 |

19 | 目前,Makefile是临时的,并且仅能在Windows系统下使用,如果想要在其他系统编译,可以更改Makefile。 20 |

21 | 编译测试程序之前,需要确保拥有以下工具(冒号后面是我的工具版本,可以参考): 22 | 23 | 1. make:GNU Make 3.82.90 24 | 2. gcc:gcc (x86_64-posix-seh-rev1, Built by MinGW-Builds project) 13.1.0 25 | 3. strip(可选):GNU strip (GNU Binutils) 2.39 26 | 4. upx(可选):upx 4.0.2 27 | 28 |
29 | upx和strip仅用于压缩可执行文件,如果不需要压缩,可以不需要这两个工具。 30 |

31 | 32 | ### 接下来要做的事: 33 | 34 | 35 | 1. 完成STVM的内部数据结构 -------------------------------------------------------------------------------- /src/exception/codegen/astir.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AstIrException.cpp", 3 | "error": [ 4 | { 5 | "type": "RootNodeError", 6 | "arg": [], 7 | "msg": ["expect ast-ir root node"], 8 | "doc": "未在解码过程中发现根节点,请检查编码文件或重新编译。" 9 | }, 10 | { 11 | "type": "RedundantEndNodeError", 12 | "arg": [], 13 | "msg": ["redundant ast-ir end node"], 14 | "doc": "解码过程中出现多余的结束单元,请检查编码文件或重新编译。" 15 | }, 16 | { 17 | "type": "RedundantRootNodeError", 18 | "arg": [], 19 | "msg": ["redundant ast-ir end node"], 20 | "doc": "解码过程中出现多个根节点,请检查编码文件或重新编译。" 21 | }, 22 | { 23 | "type": "EndNodeError", 24 | "arg": [], 25 | "msg": ["expect ast-ir end node"], 26 | "doc": "解码过程中缺失结束单元,请检查编码文件或重新编译。" 27 | } 28 | ], 29 | "warning": [] 30 | } 31 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Name: xmake.lua 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/11/24 19:15 6 | Description: Stamon的XMake构建文件 7 | --]] 8 | target("stamon") 9 | set_kind("binary") 10 | 11 | add_files("src/Main.cpp") 12 | 13 | add_includedirs("src") 14 | add_includedirs("src/ast") 15 | add_includedirs("src/data_type") 16 | add_includedirs("src/vm") 17 | add_includedirs("src/ir") 18 | add_includedirs("src/compiler") 19 | add_includedirs("src/sfn") 20 | add_includedirs("src/action") 21 | add_includedirs("src/exception") 22 | add_includedirs("src/config") 23 | add_includedirs("include/interface") 24 | add_includedirs("include/stdc_implemented") 25 | 26 | set_optimize("faster") 27 | set_languages("c++17") 28 | add_cflags("-static") 29 | add_cflags("-lm") -------------------------------------------------------------------------------- /doc/工作日志/20240821.md: -------------------------------------------------------------------------------- 1 | # 2024/08/21 工作日志 2 | 3 | 本次修复了项目的许多漏洞,正式发行了2.4.19。 4 | 5 | ### 修复了大量漏洞 6 | 7 | 随着Stamon Web端的发布,大量网友帮我找到了漏洞,感谢他们! 8 | 9 | 本次修复的漏洞有: 10 | 11 | * String equals方法无法正常使用 12 | * Lexer的String匹配算法无法对未封闭字符串进行报错 13 | * 对错误的小数点无法正常分析 14 | * SFN的语法分析语法报错信息有误 15 | * 在GC后如果内存还是溢满则直接卡退 16 | * 修复了Stamon标准库 17 | * 解决了gc之后内存泄漏的问题 18 | * stamon help指令无需检查环境变量 19 | * 打开不存在的文件出现内存泄漏的问题 20 | * 使用--isStrip=true选项会产生内存泄露 21 | 22 | ### 新增内存池 23 | 24 | 我们测试了2.4.8的性能,并生成了火焰图,发现内存申请和释放的性能开销很大。于是我在依赖库当中添加了MemoryPool类。 25 | 26 | MemoryPool提供了内存池功能,加快运行速度的同时增加了内存浪费。如果用户不希望内存池过度浪费内存,可以指定``--MemPoolCache=``参数来指定内存池的缓存大小,当缓存大小为0时,则不使用内存池(详细请参见``发行版使用指南.md``) 27 | 28 | 在使用内存池后,我们进行了一百万次的空循环测试和素数筛测试,运行时间普遍减小至85%。 29 | 30 | ### 接下来要做的事 31 | 32 | 1. 支持文件处理库 33 | 2. 支持编译为平面字节码 34 | 3. 编写词法分析的保存功能 35 | 4. 编写AST的O1优化器 36 | 5. 完善标准库 37 | 6. 编写AST的解释器 -------------------------------------------------------------------------------- /doc/工作日志/20250228.md: -------------------------------------------------------------------------------- 1 | # 2025/02/28 工作日志 2 | 3 | 本次更新不改变内部版本。 4 | 本次更新修改了一些小瑕疵,改进了CMakeList.txt,新增了基于Makefile的Github Action。 5 | 6 | ### 修复CMakeList.txt 7 | 8 | 删去了``-march=native``参数。 9 | 10 | CMakeList.txt具有被弃用的可能性。 11 | 12 | ### 新增基于Makefile的Github Action 13 | 14 | 该文件位于``.github/workflows/makefile.yml`` 15 | 16 | 经过了一晚上的调试,基于Makefile的Github Action终于被调试出来了。 17 | 使用Makefile作为Github Action有以下好处: 18 | 19 | * 构建速度快(实验得出:Github Action使用Makefile的速度通常是使用CMakeList.txt的三倍以上) 20 | * 易读性强(使用Makefile的Github Workflow文件长度比使用CMakeList.txt的长度短很多,Makefile本身也比CMakeList.txt短很多,且更方便阅读) 21 | * 可控性高(Makefile通常只用了数条命令就构建出了程序,开发者可以很方便的修改参数) 22 | 23 | ### 接下来要做的事 24 | 25 | 1. 将``TokenFileWriter``和``TokenFileReader``进行对接调试 26 | 2. 将``STVCReader.cpp``和``STVCWriter.cpp``迁移到``action``目录下,并将其改名为``AstIrReader.cpp``和``AstIrWriter.cpp`` 27 | 3. 为重构虚拟机做准备工作 28 | 4. 开发Stamon的安装工具 29 | 5. 推进Stamon包管理器的开发 30 | 6. 推进斯塔蒙的开发 31 | 7. 支持文件处理库 -------------------------------------------------------------------------------- /src/exception/codegen/objectmanager.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ObjectManagerException.cpp", 3 | "error": [ 4 | { 5 | "type": "MemoryError", 6 | "arg": [], 7 | "msg": ["out of memory"], 8 | "doc": "GC后占用内存仍然超出限制,请调整内存大小限制或优化代码。" 9 | }, 10 | { 11 | "type": "PhysicalMemoryError", 12 | "arg": [], 13 | "msg": ["out of physical memory"], 14 | "doc": "物理内存不足,请调整内存大小限制或优化代码。" 15 | }, 16 | { 17 | "type": "IdentifierError", 18 | "arg": [], 19 | "msg": ["undefined identifier"], 20 | "doc": "访问了未定义的标识符,通常情况下,内部标识符的定义问题会在编译期报告,因此需仔细检查外部标识符是否只定义未赋值,或字节码文件是否被损坏,或是否存在其他内部异常。" 21 | }, 22 | { 23 | "type": "ObjectError", 24 | "arg": [], 25 | "msg": ["unknown object"], 26 | "doc": "发现了未知类型的对象,通常情况下,对象由虚拟机生成,也可能由SFN等外部接入的代码产生,因此请仔细检查虚拟机是否存在内部异常或外部接入的代码是否存在异常。" 27 | } 28 | ], 29 | "warning": [] 30 | } 31 | -------------------------------------------------------------------------------- /include/interface/IFileReader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IFileReader.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 16:15 6 | Description: 文件读取类接口 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BasicPlatform.hpp" 12 | #include "EasySmartPtr.hpp" 13 | #include "Exception.hpp" 14 | #include "StamonLib.hpp" 15 | #include "String.hpp" 16 | 17 | namespace stamon { 18 | 19 | ArrayList ImportPaths; 20 | 21 | template class IFileReader : public Impl { 22 | //文件读取器,引用传递 23 | public: 24 | IFileReader(STMException *e, String filename) 25 | : Impl(e, move(filename)) { 26 | // 打开名为filename的文件 27 | } 28 | int getSize() { 29 | // 获取文件大小 30 | return Impl::getSize(); 31 | } 32 | EasySmartPtr read() { 33 | // 读取文件为一个字节串 34 | return Impl::read(); 35 | } 36 | void close() { 37 | // 关闭文件 38 | return Impl::close(); 39 | } 40 | }; 41 | 42 | } // namespace stamon -------------------------------------------------------------------------------- /doc/语法分析器文档.md: -------------------------------------------------------------------------------- 1 | # 语法分析器文档 2 | 3 | > 隶属于``编译器开发文档.md`` 4 | 5 | 语法分析器的代码位于``src/compiler/Parser.cpp``当中。 Parser类(``stamon::c::Parser``)即为语法分析器的主体。 6 | 7 | ##### 核心接口 8 | 9 | 语法分析器的构造函数原型为: 10 | 11 | ```C++ 12 | Parser( 13 | Matcher matcher, STMException* e, 14 | SyntaxScope global_scope, String filename, 15 | ArrayList* src, HashMap map, 16 | ArrayList* error_msg, ArrayList* warning_msg, 17 | bool is_support_import 18 | ); 19 | ``` 20 | 21 | 其中,``matcher``为Token匹配器;``global_scope``为给定的全局作用域;``filename``为当前匹配的文件名;``src``为一个以文件为单位所存储的AST列表,作为输出参数,在递归分析其他被引用的文件时,语法分析器会把被引用文件的AST加入到src,但其本身生成的AST并不会加入进去;``map``用于标记某些文件是否被分析过(防止递归引用);``error_msg``作为输出参数,用于存储报错信息;``warning_msg``作为输出参数,用于存储警告信息;``is_support_import``用于指示是否支持import(防止某些平台不支持文件处理)。 22 | 23 | 在初始化语法分析器后,调用``ast::AstNode* Parse()``函数即可获得AST的根节点。 24 | 25 | ##### 基本原理 26 | 27 | 语法分析器主要采用递归分析,具体的文法请见``src/compiler/ebnf.txt``。 -------------------------------------------------------------------------------- /src/data_type/ObjectType.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ObjectType.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 17/08/23 11:49 6 | Description: 7 | * 在STVM中,新建的类对象也是一个值,它对应的数据类型就是ObjectType 8 | * 这里就写上了ObjectType的定义 9 | */ 10 | 11 | #pragma once 12 | 13 | #include"DataType.hpp" 14 | #include"HashMap.hpp" 15 | 16 | namespace stamon::datatype { 17 | class ObjectType : public DataType { 18 | HashMap vals; 19 | //STVM内部以数字来代替标识符 20 | public: 21 | ObjectType(HashMap value) 22 | : DataType(ObjectTypeID), vals(HashMap()) { 23 | vals = value; 24 | } 25 | virtual HashMap getVal() const { 26 | return vals; 27 | } 28 | virtual ~ObjectType() { 29 | ArrayList vars = vals.getValList(); 30 | for(int i=0;i 隶属于``编译器开发文档.md`` 4 | 5 | 词法分析器的Token定义位于``src/compiler/Token.cpp``。 6 | 词法分析器的源码位于``src/compiler/Lexer.cpp``。 7 | 8 |
9 | 10 | ``Token``是基本词法单元类,其成员如下: 11 | 12 | * ``Token(int line, int tok_type)``:构造函数,line参数表示该token所在的行号,tok_type表示该token的类型编号(具体请参见``Lexer.cpp``中的``TOKEN_TYPE``) 13 | * ``lineNo``:这是一个整数变量,表示该token所在的行号 14 | * ``type``:返回一个整数,即该token的类型编号 15 | 16 |
17 | 18 | 除``Token``类外,还有``StringToken``、``IdenToken``、``IntToken``、``DoubleToken``。这些都是``Token``的子类,如果一个Token是字面量或者标识符,你需要将其转化成对应的子类才能获取更多信息。具体请参见``Token.cpp``中的相关定义。 19 | 20 |
21 | 22 | ``Lexer``是词法分析器类,其核心接口如下: 23 | 24 | * ``int getLineTok(int line, String text)``:分析一行的token,line是行号,text是文本(不包含换行符)。分析后的token会加入到缓存中。返回解析到的位置,如果返回值是text的长度就说明解析到末尾,即解析成功;否则说明解析失败。 25 | * ``Token* getTok()``:从缓存中读取出(并删除)一个Token。 26 | * ``Token* peek(int index)``:查看第index个Token(但不删除)。 27 | 28 | Lexer在分析前,应当先把源码分解成逐行的文本,然后从第一行到最后一行依次调用getLineTok。Lexer会将生成的Token序列存在内存中,交给进一步的语法分析。 -------------------------------------------------------------------------------- /include/stdc_implemented/Stack.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: Stack.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 17/01/23 18:06 6 | Description: 栈库 7 | */ 8 | #pragma once 9 | 10 | #include "ArrayList.hpp" 11 | #include "BasicPlatform.hpp" 12 | #include "IStack.hpp" 13 | #include "StamonLib.hpp" 14 | 15 | namespace stamon::stdc { 16 | 17 | template class Stack { 18 | ArrayList list; 19 | 20 | public: 21 | void clear() { 22 | list.clear(); 23 | } 24 | bool empty() { 25 | return list.empty(); 26 | } 27 | int size() { 28 | return list.size(); 29 | } 30 | T *peek() { 31 | return list[list.size() - 1]; 32 | } 33 | void push(T *data) { 34 | list.add(data); 35 | } 36 | T *pop() { 37 | T *rst = list[list.size() - 1]; 38 | list.erase(list.size() - 1); 39 | return rst; 40 | } 41 | }; 42 | 43 | } // namespace stamon::stdc 44 | 45 | namespace stamon { 46 | 47 | template 48 | using Stack = IStack>; 49 | 50 | } -------------------------------------------------------------------------------- /doc/编译器开发文档.md: -------------------------------------------------------------------------------- 1 | # 编译器开发文档 2 | 3 | ### 附属文档 4 | 5 | * ``词法分析器文档.md`` 6 | * ``语法分析器文档.md`` 7 | 8 | ### 基本定义和使用 9 | 10 | * 编译器的源码位于``src/compiler``目录下。 11 | * ``stamon::c``是编译器所在的命名空间,``c``的全称是``compiler``。 12 | * 在``Compiler.hpp``中,提供了顶层前端的设计函数。 13 | * 我们规定:Stamon源码的文件后缀为``.st`` 14 | 15 | 以下是``Compiler.hpp``包含的接口: 16 | 17 | ```C++ 18 | ArrayList *ParseTargetProject( 19 | STMException *e, 20 | ArrayList *error_msg, ArrayList *warning_msg, 21 | String filename, bool is_support_import, ArrayList *src, 22 | HashMap filemap, SyntaxScope global_scope 23 | ); 24 | 25 | /* 26 | 编译一个Stamon项目。 27 | e是异常类,error_msg是存储报错信息用的列表,warning_msg是存储警告信息用的列表,filename是源码文件名,is_support_import_表示是否支持引用其他源码,src是存储各个文件语法树用的列表,filemap是用来标记文件是否被引用过的映射表,global_scope是全局作用域。 28 | 返回处理后的存储着各个文件语法树的列表(即处理后的src)。 29 | */ 30 | 31 | ast::AstNode *MergeAST(ArrayList *syntax_list); 32 | 33 | /* 34 | 将各个语法树合并为一个。syntax_list是存储着各个文件语法树的列表。返回合并后的根节点。 35 | */ 36 | 37 | ``` -------------------------------------------------------------------------------- /demos/demo1.st: -------------------------------------------------------------------------------- 1 | import std; //支持调用文件 2 | 3 | def a = 1+1, b = a*2; //定义变量 4 | 5 | if a == b { 6 | puts("a==b!\n"); 7 | } else { 8 | puts("a!=b!\n"); 9 | } 10 | 11 | //如果代码块只有一条语句,那么可以只用冒号来编写 12 | 13 | def i = 1; 14 | 15 | while i<=3 { 16 | puts("This is a while loop!\n"); 17 | i += 1; 18 | } 19 | 20 | for j in { 1, 2, 3 } { //for循环的表达式要是列表 21 | println(j); 22 | } 23 | 24 | a = [3]; //默认填充null 25 | println(a); 26 | 27 | func f1() { //如果函数无参数,也可以把括号省略掉,写成“func f {...}”格式 28 | puts("This is function \"f\"!\n"); 29 | } 30 | 31 | def f2 = func(n) { 32 | println(n); 33 | }; 34 | 35 | f1(); 36 | f2(10); 37 | 38 | class c1 { 39 | def member; 40 | func __init__(self) { //构造函数名默认__init__,并且所有方法都需要有self参数 41 | puts("Initializing c1!\n"); 42 | self.member = 0; //用self访问成员 43 | } 44 | } 45 | 46 | def c2 = class { 47 | func __init__(self, n) { 48 | puts("Initializing c2!\n"); 49 | println(n); 50 | } 51 | }; 52 | 53 | c1.new; 54 | c2.new(8); -------------------------------------------------------------------------------- /src/exception/AstIrException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: AstIrException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::astir { 14 | inline STMInfo RootNodeError(String position) { 15 | return STMInfo(String("astir"), String("RootNodeError"), String("expect ast-ir root node"), position); 16 | } 17 | 18 | inline STMInfo RedundantEndNodeError(String position) { 19 | return STMInfo(String("astir"), String("RedundantEndNodeError"), String("redundant ast-ir end node"), position); 20 | } 21 | 22 | inline STMInfo RedundantRootNodeError(String position) { 23 | return STMInfo(String("astir"), String("RedundantRootNodeError"), String("redundant ast-ir end node"), position); 24 | } 25 | 26 | inline STMInfo EndNodeError(String position) { 27 | return STMInfo(String("astir"), String("EndNodeError"), String("expect ast-ir end node"), position); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/exception/ObjectManagerException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ObjectManagerException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::objectmanager { 14 | inline STMInfo MemoryError(String position) { 15 | return STMInfo(String("objectmanager"), String("MemoryError"), String("out of memory"), position); 16 | } 17 | 18 | inline STMInfo PhysicalMemoryError(String position) { 19 | return STMInfo(String("objectmanager"), String("PhysicalMemoryError"), String("out of physical memory"), position); 20 | } 21 | 22 | inline STMInfo IdentifierError(String position) { 23 | return STMInfo(String("objectmanager"), String("IdentifierError"), String("undefined identifier"), position); 24 | } 25 | 26 | inline STMInfo ObjectError(String position) { 27 | return STMInfo(String("objectmanager"), String("ObjectError"), String("unknown object"), position); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/config/StamonConfig.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: StamonConfig.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 08/03/25 18:21 6 | Description: 用于存储stamon的公共配置 7 | */ 8 | 9 | #pragma once 10 | 11 | // stamon的版本号 12 | namespace stamon::config { 13 | constexpr int STAMON_VER_X = 2; 14 | constexpr int STAMON_VER_Y = 4; 15 | constexpr int STAMON_VER_Z = 53; 16 | } // namespace stamon 17 | 18 | namespace stamon::config { 19 | 20 | enum STAMON_CODING_ENDIAN { 21 | // 机器的大小端控制 22 | StamonCodingLittleEndian = 0, 23 | StamonCodingBigEndian 24 | }; 25 | 26 | enum STAMON_WARNING_SAFE_LEVEL { 27 | // 警告等级 28 | StamonWarningSafeLevel_IgnoreWarning = 0, // 忽略警告 29 | StamonWarningSafeLevel_JustWarn, // 只警告,程序继续运行 30 | StamonWarningSafeLevel_FatalWarning // 将警告视为致命错误 31 | }; 32 | 33 | // 编码的端序 34 | constexpr int StamonEncodingEndian = StamonCodingLittleEndian; 35 | // 解码的顺序 36 | constexpr int StamonDecodingEndian = StamonCodingLittleEndian; 37 | 38 | } // namespace stamon::config 39 | 40 | #include "CompilerConfig.hpp" 41 | #include "VmConfig.hpp" -------------------------------------------------------------------------------- /include/interface/IMemoryPool.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IMemoryPool.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 16:37 6 | Description: 内存池接口 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "Exception.hpp" 12 | #include "StamonLib.hpp" 13 | 14 | namespace stamon { 15 | 16 | template class IMemoryPool : public Impl { 17 | // 内存池,请用智能指针托管该类对象,或手动创建并释放 18 | public: 19 | IMemoryPool(STMException *e, int mem_limit, int pool_cache_size) 20 | : Impl(e, mem_limit, pool_cache_size) { 21 | //mem_limit为内存极限,pool_cache_size为内存池缓存大小 22 | } 23 | template T *NewObject(Types &&...args) { 24 | //向内存池申请一个对象,用法:NewObject<申请类型>(构造参数...) 25 | return Impl::template NewObject(forward(args)...); 26 | } 27 | void clearAllFreeMem() { 28 | //清除内存池的所有空闲内存,以此减小内存占用 29 | return Impl::clearAllFreeMem(); 30 | } 31 | template void DeleteObject(T *object) { 32 | //删除内存池上的一个对象 33 | return Impl::DeleteObject(object); 34 | } 35 | }; 36 | 37 | } // namespace stamon -------------------------------------------------------------------------------- /include/interface/IHashMap.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IHashMap.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 16:24 6 | Description: 哈希表类接口 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "StamonLib.hpp" 12 | 13 | namespace stamon { 14 | 15 | template 16 | class IHashMap : public Impl { 17 | // 使用引用传递 18 | public: 19 | IHashMap() 20 | : Impl() { 21 | } 22 | void put(KeyType key, ValType val) { 23 | // 设置键值对 24 | return Impl::put(move(key), move(val)); 25 | } 26 | ValType get(KeyType key) { 27 | // 根据键获取值 28 | return Impl::get(move(key)); 29 | } 30 | void del(KeyType key) { 31 | // 根据键删除键值对 32 | return Impl::del(move(key)); 33 | } 34 | bool exist(KeyType key) { 35 | // 根据键判断是否存在对应的键值对 36 | return Impl::exist(move(key)); 37 | } 38 | ArrayList getValList() { 39 | // 获取一个由所有值组成的列表 40 | return Impl::getValList(); 41 | } 42 | ArrayList getKeyList() { 43 | // 获取一个由所有键组成的列表 44 | return Impl::getKeyList(); 45 | } 46 | }; 47 | 48 | } // namespace stamon -------------------------------------------------------------------------------- /include/interface/IBasicPlatform.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IBasicPlatform.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 11:48 6 | Description: 基本运行必需接口 7 | */ 8 | 9 | #pragma once 10 | 11 | // 重载new和delete 12 | 13 | #ifndef NEWDELETE_DEFINED 14 | // 如果第三方库本身已经引入了new和delete的重载,那么就不用重复实现 15 | namespace stamon { 16 | using size_t = decltype(sizeof(0)); // 自定义size_t 17 | } 18 | 19 | void *operator new(stamon::size_t size); 20 | void *operator new[](stamon::size_t size); 21 | void *operator new(stamon::size_t size, void *ptr); 22 | void *operator new[](stamon::size_t size, void *ptr); 23 | void operator delete(void *ptr); 24 | void operator delete[](void *ptr); 25 | #endif 26 | 27 | namespace stamon { 28 | 29 | // 字节类型 30 | template using Ibyte = T; 31 | 32 | // 哈希函数 33 | unsigned int toHash(char x); 34 | unsigned int toHash(int x); 35 | unsigned int toHash(unsigned int x); 36 | unsigned int toHash(float x); 37 | unsigned int toHash(double x); 38 | 39 | template unsigned int toHash(Ibyte x); // 字节数据类型的哈希 40 | 41 | } // namespace stamon 42 | -------------------------------------------------------------------------------- /src/data_type/SequenceType.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: SequenceType.cpp 3 | License: Apache 2.0 4 | Author: Climber-Rong 5 | Date: 14/08/23 10:19 6 | Description: 数列数据类型的定义 7 | * 也许你会对这种数据类型的名字感到困惑 8 | * 数列其实是数组和列表的结合 9 | * 数列可以在创建的时候就初始化长度,这点与类似Python的列表不同 10 | * 数列也可以动态的改变长度,这点与类似C++的数组不同 11 | * 数列这种数据结构很大程度上使得这个虚拟机可以兼容更多的语言,而且它的实现也并非难事 12 | */ 13 | 14 | #pragma once 15 | 16 | #include"DataType.hpp" 17 | #include"ArrayList.hpp" 18 | 19 | namespace stamon::datatype { 20 | class SequenceType : public DataType { 21 | public: 22 | ArrayList sequence; 23 | SequenceType(int length) 24 | : DataType(SequenceTypeID), sequence(ArrayList(length)) { 25 | } 26 | SequenceType(ArrayList value) 27 | : DataType(SequenceTypeID) { 28 | sequence = move(value); 29 | } 30 | virtual const ArrayList& getVal() const { 31 | return sequence; 32 | } 33 | virtual ~SequenceType() { 34 | for(int i=0;i; 53 | 54 | } 55 | 56 | #undef FILE_ERR -------------------------------------------------------------------------------- /src/data_type/NumberType.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: NumberType.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 14/08/23 10:09 6 | Description: 数字类型的定义,包括整数和浮点数 7 | */ 8 | 9 | #pragma once 10 | 11 | #include"DataType.hpp" 12 | 13 | namespace stamon::datatype { 14 | class IntegerType : public DataType { 15 | int val; 16 | public: 17 | IntegerType(int value) : DataType(IntegerTypeID) { 18 | val = value; 19 | } 20 | virtual int getVal() const { 21 | return val; 22 | } 23 | virtual ~IntegerType() = default; 24 | }; 25 | 26 | class FloatType : public DataType { 27 | float val; 28 | public: 29 | FloatType(float value) : DataType(FloatTypeID) { 30 | val = value; 31 | } 32 | virtual float getVal() { 33 | return val; 34 | } 35 | virtual ~FloatType() = default; 36 | }; 37 | 38 | class DoubleType : public DataType { 39 | double val; 40 | public: 41 | DoubleType(double value) : DataType(DoubleTypeID) { 42 | val = value; 43 | } 44 | virtual double getVal() { 45 | return val; 46 | } 47 | virtual ~DoubleType() = default; 48 | }; 49 | } //namespace stamon::datatype -------------------------------------------------------------------------------- /src/exception/codegen/astfilereader.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AstFileReaderException.cpp", 3 | "error": [ 4 | { 5 | "type": "FormatError", 6 | "arg": [], 7 | "msg": ["non-standardized ast-file"], 8 | "doc": "Ast文件综合编码错误,请检查编码格式或重新编码。" 9 | }, 10 | { 11 | "type": "NodeError", 12 | "arg": [], 13 | "msg": ["unknown ast-node"], 14 | "doc": "Ast文件出现了未知节点,请检查编码格式或重新编码。" 15 | }, 16 | { 17 | "type": "RedundantRootNodeError", 18 | "arg": [], 19 | "msg": ["redundant ast root node"], 20 | "doc": "Ast文件有多余的根节点,请检查编码格式或重新编码。" 21 | }, 22 | { 23 | "type": "RootNodeError", 24 | "arg": [], 25 | "msg": ["expect ast root node"], 26 | "doc": "Ast文件没有根节点,请检查编码格式或重新编码。" 27 | }, 28 | { 29 | "type": "RedundantEndNodeError", 30 | "arg": [], 31 | "msg": ["redundant ast end node"], 32 | "doc": "Ast文件有多余的结束单元,请检查编码格式或重新编码。" 33 | }, 34 | { 35 | "type": "EndNodeError", 36 | "arg": [], 37 | "msg": ["expect ast end node"], 38 | "doc": "Ast文件缺失结尾单元,请检查编码格式或重新编码。" 39 | } 40 | ], 41 | "warning": [] 42 | } 43 | -------------------------------------------------------------------------------- /doc/工作日志/20240624.md: -------------------------------------------------------------------------------- 1 | # 2024/06/24 工作日志 2 | 3 | 本次主要整理了代码的风格,并且发布了2.4.5。 4 | 5 | ### 修复了部分问题 6 | 7 | 将析构的函数也定义为虚函数,防止了可能的内存泄露。 8 | 9 | ### 格式化代码 10 | 11 | 本建议来自于[一个issue](https://github.com/CLimber-Rong/stamon/issues/1)。 12 | 13 | 感谢其提议,我对代码的风格进行了如下调整: 14 | 15 | 1. 将嵌套的namespace整合成一个namespace 16 | 17 | 例如: 18 | 19 | ```C++ 20 | namespace stamon { 21 | namespace ast { 22 | ... 23 | } 24 | } 25 | ``` 26 | 27 | 在整合之后成为: 28 | 29 | ```C++ 30 | namespace stamon::ast { 31 | ... 32 | } 33 | ``` 34 | 35 | 这么做可以节约代码的缩进空间。 36 | 37 | 2. 在 namespace 的末尾加上注释 38 | 39 | 例如: 40 | 41 | ```C++ 42 | namespace stamon { 43 | } // namespace stamon 44 | ``` 45 | 46 | 3. 不在头文件中且在上层namespace中using下层namespace 47 | 48 | 即不再使用``using namespace stamon:xxx;``,而是在访问成员时使用``xxx::xxxxx``。 49 | 50 | 4. 将一些能放入namespace的常量值放入了namespace里 51 | 52 | 5. 不要用 #ifndef #define 来防止头文件被重复包含,而是使用#pragma once 53 | 54 | 与此同时,我对未来的代码编写做出了一些改变: 55 | 56 | 1. 将宏用 do {} while(0) 包裹,同时在调用宏的时候在末尾加上分号 57 | 58 | 2. 将一些能封装为函数的宏封装为函数 59 | 60 | 3. 加入了我喜欢的.clang-format,以后的代码可能都使用其进行格式化 61 | 62 | ### 接下来要做的事 63 | 64 | 1. 编写AST的解释器 65 | 2. 编写词法分析的保存功能 66 | 3. 编写AST的O1优化器 67 | 4. 完善标准库 68 | 5. 支持编译为平面字节码 69 | -------------------------------------------------------------------------------- /doc/分步行动机制文档.md: -------------------------------------------------------------------------------- 1 | # 分步行动机制文档(第二版) 2 | 3 | 为了让项目更接近于“高内聚,低耦合”的状况,我汲取了管道机制、函数式编程的思想,提出了**分步行动机制**(Substep-Action,又称**分步动作机制**,简称SA机制)。 4 | 5 | ### 定义 6 | 7 | SA机制是一个用于控制数据处理的新机制。 8 | 9 | 传统的行动机制只提供一站式的完整操作(例如从源码编译到字节码,从字节码开始运行),这使得用户的自定义空间变少。 10 | 11 | SA机制将大规模的操作细分成一个个细节操作,这些细节操作被称为**行动工具**。和函数一样,一个标准的行动工具会接受一系列的输入数据,进行处理后会生成一系列输出数据。 12 | 13 | 为了避免正确的数据被多个行动工具反复检查的冗余情况。这里引入**数据安全信任**和**数据安全承诺**的概念。数据安全信任指一个行动工具在接收输入数据时,对某些数据产生的信任,**受信任的输入数据不会被该行动工具检查**,数据安全信任提升了安全检查的效率,但用户和开发者也必须保证受信任的数据是安全的,一旦受信任的数据不安全,程序可能会产生崩溃等不可预知的致命错误。数据安全承诺指一个行动工具在生成输出数据时,对某些数据产生的承诺,**受承诺的输出数据一般是安全的**,不需要被反复检查,受承诺的输出数据通常会成为下一个行动工具所信任的输入数据,当然,数据安全承诺是建立在数据安全信任之上的,如果受信任的数据不安全,那么承诺的数据也不一定安全。 14 | 15 | 用户可以把一系列行动工具组合在一起,形成一个**动作组**(又称**行动工具链**)。启动这个动作组满足以下规则: 16 | 17 | 1. SA机制会把**起始数据**交给动作组的第一个行动工具 18 | 2. 除了最后一个行动工具以外,每一个行动工具产生的输出数据都会成为下一个行动工具的输入数据 19 | 3. 最后一个行动工具产生的数据就是**结果数据** 20 | 21 | 起始数据、结果数据以及所有行动工具间产生的数据,都被统称为**行动物品**。 22 | 23 | ### 解释 24 | 25 | 利用SA机制,用户可以自由的组合行动工具,提升了数据处理的自由性。 26 | 27 | 例如:将编译动作组和运行动作组连接在一起,编译生成的字节码直接交给运行端,就能不修改项目从而实现解释器的效果。如果其他编程语言想要共用Stamon的后端,也可以编译成AST编码文件,然后利用SA机制,直接把AST编码文件作为起始数据,生成字节码。 28 | 29 | 用户也可以开发属于自己的行动工具,提升自定义属性。例如:开发一个从屏幕上输入的文本编辑器,再把编译动作组和运行动作组连接在一起,就能实现从屏幕输入代码然后解释运行的效果了。 30 | 31 | 利用SA机制,用户可以不进行或少进行文件操作,让数据在内存中传递,提升了效率,增强了可移植性。 -------------------------------------------------------------------------------- /include/interface/IEasySmartPtr.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IEasySmartPtr.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 03/10/25 12:15 6 | Description: 智能指针接口 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BasicPlatform.hpp" 12 | #include "StamonLib.hpp" 13 | 14 | /* 15 | * 对于一些不会循环引用的数据结构,可以用智能指针来封装指针 16 | * 利用引用计数法,EasySmartPtr会记录有多少个数据正在使用该指针 17 | * 当没有任何数据使用该指针时,其可以被视作垃圾内存而删除 18 | */ 19 | 20 | namespace stamon { 21 | 22 | template class IEasySmartPtr; 23 | 24 | template class IEasySmartPtr : public Impl { 25 | //智能指针 26 | public: 27 | IEasySmartPtr(T *pointer) 28 | : Impl(pointer) { 29 | // 直接传入指针,默认销毁方式为直接delete 30 | } 31 | IEasySmartPtr(T *pointer, void (*destroy_funcptr)(IEasySmartPtr *ptr)) 32 | : Impl(pointer, destroy_funcptr) { 33 | // 传入指针,并指定销毁方式 34 | } 35 | IEasySmartPtr(Impl impl) 36 | : Impl(move(impl)) { 37 | // 复制构造函数 38 | } 39 | T *get() const { 40 | // 获取指针 41 | return Impl::get(); 42 | } 43 | T *operator->() const { 44 | // 直接访问指针的成员 45 | return Impl::operator->(); 46 | } 47 | T &operator[](int index) const { 48 | // 直接访问元素 49 | return Impl::operator[](index); 50 | } 51 | T &operator*() const { 52 | // 直接访问目标 53 | return Impl::operator*(); 54 | } 55 | }; 56 | 57 | } // namespace stamon -------------------------------------------------------------------------------- /doc/工作日志/20250423.md: -------------------------------------------------------------------------------- 1 | # 2025/04/23 工作日志 2 | 3 | 本次更新发行``2.4.45``。 4 | 本次更新修复了一些漏洞,提升了对象管理器的性能。 5 | 6 | ### 修复了依赖库的标准C实现中的漏洞 7 | 8 | 1. 对于``ArrayList.hpp``中销毁内存的实现,不应该直接调用析构函数,这会导致不可控的结果。目前我封装了一个destroy_cache方法 9 | 2. 对于``strie.h``中的``ClearTrie``和``DestroyTrie``,我整理了代码逻辑,使其可读性更强 10 | 3. 对于``ByteMap.hpp``中的``getValList``,我在判断循环条件时忘记加上逻辑非运算符了,这会导致该函数直接返回一个空列表;循环中加入的元素全部都是根节点而不是当前遍历节点。我解决了这些问题 11 | 12 | ### 整理了AstRunner的代码逻辑 13 | 14 | 1. 对于获取空对象,我不直接调用``getNullConst``,而是调用``MallocObject``,``MallocObject``再返回``getNullConst``,这让代码整体更统一了 15 | 2. 以往的某些函数可能会在返回时直接返回``MallocObject``,而不先判断``MallocObject``是否抛出异常,该问题已被解决 16 | 3. 类对象在被初始化后,会调用``PopScope``,而对象管理器会误删这个类对象。为此我专门在对象管理器中加入了``PopMemberTabScope``,来防止类对象被误删 17 | 18 | ### 修复并优化了ObjectManager 19 | 20 | 做了如下优化: 21 | 22 | 1. 对于``MarkScopeObject``的数列扫描和对象扫描部分,我整理并删除了冗余代码。 23 | 2. 和``getNullConst``类似的,我设立了“整数复用池”,这是一个存储了从-5到+256的整数对象的数组。在申请整数时,如果整数在从-5到+256的范围内,就会直接返回该数组中的对象。这样一来,当代码频繁申请小整数时,对象管理器不用反复新建对象,而是可以直接返回数组中的元素。此举措减少了内存分配次数,提升了性能。 24 | 3. 为了统一对象申请,我使用了模板特化,并将对象申请的主体部分迁移到了``_malloc_object``里(不建议直接调用此方法),来特别优化空对象和整数对象的申请。这样申请对象就可以全部统一使用``MallocObject``了 25 | 26 | 解决了如下问题: 27 | 28 | 1. 对于``FreeObject``,不应该直接删除,而是要用内存池来帮忙删除,并且更新对象占用内存大小的统计变量 29 | 2. 对于``~ObjectManager``,应该把内存池的剩余空闲内存也清理掉 30 | 3. 在初始化一个新的对象时,要把其gc_flag置为false 31 | 32 | ### 修复了一个用词错误 33 | 34 | 我注意到执行的英文单词是“execute”而不是“excute”,目前所有相关的用词错误已被修复。 -------------------------------------------------------------------------------- /include/stdc_implemented/BasicIo.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: BasicIo.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 19/09/25 12:36 6 | Description: 维持项目基本输入输出的库 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "IBasicIo.hpp" 12 | #include "ctype.h" 13 | #include "stdio.h" 14 | #include "stdlib.h" 15 | #include "string.h" 16 | 17 | namespace stamon { 18 | 19 | void platform_exit(int code) { 20 | exit(code); 21 | } 22 | 23 | int platform_system(String cmd) { 24 | return system(cmd.getstr()); 25 | } 26 | 27 | #define _PLATFORM_INPUT_BUFFER_SIZE 1024 28 | 29 | String platform_input() { 30 | String rst; 31 | 32 | char buffer[_PLATFORM_INPUT_BUFFER_SIZE]; // 缓冲区 33 | int pos = 0; // 当前缓冲区所处位置 34 | 35 | memset(buffer, 0, sizeof(buffer)); // 清空buffer 36 | 37 | char ch = getchar(); 38 | 39 | // 先过滤先前的空格 40 | while (isspace(ch)) { 41 | ch = getchar(); 42 | } 43 | 44 | while (!isspace(ch)) { 45 | if (pos == _PLATFORM_INPUT_BUFFER_SIZE - 2) { 46 | // 缓冲区已满 47 | rst += String(buffer); 48 | pos = 0; 49 | memset(buffer, 0, sizeof(buffer)); 50 | } 51 | buffer[pos] = ch; 52 | pos++; 53 | ch = getchar(); 54 | } 55 | 56 | if (pos != 0) { 57 | // 输入完成后,把缓冲区剩余的内容全部加入到rst当中 58 | rst += String(buffer); 59 | } 60 | 61 | return rst; 62 | } 63 | 64 | int platform_print(String s) { 65 | return printf("%s", s.getstr()); 66 | } 67 | 68 | } // namespace stamon -------------------------------------------------------------------------------- /src/exception/AstFileReaderException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: AstFileReaderException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::astfilereader { 14 | inline STMInfo FormatError(String position) { 15 | return STMInfo(String("astfilereader"), String("FormatError"), String("non-standardized ast-file"), position); 16 | } 17 | 18 | inline STMInfo NodeError(String position) { 19 | return STMInfo(String("astfilereader"), String("NodeError"), String("unknown ast-node"), position); 20 | } 21 | 22 | inline STMInfo RedundantRootNodeError(String position) { 23 | return STMInfo(String("astfilereader"), String("RedundantRootNodeError"), String("redundant ast root node"), position); 24 | } 25 | 26 | inline STMInfo RootNodeError(String position) { 27 | return STMInfo(String("astfilereader"), String("RootNodeError"), String("expect ast root node"), position); 28 | } 29 | 30 | inline STMInfo RedundantEndNodeError(String position) { 31 | return STMInfo(String("astfilereader"), String("RedundantEndNodeError"), String("redundant ast end node"), position); 32 | } 33 | 34 | inline STMInfo EndNodeError(String position) { 35 | return STMInfo(String("astfilereader"), String("EndNodeError"), String("expect ast end node"), position); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /include/interface/StamonLib.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: StamonLib.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong, GusemFowage 5 | Date: 12/08/23 23:24 6 | Description: 该库用于存储不涉及任何平台操作的轻量功能实现 7 | */ 8 | 9 | #pragma once 10 | 11 | // 防止非标准环境中,NULL未定义 12 | #ifndef NULL 13 | #define NULL 0 14 | #endif 15 | 16 | // move和forward的实现 17 | namespace stamon { 18 | 19 | /* 20 | * remove_reference用于移除类型的引用 21 | * 传入T或T&或T&&,都返回T 22 | */ 23 | 24 | template struct remove_reference { 25 | typedef T type; 26 | }; 27 | template struct remove_reference { 28 | typedef T type; 29 | }; 30 | template struct remove_reference { 31 | typedef T type; 32 | }; 33 | 34 | /* 35 | * forward用于将左值或右值不加修饰的传递给其他代码,即完美转发 36 | * 使用forward可以减少不必要的拷贝复制 37 | */ 38 | 39 | template 40 | constexpr T &&forward(typename remove_reference::type &t) { 41 | return (T &&) (t); 42 | } 43 | 44 | template 45 | constexpr T &&forward(typename remove_reference::type &&t) { 46 | return (T &&) (t); 47 | } 48 | 49 | /* 50 | * move用于将给定的参数强转成右值,即移动语义 51 | * 如果某个被传递左值在后续不被需要,可以使用move转为左值,减少不必要的拷贝次数 52 | */ 53 | 54 | template 55 | constexpr typename remove_reference::type &&move(T &&t) { 56 | return (typename remove_reference::type &&) (t); 57 | } 58 | 59 | } // namespace stamon 60 | 61 | // 用于简化宏 62 | 63 | #define MACRO_START do { 64 | #define MACRO_END \ 65 | } \ 66 | while (0) 67 | -------------------------------------------------------------------------------- /doc/工作日志/20250402.md: -------------------------------------------------------------------------------- 1 | # 2025/04/02 工作日志 2 | 3 | 本次更新的内部版本为``2.4.43``。 4 | 本次更新并不会发布``2.4.43``。 5 | 6 | ### 调整了部分文档和代码 7 | 8 | 随着接口原型的变动,部分文档也受到了变动。 9 | 部分代码经过了微调,提升了可读性(例如``SFN.cpp``) 10 | 11 | ### 正式更新了智能指针 12 | 13 | 我将智能指针的实现也加入到了依赖库接口当中。 14 | 智能指针位于``EasySmartPtr.hpp``,正如文件名所描述的一样,EasySmartPtr旨在创建一个易实现,简单,实用的智能指针库。 15 | 16 | 该智能指针的标准C实现采用的是引用计数法,因此无法处理循环引用链,所以请在编写代码时注意不要将EasySmartPtr应用于循环引用链上。 17 | 18 | 为了解决“如何让智能指针在不同场景使用不同销毁方式”这一问题,我将销毁函数抽象成了销毁函数指针。除非特殊指定销毁函数,EasySmartPtr默认直接delete指针。销毁函数的原型应该是: 19 | 20 | ```C++ 21 | void destroy_function(EasySmartPtr* p); 22 | ``` 23 | 24 | EasySmartPtr的基本用法是: 25 | * ``EasySmartPtr(T *pointer)``:新建一个智能指针,pointer为受托管的指针,销毁方式默认为delete 26 | * ``EasySmartPtr(T *pointer, void (*destroy_funcptr)(EasySmartPtr *ptr))``:新建一个智能指针,pointer为受托管的指针,销毁方式为给定的函数指针destroy_funcptr 27 | * ``T *get()``:获取受托管的指针,我们建议只对该指针进行简单的读写,否则可能会出现该指针已被销毁但程序还在保存并使用该指针的情况。因此请记得用EasySmartPtr来传递该指针 28 | * ``ptr->member``:EasySmartPtr也支持直接获取成员。其中ptr为EasySmartPtr,member为成员标识符。该代码的效果和直接访问裸指针的成员的效果是等价的。 29 | 30 | ### 修复了大量的内存泄漏 31 | 32 | 本次更新着重修复运行端的内存漏洞。 33 | 34 | 我修复了AstRunner最为重要的“产生Variable堆对象但无法删除”的漏洞。我引用了智能指针来修复他。 35 | 我修复了“抽象语法树在被创建后无法被删除”的漏洞,我使用了递归析构的方式来修复他。 36 | 我修复了“复杂数据结构的datatype::DataType*在被删除后无法彻底清除内存”的漏洞,我使用了重载虚析构函数的方式来修复他。 37 | 38 | 还有一些细枝末节的,显然的内存泄漏也被我修复了。 39 | 40 | ### 接下来要做的事 41 | 42 | 1. 尝试将Stamon移植到受限硬件上 43 | 2. 将``TokenFileWriter``和``TokenFileReader``进行调试 44 | 3. 为重构虚拟机做准备工作 45 | 4. 实现分步行动机制 46 | 5. 开发Stamon的安装工具 47 | 6. 推进Stamon包管理器的开发 48 | 7. 推进斯塔蒙的开发 49 | 8. 支持文件处理库 -------------------------------------------------------------------------------- /src/data_type/DataType.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: DataType.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 14/08/23 10:08 6 | Description: 7 | * 这里对数据类型进行了基本的定义 8 | * 如果你想要引用所有的数据类型源码,只要写入以下代码即可: 9 | #include"DataType.hpp" 10 | using namespace stamon::datatype; 11 | */ 12 | 13 | #pragma once 14 | 15 | namespace stamon::datatype { 16 | enum _DataTypeID { 17 | DataTypeID = 0, 18 | NullTypeID, 19 | IntegerTypeID, 20 | FloatTypeID, 21 | DoubleTypeID, 22 | StringTypeID, 23 | SequenceTypeID, 24 | ClassTypeID, 25 | MethodTypeID, 26 | ObjectTypeID, 27 | DataTypeIDNum 28 | }; 29 | 30 | class DataType; 31 | class Variable; 32 | class NullType; 33 | class IntegerType; 34 | class FloatType; 35 | class DoubleType; 36 | class StringType; 37 | class SequenceType; 38 | class ClassType; 39 | class MethodType; 40 | class ObjectType; 41 | 42 | class DataType { 43 | int type; 44 | public: 45 | bool gc_flag = false; //用于在GC时标记这个值是否被遍历到 46 | 47 | DataType(int type_id) { 48 | type = type_id; 49 | } 50 | virtual int getType() const { 51 | return type; 52 | } 53 | virtual ~DataType() = default; //等价于~DataType() {} 54 | }; 55 | } //namespace stamon::datatype 56 | 57 | #include"Variable.cpp" 58 | #include"NullType.cpp" 59 | #include"NumberType.cpp" 60 | #include"StringType.cpp" 61 | #include"SequenceType.cpp" 62 | #include"ClassType.cpp" 63 | #include"MethodType.cpp" 64 | #include"ObjectType.cpp" -------------------------------------------------------------------------------- /doc/工作日志/20240205.md: -------------------------------------------------------------------------------- 1 | # 2024/02/05 工作日志 2 | 3 | ### 修复了一些bug 4 | 5 | 主要修复了``src/ast``中一些小瑕疵。 6 | 7 | ### 更新了依赖库 8 | 9 | 首先,新增了行阅读器(``LineReader``类)。 10 | 11 | LineReader用于逐行读取文本并送入词法分析器中,它的接口很简便: 12 | 13 | * ``String getLine()``:读取一行,并将结果以String形式返回 14 | * ``bool isMore()``:是否还有文本尚未读取,如果有,则返回true 15 | 16 | 用户在调用``getLine()``前理应先调用``isMore()``查看是否还有剩余文本;否则容易导致程序错误。 17 | 18 | 其次,更新了异常处理库。 19 | 20 | 以往的异常处理库非常简陋,设计方式是典型的面向过程。但是考虑到将来可能会多线程运行**编译器/虚拟机**,所以我将异常处理库封装为面向对象的。 21 | 22 | 宏的用法基本不变,用法过于简单,文档不再赘述,详见``Exception.cpp``。 23 | 24 | ### 编写了语法分析器 25 | 26 | 经过了将近两周的编写,语法分析器终于完工。 27 | 28 | 语法分析器的代码位于``src/compiler/Parser.cpp``当中。 Parser类(``stamon::c::Parser``)即为语法分析器的主体。 29 | 30 | ##### 怎样使用语法分析器? 31 | 32 | 在使用语法分析器之前,你需要有: 33 | 34 | * 一个用于处理异常的STMException对象 35 | * 一个已经将token全部分析完的Lexer对象 36 | * 一个初始化完毕的Matcher对象 37 | 38 | 在初始化Parser后,调用``Parse()``方法,即可进行语法分析,并返回语法树的根节点。如果语法分析出错,则返回NULL,并将错误存入STMException对象当中 39 | 40 | 详细请见测试用例``test_case/parser`` 41 | 42 | ##### 使用语法分析器的测试样例 43 | 44 | 语法分析器的测试样例位于``test_case/parser``中。 45 | 46 | 在该目录当中,附有``code.st``,这是测试用的Stamon代码。当然,你也可以更改code.st,来进行多次调试。 47 | 48 | 使用样例,应该这么做: 49 | 50 | * 将``test_case/parser/test.cpp``覆盖到``test/test.cpp``当中 51 | * 将``test_case/parser/code.st``复制并粘贴到源码根目录当中 52 | * 编译并运行(详见``doc/调试指南.md``) 53 | * 程序运行后,会将生成的AST输出到源码根目录下的``test.ast.txt`` 54 | 55 | ##### 语法分析器的原理 56 | 57 | 语法分析器主要采用递归分析,具体的文法请见``src/compiler/ebnf.txt``。 58 | 59 | ### 整理了文档 60 | 61 | 我将工作日志中的一些内容摘编成一份份文档。 62 | 63 | ### 接下来要做的事 64 | 65 | 1. 编写AST的解释器 66 | 2. 编写Token和AST的保存功能 67 | 3. 完成SFN功能 68 | 4. 编写AST的O1优化器 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stamon2 2 | 3 | ![stamon logo](logo/logo.svg) 4 | 5 | ## 关于Stamon2 6 | 7 | * CLimber-Rong在2023年二月份开发了简陋的第一代Stamon。在2023年7月份,CLimber-Rong决定开发第二代——Stamon2(以下简称Stamon) 8 | * Stamon是一门面向对象的动态编程语言。 9 | * Stamon可移植性极高,只需要实现为数不多的移植接口,Stamon就可以在你的平台上运行。 10 | 11 | ## 下载发行版 12 | 13 | 目前Stamon的发行版存在于以下站点,你可以挑选一个最适合的站点下载Stamon: 14 | 15 | * [Github Releases](https://github.com/CLimber-Rong/stamon/releases) 16 | * [CLimber-Rong.github.io](https://CLimber-Rong.github.io/resource/stamon2/releases) 17 | * [stamon-rsc.github.io](https://stamon-rsc.github.io/releases) 18 | 19 | 你也可以在 **Github Action** 中下载最新测试版。 20 | 21 | 我们希望更多人可以帮忙把Stamon的发行版挂载到自己的站点中,以此让更多地方可以流畅下载Stamon。 22 | 23 | ## 手动编译 24 | 25 | 见 [发行版编译指南](doc/发行版编译指南.md)。 26 | 27 | ## 运行方法 28 | 29 | 见 [发行版使用指南](doc/发行版使用指南.md)。 30 | 31 | ## 项目文档 32 | 33 | 文档位于doc目录里,如果你想要阅读每次提交的内容以及对源码的解释,请阅读``doc/工作日志``目录下的文档。 34 | 35 | 在阅读源码之前,可以先阅读[代码速览指南](doc/代码速览指南.md),来了解本项目的一些误区和源码阅读顺序。 36 | 37 | ## 开源项目保护 38 | 39 | 本项目已加入开源项目保护宣言(MOSPP),我们建议开发者们遵守此宣言所倡导的开源精神(当然,这**只具有道德约束力**)。 40 | 41 | ## 开发者 42 | 43 | * CLimber-Rong [Github主页](https://github.com/CLimber-Rong) [Gitee主页](https://gitee.com/QuXiangrong) 44 | * copi143 [Github主页](https://github.com/copi143) 45 | * GusemFowage [Github主页](https://github.com/GusemFowage) 46 | * min0911_ [Github主页](https://github.com/min0911Y) 47 | * wenxuanjun [Github主页](https://github.com/wenxuanjun) 48 | 49 | ## 特别致谢 50 | 51 | * TheFlySong [Github主页](https://github.com/TheFlySong) 52 | * XIAOYI12 [Github主页](https://github.com/XIAOYI1212) 53 | * 冻橘 [Github主页](https://github.com/MikanAffine) 54 | -------------------------------------------------------------------------------- /doc/工作日志/20230825.md: -------------------------------------------------------------------------------- 1 | # 2023/08/25 工作日志 2 | 3 | ### 1. 提交了一些漏洞修复 4 | 1. 修改了AnonClass节点(这些漏洞比较难解释,你可以通过对比源码来了解我更改了哪些地方) 5 | 2. 修复了部分依赖库(ArrayList.hpp和Stack.hpp) 6 | 3. 修复了AST代码,在原本的AST代码当中,将子节点的存储方式从值存储变成指针存储(详见src/ast下的源码) 7 | 4. 修复了文档的一些病句(详细请对比20230812.md) 8 | 9 | ### 2. 更新了依赖库 10 | 我添加了NumberMap,它和StringMap相似,是一个以整数为键的Map。 11 |
12 | 如果你要了解NumberMap的用法或实现,请阅读NumberMap.hpp。 13 |
14 | 当然,目前的NumberMap仍然是临时的,我会在后续做出改动。 15 |

16 | 此外,我也稍加改动了其他依赖库,让它在后续更容易改动 17 | 18 | ### 3. 数据类型定义的代码已经全部完工 19 | 代码位于src/data_type。 20 |
21 | 如果你想要直接**引用**所有数据类型定义,请直接引用DataType.hpp。
22 | 注意,所有的数据类型定义都存放在stamon::datatype这个命名空间里,所以如果你要**使用**数据类型定义,可以写以下代码 23 | 24 | ```C++ 25 | #include"DataType.hpp" //引用DataType.hpp 26 | using namespace stamon::datatype; //引用数据类型定义的命名空间 27 | ``` 28 | 29 | 30 | ### 4. 数据类型定义的设计 31 | 数据类型定义的设计比Ast节点的设计要简单得多(详见20230812.md): 32 |
33 | 有一个基类DataType,所有的具体数据类型都需要继承DataType。 34 |
35 |
36 | 接下来我们来简单介绍一下目录结构。 37 |
38 | 39 | ```C++ 40 | data_type 41 | |-DataType.hpp //在这里存放基类的定义 42 | |-NullType.hpp //空值数据类型定义 43 | |-NumberType.hpp //数字数据类型定义,具体定义了整数、单精度浮点数、双精度浮点数 44 | |-StringType.hpp //字符串数据类型定义 45 | |-SequenceType.cpp //数列数据类型定义 46 | |-MethodType.cpp //方法数据类型定义 47 | |-ClassType.cpp //类数据类型定义 48 | |-ObjectType.cpp //类对象数据类型定义 49 | ``` 50 | 51 |
52 | 如果你想要阅读数据类型定义的源码,建议按照以下顺序阅读 53 | 54 | 1. DataType.hpp 55 | 2. NullType.hpp 56 | 3. NumberType.hpp 57 | 4. StringType.hpp 58 | 5. SequenceType.cpp 59 | 6. MethodType.cpp 60 | 7. ClassType.cpp 61 | 8. ObjectType.cpp 62 | 63 | ### 5. 接下来要做的事 64 | 1. 完成STVM的内部数据结构 65 | 2. 对代码进行测试 -------------------------------------------------------------------------------- /doc/代码速览指南.md: -------------------------------------------------------------------------------- 1 | # 代码速览指南(第四版) 2 | 3 | ### 第四版前言 4 | 5 | 进一步优化了表达方式,将源码目录功能和阅读顺序统一为一个表格。 6 | 7 | ——2025年10月5日 8 | 9 | 10 | ### 第三版前言 11 | 12 | 使用了表格等更清晰的表述方式。 13 | 14 | ——2025年8月19日 15 | 16 | ### 第二版前言 17 | 18 | 去除了一些情绪化的描述,使该文档更加术语化。 19 | 20 | ——2025年7月29日 21 | 22 | ### 第一版前言 23 | 24 | 本指南将于其发布时起指导作用,直到下一版发布时将会过时。 25 | 本指南旨在让开发者在5分钟内快速了解项目架构以及阅读顺序。 26 | 27 | ——2024年10月15日 28 | 29 | ### 一些误区 30 | 31 | * 文件后缀 32 | 33 | 本项目中,移植接口的源码后缀统一为``.h``或``.hpp``格式;主体实现部分中,每一个源码子目录由一个``.hpp``文件来统领其他的``.cpp``文件(要求开发者只需包含此``.hpp``文件即可调用该子目录下的所有代码)。 34 | 35 | ``.hpp``和``.cpp``都可以用于代码的定义和实现,两个后缀名在本项目当中仅用来区分哪个文件统领了源码目录。 36 | 37 | 以上仅适用一般情况,并非强制要求。 38 | 39 | * include的使用 40 | 41 | 由于本项目并未采用“定义与实现分离开来,后期统一链接”的形式。 42 | 43 | 我们不建议开发者将此项目中的``#include``看作C/C++中的包含,而是看作import(类似java,python的设计)。 44 | 45 | 因为本项目的编译指令只有一条,不存在链接,因此我们不建议两个文件之间相互包含(请尽量编写不循环引用的代码)。 46 | 47 | ### 项目架构及源码阅读顺序 48 | 49 | 我们来介绍与源码有关的目录: 50 | 我们将用一个表格来解释每个目录的用途。目录从顶至底,也是推荐的源码目录阅读顺序。 51 | 52 | |目录名|用途| 53 | |:-|:-| 54 | |``include``|该目录用于存放移植接口的定义和不同方法的实现,此目录又有多个子目录。其中``interface``子目录用于存放移植接口的定义,其余子目录是移植接口的不同方法实现。移植接口的更多介绍位于``移植接口开发指导.md``| 55 | |``src/ast``|该目录存储抽象语法树(AST)的数据结构,主要用于语法分析。| 56 | |``src/config``|该目录存储程序运行的默认配置参数。| 57 | |``src/exception``|该目录存储报错信息的生成函数。| 58 | |``src/compiler``|该目录存储编译器的实现。| 59 | |``src/data_type``|该目录存储stamon数据类型的数据结构。| 60 | |``src/vm``|该目录存储虚拟机的实现。| 61 | |``src/sfn``|该目录存储Stamon调用虚拟机外部库的实现。| 62 | |``src/ir``|该目录存储Stamon的中间代码数据结构,中间代码生成器以及解析器。| 63 | |``src/action``|该目录用于存储分步行动机制的实现代码(包括一些文件的编解码实现)。| 64 | |``src``|该目录存储主体实现部分的代码,与移植接口实现分割开来,以此实现更强的可移植性。该目录下存放有若干子目录和项目主要文件(例如程序的入口源码)。| 65 | |``src/bin-include``|该目录存储Stamon的标准库源码。| -------------------------------------------------------------------------------- /doc/抽象语法树文件编码文档.md: -------------------------------------------------------------------------------- 1 | # 抽象语法树文件编码文档 2 | 3 | 为了节约篇幅,我将用``AST文件``来作为抽象语法树文件的简称。 4 | 5 | ### 前言 6 | 7 | AST文件里存储着一系列AST节点,一些应用程序可以将自己的语言解析成Stamon的AST文件格式,保存在文件,剩下编译流程交给Stamon的编译器即可。 8 | 9 | ### 格式 10 | 11 | 一个AST文件的编码由以下规则组成: 12 | 13 | * 每个AST文件的开头必须要有两字节的二进制码:0xABDD,此魔数用于区分于其他文件 14 | * AST文件中的数值存储采用大端存储(以Python的大端存储为标准) 15 | * AST标识是用于区分词法单元的数字标记,以``src/ast/Ast.hpp``当中的``_AstType``枚举为准。特别的,当AST标识为-1时,代表结尾单元 16 | * 每个AST单元由一字节的**AST标识**和若干字节的**AST数据**组成 17 | * **一些AST单元只有AST标识,不含有AST数据**(即一些AST单元只占一字节) 18 | * 一个AST节点及其子节点所组成的的标准结构是**父节点单元,第一个子节点单元,第二个子节点单元,......,最后一个子节点单元,结尾单元**。其中每个子节点还能再拥有子节点,因此这种结构是递归嵌套性的。 19 | * 任何AST单元都要拥有其对应的结尾单元,哪怕这个单元没有任何子节点单元。 20 | * 如果指定需要写入调试信息,则还需要遵循以下规则 21 | * * 在写入第一个单元之前,先写入该单元所在的文件和行号 22 | * * 若一个单元所在的文件与上一个单元不一样,则需要写入当前单元所在文件 23 | * * 若一个单元所在的行号与上一个单元不一样,则需要写入当前单元所在行号 24 | * * 写入当前单元所在行号,需要先写入一字节的数值-2,然后输出四字节的行号 25 | * * 写入当前单元所在文件,需要先写入一字节的数值-3,接着写入四字节的长度(文件名字节长度),最后逐字节地写入文件名 26 | 27 | 我们来特别看看拥有AST数据的AST单元(以及AST数据的构成): 28 | 29 | * AstIdentifierName:标识符节点,AST数据由**四字节的标识符长度**和**字节长度为标识符长度的标识符内容**组成。 30 | * AstNumber:数值节点,AST数据由**一字节的数值类型**和**若干字节的数值**组成。AstNumber根据数值类型(以``src/ast/LeafAst.cpp``的_NumberType为准)又分为以下几种节点: 31 | * * AstIntNumber:整数节点,数值由**四字节的数值**组成。 32 | * * AstFloatNumber:整数节点,数值由**四字节的float浮点数值**组成。 33 | * * AstDoubleNumber:整数节点,数值由**八字节的float浮点数值**组成。 34 | * AstString:字符串节点,其AST数据由**四字节的字符长度**和**字节长度为字符串长度的字符串内容**组成。 35 | * AstAnonClass:匿名类节点,AST数据由**一字节的布尔值(即father_flag)组成** 36 | * AstExpression:表达式节点,AST数据由**四字节的数值(即ass_type)组成** 37 | * AstBinary:双目运算节点,AST数据由**四字节的数值(即operator_type)组成** 38 | * AstUnary:单目运算节点,AST数据由**四字节的数值(即operator_type)组成** 39 | * AstPostfix:右值后缀节点,AST数据由**四字节的数值(即postfix_type)组成** -------------------------------------------------------------------------------- /include/interface/IArrayList.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: IArrayList.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 20/08/25 13:18 6 | Description: 动态数组接口类 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BasicPlatform.hpp" 12 | #include "StamonLib.hpp" 13 | 14 | namespace stamon::interface { 15 | 16 | template class IArrayList : public Impl { 17 | // 列表,拷贝传递 18 | public: 19 | IArrayList() 20 | : Impl() { 21 | // 构造空列表 22 | } 23 | IArrayList(int size) 24 | : Impl(size) { 25 | // 构造长度为size的列表 26 | } 27 | IArrayList(Impl impl) 28 | : Impl(move(impl)) { 29 | } 30 | void add(T value) { 31 | // 添加元素 32 | return Impl::add(move(value)); 33 | } 34 | void insert(int index, T value) { 35 | // 插入元素 36 | return Impl::insert(index, move(value)); 37 | } 38 | void erase(int index) { 39 | // 删除元素 40 | return Impl::erase(index); 41 | } 42 | T at(int index) const { 43 | // 获取元素 44 | return Impl::at(index); 45 | } 46 | bool empty() const { 47 | // 判断是否为空 48 | return Impl::empty(); 49 | } 50 | void clear() { 51 | // 清空 52 | return Impl::clear(); 53 | } 54 | int size() const { 55 | // 获取大小 56 | return Impl::size(); 57 | } 58 | IArrayList operator+(IArrayList src) const { 59 | // 使用加法拼接 60 | return IArrayList(Impl::operator+(move(src))); 61 | } 62 | IArrayList &operator+=(IArrayList src) { 63 | // 拼接并赋值给自己 64 | Impl::operator+=(move(src)); 65 | return *this; 66 | } 67 | T &operator[](int index) { 68 | // 获取元素 69 | return Impl::operator[](index); 70 | } 71 | T operator[](int index) const { 72 | // 获取元素 73 | return Impl::operator[](index); 74 | } 75 | }; 76 | 77 | } // namespace stamon::interface -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 可以更改$(COMPILER)以及$(REMOVE)来自定义编译环境 2 | COMPILER = g++ 3 | STRIP = strip 4 | UPX = upx 5 | REMOVE = del 6 | 7 | STD = c++17 8 | LINK = -I include/interface \ 9 | -I include/stdc_implemented \ 10 | -I src/ast \ 11 | -I src/data_type \ 12 | -I src/vm \ 13 | -I src/ir \ 14 | -I src/compiler \ 15 | -I src/sfn \ 16 | -I src/action \ 17 | -I src/exception \ 18 | -I src/config \ 19 | -I src \ 20 | -lm 21 | 22 | # 以下指令用于编译发行版 23 | # -static选项让编译的程序更通用,但是程序体积更大,删去可以缩小程序体积 24 | 25 | release: 26 | # 自定义生成可执行文件名(默认为stamon.exe) 27 | 28 | xcopy src\bin-include bin\include /s /e /y /i 29 | $(COMPILER) src/Main.cpp \ 30 | -o bin/stamon.exe \ 31 | -O2 \ 32 | -std=$(STD) \ 33 | -static \ 34 | $(LINK) 35 | 36 | $(STRIP) -s bin/stamon.exe 37 | 38 | release_win: 39 | # 编译Windows版本 40 | 41 | xcopy src\bin-include bin\include /s /e /y /i 42 | $(COMPILER) src/Main.cpp \ 43 | -o bin/stamon.exe \ 44 | -O2 \ 45 | -std=$(STD) \ 46 | -static \ 47 | $(LINK) 48 | 49 | $(STRIP) -s bin/stamon.exe 50 | 51 | release_linux: 52 | # 编译Linux版本 53 | mkdir -p "bin/include" 54 | cp -r -T src/bin-include bin/include 55 | $(COMPILER) src/Main.cpp \ 56 | -o bin/stamon \ 57 | -O2 \ 58 | -std=$(STD) \ 59 | -static \ 60 | $(LINK) 61 | 62 | $(STRIP) -s bin/stamon 63 | 64 | release_macos: 65 | # 编译MacOS版本 66 | mkdir -p "bin/include" 67 | cp -r "src/bin-include/" "bin/include" 68 | $(COMPILER) src/Main.cpp \ 69 | -o bin/stamon \ 70 | -O2 \ 71 | -std=$(STD) \ 72 | $(LINK) 73 | 74 | $(STRIP) bin/stamon 75 | 76 | zip_release_win: 77 | $(UPX) --best --lzma bin/stamon.exe 78 | 79 | zip_release_linux: 80 | $(UPX) --best --lzma bin/stamon 81 | 82 | zip_release_macos: 83 | $(UPX) --best --lzma bin/stamon -------------------------------------------------------------------------------- /doc/工作日志/20250127.md: -------------------------------------------------------------------------------- 1 | # 2025/01/27 工作日志 2 | 3 | 本次更新发行了``2.4.36``. 4 | 5 | 本次提交规范了项目的文档,整合并优化了项目的代码,同时修复了大量漏洞。 6 | 7 | 在这段时间的开发和思考当中,我结合了原有的开发经验,纠正了目前的开发路线: 8 | 9 | * 比起分步动作机制,将中间代码重构为STVC-TAC是更为重要的 10 | * 应该先重构编译器,再重构虚拟机 11 | * 在重构编译器之前,应该先把属性文法编写完毕 12 | 13 | ### 整理项目文档 14 | 15 | 我将一些工作日志的一些空白的头注释删去了,并且将过时的技术文档明确标记出来。 16 | 17 | 与此同时,也更新了抽象语法树文件的编码技术文档。 18 | 19 | ### 提交了词法编码文件的读取器和写入器 20 | 21 | 它们位于``src/action``目录下,分别是``TokenFileReader.cpp``和``TokenFileWriter.cpp``。 22 | 23 | 这两份源码目前仍未经过调试,并且并没有接入到项目主体当中,我会在接下来的更新当中接入它。 24 | 25 | ### 修复了一些漏洞 26 | 27 | 这些漏洞的具体内容是: 28 | 29 | * ``Main.cpp``:无法正常处理程序抛出的错误。 30 | * ``Parser.cpp``:表达式的语法分析有误。 31 | * ``SFN.cpp``:将字符串转为数字时,未检查格式。 32 | 33 | ### 纠正了一些不致命的错误,对部分代码做出了调整 34 | 35 | 这些调整的具体内容是: 36 | 37 | * ``Main.cpp``:补全了Stamon的帮助信息。 38 | * ``Stamon.hpp``:利用依赖库的接口实现了数组拼接。 39 | * ``ExprAst.cpp``:规范了枚举代码 40 | * ``DataType.hpp``:规范了枚举代码 41 | 42 | ### 让更多错误留在编译期 43 | 44 | 在以往的Stamon版本当中,若将``continue``或``break``写在循环体外,编译仍然可以通过,只有在虚拟机执行时才会报错。 45 | 46 | 为此,我在``Parser.cpp``当中加入了检查``continue``和``break``是否都在循环体中的功能。让更多致命性的错误留在编译期。 47 | 48 | ### 重构虚拟机的计算系统 49 | 50 | 以往的计算系统的实现代码非常冗余、且在计算时会产生大量的中间对象。 51 | 52 | 为此,我建立了``TypeCalculator.cpp``,将计算系统迁移到了这里(并与``AstRunner.cpp``对接),同时优化了代码的效率和可读性,确保每一次运算不会产生任何中间对象,即一步到位,直接产生结果对象。 53 | 54 | 计算过程中的类型判断和精度转换是个让人头疼的问题,但是我在``TypeCalculator.cpp``中用一种优雅的方式解决了它。 55 | 56 | 在重构计算系统之后,我让``2.4.31``版本与``2.4.36``版本运行百万次空循环,又递归计算了第二十项斐波那契数列,在计算期间统计了对象的申请次数。实验结果表明:**重构后的申请次数普遍低于重构前的申请次数的50%**。 57 | 58 | ### 还有更多地方有待优化 59 | 60 | ``ArrayList.hpp``的标准C实现以及``AstRunner.cpp``容易出现内存泄漏。我打算将前者改进成安全的实现,而并不打算处理后者,因为``AstRunner.cpp``将会随着重构而被弃用。 61 | 62 | ### 接下来要做的事 63 | 64 | 1. 优化``ArrayList.hpp``的标准C实现 65 | 2. 开发Stamon的安装工具 66 | 3. 推进Stamon包管理器的开发 67 | 4. 支持文件处理库 -------------------------------------------------------------------------------- /include/interface/Exception.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: Exception.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 24/08/23 18:09 6 | Description: 异常处理,该类无需特殊实现 7 | */ 8 | #pragma once 9 | 10 | #include "ArrayList.hpp" 11 | #include "StamonLib.hpp" 12 | #include "BasicPlatform.hpp" 13 | #include "String.hpp" 14 | 15 | /* 16 | CATCH的使用方法: 17 | CATCH { 18 | //在这里写上捕捉异常的代码 19 | } 20 | THROW(info)用于抛出异常,异常信息为info 21 | WARN(info)用于抛出警告,警告信息为info 22 | ISWARNING用于判断是否有警告被抛出,是则为true,否则为false 23 | */ 24 | 25 | /* 26 | * 开发者只需要在当前代码所在的作用域中定义STMException* ex 27 | * 即可使用以下的宏 28 | */ 29 | 30 | #define THROW(info) ex->Throw(info); 31 | #define CATCH if (ex->isError) 32 | 33 | #define WARN(info) ex->Warn(info); 34 | #define ISWARNING (ex->isWarning) 35 | 36 | namespace stamon { 37 | 38 | class STMInfo { 39 | // 一个信息的组成,通常用于异常和日志系统 40 | public: 41 | String sender; // 信息发送者 42 | String type; // 信息类型 43 | String message; // 信息内容 44 | String position; // 信息位置 45 | STMInfo() { 46 | } 47 | STMInfo(const String &Sender, const String &Type, const String &Message, 48 | const String &Position) 49 | : sender(Sender) 50 | , type(Type) 51 | , message(Message) 52 | , position(Position) { 53 | } 54 | String toString() { 55 | return type + String(": from ") + sender + String(": at ") + position 56 | + String(": ") + message; 57 | } 58 | }; 59 | 60 | class STMException { 61 | public: 62 | STMInfo Exception; // 异常 63 | ArrayList Warning; // 一系列的警告 64 | bool isError = false; 65 | bool isWarning = false; 66 | 67 | void Throw(STMInfo info) { 68 | Exception = info; 69 | isError = true; 70 | } 71 | 72 | void Warn(STMInfo info) { 73 | Warning.add(info); 74 | isWarning = true; 75 | } 76 | 77 | STMInfo getError() { 78 | return Exception; 79 | } 80 | 81 | ArrayList getWarning() { 82 | return Warning; 83 | } 84 | }; 85 | 86 | } // namespace stamon -------------------------------------------------------------------------------- /doc/工作日志/20240207.md: -------------------------------------------------------------------------------- 1 | # 2024/02/07 工作日志 2 | 3 | > 警告:预处理器已被弃用,其相关文档也已经过时,详情见``工作日志/20240510.md``。——2025/03/02 4 | 5 | ### 更新了依赖库 6 | 7 | 本次新增了``FileMap``类,扩充了``stmlib.hpp``、``ArrayList.hpp``。 8 | 9 | ``stmlib.hpp``主要更新了各种数据类型(诸如``int``、``bool``)转为String的方法。 10 | 11 | ``ArrayList.hpp``重载了``+``运算符和``+=``运算符(详见源码)。 12 | 13 | 在stamon中导入一个代码文件,需要输入对应的文件位置。``FileMap``就负责处理这份工作。 14 | 15 | 当stamon导入一个新的代码文件时会向FileMap进行申请(即调用``AddSource``方法),FileMap会检查这个代码文件是否已经被导入。如果没有,则会标记这个文件“已经被导入过”,并且返回打开这个文件的``LineReader``。(详见``LineReader.cpp``) 16 | 17 | ### 新增了预处理器 18 | 19 | 分析一个单独的源码文件,我们可以使用Parser。 20 | 21 | 但是如果若干个源码互相引用,构建出了一个项目。则需要用预处理器来对整个项目进行分析。 22 | 23 | 本次新增了``Preprocessor.cpp``和``ParsingQueue.cpp``个文件。 24 | 25 | ##### 预处理器原理 26 | 27 | 我们先来看看``ParsingQueue.cpp``。 28 | 29 | ParsingQueue类用于Parser和Preprocessor两个类交互数据。这个类存储着带分析的源码。 30 | 31 | Preprocessor每次会从ParsingQueue当中弹出一个待分析的源码。并用Parser进行分析。 32 | 33 | Parser在分析该源码的过程中,如果要导入新的文件,会把这个文件加入到ParsingQueue中。 34 | 35 | 由此我们实现了多个文件的编译能力。 36 | 37 | ##### 怎样使用Preprocessor 38 | 39 | * ``Preprocessor(STMException* e)``:构造函数。 40 | 41 | * ``ArrayList ParseSource(String origin, bool isSupportImport)``:分析整个源码项目。``origin``为分析的入口文件名(即整个项目的总文件);``isSupportImport``这个代表这个项目是否支持引用,如果该值为false,代表只支持分析一个源文件,否则代表支持分析多个文件。 42 | 43 | * ``ArrayList ErrorMsg``:在分析一个项目的过程中,难免会有多个源文件出现多个报错。这个数组就存储着所有的报错。如果整个项目没有任何报错则该数组为空。 44 | 45 | 预处理器的具体用法和案例,详见测试样例``test_case/preprocessor/`` 46 | 47 | ##### 使用预处理器的测试样例 48 | 49 | 预处理器的测试样例位于``test_case/preprocessor/``中。 50 | 51 | 在该目录当中,附有``code.st``,这是测试用的Stamon代码。当然,你也可以更改code.st,来进行多次调试。 52 | 53 | 使用样例,应该这么做: 54 | 55 | * 将``test_case/preprocessor/test.cpp``覆盖到``test/test.cpp``当中 56 | * 将``test_case/preprocessor/code.st``复制并粘贴到源码根目录当中 57 | * 编译并运行(详见``doc/调试指南.md``) 58 | * 程序运行后,会将生成的AST输出到源码根目录下的``test.ast.txt`` 59 | 60 | ### 接下来要做的事 61 | 62 | 1. 编写AST的解释器 63 | 2. 编写Token和AST的保存功能 64 | 3. 完成SFN功能 65 | 4. 编写AST的O1优化器 -------------------------------------------------------------------------------- /doc/对象管理器文档.md: -------------------------------------------------------------------------------- 1 | # 对象管理器文档 2 | 3 | 对象管理器的主要文件是src/data_type目录下的Variable.cpp和src/vm目录下的ObjectManager.cpp。 4 | 5 | 其中,ObjectManager.cpp基于Variable.cpp。 6 | 7 | ### 数据结构 8 | 9 | ``Variable``是一个非常简单的类,它有一个名为```data```的```DataType*```成员,所以对于任意左值,我们只需要得到这个左值的```Variable*```值,就可以对这个左值进行赋值(而不需要知道这个左值的具体信息)。 10 | 11 | 比如,对于一个名为```var```的```Variable*```对象和一个名为```dat```的```DataType*```对象,想要把```var```(也就是左值)赋值为```dat```(也就是右值),只需要这么做: 12 | 13 | ```C++ 14 | var->data = dat; 15 | ``` 16 | 17 | **换句话说,每个左值都对应着一个```Variable*```对象,而可以通过给这个对象的data成员赋值,从而达到给该左值赋值的目的** 18 | 19 | ### 核心原理 20 | 21 | ``ObjectManager.cpp``编写了对象管理器的本体。对象管理器包含了GC机制以及作用域管理功能。虚拟机的对象应向对象管理器申请。 22 | 23 | GC所采用的算法是**标记-清除法**。我用一个栈来维护运行时所有的作用域。新建或退出一个作用域时只需要入栈或出栈即可。寻找某个变量也只需要从栈顶找到栈底就行。 24 | 25 | 以下是ObjectManager.cpp的核心接口: 26 | 27 | ##### 对象管理接口 28 | 29 | |函数原型|解释| 30 | |:-|:-| 31 | |``ObjectManager(bool isGC, unsigned long long mem_limit, int pool_cache_size, STMException* e)``|``isGC``代表对象管理器是否执行GC,若执行GC,则``mem_limit``有意义,且代表GC的内存限制,若不执行GC,则对象管理器会一直运行直到内存溢出。pool_cache_size代表内存池的缓存大小。对pool_cache_size的具体解释应看``MemoryPool.hpp``的实现| 32 | |``datatype::NullType* getNullConst()``|获取``null``常量| 33 | |``template bool GCConditions(T* object)``|新建对象后,对象管理器会把这个对象交给``GCConditions``函数,根据函数返回判断是否需要GC。该函数可交给开发者自定义。虚拟机默认的条件为:对象占用内存+GC预留内存大等于内存限制时。| 34 | |``void GC()``|执行GC| 35 | |``template T* MallocObject(Types&& ...args)``|申请一个对象,需要提供申请的对象类型和初始化参数,例如``MallocObject(1)``代表申请一个类型为整数,值为1的对象| 36 | 37 | ##### 作用域管理 38 | 39 | |函数原型|解释| 40 | |:-|:-| 41 | |``Variable* NewVariable(int id)``|新建一个变量,默认为空值| 42 | |``Variable* NewVariable(int id, datatype::DataType* val)``|新建一个变量,并赋值为``val``| 43 | |``Variable* getVariable(int id)``|获取变量的值| 44 | |``void pushScope()``|压入一个空作用域| 45 | |``void popScope()``|**销毁并弹出**一个作用域| 46 | |``void pushMemberTabScope(ObjectScope s)``|压入一个指定为``s``的作用域,一般用于类对象初始化时| 47 | |``void popMemberTabScope()``|弹出一个作用域**但不销毁**,一般用于类对象初始化时| 48 | |``ObjectScope getTopScope()``|获取栈顶的作用域| -------------------------------------------------------------------------------- /doc/发行版使用指南.md: -------------------------------------------------------------------------------- 1 | # 发行版使用指南 2 | 3 | ### 配置运行时环境 4 | 5 | Stamon的运行时环境非常简便,您只需要配置至少一个环境变量即可使用: 6 | 7 | * ``STAMON``变量:该变量指向可执行文件所在的**目录(而非文件)** 8 | * **[非必要]** ``PATH``变量:在该变量末尾**追加**上可执行文件所在目录 9 | 10 | 如果不配置PATH变量,则Stamon无法全局使用(即只能在可执行文件所在目录下使用),**因此我们强烈建议您配置PATH变量** 11 | 12 | ### 运行指令 13 | 14 | Stamon目前支持以下指令: 15 | 16 |
17 | 18 | * ``stamon version``或``stamon -v`` 19 | 20 | 该指令用于输出Stamon当前的版本号。 21 | 22 |
23 | 24 | * ``stamon help``或``stamon -h`` 25 | 26 | 该指令用于输出Stamon的用法以及各种参数的介绍。 27 | 28 |
29 | 30 | * ``stamon build [src] [dst] [options...]``或``stamon -b [src] [dst] [options...]`` 31 | 32 | 该指令用于编译Stamon代码,其中src为必要参数,表示编译的Stamon文件名,dst为可选参数,表示生成的字节码文件名(默认a.stvc),options为可选参数,他们包括: 33 | 34 | |参数用法|功能|默认值|支持此参数的最低版本| 35 | |-|-|-|-| 36 | |--import=<boolean>|是否支持引用源码|true|2.4.4| 37 | |--strip=<boolean>|是否剥削字节码调试信息|false|2.4.4| 38 | |-I<path>|待引用源码所在的路径|无|2.4.4| 39 | |--IgnoreWarning|忽略警告|默认只进行警告|2.4.28| 40 | |--JustWarn|只进行警告|默认只进行警告|2.4.28| 41 | |--FatalWarning|将警告作为致命错误|默认只进行警告|2.4.28| 42 | |--locale=<text>|指定程序运行的语言环境|默认为当前系统的语言环境|2.4.29| 43 | 44 | 45 |
46 | 47 | * ``stamon run [src] [options...]``或``stamon -r [src] [options...]`` 48 | 49 | 该指令用于运行STVC文件,其中src为必要参数,表示编译的Stamon文件名,options为可选参数,他们包括: 50 | 51 | |参数用法|功能|默认值|支持此参数的最低版本| 52 | |-|-|-|-| 53 | |--GC=<boolean>|是否运行GC(垃圾自动回收)|true|2.4.4| 54 | |--MemLimit=<Integer>|设置虚拟机的对象内存限制(按字节计)|默认为16,777,216字节,即16MiB|2.4.4| 55 | |--MemPoolCache=<Integer>|设置内存池缓存大小(按字节计)|默认为16,777,216字节,即16MiB|2.4.19| 56 | |--IgnoreWarning|忽略警告|默认只进行警告|2.4.28| 57 | |--JustWarn|只进行警告|默认只进行警告|2.4.28| 58 | |--FatalWarning|将警告作为致命错误|默认只进行警告|2.4.28| 59 | |--locale=<text>|指定程序运行的语言环境|默认为当前系统的语言环境|2.4.29| 60 | 61 |
62 | 63 | * ``stamon strip [src] [options...]``或``stamon -s [src]`` 64 | 65 | 该指令用于剥削STVC调试信息,其中src为必要参数,表示待剥削的STVC文件,options为可选参数,他们包括: 66 | 67 | |参数用法|功能|默认值|支持此参数的最低版本| 68 | |-|-|-|-| 69 | |--IgnoreWarning|忽略警告|默认只进行警告|2.4.28| 70 | |--JustWarn|只进行警告|默认只进行警告|2.4.28| 71 | |--FatalWarning|将警告作为致命错误|默认只进行警告|2.4.28| -------------------------------------------------------------------------------- /src/action/ConstTabWriter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ConstTabWriter.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 05/08/25 15:01 6 | Description: 常量表编码器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "AstIr.cpp" 12 | #include "BufferStream.cpp" 13 | #include "DataType.hpp" 14 | #include "Exception.hpp" 15 | #include "String.hpp" 16 | 17 | namespace stamon::action { 18 | class ConstTabWriter { 19 | BufferOutStream &stream; 20 | 21 | public: 22 | STMException *ex; 23 | 24 | ConstTabWriter(STMException *e, BufferOutStream &outstream) 25 | : ex(e) 26 | , stream(outstream) { 27 | } 28 | 29 | void write(ArrayList tableconst) { 30 | stream.write(tableconst.size()); 31 | 32 | for (int i = 0, len = tableconst.size(); i < len; i++) { 33 | stream.write((byte) tableconst[i]->getType()); 34 | 35 | switch (tableconst[i]->getType()) { 36 | case datatype::IntegerTypeID: { 37 | int val = ((datatype::IntegerType *) tableconst[i])->getVal(); 38 | stream.write(val); 39 | break; 40 | } 41 | 42 | case datatype::FloatTypeID: { 43 | float val = ((datatype::FloatType *) tableconst[i])->getVal(); 44 | stream.write(val); 45 | break; 46 | } 47 | 48 | case datatype::DoubleTypeID: { 49 | double val = ((datatype::DoubleType *) tableconst[i])->getVal(); 50 | stream.write(val); 51 | break; 52 | } 53 | 54 | case datatype::StringTypeID: { 55 | String s = ((datatype::StringType *) tableconst[i])->getVal(); 56 | stream.write(s.length()); 57 | for (int i = 0, len = s.length(); i < len; i++) { 58 | stream.write(s[i]); 59 | } 60 | break; 61 | } 62 | 63 | case ir::IdenConstTypeID: { 64 | String s = ((ir::IdenConstType *) tableconst[i])->getVal(); 65 | stream.write(s.length()); 66 | for (int i = 0, len = s.length(); i < len; i++) { 67 | stream.write(s[i]); 68 | } 69 | break; 70 | } 71 | 72 | default: { 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | }; 79 | } // namespace stamon::action -------------------------------------------------------------------------------- /include/stdc_implemented/BasicPlatform.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: BasicPlatform.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 31/08/25 22:04 6 | Description: 基本的平台库 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "IBasicPlatform.hpp" 12 | #include "StamonLib.hpp" 13 | #include "stdint.h" 14 | #include "stdio.h" 15 | #include "stdlib.h" 16 | #include "string.h" 17 | 18 | // 重载new和delete 19 | 20 | namespace stamon::stdc { 21 | inline void *alloc(unsigned int size) noexcept { 22 | void *ptr = malloc(size); 23 | if (ptr == NULL) { 24 | puts("STAMON: FATAL ERROR: FROM ALLOC: NO MORE MEMORY!\n"); 25 | exit(-1); 26 | } 27 | return ptr; 28 | } 29 | } // namespace stamon::stdc 30 | 31 | #ifndef NEWDELETE_DEFINED 32 | 33 | // 如果第三方库本身已经引入了new和delete的重载,那么就不用重复实现 34 | 35 | void *operator new(stamon::size_t size) { 36 | return stamon::stdc::alloc(size); 37 | } 38 | 39 | void *operator new[](stamon::size_t size) { 40 | return stamon::stdc::alloc(size); 41 | } 42 | 43 | void *operator new(stamon::size_t size, void *ptr) { 44 | return ptr; 45 | } 46 | 47 | void *operator new[](stamon::size_t size, void *ptr) { 48 | return ptr; 49 | } 50 | 51 | void operator delete(void *ptr) { 52 | free(ptr); 53 | } 54 | 55 | void operator delete[](void *ptr) { 56 | free(ptr); 57 | } 58 | 59 | #endif 60 | 61 | namespace stamon { 62 | 63 | // 字节类型定义 64 | using byte = Ibyte; 65 | 66 | // 哈希函数定义 67 | unsigned int toHash(char x) { 68 | return (unsigned int) x; 69 | } 70 | unsigned int toHash(int x) { 71 | return (unsigned int) x; 72 | } 73 | unsigned int toHash(unsigned int x) { 74 | return x; 75 | } 76 | unsigned int toHash(float x) { 77 | unsigned int rst; 78 | memcpy(&rst, &x, sizeof(unsigned int)); 79 | return rst; 80 | } 81 | unsigned int toHash(double x) { 82 | unsigned long long rst; 83 | memcpy(&rst, &x, sizeof(unsigned long long)); 84 | return (unsigned int) (rst) ^ (unsigned int) (rst >> 32); 85 | } 86 | unsigned int toHash(byte x) { 87 | return (unsigned int) x; 88 | } 89 | 90 | } // namespace stamon 91 | -------------------------------------------------------------------------------- /src/action/AstIrWriter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: AstIrWriter.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 09/12/23 17:34 6 | Description: IR写入器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "AstIr.cpp" 12 | #include "BufferStream.cpp" 13 | #include "ConstTabWriter.cpp" 14 | #include "DataType.hpp" 15 | 16 | namespace stamon::action { 17 | class AstIrWriter { 18 | BufferOutStream& stream; 19 | 20 | public: 21 | STMException *ex; 22 | 23 | AstIrWriter(STMException *e, BufferOutStream &outstream) 24 | : ex(e) 25 | , stream(outstream) { 26 | stream.write((byte) 0xAB); 27 | stream.write((byte) 0xDB); 28 | } 29 | 30 | void write(ArrayList ir_list, 31 | ArrayList ir_tableconst, bool isStrip, 32 | int VerX, int VerY, int VerZ) { 33 | // 写入版本 34 | 35 | stream.write(VerX); 36 | stream.write(VerY); 37 | stream.write(VerZ); 38 | 39 | // 写入常量表 40 | 41 | ConstTabWriter tabwriter(ex, stream); 42 | tabwriter.write(ir_tableconst); 43 | 44 | // 最后写入IR 45 | 46 | int lineNo = -1; // 当前正在写入的行号 47 | String filename; // 当前正在写入的文件名 48 | 49 | for (int i = 0, len = ir_list.size(); i < len; i++) { 50 | if (isStrip == false) { 51 | // 要求附加调试信息 52 | 53 | if (lineNo != ir_list[i].lineNo && ir_list[i].type != -1) { 54 | // 注意:结束符不用在意行号和文件名 55 | // 这里要特判结束符(即ir_list[i].type!=-1) 56 | 57 | // 如果行号没更新,更新行号并且将当前行号写入 58 | lineNo = ir_list[i].lineNo; 59 | 60 | stream.write(-2); //-2代表更新行号 61 | stream.write(lineNo); 62 | } 63 | 64 | if (filename.equals(ir_list[i].filename) == false 65 | && ir_list[i].type != -1) { 66 | // 检查文件名,原理同上 67 | filename = ir_list[i].filename; 68 | 69 | stream.write(-3); //-2代表更新文件 70 | stream.write(filename.length()); 71 | 72 | for (int i = 0, len = filename.length(); i < len; i++) { 73 | stream.write(filename[i]); 74 | } 75 | } 76 | } 77 | 78 | stream.write(ir_list[i].type); 79 | stream.write(ir_list[i].data); 80 | } 81 | } 82 | }; 83 | } // namespace stamon::action -------------------------------------------------------------------------------- /src/action/TokenFileWriter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: TokenFileWriter.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 28/12/24 17:01 6 | Description: 词法单元编码文件写入器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BufferStream.cpp" 12 | #include "Exception.hpp" 13 | #include "String.hpp" 14 | #include "Token.cpp" 15 | 16 | namespace stamon::action { 17 | class TokenFileWriter { 18 | BufferOutStream& stream; 19 | 20 | public: 21 | STMException *ex; 22 | 23 | TokenFileWriter() { 24 | } 25 | 26 | TokenFileWriter(STMException *e, BufferOutStream &outstream) 27 | : ex(e) 28 | , stream(outstream) { 29 | stream.writeArray((byte) 0xAB); 30 | stream.write((byte) 0xDC); 31 | } 32 | 33 | void writeToken(c::Token *tok) { 34 | byte id = tok->type; 35 | 36 | stream.write(id); 37 | 38 | // 接着特判带有词法单元数据的词法单元 39 | 40 | switch (id) { 41 | case c::TokenInt: { 42 | // 特判整数token 43 | stream.write(((c::IntToken *) tok)->val); 44 | break; 45 | } 46 | 47 | case c::TokenDouble: { 48 | // 特判浮点token 49 | stream.write(((c::DoubleToken *) tok)->val); 50 | break; 51 | } 52 | 53 | case c::TokenString: { 54 | String val = ((c::StringToken *) tok)->val; 55 | 56 | stream.write(val.length()); 57 | 58 | for (int index = 0; index < val.length(); index++) { 59 | stream.write(val[index]); 60 | } 61 | break; 62 | } 63 | 64 | case c::TokenIden: { 65 | // 特判标识符token,其实现与字符串token类似 66 | 67 | String iden = ((c::IdenToken *) tok)->iden; 68 | 69 | stream.write(iden.length()); 70 | 71 | for (int index = 0; index < iden.length(); index++) { 72 | stream.write(iden[index]); 73 | } 74 | break; 75 | } 76 | 77 | default: { 78 | break; 79 | } 80 | } 81 | } 82 | 83 | void writeLineTokens(ArrayList tokens) { 84 | // 写入一行的Tokens 85 | 86 | for (int i = 0, len = tokens.size(); i < len; i++) { 87 | writeToken(tokens[i]); 88 | } 89 | 90 | stream.write((byte) -1); // 写入EOL 91 | } 92 | 93 | void writeEOF() { 94 | stream.write(c::TokenEOF); 95 | } 96 | }; 97 | } // namespace stamon::action -------------------------------------------------------------------------------- /doc/AstRunner开发文档.md: -------------------------------------------------------------------------------- 1 | # AstRunner开发文档 2 | 3 | > 注意:此文档旨在说明``AstRunner``(以下简称“虚拟机”)的工作原理 4 | 5 | 虚拟机的运行原理为:将二进制文件读取为AstIr,交给AstIrConverter类解析为Running-Ast,最后交给``vm/AstRunner.cpp``递归运行。 6 | 7 | 我们规定:Stamon编译后的二进制文件为**STVC文件**,文件后缀为``.stvc`` 8 | 9 | 我们来逐步讲解。 10 | 11 | 首先是二进制文件读取为AstIr,这部分的实现位于``src/action/AstIrReader.cpp``,AstIrReader类的主要接口有: 12 | 13 | * ``AstIrReader(STMException *e, String filename)``:构造函数,filename为文件名 14 | * ``bool readHeader()``:读取字节码头,读取失败则抛出异常并返回false,否则返回true 15 | * ``ArrayList ReadIR()``:读取AstIr,返回由AstIr组成的ArrayList 16 | 17 | 想要完整的读取一个STVC文件,应该要先创建一个AstIrReader对象,然后先调用``readHeader``读取文件头信息,接着调用``readIR``来读取AstIr。调用这两个函数之后要分别检查是否有异常抛出。 18 | 19 | 接着是让AstIrConverter类解析为Running-Ast,这一部分在**写了Ast与AstIr之间的互转工具**部分里已经详细提及过了,故不再赘述。 20 | 21 | 最后是交给``vm/AstRunner.cpp``递归运行,``AstRunner``类采用了和语法分析器类似的结构,下面我们来看看重点的数据接口及接口: 22 | 23 | AstRunner在递归执行Ast时的返回值为``RetStatus``类。RetStatus,全称*Return-Status(返回状态)*,用于指示当前代码运行状况,我们来看看RetStatus的定义: 24 | 25 | ```C++ 26 | class RetStatus { //返回的状态(Return Status) 27 | //这个类用于运行时 28 | public: 29 | int status; //状态码 30 | VariablePtr retval; //返回值(Return-Value),无返回值时为NULL 31 | RetStatus() : retval(NullVariablePtr()) {} 32 | RetStatus(const RetStatus& right) : retval(right.retval) { 33 | status = right.status; 34 | } 35 | RetStatus(int status_code, VariablePtr retvalue) 36 | : retval(retvalue) { 37 | status = status_code; 38 | } 39 | }; 40 | ``` 41 | 42 | 其中的``int status``一行用于存储状态码,状态码有以下几类: 43 | 44 | ```C++ 45 | enum RET_STATUS_CODE { //返回的状态码集合 46 | RetStatusErr = -1, //错误退出(Error) 47 | RetStatusNor, //正常退出(Normal) 48 | RetStatusCon, //继续循环(Continue) 49 | RetStatusBrk, //退出循环(Break) 50 | RetStatusRet //函数返回(Return) 51 | }; 52 | ``` 53 | 54 | AstRunner的主要接口有: 55 | 56 | ```C++ 57 | RetStatus execute( 58 | AstNode* main_node, bool isGC, int vm_mem_limit, 59 | ArrayList tableConst, ArrayList args, 60 | STMException* e 61 | ); 62 | ``` 63 | 64 | 虚拟机在执行过程中会向ObjectManager申请对象,来实现GC机制。详见``对象管理器文档.md`` -------------------------------------------------------------------------------- /src/compiler/ebnf.txt: -------------------------------------------------------------------------------- 1 | program: { statement } 2 | 3 | statement: def_var | def_func | def_class 4 | | (expression ";") 5 | | statement_if | statement_while | statement_for 6 | | "break;" | "continue;" | ";" 7 | | sfn | statement_return 8 | | extern_def 9 | 10 | extern_def: "extern" IDEN { "," IDEN } ";" 11 | 12 | sfn: "sfn" IDEN "," IDEN ";" 13 | 14 | block: "{" {statement} "}" 15 | | ":" statement 16 | 17 | def_var: "def" IDEN "=|+=|-=|/=|%=|&=|^=|\|=|<<=|>>=" expression { "," IDEN "=" expression } ";" 18 | def_func: "func" IDEN [ "(" IDEN { "," IDEN } ")" ] block 19 | def_class: IDEN "class" IDEN "{" def_var | def_func | def_class "}" 20 | 21 | statement_if: "if" expression block [ statement_else ] 22 | statement_else: "else" block 23 | statement_while: "while" expression block 24 | statement_for: "for" IDEN "in" expression block 25 | statement_return: "return" expression ";" 26 | 27 | expression: ( left_value "=" expression ) 28 | | binary_operator 29 | left_value: IDEN { left_postfix } 30 | left_postfix: ( "." IDEN ) | ( "[" expression "]" ) 31 | 32 | binary_operator: ( binary_operator "op..." other_binary_operators ) 33 | | other_binary_operators 34 | 35 | ...此处省略双目运算符的EBNF,双目运算符最后会匹配到unary_operator... 36 | 37 | unary_operator: ( "+|-|~|!" unary_operator ) 38 | | ( quark { postfix } ) 39 | 40 | postfix: arguments 41 | | ( "[" expression "]" ) 42 | | ( "." IDEN ) 43 | | ( ".new" [arguments] ) 44 | 45 | arguments: "(" [ expression { "," expression } ] ")" 46 | 47 | quark: "(" expression ")" 48 | | IDEN 49 | | NUM 50 | | STRING 51 | | NULL 52 | | TRUE | FALSE 53 | | array_iteral 54 | | list_literal 55 | | anon_func 56 | | anon_class 57 | 58 | array_literal: "[" expression "]" 59 | list_literal: "{" [ expression { "," expression } ] "}" 60 | anon_func: "func" [ "(" IDEN { "," IDEN } ")" ] block 61 | anon_class: "class" [ "extends" IDEN ] "{" { def_var | def_func | def_class } "}" -------------------------------------------------------------------------------- /doc/SFN机制文档.md: -------------------------------------------------------------------------------- 1 | # SFN机制文档 2 | 3 | ### 定义 4 | 5 | SFN,全称Stamon For Native。是Stamon的一个调用外部功能的机制。你可以用它与解释器交互。 6 | 7 | SFN的源码位于``src/sfn/SFN.cpp``。 8 | 9 | SFN在Stamon中的语法规定为``sfn port, arg;``,其中port必须为字符串,代表着端口号,使用不同的端口号会调用不同的本地库,arg则是参数,具体类型由本地库的要求而定。在调用SFN后,arg可能会变为调用后的结果。 10 | 11 | ### 核心接口 12 | 13 | SFN类的主要接口有: 14 | 15 | * ``SFN(STMException *e, vm::ObjectManager *objman)``:构造函数objman为当前运行时的ObjectManager对象。 16 | * ``void call(String port, datatype::Variable *arg)``:根据端口号调用本地库。 17 | 18 | ### 自定义外部功能 19 | 20 | 我们以实现一个输出功能为例。 21 | 22 | ##### 定义外部函数 23 | 24 | 想要自定义外部功能,需要先编写一个参数和返回值都符合规范的,位于全局作用域上的外部函数。由于参数列表过长,因此提供了如下宏: 25 | 26 | ```C++ 27 | #define SFN_PARA_LIST \ 28 | stamon::sfn::SFN &sfn, stamon::datatype::Variable *arg, \ 29 | stamon::vm::ObjectManager *manager 30 | 31 | // 用这个宏(SFN Parameter List)可以快速声明SFN函数的参数列表 32 | ``` 33 | 34 | 其中,sfn是调用此外部函数的sfn类,arg是参数变量,manager是对象管理器。 35 | 36 | 外部函数的返回值类型必须为``void``。因此,该输出功能的函数原型是: 37 | 38 | ```C++ 39 | void sfn_print(SFN_PARA_LIST); 40 | ``` 41 | 42 | 该定义应作为前置定义放在``SFN``类的实现之前 43 | 44 | ##### 实现外部函数 45 | 46 | 外部函数可以使用移植接口上的函数,对于移植在平台上的开发者而言,也可以引入更多平台特有的函数进行操作。外部函数应该使用``stamon::exception::sfn::SFNError``作为异常生成函数,开发者也可以引入更多细化的异常生成函数。 47 | 48 | ``sfn_print``的实际实现如下: 49 | 50 | ```C++ 51 | void sfn_print(SFN_PARA_LIST) { 52 | 53 | using namespace stamon; 54 | 55 | STMException *ex = sfn.ex; 56 | datatype::DataType *val = arg->data; 57 | if (val->getType() != datatype::StringTypeID) { 58 | THROW(exception::sfn::SFNError( 59 | "sfn_print()", "invalid type")); 60 | return; 61 | } 62 | platform_print(((datatype::StringType *) val)->getVal()); 63 | return; 64 | } 65 | ``` 66 | 67 | 该实现应该放在``SFN``类的实现之后 68 | 69 | ##### 绑定外部函数 70 | 71 | 实现完外部函数后,就要将其绑定在SFN上,以便调用时能找到并调用该外部函数。外部函数需要绑定在SFN的构造函数中。SFN的构造函数代码以如下为例: 72 | 73 | ```C++ 74 | SFN(STMException *e, vm::ObjectManager *objman) { 75 | ex = e; 76 | 77 | manager = objman; 78 | 79 | // 在这里将库函数按接口号填入 80 | sfn_functions.put(String("print"), sfn_print); 81 | sfn_functions.put(String("int"), sfn_int); 82 | sfn_functions.put(String("str"), sfn_str); 83 | //...略... 84 | } 85 | ``` 86 | 87 | ``sfn_functions``是一个Map,存储的值类型是符合外部函数原型的函数指针。在构造函数中加入利用``sfn_functions.put``来指定端口号及其对应的外部函数 -------------------------------------------------------------------------------- /doc/发行版编译指南.md: -------------------------------------------------------------------------------- 1 | # 发行版编译指南 2 | 3 | 目前已知支持编译Windows、Linux、Macos系统。 4 | 5 | 我们提供了三种编译方法:使用Makefile或CMake或XMake。 6 | 7 | > CMakeList由[copi143](https://github.com/copi143)提供,谢谢! 8 | 9 | ## 使用XMake编译 10 | 11 | ### 需要准备的环境 12 | 13 | 在编译前,请确保你有: 14 | 15 | - **XMake** 用于构建程序 **工具参考版本:xmake v2.9.6** 16 | - **C++编译器** 一个合适的本地编译器 **工具参考版本:g++ 13.1.0** 17 | 18 | ### 开始编译 19 | 20 | 执行以下命令进行构建: 21 | 22 | ``xmake`` 23 | 24 | 使用``xmake run ...``即可运行程序。 25 | 26 | > 注:请在运行前确保``src/bin-include``中的文件,被复制到可执行文件所在目录的``include``目录中。 27 | 28 | ## 使用CMake编译 29 | 30 | ### 需要准备的环境 31 | 32 | 在编译前,请确保你有: 33 | 34 | - **CMake** 用于生成构建脚本 **工具参考版本:CMake 3.29.5** 35 | - **Make** 或 **Ninja** 用于执行构建脚本 **工具参考版本:GNU Make 3.82.90 或 Ninja 1.12.1** 36 | - **C++编译器** `gcc` `clang` `msvc` 三选一 **工具参考版本:g++ 13.1.0** 37 | - **strip** 用于剥削调试信息,减小可执行文件体积 **工具参考版本:GNU strip (GNU Binutils) 2.39** 38 | 39 | 如果你未安装strip,cmake可能会报错,属正常现象,发行版仍然会编译成功。**如果你希望发行版尽可能小,我们还是建议你安装strip。** 40 | 41 | ### 开始编译 42 | 43 | 依次执行以下命令 44 | 45 | ```sh 46 | git clone https://github.com/CLimber-Rong/stamon.git 47 | cd stamon && mkdir build && cd build 48 | cmake .. 49 | make 50 | ``` 51 | 52 | 如果使用的是 **Ninja** 则最后一步替换为 53 | 54 | ```sh 55 | ninja 56 | ``` 57 | 58 | ## 使用Makefile编译 59 | 60 | ### 需要准备的环境 61 | 62 | 在编译前,请确保你有: 63 | 64 | * **[必要]** make:用于执行Makefile,**工具参考版本:GNU Make 3.82.90** 65 | * **[必要]** C++编译器:用于编译整个项目,默认为G++编译器,如需更换编译器请更改Makefile中的``COMPILER``宏,**工具参考版本:g++ 13.1.0** 66 | * **[非必要]** strip:用于剥削调试信息,减小可执行文件体积,**工具参考版本:GNU strip (GNU Binutils) 2.39** 67 | * **[非必要]** upx:用于压缩可执行文件体积,但是会降低程序性能,**工具参考版本:upx 4.0.2** 68 | 69 | 如果你未安装strip,执行Makefile可能会报错,属正常现象,发行版仍然会编译成功。**如果你希望发行版尽可能小,我们还是建议你安装strip。** 70 | 71 | ### 开始编译 72 | 73 | 打开终端,切换到源码目录(即与``Makefile``同级的目录),你可以根据系统来使用编译指令: 74 | 75 | * ``make release``:(默认)编译Windows发行版,在``bin``目录下生成``stamon.exe`` 76 | * ``make release_win``:编译Windows发行版,在``bin``目录下生成``stamon.exe`` 77 | * ``make release_linux``:编译Linux发行版,在``bin``目录下生成``stamon`` 78 | * ``make release_macos``:编译MacOS发行版,在``bin``目录下生成``stamon`` 79 | 80 | 你也可以根据系统来使用压缩指令: 81 | 82 | * ``make zip_release_win``:压缩Windows发行版,该指令将会压缩``bin/stamon.exe`` 83 | * ``make zip_release_linux``:压缩Linux发行版,该指令将会压缩``bin/stamon`` 84 | * ``make zip_release_macos``:压缩MacOS发行版,该指令将会压缩``bin/stamon`` -------------------------------------------------------------------------------- /.github/workflows/makefile.yml: -------------------------------------------------------------------------------- 1 | # github action which uses makefile 2 | 3 | name: Makefile on multiple platforms 4 | 5 | on: 6 | push: 7 | branches: ["main"] 8 | pull_request: 9 | branches: ["main"] 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | # build project for 3 platforms 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ubuntu-latest, windows-latest, macos-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | # build project 25 | 26 | - name: Build Project for Windows 27 | if: matrix.os == 'windows-latest' 28 | run: make release_win 29 | 30 | - name: Build Project for Linux 31 | if: matrix.os == 'ubuntu-latest' 32 | run: make release_linux 33 | 34 | - name: Build Project for macOS 35 | if: matrix.os == 'macos-latest' 36 | run: make release_macos 37 | 38 | - name: Upload executable for Windows 39 | uses: actions/upload-artifact@v4 40 | if: matrix.os == 'windows-latest' 41 | with: 42 | name: stamon-for-windows 43 | path: | 44 | ${{ github.workspace }}/bin/${{ matrix.build_type }}/stamon.exe 45 | ${{ github.workspace }}/bin/${{ matrix.build_type }}/include 46 | ${{ github.workspace }}/LICENSE 47 | compression-level: 9 48 | 49 | # upload executable 50 | 51 | - name: Upload executable for Linux 52 | uses: actions/upload-artifact@v4 53 | if: matrix.os == 'ubuntu-latest' 54 | with: 55 | name: stamon-for-linux 56 | path: | 57 | ${{ github.workspace }}/bin/stamon 58 | ${{ github.workspace }}/bin/include 59 | ${{ github.workspace }}/LICENSE 60 | compression-level: 9 61 | 62 | - name: Upload executable for macOS 63 | uses: actions/upload-artifact@v4 64 | if: matrix.os == 'macos-latest' 65 | with: 66 | name: stamon-for-macos 67 | path: | 68 | ${{ github.workspace }}/bin/stamon 69 | ${{ github.workspace }}/bin/include 70 | ${{ github.workspace }}/LICENSE 71 | compression-level: 9 -------------------------------------------------------------------------------- /include/stdc_implemented/FileReader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: FileReader.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 22/02/24 21:52 6 | Description: 二进制写入器 7 | */ 8 | 9 | #pragma once 10 | 11 | #define FILE_ERR(position) \ 12 | THROW(STMInfo("FileReader", "FileError", \ 13 | String("an error has occured in ") + String(position), \ 14 | "FileReader()")); 15 | 16 | #include "IFileReader.hpp" 17 | #include "stdio.h" 18 | 19 | namespace stamon::stdc { 20 | 21 | class FileReader { 22 | FILE *stream; 23 | int size; 24 | byte *buffer; 25 | 26 | public: 27 | STMException *ex; 28 | 29 | FileReader(STMException *e, String filename) { 30 | ex = e; 31 | 32 | stream = fopen(filename.getstr(), "rb"); 33 | // 尝试从当前目录打开文件 34 | 35 | for (int i = 0, len = ImportPaths.size(); i < len; i++) { 36 | if (stream != NULL) { 37 | break; // 成功打开文件 38 | } 39 | 40 | stream = fopen((ImportPaths[i] + filename).getstr(), "rb"); 41 | } 42 | 43 | if (stream == NULL) { 44 | FILE_ERR("fopen()"); 45 | return; 46 | } 47 | 48 | if (fseek(stream, 0, SEEK_END) != 0) { 49 | FILE_ERR("fseek()"); 50 | return; 51 | } 52 | // 将文件指针置于底部 53 | 54 | size = ftell(stream); 55 | // 获取文件指针(即文件底部)与文件头部的距离(即文件大小) 56 | 57 | if (size == -1) { 58 | FILE_ERR("ftell()"); 59 | return; 60 | } 61 | 62 | if (fseek(stream, 0, SEEK_SET) != 0) { 63 | FILE_ERR("fseek()"); 64 | return; 65 | } 66 | // 将文件指针重新置于顶部 67 | } 68 | 69 | int getSize() { 70 | return size; 71 | } 72 | 73 | EasySmartPtr read() { 74 | // 读取数据,会开辟并返回存储文件二进制数据的堆内存 75 | EasySmartPtr buffer(new byte[size + 1]); // 根据文件大小开辟内存 76 | buffer[size] = '\0'; 77 | 78 | if (buffer.get() == NULL) { 79 | FILE_ERR("calloc()"); 80 | return NULL; 81 | } 82 | 83 | if (fread(buffer.get(), 1, size, stream) != size) { 84 | FILE_ERR("fread()"); 85 | return NULL; 86 | } 87 | 88 | return buffer; 89 | } 90 | 91 | void close() { 92 | fclose(stream); 93 | } 94 | }; 95 | 96 | } // namespace stamon::stdc 97 | 98 | namespace stamon { 99 | 100 | using FileReader = IFileReader; 101 | 102 | } 103 | 104 | #undef FILE_ERR -------------------------------------------------------------------------------- /doc/工作日志/20231202.md: -------------------------------------------------------------------------------- 1 | # 2023/12/2 工作日志 2 | 3 | 写了三个月,这是一次较大的更新。 4 | 5 | ### 提交了代码漏洞修复 6 | 本次进行了一些测试,并且修复了代码上的一些小瑕疵(由于测试很杂,我不提交测试代码) 7 | 8 | ### 丰富了依赖库 9 | 我没有添加新的库文件,但是在原有的基础上给一些库增加了函数,这些函数方便了我的开发。 10 | 11 | ### 编写了对象管理器和GC(垃圾回收器) 12 | 这才是这次更新的重点。 13 |
14 | 本次更新的主要文件是src/data_type目录下的Variable.cpp和src/vm目录下的ObjectManager.cpp。 15 |

16 | 其中,ObjectManager.cpp基于Variable.cpp。 17 |

18 | Variable是一个非常简单的类,它有一个名为```data```的```DataType*```成员,所以对于任意左值,我们只需要得到这个左值的```Variable*```值,就可以对这个左值进行赋值(而不需要知道这个左值的具体信息)。 19 |
20 | 比如,对于一个名为```var```的```Variable*```对象和一个名为```dat```的```DataType*```对象,想要把```var```(也就是左值)赋值为```dat```(也就是右值),只需要这么做: 21 | ```C++ 22 | var->data = dat; 23 | ``` 24 |
25 | 26 | **换句话说,每个左值都对应着一个```Variable*```对象,而可以通过给这个对象的data成员赋值,从而达到给该左值赋值的目的** 27 |

28 | 由于我之前对src/data_type目录下的框架没有清楚的认识。导致了data_type的一部分代码需要修改,我在这次的更新当中修复了它。(修复的主要内容是把代码中的``DataType*``改成``Variable*``,具体见源码) 29 |

30 | ObjectManager.cpp编写了对象管理器的本体。对象管理器包含了GC机制。从今以后,如果你要新建虚拟机的对象,应该向对象管理器申请。 31 |

32 | GC所采用的算法是标记-清除算法,我参考了清华大学出版社的《编译原理(第二版)》里的伪代码。我用一个栈来维护运行时所有的作用域。新建或退出一个作用域时只需要入栈或出栈即可。寻找某个变量也只需要从栈顶找到栈底就行。 33 |

34 | 以下是ObjectManager.cpp的使用方法(我只讲述用户应该了解的接口,内部接口请见源码): 35 |

36 | 37 | * ```ObjectManager(unsigned long long mem_limit)```:构造函数,mem_limit是虚拟机对象可以占用的最大内存大小。 38 | * ```MallocObject<申请的类>(申请的类的构造函数参数...)```:用户应该通过这个函数来申请对象。

比如有一个名为man的对象管理器,申请一个SequenceType对象(假设构造参数为10)并存放到变量d里,应该这么做: 39 | ```C++ 40 | SequenceType* d = man.MallocObject(10); 41 | //注意:这个函数的模板类型不是指针类型,但是函数的返回值的是指针类型 42 | ``` 43 | 申请对象时,该函数会调用GCConditions函数,来查看申请此对象时是否需要执行GC。 44 |

45 | 46 | * ```Variable* GetLeftVariable(int id, datatype::DataType* val)```:这个函数用于获取左值变量。 47 |

48 | 因为和右值不同,在左值当中的变量可能是第一次出现的,所以我编写了这个函数,当获取的变量第一次出现时,就创建它,并返回这个变量。 49 |

50 | 这个GetLeftVariable与GetVariable的区别在于:如果要获取的变量是第一次出现的,那么GetLeftVariable函数会创建这个变量并返回;而GetVariable函数会直接报错。 51 | 52 | * ```Variable* GetVariable(int id)```:这个函数用于获取变量。这个函数通常用于获取右值变量。 53 | * ```PushScope()```:这个函数用于新建一个作用域。 54 | * ```PopScope()```:这个函数用于退出一个作用域。 55 | * ```GC()```:这个函数用于GC,和JVM等虚拟机不同,调用这个函数就一定会触发GC。(这个函数看似云淡风轻,实则我花了大把精力实现了这个函数,这是我人生中第一次编写GC) 56 | *** 57 | 开发者同样可以自定义虚拟机平时执行GC的条件:只需改变ObjectManager.cpp里的GCConditions函数即可,十分简单。 58 |

59 | 虚拟机默认: 60 |
61 | 当对象占用内存+GC预留内存大等于内存限制时,触发GC。 62 | 63 | 64 | 65 | ### 接下来要做的事: 66 | 67 | 1. 定义字节码执行器的数据结构 68 | 2. 编写字节码执行器的简单算法 69 | 3. 完成虚拟机的SFN功能 -------------------------------------------------------------------------------- /doc/AST-IR编码文件文档.md: -------------------------------------------------------------------------------- 1 | # AST-IR编码文件文档 2 | 3 | > 注意:``AST-IR规范``相对于``STVC-TAC规范``是一种“更古老”的编码规范,我们建议编译器和虚拟机采用更先进的``STVC-TAC规范``。但``AST-IR``仍然是一种稳定,方便的格式。 4 | 5 | ### 前言 6 | 7 | AST-IR(以下简称``AstIr``)是一种将抽象语法树(以下简称``Ast``)转为迭代表示方式的编码结构。 8 | 9 | 基于AstIr的编译器的基本运作原理是:在编译生成Ast后,将Ast转为AstIr的二进制编码文件,最后交给虚拟机运行,虚拟机将AstIr复原成``Running-Ast``数据结构(这种数据结构与编译器生成的Ast略有差别),然后递归执行。 10 | 11 | 有关``Running-Ast``的解释,我引用``编译器开发文档``的解释: 12 | 13 | > ``Ast``和``Running-Ast``的区别就在于:Running-Ast的叶子节点皆为``AstLeaf``,而Ast非然。 14 | 15 | 不难见得,Running-Ast是为了适应AstIr而诞生的结构。 16 | 17 | 对于AstIr的结构解释和示例,我将其放到了文末的“AST-IR格式附注”中。 18 | 19 | ### 格式 20 | 21 | 一个AstIr文件结构由以下几种规则构成: 22 | 23 | * 每个词法单元文件的开头必须要有两字节的二进制码:0xABDB,此魔数用于区分于其他文件 24 | * 在0xABDB后面,必须要跟着版本号,对于版本号X.Y.Z,则必须依次跟着X,Y,Z的数值,各为四字节 25 | * 在十二字节的版本号后面,需要跟着常量表(有关常量表的叙述在“STVC-TAC二进制编码规范”中有提及,因此我将其解释放到了文末的“常量表附注”中) 26 | * 常量表后面,需要跟着若干个AstIr的单元,如果有指定,还需在单元中穿插文件调试信息 27 | * 无论是逻辑单元,数据单元还是结束单元,每个单元的二进制长度都是八字节,前四字节是单元的类型(以``Ast.hpp``的``_AstType``为准,特别的,结束单元的类型是-1);后四字节是单元的数据,如果该单元是逻辑单元或结束单元,则后四字节默认为0,如果该单元是数据单元,则后四字节为数据的二进制存储(采用大端存储,以Python的大端存储为标准),数据单元的种类以文末的“数据单元附表”为准。 28 | * 如果指定需要写入调试信息,则还需要遵循以下规则 29 | * * 在写入第一个单元之前,先写入该单元所在的文件和行号 30 | * * 若一个单元所在的文件与上一个单元不一样,则需要写入当前单元所在文件 31 | * * 若一个单元所在的行号与上一个单元不一样,则需要写入当前单元所在行号 32 | * * 写入当前单元所在行号,需要先写入四字节的数值-2,然后输出四字节的行号 33 | * * 写入当前单元所在文件,需要先写入四字节的数值-3,接着写入四字节的长度(文件名字节长度),最后逐字节地写入文件名 34 | 35 | ### 数据单元附表 36 | 37 | |单元类型|对应的数据| 38 | |:-|:-| 39 | |ast::AstAnonClass|father_flag| 40 | |ast::AstExpression|ass_type| 41 | |ast::AstBinary|operator_type| 42 | |ast::AstUnary|operator_type| 43 | |ast::AstPostfix|postfix_type| 44 | 45 | ### AST-IR格式附注 46 | 47 | AstIr的格式和HTML的格式类似:由逻辑单元、数据单元和结束单元组成。一棵Ast可以通过深度优先遍历转为AstIr。 48 | 49 | 这样的描述也许会较为拗口,我们来看一个示例: 50 | 51 | > 考虑以下Ast: 52 | > ``` 53 | > Add 54 | > |-a 55 | > |-Sub 56 | > |--b 57 | > |--1 58 | > ``` 59 | > 该Ast可以转为以下AstIr: 60 | > ``` 61 | > //此为逻辑单元,以此类推 62 | > //此为数据单元,以此类推 63 | > 64 | > 65 | > 66 | > //此为结束单元,以此类推 67 | > 68 | > ``` 69 | > 这段AstIr和上面的Ast本质上是等价的。这样只需将Ast转为AstIr,就能获得一系列单元,方便存入文件。 70 | 71 | 逻辑单元本质上就是Ast节点的非叶子节点,数据单元本质上就是Ast节点的叶子节点。 72 | 73 | ### 常量表附注 74 | 75 | 一个常量表由常量表长度(占4字节)和若干个常量组成,其中常量又由常量类型(占1字节)和常量值组成: 76 | 77 | 其中每种类型的编码格式如下: 78 | 79 | * 整数类型:占4字节,即数值 80 | * 单精度浮点类型:占4字节,即数值 81 | * 双精度浮点类型:占8字节,即数值 82 | * 字符串类型:占4+len字节,其中前4字节记录字符串长度(即l,长度按字节计),后len字节为字符串值 83 | * 标识符类型:占4+len字节,其中前4字节记录标识符长度(即len,长度按字节计),后len字节为标识符名 84 | 85 | 一个常量表下标默认为4字节。 86 | 87 | > 摘自``STVC-TAC二进制编码规范``,2025年4月26日 -------------------------------------------------------------------------------- /doc/工作日志/20240131.md: -------------------------------------------------------------------------------- 1 | # 2024/01/31 工作日志 2 | 3 | ### 声明 4 | 5 | 本项目由STVM改进而来,[STVM地址](https://github.com/CLimber-Rong/stvm) 6 | 7 | 由于一些原因(详见后记),我不得不先从编译器开始编写。 8 | 9 | ### 新增history目录 10 | 11 | 有一些代码,在现阶段可能是废弃的,但是我无法确定将来我会使用到他,我把这些代码整合并放在了history目录里。代码阅读者和用户都不用理会。 12 | 13 | ### 全面更新依赖库 14 | 15 | 随着项目的不断开发,依赖库的接口定义也逐渐稳定了下来。 16 | 17 | 依赖库位于``src/include``中,其中的``template``目录是依赖库的模板(即接口定义),如果你想要移植Stamon,只需要实现``template``目录中定义的接口即可。 18 | 19 | 为了示范,我将接口用C语言的标准库实现了一遍,并把代码放到``stdc_implemented``中。 20 | 21 | 如果你所在的平台支持C语言的标准库(具体标准为C89),那么可以直接使用``stdc_implemented``作为依赖库。 22 | 23 | ### 全面修复以往代码的bug 24 | 25 | 几乎所有的文件都有了大大小小的改动。 26 | 27 | ### 开始编写编译器! 28 | 29 | 词法分析器已经写完了,语法分析器尚且写到了表达式分析的部分。 30 | 31 | 词法分析器的源码位于``src/compiler/Lexer.cpp``。 32 | 33 | ``Lexer.cpp``里的代码都被``stamon::c``(``c``指的是compiler)这个命名空间封装了起来。 34 | 35 | 如果你想要调用``Lexer.cpp``,可以编写以下代码: 36 | 37 | ```C++ 38 | #include"Lexer.cpp" 39 | using namespace stamon::c; 40 | ``` 41 | 42 | **接下来我想讲一讲词法分析器的架构(了解该架构前,你应该要会编译原理):** 43 | 44 | ``Token``是基本词法单元类,其成员如下: 45 | 46 | * ``Token(int line, int tok_type)``:构造函数,line参数表示该token所在的行号,tok_type表示该token的类型编号(具体请参见``Lexer.cpp``中的``TOKEN_TYPE``) 47 | * ``lineNo``:这是一个整数变量,表示该token所在的行号 48 | * ``type``:返回一个整数,即该token的类型编号 49 | 50 |
51 | 52 | 除``Token``类外,还有``StringToken``、``IdenToken``、``IntToken``、``DoubleToken``。这些都是``Token``的子类,如果一个Token是字面量或者标识符,你需要将其转化成对应的子类才能获取信息。具体请参见``Lexer.cpp``中的相关定义。 53 | 54 |
55 | 56 | ``Lexer``是词法分析器类,其接口如下: 57 | 58 | * ``int getLineTok(int line, String text)``:分析一行的token,line是行号,text是文本(不包含换行符)。分析后的token会加入到缓存中。返回解析到的位置,如果返回值是text的长度就说明解析到末尾,即解析成功;否则说明解析失败。 59 | * ``Token* getTok()``:从缓存中读取出(并删除)一个Token。 60 | * ``Token* peek(int index)``:查看第index个Token(但不删除)。 61 | 62 | Lexer在分析前,应当先把源码分解成逐行的文本,然后从第一行到最后一行依次调用getLineTok。 63 | 64 | 具体的用法请参见``Lexer.cpp``和测试样例 65 | 66 | ### 新增测试用例 67 | 68 | 新增了词法分析器的测试样例,位于``test_case/lexer/``中。 69 | 70 | 如果你想要编译运行测试样例,请将测试样例中的``test.cpp``覆盖到``test/test.cpp``当中,然后使用Makefile调试。 71 | 72 | ### 怎样编译运行测试代码 73 | 74 | 无论是调试你自己编写的测试代码,抑或是调试测试样例,都至少需要配置好以下工具: 75 | 76 | * make(参考版本:GNU Make 3.82.90) 77 | * gcc(参考版本:``gcc (x86_64-posix-seh-rev1, Built by MinGW-Builds project) 13.1.0``或``TDM-GCC 10``等) 78 | * **[可选]** strip(参考版本:GNU strip (GNU Binutils) 2.39) 79 | * **[可选]** upx(参考版本:upx 4.0.2) 80 | 81 | **注意:该项目的Makefile是基于win32的,如果你想要移植该项目到别的系统,请尝试修改Makefile。** 82 | 83 | **编译该项目时默认依赖库采用``stdc_implemented``,如果你所在的平台不支持C标准库,请自行实现依赖库。** 84 | 85 | ##### 调试: 86 | 87 | 你可以在命令行中键入以下指令: 88 | 89 | * ``make build``:编译项目,生成一个``test.exe``,即项目可执行文件 90 | * ``make run``:运行项目 91 | * ``make zip``:将项目可执行文件进行压缩 **(该指令需要用到strip和upx)** -------------------------------------------------------------------------------- /doc/工作日志/20240616.md: -------------------------------------------------------------------------------- 1 | # 2024/06/16 工作日志 2 | 3 | 本次发行了2.4.4版本。 4 | 5 | ### 修复了一些漏洞 6 | 7 | 目前已无已知Bug。 8 | 9 | 主要修复了编译器和Stamon标准库的bug。 10 | 11 | ### 成功支持Linux 12 | 13 | 成功将程序移植到Linux系统(测试环境:Debian),并且发布了发行版。 14 | 15 | ### 新增了Strip功能 16 | 17 | 你可以利用Stamon的``strip``选项剥夺字节码(即``.stvc``文件)的调试信息,来减少字节码文件大小,提升编译和运行的速度。代价就是在调试过程中无法知道错误所在位置,**该功能一般用于编译发行版的字节码**。 18 | 19 | 该功能同样内嵌与``build``选项,在该选项中加入``--strip=true``参数即可指定编译时不写入调试信息。用``--strip=false``则写入调试信息(如果不加入该参数则默认写入调试信息) 20 | 21 | ### 编译发行版 22 | 23 | 目前支持编译Windows、Linux系统。 24 | 25 | ##### 需要准备的环境 26 | 27 | 在编译前,请确保您有: 28 | 29 | * **[必要]** make:用于执行Makefile,**工具参考版本:GNU Make 3.82.90** 30 | * **[必要]** C++编译器:用于编译整个项目,默认为G++编译器,如需更换编译器请更改Makefile中的``COMPILER``宏,**工具参考版本:g++ 13.1.0** 31 | * **[非必要]** strip:用于剥削调试信息,减小可执行文件体积,**工具参考版本:GNU strip (GNU Binutils) 2.39** 32 | * **[非必要]** upx:用于压缩可执行文件体积,**工具参考版本:upx 4.0.2** 33 | 34 | 如果您未安装strip或upx,执行Makefile可能会报错,属正常现象,发行版仍然会编译成功。**如果您希望发行版尽可能小,我们还是建议您安装strip和upx。** 35 | 36 | ##### 开始编译 37 | 38 | 打开终端,切换到源码目录(即与``Makefile``同级的目录),您可以根据系统来使用编译指令: 39 | 40 | * ``make release``:编译Windows发行版(旧指令,但仍然兼容) 41 | * ``make release_win``:编译Windows发行版,在``bin``目录下生成``stamon.exe`` 42 | * ``make release_linux``:编译Linux发行版,在``bin``目录下生成``stamon`` 43 | 44 | ### 使用发行版 45 | 46 | ##### 配置运行时环境 47 | 48 | Stamon的运行时环境非常简便,您只需要配置至少一个环境变量即可使用: 49 | 50 | * **[必要]** ``STAMON``变量:该变量指向可执行文件所在的**目录(而非文件)** 51 | * **[非必要]** ``PATH``变量:在该变量末尾**追加**上可执行文件所在目录 52 | 53 | 如果不配置PATH变量,则Stamon无法全局使用(即只能在可执行文件所在目录下使用),**因此我们强烈建议您配置PATH变量** 54 | 55 | ##### 运行指令 56 | 57 | Stamon目前支持以下指令: 58 | 59 |
60 | 61 | * ``stamon version`` 62 | 63 | 该指令用于输出Stamon当前的版本号。 64 | 65 |
66 | 67 | * ``stamon help`` 68 | 69 | 该指令用于输出Stamon的用法以及各种参数的介绍。 70 | 71 |
72 | 73 | * ``stamon build [src] [dst] [options...]`` 74 | 75 | 该指令用于编译Stamon代码,其中src为必要参数,表示编译的Stamon文件名,dst为可选参数,表示生成的字节码文件名(默认a.stvc),options为可选参数,他们包括: 76 | 77 | ``` 78 | --import= 是否支持引用源码,默认为true) 79 | --strip= 是否剥削调试信息,默认为false) 80 | -I 添加引用路径 81 | ``` 82 | 83 |
84 | 85 | * ``stamon run [src] [options...]`` 86 | 87 | 该指令用于运行STVC文件,其中src为必要参数,表示编译的Stamon文件名,options为可选参数,他们包括: 88 | 89 | ``` 90 | --GC= 是否运行GC(垃圾自动回收),默认为true 91 | --MemLimit= 设置虚拟机的对象内存限制(按字节计,默认为16,777,216字节,即16MB) 92 | ``` 93 | 94 |
95 | 96 | * ``stamon strip [src]`` 97 | 98 | 该指令用于剥削STVC调试信息,其中src为必要参数,表示待剥削的STVC文件。 99 | 100 | *** 101 | 102 | ### 接下来要做的事 103 | 104 | 1. 编写AST的解释器 105 | 2. 编写词法分析的保存功能 106 | 3. 编写AST的O1优化器 107 | 4. 完善标准库 108 | 5. 支持编译为平面字节码 -------------------------------------------------------------------------------- /src/compiler/Compiler.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: Compiler.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 22/02/24 12:31 6 | Description: 编译器头文件 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BufferStream.cpp" 12 | #include "Parser.cpp" 13 | #include "HashMap.hpp" 14 | 15 | namespace stamon::c { 16 | 17 | ArrayList *ParseTargetProject(STMException *e, 18 | ArrayList *error_msg, ArrayList *warning_msg, 19 | String filename, bool is_support_import, ArrayList *src, 20 | HashMap filemap, SyntaxScope global_scope) { 21 | 22 | STMException *ex = e; 23 | 24 | filemap.put(filename, true); 25 | 26 | action::BufferInStream stream(ex, filename); 27 | 28 | CATCH { 29 | error_msg->add(ex->getError().toString()); 30 | return NULL; 31 | } 32 | 33 | ArrayList lines = stream.readLines(); 34 | 35 | int lineNo = 1; 36 | Lexer lexer(ex, filename); 37 | 38 | for (int i = 0; i < lines.size(); i++) { 39 | String text = lines[i]; 40 | 41 | int index = lexer.getLineTok(lineNo, text); 42 | 43 | CATCH { 44 | error_msg->add(ex->getError().toString()); 45 | ex->isError = false; 46 | } 47 | 48 | if (ISWARNING) { 49 | for (int i = 0, len = ex->Warning.size(); i < len; i++) { 50 | auto warning = ex->Warning[i]; 51 | warning_msg->add(warning.toString()); 52 | } 53 | ex->isWarning = false; 54 | } 55 | 56 | lineNo++; 57 | } 58 | 59 | Matcher matcher(lexer, ex); 60 | Parser *parser = new Parser(matcher, ex, global_scope, filename, src, 61 | filemap, error_msg, warning_msg, is_support_import); 62 | 63 | ast::AstNode *node = parser->Parse(); // 语法分析 64 | 65 | CATCH { 66 | error_msg->add(ex->getError().toString()); 67 | ex->isError = false; 68 | } 69 | 70 | SourceSyntax syntax; 71 | syntax.program = node; 72 | syntax.filename = filename; 73 | 74 | src->add(syntax); 75 | 76 | return src; 77 | } 78 | 79 | ast::AstNode *MergeAST(ArrayList *syntax_list) { 80 | ArrayList *program = new ArrayList(); 81 | 82 | for (int i = 0, len = syntax_list->size(); i < len; i++) { 83 | // 获取该文件的AST根节点 84 | ast::AstNode *node = syntax_list->at(i).program; 85 | 86 | // 放入总节点中 87 | (*program) += *(node->Children()); 88 | 89 | // node只是暂存了ast,需要被删除 90 | // 为了防止删除时将子节点也删除,因此需要先清空字节点列表 91 | node->Children()->clear(); 92 | delete node; 93 | } 94 | 95 | ast::AstNode *node = new ast::AstProgram(program); 96 | 97 | // 初始化node 98 | node->filename = String(); 99 | node->lineNo = -1; 100 | 101 | return node; 102 | } 103 | 104 | } // namespace stamon::c -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: test.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 09/09/23 13:54 6 | Description: 测试用 7 | */ 8 | 9 | #define JUST_DEBUG 10 | 11 | #include 12 | 13 | #include"Stamon.hpp" 14 | 15 | using namespace stamon; 16 | using namespace stamon::ast; 17 | using namespace stamon::c; 18 | using namespace stamon::datatype; 19 | using namespace stamon::ir; 20 | using namespace stamon::sfn; 21 | using namespace stamon::config; 22 | using namespace stamon::exception; 23 | using namespace stamon::action; 24 | using namespace std; 25 | 26 | void DebugAST(AstNode* node, int layer); 27 | void DebugIR(ArrayList ir); 28 | 29 | int main() { 30 | //在这里编写调试代码,调试方法见文档 31 | 32 | return 0; 33 | 34 | } 35 | 36 | //一些用于调试的函数 37 | 38 | void DebugAST(AstNode* node, int layer) { 39 | for(int i=1; i<=layer; i++) printf("\t"); 40 | printf("TYPE: %d", node->getType()); 41 | 42 | //特判表达式 43 | if(node->getType()==AstExpressionType) { 44 | AstExpression* rst = (AstExpression*)node; 45 | printf(" EXPRESSION: %d", rst->ass_type); 46 | } 47 | 48 | //特判left_postfix 49 | if(node->getType()==AstLeftPostfixType) { 50 | AstLeftPostfix* rst = (AstLeftPostfix*)node; 51 | printf(" LEFT-POSTFIX: %d", rst->postfix_type); 52 | } 53 | 54 | //特判postfix 55 | if(node->getType()==AstPostfixType) { 56 | AstPostfix* rst = (AstPostfix*)node; 57 | printf(" POSTFIX: %d",rst->postfix_type); 58 | } 59 | 60 | //特判单目运算符 61 | if(node->getType()==AstUnaryType) { 62 | AstUnary* rst = (AstUnary*)node; 63 | printf(" UNARY: %d",rst->operator_type); 64 | } 65 | //特判双目运算符 66 | if(node->getType()==AstBinaryType) { 67 | AstBinary* rst = (AstBinary*)node; 68 | printf(" BINARY: %d",rst->operator_type); 69 | } 70 | 71 | //ast的叶子节点要特判 72 | if(node->getType()==AstIdentifierType) { 73 | AstIdentifierName* rst = (AstIdentifierName*)node; 74 | printf(" IDEN: %s",rst->getName().getstr()); 75 | } 76 | if(node->getType()==AstNumberType) { 77 | AstNumber* rst = (AstNumber*)node; 78 | if(rst->getNumberType()==IntNumberType) { 79 | printf(" INT: %d",((AstIntNumber*)rst)->getVal()); 80 | } 81 | if(rst->getNumberType()==DoubleNumberType) { 82 | printf(" DOUBLE: %lf",((AstDoubleNumber*)rst)->getVal()); 83 | } 84 | } 85 | if(node->getType()==AstStringType) { 86 | AstString* rst = (AstString*)node; 87 | printf(" STRING: %s",rst->getVal().getstr()); 88 | } 89 | printf("\n"); 90 | for(int i=0,len=node->Children()->size(); iChildren()->at(i), layer+1); 92 | } 93 | return; 94 | } 95 | 96 | void DebugIR(ArrayList ir) { 97 | for(int i=0,len=ir.size(); i addr``|``condition(4) addr(4)``|condition是一个常量表下标,addr是一个整数| 48 | |``call result function arg1 arg2 ...``|``result(4) function(4) arglen(4) arg1(4) arg2(4) ...``|result、function、arg1、arg2等是常量表下标,arglen是一个整数,代表参数的个数| 49 | |``return value``|``value(4)``|value是一个常量表下标| 50 | |``new object source arg1 arg2 ...``|``object(4) source(4) arglen(4) arg1(4) arg2(4) ...``|object、source、arg1、arg2等都是常量表下标,arglen是一个整数,代表参数的个数| 51 | |``list identifier element1 element2...``|``identifier(4) arglen(4) element1(4) element2(4) ...``|identifier、element1、element2等都是常量表下标,arglen是一个整数,代表参数的个数| 52 | |``array identifier length``|``identifier(4) length(4)``|identifier和length都是常量表下标| 53 | |``free identifier``|``identifier(4)``|identifier是一个常量表下标| 54 | |``pushscope``|``nothing(0)``|没有任何操作数| 55 | |``popscope``|``nothing(0)``|没有任何操作数| 56 | |``def identifier``|``identifier(4)``|identifier是一个常量表下标| 57 | |``pushcatch addr``|``addr(4)``|addr是一个整数| 58 | |``popcatch``|``nothing(0)``|没有任何操作数| 59 | |``getexception identifier``|``identifier(4)``|identifier是一个常量表下标| 60 | |``sfn port arg``|``port(4) arg(4)``|port和arg都是常量表下标| 61 | |``function identifier arg1 arg2...``|``identifier(4) arglen(4) arg1(4) arg2(4) ...``|identifier、arg1、arg2等都是常量表下标,arglen是一个整数,代表参数的个数| 62 | |``class identifier member1 member2 * member3...``|``identifier(4) member1_methodflag(1) member1(4) member2_methodflag(1) member2(4) member3_methodflag(1) member3(4) ...``|identifier、member1、member2、member3等都是常量表下标,member1_methodflag、member2_methodflag、member3_methodflag等都是整数,表示其对应的类成员是否是类方法| 63 | |``end``|``nothing(0)``|没有任何操作数| -------------------------------------------------------------------------------- /src/ast/Ast.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: Ast.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 28/07/23 16:44 6 | Description: 7 | * 这里对Ast进行了基本的定义 8 | * 如果你想要引用所有的Ast源码,只要写入以下代码即可: 9 | #include"Ast.hpp" 10 | using namespace stamon::ast; 11 | */ 12 | 13 | #pragma once 14 | 15 | #include "ArrayList.hpp" 16 | #include "String.hpp" 17 | 18 | namespace stamon::ast { 19 | // Ast类型定义,详细作用见doc/虚拟机AST设计.md 20 | enum _AstType { 21 | AstNodeType = 0, 22 | AstProgramType, 23 | AstDefClassType, 24 | AstDefFuncType, 25 | AstDefVarType, 26 | AstAnonClassType, 27 | AstAnonFuncType, 28 | AstBlockType, 29 | AstBreakType, 30 | AstContinueType, 31 | AstIfStatementType, 32 | AstWhileStatementType, 33 | AstForStatementType, 34 | AstReturnStatementType, 35 | AstSFNType, 36 | AstExpressionType, 37 | AstLeftValueType, 38 | AstBinaryType, 39 | AstUnaryType, 40 | AstPostfixType, 41 | AstArgumentsType, 42 | AstIdentifierType, 43 | AstNumberType, 44 | AstStringType, 45 | AstNullType, 46 | AstArrayLiteralType, 47 | AstListLiteralType, 48 | AstLeafType, //用于存储常量表下标的叶子节点 49 | AstTypeNum // Ast总数 50 | }; 51 | 52 | // Ast类定义,详细实现见同目录的其他源码 53 | class AstNode; 54 | class AstProgram; 55 | class AstDefClass; 56 | class AstDefFunc; 57 | class AstAnonClass; 58 | class AstAnonFunc; 59 | class AstBlock; 60 | class AstBreak; 61 | class AstContinue; 62 | class AstIfStatement; 63 | class AstWhileStatement; 64 | class AstForStatement; 65 | class AstReturnStatement; 66 | class AstSFN; 67 | class AstExpression; 68 | class AstLeftValue; 69 | class AstBinary; 70 | class AstUnary; 71 | class AstPostfix; 72 | class AstArguments; 73 | class AstIdentifier; 74 | class AstNumber; 75 | class AstString; 76 | class AstNull; 77 | class AstArrayLiteral; 78 | class AstListLiteral; 79 | class AstLeaf; 80 | 81 | class AstNode { 82 | protected: 83 | ArrayList* children; 84 | 85 | public: 86 | int id; //语法树的节点类型 87 | int lineNo; //语法分析时用于显示行号 88 | String filename; //语义分析时用于显示文件名 89 | 90 | AstNode() { 91 | id = AstNodeType; 92 | children = new ArrayList(); 93 | } 94 | 95 | AstNode(int type_id) { 96 | id = type_id; 97 | children = new ArrayList(); 98 | } 99 | 100 | virtual ArrayList *Children() { 101 | //获得子节点列表 102 | return children; 103 | } 104 | 105 | virtual int getType() { 106 | //获得节点类型 107 | return id; 108 | } 109 | 110 | virtual ~AstNode() { 111 | //删除该节点及其所有字节点 112 | for(int i=0;isize();i++) { 113 | delete children->at(i); 114 | } 115 | delete children; 116 | } 117 | }; 118 | } 119 | 120 | #include "CodeLogicAst.cpp" 121 | #include "ExprAst.cpp" 122 | #include "LeafAst.cpp" 123 | #include "SfnAst.cpp" -------------------------------------------------------------------------------- /src/action/AstIrReader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: AstIrReader.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 09/12/23 17:34 6 | Description: IR读取器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "AstIr.cpp" 12 | #include "AstIrReaderException.cpp" 13 | #include "BufferStream.cpp" 14 | #include "ConstTabReader.cpp" 15 | #include "DataType.hpp" 16 | #include "String.hpp" 17 | 18 | // 用于简写的宏 19 | #define CE \ 20 | CATCH { \ 21 | return; \ 22 | } 23 | 24 | namespace stamon::action { 25 | class AstIrReader { 26 | BufferInStream stream; 27 | 28 | public: 29 | int VerX, VerY, VerZ; // 这三个变量代表版本为X.Y.Z 30 | ArrayList tableConst; // 常量表 31 | 32 | STMException *ex; 33 | 34 | AstIrReader(STMException *e, const BufferInStream &instream) 35 | : ex(e) 36 | , stream(instream) { 37 | } 38 | 39 | void readHeader() { 40 | // 读取魔数 41 | byte magic_number[2]; 42 | stream.readArray(magic_number); 43 | CE; 44 | 45 | if (magic_number[0] != (byte) 0xAB || magic_number[1] != (byte) 0xDB) { 46 | // 检查字节码是否为AST-IR 47 | printf("%d %d \n",magic_number[0],magic_number[1]); 48 | THROW(exception::astirreader::FormatError("readHeader()")); 49 | } 50 | 51 | // 读取版本号 52 | stream.read(VerX); 53 | CE; 54 | stream.read(VerY); 55 | CE; 56 | stream.read(VerZ); 57 | CE; 58 | 59 | // 读取常量表 60 | ConstTabReader reader(ex, stream); 61 | reader.read(); 62 | CE; 63 | tableConst = reader.tableConst; 64 | } 65 | 66 | ArrayList readIR() { 67 | ArrayList ir; 68 | 69 | int lineNo = -1; 70 | 71 | String filename; 72 | 73 | while (stream.isMore()) { 74 | int type; 75 | stream.read(type); 76 | CATCH { 77 | return ir; 78 | } 79 | 80 | if (type == -2) { 81 | // 更新行号 82 | stream.read(lineNo); 83 | 84 | } else if (type == -3) { 85 | // 更新文件名 86 | int len; 87 | stream.read(len); 88 | CATCH { 89 | return ir; 90 | } 91 | 92 | if (len < 0) { 93 | THROW(exception::astirreader::FormatError("readIR()")); 94 | return ir; 95 | } 96 | 97 | byte *cstr = new byte[len + 1]; 98 | cstr[len] = '\0'; 99 | 100 | for (int i = 0; i < len; i++) { 101 | stream.read(cstr[i]); 102 | } 103 | 104 | filename = String(cstr); 105 | 106 | delete[] cstr; 107 | 108 | } else { 109 | // 正常的IR 110 | ir::AstIr rst; 111 | 112 | stream.read(rst.data); 113 | 114 | if (type < -1 || type >= ast::AstTypeNum) { 115 | //不属于正常AST编码,也不是结尾单元 116 | THROW(exception::astirreader::NodeError("readIR()")); 117 | return ir; 118 | } 119 | 120 | CATCH { 121 | return ir; 122 | } 123 | 124 | rst.lineNo = lineNo; 125 | rst.filename = filename; 126 | rst.type = type; 127 | 128 | ir.add(rst); 129 | } 130 | } 131 | 132 | return ir; 133 | } 134 | }; 135 | } // namespace stamon::action 136 | 137 | #undef CE -------------------------------------------------------------------------------- /include/stdc_implemented/EasySmartPtr.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: EasySmartPtr.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 30/03/25 12:52 6 | Description: 一个简单的智能指针,使用了引用计数法 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "IEasySmartPtr.hpp" 12 | 13 | /* 14 | * 对于一些不会循环引用的数据结构,可以用智能指针来封装指针 15 | * 利用引用计数法,EasySmartPtr会记录有多少个数据正在使用该指针 16 | * 当没有任何数据使用该指针时,其可以被视作垃圾内存而删除 17 | */ 18 | 19 | namespace stamon::stdc { 20 | 21 | template class EasySmartPtr; 22 | 23 | template 24 | void EasySmartPtrDefaultDestroyFunction(IEasySmartPtr> *p); 25 | // 默认的销毁函数 26 | 27 | template class EasySmartPtr { 28 | int *ref_cnt; 29 | using Deverid = IEasySmartPtr>; 30 | //外部代码实际上调用到的CITP模板类 31 | 32 | public: 33 | void (*destroy_fp)(Deverid *ptr); 34 | T *ptr; 35 | 36 | EasySmartPtr(T *pointer) { 37 | // 初始化,传入指针 38 | ref_cnt = new int; // 初始化计数器 39 | (*ref_cnt) = 1; // 初始化时,只有一个EasySmartPtr拥有这个指针 40 | ptr = pointer; 41 | destroy_fp = EasySmartPtrDefaultDestroyFunction; 42 | } // 直接传入指针,默认销毁方式为直接delete 43 | 44 | EasySmartPtr(T *pointer, void (*destroy_funcptr)(Deverid *ptr)) { 45 | ref_cnt = new int; 46 | (*ref_cnt) = 1; 47 | ptr = pointer; 48 | destroy_fp = destroy_funcptr; 49 | } // 传入指针,并指定销毁方式 50 | 51 | EasySmartPtr(const EasySmartPtr &value) { 52 | ref_cnt = value.ref_cnt; // 复制计数器 53 | ptr = value.ptr; // 复制指针 54 | destroy_fp = value.destroy_fp; 55 | (*ref_cnt)++; // 又多了一个EasySmartPtr指向这个指针 56 | } // 复制构造函数 57 | 58 | EasySmartPtr &operator=(const EasySmartPtr &value) { 59 | /* 60 | * 将当前EasySmartPtr重新指定一个新的指针 61 | * 就意味着需要抛弃当前的指针,指向新的指针 62 | * 因此需要先减去当前指针的计数器,再将value的计数器加一 63 | */ 64 | 65 | if (&value == this) { 66 | // 自我赋值,直接返回即可 67 | return *this; 68 | } 69 | 70 | (*ref_cnt)--; // 减去当前计数器 71 | if ((*ref_cnt) == 0) { 72 | delete ref_cnt; 73 | destroy_fp((Deverid*)this); 74 | // 如果已经没有任何EasySmartPtr指向该指针 75 | // 那么此ptr可以被视作垃圾指针,需要被销毁 76 | } 77 | 78 | ref_cnt = value.ref_cnt; // 复制value 79 | ptr = value.ptr; 80 | destroy_fp = value.destroy_fp; 81 | 82 | (*ref_cnt)++; // value的计数器加一 83 | 84 | return *this; 85 | } // 赋值构造函数 86 | 87 | T *get() const { 88 | return ptr; 89 | } // 获取指针 90 | 91 | T *operator->() const { 92 | return ptr; 93 | } // 直接访问指针的成员 94 | 95 | T &operator[](int index) const { 96 | return ptr[index]; 97 | } // 直接访问元素 98 | 99 | T &operator*() const { 100 | return *ptr; 101 | } 102 | 103 | ~EasySmartPtr() { 104 | // 当前计数器需要减一 105 | (*ref_cnt)--; 106 | if ((*ref_cnt) == 0) { 107 | delete ref_cnt; 108 | destroy_fp((Deverid*)this); 109 | // 原理同上 110 | } 111 | } // 析构函数 112 | }; 113 | 114 | template 115 | void EasySmartPtrDefaultDestroyFunction(IEasySmartPtr> *p) { 116 | delete p->ptr; 117 | } 118 | 119 | } // namespace stamon::stdc 120 | 121 | namespace stamon { 122 | 123 | template 124 | using EasySmartPtr = IEasySmartPtr>; 125 | 126 | } -------------------------------------------------------------------------------- /src/action/ConstTabReader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ConstTabReader.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 05/08/25 21:08 6 | Description: 常量表解码器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "ArrayList.hpp" 12 | #include "BufferStream.cpp" 13 | #include "ConstTabReaderException.cpp" 14 | #include "DataType.hpp" 15 | #include "String.hpp" 16 | 17 | // 用于简写的宏 18 | #define CE \ 19 | CATCH { \ 20 | return; \ 21 | } 22 | 23 | namespace stamon::action { 24 | class ConstTabReader { 25 | BufferInStream stream; 26 | 27 | public: 28 | STMException *ex; 29 | ArrayList tableConst; // 常量表 30 | 31 | ConstTabReader(STMException *e, const BufferInStream &instream) 32 | : ex(e) 33 | , stream(instream) { 34 | } 35 | 36 | void read() { 37 | // 读取常量表,size为常量表项数,pos为下一个读取的字节的下标 38 | int size; 39 | stream.read(size); 40 | 41 | if (size < 0) { 42 | THROW(exception::consttabreader::FormatError("read()")); 43 | return; 44 | } 45 | 46 | for (int i = 1; i <= size; i++) { 47 | byte type; 48 | stream.read(type); 49 | CE; 50 | 51 | switch (type) { 52 | case datatype::IntegerTypeID: { 53 | int value; 54 | stream.read(value); 55 | CE; 56 | tableConst.add(new datatype::IntegerType(value)); 57 | break; 58 | } 59 | 60 | case datatype::FloatTypeID: { 61 | float value; 62 | stream.read(value); 63 | CE; 64 | tableConst.add(new datatype::FloatType(value)); 65 | break; 66 | } 67 | 68 | case datatype::DoubleTypeID: { 69 | double value; 70 | stream.read(value); 71 | CE; 72 | tableConst.add(new datatype::DoubleType(value)); 73 | break; 74 | } 75 | 76 | case datatype::NullTypeID: { 77 | tableConst.add(new datatype::NullType()); 78 | break; 79 | } 80 | 81 | case datatype::StringTypeID: { 82 | int len; 83 | stream.read(len); 84 | CE; 85 | 86 | if (len < 0) { 87 | THROW(exception::consttabreader::FormatError("read()")); 88 | return; 89 | } 90 | 91 | byte *cstr = new byte[len + 1]; 92 | cstr[len] = '\0'; 93 | 94 | for (int i = 0; i < len; i++) { 95 | stream.read(cstr[i]); 96 | CE; 97 | } 98 | 99 | String s(cstr); 100 | delete[] cstr; 101 | 102 | tableConst.add(new datatype::StringType(s)); 103 | break; 104 | } 105 | 106 | case (byte) ir::IdenConstTypeID: { 107 | int len; 108 | stream.read(len); 109 | CE; 110 | 111 | if (len < 0) { 112 | THROW(exception::consttabreader::FormatError("read()")); 113 | return; 114 | } 115 | 116 | byte *cstr = new byte[len + 1]; 117 | cstr[len] = '\0'; 118 | 119 | for (int i = 0; i < len; i++) { 120 | stream.read(cstr[i]); 121 | CE; 122 | } 123 | 124 | String s(cstr); 125 | delete[] cstr; 126 | 127 | tableConst.add(new ir::IdenConstType(s)); 128 | break; 129 | } 130 | 131 | default: { 132 | THROW(exception::consttabreader::ConstantsError("read()")); 133 | return; 134 | } 135 | } 136 | } 137 | } 138 | }; 139 | } // namespace stamon::action -------------------------------------------------------------------------------- /include/stdc_implemented/MemoryPool.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: MemoryPool.hpp 3 | License: Apache2.0 4 | Author: CLimber-Rong, copi143 5 | Date: 10/08/24 23:38 6 | Description: 内存池实现 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "ArrayList.hpp" 12 | #include "BasicPlatform.hpp" 13 | #include "HashMap.hpp" 14 | #include "IMemoryPool.hpp" 15 | 16 | #include "stddef.h" 17 | #include "stdio.h" 18 | #include "stdlib.h" 19 | 20 | namespace stamon::stdc { 21 | 22 | class MemoryPool { 23 | // 内存池 24 | 25 | HashMap *> free_mem_map; 26 | // 以空闲内存大小为键,空闲内存列表为值 27 | int FreeMemConsumeSize = 0; 28 | // 目前空闲内存总大小为多少 29 | int PoolCacheSize; 30 | // 内存池缓存大小,如果超过限制则把空闲内存全部释放 31 | STMException *ex; 32 | 33 | public: 34 | MemoryPool(STMException *e, int mem_limit, int pool_cache_size) { 35 | ex = e; 36 | PoolCacheSize = pool_cache_size; 37 | } 38 | 39 | template T *NewObject(Types &&...args) { 40 | if (PoolCacheSize <= 0) { 41 | // 无缓存,直接使用new 42 | return new T(forward(args)...); 43 | } else { 44 | // 使用内存池 45 | byte *ptr; 46 | 47 | if (free_mem_map.exist(sizeof(T))) { 48 | ArrayList *freelist = free_mem_map.get(sizeof(T)); 49 | if (!freelist->empty()) { 50 | // 有空闲内存 51 | ptr = freelist->at(freelist->size() - 1); 52 | freelist->erase(freelist->size() - 1); 53 | FreeMemConsumeSize -= sizeof(T); 54 | 55 | new ((T *) ptr) T(forward(args)...); 56 | return (T *) ptr; 57 | } 58 | } 59 | 60 | ptr = (byte *) malloc(sizeof(T)); 61 | 62 | new ((T *) ptr) T(forward(args)...); 63 | return (T *) ptr; 64 | } 65 | } 66 | 67 | void clearAllFreeMem() { 68 | ArrayList free_mem_type = free_mem_map.getKeyList(); 69 | // 获取空闲内存块有几种不同大小 70 | 71 | for (int i = 0, ilen = free_mem_type.size(); i < ilen; i++) { 72 | // 遍历所有空闲内存种类 73 | ArrayList *list = free_mem_map.get(free_mem_type[i]); 74 | // 获取当前种类的空闲内存列表 75 | for (int j = 0, jlen = list->size(); j < jlen; j++) { 76 | free(list->at(j)); 77 | // 释放空闲内存 78 | } 79 | FreeMemConsumeSize -= free_mem_type[i] * list->size(); 80 | // 减去占用内存 81 | list->clear(); 82 | // 清空列表 83 | delete list; 84 | // 删除列表 85 | free_mem_map.del(free_mem_type[i]); 86 | // 该类型无任何空闲内存 87 | } 88 | 89 | return; 90 | } 91 | 92 | template void DeleteObject(T *object) { 93 | if (PoolCacheSize <= 0) { 94 | // 无缓存,直接使用delete 95 | delete object; 96 | return; 97 | } else { 98 | object->~T(); 99 | 100 | byte *ptr = (byte *) object; 101 | 102 | if (PoolCacheSize == 0) { 103 | // 直接释放 104 | free(ptr); 105 | return; 106 | } 107 | 108 | if (FreeMemConsumeSize + sizeof(T) > PoolCacheSize) { 109 | // 释放所有空闲内存 110 | clearAllFreeMem(); 111 | } 112 | 113 | if (!free_mem_map.exist(sizeof(T))) { 114 | // 新建ArrayList 115 | free_mem_map.put(sizeof(T), new ArrayList()); 116 | } 117 | 118 | free_mem_map.get(sizeof(T))->add(ptr); 119 | 120 | FreeMemConsumeSize += sizeof(T); 121 | 122 | return; 123 | } 124 | } 125 | }; 126 | 127 | } // namespace stamon::stdc 128 | 129 | namespace stamon { 130 | 131 | using MemoryPool = IMemoryPool; 132 | 133 | } -------------------------------------------------------------------------------- /src/ast/LeafAst.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: LeafAst.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 29/07/23 20:14 6 | Description: 语法树叶子节点的定义 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "Ast.hpp" 12 | #include "String.hpp" 13 | 14 | namespace stamon::ast { 15 | class AstIdentifier : public AstNode { 16 | unsigned long long id; 17 | 18 | public: 19 | AstIdentifier(unsigned long long iden_id) 20 | : AstNode(AstIdentifierType) { 21 | id = iden_id; 22 | } 23 | unsigned long long getID() { 24 | return id; 25 | } 26 | virtual ~AstIdentifier() = default; 27 | }; 28 | /* 29 | * 这里需要解释一下 30 | * 由于虚拟机的设计,需要定义两种标识符节点 31 | * AstIdentifierName存储着标识符的名字(字符串) 32 | * 这个节点并不参与虚拟机的运行,而是参与目标代码的编译 33 | * 如果你是来阅读虚拟机实现代码的,请不要理会这个节点 34 | * AstIdentifier存储着标识符的编号,这个节点用作运行 35 | */ 36 | class AstIdentifierName : public AstIdentifier { 37 | String name; 38 | 39 | public: 40 | AstIdentifierName(const String &id) 41 | : AstIdentifier(-1) { 42 | name = id; 43 | } 44 | String getName() { 45 | return name; 46 | } 47 | virtual ~AstIdentifierName() = default; 48 | }; 49 | 50 | /* 51 | * 在 虚拟机AST设计.md 里,你会看到一个名为“NUMBER”的节点 52 | * 由于数字字面量有很多种(例如整数、浮点数) 53 | * 所以我先编写了AstNumber作为数字字面量的基类,所有的数字字面量节点都继承这个基类 54 | */ 55 | 56 | enum _NumberType { IntNumberType, FloatNumberType, DoubleNumberType }; 57 | 58 | class AstNumber : public AstNode { 59 | protected: 60 | int number_type; 61 | 62 | public: 63 | AstNumber() 64 | : AstNode(AstNumberType) { 65 | } 66 | int getNumberType() { 67 | return number_type; 68 | } 69 | virtual ~AstNumber() = default; 70 | }; 71 | 72 | class AstIntNumber : public AstNumber { 73 | int value; 74 | 75 | public: 76 | AstIntNumber(int val) 77 | : AstNumber() { 78 | number_type = IntNumberType; 79 | value = val; 80 | } 81 | int getVal() { 82 | return value; 83 | } 84 | virtual ~AstIntNumber() = default; 85 | }; 86 | 87 | class AstFloatNumber : public AstNumber { 88 | float value; 89 | 90 | public: 91 | AstFloatNumber(float val) 92 | : AstNumber() { 93 | number_type = FloatNumberType; 94 | value = val; 95 | } 96 | float getVal() { 97 | return value; 98 | } 99 | virtual ~AstFloatNumber() = default; 100 | }; 101 | 102 | class AstDoubleNumber : public AstNumber { 103 | double value; 104 | 105 | public: 106 | AstDoubleNumber(double val) 107 | : AstNumber() { 108 | number_type = DoubleNumberType; 109 | value = val; 110 | } 111 | double getVal() { 112 | return value; 113 | } 114 | virtual ~AstDoubleNumber() = default; 115 | }; 116 | 117 | class AstString : public AstNode { 118 | String val; 119 | 120 | public: 121 | AstString(const String &str) 122 | : AstNode(AstStringType) { 123 | val = str; 124 | } 125 | String getVal() { 126 | return val; 127 | } 128 | virtual ~AstString() = default; 129 | }; 130 | 131 | class AstNull : public AstNode { 132 | public: 133 | AstNull() 134 | : AstNode(AstNullType) { 135 | } 136 | }; 137 | 138 | class AstBreak : public AstNode { 139 | public: 140 | AstBreak() 141 | : AstNode(AstBreakType) { 142 | } 143 | }; 144 | 145 | class AstContinue : public AstNode { 146 | public: 147 | AstContinue() 148 | : AstNode(AstContinueType) { 149 | } 150 | }; 151 | 152 | class AstLeaf : public ast::AstNode { 153 | public: 154 | int index; // 常量表下标 155 | AstLeaf() 156 | : AstNode(ast::AstLeafType) { 157 | } 158 | AstLeaf(int data) 159 | : AstNode(ast::AstLeafType) { 160 | index = data; 161 | } 162 | virtual int getVal() const { 163 | return index; 164 | } 165 | virtual ~AstLeaf() = default; 166 | }; 167 | } // namespace stamon::ast -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.0) 2 | 3 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 4 | 5 | project(Stamon VERSION 2) 6 | 7 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 8 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 9 | 10 | enable_testing() 11 | 12 | macro(compressing_binary target) 13 | if(CMAKE_BUILD_TYPE STREQUAL "Release") 14 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 15 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 16 | set(filename ${EXECUTABLE_OUTPUT_PATH}/${target}.exe) 17 | else() 18 | set(filename ${EXECUTABLE_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}/${target}.exe) 19 | endif() 20 | else() 21 | set(filename ${EXECUTABLE_OUTPUT_PATH}/${target}) 22 | endif() 23 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 24 | add_custom_command(TARGET ${target} POST_BUILD 25 | COMMAND ${CMAKE_STRIP} "${filename}" 26 | ) 27 | endif() 28 | endif() 29 | endmacro(compressing_binary) 30 | 31 | include_directories(${PROJECT_SOURCE_DIR}/include/interface) 32 | include_directories(${PROJECT_SOURCE_DIR}/include/stdc_implemented) 33 | 34 | include_directories(${PROJECT_SOURCE_DIR}/src) 35 | include_directories(${PROJECT_SOURCE_DIR}/src/ast) 36 | include_directories(${PROJECT_SOURCE_DIR}/src/data_type) 37 | include_directories(${PROJECT_SOURCE_DIR}/src/vm) 38 | include_directories(${PROJECT_SOURCE_DIR}/src/ir) 39 | include_directories(${PROJECT_SOURCE_DIR}/src/compiler) 40 | include_directories(${PROJECT_SOURCE_DIR}/src/sfn) 41 | include_directories(${PROJECT_SOURCE_DIR}/src/exception) 42 | include_directories(${PROJECT_SOURCE_DIR}/src/config) 43 | include_directories(${PROJECT_SOURCE_DIR}/src/action) 44 | 45 | set(CMAKE_C_FLAGS_DEBUG "-g -Og -DDEBUG") 46 | set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -DDEBUG") 47 | 48 | set(CMAKE_C_FLAGS_RELEASE "-O2 -static") 49 | set(CMAKE_CXX_FLAGS_RELEASE "-O2 -static") 50 | 51 | if(CMAKE_HOST_APPLE) 52 | set(CMAKE_C_FLAGS_RELEASE "-O2") # 在MacOS上使用-static会报错 53 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") 54 | endif() 55 | 56 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 57 | set(CMAKE_C_FLAGS "-std=c17") 58 | set(CMAKE_CXX_FLAGS "-std=c++17") 59 | 60 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") 61 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") 62 | 63 | else() 64 | 65 | set(CMAKE_C_FLAGS "/std:c17") 66 | set(CMAKE_CXX_FLAGS "/std:c++17") 67 | endif() 68 | 69 | add_executable(stamon src/Main.cpp) 70 | compressing_binary(stamon) 71 | 72 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 73 | file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/src/bin-include" src) 74 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 75 | file(TO_NATIVE_PATH "${EXECUTABLE_OUTPUT_PATH}/include" tgt) 76 | add_custom_command(TARGET stamon POST_BUILD 77 | COMMAND Xcopy "${src}" "${tgt}" /s /e /i 78 | ) 79 | else() 80 | file(TO_NATIVE_PATH "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_BUILD_TYPE}/include" tgt) 81 | add_custom_command(TARGET stamon POST_BUILD 82 | COMMAND Xcopy "${src}" "${tgt}" /s /e /i 83 | ) 84 | endif() 85 | endif() 86 | 87 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 88 | add_custom_command(TARGET stamon POST_BUILD 89 | COMMAND cp -r -T "${PROJECT_SOURCE_DIR}/src/bin-include" "${EXECUTABLE_OUTPUT_PATH}/include" 90 | ) 91 | endif() 92 | 93 | if(CMAKE_HOST_APPLE) 94 | add_custom_command(TARGET stamon POST_BUILD 95 | COMMAND mkdir "${EXECUTABLE_OUTPUT_PATH}/include" 96 | COMMAND cp -r "${PROJECT_SOURCE_DIR}/src/bin-include/" "${EXECUTABLE_OUTPUT_PATH}/include" 97 | ) 98 | endif() -------------------------------------------------------------------------------- /src/action/TokenFileReader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: TokenFileReader.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 14/12/24 23:22 6 | Description: 词法单元文件阅读器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "BufferStream.cpp" 12 | #include "String.hpp" 13 | #include "Token.cpp" 14 | #include "TokenFileReaderException.cpp" 15 | 16 | // 为了节约篇幅,定义了一些宏用于简写 17 | // 这些宏只用于此文件 18 | #define CE \ 19 | CATCH { \ 20 | return NULL; \ 21 | } 22 | 23 | namespace stamon::action { 24 | class TokenFileReader { 25 | BufferInStream stream; 26 | int lineno; // 当前行数 27 | bool ismore; // 是否读完 28 | STMException *ex; 29 | 30 | public: 31 | TokenFileReader() { 32 | } 33 | TokenFileReader(STMException *e, const BufferInStream &instream) 34 | : ex(e) 35 | , stream(instream) 36 | , ismore(true) { 37 | byte magic_number[2]; 38 | stream.readArray(magic_number); 39 | 40 | CATCH { 41 | return; 42 | } 43 | 44 | if (magic_number[0] != (byte) 0xAB || magic_number[1] != (byte) 0xDC) { 45 | // 如果文件过小或魔数异常则报错 46 | THROW(exception::tokenfilereader::FormatError("TokenFileReader()")); 47 | return; 48 | } 49 | } 50 | 51 | c::Token *readToken() { 52 | // 获取一个Token 53 | byte id; 54 | stream.read(id); 55 | CE; 56 | 57 | if (id < 0 || id > c::TokenNum) { 58 | THROW(exception::tokenfilereader::TokenError("readToken()")); 59 | return NULL; 60 | } 61 | 62 | switch (id) { 63 | case (byte)-1: { 64 | lineno++; 65 | break; 66 | } 67 | 68 | case c::TokenInt: { 69 | // 特判整数token 70 | int val; 71 | stream.read(val); 72 | CE; 73 | 74 | return (c::Token *) (new c::IntToken(lineno, val)); 75 | } 76 | 77 | case c::TokenDouble: { 78 | // 特判浮点token 79 | double val; 80 | stream.read(val); 81 | CE; 82 | 83 | return (c::Token *) (new c::DoubleToken(lineno, val)); 84 | } 85 | 86 | case c::TokenString: { 87 | // 特判字符串token 88 | int len; 89 | stream.read(len); 90 | CE; 91 | if (len < 0) { 92 | THROW(exception::tokenfilereader::FormatError("readToken()")); 93 | } 94 | 95 | // 再读取到byte数组里 96 | byte *cstr = new byte[len + 1]; 97 | cstr[len] = '\0'; 98 | for (int i = 0; i < len; i++) { 99 | stream.read(cstr[i]); 100 | CE; 101 | } 102 | String val(cstr, len); 103 | delete cstr; // 释放内存 104 | return (c::Token *) (new c::StringToken(lineno, val)); 105 | } 106 | 107 | case c::TokenIden: { 108 | // 特判标识符token 109 | int len; 110 | stream.read(len); 111 | CE; 112 | if (len < 0) { 113 | THROW(exception::tokenfilereader::FormatError("readToken()")); 114 | } 115 | 116 | // 再读取到byte数组里 117 | byte *cstr = new byte[len + 1]; 118 | cstr[len] = '\0'; 119 | for (int i = 0; i < len; i++) { 120 | stream.read(cstr[i]); 121 | CE; 122 | } 123 | String val(cstr, len); 124 | delete cstr; // 释放内存 125 | return (c::Token *) (new c::StringToken(lineno, val)); 126 | } 127 | 128 | default: { 129 | break; 130 | } 131 | } 132 | 133 | // 返回正常的Token 134 | return (c::Token *) (new c::Token(lineno, id)); 135 | } 136 | 137 | ArrayList readLineTokens() { 138 | /* 139 | * 获取一行的Token 140 | * 在调用此方法之前,请先调用isMore()来确保还有剩余的Token 141 | */ 142 | 143 | ArrayList result; 144 | 145 | c::Token *token = readToken(); 146 | 147 | CATCH { 148 | return result; 149 | } 150 | 151 | while (token->type != -1 && token->type != c::TokenEOF) { 152 | result.add(token); 153 | token = readToken(); 154 | } 155 | 156 | if (token->type == c::TokenEOF) { 157 | ismore = false; 158 | } 159 | 160 | return result; 161 | } 162 | 163 | bool isMore() { 164 | return ismore; 165 | } 166 | 167 | void close() { 168 | reader.close(); 169 | return; 170 | } 171 | }; 172 | } // namespace stamon::action 173 | 174 | #undef CE -------------------------------------------------------------------------------- /src/exception/codegen/codegen.py: -------------------------------------------------------------------------------- 1 | """ 2 | Name: codegen.py 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/07/25 17:52 6 | Description: 报错信息及文档的自动生成器 7 | """ 8 | 9 | import time 10 | import json 11 | 12 | doc_path = "../../../doc/Stamon异常信息修复指南.md" 13 | # 生成的文档路径 14 | 15 | setting_list = [ 16 | ("astrunner", "CLimber-Rong"), 17 | ("compiler", "CLimber-Rong"), 18 | ("astfilereader", "CLimber-Rong"), 19 | ("astir", "CLimber-Rong"), 20 | ("astirreader", "CLimber-Rong"), 21 | ("objectmanager", "CLimber-Rong"), 22 | ("tokenfilereader", "CLimber-Rong"), 23 | ("sfn", "CLimber-Rong"), 24 | ("bufferstream", "CLimber-Rong"), 25 | ("consttabreader", "CLimber-Rong") 26 | ] 27 | # 生成所需的配置名称及其对应作者 28 | 29 | 30 | def gen_exception_func(f, sender: str) -> str: 31 | # 生成函数头 32 | code = "inline STMInfo " + f["type"] + "(String position" 33 | 34 | for arg in f["arg"]: 35 | code += ", String " + arg 36 | 37 | code += ") {\n\t" 38 | 39 | # 生成报错消息 40 | msg = "" 41 | first_flag = True # 第一个字符串前面不能有加号 42 | for m in f["msg"]: 43 | if first_flag is False: 44 | msg += "+" 45 | else: 46 | first_flag = False 47 | if m[0] == "$": 48 | msg += m[1:] # 参数 49 | else: 50 | msg += 'String("' + m + '")' 51 | 52 | # 整理并生成整个函数 53 | code += ( 54 | "return STMInfo(" 55 | + 'String("' 56 | + sender 57 | + '"), ' 58 | + 'String("' 59 | + f["type"] 60 | + '"), ' 61 | + msg 62 | + ", position);\n}\n\n" 63 | ) 64 | 65 | return code 66 | 67 | 68 | def gen_codefile(setting_name: str, author: str) -> None: 69 | """打开配置文件并生成对应的代码""" 70 | # 打开json文件 71 | fin = open(setting_name + ".json", "r", encoding="utf8") 72 | setting = json.loads(fin.read()) 73 | fin.close() 74 | 75 | # 生成文件头 76 | timestr = time.strftime("%d/%m/%Y %H:%M", time.localtime()) 77 | fileheader = ( 78 | "/*\n\tName: " 79 | + setting["name"] 80 | + "\n\tLicense: Apache 2.0\n\tAuthor: " 81 | + author 82 | + "\n\tDate: " 83 | + timestr 84 | + "\n\tDescription: 报错信息函数,由codegen.py自动生成" 85 | + "\n*/\n\n" 86 | + "#pragma once\n" 87 | + '#include "String.hpp"\n' 88 | + '#include "Exception.hpp"\n\n' 89 | ) 90 | 91 | # 生成代码 92 | code = "namespace stamon::exception::" + setting_name + " {\n" 93 | for f in setting["error"]: 94 | code += gen_exception_func(f, setting_name) 95 | for f in setting["warning"]: 96 | code += gen_exception_func(f, setting_name) 97 | 98 | code += "}" 99 | 100 | # 写入到对应文件 101 | fout = open("../" + setting["name"], "w", encoding="utf8") 102 | fout.write(fileheader + code) 103 | fout.close() 104 | 105 | 106 | def gen_func_doc(f) -> str: 107 | return "##### " + f["type"] + "\n\n" + f["doc"] + "\n\n" 108 | 109 | 110 | def gen_doc(setting_name: str) -> str: 111 | """打开配置文件并生成对应的文档""" 112 | # 打开json文件 113 | fin = open(setting_name + ".json", "r", encoding="utf8") 114 | setting = json.loads(fin.read()) 115 | fin.close() 116 | 117 | doc = "### " + setting_name + "\n\n" 118 | for i in setting["error"]: 119 | doc += gen_func_doc(i) 120 | for i in setting["warning"]: 121 | doc += gen_func_doc(i) 122 | 123 | return doc 124 | 125 | 126 | if __name__ == "__main__": 127 | # 遍历配置列表 128 | doc = "# Stamon错误及警告信息修复指南\n\n> 本文件为``src/exception/codegen/codegen.py``根据配置文件自动生成,用户可以用搜索文本的方式快速定位自己的问题\n\n" 129 | for i in setting_list: 130 | gen_codefile(i[0], i[1]) 131 | doc += gen_doc(i[0]) 132 | 133 | fout = open(doc_path, "w", encoding="utf8") 134 | fout.write(doc) 135 | fout.close() 136 | -------------------------------------------------------------------------------- /src/exception/CompilerException.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: CompilerException.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 07/08/2025 20:00 6 | Description: 报错信息函数,由codegen.py自动生成 7 | */ 8 | 9 | #pragma once 10 | #include "String.hpp" 11 | #include "Exception.hpp" 12 | 13 | namespace stamon::exception::compiler { 14 | inline STMInfo StringError(String position) { 15 | return STMInfo(String("compiler"), String("StringError"), String("the string was entered incorrectly"), position); 16 | } 17 | 18 | inline STMInfo FloatError(String position) { 19 | return STMInfo(String("compiler"), String("FloatError"), String("the floating point was entered incorrectly"), position); 20 | } 21 | 22 | inline STMInfo TokenError(String position, String text) { 23 | return STMInfo(String("compiler"), String("TokenError"), String("unknown token \'")+text+String("\'"), position); 24 | } 25 | 26 | inline STMInfo SyntaxError(String position) { 27 | return STMInfo(String("compiler"), String("SyntaxError"), String("invalid syntax"), position); 28 | } 29 | 30 | inline STMInfo VariableRedeclaredError(String position, String iden) { 31 | return STMInfo(String("compiler"), String("VariableRedeclaredError"), String("variable '")+iden+String("' are declared repeatedly"), position); 32 | } 33 | 34 | inline STMInfo SfnError(String position) { 35 | return STMInfo(String("compiler"), String("SfnError"), String("the port or argument of the SFN statement must be an identifier"), position); 36 | } 37 | 38 | inline STMInfo BreakError(String position) { 39 | return STMInfo(String("compiler"), String("BreakError"), String("\'break\' outside loop"), position); 40 | } 41 | 42 | inline STMInfo ContinueError(String position) { 43 | return STMInfo(String("compiler"), String("ContinueError"), String("\'continue\' outside loop"), position); 44 | } 45 | 46 | inline STMInfo MemberError(String position) { 47 | return STMInfo(String("compiler"), String("MemberError"), String("the member name must be an identifier"), position); 48 | } 49 | 50 | inline STMInfo VariableError(String position) { 51 | return STMInfo(String("compiler"), String("VariableError"), String("the name of the variable must be an identifier"), position); 52 | } 53 | 54 | inline STMInfo BraceError(String position) { 55 | return STMInfo(String("compiler"), String("BraceError"), String("the brace are not closed"), position); 56 | } 57 | 58 | inline STMInfo SquareBracketError(String position) { 59 | return STMInfo(String("compiler"), String("SquareBracketError"), String("the brace are not closed"), position); 60 | } 61 | 62 | inline STMInfo RoundBracketError(String position) { 63 | return STMInfo(String("compiler"), String("RoundBracketError"), String("the round bracket are not closed"), position); 64 | } 65 | 66 | inline STMInfo ClassDefinedError(String position) { 67 | return STMInfo(String("compiler"), String("ClassDefinedError"), String("only functions, classes and variables can be defined in a class"), position); 68 | } 69 | 70 | inline STMInfo ImportError(String position) { 71 | return STMInfo(String("compiler"), String("ImportError"), String("cannot import"), position); 72 | } 73 | 74 | inline STMInfo AssignmentError(String position) { 75 | return STMInfo(String("compiler"), String("AssignmentError"), String("the left operand of an assignment must be an lvalue"), position); 76 | } 77 | 78 | inline STMInfo UndefinedVariableError(String position, String iden) { 79 | return STMInfo(String("compiler"), String("UndefinedVariableError"), String("undefined variable \'")+iden+String("\'"), position); 80 | } 81 | 82 | inline STMInfo LargeIntegerWarning(String position) { 83 | return STMInfo(String("compiler"), String("LargeIntegerWarning"), String("the integer is too large"), position); 84 | } 85 | 86 | inline STMInfo LargeFloatWarning(String position) { 87 | return STMInfo(String("compiler"), String("LargeFloatWarning"), String("the floating point is too large"), position); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/action/AstFileWriter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: AstFileWriter.cpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 25/04/25 18:56 6 | Description: AST编码文件写入器 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "Ast.hpp" 12 | #include "BufferStream.cpp" 13 | #include "Exception.hpp" 14 | #include "Stack.hpp" 15 | 16 | // 为了节约篇幅,定义了一些宏用于简写 17 | // 这些宏只用于此文件 18 | 19 | #define CHECK_SPECIAL_AST(ast_type, special_member) \ 20 | case ast_type##Type: { \ 21 | stream.write(((ast_type *) top)->special_member); \ 22 | break; \ 23 | } 24 | 25 | namespace stamon::action { 26 | 27 | class AstFileWriter { 28 | BufferOutStream& stream; 29 | 30 | public: 31 | STMException *ex; 32 | 33 | AstFileWriter(STMException *e, BufferOutStream &outstream) 34 | : ex(e) 35 | , stream(outstream) { 36 | WRITE(0xAB); 37 | WRITE(0xDD); 38 | } 39 | 40 | void write(ast::AstNode *node, bool isstrip) { 41 | String filename; 42 | int lineno = -1; 43 | 44 | Stack stack; 45 | stack.push(node); 46 | 47 | while (!stack.empty()) { 48 | ast::AstNode *top = stack.pop(); 49 | 50 | if (top == NULL) { 51 | // 结束符 52 | stream.write((byte) -1); 53 | continue; 54 | } 55 | 56 | if (isstrip == false) { 57 | // 需要输出调试信息 58 | 59 | if (top->lineNo != lineno) { 60 | stream.write((byte) -2); 61 | lineno = top->lineNo; 62 | stream.write(lineno); 63 | } 64 | 65 | if (top->filename != filename) { 66 | // 输出文件信息 67 | stream.write((byte) -3); 68 | 69 | filename = top->filename; 70 | 71 | stream.write(filename.length()); // 写入长度 72 | 73 | for (int i = 0; i < filename.length(); i++) { 74 | stream.write(filename[i]); 75 | } 76 | } 77 | } 78 | 79 | writeNode(top); 80 | 81 | stack.push(NULL); // 压入结束符 82 | 83 | for (int i = top->Children()->size() - 1; i >= 0; i--) { 84 | stack.push(top->Children()->at(i)); 85 | } 86 | 87 | CATCH { 88 | return; 89 | } 90 | } 91 | } 92 | 93 | void writeNode(ast::AstNode *top) { 94 | stream.write((byte) (top->getType())); 95 | 96 | switch (top->getType()) { 97 | // 对有ast数据的节点进行特判 98 | case ast::AstIdentifierType: { 99 | // 标识符 100 | String iden = ((ast::AstIdentifierName *) top)->getName(); 101 | stream.write(iden.length()); 102 | for (int i = 0; i < iden.length(); i++) { 103 | stream.write((byte) (iden[i])); 104 | } 105 | break; 106 | } 107 | 108 | case ast::AstNumberType: { 109 | // 数字 110 | ast::AstNumber *number = (ast::AstNumber *) top; 111 | 112 | stream.write((byte) (number->getNumberType())); 113 | 114 | switch (number->getNumberType()) { 115 | case ast::IntNumberType: { 116 | // 整数 117 | int val = ((ast::AstIntNumber *) number)->getVal(); 118 | stream.write(val); 119 | break; 120 | } 121 | case ast::FloatNumberType: { 122 | // 单精度浮点 123 | float val = ((ast::AstFloatNumber *) number)->getVal(); 124 | stream.write(val); 125 | break; 126 | } 127 | case ast::DoubleNumberType: { 128 | // 双精度浮点 129 | double val = ((ast::AstDoubleNumber *) number)->getVal(); 130 | stream.write(val); 131 | break; 132 | } 133 | } 134 | break; 135 | } 136 | 137 | case ast::AstStringType: { 138 | // 字符串 139 | String text = ((ast::AstString *) top)->getVal(); 140 | 141 | stream.write(text.length()); 142 | for (int i = 0; i < text.length(); i++) { 143 | stream.write(text[i]); 144 | } 145 | break; 146 | } 147 | } 148 | CHECK_SPECIAL_AST(ast::AstAnonClass, father_flag); 149 | CHECK_SPECIAL_AST(ast::AstExpression, ass_type); 150 | CHECK_SPECIAL_AST(ast::AstBinary, operator_type); 151 | CHECK_SPECIAL_AST(ast::AstUnary, operator_type); 152 | CHECK_SPECIAL_AST(ast::AstPostfix, postfix_type); 153 | } 154 | }; 155 | 156 | } // namespace stamon::action 157 | 158 | #undef CHECK_SPECIAL_AST -------------------------------------------------------------------------------- /doc/工作日志/20250808.md: -------------------------------------------------------------------------------- 1 | # 2025/08/08 工作日志 2 | 3 | 本次更新不会修改版本。 4 | 本次更新重构了文件编解码部分和顶层设计部分,封装了输入输出流,并由此删去了不必要的依赖库。 5 | 6 | ### 重新规范文件编解码的端序 7 | 8 | 由于不同架构的机器在存储时会有大小端序之差,因此为了让字节码能跨平台编译运行,我规范化了端序。 9 | 10 | **编码生成的文件应该满足大端序**,而为了照顾不同机器的不同端序,我引入了新的配置,开发者可以根据自己实际平台的端序来调整配置。 11 | 12 | 我在``StamonConfig.hpp``中新增了一个枚举和两个标识符,用于标记不同机器的大小端配置,他们默认全是小端序: 13 | 14 | ```C++ 15 | enum STAMON_CODING_ENDIAN { 16 | // 机器的大小端控制 17 | StamonCodingLittleEndian = 0, 18 | StamonCodingBigEndian 19 | }; 20 | 21 | /*...中略无关代码...*/ 22 | 23 | // 编码的端序 24 | constexpr int StamonEncodingEndian = StamonCodingLittleEndian; 25 | // 解码的顺序 26 | constexpr int StamonDecodingEndian = StamonCodingLittleEndian; 27 | ``` 28 | 29 | 值得注意的是,无论本地的机器采用什么样的端序,如果满足``StamonEncodingEndian==StamonDecodingEndian``(即编解码端序一致),那么编码生成的文件一定能在本地解码运行,但在其他平台上有可能出现错误。这意味着**如果开发者只有在本地编译运行字节码的需求,那么不一定需要费尽心思处理大小端序冲突,只要让编解码端序一致即可。** 30 | 31 | ### 大规模重构stamon::action 32 | 33 | ##### 正式引入BufferStream 34 | 35 | 我将``IOStream.cpp``改名为``BufferStream.cpp``,并完全重构了它,该文件存储了内存输入输出流的实现。在我目前的构想里,分步行动机制在执行一个动作组时,相邻的行动工具应该使用内存来存储并传递数据。``BufferInStream``和``BufferOutStream``成为了内存数据的载体。 36 | 37 | 解码器通常会作为动作组的第一个行动工具,在这种情况下,起始数据可能来源于键盘输入、文件读取等操作。但无论起始数据的获取形式如何,最终都要拷贝到内存中,以``BufferInStream``的形式传入到解码器中。 38 | 39 | 和上述情况对称的,编码器通常会作为动作组的最后一个工具,在这种情况下,结果数据可能会被保存到文件。但无论结果数据的保存形式如何,最终都要先将结果数据拷贝到内存中,以``BufferOutStream``的形式返回给分步行动机制,机制再根据具体情况处理。 40 | 41 | 在完成``BufferStream.cpp``的编写后,我开始修改所有的编解码器。之前,各个解码器都是手动托管内存读取以提升安全性,冗余代码多;编码器也直接操作保存到文件,不适合分步行动机制的发展。现在,我让他们统一使用``BufferStream.cpp``来托管内存数据,这同时解决了“编解码器冗余代码多”和“不适合分步行动机制发展”两大难题。 42 | 43 | 我们来看看``BufferStream.cpp``中的核心接口。 44 | 45 | 首先是``BufferInStream``类: 46 | 47 | |接口原型|功能| 48 | |:-|:-| 49 | |``void reset()``|重新从头开始读取| 50 | |``template void read(T &data)``|读取一个数据| 51 | |``template void readArray(T (&data)[n])``|读取一个数组| 52 | 53 | 为了方便理解,我提供了一个代码片段作为例子,在该片段中,我们假设有一个``BufferInStream``类型的变量``stream``,并且stream接下来要读取的内存数据为``0x12,0x34,0x56,0x78,0x90``,本地机器的解码端序为小端序: 54 | 55 | ```C++ 56 | char x; 57 | char arr1[2], arr2[2]; 58 | stream.read(x); //读取x = 0x12 59 | stream.readArray(arr1); //读取arr1 = {0x34, 0x56} 60 | stream.read(arr2); //读取arr2 = {0x90, 0x78} 61 | ``` 62 | 63 | 值得注意的是,``arr2``没有和``arr1``一样顺序读取,这是因为``stream.read``将``arr2``看作了一个整体,又因为本地机器是小端序的缘故,所以逆序读取了数据。以上例子不仅提供了读取的示例,也警醒了开发者不要把``read``和``readArray``混为一谈。 64 | 65 | 接着是``BufferOutStream``类,他的用法基本与``BufferInStream``对称: 66 | 67 | |接口原型|功能| 68 | |:-|:-| 69 | |``template void write(T &data)``|写入一个数据| 70 | |``template void writeArray(T (&data)[n])``|写入一个数组| 71 | 72 | 73 | 在实现完``BufferStream.cpp``之后,其他的Reader和Writer也被修改,所有的数据读写操作都由``BufferInStream``和``BufferOutStream``托管,这极大减少了冗余代码,也统一了编解码器的实现模式。 74 | 75 | ##### 封装常量表编解码器 76 | 77 | 将常量表的读写功能单独提取出来,封装到``ConstTabReader.cpp``和``ConstTabWriter.cpp``中,这是在为未来的多字节码标准做准备。 78 | 79 | ### 删除不必要的代码 80 | 81 | 我剔除了AST部分一些获取节点数据的方法,我认为这是不必要的,直接访问AST节点的成员即可。 82 | 83 | 因为构造函数``ObjectManager()``并没有被用到,所以我删除了他。 84 | 85 | 由于``LineReader``可以使用``BufferInStream``实现。因此移除了LineReader。又因为LineReader被移除,所以FileMap也被认为是不需要的。所有的FileMap都被StringMap代替。 86 | 87 | ### 修正了命令行工具的文案细节 88 | 89 | 具体请参见``Main.cpp``的修改。 90 | 91 | ### 修改了logo目录的文件命名 92 | 93 | 见``mascot_v2.2.9.jpg``的文件修改记录。 94 | 95 | ### 修改了编译端的顶层设计 96 | 97 | 我认为之后的顶层前端功能都可以用函数来代替类,以此简便代码的调用方式。在这个决定上,我删除了``Compiler``类。取而代之的是两个函数: 98 | 99 | ```C++ 100 | ArrayList *ParseTargetProject( 101 | STMException *e, 102 | ArrayList *error_msg, ArrayList *warning_msg, 103 | String filename, bool is_support_import, ArrayList *src, 104 | StringMap filemap, SyntaxScope global_scope 105 | ); 106 | 107 | /* 108 | 编译一个Stamon项目。 109 | e是异常类,error_msg是存储报错信息用的列表,warning_msg是存储警告信息用的列表,filename是源码文件名,is_support_import_表示是否支持引用其他源码,src是存储各个文件语法树用的列表,filemap是用来标记文件是否被引用过的映射表,global_scope是全局作用域。 110 | 返回处理后的存储着各个文件语法树的列表(即处理后的src)。 111 | */ 112 | 113 | ast::AstNode *MergeAST(ArrayList *syntax_list); 114 | 115 | /* 116 | 将各个语法树合并为一个。syntax_list是存储着各个文件语法树的列表。返回合并后的根节点。 117 | */ 118 | 119 | ``` -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # .clang-format for Qt Creator 2 | # 3 | # This is for clang-format >= 5.0. 4 | # 5 | # The configuration below follows the Qt Creator Coding Rules [1] as closely as 6 | # possible. For documentation of the options, see [2]. 7 | # 8 | # Use ../../tests/manual/clang-format-for-qtc/test.cpp for documenting problems 9 | # or testing changes. 10 | # 11 | # In case you update this configuration please also update the qtcStyle() in src\plugins\clangformat\clangformatutils.cpp 12 | # 13 | # [1] https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html 14 | # [2] https://clang.llvm.org/docs/ClangFormatStyleOptions.html 15 | # 16 | --- 17 | Language: Cpp 18 | AccessModifierOffset: -4 19 | AlignAfterOpenBracket: DontAlign 20 | AlignConsecutiveAssignments: false 21 | AlignConsecutiveDeclarations: false 22 | AlignEscapedNewlines: DontAlign 23 | AlignOperands: AlignAfterOperator 24 | AlignTrailingComments: false 25 | AllowAllParametersOfDeclarationOnNextLine: true 26 | AllowShortLambdasOnASingleLine: None 27 | AllowShortCaseLabelsOnASingleLine: false 28 | AllowShortFunctionsOnASingleLine: None 29 | AllowShortIfStatementsOnASingleLine: Never 30 | AllowShortLoopsOnASingleLine: false 31 | AlwaysBreakAfterReturnType: None 32 | AlwaysBreakBeforeMultilineStrings: false 33 | AlwaysBreakTemplateDeclarations: MultiLine 34 | BinPackArguments: true 35 | BinPackParameters: true 36 | BraceWrapping: 37 | AfterClass: false 38 | AfterControlStatement: Never 39 | AfterEnum: false 40 | AfterFunction: false 41 | AfterNamespace: false 42 | AfterObjCDeclaration: false 43 | AfterStruct: false 44 | AfterUnion: false 45 | BeforeCatch: false 46 | BeforeElse: false 47 | IndentBraces: false 48 | SplitEmptyFunction: true 49 | SplitEmptyRecord: true 50 | SplitEmptyNamespace: true 51 | BreakBeforeBinaryOperators: All 52 | BreakBeforeBraces: Custom 53 | BreakBeforeInheritanceComma: false 54 | BreakBeforeTernaryOperators: true 55 | BreakConstructorInitializersBeforeComma: false 56 | BreakConstructorInitializers: BeforeComma 57 | BreakAfterJavaFieldAnnotations: true 58 | BreakStringLiterals: true 59 | ColumnLimit: 80 60 | CommentPragmas: '^ IWYU pragma:' 61 | CompactNamespaces: false 62 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 63 | ConstructorInitializerIndentWidth: 4 64 | ContinuationIndentWidth: 8 65 | Cpp11BracedListStyle: false 66 | DerivePointerAlignment: false 67 | DisableFormat: false 68 | ExperimentalAutoDetectBinPacking: false 69 | FixNamespaceComments: true 70 | ForEachMacros: 71 | - forever # avoids { wrapped to next line 72 | - foreach 73 | - Q_FOREACH 74 | - BOOST_FOREACH 75 | IncludeCategories: 76 | - Regex: '^ *statements) 21 | : AstNode(AstProgramType) { 22 | children = statements; 23 | } 24 | }; 25 | class AstDefClass : public AstNode { 26 | public: 27 | AstDefClass() 28 | : AstNode(AstDefClassType) { 29 | } 30 | 31 | AstDefClass(AstIdentifier *iden, AstAnonClass *object_class) 32 | : AstNode(AstDefClassType) { 33 | children->add((AstNode *) iden); 34 | children->add((AstNode *) object_class); 35 | } 36 | }; 37 | class AstDefFunc : public AstNode { 38 | public: 39 | AstDefFunc() 40 | : AstNode(AstDefFuncType) { 41 | } 42 | 43 | AstDefFunc(AstIdentifier *iden, AstAnonFunc *func) 44 | : AstNode(AstDefFuncType) { 45 | children->add((AstNode *) iden); 46 | children->add((AstNode *) func); 47 | } 48 | }; 49 | class AstDefVar : public AstNode { 50 | public: 51 | AstDefVar() 52 | : AstNode(AstDefVarType) { 53 | } 54 | 55 | AstDefVar(AstIdentifier *iden, AstExpression *expr) 56 | : AstNode(AstDefVarType) { 57 | children->add((AstNode *) iden); 58 | children->add((AstNode *) expr); 59 | } 60 | }; 61 | class AstAnonClass : public AstNode { 62 | public: 63 | bool father_flag = false; 64 | 65 | AstAnonClass() 66 | : AstNode(AstAnonClassType) { 67 | } 68 | 69 | AstAnonClass(AstIdentifier *father, ArrayList *expr) 70 | : AstNode(AstAnonClassType) { 71 | children = expr; 72 | if (father != NULL) { 73 | father_flag = true; 74 | children->insert(0, (AstNode *) father); 75 | } 76 | } 77 | 78 | virtual ~AstAnonClass() = default; 79 | }; 80 | class AstAnonFunc : public AstNode { 81 | public: 82 | AstAnonFunc() 83 | : AstNode(AstAnonFuncType) { 84 | } 85 | 86 | AstAnonFunc(ArrayList *args, AstBlock *block) 87 | : AstNode(AstAnonFuncType) { 88 | children = args; 89 | children->add((AstNode *) block); 90 | } 91 | }; 92 | class AstBlock : public AstNode { 93 | public: 94 | AstBlock() 95 | : AstNode(AstBlockType) { 96 | } 97 | 98 | AstBlock(ArrayList *statements) 99 | : AstNode(AstBlockType) { 100 | children = statements; 101 | } 102 | }; 103 | class AstIfStatement : public AstNode { 104 | public: 105 | AstIfStatement() 106 | : AstNode(AstIfStatementType) { 107 | } 108 | 109 | AstIfStatement(AstExpression *expr, AstBlock *block_if) 110 | : AstNode(AstIfStatementType) { 111 | children->add((AstNode *) expr); 112 | children->add((AstNode *) block_if); 113 | } 114 | AstIfStatement( 115 | AstExpression *expr, AstBlock *block_if, AstBlock *block_else) 116 | : AstNode(AstIfStatementType) { 117 | children->add((AstNode *) expr); 118 | children->add((AstNode *) block_if); 119 | children->add((AstNode *) block_else); 120 | } 121 | }; 122 | class AstWhileStatement : public AstNode { 123 | public: 124 | AstWhileStatement() 125 | : AstNode(AstWhileStatementType) { 126 | } 127 | 128 | AstWhileStatement(AstExpression *expr, AstBlock *block_while) 129 | : AstNode(AstWhileStatementType) { 130 | children->add((AstNode *) expr); 131 | children->add((AstNode *) block_while); 132 | } 133 | }; 134 | class AstForStatement : public AstNode { 135 | public: 136 | AstForStatement() 137 | : AstNode(AstForStatementType) { 138 | } 139 | 140 | AstForStatement( 141 | AstIdentifier *iden, AstExpression *expr, AstBlock *block_for) 142 | : AstNode(AstForStatementType) { 143 | children->add((AstNode *) iden); 144 | children->add((AstNode *) expr); 145 | children->add((AstNode *) block_for); 146 | } 147 | }; 148 | class AstReturnStatement : public AstNode { 149 | public: 150 | AstReturnStatement() 151 | : AstNode(AstReturnStatementType) { 152 | } 153 | 154 | AstReturnStatement(AstExpression *expr) 155 | : AstNode(AstReturnStatementType) { 156 | children->add((AstNode *) expr); 157 | } 158 | }; 159 | } // namespace stamon::ast -------------------------------------------------------------------------------- /src/exception/codegen/compiler.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CompilerException.cpp", 3 | "error": [ 4 | { 5 | "type": "StringError", 6 | "arg": [], 7 | "msg": ["the string was entered incorrectly"], 8 | "doc": "字符串格式有误。例如转义字符输入错误时有可能触发此错误。请检查字符串是否由一对双引号包裹,转义字符是否输入正确。" 9 | }, 10 | { 11 | "type": "FloatError", 12 | "arg": [], 13 | "msg": ["the floating point was entered incorrectly"], 14 | "doc": "字符串格式有误。例如转义字符输入错误时有可能触发此错误。请检查字符串是否由一对双引号包裹,转义字符是否输入正确。" 15 | }, 16 | { 17 | "type": "TokenError", 18 | "arg": ["text"], 19 | "msg": ["unknown token \\'", "$text", "\\'"], 20 | "doc": "未知的词法单元。当用户输入非法字符(例如英文问号)时会触发此错误,请检查代码中是否包含非法字符。" 21 | }, 22 | { 23 | "type": "SyntaxError", 24 | "arg": [], 25 | "msg": ["invalid syntax"], 26 | "doc": "综合语法错误。该报错的触发条件多种多样,在Stamon的语法当中,会出现一些固定的语法搭配,当用户输入的语法搭配与规定的不一致时,会触发此错误,例如语句末尾未加分号。请用户根据编译器报告的错误位置,对照《Stamon语法教程手册》,检查语法搭配是否一致。值得注意的是:有些错误实际需要解决的位置,或许和编译器报告的错误位置并不完全一样,但两者肯定存在对应关系,所以请用户认真检查错误位置所在行及其关联的代码是否有误。" 27 | }, 28 | { 29 | "type": "VariableRedeclaredError", 30 | "arg": ["iden"], 31 | "msg": ["variable '", "$iden", "' are declared repeatedly"], 32 | "doc": "变量被重复强定义。对于此类错误,推荐一种解决方案——前置定义设计:先为专门建立一个源码文件(被称为前置定义文件),并使用def关键字来强定义所有全局标识符(即前置定义);并在其他源文件的开头先引用这个前置定义文件,在其他源文件中,不直接使用def关键字来强定义全局标识符,而是采用直接赋值或弱定义的形式。这种代码设计思路,可以确保所有全局标识符只被强定义一次。对于局部标识符,请确保最多被强定义一次。有关如何使用强定义、弱定义和赋值,也可以参照《Stamon语法教程手册》中的相关内容。值得注意的是:请确保需要用到的全局标识符在def之后有相关的实现,否则在使用被强定义但未赋值的标识符时,会出现对空值进行操作的情况,从而引起运行时错误。" 33 | }, 34 | { 35 | "type": "SfnError", 36 | "arg": [], 37 | "msg": [ 38 | "the port or argument of the SFN statement must be an identifier" 39 | ], 40 | "doc": "SFN语句的端口或参数必须是标识符。" 41 | }, 42 | { 43 | "type": "BreakError", 44 | "arg": [], 45 | "msg": ["\\'break\\' outside loop"], 46 | "doc": "break语句在循环体外。break语句作为循环流程控制相关的语句,必须在循环语句之内出现。" 47 | }, 48 | { 49 | "type": "ContinueError", 50 | "arg": [], 51 | "msg": ["\\'continue\\' outside loop"], 52 | "doc": "continue语句在循环体外。continue语句作为循环流程控制相关的语句,必须在循环语句之内出现。" 53 | }, 54 | { 55 | "type": "MemberError", 56 | "arg": [], 57 | "msg": ["the member name must be an identifier"], 58 | "doc": "类成员名必须是标识符。" 59 | }, 60 | { 61 | "type": "VariableError", 62 | "arg": [], 63 | "msg": ["the name of the variable must be an identifier"], 64 | "doc": "变量名必须是标识符。" 65 | }, 66 | { 67 | "type": "BraceError", 68 | "arg": [], 69 | "msg": ["the brace are not closed"], 70 | "doc": "花括号未闭合,请检查错误位置及其关联位置是否存在未闭合的花括号对。" 71 | }, 72 | { 73 | "type": "SquareBracketError", 74 | "arg": [], 75 | "msg": ["the brace are not closed"], 76 | "doc": "方括号未闭合,请检查错误位置及其关联位置是否存在未闭合的方括号对。" 77 | }, 78 | { 79 | "type": "RoundBracketError", 80 | "arg": [], 81 | "msg": ["the round bracket are not closed"], 82 | "doc": "圆括号未闭合,请检查错误位置及其关联位置是否存在未闭合的圆括号对。" 83 | }, 84 | { 85 | "type": "ClassDefinedError", 86 | "arg": [], 87 | "msg": [ 88 | "only functions, classes and variables can be defined in a class" 89 | ], 90 | "doc": "类定义中,只能定义成员函数、成员类和成员变量。" 91 | }, 92 | { 93 | "type": "ImportError", 94 | "arg": [], 95 | "msg": ["cannot import"], 96 | "doc": "不能引用其他源码。只有当编译器被指定为不能引用其他文件的模式,并且源码中调用了其他文件时,才会触发此错误。" 97 | }, 98 | { 99 | "type": "AssignmentError", 100 | "arg": [], 101 | "msg": ["the left operand of an assignment must be an lvalue"], 102 | "doc": "表达式的左操作数必须是左值。其中对左值的严格定义是:一个变量取任意次元素和成员的表达式。" 103 | }, 104 | { 105 | "type": "UndefinedVariableError", 106 | "arg": ["iden"], 107 | "msg": ["undefined variable \\'", "$iden", "\\'"], 108 | "doc": "未定义变量。请在使用变量前先使用强定义或弱定义的方式定义变量。" 109 | } 110 | ], 111 | "warning": [ 112 | { 113 | "type": "LargeIntegerWarning", 114 | "arg": [], 115 | "msg": ["the integer is too large"], 116 | "doc": "整数过大从而导致溢出。这会导致程序执行时出现数值与代码中数值不同的情况。" 117 | }, 118 | { 119 | "type": "LargeFloatWarning", 120 | "arg": [], 121 | "msg": ["the floating point is too large"], 122 | "doc": "浮点数过大从而导致溢出。这会导致程序执行时出现数值与代码中数值不同的情况。" 123 | } 124 | ] 125 | } 126 | -------------------------------------------------------------------------------- /doc/CITP设计文档.md: -------------------------------------------------------------------------------- 1 | # CITP设计文档 2 | 3 | ### 摘要 4 | 5 | 在C++中,前人已经提出了多种约束接口的方式。其中,CRTP不妨是一个适用于低标准且不需要性能的静态多态设计方式。但CRTP无法约束构造函数,实现过程涉及静态转换,代码复杂。为此,我们提出了CRTP的变式——**CITP**(**Curiously Iterating Template Pattern,奇异递推模板模式**),并将其首次应用到Stamon当中。 6 | 7 | ### 需求性分析 8 | 9 | 我们先来引入一段典型的CRTP设计: 10 | 11 | ```C++ 12 | /* 例1 */ 13 | #include 14 | using namespace std; 15 | 16 | template 17 | class Constraint { 18 | public: 19 | int square(int x) { 20 | // 被开发者调用的接口 21 | return static_cast(this)->square(x); 22 | // 静态转换为Implementation,并调用其实现,实现接口约束 23 | } 24 | }; 25 | 26 | class Implementation : public Constraint { 27 | public: 28 | int square(int x) { 29 | // Constraint::f(int)的实现 30 | return x*x; 31 | } 32 | }; 33 | 34 | using Square = Implementation; 35 | 36 | int main() 37 | { 38 | Square s; 39 | cout<< s.square(5) < 73 | using namespace std; 74 | 75 | class Implementation { 76 | // 实现类作为父类受约束 77 | public: 78 | int square(int x) { 79 | // 受约束接口 80 | return x*x; 81 | } 82 | }; 83 | 84 | template 85 | class Constraint : public Impl { 86 | // 用于约束的类作为子类,继承实现类 87 | public: 88 | int square(int x) { 89 | // 调用Impl的实现 90 | return Impl::square(x); 91 | } 92 | }; 93 | 94 | using Square = Constraint; 95 | // 将Implementation传递给Constraint作为约束 96 | 97 | int main() 98 | { 99 | Square s; 100 | cout<< s.square(5) <赋予供用户使用的别名(例如 _例2_ 的Square)。 106 | 107 | 我们再看看在CITP中,如何约束构造函数: 108 | 109 | ```C++ 110 | /* 例3 */ 111 | #include 112 | using namespace std; 113 | 114 | class Implementation { 115 | // 实现类作为父类受约束 116 | int k, b; 117 | public: 118 | Implementation(int K, int B) : k(K), b(B) {}; 119 | int f(int x) { 120 | // 受约束接口 121 | return k*x + b; 122 | } 123 | }; 124 | 125 | template 126 | class Constraint : public Impl { 127 | // 用于约束的类作为子类,继承实现类 128 | public: 129 | Constraint(int K, int B) : Impl(K,B) {}; 130 | int f(int x) { 131 | // 调用Impl的实现 132 | return Impl::f(x); 133 | } 134 | }; 135 | 136 | using Linear = Constraint; 137 | // 将Implementation传递给Constraint作为约束 138 | 139 | int main() 140 | { 141 | Linear l(4,5); 142 | cout<< l.f(5) < 154 | using namespace std; 155 | 156 | class Implementation { 157 | // 实现类作为父类受约束 158 | int k, b; 159 | public: 160 | Implementation(int K, int B) : k(K), b(B) {}; 161 | int operator<<(int x) { 162 | // 受约束接口 163 | return k*x + b; 164 | } 165 | }; 166 | 167 | template 168 | class Constraint : public Impl { 169 | // 用于约束的类作为子类,继承实现类 170 | public: 171 | Constraint(int K, int B) : Impl(K,B) {}; 172 | int operator<<(int x) { 173 | // 调用Impl的实现 174 | return Impl::operator<<(x); 175 | } 176 | }; 177 | 178 | using Linear = Constraint; 179 | // 将Implementation传递给Constraint作为约束 180 | 181 | int main() 182 | { 183 | Linear l(4,5); 184 | cout<< (l<<5) <add((AstNode *) LeftValue); 64 | children->add((AstNode *) expr); 65 | ass_type = AssTok; 66 | } 67 | AstExpression(AstBinary *value) 68 | : AstNode(AstExpressionType) { 69 | children->add((AstNode *) value); 70 | ass_type = -1; 71 | } 72 | virtual ~AstExpression() = default; 73 | }; 74 | class AstLeftValue : public AstNode { 75 | public: 76 | AstLeftValue() 77 | : AstNode(AstLeftValueType) { 78 | } 79 | 80 | AstLeftValue(AstIdentifier *iden, ArrayList *postfix) 81 | : AstNode(AstLeftValueType) { 82 | children = postfix; 83 | children->insert(0, (AstNode *) iden); 84 | } 85 | }; 86 | class AstBinary : public AstNode { 87 | public: 88 | int operator_type; 89 | AstBinary() 90 | : AstNode(AstBinaryType) { 91 | } 92 | 93 | AstBinary(int OperatorType, AstNode *left, AstNode *right) 94 | : AstNode(AstBinaryType) { 95 | operator_type = OperatorType; 96 | children->add((AstNode *) left); 97 | children->add((AstNode *) right); 98 | } 99 | AstBinary(AstNode *left) 100 | : AstNode(AstBinaryType) { 101 | operator_type = -1; 102 | children->add((AstNode *) left); 103 | } 104 | virtual ~AstBinary() = default; 105 | }; 106 | class AstUnary : public AstNode { 107 | public: 108 | int operator_type; 109 | AstUnary() 110 | : AstNode(AstUnaryType) { 111 | } 112 | AstUnary(int OperatorType, AstNode *value) 113 | : AstNode(AstUnaryType) { 114 | operator_type = OperatorType; 115 | children->add((AstNode *) value); 116 | } 117 | AstUnary(AstNode *value, ArrayList *postfix) 118 | : AstNode(AstUnaryType) { 119 | operator_type = -1; 120 | children = postfix; 121 | children->insert(0, (AstNode *) value); 122 | } 123 | virtual ~AstUnary() = default; 124 | }; 125 | class AstPostfix : public AstNode { 126 | public: 127 | 128 | int postfix_type; 129 | AstPostfix() 130 | : AstNode(AstPostfixType) { 131 | } 132 | AstPostfix(int PostfixType, AstNode *value) 133 | : AstNode(AstPostfixType) { 134 | postfix_type = PostfixType; 135 | children->add((AstNode *) value); 136 | } 137 | virtual ~AstPostfix() = default; 138 | }; 139 | class AstArguments : public AstNode { 140 | public: 141 | AstArguments() 142 | : AstNode(AstArgumentsType) { 143 | } 144 | 145 | AstArguments(ArrayList *exprs) 146 | : AstNode(AstArgumentsType) { 147 | children = exprs; 148 | } 149 | }; 150 | class AstArrayLiteral : public AstNode { 151 | public: 152 | AstArrayLiteral() 153 | : AstNode(AstArrayLiteralType) { 154 | } 155 | 156 | AstArrayLiteral(AstExpression *expr) 157 | : AstNode(AstArrayLiteralType) { 158 | children->add((AstNode *) expr); 159 | } 160 | }; 161 | class AstListLiteral : public AstNode { 162 | public: 163 | AstListLiteral() 164 | : AstNode(AstListLiteralType) { 165 | } 166 | 167 | AstListLiteral(ArrayList *exprs) 168 | : AstNode(AstListLiteralType) { 169 | children = exprs; 170 | } 171 | }; 172 | 173 | } // namespace stamon::ast -------------------------------------------------------------------------------- /doc/工作日志/20250713.md: -------------------------------------------------------------------------------- 1 | # 2025/07/13 工作日志 2 | 3 | 本次更新改动了非常多的文件。 4 | 本次更新升级了异常处理系统。 5 | 本次更新发行``2.4.49``版本。 6 | 7 | ### 忽略.vscode目录 8 | 9 | 再三审查后,我认为他是不必要的。 10 | 11 | ### 升级异常处理系统 12 | 13 | 升级异常处理系统的想法诞生于以下背景: 14 | 1. 以往的异常处理系统,使用一个字符串来存储异常信息,这种简单做法带来的缺点就是,无法准确快速的获取更详细的信息(例如异常抛出者、异常类型、异常位置)。 15 | 2. 以往的异常处理系统,并没有封装文件编解码的异常生成函数,而只封装了编译端和虚拟端的异常生成函数(且两者并不完全统一)。 16 | 3. 随着项目的发展,日志系统的开发被认为是需要的。 17 | 18 | 对异常处理系统的升级,主要分为以下阶段: 19 | 20 | ##### 改装存储异常信息的载体 21 | 22 | 在``Exception.hpp``中设立``STMInfo``。以此代替存储异常信息的字符串。事实上,``STMInfo``也可以存储程序运行信息和调试信息,这是将异常处理统一到日志系统的第一步。 23 | 24 | ``STMInfo``更详细的分别存储了异常的各类信息(例如信息发出者,信息发出位置)。``STMInfo``也提供了``toString()``方法来将异常格式化为字符串。从而让各种异常信息规范化。 25 | 26 | 以下是``STMInfo``的节选代码片段,通过这个片段,我们可以了解``STMInfo``存储了哪些信息: 27 | 28 | ```C++ 29 | String sender; // 信息发送者 30 | String type; // 信息类型 31 | String message; // 信息内容 32 | String position; // 信息位置 33 | ``` 34 | 35 | 推荐调用下面这个构造函数来新建STMInfo: 36 | 37 | ```C++ 38 | STMInfo( 39 | const String &Sender, 40 | const String &Type, 41 | const String &Message, 42 | const String &Position 43 | ); 44 | ``` 45 | 46 | 在设计完``STMInfo``之后,用于异常抛出的``STMException``类也进行了改造,将抛出信息的数据类型从原本的字符串改造为``STMInfo``。对应的简便宏也做出了调整,具体宏定义如下: 47 | 48 | ```C++ 49 | #define THROW(info) ex->Throw(info); 50 | #define CATCH if (ex->isError) 51 | #define WARN(info) ex->Warn(info); 52 | #define ISWARNING (ex->isWarning) 53 | ``` 54 | 55 | ##### 将报错和警告信息统一在一起 56 | 57 | 将异常信息的生成函数迁移到``stamon::exception``命名空间中。``stamon::exception``的子命名空间存储着不同功能的异常信息生成函数。例如``stamon::exception::compiler``中存储着编译端的异常信息生成函数。 58 | 59 | 一个异常信息生成函数的原型通常符合下面的描述: 60 | 61 | ```C++ 62 | inline STMInfo 函数名(String position, String 相关参数,...); 63 | ``` 64 | 65 | 函数的返回值一般为``STMInfo``类型,函数的第一个参数一般为异常发生的位置,相关参数取决于具体需求,有些函数只有``position``参数。 66 | 67 | 例如``AstFileReader.cpp``中出现的代码为例,使用异常生成函数生成一个``STMInfo``,然后使用``THROW``宏抛出: 68 | 69 | ```C++ 70 | THROW(exception::astfilereader::RedundantRootNode("read()")); 71 | ``` 72 | 73 | 而``RedundantRootNode``的函数原型如下: 74 | 75 | ```C++ 76 | inline STMInfo RedundantEndNodeError(String position); 77 | ``` 78 | 79 | ##### 使用自动化脚本生成异常信息函数的代码及文档 80 | 81 | 将异常生成函数和对应的解释转写到``JSON``,并用Python编写代码生成脚本。使用 **配置+生成脚本** 的方式,开发者可以快速的创建异常处理生成函数,并且生成脚本也可以自动生成异常信息处理文档。 82 | 83 | 在``JSON``中声明一个异常信息生成函数,需要按照以下格式填写参数: 84 | 85 | ```json 86 | { 87 | "type": "异常信息名", // 异常类型 88 | "arg": ["参数1","参数2","..."], 89 | // 参数列表可以为空,生成的参数皆为String类型 90 | "msg": ["一段信息","$参数1","..."], 91 | // 信息列表 92 | // 如果一段信息的开头有“$”符号,则代表这是一个参数 93 | // 否则脚本会把它当作一个字符串字面量 94 | // 脚本会把这些信息和参数拼接在一起 95 | "doc": "该文件存在未知的常量,请检查编码文件或重新编译。" 96 | // 对这个异常的解释和解决措施,脚本会把他整理到文档中 97 | } 98 | ``` 99 | 100 | 一个``JSON``配置文件的文件名就是异常信息生成函数要存储的命名空间,例如``astrunner.json``中的异常信息生成函数都存在于``stamon::exception::astrunner``中。 101 | 102 | 一个``JSON``文件的总体格式如下: 103 | 104 | ```json 105 | { 106 | "name": "生成的文件名", 107 | "error": [ "一系列异常信息生成函数..." ], 108 | // 函数的格式已在上面说明 109 | "warning": [ "一系列异常信息生成函数..." ] 110 | // warning的函数格式同error 111 | } 112 | ``` 113 | 114 | 我们挑选了较为简短的``sfn.json``为例,其代码如下: 115 | 116 | ```json 117 | { 118 | "name": "SFNException.cpp", 119 | "error": [ 120 | { 121 | "type": "SFNError", 122 | "arg": ["msg"], 123 | "msg": ["$msg"], 124 | "doc": "SFN产生的综合性错误。" 125 | } 126 | ], 127 | "warning": [ 128 | { 129 | "type": "SFNWarning", 130 | "arg": ["msg"], 131 | "msg": ["$msg"], 132 | "doc": "SFN产生的综合性警告。" 133 | } 134 | ] 135 | } 136 | ``` 137 | 138 | 交给脚本后,生成``SFNException.cpp``,其代码如下: 139 | 140 | ```c++ 141 | /* 142 | Name: SFNException.cpp 143 | License: Apache 2.0 144 | Author: CLimber-Rong 145 | Date: 13/07/2025 17:43 146 | Description: 报错信息函数,由codegen.py自动生成 147 | */ 148 | 149 | #pragma once 150 | #include "String.hpp" 151 | #include "Exception.hpp" 152 | 153 | namespace stamon::exception::sfn { 154 | inline STMInfo SFNError(String position, String msg) { 155 | return STMInfo(String("sfn"), String("SFNError"), msg, position); 156 | } 157 | 158 | inline STMInfo SFNWarning(String position, String msg) { 159 | return STMInfo(String("sfn"), String("SFNWarning"), msg, position); 160 | } 161 | 162 | } 163 | ``` 164 | 165 | 自动化脚本和配置文件位于``src/exception/codegen/``目录,其中,``codegen.py``是生成脚本。 166 | 167 | 生成的文档会被命名为``Stamon异常信息修复指南.md``,并存入``doc``目录下。 168 | 169 | ##### 附:修改代码生成配置的方法 170 | 171 | * 如果想要在原配置文件的基础上进行变动,则只需要遵守上述的规则即可。 172 | * 如果想要添加或删除一些配置文件,则请在遵守上述的规则后,根据你对文件的变动,对``codegen.py``中的``setting_list``变量进行对应的修改。``setting_list``是一个列表,其成员由一系列元组组成。元组由两个元素构成,第一个元素是配置文件名(不包括``.json``后缀),第二个元素是该文件的作者(用于生成文件头注释)。 173 | * 如果想要重命名生成的文档,则可以修改``doc_path``变量。 174 | * 如果想要运行自动化脚本,则直接使用Python解释器,在``src/exception/codegen``目录下运行``codegen.py``(该文件依赖``time``和``json``库) -------------------------------------------------------------------------------- /include/stdc_implemented/ArrayList.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Name: ArrayList.hpp 3 | License: Apache 2.0 4 | Author: CLimber-Rong 5 | Date: 28/07/23 17:10 6 | Description: 动态数组 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "IArrayList.hpp" 12 | 13 | namespace stamon::stdc { 14 | template class ArrayList { 15 | /* 16 | * 采用双倍扩容法 17 | * 设立了cache_length和length 18 | * cache_length代表缓冲区长度,即当前最多能存储多少元素 19 | * length代表当前已经存储了多少元素 20 | * 不难发现,剩余的空间可以用(cache_length-length)来表示 21 | * 我们规定:当缓冲区已经被占满,将缓冲区翻倍扩容 22 | * 我们规定:当实际占用的容量小于缓冲区容量的二分之一时,将缓冲区缩小为原来的一半 23 | * 这样,我们将ArrayList的分配次数从线性级将为对数级 24 | */ 25 | 26 | T *cache = NULL; 27 | int cache_length = 0; 28 | int length = 0; 29 | 30 | void reallocList(int len) { 31 | if (len < (cache_length / 4)) { 32 | // 小于缓存的四分之一后,缓存才能缩小至二分之一 33 | // 之所以要小于四分之一的容量才缩小二分之一,是为了防止出现临界震荡 34 | 35 | T *temp = new T[cache_length / 2]; 36 | for (int i = 0; i < len; i++) { 37 | temp[i] = move(cache[i]); 38 | // 尽可能地复制 39 | } 40 | 41 | destroyCache(); // 销毁原来的cache 42 | 43 | cache = temp; 44 | cache_length = cache_length / 2; 45 | } else if (len >= cache_length) { 46 | // 扩容至两倍 47 | 48 | T *temp = new T[cache_length * 2]; 49 | for (int i = 0; i < length; i++) { 50 | temp[i] = move(cache[i]); 51 | } 52 | 53 | destroyCache(); // 销毁原来的cache 54 | 55 | cache = temp; 56 | cache_length = cache_length * 2; 57 | } 58 | // 如果不满足缩小条件,也不满足扩容条件,就什么都不做 59 | } 60 | 61 | void destroyCache() { 62 | if (cache != NULL) { 63 | delete[] cache; 64 | } 65 | } 66 | 67 | void deepCopy(const ArrayList &list) { 68 | // 深拷贝 69 | if (list.size() == 0) { 70 | // 缓冲区至少要有一个元素 71 | cache_length = 1; 72 | } else { 73 | cache_length = list.length; 74 | } 75 | 76 | cache = new T[cache_length]; 77 | 78 | for (int i = 0; i < list.size(); i++) { 79 | cache[i] = list[i]; 80 | } 81 | 82 | length = list.length; 83 | } 84 | 85 | void shallowCopy(ArrayList &&list) { 86 | // 浅拷贝 87 | cache = list.cache; 88 | cache_length = list.cache_length; 89 | length = list.length; 90 | list.cache = NULL; 91 | } 92 | 93 | public: 94 | ArrayList() { 95 | cache = new T[1]; 96 | cache_length = 1; 97 | length = 0; 98 | } 99 | 100 | ArrayList(int size) { 101 | if (size == 0) { 102 | cache_length = 1; 103 | } else { 104 | cache_length = size; 105 | } 106 | cache = new T[cache_length]; 107 | length = size; 108 | } 109 | 110 | ArrayList(const ArrayList &list) { 111 | deepCopy(list); 112 | } 113 | 114 | ArrayList(ArrayList &&list) { 115 | shallowCopy(move(list)); 116 | } 117 | 118 | ArrayList &operator=(const ArrayList &list) { 119 | destroyCache(); 120 | deepCopy(list); 121 | return *this; 122 | } 123 | 124 | ArrayList &operator=(ArrayList &&list) { 125 | destroyCache(); 126 | shallowCopy(move(list)); 127 | return *this; 128 | } 129 | 130 | void add(T value) { 131 | reallocList(length + 1); // 重新分配内存 132 | cache[length] = move(value); 133 | length++; 134 | } // 末尾添加值 135 | 136 | void insert(int index, T value) { 137 | reallocList(length + 1); // 重新分配内存 138 | 139 | for (int i = length - 1; i >= index; i--) { 140 | cache[i + 1] = cache[i]; 141 | } 142 | 143 | cache[index] = move(value); 144 | length++; 145 | } // 将value插入到[index] 146 | 147 | void erase(int index) { 148 | for (int i = index; i < length - 1; i++) { 149 | cache[i] = cache[i + 1]; 150 | } 151 | 152 | length--; 153 | reallocList(length); // 重新分配内存 154 | } // 删除[index] 155 | 156 | T at(int index) const { 157 | return cache[index]; 158 | } // 获取[index] 159 | 160 | bool empty() const { 161 | return (length == 0); 162 | } // 判断是否为空 163 | 164 | void clear() { 165 | destroyCache(); // 销毁 166 | cache = new T[1]; // 新建缓冲区 167 | cache_length = 1; 168 | length = 0; // 长度清零 169 | } // 清空列表 170 | 171 | int size() const { 172 | return length; 173 | } // 获得元素个数 174 | 175 | ArrayList operator+(ArrayList src) const { 176 | ArrayList rst = *this; 177 | 178 | for (int i = 0, len = src.size(); i < len; i++) { 179 | rst.add(move(src[i])); 180 | } 181 | 182 | return rst; 183 | } 184 | 185 | ArrayList &operator+=(ArrayList src) { 186 | return *(this) = *(this) + src; 187 | } 188 | 189 | T &operator[](int index) { 190 | return cache[index]; 191 | } 192 | 193 | T operator[](int index) const { 194 | return cache[index]; 195 | } 196 | 197 | ~ArrayList() { 198 | destroyCache(); 199 | } 200 | }; 201 | } // namespace stamon::stdc 202 | 203 | namespace stamon { 204 | 205 | template 206 | using ArrayList = interface::IArrayList>; 207 | 208 | } // namespace stamon --------------------------------------------------------------------------------