commit
4d0b0ab4c9
29
.github/workflows/benchmark.yml
vendored
Normal file
29
.github/workflows/benchmark.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
name: Benchmark
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
benchmark:
|
||||||
|
name: Run Rust benchmark
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: rustup toolchain update nightly && rustup default nightly
|
||||||
|
- name: Run benchmark
|
||||||
|
run: cargo +nightly bench | tee output.txt
|
||||||
|
- name: Store benchmark result
|
||||||
|
uses: rhysd/github-action-benchmark@v1
|
||||||
|
with:
|
||||||
|
name: Rust Benchmark
|
||||||
|
tool: 'cargo'
|
||||||
|
output-file-path: output.txt
|
||||||
|
# Use personal access token instead of GITHUB_TOKEN due to https://github.community/t5/GitHub-Actions/Github-action-not-triggering-gh-pages-upon-push/td-p/26869/highlight/false
|
||||||
|
github-token: ${{ secrets.RHAI }}
|
||||||
|
auto-push: true
|
||||||
|
# Show alert with commit comment on detecting possible performance regression
|
||||||
|
alert-threshold: '200%'
|
||||||
|
comment-on-alert: true
|
||||||
|
fail-on-alert: true
|
||||||
|
alert-comment-cc-users: '@schungx'
|
23
.github/workflows/build.yml
vendored
23
.github/workflows/build.yml
vendored
@ -77,6 +77,29 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --manifest-path=no_std/no_std_test/Cargo.toml ${{matrix.flags}}
|
args: --manifest-path=no_std/no_std_test/Cargo.toml ${{matrix.flags}}
|
||||||
|
rustfmt:
|
||||||
|
name: Check Formatting
|
||||||
|
runs-on: windows-latest
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- name: Run Rustfmt
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
- name: Run Clippy
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --all -- -Aclippy::all -Dclippy::perf
|
||||||
codegen_build:
|
codegen_build:
|
||||||
name: Codegen Build
|
name: Codegen Build
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
@ -23,7 +23,7 @@ keywords = [ "scripting" ]
|
|||||||
categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smallvec = { version = "1.4.1", default-features = false }
|
smallvec = { version = "1.4.2", default-features = false }
|
||||||
rhai_codegen = { version = "0.1", path = "codegen" }
|
rhai_codegen = { version = "0.1", path = "codegen" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@ -81,7 +81,7 @@ features = ["compile-time-rng"]
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0.111"
|
version = "1.0.116"
|
||||||
default_features = false
|
default_features = false
|
||||||
features = ["derive", "alloc"]
|
features = ["derive", "alloc"]
|
||||||
optional = true
|
optional = true
|
||||||
@ -92,7 +92,7 @@ default_features = false
|
|||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
instant= { version = "0.1.4", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
|
instant= { version = "0.1.7", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = [ "serde", "internals" ]
|
features = [ "serde", "internals" ]
|
||||||
|
@ -11,12 +11,20 @@ Bug fixes
|
|||||||
* Fixes a bug in `Module::set_fn_4_mut`.
|
* Fixes a bug in `Module::set_fn_4_mut`.
|
||||||
* Module API's now properly handle `&str` and `String` parameters.
|
* Module API's now properly handle `&str` and `String` parameters.
|
||||||
* Indexers are available under `no_object`.
|
* Indexers are available under `no_object`.
|
||||||
|
* Registered operator-assignment functions (e.g. `+=`) now work correctly.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box<EvalAltResult>>`.
|
||||||
|
* `Engine::register_indexer_XXX` and `Module::set_indexer_XXX` panic when the type is `Arrary`, `Map` or `String`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Plugins support via procedural macros.
|
* Plugins support via procedural macros.
|
||||||
* Scripted functions are allowed in packages.
|
* Scripted functions are allowed in packages.
|
||||||
|
* `parse_int` and `parse_float` functions.
|
||||||
|
|
||||||
|
|
||||||
Version 0.18.3
|
Version 0.18.3
|
||||||
|
@ -32,7 +32,10 @@ pub struct ExportInfo {
|
|||||||
|
|
||||||
pub fn parse_attr_items(args: ParseStream) -> syn::Result<ExportInfo> {
|
pub fn parse_attr_items(args: ParseStream) -> syn::Result<ExportInfo> {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Ok(ExportInfo { item_span: args.span(), items: Vec::new()});
|
return Ok(ExportInfo {
|
||||||
|
item_span: args.span(),
|
||||||
|
items: Vec::new(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let arg_list = args
|
let arg_list = args
|
||||||
.call(syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_separated_nonempty)?;
|
.call(syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_separated_nonempty)?;
|
||||||
@ -80,10 +83,17 @@ pub fn parse_punctuated_items(
|
|||||||
.ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?,
|
.ok_or_else(|| syn::Error::new(attr_path.span(), "expecting attribute name"))?,
|
||||||
x => return Err(syn::Error::new(x.span(), "expecting identifier")),
|
x => return Err(syn::Error::new(x.span(), "expecting identifier")),
|
||||||
};
|
};
|
||||||
attrs.push(AttrItem { key, value, span: arg_span });
|
attrs.push(AttrItem {
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
span: arg_span,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ExportInfo { item_span: list_span, items: attrs })
|
Ok(ExportInfo {
|
||||||
|
item_span: list_span,
|
||||||
|
items: attrs,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn outer_item_attributes<T: ExportedParams>(
|
pub(crate) fn outer_item_attributes<T: ExportedParams>(
|
||||||
|
@ -16,6 +16,7 @@ use quote::{quote, quote_spanned};
|
|||||||
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
||||||
|
|
||||||
use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
|
use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
|
||||||
|
use crate::rhai_module::flatten_type_groups;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Index {
|
pub enum Index {
|
||||||
@ -223,7 +224,8 @@ impl Parse for ExportedFn {
|
|||||||
syn::FnArg::Receiver(syn::Receiver {
|
syn::FnArg::Receiver(syn::Receiver {
|
||||||
reference: Some(_), ..
|
reference: Some(_), ..
|
||||||
}) => true,
|
}) => true,
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => match ty.as_ref() {
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
||||||
|
match flatten_type_groups(ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
&syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: Some(_),
|
mutability: Some(_),
|
||||||
..
|
..
|
||||||
@ -232,7 +234,7 @@ impl Parse for ExportedFn {
|
|||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => match elem.as_ref() {
|
}) => match flatten_type_groups(elem.as_ref()) {
|
||||||
&syn::Type::Path(ref p) if p.path == str_type_path => false,
|
&syn::Type::Path(ref p) if p.path == str_type_path => false,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
@ -243,7 +245,8 @@ impl Parse for ExportedFn {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
}
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -257,7 +260,7 @@ impl Parse for ExportedFn {
|
|||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
||||||
_ => panic!("internal error: receiver argument outside of first position!?"),
|
_ => panic!("internal error: receiver argument outside of first position!?"),
|
||||||
};
|
};
|
||||||
let is_ok = match ty.as_ref() {
|
let is_ok = match flatten_type_groups(ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
&syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: Some(_),
|
mutability: Some(_),
|
||||||
..
|
..
|
||||||
@ -266,7 +269,9 @@ impl Parse for ExportedFn {
|
|||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => matches!(elem.as_ref(), &syn::Type::Path(ref p) if p.path == str_type_path),
|
}) => {
|
||||||
|
matches!(flatten_type_groups(elem.as_ref()), &syn::Type::Path(ref p) if p.path == str_type_path)
|
||||||
|
}
|
||||||
&syn::Type::Verbatim(_) => false,
|
&syn::Type::Verbatim(_) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
@ -605,13 +610,9 @@ impl ExportedFn {
|
|||||||
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
||||||
match first_arg {
|
match first_arg {
|
||||||
syn::FnArg::Typed(pattern) => {
|
syn::FnArg::Typed(pattern) => {
|
||||||
let arg_type: &syn::Type = {
|
let arg_type: &syn::Type = match flatten_type_groups(pattern.ty.as_ref()) {
|
||||||
match pattern.ty.as_ref() {
|
&syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(),
|
||||||
&syn::Type::Reference(syn::TypeReference { ref elem, .. }) => {
|
p => p,
|
||||||
elem.as_ref()
|
|
||||||
}
|
|
||||||
ref p => p,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let downcast_span = quote_spanned!(
|
let downcast_span = quote_spanned!(
|
||||||
arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap());
|
arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap());
|
||||||
@ -648,12 +649,12 @@ impl ExportedFn {
|
|||||||
match arg {
|
match arg {
|
||||||
syn::FnArg::Typed(pattern) => {
|
syn::FnArg::Typed(pattern) => {
|
||||||
let arg_type: &syn::Type = pattern.ty.as_ref();
|
let arg_type: &syn::Type = pattern.ty.as_ref();
|
||||||
let downcast_span = match pattern.ty.as_ref() {
|
let downcast_span = match flatten_type_groups(pattern.ty.as_ref()) {
|
||||||
&syn::Type::Reference(syn::TypeReference {
|
&syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => match elem.as_ref() {
|
}) => match flatten_type_groups(elem.as_ref()) {
|
||||||
&syn::Type::Path(ref p) if p.path == str_type_path => {
|
&syn::Type::Path(ref p) if p.path == str_type_path => {
|
||||||
is_string = true;
|
is_string = true;
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
@ -672,7 +673,7 @@ impl ExportedFn {
|
|||||||
is_string = false;
|
is_string = false;
|
||||||
is_ref = false;
|
is_ref = false;
|
||||||
quote_spanned!(arg_type.span()=>
|
quote_spanned!(arg_type.span()=>
|
||||||
mem::take(args[#i]).clone().cast::<#arg_type>())
|
mem::take(args[#i]).cast::<#arg_type>())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,12 +85,12 @@ pub(crate) fn generate_body(
|
|||||||
.map(|fnarg| match fnarg {
|
.map(|fnarg| match fnarg {
|
||||||
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"),
|
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"),
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
|
||||||
let arg_type = match ty.as_ref() {
|
let arg_type = match flatten_type_groups(ty.as_ref()) {
|
||||||
syn::Type::Reference(syn::TypeReference {
|
syn::Type::Reference(syn::TypeReference {
|
||||||
mutability: None,
|
mutability: None,
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => match elem.as_ref() {
|
}) => match flatten_type_groups(elem.as_ref()) {
|
||||||
syn::Type::Path(ref p) if p.path == str_type_path => {
|
syn::Type::Path(ref p) if p.path == str_type_path => {
|
||||||
syn::parse2::<syn::Type>(quote! {
|
syn::parse2::<syn::Type>(quote! {
|
||||||
ImmutableString })
|
ImmutableString })
|
||||||
@ -107,11 +107,11 @@ pub(crate) fn generate_body(
|
|||||||
mutability: Some(_),
|
mutability: Some(_),
|
||||||
ref elem,
|
ref elem,
|
||||||
..
|
..
|
||||||
}) => match elem.as_ref() {
|
}) => match flatten_type_groups(elem.as_ref()) {
|
||||||
syn::Type::Path(ref p) => syn::parse2::<syn::Type>(quote! {
|
syn::Type::Path(ref p) => syn::parse2::<syn::Type>(quote! {
|
||||||
#p })
|
#p })
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
_ => panic!("internal error: non-string shared reference!?"),
|
_ => panic!("internal error: invalid mutable reference!?"),
|
||||||
},
|
},
|
||||||
t => t.clone(),
|
t => t.clone(),
|
||||||
};
|
};
|
||||||
@ -174,6 +174,14 @@ pub(crate) fn generate_body(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flatten_type_groups(ty: &syn::Type) -> &syn::Type {
|
||||||
|
match ty {
|
||||||
|
syn::Type::Group(syn::TypeGroup { ref elem, .. })
|
||||||
|
| syn::Type::Paren(syn::TypeParen { ref elem, .. }) => flatten_type_groups(elem.as_ref()),
|
||||||
|
_ => ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
|
pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
|
||||||
let mut renames = HashMap::<String, proc_macro2::Span>::new();
|
let mut renames = HashMap::<String, proc_macro2::Span>::new();
|
||||||
let mut names = HashMap::<String, proc_macro2::Span>::new();
|
let mut names = HashMap::<String, proc_macro2::Span>::new();
|
||||||
|
@ -323,7 +323,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +364,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(do_something(arg0)))
|
Ok(Dynamic::from(do_something(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,8 +398,8 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,7 +445,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<usize>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<usize>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0, arg1)))
|
Ok(Dynamic::from(increment(arg0, arg1)))
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_one_to(arg0)))
|
Ok(Dynamic::from(add_one_to(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_one_to(arg0)))
|
Ok(Dynamic::from(add_one_to(arg0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,8 +474,8 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<INT>();
|
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_n_to(arg0, arg1)))
|
Ok(Dynamic::from(add_n_to(arg0, arg1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,8 +540,8 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<INT>();
|
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,8 +613,8 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<INT>();
|
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1447,7 +1447,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
||||||
}
|
}
|
||||||
@ -1518,7 +1518,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
||||||
}
|
}
|
||||||
@ -1585,7 +1585,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||||
}
|
}
|
||||||
@ -1657,7 +1657,7 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||||
}
|
}
|
||||||
@ -1726,8 +1726,8 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 3usize,
|
debug_assert_eq!(args.len(), 3usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg2 = mem::take(args[2usize]).clone().cast::<FLOAT>();
|
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
|
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
|
||||||
}
|
}
|
||||||
@ -1802,8 +1802,8 @@ mod generate_tests {
|
|||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
debug_assert_eq!(args.len(), 3usize,
|
debug_assert_eq!(args.len(), 3usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg2 = mem::take(args[2usize]).clone().cast::<FLOAT>();
|
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||||
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
|
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,6 @@ fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
if s == "math::bar_fourth_adders::add_int (i64, i64)"
|
if s == "math::bar_fourth_adders::add_int (i64, i64)"
|
||||||
&& p == rhai::Position::new(3, 42)));
|
&& p == rhai::Position::new(3, 42)));
|
||||||
|
|
||||||
|
|
||||||
assert!(matches!(*engine.eval::<FLOAT>(
|
assert!(matches!(*engine.eval::<FLOAT>(
|
||||||
r#"import "Math::Advanced" as math;
|
r#"import "Math::Advanced" as math;
|
||||||
let ex = 41.0;
|
let ex = 41.0;
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "diag_test"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["J Henry Waugh <jhwgh1968@protonmail.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
[[bin]]
|
|
||||||
name = "test_template"
|
|
||||||
path = "test_template.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
rhai = { version = "*", path = ".." }
|
|
@ -1,35 +0,0 @@
|
|||||||
use rhai::plugin::*;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Point {
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[export_module]
|
|
||||||
pub mod test_module {
|
|
||||||
#[rhai_mod(name = "bar")]
|
|
||||||
pub mod test_mod {
|
|
||||||
#[rhai_fn(name = "foo")]
|
|
||||||
pub fn test_fn(input: Point) -> bool {
|
|
||||||
input.x > input.y
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rhai_fn(return_raw)]
|
|
||||||
pub fn test_fn_raw(input: Point) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
|
|
||||||
Ok(Dynamic::from(input.x > input.y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let n = Point {
|
|
||||||
x: 0.0,
|
|
||||||
y: 10.0,
|
|
||||||
};
|
|
||||||
if test_module::test_mod::test_fn(n) {
|
|
||||||
println!("yes");
|
|
||||||
} else {
|
|
||||||
println!("no");
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,7 +33,9 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but
|
|||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
| `push` | element to insert | inserts an element at the end |
|
| `push` | element to insert | inserts an element at the end |
|
||||||
| `+=` operator, `append` | array to append | concatenates the second array to the end of the first |
|
| `+=` operator | array, element to insert (not another array) | inserts an element at the end |
|
||||||
|
| `append` | array to append | concatenates the second array to the end of the first |
|
||||||
|
| `+=` operator | array, array to append | concatenates the second array to the end of the first |
|
||||||
| `+` operator | first array, second array | concatenates the first array with the second |
|
| `+` operator | first array, second array | concatenates the first array with the second |
|
||||||
| `insert` | element to insert, position<br/>(beginning if <= 0, end if >= length) | insert an element at a certain index |
|
| `insert` | element to insert, position<br/>(beginning if <= 0, end if >= length) | insert an element at a certain index |
|
||||||
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
|
||||||
@ -49,7 +51,9 @@ Use Custom Types With Arrays
|
|||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
To use a [custom type] with arrays, a number of array functions need to be manually implemented,
|
To use a [custom type] with arrays, a number of array functions need to be manually implemented,
|
||||||
in particular `push`, `pad` and the `==` operator (in order to support the `in` operator).
|
in particular `push`, `pad` and the `+=` operator. In addition, the `==` operator must be
|
||||||
|
implemented for the [custom type] in order to support the `in` operator which uses `==` to
|
||||||
|
compare elements.
|
||||||
|
|
||||||
See the section on [custom types] for more details.
|
See the section on [custom types] for more details.
|
||||||
|
|
||||||
@ -104,7 +108,7 @@ let foo = y[0];
|
|||||||
foo == 1;
|
foo == 1;
|
||||||
|
|
||||||
y.push(4); // 4 elements
|
y.push(4); // 4 elements
|
||||||
y.push(5); // 5 elements
|
y += 5; // 5 elements
|
||||||
|
|
||||||
y.len == 5;
|
y.len == 5;
|
||||||
|
|
||||||
|
@ -9,11 +9,11 @@ Integer Functions
|
|||||||
The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
|
The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
|
||||||
operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||||
|
|
||||||
| Function | Description |
|
| Function | No available under | Description |
|
||||||
| ------------ | ----------------------------------------------------------------------- |
|
| -------- | :----------------: | ---------------------------------------------------------------------- |
|
||||||
| `abs` | absolute value |
|
| `abs` | | absolute value |
|
||||||
| `sign` | returns -1 (`INT`) if the number is negative, +1 if positive, 0 if zero |
|
| `sign` | | return -1 (`INT`) if the number is negative, +1 if positive, 0 if zero |
|
||||||
| [`to_float`] | converts an integer type to `FLOAT` |
|
|
||||||
|
|
||||||
Floating-Point Functions
|
Floating-Point Functions
|
||||||
-----------------------
|
-----------------------
|
||||||
@ -31,3 +31,16 @@ operate on `f64` only:
|
|||||||
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties |
|
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties |
|
||||||
| Conversion | [`to_int`] |
|
| Conversion | [`to_int`] |
|
||||||
| Testing | `is_nan`, `is_finite`, `is_infinite` methods and properties |
|
| Testing | `is_nan`, `is_finite`, `is_infinite` methods and properties |
|
||||||
|
|
||||||
|
|
||||||
|
Conversion Functions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`])
|
||||||
|
parse numbers:
|
||||||
|
|
||||||
|
| Function | No available under | Description |
|
||||||
|
| --------------- | :----------------: | -------------------------------------------------- |
|
||||||
|
| [`to_float`] | [`no_float`] | convert an integer type to `FLOAT` |
|
||||||
|
| [`parse_int`] | | convert a [string] to `INT` with an optional radix |
|
||||||
|
| [`parse_float`] | [`no_float`] | convert a [string] to `FLOAT` |
|
||||||
|
@ -161,13 +161,15 @@ x.type_of() == "Hello";
|
|||||||
Use the Custom Type With Arrays
|
Use the Custom Type With Arrays
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
The `push` and `pad` functions for [arrays] are only defined for standard built-in types.
|
The `push` and `pad` functions, as well as the `+=` operator, for [arrays] are only defined for
|
||||||
For custom types, type-specific versions must be registered:
|
standard built-in types. For custom types, type-specific versions must be registered:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
engine
|
engine
|
||||||
.register_fn("push", |list: &mut Array, item: TestStruct| {
|
.register_fn("push", |list: &mut Array, item: TestStruct| {
|
||||||
list.push(Dynamic::from(item));
|
list.push(Dynamic::from(item));
|
||||||
|
}).register_fn("+=", |list: &mut Array, item: TestStruct| {
|
||||||
|
list.push(Dynamic::from(item));
|
||||||
}).register_fn("pad", |list: &mut Array, len: i64, item: TestStruct| {
|
}).register_fn("pad", |list: &mut Array, len: i64, item: TestStruct| {
|
||||||
if len as usize > list.len() {
|
if len as usize > list.len() {
|
||||||
list.resize(len as usize, item);
|
list.resize(len as usize, item);
|
||||||
@ -176,7 +178,7 @@ engine
|
|||||||
```
|
```
|
||||||
|
|
||||||
In particular, in order to use the `in` operator with a custom type for an [array],
|
In particular, in order to use the `in` operator with a custom type for an [array],
|
||||||
the `==` operator must be registered for that custom type:
|
the `==` operator must be registered for the custom type:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Assume 'TestStruct' implements `PartialEq`
|
// Assume 'TestStruct' implements `PartialEq`
|
||||||
|
@ -12,10 +12,19 @@ Getters and setters are disabled when the [`no_object`] feature is used.
|
|||||||
| `Engine` API | Description | Return Value of Function |
|
| `Engine` API | Description | Return Value of Function |
|
||||||
| --------------------- | ------------------------------------------------- | :-----------------------------------: |
|
| --------------------- | ------------------------------------------------- | :-----------------------------------: |
|
||||||
| `register_get` | Register a getter | _Any_ |
|
| `register_get` | Register a getter | _Any_ |
|
||||||
| `register_set` | Register a setter | _Any_ |
|
| `register_set` | Register a setter | _None_ |
|
||||||
| `register_get_set` | Short-hand to register both a getter and a setter | _None_ |
|
| `register_get_set` | Short-hand to register both a getter and a setter | _None_ |
|
||||||
| `register_get_result` | Register a getter | `Result<Dynamic, Box<EvalAltResult>>` |
|
| `register_get_result` | Register a getter | `Result<Dynamic, Box<EvalAltResult>>` |
|
||||||
| `register_set_result` | Register a setter | `Result<Dynamic, Box<EvalAltResult>>` |
|
| `register_set_result` | Register a setter | `Result<(), Box<EvalAltResult>>` |
|
||||||
|
|
||||||
|
|
||||||
|
Cannot Override Object Maps
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Getters and setters are only intended for [custom types].
|
||||||
|
|
||||||
|
Any getter or setter function registered for [object maps] is simply ignored because
|
||||||
|
the get/set calls will be interpreted as properties on the [object maps].
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
@ -28,15 +37,13 @@ struct TestStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
// Returning a 'String' is OK - Rhai converts it into 'ImmutableString'
|
// Remember &mut must be used even for getters
|
||||||
fn get_field(&mut self) -> String {
|
fn get_field(&mut self) -> String {
|
||||||
self.field.clone()
|
self.field.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember Rhai uses 'ImmutableString' or '&str' instead of 'String'
|
fn set_field(&mut self, new_val: &str) {
|
||||||
fn set_field(&mut self, new_val: ImmutableString) {
|
self.field = new_val.to_string();
|
||||||
// Get a 'String' from an 'ImmutableString'
|
|
||||||
self.field = (*new_val).clone();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
@ -51,7 +58,6 @@ let mut engine = Engine::new();
|
|||||||
.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field)
|
.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field)
|
||||||
.register_fn("new_ts", TestStruct::new);
|
.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
// Return result can be 'String' - Rhai will automatically convert it from 'ImmutableString'
|
|
||||||
let result = engine.eval::<String>(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?;
|
let result = engine.eval::<String>(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?;
|
||||||
|
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
|
@ -13,16 +13,22 @@ Like getters and setters, indexers take a `&mut` reference to the first paramete
|
|||||||
|
|
||||||
Indexers are disabled when the [`no_index`] feature is used.
|
Indexers are disabled when the [`no_index`] feature is used.
|
||||||
|
|
||||||
For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for
|
|
||||||
[arrays] and [object maps].
|
|
||||||
|
|
||||||
| `Engine` API | Description | Return Value of Function |
|
| `Engine` API | Description | Return Value of Function |
|
||||||
| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: |
|
| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: |
|
||||||
| `register_indexer_get` | Register an index getter | _Any_ |
|
| `register_indexer_get` | Register an index getter | _Any_ |
|
||||||
| `register_indexer_set` | Register an index setter | _Any_ |
|
| `register_indexer_set` | Register an index setter | _None_ |
|
||||||
| `register_indexer_get_set` | Short-hand to register both an index getter and a setter | _None_ |
|
| `register_indexer_get_set` | Short-hand to register both an index getter and a setter | _None_ |
|
||||||
| `register_indexer_get_result` | Register an index getter | `Result<Dynamic, Box<EvalAltResult>>` |
|
| `register_indexer_get_result` | Register an index getter | `Result<Dynamic, Box<EvalAltResult>>` |
|
||||||
| `register_indexer_set_result` | Register an index setter | `Result<Dynamic, Box<EvalAltResult>>` |
|
| `register_indexer_set_result` | Register an index setter | `Result<(), Box<EvalAltResult>>` |
|
||||||
|
|
||||||
|
|
||||||
|
Cannot Override Arrays, Object Maps and Strings
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
For efficiency reasons, indexers **cannot** be used to overload (i.e. override)
|
||||||
|
built-in indexing operations for [arrays], [object maps] and [strings].
|
||||||
|
|
||||||
|
Attempting to register indexers for an [array], [object map] or [string] panics.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
@ -35,6 +41,7 @@ struct TestStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
|
// Remember &mut must be used even for getters
|
||||||
fn get_field(&mut self, index: i64) -> i64 {
|
fn get_field(&mut self, index: i64) -> i64 {
|
||||||
self.fields[index as usize]
|
self.fields[index as usize]
|
||||||
}
|
}
|
||||||
@ -60,3 +67,5 @@ let result = engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?;
|
|||||||
|
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
|
||||||
|
122
src/api.rs
122
src/api.rs
@ -12,7 +12,10 @@ use crate::scope::Scope;
|
|||||||
use crate::token::{lex, Position};
|
use crate::token::{lex, Position};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
use crate::{
|
||||||
|
engine::{Array, FN_IDX_GET, FN_IDX_SET},
|
||||||
|
utils::ImmutableString,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -36,6 +39,7 @@ use crate::optimize::optimize_into_ast;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
|
string::String,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
@ -191,7 +195,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> i64 { self.field }
|
/// fn get_field(&mut self) -> i64 { self.field }
|
||||||
/// }
|
/// }
|
||||||
@ -243,7 +246,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
|
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
/// Ok(self.field.into())
|
/// Ok(self.field.into())
|
||||||
@ -324,7 +326,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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`.
|
||||||
/// Returns `Result<Dynamic, Box<EvalAltResult>>`.
|
/// Returns `Result<(), Box<EvalAltResult>>`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -338,9 +340,9 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||||
/// fn set_field(&mut self, new_val: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> {
|
||||||
/// self.field = new_val;
|
/// self.field = new_val;
|
||||||
/// Ok(().into())
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -367,13 +369,16 @@ impl Engine {
|
|||||||
pub fn register_set_result<T, U>(
|
pub fn register_set_result<T, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
callback: impl Fn(&mut T, U) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
callback: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self
|
) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
U: Variant + Clone,
|
U: Variant + Clone,
|
||||||
{
|
{
|
||||||
self.register_result_fn(&make_setter(name), callback)
|
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
|
||||||
|
callback(obj, value)?;
|
||||||
|
Ok(().into())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Short-hand for registering both getter and setter functions
|
/// Short-hand for registering both getter and setter functions
|
||||||
@ -391,8 +396,8 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||||
/// fn get_field(&mut self) -> i64 { self.field }
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
|
/// fn get_field(&mut self) -> i64 { self.field }
|
||||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -432,6 +437,11 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -442,7 +452,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
||||||
/// }
|
/// }
|
||||||
@ -475,6 +484,20 @@ impl Engine {
|
|||||||
U: Variant + Clone,
|
U: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
{
|
{
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
|
panic!("Cannot register indexer for arrays.");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
|
panic!("Cannot register indexer for object maps.");
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<String>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<&str>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<ImmutableString>()
|
||||||
|
{
|
||||||
|
panic!("Cannot register indexer for strings.");
|
||||||
|
}
|
||||||
|
|
||||||
self.register_fn(FN_IDX_GET, callback)
|
self.register_fn(FN_IDX_GET, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,6 +506,11 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -495,7 +523,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
/// fn get_field(&mut self, index: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
/// Ok(self.fields[index as usize].into())
|
/// Ok(self.fields[index as usize].into())
|
||||||
@ -527,11 +554,30 @@ impl Engine {
|
|||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
{
|
{
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
|
panic!("Cannot register indexer for arrays.");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
|
panic!("Cannot register indexer for object maps.");
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<String>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<&str>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<ImmutableString>()
|
||||||
|
{
|
||||||
|
panic!("Cannot register indexer for strings.");
|
||||||
|
}
|
||||||
|
|
||||||
self.register_result_fn(FN_IDX_GET, callback)
|
self.register_result_fn(FN_IDX_GET, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register an index setter for a custom type with the `Engine`.
|
/// Register an index setter for a custom type with the `Engine`.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -569,18 +615,37 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn register_indexer_set<T, X, U>(
|
pub fn register_indexer_set<T, X, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
callback: impl Fn(&mut T, X, U) + SendSync + 'static,
|
||||||
) -> &mut Self
|
) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
U: Variant + Clone,
|
U: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
{
|
{
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
|
panic!("Cannot register indexer for arrays.");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
|
panic!("Cannot register indexer for object maps.");
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<String>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<&str>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<ImmutableString>()
|
||||||
|
{
|
||||||
|
panic!("Cannot register indexer for strings.");
|
||||||
|
}
|
||||||
|
|
||||||
self.register_fn(FN_IDX_SET, callback)
|
self.register_fn(FN_IDX_SET, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register an index setter for a custom type with the `Engine`.
|
/// Register an index setter for a custom type with the `Engine`.
|
||||||
/// Returns `Result<Dynamic, Box<EvalAltResult>>`.
|
/// Returns `Result<(), Box<EvalAltResult>>`.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -594,9 +659,9 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> {
|
||||||
/// self.fields[index as usize] = value;
|
/// self.fields[index as usize] = value;
|
||||||
/// Ok(().into())
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -622,18 +687,40 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn register_indexer_set_result<T, X, U>(
|
pub fn register_indexer_set_result<T, X, U>(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&mut T, X, U) -> Result<Dynamic, Box<EvalAltResult>> + SendSync + 'static,
|
callback: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self
|
) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
U: Variant + Clone,
|
U: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
{
|
{
|
||||||
self.register_result_fn(FN_IDX_SET, callback)
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
|
panic!("Cannot register indexer for arrays.");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
|
panic!("Cannot register indexer for object maps.");
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<String>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<&str>()
|
||||||
|
|| TypeId::of::<T>() == TypeId::of::<ImmutableString>()
|
||||||
|
{
|
||||||
|
panic!("Cannot register indexer for strings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| {
|
||||||
|
callback(obj, index, value)?;
|
||||||
|
Ok(().into())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Short-hand for register both index getter and setter functions for a custom type with the `Engine`.
|
/// Short-hand for register both index getter and setter functions for a custom type with the `Engine`.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -644,6 +731,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
||||||
/// }
|
/// }
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::any::{map_std_type_name, Dynamic, Union};
|
use crate::any::{map_std_type_name, Dynamic, Union};
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::fn_call::run_builtin_op_assignment;
|
use crate::fn_call::run_builtin_op_assignment;
|
||||||
use crate::fn_native::{CallableFunction, Callback, FnPtr};
|
use crate::fn_native::{Callback, FnPtr};
|
||||||
use crate::module::{Module, ModuleRef};
|
use crate::module::{Module, ModuleRef};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||||
@ -756,10 +756,7 @@ impl Engine {
|
|||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
// No index getter - try to call an index setter
|
// No index getter - try to call an index setter
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
EvalAltResult::ErrorIndexingType(_, _) => {
|
EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()),
|
||||||
// Raise error if there is no index getter nor setter
|
|
||||||
Some(new_val.unwrap())
|
|
||||||
}
|
|
||||||
// Any other error - return
|
// Any other error - return
|
||||||
err => return Err(Box::new(err)),
|
err => return Err(Box::new(err)),
|
||||||
},
|
},
|
||||||
@ -1168,7 +1165,7 @@ impl Engine {
|
|||||||
.take_immutable_string()
|
.take_immutable_string()
|
||||||
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
|
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
|
||||||
|
|
||||||
map.entry(index).or_insert(Default::default()).into()
|
map.entry(index).or_insert_with(Default::default).into()
|
||||||
} else {
|
} else {
|
||||||
let index = idx
|
let index = idx
|
||||||
.read_lock::<ImmutableString>()
|
.read_lock::<ImmutableString>()
|
||||||
@ -1360,23 +1357,36 @@ impl Engine {
|
|||||||
let arg_types = once(lhs_ptr.type_id()).chain(once(rhs_val.type_id()));
|
let arg_types = once(lhs_ptr.type_id()).chain(once(rhs_val.type_id()));
|
||||||
let hash_fn = calc_fn_hash(empty(), op, 2, arg_types);
|
let hash_fn = calc_fn_hash(empty(), op, 2, arg_types);
|
||||||
|
|
||||||
if let Some(CallableFunction::Method(func)) = self
|
match self
|
||||||
.global_module
|
.global_module
|
||||||
.get_fn(hash_fn, false)
|
.get_fn(hash_fn, false)
|
||||||
.or_else(|| self.packages.get_fn(hash_fn, false))
|
.or_else(|| self.packages.get_fn(hash_fn, false))
|
||||||
{
|
{
|
||||||
|
// op= function registered as method
|
||||||
|
Some(func) if func.is_method() => {
|
||||||
|
let mut lock_guard;
|
||||||
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
let mut lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
||||||
let lhs_ptr_inner = lock_guard.deref_mut();
|
lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
|
} else {
|
||||||
|
lhs_ptr_inner = lhs_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
||||||
|
|
||||||
// Overriding exact implementation
|
// Overriding exact implementation
|
||||||
func(self, lib, &mut [lhs_ptr_inner, &mut rhs_val])?;
|
if func.is_plugin_fn() {
|
||||||
|
func.get_plugin_fn().call(args)?;
|
||||||
} else {
|
} else {
|
||||||
// Overriding exact implementation
|
func.get_native_fn()(self, lib, args)?;
|
||||||
func(self, lib, &mut [lhs_ptr, &mut rhs_val])?;
|
|
||||||
}
|
}
|
||||||
} else if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() {
|
}
|
||||||
// Not built in, map to `var = var op rhs`
|
// Built-in op-assignment function
|
||||||
|
_ if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_some() => {}
|
||||||
|
// Not built-in: expand to `var = var op rhs`
|
||||||
|
_ => {
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
|
||||||
// Clone the LHS value
|
// Clone the LHS value
|
||||||
@ -1385,17 +1395,20 @@ impl Engine {
|
|||||||
// Run function
|
// Run function
|
||||||
let (value, _) = self
|
let (value, _) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
state, lib, op, 0, args, false, false, false, None, None, level,
|
state, lib, op, 0, args, false, false, false, None, None,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.new_position(*op_pos))?;
|
.map_err(|err| err.new_position(*op_pos))?;
|
||||||
|
|
||||||
let value = value.flatten();
|
let value = value.flatten();
|
||||||
|
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
|
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
|
||||||
} else {
|
} else {
|
||||||
*lhs_ptr = value;
|
*lhs_ptr = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2014,6 +2027,6 @@ impl Engine {
|
|||||||
self.type_names
|
self.type_names
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|t| t.get(name).map(String::as_str))
|
.and_then(|t| t.get(name).map(String::as_str))
|
||||||
.unwrap_or(map_std_type_name(name))
|
.unwrap_or_else(|| map_std_type_name(name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,10 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
||||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::engine::{make_getter, make_setter};
|
use crate::engine::{make_getter, make_setter, Map};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
@ -675,6 +674,11 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// If there is a similar existing setter Rust function, it is replaced.
|
/// If there is a similar existing setter Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -686,12 +690,25 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
|
panic!("Cannot register indexer for arrays.");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<A>() == TypeId::of::<Map>() {
|
||||||
|
panic!("Cannot register indexer for object maps.");
|
||||||
|
}
|
||||||
|
if TypeId::of::<A>() == TypeId::of::<String>()
|
||||||
|
|| TypeId::of::<A>() == TypeId::of::<&str>()
|
||||||
|
|| TypeId::of::<A>() == TypeId::of::<ImmutableString>()
|
||||||
|
{
|
||||||
|
panic!("Cannot register indexer for strings.");
|
||||||
|
}
|
||||||
|
|
||||||
self.set_fn_2_mut(FN_IDX_GET, func)
|
self.set_fn_2_mut(FN_IDX_GET, func)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,6 +790,11 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -785,12 +807,25 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash, true));
|
/// assert!(module.contains_fn(hash, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
|
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
|
panic!("Cannot register indexer for arrays.");
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<A>() == TypeId::of::<Map>() {
|
||||||
|
panic!("Cannot register indexer for object maps.");
|
||||||
|
}
|
||||||
|
if TypeId::of::<A>() == TypeId::of::<String>()
|
||||||
|
|| TypeId::of::<A>() == TypeId::of::<&str>()
|
||||||
|
|| TypeId::of::<A>() == TypeId::of::<ImmutableString>()
|
||||||
|
{
|
||||||
|
panic!("Cannot register indexer for strings.");
|
||||||
|
}
|
||||||
|
|
||||||
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
|
||||||
let b = cast_arg::<B>(&mut args[1]);
|
let b = cast_arg::<B>(&mut args[1]);
|
||||||
let c = cast_arg::<C>(&mut args[2]);
|
let c = cast_arg::<C>(&mut args[2]);
|
||||||
@ -812,6 +847,11 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// If there are similar existing Rust functions, they are replaced.
|
/// If there are similar existing Rust functions, they are replaced.
|
||||||
///
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the type is `Array` or `Map`.
|
||||||
|
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -830,7 +870,6 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash_get, true));
|
/// assert!(module.contains_fn(hash_get, true));
|
||||||
/// assert!(module.contains_fn(hash_set, true));
|
/// assert!(module.contains_fn(hash_set, true));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -23,16 +23,17 @@ pub type Unit = ();
|
|||||||
|
|
||||||
macro_rules! gen_array_functions {
|
macro_rules! gen_array_functions {
|
||||||
($root:ident => $($arg_type:ident),+ ) => {
|
($root:ident => $($arg_type:ident),+ ) => {
|
||||||
pub mod $root { $(pub mod $arg_type {
|
pub mod $root { $( pub mod $arg_type {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
||||||
#[export_fn]
|
#[export_module]
|
||||||
|
pub mod functions {
|
||||||
|
#[rhai_fn(name = "push", name = "+=")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push(list: &mut Array, item: $arg_type) {
|
pub fn push(list: &mut Array, item: $arg_type) {
|
||||||
list.push(Dynamic::from(item));
|
list.push(Dynamic::from(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_fn]
|
|
||||||
pub fn insert(list: &mut Array, position: INT, item: $arg_type) {
|
pub fn insert(list: &mut Array, position: INT, item: $arg_type) {
|
||||||
if position <= 0 {
|
if position <= 0 {
|
||||||
list.insert(0, Dynamic::from(item));
|
list.insert(0, Dynamic::from(item));
|
||||||
@ -42,14 +43,14 @@ macro_rules! gen_array_functions {
|
|||||||
list.insert(position as usize, Dynamic::from(item));
|
list.insert(position as usize, Dynamic::from(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})* }
|
})* }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reg_functions {
|
macro_rules! reg_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
set_exported_fn!($mod_name, "push", $root::$arg_type::push);
|
combine_with_exported_module!($mod_name, "array_functions", $root::$arg_type::functions);
|
||||||
set_exported_fn!($mod_name, "insert", $root::$arg_type::insert);
|
|
||||||
|
|
||||||
$mod_name.set_raw_fn("pad",
|
$mod_name.set_raw_fn("pad",
|
||||||
&[TypeId::of::<Array>(), TypeId::of::<INT>(), TypeId::of::<$arg_type>()],
|
&[TypeId::of::<Array>(), TypeId::of::<INT>(), TypeId::of::<$arg_type>()],
|
||||||
@ -58,8 +59,6 @@ macro_rules! reg_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
||||||
combine_with_exported_module!(lib, "array", array_functions);
|
|
||||||
|
|
||||||
reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
@ -77,6 +76,9 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
reg_functions!(lib += map; Map);
|
reg_functions!(lib += map; Map);
|
||||||
|
|
||||||
|
// Merge in the module at the end to override `+=` for arrays
|
||||||
|
combine_with_exported_module!(lib, "array", array_functions);
|
||||||
|
|
||||||
// Register array iterator
|
// Register array iterator
|
||||||
lib.set_iter(
|
lib.set_iter(
|
||||||
TypeId::of::<Array>(),
|
TypeId::of::<Array>(),
|
||||||
|
@ -18,7 +18,6 @@ mod map_functions {
|
|||||||
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
||||||
map.contains_key(&prop)
|
map.contains_key(&prop)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "len", get = "len")]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(map: &mut Map) -> INT {
|
pub fn len(map: &mut Map) -> INT {
|
||||||
map.len() as INT
|
map.len() as INT
|
||||||
@ -46,9 +45,7 @@ mod map_functions {
|
|||||||
}
|
}
|
||||||
pub fn fill_with(map1: &mut Map, map2: Map) {
|
pub fn fill_with(map1: &mut Map, map2: Map) {
|
||||||
map2.into_iter().for_each(|(key, value)| {
|
map2.into_iter().for_each(|(key, value)| {
|
||||||
if !map1.contains_key(&key) {
|
map1.entry(key).or_insert(value);
|
||||||
map1.insert(key, value);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::parser::FLOAT;
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::{result::EvalAltResult, token::Position};
|
use crate::result::EvalAltResult;
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -67,6 +68,9 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
|
|||||||
|
|
||||||
reg_functions!(lib += basic_to_int::to_int(char));
|
reg_functions!(lib += basic_to_int::to_int(char));
|
||||||
|
|
||||||
|
set_exported_fn!(lib, "parse_int", parse_int);
|
||||||
|
set_exported_fn!(lib, "parse_int", parse_int_radix);
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
@ -223,6 +227,21 @@ mod float_functions {
|
|||||||
Ok((x.trunc() as INT).into())
|
Ok((x.trunc() as INT).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
#[inline]
|
||||||
|
pub fn parse_float(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
s.trim()
|
||||||
|
.parse::<FLOAT>()
|
||||||
|
.map(Into::<Dynamic>::into)
|
||||||
|
.map_err(|err| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Error parsing floating-point number '{}': {}", s, err),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -249,3 +268,30 @@ gen_conversion_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32,
|
|||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
gen_conversion_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
gen_conversion_functions!(num_128_to_int => to_int (i128, u128) -> INT);
|
||||||
|
|
||||||
|
#[export_fn(return_raw)]
|
||||||
|
fn parse_int_radix(s: &str, radix: INT) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
if radix < 2 || radix > 36 {
|
||||||
|
return EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Invalid radix: '{}'", radix),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
INT::from_str_radix(s.trim(), radix as u32)
|
||||||
|
.map(Into::<Dynamic>::into)
|
||||||
|
.map_err(|err| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Error parsing integer number '{}': {}", s, err),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[export_fn(return_raw)]
|
||||||
|
#[inline(always)]
|
||||||
|
fn parse_int(s: &str) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
parse_int_radix(s, 10)
|
||||||
|
}
|
||||||
|
@ -151,6 +151,7 @@ fn to_string<T: Display>(x: &mut T) -> ImmutableString {
|
|||||||
fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
fn to_debug<T: Debug>(x: &mut T) -> ImmutableString {
|
||||||
format!("{:?}", x).into()
|
format!("{:?}", x).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
mod format_map {
|
mod format_map {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -17,28 +17,30 @@ use crate::stdlib::{
|
|||||||
|
|
||||||
macro_rules! gen_concat_functions {
|
macro_rules! gen_concat_functions {
|
||||||
($root:ident => $($arg_type:ident),+ ) => {
|
($root:ident => $($arg_type:ident),+ ) => {
|
||||||
pub mod $root { $(pub mod $arg_type {
|
pub mod $root { $( pub mod $arg_type {
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
|
||||||
#[export_fn]
|
#[export_module]
|
||||||
|
pub mod functions {
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn append_func(x: &mut ImmutableString, y: $arg_type) -> String {
|
pub fn append_func(x: &str, y: $arg_type) -> String {
|
||||||
format!("{}{}", x, y)
|
format!("{}{}", x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_fn]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prepend_func(x: &mut $arg_type, y: ImmutableString) -> String {
|
pub fn prepend_func(x: &mut $arg_type, y: &str) -> String {
|
||||||
format!("{}{}", x, y)
|
format!("{}{}", x, y)
|
||||||
}
|
}
|
||||||
})* }
|
}
|
||||||
|
} )* }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! reg_functions {
|
macro_rules! reg_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
set_exported_fn!($mod_name, "+", $root::$arg_type::append_func);
|
combine_with_exported_module!($mod_name, "strings_concat", $root::$arg_type::functions);
|
||||||
set_exported_fn!($mod_name, "+", $root::$arg_type::prepend_func);
|
|
||||||
)* }
|
)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +156,7 @@ mod string_functions {
|
|||||||
|
|
||||||
#[rhai_fn(name = "len", get = "len")]
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(s: &mut ImmutableString) -> INT {
|
pub fn len(s: &str) -> INT {
|
||||||
s.chars().count() as INT
|
s.chars().count() as INT
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,17 +184,17 @@ mod string_functions {
|
|||||||
|
|
||||||
#[rhai_fn(name = "contains")]
|
#[rhai_fn(name = "contains")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_char(s: &mut ImmutableString, ch: char) -> bool {
|
pub fn contains_char(s: &str, ch: char) -> bool {
|
||||||
s.contains(ch)
|
s.contains(ch)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "contains")]
|
#[rhai_fn(name = "contains")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_string(s: &mut ImmutableString, find: ImmutableString) -> bool {
|
pub fn contains_string(s: &str, find: ImmutableString) -> bool {
|
||||||
s.contains(find.as_str())
|
s.contains(find.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char_starting_from(s: &mut ImmutableString, ch: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(s: &str, ch: char, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if (start as usize) >= s.chars().count() {
|
||||||
@ -207,17 +209,13 @@ mod string_functions {
|
|||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char(s: &mut ImmutableString, ch: char) -> INT {
|
pub fn index_of_char(s: &str, ch: char) -> INT {
|
||||||
s.find(ch)
|
s.find(ch)
|
||||||
.map(|index| s[0..index].chars().count() as INT)
|
.map(|index| s[0..index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_string_starting_from(
|
pub fn index_of_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT {
|
||||||
s: &mut ImmutableString,
|
|
||||||
find: ImmutableString,
|
|
||||||
start: INT,
|
|
||||||
) -> INT {
|
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if (start as usize) >= s.chars().count() {
|
||||||
@ -232,14 +230,14 @@ mod string_functions {
|
|||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_string(s: &mut ImmutableString, find: ImmutableString) -> INT {
|
pub fn index_of_string(s: &str, find: ImmutableString) -> INT {
|
||||||
s.find(find.as_str())
|
s.find(find.as_str())
|
||||||
.map(|index| s[0..index].chars().count() as INT)
|
.map(|index| s[0..index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT)
|
.unwrap_or(-1 as INT)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "sub_string")]
|
#[rhai_fn(name = "sub_string")]
|
||||||
pub fn sub_string(s: ImmutableString, start: INT, len: INT) -> ImmutableString {
|
pub fn sub_string(s: &str, start: INT, len: INT) -> ImmutableString {
|
||||||
let offset = if s.is_empty() || len <= 0 {
|
let offset = if s.is_empty() || len <= 0 {
|
||||||
return "".to_string().into();
|
return "".to_string().into();
|
||||||
} else if start < 0 {
|
} else if start < 0 {
|
||||||
@ -268,7 +266,7 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
#[rhai_fn(name = "sub_string")]
|
#[rhai_fn(name = "sub_string")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn sub_string_starting_from(s: ImmutableString, start: INT) -> ImmutableString {
|
pub fn sub_string_starting_from(s: &str, start: INT) -> ImmutableString {
|
||||||
let len = s.len() as INT;
|
let len = s.len() as INT;
|
||||||
sub_string(s, start, len)
|
sub_string(s, start, len)
|
||||||
}
|
}
|
||||||
@ -332,13 +330,29 @@ mod string_functions {
|
|||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn append(x: &mut ImmutableString, y: Array) -> String {
|
pub fn append(x: &str, y: Array) -> String {
|
||||||
format!("{}{:?}", x, y)
|
format!("{}{:?}", x, y)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prepend(x: &mut Array, y: ImmutableString) -> String {
|
pub fn prepend(x: &mut Array, y: &str) -> String {
|
||||||
format!("{:?}{}", x, y)
|
format!("{:?}{}", x, y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
pub mod maps {
|
||||||
|
use crate::engine::Map;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
|
#[inline]
|
||||||
|
pub fn append(x: &str, y: Map) -> String {
|
||||||
|
format!("{}#{:?}", x, y)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
|
#[inline]
|
||||||
|
pub fn prepend(x: &mut Map, y: &str) -> String {
|
||||||
|
format!("#{:?}{}", x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3479,7 +3479,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
Union::Unit(_) => Some(Expr::Unit(pos)),
|
Union::Unit(_) => Some(Expr::Unit(pos)),
|
||||||
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
Union::Int(value) => Some(Expr::IntegerConstant(Box::new((value, pos)))),
|
||||||
Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))),
|
Union::Char(value) => Some(Expr::CharConstant(Box::new((value, pos)))),
|
||||||
Union::Str(value) => Some(Expr::StringConstant(Box::new((value.clone(), pos)))),
|
Union::Str(value) => Some(Expr::StringConstant(Box::new((value, pos)))),
|
||||||
Union::Bool(true) => Some(Expr::True(pos)),
|
Union::Bool(true) => Some(Expr::True(pos)),
|
||||||
Union::Bool(false) => Some(Expr::False(pos)),
|
Union::Bool(false) => Some(Expr::False(pos)),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
12
src/token.rs
12
src/token.rs
@ -752,8 +752,10 @@ pub fn parse_string_literal(
|
|||||||
let mut result: StaticVec<char> = Default::default();
|
let mut result: StaticVec<char> = Default::default();
|
||||||
let mut escape: StaticVec<char> = Default::default();
|
let mut escape: StaticVec<char> = Default::default();
|
||||||
|
|
||||||
|
let start = *pos;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next_char = stream.get_next().ok_or((LERR::UnterminatedString, *pos))?;
|
let next_char = stream.get_next().ok_or((LERR::UnterminatedString, start))?;
|
||||||
|
|
||||||
pos.advance();
|
pos.advance();
|
||||||
|
|
||||||
@ -838,17 +840,19 @@ pub fn parse_string_literal(
|
|||||||
ch if enclosing_char == ch && escape.is_empty() => break,
|
ch if enclosing_char == ch && escape.is_empty() => break,
|
||||||
|
|
||||||
// Unknown escape sequence
|
// Unknown escape sequence
|
||||||
_ if !escape.is_empty() => {
|
ch if !escape.is_empty() => {
|
||||||
|
escape.push(ch);
|
||||||
|
|
||||||
return Err((
|
return Err((
|
||||||
LERR::MalformedEscapeSequence(escape.into_iter().collect()),
|
LERR::MalformedEscapeSequence(escape.into_iter().collect()),
|
||||||
*pos,
|
*pos,
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot have new-lines inside string literals
|
// Cannot have new-lines inside string literals
|
||||||
'\n' => {
|
'\n' => {
|
||||||
pos.rewind();
|
pos.rewind();
|
||||||
return Err((LERR::UnterminatedString, *pos));
|
return Err((LERR::UnterminatedString, start));
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other characters
|
// All other characters
|
||||||
|
@ -13,6 +13,10 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
'3'
|
'3'
|
||||||
);
|
);
|
||||||
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
assert!(engine.eval::<bool>("let y = [1, 2, 3]; 2 in y")?);
|
||||||
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y += 4; y[3]")?, 4);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y.push(4); y[3]")?, 4);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -20,6 +20,15 @@ fn test_float() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_float_parse() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!((engine.eval::<FLOAT>(r#"parse_float("9.9999")"#)? - 9.9999 as FLOAT).abs() < EPSILON);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn test_struct_with_float() -> Result<(), Box<EvalAltResult>> {
|
fn test_struct_with_float() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
@ -108,3 +108,14 @@ fn test_math() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_math_parse() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"parse_int("42")"#)?, 42);
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"parse_int("42", 16)"#)?, 0x42);
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"parse_int("abcdef", 16)"#)?, 0xabcdef);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user