关于本指南

本文档是开发Erg项目的指南 它描述了如何做出贡献,做开发的指南,以及如何建立开发环境等

分支机构命名和运营策略

badge

main(基于主干的开发)

  • 主要开发分支

  • 必须满足以下条件

  • 编译成功

beta(目前不创建)

  • 最新的 Beta 版本

  • 必须满足以下条件

  • 编译成功

  • 所有测试都会成功

feature-*(名字)

  • 开发特定功能的分支

  • 切开 main

  • 没有条件

issue-*(#issue)

  • 解决特定 issue 的分支

  • 没有条件

fix-*(#issue or bug 名字)

  • 修复特定错误的分支(如果该问题是一个错误,则代替issue-*创建)

  • 没有条件。

erg 构建功能

badge

debug

进入调试模式。结果,Erg 内部的行为顺序显示在日志中 独立于 Rust 的 debug_assertions 标志

japanese

将系统语言设置为日语 Erg 内部选项、帮助(帮助、版权、许可证等)和错误显示为日语

simplified_chinese

将系统语言设置为简体中文 Erg 内部选项、帮助(帮助、版权、许可证等)和错误显示为简体中文

traditional_chinese

将系统语言设置为繁体中文 Erg 内部选项、帮助(帮助、版权、许可证等)和错误显示为繁体中文。

unicode/pretty

使得编译器显示丰富内容

pre-commit

用于在预提交中运行测试。这是一个bug解决方案

large_thread

增加线程堆栈大小。用于Windows执行和测试执行

els

通过 --language-server 使其变得可用 通过 erg --language-server 打开

py_compatible

启用Python兼容模式,使部分api和语法与Python兼容。用于pylyzer

Erg存储表结构

badge

  └─┬ assets: 图片等
    ├─ CODE_OF_CONDUCT: 行为准则
    ├─┬ crates
    │ ├─ els: Erg语言服务器 (Language Server)
    │ ├─ erg_common: 通用工具
    │ ├─ erg_compiler: 编译器,**Erg核心**
    │ └─ erg_parser: 解析器
    ├─┬ doc: 文档
    │ ├─┬ CN
    │ │ ├─ API: Erg标准API
    │ │ ├─ compiler: 关于编译器实现
    │ │ ├─ dev_guide: 开发者和贡献者指南
    │ │ ├─ python: Erg开发必备的Python知识
    │ │ ├─ syntax: Erg语法
    │ │ └─ tools: Erg命令行工具
    │ └─┬ JA
    │ ...
    ├─ examples: 示例代码
    ├─ library: Erg脚本库
    ├─ src: main.rs和驱动所在目录
    └─ tests: 测试代码

格式

badge

任何不符合以下规则的文件都将得到更正

  • 以某种语气写代码注释或内部文档
  • 向外部(普通用户)展示的文档应该越来越多地编写
  • 始终包含定义、含义或文档中首次出现的术语的链接
  • 仅对理解正文所必需的补充句子使用括号作为附加条件,对于理解正文不是必需的句子使用脚注1</ sup>
  • 如果文档内容过时,按照此方法更新

1 参见这里了解如何编写脚注。

在应用程序中嵌入Erg编译器

badge

在应用程序中嵌入Erg很容易

[dependencies]
erg = "0.5.12" # 选择最新版本
use erg::DummyVM;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut vm = DummyVM::default();
    let _res: String = vm.eval("print! \"Hello, world!\"")?;
    Ok(())
}

执行需要Python

还有一个不连接到运行时的独立编译器版本

[dependencies]
erg_compiler = "0.5.12" # 选择最新版本
use erg_compiler::Compiler;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut compiler = Compiler::default();
    let code = compiler.compile("print!\"Hello, world!\"", "exec")?;
    code.dump_as_pyc("o.pyc", None)?;
    Ok(())
}

Compiler输出一个名为CodeObj的结构。这通常不是很有用,所以你可能想要使用Transpiler,它输出一个Python脚本

use erg_compiler::Transpiler;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut transpiler = Transpiler::default();
    let script = transpiler.transpile("print!\"Hello, world!\"", "exec")?;
    println!("{}", script.code);
    Ok(())
}

其他示例还有输出HIR(高级中间表示)的HIRBuilder和输出AST(抽象语法树)的ASTBuilder

use erg_compiler::HIRBuilder;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut builder = HIRBuilder::default();
    let artifact = builder.build("print!\"Hello, world!\"", "exec")?;
    println!("{}", artifact.hir);
    Ok(())
}
use erg_compiler::ASTBuilder;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut builder = ASTBuilder::default();
    let ast = builder.build("print! \"Hello, world!\")")?;
    println!("{}", ast);
    Ok(())
}

执行语义分析的结构实现了一个名为ContextProvider的trait。它可以获取模块中变量的信息,等等

use erg_compiler::Transpiler;
use erg_compiler::context::ContextProvider;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut transpiler = Transpiler::default();
    let script = transpiler.transpile("i = 0", "exec")?;
    println!("{}", script.code);
    let typ = transpiler.get_var_info("i").0.t;
    println!("{typ}");
    Ok(())
}

开发环境

badge

你需要安装什么

  • Rust(与 rustup 一起安装)

    • 版本 >= 1.64.0
    • 2021年版
  • pre-commit

我们使用pre-commit来自动进行clippy检查和测试。即使没有错误,检查也可能在第一次运行时失败,在这种情况下,您应该再次尝试提交。

  • Python3 解释器

推荐

  • 编辑器: Visual Studio Code
  • VSCode 扩展: Rust-analyzer、GitLens、Git Graph、GitHub Pull Requests and Issues、Markdown All in One、markdownlint
  • 操作系统: Windows 10/11 | Ubuntu 20.04/22.04 | macOS Monterey/Ventura

多语言错误信息

badge

Erg 正在推动消息(开始、选项、文档、提示、警告、错误消息等)的多语言化。如果你不熟悉 Rust 或 Erg,也可以参与此项目。请务必配合

以下是多语种方法的说明

查找switch_lang!

在 Erg 源代码中找到(使用 grep 或编辑器的搜索功能)。我们应该能找到下面这样的东西


#![allow(unused)]
fn main() {
switch_lang!(
    "japanese" => format!("この機能({name})はまだ正式に提供されていません"),
    "english" => format!("this feature({name}) is not implemented yet"),
),
}

此消息目前仅支持日语和英语。让我们尝试添加简体消息

添加消息

请在查看其他语言内容的同时添加翻译消息。最后不要忘记逗号(,)


#![allow(unused)]
fn main() {
switch_lang!(
    "japanese" => format!("この機能({name})はまだ正式に提供されていません"),
    "simplified_chinese" => format!("该功能({name})还没有正式提供"),
    "english" => format!("this feature({name}) is not implemented yet"),
),
}

另外,英语是默认设置,一定要排在最后

{name}部分是 Rust 的格式化功能,允许你将变量的内容(name)嵌入到字符串中

构建

现在,我们使用选项构建它

screenshot_i18n_messages

你做到了!

FAQ

Q: 像这样的指定是什么意思?A: {RED} 及更高版本将显示为红色。重新启动交互渲染

Q: 如果想添加自己的语言,该如何替换部分?答: 目前支持以下语言

  • "english"(默认设置)
  • "japanese" (日语)
  • "simplified_chinese" (简体中文)
  • "traditional_chinese" (繁体中文)

如果你想添加其他语言,请提出请求。

Rust 代码指南

badge

本地规则

  • 使用 log! 进行调试输出(使用 println! 等进行输出处理,这也是发布所必需的)
  • 未使用或内部变量/方法(私有且仅用于特定功能)必须以 _ 为前缀。如果要避免与保留字冲突,请在末尾添加一个_
  • 使用clippy。然而,有些规则是不合理的,所以你可以使用#[allow(clippy::…)]来忽略除「deny」之外的规则。

推荐代码

  • 定义和使用特定领域的枚举而不是数字枚举或布尔值
  • 将访问修饰符保持在最低限度。即使在发布时也要优先使用 pub(mod)pub(crate)
  • 将 for 表达式中的可迭代对象显式转换为迭代器(for i in x.iter() 而不是 for i in x)
  • 懒惰的评价。例如,如果 default 不是文字,请使用 unwrap_or_else 而不是 unwrap_or

不鼓励使用代码

  • 大量使用返回类型重载。特别是使用大量非显而易见的 .into 的代码。这是因为类型推断结果可能违反直觉。在这种情况下,建议使用 from 代替
  • 大量使用 Deref。这有效地提出了与继承相同的问题

根据上下文做出决策的代码

  • 定义未使用的辅助方法
  • 大量使用 unwrapclone。在某些情况下,没有什麽比这样做更好的了。

依赖关系

依赖关系应该尽可能地最小化,那些必要的依赖关系应该由Erg开发团队来实现。只有当外部依赖很难实现或依赖于硬件时才允许使用。例如:libcwinapi),或者没有外部依赖的crate(例如:unicode-xid)。否则,它们可能被允许作为可选依赖项(例如https客户端)。在任何情况下,都应选择保养良好和广泛使用的

此规则仅适用于Erg编译器, Erg工具和库可以自由添加它们自己的依赖项。

测试

badge

测试是确保代码质量的重要部分

使用以下命令执行测试

cargo test --features large_thread

由于cargo需要一个小线程来运行测试,我们使用 large_thread 标志来避免堆栈溢出

放置测试

根据实现的特性来安排它们。将解析器测试放置在erg_parser/tests下,将编译器(类型检查器等)测试放置在erg_compiler/tests下,将用户可以直接使用的语言特性测试放置在erg/tests下(然而,这些测试目前正在开发中,不一定按照这种惯例安排)

如何编写测试

有两种类型的测试。positive测试和negative测试。 positive测试是检查编译器是否按预期运行的测试,而negative测试是检查编译器是否正确地输出无效输入的错误。 由于编程语言处理器的性质,在所有软件中,它们特别容易受到无效输入的影响,并且必须始终将错误呈现给用户,因此后者也必须得到照顾。

如果你在语言中添加了一个新特性,你至少需要写一个positive测试。另外,如果可能的话,请写同时编写negative测试。

故障诊断

badge

Q: 本地生成成功, 但 GitHub Actions 生成失败

A: 您正在处理的分支可能没有Pullmain中的更改

Q: 提交前检查失败

A: 尝试再次提交, 第一次可能会误判, 如果一次又一次的失败, 那么你的代码可能包含错误

Q: build.rs 无法正常运行

A: 检查 build.rs 运行目录中的额外文件/文件夹 (例如 __pychache__)

版本控制

badge

Erg 编译器根据语义版本控制分配版本号。 但是,在版本 0 期间,应用的规则与平时不同(遵循比语义版本控制更详细的规则)。 需要注意的是,Erg 中有两种类型的兼容性。一个是规范兼容性,表示与语言规范的兼容性,另一个是内部兼容性,表示与(公共)API(如编译器)的兼容性。

  • 在版本 0 期间,次要版本中的规范和内部兼容性可能会中断。这与正常的语义版本控制相同。
  • 补丁版本不会破坏规范兼容性,但不能保证内部兼容性。
  • 新功能主要在次要版本中添加,但如果它们是简单的语言功能或编译器功能,也可以在补丁版本中添加。

发布周期

  • 补丁大约每 1~2 周发布一次。
  • 次要版本的发布频率大约是补丁发布的 10 倍,即每 3~6 个月发布一次。
  • 主要版本是无限期发布的。目前未计划版本 1 版本的计划。

每晚/测试版

Erg 将不定期进行夜间和测试版发布。每晚发布是新补丁版本的预发布,测试版是新的次要/主要版本的预发布。 每晚和测试版发布在 crates.io 上,测试版也发布在 GitHub 版本上。

每晚版本的格式是0.x.y-nightly.z。测试版也是如此。

几乎每天都会发布每晚版本(如果没有更改,则不会发布),而测试版则不定期发布。但是,一旦发布测试版,几乎每天都会发布新的测试版。