rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.79k stars 177 forks source link

Calling a function and passing a custom type as argument #125

Closed KaiserKarel closed 4 years ago

KaiserKarel commented 4 years ago

I'm trying to call a function which I previously registered using engine.consume; however rhai types the argument as dynamic.

let script = r#"
            fn main(arg) {
                print(type_of(arg));

            }
        "#;
engine.register_type::<Arg>();
let ast = engine.compile(&script)?;
engine.consume_ast(true, &ast)?;
engine.call_fn("main", Arg::new())?;

>>> dynamic

How do I have rhai use the registered Args type?

schungx commented 4 years ago

First of all, the API for call_fn is not ideal, especially when dealing with functions taking one argument. A new version will land in crates.io soon that has a different API, so I suggest you pull directly from this repo to get it. For functions with one argument, you call call_fn1 which takes an AST and any argument.

let ast = engine.compile(&script)?;
engine.call_fn1(Scope::new(), &ast, "main", copy)?;

The value in copy, regardless of what it is, will show up as the arg argument in main.

KaiserKarel commented 4 years ago

An thanks; so this would be the idiomatic way to call scripts which always have a main function?

On a sidenote; is there a way to store the ast; so that I can store ASTs instead of the raw scripts to save on some computational overhead?

schungx commented 4 years ago

On a sidenote; is there a way to store the ast; so that I can store ASTs instead of the raw scripts to save on some computational overhead?

This is one of the outstanding issues (see https://github.com/jonathandturner/rhai/issues/100). However, I wonder how useful it'll be, since persisting an AST is most likely less efficient than persisting the script text itself -- which can be compressed etc.

Parsing the script doesn't take much time or resources, so it is probably not worth it unless you have really large scripts.

schungx commented 4 years ago

An thanks; so this would be the idiomatic way to call scripts which always have a main function?

Yes. Compiling to AST and then repeatedly calling call_fn to access one function is the correct way of calling Rhai functions from Rust.

If you only need to call it once, you can omit the main function and just put the body of the main function as the script text itself. Then you can simply evaluate the script.

call_fn is for you to call a script multiple times.

KaiserKarel commented 4 years ago

Being able to persist an AST would allow for persisting optimization, as well as guaranteeing that the script is valid rhai.

schungx commented 4 years ago

Once you successfully parse a script, you know it is valid. When you store it and retrieve it later, it will not suddenly become invalid...

KaiserKarel commented 4 years ago

Yeah, right now it doesn't make much sense, as parsing and optimizing is cheap, however you are repeating work. There might be cases where parsing and running do not happen that consecutively.

schungx commented 4 years ago

I'm actually toying with the idea of further compiling the AST into byte-code. That would give more speed plus an easily persistable data format. However, I'm still wondering whether it'd be worth doing, as currently I don't run massively parallel scripts under heavy loading.

KaiserKarel commented 4 years ago

Reading through some of the issues; this would definitely be useful for ECS; an easy option would also be to add a serde feature flag; to compare speeds between deserializing from bincode and just reparsing for example.