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