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:
|
||||
command: build
|
||||
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:
|
||||
name: Codegen Build
|
||||
runs-on: ${{matrix.os}}
|
||||
|
@ -23,7 +23,7 @@ keywords = [ "scripting" ]
|
||||
categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
||||
|
||||
[dependencies]
|
||||
smallvec = { version = "1.4.1", default-features = false }
|
||||
smallvec = { version = "1.4.2", default-features = false }
|
||||
rhai_codegen = { version = "0.1", path = "codegen" }
|
||||
|
||||
[features]
|
||||
@ -81,7 +81,7 @@ features = ["compile-time-rng"]
|
||||
optional = true
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.111"
|
||||
version = "1.0.116"
|
||||
default_features = false
|
||||
features = ["derive", "alloc"]
|
||||
optional = true
|
||||
@ -92,7 +92,7 @@ default_features = false
|
||||
optional = true
|
||||
|
||||
[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]
|
||||
features = [ "serde", "internals" ]
|
||||
|
@ -11,12 +11,20 @@ Bug fixes
|
||||
* Fixes a bug in `Module::set_fn_4_mut`.
|
||||
* Module API's now properly handle `&str` and `String` parameters.
|
||||
* 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
|
||||
------------
|
||||
|
||||
* Plugins support via procedural macros.
|
||||
* Scripted functions are allowed in packages.
|
||||
* `parse_int` and `parse_float` functions.
|
||||
|
||||
|
||||
Version 0.18.3
|
||||
|
@ -32,7 +32,10 @@ pub struct ExportInfo {
|
||||
|
||||
pub fn parse_attr_items(args: ParseStream) -> syn::Result<ExportInfo> {
|
||||
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
|
||||
.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"))?,
|
||||
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>(
|
||||
|
@ -16,6 +16,7 @@ use quote::{quote, quote_spanned};
|
||||
use syn::{parse::Parse, parse::ParseStream, parse::Parser, spanned::Spanned};
|
||||
|
||||
use crate::attrs::{ExportInfo, ExportScope, ExportedParams};
|
||||
use crate::rhai_module::flatten_type_groups;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Index {
|
||||
@ -223,7 +224,8 @@ impl Parse for ExportedFn {
|
||||
syn::FnArg::Receiver(syn::Receiver {
|
||||
reference: Some(_), ..
|
||||
}) => 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 {
|
||||
mutability: Some(_),
|
||||
..
|
||||
@ -232,7 +234,7 @@ impl Parse for ExportedFn {
|
||||
mutability: None,
|
||||
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,
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
@ -243,7 +245,8 @@ impl Parse for ExportedFn {
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
@ -257,7 +260,7 @@ impl Parse for ExportedFn {
|
||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
||||
_ => 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 {
|
||||
mutability: Some(_),
|
||||
..
|
||||
@ -266,7 +269,9 @@ impl Parse for ExportedFn {
|
||||
mutability: None,
|
||||
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,
|
||||
_ => true,
|
||||
};
|
||||
@ -605,13 +610,9 @@ impl ExportedFn {
|
||||
let var = syn::Ident::new("arg0", proc_macro2::Span::call_site());
|
||||
match first_arg {
|
||||
syn::FnArg::Typed(pattern) => {
|
||||
let arg_type: &syn::Type = {
|
||||
match pattern.ty.as_ref() {
|
||||
&syn::Type::Reference(syn::TypeReference { ref elem, .. }) => {
|
||||
elem.as_ref()
|
||||
}
|
||||
ref p => p,
|
||||
}
|
||||
let arg_type: &syn::Type = match flatten_type_groups(pattern.ty.as_ref()) {
|
||||
&syn::Type::Reference(syn::TypeReference { ref elem, .. }) => elem.as_ref(),
|
||||
p => p,
|
||||
};
|
||||
let downcast_span = quote_spanned!(
|
||||
arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap());
|
||||
@ -648,12 +649,12 @@ impl ExportedFn {
|
||||
match arg {
|
||||
syn::FnArg::Typed(pattern) => {
|
||||
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 {
|
||||
mutability: None,
|
||||
ref elem,
|
||||
..
|
||||
}) => match elem.as_ref() {
|
||||
}) => match flatten_type_groups(elem.as_ref()) {
|
||||
&syn::Type::Path(ref p) if p.path == str_type_path => {
|
||||
is_string = true;
|
||||
is_ref = true;
|
||||
@ -672,7 +673,7 @@ impl ExportedFn {
|
||||
is_string = false;
|
||||
is_ref = false;
|
||||
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 {
|
||||
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"),
|
||||
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 {
|
||||
mutability: None,
|
||||
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::parse2::<syn::Type>(quote! {
|
||||
ImmutableString })
|
||||
@ -107,11 +107,11 @@ pub(crate) fn generate_body(
|
||||
mutability: Some(_),
|
||||
ref elem,
|
||||
..
|
||||
}) => match elem.as_ref() {
|
||||
}) => match flatten_type_groups(elem.as_ref()) {
|
||||
syn::Type::Path(ref p) => syn::parse2::<syn::Type>(quote! {
|
||||
#p })
|
||||
.unwrap(),
|
||||
_ => panic!("internal error: non-string shared reference!?"),
|
||||
_ => panic!("internal error: invalid mutable reference!?"),
|
||||
},
|
||||
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> {
|
||||
let mut renames = 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>> {
|
||||
debug_assert_eq!(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)))
|
||||
}
|
||||
|
||||
@ -364,7 +364,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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)))
|
||||
}
|
||||
|
||||
@ -398,8 +398,8 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(args.len(), 2usize,
|
||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||
let arg0 = mem::take(args[0usize]).clone().cast::<usize>();
|
||||
let arg1 = mem::take(args[1usize]).clone().cast::<usize>();
|
||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||
}
|
||||
|
||||
@ -445,7 +445,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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();
|
||||
Ok(Dynamic::from(increment(arg0, arg1)))
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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)))
|
||||
}
|
||||
|
||||
@ -446,7 +446,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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)))
|
||||
}
|
||||
|
||||
@ -474,8 +474,8 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(args.len(), 2usize,
|
||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).clone().cast::<INT>();
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_n_to(arg0, arg1)))
|
||||
}
|
||||
|
||||
@ -540,8 +540,8 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(args.len(), 2usize,
|
||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).clone().cast::<INT>();
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||
}
|
||||
|
||||
@ -613,8 +613,8 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(args.len(), 2usize,
|
||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||
let arg0 = mem::take(args[0usize]).clone().cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).clone().cast::<INT>();
|
||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<INT>();
|
||||
Ok(Dynamic::from(add_together(arg0, arg1)))
|
||||
}
|
||||
|
||||
@ -1447,7 +1447,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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();
|
||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
||||
}
|
||||
@ -1518,7 +1518,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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();
|
||||
Ok(Dynamic::from(int_foo(arg0, arg1)))
|
||||
}
|
||||
@ -1585,7 +1585,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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();
|
||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||
}
|
||||
@ -1657,7 +1657,7 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(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();
|
||||
Ok(Dynamic::from(get_by_index(arg0, arg1)))
|
||||
}
|
||||
@ -1726,8 +1726,8 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(args.len(), 3usize,
|
||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
||||
let arg2 = mem::take(args[2usize]).clone().cast::<FLOAT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
|
||||
}
|
||||
@ -1802,8 +1802,8 @@ mod generate_tests {
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
debug_assert_eq!(args.len(), 3usize,
|
||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||
let arg1 = mem::take(args[1usize]).clone().cast::<u64>();
|
||||
let arg2 = mem::take(args[2usize]).clone().cast::<FLOAT>();
|
||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||
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)"
|
||||
&& p == rhai::Position::new(3, 42)));
|
||||
|
||||
|
||||
assert!(matches!(*engine.eval::<FLOAT>(
|
||||
r#"import "Math::Advanced" as math;
|
||||
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 |
|
||||
| ------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| `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 |
|
||||
| `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) |
|
||||
@ -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,
|
||||
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.
|
||||
|
||||
@ -104,7 +108,7 @@ let foo = y[0];
|
||||
foo == 1;
|
||||
|
||||
y.push(4); // 4 elements
|
||||
y.push(5); // 5 elements
|
||||
y += 5; // 5 elements
|
||||
|
||||
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`])
|
||||
operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||
|
||||
| Function | Description |
|
||||
| ------------ | ----------------------------------------------------------------------- |
|
||||
| `abs` | absolute value |
|
||||
| `sign` | returns -1 (`INT`) if the number is negative, +1 if positive, 0 if zero |
|
||||
| [`to_float`] | converts an integer type to `FLOAT` |
|
||||
| Function | No available under | Description |
|
||||
| -------- | :----------------: | ---------------------------------------------------------------------- |
|
||||
| `abs` | | absolute value |
|
||||
| `sign` | | return -1 (`INT`) if the number is negative, +1 if positive, 0 if zero |
|
||||
|
||||
|
||||
Floating-Point Functions
|
||||
-----------------------
|
||||
@ -31,3 +31,16 @@ operate on `f64` only:
|
||||
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties |
|
||||
| Conversion | [`to_int`] |
|
||||
| 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
|
||||
------------------------------
|
||||
|
||||
The `push` and `pad` functions for [arrays] are only defined for standard built-in types.
|
||||
For custom types, type-specific versions must be registered:
|
||||
The `push` and `pad` functions, as well as the `+=` operator, for [arrays] are only defined for
|
||||
standard built-in types. For custom types, type-specific versions must be registered:
|
||||
|
||||
```rust
|
||||
engine
|
||||
.register_fn("push", |list: &mut Array, item: TestStruct| {
|
||||
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| {
|
||||
if len as usize > list.len() {
|
||||
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],
|
||||
the `==` operator must be registered for that custom type:
|
||||
the `==` operator must be registered for the custom type:
|
||||
|
||||
```rust
|
||||
// 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 |
|
||||
| --------------------- | ------------------------------------------------- | :-----------------------------------: |
|
||||
| `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_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
|
||||
@ -28,15 +37,13 @@ struct 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 {
|
||||
self.field.clone()
|
||||
}
|
||||
|
||||
// Remember Rhai uses 'ImmutableString' or '&str' instead of 'String'
|
||||
fn set_field(&mut self, new_val: ImmutableString) {
|
||||
// Get a 'String' from an 'ImmutableString'
|
||||
self.field = (*new_val).clone();
|
||||
fn set_field(&mut self, new_val: &str) {
|
||||
self.field = new_val.to_string();
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
@ -51,7 +58,6 @@ let mut engine = Engine::new();
|
||||
.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field)
|
||||
.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"#)?;
|
||||
|
||||
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.
|
||||
|
||||
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 |
|
||||
| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: |
|
||||
| `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_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
|
||||
@ -35,6 +41,7 @@ struct TestStruct {
|
||||
}
|
||||
|
||||
impl TestStruct {
|
||||
// Remember &mut must be used even for getters
|
||||
fn get_field(&mut self, index: i64) -> i64 {
|
||||
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
|
||||
```
|
||||
|
||||
**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};
|
||||
|
||||
#[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"))]
|
||||
use crate::{
|
||||
@ -36,6 +39,7 @@ use crate::optimize::optimize_into_ast;
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
string::String,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
@ -191,7 +195,6 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||
///
|
||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||
/// fn get_field(&mut self) -> i64 { self.field }
|
||||
/// }
|
||||
@ -243,7 +246,6 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// fn new() -> Self { TestStruct { field: 1 } }
|
||||
///
|
||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||
/// fn get_field(&mut self) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// Ok(self.field.into())
|
||||
@ -324,7 +326,7 @@ impl 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
|
||||
///
|
||||
@ -338,9 +340,9 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// 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;
|
||||
/// Ok(().into())
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -367,13 +369,16 @@ impl Engine {
|
||||
pub fn register_set_result<T, U>(
|
||||
&mut self,
|
||||
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
|
||||
where
|
||||
T: 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
|
||||
@ -391,8 +396,8 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// 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`.
|
||||
/// fn get_field(&mut self) -> i64 { self.field }
|
||||
/// 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`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the type is `Array` or `Map`.
|
||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -442,7 +452,6 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// 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] }
|
||||
/// }
|
||||
@ -475,6 +484,20 @@ impl Engine {
|
||||
U: 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)
|
||||
}
|
||||
|
||||
@ -483,6 +506,11 @@ impl Engine {
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
@ -495,7 +523,6 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// 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) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
/// Ok(self.fields[index as usize].into())
|
||||
@ -527,11 +554,30 @@ impl Engine {
|
||||
T: 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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
@ -569,18 +615,37 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn register_indexer_set<T, X, U>(
|
||||
&mut self,
|
||||
callback: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
||||
callback: impl Fn(&mut T, X, U) + SendSync + 'static,
|
||||
) -> &mut Self
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: 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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
@ -594,9 +659,9 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// 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;
|
||||
/// Ok(().into())
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -622,18 +687,40 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn register_indexer_set_result<T, X, U>(
|
||||
&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
|
||||
where
|
||||
T: Variant + Clone,
|
||||
U: 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`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the type is `Array` or `Map`.
|
||||
/// Indexers for arrays, object maps and strings cannot be registered.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -644,6 +731,7 @@ impl Engine {
|
||||
///
|
||||
/// impl TestStruct {
|
||||
/// 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 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::calc_fn_hash;
|
||||
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::optimize::OptimizationLevel;
|
||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||
@ -756,10 +756,7 @@ impl Engine {
|
||||
Err(err) => match *err {
|
||||
// No index getter - try to call an index setter
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
EvalAltResult::ErrorIndexingType(_, _) => {
|
||||
// Raise error if there is no index getter nor setter
|
||||
Some(new_val.unwrap())
|
||||
}
|
||||
EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()),
|
||||
// Any other error - return
|
||||
err => return Err(Box::new(err)),
|
||||
},
|
||||
@ -1168,7 +1165,7 @@ impl Engine {
|
||||
.take_immutable_string()
|
||||
.map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
|
||||
|
||||
map.entry(index).or_insert(Default::default()).into()
|
||||
map.entry(index).or_insert_with(Default::default).into()
|
||||
} else {
|
||||
let index = idx
|
||||
.read_lock::<ImmutableString>()
|
||||
@ -1360,23 +1357,36 @@ impl Engine {
|
||||
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);
|
||||
|
||||
if let Some(CallableFunction::Method(func)) = self
|
||||
match self
|
||||
.global_module
|
||||
.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() {
|
||||
let mut lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
||||
let lhs_ptr_inner = lock_guard.deref_mut();
|
||||
lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
||||
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
|
||||
func(self, lib, &mut [lhs_ptr_inner, &mut rhs_val])?;
|
||||
if func.is_plugin_fn() {
|
||||
func.get_plugin_fn().call(args)?;
|
||||
} else {
|
||||
// Overriding exact implementation
|
||||
func(self, lib, &mut [lhs_ptr, &mut rhs_val])?;
|
||||
func.get_native_fn()(self, lib, args)?;
|
||||
}
|
||||
} 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 =
|
||||
|
||||
// Clone the LHS value
|
||||
@ -1385,17 +1395,20 @@ impl Engine {
|
||||
// Run function
|
||||
let (value, _) = self
|
||||
.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))?;
|
||||
|
||||
let value = value.flatten();
|
||||
|
||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
|
||||
} else {
|
||||
*lhs_ptr = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
@ -2014,6 +2027,6 @@ impl Engine {
|
||||
self.type_names
|
||||
.as_ref()
|
||||
.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_object"))]
|
||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::engine::{make_getter, make_setter};
|
||||
use crate::engine::{make_getter, make_setter, Map};
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
@ -675,6 +674,11 @@ impl Module {
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
@ -686,12 +690,25 @@ impl Module {
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn set_indexer_get_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
func: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
@ -773,6 +790,11 @@ impl Module {
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
@ -785,12 +807,25 @@ impl Module {
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash, true));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn set_indexer_set_fn<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone>(
|
||||
&mut self,
|
||||
func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
|
||||
) -> 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 b = cast_arg::<B>(&mut args[1]);
|
||||
let c = cast_arg::<C>(&mut args[2]);
|
||||
@ -812,6 +847,11 @@ impl Module {
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
@ -830,7 +870,6 @@ impl Module {
|
||||
/// assert!(module.contains_fn(hash_get, true));
|
||||
/// assert!(module.contains_fn(hash_set, true));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
|
||||
&mut self,
|
||||
|
@ -26,13 +26,14 @@ macro_rules! gen_array_functions {
|
||||
pub mod $root { $( pub mod $arg_type {
|
||||
use super::super::*;
|
||||
|
||||
#[export_fn]
|
||||
#[export_module]
|
||||
pub mod functions {
|
||||
#[rhai_fn(name = "push", name = "+=")]
|
||||
#[inline(always)]
|
||||
pub fn push(list: &mut Array, item: $arg_type) {
|
||||
list.push(Dynamic::from(item));
|
||||
}
|
||||
|
||||
#[export_fn]
|
||||
pub fn insert(list: &mut Array, position: INT, item: $arg_type) {
|
||||
if position <= 0 {
|
||||
list.insert(0, Dynamic::from(item));
|
||||
@ -42,14 +43,14 @@ macro_rules! gen_array_functions {
|
||||
list.insert(position as usize, Dynamic::from(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
})* }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_functions {
|
||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||
set_exported_fn!($mod_name, "push", $root::$arg_type::push);
|
||||
set_exported_fn!($mod_name, "insert", $root::$arg_type::insert);
|
||||
combine_with_exported_module!($mod_name, "array_functions", $root::$arg_type::functions);
|
||||
|
||||
$mod_name.set_raw_fn("pad",
|
||||
&[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, {
|
||||
combine_with_exported_module!(lib, "array", array_functions);
|
||||
|
||||
reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
@ -77,6 +76,9 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
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
|
||||
lib.set_iter(
|
||||
TypeId::of::<Array>(),
|
||||
|
@ -18,7 +18,6 @@ mod map_functions {
|
||||
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
||||
map.contains_key(&prop)
|
||||
}
|
||||
#[rhai_fn(name = "len", get = "len")]
|
||||
#[inline(always)]
|
||||
pub fn len(map: &mut Map) -> INT {
|
||||
map.len() as INT
|
||||
@ -46,9 +45,7 @@ mod map_functions {
|
||||
}
|
||||
pub fn fill_with(map1: &mut Map, map2: Map) {
|
||||
map2.into_iter().for_each(|(key, value)| {
|
||||
if !map1.contains_key(&key) {
|
||||
map1.insert(key, value);
|
||||
}
|
||||
map1.entry(key).or_insert(value);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,13 @@
|
||||
use crate::def_package;
|
||||
use crate::parser::INT;
|
||||
use crate::plugin::*;
|
||||
use crate::token::Position;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::parser::FLOAT;
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::{result::EvalAltResult, token::Position};
|
||||
use crate::result::EvalAltResult;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
#[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));
|
||||
|
||||
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_i64"))]
|
||||
{
|
||||
@ -223,6 +227,21 @@ mod float_functions {
|
||||
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"))]
|
||||
@ -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(target_arch = "wasm32"))]
|
||||
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 {
|
||||
format!("{:?}", x).into()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
mod format_map {
|
||||
use super::*;
|
||||
|
@ -20,25 +20,27 @@ macro_rules! gen_concat_functions {
|
||||
pub mod $root { $( pub mod $arg_type {
|
||||
use super::super::*;
|
||||
|
||||
#[export_fn]
|
||||
#[export_module]
|
||||
pub mod functions {
|
||||
#[rhai_fn(name = "+")]
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[export_fn]
|
||||
#[rhai_fn(name = "+")]
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
} )* }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_functions {
|
||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||
set_exported_fn!($mod_name, "+", $root::$arg_type::append_func);
|
||||
set_exported_fn!($mod_name, "+", $root::$arg_type::prepend_func);
|
||||
combine_with_exported_module!($mod_name, "strings_concat", $root::$arg_type::functions);
|
||||
)* }
|
||||
}
|
||||
|
||||
@ -154,7 +156,7 @@ mod string_functions {
|
||||
|
||||
#[rhai_fn(name = "len", get = "len")]
|
||||
#[inline(always)]
|
||||
pub fn len(s: &mut ImmutableString) -> INT {
|
||||
pub fn len(s: &str) -> INT {
|
||||
s.chars().count() as INT
|
||||
}
|
||||
|
||||
@ -182,17 +184,17 @@ mod string_functions {
|
||||
|
||||
#[rhai_fn(name = "contains")]
|
||||
#[inline(always)]
|
||||
pub fn contains_char(s: &mut ImmutableString, ch: char) -> bool {
|
||||
pub fn contains_char(s: &str, ch: char) -> bool {
|
||||
s.contains(ch)
|
||||
}
|
||||
#[rhai_fn(name = "contains")]
|
||||
#[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())
|
||||
}
|
||||
|
||||
#[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 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
@ -207,17 +209,13 @@ mod string_functions {
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
#[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)
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
#[rhai_fn(name = "index_of")]
|
||||
pub fn index_of_string_starting_from(
|
||||
s: &mut ImmutableString,
|
||||
find: ImmutableString,
|
||||
start: INT,
|
||||
) -> INT {
|
||||
pub fn index_of_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT {
|
||||
let start = if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
@ -232,14 +230,14 @@ mod string_functions {
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
#[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())
|
||||
.map(|index| s[0..index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
|
||||
#[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 {
|
||||
return "".to_string().into();
|
||||
} else if start < 0 {
|
||||
@ -268,7 +266,7 @@ mod string_functions {
|
||||
}
|
||||
#[rhai_fn(name = "sub_string")]
|
||||
#[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;
|
||||
sub_string(s, start, len)
|
||||
}
|
||||
@ -332,13 +330,29 @@ mod string_functions {
|
||||
|
||||
#[rhai_fn(name = "+")]
|
||||
#[inline]
|
||||
pub fn append(x: &mut ImmutableString, y: Array) -> String {
|
||||
pub fn append(x: &str, y: Array) -> String {
|
||||
format!("{}{:?}", x, y)
|
||||
}
|
||||
#[rhai_fn(name = "+")]
|
||||
#[inline]
|
||||
pub fn prepend(x: &mut Array, y: ImmutableString) -> String {
|
||||
pub fn prepend(x: &mut Array, y: &str) -> String {
|
||||
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::Int(value) => Some(Expr::IntegerConstant(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(false) => Some(Expr::False(pos)),
|
||||
#[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 escape: StaticVec<char> = Default::default();
|
||||
|
||||
let start = *pos;
|
||||
|
||||
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();
|
||||
|
||||
@ -838,17 +840,19 @@ pub fn parse_string_literal(
|
||||
ch if enclosing_char == ch && escape.is_empty() => break,
|
||||
|
||||
// Unknown escape sequence
|
||||
_ if !escape.is_empty() => {
|
||||
ch if !escape.is_empty() => {
|
||||
escape.push(ch);
|
||||
|
||||
return Err((
|
||||
LERR::MalformedEscapeSequence(escape.into_iter().collect()),
|
||||
*pos,
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
// Cannot have new-lines inside string literals
|
||||
'\n' => {
|
||||
pos.rewind();
|
||||
return Err((LERR::UnterminatedString, *pos));
|
||||
return Err((LERR::UnterminatedString, start));
|
||||
}
|
||||
|
||||
// All other characters
|
||||
|
@ -13,6 +13,10 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
||||
'3'
|
||||
);
|
||||
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"))]
|
||||
assert_eq!(
|
||||
|
@ -20,6 +20,15 @@ fn test_float() -> Result<(), Box<EvalAltResult>> {
|
||||
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]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn test_struct_with_float() -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -108,3 +108,14 @@ fn test_math() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
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