2 个版本
使用旧的 Rust 2015
0.0.2 | 2015 年 2 月 27 日 |
---|---|
0.0.1 | 2015 年 2 月 27 日 |
#2265 在 Rust 模式
23 次每月下载
在 3 个crate(2 个直接使用)中使用
21KB
314 行
Rust 的混入
使用任意语言编写代码,直接在您的crate中生成Rust代码。
#![feature(plugin)]
#![plugin(external_mixin)]
#![plugin(rust_mixin)]
python_mixin! {"
x = 1 + 2
print('fn get_x() -> u64 { %d }' % x)
"}
fn main() {
let value = get_x();
let other_value = rust_mixin! {r#"
fn main() {
println!("{}", 3 + 4);
}
"#};
assert_eq!(value, 3);
assert_eq!(other_value, 7);
}
这包括三个库
rust_mixin
— 使用 Rust 生成您的代码。external_mixin
— 使用 Python 或 Ruby 等脚本语言生成您的代码。- `external_mixin_umbrella — 支持库,以保持上述简洁。
我实际上应该使用这些吗?
可能不是,这是我正在尝试更多语言插件的实验。更便携/可用的代码生成方法是使用 一个 Cargo
构建脚本 加上 include!
宏。
一些缺点(不全面)
-
混入,如
python_mixin!
依赖于用户路径中有正确命名的二进制文件,并且,例如,“python
”有时是 Python 2,有时是 Python 3,并且意味着要求用户在 Windows 上安装 Python。(构建脚本只需要 Cargo 和 Rust 编译器,如果用户正在尝试构建您的 Rust 代码,则可以保证用户拥有这些。) -
生成的代码中的错误难以调试,尽管宏尝试提供尽可能有用的错误消息,例如,例如,文件/行号对于与原始字符串中包含源代码的相关部分尽可能接近的错误。然而,解析的 Rust 实际上并没有出现在磁盘上或任何其他地方,因此您无法轻松地看到编译器抱怨时的完整上下文(相比之下,构建脚本只需在您的文件系统中生成一个普通文件)。
安装
这两个插件crate都可在crates.io上找到: rust_mixin
, external_mixin
。因此,您可以添加任何子集
[dependencies]
rust_mixin = "*"
external_mixin = "*"
将其添加到您的 Cargo.toml
中。
rust_mixin
用Rust编写生成您的Rust代码,直接在您的Rust中(yo dawg)。该插件将其参数编译和运行为Rust程序,然后将输出插入主crate中,类似于 macro_rules!
宏。
rust_mixin
插件接受一个字符串,其中包含一个要使用 rustc
编译的Rust程序。该程序应打印有效的Rust代码到stdout。每个 rust_mixin
调用都是独立的。字符串参数在使用之前先进行宏展开,因此使用 concat!()
构造调用是合法的。
该宏支持在字符串字面量之前有一个可选的 { ... }
块,以指定选项。当前支持的唯一选项是 arg
:它可以多次指定,并且按顺序将参数传递给 rustc
。
示例
尽可能好地计算斐波那契数,通过让Rust打印一个计算每个数的函数
#![feature(plugin)]
#![plugin(rust_mixin)]
rust_mixin! {r#"
fn main() {
println!("fn fib_0() -> i32 {{ 0 }}");
println!("fn fib_1() -> i32 {{ 1 }}");
for i in 2..(40 + 1) {
println!("fn fib_{}() -> i32 {{ fib_{}() + fib_{}() }}",
i, i - 1, i - 2);
}
}
"#}
fn main() {
println!("the 30th fibonacci number is {}", fib_30());
}
在编译时进行斐波那契计算,直接了当地,因此我们想要一些优化
#![feature(plugin)]
#![plugin(rust_mixin)]
fn main() {
let fib_30 = rust_mixin! {
{ arg = "-C", arg = "opt-level=3" }
r#"
fn fib(n: u64) -> u64 {
if n <= 1 { n } else { fib(n - 1) + fib(n - 2) }
}
fn main() {
println!("{}", fib(30))
}
"#};
println!("the 30th fibonacci number is {}", fib_30);
}
external_mixin
使用各种脚本语言生成Rust代码。这有一个 external_mixin!
宏,它支持任意解释器,以及为几种语言提供的特殊支持:python_mixin!
、ruby_mixin!
、sh_mixin!
、perl_mixin!
。
与 rust_mixin!
一样,这些宏将它们的程序作为字符串接受,该字符串会进行宏展开,并且每个调用都是独立的。程序应打印有效的Rust代码到stdout。可以通过可选的 { ... }
块指定选项,在字符串字面量之前。
external_mixin!
宏是最灵活的形式,它接受一个必选的 interpreter
参数:这个程序会与一个包含代码片段的文件一起调用,该文件作为最后一个参数。
external_mixin!
和语言特定宏都支持 arg
选项,它可以多次指定,并且按顺序传递给主二进制文件。
可移植性?
这些宏依赖于将外壳程序输出到解释器,依赖于用户路径中存在适当命名的可执行文件(希望它是正确的版本……)。因此,这既不可移植也不可靠。至少 rust_mixin!
的用户保证有一个可用的 rustc
,这里没有这样的保证。
示例
计算文件/文件夹在(Unix)文件系统顶部的数量。
#![feature(plugin)]
#![plugin(external_mixin)]
fn main() {
let file_count = sh_mixin!("ls / | wc -l");
println!("there are {} files in /", file_count);
}
通过Ruby计算程序构建时的Unix时间。
#![feature(plugin)]
#![plugin(external_mixin)]
fn main() {
let build_time = ruby_mixin!("puts Time.now.to_i");
}
使用Python 2的裸打印语句和Python 3的除法语义(并猜测由 python_mixin!
使用的 python
二进制文件的版本)
#![feature(plugin)]
#![plugin(external_mixin)]
fn main() {
let value2 = external_mixin! {
{ interpreter = "python2" }
"print 1 / 2"
};
let value3 = external_mixin! {
{ interpreter = "python3" }
"print(1 / 2)"
};
let value_unknown = python_mixin!("print(1 / 2)");
if value_unknown as f64 == value3 {
println!("`python_mixin!` is Python 3");
} else {
println!("`python_mixin!` is Python 2");
}
}
external_mixin_umbrella
此存储库的最高级别项是一个库,旨在最大化 external_mixin
和 rust_mixin
之间的代码共享,以便它们的实现分别只有100和50行。