Map in operator to contains function call.
This commit is contained in:
parent
ff7844893d
commit
975bb3d6bf
25
src/ast.rs
25
src/ast.rs
@ -1300,7 +1300,7 @@ pub enum Expr {
|
|||||||
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
|
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
|
||||||
/// Property access - (getter, hash, setter, hash, prop)
|
/// Property access - (getter, hash, setter, hash, prop)
|
||||||
Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>),
|
Property(Box<(ImmutableString, u64, ImmutableString, u64, Ident)>),
|
||||||
/// { [statement][Stmt] }
|
/// { [statement][Stmt] ... }
|
||||||
Stmt(Box<StaticVec<Stmt>>, Position),
|
Stmt(Box<StaticVec<Stmt>>, Position),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
FnCall(Box<FnCallExpr>, Position),
|
FnCall(Box<FnCallExpr>, Position),
|
||||||
@ -1308,8 +1308,6 @@ pub enum Expr {
|
|||||||
Dot(Box<BinaryExpr>, Position),
|
Dot(Box<BinaryExpr>, Position),
|
||||||
/// expr `[` expr `]`
|
/// expr `[` expr `]`
|
||||||
Index(Box<BinaryExpr>, Position),
|
Index(Box<BinaryExpr>, Position),
|
||||||
/// lhs `in` rhs
|
|
||||||
In(Box<BinaryExpr>, Position),
|
|
||||||
/// lhs `&&` rhs
|
/// lhs `&&` rhs
|
||||||
And(Box<BinaryExpr>, Position),
|
And(Box<BinaryExpr>, Position),
|
||||||
/// lhs `||` rhs
|
/// lhs `||` rhs
|
||||||
@ -1397,7 +1395,7 @@ impl Expr {
|
|||||||
Self::Variable(x) => (x.2).pos,
|
Self::Variable(x) => (x.2).pos,
|
||||||
Self::FnCall(_, pos) => *pos,
|
Self::FnCall(_, pos) => *pos,
|
||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(),
|
Self::And(x, _) | Self::Or(x, _) => x.lhs.position(),
|
||||||
|
|
||||||
Self::Unit(pos) => *pos,
|
Self::Unit(pos) => *pos,
|
||||||
|
|
||||||
@ -1424,7 +1422,7 @@ impl Expr {
|
|||||||
Self::Property(x) => (x.4).pos = new_pos,
|
Self::Property(x) => (x.4).pos = new_pos,
|
||||||
Self::Stmt(_, pos) => *pos = new_pos,
|
Self::Stmt(_, pos) => *pos = new_pos,
|
||||||
Self::FnCall(_, pos) => *pos = new_pos,
|
Self::FnCall(_, pos) => *pos = new_pos,
|
||||||
Self::And(_, pos) | Self::Or(_, pos) | Self::In(_, pos) => *pos = new_pos,
|
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
|
||||||
Self::Unit(pos) => *pos = new_pos,
|
Self::Unit(pos) => *pos = new_pos,
|
||||||
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
|
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
|
||||||
Self::Custom(_, pos) => *pos = new_pos,
|
Self::Custom(_, pos) => *pos = new_pos,
|
||||||
@ -1441,7 +1439,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure),
|
Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure),
|
||||||
|
|
||||||
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => {
|
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => {
|
||||||
x.lhs.is_pure() && x.rhs.is_pure()
|
x.lhs.is_pure() && x.rhs.is_pure()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1480,13 +1478,6 @@ impl Expr {
|
|||||||
// An map literal is constant if all items are constant
|
// An map literal is constant if all items are constant
|
||||||
Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
||||||
|
|
||||||
// Check in expression
|
|
||||||
Self::In(x, _) => match (&x.lhs, &x.rhs) {
|
|
||||||
(Self::StringConstant(_, _), Self::StringConstant(_, _))
|
|
||||||
| (Self::CharConstant(_, _), Self::StringConstant(_, _)) => true,
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1507,7 +1498,6 @@ impl Expr {
|
|||||||
| Self::IntegerConstant(_, _)
|
| Self::IntegerConstant(_, _)
|
||||||
| Self::CharConstant(_, _)
|
| Self::CharConstant(_, _)
|
||||||
| Self::FnPointer(_, _)
|
| Self::FnPointer(_, _)
|
||||||
| Self::In(_, _)
|
|
||||||
| Self::And(_, _)
|
| Self::And(_, _)
|
||||||
| Self::Or(_, _)
|
| Self::Or(_, _)
|
||||||
| Self::Unit(_) => false,
|
| Self::Unit(_) => false,
|
||||||
@ -1553,11 +1543,7 @@ impl Expr {
|
|||||||
Self::Stmt(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
Self::Stmt(x, _) => x.iter().for_each(|s| s.walk(path, on_node)),
|
||||||
Self::Array(x, _) => x.iter().for_each(|e| e.walk(path, on_node)),
|
Self::Array(x, _) => x.iter().for_each(|e| e.walk(path, on_node)),
|
||||||
Self::Map(x, _) => x.iter().for_each(|(_, e)| e.walk(path, on_node)),
|
Self::Map(x, _) => x.iter().for_each(|(_, e)| e.walk(path, on_node)),
|
||||||
Self::Index(x, _)
|
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {
|
||||||
| Self::Dot(x, _)
|
|
||||||
| Expr::In(x, _)
|
|
||||||
| Expr::And(x, _)
|
|
||||||
| Expr::Or(x, _) => {
|
|
||||||
x.lhs.walk(path, on_node);
|
x.lhs.walk(path, on_node);
|
||||||
x.rhs.walk(path, on_node);
|
x.rhs.walk(path, on_node);
|
||||||
}
|
}
|
||||||
@ -1582,6 +1568,7 @@ mod tests {
|
|||||||
assert_eq!(size_of::<Option<Dynamic>>(), 16);
|
assert_eq!(size_of::<Option<Dynamic>>(), 16);
|
||||||
assert_eq!(size_of::<Position>(), 4);
|
assert_eq!(size_of::<Position>(), 4);
|
||||||
assert_eq!(size_of::<ast::Expr>(), 16);
|
assert_eq!(size_of::<ast::Expr>(), 16);
|
||||||
|
assert_eq!(size_of::<crate::ast_packed::ExprPacked>(), 32);
|
||||||
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
||||||
assert_eq!(size_of::<ast::Stmt>(), 32);
|
assert_eq!(size_of::<ast::Stmt>(), 32);
|
||||||
assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
|
assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
|
||||||
|
@ -202,9 +202,15 @@ pub const FN_IDX_GET: &str = "index$get$";
|
|||||||
pub const FN_IDX_SET: &str = "index$set$";
|
pub const FN_IDX_SET: &str = "index$set$";
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub const FN_ANONYMOUS: &str = "anon$";
|
pub const FN_ANONYMOUS: &str = "anon$";
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
|
/// Standard equality comparison operator.
|
||||||
pub const OP_EQUALS: &str = "==";
|
pub const OP_EQUALS: &str = "==";
|
||||||
|
|
||||||
|
/// Standard method function for containment testing.
|
||||||
|
///
|
||||||
|
/// The `in` operator is implemented as a call to this method.
|
||||||
|
pub const OP_CONTAINS: &str = "contains";
|
||||||
|
|
||||||
/// Method of chaining.
|
/// Method of chaining.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
@ -1609,78 +1615,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate an 'in' expression.
|
|
||||||
#[inline(always)]
|
|
||||||
fn eval_in_expr(
|
|
||||||
&self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
mods: &mut Imports,
|
|
||||||
state: &mut State,
|
|
||||||
lib: &[&Module],
|
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
|
||||||
lhs: &Expr,
|
|
||||||
rhs: &Expr,
|
|
||||||
level: usize,
|
|
||||||
) -> RhaiResult {
|
|
||||||
self.inc_operations(state, rhs.position())?;
|
|
||||||
|
|
||||||
let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?;
|
|
||||||
|
|
||||||
let mut rhs_target = if rhs.get_variable_access(false).is_some() {
|
|
||||||
let (rhs_ptr, pos) = self.search_namespace(scope, mods, state, lib, this_ptr, rhs)?;
|
|
||||||
self.inc_operations(state, pos)?;
|
|
||||||
rhs_ptr
|
|
||||||
} else {
|
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?
|
|
||||||
.into()
|
|
||||||
};
|
|
||||||
|
|
||||||
match rhs_target.as_mut() {
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Dynamic(Union::Array(rhs_value, _)) => {
|
|
||||||
// Call the `==` operator to compare each value
|
|
||||||
let hash = calc_fn_hash(empty(), OP_EQUALS, 2);
|
|
||||||
for value in rhs_value.iter_mut() {
|
|
||||||
let args = &mut [&mut lhs_value.clone(), &mut value.clone()];
|
|
||||||
let pos = rhs.position();
|
|
||||||
|
|
||||||
if self
|
|
||||||
.call_native_fn(mods, state, lib, OP_EQUALS, hash, args, false, false, pos)?
|
|
||||||
.0
|
|
||||||
.as_bool()
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
return Ok(true.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false.into())
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Dynamic(Union::Map(rhs_value, _)) => {
|
|
||||||
// Only allows string or char
|
|
||||||
if let Ok(c) = lhs_value.as_char() {
|
|
||||||
Ok(rhs_value.contains_key(&c.to_string()).into())
|
|
||||||
} else if let Some(s) = lhs_value.read_lock::<ImmutableString>() {
|
|
||||||
Ok(rhs_value.contains_key(&*s).into())
|
|
||||||
} else {
|
|
||||||
EvalAltResult::ErrorInExpr(lhs.position()).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Dynamic(Union::Str(rhs_value, _)) => {
|
|
||||||
// Only allows string or char
|
|
||||||
if let Ok(c) = lhs_value.as_char() {
|
|
||||||
Ok(rhs_value.contains(c).into())
|
|
||||||
} else if let Some(s) = lhs_value.read_lock::<ImmutableString>() {
|
|
||||||
Ok(rhs_value.contains(s.as_str()).into())
|
|
||||||
} else {
|
|
||||||
EvalAltResult::ErrorInExpr(lhs.position()).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => EvalAltResult::ErrorInExpr(rhs.position()).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate an expression.
|
/// Evaluate an expression.
|
||||||
pub(crate) fn eval_expr(
|
pub(crate) fn eval_expr(
|
||||||
&self,
|
&self,
|
||||||
@ -1792,10 +1726,6 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::In(x, _) => {
|
|
||||||
self.eval_in_expr(scope, mods, state, lib, this_ptr, &x.lhs, &x.rhs, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::And(x, _) => {
|
Expr::And(x, _) => {
|
||||||
Ok((self
|
Ok((self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! Built-in implementations for common operators.
|
//! Built-in implementations for common operators.
|
||||||
|
|
||||||
|
use crate::engine::OP_CONTAINS;
|
||||||
use crate::fn_native::{FnCallArgs, NativeCallContext};
|
use crate::fn_native::{FnCallArgs, NativeCallContext};
|
||||||
use crate::stdlib::{any::TypeId, format, string::ToString};
|
use crate::stdlib::{any::TypeId, format, string::ToString};
|
||||||
use crate::{Dynamic, ImmutableString, RhaiResult, INT};
|
use crate::{Dynamic, ImmutableString, RhaiResult, INT};
|
||||||
@ -77,6 +78,13 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
Ok((x $op y).into())
|
Ok((x $op y).into())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
($xx:ident . $func:ident ( $yy:ty )) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
|
Ok(x.$func(y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
($func:ident ( $op:tt )) => {
|
($func:ident ( $op:tt )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let (x, y) = $func(args);
|
let (x, y) = $func(args);
|
||||||
@ -259,6 +267,24 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
">=" => impl_op!(get_s1s2(>=)),
|
">=" => impl_op!(get_s1s2(>=)),
|
||||||
"<" => impl_op!(get_s1s2(<)),
|
"<" => impl_op!(get_s1s2(<)),
|
||||||
"<=" => impl_op!(get_s1s2(<=)),
|
"<=" => impl_op!(get_s1s2(<=)),
|
||||||
|
OP_CONTAINS => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let s = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let c = args[1].as_char().unwrap();
|
||||||
|
Ok((s.contains(c)).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// map op string
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if types_pair == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
use crate::Map;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
OP_CONTAINS => impl_op!(Map.contains_key(ImmutableString)),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,6 +368,13 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
">=" => impl_op!(ImmutableString >= ImmutableString),
|
">=" => impl_op!(ImmutableString >= ImmutableString),
|
||||||
"<" => impl_op!(ImmutableString < ImmutableString),
|
"<" => impl_op!(ImmutableString < ImmutableString),
|
||||||
"<=" => impl_op!(ImmutableString <= ImmutableString),
|
"<=" => impl_op!(ImmutableString <= ImmutableString),
|
||||||
|
OP_CONTAINS => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let s1 = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||||
|
let s2 = &*args[1].read_lock::<ImmutableString>().unwrap();
|
||||||
|
Ok((s1.contains(s2.as_str())).into())
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -831,6 +831,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a text script in place - used primarily for 'eval'.
|
/// Evaluate a text script in place - used primarily for 'eval'.
|
||||||
|
#[inline]
|
||||||
fn eval_script_expr_in_place(
|
fn eval_script_expr_in_place(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
|
@ -124,7 +124,7 @@ pub type FLOAT = f32;
|
|||||||
|
|
||||||
pub use ast::{FnAccess, AST};
|
pub use ast::{FnAccess, AST};
|
||||||
pub use dynamic::Dynamic;
|
pub use dynamic::Dynamic;
|
||||||
pub use engine::{Engine, EvalContext};
|
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
|
||||||
pub use fn_native::{FnPtr, NativeCallContext};
|
pub use fn_native::{FnPtr, NativeCallContext};
|
||||||
pub use fn_register::{RegisterFn, RegisterResultFn};
|
pub use fn_register::{RegisterFn, RegisterResultFn};
|
||||||
pub use module::{FnNamespace, Module};
|
pub use module::{FnNamespace, Module};
|
||||||
|
@ -15,7 +15,6 @@ use crate::stdlib::{
|
|||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::token::is_valid_identifier;
|
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, Module, Position, Scope,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, Module, Position, Scope,
|
||||||
@ -598,32 +597,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
// #{ key:value, .. }
|
// #{ key:value, .. }
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)),
|
Expr::Map(x, _) => x.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state)),
|
||||||
// lhs in rhs
|
|
||||||
Expr::In(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
|
||||||
// "xxx" in "xxxxx"
|
|
||||||
(Expr::StringConstant(a, pos), Expr::StringConstant(b, _)) => {
|
|
||||||
state.set_dirty();
|
|
||||||
*expr = Expr::BoolConstant( b.contains(a.as_str()), *pos);
|
|
||||||
}
|
|
||||||
// 'x' in "xxxxx"
|
|
||||||
(Expr::CharConstant(a, pos), Expr::StringConstant(b, _)) => {
|
|
||||||
state.set_dirty();
|
|
||||||
*expr = Expr::BoolConstant(b.contains(*a), *pos);
|
|
||||||
}
|
|
||||||
// "xxx" in #{...}
|
|
||||||
(Expr::StringConstant(a, pos), Expr::Map(b, _)) => {
|
|
||||||
state.set_dirty();
|
|
||||||
*expr = Expr::BoolConstant(b.iter().find(|(x, _)| x.name == *a).is_some(), *pos);
|
|
||||||
}
|
|
||||||
// 'x' in #{...}
|
|
||||||
(Expr::CharConstant(a, pos), Expr::Map(b, _)) => {
|
|
||||||
state.set_dirty();
|
|
||||||
let ch = a.to_string();
|
|
||||||
*expr = Expr::BoolConstant(b.iter().find(|(x, _)| x.name == &ch).is_some(), *pos);
|
|
||||||
}
|
|
||||||
// lhs in rhs
|
|
||||||
(lhs, rhs) => { optimize_expr(lhs, state); optimize_expr(rhs, state); }
|
|
||||||
},
|
|
||||||
// lhs && rhs
|
// lhs && rhs
|
||||||
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// true && rhs -> rhs
|
// true && rhs -> rhs
|
||||||
@ -684,7 +657,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.args.len() == 2 // binary call
|
&& x.args.len() == 2 // binary call
|
||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| e.get_constant_value().unwrap()).collect();
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
@ -237,11 +237,11 @@ mod array_functions {
|
|||||||
pub fn contains(
|
pub fn contains(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
array: &mut Array,
|
array: &mut Array,
|
||||||
mut value: Dynamic,
|
value: Dynamic,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
for item in array.iter() {
|
for item in array.iter_mut() {
|
||||||
if ctx
|
if ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [&mut value, &mut item.clone()])
|
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
|
||||||
if fn_sig.starts_with(OP_EQUALS) =>
|
if fn_sig.starts_with(OP_EQUALS) =>
|
||||||
@ -268,11 +268,11 @@ mod array_functions {
|
|||||||
pub fn index_of(
|
pub fn index_of(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
array: &mut Array,
|
array: &mut Array,
|
||||||
mut value: Dynamic,
|
value: Dynamic,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
for (i, item) in array.iter().enumerate() {
|
for (i, item) in array.iter_mut().enumerate() {
|
||||||
if ctx
|
if ctx
|
||||||
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [&mut value, &mut item.clone()])
|
.call_fn_dynamic_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
|
||||||
if fn_sig.starts_with(OP_EQUALS) =>
|
if fn_sig.starts_with(OP_EQUALS) =>
|
||||||
|
@ -13,8 +13,8 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod map_functions {
|
mod map_functions {
|
||||||
#[rhai_fn(pure)]
|
#[rhai_fn(name = "has", pure)]
|
||||||
pub fn has(map: &mut Map, prop: ImmutableString) -> bool {
|
pub fn contains(map: &mut Map, prop: ImmutableString) -> bool {
|
||||||
map.contains_key(&prop)
|
map.contains_key(&prop)
|
||||||
}
|
}
|
||||||
#[rhai_fn(pure)]
|
#[rhai_fn(pure)]
|
||||||
|
@ -74,14 +74,6 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "contains")]
|
|
||||||
pub fn contains_char(string: &str, character: char) -> bool {
|
|
||||||
string.contains(character)
|
|
||||||
}
|
|
||||||
pub fn contains(string: &str, find_string: &str) -> bool {
|
|
||||||
string.contains(find_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
|
159
src/parser.rs
159
src/parser.rs
@ -5,7 +5,7 @@ use crate::ast::{
|
|||||||
Stmt,
|
Stmt,
|
||||||
};
|
};
|
||||||
use crate::dynamic::{AccessMode, Union};
|
use crate::dynamic::{AccessMode, Union};
|
||||||
use crate::engine::KEYWORD_THIS;
|
use crate::engine::{KEYWORD_THIS, OP_CONTAINS};
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
@ -480,7 +480,6 @@ fn parse_index_chain(
|
|||||||
Expr::CharConstant(_, _)
|
Expr::CharConstant(_, _)
|
||||||
| Expr::And(_, _)
|
| Expr::And(_, _)
|
||||||
| Expr::Or(_, _)
|
| Expr::Or(_, _)
|
||||||
| Expr::In(_, _)
|
|
||||||
| Expr::BoolConstant(_, _)
|
| Expr::BoolConstant(_, _)
|
||||||
| Expr::Unit(_) => {
|
| Expr::Unit(_) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
@ -514,7 +513,6 @@ fn parse_index_chain(
|
|||||||
Expr::CharConstant(_, _)
|
Expr::CharConstant(_, _)
|
||||||
| Expr::And(_, _)
|
| Expr::And(_, _)
|
||||||
| Expr::Or(_, _)
|
| Expr::Or(_, _)
|
||||||
| Expr::In(_, _)
|
|
||||||
| Expr::BoolConstant(_, _)
|
| Expr::BoolConstant(_, _)
|
||||||
| Expr::Unit(_) => {
|
| Expr::Unit(_) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
@ -548,8 +546,8 @@ fn parse_index_chain(
|
|||||||
)
|
)
|
||||||
.into_err(x.position()))
|
.into_err(x.position()))
|
||||||
}
|
}
|
||||||
// lhs[??? && ???], lhs[??? || ???], lhs[??? in ???]
|
// lhs[??? && ???], lhs[??? || ???]
|
||||||
x @ Expr::And(_, _) | x @ Expr::Or(_, _) | x @ Expr::In(_, _) => {
|
x @ Expr::And(_, _) | x @ Expr::Or(_, _) => {
|
||||||
return Err(PERR::MalformedIndexExpr(
|
return Err(PERR::MalformedIndexExpr(
|
||||||
"Array access expects integer index, not a boolean".into(),
|
"Array access expects integer index, not a boolean".into(),
|
||||||
)
|
)
|
||||||
@ -1602,139 +1600,6 @@ fn make_dot_expr(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make an 'in' expression.
|
|
||||||
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
|
|
||||||
match (&lhs, &rhs) {
|
|
||||||
(_, x @ Expr::IntegerConstant(_, _))
|
|
||||||
| (_, x @ Expr::And(_, _))
|
|
||||||
| (_, x @ Expr::Or(_, _))
|
|
||||||
| (_, x @ Expr::In(_, _))
|
|
||||||
| (_, x @ Expr::BoolConstant(_, _))
|
|
||||||
| (_, x @ Expr::Unit(_)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression expects a string, array or object map".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
(_, x @ Expr::FloatConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression expects a string, array or object map".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// "xxx" in "xxxx", 'x' in "xxxx" - OK!
|
|
||||||
(Expr::StringConstant(_, _), Expr::StringConstant(_, _))
|
|
||||||
| (Expr::CharConstant(_, _), Expr::StringConstant(_, _)) => (),
|
|
||||||
|
|
||||||
// 123.456 in "xxxx"
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
(x @ Expr::FloatConstant(_, _), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for a string expects a string, not a float".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// 123 in "xxxx"
|
|
||||||
(x @ Expr::IntegerConstant(_, _), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for a string expects a string, not a number".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx",
|
|
||||||
// true in "xxxx", false in "xxxx"
|
|
||||||
(x @ Expr::And(_, _), Expr::StringConstant(_, _))
|
|
||||||
| (x @ Expr::Or(_, _), Expr::StringConstant(_, _))
|
|
||||||
| (x @ Expr::In(_, _), Expr::StringConstant(_, _))
|
|
||||||
| (x @ Expr::BoolConstant(_, _), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for a string expects a string, not a boolean".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// [???, ???, ???] in "xxxx"
|
|
||||||
(x @ Expr::Array(_, _), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for a string expects a string, not an array".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// #{...} in "xxxx"
|
|
||||||
(x @ Expr::Map(_, _), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for a string expects a string, not an object map".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// () in "xxxx"
|
|
||||||
(x @ Expr::Unit(_), Expr::StringConstant(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for a string expects a string, not ()".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// "xxx" in #{...}, 'x' in #{...} - OK!
|
|
||||||
(Expr::StringConstant(_, _), Expr::Map(_, _))
|
|
||||||
| (Expr::CharConstant(_, _), Expr::Map(_, _)) => (),
|
|
||||||
|
|
||||||
// 123.456 in #{...}
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
(x @ Expr::FloatConstant(_, _), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for an object map expects a string, not a float".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// 123 in #{...}
|
|
||||||
(x @ Expr::IntegerConstant(_, _), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for an object map expects a string, not a number".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
|
|
||||||
// true in #{...}, false in #{...}
|
|
||||||
(x @ Expr::And(_, _), Expr::Map(_, _))
|
|
||||||
| (x @ Expr::Or(_, _), Expr::Map(_, _))
|
|
||||||
| (x @ Expr::In(_, _), Expr::Map(_, _))
|
|
||||||
| (x @ Expr::BoolConstant(_, _), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for an object map expects a string, not a boolean".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// [???, ???, ???] in #{..}
|
|
||||||
(x @ Expr::Array(_, _), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for an object map expects a string, not an array".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// #{...} in #{..}
|
|
||||||
(x @ Expr::Map(_, _), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for an object map expects a string, not an object map".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
// () in #{...}
|
|
||||||
(x @ Expr::Unit(_), Expr::Map(_, _)) => {
|
|
||||||
return Err(PERR::MalformedInExpr(
|
|
||||||
"'in' expression for an object map expects a string, not ()".into(),
|
|
||||||
)
|
|
||||||
.into_err(x.position()))
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Expr::In(Box::new(BinaryExpr { lhs, rhs }), op_pos))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a binary expression.
|
/// Parse a binary expression.
|
||||||
fn parse_binary_op(
|
fn parse_binary_op(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
@ -1880,9 +1745,21 @@ fn parse_binary_op(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::In => {
|
Token::In => {
|
||||||
let rhs = args.pop().unwrap();
|
// Swap the arguments
|
||||||
let current_lhs = args.pop().unwrap();
|
let current_lhs = args.remove(0);
|
||||||
make_in_expr(current_lhs, rhs, pos)?
|
args.push(current_lhs);
|
||||||
|
|
||||||
|
// Convert into a call to `contains`
|
||||||
|
let hash = calc_fn_hash(empty(), OP_CONTAINS, 2);
|
||||||
|
Expr::FnCall(
|
||||||
|
Box::new(FnCallExpr {
|
||||||
|
hash: FnHash::from_script(hash),
|
||||||
|
args,
|
||||||
|
name: OP_CONTAINS.into(),
|
||||||
|
..op_base
|
||||||
|
}),
|
||||||
|
pos,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Token::Custom(s)
|
Token::Custom(s)
|
||||||
|
@ -38,7 +38,7 @@ fn test_map_indexing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<()>("let y = #{a: 1, b: 2, c: 3}; y.z")?;
|
engine.eval::<()>("let y = #{a: 1, b: 2, c: 3}; y.z")?;
|
||||||
|
|
||||||
assert!(engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "c" in y"#)?);
|
assert!(engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "c" in y"#)?);
|
||||||
assert!(engine.eval::<bool>("let y = #{a: 1, b: 2, c: 3}; 'b' in y")?);
|
assert!(engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "b" in y"#)?);
|
||||||
assert!(!engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "z" in y"#)?);
|
assert!(!engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "z" in y"#)?);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user