33个版本

0.4.10 2023年12月15日
0.4.4 2023年9月3日
0.4.2 2021年12月31日
0.4.1 2021年4月10日
0.3.1 2020年6月7日

#46编程语言

Download history 53/week @ 2024-07-01 9/week @ 2024-07-22 35/week @ 2024-07-29

97 每月下载量
用于 game_kernel

MIT/Apache

405KB
10K SLoC

C 7K SLoC // 0.2% comments Rust 2K SLoC // 0.0% comments Bitbake 615 SLoC Nushell 14 SLoC // 0.1% comments

Ruwren: Wren绑定用于Rust Crates.io docs.rs

构建状态: Travis CI

这是一个在更Rust风格的尝试,制作一些Rust Wren绑定。它在某些时候表现得相当透明(你仍然需要处理slot / foreign API),但它应该是

  • 比直接使用C API更类型安全
  • 不应该妨碍快速执行
  • 应该相对简单易上手

包括

只需将

ruwren = "0.4"

添加到你的Cargo.toml。

主要API

创建一个虚拟机

要创建一个虚拟机,使用VMConfig结构体。要创建一个将输出记录到控制台的基本虚拟机,只需调用

let vm = VMConfig::new().build();

你可以通过interpret直接运行代码

vm.interpret("main", r##"System.print("Cool beans!")"##).unwrap();

在成功执行时返回一个 Ok(()),在失败时返回 Err(e)(有关更多详细信息,请参阅VMError)。

你还可以使用FunctionHandle调用Wren中定义的代码,如下所示

vm.interpret("main", r##"
class GameEngine {
    static update(delta) {
        System.print(delta)
    }
}
"##).unwrap();
let handle = vm.make_call_handle(FunctionSignature::new_function("update", 1));
vm.execute(|vm| {
    vm.ensure_slots(2);
    vm.get_variable("main", "GameEngine", 0);
    vm.set_slot_double(1, 0.016);
});
vm.call_handle(&handle);

或更直接地

vm.interpret("main", r##"
class GameEngine {
    static update(delta) {
        System.print(delta)
    }
}
"##).unwrap();
vm.execute(|vm| {
    vm.ensure_slots(2);
    vm.get_variable("main", "GameEngine", 0);
    vm.set_slot_double(1, 0.016);
});
vm.call(FunctionSignature::new_function("update", 1));

在Wren中嵌入Rust代码

以下是一个简短的示例,说明如何将你的Rust数据嵌入到Wren中

use ruwren::{Class, VM, VMConfig, ModuleLibrary, get_slot_checked, create_module};
struct Foo {
    bar: f64,
}

impl Class for Foo {
    fn initialize(vm: &VM) -> Self {
        let bar = get_slot_checked!(vm => num 1);
        Foo { bar }
    }
}

impl Foo {
    fn instance(&self, vm: &VM) {
        vm.set_slot_double(0, self.bar);
    }

    fn static_fn(vm: &VM) {
        let num = get_slot_checked!(vm => num 1);
        vm.set_slot_double(0, num + 5.0)
    }
}

create_module! {
    class("Foo") crate::Foo => foo {
        instance(fn "instance", 0) instance,
        static(fn "static_fn", 1) static_fn
    }

    module => foobar
}

fn main() {
    let mut lib = ModuleLibrary::new();
    foobar::publish_module(&mut lib);

    let vm = VMConfig::new().library(&lib).build();
    vm.interpret("foobar", r##"
    foreign class Foo {
        construct new(bar) {}
        foreign instance()
        foreign static static_fn(num)
    }
    "##).unwrap();

    // You could now write Wren code like:

    vm.interpret("main", r##"
    import "foobar" for Foo
    var f = Foo.new(4)
    System.print(Foo.static_fn(f.instance()))
    "##).unwrap();

    // This should print "9".
}

V2 Foreign

V2 foreigns在原始foreign API之上模拟Wren的类系统,因此上述示例将是

use ruwren::{wren_impl, wren_module, ModuleLibrary, VMConfig, WrenObject};
#[derive(WrenObject, Default)]
struct Foo {
    bar: f64,
}

#[wren_impl]
impl Foo {
    /*
    you can also write out an allocator, if you
    don't want the base struct itself to implement Default

    #[wren_impl(allocator)]
    fn allocator() -> FooClass {
        FooClass {}
    }
    */

    #[wren_impl(constructor)]
    fn constructor(&self, bar: f64) -> FooInstance {
        FooInstance { bar }
    }

    #[wren_impl(instance)]
    fn instance(&self) -> f64 {
        self.bar
    }

    fn static_fn(&self, num: f64) -> f64 {
        num + 5.0
    }
}

wren_module! {
    mod foobar {
        pub crate::Foo;
    }
}

fn main() {
    let mut lib = ModuleLibrary::new();
    foobar::publish_module(&mut lib);

    let vm = VMConfig::new().library(&lib).build();
    vm.interpret("foobar", r##"
    foreign class Foo {
        construct new(bar) {}
        foreign instance()
        foreign static static_fn(num)
    }
    "##).unwrap();

    // You could now write Wren code like:

    vm.interpret("main", r##"
    import "foobar" for Foo
    var f = Foo.new(4)
    System.print(Foo.static_fn(f.instance()))
    "##).unwrap();

    // This should print "9".
}

关于WASM编译

只要你的目标WASI,并且你在某处设置了一个WASI SDK,它在技术上就可以工作。请查看justfile或example-wasi.nu,以获取运行所需设置的环
境变量。但是有一个大前提

WASM(即使是WASI)是一个panic=abort平台,因此catch_unwind不起作用,panic是无法处理的。

这意味着v1外部API(例如 get_slot_checked!)的一些惯用语在该平台上表现不佳。基本上,任何引发恐慌的操作都无法正常工作。

对v2外部API进行最小更改(即构造函数可能失败)意味着v2在Web上应该相对不变,v1是 可用的,只是不应该触发恐慌,否则wasm运行时会崩溃。

依赖项

~0.3–3MB
~63K SLoC