Enable function pointers even under no_function.

This commit is contained in:
Stephen Chung
2020-06-30 18:34:58 +08:00
parent 52ef223f83
commit e614790897
10 changed files with 162 additions and 24 deletions

View File

@@ -137,7 +137,6 @@ pub enum Union {
Array(Box<Array>),
#[cfg(not(feature = "no_object"))]
Map(Box<Map>),
#[cfg(not(feature = "no_function"))]
FnPtr(FnPtr),
Variant(Box<Box<dyn Variant>>),
}
@@ -175,7 +174,6 @@ impl Dynamic {
Union::Array(_) => TypeId::of::<Array>(),
#[cfg(not(feature = "no_object"))]
Union::Map(_) => TypeId::of::<Map>(),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
Union::Variant(value) => (***value).type_id(),
}
@@ -195,7 +193,6 @@ impl Dynamic {
Union::Array(_) => "array",
#[cfg(not(feature = "no_object"))]
Union::Map(_) => "map",
#[cfg(not(feature = "no_function"))]
Union::FnPtr(_) => "Fn",
#[cfg(not(feature = "no_std"))]
@@ -220,7 +217,6 @@ impl fmt::Display for Dynamic {
Union::Array(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => fmt::Display::fmt(value, f),
#[cfg(not(feature = "no_std"))]
@@ -245,7 +241,6 @@ impl fmt::Debug for Dynamic {
Union::Array(value) => fmt::Debug::fmt(value, f),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => write!(f, "#{:?}", value),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => fmt::Display::fmt(value, f),
#[cfg(not(feature = "no_std"))]
@@ -270,7 +265,6 @@ impl Clone for Dynamic {
Union::Array(ref value) => Self(Union::Array(value.clone())),
#[cfg(not(feature = "no_object"))]
Union::Map(ref value) => Self(Union::Map(value.clone())),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
Union::Variant(ref value) => (***value).clone_into_dynamic(),
}
@@ -400,7 +394,6 @@ impl Dynamic {
Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => unsafe_try_cast(value),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
}
@@ -444,7 +437,6 @@ impl Dynamic {
Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
}
@@ -471,7 +463,6 @@ impl Dynamic {
Union::Array(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
}
@@ -497,7 +488,6 @@ impl Dynamic {
Union::Array(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
#[cfg(not(feature = "no_object"))]
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
#[cfg(not(feature = "no_function"))]
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
}
@@ -626,7 +616,6 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<HashMap<K, T>> for Dynam
)))
}
}
#[cfg(not(feature = "no_function"))]
impl From<FnPtr> for Dynamic {
fn from(value: FnPtr) -> Self {
Self(Union::FnPtr(value))

View File

@@ -103,6 +103,9 @@ pub use scope::Scope;
pub use token::Position;
pub use utils::calc_fn_spec as calc_fn_hash;
#[cfg(not(feature = "no_function"))]
pub use parser::FnAccess;
#[cfg(not(feature = "no_function"))]
pub use fn_func::Func;
@@ -135,7 +138,7 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]
pub use parser::{Expr, FnAccess, ReturnType, ScriptFnDef, Stmt};
pub use parser::{Expr, ReturnType, ScriptFnDef, Stmt};
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]

View File

@@ -859,12 +859,53 @@ impl Module {
/// Merge another module into this module.
pub fn merge(&mut self, other: &Self) {
self.merge_filtered(other, |_, _, _| true)
}
/// Merge another module into this module, with only selected functions based on a filter predicate.
pub(crate) fn merge_filtered(
&mut self,
other: &Self,
filter: impl Fn(FnAccess, &str, usize) -> bool,
) {
self.variables
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
self.functions
.extend(other.functions.iter().map(|(&k, v)| (k, v.clone())));
self.functions.extend(
other
.functions
.iter()
.filter(|(_, (_, _, _, v))| match v {
CallableFunction::Pure(_)
| CallableFunction::Method(_)
| CallableFunction::Iterator(_) => true,
CallableFunction::Script(ref f) => {
filter(f.access, f.name.as_str(), f.params.len())
}
})
.map(|(&k, v)| (k, v.clone())),
);
self.type_iterators
.extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone())));
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
}
/// Filter out the functions, retaining only some based on a filter predicate.
pub(crate) fn retain_functions(&mut self, filter: impl Fn(FnAccess, &str, usize) -> bool) {
self.functions.retain(|_, (_, _, _, v)| match v {
CallableFunction::Pure(_)
| CallableFunction::Method(_)
| CallableFunction::Iterator(_) => true,
CallableFunction::Script(ref f) => filter(f.access, f.name.as_str(), f.params.len()),
});
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
}
/// Get the number of variables in the module.

View File

@@ -116,8 +116,15 @@ impl AST {
///
/// let engine = Engine::new();
///
/// let ast1 = engine.compile(r#"fn foo(x) { 42 + x } foo(1)"#)?;
/// let ast2 = engine.compile(r#"fn foo(n) { "hello" + n } foo("!")"#)?;
/// let ast1 = engine.compile(r#"
/// fn foo(x) { 42 + x }
/// foo(1)
/// "#)?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { "hello" + n }
/// foo("!")
/// "#)?;
///
/// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1'
///
@@ -138,6 +145,65 @@ impl AST {
/// # }
/// ```
pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered(other, |_, _, _| true)
}
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
/// is returned.
///
/// The second [`AST`] is simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then
/// the second [`AST`] will essentially be dead code.
///
/// All script-defined functions in the second [`AST`] are first selected based on a filter
/// predicate, then overwrite similarly-named functions in the first [`AST`] with the
/// same number of parameters.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let ast1 = engine.compile(r#"
/// fn foo(x) { 42 + x }
/// foo(1)
/// "#)?;
///
/// let ast2 = engine.compile(r#"
/// fn foo(n) { "hello" + n }
/// fn error() { 0 }
/// foo("!")
/// "#)?;
///
/// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1'
/// let ast = ast1.merge_filtered(&ast2, |_, name, params| name == "error" && params == 0);
///
/// // 'ast' is essentially:
/// //
/// // fn foo(n) { 42 + n } // <- definition of 'ast1::foo' is not overwritten
/// // // because 'ast2::foo' is filtered away
/// // foo(1) // <- notice this will be 43 instead of "hello1",
/// // // but it is no longer the return value
/// // fn error() { 0 } // <- this function passes the filter and is merged
/// // foo("!") // <- returns "42!"
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!");
/// # }
/// # Ok(())
/// # }
/// ```
pub fn merge_filtered(
&self,
other: &Self,
filter: impl Fn(FnAccess, &str, usize) -> bool,
) -> Self {
let Self(statements, functions) = self;
let ast = match (statements.is_empty(), other.0.is_empty()) {
@@ -152,11 +218,39 @@ impl AST {
};
let mut functions = functions.clone();
functions.merge(&other.1);
functions.merge_filtered(&other.1, filter);
Self::new(ast, functions)
}
/// Filter out the functions, retaining only some based on a filter predicate.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine;
///
/// let engine = Engine::new();
///
/// let mut ast = engine.compile(r#"
/// fn foo(n) { n + 1 }
/// fn bar() { print("hello"); }
/// "#)?;
///
/// // Remove all functions except 'foo(_)'
/// ast.retain_functions(|_, name, params| name == "foo" && params == 1);
/// # }
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_function"))]
pub fn retain_functions(&mut self, filter: impl Fn(FnAccess, &str, usize) -> bool) {
self.1.retain_functions(filter);
}
/// Clear all function definitions in the [`AST`].
#[cfg(not(feature = "no_function"))]
pub fn clear_functions(&mut self) {
@@ -164,8 +258,7 @@ impl AST {
}
/// Clear all statements in the [`AST`], leaving only function definitions.
#[cfg(not(feature = "no_function"))]
pub fn retain_functions(&mut self) {
pub fn clear_statements(&mut self) {
self.0 = vec![];
}
}
@@ -187,6 +280,15 @@ pub enum FnAccess {
Public,
}
impl fmt::Display for FnAccess {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Private => write!(f, "private"),
Self::Public => write!(f, "public"),
}
}
}
/// A scripted function definition.
#[derive(Debug, Clone)]
pub struct ScriptFnDef {