Merge branch 'master' into plugins

This commit is contained in:
Stephen Chung 2020-07-25 16:16:09 +08:00
commit d278f3725c
21 changed files with 404 additions and 97 deletions

View File

@ -5,9 +5,7 @@ set -ex
cargo build --verbose cargo build --verbose
cargo test --verbose cargo test --verbose
if [ "$TRAVIS_RUST_VERSION" = "nightly" ] if [[ $TRAVIS_RUST_VERSION == "nightly" ]]; then
then
cargo build --verbose --features no_std cargo build --verbose --features no_std
cargo test --verbose --features no_std cargo test --verbose --features no_std
fi fi

View File

@ -75,3 +75,6 @@ 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.4", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
[package.metadata.docs.rs]
features = [ "serde", "internals" ]

View File

@ -174,28 +174,3 @@
of your accepting any such warranty or additional liability. of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

23
LICENSE-MIT.txt Normal file
View File

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -64,3 +64,17 @@ Playground
An [Online Playground](https://alvinhochun.github.io/rhai-demo/) is available with syntax-highlighting editor. An [Online Playground](https://alvinhochun.github.io/rhai-demo/) is available with syntax-highlighting editor.
Scripts can be evaluated directly from the editor. Scripts can be evaluated directly from the editor.
License
-------
Licensed under either:
* [Apache License, Version 2.0](https://github.com/jonathandturner/rhai/blob/master/LICENSE-APACHE.txt), or
* [MIT license](https://github.com/jonathandturner/rhai/blob/master/LICENSE-MIT.txt)
at your option.
Unless explicitly stated otherwise, any contribution intentionally submitted
for inclusion in this crate, as defined in the Apache-2.0 license, shall
be dual-licensed as above, without any additional terms or conditions.

View File

@ -18,6 +18,7 @@ New features
* Anonymous functions in the syntax of a closure, e.g. `|x, y, z| x + y - z`. * Anonymous functions in the syntax of a closure, e.g. `|x, y, z| x + y - z`.
* Custom syntax now works even without the `internals` feature. * Custom syntax now works even without the `internals` feature.
* Currying of function pointers is supported via the `curry` keyword. * Currying of function pointers is supported via the `curry` keyword.
* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`.
Breaking changes Breaking changes
---------------- ----------------

View File

@ -30,12 +30,24 @@ use crate::stdlib::time::Instant;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use instant::Instant; use instant::Instant;
mod private {
use crate::fn_native::SendSync;
use crate::stdlib::any::Any;
/// A sealed trait that prevents other crates from implementing [`Variant`].
///
/// [`Variant`]: super::Variant
pub trait Sealed {}
impl<T: Any + Clone + SendSync> Sealed for T {}
}
/// Trait to represent any type. /// Trait to represent any type.
/// ///
/// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type. /// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type.
/// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`. /// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
pub trait Variant: Any { pub trait Variant: Any + private::Sealed {
/// Convert this `Variant` trait object to `&dyn Any`. /// Convert this `Variant` trait object to `&dyn Any`.
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
@ -53,10 +65,6 @@ pub trait Variant: Any {
/// Clone into `Dynamic`. /// Clone into `Dynamic`.
fn clone_into_dynamic(&self) -> Dynamic; fn clone_into_dynamic(&self) -> Dynamic;
/// This trait may only be implemented by `rhai`.
#[doc(hidden)]
fn _closed(&self) -> _Private;
} }
/// Trait to represent any type. /// Trait to represent any type.
@ -64,7 +72,7 @@ pub trait Variant: Any {
/// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`), /// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`),
/// `bool`, `String`, `char`, `Vec<T>` (into `Array`) and `HashMap<String, T>` (into `Map`). /// `bool`, `String`, `char`, `Vec<T>` (into `Array`) and `HashMap<String, T>` (into `Map`).
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
pub trait Variant: Any + Send + Sync { pub trait Variant: Any + Send + Sync + private::Sealed {
/// Convert this `Variant` trait object to `&dyn Any`. /// Convert this `Variant` trait object to `&dyn Any`.
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
@ -82,10 +90,6 @@ pub trait Variant: Any + Send + Sync {
/// Clone into `Dynamic`. /// Clone into `Dynamic`.
fn clone_into_dynamic(&self) -> Dynamic; fn clone_into_dynamic(&self) -> Dynamic;
/// This trait may only be implemented by `rhai`.
#[doc(hidden)]
fn _closed(&self) -> _Private;
} }
impl<T: Any + Clone + SendSync> Variant for T { impl<T: Any + Clone + SendSync> Variant for T {
@ -107,9 +111,6 @@ impl<T: Any + Clone + SendSync> Variant for T {
fn clone_into_dynamic(&self) -> Dynamic { fn clone_into_dynamic(&self) -> Dynamic {
Dynamic::from(self.clone()) Dynamic::from(self.clone())
} }
fn _closed(&self) -> _Private {
_Private
}
} }
impl dyn Variant { impl dyn Variant {
@ -809,8 +810,3 @@ impl From<Box<FnPtr>> for Dynamic {
Self(Union::FnPtr(value)) Self(Union::FnPtr(value))
} }
} }
/// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only
/// be implemented by this crate.
#[doc(hidden)]
pub struct _Private;

View File

@ -11,7 +11,7 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult; use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope}; use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::{CustomSyntax, EvalContext, Expression}; use crate::syntax::{CustomSyntax, EvalContext};
use crate::token::Position; use crate::token::Position;
use crate::utils::StaticVec; use crate::utils::StaticVec;
@ -38,7 +38,12 @@ pub type Array = Vec<Dynamic>;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub type Map = HashMap<ImmutableString, Dynamic>; pub type Map = HashMap<ImmutableString, Dynamic>;
/// A stack of imported modules. /// [INTERNALS] A stack of imported modules.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
pub type Imports<'a> = Vec<(Cow<'a, str>, Module)>; pub type Imports<'a> = Vec<(Cow<'a, str>, Module)>;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -189,12 +194,17 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
} }
} }
/// A type that holds all the current states of the Engine. /// [INTERNALS] A type that holds all the current states of the Engine.
/// Exported under the `internals` feature only.
/// ///
/// # Safety /// # Safety
/// ///
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via /// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting. /// direct lifetime casting.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] #[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct State { pub struct State {
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. /// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
@ -1020,7 +1030,7 @@ impl Engine {
map.entry(index).or_insert(Default::default()).into() map.entry(index).or_insert(Default::default()).into()
} else { } else {
let index = idx let index = idx
.downcast_ref::<String>() .downcast_ref::<ImmutableString>()
.ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?; .ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
map.get_mut(index.as_str()) map.get_mut(index.as_str())
@ -1050,19 +1060,20 @@ impl Engine {
} }
} }
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
_ => { _ => {
let type_name = self.map_type_name(val.type_name()); let type_name = val.type_name();
let args = &mut [val, &mut idx]; let args = &mut [val, &mut idx];
self.exec_fn_call( self.exec_fn_call(
state, lib, FN_IDX_GET, true, 0, args, is_ref, true, None, level, state, lib, FN_IDX_GET, true, 0, args, is_ref, true, None, level,
) )
.map(|(v, _)| v.into()) .map(|(v, _)| v.into())
.map_err(|_| { .map_err(|err| match *err {
Box::new(EvalAltResult::ErrorIndexingType( EvalAltResult::ErrorFunctionNotFound(_, _) => Box::new(
type_name.into(), EvalAltResult::ErrorIndexingType(type_name.into(), Position::none()),
Position::none(), ),
)) _ => err,
}) })
} }

View File

@ -10,7 +10,12 @@ use crate::stdlib::{
string::{String, ToString}, string::{String, ToString},
}; };
/// Error when tokenizing the script text. /// [INTERNALS] Error encountered when tokenizing the script text.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Eq, PartialEq, Clone, Hash)] #[derive(Debug, Eq, PartialEq, Clone, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum LexError { pub enum LexError {

View File

@ -10,7 +10,9 @@ use crate::token::{is_valid_identifier, Position};
use crate::utils::{ImmutableString, StaticVec}; use crate::utils::{ImmutableString, StaticVec};
use crate::Scope; use crate::Scope;
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc}; use crate::stdlib::{
boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc, vec::Vec,
};
/// Trait that maps to `Send + Sync` only under the `sync` feature. /// Trait that maps to `Send + Sync` only under the `sync` feature.
#[cfg(feature = "sync")] #[cfg(feature = "sync")]

View File

@ -170,7 +170,7 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[deprecated(note = "this type is volatile and may change")]
pub use parser::{CustomExpr, Expr, ReturnType, ScriptFnDef, Stmt}; pub use parser::{CustomExpr, Expr, FloatWrapper, ReturnType, ScriptFnDef, Stmt};
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")] #[deprecated(note = "this type is volatile and may change")]

View File

@ -738,18 +738,18 @@ impl Module {
/// }); /// });
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
pub fn set_indexer_set_fn<A: Variant + Clone, B: 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, A) -> FuncReturn<()> + SendSync + 'static, func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static,
) -> u64 { ) -> u64 {
let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| {
let b = mem::take(args[1]).cast::<B>(); let b = mem::take(args[1]).cast::<B>();
let c = mem::take(args[2]).cast::<A>(); let c = mem::take(args[2]).cast::<C>();
let a = args[0].downcast_mut::<A>().unwrap(); let a = args[0].downcast_mut::<A>().unwrap();
func(a, b, c).map(Dynamic::from) func(a, b, c).map(Dynamic::from)
}; };
let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<A>()]; let arg_types = [TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn( self.set_fn(
FN_IDX_SET, FN_IDX_SET,
Public, Public,
@ -758,6 +758,40 @@ impl Module {
) )
} }
/// Set a pair of Rust index getter and setter functions, returning both hash keys.
/// This is a shorthand for `set_indexer_get_fn` and `set_indexer_set_fn`.
///
/// If there are similar existing Rust functions, they are replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let (hash_get, hash_set) = module.set_indexer_get_set_fn(
/// |x: &mut i64, y: ImmutableString| {
/// Ok(*x + y.len() as i64)
/// },
/// |x: &mut i64, y: ImmutableString, value: i64| {
/// *x = y.len() as i64 + value;
/// Ok(())
/// }
/// );
/// assert!(module.contains_fn(hash_get));
/// assert!(module.contains_fn(hash_set));
/// ```
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
setter: impl Fn(&mut A, B, T) -> FuncReturn<()> + SendSync + 'static,
) -> (u64, u64) {
(
self.set_indexer_get_fn(getter),
self.set_indexer_set_fn(setter),
)
}
/// Set a Rust function taking four parameters into the module, returning a hash key. /// Set a Rust function taking four parameters into the module, returning a hash key.
/// ///
/// If there is a similar existing Rust function, it is replaced. /// If there is a similar existing Rust function, it is replaced.
@ -1094,11 +1128,17 @@ impl Module {
} }
} }
/// A chain of module names to qualify a variable or function call. /// [INTERNALS] A chain of module names to qualify a variable or function call.
/// A `u64` hash key is kept for quick search purposes. /// Exported under the `internals` feature only.
///
/// A `u64` hash key is cached for quick search purposes.
/// ///
/// A `StaticVec` is used because most module-level access contains only one level, /// A `StaticVec` is used because most module-level access contains only one level,
/// and it is wasteful to always allocate a `Vec` with one element. /// and it is wasteful to always allocate a `Vec` with one element.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Clone, Eq, PartialEq, Default, Hash)] #[derive(Clone, Eq, PartialEq, Default, Hash)]
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>); pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);

View File

@ -456,7 +456,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// "xxx" in "xxxxx" // "xxx" in "xxxxx"
(Expr::StringConstant(a), Expr::StringConstant(b)) => { (Expr::StringConstant(a), Expr::StringConstant(b)) => {
state.set_dirty(); state.set_dirty();
if b.0.contains(a.0.as_ref()) { Expr::True(a.1) } else { Expr::False(a.1) } if b.0.contains(a.0.as_str()) { Expr::True(a.1) } else { Expr::False(a.1) }
} }
// 'x' in "xxxxx" // 'x' in "xxxxx"
(Expr::CharConstant(a), Expr::StringConstant(b)) => { (Expr::CharConstant(a), Expr::StringConstant(b)) => {
@ -560,7 +560,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
let has_script_fn = state.lib.iter_fn().find(|(_, _, _, f)| { let has_script_fn = state.lib.iter_fn().find(|(_, _, _, f)| {
if !f.is_script() { return false; } if !f.is_script() { return false; }
let fn_def = f.get_fn_def(); let fn_def = f.get_fn_def();
&fn_def.name == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len()) fn_def.name.as_str() == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
}).is_some(); }).is_some();
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]

View File

@ -342,11 +342,16 @@ impl fmt::Display for FnAccess {
} }
} }
/// A scripted function definition. /// [INTERNALS] A type containing information on a scripted function.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct ScriptFnDef { pub struct ScriptFnDef {
/// Function name. /// Function name.
pub name: String, pub name: ImmutableString,
/// Function access mode. /// Function access mode.
pub access: FnAccess, pub access: FnAccess,
/// Names of function parameters. /// Names of function parameters.
@ -376,7 +381,12 @@ impl fmt::Display for ScriptFnDef {
} }
} }
/// `return`/`throw` statement. /// [INTERNALS] A type encapsulating the mode of a `return`/`throw` statement.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)] #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
pub enum ReturnType { pub enum ReturnType {
/// `return` statement. /// `return` statement.
@ -477,7 +487,8 @@ impl ParseSettings {
} }
} }
/// A statement. /// [INTERNALS] A Rhai statement.
/// Exported under the `internals` feature only.
/// ///
/// Each variant is at most one pointer in size (for speed), /// Each variant is at most one pointer in size (for speed),
/// with everything being allocated together in one single tuple. /// with everything being allocated together in one single tuple.
@ -582,6 +593,12 @@ impl Stmt {
} }
} }
/// [INTERNALS] A type wrapping a custom syntax definition.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Clone)] #[derive(Clone)]
pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>); pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>);
@ -592,11 +609,20 @@ impl fmt::Debug for CustomExpr {
} }
impl Hash for CustomExpr { impl Hash for CustomExpr {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state); self.0.hash(state);
} }
} }
/// [INTERNALS] A type wrapping a floating-point number.
/// Exported under the `internals` feature only.
///
/// This type is mainly used to provide a standard `Hash` implementation
/// to floating-point numbers, allowing `Expr` to derive `Hash` automatically.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[derive(Debug, PartialEq, PartialOrd, Clone)] #[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct FloatWrapper(pub FLOAT, pub Position); pub struct FloatWrapper(pub FLOAT, pub Position);
@ -609,10 +635,15 @@ impl Hash for FloatWrapper {
} }
} }
/// An expression. /// [INTERNALS] An expression sub-tree.
/// Exported under the `internals` feature only.
/// ///
/// Each variant is at most one pointer in size (for speed), /// Each variant is at most one pointer in size (for speed),
/// with everything being allocated together in one single tuple. /// with everything being allocated together in one single tuple.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub enum Expr { pub enum Expr {
/// Integer constant. /// Integer constant.
@ -2852,7 +2883,7 @@ fn parse_fn(
let params = params.into_iter().map(|(p, _)| p).collect(); let params = params.into_iter().map(|(p, _)| p).collect();
Ok(ScriptFnDef { Ok(ScriptFnDef {
name, name: name.into(),
access, access,
params, params,
body, body,
@ -2940,7 +2971,7 @@ fn parse_anon_fn(
let hash = s.finish(); let hash = s.finish();
// Create unique function name // Create unique function name
let fn_name = format!("{}{}", FN_ANONYMOUS, hash); let fn_name: ImmutableString = format!("{}{:16x}", FN_ANONYMOUS, hash).into();
let script = ScriptFnDef { let script = ScriptFnDef {
name: fn_name.clone(), name: fn_name.clone(),
@ -2950,7 +2981,7 @@ fn parse_anon_fn(
pos: settings.pos, pos: settings.pos,
}; };
let expr = Expr::FnPointer(Box::new((fn_name.into(), settings.pos))); let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
Ok((expr, script)) Ok((expr, script))
} }

View File

@ -162,10 +162,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
Union::Variant(value) if value.is::<i16>() => self.deserialize_i16(visitor), Union::Variant(value) if value.is::<i16>() => self.deserialize_i16(visitor),
Union::Variant(value) if value.is::<i32>() => self.deserialize_i32(visitor), Union::Variant(value) if value.is::<i32>() => self.deserialize_i32(visitor),
Union::Variant(value) if value.is::<i64>() => self.deserialize_i64(visitor), Union::Variant(value) if value.is::<i64>() => self.deserialize_i64(visitor),
Union::Variant(value) if value.is::<i128>() => self.deserialize_i128(visitor),
Union::Variant(value) if value.is::<u8>() => self.deserialize_u8(visitor), Union::Variant(value) if value.is::<u8>() => self.deserialize_u8(visitor),
Union::Variant(value) if value.is::<u16>() => self.deserialize_u16(visitor), Union::Variant(value) if value.is::<u16>() => self.deserialize_u16(visitor),
Union::Variant(value) if value.is::<u32>() => self.deserialize_u32(visitor), Union::Variant(value) if value.is::<u32>() => self.deserialize_u32(visitor),
Union::Variant(value) if value.is::<u64>() => self.deserialize_u64(visitor), Union::Variant(value) if value.is::<u64>() => self.deserialize_u64(visitor),
Union::Variant(value) if value.is::<u128>() => self.deserialize_u128(visitor),
Union::Variant(_) => self.type_error(), Union::Variant(_) => self.type_error(),
} }
@ -219,6 +221,18 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) {
self.type_error()
} else {
self.value
.downcast_ref::<i128>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x))
}
}
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
@ -259,6 +273,16 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
if let Ok(v) = self.value.as_int() {
self.deserialize_int(v, visitor)
} else {
self.value
.downcast_ref::<u128>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x))
}
}
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self

View File

@ -150,6 +150,21 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_i128(self, v: i128) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "only_i32"))]
if v > i64::MAX as i128 {
return Ok(Dynamic::from(v));
} else {
return self.serialize_i64(v as i64);
}
#[cfg(feature = "only_i32")]
if v > i32::MAX as i128 {
return Ok(Dynamic::from(v));
} else {
return self.serialize_i32(v as i32);
}
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_u8(self, v: u8) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
@ -190,6 +205,21 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
fn serialize_u128(self, v: u128) -> Result<Self::Ok, Box<EvalAltResult>> {
#[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u128 {
return Ok(Dynamic::from(v));
} else {
return self.serialize_i64(v as i64);
}
#[cfg(feature = "only_i32")]
if v > i32::MAX as u128 {
return Ok(Dynamic::from(v));
} else {
return self.serialize_i32(v as i32);
}
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Box<EvalAltResult>> { fn serialize_f32(self, v: f32) -> Result<Self::Ok, Box<EvalAltResult>> {
Ok(Dynamic::from(v)) Ok(Dynamic::from(v))
} }

View File

@ -12,7 +12,8 @@ use crate::token::{is_valid_identifier, Position, Token};
use crate::utils::StaticVec; use crate::utils::StaticVec;
use crate::stdlib::{ use crate::stdlib::{
fmt, boxed::Box,
fmt, format,
rc::Rc, rc::Rc,
string::{String, ToString}, string::{String, ToString},
sync::Arc, sync::Arc,

View File

@ -136,89 +136,181 @@ impl fmt::Debug for Position {
} }
} }
/// Tokens. /// [INTERNALS] A Rhai language token.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Token { pub enum Token {
/// An `INT` constant.
IntegerConstant(INT), IntegerConstant(INT),
/// A `FLOAT` constaint.
///
/// Never appears under the `no_float` feature.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT), FloatConstant(FLOAT),
/// An identifier.
Identifier(String), Identifier(String),
/// A character constant.
CharConstant(char), CharConstant(char),
/// A string constant.
StringConstant(String), StringConstant(String),
/// `{`
LeftBrace, LeftBrace,
/// `}`
RightBrace, RightBrace,
/// `(`
LeftParen, LeftParen,
/// `)`
RightParen, RightParen,
/// `[`
LeftBracket, LeftBracket,
/// `]`
RightBracket, RightBracket,
/// `+`
Plus, Plus,
/// `+` (unary)
UnaryPlus, UnaryPlus,
/// `-`
Minus, Minus,
/// `-` (unary)
UnaryMinus, UnaryMinus,
/// `*`
Multiply, Multiply,
/// `/`
Divide, Divide,
/// `%`
Modulo, Modulo,
/// `~`
PowerOf, PowerOf,
/// `<<`
LeftShift, LeftShift,
/// `>>`
RightShift, RightShift,
/// `;`
SemiColon, SemiColon,
/// `:`
Colon, Colon,
/// `::`
DoubleColon, DoubleColon,
/// `,`
Comma, Comma,
/// `.`
Period, Period,
/// `#{`
MapStart, MapStart,
/// `=`
Equals, Equals,
/// `true`
True, True,
/// `false`
False, False,
/// `let`
Let, Let,
/// `const`
Const, Const,
/// `if`
If, If,
/// `else`
Else, Else,
/// `while`
While, While,
/// `loop`
Loop, Loop,
/// `for`
For, For,
/// `in`
In, In,
/// `<`
LessThan, LessThan,
/// `>`
GreaterThan, GreaterThan,
/// `<=`
LessThanEqualsTo, LessThanEqualsTo,
/// `>=`
GreaterThanEqualsTo, GreaterThanEqualsTo,
/// `==`
EqualsTo, EqualsTo,
/// `!=`
NotEqualsTo, NotEqualsTo,
/// `!`
Bang, Bang,
/// `|`
Pipe, Pipe,
/// `||`
Or, Or,
/// `^`
XOr, XOr,
/// `&`
Ampersand, Ampersand,
/// `&&`
And, And,
/// `fn`
///
/// Never appears under the `no_function` feature.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Fn, Fn,
/// `continue`
Continue, Continue,
/// `break`
Break, Break,
/// `return`
Return, Return,
/// `throw`
Throw, Throw,
/// `+=`
PlusAssign, PlusAssign,
/// `-=`
MinusAssign, MinusAssign,
/// `*=`
MultiplyAssign, MultiplyAssign,
/// `/=`
DivideAssign, DivideAssign,
/// `<<=`
LeftShiftAssign, LeftShiftAssign,
/// `>>=`
RightShiftAssign, RightShiftAssign,
/// `&=`
AndAssign, AndAssign,
/// `|=`
OrAssign, OrAssign,
/// `^=`
XOrAssign, XOrAssign,
/// `%=`
ModuloAssign, ModuloAssign,
/// `~=`
PowerOfAssign, PowerOfAssign,
/// `private`
///
/// Never appears under the `no_function` feature.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Private, Private,
/// `import`
///
/// Never appears under the `no_module` feature.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Import, Import,
/// `export`
///
/// Never appears under the `no_module` feature.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export, Export,
/// `as`
///
/// Never appears under the `no_module` feature.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
As, As,
/// A lexer error.
LexError(Box<LexError>), LexError(Box<LexError>),
/// A comment block.
Comment(String), Comment(String),
/// A reserved symbol.
Reserved(String), Reserved(String),
/// A custom keyword.
Custom(String), Custom(String),
/// End of the input stream.
EOF, EOF,
} }
@ -566,7 +658,7 @@ impl Token {
} }
} }
/// Is this token a reserved keyword? /// Is this token a reserved symbol?
pub fn is_reserved(&self) -> bool { pub fn is_reserved(&self) -> bool {
match self { match self {
Self::Reserved(_) => true, Self::Reserved(_) => true,
@ -589,7 +681,12 @@ impl From<Token> for String {
} }
} }
/// State of the tokenizer. /// [INTERNALS] State of the tokenizer.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Default)] #[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct TokenizeState { pub struct TokenizeState {
/// Maximum length of a string (0 = unlimited). /// Maximum length of a string (0 = unlimited).
@ -604,7 +701,12 @@ pub struct TokenizeState {
pub include_comments: bool, pub include_comments: bool,
} }
/// Trait that encapsulates a peekable character input stream. /// [INTERNALS] Trait that encapsulates a peekable character input stream.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This trait is volatile and may change.
pub trait InputStream { pub trait InputStream {
/// Get the next character /// Get the next character
fn get_next(&mut self) -> Option<char>; fn get_next(&mut self) -> Option<char>;
@ -628,7 +730,12 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
first_alphabetic first_alphabetic
} }
/// Parse a string literal wrapped by `enclosing_char`. /// [INTERNALS] Parse a string literal wrapped by `enclosing_char`.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
pub fn parse_string_literal( pub fn parse_string_literal(
stream: &mut impl InputStream, stream: &mut impl InputStream,
state: &mut TokenizeState, state: &mut TokenizeState,
@ -794,7 +901,12 @@ fn scan_comment(
} }
} }
/// Get the next token. /// [INTERNALS] Get the next token from the `InputStream`.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
pub fn get_next_token( pub fn get_next_token(
stream: &mut impl InputStream, stream: &mut impl InputStream,
state: &mut TokenizeState, state: &mut TokenizeState,
@ -810,6 +922,32 @@ pub fn get_next_token(
result result
} }
/// Test if the given character is a hex character.
fn is_hex_char(c: char) -> bool {
match c {
'a'..='f' => true,
'A'..='F' => true,
'0'..='9' => true,
_ => false,
}
}
/// Test if the given character is an octal character.
fn is_octal_char(c: char) -> bool {
match c {
'0'..='7' => true,
_ => false,
}
}
/// Test if the given character is a binary character.
fn is_binary_char(c: char) -> bool {
match c {
'0' | '1' => true,
_ => false,
}
}
/// Get the next token. /// Get the next token.
fn get_next_token_inner( fn get_next_token_inner(
stream: &mut impl InputStream, stream: &mut impl InputStream,
@ -872,18 +1010,9 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
let valid = match ch { let valid = match ch {
'x' | 'X' => [ 'x' | 'X' => is_hex_char,
'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F', 'o' | 'O' => is_octal_char,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', 'b' | 'B' => is_binary_char,
],
'o' | 'O' => [
'0', '1', '2', '3', '4', '5', '6', '7', '_', '_', '_', '_',
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
],
'b' | 'B' => [
'0', '1', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
],
_ => unreachable!(), _ => unreachable!(),
}; };
@ -895,7 +1024,7 @@ fn get_next_token_inner(
}); });
while let Some(next_char_in_escape_seq) = stream.peek_next() { while let Some(next_char_in_escape_seq) = stream.peek_next() {
if !valid.contains(&next_char_in_escape_seq) { if !valid(next_char_in_escape_seq) {
break; break;
} }

View File

@ -92,9 +92,12 @@ pub fn calc_fn_spec<'a>(
s.finish() s.finish()
} }
/// A type to hold a number of values in static storage for no-allocation, quick access. /// [INTERNALS] An array-like type that holds a number of values in static storage for no-allocation, quick access.
/// Exported under the `internals` feature only.
///
/// If too many items are stored, it converts into using a `Vec`. /// If too many items are stored, it converts into using a `Vec`.
/// ///
///
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate. /// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
/// This simplified implementation here is to avoid pulling in another crate. /// This simplified implementation here is to avoid pulling in another crate.
/// ///
@ -130,6 +133,10 @@ pub fn calc_fn_spec<'a>(
/// # Safety /// # Safety
/// ///
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency. /// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
///
/// ## WARNING
///
/// This type is volatile and may change.
// //
// TODO - remove unsafe code // TODO - remove unsafe code
pub struct StaticVec<T> { pub struct StaticVec<T> {

View File

@ -104,5 +104,19 @@ fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
42 42
); );
#[cfg(not(feature = "no_object"))]
assert_eq!(
engine.eval::<INT>(
r#"
fn foo(x) { this.data += x; }
let x = #{ data: 40, action: Fn("foo") };
x.action(2);
x.data
"#
)?,
42
);
Ok(()) Ok(())
} }

View File

@ -14,6 +14,7 @@ fn test_hex_literal() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 0xf; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0xf; x")?, 15);
assert_eq!(engine.eval::<INT>("let x = 0Xf; x")?, 15);
assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255); assert_eq!(engine.eval::<INT>("let x = 0xff; x")?, 255);
Ok(()) Ok(())
@ -24,6 +25,7 @@ fn test_octal_literal() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 0o77; x")?, 63); assert_eq!(engine.eval::<INT>("let x = 0o77; x")?, 63);
assert_eq!(engine.eval::<INT>("let x = 0O77; x")?, 63);
assert_eq!(engine.eval::<INT>("let x = 0o1234; x")?, 668); assert_eq!(engine.eval::<INT>("let x = 0o1234; x")?, 668);
Ok(()) Ok(())
@ -34,6 +36,7 @@ fn test_binary_literal() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert_eq!(engine.eval::<INT>("let x = 0b1111; x")?, 15); assert_eq!(engine.eval::<INT>("let x = 0b1111; x")?, 15);
assert_eq!(engine.eval::<INT>("let x = 0B1111; x")?, 15);
assert_eq!( assert_eq!(
engine.eval::<INT>("let x = 0b0011_1100_1010_0101; x")?, engine.eval::<INT>("let x = 0b0011_1100_1010_0101; x")?,
15525 15525