Enable function pointers even under no_function.
This commit is contained in:
parent
52ef223f83
commit
e614790897
@ -14,6 +14,7 @@ Breaking changes
|
|||||||
The method-call style will bind the object to the `this` parameter instead of consuming the first parameter.
|
The method-call style will bind the object to the `this` parameter instead of consuming the first parameter.
|
||||||
* Imported modules are no longer stored in the `Scope`. `Scope::push_module` is removed.
|
* Imported modules are no longer stored in the `Scope`. `Scope::push_module` is removed.
|
||||||
Therefore, cannot rely on module imports to persist across invocations using a `Scope`.
|
Therefore, cannot rely on module imports to persist across invocations using a `Scope`.
|
||||||
|
* `AST::retain_functions` is used for another purpose. The old `AST::retain_functions` is renamed to `AST::clear_statements`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -21,6 +22,7 @@ New features
|
|||||||
* Support for _function pointers_ via `Fn(name)` and `Fn.call(...)` syntax - a poor man's first-class function.
|
* Support for _function pointers_ via `Fn(name)` and `Fn.call(...)` syntax - a poor man's first-class function.
|
||||||
* Support for calling script-defined functions in method-call style with `this` binding to the object.
|
* Support for calling script-defined functions in method-call style with `this` binding to the object.
|
||||||
* Special support in object maps for OOP.
|
* Special support in object maps for OOP.
|
||||||
|
* Expanded the `AST` API for fine-tuned manipulation of functions.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -24,8 +24,8 @@ Keywords List
|
|||||||
| `as` | Alias for variable export | [`no_module`] |
|
| `as` | Alias for variable export | [`no_module`] |
|
||||||
| `private` | Mark function private | [`no_function`] |
|
| `private` | Mark function private | [`no_function`] |
|
||||||
| `fn` (lower-case `f`) | Function definition | [`no_function`] |
|
| `fn` (lower-case `f`) | Function definition | [`no_function`] |
|
||||||
| `Fn` (capital `F`) | Function to create a [function pointer] | [`no_function`] |
|
| `Fn` (capital `F`) | Function to create a [function pointer] | |
|
||||||
| `call` | Call a [function pointer] | [`no_function`] |
|
| `call` | Call a [function pointer] | |
|
||||||
| `this` | Reference to base object for method call | [`no_function`] |
|
| `this` | Reference to base object for method call | [`no_function`] |
|
||||||
| `type_of` | Get type name of value | |
|
| `type_of` | Get type name of value | |
|
||||||
| `print` | Print value | |
|
| `print` | Print value | |
|
||||||
|
@ -15,6 +15,7 @@ The following are reserved keywords in Rhai:
|
|||||||
| `return` | Return values | |
|
| `return` | Return values | |
|
||||||
| `throw` | throw exceptions | |
|
| `throw` | throw exceptions | |
|
||||||
| `import`, `export`, `as` | Modules | [`no_module`] |
|
| `import`, `export`, `as` | Modules | [`no_module`] |
|
||||||
|
| `Fn`, `call` | Function pointers | |
|
||||||
| `type_of`, `print`, `debug`, `eval` | Special functions | |
|
| `type_of`, `print`, `debug`, `eval` | Special functions | |
|
||||||
|
|
||||||
Keywords cannot be the name of a [function] or [variable], unless the relevant feature is enabled.
|
Keywords cannot be the name of a [function] or [variable], unless the relevant feature is enabled.
|
||||||
|
@ -15,7 +15,7 @@ The following primitive types are supported natively:
|
|||||||
| **[`Array`]** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
|
| **[`Array`]** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
|
||||||
| **[Object map]** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `"#{ "a": 1, "b": 2 }"` |
|
| **[Object map]** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `"#{ "a": 1, "b": 2 }"` |
|
||||||
| **[Timestamp]** (implemented in the [`BasicTimePackage`][packages], disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`] if not [WASM] build) | `"timestamp"` | _not supported_ |
|
| **[Timestamp]** (implemented in the [`BasicTimePackage`][packages], disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`] if not [WASM] build) | `"timestamp"` | _not supported_ |
|
||||||
| **[Function pointer]** (disabled with [`no_function`]) | _None_ | `Fn` | `"Fn(foo)"` |
|
| **[Function pointer]** | _None_ | `Fn` | `"Fn(foo)"` |
|
||||||
| **[`Dynamic`] value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
| **[`Dynamic`] value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
||||||
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
||||||
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
||||||
|
@ -166,7 +166,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Throw away all the statements, leaving only the functions
|
// Throw away all the statements, leaving only the functions
|
||||||
#[cfg(not(feature = "no_function"))]
|
main_ast.clear_statements();
|
||||||
main_ast.retain_functions();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/any.rs
11
src/any.rs
@ -137,7 +137,6 @@ pub enum Union {
|
|||||||
Array(Box<Array>),
|
Array(Box<Array>),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Map(Box<Map>),
|
Map(Box<Map>),
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
FnPtr(FnPtr),
|
FnPtr(FnPtr),
|
||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>),
|
||||||
}
|
}
|
||||||
@ -175,7 +174,6 @@ impl Dynamic {
|
|||||||
Union::Array(_) => TypeId::of::<Array>(),
|
Union::Array(_) => TypeId::of::<Array>(),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => TypeId::of::<Map>(),
|
Union::Map(_) => TypeId::of::<Map>(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
||||||
Union::Variant(value) => (***value).type_id(),
|
Union::Variant(value) => (***value).type_id(),
|
||||||
}
|
}
|
||||||
@ -195,7 +193,6 @@ impl Dynamic {
|
|||||||
Union::Array(_) => "array",
|
Union::Array(_) => "array",
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => "map",
|
Union::Map(_) => "map",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Union::FnPtr(_) => "Fn",
|
Union::FnPtr(_) => "Fn",
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -220,7 +217,6 @@ impl fmt::Display for Dynamic {
|
|||||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -245,7 +241,6 @@ impl fmt::Debug for Dynamic {
|
|||||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -270,7 +265,6 @@ impl Clone for Dynamic {
|
|||||||
Union::Array(ref value) => Self(Union::Array(value.clone())),
|
Union::Array(ref value) => Self(Union::Array(value.clone())),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
||||||
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
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),
|
Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
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::FnPtr(value) => unsafe_try_cast(value),
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
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(),
|
Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
|
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).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()),
|
Union::Array(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
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::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
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()),
|
Union::Array(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
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::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
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 {
|
impl From<FnPtr> for Dynamic {
|
||||||
fn from(value: FnPtr) -> Self {
|
fn from(value: FnPtr) -> Self {
|
||||||
Self(Union::FnPtr(value))
|
Self(Union::FnPtr(value))
|
||||||
|
@ -103,6 +103,9 @@ pub use scope::Scope;
|
|||||||
pub use token::Position;
|
pub use token::Position;
|
||||||
pub use utils::calc_fn_spec as calc_fn_hash;
|
pub use utils::calc_fn_spec as calc_fn_hash;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub use parser::FnAccess;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use fn_func::Func;
|
pub use fn_func::Func;
|
||||||
|
|
||||||
@ -135,7 +138,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::{Expr, FnAccess, ReturnType, ScriptFnDef, Stmt};
|
pub use parser::{Expr, 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")]
|
||||||
|
@ -859,12 +859,53 @@ impl Module {
|
|||||||
|
|
||||||
/// Merge another module into this module.
|
/// Merge another module into this module.
|
||||||
pub fn merge(&mut self, other: &Self) {
|
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
|
self.variables
|
||||||
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
|
.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
|
self.type_iterators
|
||||||
.extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone())));
|
.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.
|
/// Get the number of variables in the module.
|
||||||
|
112
src/parser.rs
112
src/parser.rs
@ -116,8 +116,15 @@ impl AST {
|
|||||||
///
|
///
|
||||||
/// let engine = Engine::new();
|
/// let engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// let ast1 = engine.compile(r#"fn foo(x) { 42 + x } foo(1)"#)?;
|
/// let ast1 = engine.compile(r#"
|
||||||
/// let ast2 = engine.compile(r#"fn foo(n) { "hello" + n } foo("!")"#)?;
|
/// 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'
|
/// let ast = ast1.merge(&ast2); // Merge 'ast2' into 'ast1'
|
||||||
///
|
///
|
||||||
@ -138,6 +145,65 @@ impl AST {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn merge(&self, other: &Self) -> Self {
|
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 Self(statements, functions) = self;
|
||||||
|
|
||||||
let ast = match (statements.is_empty(), other.0.is_empty()) {
|
let ast = match (statements.is_empty(), other.0.is_empty()) {
|
||||||
@ -152,11 +218,39 @@ impl AST {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut functions = functions.clone();
|
let mut functions = functions.clone();
|
||||||
functions.merge(&other.1);
|
functions.merge_filtered(&other.1, filter);
|
||||||
|
|
||||||
Self::new(ast, functions)
|
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`].
|
/// Clear all function definitions in the [`AST`].
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub fn clear_functions(&mut self) {
|
pub fn clear_functions(&mut self) {
|
||||||
@ -164,8 +258,7 @@ impl AST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all statements in the [`AST`], leaving only function definitions.
|
/// Clear all statements in the [`AST`], leaving only function definitions.
|
||||||
#[cfg(not(feature = "no_function"))]
|
pub fn clear_statements(&mut self) {
|
||||||
pub fn retain_functions(&mut self) {
|
|
||||||
self.0 = vec![];
|
self.0 = vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,6 +280,15 @@ pub enum FnAccess {
|
|||||||
Public,
|
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.
|
/// A scripted function definition.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ScriptFnDef {
|
pub struct ScriptFnDef {
|
||||||
|
@ -250,6 +250,7 @@ fn test_module_export() -> Result<(), Box<EvalAltResult>> {
|
|||||||
ParseError(x, _) if *x == ParseErrorType::WrongExport
|
ParseError(x, _) if *x == ParseErrorType::WrongExport
|
||||||
));
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile(r"fn abc(x) { export x; }").expect_err("should error"),
|
engine.compile(r"fn abc(x) { export x; }").expect_err("should error"),
|
||||||
ParseError(x, _) if *x == ParseErrorType::WrongExport
|
ParseError(x, _) if *x == ParseErrorType::WrongExport
|
||||||
|
Loading…
Reference in New Issue
Block a user