# 2025/08/08 工作日志

本次更新不会修改版本。
本次更新重构了文件编解码部分和顶层设计部分，封装了输入输出流，并由此删去了不必要的依赖库。

### 重新规范文件编解码的端序

由于不同架构的机器在存储时会有大小端序之差，因此为了让字节码能跨平台编译运行，我规范化了端序。

**编码生成的文件应该满足大端序**，而为了照顾不同机器的不同端序，我引入了新的配置，开发者可以根据自己实际平台的端序来调整配置。

我在``StamonConfig.hpp``中新增了一个枚举和两个标识符，用于标记不同机器的大小端配置，他们默认全是小端序：

```C++
enum STAMON_CODING_ENDIAN {
	// 机器的大小端控制
	StamonCodingLittleEndian = 0,
	StamonCodingBigEndian
};

/*...中略无关代码...*/

// 编码的端序
constexpr int StamonEncodingEndian = StamonCodingLittleEndian;
// 解码的顺序
constexpr int StamonDecodingEndian = StamonCodingLittleEndian;
```

值得注意的是，无论本地的机器采用什么样的端序，如果满足``StamonEncodingEndian==StamonDecodingEndian``（即编解码端序一致），那么编码生成的文件一定能在本地解码运行，但在其他平台上有可能出现错误。这意味着**如果开发者只有在本地编译运行字节码的需求，那么不一定需要费尽心思处理大小端序冲突，只要让编解码端序一致即可。**

### 大规模重构stamon::action

##### 正式引入BufferStream

我将``IOStream.cpp``改名为``BufferStream.cpp``，并完全重构了它，该文件存储了内存输入输出流的实现。在我目前的构想里，分步行动机制在执行一个动作组时，相邻的行动工具应该使用内存来存储并传递数据。``BufferInStream``和``BufferOutStream``成为了内存数据的载体。

解码器通常会作为动作组的第一个行动工具，在这种情况下，起始数据可能来源于键盘输入、文件读取等操作。但无论起始数据的获取形式如何，最终都要拷贝到内存中，以``BufferInStream``的形式传入到解码器中。

和上述情况对称的，编码器通常会作为动作组的最后一个工具，在这种情况下，结果数据可能会被保存到文件。但无论结果数据的保存形式如何，最终都要先将结果数据拷贝到内存中，以``BufferOutStream``的形式返回给分步行动机制，机制再根据具体情况处理。

在完成``BufferStream.cpp``的编写后，我开始修改所有的编解码器。之前，各个解码器都是手动托管内存读取以提升安全性，冗余代码多；编码器也直接操作保存到文件，不适合分步行动机制的发展。现在，我让他们统一使用``BufferStream.cpp``来托管内存数据，这同时解决了“编解码器冗余代码多”和“不适合分步行动机制发展”两大难题。

我们来看看``BufferStream.cpp``中的核心接口。

首先是``BufferInStream``类：

|接口原型|功能|
|:-|:-|
|``void reset()``|重新从头开始读取|
|``template<typename T> void read(T &data)``|读取一个数据|
|``template<typename T, int n> void readArray(T (&data)[n])``|读取一个数组|

为了方便理解，我提供了一个代码片段作为例子，在该片段中，我们假设有一个``BufferInStream``类型的变量``stream``，并且stream接下来要读取的内存数据为``0x12,0x34,0x56,0x78,0x90``，本地机器的解码端序为小端序：

```C++
char x;
char arr1[2], arr2[2];
stream.read(x); //读取x = 0x12
stream.readArray(arr1); //读取arr1 = {0x34, 0x56}
stream.read(arr2); //读取arr2 = {0x90, 0x78}
```

值得注意的是，``arr2``没有和``arr1``一样顺序读取，这是因为``stream.read``将``arr2``看作了一个整体，又因为本地机器是小端序的缘故，所以逆序读取了数据。以上例子不仅提供了读取的示例，也警醒了开发者不要把``read``和``readArray``混为一谈。

接着是``BufferOutStream``类，他的用法基本与``BufferInStream``对称：

|接口原型|功能|
|:-|:-|
|``template<typename T> void write(T &data)``|写入一个数据|
|``template<typename T, int n> void writeArray(T (&data)[n])``|写入一个数组|


在实现完``BufferStream.cpp``之后，其他的Reader和Writer也被修改，所有的数据读写操作都由``BufferInStream``和``BufferOutStream``托管，这极大减少了冗余代码，也统一了编解码器的实现模式。

##### 封装常量表编解码器

将常量表的读写功能单独提取出来，封装到``ConstTabReader.cpp``和``ConstTabWriter.cpp``中，这是在为未来的多字节码标准做准备。

### 删除不必要的代码

我剔除了AST部分一些获取节点数据的方法，我认为这是不必要的，直接访问AST节点的成员即可。

因为构造函数``ObjectManager()``并没有被用到，所以我删除了他。

由于``LineReader``可以使用``BufferInStream``实现。因此移除了LineReader。又因为LineReader被移除，所以FileMap也被认为是不需要的。所有的FileMap都被StringMap代替。

### 修正了命令行工具的文案细节

具体请参见``Main.cpp``的修改。

### 修改了logo目录的文件命名

见``mascot_v2.2.9.jpg``的文件修改记录。

### 修改了编译端的顶层设计

我认为之后的顶层前端功能都可以用函数来代替类，以此简便代码的调用方式。在这个决定上，我删除了``Compiler``类。取而代之的是两个函数：

```C++
ArrayList<SourceSyntax> *ParseTargetProject(
	STMException *e,
	ArrayList<String> *error_msg, ArrayList<String> *warning_msg,
	String filename, bool is_support_import, ArrayList<SourceSyntax> *src,
	StringMap<void> filemap, SyntaxScope global_scope
);

/*
编译一个Stamon项目。
e是异常类，error_msg是存储报错信息用的列表，warning_msg是存储警告信息用的列表，filename是源码文件名，is_support_import_表示是否支持引用其他源码，src是存储各个文件语法树用的列表，filemap是用来标记文件是否被引用过的映射表，global_scope是全局作用域。
返回处理后的存储着各个文件语法树的列表（即处理后的src）。
*/

ast::AstNode *MergeAST(ArrayList<SourceSyntax> *syntax_list);

/*
将各个语法树合并为一个。syntax_list是存储着各个文件语法树的列表。返回合并后的根节点。
*/

```