Add currying support.
This commit is contained in:
parent
e103c8e66c
commit
6d551f1596
@ -7,6 +7,7 @@ Version 0.18.0
|
|||||||
This version adds:
|
This version adds:
|
||||||
|
|
||||||
* Anonymous functions (in closure syntax). Simplifies creation of ad hoc functions.
|
* Anonymous functions (in closure syntax). Simplifies creation of ad hoc functions.
|
||||||
|
* Currying of function pointers.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -16,6 +17,7 @@ New features
|
|||||||
* `x.call(f, ...)` allows binding `x` to `this` for the function referenced by the function pointer `f`.
|
* `x.call(f, ...)` allows binding `x` to `this` for the function referenced by the function pointer `f`.
|
||||||
* 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.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
@ -76,6 +76,7 @@ The Rhai Scripting Language
|
|||||||
3. [Namespaces](language/fn-namespaces.md)
|
3. [Namespaces](language/fn-namespaces.md)
|
||||||
4. [Function Pointers](language/fn-ptr.md)
|
4. [Function Pointers](language/fn-ptr.md)
|
||||||
5. [Anonymous Functions](language/fn-anon.md)
|
5. [Anonymous Functions](language/fn-anon.md)
|
||||||
|
6. [Currying](language/fn-curry.md)
|
||||||
15. [Print and Debug](language/print-debug.md)
|
15. [Print and Debug](language/print-debug.md)
|
||||||
16. [Modules](language/modules/index.md)
|
16. [Modules](language/modules/index.md)
|
||||||
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
||||||
|
@ -26,6 +26,7 @@ Keywords List
|
|||||||
| `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] | |
|
| `Fn` (capital `F`) | Function to create a [function pointer] | |
|
||||||
| `call` | Call a [function pointer] | |
|
| `call` | Call a [function pointer] | |
|
||||||
|
| `curry` | Curry 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 | |
|
||||||
|
30
doc/src/language/fn-curry.md
Normal file
30
doc/src/language/fn-curry.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Function Pointer Currying
|
||||||
|
========================
|
||||||
|
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
It is possible to _curry_ a [function pointer] by providing partial (or all) arguments.
|
||||||
|
|
||||||
|
Currying is done via the `curry` keyword and produces a new [function pointer] which carries
|
||||||
|
the curried arguments.
|
||||||
|
|
||||||
|
When the curried [function pointer] is called, the curried arguments are inserted starting from the left.
|
||||||
|
The actual call arguments should be reduced by the number of curried arguments.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn mul(x, y) { // function with two parameters
|
||||||
|
x * y
|
||||||
|
}
|
||||||
|
|
||||||
|
let func = Fn("mul");
|
||||||
|
|
||||||
|
func.call(21, 2) == 42; // two arguments are required for 'mul'
|
||||||
|
|
||||||
|
let curried = func.curry(21); // currying produces a new function pointer which
|
||||||
|
// carries 21 as the first argument
|
||||||
|
|
||||||
|
let curried = curry(func, 21); // function-call style also works
|
||||||
|
|
||||||
|
curried.call(2) == 42; // <- de-sugars to 'func.call(21, 2)'
|
||||||
|
// only one argument is now required
|
||||||
|
```
|
@ -76,6 +76,7 @@
|
|||||||
[functions]: {{rootUrl}}/language/functions.md
|
[functions]: {{rootUrl}}/language/functions.md
|
||||||
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
||||||
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
||||||
|
[currying]: {{rootUrl}}/language/fn-curry.md
|
||||||
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
||||||
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
|
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
|
||||||
[anonymous function]: {{rootUrl}}/language/fn-anon.md
|
[anonymous function]: {{rootUrl}}/language/fn-anon.md
|
||||||
|
15
src/any.rs
15
src/any.rs
@ -137,7 +137,7 @@ 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>),
|
||||||
FnPtr(FnPtr),
|
FnPtr(Box<FnPtr>),
|
||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ impl fmt::Debug for Dynamic {
|
|||||||
f.write_str("#")?;
|
f.write_str("#")?;
|
||||||
fmt::Debug::fmt(value, f)
|
fmt::Debug::fmt(value, f)
|
||||||
}
|
}
|
||||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
Union::FnPtr(value) => fmt::Debug::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||||
@ -481,7 +481,7 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
if type_id == TypeId::of::<FnPtr>() {
|
if type_id == TypeId::of::<FnPtr>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::FnPtr(value) => unsafe_try_cast(value),
|
Union::FnPtr(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -582,7 +582,7 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
if type_id == TypeId::of::<FnPtr>() {
|
if type_id == TypeId::of::<FnPtr>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -656,7 +656,7 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
if type_id == TypeId::of::<FnPtr>() {
|
if type_id == TypeId::of::<FnPtr>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -801,6 +801,11 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<HashMap<K, T>> for Dynam
|
|||||||
}
|
}
|
||||||
impl From<FnPtr> for Dynamic {
|
impl From<FnPtr> for Dynamic {
|
||||||
fn from(value: FnPtr) -> Self {
|
fn from(value: FnPtr) -> Self {
|
||||||
|
Box::new(value).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Box<FnPtr>> for Dynamic {
|
||||||
|
fn from(value: Box<FnPtr>) -> Self {
|
||||||
Self(Union::FnPtr(value))
|
Self(Union::FnPtr(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
104
src/engine.rs
104
src/engine.rs
@ -79,6 +79,7 @@ pub const KEYWORD_TYPE_OF: &str = "type_of";
|
|||||||
pub const KEYWORD_EVAL: &str = "eval";
|
pub const KEYWORD_EVAL: &str = "eval";
|
||||||
pub const KEYWORD_FN_PTR: &str = "Fn";
|
pub const KEYWORD_FN_PTR: &str = "Fn";
|
||||||
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
||||||
|
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
|
||||||
pub const KEYWORD_THIS: &str = "this";
|
pub const KEYWORD_THIS: &str = "this";
|
||||||
pub const FN_TO_STRING: &str = "to_string";
|
pub const FN_TO_STRING: &str = "to_string";
|
||||||
pub const FN_GET: &str = "get$";
|
pub const FN_GET: &str = "get$";
|
||||||
@ -1042,12 +1043,17 @@ impl Engine {
|
|||||||
|
|
||||||
let (result, updated) = if fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
let (result, updated) = if fn_name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
||||||
// FnPtr call
|
// FnPtr call
|
||||||
|
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
|
||||||
|
let mut curry: StaticVec<_> = fn_ptr.curry().iter().cloned().collect();
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = obj.as_str().unwrap();
|
let fn_name = fn_ptr.fn_name();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
let hash = calc_fn_hash(empty(), fn_name, curry.len() + idx.len(), empty());
|
||||||
// Arguments are passed as-is
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
let mut arg_values = idx.iter_mut().collect::<StaticVec<_>>();
|
let mut arg_values = curry
|
||||||
|
.iter_mut()
|
||||||
|
.chain(idx.iter_mut())
|
||||||
|
.collect::<StaticVec<_>>();
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
@ -1056,16 +1062,15 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
|
let fn_ptr = idx[0].downcast_ref::<FnPtr>().unwrap();
|
||||||
|
let mut curry: StaticVec<_> = fn_ptr.curry().iter().cloned().collect();
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = idx[0]
|
let fn_name = fn_ptr.get_fn_name().clone();
|
||||||
.downcast_ref::<FnPtr>()
|
|
||||||
.unwrap()
|
|
||||||
.get_fn_name()
|
|
||||||
.clone();
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = calc_fn_hash(empty(), &fn_name, idx.len() - 1, empty());
|
let hash = calc_fn_hash(empty(), &fn_name, curry.len() + idx.len() - 1, empty());
|
||||||
// Replace the first argument with the object pointer
|
// Replace the first argument with the object pointer, adding the curried arguments
|
||||||
let mut arg_values = once(obj)
|
let mut arg_values = once(obj)
|
||||||
|
.chain(curry.iter_mut())
|
||||||
.chain(idx.iter_mut().skip(1))
|
.chain(idx.iter_mut().skip(1))
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
@ -1074,8 +1079,19 @@ impl Engine {
|
|||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level,
|
state, lib, &fn_name, *native, hash, args, is_ref, true, *def_val, level,
|
||||||
)
|
)
|
||||||
|
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
||||||
|
// Curry call
|
||||||
|
let fn_ptr = obj.downcast_ref::<FnPtr>().unwrap();
|
||||||
|
Ok((
|
||||||
|
FnPtr::new_unchecked(
|
||||||
|
fn_ptr.get_fn_name().clone(),
|
||||||
|
fn_ptr.curry().iter().chain(idx.iter()).cloned().collect(),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
false,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
let redirected: Option<ImmutableString>;
|
let redirected;
|
||||||
let mut hash = *hash;
|
let mut hash = *hash;
|
||||||
|
|
||||||
// Check if it is a map method call in OOP style
|
// Check if it is a map method call in OOP style
|
||||||
@ -1084,9 +1100,8 @@ impl Engine {
|
|||||||
if let Some(val) = map.get(fn_name) {
|
if let Some(val) = map.get(fn_name) {
|
||||||
if let Some(f) = val.downcast_ref::<FnPtr>() {
|
if let Some(f) = val.downcast_ref::<FnPtr>() {
|
||||||
// Remap the function name
|
// Remap the function name
|
||||||
redirected = Some(f.get_fn_name().clone());
|
redirected = f.get_fn_name().clone();
|
||||||
fn_name = redirected.as_ref().unwrap();
|
fn_name = &redirected;
|
||||||
|
|
||||||
// Recalculate the hash based on the new function name
|
// Recalculate the hash based on the new function name
|
||||||
hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
||||||
}
|
}
|
||||||
@ -1731,7 +1746,7 @@ impl Engine {
|
|||||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||||
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone()).into()),
|
Expr::FnPointer(x) => Ok(FnPtr::new_unchecked(x.0.clone(), Default::default()).into()),
|
||||||
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
Expr::Variable(x) if (x.0).0 == KEYWORD_THIS => {
|
||||||
if let Some(val) = this_ptr {
|
if let Some(val) = this_ptr {
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
@ -1919,6 +1934,39 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle curry()
|
||||||
|
if name == KEYWORD_FN_PTR_CURRY && args_expr.len() > 1 {
|
||||||
|
let expr = args_expr.get(0);
|
||||||
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
|
if !fn_ptr.is::<FnPtr>() {
|
||||||
|
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
self.map_type_name(type_name::<FnPtr>()).into(),
|
||||||
|
self.map_type_name(fn_ptr.type_name()).into(),
|
||||||
|
expr.position(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let fn_ptr = fn_ptr.downcast_ref::<FnPtr>().unwrap();
|
||||||
|
|
||||||
|
let curry: StaticVec<_> = args_expr
|
||||||
|
.iter()
|
||||||
|
.skip(1)
|
||||||
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
return Ok(FnPtr::new_unchecked(
|
||||||
|
fn_ptr.get_fn_name().clone(),
|
||||||
|
fn_ptr
|
||||||
|
.curry()
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.chain(curry.into_iter())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
// Handle eval()
|
// Handle eval()
|
||||||
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||||
let hash_fn =
|
let hash_fn =
|
||||||
@ -1948,6 +1996,7 @@ impl Engine {
|
|||||||
let redirected;
|
let redirected;
|
||||||
let mut name = name.as_ref();
|
let mut name = name.as_ref();
|
||||||
let mut args_expr = args_expr.as_ref();
|
let mut args_expr = args_expr.as_ref();
|
||||||
|
let mut curry: StaticVec<_> = Default::default();
|
||||||
let mut hash = *hash;
|
let mut hash = *hash;
|
||||||
|
|
||||||
if name == KEYWORD_FN_PTR_CALL
|
if name == KEYWORD_FN_PTR_CALL
|
||||||
@ -1955,20 +2004,22 @@ impl Engine {
|
|||||||
&& !self.has_override(lib, 0, hash)
|
&& !self.has_override(lib, 0, hash)
|
||||||
{
|
{
|
||||||
let expr = args_expr.get(0).unwrap();
|
let expr = args_expr.get(0).unwrap();
|
||||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let fn_name = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
if fn_ptr.is::<FnPtr>() {
|
if fn_name.is::<FnPtr>() {
|
||||||
|
let fn_ptr = fn_name.cast::<FnPtr>();
|
||||||
|
curry = fn_ptr.curry().iter().cloned().collect();
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
redirected = Some(fn_ptr.cast::<FnPtr>().take_fn_name());
|
redirected = fn_ptr.take_fn_name();
|
||||||
name = redirected.as_ref().unwrap();
|
name = &redirected;
|
||||||
// Skip the first argument
|
// Skip the first argument
|
||||||
args_expr = &args_expr.as_ref()[1..];
|
args_expr = &args_expr.as_ref()[1..];
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
hash = calc_fn_hash(empty(), name, args_expr.len(), empty());
|
hash = calc_fn_hash(empty(), name, curry.len() + args_expr.len(), empty());
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<FnPtr>()).into(),
|
self.map_type_name(type_name::<FnPtr>()).into(),
|
||||||
fn_ptr.type_name().into(),
|
fn_name.type_name().into(),
|
||||||
expr.position(),
|
expr.position(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -1979,7 +2030,7 @@ impl Engine {
|
|||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
let mut is_ref = false;
|
let mut is_ref = false;
|
||||||
|
|
||||||
if args_expr.is_empty() {
|
if args_expr.is_empty() && curry.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
@ -2002,7 +2053,10 @@ impl Engine {
|
|||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.new_position(pos))?;
|
.map_err(|err| err.new_position(pos))?;
|
||||||
|
|
||||||
args = once(target).chain(arg_values.iter_mut()).collect();
|
args = once(target)
|
||||||
|
.chain(curry.iter_mut())
|
||||||
|
.chain(arg_values.iter_mut())
|
||||||
|
.collect();
|
||||||
|
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
}
|
}
|
||||||
@ -2015,7 +2069,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = arg_values.iter_mut().collect();
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,13 +50,13 @@ pub fn shared_take<T: Clone>(value: Shared<T>) -> T {
|
|||||||
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
||||||
|
|
||||||
/// A general function pointer.
|
/// A general function pointer.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct FnPtr(ImmutableString);
|
pub struct FnPtr(ImmutableString, Vec<Dynamic>);
|
||||||
|
|
||||||
impl FnPtr {
|
impl FnPtr {
|
||||||
/// Create a new function pointer.
|
/// Create a new function pointer.
|
||||||
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S) -> Self {
|
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(name: S, curry: Vec<Dynamic>) -> Self {
|
||||||
Self(name.into())
|
Self(name.into(), curry)
|
||||||
}
|
}
|
||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
pub fn fn_name(&self) -> &str {
|
pub fn fn_name(&self) -> &str {
|
||||||
@ -70,6 +70,10 @@ impl FnPtr {
|
|||||||
pub(crate) fn take_fn_name(self) -> ImmutableString {
|
pub(crate) fn take_fn_name(self) -> ImmutableString {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
/// Get the curried data.
|
||||||
|
pub(crate) fn curry(&self) -> &[Dynamic] {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FnPtr {
|
impl fmt::Display for FnPtr {
|
||||||
@ -83,7 +87,7 @@ impl TryFrom<ImmutableString> for FnPtr {
|
|||||||
|
|
||||||
fn try_from(value: ImmutableString) -> Result<Self, Self::Error> {
|
fn try_from(value: ImmutableString) -> Result<Self, Self::Error> {
|
||||||
if is_valid_identifier(value.chars()) {
|
if is_valid_identifier(value.chars()) {
|
||||||
Ok(Self(value))
|
Ok(Self(value, Default::default()))
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
Err(Box::new(EvalAltResult::ErrorFunctionNotFound(
|
||||||
value.into(),
|
value.into(),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Main module defining the lexer and parser.
|
//! Main module defining the lexer and parser.
|
||||||
|
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_PRINT,
|
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
|
||||||
KEYWORD_THIS, KEYWORD_TYPE_OF,
|
KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::LexError;
|
use crate::error::LexError;
|
||||||
@ -404,7 +404,7 @@ impl Token {
|
|||||||
Reserved(syntax.into())
|
Reserved(syntax.into())
|
||||||
}
|
}
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_THIS => Reserved(syntax.into()),
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS => Reserved(syntax.into()),
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
|
@ -78,3 +78,35 @@ fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fn_ptr_curry() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.register_fn("foo", |x: &mut INT, y: INT| *x + y);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let f = Fn("foo");
|
||||||
|
let f2 = f.curry(40);
|
||||||
|
f2.call(2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let f = Fn("foo");
|
||||||
|
let f2 = curry(f, 40);
|
||||||
|
call(f2, 2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user