2023/08/12 工作日志

本次的提交内容很多,架构的改动也很大。让我们来依次介绍。

1. 改用VSCode开发

这个项目一直以来都是使用dev-c++开发,现在我正式改用vscode开发,效率更高。

不用担心,这个项目可以在没有VSCode的情况下编译运行。

2. AST设计

我优化了Ast节点的设计,成功的把33个节点整合到了24个节点。

3. 依赖库架构

在以前,我提供了依赖库的模板(以下简称库模板)和基于标准库的依赖库(以下简称标准库)实现方法。

但是,每当添加库函数的时候,我需要同时在库模板和标准库添加函数,来保证两者之间的同步,这很麻烦,有的时候也会忘记同步。

所以我做了一个违背祖宗的决定:删除库模板,只保留标准库。

当然,这并不意味着我会放弃“只需要实现依赖库就可以运行STVM”的特性。在项目发布之后,我会根据标准库重新编写库模板,来恢复这一特性。这样,我的项目架构会变得更加精简。

4. 依赖库是临时的

由于虚拟机具有GC功能,代码(包括依赖库)都应该向我的内存管理器申请内存。不过由于内存管理器的开发是比较后面的事,所以现在的代码直接向堆申请。

故此,现在的依赖库是临时的,我们将在后面把依赖库改装成“向内存管理器申请内存的依赖库”。

5. Ast节点定义的代码全部完工

大部分的节点代码来自我的代码生成器(接下来会详细介绍),其他的节点代码都是我手写的。

我们先来看一看src/ast目录下每个文件的作用

ast
|-Ast.hpp       //里面声明了Ast节点的基类,以及各个节点的类型,所有的Ast节点都应该继承这个类
|-LeafAst.cpp   //里面写了例如标识符、字面量等基本节点
|-SfnAst.cpp    //里面写了SFN(后续开发时会详细阐述)节点的定义
|-ExprAst.cpp   //与表达式有关的节点定义
|-CodeLogicAst.cpp  //里面写了如流程控制等与代码逻辑有关的节点
|-code_generator    //这是一个目录,目录里存放着代码生成器的源码

如果你尝试阅读Ast节点定义的代码,建议按照以下顺序阅读:

  1. Ast.hpp
  2. LeafAst.cpp
  3. SfnAst.cpp
  4. ExprAst.cpp
  5. CodeLogic.cpp

如果你想要直接引用所有Ast节点,请直接引用Ast.hpp。
注意,所有的Ast都存放在stamon::ast这个命名空间里,所以如果你要使用Ast节点,可以写以下代码

#include"Ast.hpp"   //引用Ast.hpp
using namespace stamon::ast;    //引用Ast的命名空间

6. 关于Ast代码生成器的原理

如果你不想了解项目的开发流程,请不要阅读这节内容,也不需要阅读src/ast/code_generator目录下的源码。

code_generator的目录如下

|-setting.json      //里面描述了每个节点类的具体信息
|-generator.py      //可以根据setting.json描述的具体信息生成C++代码到code.cpp里
|-code.cpp          //生成出来的C++代码

代码生成的流程显而易见:
setting.json(配置件)->generator.py(生成器)->code.cpp(生成结果 )

注意,生成出来的code.cpp还需要复制粘贴到src/ast目录的源码里。

如果你想要尝试着自己生成code.cpp,直接运行generator.py即可,运行它不需要任何额外的参数

接下来我们来从里到外的讲一讲setting.json的格式

如果你想要声明一个构造函数,应该这么写:

{
    "args": "参数1,参数2,...,参数n",
    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
}

然而,一个类通常有多个构造函数,如果你想描述一个类的所有构造函数,应该这么写

"structure": [
    {
        "args": "参数1,参数2,...,参数n",
        "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
    },
    {
        "args": "参数1,参数2,...,参数n",
        "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
    },
    ...
    {
        "args": "参数1,参数2,...,参数n",
        "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
    }
]

是的,用一个列表来存放所有的构造函数

当然,一个类不止有构造函数,还有公共成员和私有成员,声明公共成员和私有成员应该这么写:

"private_members": "在这里写私有成员的定义,不需要写 “private:” ",
"public_members": "在这里写公共成员的定义,不需要写 “public:” "

描述一个节点类的类名,应该这么写:

"name": "节点的名字(如Expression、Program),不需要加前缀“Ast” "

至此,我们已经知道怎么声明构造函数、公共成员、私有成员和类名了,我们用一个字典来存放他们:

{
    "name": "节点的名字(如Expression、Program),不需要加前缀“Ast” ",
    "private_members": "在这里写私有成员的定义,不需要写 “private:” ",
    "public_members": "在这里写公共成员的定义,不需要写 “public:” ",
    "structure": [
        {
            "args": "参数1,参数2,...,参数n",
            "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
        },
        {
            "args": "参数1,参数2,...,参数n",
            "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
        },
        ...
        {
            "args": "参数1,参数2,...,参数n",
            "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
        }
    ]
}

现在,我们学会了怎么去声明一个具体的类,但是我们需要声明很多的类,所以我们还要再套一个列表,来存储若干个类。

套了列表的代码看起来是这样的:

"ast_class": [
    {
        "name": "节点的名字(如Expression、Program),不需要加前缀“Ast” ",
        "private_members": "在这里写私有成员的定义,不需要写 “private:” ",
        "public_members": "在这里写公共成员的定义,不需要写 “public:” ",
        "structure": [
            {
                "args": "参数1,参数2,...,参数n",
                "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
            },
            {
                "args": "参数1,参数2,...,参数n",
                "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
            },
            ...
            {
                "args": "参数1,参数2,...,参数n",
                "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
            }
        ]
    },
    ...
    {
        "name": "节点的名字(如Expression、Program),不需要加前缀“Ast” ",
        "private_members": "在这里写私有成员的定义,不需要写 “private:” ",
        "public_members": "在这里写公共成员的定义,不需要写 “public:” ",
        "structure": [
            {
                "args": "参数1,参数2,...,参数n",
                "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
            },
            {
                "args": "参数1,参数2,...,参数n",
                "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
            },
            ...
            {
                "args": "参数1,参数2,...,参数n",
                "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
            }
        ]
    }
]

我们已经学会了怎么声明若干个节点类,现在我们用一个字典来存放若干个节点类,就大功告成了:

{
    "ast_class": [
        {
            "name": "节点的名字(如Expression、Program),不需要加前缀“Ast” ",
            "private_members": "在这里写私有成员的定义,不需要写 “private:” ",
            "public_members": "在这里写公共成员的定义,不需要写 “public:” ",
            "structure": [
                {
                    "args": "参数1,参数2,...,参数n",
                    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
                },
                {
                    "args": "参数1,参数2,...,参数n",
                    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
                },
                ...
                {
                    "args": "参数1,参数2,...,参数n",
                    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
                }
            ]
        },
        ...
        {
            "name": "节点的名字(如Expression、Program),不需要加前缀“Ast” ",
            "private_members": "在这里写私有成员的定义,不需要写 “private:” ",
            "public_members": "在这里写公共成员的定义,不需要写 “public:” ",
            "structure": [
                {
                    "args": "参数1,参数2,...,参数n",
                    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
                },
                {
                    "args": "参数1,参数2,...,参数n",
                    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
                },
                ...
                {
                    "args": "参数1,参数2,...,参数n",
                    "code": "在这里写上构造函数的函数体(不需要用花括号嵌套)"
                }
            ]
        }
    ]
}

这就是描述若干个节点类的格式,从构造函数的声明开始一步一步看下来,也不会太复杂。

7. 接下来要做的事

  1. 完成STVM数据类型的定义
  2. 完成STVM内部数据结构的部分定义

后记

今天早上代码终于写完了,代码格式化、工作日志等等折腾完之后已经下午了 (我真爱死VSCode了) ,在准备git commit的时候突然发现数字字面量的节点类好像有瑕疵,还好抢救过来了。

希望不要提交之后发现代码有瑕疵。