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

BSD-2-Clause

450KB
12K SLoC

SPAIK LISP 编程语言

SPAIK 是 Rust 的动态扩展语言。它实现了宏、垃圾回收、迭代器、延续、async/await,并将其封装在一个(希望)易于使用的高级 Rust API 中。

本 README 包含许多关于如何使用 SPAIK 的简短代码片段,而完整的示例可以在 examples 目录中找到,更详细的 API 文档可以在 docs.rs 中找到。

您还可以直接在浏览器中尝试使用 SPAIK!

基本用法

对于基本用法,您只需要 evalexec 方法(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