commit
fe5af8a272
@ -1,6 +1,15 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 1.0.1
|
||||||
|
=============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Fixed bug in using indexing/dotting inside index bracket.
|
||||||
|
|
||||||
|
|
||||||
Version 1.0.0
|
Version 1.0.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -19,11 +19,11 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
|||||||
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
||||||
ahash = { version = "0.7", default-features = false }
|
ahash = { version = "0.7", default-features = false }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
smartstring = { version = "0.2.6", default-features = false }
|
smartstring = { version = "0.2.7", default-features = false }
|
||||||
rhai_codegen = { version = ">=0.4.0", path = "codegen", default-features = false }
|
rhai_codegen = { version = "1.0", path = "codegen", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std
|
default = ["ahash/std", "num-traits/std"]
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # unchecked arithmetic
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
no_position = [] # do not track position in the parser
|
no_position = [] # do not track position in the parser
|
||||||
@ -32,7 +32,7 @@ no_float = [] # no floating-point
|
|||||||
f32_float = [] # set FLOAT=f32
|
f32_float = [] # set FLOAT=f32
|
||||||
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||||
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||||
decimal = ["rust_decimal/std"] # add the Decimal number type
|
decimal = ["rust_decimal"] # add the Decimal number type
|
||||||
no_index = [] # no arrays and indexing
|
no_index = [] # no arrays and indexing
|
||||||
no_object = [] # no custom objects
|
no_object = [] # no custom objects
|
||||||
no_function = ["no_closure"] # no script-defined functions (meaning no closures)
|
no_function = ["no_closure"] # no script-defined functions (meaning no closures)
|
||||||
@ -40,7 +40,7 @@ no_closure = [] # no automatic sharing and capture of anonymous
|
|||||||
no_module = [] # no modules
|
no_module = [] # no modules
|
||||||
internals = [] # expose internal data structures
|
internals = [] # expose internal data structures
|
||||||
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
||||||
metadata = ["serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata
|
metadata = ["serde", "serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata
|
||||||
|
|
||||||
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ pub fn inner_item_attributes<T: ExportedParams>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deny_cfg_attr(attrs: &Vec<syn::Attribute>) -> syn::Result<()> {
|
pub fn deny_cfg_attr(attrs: &[syn::Attribute]) -> syn::Result<()> {
|
||||||
if let Some(cfg_attr) = attrs
|
if let Some(cfg_attr) = attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
.find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))
|
||||||
|
@ -152,13 +152,13 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
("get", None) | ("set", None) | ("name", None) => {
|
("get", None) | ("set", None) | ("name", None) => {
|
||||||
return Err(syn::Error::new(key.span(), "requires value"))
|
return Err(syn::Error::new(key.span(), "requires value"))
|
||||||
}
|
}
|
||||||
("name", Some(s)) if &s.value() == FN_IDX_GET => {
|
("name", Some(s)) if s.value() == FN_IDX_GET => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
item_span,
|
item_span,
|
||||||
"use attribute 'index_get' instead",
|
"use attribute 'index_get' instead",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
("name", Some(s)) if &s.value() == FN_IDX_SET => {
|
("name", Some(s)) if s.value() == FN_IDX_SET => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
item_span,
|
item_span,
|
||||||
"use attribute 'index_set' instead",
|
"use attribute 'index_set' instead",
|
||||||
@ -268,7 +268,6 @@ impl ExportedParams for ExportedFnParams {
|
|||||||
special,
|
special,
|
||||||
namespace,
|
namespace,
|
||||||
span: Some(span),
|
span: Some(span),
|
||||||
..Default::default()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,7 +316,7 @@ impl Parse for ExportedFn {
|
|||||||
let skip_slots = if pass_context { 1 } else { 0 };
|
let skip_slots = if pass_context { 1 } else { 0 };
|
||||||
|
|
||||||
// Determine whether function generates a special calling convention for a mutable receiver.
|
// Determine whether function generates a special calling convention for a mutable receiver.
|
||||||
let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() {
|
let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) {
|
||||||
Some(syn::FnArg::Receiver(syn::Receiver {
|
Some(syn::FnArg::Receiver(syn::Receiver {
|
||||||
reference: Some(_), ..
|
reference: Some(_), ..
|
||||||
})) => true,
|
})) => true,
|
||||||
@ -474,7 +473,7 @@ impl ExportedFn {
|
|||||||
literals
|
literals
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exported_name<'n>(&'n self) -> Cow<'n, str> {
|
pub fn exported_name(&self) -> Cow<str> {
|
||||||
self.params
|
self.params
|
||||||
.name
|
.name
|
||||||
.last()
|
.last()
|
||||||
@ -629,7 +628,7 @@ impl ExportedFn {
|
|||||||
let return_span = self
|
let return_span = self
|
||||||
.return_type()
|
.return_type()
|
||||||
.map(|r| r.span())
|
.map(|r| r.span())
|
||||||
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
.unwrap_or_else(proc_macro2::Span::call_site);
|
||||||
if self.params.return_raw.is_some() {
|
if self.params.return_raw.is_some() {
|
||||||
quote_spanned! { return_span =>
|
quote_spanned! { return_span =>
|
||||||
pub #dynamic_signature {
|
pub #dynamic_signature {
|
||||||
@ -691,7 +690,7 @@ impl ExportedFn {
|
|||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
if !self.params().pure.is_some() {
|
if self.params().pure.is_none() {
|
||||||
let arg_lit_str =
|
let arg_lit_str =
|
||||||
syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span());
|
syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span());
|
||||||
unpack_statements.push(
|
unpack_statements.push(
|
||||||
@ -812,8 +811,8 @@ impl ExportedFn {
|
|||||||
let return_span = self
|
let return_span = self
|
||||||
.return_type()
|
.return_type()
|
||||||
.map(|r| r.span())
|
.map(|r| r.span())
|
||||||
.unwrap_or_else(|| proc_macro2::Span::call_site());
|
.unwrap_or_else(proc_macro2::Span::call_site);
|
||||||
let return_expr = if !self.params.return_raw.is_some() {
|
let return_expr = if self.params.return_raw.is_none() {
|
||||||
quote_spanned! { return_span =>
|
quote_spanned! { return_span =>
|
||||||
Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*)))
|
Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*)))
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,6 @@ impl ExportedParams for ExportedModParams {
|
|||||||
name,
|
name,
|
||||||
skip,
|
skip,
|
||||||
scope: scope.unwrap_or_default(),
|
scope: scope.unwrap_or_default(),
|
||||||
..Default::default()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ pub fn generate_body(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
|
pub fn check_rename_collisions(fns: &[ExportedFn]) -> Result<(), syn::Error> {
|
||||||
fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String {
|
fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String {
|
||||||
item_fn
|
item_fn
|
||||||
.arg_list()
|
.arg_list()
|
||||||
|
38
no_std/no_std_repl/Cargo.toml
Normal file
38
no_std/no_std_repl/Cargo.toml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
cargo-features = ["named-profiles"]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "no_std_repl"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
authors = ["Stephen Chung"]
|
||||||
|
description = "no-std REPL application"
|
||||||
|
homepage = "https://github.com/rhaiscript/rhai/tree/no_std/no_std_repl"
|
||||||
|
repository = "https://github.com/rhaiscript/rhai"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rhai = { path = "../../", default_features = false, features = [ "no_std", "decimal", "metadata" ] }
|
||||||
|
wee_alloc = { version = "0.4.5", default_features = false }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z" # optimize for size
|
||||||
|
debug = false
|
||||||
|
rpath = false
|
||||||
|
debug-assertions = false
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.unix]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
|
||||||
|
[profile.windows]
|
||||||
|
inherits = "release"
|
||||||
|
|
||||||
|
[profile.macos]
|
||||||
|
inherits = "release"
|
||||||
|
lto = "fat"
|
26
no_std/no_std_repl/README.md
Normal file
26
no_std/no_std_repl/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
`no-std` REPL Sample
|
||||||
|
====================
|
||||||
|
|
||||||
|
This sample application is the same version as the `rhai-repl` tool, compiled for `no-std`.
|
||||||
|
|
||||||
|
[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator.
|
||||||
|
|
||||||
|
|
||||||
|
To Build
|
||||||
|
--------
|
||||||
|
|
||||||
|
The nightly compiler is required:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo +nightly build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
A specific profile can also be used:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo +nightly build --profile unix -Z unstable-options
|
||||||
|
```
|
||||||
|
|
||||||
|
Three profiles are defined: `unix`, `windows` and `macos`.
|
||||||
|
|
||||||
|
The release build is optimized for size. It can be changed to optimize on speed instead.
|
214
no_std/no_std_repl/src/main.rs
Normal file
214
no_std/no_std_repl/src/main.rs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
use rhai::{Dynamic, Engine, EvalAltResult, Scope, AST};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
io::{stdin, stdout, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Pretty-print error.
|
||||||
|
fn print_error(input: &str, mut err: EvalAltResult) {
|
||||||
|
let lines: Vec<_> = input.trim().split('\n').collect();
|
||||||
|
let pos = err.take_position();
|
||||||
|
|
||||||
|
let line_no = if lines.len() > 1 {
|
||||||
|
if pos.is_none() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}: ", pos.line().unwrap())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Print error position
|
||||||
|
if pos.is_none() {
|
||||||
|
// No position
|
||||||
|
println!("{}", err);
|
||||||
|
} else {
|
||||||
|
// Specific position - print line text
|
||||||
|
println!("{}{}", line_no, lines[pos.line().unwrap() - 1]);
|
||||||
|
|
||||||
|
// Display position marker
|
||||||
|
println!(
|
||||||
|
"{0:>1$} {2}",
|
||||||
|
"^",
|
||||||
|
line_no.len() + pos.position().unwrap(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print help text.
|
||||||
|
fn print_help() {
|
||||||
|
println!("help => print this help");
|
||||||
|
println!("quit, exit => quit");
|
||||||
|
println!("scope => print all variables in the scope");
|
||||||
|
println!("functions => print all functions defined");
|
||||||
|
println!("json => output all functions in JSON format");
|
||||||
|
println!("ast => print the last AST (optimized)");
|
||||||
|
println!("astu => print the last raw, un-optimized AST");
|
||||||
|
println!(r"end a line with '\' to continue to the next line.");
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION"));
|
||||||
|
println!("{}", title);
|
||||||
|
println!("{0:=<1$}", "", title.len());
|
||||||
|
print_help();
|
||||||
|
|
||||||
|
// Initialize scripting engine
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Setup Engine
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
||||||
|
|
||||||
|
// Make Engine immutable
|
||||||
|
let engine = engine;
|
||||||
|
|
||||||
|
// Create scope
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
// REPL loop
|
||||||
|
let mut input = String::new();
|
||||||
|
let mut main_ast: AST = Default::default();
|
||||||
|
let mut ast_u: AST = Default::default();
|
||||||
|
let mut ast: AST = Default::default();
|
||||||
|
|
||||||
|
'main_loop: loop {
|
||||||
|
print!("rhai-repl> ");
|
||||||
|
stdout().flush().expect("couldn't flush stdout");
|
||||||
|
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match stdin().read_line(&mut input) {
|
||||||
|
Ok(0) => break 'main_loop,
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => panic!("input error: {}", err),
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = input.as_str().trim_end();
|
||||||
|
|
||||||
|
// Allow line continuation
|
||||||
|
if line.ends_with('\\') {
|
||||||
|
let len = line.len();
|
||||||
|
input.truncate(len - 1);
|
||||||
|
input.push('\n');
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("> ");
|
||||||
|
stdout().flush().expect("couldn't flush stdout");
|
||||||
|
}
|
||||||
|
|
||||||
|
let script = input.trim();
|
||||||
|
|
||||||
|
if script.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement standard commands
|
||||||
|
match script {
|
||||||
|
"help" => {
|
||||||
|
print_help();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
"exit" | "quit" => break, // quit
|
||||||
|
"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!();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
"astu" => {
|
||||||
|
// print the last un-optimized AST
|
||||||
|
println!("{:#?}\n", ast_u);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
"ast" => {
|
||||||
|
// print the last AST
|
||||||
|
println!("{:#?}\n", ast);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
"functions" => {
|
||||||
|
// print a list of all registered functions
|
||||||
|
engine
|
||||||
|
.gen_fn_signatures(false)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|f| println!("{}", f));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
main_ast.iter_functions().for_each(|f| println!("{}", f));
|
||||||
|
|
||||||
|
println!();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
"json" => {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
engine
|
||||||
|
.gen_fn_metadata_with_ast_to_json(&main_ast, true)
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
match engine
|
||||||
|
.compile_with_scope(&scope, &script)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.and_then(|r| {
|
||||||
|
ast_u = r.clone();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
{
|
||||||
|
ast = engine.optimize_ast(&scope, r, rhai::OptimizationLevel::Simple);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_optimize")]
|
||||||
|
{
|
||||||
|
ast = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the AST into the main
|
||||||
|
main_ast += ast.clone();
|
||||||
|
|
||||||
|
// Evaluate
|
||||||
|
engine.eval_ast_with_scope::<Dynamic>(&mut scope, &main_ast)
|
||||||
|
}) {
|
||||||
|
Ok(result) if !result.is::<()>() => {
|
||||||
|
println!("=> {:?}", result);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
println!();
|
||||||
|
print_error(&input, *err);
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw away all the statements, leaving only the functions
|
||||||
|
main_ast.clear_statements();
|
||||||
|
}
|
||||||
|
}
|
@ -6,15 +6,21 @@ This sample application is a bare-bones `no-std` build for testing.
|
|||||||
[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator.
|
[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator.
|
||||||
|
|
||||||
|
|
||||||
To Compile
|
To Build
|
||||||
----------
|
--------
|
||||||
|
|
||||||
The nightly compiler is required:
|
The nightly compiler is required:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo +nightly build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
A specific profile can also be used:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo +nightly build --profile unix -Z unstable-options
|
cargo +nightly build --profile unix -Z unstable-options
|
||||||
```
|
```
|
||||||
|
|
||||||
Available profiles are: `unix`, `windows` and `macos`.
|
Three profiles are defined: `unix`, `windows` and `macos`.
|
||||||
|
|
||||||
The release build is optimized for size. It can be changed to optimize on speed instead.
|
The release build is optimized for size. It can be changed to optimize on speed instead.
|
||||||
|
105
src/ast.rs
105
src/ast.rs
@ -78,7 +78,6 @@ pub struct ScriptFnDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScriptFnDef {
|
impl fmt::Display for ScriptFnDef {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
@ -129,7 +128,6 @@ pub struct ScriptFnMetadata<'a> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
impl fmt::Display for ScriptFnMetadata<'_> {
|
impl fmt::Display for ScriptFnMetadata<'_> {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
@ -149,16 +147,16 @@ impl fmt::Display for ScriptFnMetadata<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef {
|
impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn into(self) -> ScriptFnMetadata<'a> {
|
fn from(value: &'a ScriptFnDef) -> Self {
|
||||||
ScriptFnMetadata {
|
Self {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: self.comments.iter().map(|s| s.as_str()).collect(),
|
comments: value.comments.iter().map(|s| s.as_str()).collect(),
|
||||||
access: self.access,
|
access: value.access,
|
||||||
name: &self.name,
|
name: &value.name,
|
||||||
params: self.params.iter().map(|s| s.as_str()).collect(),
|
params: value.params.iter().map(|s| s.as_str()).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +213,7 @@ impl Default for AST {
|
|||||||
|
|
||||||
impl AST {
|
impl AST {
|
||||||
/// Create a new [`AST`].
|
/// Create a new [`AST`].
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
@ -223,7 +221,7 @@ impl AST {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: None,
|
source: None,
|
||||||
body: StmtBlock::new(statements.into_iter().collect(), Position::NONE),
|
body: StmtBlock::new(statements, Position::NONE),
|
||||||
functions: functions.into(),
|
functions: functions.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
@ -239,7 +237,7 @@ impl AST {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: Some(source.into()),
|
source: Some(source.into()),
|
||||||
body: StmtBlock::new(statements.into_iter().collect(), Position::NONE),
|
body: StmtBlock::new(statements, Position::NONE),
|
||||||
functions: functions.into(),
|
functions: functions.into(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
resolver: None,
|
resolver: None,
|
||||||
@ -258,7 +256,7 @@ impl AST {
|
|||||||
self.source.as_ref()
|
self.source.as_ref()
|
||||||
}
|
}
|
||||||
/// Set the source.
|
/// Set the source.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
||||||
let source = source.into();
|
let source = source.into();
|
||||||
Shared::get_mut(&mut self.functions)
|
Shared::get_mut(&mut self.functions)
|
||||||
@ -386,7 +384,7 @@ impl AST {
|
|||||||
///
|
///
|
||||||
/// This operation is cheap because functions are shared.
|
/// This operation is cheap because functions are shared.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn clone_functions_only_filtered(
|
pub fn clone_functions_only_filtered(
|
||||||
&self,
|
&self,
|
||||||
@ -657,7 +655,7 @@ impl AST {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn combine_filtered(
|
pub fn combine_filtered(
|
||||||
&mut self,
|
&mut self,
|
||||||
other: Self,
|
other: Self,
|
||||||
@ -696,7 +694,7 @@ impl AST {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn retain_functions(
|
pub fn retain_functions(
|
||||||
&mut self,
|
&mut self,
|
||||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||||
@ -712,7 +710,6 @@ impl AST {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &ScriptFnDef> {
|
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &ScriptFnDef> {
|
||||||
self.functions
|
self.functions
|
||||||
.iter_script_fn()
|
.iter_script_fn()
|
||||||
@ -723,7 +720,6 @@ impl AST {
|
|||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a {
|
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a {
|
||||||
self.functions
|
self.functions
|
||||||
.iter_script_fn()
|
.iter_script_fn()
|
||||||
@ -748,7 +744,7 @@ impl AST {
|
|||||||
/// Return `false` from the callback to terminate the walk.
|
/// Return `false` from the callback to terminate the walk.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
|
pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
|
||||||
let path = &mut Default::default();
|
let path = &mut Default::default();
|
||||||
|
|
||||||
@ -770,7 +766,7 @@ impl AST {
|
|||||||
/// Return `false` from the callback to terminate the walk.
|
/// Return `false` from the callback to terminate the walk.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
|
pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
|
||||||
let path = &mut Default::default();
|
let path = &mut Default::default();
|
||||||
|
|
||||||
@ -835,7 +831,6 @@ pub struct Ident {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Ident {
|
impl fmt::Debug for Ident {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?}", self.name)?;
|
write!(f, "{:?}", self.name)?;
|
||||||
self.pos.debug_print(f)
|
self.pos.debug_print(f)
|
||||||
@ -892,7 +887,8 @@ pub struct StmtBlock(StaticVec<Stmt>, Position);
|
|||||||
impl StmtBlock {
|
impl StmtBlock {
|
||||||
/// Create a new [`StmtBlock`].
|
/// Create a new [`StmtBlock`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(mut statements: StaticVec<Stmt>, pos: Position) -> Self {
|
pub fn new(statements: impl IntoIterator<Item = Stmt>, pos: Position) -> Self {
|
||||||
|
let mut statements: StaticVec<_> = statements.into_iter().collect();
|
||||||
statements.shrink_to_fit();
|
statements.shrink_to_fit();
|
||||||
Self(statements, pos)
|
Self(statements, pos)
|
||||||
}
|
}
|
||||||
@ -939,7 +935,6 @@ impl DerefMut for StmtBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for StmtBlock {
|
impl fmt::Debug for StmtBlock {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("Block")?;
|
f.write_str("Block")?;
|
||||||
fmt::Debug::fmt(&self.0, f)?;
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
@ -1027,7 +1022,7 @@ impl Default for Stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<Stmt> for StmtBlock {
|
impl From<Stmt> for StmtBlock {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn from(stmt: Stmt) -> Self {
|
fn from(stmt: Stmt) -> Self {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos),
|
Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos),
|
||||||
@ -1045,10 +1040,7 @@ impl Stmt {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_noop(&self) -> bool {
|
pub const fn is_noop(&self) -> bool {
|
||||||
match self {
|
matches!(self, Self::Noop(_))
|
||||||
Self::Noop(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Get the [position][Position] of this statement.
|
/// Get the [position][Position] of this statement.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -1226,7 +1218,7 @@ impl Stmt {
|
|||||||
///
|
///
|
||||||
/// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements
|
/// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements
|
||||||
/// are internally pure.
|
/// are internally pure.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_internally_pure(&self) -> bool {
|
pub fn is_internally_pure(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -1245,7 +1237,7 @@ impl Stmt {
|
|||||||
/// Currently this is only true for `return`, `throw`, `break` and `continue`.
|
/// Currently this is only true for `return`, `throw`, `break` and `continue`.
|
||||||
///
|
///
|
||||||
/// All statements following this statement will essentially be dead code.
|
/// All statements following this statement will essentially be dead code.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_control_flow_break(&self) -> bool {
|
pub const fn is_control_flow_break(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -1634,7 +1626,6 @@ impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
|
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let abs = self.0.abs();
|
let abs = self.0.abs();
|
||||||
if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
|
if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
|
||||||
@ -1690,7 +1681,7 @@ impl FloatWrapper<FLOAT> {
|
|||||||
/// Create a new [`FloatWrapper`].
|
/// Create a new [`FloatWrapper`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) const fn const_new(value: FLOAT) -> Self {
|
pub const fn new_const(value: FLOAT) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1759,10 +1750,10 @@ pub enum Expr {
|
|||||||
Stmt(Box<StmtBlock>),
|
Stmt(Box<StmtBlock>),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
FnCall(Box<FnCallExpr>, Position),
|
FnCall(Box<FnCallExpr>, Position),
|
||||||
/// lhs `.` rhs
|
/// lhs `.` rhs - bool variable is a dummy
|
||||||
Dot(Box<BinaryExpr>, Position),
|
Dot(Box<BinaryExpr>, bool, Position),
|
||||||
/// expr `[` expr `]`
|
/// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops
|
||||||
Index(Box<BinaryExpr>, Position),
|
Index(Box<BinaryExpr>, bool, Position),
|
||||||
/// lhs `&&` rhs
|
/// lhs `&&` rhs
|
||||||
And(Box<BinaryExpr>, Position),
|
And(Box<BinaryExpr>, Position),
|
||||||
/// lhs `||` rhs
|
/// lhs `||` rhs
|
||||||
@ -1839,10 +1830,18 @@ impl fmt::Debug for Expr {
|
|||||||
}
|
}
|
||||||
ff.finish()
|
ff.finish()
|
||||||
}
|
}
|
||||||
Self::Dot(x, pos) | Self::Index(x, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
|
Self::Index(x, term, pos) => {
|
||||||
|
display_pos = *pos;
|
||||||
|
|
||||||
|
f.debug_struct("Index")
|
||||||
|
.field("lhs", &x.lhs)
|
||||||
|
.field("rhs", &x.rhs)
|
||||||
|
.field("terminate", term)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
|
||||||
let op_name = match self {
|
let op_name = match self {
|
||||||
Self::Dot(_, _) => "Dot",
|
Self::Dot(_, _, _) => "Dot",
|
||||||
Self::Index(_, _) => "Index",
|
|
||||||
Self::And(_, _) => "And",
|
Self::And(_, _) => "And",
|
||||||
Self::Or(_, _) => "Or",
|
Self::Or(_, _) => "Or",
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -1915,9 +1914,18 @@ impl Expr {
|
|||||||
Union::Char(c, _, _) => Self::CharConstant(c, pos),
|
Union::Char(c, _, _) => Self::CharConstant(c, pos),
|
||||||
Union::Int(i, _, _) => Self::IntegerConstant(i, pos),
|
Union::Int(i, _, _) => Self::IntegerConstant(i, pos),
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(f, _, _) => Self::FloatConstant(f, pos),
|
Union::Float(f, _, _) => Self::FloatConstant(f, pos),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos),
|
||||||
|
|
||||||
_ => Self::DynamicConstant(value.into(), pos),
|
_ => Self::DynamicConstant(value.into(), pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1964,7 +1972,7 @@ impl Expr {
|
|||||||
Self::Property(x) => (x.2).1,
|
Self::Property(x) => (x.2).1,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
|
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) | Self::Index(x, _, _) => {
|
||||||
x.lhs.position()
|
x.lhs.position()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1986,8 +1994,8 @@ impl Expr {
|
|||||||
| Self::Map(_, pos)
|
| Self::Map(_, pos)
|
||||||
| Self::And(_, pos)
|
| Self::And(_, pos)
|
||||||
| Self::Or(_, pos)
|
| Self::Or(_, pos)
|
||||||
| Self::Dot(_, pos)
|
| Self::Dot(_, _, pos)
|
||||||
| Self::Index(_, pos)
|
| Self::Index(_, _, pos)
|
||||||
| Self::Variable(_, pos, _)
|
| Self::Variable(_, pos, _)
|
||||||
| Self::Stack(_, pos)
|
| Self::Stack(_, pos)
|
||||||
| Self::FnCall(_, pos)
|
| Self::FnCall(_, pos)
|
||||||
@ -2024,10 +2032,7 @@ impl Expr {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_unit(&self) -> bool {
|
pub const fn is_unit(&self) -> bool {
|
||||||
match self {
|
matches!(self, Self::Unit(_))
|
||||||
Self::Unit(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Is the expression a constant?
|
/// Is the expression a constant?
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -2078,8 +2083,8 @@ impl Expr {
|
|||||||
| Self::InterpolatedString(_, _)
|
| Self::InterpolatedString(_, _)
|
||||||
| Self::FnCall(_, _)
|
| Self::FnCall(_, _)
|
||||||
| Self::Stmt(_)
|
| Self::Stmt(_)
|
||||||
| Self::Dot(_, _)
|
| Self::Dot(_, _, _)
|
||||||
| Self::Index(_, _)
|
| Self::Index(_, _, _)
|
||||||
| Self::Array(_, _)
|
| Self::Array(_, _)
|
||||||
| Self::Map(_, _) => match token {
|
| Self::Map(_, _) => match token {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -2144,7 +2149,7 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {
|
Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => {
|
||||||
if !x.lhs.walk(path, on_node) {
|
if !x.lhs.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ fn print_help() {
|
|||||||
println!("scope => print all variables in the scope");
|
println!("scope => print all variables in the scope");
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
println!("functions => print all functions defined");
|
println!("functions => print all functions defined");
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
println!("json => output all functions in JSON format");
|
||||||
println!("ast => print the last AST (optimized)");
|
println!("ast => print the last AST (optimized)");
|
||||||
println!("astu => print the last raw, un-optimized AST");
|
println!("astu => print the last raw, un-optimized AST");
|
||||||
println!(r"end a line with '\' to continue to the next line.");
|
println!(r"end a line with '\' to continue to the next line.");
|
||||||
@ -76,7 +78,12 @@ fn main() {
|
|||||||
eprintln!("Error script file path: {}\n{}", filename, err);
|
eprintln!("Error script file path: {}\n{}", filename, err);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Ok(f) => f,
|
Ok(f) => {
|
||||||
|
match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) {
|
||||||
|
Ok(f) => f.into(),
|
||||||
|
_ => f,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
contents.clear();
|
contents.clear();
|
||||||
@ -207,7 +214,7 @@ fn main() {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.for_each(|(i, (name, constant, value))| {
|
.for_each(|(i, (name, constant, value))| {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let value_is_shared = if value.is_shared() { " (shared" } else { "" };
|
let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
let value_is_shared = "";
|
let value_is_shared = "";
|
||||||
|
|
||||||
@ -247,15 +254,16 @@ fn main() {
|
|||||||
println!();
|
println!();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// "json" => {
|
#[cfg(feature = "metadata")]
|
||||||
// println!(
|
"json" => {
|
||||||
// "{}",
|
println!(
|
||||||
// engine
|
"{}",
|
||||||
// .gen_fn_metadata_with_ast_to_json(&main_ast, true)
|
engine
|
||||||
// .unwrap()
|
.gen_fn_metadata_with_ast_to_json(&main_ast, true)
|
||||||
// );
|
.unwrap()
|
||||||
// continue;
|
);
|
||||||
// }
|
continue;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,11 @@ fn main() {
|
|||||||
eprintln!("Error script file path: {}\n{}", filename, err);
|
eprintln!("Error script file path: {}\n{}", filename, err);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Ok(f) => f,
|
Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap())
|
||||||
|
{
|
||||||
|
Ok(f) => f.into(),
|
||||||
|
_ => f,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
@ -134,7 +134,6 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
|
|||||||
///
|
///
|
||||||
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
|
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult {
|
pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult {
|
||||||
self.engine.eval_expr(
|
self.engine.eval_expr(
|
||||||
self.scope,
|
self.scope,
|
||||||
@ -179,15 +178,12 @@ impl Engine {
|
|||||||
/// Replacing one variable with another (i.e. adding a new variable and removing one variable at
|
/// Replacing one variable with another (i.e. adding a new variable and removing one variable at
|
||||||
/// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also
|
/// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also
|
||||||
/// does NOT count, so `false` should be passed.
|
/// does NOT count, so `false` should be passed.
|
||||||
#[must_use]
|
|
||||||
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
|
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
keywords: &[S],
|
keywords: &[S],
|
||||||
scope_may_be_changed: bool,
|
scope_may_be_changed: bool,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||||
) -> Result<&mut Self, ParseError> {
|
) -> Result<&mut Self, ParseError> {
|
||||||
let keywords = keywords.as_ref();
|
|
||||||
|
|
||||||
let mut segments: StaticVec<ImmutableString> = Default::default();
|
let mut segments: StaticVec<ImmutableString> = Default::default();
|
||||||
|
|
||||||
for s in keywords {
|
for s in keywords {
|
||||||
@ -240,8 +236,7 @@ impl Engine {
|
|||||||
s
|
s
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into_err(Position::NONE)
|
.into_err(Position::NONE));
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
// Identifier in first position
|
// Identifier in first position
|
||||||
s if segments.is_empty() && is_valid_identifier(s.chars()) => {
|
s if segments.is_empty() && is_valid_identifier(s.chars()) => {
|
||||||
@ -264,8 +259,7 @@ impl Engine {
|
|||||||
s
|
s
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into_err(Position::NONE)
|
.into_err(Position::NONE));
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -212,6 +212,7 @@ pub enum Union {
|
|||||||
TimeStamp(Box<Instant>, Tag, AccessMode),
|
TimeStamp(Box<Instant>, Tag, AccessMode),
|
||||||
|
|
||||||
/// Any type as a trait object.
|
/// Any type as a trait object.
|
||||||
|
#[allow(clippy::redundant_allocation)]
|
||||||
Variant(Box<Box<dyn Variant>>, Tag, AccessMode),
|
Variant(Box<Box<dyn Variant>>, Tag, AccessMode),
|
||||||
|
|
||||||
/// A _shared_ value of any type.
|
/// A _shared_ value of any type.
|
||||||
@ -378,10 +379,7 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_variant(&self) -> bool {
|
pub const fn is_variant(&self) -> bool {
|
||||||
match self.0 {
|
matches!(self.0, Union::Variant(_, _, _))
|
||||||
Union::Variant(_, _, _) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Is the value held by this [`Dynamic`] shared?
|
/// Is the value held by this [`Dynamic`] shared?
|
||||||
///
|
///
|
||||||
@ -390,13 +388,11 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_shared(&self) -> bool {
|
pub const fn is_shared(&self) -> bool {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Shared(_, _, _) => return true,
|
#[cfg(not(feature = "no_closure"))]
|
||||||
_ => (),
|
Union::Shared(_, _, _) => true,
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
/// Is the value held by this [`Dynamic`] a particular type?
|
/// Is the value held by this [`Dynamic`] a particular type?
|
||||||
///
|
///
|
||||||
@ -405,13 +401,11 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is<T: Any + Clone>(&self) -> bool {
|
pub fn is<T: Any + Clone>(&self) -> bool {
|
||||||
let mut target_type_id = TypeId::of::<T>();
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
|
self.type_id() == TypeId::of::<ImmutableString>()
|
||||||
if target_type_id == TypeId::of::<String>() {
|
} else {
|
||||||
target_type_id = TypeId::of::<ImmutableString>();
|
self.type_id() == TypeId::of::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.type_id() == target_type_id
|
|
||||||
}
|
}
|
||||||
/// Get the [`TypeId`] of the value held by this [`Dynamic`].
|
/// Get the [`TypeId`] of the value held by this [`Dynamic`].
|
||||||
///
|
///
|
||||||
@ -577,7 +571,7 @@ impl Hash for Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Map the name of a standard type into a friendly form.
|
/// Map the name of a standard type into a friendly form.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||||
if name == type_name::<String>() {
|
if name == type_name::<String>() {
|
||||||
@ -845,7 +839,7 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(
|
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(
|
||||||
FloatWrapper::const_new(0.0),
|
FloatWrapper::new_const(0.0),
|
||||||
DEFAULT_TAG_VALUE,
|
DEFAULT_TAG_VALUE,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
));
|
));
|
||||||
@ -854,7 +848,7 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_ONE: Dynamic = Self(Union::Float(
|
pub const FLOAT_ONE: Dynamic = Self(Union::Float(
|
||||||
FloatWrapper::const_new(1.0),
|
FloatWrapper::new_const(1.0),
|
||||||
DEFAULT_TAG_VALUE,
|
DEFAULT_TAG_VALUE,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
));
|
));
|
||||||
@ -863,7 +857,7 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_TWO: Dynamic = Self(Union::Float(
|
pub const FLOAT_TWO: Dynamic = Self(Union::Float(
|
||||||
FloatWrapper::const_new(2.0),
|
FloatWrapper::new_const(2.0),
|
||||||
DEFAULT_TAG_VALUE,
|
DEFAULT_TAG_VALUE,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
));
|
));
|
||||||
@ -872,7 +866,7 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_TEN: Dynamic = Self(Union::Float(
|
pub const FLOAT_TEN: Dynamic = Self(Union::Float(
|
||||||
FloatWrapper::const_new(10.0),
|
FloatWrapper::new_const(10.0),
|
||||||
DEFAULT_TAG_VALUE,
|
DEFAULT_TAG_VALUE,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
));
|
));
|
||||||
@ -881,7 +875,7 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(
|
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(
|
||||||
FloatWrapper::const_new(-1.0),
|
FloatWrapper::new_const(-1.0),
|
||||||
DEFAULT_TAG_VALUE,
|
DEFAULT_TAG_VALUE,
|
||||||
ReadWrite,
|
ReadWrite,
|
||||||
));
|
));
|
||||||
@ -1056,21 +1050,21 @@ impl Dynamic {
|
|||||||
let val = value.as_any();
|
let val = value.as_any();
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return val.downcast_ref::<INT>().expect(CHECKED).clone().into();
|
return (*val.downcast_ref::<INT>().expect(CHECKED)).into();
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return val.downcast_ref::<FLOAT>().expect(CHECKED).clone().into();
|
return (*val.downcast_ref::<FLOAT>().expect(CHECKED)).into();
|
||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||||
return val.downcast_ref::<Decimal>().expect(CHECKED).clone().into();
|
return (*val.downcast_ref::<Decimal>().expect(CHECKED)).into();
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return val.downcast_ref::<bool>().expect(CHECKED).clone().into();
|
return (*val.downcast_ref::<bool>().expect(CHECKED)).into();
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return val.downcast_ref::<char>().expect(CHECKED).clone().into();
|
return (*val.downcast_ref::<char>().expect(CHECKED)).into();
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return val
|
return val
|
||||||
@ -1321,7 +1315,7 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(x.cast::<u32>(), 42);
|
/// assert_eq!(x.cast::<u32>(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn cast<T: Any + Clone>(self) -> T {
|
pub fn cast<T: Any + Clone>(self) -> T {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -1481,7 +1475,7 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
@ -1515,7 +1509,7 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
@ -1735,7 +1729,6 @@ impl Dynamic {
|
|||||||
/// Cast the [`Dynamic`] as a unit `()` and return it.
|
/// Cast the [`Dynamic`] as a unit `()` and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_unit(&self) -> Result<(), &'static str> {
|
pub fn as_unit(&self) -> Result<(), &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(value, _, _) => Ok(value),
|
Union::Unit(value, _, _) => Ok(value),
|
||||||
@ -1747,7 +1740,6 @@ impl Dynamic {
|
|||||||
/// Cast the [`Dynamic`] as the system integer type [`INT`] and return it.
|
/// Cast the [`Dynamic`] as the system integer type [`INT`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_int(&self) -> Result<INT, &'static str> {
|
pub fn as_int(&self) -> Result<INT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Int(n, _, _) => Ok(n),
|
Union::Int(n, _, _) => Ok(n),
|
||||||
@ -1762,7 +1754,6 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Float(n, _, _) => Ok(*n),
|
Union::Float(n, _, _) => Ok(*n),
|
||||||
@ -1777,7 +1768,6 @@ impl Dynamic {
|
|||||||
/// Exported under the `decimal` feature only.
|
/// Exported under the `decimal` feature only.
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
|
pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Decimal(ref n, _, _) => Ok(**n),
|
Union::Decimal(ref n, _, _) => Ok(**n),
|
||||||
@ -1789,7 +1779,6 @@ impl Dynamic {
|
|||||||
/// Cast the [`Dynamic`] as a [`bool`] and return it.
|
/// Cast the [`Dynamic`] as a [`bool`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Bool(b, _, _) => Ok(b),
|
Union::Bool(b, _, _) => Ok(b),
|
||||||
@ -1801,7 +1790,6 @@ impl Dynamic {
|
|||||||
/// Cast the [`Dynamic`] as a [`char`] and return it.
|
/// Cast the [`Dynamic`] as a [`char`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_char(&self) -> Result<char, &'static str> {
|
pub fn as_char(&self) -> Result<char, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Char(n, _, _) => Ok(n),
|
Union::Char(n, _, _) => Ok(n),
|
||||||
@ -1817,7 +1805,6 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Panics if the value is shared.
|
/// Panics if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
|
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(ref s, _, _) => Ok(s),
|
Union::Str(ref s, _, _) => Ok(s),
|
||||||
@ -1830,14 +1817,12 @@ impl Dynamic {
|
|||||||
/// If there are other references to the same string, a cloned copy is returned.
|
/// If there are other references to the same string, a cloned copy is returned.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn as_string(self) -> Result<String, &'static str> {
|
pub fn as_string(self) -> Result<String, &'static str> {
|
||||||
self.as_immutable_string().map(ImmutableString::into_owned)
|
self.as_immutable_string().map(ImmutableString::into_owned)
|
||||||
}
|
}
|
||||||
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(s, _, _) => Ok(s),
|
Union::Str(s, _, _) => Ok(s),
|
||||||
@ -2057,6 +2042,6 @@ impl From<Instant> for Dynamic {
|
|||||||
impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic {
|
impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: crate::Shared<crate::Locked<Self>>) -> Self {
|
fn from(value: crate::Shared<crate::Locked<Self>>) -> Self {
|
||||||
Self(Union::Shared(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Shared(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
209
src/engine.rs
209
src/engine.rs
@ -67,7 +67,10 @@ impl Imports {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Self {
|
||||||
|
keys: StaticVec::new(),
|
||||||
|
modules: StaticVec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Get the length of this stack of imported [modules][Module].
|
/// Get the length of this stack of imported [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -119,7 +122,6 @@ impl Imports {
|
|||||||
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.keys
|
self.keys
|
||||||
.iter()
|
.iter()
|
||||||
@ -130,14 +132,12 @@ impl Imports {
|
|||||||
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
||||||
self.keys.iter().rev().zip(self.modules.iter().rev())
|
self.keys.iter().rev().zip(self.modules.iter().rev())
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
||||||
self.keys.iter().zip(self.modules.iter())
|
self.keys.iter().zip(self.modules.iter())
|
||||||
}
|
}
|
||||||
@ -290,10 +290,10 @@ pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
|
|||||||
enum ChainType {
|
enum ChainType {
|
||||||
/// Indexing.
|
/// Indexing.
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Index,
|
Indexing,
|
||||||
/// Dotting.
|
/// Dotting.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dot,
|
Dotting,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Value of a chaining argument.
|
/// Value of a chaining argument.
|
||||||
@ -318,9 +318,10 @@ impl ChainArgument {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn as_index_value(self) -> Option<Dynamic> {
|
pub fn into_index_value(self) -> Option<Dynamic> {
|
||||||
match self {
|
match self {
|
||||||
Self::IndexValue(value, _) => Some(value),
|
Self::IndexValue(value, _) => Some(value),
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,7 +329,7 @@ impl ChainArgument {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn as_fn_call_args(self) -> Option<(StaticVec<Dynamic>, Position)> {
|
pub fn into_fn_call_args(self) -> Option<(StaticVec<Dynamic>, Position)> {
|
||||||
match self {
|
match self {
|
||||||
Self::MethodCallArgs(values, pos) => Some((values, pos)),
|
Self::MethodCallArgs(values, pos) => Some((values, pos)),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -352,6 +353,18 @@ impl From<(Dynamic, Position)> for ChainArgument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the chaining type for an [`Expr`].
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
fn match_chaining_type(expr: &Expr) -> ChainType {
|
||||||
|
match expr {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Expr::Index(_, _, _) => ChainType::Indexing,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Dot(_, _, _) => ChainType::Dotting,
|
||||||
|
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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> {
|
||||||
@ -376,7 +389,7 @@ pub enum Target<'a> {
|
|||||||
impl<'a> Target<'a> {
|
impl<'a> Target<'a> {
|
||||||
/// Is the `Target` a reference pointing to other data?
|
/// Is the `Target` a reference pointing to other data?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_ref(&self) -> bool {
|
pub const fn is_ref(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -391,7 +404,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the `Target` a temp value?
|
/// Is the `Target` a temp value?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_temp_value(&self) -> bool {
|
pub const fn is_temp_value(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -407,7 +420,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
/// Is the `Target` a shared value?
|
/// Is the `Target` a shared value?
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -423,7 +436,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
/// Is the `Target` a specific type?
|
/// Is the `Target` a specific type?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is<T: Variant + Clone>(&self) -> bool {
|
pub fn is<T: Variant + Clone>(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -438,7 +451,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
|
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn take_or_clone(self) -> Dynamic {
|
pub fn take_or_clone(self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
@ -469,8 +482,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
/// Propagate a changed value back to the original source.
|
/// Propagate a changed value back to the original source.
|
||||||
/// This has no effect except for string indexing.
|
/// This has no effect except for string indexing.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
|
pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(_) | Self::TempValue(_) => (),
|
Self::RefMut(_) | Self::TempValue(_) => (),
|
||||||
@ -534,7 +546,7 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn from(value: &'a mut Dynamic) -> Self {
|
fn from(value: &'a mut Dynamic) -> Self {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if value.is_shared() {
|
if value.is_shared() {
|
||||||
@ -555,7 +567,7 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
|||||||
impl Deref for Target<'_> {
|
impl Deref for Target<'_> {
|
||||||
type Target = Dynamic;
|
type Target = Dynamic;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn deref(&self) -> &Dynamic {
|
fn deref(&self) -> &Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => *r,
|
Self::RefMut(r) => *r,
|
||||||
@ -578,7 +590,7 @@ impl AsRef<Dynamic> for Target<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for Target<'_> {
|
impl DerefMut for Target<'_> {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut Dynamic {
|
fn deref_mut(&mut self) -> &mut Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => *r,
|
Self::RefMut(r) => *r,
|
||||||
@ -818,7 +830,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
|
|||||||
/// Get an iterator over the current set of modules imported via `import` statements.
|
/// Get an iterator over the current set of modules imported via `import` statements.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.mods.iter()
|
self.mods.iter()
|
||||||
}
|
}
|
||||||
@ -833,7 +844,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
|
|||||||
}
|
}
|
||||||
/// Get an iterator over the namespaces containing definition of all script-defined functions.
|
/// Get an iterator over the namespaces containing definition of all script-defined functions.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
||||||
self.lib.iter().cloned()
|
self.lib.iter().cloned()
|
||||||
}
|
}
|
||||||
@ -947,7 +957,7 @@ impl Default for Engine {
|
|||||||
|
|
||||||
/// Make getter function
|
/// Make getter function
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn make_getter(id: &str) -> String {
|
pub fn make_getter(id: &str) -> String {
|
||||||
format!("{}{}", FN_GET, id)
|
format!("{}{}", FN_GET, id)
|
||||||
@ -955,7 +965,7 @@ pub fn make_getter(id: &str) -> String {
|
|||||||
|
|
||||||
/// Make setter function
|
/// Make setter function
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn make_setter(id: &str) -> String {
|
pub fn make_setter(id: &str) -> String {
|
||||||
format!("{}{}", FN_SET, id)
|
format!("{}{}", FN_SET, id)
|
||||||
@ -970,7 +980,7 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Print to `stdout`
|
/// Print to `stdout`
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn print_to_stdout(s: &str) {
|
fn print_to_stdout(s: &str) {
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -979,7 +989,7 @@ fn print_to_stdout(s: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Debug to `stdout`
|
/// Debug to `stdout`
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) {
|
fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) {
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -1021,7 +1031,7 @@ impl Engine {
|
|||||||
/// Create a new [`Engine`] with minimal built-in functions.
|
/// Create a new [`Engine`] with minimal built-in functions.
|
||||||
///
|
///
|
||||||
/// Use [`register_global_module`][Engine::register_global_module] to add packages of functions.
|
/// Use [`register_global_module`][Engine::register_global_module] to add packages of functions.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
let mut engine = Self {
|
let mut engine = Self {
|
||||||
@ -1093,7 +1103,6 @@ impl Engine {
|
|||||||
|
|
||||||
/// Search for a variable within the scope or within imports,
|
/// Search for a variable within the scope or within imports,
|
||||||
/// depending on whether the variable name is namespace-qualified.
|
/// depending on whether the variable name is namespace-qualified.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn search_namespace<'s>(
|
pub(crate) fn search_namespace<'s>(
|
||||||
&self,
|
&self,
|
||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
@ -1143,7 +1152,6 @@ impl Engine {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `expr` is not [`Expr::Variable`].
|
/// Panics if `expr` is not [`Expr::Variable`].
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn search_scope_only<'s>(
|
pub(crate) fn search_scope_only<'s>(
|
||||||
&self,
|
&self,
|
||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
@ -1217,7 +1225,6 @@ impl Engine {
|
|||||||
/// Chain-evaluate a dot/index chain.
|
/// Chain-evaluate a dot/index chain.
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[must_use]
|
|
||||||
fn eval_dot_index_chain_helper(
|
fn eval_dot_index_chain_helper(
|
||||||
&self,
|
&self,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -1227,21 +1234,12 @@ impl Engine {
|
|||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
root: (&str, Position),
|
root: (&str, Position),
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
|
_terminate_chaining: bool,
|
||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
fn match_chain_type(expr: &Expr) -> ChainType {
|
|
||||||
match expr {
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(_, _) => ChainType::Index,
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::Dot(_, _) => ChainType::Dot,
|
|
||||||
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_ref_mut = target.is_ref();
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
// Pop the last index value
|
// Pop the last index value
|
||||||
@ -1251,23 +1249,25 @@ impl Engine {
|
|||||||
|
|
||||||
match chain_type {
|
match chain_type {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
ChainType::Index => {
|
ChainType::Indexing => {
|
||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
let idx_val = idx_val
|
let idx_val = idx_val
|
||||||
.as_index_value()
|
.into_index_value()
|
||||||
.expect("never fails because `chain_type` is `ChainType::Index`");
|
.expect("never fails because `chain_type` is `ChainType::Index`");
|
||||||
|
|
||||||
match rhs {
|
match rhs {
|
||||||
// xxx[idx].expr... | xxx[idx][expr]...
|
// xxx[idx].expr... | xxx[idx][expr]...
|
||||||
Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => {
|
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
|
||||||
|
if !_terminate_chaining =>
|
||||||
|
{
|
||||||
let idx_pos = x.lhs.position();
|
let idx_pos = x.lhs.position();
|
||||||
let obj_ptr = &mut self.get_indexed_mut(
|
let obj_ptr = &mut self.get_indexed_mut(
|
||||||
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
||||||
)?;
|
)?;
|
||||||
let rhs_chain = match_chain_type(rhs);
|
|
||||||
|
|
||||||
|
let rhs_chain = match_chaining_type(rhs);
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, idx_values,
|
||||||
rhs_chain, level, new_val,
|
rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
@ -1324,13 +1324,13 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
ChainType::Dot => {
|
ChainType::Dotting => {
|
||||||
match rhs {
|
match rhs {
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
|
Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
|
||||||
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
||||||
let call_args = &mut idx_val
|
let call_args = &mut idx_val
|
||||||
.as_fn_call_args()
|
.into_fn_call_args()
|
||||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, call_args, *pos, level,
|
mods, state, lib, name, *hashes, target, call_args, *pos, level,
|
||||||
@ -1482,7 +1482,9 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos)
|
||||||
|
if target.is::<Map>() =>
|
||||||
|
{
|
||||||
let val_target = &mut match x.lhs {
|
let val_target = &mut match x.lhs {
|
||||||
Expr::Property(ref p) => {
|
Expr::Property(ref p) => {
|
||||||
let (name, pos) = &p.2;
|
let (name, pos) = &p.2;
|
||||||
@ -1495,7 +1497,7 @@ impl Engine {
|
|||||||
Expr::FnCall(ref x, pos) if !x.is_qualified() => {
|
Expr::FnCall(ref x, pos) if !x.is_qualified() => {
|
||||||
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
||||||
let call_args = &mut idx_val
|
let call_args = &mut idx_val
|
||||||
.as_fn_call_args()
|
.into_fn_call_args()
|
||||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||||
let (val, _) = self.make_method_call(
|
let (val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, call_args, pos, level,
|
mods, state, lib, name, *hashes, target, call_args, pos, level,
|
||||||
@ -1509,22 +1511,22 @@ impl Engine {
|
|||||||
// Others - syntax error
|
// Others - syntax error
|
||||||
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||||
};
|
};
|
||||||
let rhs_chain = match_chain_type(rhs);
|
let rhs_chain = match_chaining_type(rhs);
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, val_target, root, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, val_target, root, &x.rhs, *term,
|
||||||
rhs_chain, level, new_val,
|
idx_values, rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
}
|
}
|
||||||
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
|
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
|
||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) => {
|
Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) => {
|
||||||
match x.lhs {
|
match x.lhs {
|
||||||
// xxx.prop[expr] | xxx.prop.expr
|
// xxx.prop[expr] | xxx.prop.expr
|
||||||
Expr::Property(ref p) => {
|
Expr::Property(ref p) => {
|
||||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
||||||
p.as_ref();
|
p.as_ref();
|
||||||
let rhs_chain = match_chain_type(rhs);
|
let rhs_chain = match_chaining_type(rhs);
|
||||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||||
let hash_set = FnCallHashes::from_native(*hash_set);
|
let hash_set = FnCallHashes::from_native(*hash_set);
|
||||||
let mut arg_values = [target.as_mut(), &mut Default::default()];
|
let mut arg_values = [target.as_mut(), &mut Default::default()];
|
||||||
@ -1564,6 +1566,7 @@ impl Engine {
|
|||||||
&mut val.into(),
|
&mut val.into(),
|
||||||
root,
|
root,
|
||||||
&x.rhs,
|
&x.rhs,
|
||||||
|
*term,
|
||||||
idx_values,
|
idx_values,
|
||||||
rhs_chain,
|
rhs_chain,
|
||||||
level,
|
level,
|
||||||
@ -1614,9 +1617,9 @@ impl Engine {
|
|||||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||||
Expr::FnCall(ref f, pos) if !f.is_qualified() => {
|
Expr::FnCall(ref f, pos) if !f.is_qualified() => {
|
||||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||||
let rhs_chain = match_chain_type(rhs);
|
let rhs_chain = match_chaining_type(rhs);
|
||||||
let args = &mut idx_val
|
let args = &mut idx_val
|
||||||
.as_fn_call_args()
|
.into_fn_call_args()
|
||||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||||
let (mut val, _) = self.make_method_call(
|
let (mut val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, args, pos, level,
|
mods, state, lib, name, *hashes, target, args, pos, level,
|
||||||
@ -1625,8 +1628,8 @@ impl Engine {
|
|||||||
let target = &mut val.into();
|
let target = &mut val.into();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, target, root, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, target, root, &x.rhs, *term,
|
||||||
rhs_chain, level, new_val,
|
idx_values, rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(pos))
|
.map_err(|err| err.fill_position(pos))
|
||||||
}
|
}
|
||||||
@ -1647,8 +1650,6 @@ impl Engine {
|
|||||||
|
|
||||||
/// Evaluate a dot/index chain.
|
/// Evaluate a dot/index chain.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[must_use]
|
|
||||||
#[must_use]
|
|
||||||
fn eval_dot_index_chain(
|
fn eval_dot_index_chain(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1660,18 +1661,18 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
|
Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos),
|
||||||
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx_values = &mut Default::default();
|
let idx_values = &mut Default::default();
|
||||||
|
|
||||||
self.eval_indexed_chain(
|
self.eval_dot_index_chain_arguments(
|
||||||
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, 0, level,
|
scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
@ -1680,13 +1681,15 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, *var_pos)?;
|
self.inc_operations(state, *var_pos)?;
|
||||||
|
|
||||||
let (target, _) = self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
let (mut target, _) =
|
||||||
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
||||||
|
|
||||||
let obj_ptr = &mut target.into();
|
let obj_ptr = &mut target;
|
||||||
let root = (x.2.as_str(), *var_pos);
|
let root = (x.2.as_str(), *var_pos);
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, &mut None, obj_ptr, root, rhs, idx_values, chain_type, level,
|
mods, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, chain_type,
|
||||||
new_val,
|
level, new_val,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| err.fill_position(op_pos))
|
.map_err(|err| err.fill_position(op_pos))
|
||||||
@ -1699,8 +1702,8 @@ impl Engine {
|
|||||||
let obj_ptr = &mut value.into();
|
let obj_ptr = &mut value.into();
|
||||||
let root = ("", expr.position());
|
let root = ("", expr.position());
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, obj_ptr, root, rhs, idx_values, chain_type, level,
|
mods, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type,
|
||||||
new_val,
|
level, new_val,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.map_err(|err| err.fill_position(op_pos))
|
.map_err(|err| err.fill_position(op_pos))
|
||||||
@ -1712,8 +1715,7 @@ impl Engine {
|
|||||||
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of
|
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of
|
||||||
/// just a few levels of indexing.
|
/// just a few levels of indexing.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[must_use]
|
fn eval_dot_index_chain_arguments(
|
||||||
fn eval_indexed_chain(
|
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -1721,6 +1723,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
|
terminate_chaining: bool,
|
||||||
_parent_chain_type: ChainType,
|
_parent_chain_type: ChainType,
|
||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
size: usize,
|
size: usize,
|
||||||
@ -1731,7 +1734,7 @@ impl Engine {
|
|||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => {
|
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => {
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
args, constants, ..
|
args, constants, ..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
@ -1751,30 +1754,30 @@ impl Engine {
|
|||||||
idx_values.push((arg_values, first_arg_pos).into());
|
idx_values.push((arg_values, first_arg_pos).into());
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
Expr::Property(x) if _parent_chain_type == ChainType::Dotting => {
|
||||||
idx_values.push(ChainArgument::Property((x.2).1))
|
idx_values.push(ChainArgument::Property((x.2).1))
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
Expr::Index(x, _) | Expr::Dot(x, _) => {
|
Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => {
|
||||||
let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref();
|
let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref();
|
||||||
|
|
||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
let lhs_val = match lhs {
|
let lhs_val = match lhs {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
Expr::Property(x) if _parent_chain_type == ChainType::Dotting => {
|
||||||
ChainArgument::Property((x.2).1)
|
ChainArgument::Property((x.2).1)
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::FnCall(x, _)
|
Expr::FnCall(x, _)
|
||||||
if _parent_chain_type == ChainType::Dot && !x.is_qualified() =>
|
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||||
{
|
{
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
args, constants, ..
|
args, constants, ..
|
||||||
@ -1795,41 +1798,37 @@ impl Engine {
|
|||||||
(arg_values, first_arg_pos).into()
|
(arg_values, first_arg_pos).into()
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
expr if _parent_chain_type == ChainType::Dot => {
|
expr if _parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("invalid dot expression: {:?}", expr);
|
unreachable!("invalid dot expression: {:?}", expr);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if _parent_chain_type == ChainType::Index => self
|
_ if _parent_chain_type == ChainType::Indexing => self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
|
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
|
||||||
.map(|v| (v.flatten(), lhs.position()).into())?,
|
.map(|v| (v.flatten(), lhs.position()).into())?,
|
||||||
expr => unreachable!("unknown chained expression: {:?}", expr),
|
expr => unreachable!("unknown chained expression: {:?}", expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push in reverse order
|
// Push in reverse order
|
||||||
let chain_type = match expr {
|
let chain_type = match_chaining_type(expr);
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Expr::Index(_, _) => ChainType::Index,
|
self.eval_dot_index_chain_arguments(
|
||||||
#[cfg(not(feature = "no_object"))]
|
scope, mods, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size,
|
||||||
Expr::Dot(_, _) => ChainType::Dot,
|
level,
|
||||||
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
|
||||||
};
|
|
||||||
self.eval_indexed_chain(
|
|
||||||
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, size, level,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
idx_values.push(lhs_val);
|
idx_values.push(lhs_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
_ if _parent_chain_type == ChainType::Dot => {
|
_ if _parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("invalid dot expression: {:?}", expr);
|
unreachable!("invalid dot expression: {:?}", expr);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if _parent_chain_type == ChainType::Index => idx_values.push(
|
_ if _parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(|v| (v.flatten(), expr.position()).into())?,
|
.map(|v| (v.flatten(), expr.position()).into())?,
|
||||||
),
|
),
|
||||||
@ -1842,7 +1841,6 @@ impl Engine {
|
|||||||
/// Get the value at the indexed position of a base type.
|
/// Get the value at the indexed position of a base type.
|
||||||
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[must_use]
|
|
||||||
fn get_indexed_mut<'t>(
|
fn get_indexed_mut<'t>(
|
||||||
&self,
|
&self,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -2013,7 +2011,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an expression.
|
/// Evaluate an expression.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn eval_expr(
|
pub(crate) fn eval_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -2051,13 +2048,13 @@ impl Engine {
|
|||||||
|
|
||||||
// lhs[idx_expr]
|
// lhs[idx_expr]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(_, _) => {
|
Expr::Index(_, _, _) => {
|
||||||
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
|
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs.dot_rhs
|
// lhs.dot_rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(_, _) => {
|
Expr::Dot(_, _, _) => {
|
||||||
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
|
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2214,7 +2211,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a statements block.
|
/// Evaluate a statements block.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn eval_stmt_block(
|
pub(crate) fn eval_stmt_block(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -2292,7 +2288,6 @@ impl Engine {
|
|||||||
|
|
||||||
/// Evaluate an op-assignment statement.
|
/// Evaluate an op-assignment statement.
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn eval_op_assignment(
|
pub(crate) fn eval_op_assignment(
|
||||||
&self,
|
&self,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -2366,7 +2361,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// This method uses some unsafe code, mainly for avoiding cloning of local variable names via
|
/// This method uses some unsafe code, mainly for avoiding cloning of local variable names via
|
||||||
/// direct lifetime casting.
|
/// direct lifetime casting.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn eval_stmt(
|
pub(crate) fn eval_stmt(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -2436,7 +2430,7 @@ impl Engine {
|
|||||||
let rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info.clone(), *op_pos)));
|
let _new_val = Some(((rhs_val, rhs_expr.position()), (*op_info, *op_pos)));
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
match lhs_expr {
|
match lhs_expr {
|
||||||
@ -2446,7 +2440,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// idx_lhs[idx_expr] op= rhs
|
// idx_lhs[idx_expr] op= rhs
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(_, _) => {
|
Expr::Index(_, _, _) => {
|
||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||||
)?;
|
)?;
|
||||||
@ -2454,7 +2448,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// dot_lhs.dot_rhs op= rhs
|
// dot_lhs.dot_rhs op= rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(_, _) => {
|
Expr::Dot(_, _, _) => {
|
||||||
self.eval_dot_index_chain(
|
self.eval_dot_index_chain(
|
||||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||||
)?;
|
)?;
|
||||||
@ -2953,7 +2947,7 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorModuleNotFound(path.to_string(), path_pos).into()
|
EvalAltResult::ErrorModuleNotFound(path.to_string(), path_pos).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
export.as_ref().map(|x| x.name.clone()).map(|name| {
|
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = crate::fn_native::shared_take_or_clone(module);
|
let mut module = crate::fn_native::shared_take_or_clone(module);
|
||||||
@ -2962,7 +2956,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
mods.push(name, module);
|
mods.push(name, module);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
state.modules += 1;
|
state.modules += 1;
|
||||||
|
|
||||||
@ -2992,14 +2986,14 @@ impl Engine {
|
|||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(name) => {
|
Stmt::Share(name) => {
|
||||||
scope.get_index(name).map(|(index, _)| {
|
if let Some((index, _)) = scope.get_index(name) {
|
||||||
let val = scope.get_mut_by_index(index);
|
let val = scope.get_mut_by_index(index);
|
||||||
|
|
||||||
if !val.is_shared() {
|
if !val.is_shared() {
|
||||||
// Replace the variable with a shared value.
|
// Replace the variable with a shared value.
|
||||||
*val = std::mem::take(val).into_shared();
|
*val = std::mem::take(val).into_shared();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -3011,7 +3005,6 @@ impl Engine {
|
|||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -3019,20 +3012,17 @@ impl Engine {
|
|||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
|
||||||
result.and_then(|r| self.check_data_size(&r).map(|_| r))
|
result.and_then(|r| self.check_data_size(&r).map(|_| r))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn check_data_size(&self, _value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
|
fn check_data_size(&self, _value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[must_use]
|
|
||||||
fn check_data_size(&self, value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
|
fn check_data_size(&self, value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
|
||||||
// Recursively calculate the size of a value (especially `Array` and `Map`)
|
// Recursively calculate the size of a value (especially `Array` and `Map`)
|
||||||
fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
|
fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
|
||||||
@ -3135,7 +3125,6 @@ impl Engine {
|
|||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn inc_operations(
|
pub(crate) fn inc_operations(
|
||||||
&self,
|
&self,
|
||||||
state: &mut EvalState,
|
state: &mut EvalState,
|
||||||
|
@ -329,8 +329,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
|
get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_getter;
|
self.register_fn(&crate::engine::make_getter(name), get_fn)
|
||||||
self.register_fn(&make_getter(name), get_fn)
|
|
||||||
}
|
}
|
||||||
/// Register a getter function for a member of a registered type with the [`Engine`].
|
/// Register a getter function for a member of a registered type with the [`Engine`].
|
||||||
///
|
///
|
||||||
@ -378,8 +377,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
get_fn: impl Fn(&mut T) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_getter;
|
self.register_result_fn(&crate::engine::make_getter(name), get_fn)
|
||||||
self.register_result_fn(&make_getter(name), get_fn)
|
|
||||||
}
|
}
|
||||||
/// Register a setter function for a member of a registered type with the [`Engine`].
|
/// Register a setter function for a member of a registered type with the [`Engine`].
|
||||||
///
|
///
|
||||||
@ -426,8 +424,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
set_fn: impl Fn(&mut T, V) + SendSync + 'static,
|
set_fn: impl Fn(&mut T, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_setter;
|
self.register_fn(&crate::engine::make_setter(name), set_fn)
|
||||||
self.register_fn(&make_setter(name), set_fn)
|
|
||||||
}
|
}
|
||||||
/// Register a setter function for a member of a registered type with the [`Engine`].
|
/// Register a setter function for a member of a registered type with the [`Engine`].
|
||||||
///
|
///
|
||||||
@ -477,8 +474,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_setter;
|
self.register_result_fn(&crate::engine::make_setter(name), set_fn)
|
||||||
self.register_result_fn(&make_setter(name), set_fn)
|
|
||||||
}
|
}
|
||||||
/// Short-hand for registering both getter and setter functions
|
/// Short-hand for registering both getter and setter functions
|
||||||
/// of a registered type with the [`Engine`].
|
/// of a registered type with the [`Engine`].
|
||||||
@ -883,7 +879,7 @@ impl Engine {
|
|||||||
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
||||||
set_fn: impl Fn(&mut T, X, V) -> () + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_indexer_get(get_fn)
|
self.register_indexer_get(get_fn)
|
||||||
.register_indexer_set(set_fn)
|
.register_indexer_set(set_fn)
|
||||||
@ -1011,7 +1007,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_with_scope(&Default::default(), script)
|
self.compile_with_scope(&Default::default(), script)
|
||||||
}
|
}
|
||||||
@ -1054,7 +1049,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_scripts_with_scope(scope, &[script])
|
self.compile_scripts_with_scope(scope, &[script])
|
||||||
}
|
}
|
||||||
@ -1068,7 +1062,6 @@ impl Engine {
|
|||||||
/// [`AST`]. When it is evaluated later, `import` statement directly recall pre-resolved
|
/// [`AST`]. When it is evaluated later, `import` statement directly recall pre-resolved
|
||||||
/// [modules][Module] and the resolution process is not performed again.
|
/// [modules][Module] and the resolution process is not performed again.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_into_self_contained(
|
pub fn compile_into_self_contained(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1107,7 +1100,7 @@ impl Engine {
|
|||||||
let mut resolver = StaticModuleResolver::new();
|
let mut resolver = StaticModuleResolver::new();
|
||||||
let mut imports = Default::default();
|
let mut imports = Default::default();
|
||||||
|
|
||||||
collect_imports(&ast, &mut resolver, &mut imports);
|
collect_imports(&ast, &resolver, &mut imports);
|
||||||
|
|
||||||
if !imports.is_empty() {
|
if !imports.is_empty() {
|
||||||
while let Some(path) = imports.iter().next() {
|
while let Some(path) = imports.iter().next() {
|
||||||
@ -1115,7 +1108,7 @@ impl Engine {
|
|||||||
|
|
||||||
match module_resolver.resolve_ast(self, None, &path, Position::NONE) {
|
match module_resolver.resolve_ast(self, None, &path, Position::NONE) {
|
||||||
Some(Ok(module_ast)) => {
|
Some(Ok(module_ast)) => {
|
||||||
collect_imports(&module_ast, &mut resolver, &mut imports)
|
collect_imports(&module_ast, &resolver, &mut imports)
|
||||||
}
|
}
|
||||||
Some(err) => return err,
|
Some(err) => return err,
|
||||||
None => (),
|
None => (),
|
||||||
@ -1180,7 +1173,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_scripts_with_scope(
|
pub fn compile_scripts_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1189,8 +1181,7 @@ impl Engine {
|
|||||||
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
|
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
|
||||||
}
|
}
|
||||||
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
|
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn compile_with_scope_and_optimization_level(
|
pub(crate) fn compile_with_scope_and_optimization_level(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1209,7 +1200,6 @@ impl Engine {
|
|||||||
/// Read the contents of a file into a string.
|
/// Read the contents of a file into a string.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
#[must_use]
|
|
||||||
fn read_file(path: std::path::PathBuf) -> Result<String, Box<EvalAltResult>> {
|
fn read_file(path: std::path::PathBuf) -> Result<String, Box<EvalAltResult>> {
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
@ -1265,7 +1255,6 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_file(&self, path: std::path::PathBuf) -> Result<AST, Box<EvalAltResult>> {
|
pub fn compile_file(&self, path: std::path::PathBuf) -> Result<AST, Box<EvalAltResult>> {
|
||||||
self.compile_file_with_scope(&Default::default(), path)
|
self.compile_file_with_scope(&Default::default(), path)
|
||||||
}
|
}
|
||||||
@ -1305,7 +1294,6 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_file_with_scope(
|
pub fn compile_file_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1357,7 +1345,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[must_use]
|
|
||||||
pub fn parse_json(
|
pub fn parse_json(
|
||||||
&self,
|
&self,
|
||||||
json: impl AsRef<str>,
|
json: impl AsRef<str>,
|
||||||
@ -1433,7 +1420,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
|
||||||
self.compile_expression_with_scope(&Default::default(), script)
|
self.compile_expression_with_scope(&Default::default(), script)
|
||||||
}
|
}
|
||||||
@ -1476,8 +1462,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn compile_expression_with_scope(
|
pub fn compile_expression_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
@ -1510,7 +1495,6 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_file<T: Variant + Clone>(
|
pub fn eval_file<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
path: std::path::PathBuf,
|
path: std::path::PathBuf,
|
||||||
@ -1541,7 +1525,6 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_file_with_scope<T: Variant + Clone>(
|
pub fn eval_file_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1564,7 +1547,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
|
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
|
||||||
self.eval_with_scope(&mut Default::default(), script)
|
self.eval_with_scope(&mut Default::default(), script)
|
||||||
}
|
}
|
||||||
@ -1591,7 +1573,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_with_scope<T: Variant + Clone>(
|
pub fn eval_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1619,7 +1600,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_expression<T: Variant + Clone>(
|
pub fn eval_expression<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
@ -1644,8 +1624,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_expression_with_scope<T: Variant + Clone>(
|
pub fn eval_expression_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1684,7 +1663,6 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
||||||
self.eval_ast_with_scope(&mut Default::default(), ast)
|
self.eval_ast_with_scope(&mut Default::default(), ast)
|
||||||
}
|
}
|
||||||
@ -1717,8 +1695,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_ast_with_scope<T: Variant + Clone>(
|
pub fn eval_ast_with_scope<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1730,18 +1707,17 @@ impl Engine {
|
|||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
return result.try_cast::<T>().ok_or_else(|| {
|
result.try_cast::<T>().ok_or_else(|| {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<T>()).into(),
|
self.map_type_name(type_name::<T>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
/// Evaluate an [`AST`] with own scope.
|
/// Evaluate an [`AST`] with own scope.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1797,7 +1773,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
||||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn consume_with_scope(
|
pub fn consume_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1824,7 +1800,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
|
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
|
||||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn consume_ast_with_scope(
|
pub fn consume_ast_with_scope(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1889,8 +1865,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn call_fn<T: Variant + Clone>(
|
pub fn call_fn<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1901,6 +1876,7 @@ impl Engine {
|
|||||||
let mut arg_values: crate::StaticVec<_> = Default::default();
|
let mut arg_values: crate::StaticVec<_> = Default::default();
|
||||||
args.parse(&mut arg_values);
|
args.parse(&mut arg_values);
|
||||||
let mut args: crate::StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: crate::StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
let name = name.as_ref();
|
||||||
|
|
||||||
let result = self.call_fn_dynamic_raw(scope, ast, true, name, &mut None, &mut args)?;
|
let result = self.call_fn_dynamic_raw(scope, ast, true, name, &mut None, &mut args)?;
|
||||||
|
|
||||||
@ -1969,8 +1945,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn call_fn_dynamic(
|
pub fn call_fn_dynamic(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1980,6 +1955,7 @@ impl Engine {
|
|||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let name = name.as_ref();
|
||||||
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
|
||||||
|
|
||||||
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, &mut args)
|
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, &mut args)
|
||||||
@ -1993,14 +1969,13 @@ impl Engine {
|
|||||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn call_fn_dynamic_raw(
|
pub(crate) fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
eval_ast: bool,
|
eval_ast: bool,
|
||||||
name: impl AsRef<str>,
|
name: &str,
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -2008,7 +1983,6 @@ impl Engine {
|
|||||||
let mods = &mut Default::default();
|
let mods = &mut Default::default();
|
||||||
let lib = &[ast.lib()];
|
let lib = &[ast.lib()];
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
let name = name.as_ref();
|
|
||||||
|
|
||||||
if eval_ast && !statements.is_empty() {
|
if eval_ast && !statements.is_empty() {
|
||||||
self.eval_global_statements(scope, mods, state, statements, lib, 0)?;
|
self.eval_global_statements(scope, mods, state, statements, lib, 0)?;
|
||||||
@ -2051,7 +2025,7 @@ impl Engine {
|
|||||||
/// (i.e. with [`Scope::push_constant`]).
|
/// (i.e. with [`Scope::push_constant`]).
|
||||||
/// Then, the [`AST`] is cloned and the copy re-optimized before running.
|
/// Then, the [`AST`] is cloned and the copy re-optimized before running.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn optimize_ast(
|
pub fn optimize_ast(
|
||||||
&self,
|
&self,
|
||||||
@ -2086,6 +2060,7 @@ impl Engine {
|
|||||||
/// 2) Functions in registered sub-modules
|
/// 2) Functions in registered sub-modules
|
||||||
/// 3) Functions in packages (optional)
|
/// 3) Functions in packages (optional)
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
|
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
|
||||||
let mut signatures: Vec<_> = Default::default();
|
let mut signatures: Vec<_> = Default::default();
|
||||||
|
@ -291,7 +291,6 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
|
||||||
pub fn register_custom_operator(
|
pub fn register_custom_operator(
|
||||||
&mut self,
|
&mut self,
|
||||||
keyword: impl AsRef<str> + Into<Identifier>,
|
keyword: impl AsRef<str> + Into<Identifier>,
|
||||||
@ -310,18 +309,18 @@ impl Engine {
|
|||||||
// Disabled keywords are OK
|
// Disabled keywords are OK
|
||||||
Some(token) if token.is_keyword() => {
|
Some(token) if token.is_keyword() => {
|
||||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||||
return Err(format!("'{}' is a reserved keyword", keyword.as_ref()).into());
|
return Err(format!("'{}' is a reserved keyword", keyword.as_ref()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Active standard symbols cannot be made custom
|
// Active standard symbols cannot be made custom
|
||||||
Some(token) if token.is_symbol() => {
|
Some(token) if token.is_symbol() => {
|
||||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||||
return Err(format!("'{}' is a reserved operator", keyword.as_ref()).into());
|
return Err(format!("'{}' is a reserved operator", keyword.as_ref()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Active standard symbols cannot be made custom
|
// Active standard symbols cannot be made custom
|
||||||
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
|
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||||
return Err(format!("'{}' is a reserved symbol", keyword.as_ref()).into())
|
return Err(format!("'{}' is a reserved symbol", keyword.as_ref()))
|
||||||
}
|
}
|
||||||
// Disabled symbols are OK
|
// Disabled symbols are OK
|
||||||
Some(_) => (),
|
Some(_) => (),
|
||||||
|
@ -39,7 +39,6 @@ pub enum LexError {
|
|||||||
impl Error for LexError {}
|
impl Error for LexError {}
|
||||||
|
|
||||||
impl fmt::Display for LexError {
|
impl fmt::Display for LexError {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
||||||
@ -283,7 +282,6 @@ pub struct ParseError(pub Box<ParseErrorType>, pub Position);
|
|||||||
impl Error for ParseError {}
|
impl Error for ParseError {}
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0, f)?;
|
fmt::Display::fmt(&self.0, f)?;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ use rust_decimal::Decimal;
|
|||||||
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
|
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
|
||||||
|
|
||||||
/// Is the type a numeric type?
|
/// Is the type a numeric type?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn is_numeric(type_id: TypeId) -> bool {
|
fn is_numeric(type_id: TypeId) -> bool {
|
||||||
let result = type_id == TypeId::of::<u8>()
|
let result = type_id == TypeId::of::<u8>()
|
||||||
@ -206,7 +206,6 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
|
|
||||||
// char op string
|
// char op string
|
||||||
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||||
#[inline(always)]
|
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = args[0].as_char().expect(BUILTIN);
|
let x = args[0].as_char().expect(BUILTIN);
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
@ -233,7 +232,6 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
}
|
}
|
||||||
// string op char
|
// string op char
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||||
#[inline(always)]
|
|
||||||
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
let y = args[1].as_char().expect(BUILTIN);
|
let y = args[1].as_char().expect(BUILTIN);
|
||||||
|
@ -84,7 +84,9 @@ impl<'a> ArgBackup<'a> {
|
|||||||
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
|
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
|
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
|
||||||
self.orig_mut.take().map(|p| args[0] = p);
|
if let Some(p) = self.orig_mut.take() {
|
||||||
|
args[0] = p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +102,7 @@ impl Drop for ArgBackup<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn ensure_no_data_race(
|
pub fn ensure_no_data_race(
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &FnCallArgs,
|
args: &FnCallArgs,
|
||||||
@ -157,7 +158,6 @@ impl Engine {
|
|||||||
/// 3) Global modules - packages
|
/// 3) Global modules - packages
|
||||||
/// 4) Imported modules - functions marked with global namespace
|
/// 4) Imported modules - functions marked with global namespace
|
||||||
/// 5) Global sub-modules - functions marked with global namespace
|
/// 5) Global sub-modules - functions marked with global namespace
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn resolve_fn<'s>(
|
fn resolve_fn<'s>(
|
||||||
&self,
|
&self,
|
||||||
@ -262,7 +262,6 @@ impl Engine {
|
|||||||
/// Function call arguments be _consumed_ when the function requires them to be passed by value.
|
/// Function call arguments be _consumed_ when the function requires them to be passed by value.
|
||||||
/// All function arguments not in the first position are always passed by value and thus consumed.
|
/// All function arguments not in the first position are always passed by value and thus consumed.
|
||||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn call_native_fn(
|
pub(crate) fn call_native_fn(
|
||||||
&self,
|
&self,
|
||||||
mods: &Imports,
|
mods: &Imports,
|
||||||
@ -291,7 +290,7 @@ impl Engine {
|
|||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_method_call && func.is_pure() && !args.is_empty() {
|
if is_method_call && func.is_pure() && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
backup.as_mut().unwrap().change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
@ -312,7 +311,9 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
backup.map(|bk| bk.restore_first_arg(args));
|
if let Some(bk) = backup {
|
||||||
|
bk.restore_first_arg(args)
|
||||||
|
}
|
||||||
|
|
||||||
let result = result.map_err(|err| err.fill_position(pos))?;
|
let result = result.map_err(|err| err.fill_position(pos))?;
|
||||||
|
|
||||||
@ -434,7 +435,6 @@ impl Engine {
|
|||||||
/// All function arguments not in the first position are always passed by value and thus consumed.
|
/// All function arguments not in the first position are always passed by value and thus consumed.
|
||||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn call_script_fn(
|
pub(crate) fn call_script_fn(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -447,7 +447,6 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
#[inline(always)]
|
|
||||||
fn make_error(
|
fn make_error(
|
||||||
name: String,
|
name: String,
|
||||||
fn_def: &crate::ast::ScriptFnDef,
|
fn_def: &crate::ast::ScriptFnDef,
|
||||||
@ -564,7 +563,6 @@ impl Engine {
|
|||||||
|
|
||||||
// Does a scripted function exist?
|
// Does a scripted function exist?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn has_script_fn(
|
pub(crate) fn has_script_fn(
|
||||||
&self,
|
&self,
|
||||||
@ -604,7 +602,6 @@ impl Engine {
|
|||||||
/// Function call arguments may be _consumed_ when the function requires them to be passed by value.
|
/// Function call arguments may be _consumed_ when the function requires them to be passed by value.
|
||||||
/// All function arguments not in the first position are always passed by value and thus consumed.
|
/// All function arguments not in the first position are always passed by value and thus consumed.
|
||||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn exec_fn_call(
|
pub(crate) fn exec_fn_call(
|
||||||
&self,
|
&self,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -619,7 +616,6 @@ impl Engine {
|
|||||||
_capture_scope: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
#[inline(always)]
|
|
||||||
fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
let msg = format!("'{0}' should not be called this way. Try {0}(...);", name);
|
let msg = format!("'{0}' should not be called this way. Try {0}(...);", name);
|
||||||
EvalAltResult::ErrorRuntime(msg.into(), pos).into()
|
EvalAltResult::ErrorRuntime(msg.into(), pos).into()
|
||||||
@ -706,7 +702,7 @@ impl Engine {
|
|||||||
// Move captured variables into scope
|
// Move captured variables into scope
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if !func.externals.is_empty() {
|
if !func.externals.is_empty() {
|
||||||
_capture_scope.map(|captured| {
|
if let Some(captured) = _capture_scope {
|
||||||
captured
|
captured
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
|
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
|
||||||
@ -714,7 +710,7 @@ impl Engine {
|
|||||||
// Consume the scope values.
|
// Consume the scope values.
|
||||||
scope.push_dynamic(name, value);
|
scope.push_dynamic(name, value);
|
||||||
})
|
})
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if _is_method_call {
|
let result = if _is_method_call {
|
||||||
@ -750,7 +746,7 @@ impl Engine {
|
|||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_ref_mut && !args.is_empty() {
|
if is_ref_mut && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
backup.as_mut().unwrap().change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = state.source.take();
|
||||||
@ -765,7 +761,9 @@ impl Engine {
|
|||||||
state.source = orig_source;
|
state.source = orig_source;
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
backup.map(|bk| bk.restore_first_arg(args));
|
if let Some(bk) = backup {
|
||||||
|
bk.restore_first_arg(args)
|
||||||
|
}
|
||||||
|
|
||||||
result?
|
result?
|
||||||
};
|
};
|
||||||
@ -782,8 +780,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// Evaluate a list of statements with no `this` pointer.
|
/// Evaluate a list of statements with no `this` pointer.
|
||||||
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
|
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn eval_global_statements(
|
pub(crate) fn eval_global_statements(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -804,7 +801,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a text script in place - used primarily for 'eval'.
|
/// Evaluate a text script in place - used primarily for 'eval'.
|
||||||
#[must_use]
|
|
||||||
fn eval_script_expr_in_place(
|
fn eval_script_expr_in_place(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -856,7 +852,6 @@ impl Engine {
|
|||||||
|
|
||||||
/// Call a dot method.
|
/// Call a dot method.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn make_method_call(
|
pub(crate) fn make_method_call(
|
||||||
&self,
|
&self,
|
||||||
mods: &mut Imports,
|
mods: &mut Imports,
|
||||||
@ -895,7 +890,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
KEYWORD_FN_PTR_CALL => {
|
KEYWORD_FN_PTR_CALL => {
|
||||||
if call_args.len() > 0 {
|
if !call_args.is_empty() {
|
||||||
if !call_args[0].is::<FnPtr>() {
|
if !call_args[0].is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
self.map_type_name(call_args[0].type_name()),
|
self.map_type_name(call_args[0].type_name()),
|
||||||
@ -1018,8 +1013,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an argument.
|
/// Evaluate an argument.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn get_arg_value(
|
pub(crate) fn get_arg_value(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1041,7 +1035,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn make_function_call(
|
pub(crate) fn make_function_call(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1107,7 +1100,7 @@ impl Engine {
|
|||||||
return arg
|
return arg
|
||||||
.as_immutable_string()
|
.as_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
|
||||||
.and_then(|s| FnPtr::try_from(s))
|
.and_then(FnPtr::try_from)
|
||||||
.map(Into::<Dynamic>::into)
|
.map(Into::<Dynamic>::into)
|
||||||
.map_err(|err| err.fill_position(arg_pos));
|
.map_err(|err| err.fill_position(arg_pos));
|
||||||
}
|
}
|
||||||
@ -1293,7 +1286,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a namespace-qualified function in normal function-call style.
|
/// Call a namespace-qualified function in normal function-call style.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn make_qualified_function_call(
|
pub(crate) fn make_qualified_function_call(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -1390,10 +1382,10 @@ impl Engine {
|
|||||||
|
|
||||||
// Clone first argument if the function is not a method after-all
|
// Clone first argument if the function is not a method after-all
|
||||||
if !func.map(|f| f.is_method()).unwrap_or(true) {
|
if !func.map(|f| f.is_method()).unwrap_or(true) {
|
||||||
first_arg_value.map(|first| {
|
if let Some(first) = first_arg_value {
|
||||||
*first = args[0].clone();
|
*first = args[0].clone();
|
||||||
args[0] = first;
|
args[0] = first;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match func {
|
match func {
|
||||||
|
@ -21,7 +21,7 @@ impl Hasher for StraightHasher {
|
|||||||
fn finish(&self) -> u64 {
|
fn finish(&self) -> u64 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn write(&mut self, bytes: &[u8]) {
|
fn write(&mut self, bytes: &[u8]) {
|
||||||
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
|
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
|
||||||
|
|
||||||
@ -62,10 +62,7 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_qualified_var_hash<'a>(
|
pub fn calc_qualified_var_hash<'a>(modules: impl Iterator<Item = &'a str>, var_name: &str) -> u64 {
|
||||||
modules: impl Iterator<Item = &'a str>,
|
|
||||||
var_name: impl AsRef<str>,
|
|
||||||
) -> u64 {
|
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
|
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
@ -75,7 +72,7 @@ pub fn calc_qualified_var_hash<'a>(
|
|||||||
.skip(1)
|
.skip(1)
|
||||||
.for_each(|m| m.hash(s));
|
.for_each(|m| m.hash(s));
|
||||||
len.hash(s);
|
len.hash(s);
|
||||||
var_name.as_ref().hash(s);
|
var_name.hash(s);
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +89,7 @@ pub fn calc_qualified_var_hash<'a>(
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_qualified_fn_hash<'a>(
|
pub fn calc_qualified_fn_hash<'a>(
|
||||||
modules: impl Iterator<Item = &'a str>,
|
modules: impl Iterator<Item = &'a str>,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: &str,
|
||||||
num: usize,
|
num: usize,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
@ -104,7 +101,7 @@ pub fn calc_qualified_fn_hash<'a>(
|
|||||||
.skip(1)
|
.skip(1)
|
||||||
.for_each(|m| m.hash(s));
|
.for_each(|m| m.hash(s));
|
||||||
len.hash(s);
|
len.hash(s);
|
||||||
fn_name.as_ref().hash(s);
|
fn_name.hash(s);
|
||||||
num.hash(s);
|
num.hash(s);
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
@ -115,7 +112,7 @@ pub fn calc_qualified_fn_hash<'a>(
|
|||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
|
pub fn calc_fn_hash(fn_name: &str, num: usize) -> u64 {
|
||||||
calc_qualified_fn_hash(empty(), fn_name, num)
|
calc_qualified_fn_hash(empty(), fn_name, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +124,10 @@ pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
|
|||||||
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
params.inspect(|_| len += 1).for_each(|t| t.hash(s));
|
params.for_each(|t| {
|
||||||
|
len += 1;
|
||||||
|
t.hash(s);
|
||||||
|
});
|
||||||
len.hash(s);
|
len.hash(s);
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,6 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.mods.iter().flat_map(|&m| m.iter())
|
self.mods.iter().flat_map(|&m| m.iter())
|
||||||
}
|
}
|
||||||
@ -152,7 +151,6 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> {
|
) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> {
|
||||||
@ -171,7 +169,6 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
}
|
}
|
||||||
/// Get an iterator over the namespaces containing definitions of all script-defined functions.
|
/// Get an iterator over the namespaces containing definitions of all script-defined functions.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
||||||
self.lib.iter().cloned()
|
self.lib.iter().cloned()
|
||||||
}
|
}
|
||||||
@ -195,8 +192,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
///
|
///
|
||||||
/// If `is_method` is [`true`], the first argument is assumed to be passed
|
/// If `is_method` is [`true`], the first argument is assumed to be passed
|
||||||
/// by reference and is not consumed.
|
/// by reference and is not consumed.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn call_fn_dynamic_raw(
|
pub fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
@ -249,7 +245,6 @@ pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
|
|||||||
|
|
||||||
/// Consume a [`Shared`] resource if is unique (i.e. not shared).
|
/// Consume a [`Shared`] resource if is unique (i.e. not shared).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
||||||
Shared::try_unwrap(value)
|
Shared::try_unwrap(value)
|
||||||
}
|
}
|
||||||
@ -338,7 +333,6 @@ pub enum CallableFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CallableFunction {
|
impl fmt::Debug for CallableFunction {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(_) => write!(f, "NativePureFunction"),
|
Self::Pure(_) => write!(f, "NativePureFunction"),
|
||||||
@ -353,7 +347,6 @@ impl fmt::Debug for CallableFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CallableFunction {
|
impl fmt::Display for CallableFunction {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(_) => write!(f, "NativePureFunction"),
|
Self::Pure(_) => write!(f, "NativePureFunction"),
|
||||||
@ -369,7 +362,7 @@ impl fmt::Display for CallableFunction {
|
|||||||
|
|
||||||
impl CallableFunction {
|
impl CallableFunction {
|
||||||
/// Is this a pure native Rust function?
|
/// Is this a pure native Rust function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_pure(&self) -> bool {
|
pub fn is_pure(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -383,7 +376,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this a native Rust method function?
|
/// Is this a native Rust method function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_method(&self) -> bool {
|
pub fn is_method(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -397,7 +390,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this an iterator function?
|
/// Is this an iterator function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_iter(&self) -> bool {
|
pub const fn is_iter(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -409,7 +402,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this a Rhai-scripted function?
|
/// Is this a Rhai-scripted function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_script(&self) -> bool {
|
pub const fn is_script(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -420,7 +413,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this a plugin function?
|
/// Is this a plugin function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_plugin_fn(&self) -> bool {
|
pub const fn is_plugin_fn(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -432,7 +425,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this a native Rust function?
|
/// Is this a native Rust function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_native(&self) -> bool {
|
pub const fn is_native(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -445,7 +438,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the access mode.
|
/// Get the access mode.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn access(&self) -> FnAccess {
|
pub fn access(&self) -> FnAccess {
|
||||||
match self {
|
match self {
|
||||||
@ -457,7 +450,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a shared reference to a native Rust function.
|
/// Get a shared reference to a native Rust function.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
||||||
match self {
|
match self {
|
||||||
@ -472,7 +465,7 @@ impl CallableFunction {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
|
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
|
||||||
match self {
|
match self {
|
||||||
@ -481,7 +474,7 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a reference to an iterator function.
|
/// Get a reference to an iterator function.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_iter_fn(&self) -> Option<IteratorFn> {
|
pub fn get_iter_fn(&self) -> Option<IteratorFn> {
|
||||||
match self {
|
match self {
|
||||||
@ -493,9 +486,9 @@ impl CallableFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a shared reference to a plugin function.
|
/// Get a shared reference to a plugin function.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_plugin_fn<'s>(&'s self) -> Option<&Shared<FnPlugin>> {
|
pub fn get_plugin_fn(&self) -> Option<&Shared<FnPlugin>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Plugin(f) => Some(f),
|
Self::Plugin(f) => Some(f),
|
||||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None,
|
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None,
|
||||||
@ -557,6 +550,6 @@ impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
|
|||||||
impl From<Shared<FnPlugin>> for CallableFunction {
|
impl From<Shared<FnPlugin>> for CallableFunction {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(func: Shared<FnPlugin>) -> Self {
|
fn from(func: Shared<FnPlugin>) -> Self {
|
||||||
Self::Plugin(func.into())
|
Self::Plugin(func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ pub struct FnPtr(Identifier, StaticVec<Dynamic>);
|
|||||||
impl FnPtr {
|
impl FnPtr {
|
||||||
/// Create a new function pointer.
|
/// Create a new function pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> {
|
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> {
|
||||||
name.into().try_into()
|
name.into().try_into()
|
||||||
}
|
}
|
||||||
@ -27,7 +26,7 @@ impl FnPtr {
|
|||||||
#[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: Identifier, curry: StaticVec<Dynamic>) -> Self {
|
||||||
Self(name.into(), curry)
|
Self(name, curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -96,8 +95,7 @@ impl FnPtr {
|
|||||||
/// This is to avoid unnecessarily cloning the arguments.
|
/// This is to avoid unnecessarily cloning the arguments.
|
||||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||||
/// clone them _before_ calling this function.
|
/// clone them _before_ calling this function.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn call_dynamic(
|
pub fn call_dynamic(
|
||||||
&self,
|
&self,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
@ -127,7 +125,6 @@ impl FnPtr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FnPtr {
|
impl fmt::Display for FnPtr {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Fn({})", self.0)
|
write!(f, "Fn({})", self.0)
|
||||||
}
|
}
|
||||||
@ -136,7 +133,7 @@ impl fmt::Display for FnPtr {
|
|||||||
impl TryFrom<Identifier> for FnPtr {
|
impl TryFrom<Identifier> for FnPtr {
|
||||||
type Error = Box<EvalAltResult>;
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn try_from(value: Identifier) -> Result<Self, Self::Error> {
|
fn try_from(value: Identifier) -> Result<Self, Self::Error> {
|
||||||
if is_valid_identifier(value.chars()) {
|
if is_valid_identifier(value.chars()) {
|
||||||
Ok(Self(value, Default::default()))
|
Ok(Self(value, Default::default()))
|
||||||
|
@ -88,7 +88,7 @@ pub trait RegisterNativeFunction<Args, Result> {
|
|||||||
fn return_type_name() -> &'static str;
|
fn return_type_name() -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn is_setter(_fn_name: &str) -> bool {
|
fn is_setter(_fn_name: &str) -> bool {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
@ -16,8 +16,8 @@ use std::{
|
|||||||
|
|
||||||
/// The system immutable string type.
|
/// The system immutable string type.
|
||||||
///
|
///
|
||||||
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>`
|
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`SmartString`][smartstring::SmartString]`>`
|
||||||
/// (or [`Arc`][std::sync::Arc]`<`[`String`]`>` under the `sync` feature)
|
/// (or [`Arc`][std::sync::Arc]`<`[`SmartString`][smartstring::SmartString]`>` under the `sync` feature)
|
||||||
/// so that it can be simply shared and not cloned.
|
/// so that it can be simply shared and not cloned.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -255,12 +255,10 @@ impl Add<&str> for ImmutableString {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add(mut self, rhs: &str) -> Self::Output {
|
fn add(mut self, rhs: &str) -> Self::Output {
|
||||||
if rhs.is_empty() {
|
if !rhs.is_empty() {
|
||||||
self
|
|
||||||
} else {
|
|
||||||
self.make_mut().push_str(rhs);
|
self.make_mut().push_str(rhs);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,7 +320,7 @@ impl Add<String> for &ImmutableString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<String> for ImmutableString {
|
impl AddAssign<String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn add_assign(&mut self, rhs: String) {
|
fn add_assign(&mut self, rhs: String) {
|
||||||
if !rhs.is_empty() {
|
if !rhs.is_empty() {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
|
@ -112,10 +112,10 @@ impl FuncInfo {
|
|||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn calc_native_fn_hash<'a>(
|
fn calc_native_fn_hash<'a>(
|
||||||
modules: impl Iterator<Item = &'a str>,
|
modules: impl Iterator<Item = &'a str>,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: &str,
|
||||||
params: &[TypeId],
|
params: &[TypeId],
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len());
|
let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len());
|
||||||
@ -324,7 +324,7 @@ impl Module {
|
|||||||
/// let module = Module::new();
|
/// let module = Module::new();
|
||||||
/// assert!(module.is_empty());
|
/// assert!(module.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.functions.is_empty()
|
self.functions.is_empty()
|
||||||
@ -437,7 +437,7 @@ impl Module {
|
|||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn set_var(
|
pub fn set_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Identifier>,
|
name: impl Into<Identifier>,
|
||||||
@ -457,7 +457,6 @@ impl Module {
|
|||||||
/// Get a reference to a namespace-qualified variable.
|
/// Get a reference to a namespace-qualified variable.
|
||||||
/// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
|
/// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
|
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
|
||||||
self.all_variables.get(&hash_var).ok_or_else(|| {
|
self.all_variables.get(&hash_var).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
|
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
|
||||||
@ -519,7 +518,7 @@ impl Module {
|
|||||||
/// By taking a mutable reference, it is assumed that some sub-modules will be modified.
|
/// By taking a mutable reference, it is assumed that some sub-modules will be modified.
|
||||||
/// Thus the [`Module`] is automatically set to be non-indexed.
|
/// Thus the [`Module`] is automatically set to be non-indexed.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
|
pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
|
||||||
// We must assume that the user has changed the sub-modules
|
// We must assume that the user has changed the sub-modules
|
||||||
@ -580,7 +579,7 @@ impl Module {
|
|||||||
/// module.set_sub_module("question", sub_module);
|
/// module.set_sub_module("question", sub_module);
|
||||||
/// assert!(module.get_sub_module("question").is_some());
|
/// assert!(module.get_sub_module("question").is_some());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn set_sub_module(
|
pub fn set_sub_module(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Identifier>,
|
name: impl Into<Identifier>,
|
||||||
@ -624,7 +623,7 @@ impl Module {
|
|||||||
/// The _last entry_ in the list should be the _return type_ of the function.
|
/// The _last entry_ in the list should be the _return type_ of the function.
|
||||||
/// In other words, the number of entries should be one larger than the number of parameters.
|
/// In other words, the number of entries should be one larger than the number of parameters.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
|
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
|
||||||
let param_names = arg_names
|
let param_names = arg_names
|
||||||
.iter()
|
.iter()
|
||||||
@ -641,7 +640,7 @@ impl Module {
|
|||||||
/// Update the namespace of a registered function.
|
/// Update the namespace of a registered function.
|
||||||
///
|
///
|
||||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||||
#[inline(always)]
|
#[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.namespace = namespace;
|
||||||
@ -652,7 +651,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remap type ID.
|
/// Remap type ID.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn map_type(map: bool, type_id: TypeId) -> TypeId {
|
fn map_type(map: bool, type_id: TypeId) -> TypeId {
|
||||||
if !map {
|
if !map {
|
||||||
@ -706,7 +705,7 @@ impl Module {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
param_names.shrink_to_fit();
|
param_names.shrink_to_fit();
|
||||||
|
|
||||||
let hash_fn = calc_native_fn_hash(empty(), &name, ¶m_types);
|
let hash_fn = calc_native_fn_hash(empty(), name.as_ref(), ¶m_types);
|
||||||
|
|
||||||
self.functions.insert(
|
self.functions.insert(
|
||||||
hash_fn,
|
hash_fn,
|
||||||
@ -1207,7 +1206,7 @@ impl Module {
|
|||||||
/// Merge another [`Module`] into this [`Module`].
|
/// Merge another [`Module`] into this [`Module`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn merge(&mut self, other: &Self) -> &mut Self {
|
pub fn merge(&mut self, other: &Self) -> &mut Self {
|
||||||
self.merge_filtered(other, &mut |_, _, _, _, _| true)
|
self.merge_filtered(other, &|_, _, _, _, _| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge another [`Module`] into this [`Module`] based on a filter predicate.
|
/// Merge another [`Module`] into this [`Module`] based on a filter predicate.
|
||||||
@ -1292,14 +1291,12 @@ impl Module {
|
|||||||
|
|
||||||
/// Get an iterator to the sub-modules in the [`Module`].
|
/// Get an iterator to the sub-modules in the [`Module`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
|
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
|
||||||
self.modules.iter().map(|(k, m)| (k.as_str(), m.clone()))
|
self.modules.iter().map(|(k, m)| (k.as_str(), m.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the variables in the [`Module`].
|
/// Get an iterator to the variables in the [`Module`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||||
self.variables.iter().map(|(k, v)| (k.as_str(), v))
|
self.variables.iter().map(|(k, v)| (k.as_str(), v))
|
||||||
}
|
}
|
||||||
@ -1307,7 +1304,6 @@ impl Module {
|
|||||||
/// Get an iterator to the functions in the [`Module`].
|
/// Get an iterator to the functions in the [`Module`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
|
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
|
||||||
self.functions.values().map(Box::as_ref)
|
self.functions.values().map(Box::as_ref)
|
||||||
}
|
}
|
||||||
@ -1322,7 +1318,6 @@ impl Module {
|
|||||||
/// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
/// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn iter_script_fn(
|
pub(crate) fn iter_script_fn(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<
|
) -> impl Iterator<
|
||||||
@ -1360,7 +1355,6 @@ impl Module {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
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)> {
|
||||||
@ -1419,7 +1413,6 @@ impl Module {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[must_use]
|
|
||||||
pub fn eval_ast_as_new(
|
pub fn eval_ast_as_new(
|
||||||
mut scope: crate::Scope,
|
mut scope: crate::Scope,
|
||||||
ast: &crate::AST,
|
ast: &crate::AST,
|
||||||
@ -1529,13 +1522,13 @@ impl Module {
|
|||||||
|
|
||||||
// Index all variables
|
// Index all variables
|
||||||
module.variables.iter().for_each(|(var_name, value)| {
|
module.variables.iter().for_each(|(var_name, value)| {
|
||||||
let hash_var = crate::calc_qualified_var_hash(path.iter().map(|&v| v), var_name);
|
let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
|
||||||
variables.insert(hash_var, value.clone());
|
variables.insert(hash_var, value.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Index type iterators
|
// Index type iterators
|
||||||
module.type_iterators.iter().for_each(|(&type_id, func)| {
|
module.type_iterators.iter().for_each(|(&type_id, func)| {
|
||||||
type_iterators.insert(type_id, func.clone());
|
type_iterators.insert(type_id, *func);
|
||||||
contains_indexed_global_functions = true;
|
contains_indexed_global_functions = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1611,10 +1604,10 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a type iterator into the [`Module`].
|
/// Set a type iterator into the [`Module`].
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self {
|
pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self {
|
||||||
if self.indexed {
|
if self.indexed {
|
||||||
self.all_type_iterators.insert(type_id, func.clone());
|
self.all_type_iterators.insert(type_id, func);
|
||||||
self.contains_indexed_global_functions = true;
|
self.contains_indexed_global_functions = true;
|
||||||
}
|
}
|
||||||
self.type_iterators.insert(type_id, func);
|
self.type_iterators.insert(type_id, func);
|
||||||
@ -1678,7 +1671,6 @@ pub struct NamespaceRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for NamespaceRef {
|
impl fmt::Debug for NamespaceRef {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(index) = self.index {
|
if let Some(index) = self.index {
|
||||||
write!(f, "{} -> ", index)?;
|
write!(f, "{} -> ", index)?;
|
||||||
@ -1696,7 +1688,6 @@ impl fmt::Debug for NamespaceRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for NamespaceRef {
|
impl fmt::Display for NamespaceRef {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
for Ident { name, .. } in self.path.iter() {
|
for Ident { name, .. } in self.path.iter() {
|
||||||
write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
|
write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
|
||||||
@ -1735,7 +1726,10 @@ impl NamespaceRef {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(&self) -> Self {
|
pub fn new(&self) -> Self {
|
||||||
Default::default()
|
Self {
|
||||||
|
index: None,
|
||||||
|
path: StaticVec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Get the [`Scope`][crate::Scope] index offset.
|
/// Get the [`Scope`][crate::Scope] index offset.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -77,16 +77,9 @@ impl ModuleResolversCollection {
|
|||||||
}
|
}
|
||||||
/// Get an iterator of all the [module resolvers][ModuleResolver].
|
/// Get an iterator of all the [module resolvers][ModuleResolver].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
|
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
|
||||||
self.0.iter().map(|v| v.as_ref())
|
self.0.iter().map(|v| v.as_ref())
|
||||||
}
|
}
|
||||||
/// Get a mutable iterator of all the [module resolvers][ModuleResolver].
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = Box<dyn ModuleResolver>> {
|
|
||||||
self.0.into_iter()
|
|
||||||
}
|
|
||||||
/// Remove all [module resolvers][ModuleResolver].
|
/// Remove all [module resolvers][ModuleResolver].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) -> &mut Self {
|
pub fn clear(&mut self) -> &mut Self {
|
||||||
@ -114,6 +107,16 @@ impl ModuleResolversCollection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for ModuleResolversCollection {
|
||||||
|
type Item = Box<dyn ModuleResolver>;
|
||||||
|
type IntoIter = std::vec::IntoIter<Box<dyn ModuleResolver>>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ModuleResolver for ModuleResolversCollection {
|
impl ModuleResolver for ModuleResolversCollection {
|
||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
|
@ -200,7 +200,7 @@ impl FileModuleResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Is a particular path cached?
|
/// Is a particular path cached?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool {
|
pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool {
|
||||||
if !self.cache_enabled {
|
if !self.cache_enabled {
|
||||||
@ -227,7 +227,7 @@ impl FileModuleResolver {
|
|||||||
/// Remove the specified path from internal cache.
|
/// Remove the specified path from internal cache.
|
||||||
///
|
///
|
||||||
/// The next time this path is resolved, the script file will be loaded once again.
|
/// The next time this path is resolved, the script file will be loaded once again.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn clear_cache_for_path(
|
pub fn clear_cache_for_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -252,7 +252,6 @@ impl FileModuleResolver {
|
|||||||
}
|
}
|
||||||
/// Construct a full file path.
|
/// Construct a full file path.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[must_use]
|
|
||||||
fn get_file_path(&self, path: &str, source_path: Option<&str>) -> PathBuf {
|
fn get_file_path(&self, path: &str, source_path: Option<&str>) -> PathBuf {
|
||||||
let path = Path::new(path);
|
let path = Path::new(path);
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ pub use stat::StaticModuleResolver;
|
|||||||
/// Trait that encapsulates a module resolution service.
|
/// Trait that encapsulates a module resolution service.
|
||||||
pub trait ModuleResolver: SendSync {
|
pub trait ModuleResolver: SendSync {
|
||||||
/// Resolve a module based on a path string.
|
/// Resolve a module based on a path string.
|
||||||
#[must_use]
|
|
||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
@ -64,33 +64,28 @@ impl StaticModuleResolver {
|
|||||||
}
|
}
|
||||||
/// Get an iterator of all the [modules][Module].
|
/// Get an iterator of all the [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
||||||
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
||||||
}
|
}
|
||||||
/// Get a mutable iterator of all the [modules][Module].
|
/// Get a mutable iterator of all the [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> {
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> {
|
||||||
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||||
}
|
}
|
||||||
/// Get a mutable iterator of all the modules.
|
/// Get a mutable iterator of all the modules.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the [module][Module] paths.
|
/// Get an iterator of all the [module][Module] paths.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
||||||
self.0.keys().map(|s| s.as_str())
|
self.0.keys().map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the [modules][Module].
|
/// Get an iterator of all the [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> {
|
pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> {
|
||||||
self.0.values().map(|m| m)
|
self.0.values()
|
||||||
}
|
}
|
||||||
/// Remove all [modules][Module].
|
/// Remove all [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
112
src/optimize.rs
112
src/optimize.rs
@ -18,6 +18,9 @@ use std::{
|
|||||||
mem,
|
mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
use crate::engine::KEYWORD_IS_SHARED;
|
||||||
|
|
||||||
/// Level of optimization performed.
|
/// Level of optimization performed.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
pub enum OptimizationLevel {
|
pub enum OptimizationLevel {
|
||||||
@ -118,33 +121,18 @@ impl<'a> OptimizerState<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Has a system function a Rust-native override?
|
|
||||||
fn has_native_fn(state: &OptimizerState, hash_script: u64, arg_types: &[TypeId]) -> bool {
|
|
||||||
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
|
||||||
let hash = combine_hashes(hash_script, hash_params);
|
|
||||||
|
|
||||||
// First check registered functions
|
|
||||||
state.engine.global_namespace.contains_fn(hash)
|
|
||||||
// Then check packages
|
|
||||||
|| state.engine.global_modules.iter().any(|m| m.contains_fn(hash))
|
|
||||||
// Then check sub-modules
|
|
||||||
|| state.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
fn call_fn_with_constant_arguments(
|
#[inline(always)]
|
||||||
state: &OptimizerState,
|
pub fn call_fn_with_constant_arguments(
|
||||||
|
&self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
state
|
self.engine
|
||||||
.engine
|
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
|
&Default::default(),
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
&mut Default::default(),
|
self.lib,
|
||||||
state.lib,
|
|
||||||
fn_name,
|
fn_name,
|
||||||
calc_fn_hash(fn_name, arg_values.len()),
|
calc_fn_hash(fn_name, arg_values.len()),
|
||||||
&mut arg_values.iter_mut().collect::<StaticVec<_>>(),
|
&mut arg_values.iter_mut().collect::<StaticVec<_>>(),
|
||||||
@ -155,6 +143,19 @@ fn call_fn_with_constant_arguments(
|
|||||||
.ok()
|
.ok()
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
// Has a system function a Rust-native override?
|
||||||
|
pub fn has_native_fn(&self, hash_script: u64, arg_types: &[TypeId]) -> bool {
|
||||||
|
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
||||||
|
let hash = combine_hashes(hash_script, hash_params);
|
||||||
|
|
||||||
|
// First check registered functions
|
||||||
|
self.engine.global_namespace.contains_fn(hash)
|
||||||
|
// Then check packages
|
||||||
|
|| self.engine.global_modules.iter().any(|m| m.contains_fn(hash))
|
||||||
|
// Then check sub-modules
|
||||||
|
|| self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Optimize a block of [statements][Stmt].
|
/// Optimize a block of [statements][Stmt].
|
||||||
fn optimize_stmt_block(
|
fn optimize_stmt_block(
|
||||||
@ -705,14 +706,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Dot(x,_, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let prop = p.2.0.as_str();
|
let prop = p.2.0.as_str();
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = mem::take(&mut m.0).into_iter().find(|(x, _)| &x.name == prop)
|
*expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name == prop)
|
||||||
.map(|(_, mut expr)| { expr.set_position(*pos); expr })
|
.map(|(_, mut expr)| { expr.set_position(*pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(*pos));
|
.unwrap_or_else(|| Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
@ -723,11 +724,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
// ....lhs.rhs
|
// ....lhs.rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
Expr::Dot(x,_, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||||
|
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// array[int]
|
// array[int]
|
||||||
(Expr::Array(a, pos), Expr::IntegerConstant(i, _))
|
(Expr::Array(a, pos), Expr::IntegerConstant(i, _))
|
||||||
if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) =>
|
if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) =>
|
||||||
@ -791,7 +792,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
},
|
},
|
||||||
// ...[lhs][rhs]
|
// ...[lhs][rhs]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
Expr::Index(x, _, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||||
// ``
|
// ``
|
||||||
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -944,32 +945,36 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
Expr::FnCall(x, pos)
|
Expr::FnCall(x, pos)
|
||||||
if !x.is_qualified() // Non-qualified
|
if !x.is_qualified() // Non-qualified
|
||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.args.len() == 2 // binary call
|
|
||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
let arg_values = &mut x.args.iter().map(|e| match e {
|
||||||
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
_ => e.get_literal_value().unwrap()
|
_ => e.get_literal_value().unwrap()
|
||||||
}).collect();
|
}).collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
let result = match x.name.as_str() {
|
||||||
if !has_native_fn(state, x.hashes.native, arg_types.as_ref()) {
|
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
||||||
if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
|
||||||
|
// Overloaded operators can override built-in.
|
||||||
|
_ if x.args.len() == 2 && !state.has_native_fn(x.hashes.native, arg_types.as_ref()) => {
|
||||||
|
get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
||||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
||||||
})
|
})
|
||||||
.map(Expr::from)
|
|
||||||
{
|
|
||||||
state.set_dirty();
|
|
||||||
result.set_position(*pos);
|
|
||||||
*expr = result;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(result) = result {
|
||||||
|
state.set_dirty();
|
||||||
|
*expr = Expr::from_dynamic(result, *pos);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
||||||
@ -997,30 +1002,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
let has_script_fn = false;
|
let has_script_fn = false;
|
||||||
|
|
||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e {
|
let arg_values = &mut x.args.iter().map(|e| match e {
|
||||||
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
_ => e.get_literal_value().unwrap()
|
_ => e.get_literal_value().unwrap()
|
||||||
}).collect();
|
}).collect::<StaticVec<_>>();
|
||||||
|
|
||||||
// Save the typename of the first argument if it is `type_of()`
|
let result = match x.name.as_str() {
|
||||||
// This is to avoid `call_args` being passed into the closure
|
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
||||||
let arg_for_type_of = if x.name == KEYWORD_TYPE_OF && arg_values.len() == 1 {
|
#[cfg(not(feature = "no_closure"))]
|
||||||
state.engine.map_type_name(arg_values[0].type_name())
|
KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
|
||||||
} else {
|
_ => state.call_fn_with_constant_arguments(x.name.as_ref(), arg_values)
|
||||||
""
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
|
if let Some(result) = result {
|
||||||
.or_else(|| if !arg_for_type_of.is_empty() {
|
|
||||||
// Handle `type_of()`
|
|
||||||
Some(arg_for_type_of.to_string().into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}).map(Expr::from)
|
|
||||||
{
|
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
result.set_position(*pos);
|
*expr = Expr::from_dynamic(result, *pos);
|
||||||
*expr = result;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1043,9 +1039,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
let pos = *pos;
|
*expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos);
|
||||||
*expr = Expr::from(state.find_constant(&x.2).unwrap().clone());
|
|
||||||
expr.set_position(pos);
|
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ mod array_functions {
|
|||||||
len as usize
|
len as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
array[start..start + len].iter().cloned().collect()
|
array[start..start + len].to_vec()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "extract")]
|
#[rhai_fn(name = "extract")]
|
||||||
pub fn extract_tail(array: &mut Array, start: INT) -> Array {
|
pub fn extract_tail(array: &mut Array, start: INT) -> Array {
|
||||||
@ -170,7 +170,7 @@ mod array_functions {
|
|||||||
start as usize
|
start as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
array[start..].iter().cloned().collect()
|
array[start..].to_vec()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "split")]
|
#[rhai_fn(name = "split")]
|
||||||
pub fn split_at(array: &mut Array, start: INT) -> Array {
|
pub fn split_at(array: &mut Array, start: INT) -> Array {
|
||||||
|
@ -50,14 +50,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
|||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
|
|
||||||
if let Some(ns) = namespace {
|
if let Some(ns) = namespace {
|
||||||
map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into());
|
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
|
||||||
}
|
}
|
||||||
|
map.insert(dict.get("name").expect(DICT).clone(), f.name.clone().into());
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("name").expect(DICT).clone().into(),
|
dict.get("access").expect(DICT).clone(),
|
||||||
f.name.clone().into(),
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
dict.get("access").expect(DICT).clone().into(),
|
|
||||||
match f.access {
|
match f.access {
|
||||||
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
||||||
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
||||||
@ -65,11 +62,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("is_anonymous").expect(DICT).clone().into(),
|
dict.get("is_anonymous").expect(DICT).clone(),
|
||||||
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("params").expect(DICT).clone().into(),
|
dict.get("params").expect(DICT).clone(),
|
||||||
f.params
|
f.params
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -78,7 +75,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
map.into()
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intern strings
|
// Intern strings
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::dynamic::Variant;
|
use crate::dynamic::Variant;
|
||||||
use crate::{def_package, EvalAltResult, INT};
|
use crate::{def_package, EvalAltResult, INT};
|
||||||
|
use std::iter::{ExactSizeIterator, FusedIterator};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -53,31 +54,19 @@ where
|
|||||||
None
|
None
|
||||||
} else if self.0 < self.1 {
|
} else if self.0 < self.1 {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let diff1 = if let Some(diff) = self.1.checked_sub(&self.0) {
|
let diff1 = self.1.checked_sub(&self.0)?;
|
||||||
diff
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
let diff1 = self.1 - self.0;
|
let diff1 = self.1 - self.0;
|
||||||
|
|
||||||
let v = self.0;
|
let v = self.0;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let n = if let Some(num) = self.0.checked_add(&self.2) {
|
let n = self.0.checked_add(&self.2)?;
|
||||||
num
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
let n = self.0 + self.2;
|
let n = self.0 + self.2;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let diff2 = if let Some(diff) = self.1.checked_sub(&n) {
|
let diff2 = self.1.checked_sub(&n)?;
|
||||||
diff
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
let diff2 = self.1 - n;
|
let diff2 = self.1 - n;
|
||||||
|
|
||||||
@ -89,31 +78,19 @@ where
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let diff1 = if let Some(diff) = self.0.checked_sub(&self.1) {
|
let diff1 = self.0.checked_sub(&self.1)?;
|
||||||
diff
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
let diff1 = self.0 - self.1;
|
let diff1 = self.0 - self.1;
|
||||||
|
|
||||||
let v = self.0;
|
let v = self.0;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let n = if let Some(num) = self.0.checked_add(&self.2) {
|
let n = self.0.checked_add(&self.2)?;
|
||||||
num
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
let n = self.0 + self.2;
|
let n = self.0 + self.2;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let diff2 = if let Some(diff) = n.checked_sub(&self.1) {
|
let diff2 = n.checked_sub(&self.1)?;
|
||||||
diff
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
let diff2 = n - self.1;
|
let diff2 = n - self.1;
|
||||||
|
|
||||||
@ -127,6 +104,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> FusedIterator for StepRange<T> where
|
||||||
|
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// Bit-field iterator with step
|
// Bit-field iterator with step
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
struct BitRange(INT, INT, usize);
|
struct BitRange(INT, INT, usize);
|
||||||
@ -190,6 +172,20 @@ impl Iterator for BitRange {
|
|||||||
Some(r)
|
Some(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
(self.2, Some(self.2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for BitRange {}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for BitRange {
|
||||||
|
#[inline(always)]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// String iterator over characters
|
// String iterator over characters
|
||||||
@ -248,6 +244,21 @@ impl Iterator for CharsStream {
|
|||||||
Some(ch)
|
Some(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let remaining = self.0.len() - self.1;
|
||||||
|
(remaining, Some(remaining))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for CharsStream {}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for CharsStream {
|
||||||
|
#[inline(always)]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len() - self.1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reg_range {
|
macro_rules! reg_range {
|
||||||
@ -356,11 +367,11 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::iter::FusedIterator for StepFloatRange {}
|
impl FusedIterator for StepFloatRange {}
|
||||||
|
|
||||||
lib.set_iterator::<StepFloatRange>();
|
lib.set_iterator::<StepFloatRange>();
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("range", |from, to, step| StepFloatRange::new(from, to, step));
|
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(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
|
||||||
}
|
}
|
||||||
@ -418,11 +429,11 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::iter::FusedIterator for StepDecimalRange {}
|
impl FusedIterator for StepDecimalRange {}
|
||||||
|
|
||||||
lib.set_iterator::<StepDecimalRange>();
|
lib.set_iterator::<StepDecimalRange>();
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
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(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||||
}
|
}
|
||||||
@ -445,7 +456,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
// Register bit-field iterator
|
// Register bit-field iterator
|
||||||
lib.set_iterator::<BitRange>();
|
lib.set_iterator::<BitRange>();
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value, from, len| BitRange::new(value, from, len));
|
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(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
|||||||
mod int_functions {
|
mod int_functions {
|
||||||
#[rhai_fn(name = "parse_int", return_raw)]
|
#[rhai_fn(name = "parse_int", return_raw)]
|
||||||
pub fn parse_int_radix(s: &str, radix: INT) -> Result<INT, Box<EvalAltResult>> {
|
pub fn parse_int_radix(s: &str, radix: INT) -> Result<INT, Box<EvalAltResult>> {
|
||||||
if radix < 2 || radix > 36 {
|
if !(2..=36).contains(&radix) {
|
||||||
return EvalAltResult::ErrorArithmetic(
|
return EvalAltResult::ErrorArithmetic(
|
||||||
format!("Invalid radix: '{}'", radix),
|
format!("Invalid radix: '{}'", radix),
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
|
@ -79,8 +79,13 @@ macro_rules! def_package {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for $package {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl $package {
|
impl $package {
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut module = $root::Module::new();
|
let mut module = $root::Module::new();
|
||||||
<Self as $root::packages::Package>::init(&mut module);
|
<Self as $root::packages::Package>::init(&mut module);
|
||||||
|
@ -12,8 +12,8 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
pub const FUNC_TO_STRING: &'static str = "to_string";
|
pub const FUNC_TO_STRING: &str = "to_string";
|
||||||
pub const FUNC_TO_DEBUG: &'static str = "to_debug";
|
pub const FUNC_TO_DEBUG: &str = "to_debug";
|
||||||
|
|
||||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||||
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
||||||
@ -22,7 +22,7 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
|||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn print_with_func(
|
pub fn print_with_func(
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
@ -106,7 +106,7 @@ mod print_debug_functions {
|
|||||||
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
|
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
|
||||||
let len = array.len();
|
let len = array.len();
|
||||||
let mut result = String::with_capacity(len * 5 + 2);
|
let mut result = String::with_capacity(len * 5 + 2);
|
||||||
result.push_str("[");
|
result.push('[');
|
||||||
|
|
||||||
array.iter_mut().enumerate().for_each(|(i, x)| {
|
array.iter_mut().enumerate().for_each(|(i, x)| {
|
||||||
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x));
|
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x));
|
||||||
@ -115,7 +115,7 @@ mod print_debug_functions {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
result.push_str("]");
|
result.push(']');
|
||||||
result.into()
|
result.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ mod print_debug_functions {
|
|||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
result.push_str("}");
|
result.push('}');
|
||||||
result.into()
|
result.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,11 @@ mod string_functions {
|
|||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
let mut s = print_with_func(FUNC_TO_STRING, &ctx, item);
|
let mut s = print_with_func(FUNC_TO_STRING, &ctx, item);
|
||||||
|
|
||||||
if string.is_empty() {
|
if !string.is_empty() {
|
||||||
s
|
|
||||||
} else {
|
|
||||||
s.make_mut().push_str(string);
|
s.make_mut().push_str(string);
|
||||||
s.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+", name = "append")]
|
#[rhai_fn(name = "+", name = "append")]
|
||||||
@ -240,7 +239,7 @@ mod string_functions {
|
|||||||
let mut chars = StaticVec::with_capacity(string.len());
|
let mut chars = StaticVec::with_capacity(string.len());
|
||||||
|
|
||||||
let offset = if string.is_empty() || len <= 0 {
|
let offset = if string.is_empty() || len <= 0 {
|
||||||
return ctx.engine().empty_string.clone().into();
|
return ctx.engine().empty_string.clone();
|
||||||
} else if start < 0 {
|
} else if start < 0 {
|
||||||
if let Some(n) = start.checked_abs() {
|
if let Some(n) = start.checked_abs() {
|
||||||
chars.extend(string.chars());
|
chars.extend(string.chars());
|
||||||
@ -253,7 +252,7 @@ mod string_functions {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
} else if start as usize >= string.chars().count() {
|
} else if start as usize >= string.chars().count() {
|
||||||
return ctx.engine().empty_string.clone().into();
|
return ctx.engine().empty_string.clone();
|
||||||
} else {
|
} else {
|
||||||
start as usize
|
start as usize
|
||||||
};
|
};
|
||||||
|
151
src/parse.rs
151
src/parse.rs
@ -8,7 +8,7 @@ use crate::custom_syntax::{
|
|||||||
CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR,
|
CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR,
|
||||||
CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING,
|
CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING,
|
||||||
};
|
};
|
||||||
use crate::dynamic::{AccessMode, Union};
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||||
use crate::fn_hash::get_hasher;
|
use crate::fn_hash::get_hasher;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
@ -17,8 +17,8 @@ use crate::token::{
|
|||||||
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, Identifier,
|
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier, LexError,
|
||||||
LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -59,7 +59,7 @@ pub struct IdentifierBuilder(
|
|||||||
|
|
||||||
impl IdentifierBuilder {
|
impl IdentifierBuilder {
|
||||||
/// Get an identifier from a text string.
|
/// Get an identifier from a text string.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
|
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
#[cfg(not(feature = "no_smartstring"))]
|
||||||
@ -141,7 +141,7 @@ impl<'e> ParseState<'e> {
|
|||||||
/// i.e. the top element of [`ParseState`]'s variables stack is offset 1.
|
/// i.e. the top element of [`ParseState`]'s variables stack is offset 1.
|
||||||
///
|
///
|
||||||
/// Return `None` when the variable name is not found in the `stack`.
|
/// Return `None` when the variable name is not found in the `stack`.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
pub fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
||||||
let mut barrier = false;
|
let mut barrier = false;
|
||||||
|
|
||||||
@ -242,8 +242,7 @@ impl ParseSettings {
|
|||||||
}
|
}
|
||||||
/// Make sure that the current level of expression nesting is within the maximum limit.
|
/// Make sure that the current level of expression nesting is within the maximum limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
pub fn ensure_level_within_max_limit(
|
pub fn ensure_level_within_max_limit(
|
||||||
&self,
|
&self,
|
||||||
limit: Option<NonZeroUsize>,
|
limit: Option<NonZeroUsize>,
|
||||||
@ -261,7 +260,7 @@ 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.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn into_property(self, state: &mut ParseState) -> Self {
|
fn into_property(self, state: &mut ParseState) -> Self {
|
||||||
match self {
|
match self {
|
||||||
@ -412,7 +411,7 @@ fn parse_paren_expr(
|
|||||||
// ( xxx )
|
// ( xxx )
|
||||||
(Token::RightParen, _) => Ok(expr),
|
(Token::RightParen, _) => Ok(expr),
|
||||||
// ( <error>
|
// ( <error>
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
||||||
// ( xxx ???
|
// ( xxx ???
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
Token::RightParen.into(),
|
Token::RightParen.into(),
|
||||||
@ -700,17 +699,19 @@ fn parse_index_chain(
|
|||||||
// Indexing binds to right
|
// Indexing binds to right
|
||||||
Ok(Expr::Index(
|
Ok(Expr::Index(
|
||||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||||
|
false,
|
||||||
prev_pos,
|
prev_pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// Otherwise terminate the indexing chain
|
// Otherwise terminate the indexing chain
|
||||||
_ => Ok(Expr::Index(
|
_ => Ok(Expr::Index(
|
||||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||||
|
true,
|
||||||
settings.pos,
|
settings.pos,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.clone().into_err(*pos)),
|
(Token::LexError(err), pos) => Err(err.clone().into_err(*pos)),
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
Token::RightBracket.into(),
|
Token::RightBracket.into(),
|
||||||
"for a matching [ in this index expression".into(),
|
"for a matching [ in this index expression".into(),
|
||||||
@ -806,7 +807,7 @@ fn parse_map_literal(
|
|||||||
settings.pos = eat_token(input, Token::MapStart);
|
settings.pos = eat_token(input, Token::MapStart);
|
||||||
|
|
||||||
let mut map: StaticVec<(Ident, Expr)> = Default::default();
|
let mut map: StaticVec<(Ident, Expr)> = Default::default();
|
||||||
let mut template: BTreeMap<Identifier, Dynamic> = Default::default();
|
let mut template: BTreeMap<Identifier, crate::Dynamic> = Default::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
const MISSING_RBRACE: &str = "to end this object map literal";
|
const MISSING_RBRACE: &str = "to end this object map literal";
|
||||||
@ -878,7 +879,7 @@ fn parse_map_literal(
|
|||||||
|
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
let name = state.get_identifier(name);
|
let name = state.get_identifier(name);
|
||||||
template.insert(name.clone().into(), Default::default());
|
template.insert(name.clone(), Default::default());
|
||||||
map.push((Ident { name, pos }, expr));
|
map.push((Ident { name, pos }, expr));
|
||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
@ -1085,7 +1086,7 @@ fn parse_primary(
|
|||||||
},
|
},
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Token::FloatConstant(x) => {
|
Token::FloatConstant(x) => {
|
||||||
let x = (*x).into();
|
let x = *x;
|
||||||
input.next().expect(NEVER_ENDS);
|
input.next().expect(NEVER_ENDS);
|
||||||
Expr::FloatConstant(x, settings.pos)
|
Expr::FloatConstant(x, settings.pos)
|
||||||
}
|
}
|
||||||
@ -1396,7 +1397,7 @@ fn parse_primary(
|
|||||||
|
|
||||||
let rhs = parse_primary(input, state, lib, settings.level_up())?;
|
let rhs = parse_primary(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
make_dot_expr(state, expr, rhs, tail_pos)?
|
make_dot_expr(state, expr, false, rhs, tail_pos)?
|
||||||
}
|
}
|
||||||
// Unknown postfix operator
|
// Unknown postfix operator
|
||||||
(expr, token) => unreachable!(
|
(expr, token) => unreachable!(
|
||||||
@ -1408,15 +1409,17 @@ fn parse_primary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cache the hash key for namespace-qualified variables
|
// Cache the hash key for namespace-qualified variables
|
||||||
match root_expr {
|
let namespaced_variable = match root_expr {
|
||||||
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
||||||
Expr::Index(ref mut x, _) | Expr::Dot(ref mut x, _) => match x.lhs {
|
Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs {
|
||||||
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
};
|
||||||
.map(|x| match x {
|
|
||||||
|
if let Some(x) = namespaced_variable {
|
||||||
|
match x {
|
||||||
(_, Some((namespace, hash)), name) => {
|
(_, Some((namespace, hash)), name) => {
|
||||||
*hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name);
|
*hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name);
|
||||||
|
|
||||||
@ -1424,7 +1427,8 @@ fn parse_primary(
|
|||||||
namespace.set_index(state.find_module(&namespace[0].name));
|
namespace.set_index(state.find_module(&namespace[0].name));
|
||||||
}
|
}
|
||||||
_ => unreachable!("expecting namespace-qualified variable access"),
|
_ => unreachable!("expecting namespace-qualified variable access"),
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure identifiers are valid
|
// Make sure identifiers are valid
|
||||||
Ok(root_expr)
|
Ok(root_expr)
|
||||||
@ -1529,7 +1533,7 @@ fn parse_unary(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Make an assignment statement.
|
/// Make an assignment statement.
|
||||||
fn make_assignment_stmt<'a>(
|
fn make_assignment_stmt(
|
||||||
op: Option<Token>,
|
op: Option<Token>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
@ -1539,13 +1543,13 @@ fn make_assignment_stmt<'a>(
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
|
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs {
|
Expr::Index(x, _, _) | Expr::Dot(x, _, _) if parent_is_dot => match x.lhs {
|
||||||
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
|
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
|
||||||
ref e => Some(e.position()),
|
ref e => Some(e.position()),
|
||||||
},
|
},
|
||||||
Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs {
|
Expr::Index(x, _, _) | Expr::Dot(x, _, _) => match x.lhs {
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
||||||
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
|
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
|
||||||
},
|
},
|
||||||
Expr::Property(_) if parent_is_dot => None,
|
Expr::Property(_) if parent_is_dot => None,
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
||||||
@ -1554,7 +1558,7 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op_info = op.map(|v| OpAssignment::new(v));
|
let op_info = op.map(OpAssignment::new);
|
||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
// const_expr = rhs
|
// const_expr = rhs
|
||||||
@ -1587,8 +1591,8 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// xxx[???]... = rhs, xxx.prop... = rhs
|
// xxx[???]... = rhs, xxx.prop... = rhs
|
||||||
Expr::Index(ref x, _) | Expr::Dot(ref x, _) => {
|
Expr::Index(ref x, _, _) | Expr::Dot(ref x, _, _) => {
|
||||||
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
|
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _, _))) {
|
||||||
None => match x.lhs {
|
None => match x.lhs {
|
||||||
// var[???] = rhs, var.??? = rhs
|
// var[???] = rhs, var.??? = rhs
|
||||||
Expr::Variable(_, _, _) => {
|
Expr::Variable(_, _, _) => {
|
||||||
@ -1645,41 +1649,38 @@ fn parse_op_assignment_stmt(
|
|||||||
fn make_dot_expr(
|
fn make_dot_expr(
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
|
terminate_chaining: bool,
|
||||||
rhs: Expr,
|
rhs: Expr,
|
||||||
op_pos: Position,
|
op_pos: Position,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
Ok(match (lhs, rhs) {
|
Ok(match (lhs, rhs) {
|
||||||
// idx_lhs[idx_expr].rhs
|
// lhs[???]...[???].rhs
|
||||||
|
(Expr::Index(mut x, false, pos), rhs) if !terminate_chaining => {
|
||||||
// Attach dot chain to the bottom level of indexing chain
|
// Attach dot chain to the bottom level of indexing chain
|
||||||
(Expr::Index(mut x, pos), rhs) => {
|
x.rhs = make_dot_expr(state, x.rhs, false, rhs, op_pos)?;
|
||||||
x.rhs = make_dot_expr(state, x.rhs, rhs, op_pos)?;
|
Expr::Index(x, false, pos)
|
||||||
Expr::Index(x, pos)
|
}
|
||||||
|
// lhs[idx_expr].rhs
|
||||||
|
(Expr::Index(mut x, _, pos), rhs) => {
|
||||||
|
x.rhs = make_dot_expr(state, x.rhs, true, rhs, op_pos)?;
|
||||||
|
Expr::Index(x, false, pos)
|
||||||
}
|
}
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, Expr::Variable(_, var_pos, x)) if x.1.is_none() => {
|
(lhs, var_expr @ Expr::Variable(_, _, _)) if var_expr.is_variable_access(true) => {
|
||||||
let ident = x.2;
|
let rhs = var_expr.into_property(state);
|
||||||
let getter = state.get_identifier(crate::engine::make_getter(&ident));
|
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||||
let hash_get = calc_fn_hash(&getter, 1);
|
|
||||||
let setter = state.get_identifier(crate::engine::make_setter(&ident));
|
|
||||||
let hash_set = calc_fn_hash(&setter, 2);
|
|
||||||
|
|
||||||
let rhs = Expr::Property(Box::new((
|
|
||||||
(getter, hash_get),
|
|
||||||
(setter, hash_set),
|
|
||||||
(state.get_identifier(ident).into(), var_pos),
|
|
||||||
)));
|
|
||||||
|
|
||||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
|
||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(_, _, x)) if x.1.is_some() => {
|
(_, Expr::Variable(_, _, x)) => {
|
||||||
return Err(PERR::PropertyExpected
|
return Err(PERR::PropertyExpected
|
||||||
.into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
|
.into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
|
||||||
}
|
}
|
||||||
// lhs.prop
|
// lhs.prop
|
||||||
(lhs, prop @ Expr::Property(_)) => Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), op_pos),
|
(lhs, prop @ Expr::Property(_)) => {
|
||||||
|
Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), false, op_pos)
|
||||||
|
}
|
||||||
// lhs.dot_lhs.dot_rhs
|
// lhs.dot_lhs.dot_rhs
|
||||||
(lhs, Expr::Dot(x, pos)) => match x.lhs {
|
(lhs, Expr::Dot(x, _, pos)) => match x.lhs {
|
||||||
Expr::Variable(_, _, _) | Expr::Property(_) => {
|
Expr::Variable(_, _, _) | Expr::Property(_) => {
|
||||||
let rhs = Expr::Dot(
|
let rhs = Expr::Dot(
|
||||||
BinaryExpr {
|
BinaryExpr {
|
||||||
@ -1687,9 +1688,10 @@ fn make_dot_expr(
|
|||||||
rhs: x.rhs,
|
rhs: x.rhs,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
false,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||||
}
|
}
|
||||||
Expr::FnCall(mut func, func_pos) => {
|
Expr::FnCall(mut func, func_pos) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
@ -1704,23 +1706,25 @@ fn make_dot_expr(
|
|||||||
rhs: x.rhs,
|
rhs: x.rhs,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
false,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||||
}
|
}
|
||||||
_ => unreachable!("invalid dot expression: {:?}", x.lhs),
|
_ => unreachable!("invalid dot expression: {:?}", x.lhs),
|
||||||
},
|
},
|
||||||
// lhs.idx_lhs[idx_rhs]
|
// lhs.idx_lhs[idx_rhs]
|
||||||
(lhs, Expr::Index(x, pos)) => {
|
(lhs, Expr::Index(x, term, pos)) => {
|
||||||
let rhs = Expr::Index(
|
let rhs = Expr::Index(
|
||||||
BinaryExpr {
|
BinaryExpr {
|
||||||
lhs: x.lhs.into_property(state),
|
lhs: x.lhs.into_property(state),
|
||||||
rhs: x.rhs,
|
rhs: x.rhs,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
term,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||||
}
|
}
|
||||||
// lhs.nnn::func(...)
|
// lhs.nnn::func(...)
|
||||||
(_, Expr::FnCall(x, _)) if x.is_qualified() => {
|
(_, Expr::FnCall(x, _)) if x.is_qualified() => {
|
||||||
@ -1756,7 +1760,7 @@ fn make_dot_expr(
|
|||||||
calc_fn_hash(&func.name, func.args.len() + 1),
|
calc_fn_hash(&func.name, func.args.len() + 1),
|
||||||
);
|
);
|
||||||
let rhs = Expr::FnCall(func, func_pos);
|
let rhs = Expr::FnCall(func, func_pos);
|
||||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
|
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
|
||||||
@ -2715,7 +2719,7 @@ fn parse_stmt(
|
|||||||
is_function_scope: true,
|
is_function_scope: true,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
level: 0,
|
level: 0,
|
||||||
pos: pos,
|
pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
let func = parse_fn(
|
let func = parse_fn(
|
||||||
@ -3033,8 +3037,7 @@ fn parse_anon_fn(
|
|||||||
|
|
||||||
let mut params_list: StaticVec<_> = Default::default();
|
let mut params_list: StaticVec<_> = Default::default();
|
||||||
|
|
||||||
if input.next().expect(NEVER_ENDS).0 != Token::Or {
|
if input.next().expect(NEVER_ENDS).0 != Token::Or && !match_token(input, Token::Pipe).0 {
|
||||||
if !match_token(input, Token::Pipe).0 {
|
|
||||||
loop {
|
loop {
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Pipe, _) => break,
|
(Token::Pipe, _) => break,
|
||||||
@ -3070,7 +3073,6 @@ fn parse_anon_fn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
settings.is_breakable = false;
|
settings.is_breakable = false;
|
||||||
@ -3119,7 +3121,7 @@ fn parse_anon_fn(
|
|||||||
comments: Default::default(),
|
comments: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_ptr = crate::FnPtr::new_unchecked(fn_name.into(), Default::default());
|
let fn_ptr = crate::FnPtr::new_unchecked(fn_name, Default::default());
|
||||||
let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos);
|
let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -3130,7 +3132,6 @@ fn parse_anon_fn(
|
|||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Parse a global level expression.
|
/// Parse a global level expression.
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn parse_global_expr(
|
pub(crate) fn parse_global_expr(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
@ -3172,7 +3173,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the global level statements.
|
/// Parse the global level statements.
|
||||||
#[must_use]
|
|
||||||
fn parse_global_level(
|
fn parse_global_level(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
@ -3234,7 +3234,6 @@ impl Engine {
|
|||||||
|
|
||||||
/// Run the parser on an input stream, returning an AST.
|
/// Run the parser on an input stream, returning an AST.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn parse(
|
pub(crate) fn parse(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
@ -3250,33 +3249,3 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Dynamic> for Expr {
|
|
||||||
fn from(value: Dynamic) -> Self {
|
|
||||||
match value.0 {
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Union::Float(value, _, _) => Self::FloatConstant(value, Position::NONE),
|
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
Union::Decimal(value, _, _) => {
|
|
||||||
Self::DynamicConstant(Box::new((*value).into()), Position::NONE)
|
|
||||||
}
|
|
||||||
|
|
||||||
Union::Unit(_, _, _) => Self::Unit(Position::NONE),
|
|
||||||
Union::Int(value, _, _) => Self::IntegerConstant(value, Position::NONE),
|
|
||||||
Union::Char(value, _, _) => Self::CharConstant(value, Position::NONE),
|
|
||||||
Union::Str(value, _, _) => Self::StringConstant(value, Position::NONE),
|
|
||||||
Union::Bool(value, _, _) => Self::BoolConstant(value, Position::NONE),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Array(array, _, _) => {
|
|
||||||
Self::DynamicConstant(Box::new((*array).into()), Position::NONE)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(map, _, _) => Self::DynamicConstant(Box::new((*map).into()), Position::NONE),
|
|
||||||
|
|
||||||
_ => Self::DynamicConstant(Box::new(value.into()), Position::NONE),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -22,7 +22,6 @@ pub use rhai_codegen::{export_fn, register_exported_fn};
|
|||||||
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||||
pub trait PluginFunction {
|
pub trait PluginFunction {
|
||||||
/// Call the plugin function with the arguments provided.
|
/// Call the plugin function with the arguments provided.
|
||||||
#[must_use]
|
|
||||||
fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult;
|
fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult;
|
||||||
|
|
||||||
/// Is this plugin function a method?
|
/// Is this plugin function a method?
|
||||||
|
28
src/scope.rs
28
src/scope.rs
@ -49,7 +49,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
|
|||||||
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
|
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
|
||||||
//
|
//
|
||||||
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash, Default)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope<'a> {
|
||||||
/// Current value of the entry.
|
/// Current value of the entry.
|
||||||
values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
||||||
@ -59,13 +59,6 @@ pub struct Scope<'a> {
|
|||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Scope<'_> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoIterator for Scope<'a> {
|
impl<'a> IntoIterator for Scope<'a> {
|
||||||
type Item = (Cow<'a, str>, Dynamic);
|
type Item = (Cow<'a, str>, Dynamic);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
||||||
@ -97,10 +90,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Default::default()
|
||||||
values: Default::default(),
|
|
||||||
names: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Empty the [`Scope`].
|
/// Empty the [`Scope`].
|
||||||
///
|
///
|
||||||
@ -246,7 +236,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
|
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
|
||||||
}
|
}
|
||||||
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
|
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
|
||||||
#[inline(always)]
|
#[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<Cow<'a, str>>,
|
||||||
@ -255,7 +245,7 @@ impl<'a> Scope<'a> {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.names.push((name.into(), Default::default()));
|
self.names.push((name.into(), Default::default()));
|
||||||
value.set_access_mode(access);
|
value.set_access_mode(access);
|
||||||
self.values.push(value.into());
|
self.values.push(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Truncate (rewind) the [`Scope`] to a previous size.
|
/// Truncate (rewind) the [`Scope`] to a previous size.
|
||||||
@ -371,7 +361,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// my_scope.set_value("x", 0_i64);
|
/// my_scope.set_value("x", 0_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self {
|
pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self {
|
||||||
match self.get_index(name) {
|
match self.get_index(name) {
|
||||||
None => {
|
None => {
|
||||||
@ -408,6 +398,7 @@ impl<'a> Scope<'a> {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123);
|
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||||
self.get_index(name)
|
self.get_index(name)
|
||||||
@ -434,7 +425,7 @@ impl<'a> Scope<'a> {
|
|||||||
///
|
///
|
||||||
/// Panics if the index is out of bounds.
|
/// Panics if the index is out of bounds.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||||
let (_, aliases) = self
|
let (_, aliases) = self
|
||||||
.names
|
.names
|
||||||
@ -456,7 +447,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn clone_visible(&self) -> Self {
|
pub(crate) fn clone_visible(&self) -> Self {
|
||||||
let mut entries: Self = Default::default();
|
let mut entries = Self::new();
|
||||||
|
|
||||||
self.names
|
self.names
|
||||||
.iter()
|
.iter()
|
||||||
@ -474,7 +465,6 @@ impl<'a> Scope<'a> {
|
|||||||
/// Get an iterator to entries in the [`Scope`].
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn into_iter(
|
pub(crate) fn into_iter(
|
||||||
self,
|
self,
|
||||||
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> {
|
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> {
|
||||||
@ -511,7 +501,6 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert_eq!(value.cast::<String>(), "hello");
|
/// assert_eq!(value.cast::<String>(), "hello");
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
||||||
self.iter_raw()
|
self.iter_raw()
|
||||||
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
|
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
|
||||||
@ -519,7 +508,6 @@ impl<'a> Scope<'a> {
|
|||||||
/// Get an iterator to entries in the [`Scope`].
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
/// Shared values are not expanded.
|
/// Shared values are not expanded.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
|
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
|
||||||
self.names
|
self.names
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Implementations of [`serde::Serialize`].
|
//! Implementations of [`serde::Serialize`].
|
||||||
|
|
||||||
use crate::dynamic::{Union, Variant};
|
use crate::dynamic::Union;
|
||||||
use crate::{Dynamic, ImmutableString};
|
use crate::{Dynamic, ImmutableString};
|
||||||
use serde::ser::{Serialize, Serializer};
|
use serde::ser::{Serialize, Serializer};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -9,6 +9,9 @@ use std::prelude::v1::*;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
use crate::dynamic::Variant;
|
||||||
|
|
||||||
impl Serialize for Dynamic {
|
impl Serialize for Dynamic {
|
||||||
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
132
src/token.rs
132
src/token.rs
@ -83,53 +83,73 @@ impl Position {
|
|||||||
/// Create a new [`Position`].
|
/// Create a new [`Position`].
|
||||||
///
|
///
|
||||||
/// `line` must not be zero.
|
/// `line` must not be zero.
|
||||||
/// If [`Position`] is zero, then it is at the beginning of a line.
|
///
|
||||||
|
/// If `position` is zero, then it is at the beginning of a line.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `line` is zero.
|
/// Panics if `line` is zero.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(line: u16, _position: u16) -> Self {
|
pub fn new(line: u16, position: u16) -> Self {
|
||||||
assert!(line != 0, "line cannot be zero");
|
assert!(line != 0, "line cannot be zero");
|
||||||
|
|
||||||
|
let _pos = position;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
line,
|
line,
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
pos: _position,
|
pos: _pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Create a new [`Position`].
|
||||||
|
///
|
||||||
|
/// If `line` is zero, then [`None`] is returned.
|
||||||
|
///
|
||||||
|
/// If `position` is zero, then it is at the beginning of a line.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new_const(line: u16, position: u16) -> Option<Self> {
|
||||||
|
if line == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let _pos = position;
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
line,
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
pos: _pos,
|
||||||
|
})
|
||||||
|
}
|
||||||
/// Get the line number (1-based), or [`None`] if there is no position.
|
/// Get the line number (1-based), or [`None`] if there is no position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn line(self) -> Option<usize> {
|
pub const fn line(self) -> Option<usize> {
|
||||||
if self.is_none() {
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
return if self.is_none() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "no_position"))]
|
Some(self.line as usize)
|
||||||
return Some(self.line as usize);
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_position")]
|
#[cfg(feature = "no_position")]
|
||||||
unreachable!("there is no Position");
|
return None;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn position(self) -> Option<usize> {
|
pub const fn position(self) -> Option<usize> {
|
||||||
if self.is_none() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
return if self.pos == 0 {
|
return if self.is_none() || self.pos == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(self.pos as usize)
|
Some(self.pos as usize)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_position")]
|
#[cfg(feature = "no_position")]
|
||||||
unreachable!("there is no Position");
|
return None;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Advance by one character position.
|
/// Advance by one character position.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -192,7 +212,6 @@ impl Position {
|
|||||||
}
|
}
|
||||||
/// Print this [`Position`] for debug purposes.
|
/// Print this [`Position`] for debug purposes.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
if !self.is_none() {
|
if !self.is_none() {
|
||||||
@ -211,7 +230,6 @@ impl Default for Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Position {
|
impl fmt::Display for Position {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if self.is_none() {
|
if self.is_none() {
|
||||||
write!(f, "none")?;
|
write!(f, "none")?;
|
||||||
@ -227,7 +245,6 @@ impl fmt::Display for Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Position {
|
impl fmt::Debug for Position {
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
write!(f, "{}:{}", self.line, self.pos)?;
|
write!(f, "{}:{}", self.line, self.pos)?;
|
||||||
@ -586,7 +603,8 @@ impl Token {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_op_assignment(&self) -> bool {
|
pub const fn is_op_assignment(&self) -> bool {
|
||||||
match self {
|
matches!(
|
||||||
|
self,
|
||||||
Self::PlusAssign
|
Self::PlusAssign
|
||||||
| Self::MinusAssign
|
| Self::MinusAssign
|
||||||
| Self::MultiplyAssign
|
| Self::MultiplyAssign
|
||||||
@ -597,9 +615,8 @@ impl Token {
|
|||||||
| Self::PowerOfAssign
|
| Self::PowerOfAssign
|
||||||
| Self::AndAssign
|
| Self::AndAssign
|
||||||
| Self::OrAssign
|
| Self::OrAssign
|
||||||
| Self::XOrAssign => true,
|
| Self::XOrAssign
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
||||||
@ -625,7 +642,8 @@ impl Token {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn has_op_assignment(&self) -> bool {
|
pub const fn has_op_assignment(&self) -> bool {
|
||||||
match self {
|
matches!(
|
||||||
|
self,
|
||||||
Self::Plus
|
Self::Plus
|
||||||
| Self::Minus
|
| Self::Minus
|
||||||
| Self::Multiply
|
| Self::Multiply
|
||||||
@ -636,9 +654,8 @@ impl Token {
|
|||||||
| Self::PowerOf
|
| Self::PowerOf
|
||||||
| Self::Ampersand
|
| Self::Ampersand
|
||||||
| Self::Pipe
|
| Self::Pipe
|
||||||
| Self::XOr => true,
|
| Self::XOr
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the corresponding op-assignment operator of the token.
|
/// Get the corresponding op-assignment operator of the token.
|
||||||
@ -772,12 +789,7 @@ impl Token {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_eof(&self) -> bool {
|
pub const fn is_eof(&self) -> bool {
|
||||||
use Token::*;
|
matches!(self, Self::EOF)
|
||||||
|
|
||||||
match self {
|
|
||||||
EOF => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If another operator is after these, it's probably an unary operator
|
// If another operator is after these, it's probably an unary operator
|
||||||
@ -917,6 +929,7 @@ impl Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Is this token an active standard keyword?
|
/// Is this token an active standard keyword?
|
||||||
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_keyword(&self) -> bool {
|
pub const fn is_keyword(&self) -> bool {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
@ -939,15 +952,12 @@ impl Token {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_reserved(&self) -> bool {
|
pub const fn is_reserved(&self) -> bool {
|
||||||
match self {
|
matches!(self, Self::Reserved(_))
|
||||||
Self::Reserved(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a token into a function name, if possible.
|
/// Convert a token into a function name, if possible.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[must_use]
|
#[inline]
|
||||||
pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> {
|
pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s),
|
Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s),
|
||||||
@ -959,10 +969,7 @@ impl Token {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_custom(&self) -> bool {
|
pub const fn is_custom(&self) -> bool {
|
||||||
match self {
|
matches!(self, Self::Custom(_))
|
||||||
Self::Custom(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1045,7 +1052,6 @@ pub trait InputStream {
|
|||||||
/// # Volatile API
|
/// # Volatile API
|
||||||
///
|
///
|
||||||
/// This function is volatile and may change.
|
/// This function is volatile and may change.
|
||||||
#[must_use]
|
|
||||||
pub fn parse_string_literal(
|
pub fn parse_string_literal(
|
||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
state: &mut TokenizeState,
|
state: &mut TokenizeState,
|
||||||
@ -1269,20 +1275,26 @@ fn scan_block_comment(
|
|||||||
while let Some(c) = stream.get_next() {
|
while let Some(c) = stream.get_next() {
|
||||||
pos.advance();
|
pos.advance();
|
||||||
|
|
||||||
comment.as_mut().map(|comment| comment.push(c));
|
if let Some(comment) = comment.as_mut() {
|
||||||
|
comment.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
match c {
|
match c {
|
||||||
'/' => {
|
'/' => {
|
||||||
stream.peek_next().filter(|&c2| c2 == '*').map(|c2| {
|
stream.peek_next().filter(|&c2| c2 == '*').map(|c2| {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
comment.as_mut().map(|comment| comment.push(c2));
|
if let Some(comment) = comment.as_mut() {
|
||||||
|
comment.push(c2);
|
||||||
|
}
|
||||||
level += 1;
|
level += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
stream.peek_next().filter(|&c2| c2 == '/').map(|c2| {
|
stream.peek_next().filter(|&c2| c2 == '/').map(|c2| {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
comment.as_mut().map(|comment| comment.push(c2));
|
if let Some(comment) = comment.as_mut() {
|
||||||
|
comment.push(c2);
|
||||||
|
}
|
||||||
level -= 1;
|
level -= 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1304,7 +1316,7 @@ fn scan_block_comment(
|
|||||||
/// # Volatile API
|
/// # Volatile API
|
||||||
///
|
///
|
||||||
/// This function is volatile and may change.
|
/// This function is volatile and may change.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_next_token(
|
pub fn get_next_token(
|
||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
@ -1324,21 +1336,13 @@ pub fn get_next_token(
|
|||||||
/// Test if the given character is a hex character.
|
/// Test if the given character is a hex character.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_hex_digit(c: char) -> bool {
|
fn is_hex_digit(c: char) -> bool {
|
||||||
match c {
|
matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9')
|
||||||
'a'..='f' => true,
|
|
||||||
'A'..='F' => true,
|
|
||||||
'0'..='9' => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if the given character is a numeric digit.
|
/// Test if the given character is a numeric digit.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_numeric_digit(c: char) -> bool {
|
fn is_numeric_digit(c: char) -> bool {
|
||||||
match c {
|
matches!(c, '0'..='9')
|
||||||
'0'..='9' => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if the comment block is a doc-comment.
|
/// Test if the comment block is a doc-comment.
|
||||||
@ -1455,7 +1459,7 @@ fn get_next_token_inner(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// symbol after period - probably a float
|
// symbol after period - probably a float
|
||||||
ch @ _ if !is_id_first_alphabetic(ch) => {
|
ch if !is_id_first_alphabetic(ch) => {
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
pos.advance();
|
pos.advance();
|
||||||
result.push('0');
|
result.push('0');
|
||||||
@ -1750,7 +1754,9 @@ fn get_next_token_inner(
|
|||||||
pos.new_line();
|
pos.new_line();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
comment.as_mut().map(|comment| comment.push(c));
|
if let Some(comment) = comment.as_mut() {
|
||||||
|
comment.push(c);
|
||||||
|
}
|
||||||
pos.advance();
|
pos.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1982,11 +1988,11 @@ fn get_identifier(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some((Token::Identifier(identifier), start_pos));
|
Some((Token::Identifier(identifier), start_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this keyword allowed as a function?
|
/// Is this keyword allowed as a function?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_keyword_function(name: &str) -> bool {
|
pub fn is_keyword_function(name: &str) -> bool {
|
||||||
match name {
|
match name {
|
||||||
@ -2062,7 +2068,7 @@ pub struct MultiInputsStream<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InputStream for MultiInputsStream<'_> {
|
impl InputStream for MultiInputsStream<'_> {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn unget(&mut self, ch: char) {
|
fn unget(&mut self, ch: char) {
|
||||||
if self.buf.is_some() {
|
if self.buf.is_some() {
|
||||||
panic!("cannot unget two characters in a row");
|
panic!("cannot unget two characters in a row");
|
||||||
@ -2252,7 +2258,7 @@ impl Engine {
|
|||||||
self.lex_raw(input, Some(map))
|
self.lex_raw(input, Some(map))
|
||||||
}
|
}
|
||||||
/// Tokenize an input text stream with an optional mapping function.
|
/// Tokenize an input text stream with an optional mapping function.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn lex_raw<'a>(
|
pub(crate) fn lex_raw<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -9,7 +9,6 @@ use std::{
|
|||||||
|
|
||||||
/// Cast a type into another type.
|
/// Cast a type into another type.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> {
|
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> {
|
||||||
if TypeId::of::<B>() == a.type_id() {
|
if TypeId::of::<B>() == a.type_id() {
|
||||||
// SAFETY: Just checked we have the right type. We explicitly forget the
|
// SAFETY: Just checked we have the right type. We explicitly forget the
|
||||||
@ -27,7 +26,6 @@ pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> {
|
|||||||
|
|
||||||
/// Cast a Boxed type into another type.
|
/// Cast a Boxed type into another type.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
||||||
// Only allow casting to the exact same type
|
// Only allow casting to the exact same type
|
||||||
if TypeId::of::<X>() == TypeId::of::<T>() {
|
if TypeId::of::<X>() == TypeId::of::<T>() {
|
||||||
|
@ -53,6 +53,18 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?),
|
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?),
|
||||||
[4, 1, 2, 3]
|
[4, 1, 2, 3]
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let y = [1, 2, 3]; let z = [42]; y[z.len]")?,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let y = [1, 2, [3, 4, 5, 6]]; let z = [42]; y[2][z.len]")?,
|
||||||
|
4
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("let y = [1, 2, 3]; let z = [2]; y[z[0]]")?,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
convert_to_vec::<INT>(engine.eval(
|
convert_to_vec::<INT>(engine.eval(
|
||||||
|
Loading…
Reference in New Issue
Block a user