Allow native overriding implementation of assignment operators.
This commit is contained in:
parent
24a93ef824
commit
854634afa0
45
README.md
45
README.md
@ -395,14 +395,15 @@ are supported.
|
||||
|
||||
### Built-in operators
|
||||
|
||||
| Operator | Supported for type (see [standard types]) |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------- |
|
||||
| `+`, `-`, `*`, `/`, `%`, `~` | `INT`, `FLOAT` (if not [`no_float`]) |
|
||||
| `<<`, `>>`, `^` | `INT` |
|
||||
| `&`, `\|` | `INT`, `bool` |
|
||||
| `&&`, `\|\|` | `bool` |
|
||||
| `==`, `!=` | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
|
||||
| `>`, `>=`, `<`, `<=` | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |
|
||||
| Operators | Assignment operators | Supported for type (see [standard types]) |
|
||||
| ------------------------ | ---------------------------- | ----------------------------------------------------------------------------- |
|
||||
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` |
|
||||
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
|
||||
| `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` |
|
||||
| `&`, `\|`, | `&=`, `|=` | `INT`, `bool` |
|
||||
| `&&`, `\|\|` | | `bool` |
|
||||
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
|
||||
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |
|
||||
|
||||
### Packages
|
||||
|
||||
@ -426,20 +427,20 @@ engine.load_package(package.get()); // load the package manually. 'g
|
||||
|
||||
The follow packages are available:
|
||||
|
||||
| Package | Description | In `CorePackage` | In `StandardPackage` |
|
||||
| ---------------------- | -------------------------------------------------------------------------- | :--------------: | :------------------: |
|
||||
| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) for different numeric types | Yes | Yes |
|
||||
| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes |
|
||||
| `LogicPackage` | Logical and comparison operators (e.g. `==`, `>`) | Yes | Yes |
|
||||
| `BasicStringPackage` | Basic string functions | Yes | Yes |
|
||||
| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes |
|
||||
| `MoreStringPackage` | Additional string functions | No | Yes |
|
||||
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
||||
| `BasicArrayPackage` | Basic [array] functions | No | Yes |
|
||||
| `BasicMapPackage` | Basic [object map] functions | No | Yes |
|
||||
| `EvalPackage` | Disable [`eval`] | No | No |
|
||||
| `CorePackage` | Basic essentials | | |
|
||||
| `StandardPackage` | Standard library | | |
|
||||
| Package | Description | In `CorePackage` | In `StandardPackage` |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------ | :--------------: | :------------------: |
|
||||
| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) for numeric types that are not built in (e.g. `u16`) | Yes | Yes |
|
||||
| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes |
|
||||
| `LogicPackage` | Logical and comparison operators (e.g. `==`, `>`) for numeric types that are not built in (e.g. `u16`) | Yes | Yes |
|
||||
| `BasicStringPackage` | Basic string functions (e.g. `print`, `debug`, `len`) that are not built in | Yes | Yes |
|
||||
| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes |
|
||||
| `MoreStringPackage` | Additional string functions, including converting common types to string | No | Yes |
|
||||
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
||||
| `BasicArrayPackage` | Basic [array] functions (not available under `no_index`) | No | Yes |
|
||||
| `BasicMapPackage` | Basic [object map] functions (not available under `no_object`) | No | Yes |
|
||||
| `EvalPackage` | Disable [`eval`] | No | No |
|
||||
| `CorePackage` | Basic essentials | Yes | Yes |
|
||||
| `StandardPackage` | Standard library | No | Yes |
|
||||
|
||||
Packages typically contain Rust functions that are callable within a Rhai script.
|
||||
All functions registered in a package is loaded under the _global namespace_ (i.e. they're available without module qualifiers).
|
||||
|
@ -34,9 +34,11 @@ Speed enhancements
|
||||
------------------
|
||||
|
||||
* Common operators (e.g. `+`, `>`, `==`) now call into highly efficient built-in implementations for standard types
|
||||
(i.e. `INT`, `FLOAT`, `bool`, `char`, `()` and some `String`) if not overridden by a registered function.
|
||||
(i.e. `INT`, `FLOAT`, `bool`, `char`, `()` and `ImmutableString`) if not overridden by a registered function.
|
||||
This yields a 5-10% speed benefit depending on script operator usage. Scripts running tight loops will see
|
||||
significant speed-up.
|
||||
* Common assignment operators (e.g. `+=`, `%=`) now call into highly efficient built-in implementations for
|
||||
standard types (i.e. `INT`, `FLOAT`, `bool`, `char`, `()` and `ImmutableString`) if not overridden by a registered function.
|
||||
* Implementations of common operators for standard types are removed from the `ArithmeticPackage` and `LogicPackage`
|
||||
(and therefore the `CorePackage`) because they are now always available, even under `Engine::new_raw`.
|
||||
* Operator-assignment statements (e.g. `+=`) are now handled directly and much faster.
|
||||
|
279
src/engine.rs
279
src/engine.rs
@ -3,7 +3,7 @@
|
||||
use crate::any::{Dynamic, Union};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::error::ParseErrorType;
|
||||
use crate::fn_native::{FnCallArgs, Shared};
|
||||
use crate::fn_native::{CallableFunction, FnCallArgs, Shared};
|
||||
use crate::module::Module;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::packages::{CorePackage, Package, PackageLibrary, PackagesCollection, StandardPackage};
|
||||
@ -438,14 +438,18 @@ fn default_print(s: &str) {
|
||||
/// Search for a variable within the scope
|
||||
fn search_scope<'a>(
|
||||
scope: &'a mut Scope,
|
||||
name: &str,
|
||||
modules: Option<(&ModuleRef, u64)>,
|
||||
index: Option<NonZeroUsize>,
|
||||
pos: Position,
|
||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||
state: &mut State,
|
||||
expr: &'a Expr,
|
||||
) -> Result<(&'a mut Dynamic, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||
let ((name, pos), modules, hash_var, index) = match expr {
|
||||
Expr::Variable(x) => x.as_ref(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let index = if state.always_search { None } else { *index };
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
if let Some((modules, hash_var)) = modules {
|
||||
if let Some(modules) = modules.as_ref() {
|
||||
let module = if let Some(index) = modules.index() {
|
||||
scope
|
||||
.get_mut(scope.len() - index.get())
|
||||
@ -461,9 +465,11 @@ fn search_scope<'a>(
|
||||
};
|
||||
|
||||
return Ok((
|
||||
module.get_qualified_var_mut(name, hash_var, pos)?,
|
||||
module.get_qualified_var_mut(name, *hash_var, *pos)?,
|
||||
name,
|
||||
// Module variables are constant
|
||||
ScopeEntryType::Constant,
|
||||
*pos,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -473,11 +479,12 @@ fn search_scope<'a>(
|
||||
} else {
|
||||
scope
|
||||
.get_index(name)
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?
|
||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), *pos)))?
|
||||
.0
|
||||
};
|
||||
|
||||
Ok(scope.get_mut(index))
|
||||
let (val, typ) = scope.get_mut(index);
|
||||
Ok((val, name, typ, *pos))
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
@ -677,11 +684,8 @@ impl Engine {
|
||||
});
|
||||
}
|
||||
|
||||
// See if it is built in. Only consider situations where:
|
||||
// 1) It is not a method call,
|
||||
// 2) the call explicitly specifies `native_only`,
|
||||
// 3) there are two parameters.
|
||||
if !is_ref && native_only && args.len() == 2 {
|
||||
// See if it is built in.
|
||||
if args.len() == 2 {
|
||||
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
||||
Some(v) => return Ok((v, false)),
|
||||
None => (),
|
||||
@ -1147,20 +1151,17 @@ impl Engine {
|
||||
|
||||
match dot_lhs {
|
||||
// id.??? or id[???]
|
||||
Expr::Variable(x) => {
|
||||
let ((name, pos), modules, hash_var, index) = x.as_ref();
|
||||
let index = if state.always_search { None } else { *index };
|
||||
let mod_and_hash = modules.as_ref().map(|m| (m.as_ref(), *hash_var));
|
||||
let (target, typ) = search_scope(scope, &name, mod_and_hash, index, *pos)?;
|
||||
self.inc_operations(state, *pos)?;
|
||||
Expr::Variable(_) => {
|
||||
let (target, name, typ, pos) = search_scope(scope, state, dot_lhs)?;
|
||||
self.inc_operations(state, pos)?;
|
||||
|
||||
// Constants cannot be modified
|
||||
match typ {
|
||||
ScopeEntryType::Module => unreachable!(),
|
||||
ScopeEntryType::Constant if new_val.is_some() => {
|
||||
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
name.clone(),
|
||||
*pos,
|
||||
name.to_string(),
|
||||
pos,
|
||||
)));
|
||||
}
|
||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||
@ -1404,11 +1405,8 @@ impl Engine {
|
||||
Expr::FloatConstant(x) => Ok(x.0.into()),
|
||||
Expr::StringConstant(x) => Ok(x.0.to_string().into()),
|
||||
Expr::CharConstant(x) => Ok(x.0.into()),
|
||||
Expr::Variable(x) => {
|
||||
let ((name, pos), modules, hash_var, index) = x.as_ref();
|
||||
let index = if state.always_search { None } else { *index };
|
||||
let mod_and_hash = modules.as_ref().map(|m| (m.as_ref(), *hash_var));
|
||||
let (val, _) = search_scope(scope, name, mod_and_hash, index, *pos)?;
|
||||
Expr::Variable(_) => {
|
||||
let (val, _, _, _) = search_scope(scope, state, expr)?;
|
||||
Ok(val.clone())
|
||||
}
|
||||
Expr::Property(_) => unreachable!(),
|
||||
@ -1416,89 +1414,106 @@ impl Engine {
|
||||
// Statement block
|
||||
Expr::Stmt(stmt) => self.eval_stmt(scope, state, lib, &stmt.0, level),
|
||||
|
||||
// lhs = rhs
|
||||
// var op= rhs
|
||||
Expr::Assignment(x) if matches!(x.0, Expr::Variable(_)) => {
|
||||
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
||||
let mut rhs_val = self.eval_expr(scope, state, lib, rhs_expr, level)?;
|
||||
let (lhs_ptr, name, typ, pos) = search_scope(scope, state, lhs_expr)?;
|
||||
self.inc_operations(state, pos)?;
|
||||
|
||||
match typ {
|
||||
// Assignment to constant variable
|
||||
ScopeEntryType::Constant => Err(Box::new(
|
||||
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos),
|
||||
)),
|
||||
// Normal assignment
|
||||
ScopeEntryType::Normal if op.is_empty() => {
|
||||
*lhs_ptr = rhs_val;
|
||||
Ok(Default::default())
|
||||
}
|
||||
// Op-assignment - in order of precedence:
|
||||
ScopeEntryType::Normal => {
|
||||
// 1) Native registered overriding function
|
||||
// 2) Built-in implementation
|
||||
// 3) Map to `var = var op rhs`
|
||||
|
||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||
let arg_types = once(lhs_ptr.type_id()).chain(once(rhs_val.type_id()));
|
||||
let hash_fn = calc_fn_hash(empty(), op, 2, arg_types);
|
||||
|
||||
if let Some(CallableFunction::Method(func)) = self
|
||||
.global_module
|
||||
.get_fn(hash_fn)
|
||||
.or_else(|| self.packages.get_fn(hash_fn))
|
||||
{
|
||||
// Overriding exact implementation
|
||||
func(&mut [lhs_ptr, &mut rhs_val])?;
|
||||
} else if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() {
|
||||
// Not built in, map to `var = var op rhs`
|
||||
let op = &op[..op.len() - 1]; // extract operator without =
|
||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||
let args = &mut [&mut lhs_ptr.clone(), &mut rhs_val];
|
||||
|
||||
// Set variable value
|
||||
*lhs_ptr = self
|
||||
.exec_fn_call(
|
||||
state, lib, op, true, hash, args, false, None, *op_pos, level,
|
||||
)
|
||||
.map(|(v, _)| v)?;
|
||||
}
|
||||
Ok(Default::default())
|
||||
}
|
||||
// A module cannot be assigned to
|
||||
ScopeEntryType::Module => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// lhs op= rhs
|
||||
Expr::Assignment(x) => {
|
||||
let lhs_expr = &x.0;
|
||||
let op = x.1.as_ref();
|
||||
let op_pos = x.3;
|
||||
let mut rhs_val = self.eval_expr(scope, state, lib, &x.2, level)?;
|
||||
let (lhs_expr, op, rhs_expr, op_pos) = x.as_ref();
|
||||
let mut rhs_val = self.eval_expr(scope, state, lib, rhs_expr, level)?;
|
||||
|
||||
// name op= rhs
|
||||
if let Expr::Variable(x) = &x.0 {
|
||||
let ((name, pos), modules, hash_var, index) = x.as_ref();
|
||||
let index = if state.always_search { None } else { *index };
|
||||
let mod_and_hash = modules.as_ref().map(|m| (m.as_ref(), *hash_var));
|
||||
let (lhs_ptr, typ) = search_scope(scope, name, mod_and_hash, index, *pos)?;
|
||||
self.inc_operations(state, *pos)?;
|
||||
|
||||
match typ {
|
||||
ScopeEntryType::Constant => Err(Box::new(
|
||||
EvalAltResult::ErrorAssignmentToConstant(name.clone(), *pos),
|
||||
)),
|
||||
ScopeEntryType::Normal if !op.is_empty() => {
|
||||
// Complex op-assignment
|
||||
if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() {
|
||||
// Not built in, do function call
|
||||
let mut lhs_val = lhs_ptr.clone();
|
||||
let args = &mut [&mut lhs_val, &mut rhs_val];
|
||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||
*lhs_ptr = self
|
||||
.exec_fn_call(
|
||||
state, lib, op, true, hash, args, false, None, op_pos,
|
||||
level,
|
||||
)
|
||||
.map(|(v, _)| v)?;
|
||||
}
|
||||
Ok(Default::default())
|
||||
}
|
||||
ScopeEntryType::Normal => {
|
||||
*lhs_ptr = rhs_val;
|
||||
Ok(Default::default())
|
||||
}
|
||||
// End variable cannot be a module
|
||||
ScopeEntryType::Module => unreachable!(),
|
||||
}
|
||||
let new_val = Some(if op.is_empty() {
|
||||
// Normal assignment
|
||||
rhs_val
|
||||
} else {
|
||||
let new_val = Some(if op.is_empty() {
|
||||
rhs_val
|
||||
} else {
|
||||
// Complex op-assignment - always do function call
|
||||
let args = &mut [
|
||||
&mut self.eval_expr(scope, state, lib, lhs_expr, level)?,
|
||||
&mut rhs_val,
|
||||
];
|
||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||
self.exec_fn_call(
|
||||
state, lib, op, true, hash, args, false, None, op_pos, level,
|
||||
)
|
||||
.map(|(v, _)| v)?
|
||||
});
|
||||
// Op-assignment - always map to `lhs = lhs op rhs`
|
||||
let op = &op[..op.len() - 1]; // extract operator without =
|
||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||
let args = &mut [
|
||||
&mut self.eval_expr(scope, state, lib, lhs_expr, level)?,
|
||||
&mut rhs_val,
|
||||
];
|
||||
self.exec_fn_call(
|
||||
state, lib, op, true, hash, args, false, None, *op_pos, level,
|
||||
)
|
||||
.map(|(v, _)| v)?
|
||||
});
|
||||
|
||||
match &x.0 {
|
||||
// name op= rhs
|
||||
Expr::Variable(_) => unreachable!(),
|
||||
// idx_lhs[idx_expr] op= rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x) => self.eval_dot_index_chain(
|
||||
scope, state, lib, &x.0, &x.1, true, x.2, level, new_val,
|
||||
),
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x) => self.eval_dot_index_chain(
|
||||
scope, state, lib, &x.0, &x.1, false, op_pos, level, new_val,
|
||||
),
|
||||
// Error assignment to constant
|
||||
expr if expr.is_constant() => {
|
||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
expr.get_constant_str(),
|
||||
expr.position(),
|
||||
)))
|
||||
}
|
||||
// Syntax error
|
||||
expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS(
|
||||
match lhs_expr {
|
||||
// name op= rhs
|
||||
Expr::Variable(_) => unreachable!(),
|
||||
// idx_lhs[idx_expr] op= rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x) => self.eval_dot_index_chain(
|
||||
scope, state, lib, &x.0, &x.1, true, x.2, level, new_val,
|
||||
),
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x) => self.eval_dot_index_chain(
|
||||
scope, state, lib, &x.0, &x.1, false, *op_pos, level, new_val,
|
||||
),
|
||||
// Error assignment to constant
|
||||
expr if expr.is_constant() => {
|
||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
expr.get_constant_str(),
|
||||
expr.position(),
|
||||
))),
|
||||
)))
|
||||
}
|
||||
// Syntax error
|
||||
expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS(
|
||||
expr.position(),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2132,34 +2147,34 @@ fn run_builtin_op_assignment(
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
"+" => return Ok(Some(*x = add(*x, y)?)),
|
||||
"-" => return Ok(Some(*x = sub(*x, y)?)),
|
||||
"*" => return Ok(Some(*x = mul(*x, y)?)),
|
||||
"/" => return Ok(Some(*x = div(*x, y)?)),
|
||||
"%" => return Ok(Some(*x = modulo(*x, y)?)),
|
||||
"~" => return Ok(Some(*x = pow_i_i(*x, y)?)),
|
||||
">>" => return Ok(Some(*x = shr(*x, y)?)),
|
||||
"<<" => return Ok(Some(*x = shl(*x, y)?)),
|
||||
"+=" => return Ok(Some(*x = add(*x, y)?)),
|
||||
"-=" => return Ok(Some(*x = sub(*x, y)?)),
|
||||
"*=" => return Ok(Some(*x = mul(*x, y)?)),
|
||||
"/=" => return Ok(Some(*x = div(*x, y)?)),
|
||||
"%=" => return Ok(Some(*x = modulo(*x, y)?)),
|
||||
"~=" => return Ok(Some(*x = pow_i_i(*x, y)?)),
|
||||
">>=" => return Ok(Some(*x = shr(*x, y)?)),
|
||||
"<<=" => return Ok(Some(*x = shl(*x, y)?)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
match op {
|
||||
"+" => return Ok(Some(*x += y)),
|
||||
"-" => return Ok(Some(*x -= y)),
|
||||
"*" => return Ok(Some(*x *= y)),
|
||||
"/" => return Ok(Some(*x /= y)),
|
||||
"%" => return Ok(Some(*x %= y)),
|
||||
"~" => return Ok(Some(*x = pow_i_i_u(*x, y)?)),
|
||||
">>" => return Ok(Some(*x = shr_u(*x, y)?)),
|
||||
"<<" => return Ok(Some(*x = shl_u(*x, y)?)),
|
||||
"+=" => return Ok(Some(*x += y)),
|
||||
"-=" => return Ok(Some(*x -= y)),
|
||||
"*=" => return Ok(Some(*x *= y)),
|
||||
"/=" => return Ok(Some(*x /= y)),
|
||||
"%=" => return Ok(Some(*x %= y)),
|
||||
"~=" => return Ok(Some(*x = pow_i_i_u(*x, y)?)),
|
||||
">>=" => return Ok(Some(*x = shr_u(*x, y)?)),
|
||||
"<<=" => return Ok(Some(*x = shl_u(*x, y)?)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match op {
|
||||
"&" => return Ok(Some(*x &= y)),
|
||||
"|" => return Ok(Some(*x |= y)),
|
||||
"^" => return Ok(Some(*x ^= y)),
|
||||
"&=" => return Ok(Some(*x &= y)),
|
||||
"|=" => return Ok(Some(*x |= y)),
|
||||
"^=" => return Ok(Some(*x ^= y)),
|
||||
_ => (),
|
||||
}
|
||||
} else if args_type == TypeId::of::<bool>() {
|
||||
@ -2167,8 +2182,8 @@ fn run_builtin_op_assignment(
|
||||
let y = y.downcast_ref::<bool>().unwrap().clone();
|
||||
|
||||
match op {
|
||||
"&" => return Ok(Some(*x = *x && y)),
|
||||
"|" => return Ok(Some(*x = *x || y)),
|
||||
"&=" => return Ok(Some(*x = *x && y)),
|
||||
"|=" => return Ok(Some(*x = *x || y)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@ -2180,12 +2195,12 @@ fn run_builtin_op_assignment(
|
||||
let y = y.downcast_ref::<FLOAT>().unwrap().clone();
|
||||
|
||||
match op {
|
||||
"+" => return Ok(Some(*x += y)),
|
||||
"-" => return Ok(Some(*x -= y)),
|
||||
"*" => return Ok(Some(*x *= y)),
|
||||
"/" => return Ok(Some(*x /= y)),
|
||||
"%" => return Ok(Some(*x %= y)),
|
||||
"~" => return Ok(Some(*x = pow_f_f(*x, y)?)),
|
||||
"+=" => return Ok(Some(*x += y)),
|
||||
"-=" => return Ok(Some(*x -= y)),
|
||||
"*=" => return Ok(Some(*x *= y)),
|
||||
"/=" => return Ok(Some(*x /= y)),
|
||||
"%=" => return Ok(Some(*x %= y)),
|
||||
"~=" => return Ok(Some(*x = pow_f_f(*x, y)?)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -1444,17 +1444,18 @@ fn parse_op_assignment_stmt<'a>(
|
||||
|
||||
let op = match token {
|
||||
Token::Equals => "".into(),
|
||||
Token::PlusAssign => Token::Plus.syntax(),
|
||||
Token::MinusAssign => Token::Minus.syntax(),
|
||||
Token::MultiplyAssign => Token::Multiply.syntax(),
|
||||
Token::DivideAssign => Token::Divide.syntax(),
|
||||
Token::LeftShiftAssign => Token::LeftShift.syntax(),
|
||||
Token::RightShiftAssign => Token::RightShift.syntax(),
|
||||
Token::ModuloAssign => Token::Modulo.syntax(),
|
||||
Token::PowerOfAssign => Token::PowerOf.syntax(),
|
||||
Token::AndAssign => Token::Ampersand.syntax(),
|
||||
Token::OrAssign => Token::Pipe.syntax(),
|
||||
Token::XOrAssign => Token::XOr.syntax(),
|
||||
|
||||
Token::PlusAssign
|
||||
| Token::MinusAssign
|
||||
| Token::MultiplyAssign
|
||||
| Token::DivideAssign
|
||||
| Token::LeftShiftAssign
|
||||
| Token::RightShiftAssign
|
||||
| Token::ModuloAssign
|
||||
| Token::PowerOfAssign
|
||||
| Token::AndAssign
|
||||
| Token::OrAssign
|
||||
| Token::XOrAssign => token.syntax(),
|
||||
|
||||
_ => return Ok(lhs),
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user