rune-rs / rune

An embeddable dynamic programming language for Rust.
https://rune-rs.github.io
Apache License 2.0
1.7k stars 86 forks source link

Loading modules #452

Open jasal82 opened 1 year ago

jasal82 commented 1 year ago

I have a main script file which references different modules. These should be loaded from a global modules directory. I got this working by using a custom SourceLoader. The individual modules are located in different subdirectories of the global modules directory, but they should be able to share functionality between each other. For example, let's suppose I have the following two modules:

core/mod.rn

pub fn function1() {}

regex/mod.rn

pub fn function2() {}

I can call these from the main script file by declaring the modules first:

mod core;
mod regex;

fn main() {
  core::function1();
  regex::function2();
}

However, I cannot call functions in core from regex. I tried something like this:

regex/mod.rn

pub fn function2() {
  core::function1();
}

But the SourceLoader seems to resolve the item to regex::core::function1 and there is no syntax like ::core to request an absolute namespace resolution.

Is there a way to achieve the desired functionality?

udoprog commented 1 year ago

Have you tried this?

crate::core::function1()

Importing it should also work, all though this is not an area of the codebase I've touched a lot since it was initially implemented :sweat_smile:

The things that should work are:

jasal82 commented 1 year ago

That worked, thanks! Is there maybe a way to get rid of the two lines

mod core;
mod regex;

in the main script? The modules are pulled in via my own dependency management mechanism, so having to declare them as submodules seems a bit awkward. I'd prefer if they were importable like the native Rust modules.

Unfortunately many great features of Rune are entirely undocumented. I'm getting better at understanding the internals from the code, but it always takes a lot of extra time. I still want to use Rune, though, because the Rust integration is much better done than in Rhai. Kudos for that!

jasal82 commented 1 year ago

Thought about it some more. Having the modules importable without the mod would probably require handling them as separate crates, which I believe is not supported right now?

udoprog commented 1 year ago

Separate "crates" are only supported for native modules right now. I believe Module has a constructor that defines them in a different crate. The way you'd refer to them should be the same as in Rust: cratename or ::cratename if you need to disambiguate.

Dynamic crates are not supported, that is, other crates defined in Rune yet.

What I'm really lacking in terms of documentation is a rune doc command and API. Some parts of that have been laid out, but we really need the ability to document native fns.

I would also try and make sure you check out the book. Most major language features should be documented there already, all though module resolution seems to be missing!

EDIT: here's the constructor: https://docs.rs/rune/latest/rune/struct.Module.html#method.with_crate

jasal82 commented 1 year ago

I know the book, but I didn't find it very helpful. Don't get me wrong, take this as constructive criticism. The book is missing more than just the modules part. Interaction with basic types is something you have to figure out by trial and error. For example, I needed to obtain a slice of a vector. This is a very common operation in script languages, yet there is no documentation about it. I found out that you can't use ranges in the index (let slice = v[1..]) because it is not implemented in the Rune type system. What I used in the end was v.iter().skip(1).collect::<Vec>(). Unfortunately all of the Rune examples in the book are absolutely minimal. If you'd like, I can provide a few of my code snippets with comments where I see lack of documentation.

udoprog commented 1 year ago

None taken, and please do! I still think the situation would be improved with generated documentation since it would cover all native functions as well. Like currently there is no way to find out that Vec::iter even exists without looking at the native module declaration.

This is a very common operation in script languages, yet there is no documentation about it. I found out that you can't use ranges in the index (let slice = v[1..]) because it is not implemented in the Rune type system.

This operation is called Protocol::INDEX_GET, what's missing to support your use case is either registering an instance function or adding an internal vm implementation for it. It probably should be implemented in the Vm since it only involves internal types.

Please open up an issue for it! And if you decide to take a stab at it I'd be happy to give you a hand. Just make sure to say that you're interested in the issue.

jasal82 commented 1 year ago

I just tried to implement the INDEX_GET protocol, and have a working solution ready. I'll open an issue and then we can look at my solution together. Probably a few things can be improved, especially I don't know your architecture constraints so maybe we have to move a few things around.