Open tripokey opened 4 days ago
However it seems that script functions in a global module cannot be directly accessed from rust side preventing me from calling them using Engine::call_fn().
I'm pretty sure you can call the scripted functions in global... That's how the Rhai REPL does it...
If all your imports are static then just load them once into the engine. If they are volatile, then yes you'd want to keep the loaded modules that you imported before...
The Rhai REPL currently forces you to reimport the modules on every run.
Given the following changes to the Rhai REPL:
diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs
index 6a1215c7..3a0d161f 100644
--- a/src/bin/rhai-repl.rs
+++ b/src/bin/rhai-repl.rs
@@ -286,6 +286,10 @@ mod sample_functions {
*x += y + (z.len() as INT);
println!("{x} {y} {z}");
}
+
+ pub fn test3(x: INT, y: INT) {
+ println!("{x} {y}");
+ }
}
fn main() {
@@ -596,6 +600,15 @@ fn main() {
// Throw away all the statements, leaving only the functions
main_ast.clear_statements();
+
+ engine
+ .call_fn::<()>(
+ &mut scope,
+ &main_ast,
+ "test3",
+ (1 as rhai::INT, 2 as rhai::INT),
+ )
+ .expect("Failed...");
}
rl.save_history(HISTORY_FILE)
The functions in the global module seems to be available from the script, but they do not seem to be directly callable from Rust:
Rhai REPL tool (version 1.20.0)
===============================
help => print this help
quit, exit => quit
keys => print list of key bindings
history => print lines history
!! => repeat the last history line
!<#> => repeat a particular history line
!<text> => repeat the last history line starting with some text
!?<text> => repeat the last history line containing some text
scope => print all variables in the scope
strict => toggle on/off Strict Variables Mode
optimize => toggle on/off script optimization
ast => print the last AST (optimized)
astu => print the last raw, un-optimized AST
press Ctrl-Enter or end a line with `\`
to continue to the next line.
repl> test3(1, 3);
1 3
thread 'main' panicked at src/bin/rhai-repl.rs:611:14:
Failed...: ErrorFunctionNotFound("test3", none)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
You cannot call a native Rust function via call_fn
. The idea is that it is much easier to just call the Rust version instead of going through Rhai.
Ok, here is another example with the function defined in a Rhai script which still does not work:
Executing: cargo run --release --bin rhai-repl --features rustyline -- testing.rhai
testing.rhai:
fn testing() {
print("Works");
}
Changes:
diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs
index 6a1215c7..6959e6b7 100644
--- a/src/bin/rhai-repl.rs
+++ b/src/bin/rhai-repl.rs
@@ -596,6 +596,10 @@ fn main() {
// Throw away all the statements, leaving only the functions
main_ast.clear_statements();
+
+ engine
+ .call_fn::<()>(&mut scope, &main_ast, "testing", ())
+ .expect("Failed...");
}
rl.save_history(HISTORY_FILE)
Output:
Rhai REPL tool (version 1.20.0)
===============================
Script 'testing.rhai' loaded.
help => print this help
quit, exit => quit
keys => print list of key bindings
history => print lines history
!! => repeat the last history line
!<#> => repeat a particular history line
!<text> => repeat the last history line starting with some text
!?<text> => repeat the last history line containing some text
scope => print all variables in the scope
strict => toggle on/off Strict Variables Mode
optimize => toggle on/off script optimization
ast => print the last AST (optimized)
astu => print the last raw, un-optimized AST
press Ctrl-Enter or end a line with `\`
to continue to the next line.
repl> testing();
Works
thread 'main' panicked at src/bin/rhai-repl.rs:602:14:
Failed...: ErrorFunctionNotFound("testing", none)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
That would be interesting. Can you put a testing();
inside the script to see if it works?
Also you can dump the function signatures to see if testing
is properly seen...
Sure,
Updated script:
fn testing() {
print("Works");
}
testing();
Executing: cargo run --release --bin rhai-repl --features rustyline --features metadata -- testing.rhai
Changes:
diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs
index 6a1215c7..6959e6b7 100644
--- a/src/bin/rhai-repl.rs
+++ b/src/bin/rhai-repl.rs
@@ -596,6 +596,10 @@ fn main() {
// Throw away all the statements, leaving only the functions
main_ast.clear_statements();
+
+ engine
+ .call_fn::<()>(&mut scope, &main_ast, "testing", ())
+ .expect("Failed...");
}
rl.save_history(HISTORY_FILE)
Output:
Rhai REPL tool (version 1.20.0)
===============================
Works
Script 'testing.rhai' loaded.
help => print this help
quit, exit => quit
keys => print list of key bindings
history => print lines history
!! => repeat the last history line
!<#> => repeat a particular history line
!<text> => repeat the last history line starting with some text
!?<text> => repeat the last history line containing some text
scope => print all variables in the scope
strict => toggle on/off Strict Variables Mode
optimize => toggle on/off script optimization
functions => print all functions defined
json => output all functions to `metadata.json`
ast => print the last AST (optimized)
astu => print the last raw, un-optimized AST
press Ctrl-Enter or end a line with `\`
to continue to the next line.
repl> functions
test(x: i64, y: i64) -> MyType
test(x: &mut i64, y: i64, z: string)
testing()
repl> testing()
Works
thread 'main' panicked at src/bin/rhai-repl.rs:602:14:
Failed...: ErrorFunctionNotFound("testing", none)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Conclusions:
Works
is printed once when the module is evaluated.Works
is printed once more when testing();
is executed from the REPL.testing()
is listed when the functions is printed.testing()
is not available to Engine::call_fn
.Can you use rhai-run
instead of repl
?
Because the function is not in the AST.
You can define testing in the REPL and it'll work, I'm pretty sure...
Yes, using rhai-run
would work since it does not load the file into a global module (and the REPL will also work when the function is defined through the repl>
interface).
The problem I'm having is combining the event-handler pattern with a REPL wich requires that i use AST::clear_statements()
which in turn removes the top-level import statements.
I tried to work around this limitation by registering the source as a global module but then discovered that it was not possible to call any of the event handler functions defined in the registered global module.
So it seems call_fn
may not find functions loaded into global. I'll take a look at that.
Thank you :slightly_smiling_face:
I'm sorry it was misleading. call_fn
only calls functions defined within the current script.
/// Call a script function defined in an [`AST`] with multiple arguments.
Functions inside your scripts, however, I believe should be able to call your scripts loaded into global.
Yes, it would be great however if there was a way to call a function from rust side that first attempts to call the function in the given AST and if the function was not found then attempt to call the function in the registered global modules.
Yes, it would be great however if there was a way to call a function from rust side that first attempts to call the function in the given AST and if the function was not found then attempt to call the function in the registered global modules.
I fear that may be a breaking change...
Engine::call_fn
returns ErrorFunctionNotFound
if the AST does not contain the function, couldn't an API be exposed that calls a function defined in a global module. Then it would be up to the user to do the extra scaffolding if call_fn returns FunctionNotFound but it would at least be possible and it would be a non breaking change?
I have an issue with an application that implements the scriptable event handler use case and also provides a REPL interface:
The book recommends that imports be placed at the top of the script: https://rhai.rs/book/language/modules/import.html#admonition-place-import-statements-at-the-top
REPL implementations typically use AST::clear_statements() to prevent statements from being executed twice.
After
clear_statements
has been called any imports at top level will be gone from the AST causing event handler functions that depends on the global imports to fail.One approach that I tried to circumvent this is to add the loaded script into a global module similar to how the Rhai REPL does things. However it seems that script functions in a global module cannot be directly accessed from rust side preventing me from calling them using Engine::call_fn().
Am I missing something or is this use-case simply not supported unless the imports are placed inside the event handler functions?