21个版本 (1个稳定版)
| 1.0.0 | 2024年1月5日 |
|---|---|
| 0.6.1 | 2024年1月4日 |
| 0.5.2 | 2023年12月29日 |
| 0.5.0 | 2023年11月24日 |
| 0.2.6 | 2023年10月27日 |
#311 在 进程宏
每月251次下载
495KB
10K SLoC
Chandeliers-Lus
Chandeliers套件的客户端。
该crate提供了定义Lustre在Rust中深度嵌入的decl宏。
用法
在本节中,我们假设读者熟悉Lustre语言。
声明常量和节点
一个自包含的Lustre程序通常可以仅通过少量的修改,封装在decl宏中。以下是一些示例
chandeliers_lus::decl! {
const ZERO: int = 0;
const ONE: int = 1;
node fibonacci() returns (n : int);
let
n = ZERO -> ONE -> (pre n + pre pre n);
tel;
node fibsum() returns (s : int);
let
s = fibonacci() + (0 fby s);
tel;
}
chandeliers_lus::decl! {
node selector(left, right : float; switch : bool) returns (out : float);
let
out = if switch then left else right end;
tel;
}
导入外部定义
Chandeliers不提供任何内置函数。这是故意的。
而是提供了一个灵活且健壮的方法来声明外部定义。任何extern定义的对象将被Chandeliers的静态分析器假设为已定义,如果在代码生成时缺失,则Rustc将发出错误。
extern包括
- 另一个
decl块中的声明(以下示例), - 标准库中的节点(见crate
chandeliers-std)或另一个crate中的节点, - 直接在Candle中编写的自定义节点(更多信息请参阅
chandeliers-sem,示例请参阅tests/pass/std和特别地tests/pass/std/rand.rs)。
使用另一个decl块中的定义
chandeliers_lus::decl! {
node counter() returns (n : int);
let n = 0 fby (n + 1); tel;
}
// `decl` is local and cannot know that `counter` exists in another block.
// If we want to use it here, we need to redeclare it as an `extern node`.
chandeliers_lus::decl! {
// any signature inconsistencies will produce type errors.
extern node counter() returns (n : int);
node cumul() returns (n : int);
let n = counter() + (0 fby n); tel;
}
同样,一个
chandeliers_lus::decl! {
extern const PI: float;
...
makes PI 可用于其余的块。
使用标准库中的定义
use chandeliers_std::float_of_int;
chandeliers_lus::decl! {
extern node float_of_int(i : int) returns (f : float);
...
}
如何编写main函数
decl宏永远不会插入main,相反,应该手动定义以与环境交互。这是由于decl块在其定义下公开提供相同名称的事实。
main可能看起来像这样
use chandeliers_sem::traits::*;
chandeliers_lus::decl! {
node cumul(i : int) returns (s : int);
let s = i + (0 fby s); tel;
}
fn main() {
let mut cumul = cumul::default();
for i in 0..100 {
let s = cumul.step(i.embed()).trusted();
println!("{s}");
}
}
上述功能由特征 Step、Default、Embed 和 Trusted 提供,这些特征在相关对象上自动实现,要么是通过 decl 宏本身实现的,要么是在 chandeliers-sem 中通用定义的。
decl 生成的节点在内部操作不同类型的值(同构于 Option<T>),而特征 Embed 和 Trusted 在所有元组类型上产生等价的 map(Some) 和 map(Option::unwrap)。
更具体地说,元组类型 (T, U, V) 实现了 Embed,输出类型为 (Nillable<T>, Nillable<U>, Nillable<V>),而 Trusted 产生其逆。使用 Embed 总是安全的,但您应该只在非 Nil 的值上使用 Trusted。在 decl 内部执行的静态分析保证,所有输入都不是 Nil 的节点将具有非 Nil 的输出,因此只需要验证不是由 decl 块生成的 extern 节点 的实现。
与标准 Lustre 的显著局限性和差异
由于各种原因——包括对 Rust 语法有限的控制和解析效率——一些 Lustre 程序可能无法直接支持,并需要微小的语法调整。以下是在您急于复制粘贴 Lustre 代码时可能会遇到的一些情况:
node定义必须以分号;结尾,- 元组解包
(x, y, z) = foo()需要周围的(...), - 使用 Rust 习惯用法来确定允许尾随逗号的上下文:
1 = (1) <> (1,)和(1, 2) = (1, 2,)。 - 一些Rust保留关键字(如
self、super、move等)不能用作标识符, - 只支持Rust风格的注释,即行注释使用
//,块注释使用/* ... */, f((a, b, c))可以隐式转换为f(a, b, c),使得当函数的输出元组与另一个函数的输入元组的阶数相同时,可以轻松地进行函数组合。
此外,关于语义,还做了以下选择
- 延迟操作符
->具有特殊的结合规则,使得a -> b -> c(a1, b2, c3, c4, c5, c6, ...)既不等于a -> (b -> c)(a1, c2, c3, c4, c5, c6, ...),也不等于(a -> b) -> c(a1, c2, c3, c4, c5, c6, ...)。 - 时间操作符
_ -> _、pre _、_ fby _仅适用于具有节点隐式时钟的表达式。使用注解#[universal_pre]可以取消此限制,该注解使用与时钟表达式兼容的时间操作符的另一种编码,但可能会带来轻微的性能损失。
依赖项
~0.4–0.8MB
~19K SLoC