Allow binding of this pointer in FnPtr calls.
This commit is contained in:
parent
197f5d370f
commit
d119e13b79
@ -9,6 +9,7 @@ New features
|
|||||||
|
|
||||||
* `call` can now be called function-call style for function pointers - this is to handle builds with `no_object`.
|
* `call` can now be called function-call style for function pointers - this is to handle builds with `no_object`.
|
||||||
* Disallow many keywords as variables, such as `print`, `eval`, `call`, `this` etc.
|
* Disallow many keywords as variables, such as `print`, `eval`, `call`, `this` etc.
|
||||||
|
* `x.call(f, ...)` allows binding `x` to `this` for the function referenced by the function pointer `f`.
|
||||||
|
|
||||||
|
|
||||||
Version 0.17.0
|
Version 0.17.0
|
||||||
|
@ -66,20 +66,18 @@ Because of their dynamic nature, function pointers cannot refer to functions in
|
|||||||
See [function namespaces] for more details.
|
See [function namespaces] for more details.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
import "foo" as f; // assume there is 'f::do_something()'
|
import "foo" as f; // assume there is 'f::do_work()'
|
||||||
|
|
||||||
f::do_something(); // works!
|
f::do_work(); // works!
|
||||||
|
|
||||||
let p = Fn("f::do_something");
|
let p = Fn("f::do_work"); // error: invalid function name
|
||||||
|
|
||||||
p.call(); // error: function not found - 'f::do_something'
|
fn do_work_now() { // call it from a local function
|
||||||
|
|
||||||
fn do_something_now() { // call it from a local function
|
|
||||||
import "foo" as f;
|
import "foo" as f;
|
||||||
f::do_something();
|
f::do_work();
|
||||||
}
|
}
|
||||||
|
|
||||||
let p = Fn("do_something_now");
|
let p = Fn("do_work_now");
|
||||||
|
|
||||||
p.call(); // works!
|
p.call(); // works!
|
||||||
```
|
```
|
||||||
@ -134,3 +132,35 @@ let func = sign(x) + 1;
|
|||||||
// Dynamic dispatch
|
// Dynamic dispatch
|
||||||
map[func].call(42);
|
map[func].call(42);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Binding the `this` Pointer
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
When `call` is called as a _method_ but not on a `FnPtr` value, it is possible to dynamically dispatch
|
||||||
|
to a function call while binding the object in the method call to the `this` pointer of the function.
|
||||||
|
|
||||||
|
To achieve this, pass the `FnPtr` value as the _first_ argument to `call`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn add(x) { this += x; } // define function which uses 'this'
|
||||||
|
|
||||||
|
let func = Fn("add"); // function pointer to 'add'
|
||||||
|
|
||||||
|
func.call(1); // error: 'this' pointer is not bound
|
||||||
|
|
||||||
|
let x = 41;
|
||||||
|
|
||||||
|
func.call(x, 1); // error: function 'add (i64, i64)' not found
|
||||||
|
|
||||||
|
call(func, x, 1); // error: function 'add (i64, i64)' not found
|
||||||
|
|
||||||
|
x.call(func, 1); // 'this' is bound to 'x', dispatched to 'func'
|
||||||
|
|
||||||
|
x == 42;
|
||||||
|
```
|
||||||
|
|
||||||
|
Beware that this only works for _method-call_ style. Normal function-call style cannot bind
|
||||||
|
the `this` pointer (for syntactic reasons).
|
||||||
|
|
||||||
|
Therefore, obviously, binding the `this` pointer is unsupported under [`no_function`].
|
||||||
|
@ -96,7 +96,7 @@ pub const MARKER_BLOCK: &str = "$block$";
|
|||||||
pub const MARKER_IDENT: &str = "$ident$";
|
pub const MARKER_IDENT: &str = "$ident$";
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct Expression<'a>(&'a Expr);
|
pub struct Expression<'a>(&'a Expr);
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
@ -1088,10 +1088,10 @@ impl Engine {
|
|||||||
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
|
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
|
||||||
let mut fn_name = name.as_ref();
|
let mut fn_name = name.as_ref();
|
||||||
|
|
||||||
// Check if it is a FnPtr call
|
|
||||||
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
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
fn_name = obj.as_str().unwrap();
|
let fn_name = obj.as_str().unwrap();
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
let hash = calc_fn_hash(empty(), fn_name, idx.len(), empty());
|
||||||
// Arguments are passed as-is
|
// Arguments are passed as-is
|
||||||
@ -1102,6 +1102,26 @@ impl Engine {
|
|||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, fn_name, *native, hash, args, false, false, def_val, level,
|
state, lib, fn_name, *native, hash, args, false, false, def_val, level,
|
||||||
)
|
)
|
||||||
|
} else if fn_name == KEYWORD_FN_PTR_CALL && idx.len() > 0 && idx[0].is::<FnPtr>() {
|
||||||
|
// FnPtr call on object
|
||||||
|
// Redirect function name
|
||||||
|
let fn_name = idx[0]
|
||||||
|
.downcast_ref::<FnPtr>()
|
||||||
|
.unwrap()
|
||||||
|
.get_fn_name()
|
||||||
|
.clone();
|
||||||
|
// Recalculate hash
|
||||||
|
let hash = calc_fn_hash(empty(), &fn_name, idx.len() - 1, empty());
|
||||||
|
// Replace the first argument with the object pointer
|
||||||
|
let mut arg_values = once(obj)
|
||||||
|
.chain(idx.iter_mut().skip(1))
|
||||||
|
.collect::<StaticVec<_>>();
|
||||||
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
|
// Map it to name(args) in function-call style
|
||||||
|
self.exec_fn_call(
|
||||||
|
state, lib, &fn_name, *native, hash, args, is_ref, true, def_val, level,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let redirected: Option<ImmutableString>;
|
let redirected: Option<ImmutableString>;
|
||||||
let mut hash = *hash;
|
let mut hash = *hash;
|
||||||
|
@ -98,6 +98,8 @@ pub enum ParseErrorType {
|
|||||||
PropertyExpected,
|
PropertyExpected,
|
||||||
/// Missing a variable name after the `let`, `const` or `for` keywords.
|
/// Missing a variable name after the `let`, `const` or `for` keywords.
|
||||||
VariableExpected,
|
VariableExpected,
|
||||||
|
/// An identifier is a reserved keyword.
|
||||||
|
Reserved(String),
|
||||||
/// Missing an expression. Wrapped value is the expression type.
|
/// Missing an expression. Wrapped value is the expression type.
|
||||||
ExprExpected(String),
|
ExprExpected(String),
|
||||||
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
||||||
@ -163,6 +165,7 @@ impl ParseErrorType {
|
|||||||
Self::ForbiddenConstantExpr(_) => "Expecting a constant",
|
Self::ForbiddenConstantExpr(_) => "Expecting a constant",
|
||||||
Self::PropertyExpected => "Expecting name of a property",
|
Self::PropertyExpected => "Expecting name of a property",
|
||||||
Self::VariableExpected => "Expecting name of a variable",
|
Self::VariableExpected => "Expecting name of a variable",
|
||||||
|
Self::Reserved(_) => "Invalid use of reserved keyword",
|
||||||
Self::ExprExpected(_) => "Expecting an expression",
|
Self::ExprExpected(_) => "Expecting an expression",
|
||||||
Self::FnMissingName => "Expecting name in function declaration",
|
Self::FnMissingName => "Expecting name in function declaration",
|
||||||
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||||
@ -224,6 +227,7 @@ impl fmt::Display for ParseErrorType {
|
|||||||
Self::LiteralTooLarge(typ, max) => {
|
Self::LiteralTooLarge(typ, max) => {
|
||||||
write!(f, "{} exceeds the maximum limit ({})", typ, max)
|
write!(f, "{} exceeds the maximum limit ({})", typ, max)
|
||||||
}
|
}
|
||||||
|
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
||||||
_ => f.write_str(self.desc()),
|
_ => f.write_str(self.desc()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
153
src/parser.rs
153
src/parser.rs
@ -25,6 +25,7 @@ use crate::stdlib::{
|
|||||||
char,
|
char,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt, format,
|
fmt, format,
|
||||||
|
hash::Hash,
|
||||||
iter::empty,
|
iter::empty,
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroUsize,
|
num::NonZeroUsize,
|
||||||
@ -340,7 +341,7 @@ impl fmt::Display for FnAccess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A scripted function definition.
|
/// A scripted function definition.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct ScriptFnDef {
|
pub struct ScriptFnDef {
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -374,7 +375,7 @@ impl fmt::Display for ScriptFnDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `return`/`throw` statement.
|
/// `return`/`throw` statement.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||||
pub enum ReturnType {
|
pub enum ReturnType {
|
||||||
/// `return` statement.
|
/// `return` statement.
|
||||||
Return,
|
Return,
|
||||||
@ -478,7 +479,7 @@ impl ParseSettings {
|
|||||||
///
|
///
|
||||||
/// 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.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
@ -590,6 +591,13 @@ impl fmt::Debug for CustomExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
impl Hash for CustomExpr {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An expression.
|
/// An expression.
|
||||||
///
|
///
|
||||||
/// Each variant is at most one pointer in size (for speed),
|
/// Each variant is at most one pointer in size (for speed),
|
||||||
@ -665,6 +673,18 @@ impl Default for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hash for Expr {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
match self {
|
||||||
|
Self::FloatConstant(x) => {
|
||||||
|
state.write(&x.0.to_le_bytes());
|
||||||
|
x.1.hash(state);
|
||||||
|
}
|
||||||
|
_ => self.hash(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Get the `Dynamic` value of a constant expression.
|
/// Get the `Dynamic` value of a constant expression.
|
||||||
///
|
///
|
||||||
@ -1345,56 +1365,57 @@ fn parse_map_literal(
|
|||||||
eat_token(input, Token::RightBrace);
|
eat_token(input, Token::RightBrace);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => (),
|
||||||
let (name, pos) = match input.next().unwrap() {
|
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
|
||||||
(Token::StringConstant(s), pos) => (s, pos),
|
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
|
||||||
(_, pos) if map.is_empty() => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::RightBrace.into(),
|
|
||||||
MISSING_RBRACE.into(),
|
|
||||||
)
|
|
||||||
.into_err(pos))
|
|
||||||
}
|
|
||||||
(Token::EOF, pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::RightBrace.into(),
|
|
||||||
MISSING_RBRACE.into(),
|
|
||||||
)
|
|
||||||
.into_err(pos))
|
|
||||||
}
|
|
||||||
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
|
||||||
};
|
|
||||||
|
|
||||||
match input.next().unwrap() {
|
|
||||||
(Token::Colon, _) => (),
|
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
|
||||||
(_, pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::Colon.into(),
|
|
||||||
format!(
|
|
||||||
"to follow the property '{}' in this object map literal",
|
|
||||||
name
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into_err(pos))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if state.engine.max_map_size > 0 && map.len() >= state.engine.max_map_size {
|
|
||||||
return Err(PERR::LiteralTooLarge(
|
|
||||||
"Number of properties in object map literal".to_string(),
|
|
||||||
state.engine.max_map_size,
|
|
||||||
)
|
|
||||||
.into_err(input.peek().unwrap().1));
|
|
||||||
}
|
|
||||||
|
|
||||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
|
||||||
map.push(((Into::<ImmutableString>::into(name), pos), expr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (name, pos) = match input.next().unwrap() {
|
||||||
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
|
(Token::StringConstant(s), pos) => (s, pos),
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
(_, pos) if map.is_empty() => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
|
.into_err(pos),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Token::EOF, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
|
.into_err(pos),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match input.next().unwrap() {
|
||||||
|
(Token::Colon, _) => (),
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Colon.into(),
|
||||||
|
format!(
|
||||||
|
"to follow the property '{}' in this object map literal",
|
||||||
|
name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if state.engine.max_map_size > 0 && map.len() >= state.engine.max_map_size {
|
||||||
|
return Err(PERR::LiteralTooLarge(
|
||||||
|
"Number of properties in object map literal".to_string(),
|
||||||
|
state.engine.max_map_size,
|
||||||
|
)
|
||||||
|
.into_err(input.peek().unwrap().1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
|
map.push(((Into::<ImmutableString>::into(name), pos), expr));
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => {
|
(Token::Comma, _) => {
|
||||||
eat_token(input, Token::Comma);
|
eat_token(input, Token::Comma);
|
||||||
@ -1477,6 +1498,9 @@ fn parse_primary(
|
|||||||
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
|
Expr::Variable(Box::new(((s, settings.pos), None, 0, None)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Token::Reserved(s) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(settings.pos));
|
||||||
|
}
|
||||||
Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
|
Token::LeftParen => parse_paren_expr(input, state, lib, settings.level_up())?,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
|
Token::LeftBracket => parse_array_literal(input, state, lib, settings.level_up())?,
|
||||||
@ -1488,7 +1512,7 @@ fn parse_primary(
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos)
|
PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1526,6 +1550,9 @@ fn parse_primary(
|
|||||||
|
|
||||||
Expr::Variable(Box::new(((id2, pos2), modules, 0, index)))
|
Expr::Variable(Box::new(((id2, pos2), modules, 0, index)))
|
||||||
}
|
}
|
||||||
|
(Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => {
|
||||||
|
return Err(PERR::Reserved(id2).into_err(pos2));
|
||||||
|
}
|
||||||
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||||
},
|
},
|
||||||
// Indexing
|
// Indexing
|
||||||
@ -2112,6 +2139,9 @@ fn parse_expr(
|
|||||||
(Token::Identifier(s), pos) => {
|
(Token::Identifier(s), pos) => {
|
||||||
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
|
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
|
||||||
}
|
}
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
},
|
},
|
||||||
MARKER_EXPR => exprs.push(parse_expr(input, state, lib, settings)?),
|
MARKER_EXPR => exprs.push(parse_expr(input, state, lib, settings)?),
|
||||||
@ -2279,10 +2309,12 @@ fn parse_for(
|
|||||||
let name = match input.next().unwrap() {
|
let name = match input.next().unwrap() {
|
||||||
// Variable name
|
// Variable name
|
||||||
(Token::Identifier(s), _) => s,
|
(Token::Identifier(s), _) => s,
|
||||||
|
// Reserved keyword
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
// Bad identifier
|
// Bad identifier
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
// EOF
|
|
||||||
(Token::EOF, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
|
||||||
// Not a variable name
|
// Not a variable name
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -2329,6 +2361,9 @@ fn parse_let(
|
|||||||
// let name ...
|
// let name ...
|
||||||
let (name, pos) = match input.next().unwrap() {
|
let (name, pos) = match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -2398,6 +2433,9 @@ fn parse_import(
|
|||||||
// import expr as name ...
|
// import expr as name ...
|
||||||
let (name, _) = match input.next().unwrap() {
|
let (name, _) = match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -2422,6 +2460,9 @@ fn parse_export(
|
|||||||
loop {
|
loop {
|
||||||
let (id, id_pos) = match input.next().unwrap() {
|
let (id, id_pos) = match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => (s.clone(), pos),
|
(Token::Identifier(s), pos) => (s.clone(), pos),
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
@ -2429,6 +2470,10 @@ fn parse_export(
|
|||||||
let rename = if match_token(input, Token::As)? {
|
let rename = if match_token(input, Token::As)? {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => Some((s.clone(), pos)),
|
(Token::Identifier(s), pos) => Some((s.clone(), pos)),
|
||||||
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
|
}
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
19
src/token.rs
19
src/token.rs
@ -279,8 +279,6 @@ impl Token {
|
|||||||
Or => "||",
|
Or => "||",
|
||||||
Ampersand => "&",
|
Ampersand => "&",
|
||||||
And => "&&",
|
And => "&&",
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
Fn => "fn",
|
|
||||||
Continue => "continue",
|
Continue => "continue",
|
||||||
Break => "break",
|
Break => "break",
|
||||||
Return => "return",
|
Return => "return",
|
||||||
@ -301,8 +299,12 @@ impl Token {
|
|||||||
ModuloAssign => "%=",
|
ModuloAssign => "%=",
|
||||||
PowerOf => "~",
|
PowerOf => "~",
|
||||||
PowerOfAssign => "~=",
|
PowerOfAssign => "~=",
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Fn => "fn",
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Private => "private",
|
Private => "private",
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import => "import",
|
Import => "import",
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -359,8 +361,6 @@ impl Token {
|
|||||||
"||" => Or,
|
"||" => Or,
|
||||||
"&" => Ampersand,
|
"&" => Ampersand,
|
||||||
"&&" => And,
|
"&&" => And,
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
"fn" => Fn,
|
|
||||||
"continue" => Continue,
|
"continue" => Continue,
|
||||||
"break" => Break,
|
"break" => Break,
|
||||||
"return" => Return,
|
"return" => Return,
|
||||||
@ -381,14 +381,25 @@ impl Token {
|
|||||||
"%=" => ModuloAssign,
|
"%=" => ModuloAssign,
|
||||||
"~" => PowerOf,
|
"~" => PowerOf,
|
||||||
"~=" => PowerOfAssign,
|
"~=" => PowerOfAssign,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
"fn" => Fn,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
"private" => Private,
|
"private" => Private,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
"import" => Import,
|
"import" => Import,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
"export" => Export,
|
"export" => Export,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
"as" => As,
|
"as" => As,
|
||||||
|
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
"fn" | "private" => Reserved(syntax.into()),
|
||||||
|
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" => {
|
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" => {
|
||||||
Reserved(syntax.into())
|
Reserved(syntax.into())
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,12 @@ impl<T> Drop for StaticVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Hash> Hash for StaticVec<T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.iter().for_each(|x| x.hash(state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Default for StaticVec<T> {
|
impl<T> Default for StaticVec<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -116,7 +116,7 @@ fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
fn test_fn_ptr_raw() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_raw_fn(
|
engine.register_raw_fn(
|
||||||
|
80
tests/fn_ptr.rs
Normal file
80
tests/fn_ptr.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.register_fn("bar", |x: &mut INT, y: INT| *x += y);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let f = Fn("bar");
|
||||||
|
let x = 40;
|
||||||
|
f.call(x, 2);
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
40
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let f = Fn("bar");
|
||||||
|
let x = 40;
|
||||||
|
x.call(f, 2);
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let f = Fn("bar");
|
||||||
|
let x = 40;
|
||||||
|
call(f, x, 2);
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { this += x; }
|
||||||
|
|
||||||
|
let f = Fn("foo");
|
||||||
|
let x = 40;
|
||||||
|
x.call(f, 2);
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { this += x; }
|
||||||
|
|
||||||
|
let f = Fn("foo");
|
||||||
|
call(f, 2);
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorInFunctionCall(fn_name, err, _) if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundedThis(_))
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user