0.1.0 2019年6月24日

#22 in #visibility

MIT 许可证

68KB
1.5K SLoC

logo

Drunk Octopus

ZZ (醉章鱼) 是一个 C 预编译器,用于处理 C 语言的一些痛点。

其主要用途是嵌入式系统,在那里我们仍然因为绝望而用 C 语言编程,因为没有其他真正有效的方法。

您还可以用它来构建跨平台库。构建和链接概念旨在导出一个带有命名空间的干净的 API。

太长了,没尝试

cd example
cargo run && ./target/exe

最小侵入性。

ZZ 是 C,并且仅在有用时才偏离 C。是的,C 语法很糟糕,但解决方案不是编造另一种糟糕的语法,你还需要无理由地学习它。

没有头文件

它将生成头文件以确保它仍然是 C 并与 C 代码一起工作,但你永远不会再次需要编写头文件。基本上,ZZ 文件在顶级使用 ZZ 语法,而在函数体中使用纯 C。编译器处理所有乏味的函数定义工作,并允许你用纯 C 编写其他内容。

模块/命名空间

许多 ZZ 是受 Rust 启发的,因此它只是从 Rust 精确复制了相同的命名空间概念。

在项目 "yo" 的 "foo.zz" 文件中名为 "bob" 的函数将被导出为 C 符号 "yo_foo_bob",但导入 "yo::foo::bob" 将将其重新引入作用域为 "bob"。

默认构建系统

C 的所有构建系统都是垃圾,因为没有约定,人们想要奇怪的功能。ZZ 没有任何功能。它从 ZZ 源文件构建目标二进制文件。如果你需要其他东西,你很可能不在嵌入式系统上工作,那么你为什么在这里?

它看起来怎么样

import errors::error;
import math::add;

fn some_helper(mutable error* err, mutable uint32_t* bob) -> uint32_t {
    printf("lol\n");
    if (bob) {
        *bob = add(horst(), foo);
        printf("bob %d\n", *bob);
    }
    return 32;
}


export fn horst() -> uint32_t {
    return 3;
}

它受 Rust 启发,但保留了 C 的参数声明顺序,因为如果身体仍然使用 C 风格,这将是令人困惑的。

默认情况下,参数是 const,除非声明为可变。您可以声明它们为 const,但这没有任何作用。

注意 horst() 的声明晚于其使用。声明顺序不再重要。

语言参考

顶级声明:fn,struct

fn 声明一个函数。体直接传递给 C 编译器。

struct 对结构体执行相同的操作。

宏定义几乎类似于 C 的 #define,但稍微稳定一些

macro CHECK(e) {
    if ((e) != 0) {
        printf("oh noes!\n");
    }
}

宏可以是多行的,但限制在花括号 {} 内。这有意禁用了某些疯狂的使用场景。

因为宏体未进行解析,zz 不知道宏依赖于什么。您可以通过使用 using 属性指定当宏导入时导入的额外模块,如下所示

export macro using(c::stdio::printf) CHECK(e) {
    if ((e) != 0) {
        printf("oh noes!\n");
    }
}

存储:const,static,atomic 和 thread_local

const 和 static 与 Rust 中的完全相同,但使用 C 语法。

export const uint32_t foo = 3;
static mutable float blarg = 2.0/0.3;
thread_local mutable bool bob = true;
atomic mutable int marvin = 0;

常量在每个模块中都是内联的,因此每个模块指向不同的内存。静态变量有一个全局存储位置,但对当前模块是私有的。

实际上,没有声明共享全局可写变量的方法。ZZ没有借用检查器,这个限制与防止多线程竞态无关。相反,声明是选定的,以便生成的导出二进制接口可以映射到任何其他语言。

如果您需要导出一个全局可写内存位置(这仍然是一个坏主意,因为线程),您可以定义一个函数,该函数返回指向局部静态变量的指针。

thread_local和atomic直接映射到C11关键字。ZZ可以使用更友好的关键字,因为没有顶层用户定义的名称。

可见性:私有,导出

默认情况下,所有声明对整个模块都是可见的,但在最终的二进制文件中不会被导出。

"导出"可以确保声明最终出现在最终结果中,即在二进制文件和导出头文件中。

"私有"将声明标记为当前模块/文件的本地声明。它不能在其他模块中使用,因此编译器可以优化它。在大多数情况下,这相当于C中的'static'关键字,但可以添加到任何声明中。

可变性:const,mutable

默认情况下,所有内容都是const。这与C相反。mutable关键字用于使全局变量或函数参数可变。

依赖项

~7–17MB
~212K SLoC