5 个不稳定版本
0.3.1 | 2023年6月8日 |
---|---|
0.2.2 | 2022年5月22日 |
0.2.1 | 2022年5月22日 |
0.2.0 | 2022年5月22日 |
0.1.0 | 2021年1月26日 |
在 编程语言 中排名 149
每月下载量 21 次
450KB
12K SLoC
SPAIK LISP 编程语言
SPAIK 是 Rust 的动态扩展语言。它实现了宏、垃圾回收、迭代器、延续、async/await,并将其封装在一个(希望)易于使用的高级 Rust API 中。
本 README 包含许多关于如何使用 SPAIK 的简短代码片段,而完整的示例可以在 examples 目录中找到,更详细的 API 文档可以在 docs.rs 中找到。
基本用法
对于基本用法,您只需要 eval
和 exec
方法(exec
只是 eval
但它会丢弃结果以帮助类型推断。)
let mut vm = Spaik::new();
vm.exec(r#"(println "Hello, World!")"#)?;
vm.set("*global*", 3);
let res: i32 = vm.eval(r#"(+ 1 *global*)"#)?;
assert_eq!(res, 4);
// Equivalent to exec
let _: Ignore = vm.eval(r#"(+ 1 *global*)"#)?;
加载代码
您可能不想将所有 SPAIK 代码都存储为 Rust 中的嵌入字符串,因此您当然可以从文件系统中加载 SPAIK 脚本。
vm.add_load_path("my-spaik-programs");
vm.load("stuff");
add_load_path
方法将给定的字符串添加到全局变量 sys/load-path
中,它只是一个 SPAIK 向量。您也可以从 SPAIK 中修改它
(eval-when-compile (push sys/load-path "my-dependencies"))
(load dependency)
但请注意,我们不得不在添加新路径时使用 (eval-when-compile ...)
,因为 (load ...)
也会在编译期间运行。
将函数导出到 SPAIK
对于函数既能从 Rust 调用也能从 Lisp 调用通常很有用,这里函数 add_to
被定义为全局作用域中的 Rust 函数和 Fns
中的 SPAIK 函数。然后我们可以选择将 Fns::add_to
函数注册到虚拟机中。
use spaik::prelude::*;
struct Fns;
#[spaikfn(Fns)]
fn add_to(x: i32) -> i32 {
x + 1
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut vm = Spaik::new();
println!("Calling from Rust: {}", add_to(2));
vm.register(Fns::add_to);
vm.exec(r#"(let ((r (add-to 2))) (println "Calling Rust from SPAIK: {r}"))"#)?;
Ok(())
}
命令模式,或“枚举调用”
与动态PL VM交互的一种人机工程学方法是使用命令模式,这可以理解为“枚举调用”。完整示例可以在examples/command-pattern-multi-threaded中找到。
enum Cmd {
Add(i32),
Subtract(i32),
}
// We can fork the vm onto its own thread first, this takes a Spaik and
// returns a thread-safe SpaikPlug handle.
let mut vm = vm.fork::<Msg, Cmd>();
vm.cmd(Cmd::Add(1337));
vm.cmd(Cmd::Add(31337));
// Loop until all commands have been responded to
while recvd < 2 {
processing();
while let Some(p) = vm.recv() {
vm.fulfil(p, 31337);
recvd += 1;
}
}
// Join with the VM on the same thread again, turning the SpaikPlug handle
// back into a Spaik.
let mut vm = vm.join();
let glob: i32 = vm.eval("*global*").unwrap();
(define *global* 0)
(defun init ())
(defun add (x)
(let ((res (await '(test :id 1337))))
(set *global* (+ *global* res x 1))))
在单线程设置中使用枚举调用时,请使用Spaik::query
方法,该方法会立即返回结果。对于Spaik
,也存在cmd
方法,但它返回Result<()>
。这与eval
/ exec
的分离相似,并且出于相同的原因(类型推断)。
html
宏
由于在LISPs中创建新的语法结构非常容易,因此可以使用SPAIK作为基本的HTML模板引擎。
(load html)
(html (p :class 'interjection "Interjection!"))
<p class="interjection">Interjection!</p>
内部架构
SPAIK代码是字节码编译,并在称为Rodent VM(R8VM)的自定义虚拟机上运行,该虚拟机使用移动追踪垃圾收集器。有关其内部结构的更多详细信息,请参阅HACKING.md。
依赖关系
~1.5–4.5MB
~94K SLoC