4个版本 (2个重大变更)

0.3.0 2024年5月24日
0.2.1 2024年4月5日
0.2.0 2024年3月2日
0.1.0 2024年1月29日

#150 in 过程宏


2 crates 中使用

Apache-2.0

18KB
414

Stageleft

Stageleft将阶段性编程的魔力带给Rust,使编写具有类型安全逻辑和高级API的宏变得容易,这些API可以在底层生成高效的代码。

示例

Stageleft使编写类型安全的代码生成器变得容易。例如,考虑一个将数字提高到幂次的函数,但幂次在编译时已知。然后,我们可以将幂次编译成反复平方基数。我们可以为此实现一个阶段性程序

use stageleft::{q, BorrowBounds, IntoQuotedOnce, Quoted, RuntimeData};

#[stageleft::entry]
fn raise_to_power(_ctx: BorrowBounds<'_>, value: RuntimeData<i32>, power: u32) -> impl Quoted<i32> {
    if power == 1 {
        q!(value).boxed()
    } else if power % 2 == 0 {
        let half_result = raise_to_power(_ctx, value, power / 2);
        q!({
            let v = half_result;
            v * v
        })
        .boxed()
    } else {
        let half_result = raise_to_power(_ctx, value, power / 2);
        q!({
            let v = half_result;
            (v * v) * value
        })
        .boxed()
    }
}

q!(...) 将代码 引用,这意味着它将被拼接到最终生成的代码中。我们可以将未知基数作为运行时参数(RuntimeData<i32>),但幂次在编译时已知,所以我们将其作为 u32。在这种情况下,_ctx 参数未使用,因为我们正在返回任何借用数据(有关详细信息,请参阅 stageleft::entry)。boxed API 允许我们从同一函数返回不同的拼接代码,而返回类型 impl Quoted<i32> 告诉编译器该函数将返回一个评估为 i32 的代码片段。我们可以像调用常规Rust宏一样调用这个阶段性函数

let result = raise_to_power!(2, 5);
assert_eq!(result, 1024);

但是,如果我们展开宏,我们可以看到代码已经被优化(为了简洁起见进行了简化)

{
    fn expand_staged(value: i32) -> i32 {
        let v = {
            let v = {
                let v = value;
                v * v  // 2^2
            };
            (v * v) * value // 2^5
        };
        v * v // 2^10
    }
    expand_staged(2)
}

设置

Stageleft 需要特定的工作空间设置,因为任何使用 Stageleft 的 crate 都必须有一个支持性宏 crate(其内容将被自动生成)。对于一个名为 foo 的 crate,您还需要一个辅助 crate foo_macro

主 crate foo 需要以下 Cargo.toml

[package]
// ...

[dependencies]
stageleft = "0.1.0"
foo_macro = { path = "../foo_macro" }

[build-dependencies]
stageleft_tool = "0.1.0"

辅助 crate 应该具有以下 Cargo.toml

[package]
name = "foo_macro"
// ...

[lib]
proc-macro = true
path = "../foo/src/lib.rs"

[features]
default = ["macro"]
macro = []

[dependencies]
// all dependencies of foo

[build-dependencies]
stageleft_tool = "0.1.0"

接下来,您需要为您的两个 crate 设置 build.rs 脚本。

foo

fn main() {
    stageleft_tool::gen_final!();
}

以及在 foo_macro

use std::path::Path;

fn main() {
    stageleft_tool::gen_macro(Path::new("../foo"), "foo");
}

依赖项

~3.5MB
~71K SLoC