Merge pull request #507 from schungx/master
Revise library and fix bugs.
This commit is contained in:
commit
2805690b11
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,6 +1,29 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 1.5.0
|
||||||
|
=============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Padding arrays with another array via `pad` no longer loops indefinitely.
|
||||||
|
* `chop` for arrays and BLOB's now works properly.
|
||||||
|
* `set_bit` for bit-flags with negative index now works correctly.
|
||||||
|
* Misnamed `params` field `name` in the JSON output of `Engine::gen_fn_metadata_to_json` is fixed (was incorrectly named `type`).
|
||||||
|
* Fixes a potential `unsafe` violation in `for` loop.
|
||||||
|
* Missing `to_hex`, `to_octal` and `to_binary` for `i128` and `u128` are added.
|
||||||
|
* `remove` for arrays and BLOB's now treat negative index correctly.
|
||||||
|
* `parse_int` now works properly for negative numbers.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Formatting of return types in functions metadata info is improved.
|
||||||
|
* Use `SmartString` for `Scope` variable names and remove `unsafe` lifetime casting.
|
||||||
|
* Functions in the standard library now have doc-comments (which can be obtained via `Engine::gen_fn_metadata_to_json`).
|
||||||
|
|
||||||
|
|
||||||
Version 1.4.0
|
Version 1.4.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
@ -97,10 +97,7 @@ default-features = false
|
|||||||
features = ["maths"]
|
features = ["maths"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||||
instant = { version = "0.1.10" } # WASM implementation of std::time::Instant
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm64")'.dependencies]
|
|
||||||
instant = { version = "0.1.10" } # WASM implementation of std::time::Instant
|
instant = { version = "0.1.10" } # WASM implementation of std::time::Instant
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
@ -16,7 +16,7 @@ default = []
|
|||||||
metadata = []
|
metadata = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rhai = { path = "..", version = "1.4" }
|
rhai = { path = "..", version = "1.4", features = ["metadata"] }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: INT,
|
x: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: INT,
|
x: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
|
168
examples/event_handler_js/main.rs
Normal file
168
examples/event_handler_js/main.rs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
//! Implementation of the Event Handler With State Pattern - JS Style
|
||||||
|
use rhai::{Dynamic, Engine, Scope, AST};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use rhai::Map;
|
||||||
|
|
||||||
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
|
const SCRIPT_FILE: &str = "event_handler_js/script.rhai";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Handler {
|
||||||
|
pub engine: Engine,
|
||||||
|
pub scope: Scope<'static>,
|
||||||
|
pub states: Dynamic,
|
||||||
|
pub ast: AST,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_scope(scope: &Scope) {
|
||||||
|
scope
|
||||||
|
.iter_raw()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, (name, constant, value))| {
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
let value_is_shared = "";
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[{}] {}{}{} = {:?}",
|
||||||
|
i + 1,
|
||||||
|
if constant { "const " } else { "" },
|
||||||
|
name,
|
||||||
|
value_is_shared,
|
||||||
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
pub fn main() {
|
||||||
|
println!("Events Handler Example - JS Style");
|
||||||
|
println!("==================================");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
|
||||||
|
// Read script file
|
||||||
|
print!("Script file [{}]: ", SCRIPT_FILE);
|
||||||
|
stdout().flush().expect("flush stdout");
|
||||||
|
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
stdin().read_line(&mut input).expect("read input");
|
||||||
|
|
||||||
|
let path = match input.trim() {
|
||||||
|
"" => SCRIPT_FILE,
|
||||||
|
path => path,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create Engine
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Use an object map to hold state
|
||||||
|
let mut states = Map::new();
|
||||||
|
|
||||||
|
// Default states can be added
|
||||||
|
states.insert("bool_state".into(), Dynamic::FALSE);
|
||||||
|
|
||||||
|
// Convert the object map into 'Dynamic'
|
||||||
|
let mut states: Dynamic = states.into();
|
||||||
|
|
||||||
|
// Create a custom 'Scope' to hold state
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
// Add any system-provided state into the custom 'Scope'.
|
||||||
|
// Constants can be used to optimize the script.
|
||||||
|
scope.push_constant("MY_CONSTANT", 42_i64);
|
||||||
|
|
||||||
|
// Compile the handler script.
|
||||||
|
println!("> Loading script file: {}", path);
|
||||||
|
|
||||||
|
let ast = match engine.compile_file_with_scope(&mut scope, path.into()) {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("! Error: {}", err);
|
||||||
|
println!("Cannot continue. Bye!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("> Script file loaded.");
|
||||||
|
println!();
|
||||||
|
println!("quit = exit program");
|
||||||
|
println!("scope = print scope");
|
||||||
|
println!("states = print states");
|
||||||
|
println!("event arg = run function with argument");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// Run the 'init' function to initialize the state, retaining variables.
|
||||||
|
let result = engine.call_fn_raw(&mut scope, &ast, false, true, "init", Some(&mut states), []);
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("! {}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create handler instance
|
||||||
|
let mut handler = Handler {
|
||||||
|
engine,
|
||||||
|
scope,
|
||||||
|
states,
|
||||||
|
ast,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Events loop
|
||||||
|
loop {
|
||||||
|
print!("event> ");
|
||||||
|
stdout().flush().expect("flush stdout");
|
||||||
|
|
||||||
|
// Read event
|
||||||
|
input.clear();
|
||||||
|
stdin().read_line(&mut input).expect("read input");
|
||||||
|
|
||||||
|
let mut fields = input.trim().splitn(2, ' ');
|
||||||
|
|
||||||
|
let event = fields.next().expect("event").trim();
|
||||||
|
let arg = fields.next().unwrap_or("");
|
||||||
|
|
||||||
|
// Process event
|
||||||
|
match event {
|
||||||
|
"quit" => break,
|
||||||
|
|
||||||
|
"scope" => {
|
||||||
|
print_scope(&handler.scope);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
"states" => {
|
||||||
|
println!("{:?}", handler.states);
|
||||||
|
println!();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map all other events to function calls
|
||||||
|
_ => {
|
||||||
|
let engine = &handler.engine;
|
||||||
|
let scope = &mut handler.scope;
|
||||||
|
let ast = &handler.ast;
|
||||||
|
let this_ptr = Some(&mut handler.states);
|
||||||
|
|
||||||
|
let result =
|
||||||
|
engine.call_fn_raw(scope, ast, false, true, event, this_ptr, [arg.into()]);
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("! {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Bye!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "no_function", feature = "no_object"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("This example does not run under 'no_function' or 'no_object'.")
|
||||||
|
}
|
49
examples/event_handler_js/script.rhai
Normal file
49
examples/event_handler_js/script.rhai
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Implementation of the Event Handler With State Pattern - JS Style
|
||||||
|
|
||||||
|
/// Initialize user-provided state.
|
||||||
|
fn init() {
|
||||||
|
// Can detect system-provided default states!
|
||||||
|
// Add 'bool_state' as new state variable if one does not exist
|
||||||
|
if !("bool_state" in this) {
|
||||||
|
this.bool_state = false;
|
||||||
|
}
|
||||||
|
// Add 'value' as new state variable (overwrites any existing)
|
||||||
|
this.value = 0;
|
||||||
|
|
||||||
|
// Can also add OOP-style functions!
|
||||||
|
this.log = |x| print(`State = ${this.value}, data = ${x}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'start' event handler
|
||||||
|
fn start(data) {
|
||||||
|
if this.bool_state {
|
||||||
|
throw "Already started!";
|
||||||
|
}
|
||||||
|
if this.value <= 0 {
|
||||||
|
throw "Conditions not yet ready to start!";
|
||||||
|
}
|
||||||
|
this.bool_state = true;
|
||||||
|
this.value += parse_int(data);
|
||||||
|
|
||||||
|
// Constant 'MY_CONSTANT' in custom scope is also visible!
|
||||||
|
print(`MY_CONSTANT = ${MY_CONSTANT}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'end' event handler
|
||||||
|
fn end(data) {
|
||||||
|
if !this.bool_state {
|
||||||
|
throw "Not yet started!";
|
||||||
|
}
|
||||||
|
if this.value > 0 {
|
||||||
|
throw "Conditions not yet ready to end!";
|
||||||
|
}
|
||||||
|
this.bool_state = false;
|
||||||
|
this.value = parse_int(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'update' event handler
|
||||||
|
fn update(data) {
|
||||||
|
let data = parse_int(data);
|
||||||
|
this.value += data;
|
||||||
|
this.log(data);
|
||||||
|
}
|
140
examples/event_handler_main/main.rs
Normal file
140
examples/event_handler_main/main.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
//! Implementation of the Event Handler With State Pattern - Main Style
|
||||||
|
use rhai::{Dynamic, Engine, Scope, AST};
|
||||||
|
|
||||||
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
|
const SCRIPT_FILE: &str = "event_handler_main/script.rhai";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Handler {
|
||||||
|
pub engine: Engine,
|
||||||
|
pub scope: Scope<'static>,
|
||||||
|
pub ast: AST,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_scope(scope: &Scope) {
|
||||||
|
scope
|
||||||
|
.iter_raw()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, (name, constant, value))| {
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
let value_is_shared = "";
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[{}] {}{}{} = {:?}",
|
||||||
|
i + 1,
|
||||||
|
if constant { "const " } else { "" },
|
||||||
|
name,
|
||||||
|
value_is_shared,
|
||||||
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub fn main() {
|
||||||
|
println!("Events Handler Example - Main Style");
|
||||||
|
println!("===================================");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
|
||||||
|
// Read script file
|
||||||
|
print!("Script file [{}]: ", SCRIPT_FILE);
|
||||||
|
stdout().flush().expect("flush stdout");
|
||||||
|
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
stdin().read_line(&mut input).expect("read input");
|
||||||
|
|
||||||
|
let path = match input.trim() {
|
||||||
|
"" => SCRIPT_FILE,
|
||||||
|
path => path,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create Engine
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Create a custom 'Scope' to hold state
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
// Add any system-provided state into the custom 'Scope'.
|
||||||
|
// Constants can be used to optimize the script.
|
||||||
|
scope.push_constant("MY_CONSTANT", 42_i64);
|
||||||
|
|
||||||
|
// Compile the handler script.
|
||||||
|
println!("> Loading script file: {}", path);
|
||||||
|
|
||||||
|
let ast = match engine.compile_file_with_scope(&mut scope, path.into()) {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("! Error: {}", err);
|
||||||
|
println!("Cannot continue. Bye!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("> Script file loaded.");
|
||||||
|
println!();
|
||||||
|
println!("quit = exit program");
|
||||||
|
println!("scope = print scope");
|
||||||
|
println!("event arg = run function with argument");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// Run the 'init' function to initialize the state, retaining variables.
|
||||||
|
let result = engine.call_fn_raw(&mut scope, &ast, false, false, "init", None, []);
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("! {}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create handler instance
|
||||||
|
let mut handler = Handler { engine, scope, ast };
|
||||||
|
|
||||||
|
// Events loop
|
||||||
|
loop {
|
||||||
|
print!("event> ");
|
||||||
|
stdout().flush().expect("flush stdout");
|
||||||
|
|
||||||
|
// Read event
|
||||||
|
input.clear();
|
||||||
|
stdin().read_line(&mut input).expect("read input");
|
||||||
|
|
||||||
|
let mut fields = input.trim().splitn(2, ' ');
|
||||||
|
|
||||||
|
let event = fields.next().expect("event").trim();
|
||||||
|
let arg = fields.next().unwrap_or("");
|
||||||
|
|
||||||
|
// Process event
|
||||||
|
match event {
|
||||||
|
"quit" => break,
|
||||||
|
|
||||||
|
"scope" => {
|
||||||
|
print_scope(&handler.scope);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map all other events to function calls
|
||||||
|
_ => {
|
||||||
|
let engine = &handler.engine;
|
||||||
|
let scope = &mut handler.scope;
|
||||||
|
let ast = &handler.ast;
|
||||||
|
|
||||||
|
let result: Result<(), _> = engine.call_fn(scope, ast, event, (arg.to_string(),));
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("! {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Bye!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("This example does not run under 'no_function'.")
|
||||||
|
}
|
56
examples/event_handler_main/script.rhai
Normal file
56
examples/event_handler_main/script.rhai
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Implementation of the Event Handler With State Pattern - Main Style
|
||||||
|
|
||||||
|
/// Initialize user-provided state (shadows system-provided state, if any).
|
||||||
|
fn init() {
|
||||||
|
// Add 'bool_state' and 'obj_state' as new state variables
|
||||||
|
let bool_state = false;
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
// Constants can also be added!
|
||||||
|
const EXTRA_CONSTANT = "hello, world!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Without 'OOP' support, the can only be a function.
|
||||||
|
fn log(value, data) {
|
||||||
|
print(`State = ${value}, data = ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'start' event handler
|
||||||
|
fn start(data) {
|
||||||
|
if bool_state {
|
||||||
|
throw "Already started!";
|
||||||
|
}
|
||||||
|
if value <= 0 {
|
||||||
|
throw "Conditions not yet ready to start!";
|
||||||
|
}
|
||||||
|
bool_state = true;
|
||||||
|
|
||||||
|
// Constants 'MY_CONSTANT' and 'EXTRA_CONSTANT'
|
||||||
|
// in custom scope are also visible!
|
||||||
|
print(`MY_CONSTANT = ${MY_CONSTANT}`);
|
||||||
|
print(`EXTRA_CONSTANT = ${EXTRA_CONSTANT}`);
|
||||||
|
|
||||||
|
value += parse_int(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'end' event handler
|
||||||
|
fn end(data) {
|
||||||
|
if !bool_state {
|
||||||
|
throw "Not yet started!";
|
||||||
|
}
|
||||||
|
if value > 0 {
|
||||||
|
throw "Conditions not yet ready to end!";
|
||||||
|
}
|
||||||
|
bool_state = false;
|
||||||
|
value = parse_int(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'update' event handler
|
||||||
|
fn update(data) {
|
||||||
|
let data = parse_int(data);
|
||||||
|
|
||||||
|
value += data;
|
||||||
|
|
||||||
|
// Without OOP support, can only call function
|
||||||
|
log(value, data);
|
||||||
|
}
|
153
examples/event_handler_map/main.rs
Normal file
153
examples/event_handler_map/main.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
//! Implementation of the Event Handler With State Pattern - Map Style
|
||||||
|
use rhai::{Dynamic, Engine, Scope, AST};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use rhai::Map;
|
||||||
|
|
||||||
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
|
const SCRIPT_FILE: &str = "event_handler_map/script.rhai";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Handler {
|
||||||
|
pub engine: Engine,
|
||||||
|
pub scope: Scope<'static>,
|
||||||
|
pub ast: AST,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_scope(scope: &Scope) {
|
||||||
|
scope
|
||||||
|
.iter_raw()
|
||||||
|
.enumerate()
|
||||||
|
.for_each(|(i, (name, constant, value))| {
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
let value_is_shared = "";
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[{}] {}{}{} = {:?}",
|
||||||
|
i + 1,
|
||||||
|
if constant { "const " } else { "" },
|
||||||
|
name,
|
||||||
|
value_is_shared,
|
||||||
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
pub fn main() {
|
||||||
|
println!("Events Handler Example - Map Style");
|
||||||
|
println!("==================================");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
|
||||||
|
// Read script file
|
||||||
|
print!("Script file [{}]: ", SCRIPT_FILE);
|
||||||
|
stdout().flush().expect("flush stdout");
|
||||||
|
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
stdin().read_line(&mut input).expect("read input");
|
||||||
|
|
||||||
|
let path = match input.trim() {
|
||||||
|
"" => SCRIPT_FILE,
|
||||||
|
path => path,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create Engine
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Create a custom 'Scope' to hold state
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
// Add any system-provided state into the custom 'Scope'.
|
||||||
|
// Constants can be used to optimize the script.
|
||||||
|
scope.push_constant("MY_CONSTANT", 42_i64);
|
||||||
|
|
||||||
|
// Use an object map to hold state
|
||||||
|
let mut states = Map::new();
|
||||||
|
|
||||||
|
// Default states can be added
|
||||||
|
states.insert("bool_state".into(), Dynamic::FALSE);
|
||||||
|
|
||||||
|
// Add the main states-holding object map and call it 'state'
|
||||||
|
scope.push("state", Map::new());
|
||||||
|
|
||||||
|
// Compile the handler script.
|
||||||
|
println!("> Loading script file: {}", path);
|
||||||
|
|
||||||
|
let ast = match engine.compile_file_with_scope(&mut scope, path.into()) {
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("! Error: {}", err);
|
||||||
|
println!("Cannot continue. Bye!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("> Script file loaded.");
|
||||||
|
println!();
|
||||||
|
println!("quit = exit program");
|
||||||
|
println!("scope = print scope");
|
||||||
|
println!("event arg = run function with argument");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// Run the 'init' function to initialize the state, retaining variables.
|
||||||
|
let result: Result<(), _> = engine.call_fn(&mut scope, &ast, "init", ());
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("! {}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create handler instance
|
||||||
|
let mut handler = Handler { engine, scope, ast };
|
||||||
|
|
||||||
|
// Events loop
|
||||||
|
loop {
|
||||||
|
print!("event> ");
|
||||||
|
stdout().flush().expect("flush stdout");
|
||||||
|
|
||||||
|
// Read event
|
||||||
|
input.clear();
|
||||||
|
stdin().read_line(&mut input).expect("read input");
|
||||||
|
|
||||||
|
let mut fields = input.trim().splitn(2, ' ');
|
||||||
|
|
||||||
|
let event = fields.next().expect("event").trim();
|
||||||
|
let arg = fields.next().unwrap_or("");
|
||||||
|
|
||||||
|
// Process event
|
||||||
|
match event {
|
||||||
|
"quit" => break,
|
||||||
|
|
||||||
|
"scope" => {
|
||||||
|
print_scope(&handler.scope);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map all other events to function calls
|
||||||
|
_ => {
|
||||||
|
let engine = &handler.engine;
|
||||||
|
let scope = &mut handler.scope;
|
||||||
|
let ast = &handler.ast;
|
||||||
|
|
||||||
|
let result: Result<(), _> = engine.call_fn(scope, ast, event, (arg.to_string(),));
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("! {}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Bye!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "no_function", feature = "no_object"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("This example does not run under 'no_function' or 'no_object'.")
|
||||||
|
}
|
57
examples/event_handler_map/script.rhai
Normal file
57
examples/event_handler_map/script.rhai
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Implementation of the Event Handler With State Pattern - Map Style
|
||||||
|
|
||||||
|
/// Initialize user-provided state.
|
||||||
|
/// State is stored inside an object map bound to 'state'.
|
||||||
|
fn init() {
|
||||||
|
// Add 'bool_state' as new state variable if one does not exist
|
||||||
|
if !("bool_state" in state) {
|
||||||
|
state.bool_state = false;
|
||||||
|
}
|
||||||
|
// Add 'obj_state' as new state variable (overwrites any existing)
|
||||||
|
state.value = 0;
|
||||||
|
|
||||||
|
// Can also add OOP-style functions!
|
||||||
|
state.log = |x| print(`State = ${this.value}, data = ${x}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'start' event handler
|
||||||
|
fn start(data) {
|
||||||
|
// Can detect system-provided default states!
|
||||||
|
// Access state variables in 'state'
|
||||||
|
if state.bool_state {
|
||||||
|
throw "Already started!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// New values can be added to the state
|
||||||
|
state.start_mode = data;
|
||||||
|
|
||||||
|
if state.value <= 0 {
|
||||||
|
throw "Conditions not yet ready to start!";
|
||||||
|
}
|
||||||
|
state.bool_state = true;
|
||||||
|
state.value = parse_int(data);
|
||||||
|
|
||||||
|
// Constant 'MY_CONSTANT' in custom scope is also visible!
|
||||||
|
print(`MY_CONSTANT = ${MY_CONSTANT}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'end' event handler
|
||||||
|
fn end(data) {
|
||||||
|
if !state.bool_state || !("start_mode" in state) {
|
||||||
|
throw "Not yet started!";
|
||||||
|
}
|
||||||
|
if state.value > 0 {
|
||||||
|
throw "Conditions not yet ready to end!";
|
||||||
|
}
|
||||||
|
state.bool_state = false;
|
||||||
|
state.value = parse_int(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 'update' event handler
|
||||||
|
fn update(data) {
|
||||||
|
let data = parse_int(data);
|
||||||
|
state.value += data;
|
||||||
|
|
||||||
|
// Call user-defined function OOP-style!
|
||||||
|
state.log(data);
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
engine.run(r#"print("hello, world!")"#)?;
|
engine.run(r#"print("hello, world!")"#)?;
|
||||||
|
|
||||||
let result = engine.eval::<INT>("40 + 2")?;
|
let result = engine.eval::<i64>("40 + 2")?;
|
||||||
|
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
|
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
use rhai::{Engine, EvalAltResult, Scope};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?;
|
engine.run_with_scope(&mut scope, "let x = 4 + 5")?;
|
||||||
|
|
||||||
println!("x = {}", scope.get_value::<INT>("x").unwrap());
|
println!("x = {}", scope.get_value::<i64>("x").unwrap());
|
||||||
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let result = engine.eval_with_scope::<INT>(&mut scope, "x += 1; x")?;
|
let result = engine.eval_with_scope::<i64>(&mut scope, "x += 1; x")?;
|
||||||
|
|
||||||
println!("result: {}", result);
|
println!("result: {}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("x = {}", scope.get_value::<INT>("x").unwrap());
|
println!("x = {}", scope.get_value::<i64>("x").unwrap());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
fn add(x: INT, y: INT) -> INT {
|
fn add(x: i64, y: i64) -> i64 {
|
||||||
x + y
|
x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.register_fn("add", add);
|
engine.register_fn("add", add);
|
||||||
|
|
||||||
let result = engine.eval::<INT>("add(40, 2)")?;
|
let result = engine.eval::<i64>("add(40, 2)")?;
|
||||||
|
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
///! This example registers a variety of functions that operate on strings.
|
///! This example registers a variety of functions that operate on strings.
|
||||||
///! Remember to use `ImmutableString` or `&str` instead of `String` as parameters.
|
///! Remember to use `ImmutableString` or `&str` instead of `String` as parameters.
|
||||||
use rhai::{Engine, EvalAltResult, ImmutableString, Scope, INT};
|
use rhai::{Engine, EvalAltResult, ImmutableString, Scope};
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
/// Trim whitespace from a string. The original string argument is changed.
|
/// Trim whitespace from a string. The original string argument is changed.
|
||||||
@ -15,26 +15,26 @@ fn trim_string(s: &mut ImmutableString) {
|
|||||||
/// This version simply counts the number of _bytes_ in the UTF-8 representation.
|
/// This version simply counts the number of _bytes_ in the UTF-8 representation.
|
||||||
///
|
///
|
||||||
/// This version uses `&str`.
|
/// This version uses `&str`.
|
||||||
fn count_string_bytes(s: &str) -> INT {
|
fn count_string_bytes(s: &str) -> i64 {
|
||||||
s.len() as INT
|
s.len() as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This version uses `ImmutableString` and `&str`.
|
/// This version uses `ImmutableString` and `&str`.
|
||||||
fn find_substring(s: ImmutableString, sub: &str) -> INT {
|
fn find_substring(s: ImmutableString, sub: &str) -> i64 {
|
||||||
s.find(sub).map(|x| x as INT).unwrap_or(-1)
|
s.find(sub).map(|x| x as i64).unwrap_or(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
// Create a `raw` Engine with no built-in string functions.
|
// Create a `raw` Engine with no built-in string functions.
|
||||||
let mut engine = Engine::new_raw();
|
let mut engine = Engine::new_raw();
|
||||||
|
|
||||||
// Register string functions
|
|
||||||
engine
|
engine
|
||||||
|
// Register string functions
|
||||||
.register_fn("trim", trim_string)
|
.register_fn("trim", trim_string)
|
||||||
.register_fn("len", count_string_bytes)
|
.register_fn("len", count_string_bytes)
|
||||||
.register_fn("index_of", find_substring)
|
.register_fn("index_of", find_substring)
|
||||||
// Register string functions using closures
|
// Register string functions using closures
|
||||||
.register_fn("display", |label: &str, value: INT| {
|
.register_fn("display", |label: &str, value: i64| {
|
||||||
println!("{}: {}", label, value)
|
println!("{}: {}", label, value)
|
||||||
})
|
})
|
||||||
.register_fn("display", |label: ImmutableString, value: &str| {
|
.register_fn("display", |label: ImmutableString, value: &str| {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, INT};
|
use rhai::Engine;
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
@ -23,12 +23,12 @@ fn main() {
|
|||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
engine
|
engine
|
||||||
.register_fn("get", move || rx_script.recv().unwrap_or_default())
|
.register_fn("get", move || rx_script.recv().unwrap_or_default())
|
||||||
.register_fn("put", move |v: INT| tx_script.send(v).unwrap());
|
.register_fn("put", move |v: i64| tx_script.send(v).unwrap());
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
engine
|
engine
|
||||||
.register_fn("get", move || rx_script.lock().unwrap().recv().unwrap())
|
.register_fn("get", move || rx_script.lock().unwrap().recv().unwrap())
|
||||||
.register_fn("put", move |v: INT| {
|
.register_fn("put", move |v: i64| {
|
||||||
tx_script.lock().unwrap().send(v).unwrap()
|
tx_script.lock().unwrap().send(v).unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ fn main() {
|
|||||||
|
|
||||||
println!("Starting main loop...");
|
println!("Starting main loop...");
|
||||||
|
|
||||||
let mut value: INT = 0;
|
let mut value: i64 = 0;
|
||||||
|
|
||||||
while value < 10 {
|
while value < 10 {
|
||||||
println!("Value: {}", value);
|
println!("Value: {}", value);
|
||||||
|
@ -20,8 +20,7 @@ impl Engine {
|
|||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn consume_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> {
|
pub fn consume_file(&self, path: std::path::PathBuf) -> RhaiResultOf<()> {
|
||||||
self.run_file(path)
|
self.run_file(path)
|
||||||
@ -39,8 +38,7 @@ impl Engine {
|
|||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
#[deprecated(since = "1.1.0", note = "use `run_file_with_scope` instead")]
|
#[deprecated(since = "1.1.0", note = "use `run_file_with_scope` instead")]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn consume_file_with_scope(
|
pub fn consume_file_with_scope(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Module that defines the public file-based API of [`Engine`].
|
//! Module that defines the public file-based API of [`Engine`].
|
||||||
#![cfg(not(feature = "no_std"))]
|
#![cfg(not(feature = "no_std"))]
|
||||||
#![cfg(not(target_arch = "wasm32"))]
|
#![cfg(not(target_family = "wasm"))]
|
||||||
#![cfg(not(target_arch = "wasm64"))]
|
|
||||||
|
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{Engine, RhaiResultOf, Scope, AST, ERR};
|
use crate::{Engine, RhaiResultOf, Scope, AST, ERR};
|
||||||
|
@ -203,40 +203,6 @@ pub fn make_setter(id: &str) -> Identifier {
|
|||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this function an anonymous function?
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_anonymous_fn(fn_name: &str) -> bool {
|
|
||||||
fn_name.starts_with(FN_ANONYMOUS)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print to `stdout`
|
|
||||||
#[inline]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn print_to_stdout(s: &str) {
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
println!("{}", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Debug to `stdout`
|
|
||||||
#[inline]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) {
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
if let Some(source) = source {
|
|
||||||
println!("{}{:?} | {}", source, pos, s);
|
|
||||||
} else if pos.is_none() {
|
|
||||||
println!("{}", s);
|
|
||||||
} else {
|
|
||||||
println!("{:?} | {}", pos, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Create a new [`Engine`].
|
/// Create a new [`Engine`].
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -247,16 +213,32 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
{
|
{
|
||||||
engine.module_resolver =
|
engine.module_resolver =
|
||||||
Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
|
Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// default print/debug implementations
|
// default print/debug implementations
|
||||||
engine.print = Some(Box::new(print_to_stdout));
|
#[cfg(not(feature = "no_std"))]
|
||||||
engine.debug = Some(Box::new(debug_to_stdout));
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
{
|
||||||
|
engine.print = Some(Box::new(|s| println!("{}", s)));
|
||||||
|
engine.debug = Some(Box::new(|s, source, pos| {
|
||||||
|
if let Some(source) = source {
|
||||||
|
println!("{}{:?} | {}", source, pos, s);
|
||||||
|
} else if pos.is_none() {
|
||||||
|
println!("{}", s);
|
||||||
|
} else {
|
||||||
|
println!("{:?} | {}", pos, s);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
#[cfg(any(feature = "no_std", target_family = "wasm"))]
|
||||||
|
{
|
||||||
|
engine.print = None;
|
||||||
|
engine.debug = None;
|
||||||
|
}
|
||||||
|
|
||||||
engine.register_global_module(StandardPackage::new().as_shared_module());
|
engine.register_global_module(StandardPackage::new().as_shared_module());
|
||||||
|
|
||||||
|
@ -771,7 +771,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
target: &'t mut Dynamic,
|
target: &'t mut Dynamic,
|
||||||
idx: Dynamic,
|
idx: Dynamic,
|
||||||
idx_pos: Position,
|
pos: Position,
|
||||||
add_if_not_found: bool,
|
add_if_not_found: bool,
|
||||||
use_indexers: bool,
|
use_indexers: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -788,38 +788,13 @@ impl Engine {
|
|||||||
// val_array[idx]
|
// val_array[idx]
|
||||||
let index = idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
|
||||||
|
let len = arr.len();
|
||||||
|
let arr_idx = super::calc_index(len, index, true, || {
|
||||||
|
ERR::ErrorArrayBounds(len, index, pos).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
let arr_len = arr.len();
|
Ok(arr.get_mut(arr_idx).map(Target::from).unwrap())
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
let arr_idx = if index < 0 {
|
|
||||||
// Count from end if negative
|
|
||||||
arr_len
|
|
||||||
- index
|
|
||||||
.checked_abs()
|
|
||||||
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos))
|
|
||||||
.and_then(|n| {
|
|
||||||
if n as usize > arr_len {
|
|
||||||
Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
|
|
||||||
} else {
|
|
||||||
Ok(n as usize)
|
|
||||||
}
|
|
||||||
})?
|
|
||||||
} else {
|
|
||||||
index as usize
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
let arr_idx = if index < 0 {
|
|
||||||
// Count from end if negative
|
|
||||||
arr_len - index.abs() as usize
|
|
||||||
} else {
|
|
||||||
index as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
arr.get_mut(arr_idx)
|
|
||||||
.map(Target::from)
|
|
||||||
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -827,39 +802,14 @@ impl Engine {
|
|||||||
// val_blob[idx]
|
// val_blob[idx]
|
||||||
let index = idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
|
||||||
|
let len = arr.len();
|
||||||
|
let arr_idx = super::calc_index(len, index, true, || {
|
||||||
|
ERR::ErrorArrayBounds(len, index, pos).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
let arr_len = arr.len();
|
let value = arr.get(arr_idx).map(|&v| (v as crate::INT).into()).unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
let arr_idx = if index < 0 {
|
|
||||||
// Count from end if negative
|
|
||||||
arr_len
|
|
||||||
- index
|
|
||||||
.checked_abs()
|
|
||||||
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos))
|
|
||||||
.and_then(|n| {
|
|
||||||
if n as usize > arr_len {
|
|
||||||
Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
|
|
||||||
} else {
|
|
||||||
Ok(n as usize)
|
|
||||||
}
|
|
||||||
})?
|
|
||||||
} else {
|
|
||||||
index as usize
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
let arr_idx = if index < 0 {
|
|
||||||
// Count from end if negative
|
|
||||||
arr_len - index.abs() as usize
|
|
||||||
} else {
|
|
||||||
index as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = arr
|
|
||||||
.get(arr_idx)
|
|
||||||
.map(|&v| (v as crate::INT).into())
|
|
||||||
.ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?;
|
|
||||||
Ok(Target::BlobByte {
|
Ok(Target::BlobByte {
|
||||||
source: target,
|
source: target,
|
||||||
value,
|
value,
|
||||||
@ -871,7 +821,7 @@ impl Engine {
|
|||||||
Dynamic(Union::Map(map, _, _)) => {
|
Dynamic(Union::Map(map, _, _)) => {
|
||||||
// val_map[idx]
|
// val_map[idx]
|
||||||
let index = idx.read_lock::<crate::ImmutableString>().ok_or_else(|| {
|
let index = idx.read_lock::<crate::ImmutableString>().ok_or_else(|| {
|
||||||
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), idx_pos)
|
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), pos)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if _add_if_not_found && !map.contains_key(index.as_str()) {
|
if _add_if_not_found && !map.contains_key(index.as_str()) {
|
||||||
@ -888,11 +838,6 @@ impl Engine {
|
|||||||
Dynamic(Union::Int(value, _, _))
|
Dynamic(Union::Int(value, _, _))
|
||||||
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
|
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
|
||||||
{
|
{
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
type BASE = u64;
|
|
||||||
#[cfg(feature = "only_i32")]
|
|
||||||
type BASE = u32;
|
|
||||||
|
|
||||||
// val_int[range]
|
// val_int[range]
|
||||||
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
||||||
|
|
||||||
@ -900,40 +845,49 @@ impl Engine {
|
|||||||
let start = range.start;
|
let start = range.start;
|
||||||
let end = range.end;
|
let end = range.end;
|
||||||
|
|
||||||
if start < 0 || start as usize >= BITS {
|
let start = super::calc_index(BITS, start, false, || {
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into());
|
ERR::ErrorBitFieldBounds(BITS, start, pos).into()
|
||||||
} else if end < 0 || end as usize >= BITS {
|
})?;
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into());
|
let end = super::calc_index(BITS, end, false, || {
|
||||||
} else if end <= start {
|
ERR::ErrorBitFieldBounds(BITS, end, pos).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if end <= start {
|
||||||
(0, 0)
|
(0, 0)
|
||||||
} else if end as usize == BITS && start == 0 {
|
} else if end == BITS && start == 0 {
|
||||||
// -1 = all bits set
|
// -1 = all bits set
|
||||||
(0, -1)
|
(0, -1)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
start as u8,
|
start as u8,
|
||||||
// 2^bits - 1
|
// 2^bits - 1
|
||||||
(((2 as BASE).pow((end - start) as u32) - 1) as crate::INT) << start,
|
(((2 as crate::UNSIGNED_INT).pow((end - start) as u32) - 1)
|
||||||
|
as crate::INT)
|
||||||
|
<< start,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if let Some(range) = idx.read_lock::<crate::InclusiveRange>() {
|
} else if let Some(range) = idx.read_lock::<crate::InclusiveRange>() {
|
||||||
let start = *range.start();
|
let start = *range.start();
|
||||||
let end = *range.end();
|
let end = *range.end();
|
||||||
|
|
||||||
if start < 0 || start as usize >= BITS {
|
let start = super::calc_index(BITS, start, false, || {
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into());
|
ERR::ErrorBitFieldBounds(BITS, start, pos).into()
|
||||||
} else if end < 0 || end as usize >= BITS {
|
})?;
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into());
|
let end = super::calc_index(BITS, end, false, || {
|
||||||
} else if end < start {
|
ERR::ErrorBitFieldBounds(BITS, end, pos).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if end < start {
|
||||||
(0, 0)
|
(0, 0)
|
||||||
} else if end as usize == BITS - 1 && start == 0 {
|
} else if end == BITS - 1 && start == 0 {
|
||||||
// -1 = all bits set
|
// -1 = all bits set
|
||||||
(0, -1)
|
(0, -1)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
start as u8,
|
start as u8,
|
||||||
// 2^bits - 1
|
// 2^bits - 1
|
||||||
(((2 as BASE).pow((end - start + 1) as u32) - 1) as crate::INT)
|
(((2 as crate::UNSIGNED_INT).pow((end - start + 1) as u32) - 1)
|
||||||
|
as crate::INT)
|
||||||
<< start,
|
<< start,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -956,39 +910,20 @@ impl Engine {
|
|||||||
// val_int[idx]
|
// val_int[idx]
|
||||||
let index = idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
|
||||||
|
|
||||||
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
||||||
|
|
||||||
let (bit_value, offset) = if index >= 0 {
|
let bit = super::calc_index(BITS, index, true, || {
|
||||||
let offset = index as usize;
|
ERR::ErrorBitFieldBounds(BITS, index, pos).into()
|
||||||
(
|
})?;
|
||||||
if offset >= BITS {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
let bit_value = (*value & (1 << bit)) != 0;
|
||||||
} else {
|
|
||||||
(*value & (1 << offset)) != 0
|
|
||||||
},
|
|
||||||
offset as u8,
|
|
||||||
)
|
|
||||||
} else if let Some(abs_index) = index.checked_abs() {
|
|
||||||
let offset = abs_index as usize;
|
|
||||||
(
|
|
||||||
// Count from end if negative
|
|
||||||
if offset > BITS {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
|
||||||
} else {
|
|
||||||
(*value & (1 << (BITS - offset))) != 0
|
|
||||||
},
|
|
||||||
offset as u8,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Target::Bit {
|
Ok(Target::Bit {
|
||||||
source: target,
|
source: target,
|
||||||
value: bit_value.into(),
|
value: bit_value.into(),
|
||||||
bit: offset,
|
bit: bit as u8,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -997,14 +932,14 @@ impl Engine {
|
|||||||
// val_string[idx]
|
// val_string[idx]
|
||||||
let index = idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, pos))?;
|
||||||
|
|
||||||
let (ch, offset) = if index >= 0 {
|
let (ch, offset) = if index >= 0 {
|
||||||
let offset = index as usize;
|
let offset = index as usize;
|
||||||
(
|
(
|
||||||
s.chars().nth(offset).ok_or_else(|| {
|
s.chars().nth(offset).ok_or_else(|| {
|
||||||
let chars_len = s.chars().count();
|
let chars_len = s.chars().count();
|
||||||
ERR::ErrorStringBounds(chars_len, index, idx_pos)
|
ERR::ErrorStringBounds(chars_len, index, pos)
|
||||||
})?,
|
})?,
|
||||||
offset,
|
offset,
|
||||||
)
|
)
|
||||||
@ -1014,13 +949,13 @@ impl Engine {
|
|||||||
// Count from end if negative
|
// Count from end if negative
|
||||||
s.chars().rev().nth(offset - 1).ok_or_else(|| {
|
s.chars().rev().nth(offset - 1).ok_or_else(|| {
|
||||||
let chars_len = s.chars().count();
|
let chars_len = s.chars().count();
|
||||||
ERR::ErrorStringBounds(chars_len, index, idx_pos)
|
ERR::ErrorStringBounds(chars_len, index, pos)
|
||||||
})?,
|
})?,
|
||||||
offset,
|
offset,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let chars_len = s.chars().count();
|
let chars_len = s.chars().count();
|
||||||
return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into());
|
return Err(ERR::ErrorStringBounds(chars_len, index, pos).into());
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Target::StringChar {
|
Ok(Target::StringChar {
|
||||||
@ -1033,8 +968,6 @@ impl Engine {
|
|||||||
_ if use_indexers => {
|
_ if use_indexers => {
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
|
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
|
||||||
let idx_pos = Position::NONE;
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
global,
|
global,
|
||||||
state,
|
state,
|
||||||
@ -1044,7 +977,7 @@ impl Engine {
|
|||||||
args,
|
args,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
idx_pos,
|
Position::NONE,
|
||||||
None,
|
None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
|
@ -12,4 +12,4 @@ pub use chaining::{ChainArgument, ChainType};
|
|||||||
pub use eval_context::EvalContext;
|
pub use eval_context::EvalContext;
|
||||||
pub use eval_state::EvalState;
|
pub use eval_state::EvalState;
|
||||||
pub use global_state::GlobalRuntimeState;
|
pub use global_state::GlobalRuntimeState;
|
||||||
pub use target::Target;
|
pub use target::{calc_index, calc_offset_len, Target};
|
||||||
|
@ -3,15 +3,11 @@
|
|||||||
use super::{EvalState, GlobalRuntimeState, Target};
|
use super::{EvalState, GlobalRuntimeState, Target};
|
||||||
use crate::ast::{Expr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
|
use crate::ast::{Expr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
|
||||||
use crate::func::get_hasher;
|
use crate::func::get_hasher;
|
||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
|
||||||
use crate::types::dynamic::{AccessMode, Union};
|
use crate::types::dynamic::{AccessMode, Union};
|
||||||
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
|
||||||
borrow::Cow,
|
|
||||||
hash::{Hash, Hasher},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Evaluate a statements block.
|
/// Evaluate a statements block.
|
||||||
@ -322,11 +318,10 @@ impl Engine {
|
|||||||
if let Some(t) = table.get(&hash) {
|
if let Some(t) = table.get(&hash) {
|
||||||
if let Some(ref c) = t.0 {
|
if let Some(ref c) = t.0 {
|
||||||
if self
|
if self
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, &c, level)
|
.eval_expr(scope, global, state, lib, this_ptr, &c, level)?
|
||||||
.and_then(|v| {
|
.as_bool()
|
||||||
v.as_bool().map_err(|typ| {
|
.map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<bool>(typ, c.position())
|
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||||
})
|
|
||||||
})?
|
})?
|
||||||
{
|
{
|
||||||
Some(&t.1)
|
Some(&t.1)
|
||||||
@ -349,11 +344,10 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
if let Some(c) = condition {
|
if let Some(c) = condition {
|
||||||
if !self
|
if !self
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, &c, level)
|
.eval_expr(scope, global, state, lib, this_ptr, &c, level)?
|
||||||
.and_then(|v| {
|
.as_bool()
|
||||||
v.as_bool().map_err(|typ| {
|
.map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<bool>(typ, c.position())
|
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||||
})
|
|
||||||
})?
|
})?
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -466,7 +460,7 @@ impl Engine {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(expr, x, _) => {
|
Stmt::For(expr, x, _) => {
|
||||||
let (Ident { name, .. }, counter, statements) = x.as_ref();
|
let (Ident { name: var_name, .. }, counter, statements) = x.as_ref();
|
||||||
let iter_obj = self
|
let iter_obj = self
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, global, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -494,24 +488,28 @@ impl Engine {
|
|||||||
// Add the loop variables
|
// Add the loop variables
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
let counter_index = if let Some(counter) = counter {
|
let counter_index = if let Some(counter) = counter {
|
||||||
scope.push(unsafe_cast_var_name_to_lifetime(&counter.name), 0 as INT);
|
scope.push(counter.name.clone(), 0 as INT);
|
||||||
scope.len() - 1
|
scope.len() - 1
|
||||||
} else {
|
} else {
|
||||||
usize::MAX
|
usize::MAX
|
||||||
};
|
};
|
||||||
scope.push(unsafe_cast_var_name_to_lifetime(name), ());
|
|
||||||
|
scope.push(var_name.clone(), ());
|
||||||
let index = scope.len() - 1;
|
let index = scope.len() - 1;
|
||||||
|
|
||||||
|
let mut loop_result = Ok(Dynamic::UNIT);
|
||||||
|
|
||||||
for (x, iter_value) in func(iter_obj).enumerate() {
|
for (x, iter_value) in func(iter_obj).enumerate() {
|
||||||
// Increment counter
|
// Increment counter
|
||||||
if counter_index < usize::MAX {
|
if counter_index < usize::MAX {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if x > INT::MAX as usize {
|
if x > INT::MAX as usize {
|
||||||
return Err(ERR::ErrorArithmetic(
|
loop_result = Err(ERR::ErrorArithmetic(
|
||||||
format!("for-loop counter overflow: {}", x),
|
format!("for-loop counter overflow: {}", x),
|
||||||
counter.as_ref().expect("`Some`").pos,
|
counter.as_ref().expect("`Some`").pos,
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let index_value = (x as INT).into();
|
let index_value = (x as INT).into();
|
||||||
@ -548,7 +546,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(&mut global.num_operations, statements.position())?;
|
if let Err(err) =
|
||||||
|
self.inc_operations(&mut global.num_operations, statements.position())
|
||||||
|
{
|
||||||
|
loop_result = Err(err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if statements.is_empty() {
|
if statements.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@ -563,13 +566,17 @@ impl Engine {
|
|||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
ERR::LoopBreak(false, _) => (),
|
ERR::LoopBreak(false, _) => (),
|
||||||
ERR::LoopBreak(true, _) => break,
|
ERR::LoopBreak(true, _) => break,
|
||||||
_ => return Err(err),
|
_ => {
|
||||||
|
loop_result = Err(err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.rewind(orig_scope_len);
|
scope.rewind(orig_scope_len);
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
|
loop_result
|
||||||
} else {
|
} else {
|
||||||
Err(ERR::ErrorFor(expr.position()).into())
|
Err(ERR::ErrorFor(expr.position()).into())
|
||||||
}
|
}
|
||||||
@ -582,7 +589,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Try/Catch statement
|
// Try/Catch statement
|
||||||
Stmt::TryCatch(x, _) => {
|
Stmt::TryCatch(x, _) => {
|
||||||
let (try_stmt, err_var, catch_stmt) = x.as_ref();
|
let (try_stmt, err_var_name, catch_stmt) = x.as_ref();
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt_block(scope, global, state, lib, this_ptr, try_stmt, true, level)
|
.eval_stmt_block(scope, global, state, lib, this_ptr, try_stmt, true, level)
|
||||||
@ -632,9 +639,9 @@ impl Engine {
|
|||||||
|
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
|
|
||||||
err_var.as_ref().map(|Ident { name, .. }| {
|
err_var_name
|
||||||
scope.push(unsafe_cast_var_name_to_lifetime(name), err_value)
|
.as_ref()
|
||||||
});
|
.map(|Ident { name, .. }| scope.push(name.clone(), err_value));
|
||||||
|
|
||||||
let result = self.eval_stmt_block(
|
let result = self.eval_stmt_block(
|
||||||
scope, global, state, lib, this_ptr, catch_stmt, true, level,
|
scope, global, state, lib, this_ptr, catch_stmt, true, level,
|
||||||
@ -685,7 +692,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Let/const statement
|
// Let/const statement
|
||||||
Stmt::Var(expr, x, options, _) => {
|
Stmt::Var(expr, x, options, _) => {
|
||||||
let name = &x.name;
|
let var_name = &x.name;
|
||||||
let entry_type = if options.contains(AST_OPTION_CONSTANT) {
|
let entry_type = if options.contains(AST_OPTION_CONSTANT) {
|
||||||
AccessMode::ReadOnly
|
AccessMode::ReadOnly
|
||||||
} else {
|
} else {
|
||||||
@ -697,7 +704,7 @@ impl Engine {
|
|||||||
.eval_expr(scope, global, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, global, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let (var_name, _alias): (Cow<'_, str>, _) = if !rewind_scope {
|
let _alias = if !rewind_scope {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if state.scope_level == 0
|
if state.scope_level == 0
|
||||||
@ -705,23 +712,26 @@ impl Engine {
|
|||||||
&& lib.iter().any(|&m| !m.is_empty())
|
&& lib.iter().any(|&m| !m.is_empty())
|
||||||
{
|
{
|
||||||
// Add a global constant if at top level and there are functions
|
// Add a global constant if at top level and there are functions
|
||||||
global.set_constant(name.clone(), value.clone());
|
global.set_constant(var_name.clone(), value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
if export {
|
||||||
name.to_string().into(),
|
Some(var_name)
|
||||||
if export { Some(name.clone()) } else { None },
|
} else {
|
||||||
)
|
None
|
||||||
|
}
|
||||||
} else if export {
|
} else if export {
|
||||||
unreachable!("exported variable not on global level");
|
unreachable!("exported variable not on global level");
|
||||||
} else {
|
} else {
|
||||||
(unsafe_cast_var_name_to_lifetime(name).into(), None)
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.push_dynamic_value(var_name, entry_type, value);
|
scope.push_dynamic_value(var_name.clone(), entry_type, value);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
_alias.map(|alias| scope.add_entry_alias(scope.len() - 1, alias));
|
if let Some(alias) = _alias {
|
||||||
|
scope.add_entry_alias(scope.len() - 1, alias.clone());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,74 @@ use std::ops::{Deref, DerefMut};
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
// Calculate an offset+len pair given an actual length of the underlying array.
|
||||||
|
//
|
||||||
|
// Negative starting positions count from the end.
|
||||||
|
//
|
||||||
|
// Values going over bounds are limited to the actual length.
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) {
|
||||||
|
let start = if start < 0 {
|
||||||
|
start.checked_abs().map_or(0, |positive_start| {
|
||||||
|
length - usize::min(positive_start as usize, length)
|
||||||
|
})
|
||||||
|
} else if start as usize >= length {
|
||||||
|
return (length, 0);
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = if len <= 0 {
|
||||||
|
0
|
||||||
|
} else if len as usize > length - start {
|
||||||
|
length - start
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
(start, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate an offset+len pair given an actual length of the underlying array.
|
||||||
|
//
|
||||||
|
// Negative starting positions count from the end.
|
||||||
|
//
|
||||||
|
// Values going over bounds call the provided closure to return a default value or an error.
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn calc_index<E>(
|
||||||
|
length: usize,
|
||||||
|
start: crate::INT,
|
||||||
|
negative_count_from_end: bool,
|
||||||
|
err: impl Fn() -> Result<usize, E>,
|
||||||
|
) -> Result<usize, E> {
|
||||||
|
if start < 0 {
|
||||||
|
if negative_count_from_end {
|
||||||
|
// Count from end if negative
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
return match start.checked_abs() {
|
||||||
|
Some(positive_start) => {
|
||||||
|
if (positive_start as usize) > length {
|
||||||
|
err()
|
||||||
|
} else {
|
||||||
|
Ok(length - (positive_start as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => err(),
|
||||||
|
};
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
return Ok(length - (start.abs() as usize));
|
||||||
|
} else {
|
||||||
|
err()
|
||||||
|
}
|
||||||
|
} else if start as usize >= length {
|
||||||
|
err()
|
||||||
|
} else {
|
||||||
|
Ok(start as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A type that encapsulates a mutation target for an expression with side effects.
|
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Target<'a> {
|
pub enum Target<'a> {
|
||||||
|
@ -41,8 +41,7 @@ fn is_numeric(type_id: TypeId) -> bool {
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
|
let result = result || type_id == TypeId::of::<u128>() || type_id == TypeId::of::<i128>();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
use super::call::FnCallArgs;
|
use super::call::FnCallArgs;
|
||||||
use crate::ast::ScriptFnDef;
|
use crate::ast::ScriptFnDef;
|
||||||
use crate::eval::{EvalState, GlobalRuntimeState};
|
use crate::eval::{EvalState, GlobalRuntimeState};
|
||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
|
||||||
use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR};
|
use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, StaticVec, ERR};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -74,18 +73,10 @@ impl Engine {
|
|||||||
let orig_mods_len = global.num_imported_modules();
|
let orig_mods_len = global.num_imported_modules();
|
||||||
|
|
||||||
// Put arguments into scope as variables
|
// Put arguments into scope as variables
|
||||||
|
scope.extend(fn_def.params.iter().cloned().zip(args.into_iter().map(|v| {
|
||||||
// Actually consume the arguments instead of cloning them
|
// Actually consume the arguments instead of cloning them
|
||||||
scope.extend(
|
mem::take(*v)
|
||||||
fn_def
|
})));
|
||||||
.params
|
|
||||||
.iter()
|
|
||||||
.zip(args.iter_mut().map(|v| mem::take(*v)))
|
|
||||||
.map(|(name, value)| {
|
|
||||||
let var_name: std::borrow::Cow<'_, str> =
|
|
||||||
unsafe_cast_var_name_to_lifetime(name).into();
|
|
||||||
(var_name, value)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merge in encapsulated environment, if any
|
// Merge in encapsulated environment, if any
|
||||||
let mut lib_merged = StaticVec::with_capacity(lib.len() + 1);
|
let mut lib_merged = StaticVec::with_capacity(lib.len() + 1);
|
||||||
|
13
src/lib.rs
13
src/lib.rs
@ -41,8 +41,8 @@
|
|||||||
//! engine.register_fn("compute", compute_something);
|
//! engine.register_fn("compute", compute_something);
|
||||||
//!
|
//!
|
||||||
//! # #[cfg(not(feature = "no_std"))]
|
//! # #[cfg(not(feature = "no_std"))]
|
||||||
//! # #[cfg(not(target_arch = "wasm32"))]
|
//! # #[cfg(not(target_family = "wasm"))]
|
||||||
//! # #[cfg(not(target_arch = "wasm64"))]
|
//! #
|
||||||
//! // Evaluate the script, expecting a 'bool' result
|
//! // Evaluate the script, expecting a 'bool' result
|
||||||
//! let result = engine.eval_file::<bool>("my_script.rhai".into())?;
|
//! let result = engine.eval_file::<bool>("my_script.rhai".into())?;
|
||||||
//!
|
//!
|
||||||
@ -113,6 +113,7 @@ pub type INT = i32;
|
|||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
type UNSIGNED_INT = u64;
|
type UNSIGNED_INT = u64;
|
||||||
|
|
||||||
/// The unsigned system integer base type.
|
/// The unsigned system integer base type.
|
||||||
/// It is defined as [`u32`] since the `only_i32` feature is used.
|
/// It is defined as [`u32`] since the `only_i32` feature is used.
|
||||||
///
|
///
|
||||||
@ -365,17 +366,15 @@ compile_error!("`wasm-bindgen` cannot be used with `no-std`");
|
|||||||
#[cfg(feature = "stdweb")]
|
#[cfg(feature = "stdweb")]
|
||||||
compile_error!("`stdweb` cannot be used with `no-std`");
|
compile_error!("`stdweb` cannot be used with `no-std`");
|
||||||
|
|
||||||
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
|
#[cfg(target_family = "wasm")]
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
compile_error!("`no_std` cannot be used for WASM target");
|
compile_error!("`no_std` cannot be used for WASM target");
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
#[cfg(feature = "wasm-bindgen")]
|
#[cfg(feature = "wasm-bindgen")]
|
||||||
compile_error!("`wasm-bindgen` cannot be used for non-WASM target");
|
compile_error!("`wasm-bindgen` cannot be used for non-WASM target");
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
#[cfg(feature = "stdweb")]
|
#[cfg(feature = "stdweb")]
|
||||||
compile_error!("`stdweb` cannot be used non-WASM target");
|
compile_error!("`stdweb` cannot be used non-WASM target");
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ use crate::{
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
|
cmp::Ordering,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
fmt,
|
fmt,
|
||||||
iter::{empty, once},
|
iter::{empty, once},
|
||||||
@ -31,11 +32,9 @@ pub enum FnNamespace {
|
|||||||
Internal,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data structure containing a single registered function.
|
/// A type containing all metadata for a registered function.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct FuncInfo {
|
pub struct FnMetadata {
|
||||||
/// Function instance.
|
|
||||||
pub func: Shared<CallableFunction>,
|
|
||||||
/// Function namespace.
|
/// Function namespace.
|
||||||
pub namespace: FnNamespace,
|
pub namespace: FnNamespace,
|
||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
@ -44,59 +43,111 @@ pub struct FuncInfo {
|
|||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
/// Number of parameters.
|
/// Number of parameters.
|
||||||
pub params: usize,
|
pub params: usize,
|
||||||
/// Parameter types (if applicable).
|
|
||||||
pub param_types: StaticVec<TypeId>,
|
|
||||||
/// Parameter names and types (if available).
|
/// Parameter names and types (if available).
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub param_names_and_types: StaticVec<Identifier>,
|
pub params_info: StaticVec<Identifier>,
|
||||||
/// Return type name.
|
/// Return type name.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub return_type_name: Identifier,
|
pub return_type: Identifier,
|
||||||
/// Comments.
|
/// Comments.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Option<Box<[Box<str>]>>,
|
pub comments: Option<Box<[Box<str>]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for FnMetadata {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for FnMetadata {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
match self.name.cmp(&other.name) {
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
Ordering::Equal => match self.params.cmp(&other.params) {
|
||||||
|
Ordering::Equal => self.params_info.cmp(&other.params_info),
|
||||||
|
cmp => cmp,
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "metadata"))]
|
||||||
|
Ordering::Equal => self.params.cmp(&other.params),
|
||||||
|
cmp => cmp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type containing a single registered function.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FuncInfo {
|
||||||
|
/// Function instance.
|
||||||
|
pub func: Shared<CallableFunction>,
|
||||||
|
/// Parameter types (if applicable).
|
||||||
|
pub param_types: StaticVec<TypeId>,
|
||||||
|
/// Function metadata.
|
||||||
|
pub metadata: FnMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
impl FuncInfo {
|
impl FuncInfo {
|
||||||
|
/// Format a return type to be display-friendly.
|
||||||
|
///
|
||||||
|
/// `()` is cleared.
|
||||||
|
/// [`RhaiResult`][crate::RhaiResult] and [`RhaiResultOf<T>`] are expanded.
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
pub fn format_return_type(typ: &str) -> std::borrow::Cow<str> {
|
||||||
|
const RHAI_RESULT_TYPE: &str = "RhaiResult";
|
||||||
|
const RHAI_RESULT_TYPE_EXPAND: &str = "Result<Dynamic, Box<EvalAltResult>>";
|
||||||
|
const RHAI_RESULT_OF_TYPE: &str = "RhaiResultOf<";
|
||||||
|
const RHAI_RESULT_OF_TYPE_EXPAND: &str = "Result<{}, Box<EvalAltResult>>";
|
||||||
|
|
||||||
|
match typ {
|
||||||
|
"" | "()" => "".into(),
|
||||||
|
RHAI_RESULT_TYPE => RHAI_RESULT_TYPE_EXPAND.into(),
|
||||||
|
ty if ty.starts_with(RHAI_RESULT_OF_TYPE) && ty.ends_with(">") => {
|
||||||
|
RHAI_RESULT_OF_TYPE_EXPAND
|
||||||
|
.replace("{}", ty[RHAI_RESULT_OF_TYPE.len()..ty.len() - 1].trim())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
ty => ty.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Generate a signature of the function.
|
/// Generate a signature of the function.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn gen_signature(&self) -> String {
|
pub fn gen_signature(&self) -> String {
|
||||||
let mut sig = format!("{}(", self.name);
|
let mut sig = format!("{}(", self.metadata.name);
|
||||||
|
|
||||||
if !self.param_names_and_types.is_empty() {
|
let return_type = Self::format_return_type(&self.metadata.return_type);
|
||||||
|
|
||||||
|
if !self.metadata.params_info.is_empty() {
|
||||||
let params: StaticVec<_> = self
|
let params: StaticVec<_> = self
|
||||||
.param_names_and_types
|
.metadata
|
||||||
|
.params_info
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.as_str())
|
.map(|s| s.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
sig.push_str(¶ms.join(", "));
|
sig.push_str(¶ms.join(", "));
|
||||||
sig.push_str(")");
|
sig.push(')');
|
||||||
|
|
||||||
match self.return_type_name.as_str() {
|
if !return_type.is_empty() {
|
||||||
"" | "()" => (),
|
|
||||||
ty => {
|
|
||||||
sig.push_str(" -> ");
|
sig.push_str(" -> ");
|
||||||
sig.push_str(ty);
|
sig.push_str(&return_type);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for x in 0..self.params {
|
for x in 0..self.metadata.params {
|
||||||
sig.push('_');
|
sig.push('_');
|
||||||
if x < self.params - 1 {
|
if x < self.metadata.params - 1 {
|
||||||
sig.push_str(", ");
|
sig.push_str(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.func.is_script() {
|
|
||||||
sig.push(')');
|
sig.push(')');
|
||||||
} else {
|
|
||||||
sig.push_str(")");
|
|
||||||
|
|
||||||
match self.return_type_name.as_str() {
|
if !self.func.is_script() {
|
||||||
"()" => (),
|
sig.push(')');
|
||||||
_ => sig.push_str(" -> ?"),
|
|
||||||
|
if !return_type.is_empty() {
|
||||||
|
sig.push_str(" -> ");
|
||||||
|
sig.push_str(&return_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,7 +423,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
||||||
self.iter_fn()
|
self.iter_fn()
|
||||||
.filter(|&f| match f.access {
|
.filter(|&f| match f.metadata.access {
|
||||||
FnAccess::Public => true,
|
FnAccess::Public => true,
|
||||||
FnAccess::Private => false,
|
FnAccess::Private => false,
|
||||||
})
|
})
|
||||||
@ -478,22 +529,24 @@ impl Module {
|
|||||||
let num_params = fn_def.params.len();
|
let num_params = fn_def.params.len();
|
||||||
let hash_script = crate::calc_fn_hash(&fn_def.name, num_params);
|
let hash_script = crate::calc_fn_hash(&fn_def.name, num_params);
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
let param_names_and_types = fn_def.params.iter().cloned().collect();
|
let params_info = fn_def.params.iter().cloned().collect();
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
hash_script,
|
hash_script,
|
||||||
FuncInfo {
|
FuncInfo {
|
||||||
|
metadata: FnMetadata {
|
||||||
name: fn_def.name.clone(),
|
name: fn_def.name.clone(),
|
||||||
namespace: FnNamespace::Internal,
|
namespace: FnNamespace::Internal,
|
||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
params: num_params,
|
params: num_params,
|
||||||
param_types: StaticVec::new_const(),
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
param_names_and_types,
|
params_info,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
return_type_name: "Dynamic".into(),
|
return_type: "Dynamic".into(),
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: None,
|
||||||
|
},
|
||||||
func: Into::<CallableFunction>::into(fn_def).into(),
|
func: Into::<CallableFunction>::into(fn_def).into(),
|
||||||
|
param_types: StaticVec::new_const(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
@ -518,7 +571,7 @@ impl Module {
|
|||||||
let name = name.as_ref();
|
let name = name.as_ref();
|
||||||
|
|
||||||
self.iter_fn()
|
self.iter_fn()
|
||||||
.find(|f| f.params == num_params && f.name == name)
|
.find(|f| f.metadata.params == num_params && f.metadata.name == name)
|
||||||
.and_then(|f| f.func.get_script_fn_def())
|
.and_then(|f| f.func.get_script_fn_def())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -648,14 +701,14 @@ impl Module {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
let (param_names, return_type_name) = if param_names.len() > f.params {
|
let (param_names, return_type_name) = if param_names.len() > f.metadata.params {
|
||||||
let return_type = param_names.pop().unwrap();
|
let return_type = param_names.pop().unwrap();
|
||||||
(param_names, return_type)
|
(param_names, return_type)
|
||||||
} else {
|
} else {
|
||||||
(param_names, Default::default())
|
(param_names, Default::default())
|
||||||
};
|
};
|
||||||
f.param_names_and_types = param_names;
|
f.metadata.params_info = param_names;
|
||||||
f.return_type_name = return_type_name;
|
f.metadata.return_type = return_type_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -698,7 +751,7 @@ impl Module {
|
|||||||
|
|
||||||
if !comments.is_empty() {
|
if !comments.is_empty() {
|
||||||
let f = self.functions.get_mut(&hash_fn).unwrap();
|
let f = self.functions.get_mut(&hash_fn).unwrap();
|
||||||
f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
|
f.metadata.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -710,7 +763,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
||||||
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
if let Some(f) = self.functions.get_mut(&hash_fn) {
|
||||||
f.namespace = namespace;
|
f.metadata.namespace = namespace;
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self.contains_indexed_global_functions = false;
|
self.contains_indexed_global_functions = false;
|
||||||
}
|
}
|
||||||
@ -795,18 +848,20 @@ impl Module {
|
|||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
hash_fn,
|
hash_fn,
|
||||||
FuncInfo {
|
FuncInfo {
|
||||||
|
metadata: FnMetadata {
|
||||||
name: name.as_ref().into(),
|
name: name.as_ref().into(),
|
||||||
namespace,
|
namespace,
|
||||||
access,
|
access,
|
||||||
params: param_types.len(),
|
params: param_types.len(),
|
||||||
param_types,
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
param_names_and_types: param_names,
|
params_info: param_names,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
return_type_name,
|
return_type: return_type_name,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: None,
|
||||||
|
},
|
||||||
func: func.into(),
|
func: func.into(),
|
||||||
|
param_types,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
@ -861,7 +916,7 @@ impl Module {
|
|||||||
|
|
||||||
if !comments.is_empty() {
|
if !comments.is_empty() {
|
||||||
let f = self.functions.get_mut(&hash).unwrap();
|
let f = self.functions.get_mut(&hash).unwrap();
|
||||||
f.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
|
f.metadata.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
|
||||||
}
|
}
|
||||||
|
|
||||||
hash
|
hash
|
||||||
@ -1374,11 +1429,11 @@ impl Module {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|&(_, f)| {
|
.filter(|&(_, f)| {
|
||||||
_filter(
|
_filter(
|
||||||
f.namespace,
|
f.metadata.namespace,
|
||||||
f.access,
|
f.metadata.access,
|
||||||
f.func.is_script(),
|
f.func.is_script(),
|
||||||
f.name.as_str(),
|
f.metadata.name.as_str(),
|
||||||
f.params,
|
f.metadata.params,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(&k, v)| (k, v.clone())),
|
.map(|(&k, v)| (k, v.clone())),
|
||||||
@ -1404,7 +1459,12 @@ impl Module {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(_, f)| {
|
.filter(|(_, f)| {
|
||||||
if f.func.is_script() {
|
if f.func.is_script() {
|
||||||
filter(f.namespace, f.access, f.name.as_str(), f.params)
|
filter(
|
||||||
|
f.metadata.namespace,
|
||||||
|
f.metadata.access,
|
||||||
|
f.metadata.name.as_str(),
|
||||||
|
f.metadata.params,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -1472,10 +1532,10 @@ impl Module {
|
|||||||
> + '_ {
|
> + '_ {
|
||||||
self.iter_fn().filter(|&f| f.func.is_script()).map(|f| {
|
self.iter_fn().filter(|&f| f.func.is_script()).map(|f| {
|
||||||
(
|
(
|
||||||
f.namespace,
|
f.metadata.namespace,
|
||||||
f.access,
|
f.metadata.access,
|
||||||
f.name.as_str(),
|
f.metadata.name.as_str(),
|
||||||
f.params,
|
f.metadata.params,
|
||||||
f.func.get_script_fn_def().expect("script-defined function"),
|
f.func.get_script_fn_def().expect("script-defined function"),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1494,9 +1554,14 @@ impl Module {
|
|||||||
pub fn iter_script_fn_info(
|
pub fn iter_script_fn_info(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> {
|
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> {
|
||||||
self.iter_fn()
|
self.iter_fn().filter(|&f| f.func.is_script()).map(|f| {
|
||||||
.filter(|&f| f.func.is_script())
|
(
|
||||||
.map(|f| (f.namespace, f.access, f.name.as_str(), f.params))
|
f.metadata.namespace,
|
||||||
|
f.metadata.access,
|
||||||
|
f.metadata.name.as_str(),
|
||||||
|
f.metadata.params,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ Get an iterator over all script-defined functions in the [`Module`].
|
/// _(internals)_ Get an iterator over all script-defined functions in the [`Module`].
|
||||||
@ -1607,7 +1672,7 @@ impl Module {
|
|||||||
if ast.has_functions() {
|
if ast.has_functions() {
|
||||||
ast.shared_lib()
|
ast.shared_lib()
|
||||||
.iter_fn()
|
.iter_fn()
|
||||||
.filter(|&f| match f.access {
|
.filter(|&f| match f.metadata.access {
|
||||||
FnAccess::Public => true,
|
FnAccess::Public => true,
|
||||||
FnAccess::Private => false,
|
FnAccess::Private => false,
|
||||||
})
|
})
|
||||||
@ -1682,7 +1747,7 @@ impl Module {
|
|||||||
|
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
module.functions.iter().for_each(|(&hash, f)| {
|
module.functions.iter().for_each(|(&hash, f)| {
|
||||||
match f.namespace {
|
match f.metadata.namespace {
|
||||||
FnNamespace::Global => {
|
FnNamespace::Global => {
|
||||||
// Flatten all functions with global namespace
|
// Flatten all functions with global namespace
|
||||||
functions.insert(hash, f.func.clone());
|
functions.insert(hash, f.func.clone());
|
||||||
@ -1690,20 +1755,23 @@ impl Module {
|
|||||||
}
|
}
|
||||||
FnNamespace::Internal => (),
|
FnNamespace::Internal => (),
|
||||||
}
|
}
|
||||||
match f.access {
|
match f.metadata.access {
|
||||||
FnAccess::Public => (),
|
FnAccess::Public => (),
|
||||||
FnAccess::Private => return, // Do not index private functions
|
FnAccess::Private => return, // Do not index private functions
|
||||||
}
|
}
|
||||||
|
|
||||||
if !f.func.is_script() {
|
if !f.func.is_script() {
|
||||||
let hash_qualified_fn =
|
let hash_qualified_fn = calc_native_fn_hash(
|
||||||
calc_native_fn_hash(path.iter().cloned(), f.name.as_str(), &f.param_types);
|
path.iter().cloned(),
|
||||||
|
f.metadata.name.as_str(),
|
||||||
|
&f.param_types,
|
||||||
|
);
|
||||||
functions.insert(hash_qualified_fn, f.func.clone());
|
functions.insert(hash_qualified_fn, f.func.clone());
|
||||||
} else if cfg!(not(feature = "no_function")) {
|
} else if cfg!(not(feature = "no_function")) {
|
||||||
let hash_qualified_script = crate::calc_qualified_fn_hash(
|
let hash_qualified_script = crate::calc_qualified_fn_hash(
|
||||||
path.iter().cloned(),
|
path.iter().cloned(),
|
||||||
f.name.as_str(),
|
f.metadata.name.as_str(),
|
||||||
f.params,
|
f.metadata.params,
|
||||||
);
|
);
|
||||||
functions.insert(hash_qualified_script, f.func.clone());
|
functions.insert(hash_qualified_script, f.func.clone());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_std"))]
|
#![cfg(not(feature = "no_std"))]
|
||||||
#![cfg(not(target_arch = "wasm32"))]
|
#![cfg(not(target_family = "wasm"))]
|
||||||
#![cfg(not(target_arch = "wasm64"))]
|
|
||||||
|
|
||||||
use crate::func::native::shared_write_lock;
|
use crate::func::native::shared_write_lock;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -11,8 +11,7 @@ mod stat;
|
|||||||
pub use collection::ModuleResolversCollection;
|
pub use collection::ModuleResolversCollection;
|
||||||
pub use dummy::DummyModuleResolver;
|
pub use dummy::DummyModuleResolver;
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
pub use file::FileModuleResolver;
|
pub use file::FileModuleResolver;
|
||||||
pub use stat::StaticModuleResolver;
|
pub use stat::StaticModuleResolver;
|
||||||
|
|
||||||
|
@ -121,14 +121,17 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||||
x ^ y
|
x ^ y
|
||||||
}
|
}
|
||||||
|
/// Return true if the number is zero.
|
||||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: $arg_type) -> bool {
|
pub fn is_zero(x: $arg_type) -> bool {
|
||||||
x == 0
|
x == 0
|
||||||
}
|
}
|
||||||
|
/// Return true if the number is odd.
|
||||||
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||||
pub fn is_odd(x: $arg_type) -> bool {
|
pub fn is_odd(x: $arg_type) -> bool {
|
||||||
x % 2 != 0
|
x % 2 != 0
|
||||||
}
|
}
|
||||||
|
/// Return true if the number is even.
|
||||||
#[rhai_fn(get = "is_even", name = "is_even")]
|
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||||
pub fn is_even(x: $arg_type) -> bool {
|
pub fn is_even(x: $arg_type) -> bool {
|
||||||
x % 2 == 0
|
x % 2 == 0
|
||||||
@ -157,6 +160,7 @@ macro_rules! gen_signed_functions {
|
|||||||
pub fn plus(x: $arg_type) -> $arg_type {
|
pub fn plus(x: $arg_type) -> $arg_type {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
/// Return the absolute value of the number.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn abs(x: $arg_type) -> RhaiResultOf<$arg_type> {
|
pub fn abs(x: $arg_type) -> RhaiResultOf<$arg_type> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
@ -165,6 +169,11 @@ macro_rules! gen_signed_functions {
|
|||||||
Ok(x.abs())
|
Ok(x.abs())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return the sign (as an integer) of the number according to the following:
|
||||||
|
///
|
||||||
|
/// * `0` if the number is zero
|
||||||
|
/// * `1` if the number is positive
|
||||||
|
/// * `-1` if the number is negative
|
||||||
pub fn sign(x: $arg_type) -> INT {
|
pub fn sign(x: $arg_type) -> INT {
|
||||||
x.signum() as INT
|
x.signum() as INT
|
||||||
}
|
}
|
||||||
@ -193,8 +202,8 @@ def_package! {
|
|||||||
reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64);
|
reg_functions!(lib += arith_numbers; i8, u8, i16, u16, i32, u32, u64);
|
||||||
reg_functions!(lib += signed_numbers; i8, i16, i32);
|
reg_functions!(lib += signed_numbers; i8, i16, i32);
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
{
|
{
|
||||||
reg_functions!(lib += arith_num_128; i128, u128);
|
reg_functions!(lib += arith_num_128; i128, u128);
|
||||||
reg_functions!(lib += signed_num_128; i128);
|
reg_functions!(lib += signed_num_128; i128);
|
||||||
@ -216,14 +225,17 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod int_functions {
|
mod int_functions {
|
||||||
|
/// Return true if the number is zero.
|
||||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: INT) -> bool {
|
pub fn is_zero(x: INT) -> bool {
|
||||||
x == 0
|
x == 0
|
||||||
}
|
}
|
||||||
|
/// Return true if the number is odd.
|
||||||
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||||
pub fn is_odd(x: INT) -> bool {
|
pub fn is_odd(x: INT) -> bool {
|
||||||
x % 2 != 0
|
x % 2 != 0
|
||||||
}
|
}
|
||||||
|
/// Return true if the number is even.
|
||||||
#[rhai_fn(get = "is_even", name = "is_even")]
|
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||||
pub fn is_even(x: INT) -> bool {
|
pub fn is_even(x: INT) -> bool {
|
||||||
x % 2 == 0
|
x % 2 == 0
|
||||||
@ -238,8 +250,8 @@ gen_arithmetic_functions!(arith_numbers => i8, u8, i16, u16, i32, u32, u64);
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
gen_arithmetic_functions!(arith_num_128 => i128, u128);
|
gen_arithmetic_functions!(arith_num_128 => i128, u128);
|
||||||
|
|
||||||
gen_signed_functions!(signed_basic => INT);
|
gen_signed_functions!(signed_basic => INT);
|
||||||
@ -250,8 +262,8 @@ gen_signed_functions!(signed_numbers => i8, i16, i32);
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
gen_signed_functions!(signed_num_128 => i128);
|
gen_signed_functions!(signed_num_128 => i128);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -334,9 +346,15 @@ mod f32_functions {
|
|||||||
pub fn plus(x: f32) -> f32 {
|
pub fn plus(x: f32) -> f32 {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
/// Return the absolute value of the floating-point number.
|
||||||
pub fn abs(x: f32) -> f32 {
|
pub fn abs(x: f32) -> f32 {
|
||||||
x.abs()
|
x.abs()
|
||||||
}
|
}
|
||||||
|
/// Return the sign (as an integer) of the floating-point number according to the following:
|
||||||
|
///
|
||||||
|
/// * `0` if the number is zero
|
||||||
|
/// * `1` if the number is positive
|
||||||
|
/// * `-1` if the number is negative
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn sign(x: f32) -> RhaiResultOf<INT> {
|
pub fn sign(x: f32) -> RhaiResultOf<INT> {
|
||||||
match x.signum() {
|
match x.signum() {
|
||||||
@ -345,6 +363,7 @@ mod f32_functions {
|
|||||||
x => Ok(x as INT),
|
x => Ok(x as INT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return true if the floating-point number is zero.
|
||||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: f32) -> bool {
|
pub fn is_zero(x: f32) -> bool {
|
||||||
x == 0.0
|
x == 0.0
|
||||||
@ -442,9 +461,15 @@ mod f64_functions {
|
|||||||
pub fn plus(x: f64) -> f64 {
|
pub fn plus(x: f64) -> f64 {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
/// Return the absolute value of the floating-point number.
|
||||||
pub fn abs(x: f64) -> f64 {
|
pub fn abs(x: f64) -> f64 {
|
||||||
x.abs()
|
x.abs()
|
||||||
}
|
}
|
||||||
|
/// Return the sign (as an integer) of the floating-point number according to the following:
|
||||||
|
///
|
||||||
|
/// * `0` if the number is zero
|
||||||
|
/// * `1` if the number is positive
|
||||||
|
/// * `-1` if the number is negative
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn sign(x: f64) -> RhaiResultOf<INT> {
|
pub fn sign(x: f64) -> RhaiResultOf<INT> {
|
||||||
match x.signum() {
|
match x.signum() {
|
||||||
@ -453,6 +478,7 @@ mod f64_functions {
|
|||||||
x => Ok(x as INT),
|
x => Ok(x as INT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return true if the floating-point number is zero.
|
||||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: f64) -> bool {
|
pub fn is_zero(x: f64) -> bool {
|
||||||
x == 0.0
|
x == 0.0
|
||||||
@ -536,9 +562,15 @@ pub mod decimal_functions {
|
|||||||
pub fn plus(x: Decimal) -> Decimal {
|
pub fn plus(x: Decimal) -> Decimal {
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
|
/// Return the absolute value of the decimal number.
|
||||||
pub fn abs(x: Decimal) -> Decimal {
|
pub fn abs(x: Decimal) -> Decimal {
|
||||||
x.abs()
|
x.abs()
|
||||||
}
|
}
|
||||||
|
/// Return the sign (as an integer) of the decimal number according to the following:
|
||||||
|
///
|
||||||
|
/// * `0` if the number is zero
|
||||||
|
/// * `1` if the number is positive
|
||||||
|
/// * `-1` if the number is negative
|
||||||
pub fn sign(x: Decimal) -> INT {
|
pub fn sign(x: Decimal) -> INT {
|
||||||
if x == Decimal::zero() {
|
if x == Decimal::zero() {
|
||||||
0
|
0
|
||||||
@ -548,6 +580,7 @@ pub mod decimal_functions {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return true if the decimal number is zero.
|
||||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: Decimal) -> bool {
|
pub fn is_zero(x: Decimal) -> bool {
|
||||||
x.is_zero()
|
x.is_zero()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,10 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use crate::eval::calc_index;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT};
|
use crate::{
|
||||||
|
def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT,
|
||||||
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -18,118 +21,145 @@ def_package! {
|
|||||||
mod bit_field_functions {
|
mod bit_field_functions {
|
||||||
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||||
|
|
||||||
|
/// Return `true` if the specified `bit` in the number is set.
|
||||||
|
///
|
||||||
|
/// If `bit` < 0, position counts from the MSB (Most Significant Bit).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// print(x.get_bit(5)); // prints false
|
||||||
|
///
|
||||||
|
/// print(x.get_bit(6)); // prints true
|
||||||
|
///
|
||||||
|
/// print(x.get_bit(-48)); // prints true on 64-bit
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn get_bit(value: INT, index: INT) -> RhaiResultOf<bool> {
|
pub fn get_bit(value: INT, bit: INT) -> RhaiResultOf<bool> {
|
||||||
if index >= 0 {
|
let bit = calc_index(BITS, bit, true, || {
|
||||||
let offset = index as usize;
|
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
if offset >= BITS {
|
Ok((value & (1 << bit)) != 0)
|
||||||
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
|
||||||
} else {
|
|
||||||
Ok((value & (1 << offset)) != 0)
|
|
||||||
}
|
|
||||||
} else if let Some(abs_index) = index.checked_abs() {
|
|
||||||
let offset = abs_index as usize;
|
|
||||||
|
|
||||||
// Count from end if negative
|
|
||||||
if offset > BITS {
|
|
||||||
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
|
||||||
} else {
|
|
||||||
Ok((value & (1 << (BITS - offset))) != 0)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/// Set the specified `bit` in the number if the new value is `true`.
|
||||||
|
/// Clear the `bit` if the new value is `false`.
|
||||||
|
///
|
||||||
|
/// If `bit` < 0, position counts from the MSB (Most Significant Bit).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// x.set_bit(5, true);
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123488
|
||||||
|
///
|
||||||
|
/// x.set_bit(6, false);
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123424
|
||||||
|
///
|
||||||
|
/// x.set_bit(-48, false);
|
||||||
|
///
|
||||||
|
/// print(x); // prints 57888 on 64-bit
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn set_bit(value: &mut INT, index: INT, new_value: bool) -> RhaiResultOf<()> {
|
pub fn set_bit(value: &mut INT, bit: INT, new_value: bool) -> RhaiResultOf<()> {
|
||||||
if index >= 0 {
|
let bit = calc_index(BITS, bit, true, || {
|
||||||
let offset = index as usize;
|
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
if offset >= BITS {
|
let mask = 1 << bit;
|
||||||
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
|
||||||
} else {
|
|
||||||
let mask = 1 << offset;
|
|
||||||
if new_value {
|
if new_value {
|
||||||
*value |= mask;
|
*value |= mask;
|
||||||
} else {
|
} else {
|
||||||
*value &= !mask;
|
*value &= !mask;
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else if let Some(abs_index) = index.checked_abs() {
|
|
||||||
let offset = abs_index as usize;
|
|
||||||
|
|
||||||
// Count from end if negative
|
|
||||||
if offset > BITS {
|
|
||||||
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
|
||||||
} else {
|
|
||||||
let mask = 1 << offset;
|
|
||||||
if new_value {
|
|
||||||
*value |= mask;
|
|
||||||
} else {
|
|
||||||
*value &= !mask;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
} else {
|
/// Return an exclusive range of bits in the number as a new number.
|
||||||
Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
///
|
||||||
}
|
/// # Example
|
||||||
}
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// print(x.get_bits(5..10)); // print 18
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "get_bits", return_raw)]
|
#[rhai_fn(name = "get_bits", return_raw)]
|
||||||
pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf<INT> {
|
pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf<INT> {
|
||||||
let from = INT::max(range.start, 0);
|
let from = INT::max(range.start, 0);
|
||||||
let to = INT::max(range.end, from);
|
let to = INT::max(range.end, from);
|
||||||
get_bits(value, from, to - from)
|
get_bits(value, from, to - from)
|
||||||
}
|
}
|
||||||
|
/// Return an inclusive range of bits in the number as a new number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// print(x.get_bits(5..=9)); // print 18
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "get_bits", return_raw)]
|
#[rhai_fn(name = "get_bits", return_raw)]
|
||||||
pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf<INT> {
|
pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf<INT> {
|
||||||
let from = INT::max(*range.start(), 0);
|
let from = INT::max(*range.start(), 0);
|
||||||
let to = INT::max(*range.end(), from - 1);
|
let to = INT::max(*range.end(), from - 1);
|
||||||
get_bits(value, from, to - from + 1)
|
get_bits(value, from, to - from + 1)
|
||||||
}
|
}
|
||||||
|
/// Return a portion of bits in the number as a new number.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the MSB (Most Significant Bit).
|
||||||
|
/// * If `bits` ≤ 0, zero is returned.
|
||||||
|
/// * If `start` position + `bits` ≥ total number of bits, the bits after the `start` position are returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// print(x.get_bits(5, 8)); // print 18
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn get_bits(value: INT, index: INT, bits: INT) -> RhaiResultOf<INT> {
|
pub fn get_bits(value: INT, start: INT, bits: INT) -> RhaiResultOf<INT> {
|
||||||
if bits < 1 {
|
if bits <= 0 {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = if index >= 0 {
|
let bit = calc_index(BITS, start, true, || {
|
||||||
let offset = index as usize;
|
ERR::ErrorBitFieldBounds(BITS, start, Position::NONE).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
if offset >= BITS {
|
let bits = if bit + bits as usize > BITS {
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
|
BITS - bit
|
||||||
}
|
|
||||||
|
|
||||||
offset
|
|
||||||
} else if let Some(abs_index) = index.checked_abs() {
|
|
||||||
let offset = abs_index as usize;
|
|
||||||
|
|
||||||
// Count from end if negative
|
|
||||||
if offset > BITS {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
|
|
||||||
}
|
|
||||||
BITS - offset
|
|
||||||
} else {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
|
|
||||||
};
|
|
||||||
|
|
||||||
let bits = if offset + bits as usize > BITS {
|
|
||||||
BITS - offset
|
|
||||||
} else {
|
} else {
|
||||||
bits as usize
|
bits as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut base = 1;
|
if bit == 0 && bits == BITS {
|
||||||
let mut mask = 0;
|
return Ok(value);
|
||||||
|
|
||||||
for _ in 0..bits {
|
|
||||||
mask |= base;
|
|
||||||
base <<= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(((value & (mask << index)) >> index) & mask)
|
// 2^bits - 1
|
||||||
|
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as crate::INT;
|
||||||
|
|
||||||
|
Ok(((value & (mask << bit)) >> bit) & mask)
|
||||||
}
|
}
|
||||||
|
/// Replace an exclusive range of bits in the number with a new value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// x.set_bits(5..10, 42);
|
||||||
|
///
|
||||||
|
/// print(x); // print 123200
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "set_bits", return_raw)]
|
#[rhai_fn(name = "set_bits", return_raw)]
|
||||||
pub fn set_bits_range(
|
pub fn set_bits_range(
|
||||||
value: &mut INT,
|
value: &mut INT,
|
||||||
@ -140,6 +170,17 @@ mod bit_field_functions {
|
|||||||
let to = INT::max(range.end, from);
|
let to = INT::max(range.end, from);
|
||||||
set_bits(value, from, to - from, new_value)
|
set_bits(value, from, to - from, new_value)
|
||||||
}
|
}
|
||||||
|
/// Replace an inclusive range of bits in the number with a new value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// x.set_bits(5..=9, 42);
|
||||||
|
///
|
||||||
|
/// print(x); // print 123200
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "set_bits", return_raw)]
|
#[rhai_fn(name = "set_bits", return_raw)]
|
||||||
pub fn set_bits_range_inclusive(
|
pub fn set_bits_range_inclusive(
|
||||||
value: &mut INT,
|
value: &mut INT,
|
||||||
@ -150,48 +191,51 @@ mod bit_field_functions {
|
|||||||
let to = INT::max(*range.end(), from - 1);
|
let to = INT::max(*range.end(), from - 1);
|
||||||
set_bits(value, from, to - from + 1, new_value)
|
set_bits(value, from, to - from + 1, new_value)
|
||||||
}
|
}
|
||||||
|
/// Replace a portion of bits in the number with a new value.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the MSB (Most Significant Bit).
|
||||||
|
/// * If `bits` ≤ 0, the number is not modified.
|
||||||
|
/// * If `start` position + `bits` ≥ total number of bits, the bits after the `start` position are replaced.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 123456;
|
||||||
|
///
|
||||||
|
/// x.set_bits(5, 8, 42);
|
||||||
|
///
|
||||||
|
/// print(x); // prints 124224
|
||||||
|
///
|
||||||
|
/// x.set_bits(-16, 10, 42);
|
||||||
|
///
|
||||||
|
/// print(x); // prints 11821949021971776 on 64-bit
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn set_bits(value: &mut INT, index: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> {
|
pub fn set_bits(value: &mut INT, bit: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> {
|
||||||
if bits < 1 {
|
if bits <= 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = if index >= 0 {
|
let bit = calc_index(BITS, bit, true, || {
|
||||||
let offset = index as usize;
|
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
||||||
|
})?;
|
||||||
|
|
||||||
if offset >= BITS {
|
let bits = if bit + bits as usize > BITS {
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
|
BITS - bit
|
||||||
}
|
|
||||||
|
|
||||||
offset
|
|
||||||
} else if let Some(abs_index) = index.checked_abs() {
|
|
||||||
let offset = abs_index as usize;
|
|
||||||
|
|
||||||
// Count from end if negative
|
|
||||||
if offset > BITS {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
|
|
||||||
}
|
|
||||||
BITS - offset
|
|
||||||
} else {
|
|
||||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, Position::NONE).into());
|
|
||||||
};
|
|
||||||
|
|
||||||
let bits = if offset + bits as usize > BITS {
|
|
||||||
BITS - offset
|
|
||||||
} else {
|
} else {
|
||||||
bits as usize
|
bits as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut base = 1;
|
if bit == 0 && bits == BITS {
|
||||||
let mut mask = 0;
|
*value = new_value;
|
||||||
|
return Ok(());
|
||||||
for _ in 0..bits {
|
|
||||||
mask |= base;
|
|
||||||
base <<= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*value &= !(mask << index);
|
// 2^bits - 1
|
||||||
*value |= (new_value & mask) << index;
|
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as crate::INT;
|
||||||
|
|
||||||
|
*value &= !(mask << bit);
|
||||||
|
*value |= (new_value & mask) << bit;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -14,122 +14,34 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod fn_ptr_functions {
|
mod fn_ptr_functions {
|
||||||
|
/// Return the name of the function.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// fn double(x) { x * 2 }
|
||||||
|
///
|
||||||
|
/// let f = Fn("double");
|
||||||
|
///
|
||||||
|
/// print(f.name); // prints "double"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "name", get = "name", pure)]
|
#[rhai_fn(name = "name", get = "name", pure)]
|
||||||
pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString {
|
pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString {
|
||||||
fn_ptr.fn_name_raw().into()
|
fn_ptr.fn_name_raw().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if the function is an anonymous function.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let f = |x| x * 2;
|
||||||
|
///
|
||||||
|
/// print(f.is_anonymous); // prints true
|
||||||
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[rhai_fn(name = "is_anonymous", get = "is_anonymous", pure)]
|
#[rhai_fn(name = "is_anonymous", get = "is_anonymous", pure)]
|
||||||
pub fn is_anonymous(fn_ptr: &mut FnPtr) -> bool {
|
pub fn is_anonymous(fn_ptr: &mut FnPtr) -> bool {
|
||||||
fn_ptr.is_anonymous()
|
fn_ptr.is_anonymous()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array {
|
|
||||||
collect_fn_metadata(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
|
||||||
use crate::{ast::ScriptFnDef, Array, Identifier, Map};
|
|
||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
// Create a metadata record for a function.
|
|
||||||
fn make_metadata(
|
|
||||||
dict: &BTreeSet<Identifier>,
|
|
||||||
namespace: Option<Identifier>,
|
|
||||||
func: &ScriptFnDef,
|
|
||||||
) -> Map {
|
|
||||||
const DICT: &str = "key exists";
|
|
||||||
|
|
||||||
let mut map = Map::new();
|
|
||||||
|
|
||||||
if let Some(ns) = namespace {
|
|
||||||
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
|
|
||||||
}
|
|
||||||
map.insert(
|
|
||||||
dict.get("name").expect(DICT).clone(),
|
|
||||||
func.name.clone().into(),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
dict.get("access").expect(DICT).clone(),
|
|
||||||
match func.access {
|
|
||||||
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
|
||||||
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
dict.get("is_anonymous").expect(DICT).clone(),
|
|
||||||
func.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
dict.get("params").expect(DICT).clone(),
|
|
||||||
func.params
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect::<Array>()
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
map
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intern strings
|
|
||||||
let dict: BTreeSet<Identifier> = [
|
|
||||||
"namespace",
|
|
||||||
"name",
|
|
||||||
"access",
|
|
||||||
"public",
|
|
||||||
"private",
|
|
||||||
"is_anonymous",
|
|
||||||
"params",
|
|
||||||
]
|
|
||||||
.iter()
|
|
||||||
.map(|&s| s.into())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut _list = ctx.iter_namespaces().flat_map(Module::iter_script_fn).fold(
|
|
||||||
Array::new(),
|
|
||||||
|mut list, (_, _, _, _, f)| {
|
|
||||||
list.push(make_metadata(&dict, None, f).into());
|
|
||||||
list
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
{
|
|
||||||
// Recursively scan modules for script-defined functions.
|
|
||||||
fn scan_module(
|
|
||||||
list: &mut Array,
|
|
||||||
dict: &BTreeSet<Identifier>,
|
|
||||||
namespace: Identifier,
|
|
||||||
module: &Module,
|
|
||||||
) {
|
|
||||||
module.iter_script_fn().for_each(|(_, _, _, _, f)| {
|
|
||||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
|
||||||
});
|
|
||||||
module.iter_sub_modules().for_each(|(ns, m)| {
|
|
||||||
let ns = format!(
|
|
||||||
"{}{}{}",
|
|
||||||
namespace,
|
|
||||||
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
|
||||||
ns
|
|
||||||
);
|
|
||||||
scan_module(list, dict, ns.into(), m.as_ref())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.iter_imports_raw()
|
|
||||||
.for_each(|(ns, m)| scan_module(&mut _list, &dict, ns.clone(), m.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
_list
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::eval::calc_index;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT};
|
use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT};
|
||||||
@ -120,30 +121,9 @@ const BITS: usize = std::mem::size_of::<INT>() * 8;
|
|||||||
|
|
||||||
impl BitRange {
|
impl BitRange {
|
||||||
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
||||||
let from = if from >= 0 {
|
let from = calc_index(BITS, from, true, || {
|
||||||
let offset = from as usize;
|
crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into()
|
||||||
|
})?;
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if offset >= BITS {
|
|
||||||
return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into());
|
|
||||||
}
|
|
||||||
offset
|
|
||||||
} else {
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if let Some(abs_from) = from.checked_abs() {
|
|
||||||
if (abs_from as usize) > BITS {
|
|
||||||
return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into());
|
|
||||||
}
|
|
||||||
BITS - (abs_from as usize)
|
|
||||||
} else {
|
|
||||||
return Err(crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
{
|
|
||||||
BITS - (from.abs() as usize)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let len = if len < 0 {
|
let len = if len < 0 {
|
||||||
0
|
0
|
||||||
@ -266,10 +246,20 @@ macro_rules! reg_range {
|
|||||||
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
$lib.update_fn_metadata(_hash, &[
|
$lib.update_fn_metadata_with_comments(_hash, [
|
||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("Iterator<Item=", stringify!($y), ">")
|
concat!("Iterator<Item=", stringify!($y), ">"),
|
||||||
|
], [
|
||||||
|
"/// Return an iterator over the range of `from..to`.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// for n in range(8, 18) {",
|
||||||
|
"/// print(n);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$lib.set_iterator::<RangeInclusive<$y>>();
|
$lib.set_iterator::<RangeInclusive<$y>>();
|
||||||
@ -281,11 +271,27 @@ macro_rules! reg_range {
|
|||||||
let _hash = $lib.set_native_fn($x, |from: $y, to: $y, step: $y| StepRange::new(from, to, step));
|
let _hash = $lib.set_native_fn($x, |from: $y, to: $y, step: $y| StepRange::new(from, to, step));
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
$lib.update_fn_metadata(_hash, &[
|
$lib.update_fn_metadata_with_comments(_hash, [
|
||||||
concat!("from: ", stringify!($y)),
|
concat!("from: ", stringify!($y)),
|
||||||
concat!("to: ", stringify!($y)),
|
concat!("to: ", stringify!($y)),
|
||||||
concat!("step: ", stringify!($y)),
|
concat!("step: ", stringify!($y)),
|
||||||
concat!("Iterator<Item=", stringify!($y), ">")
|
concat!("Iterator<Item=", stringify!($y), ">")
|
||||||
|
], [
|
||||||
|
"/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.",
|
||||||
|
"///",
|
||||||
|
"/// If `from` > `to` and `step` < 0, the iteration goes backwards.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// for n in range(8, 18, 3) {",
|
||||||
|
"/// print(n);",
|
||||||
|
"/// }",
|
||||||
|
"///",
|
||||||
|
"/// for n in range(18, 8, -3) {",
|
||||||
|
"/// print(n);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
]);
|
]);
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
@ -303,8 +309,8 @@ def_package! {
|
|||||||
{
|
{
|
||||||
reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64);
|
reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64);
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
reg_range!(lib | "range" => i128, u128);
|
reg_range!(lib | "range" => i128, u128);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,8 +321,8 @@ def_package! {
|
|||||||
{
|
{
|
||||||
reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
|
reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
reg_range!(lib | step "range" => i128, u128);
|
reg_range!(lib | step "range" => i128, u128);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +385,27 @@ def_package! {
|
|||||||
|
|
||||||
let _hash = lib.set_native_fn("range", StepFloatRange::new);
|
let _hash = lib.set_native_fn("range", StepFloatRange::new);
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.",
|
||||||
|
"///",
|
||||||
|
"/// If `from` > `to` and `step` < 0, the iteration goes backwards.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// for n in range(8.0, 18.0, 3.0) {",
|
||||||
|
"/// print(n);",
|
||||||
|
"/// }",
|
||||||
|
"///",
|
||||||
|
"/// for n in range(18.0, 8.0, -3.0) {",
|
||||||
|
"/// print(n);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
@ -441,7 +467,15 @@ def_package! {
|
|||||||
|
|
||||||
let _hash = lib.set_native_fn("range", StepDecimalRange::new);
|
let _hash = lib.set_native_fn("range", StepDecimalRange::new);
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.",
|
||||||
|
"///",
|
||||||
|
"/// If `from` > `to` and `step` < 0, the iteration goes backwards.",
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register string iterator
|
// Register string iterator
|
||||||
@ -453,7 +487,21 @@ def_package! {
|
|||||||
Ok(CharsStream::new(string, from, to - from))
|
Ok(CharsStream::new(string, from, to - from))
|
||||||
});
|
});
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "range: Range<INT>", "Iterator<Item=char>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["string: &str", "range: Range<INT>", "Iterator<Item=char>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over an exclusive range of characters in the string.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
r#"/// for ch in "hello, world!".chars(2..5) {"#,
|
||||||
|
"/// print(ch);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| {
|
let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| {
|
||||||
let from = INT::max(*range.start(), 0);
|
let from = INT::max(*range.start(), 0);
|
||||||
@ -461,25 +509,105 @@ def_package! {
|
|||||||
Ok(CharsStream::new(string, from, to-from + 1))
|
Ok(CharsStream::new(string, from, to-from + 1))
|
||||||
});
|
});
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive<INT>", "Iterator<Item=char>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["string: &str", "range: RangeInclusive<INT>", "Iterator<Item=char>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over an inclusive range of characters in the string.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
r#"/// for ch in "hello, world!".chars(2..=6) {"#,
|
||||||
|
"/// print(ch);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
|
let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["string: &str", "start: INT", "len: INT", "Iterator<Item=char>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over a portion of characters in the string.",
|
||||||
|
"///",
|
||||||
|
"/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).",
|
||||||
|
"/// * If `start` < -length of string, position counts from the beginning of the string.",
|
||||||
|
"/// * If `start` ≥ length of string, an empty iterator is returned.",
|
||||||
|
"/// * If `len` ≤ 0, an empty iterator is returned.",
|
||||||
|
"/// * If `start` position + `len` ≥ length of string, all characters of the string after the `start` position are iterated.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
r#"/// for ch in "hello, world!".chars(2, 4) {"#,
|
||||||
|
"/// print(ch);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX)));
|
let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX)));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "Iterator<Item=char>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["string: &str", "from: INT", "Iterator<Item=char>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over the characters in the string starting from the `start` position.",
|
||||||
|
"///",
|
||||||
|
"/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).",
|
||||||
|
"/// * If `start` < -length of string, position counts from the beginning of the string.",
|
||||||
|
"/// * If `start` ≥ length of string, an empty iterator is returned.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
r#"/// for ch in "hello, world!".chars(2) {"#,
|
||||||
|
"/// print(ch);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX)));
|
let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX)));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "Iterator<Item=char>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["string: &str", "Iterator<Item=char>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over the characters in the string.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
r#"/// for ch in "hello, world!".chars() {"#,
|
||||||
|
"/// print(ch);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
{
|
{
|
||||||
let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX)));
|
let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX)));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator<Item=char>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["string: &mut ImmutableString", "Iterator<Item=char>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over all the characters in the string.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
r#"/// for ch in "hello, world!".chars {"#,
|
||||||
|
"/// print(ch);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register bit-field iterator
|
// Register bit-field iterator
|
||||||
@ -491,7 +619,23 @@ def_package! {
|
|||||||
BitRange::new(value, from, to - from)
|
BitRange::new(value, from, to - from)
|
||||||
});
|
});
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "range: Range<INT>", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["value: INT", "range: Range<INT>", "Iterator<Item=bool>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over an exclusive range of bits in the number.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// let x = 123456;",
|
||||||
|
"///",
|
||||||
|
"/// for bit in x.bits(10..24) {",
|
||||||
|
"/// print(bit);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| {
|
let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| {
|
||||||
let from = INT::max(*range.start(), 0);
|
let from = INT::max(*range.start(), 0);
|
||||||
@ -499,25 +643,111 @@ def_package! {
|
|||||||
BitRange::new(value, from, to - from + 1)
|
BitRange::new(value, from, to - from + 1)
|
||||||
});
|
});
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive<INT>", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["value: INT", "range: RangeInclusive<INT>", "Iterator<Item=bool>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over an inclusive range of bits in the number.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// let x = 123456;",
|
||||||
|
"///",
|
||||||
|
"/// for bit in x.bits(10..=23) {",
|
||||||
|
"/// print(bit);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", BitRange::new);
|
let _hash = lib.set_native_fn("bits", BitRange::new);
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over a portion of bits in the number.",
|
||||||
|
"///",
|
||||||
|
"/// * If `start` < 0, position counts from the MSB (Most Significant Bit)>.",
|
||||||
|
"/// * If `len` ≤ 0, an empty iterator is returned.",
|
||||||
|
"/// * If `start` position + `len` ≥ length of string, all bits of the number after the `start` position are iterated.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// let x = 123456;",
|
||||||
|
"///",
|
||||||
|
"/// for bit in x.bits(10, 8) {",
|
||||||
|
"/// print(bit);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
|
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["value: INT", "from: INT", "Iterator<Item=bool>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over the bits in the number starting from the specified `start` position.",
|
||||||
|
"///",
|
||||||
|
"/// If `start` < 0, position counts from the MSB (Most Significant Bit)>.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// let x = 123456;",
|
||||||
|
"///",
|
||||||
|
"/// for bit in x.bits(10) {",
|
||||||
|
"/// print(bit);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) );
|
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) );
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["value: INT", "Iterator<Item=bool>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over all the bits in the number.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// let x = 123456;",
|
||||||
|
"///",
|
||||||
|
"/// for bit in x.bits() {",
|
||||||
|
"/// print(bit);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
{
|
{
|
||||||
let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) );
|
let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) );
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range<INT>", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata_with_comments(
|
||||||
|
_hash,
|
||||||
|
["value: &mut INT", "range: Range<INT>", "Iterator<Item=bool>"],
|
||||||
|
[
|
||||||
|
"/// Return an iterator over all the bits in the number.",
|
||||||
|
"///",
|
||||||
|
"/// # Example",
|
||||||
|
"///",
|
||||||
|
"/// ```rhai",
|
||||||
|
"/// let x = 123456;",
|
||||||
|
"///",
|
||||||
|
"/// for bit in x.bits {",
|
||||||
|
"/// print(bit);",
|
||||||
|
"/// }",
|
||||||
|
"/// ```"
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
combine_with_exported_module!(lib, "range", range_functions);
|
combine_with_exported_module!(lib, "range", range_functions);
|
||||||
@ -526,37 +756,45 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod range_functions {
|
mod range_functions {
|
||||||
|
/// Return the start of the exclusive range.
|
||||||
#[rhai_fn(get = "start", name = "start", pure)]
|
#[rhai_fn(get = "start", name = "start", pure)]
|
||||||
pub fn start(range: &mut ExclusiveRange) -> INT {
|
pub fn start(range: &mut ExclusiveRange) -> INT {
|
||||||
range.start
|
range.start
|
||||||
}
|
}
|
||||||
|
/// Return the end of the exclusive range.
|
||||||
#[rhai_fn(get = "end", name = "end", pure)]
|
#[rhai_fn(get = "end", name = "end", pure)]
|
||||||
pub fn end(range: &mut ExclusiveRange) -> INT {
|
pub fn end(range: &mut ExclusiveRange) -> INT {
|
||||||
range.end
|
range.end
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the range is inclusive.
|
||||||
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
||||||
pub fn is_inclusive(range: &mut ExclusiveRange) -> bool {
|
pub fn is_inclusive(range: &mut ExclusiveRange) -> bool {
|
||||||
let _range = range;
|
let _range = range;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the range is exclusive.
|
||||||
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
|
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
|
||||||
pub fn is_exclusive(range: &mut ExclusiveRange) -> bool {
|
pub fn is_exclusive(range: &mut ExclusiveRange) -> bool {
|
||||||
let _range = range;
|
let _range = range;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
/// Return the start of the inclusive range.
|
||||||
#[rhai_fn(get = "start", name = "start", pure)]
|
#[rhai_fn(get = "start", name = "start", pure)]
|
||||||
pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
|
pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
|
||||||
*range.start()
|
*range.start()
|
||||||
}
|
}
|
||||||
|
/// Return the end of the inclusive range.
|
||||||
#[rhai_fn(get = "end", name = "end", pure)]
|
#[rhai_fn(get = "end", name = "end", pure)]
|
||||||
pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
|
pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
|
||||||
*range.end()
|
*range.end()
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the range is inclusive.
|
||||||
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
||||||
pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool {
|
pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool {
|
||||||
let _range = range;
|
let _range = range;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the range is exclusive.
|
||||||
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
|
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
|
||||||
pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool {
|
pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool {
|
||||||
let _range = range;
|
let _range = range;
|
||||||
|
@ -53,4 +53,112 @@ mod core_functions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array {
|
||||||
|
collect_fn_metadata(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
||||||
|
use crate::{ast::ScriptFnDef, Array, Identifier, Map};
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
// Create a metadata record for a function.
|
||||||
|
fn make_metadata(
|
||||||
|
dict: &BTreeSet<Identifier>,
|
||||||
|
namespace: Option<Identifier>,
|
||||||
|
func: &ScriptFnDef,
|
||||||
|
) -> Map {
|
||||||
|
const DICT: &str = "key exists";
|
||||||
|
|
||||||
|
let mut map = Map::new();
|
||||||
|
|
||||||
|
if let Some(ns) = namespace {
|
||||||
|
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
|
||||||
|
}
|
||||||
|
map.insert(
|
||||||
|
dict.get("name").expect(DICT).clone(),
|
||||||
|
func.name.clone().into(),
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
dict.get("access").expect(DICT).clone(),
|
||||||
|
match func.access {
|
||||||
|
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
||||||
|
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
dict.get("is_anonymous").expect(DICT).clone(),
|
||||||
|
func.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
dict.get("params").expect(DICT).clone(),
|
||||||
|
func.params
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect::<Array>()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intern strings
|
||||||
|
let dict: BTreeSet<Identifier> = [
|
||||||
|
"namespace",
|
||||||
|
"name",
|
||||||
|
"access",
|
||||||
|
"public",
|
||||||
|
"private",
|
||||||
|
"is_anonymous",
|
||||||
|
"params",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|&s| s.into())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut _list = ctx.iter_namespaces().flat_map(Module::iter_script_fn).fold(
|
||||||
|
Array::new(),
|
||||||
|
|mut list, (_, _, _, _, f)| {
|
||||||
|
list.push(make_metadata(&dict, None, f).into());
|
||||||
|
list
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
{
|
||||||
|
// Recursively scan modules for script-defined functions.
|
||||||
|
fn scan_module(
|
||||||
|
list: &mut Array,
|
||||||
|
dict: &BTreeSet<Identifier>,
|
||||||
|
namespace: Identifier,
|
||||||
|
module: &Module,
|
||||||
|
) {
|
||||||
|
module.iter_script_fn().for_each(|(_, _, _, _, f)| {
|
||||||
|
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
||||||
|
});
|
||||||
|
module.iter_sub_modules().for_each(|(ns, m)| {
|
||||||
|
let ns = format!(
|
||||||
|
"{}{}{}",
|
||||||
|
namespace,
|
||||||
|
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
||||||
|
ns
|
||||||
|
);
|
||||||
|
scan_module(list, dict, ns.into(), m.as_ref())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.iter_imports_raw()
|
||||||
|
.for_each(|(ns, m)| scan_module(&mut _list, &dict, ns.clone(), m.as_ref()));
|
||||||
|
}
|
||||||
|
|
||||||
|
_list
|
||||||
}
|
}
|
||||||
|
@ -47,8 +47,8 @@ def_package! {
|
|||||||
{
|
{
|
||||||
reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64);
|
reg_functions!(lib += numbers; i8, u8, i16, u16, i32, u32, u64);
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
reg_functions!(lib += num_128; i128, u128);
|
reg_functions!(lib += num_128; i128, u128);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,8 +71,8 @@ gen_cmp_functions!(numbers => i8, u8, i16, u16, i32, u32, u64);
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
gen_cmp_functions!(num_128 => i128, u128);
|
gen_cmp_functions!(num_128 => i128, u128);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
@ -20,15 +20,32 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod map_functions {
|
mod map_functions {
|
||||||
|
/// Return the number of properties in the object map.
|
||||||
#[rhai_fn(pure)]
|
#[rhai_fn(pure)]
|
||||||
pub fn len(map: &mut Map) -> INT {
|
pub fn len(map: &mut Map) -> INT {
|
||||||
map.len() as INT
|
map.len() as INT
|
||||||
}
|
}
|
||||||
|
/// Clear the object map.
|
||||||
pub fn clear(map: &mut Map) {
|
pub fn clear(map: &mut Map) {
|
||||||
if !map.is_empty() {
|
if !map.is_empty() {
|
||||||
map.clear();
|
map.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Remove any property of the specified `name` from the object map, returning its value.
|
||||||
|
///
|
||||||
|
/// If the property does not exist, `()` is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = #{a:1, b:2, c:3};
|
||||||
|
///
|
||||||
|
/// let x = m.remove("b");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 2
|
||||||
|
///
|
||||||
|
/// print(m); // prints "#{a:1, c:3}"
|
||||||
|
/// ```
|
||||||
pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic {
|
pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic {
|
||||||
if !map.is_empty() {
|
if !map.is_empty() {
|
||||||
map.remove(name.as_str()).unwrap_or_else(|| Dynamic::UNIT)
|
map.remove(name.as_str()).unwrap_or_else(|| Dynamic::UNIT)
|
||||||
@ -36,12 +53,38 @@ mod map_functions {
|
|||||||
Dynamic::UNIT
|
Dynamic::UNIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Add all property values of another object map into the object map.
|
||||||
|
/// Existing property values of the same names are replaced.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = #{a:1, b:2, c:3};
|
||||||
|
/// let n = #{a: 42, d:0};
|
||||||
|
///
|
||||||
|
/// m.mixin(n);
|
||||||
|
///
|
||||||
|
/// print(m); // prints "#{a:42, b:2, c:3, d:0}"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "mixin", name = "+=")]
|
#[rhai_fn(name = "mixin", name = "+=")]
|
||||||
pub fn mixin(map: &mut Map, map2: Map) {
|
pub fn mixin(map: &mut Map, map2: Map) {
|
||||||
if !map2.is_empty() {
|
if !map2.is_empty() {
|
||||||
map.extend(map2.into_iter());
|
map.extend(map2.into_iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Make a copy of the object map, add all property values of another object map
|
||||||
|
/// (existing property values of the same names are replaced), then returning it.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = #{a:1, b:2, c:3};
|
||||||
|
/// let n = #{a: 42, d:0};
|
||||||
|
///
|
||||||
|
/// print(m + n); // prints "#{a:42, b:2, c:3, d:0}"
|
||||||
|
///
|
||||||
|
/// print(m); // prints "#{a:1, b:2, c:3}"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn merge(map1: Map, map2: Map) -> Map {
|
pub fn merge(map1: Map, map2: Map) -> Map {
|
||||||
if map2.is_empty() {
|
if map2.is_empty() {
|
||||||
@ -54,6 +97,19 @@ mod map_functions {
|
|||||||
map1
|
map1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Add all property values of another object map into the object map.
|
||||||
|
/// Only properties that do not originally exist in the object map are added.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = #{a:1, b:2, c:3};
|
||||||
|
/// let n = #{a: 42, d:0};
|
||||||
|
///
|
||||||
|
/// m.fill_with(n);
|
||||||
|
///
|
||||||
|
/// print(m); // prints "#{a:1, b:2, c:3, d:0}"
|
||||||
|
/// ```
|
||||||
pub fn fill_with(map: &mut Map, map2: Map) {
|
pub fn fill_with(map: &mut Map, map2: Map) {
|
||||||
if !map2.is_empty() {
|
if !map2.is_empty() {
|
||||||
if map.is_empty() {
|
if map.is_empty() {
|
||||||
@ -65,6 +121,22 @@ mod map_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return `true` if two object maps are equal (i.e. all property values are equal).
|
||||||
|
///
|
||||||
|
/// The operator `==` is used to compare property values and must be defined,
|
||||||
|
/// otherwise `false` is assumed.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m1 = #{a:1, b:2, c:3};
|
||||||
|
/// let m2 = #{a:1, b:2, c:3};
|
||||||
|
/// let m3 = #{a:1, c:3};
|
||||||
|
///
|
||||||
|
/// print(m1 == m2); // prints true
|
||||||
|
///
|
||||||
|
/// print(m1 == m3); // prints false
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "==", return_raw, pure)]
|
#[rhai_fn(name = "==", return_raw, pure)]
|
||||||
pub fn equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
pub fn equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
||||||
if map1.len() != map2.len() {
|
if map1.len() != map2.len() {
|
||||||
@ -77,8 +149,9 @@ mod map_functions {
|
|||||||
for (m1, v1) in map1.iter_mut() {
|
for (m1, v1) in map1.iter_mut() {
|
||||||
if let Some(v2) = map2.get_mut(m1) {
|
if let Some(v2) = map2.get_mut(m1) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_raw(OP_EQUALS, true, false, &mut [v1, v2])
|
.call_fn_raw(OP_EQUALS, true, false, &mut [v1, v2])?
|
||||||
.map(|v| v.as_bool().unwrap_or(false))?;
|
.as_bool()
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
if !equals {
|
if !equals {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
@ -91,11 +164,36 @@ mod map_functions {
|
|||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
/// Return `true` if two object maps are not equal (i.e. at least one property value is not equal).
|
||||||
|
///
|
||||||
|
/// The operator `==` is used to compare property values and must be defined,
|
||||||
|
/// otherwise `false` is assumed.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m1 = #{a:1, b:2, c:3};
|
||||||
|
/// let m2 = #{a:1, b:2, c:3};
|
||||||
|
/// let m3 = #{a:1, c:3};
|
||||||
|
///
|
||||||
|
/// print(m1 != m2); // prints false
|
||||||
|
///
|
||||||
|
/// print(m1 != m3); // prints true
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "!=", return_raw, pure)]
|
#[rhai_fn(name = "!=", return_raw, pure)]
|
||||||
pub fn not_equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
pub fn not_equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
||||||
equals(ctx, map1, map2).map(|r| !r)
|
equals(ctx, map1, map2).map(|r| !r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return an array with all the property names in the object map.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = #{a:1, b:2, c:3};
|
||||||
|
///
|
||||||
|
/// print(m.keys()); // prints ["a", "b", "c"]
|
||||||
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[rhai_fn(pure)]
|
#[rhai_fn(pure)]
|
||||||
pub fn keys(map: &mut Map) -> Array {
|
pub fn keys(map: &mut Map) -> Array {
|
||||||
@ -105,6 +203,15 @@ mod map_functions {
|
|||||||
map.keys().cloned().map(Into::into).collect()
|
map.keys().cloned().map(Into::into).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return an array with all the property values in the object map.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = #{a:1, b:2, c:3};
|
||||||
|
///
|
||||||
|
/// print(m.values()); // prints "[1, 2, 3]""
|
||||||
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[rhai_fn(pure)]
|
#[rhai_fn(pure)]
|
||||||
pub fn values(map: &mut Map) -> Array {
|
pub fn values(map: &mut Map) -> Array {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT};
|
use crate::{def_package, Position, RhaiResultOf, ERR, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -66,8 +66,8 @@ def_package! {
|
|||||||
{
|
{
|
||||||
reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64));
|
reg_functions!(lib += numbers_to_int::to_int(i8, u8, i16, u16, i32, u32, i64, u64));
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
reg_functions!(lib += num_128_to_int::to_int(i128, u128));
|
reg_functions!(lib += num_128_to_int::to_int(i128, u128));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,8 +86,8 @@ def_package! {
|
|||||||
{
|
{
|
||||||
reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32));
|
reg_functions!(lib += numbers_to_float::to_float(i8, u8, i16, u16, i32, u32, i64, u32));
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
reg_functions!(lib += num_128_to_float::to_float(i128, u128));
|
reg_functions!(lib += num_128_to_float::to_float(i128, u128));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,6 +108,34 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod int_functions {
|
mod int_functions {
|
||||||
|
/// Parse a string into an integer number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = parse_int("123");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123
|
||||||
|
/// ```
|
||||||
|
#[rhai_fn(name = "parse_int", return_raw)]
|
||||||
|
pub fn parse_int(string: &str) -> RhaiResultOf<INT> {
|
||||||
|
parse_int_radix(string, 10)
|
||||||
|
}
|
||||||
|
/// Parse a string into an integer number of the specified `radix`.
|
||||||
|
///
|
||||||
|
/// `radix` must be between 2 and 36.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = parse_int("123");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123
|
||||||
|
///
|
||||||
|
/// let y = parse_int("123abc", 16);
|
||||||
|
///
|
||||||
|
/// print(y); // prints 1194684 (0x123abc)
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "parse_int", return_raw)]
|
#[rhai_fn(name = "parse_int", return_raw)]
|
||||||
pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf<INT> {
|
pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf<INT> {
|
||||||
if !(2..=36).contains(&radix) {
|
if !(2..=36).contains(&radix) {
|
||||||
@ -118,9 +146,7 @@ mod int_functions {
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSIGNED_INT::from_str_radix(string.trim(), radix as u32)
|
INT::from_str_radix(string.trim(), radix as u32).map_err(|err| {
|
||||||
.map(|v| v as INT)
|
|
||||||
.map_err(|err| {
|
|
||||||
ERR::ErrorArithmetic(
|
ERR::ErrorArithmetic(
|
||||||
format!("Error parsing integer number '{}': {}", string, err),
|
format!("Error parsing integer number '{}': {}", string, err),
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
@ -128,10 +154,6 @@ mod int_functions {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "parse_int", return_raw)]
|
|
||||||
pub fn parse_int(string: &str) -> RhaiResultOf<INT> {
|
|
||||||
parse_int_radix(string, 10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -139,46 +161,60 @@ mod int_functions {
|
|||||||
mod trig_functions {
|
mod trig_functions {
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
|
|
||||||
|
/// Return the sine of the floating-point number in radians.
|
||||||
pub fn sin(x: FLOAT) -> FLOAT {
|
pub fn sin(x: FLOAT) -> FLOAT {
|
||||||
x.sin()
|
x.sin()
|
||||||
}
|
}
|
||||||
|
/// Return the cosine of the floating-point number in radians.
|
||||||
pub fn cos(x: FLOAT) -> FLOAT {
|
pub fn cos(x: FLOAT) -> FLOAT {
|
||||||
x.cos()
|
x.cos()
|
||||||
}
|
}
|
||||||
|
/// Return the tangent of the floating-point number in radians.
|
||||||
pub fn tan(x: FLOAT) -> FLOAT {
|
pub fn tan(x: FLOAT) -> FLOAT {
|
||||||
x.tan()
|
x.tan()
|
||||||
}
|
}
|
||||||
|
/// Return the hyperbolic sine of the floating-point number in radians.
|
||||||
pub fn sinh(x: FLOAT) -> FLOAT {
|
pub fn sinh(x: FLOAT) -> FLOAT {
|
||||||
x.sinh()
|
x.sinh()
|
||||||
}
|
}
|
||||||
|
/// Return the hyperbolic cosine of the floating-point number in radians.
|
||||||
pub fn cosh(x: FLOAT) -> FLOAT {
|
pub fn cosh(x: FLOAT) -> FLOAT {
|
||||||
x.cosh()
|
x.cosh()
|
||||||
}
|
}
|
||||||
|
/// Return the hyperbolic tangent of the floating-point number in radians.
|
||||||
pub fn tanh(x: FLOAT) -> FLOAT {
|
pub fn tanh(x: FLOAT) -> FLOAT {
|
||||||
x.tanh()
|
x.tanh()
|
||||||
}
|
}
|
||||||
|
/// Return the arc-sine of the floating-point number, in radians.
|
||||||
pub fn asin(x: FLOAT) -> FLOAT {
|
pub fn asin(x: FLOAT) -> FLOAT {
|
||||||
x.asin()
|
x.asin()
|
||||||
}
|
}
|
||||||
|
/// Return the arc-cosine of the floating-point number, in radians.
|
||||||
pub fn acos(x: FLOAT) -> FLOAT {
|
pub fn acos(x: FLOAT) -> FLOAT {
|
||||||
x.acos()
|
x.acos()
|
||||||
}
|
}
|
||||||
|
/// Return the arc-tangent of the floating-point number, in radians.
|
||||||
pub fn atan(x: FLOAT) -> FLOAT {
|
pub fn atan(x: FLOAT) -> FLOAT {
|
||||||
x.atan()
|
x.atan()
|
||||||
}
|
}
|
||||||
|
/// Return the arc-tangent of the floating-point numbers `x` and `y`, in radians.
|
||||||
#[rhai_fn(name = "atan")]
|
#[rhai_fn(name = "atan")]
|
||||||
pub fn atan2(x: FLOAT, y: FLOAT) -> FLOAT {
|
pub fn atan2(x: FLOAT, y: FLOAT) -> FLOAT {
|
||||||
x.atan2(y)
|
x.atan2(y)
|
||||||
}
|
}
|
||||||
|
/// Return the arc-hyperbolic-sine of the floating-point number, in radians.
|
||||||
pub fn asinh(x: FLOAT) -> FLOAT {
|
pub fn asinh(x: FLOAT) -> FLOAT {
|
||||||
x.asinh()
|
x.asinh()
|
||||||
}
|
}
|
||||||
|
/// Return the arc-hyperbolic-cosine of the floating-point number, in radians.
|
||||||
pub fn acosh(x: FLOAT) -> FLOAT {
|
pub fn acosh(x: FLOAT) -> FLOAT {
|
||||||
x.acosh()
|
x.acosh()
|
||||||
}
|
}
|
||||||
|
/// Return the arc-hyperbolic-tangent of the floating-point number, in radians.
|
||||||
pub fn atanh(x: FLOAT) -> FLOAT {
|
pub fn atanh(x: FLOAT) -> FLOAT {
|
||||||
x.atanh()
|
x.atanh()
|
||||||
}
|
}
|
||||||
|
/// Return the hypotenuse of a triangle with sides `x` and `y`.
|
||||||
pub fn hypot(x: FLOAT, y: FLOAT) -> FLOAT {
|
pub fn hypot(x: FLOAT, y: FLOAT) -> FLOAT {
|
||||||
x.hypot(y)
|
x.hypot(y)
|
||||||
}
|
}
|
||||||
@ -189,6 +225,7 @@ mod trig_functions {
|
|||||||
mod float_functions {
|
mod float_functions {
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
|
|
||||||
|
/// Return the natural number _e_.
|
||||||
#[rhai_fn(name = "E")]
|
#[rhai_fn(name = "E")]
|
||||||
pub fn e() -> FLOAT {
|
pub fn e() -> FLOAT {
|
||||||
#[cfg(not(feature = "f32_float"))]
|
#[cfg(not(feature = "f32_float"))]
|
||||||
@ -196,6 +233,7 @@ mod float_functions {
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
return std::f32::consts::E;
|
return std::f32::consts::E;
|
||||||
}
|
}
|
||||||
|
/// Return the number π.
|
||||||
#[rhai_fn(name = "PI")]
|
#[rhai_fn(name = "PI")]
|
||||||
pub fn pi() -> FLOAT {
|
pub fn pi() -> FLOAT {
|
||||||
#[cfg(not(feature = "f32_float"))]
|
#[cfg(not(feature = "f32_float"))]
|
||||||
@ -203,60 +241,77 @@ mod float_functions {
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
return std::f32::consts::PI;
|
return std::f32::consts::PI;
|
||||||
}
|
}
|
||||||
|
/// Convert degrees to radians.
|
||||||
pub fn to_radians(x: FLOAT) -> FLOAT {
|
pub fn to_radians(x: FLOAT) -> FLOAT {
|
||||||
x.to_radians()
|
x.to_radians()
|
||||||
}
|
}
|
||||||
|
/// Convert radians to degrees.
|
||||||
pub fn to_degrees(x: FLOAT) -> FLOAT {
|
pub fn to_degrees(x: FLOAT) -> FLOAT {
|
||||||
x.to_degrees()
|
x.to_degrees()
|
||||||
}
|
}
|
||||||
|
/// Return the square root of the floating-point number.
|
||||||
pub fn sqrt(x: FLOAT) -> FLOAT {
|
pub fn sqrt(x: FLOAT) -> FLOAT {
|
||||||
x.sqrt()
|
x.sqrt()
|
||||||
}
|
}
|
||||||
|
/// Return the exponential of the floating-point number.
|
||||||
pub fn exp(x: FLOAT) -> FLOAT {
|
pub fn exp(x: FLOAT) -> FLOAT {
|
||||||
x.exp()
|
x.exp()
|
||||||
}
|
}
|
||||||
|
/// Return the natural log of the floating-point number.
|
||||||
pub fn ln(x: FLOAT) -> FLOAT {
|
pub fn ln(x: FLOAT) -> FLOAT {
|
||||||
x.ln()
|
x.ln()
|
||||||
}
|
}
|
||||||
|
/// Return the log of the floating-point number with `base`.
|
||||||
pub fn log(x: FLOAT, base: FLOAT) -> FLOAT {
|
pub fn log(x: FLOAT, base: FLOAT) -> FLOAT {
|
||||||
x.log(base)
|
x.log(base)
|
||||||
}
|
}
|
||||||
|
/// Return the log of the floating-point number with base 10.
|
||||||
#[rhai_fn(name = "log")]
|
#[rhai_fn(name = "log")]
|
||||||
pub fn log10(x: FLOAT) -> FLOAT {
|
pub fn log10(x: FLOAT) -> FLOAT {
|
||||||
x.log10()
|
x.log10()
|
||||||
}
|
}
|
||||||
|
/// Return the largest whole number less than or equals to the floating-point number.
|
||||||
#[rhai_fn(name = "floor", get = "floor")]
|
#[rhai_fn(name = "floor", get = "floor")]
|
||||||
pub fn floor(x: FLOAT) -> FLOAT {
|
pub fn floor(x: FLOAT) -> FLOAT {
|
||||||
x.floor()
|
x.floor()
|
||||||
}
|
}
|
||||||
|
/// Return the smallest whole number larger than or equals to the floating-point number.
|
||||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||||
pub fn ceiling(x: FLOAT) -> FLOAT {
|
pub fn ceiling(x: FLOAT) -> FLOAT {
|
||||||
x.ceil()
|
x.ceil()
|
||||||
}
|
}
|
||||||
|
/// Return the nearest whole number closest to the floating-point number.
|
||||||
|
/// Rounds away from zero.
|
||||||
#[rhai_fn(name = "round", get = "round")]
|
#[rhai_fn(name = "round", get = "round")]
|
||||||
pub fn round(x: FLOAT) -> FLOAT {
|
pub fn round(x: FLOAT) -> FLOAT {
|
||||||
x.round()
|
x.round()
|
||||||
}
|
}
|
||||||
|
/// Return the integral part of the floating-point number.
|
||||||
#[rhai_fn(name = "int", get = "int")]
|
#[rhai_fn(name = "int", get = "int")]
|
||||||
pub fn int(x: FLOAT) -> FLOAT {
|
pub fn int(x: FLOAT) -> FLOAT {
|
||||||
x.trunc()
|
x.trunc()
|
||||||
}
|
}
|
||||||
|
/// Return the fractional part of the floating-point number.
|
||||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||||
pub fn fraction(x: FLOAT) -> FLOAT {
|
pub fn fraction(x: FLOAT) -> FLOAT {
|
||||||
x.fract()
|
x.fract()
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the floating-point number is `NaN` (Not A Number).
|
||||||
#[rhai_fn(name = "is_nan", get = "is_nan")]
|
#[rhai_fn(name = "is_nan", get = "is_nan")]
|
||||||
pub fn is_nan(x: FLOAT) -> bool {
|
pub fn is_nan(x: FLOAT) -> bool {
|
||||||
x.is_nan()
|
x.is_nan()
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the floating-point number is finite.
|
||||||
#[rhai_fn(name = "is_finite", get = "is_finite")]
|
#[rhai_fn(name = "is_finite", get = "is_finite")]
|
||||||
pub fn is_finite(x: FLOAT) -> bool {
|
pub fn is_finite(x: FLOAT) -> bool {
|
||||||
x.is_finite()
|
x.is_finite()
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the floating-point number is infinite.
|
||||||
#[rhai_fn(name = "is_infinite", get = "is_infinite")]
|
#[rhai_fn(name = "is_infinite", get = "is_infinite")]
|
||||||
pub fn is_infinite(x: FLOAT) -> bool {
|
pub fn is_infinite(x: FLOAT) -> bool {
|
||||||
x.is_infinite()
|
x.is_infinite()
|
||||||
}
|
}
|
||||||
|
/// Return the integral part of the floating-point number.
|
||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
|
pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f32) {
|
if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f32) {
|
||||||
@ -268,6 +323,7 @@ mod float_functions {
|
|||||||
Ok(x.trunc() as INT)
|
Ok(x.trunc() as INT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return the integral part of the floating-point number.
|
||||||
#[rhai_fn(name = "to_int", return_raw)]
|
#[rhai_fn(name = "to_int", return_raw)]
|
||||||
pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
|
pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
|
||||||
if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f64) {
|
if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f64) {
|
||||||
@ -279,6 +335,15 @@ mod float_functions {
|
|||||||
Ok(x.trunc() as INT)
|
Ok(x.trunc() as INT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Parse a string into a floating-point number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = parse_int("123.456");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123.456
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn parse_float(string: &str) -> RhaiResultOf<FLOAT> {
|
pub fn parse_float(string: &str) -> RhaiResultOf<FLOAT> {
|
||||||
string.trim().parse::<FLOAT>().map_err(|err| {
|
string.trim().parse::<FLOAT>().map_err(|err| {
|
||||||
@ -289,6 +354,7 @@ mod float_functions {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Convert the 32-bit floating-point number to 64-bit.
|
||||||
#[cfg(not(feature = "f32_float"))]
|
#[cfg(not(feature = "f32_float"))]
|
||||||
#[rhai_fn(name = "to_float")]
|
#[rhai_fn(name = "to_float")]
|
||||||
pub fn f32_to_f64(x: f32) -> f64 {
|
pub fn f32_to_f64(x: f32) -> f64 {
|
||||||
@ -306,36 +372,52 @@ mod decimal_functions {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
/// Return the natural number _e_.
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
#[rhai_fn(name = "PI")]
|
#[rhai_fn(name = "PI")]
|
||||||
pub fn pi() -> Decimal {
|
pub fn pi() -> Decimal {
|
||||||
Decimal::PI
|
Decimal::PI
|
||||||
}
|
}
|
||||||
|
/// Return the number π.
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
#[rhai_fn(name = "E")]
|
#[rhai_fn(name = "E")]
|
||||||
pub fn e() -> Decimal {
|
pub fn e() -> Decimal {
|
||||||
Decimal::E
|
Decimal::E
|
||||||
}
|
}
|
||||||
|
/// Parse a string into a decimal number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = parse_float("123.456");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123.456
|
||||||
|
/// ```
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn parse_float(s: &str) -> RhaiResultOf<Decimal> {
|
pub fn parse_float(s: &str) -> RhaiResultOf<Decimal> {
|
||||||
parse_decimal(s)
|
parse_decimal(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the sine of the decimal number in radians.
|
||||||
pub fn sin(x: Decimal) -> Decimal {
|
pub fn sin(x: Decimal) -> Decimal {
|
||||||
x.sin()
|
x.sin()
|
||||||
}
|
}
|
||||||
|
/// Return the cosine of the decimal number in radians.
|
||||||
pub fn cos(x: Decimal) -> Decimal {
|
pub fn cos(x: Decimal) -> Decimal {
|
||||||
x.cos()
|
x.cos()
|
||||||
}
|
}
|
||||||
|
/// Return the tangent of the decimal number in radians.
|
||||||
pub fn tan(x: Decimal) -> Decimal {
|
pub fn tan(x: Decimal) -> Decimal {
|
||||||
x.tan()
|
x.tan()
|
||||||
}
|
}
|
||||||
|
/// Return the square root of the decimal number.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn sqrt(x: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn sqrt(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
x.sqrt()
|
x.sqrt()
|
||||||
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
|
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
|
||||||
}
|
}
|
||||||
|
/// Return the exponential of the decimal number.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn exp(x: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn exp(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
@ -345,6 +427,7 @@ mod decimal_functions {
|
|||||||
Ok(x.exp())
|
Ok(x.exp())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return the natural log of the decimal number.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn ln(x: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn ln(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
@ -354,6 +437,7 @@ mod decimal_functions {
|
|||||||
Ok(x.ln())
|
Ok(x.ln())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return the log of the decimal number with base 10.
|
||||||
#[rhai_fn(name = "log", return_raw)]
|
#[rhai_fn(name = "log", return_raw)]
|
||||||
pub fn log10(x: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn log10(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
@ -363,106 +447,131 @@ mod decimal_functions {
|
|||||||
Ok(x.log10())
|
Ok(x.log10())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return the largest whole number less than or equals to the decimal number.
|
||||||
#[rhai_fn(name = "floor", get = "floor")]
|
#[rhai_fn(name = "floor", get = "floor")]
|
||||||
pub fn floor(x: Decimal) -> Decimal {
|
pub fn floor(x: Decimal) -> Decimal {
|
||||||
x.floor()
|
x.floor()
|
||||||
}
|
}
|
||||||
|
/// Return the smallest whole number larger than or equals to the decimal number.
|
||||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||||
pub fn ceiling(x: Decimal) -> Decimal {
|
pub fn ceiling(x: Decimal) -> Decimal {
|
||||||
x.ceil()
|
x.ceil()
|
||||||
}
|
}
|
||||||
|
/// Return the nearest whole number closest to the decimal number.
|
||||||
|
/// Always round mid-point towards the closest even number.
|
||||||
#[rhai_fn(name = "round", get = "round")]
|
#[rhai_fn(name = "round", get = "round")]
|
||||||
pub fn round(x: Decimal) -> Decimal {
|
pub fn round(x: Decimal) -> Decimal {
|
||||||
x.round()
|
x.round()
|
||||||
}
|
}
|
||||||
|
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||||
|
/// Always round mid-point towards the closest even number.
|
||||||
#[rhai_fn(name = "round", return_raw)]
|
#[rhai_fn(name = "round", return_raw)]
|
||||||
pub fn round_dp(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
pub fn round_dp(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if dp < 0 {
|
if digits < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Invalid number of digits for rounding: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
dp
|
digits
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp(dp as u32))
|
Ok(x.round_dp(digits as u32))
|
||||||
}
|
}
|
||||||
|
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||||
|
/// Always round away from zero.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_up(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
pub fn round_up(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if dp < 0 {
|
if digits < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Invalid number of digits for rounding: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
dp
|
digits
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
|
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::AwayFromZero))
|
||||||
}
|
}
|
||||||
|
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||||
|
/// Always round towards zero.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_down(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
pub fn round_down(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if dp < 0 {
|
if digits < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Invalid number of digits for rounding: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
dp
|
digits
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
|
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::ToZero))
|
||||||
}
|
}
|
||||||
|
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||||
|
/// Always round mid-points away from zero.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_half_up(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
pub fn round_half_up(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if dp < 0 {
|
if digits < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Invalid number of digits for rounding: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
dp
|
digits
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
|
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointAwayFromZero))
|
||||||
}
|
}
|
||||||
|
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||||
|
/// Always round mid-points towards zero.
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn round_half_down(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
pub fn round_half_down(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
if cfg!(not(feature = "unchecked")) {
|
||||||
if dp < 0 {
|
if digits < 0 {
|
||||||
return Err(make_err(format!(
|
return Err(make_err(format!(
|
||||||
"Invalid number of digits for rounding: {}",
|
"Invalid number of digits for rounding: {}",
|
||||||
dp
|
digits
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||||
return Ok(x);
|
return Ok(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointTowardZero))
|
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointTowardZero))
|
||||||
}
|
}
|
||||||
|
/// Return the integral part of the decimal number.
|
||||||
#[rhai_fn(name = "int", get = "int")]
|
#[rhai_fn(name = "int", get = "int")]
|
||||||
pub fn int(x: Decimal) -> Decimal {
|
pub fn int(x: Decimal) -> Decimal {
|
||||||
x.trunc()
|
x.trunc()
|
||||||
}
|
}
|
||||||
|
/// Return the fractional part of the decimal number.
|
||||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||||
pub fn fraction(x: Decimal) -> Decimal {
|
pub fn fraction(x: Decimal) -> Decimal {
|
||||||
x.fract()
|
x.fract()
|
||||||
}
|
}
|
||||||
|
/// Parse a string into a decimal number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = parse_decimal("123.456");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123.456
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn parse_decimal(string: &str) -> RhaiResultOf<Decimal> {
|
pub fn parse_decimal(string: &str) -> RhaiResultOf<Decimal> {
|
||||||
Decimal::from_str(string)
|
Decimal::from_str(string)
|
||||||
@ -476,6 +585,7 @@ mod decimal_functions {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the floating-point number to decimal.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "to_decimal", return_raw)]
|
#[rhai_fn(name = "to_decimal", return_raw)]
|
||||||
pub fn f32_to_decimal(x: f32) -> RhaiResultOf<Decimal> {
|
pub fn f32_to_decimal(x: f32) -> RhaiResultOf<Decimal> {
|
||||||
@ -487,6 +597,7 @@ mod decimal_functions {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Convert the floating-point number to decimal.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "to_decimal", return_raw)]
|
#[rhai_fn(name = "to_decimal", return_raw)]
|
||||||
pub fn f64_to_decimal(x: f64) -> RhaiResultOf<Decimal> {
|
pub fn f64_to_decimal(x: f64) -> RhaiResultOf<Decimal> {
|
||||||
@ -498,6 +609,7 @@ mod decimal_functions {
|
|||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Convert the decimal number to floating-point.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn to_float(x: Decimal) -> RhaiResultOf<FLOAT> {
|
pub fn to_float(x: Decimal) -> RhaiResultOf<FLOAT> {
|
||||||
@ -522,8 +634,8 @@ gen_conversion_as_functions!(numbers_to_float => to_float (i8, u8, i16, u16, i32
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
|
gen_conversion_as_functions!(num_128_to_float => to_float (i128, u128) -> FLOAT);
|
||||||
|
|
||||||
gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT);
|
gen_conversion_as_functions!(basic_to_int => to_int (char) -> INT);
|
||||||
@ -534,8 +646,8 @@ gen_conversion_as_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u3
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
gen_conversion_as_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
|
@ -46,56 +46,68 @@ pub fn print_with_func(
|
|||||||
mod print_debug_functions {
|
mod print_debug_functions {
|
||||||
use crate::ImmutableString;
|
use crate::ImmutableString;
|
||||||
|
|
||||||
|
/// Convert the value of the `item` into a string.
|
||||||
#[rhai_fn(name = "print", pure)]
|
#[rhai_fn(name = "print", pure)]
|
||||||
pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
print_with_func(FUNC_TO_STRING, &ctx, item)
|
print_with_func(FUNC_TO_STRING, &ctx, item)
|
||||||
}
|
}
|
||||||
|
/// Convert the value of the `item` into a string.
|
||||||
#[rhai_fn(name = "to_string", pure)]
|
#[rhai_fn(name = "to_string", pure)]
|
||||||
pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
ctx.engine().map_type_name(&item.to_string()).into()
|
ctx.engine().map_type_name(&item.to_string()).into()
|
||||||
}
|
}
|
||||||
|
/// Convert the value of the `item` into a string in debug format.
|
||||||
#[rhai_fn(name = "debug", pure)]
|
#[rhai_fn(name = "debug", pure)]
|
||||||
pub fn debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
pub fn debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
print_with_func(FUNC_TO_DEBUG, &ctx, item)
|
print_with_func(FUNC_TO_DEBUG, &ctx, item)
|
||||||
}
|
}
|
||||||
|
/// Convert the value of the `item` into a string in debug format.
|
||||||
#[rhai_fn(name = "to_debug", pure)]
|
#[rhai_fn(name = "to_debug", pure)]
|
||||||
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
ctx.engine().map_type_name(&format!("{:?}", item)).into()
|
ctx.engine().map_type_name(&format!("{:?}", item)).into()
|
||||||
}
|
}
|
||||||
|
/// Return the empty string.
|
||||||
#[rhai_fn(name = "print", name = "debug")]
|
#[rhai_fn(name = "print", name = "debug")]
|
||||||
pub fn print_empty_string(ctx: NativeCallContext) -> ImmutableString {
|
pub fn print_empty_string(ctx: NativeCallContext) -> ImmutableString {
|
||||||
ctx.engine().const_empty_string()
|
ctx.engine().const_empty_string()
|
||||||
}
|
}
|
||||||
|
/// Return the `string`.
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_string(s: ImmutableString) -> ImmutableString {
|
pub fn print_string(string: ImmutableString) -> ImmutableString {
|
||||||
s
|
string
|
||||||
}
|
}
|
||||||
|
/// Convert the function pointer into a string in debug format.
|
||||||
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
||||||
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
||||||
f.to_string().into()
|
f.to_string().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_f64(number: f64) -> ImmutableString {
|
pub fn print_f64(number: f64) -> ImmutableString {
|
||||||
crate::ast::FloatWrapper::new(number).to_string().into()
|
crate::ast::FloatWrapper::new(number).to_string().into()
|
||||||
}
|
}
|
||||||
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_f32(number: f32) -> ImmutableString {
|
pub fn print_f32(number: f32) -> ImmutableString {
|
||||||
crate::ast::FloatWrapper::new(number).to_string().into()
|
crate::ast::FloatWrapper::new(number).to_string().into()
|
||||||
}
|
}
|
||||||
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
pub fn debug_f64(number: f64) -> ImmutableString {
|
pub fn debug_f64(number: f64) -> ImmutableString {
|
||||||
format!("{:?}", crate::ast::FloatWrapper::new(number)).into()
|
format!("{:?}", crate::ast::FloatWrapper::new(number)).into()
|
||||||
}
|
}
|
||||||
|
/// Convert the value of `number` into a string.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||||
pub fn debug_f32(number: f32) -> ImmutableString {
|
pub fn debug_f32(number: f32) -> ImmutableString {
|
||||||
format!("{:?}", crate::ast::FloatWrapper::new(number)).into()
|
format!("{:?}", crate::ast::FloatWrapper::new(number)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the array into a string.
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[rhai_fn(
|
#[rhai_fn(
|
||||||
name = "print",
|
name = "print",
|
||||||
@ -119,6 +131,8 @@ mod print_debug_functions {
|
|||||||
result.push(']');
|
result.push(']');
|
||||||
result.into()
|
result.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the object map into a string.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[rhai_fn(
|
#[rhai_fn(
|
||||||
name = "print",
|
name = "print",
|
||||||
@ -148,27 +162,27 @@ mod print_debug_functions {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod number_formatting {
|
mod number_formatting {
|
||||||
#[rhai_fn(skip)]
|
fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
|
||||||
pub fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
|
|
||||||
format!("{:x}", value).into()
|
format!("{:x}", value).into()
|
||||||
}
|
}
|
||||||
#[rhai_fn(skip)]
|
fn to_octal<T: Octal>(value: T) -> ImmutableString {
|
||||||
pub fn to_octal<T: Octal>(value: T) -> ImmutableString {
|
|
||||||
format!("{:o}", value).into()
|
format!("{:o}", value).into()
|
||||||
}
|
}
|
||||||
#[rhai_fn(skip)]
|
fn to_binary<T: Binary>(value: T) -> ImmutableString {
|
||||||
pub fn to_binary<T: Binary>(value: T) -> ImmutableString {
|
|
||||||
format!("{:b}", value).into()
|
format!("{:b}", value).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn int_to_hex(value: INT) -> ImmutableString {
|
pub fn int_to_hex(value: INT) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn int_to_octal(value: INT) -> ImmutableString {
|
pub fn int_to_octal(value: INT) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn int_to_binary(value: INT) -> ImmutableString {
|
pub fn int_to_binary(value: INT) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
@ -177,114 +191,156 @@ mod number_formatting {
|
|||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
pub mod numbers {
|
pub mod numbers {
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn u8_to_hex(value: u8) -> ImmutableString {
|
pub fn u8_to_hex(value: u8) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn u16_to_hex(value: u16) -> ImmutableString {
|
pub fn u16_to_hex(value: u16) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn u32_to_hex(value: u32) -> ImmutableString {
|
pub fn u32_to_hex(value: u32) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn u64_to_hex(value: u64) -> ImmutableString {
|
pub fn u64_to_hex(value: u64) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn i8_to_hex(value: i8) -> ImmutableString {
|
pub fn i8_to_hex(value: i8) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn i16_to_hex(value: i16) -> ImmutableString {
|
pub fn i16_to_hex(value: i16) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn i32_to_hex(value: i32) -> ImmutableString {
|
pub fn i32_to_hex(value: i32) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn i64_to_hex(value: i64) -> ImmutableString {
|
pub fn i64_to_hex(value: i64) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn u8_to_octal(value: u8) -> ImmutableString {
|
pub fn u8_to_octal(value: u8) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn u16_to_octal(value: u16) -> ImmutableString {
|
pub fn u16_to_octal(value: u16) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn u32_to_octal(value: u32) -> ImmutableString {
|
pub fn u32_to_octal(value: u32) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn u64_to_octal(value: u64) -> ImmutableString {
|
pub fn u64_to_octal(value: u64) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn i8_to_octal(value: i8) -> ImmutableString {
|
pub fn i8_to_octal(value: i8) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn i16_to_octal(value: i16) -> ImmutableString {
|
pub fn i16_to_octal(value: i16) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn i32_to_octal(value: i32) -> ImmutableString {
|
pub fn i32_to_octal(value: i32) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn i64_to_octal(value: i64) -> ImmutableString {
|
pub fn i64_to_octal(value: i64) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn u8_to_binary(value: u8) -> ImmutableString {
|
pub fn u8_to_binary(value: u8) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn u16_to_binary(value: u16) -> ImmutableString {
|
pub fn u16_to_binary(value: u16) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn u32_to_binary(value: u32) -> ImmutableString {
|
pub fn u32_to_binary(value: u32) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn u64_to_binary(value: u64) -> ImmutableString {
|
pub fn u64_to_binary(value: u64) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn i8_to_binary(value: i8) -> ImmutableString {
|
pub fn i8_to_binary(value: i8) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn i16_to_binary(value: i16) -> ImmutableString {
|
pub fn i16_to_binary(value: i16) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn i32_to_binary(value: i32) -> ImmutableString {
|
pub fn i32_to_binary(value: i32) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn i64_to_binary(value: i64) -> ImmutableString {
|
pub fn i64_to_binary(value: i64) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
pub mod num_128 {
|
pub mod num_128 {
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
#[rhai_fn(name = "to_hex")]
|
#[rhai_fn(name = "to_hex")]
|
||||||
pub fn u128_to_hex(value: u128) -> ImmutableString {
|
pub fn u128_to_hex(value: u128) -> ImmutableString {
|
||||||
to_hex(value)
|
to_hex(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in hex format.
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn i128_to_hex(value: i128) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn u128_to_octal(value: u128) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
/// Convert the `value` into a string in octal format.
|
||||||
#[rhai_fn(name = "to_octal")]
|
#[rhai_fn(name = "to_octal")]
|
||||||
pub fn i128_to_octal(value: i128) -> ImmutableString {
|
pub fn i128_to_octal(value: i128) -> ImmutableString {
|
||||||
to_octal(value)
|
to_octal(value)
|
||||||
}
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn u128_to_binary(value: u128) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
/// Convert the `value` into a string in binary format.
|
||||||
#[rhai_fn(name = "to_binary")]
|
#[rhai_fn(name = "to_binary")]
|
||||||
pub fn i128_to_binary(value: i128) -> ImmutableString {
|
pub fn i128_to_binary(value: i128) -> ImmutableString {
|
||||||
to_binary(value)
|
to_binary(value)
|
||||||
|
@ -73,6 +73,15 @@ mod string_functions {
|
|||||||
string
|
string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the length of the string, in number of characters.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||||
|
///
|
||||||
|
/// print(text.len); // prints 17
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "len", get = "len")]
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
pub fn len(string: &str) -> INT {
|
pub fn len(string: &str) -> INT {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -81,6 +90,15 @@ mod string_functions {
|
|||||||
string.chars().count() as INT
|
string.chars().count() as INT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Return the length of the string, in number of bytes used to store it in UTF-8 encoding.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||||
|
///
|
||||||
|
/// print(text.bytes); // prints 51
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "bytes", get = "bytes")]
|
#[rhai_fn(name = "bytes", get = "bytes")]
|
||||||
pub fn bytes(string: &str) -> INT {
|
pub fn bytes(string: &str) -> INT {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -89,18 +107,59 @@ mod string_functions {
|
|||||||
string.len() as INT
|
string.len() as INT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Remove all occurrences of a sub-string from the string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.remove("hello");
|
||||||
|
///
|
||||||
|
/// print(text); // prints ", world! , foobar!"
|
||||||
|
/// ```
|
||||||
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
||||||
*string -= sub_string;
|
*string -= sub_string;
|
||||||
}
|
}
|
||||||
|
/// Remove all occurrences of a character from the string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.remove("o");
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hell, wrld! hell, fbar!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "remove")]
|
#[rhai_fn(name = "remove")]
|
||||||
pub fn remove_char(string: &mut ImmutableString, character: char) {
|
pub fn remove_char(string: &mut ImmutableString, character: char) {
|
||||||
*string -= character;
|
*string -= character;
|
||||||
}
|
}
|
||||||
|
/// Clear the string, making it empty.
|
||||||
pub fn clear(string: &mut ImmutableString) {
|
pub fn clear(string: &mut ImmutableString) {
|
||||||
if !string.is_empty() {
|
if !string.is_empty() {
|
||||||
string.make_mut().clear();
|
string.make_mut().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Cut off the string at the specified number of characters.
|
||||||
|
///
|
||||||
|
/// * If `len` ≤ 0, the string is cleared.
|
||||||
|
/// * If `len` ≥ length of string, the string is not truncated.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.truncate(13);
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello, world!"
|
||||||
|
///
|
||||||
|
/// x.truncate(10);
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello, world!"
|
||||||
|
/// ```
|
||||||
pub fn truncate(string: &mut ImmutableString, len: INT) {
|
pub fn truncate(string: &mut ImmutableString, len: INT) {
|
||||||
if len > 0 {
|
if len > 0 {
|
||||||
let chars: StaticVec<_> = string.chars().collect();
|
let chars: StaticVec<_> = string.chars().collect();
|
||||||
@ -111,6 +170,15 @@ mod string_functions {
|
|||||||
string.make_mut().clear();
|
string.make_mut().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Remove whitespace characters from both ends of the string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = " hello ";
|
||||||
|
///
|
||||||
|
/// print(text.trim()); // prints "hello"
|
||||||
|
/// ```
|
||||||
pub fn trim(string: &mut ImmutableString) {
|
pub fn trim(string: &mut ImmutableString) {
|
||||||
let trimmed = string.trim();
|
let trimmed = string.trim();
|
||||||
|
|
||||||
@ -118,6 +186,19 @@ mod string_functions {
|
|||||||
*string = trimmed.to_string().into();
|
*string = trimmed.to_string().into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Remove the last character from the string and return it.
|
||||||
|
///
|
||||||
|
/// If the string is empty, `()` is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.pop()); // prints '!'
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello, world"
|
||||||
|
/// ```
|
||||||
pub fn pop(string: &mut ImmutableString) -> Dynamic {
|
pub fn pop(string: &mut ImmutableString) -> Dynamic {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
Dynamic::UNIT
|
Dynamic::UNIT
|
||||||
@ -128,6 +209,21 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Remove the a specified number of characters from the end of the string and return it as a
|
||||||
|
/// new string.
|
||||||
|
///
|
||||||
|
/// * If `len` ≤ 0, the string is not modified and an empty string is returned.
|
||||||
|
/// * If `len` ≥ length of string, the string is cleared and the entire string returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.pop(4)); // prints "rld!"
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello, wo"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "pop")]
|
#[rhai_fn(name = "pop")]
|
||||||
pub fn pop_string(
|
pub fn pop_string(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -150,6 +246,17 @@ mod string_functions {
|
|||||||
chars.into_iter().rev().collect::<SmartString>().into()
|
chars.into_iter().rev().collect::<SmartString>().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the string to all upper-case and return it as a new string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!"
|
||||||
|
///
|
||||||
|
/// print(text.to_upper()); // prints "HELLO, WORLD!"
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello, world!"
|
||||||
|
/// ```
|
||||||
pub fn to_upper(string: ImmutableString) -> ImmutableString {
|
pub fn to_upper(string: ImmutableString) -> ImmutableString {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
string
|
string
|
||||||
@ -157,11 +264,33 @@ mod string_functions {
|
|||||||
string.to_uppercase().into()
|
string.to_uppercase().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Convert the string to all upper-case.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!"
|
||||||
|
///
|
||||||
|
/// text.make_upper();
|
||||||
|
///
|
||||||
|
/// print(text); // prints "HELLO, WORLD!";
|
||||||
|
/// ```
|
||||||
pub fn make_upper(string: &mut ImmutableString) {
|
pub fn make_upper(string: &mut ImmutableString) {
|
||||||
if !string.is_empty() {
|
if !string.is_empty() {
|
||||||
*string = string.to_uppercase().into();
|
*string = string.to_uppercase().into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Convert the string to all lower-case and return it as a new string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "HELLO, WORLD!"
|
||||||
|
///
|
||||||
|
/// print(text.to_lower()); // prints "hello, world!"
|
||||||
|
///
|
||||||
|
/// print(text); // prints "HELLO, WORLD!"
|
||||||
|
/// ```
|
||||||
pub fn to_lower(string: ImmutableString) -> ImmutableString {
|
pub fn to_lower(string: ImmutableString) -> ImmutableString {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
string
|
string
|
||||||
@ -169,12 +298,34 @@ mod string_functions {
|
|||||||
string.to_lowercase().into()
|
string.to_lowercase().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Convert the string to all lower-case.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "HELLO, WORLD!"
|
||||||
|
///
|
||||||
|
/// text.make_lower();
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello, world!";
|
||||||
|
/// ```
|
||||||
pub fn make_lower(string: &mut ImmutableString) {
|
pub fn make_lower(string: &mut ImmutableString) {
|
||||||
if !string.is_empty() {
|
if !string.is_empty() {
|
||||||
*string = string.to_lowercase().into();
|
*string = string.to_lowercase().into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the character to upper-case and return it as a new character.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let ch = 'a';
|
||||||
|
///
|
||||||
|
/// print(ch.to_upper()); // prints 'A'
|
||||||
|
///
|
||||||
|
/// print(ch); // prints 'a'
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "to_upper")]
|
#[rhai_fn(name = "to_upper")]
|
||||||
pub fn to_upper_char(character: char) -> char {
|
pub fn to_upper_char(character: char) -> char {
|
||||||
let mut stream = character.to_uppercase();
|
let mut stream = character.to_uppercase();
|
||||||
@ -185,10 +336,32 @@ mod string_functions {
|
|||||||
ch
|
ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Convert the character to upper-case.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let ch = 'a';
|
||||||
|
///
|
||||||
|
/// ch.make_upper();
|
||||||
|
///
|
||||||
|
/// print(ch); // prints 'A'
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "make_upper")]
|
#[rhai_fn(name = "make_upper")]
|
||||||
pub fn make_upper_char(character: &mut char) {
|
pub fn make_upper_char(character: &mut char) {
|
||||||
*character = to_upper_char(*character)
|
*character = to_upper_char(*character)
|
||||||
}
|
}
|
||||||
|
/// Convert the character to lower-case and return it as a new character.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let ch = 'A';
|
||||||
|
///
|
||||||
|
/// print(ch.to_lower()); // prints 'a'
|
||||||
|
///
|
||||||
|
/// print(ch); // prints 'A'
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "to_lower")]
|
#[rhai_fn(name = "to_lower")]
|
||||||
pub fn to_lower_char(character: char) -> char {
|
pub fn to_lower_char(character: char) -> char {
|
||||||
let mut stream = character.to_lowercase();
|
let mut stream = character.to_lowercase();
|
||||||
@ -199,11 +372,41 @@ mod string_functions {
|
|||||||
ch
|
ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Convert the character to lower-case.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let ch = 'A';
|
||||||
|
///
|
||||||
|
/// ch.make_lower();
|
||||||
|
///
|
||||||
|
/// print(ch); // prints 'a'
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "make_lower")]
|
#[rhai_fn(name = "make_lower")]
|
||||||
pub fn make_lower_char(character: &mut char) {
|
pub fn make_lower_char(character: &mut char) {
|
||||||
*character = to_lower_char(*character)
|
*character = to_lower_char(*character)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the specified `character` in the string, starting from the specified `start` position,
|
||||||
|
/// and return the first index where it is found.
|
||||||
|
/// If the `character` is not found, `-1` is returned.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||||
|
/// * If `start` ≥ length of string, `-1` is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.index_of('l', 5)); // prints 10 (first index after 5)
|
||||||
|
///
|
||||||
|
/// print(text.index_of('o', -7)); // prints 8
|
||||||
|
///
|
||||||
|
/// print(text.index_of('x', 0)); // prints -1
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -243,6 +446,18 @@ mod string_functions {
|
|||||||
.map(|index| string[0..start + index].chars().count() as INT)
|
.map(|index| string[0..start + index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
|
/// Find the specified `character` in the string and return the first index where it is found.
|
||||||
|
/// If the `character` is not found, `-1` is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.index_of('l')); // prints 2 (first index)
|
||||||
|
///
|
||||||
|
/// print(text.index_of('x')); // prints -1
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char(string: &str, character: char) -> INT {
|
pub fn index_of_char(string: &str, character: char) -> INT {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -254,6 +469,25 @@ mod string_functions {
|
|||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Find the specified sub-string in the string, starting from the specified `start` position,
|
||||||
|
/// and return the first index where it is found.
|
||||||
|
/// If the sub-string is not found, `-1` is returned.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||||
|
/// * If `start` ≥ length of string, `-1` is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// print(text.index_of("ll", 5)); // prints 16 (first index after 5)
|
||||||
|
///
|
||||||
|
/// print(text.index_of("ll", -15)); // prints 16
|
||||||
|
///
|
||||||
|
/// print(text.index_of("xx", 0)); // prints -1
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT {
|
pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -293,6 +527,18 @@ mod string_functions {
|
|||||||
.map(|index| string[0..start + index].chars().count() as INT)
|
.map(|index| string[0..start + index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
|
/// Find the specified `character` in the string and return the first index where it is found.
|
||||||
|
/// If the `character` is not found, `-1` is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// print(text.index_of("ll")); // prints 2 (first index)
|
||||||
|
///
|
||||||
|
/// print(text.index_of("xx:)); // prints -1
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of(string: &str, find_string: &str) -> INT {
|
pub fn index_of(string: &str, find_string: &str) -> INT {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -305,6 +551,15 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy an exclusive range of characters from the string and return it as a new string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.sub_string(3..7)); // prints "lo, "
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "sub_string")]
|
#[rhai_fn(name = "sub_string")]
|
||||||
pub fn sub_string_range(
|
pub fn sub_string_range(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -315,6 +570,15 @@ mod string_functions {
|
|||||||
let end = INT::max(range.end, start);
|
let end = INT::max(range.end, start);
|
||||||
sub_string(ctx, string, start, end - start)
|
sub_string(ctx, string, start, end - start)
|
||||||
}
|
}
|
||||||
|
/// Copy an inclusive range of characters from the string and return it as a new string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.sub_string(3..=7)); // prints "lo, w"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "sub_string")]
|
#[rhai_fn(name = "sub_string")]
|
||||||
pub fn sub_string_inclusive_range(
|
pub fn sub_string_inclusive_range(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -325,6 +589,23 @@ mod string_functions {
|
|||||||
let end = INT::max(*range.end(), start);
|
let end = INT::max(*range.end(), start);
|
||||||
sub_string(ctx, string, start, end - start + 1)
|
sub_string(ctx, string, start, end - start + 1)
|
||||||
}
|
}
|
||||||
|
/// Copy a portion of the string and return it as a new string.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||||
|
/// * If `start` ≥ length of string, an empty string is returned.
|
||||||
|
/// * If `len` ≤ 0, an empty string is returned.
|
||||||
|
/// * If `start` position + `len` ≥ length of string, entire portion of the string after the `start` position is copied and returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.sub_string(3, 4)); // prints "lo, "
|
||||||
|
///
|
||||||
|
/// print(text.sub_string(-8, 3)); // prints ", w"
|
||||||
|
/// ```
|
||||||
pub fn sub_string(
|
pub fn sub_string(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
string: &str,
|
string: &str,
|
||||||
@ -374,6 +655,22 @@ mod string_functions {
|
|||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
/// Copy a portion of the string beginning at the `start` position till the end and return it as
|
||||||
|
/// a new string.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `start` < -length of string, the entire string is copied and returned.
|
||||||
|
/// * If `start` ≥ length of string, an empty string is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.sub_string(5)); // prints ", world!"
|
||||||
|
///
|
||||||
|
/// print(text.sub_string(-5)); // prints "orld!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "sub_string")]
|
#[rhai_fn(name = "sub_string")]
|
||||||
pub fn sub_string_starting_from(
|
pub fn sub_string_starting_from(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -388,18 +685,62 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove all characters from the string except those within an exclusive range.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// text.crop(2..8);
|
||||||
|
///
|
||||||
|
/// print(text); // prints "llo, w"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "crop")]
|
#[rhai_fn(name = "crop")]
|
||||||
pub fn crop_range(string: &mut ImmutableString, range: ExclusiveRange) {
|
pub fn crop_range(string: &mut ImmutableString, range: ExclusiveRange) {
|
||||||
let start = INT::max(range.start, 0);
|
let start = INT::max(range.start, 0);
|
||||||
let end = INT::max(range.end, start);
|
let end = INT::max(range.end, start);
|
||||||
crop(string, start, end - start)
|
crop(string, start, end - start)
|
||||||
}
|
}
|
||||||
|
/// Remove all characters from the string except those within an inclusive range.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// text.crop(2..=8);
|
||||||
|
///
|
||||||
|
/// print(text); // prints "llo, wo"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "crop")]
|
#[rhai_fn(name = "crop")]
|
||||||
pub fn crop_inclusive_range(string: &mut ImmutableString, range: InclusiveRange) {
|
pub fn crop_inclusive_range(string: &mut ImmutableString, range: InclusiveRange) {
|
||||||
let start = INT::max(*range.start(), 0);
|
let start = INT::max(*range.start(), 0);
|
||||||
let end = INT::max(*range.end(), start);
|
let end = INT::max(*range.end(), start);
|
||||||
crop(string, start, end - start + 1)
|
crop(string, start, end - start + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove all characters from the string except those within a range.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||||
|
/// * If `start` ≥ length of string, the entire string is cleared.
|
||||||
|
/// * If `len` ≤ 0, the entire string is cleared.
|
||||||
|
/// * If `start` position + `len` ≥ length of string, only the portion of the string after the `start` position is retained.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// text.crop(2, 8);
|
||||||
|
///
|
||||||
|
/// print(text); // prints "llo, wor"
|
||||||
|
///
|
||||||
|
/// text.crop(-5, 3);
|
||||||
|
///
|
||||||
|
/// print(text); // prints ", w"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "crop")]
|
#[rhai_fn(name = "crop")]
|
||||||
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
|
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -443,17 +784,58 @@ mod string_functions {
|
|||||||
copy.clear();
|
copy.clear();
|
||||||
copy.extend(chars.iter().skip(offset).take(len));
|
copy.extend(chars.iter().skip(offset).take(len));
|
||||||
}
|
}
|
||||||
|
/// Remove all characters from the string except until the `start` position.
|
||||||
|
///
|
||||||
|
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `start` < -length of string, the string is not modified.
|
||||||
|
/// * If `start` ≥ length of string, the entire string is cleared.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// text.crop(5);
|
||||||
|
///
|
||||||
|
/// print(text); // prints ", world!"
|
||||||
|
///
|
||||||
|
/// text.crop(-3);
|
||||||
|
///
|
||||||
|
/// print(text); // prints "ld!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "crop")]
|
#[rhai_fn(name = "crop")]
|
||||||
pub fn crop_string_starting_from(string: &mut ImmutableString, start: INT) {
|
pub fn crop_string_starting_from(string: &mut ImmutableString, start: INT) {
|
||||||
crop(string, start, string.len() as INT);
|
crop(string, start, string.len() as INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace all occurrences of the specified sub-string in the string with another string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.replace("hello", "hey");
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hey, world! hey, foobar!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) {
|
pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) {
|
||||||
if !string.is_empty() {
|
if !string.is_empty() {
|
||||||
*string = string.replace(find_string, substitute_string).into();
|
*string = string.replace(find_string, substitute_string).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Replace all occurrences of the specified sub-string in the string with the specified character.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.replace("hello", '*');
|
||||||
|
///
|
||||||
|
/// print(text); // prints "*, world! *, foobar!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
pub fn replace_string_with_char(
|
pub fn replace_string_with_char(
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
@ -466,6 +848,17 @@ mod string_functions {
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Replace all occurrences of the specified character in the string with another string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.replace('l', "(^)");
|
||||||
|
///
|
||||||
|
/// print(text); // prints "he(^)(^)o, wor(^)d! he(^)(^)o, foobar!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
pub fn replace_char_with_string(
|
pub fn replace_char_with_string(
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
@ -478,6 +871,17 @@ mod string_functions {
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Replace all occurrences of the specified character in the string with another character.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foobar!";
|
||||||
|
///
|
||||||
|
/// text.replace("l", '*');
|
||||||
|
///
|
||||||
|
/// print(text); // prints "he**o, wor*d! he**o, foobar!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "replace")]
|
#[rhai_fn(name = "replace")]
|
||||||
pub fn replace_char(
|
pub fn replace_char(
|
||||||
string: &mut ImmutableString,
|
string: &mut ImmutableString,
|
||||||
@ -494,6 +898,23 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pad the string to at least the specified number of characters with the specified `character`.
|
||||||
|
///
|
||||||
|
/// If `len` ≤ length of string, no padding is done.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello";
|
||||||
|
///
|
||||||
|
/// text.pad(8, '!');
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello!!!"
|
||||||
|
///
|
||||||
|
/// text.pad(5, '*');
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello!!!"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn pad(
|
pub fn pad(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -538,6 +959,23 @@ mod string_functions {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/// Pad the string to at least the specified number of characters with the specified string.
|
||||||
|
///
|
||||||
|
/// If `len` ≤ length of string, no padding is done.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello";
|
||||||
|
///
|
||||||
|
/// text.pad(10, "(!)");
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello(!)(!)"
|
||||||
|
///
|
||||||
|
/// text.pad(8, '***');
|
||||||
|
///
|
||||||
|
/// print(text); // prints "hello(!)(!)"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "pad", return_raw)]
|
#[rhai_fn(name = "pad", return_raw)]
|
||||||
pub fn pad_with_string(
|
pub fn pad_with_string(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -594,6 +1032,14 @@ mod string_functions {
|
|||||||
pub mod arrays {
|
pub mod arrays {
|
||||||
use crate::{Array, ImmutableString};
|
use crate::{Array, ImmutableString};
|
||||||
|
|
||||||
|
/// Return an array containing all the characters of the string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello";
|
||||||
|
///
|
||||||
|
/// print(text.split()); // prints "['h', 'e', 'l', 'l', 'o']"
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn chars(string: &str) -> Array {
|
pub fn chars(string: &str) -> Array {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
@ -602,10 +1048,32 @@ mod string_functions {
|
|||||||
string.chars().map(Into::into).collect()
|
string.chars().map(Into::into).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Split the string into two at the specified `index` position and return it both strings
|
||||||
|
/// as an array.
|
||||||
|
///
|
||||||
|
/// The character at the `index` position (if any) is returned in the _second_ string.
|
||||||
|
///
|
||||||
|
/// * If `index` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
|
/// * If `index` < -length of string, it is equivalent to cutting at position 0.
|
||||||
|
/// * If `index` ≥ length of string, it is equivalent to cutting at the end of the string.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(text.split(6)); // prints ["hello,", " world!"]
|
||||||
|
///
|
||||||
|
/// print(text.split(13)); // prints ["hello, world!", ""]
|
||||||
|
///
|
||||||
|
/// print(text.split(-6)); // prints ["hello, ", "world!"]
|
||||||
|
///
|
||||||
|
/// print(text.split(-99)); // prints ["", "hello, world!"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn split_at(ctx: NativeCallContext, string: ImmutableString, start: INT) -> Array {
|
pub fn split_at(ctx: NativeCallContext, string: ImmutableString, index: INT) -> Array {
|
||||||
if start <= 0 {
|
if index <= 0 {
|
||||||
if let Some(n) = start.checked_abs() {
|
if let Some(n) = index.checked_abs() {
|
||||||
let num_chars = string.chars().count();
|
let num_chars = string.chars().count();
|
||||||
if n as usize > num_chars {
|
if n as usize > num_chars {
|
||||||
vec![ctx.engine().const_empty_string().into(), string.into()]
|
vec![ctx.engine().const_empty_string().into(), string.into()]
|
||||||
@ -618,41 +1086,127 @@ mod string_functions {
|
|||||||
vec![ctx.engine().const_empty_string().into(), string.into()]
|
vec![ctx.engine().const_empty_string().into(), string.into()]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let prefix: String = string.chars().take(start as usize).collect();
|
let prefix: String = string.chars().take(index as usize).collect();
|
||||||
let prefix_len = prefix.len();
|
let prefix_len = prefix.len();
|
||||||
vec![prefix.into(), string[prefix_len..].into()]
|
vec![prefix.into(), string[prefix_len..].into()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Split the string into segments based on a `delimiter` string, returning an array of the segments.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split("ll")); // prints ["he", "o, world! he", "o, foo!"]
|
||||||
|
/// ```
|
||||||
pub fn split(string: &str, delimiter: &str) -> Array {
|
pub fn split(string: &str, delimiter: &str) -> Array {
|
||||||
string.split(delimiter).map(Into::into).collect()
|
string.split(delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into at most the specified number of `segments` based on a `delimiter` string,
|
||||||
|
/// returning an array of the segments.
|
||||||
|
///
|
||||||
|
/// If `segments` < 1, only one segment is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split("ll", 2)); // prints ["he", "o, world! hello, foo!"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn splitn(string: &str, delimiter: &str, segments: INT) -> Array {
|
pub fn splitn(string: &str, delimiter: &str, segments: INT) -> Array {
|
||||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||||
string.splitn(pieces, delimiter).map(Into::into).collect()
|
string.splitn(pieces, delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into segments based on a `delimiter` character, returning an array of the segments.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split('l')); // prints ["he", "", "o, wor", "d! he", "", "o, foo!"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn split_char(string: &str, delimiter: char) -> Array {
|
pub fn split_char(string: &str, delimiter: char) -> Array {
|
||||||
string.split(delimiter).map(Into::into).collect()
|
string.split(delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into at most the specified number of `segments` based on a `delimiter` character,
|
||||||
|
/// returning an array of the segments.
|
||||||
|
///
|
||||||
|
/// If `segments` < 1, only one segment is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split('l', 3)); // prints ["he", "", "o, world! hello, foo!"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn splitn_char(string: &str, delimiter: char, segments: INT) -> Array {
|
pub fn splitn_char(string: &str, delimiter: char, segments: INT) -> Array {
|
||||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||||
string.splitn(pieces, delimiter).map(Into::into).collect()
|
string.splitn(pieces, delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into segments based on a `delimiter` string, returning an array of the
|
||||||
|
/// segments in _reverse_ order.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split_rev("ll")); // prints ["o, foo!", "o, world! he", "he"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split_rev")]
|
#[rhai_fn(name = "split_rev")]
|
||||||
pub fn rsplit(string: &str, delimiter: &str) -> Array {
|
pub fn rsplit(string: &str, delimiter: &str) -> Array {
|
||||||
string.rsplit(delimiter).map(Into::into).collect()
|
string.rsplit(delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into at most a specified number of `segments` based on a `delimiter` string,
|
||||||
|
/// returning an array of the segments in _reverse_ order.
|
||||||
|
///
|
||||||
|
/// If `segments` < 1, only one segment is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split_rev("ll", 2)); // prints ["o, foo!", "hello, world! he"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split_rev")]
|
#[rhai_fn(name = "split_rev")]
|
||||||
pub fn rsplitn(string: &str, delimiter: &str, segments: INT) -> Array {
|
pub fn rsplitn(string: &str, delimiter: &str, segments: INT) -> Array {
|
||||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||||
string.rsplitn(pieces, delimiter).map(Into::into).collect()
|
string.rsplitn(pieces, delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into segments based on a `delimiter` character, returning an array of
|
||||||
|
/// the segments in _reverse_ order.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split_rev('l')); // prints ["o, foo!", "", "d! he", "o, wor", "", "he"]
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split_rev")]
|
#[rhai_fn(name = "split_rev")]
|
||||||
pub fn rsplit_char(string: &str, delimiter: char) -> Array {
|
pub fn rsplit_char(string: &str, delimiter: char) -> Array {
|
||||||
string.rsplit(delimiter).map(Into::into).collect()
|
string.rsplit(delimiter).map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
/// Split the string into at most the specified number of `segments` based on a `delimiter` character,
|
||||||
|
/// returning an array of the segments.
|
||||||
|
///
|
||||||
|
/// If `segments` < 1, only one segment is returned.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "hello, world! hello, foo!";
|
||||||
|
///
|
||||||
|
/// print(text.split('l', 3)); // prints ["o, foo!", "", "hello, world! he"
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "split_rev")]
|
#[rhai_fn(name = "split_rev")]
|
||||||
pub fn rsplitn_char(string: &str, delimiter: char, segments: INT) -> Array {
|
pub fn rsplitn_char(string: &str, delimiter: char, segments: INT) -> Array {
|
||||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||||
|
@ -7,11 +7,10 @@ use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT};
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
|
#[cfg(target_family = "wasm")]
|
||||||
use instant::{Duration, Instant};
|
use instant::{Duration, Instant};
|
||||||
|
|
||||||
def_package! {
|
def_package! {
|
||||||
@ -26,10 +25,12 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod time_functions {
|
mod time_functions {
|
||||||
|
/// Create a timestamp containing the current system time.
|
||||||
pub fn timestamp() -> Instant {
|
pub fn timestamp() -> Instant {
|
||||||
Instant::now()
|
Instant::now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the number of seconds between the current system time and the timestamp.
|
||||||
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
|
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
|
||||||
pub fn elapsed(timestamp: Instant) -> RhaiResult {
|
pub fn elapsed(timestamp: Instant) -> RhaiResult {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -56,6 +57,7 @@ mod time_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the number of seconds between two timestamps.
|
||||||
#[rhai_fn(return_raw, name = "-")]
|
#[rhai_fn(return_raw, name = "-")]
|
||||||
pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult {
|
pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -141,19 +143,23 @@ mod time_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the specified number of `seconds` to the timestamp and return it as a new timestamp.
|
||||||
#[rhai_fn(return_raw, name = "+")]
|
#[rhai_fn(return_raw, name = "+")]
|
||||||
pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
|
pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
|
||||||
add_impl(timestamp, seconds)
|
add_impl(timestamp, seconds)
|
||||||
}
|
}
|
||||||
|
/// Add the specified number of `seconds` to the timestamp.
|
||||||
#[rhai_fn(return_raw, name = "+=")]
|
#[rhai_fn(return_raw, name = "+=")]
|
||||||
pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
|
pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
|
||||||
*timestamp = add_impl(*timestamp, seconds)?;
|
*timestamp = add_impl(*timestamp, seconds)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/// Subtract the specified number of `seconds` from the timestamp and return it as a new timestamp.
|
||||||
#[rhai_fn(return_raw, name = "-")]
|
#[rhai_fn(return_raw, name = "-")]
|
||||||
pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
|
pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
|
||||||
subtract_impl(timestamp, seconds)
|
subtract_impl(timestamp, seconds)
|
||||||
}
|
}
|
||||||
|
/// Subtract the specified number of `seconds` from the timestamp.
|
||||||
#[rhai_fn(return_raw, name = "-=")]
|
#[rhai_fn(return_raw, name = "-=")]
|
||||||
pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
|
pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
|
||||||
*timestamp = subtract_impl(*timestamp, seconds)?;
|
*timestamp = subtract_impl(*timestamp, seconds)?;
|
||||||
@ -194,45 +200,55 @@ mod time_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the specified number of `seconds` to the timestamp and return it as a new timestamp.
|
||||||
#[rhai_fn(return_raw, name = "+")]
|
#[rhai_fn(return_raw, name = "+")]
|
||||||
pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
|
pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
|
||||||
add_impl(timestamp, seconds)
|
add_impl(timestamp, seconds)
|
||||||
}
|
}
|
||||||
|
/// Add the specified number of `seconds` to the timestamp.
|
||||||
#[rhai_fn(return_raw, name = "+=")]
|
#[rhai_fn(return_raw, name = "+=")]
|
||||||
pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
|
pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
|
||||||
*timestamp = add_impl(*timestamp, seconds)?;
|
*timestamp = add_impl(*timestamp, seconds)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/// Subtract the specified number of `seconds` from the timestamp and return it as a new timestamp.
|
||||||
#[rhai_fn(return_raw, name = "-")]
|
#[rhai_fn(return_raw, name = "-")]
|
||||||
pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
|
pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
|
||||||
subtract_impl(timestamp, seconds)
|
subtract_impl(timestamp, seconds)
|
||||||
}
|
}
|
||||||
|
/// Subtract the specified number of `seconds` from the timestamp.
|
||||||
#[rhai_fn(return_raw, name = "-=")]
|
#[rhai_fn(return_raw, name = "-=")]
|
||||||
pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
|
pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
|
||||||
*timestamp = subtract_impl(*timestamp, seconds)?;
|
*timestamp = subtract_impl(*timestamp, seconds)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if two timestamps are equal.
|
||||||
#[rhai_fn(name = "==")]
|
#[rhai_fn(name = "==")]
|
||||||
pub fn eq(timestamp1: Instant, timestamp2: Instant) -> bool {
|
pub fn eq(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||||
timestamp1 == timestamp2
|
timestamp1 == timestamp2
|
||||||
}
|
}
|
||||||
|
/// Return `true` if two timestamps are not equal.
|
||||||
#[rhai_fn(name = "!=")]
|
#[rhai_fn(name = "!=")]
|
||||||
pub fn ne(timestamp1: Instant, timestamp2: Instant) -> bool {
|
pub fn ne(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||||
timestamp1 != timestamp2
|
timestamp1 != timestamp2
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the first timestamp is earlier than the second.
|
||||||
#[rhai_fn(name = "<")]
|
#[rhai_fn(name = "<")]
|
||||||
pub fn lt(timestamp1: Instant, timestamp2: Instant) -> bool {
|
pub fn lt(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||||
timestamp1 < timestamp2
|
timestamp1 < timestamp2
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the first timestamp is earlier than or equals to the second.
|
||||||
#[rhai_fn(name = "<=")]
|
#[rhai_fn(name = "<=")]
|
||||||
pub fn lte(timestamp1: Instant, timestamp2: Instant) -> bool {
|
pub fn lte(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||||
timestamp1 <= timestamp2
|
timestamp1 <= timestamp2
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the first timestamp is later than the second.
|
||||||
#[rhai_fn(name = ">")]
|
#[rhai_fn(name = ">")]
|
||||||
pub fn gt(timestamp1: Instant, timestamp2: Instant) -> bool {
|
pub fn gt(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||||
timestamp1 > timestamp2
|
timestamp1 > timestamp2
|
||||||
}
|
}
|
||||||
|
/// Return `true` if the first timestamp is later than or equals to the second.
|
||||||
#[rhai_fn(name = ">=")]
|
#[rhai_fn(name = ">=")]
|
||||||
pub fn gte(timestamp1: Instant, timestamp2: Instant) -> bool {
|
pub fn gte(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||||
timestamp1 >= timestamp2
|
timestamp1 >= timestamp2
|
||||||
|
@ -242,6 +242,22 @@ impl ParseSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make an anonymous function.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn make_anonymous_fn(hash: u64) -> String {
|
||||||
|
format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this function an anonymous function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_anonymous_fn(fn_name: &str) -> bool {
|
||||||
|
fn_name.starts_with(crate::engine::FN_ANONYMOUS)
|
||||||
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Convert a [`Variable`][Expr::Variable] into a [`Property`][Expr::Property].
|
/// Convert a [`Variable`][Expr::Variable] into a [`Property`][Expr::Property].
|
||||||
/// All other variants are untouched.
|
/// All other variants are untouched.
|
||||||
@ -3253,7 +3269,7 @@ fn parse_anon_fn(
|
|||||||
params.iter().for_each(|p| p.hash(hasher));
|
params.iter().for_each(|p| p.hash(hasher));
|
||||||
body.hash(hasher);
|
body.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
let fn_name = state.get_identifier("", format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash));
|
let fn_name = state.get_identifier("", make_anonymous_fn(hash));
|
||||||
|
|
||||||
// Define the function
|
// Define the function
|
||||||
let script = ScriptFnDef {
|
let script = ScriptFnDef {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#![cfg(feature = "metadata")]
|
#![cfg(feature = "metadata")]
|
||||||
|
|
||||||
use crate::module::calc_native_fn_hash;
|
use crate::module::{calc_native_fn_hash, FuncInfo};
|
||||||
use crate::{calc_fn_hash, Engine, AST};
|
use crate::{calc_fn_hash, Engine, AST};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -48,34 +48,15 @@ impl From<crate::FnAccess> for FnAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct FnParam<'a> {
|
struct FnParam<'a> {
|
||||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub name: Option<&'a str>,
|
pub name: Option<&'a str>,
|
||||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||||
pub typ: Option<&'a str>,
|
pub typ: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for FnParam<'_> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(match self.name.partial_cmp(&other.name).expect("succeed") {
|
|
||||||
Ordering::Less => Ordering::Less,
|
|
||||||
Ordering::Greater => Ordering::Greater,
|
|
||||||
Ordering::Equal => self.typ.partial_cmp(&other.typ).expect("succeed"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for FnParam<'_> {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
match self.name.cmp(&other.name) {
|
|
||||||
Ordering::Equal => self.typ.cmp(&other.typ),
|
|
||||||
cmp => cmp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct FnMetadata<'a> {
|
struct FnMetadata<'a> {
|
||||||
@ -89,15 +70,18 @@ struct FnMetadata<'a> {
|
|||||||
pub num_params: usize,
|
pub num_params: usize,
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub params: Vec<FnParam<'a>>,
|
pub params: Vec<FnParam<'a>>,
|
||||||
|
// No idea why the following is needed otherwise serde comes back with a lifetime error
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub return_type_name: Option<&'a str>,
|
pub _dummy: Option<&'a str>,
|
||||||
|
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||||
|
pub return_type: String,
|
||||||
pub signature: String,
|
pub signature: String,
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub doc_comments: Vec<&'a str>,
|
pub doc_comments: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for FnMetadata<'_> {
|
impl PartialOrd for FnMetadata<'_> {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,41 +89,39 @@ impl PartialOrd for FnMetadata<'_> {
|
|||||||
impl Ord for FnMetadata<'_> {
|
impl Ord for FnMetadata<'_> {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
match self.name.cmp(&other.name) {
|
match self.name.cmp(&other.name) {
|
||||||
Ordering::Equal => match self.num_params.cmp(&other.num_params) {
|
Ordering::Equal => self.num_params.cmp(&other.num_params),
|
||||||
Ordering::Equal => self.params.cmp(&other.params),
|
|
||||||
cmp => cmp,
|
|
||||||
},
|
|
||||||
cmp => cmp,
|
cmp => cmp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> {
|
impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
||||||
fn from(info: &'a crate::module::FuncInfo) -> Self {
|
fn from(info: &'a FuncInfo) -> Self {
|
||||||
let base_hash = calc_fn_hash(&info.name, info.params);
|
let base_hash = calc_fn_hash(&info.metadata.name, info.metadata.params);
|
||||||
let (typ, full_hash) = if info.func.is_script() {
|
let (typ, full_hash) = if info.func.is_script() {
|
||||||
(FnType::Script, base_hash)
|
(FnType::Script, base_hash)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
FnType::Native,
|
FnType::Native,
|
||||||
calc_native_fn_hash(empty::<&str>(), &info.name, &info.param_types),
|
calc_native_fn_hash(empty::<&str>(), &info.metadata.name, &info.param_types),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
base_hash,
|
base_hash,
|
||||||
full_hash,
|
full_hash,
|
||||||
namespace: info.namespace.into(),
|
namespace: info.metadata.namespace.into(),
|
||||||
access: info.access.into(),
|
access: info.metadata.access.into(),
|
||||||
name: info.name.to_string(),
|
name: info.metadata.name.to_string(),
|
||||||
typ,
|
typ,
|
||||||
num_params: info.params,
|
num_params: info.metadata.params,
|
||||||
params: info
|
params: info
|
||||||
.param_names_and_types
|
.metadata
|
||||||
|
.params_info
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
let mut seg = s.splitn(2, ':');
|
let mut seg = s.splitn(2, ':');
|
||||||
let name = match seg.next().map(&str::trim).unwrap_or("_") {
|
let name = match seg.next().unwrap().trim() {
|
||||||
"_" => None,
|
"_" => None,
|
||||||
s => Some(s),
|
s => Some(s),
|
||||||
};
|
};
|
||||||
@ -147,10 +129,8 @@ impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> {
|
|||||||
FnParam { name, typ }
|
FnParam { name, typ }
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
return_type_name: match info.return_type_name.as_str() {
|
_dummy: None,
|
||||||
"" | "()" => None,
|
return_type: FuncInfo::format_return_type(&info.metadata.return_type).into_owned(),
|
||||||
ty => Some(ty),
|
|
||||||
},
|
|
||||||
signature: info.gen_signature(),
|
signature: info.gen_signature(),
|
||||||
doc_comments: if info.func.is_script() {
|
doc_comments: if info.func.is_script() {
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
@ -164,7 +144,8 @@ impl<'a> From<&'a crate::module::FuncInfo> for FnMetadata<'a> {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
|
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
|
||||||
} else {
|
} else {
|
||||||
info.comments
|
info.metadata
|
||||||
|
.comments
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
|
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
|
||||||
},
|
},
|
||||||
|
16
src/tests.rs
16
src/tests.rs
@ -10,7 +10,7 @@ fn check_struct_sizes() {
|
|||||||
const PACKED: bool = cfg!(all(
|
const PACKED: bool = cfg!(all(
|
||||||
target_pointer_width = "32",
|
target_pointer_width = "32",
|
||||||
feature = "only_i32",
|
feature = "only_i32",
|
||||||
feature = "no_float"
|
any(feature = "no_float", feature = "f32_float")
|
||||||
));
|
));
|
||||||
|
|
||||||
assert_eq!(size_of::<Dynamic>(), if PACKED { 8 } else { 16 });
|
assert_eq!(size_of::<Dynamic>(), if PACKED { 8 } else { 16 });
|
||||||
@ -19,12 +19,15 @@ fn check_struct_sizes() {
|
|||||||
size_of::<Position>(),
|
size_of::<Position>(),
|
||||||
if cfg!(feature = "no_position") { 0 } else { 4 }
|
if cfg!(feature = "no_position") { 0 } else { 4 }
|
||||||
);
|
);
|
||||||
assert_eq!(size_of::<ast::Expr>(), 16);
|
assert_eq!(size_of::<ast::Expr>(), if PACKED { 12 } else { 16 });
|
||||||
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
assert_eq!(size_of::<Option<ast::Expr>>(), if PACKED { 12 } else { 16 });
|
||||||
assert_eq!(size_of::<ast::Stmt>(), 32);
|
assert_eq!(size_of::<ast::Stmt>(), if PACKED { 24 } else { 32 });
|
||||||
assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
|
assert_eq!(size_of::<Option<ast::Stmt>>(), if PACKED { 24 } else { 32 });
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
{
|
||||||
|
assert_eq!(size_of::<Scope>(), 400);
|
||||||
assert_eq!(size_of::<FnPtr>(), 80);
|
assert_eq!(size_of::<FnPtr>(), 80);
|
||||||
assert_eq!(size_of::<Scope>(), 464);
|
|
||||||
assert_eq!(size_of::<LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<ParseError>(),
|
size_of::<ParseError>(),
|
||||||
@ -40,3 +43,4 @@ fn check_struct_sizes() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -15,12 +15,11 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
|
#[cfg(target_family = "wasm")]
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
|
|
||||||
/// The message: data type was checked
|
/// The message: data type was checked
|
||||||
@ -525,8 +524,7 @@ impl Hash for Dynamic {
|
|||||||
value_any.downcast_ref::<i64>().expect(CHECKED).hash(state);
|
value_any.downcast_ref::<i64>().expect(CHECKED).hash(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
if type_id == TypeId::of::<u128>() {
|
if type_id == TypeId::of::<u128>() {
|
||||||
TypeId::of::<u128>().hash(state);
|
TypeId::of::<u128>().hash(state);
|
||||||
value_any.downcast_ref::<u128>().expect(CHECKED).hash(state);
|
value_any.downcast_ref::<u128>().expect(CHECKED).hash(state);
|
||||||
@ -650,8 +648,7 @@ impl fmt::Display for Dynamic {
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
if _type_id == TypeId::of::<u128>() {
|
if _type_id == TypeId::of::<u128>() {
|
||||||
return fmt::Display::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
|
return fmt::Display::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i128>() {
|
} else if _type_id == TypeId::of::<i128>() {
|
||||||
@ -756,8 +753,7 @@ impl fmt::Debug for Dynamic {
|
|||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
#[cfg(not(target_arch = "wasm64"))]
|
|
||||||
if _type_id == TypeId::of::<u128>() {
|
if _type_id == TypeId::of::<u128>() {
|
||||||
return fmt::Debug::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
|
return fmt::Debug::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
|
||||||
} else if _type_id == TypeId::of::<i128>() {
|
} else if _type_id == TypeId::of::<i128>() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Module containing error definitions for the evaluation process.
|
//! Module containing error definitions for the evaluation process.
|
||||||
|
|
||||||
use crate::{Dynamic, ImmutableString, ParseErrorType, Position, RhaiError, INT};
|
use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use core_error::Error;
|
use core_error::Error;
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -34,22 +34,28 @@ pub enum EvalAltResult {
|
|||||||
ErrorVariableNotFound(String, Position),
|
ErrorVariableNotFound(String, Position),
|
||||||
/// Call to an unknown function. Wrapped value is the function signature.
|
/// Call to an unknown function. Wrapped value is the function signature.
|
||||||
ErrorFunctionNotFound(String, Position),
|
ErrorFunctionNotFound(String, Position),
|
||||||
/// An error has occurred inside a called function.
|
|
||||||
/// Wrapped values are the function name, function source, and the interior error.
|
|
||||||
ErrorInFunctionCall(String, String, RhaiError, Position),
|
|
||||||
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
|
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
|
||||||
ErrorModuleNotFound(String, Position),
|
ErrorModuleNotFound(String, Position),
|
||||||
|
|
||||||
|
/// An error has occurred inside a called function.
|
||||||
|
/// Wrapped values are the function name, function source, and the interior error.
|
||||||
|
ErrorInFunctionCall(String, String, Box<Self>, Position),
|
||||||
/// An error has occurred while loading a [module][crate::Module].
|
/// An error has occurred while loading a [module][crate::Module].
|
||||||
/// Wrapped value are the [module][crate::Module] name and the interior error.
|
/// Wrapped value are the [module][crate::Module] name and the interior error.
|
||||||
ErrorInModule(String, RhaiError, Position),
|
ErrorInModule(String, Box<Self>, Position),
|
||||||
|
|
||||||
/// Access to `this` that is not bound.
|
/// Access to `this` that is not bound.
|
||||||
ErrorUnboundThis(Position),
|
ErrorUnboundThis(Position),
|
||||||
|
|
||||||
/// Data is not of the required type.
|
/// Data is not of the required type.
|
||||||
/// Wrapped values are the type requested and type of the actual result.
|
/// Wrapped values are the type requested and type of the actual result.
|
||||||
ErrorMismatchDataType(String, String, Position),
|
ErrorMismatchDataType(String, String, Position),
|
||||||
/// Returned type is not the same as the required output type.
|
/// Returned type is not the same as the required output type.
|
||||||
/// Wrapped values are the type requested and type of the actual result.
|
/// Wrapped values are the type requested and type of the actual result.
|
||||||
ErrorMismatchOutputType(String, String, Position),
|
ErrorMismatchOutputType(String, String, Position),
|
||||||
|
/// Trying to index into a type that has no indexer function defined. Wrapped value is the type name.
|
||||||
|
ErrorIndexingType(String, Position),
|
||||||
|
|
||||||
/// Array access out-of-bounds.
|
/// Array access out-of-bounds.
|
||||||
/// Wrapped values are the current number of elements in the array and the index number.
|
/// Wrapped values are the current number of elements in the array and the index number.
|
||||||
ErrorArrayBounds(usize, INT, Position),
|
ErrorArrayBounds(usize, INT, Position),
|
||||||
@ -59,10 +65,10 @@ pub enum EvalAltResult {
|
|||||||
/// Bit-field indexing out-of-bounds.
|
/// Bit-field indexing out-of-bounds.
|
||||||
/// Wrapped values are the current number of bits in the bit-field and the index number.
|
/// Wrapped values are the current number of bits in the bit-field and the index number.
|
||||||
ErrorBitFieldBounds(usize, INT, Position),
|
ErrorBitFieldBounds(usize, INT, Position),
|
||||||
/// Trying to index into a type that has no indexer function defined. Wrapped value is the type name.
|
|
||||||
ErrorIndexingType(String, Position),
|
/// The `for` statement encounters a type that is not iterable.
|
||||||
/// The `for` statement encounters a type that is not an iterator.
|
|
||||||
ErrorFor(Position),
|
ErrorFor(Position),
|
||||||
|
|
||||||
/// Data race detected when accessing a variable. Wrapped value is the variable name.
|
/// Data race detected when accessing a variable. Wrapped value is the variable name.
|
||||||
ErrorDataRace(String, Position),
|
ErrorDataRace(String, Position),
|
||||||
/// Assignment to a constant variable. Wrapped value is the variable name.
|
/// Assignment to a constant variable. Wrapped value is the variable name.
|
||||||
@ -115,7 +121,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Self::ErrorInFunctionCall(s, src, err, _) if crate::engine::is_anonymous_fn(s) => {
|
Self::ErrorInFunctionCall(s, src, err, _) if crate::parser::is_anonymous_fn(s) => {
|
||||||
write!(f, "{} in call to closure", err)?;
|
write!(f, "{} in call to closure", err)?;
|
||||||
if !src.is_empty() {
|
if !src.is_empty() {
|
||||||
write!(f, " @ '{}'", src)?;
|
write!(f, " @ '{}'", src)?;
|
||||||
@ -145,7 +151,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}?,
|
}?,
|
||||||
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for {}", s)?,
|
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for {}", s)?,
|
||||||
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?,
|
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?,
|
||||||
Self::ErrorFor(_) => f.write_str("For loop expects a type with an iterator defined")?,
|
Self::ErrorFor(_) => f.write_str("For loop expects a type that is iterable")?,
|
||||||
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?,
|
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?,
|
||||||
Self::ErrorTooManyModules(_) => f.write_str("Too many modules imported")?,
|
Self::ErrorTooManyModules(_) => f.write_str("Too many modules imported")?,
|
||||||
Self::ErrorStackOverflow(_) => f.write_str("Stack overflow")?,
|
Self::ErrorStackOverflow(_) => f.write_str("Stack overflow")?,
|
||||||
@ -233,7 +239,7 @@ impl<T: AsRef<str>> From<T> for EvalAltResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for RhaiError {
|
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
||||||
@ -277,8 +283,13 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorArithmetic(_, _)
|
| Self::ErrorArithmetic(_, _)
|
||||||
| Self::ErrorRuntime(_, _) => true,
|
| Self::ErrorRuntime(_, _) => true,
|
||||||
|
|
||||||
Self::ErrorCustomSyntax(_, _, _)
|
// Custom syntax raises errors only when they are compiled by one
|
||||||
| Self::ErrorTooManyOperations(_)
|
// [`Engine`][crate::Engine] and run by another, causing a mismatch.
|
||||||
|
//
|
||||||
|
// Therefore, this error should not be catchable.
|
||||||
|
Self::ErrorCustomSyntax(_, _, _) => false,
|
||||||
|
|
||||||
|
Self::ErrorTooManyOperations(_)
|
||||||
| Self::ErrorTooManyModules(_)
|
| Self::ErrorTooManyModules(_)
|
||||||
| Self::ErrorStackOverflow(_)
|
| Self::ErrorStackOverflow(_)
|
||||||
| Self::ErrorDataTooLarge(_, _)
|
| Self::ErrorDataTooLarge(_, _)
|
||||||
|
@ -43,8 +43,8 @@ impl FnPtr {
|
|||||||
/// Create a new function pointer without checking its parameters.
|
/// Create a new function pointer without checking its parameters.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
|
pub(crate) fn new_unchecked(name: impl Into<Identifier>, curry: StaticVec<Dynamic>) -> Self {
|
||||||
Self(name, curry)
|
Self(name.into(), curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
use super::dynamic::{AccessMode, Variant};
|
use super::dynamic::{AccessMode, Variant};
|
||||||
use crate::{Dynamic, Identifier, StaticVec};
|
use crate::{Dynamic, Identifier, StaticVec};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::iter::FromIterator;
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{borrow::Cow, iter::Extend};
|
use std::{
|
||||||
|
iter::{Extend, FromIterator},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
|
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
|
||||||
const SCOPE_ENTRIES_INLINED: usize = 8;
|
const SCOPE_ENTRIES_INLINED: usize = 8;
|
||||||
@ -14,6 +16,11 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
|
|||||||
/// Type containing information about the current scope. Useful for keeping state between
|
/// Type containing information about the current scope. Useful for keeping state between
|
||||||
/// [`Engine`][crate::Engine] evaluation runs.
|
/// [`Engine`][crate::Engine] evaluation runs.
|
||||||
///
|
///
|
||||||
|
/// # Lifetime
|
||||||
|
///
|
||||||
|
/// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for
|
||||||
|
/// future versions. Until then, `'static` can be used.
|
||||||
|
///
|
||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it
|
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it
|
||||||
@ -47,7 +54,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
|
|||||||
//
|
//
|
||||||
// [`Scope`] is implemented as two [`Vec`]'s of exactly the same length. Variables data (name,
|
// [`Scope`] is implemented as two [`Vec`]'s of exactly the same length. Variables data (name,
|
||||||
// type, etc.) is manually split into two equal-length arrays. That's because variable names take
|
// type, etc.) is manually split into two equal-length arrays. That's because variable names take
|
||||||
// up the most space, with [`Cow<str>`][Cow] being four words long, but in the vast majority of
|
// up the most space, with [`Identifier`] being four words long, but in the vast majority of
|
||||||
// cases the name is NOT used to look up a variable. Variable lookup is usually via direct
|
// cases the name is NOT used to look up a variable. Variable lookup is usually via direct
|
||||||
// indexing, by-passing the name altogether.
|
// indexing, by-passing the name altogether.
|
||||||
//
|
//
|
||||||
@ -58,25 +65,30 @@ pub struct Scope<'a> {
|
|||||||
/// Current value of the entry.
|
/// Current value of the entry.
|
||||||
values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
||||||
/// (Name, aliases) of the entry.
|
/// (Name, aliases) of the entry.
|
||||||
names: SmallVec<[(Cow<'a, str>, Option<Box<StaticVec<Identifier>>>); SCOPE_ENTRIES_INLINED]>,
|
names: SmallVec<[(Identifier, Option<Box<StaticVec<Identifier>>>); SCOPE_ENTRIES_INLINED]>,
|
||||||
|
/// Phantom to keep the lifetime parameter in order not to break existing code.
|
||||||
|
phantom: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for Scope<'a> {
|
impl IntoIterator for Scope<'_> {
|
||||||
type Item = (Cow<'a, str>, Dynamic);
|
type Item = (String, Dynamic, Vec<Identifier>);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Box::new(
|
Box::new(self.values.into_iter().zip(self.names.into_iter()).map(
|
||||||
self.values
|
|(value, (name, alias))| {
|
||||||
.into_iter()
|
(
|
||||||
.zip(self.names.into_iter())
|
name.into(),
|
||||||
.map(|(value, (name, _))| (name, value)),
|
value,
|
||||||
|
alias.map(|a| a.to_vec()).unwrap_or_default(),
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Scope<'a> {
|
impl Scope<'_> {
|
||||||
/// Create a new [`Scope`].
|
/// Create a new [`Scope`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -95,6 +107,7 @@ impl<'a> Scope<'a> {
|
|||||||
Self {
|
Self {
|
||||||
values: SmallVec::new_const(),
|
values: SmallVec::new_const(),
|
||||||
names: SmallVec::new_const(),
|
names: SmallVec::new_const(),
|
||||||
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Empty the [`Scope`].
|
/// Empty the [`Scope`].
|
||||||
@ -171,11 +184,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push(
|
pub fn push(&mut self, name: impl Into<Identifier>, value: impl Variant + Clone) -> &mut Self {
|
||||||
&mut self,
|
|
||||||
name: impl Into<Cow<'a, str>>,
|
|
||||||
value: impl Variant + Clone,
|
|
||||||
) -> &mut Self {
|
|
||||||
self.push_dynamic_value(name, AccessMode::ReadWrite, Dynamic::from(value))
|
self.push_dynamic_value(name, AccessMode::ReadWrite, Dynamic::from(value))
|
||||||
}
|
}
|
||||||
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
||||||
@ -191,7 +200,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
|
pub fn push_dynamic(&mut self, name: impl Into<Identifier>, value: Dynamic) -> &mut Self {
|
||||||
self.push_dynamic_value(name, value.access_mode(), value)
|
self.push_dynamic_value(name, value.access_mode(), value)
|
||||||
}
|
}
|
||||||
/// Add (push) a new constant to the [`Scope`].
|
/// Add (push) a new constant to the [`Scope`].
|
||||||
@ -212,7 +221,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_constant(
|
pub fn push_constant(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Identifier>,
|
||||||
value: impl Variant + Clone,
|
value: impl Variant + Clone,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, AccessMode::ReadOnly, Dynamic::from(value))
|
self.push_dynamic_value(name, AccessMode::ReadOnly, Dynamic::from(value))
|
||||||
@ -235,7 +244,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_constant_dynamic(
|
pub fn push_constant_dynamic(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Identifier>,
|
||||||
value: Dynamic,
|
value: Dynamic,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
|
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
|
||||||
@ -244,7 +253,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_dynamic_value(
|
pub(crate) fn push_dynamic_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Identifier>,
|
||||||
access: AccessMode,
|
access: AccessMode,
|
||||||
mut value: Dynamic,
|
mut value: Dynamic,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
@ -301,7 +310,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains(&self, name: &str) -> bool {
|
pub fn contains(&self, name: &str) -> bool {
|
||||||
self.names.iter().any(|(key, _)| name == key.as_ref())
|
self.names.iter().any(|(key, _)| name == key)
|
||||||
}
|
}
|
||||||
/// Find an entry in the [`Scope`], starting from the last.
|
/// Find an entry in the [`Scope`], starting from the last.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -314,7 +323,7 @@ impl<'a> Scope<'a> {
|
|||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map(|(i, (key, _))| {
|
.find_map(|(i, (key, _))| {
|
||||||
if name == key.as_ref() {
|
if name == key {
|
||||||
let index = len - 1 - i;
|
let index = len - 1 - i;
|
||||||
Some((index, self.values[index].access_mode()))
|
Some((index, self.values[index].access_mode()))
|
||||||
} else {
|
} else {
|
||||||
@ -343,7 +352,7 @@ impl<'a> Scope<'a> {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, (key, _))| name == key.as_ref())
|
.find(|(_, (key, _))| name == key)
|
||||||
.and_then(|(index, _)| self.values[len - 1 - index].flatten_clone().try_cast())
|
.and_then(|(index, _)| self.values[len - 1 - index].flatten_clone().try_cast())
|
||||||
}
|
}
|
||||||
/// Check if the named entry in the [`Scope`] is constant.
|
/// Check if the named entry in the [`Scope`] is constant.
|
||||||
@ -398,7 +407,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_or_push(
|
pub fn set_or_push(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Cow<'a, str>>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
value: impl Variant + Clone,
|
value: impl Variant + Clone,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
match self.get_index(name.as_ref()) {
|
match self.get_index(name.as_ref()) {
|
||||||
@ -437,7 +446,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_value(
|
pub fn set_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Cow<'a, str>>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
value: impl Variant + Clone,
|
value: impl Variant + Clone,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
match self.get_index(name.as_ref()) {
|
match self.get_index(name.as_ref()) {
|
||||||
@ -535,9 +544,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// Get an iterator to entries in the [`Scope`].
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn into_iter(
|
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Identifier, Dynamic, Vec<Identifier>)> {
|
||||||
self,
|
|
||||||
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> {
|
|
||||||
self.names
|
self.names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.values.into_iter())
|
.zip(self.values.into_iter())
|
||||||
@ -597,7 +604,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {
|
impl<K: Into<Identifier>> Extend<(K, Dynamic)> for Scope<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||||
iter.into_iter().for_each(|(name, value)| {
|
iter.into_iter().for_each(|(name, value)| {
|
||||||
@ -606,7 +613,7 @@ impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, Dynamic)> for Scope<'a> {
|
impl<K: Into<Identifier>> FromIterator<(K, Dynamic)> for Scope<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_iter<T: IntoIterator<Item = (K, Dynamic)>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = (K, Dynamic)>>(iter: T) -> Self {
|
||||||
let mut scope = Self::new();
|
let mut scope = Self::new();
|
||||||
@ -615,7 +622,7 @@ impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, Dynamic)> for Scope<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> Extend<(K, bool, Dynamic)> for Scope<'a> {
|
impl<K: Into<Identifier>> Extend<(K, bool, Dynamic)> for Scope<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, bool, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, bool, Dynamic)>>(&mut self, iter: T) {
|
||||||
iter.into_iter().for_each(|(name, is_constant, value)| {
|
iter.into_iter().for_each(|(name, is_constant, value)| {
|
||||||
@ -632,7 +639,7 @@ impl<'a, K: Into<Cow<'a, str>>> Extend<(K, bool, Dynamic)> for Scope<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, bool, Dynamic)> for Scope<'a> {
|
impl<K: Into<Identifier>> FromIterator<(K, bool, Dynamic)> for Scope<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_iter<T: IntoIterator<Item = (K, bool, Dynamic)>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = (K, bool, Dynamic)>>(iter: T) -> Self {
|
||||||
let mut scope = Self::new();
|
let mut scope = Self::new();
|
||||||
|
@ -52,20 +52,3 @@ pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Option<T> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # DANGEROUS!!!
|
|
||||||
///
|
|
||||||
/// A dangerous function that blindly casts a `&str` from one lifetime to a `&str` of
|
|
||||||
/// another lifetime. This is mainly used to let us push a block-local variable into the
|
|
||||||
/// current [`Scope`][crate::Scope] without cloning the variable name. Doing this is safe because all local
|
|
||||||
/// variables in the [`Scope`][crate::Scope] are cleared out before existing the block.
|
|
||||||
///
|
|
||||||
/// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves
|
|
||||||
/// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
|
|
||||||
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
|
|
||||||
// this is safe because all local variables are cleared at the end of the block
|
|
||||||
unsafe { mem::transmute(name) }
|
|
||||||
}
|
|
||||||
|
@ -22,12 +22,48 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-3]")?, 1);
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[-3]")?, 1);
|
||||||
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
||||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y += 4; y[3]")?, 4);
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y += 4; y[3]")?, 4);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let y = [1, 2, 3]; pad(y, 5, 42); len(y)")?,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let y = [1, 2, 3]; pad(y, 5, [42]); len(y)")?,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let y = [1, 2, 3]; pad(y, 5, [42, 999, 123]); y[4][0]")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine
|
engine
|
||||||
.eval::<Dynamic>("let y = [1, 2, 3]; y[1] += 4; y")?
|
.eval::<Dynamic>("let y = [1, 2, 3]; y[1] += 4; y")?
|
||||||
.into_typed_array::<INT>()?,
|
.into_typed_array::<INT>()?,
|
||||||
[1, 6, 3]
|
[1, 6, 3]
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<Dynamic>("let y = [1, 2, 3]; extract(y, 1, 10)")?
|
||||||
|
.into_typed_array::<INT>()?,
|
||||||
|
vec![2, 3]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<Dynamic>("let y = [1, 2, 3]; extract(y, -3, 1)")?
|
||||||
|
.into_typed_array::<INT>()?,
|
||||||
|
vec![1]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<Dynamic>("let y = [1, 2, 3]; extract(y, -99, 2)")?
|
||||||
|
.into_typed_array::<INT>()?,
|
||||||
|
vec![1, 2]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<Dynamic>("let y = [1, 2, 3]; extract(y, 99, 1)")?
|
||||||
|
.into_typed_array::<INT>()?,
|
||||||
|
vec![] as Vec<INT>
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
{
|
{
|
||||||
|
@ -27,10 +27,25 @@ fn test_bit_fields() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; get_bits(x, 1, 3)")?, 5);
|
assert_eq!(engine.eval::<INT>("let x = 10; get_bits(x, 1, 3)")?, 5);
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; x[1..=3]")?, 5);
|
assert_eq!(engine.eval::<INT>("let x = 10; x[1..=3]")?, 5);
|
||||||
|
assert!(engine.eval::<INT>("let x = 10; x[1..99]").is_err());
|
||||||
|
assert!(engine.eval::<INT>("let x = 10; x[-1..3]").is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("let x = 10; set_bits(x, 1, 3, 7); x")?,
|
engine.eval::<INT>("let x = 10; set_bits(x, 1, 3, 7); x")?,
|
||||||
14
|
14
|
||||||
);
|
);
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
{
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 255; get_bits(x, -60, 2)")?, 3);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let x = 0; set_bits(x, -64, 1, 15); x")?,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let x = 0; set_bits(x, -60, 2, 15); x")?,
|
||||||
|
0b00110000
|
||||||
|
);
|
||||||
|
}
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; x[1..4] = 7; x")?, 14);
|
assert_eq!(engine.eval::<INT>("let x = 10; x[1..4] = 7; x")?, 14);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
@ -12,7 +12,7 @@ use rhai::Array;
|
|||||||
use rhai::Map;
|
use rhai::Map;
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use rhai::FLOAT;
|
use rhai::FLOAT;
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(feature = "no_float")]
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
@ -751,7 +751,7 @@ fn test_serde_json() -> serde_json::Result<()> {
|
|||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
|
fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
foo: Option<char>,
|
foo: Option<char>,
|
||||||
}
|
}
|
||||||
@ -774,6 +774,24 @@ fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(from_dynamic::<TestStruct>(&r)?, TestStruct { foo: None });
|
assert_eq!(from_dynamic::<TestStruct>(&r)?, TestStruct { foo: None });
|
||||||
|
|
||||||
|
let ts = TestStruct { foo: Some('a') };
|
||||||
|
|
||||||
|
let r = to_dynamic(&ts)?;
|
||||||
|
|
||||||
|
let map = r.cast::<Map>();
|
||||||
|
|
||||||
|
assert_eq!(map.len(), 1);
|
||||||
|
assert_eq!(map.get("foo").unwrap().as_char().unwrap(), 'a');
|
||||||
|
|
||||||
|
let ts = TestStruct { foo: None };
|
||||||
|
|
||||||
|
let r = to_dynamic(&ts)?;
|
||||||
|
|
||||||
|
let map = r.cast::<Map>();
|
||||||
|
|
||||||
|
assert_eq!(map.len(), 1);
|
||||||
|
assert_eq!(map.get("foo").unwrap().as_unit().unwrap(), ());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_std"))]
|
#![cfg(not(feature = "no_std"))]
|
||||||
#![cfg(not(target_arch = "wasm32"))]
|
#![cfg(not(target_family = "wasm"))]
|
||||||
#![cfg(not(target_arch = "wasm64"))]
|
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult};
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user