diff --git a/src/func/builtin.rs b/src/func/builtin.rs index d97d1a27..cc70a0b6 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -115,6 +115,143 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option() { + #[cfg(not(feature = "unchecked"))] + use crate::packages::arithmetic::arith_basic::INT::functions::*; + + #[cfg(not(feature = "unchecked"))] + match op { + "+" => return Some(impl_op!(INT => add(as_int, as_int))), + "-" => return Some(impl_op!(INT => subtract(as_int, as_int))), + "*" => return Some(impl_op!(INT => multiply(as_int, as_int))), + "/" => return Some(impl_op!(INT => divide(as_int, as_int))), + "%" => return Some(impl_op!(INT => modulo(as_int, as_int))), + "**" => return Some(impl_op!(INT => power(as_int, as_int))), + ">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))), + "<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))), + _ => (), + } + + #[cfg(feature = "unchecked")] + match op { + "+" => return Some(impl_op!(INT => as_int + as_int)), + "-" => return Some(impl_op!(INT => as_int - as_int)), + "*" => return Some(impl_op!(INT => as_int * as_int)), + "/" => return Some(impl_op!(INT => as_int / as_int)), + "%" => return Some(impl_op!(INT => as_int % as_int)), + "**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + ">>" => return Some(impl_op!(INT => as_int >> as_int)), + "<<" => return Some(impl_op!(INT => as_int << as_int)), + _ => (), + } + + return match op { + "==" => Some(impl_op!(INT => as_int == as_int)), + "!=" => Some(impl_op!(INT => as_int != as_int)), + ">" => Some(impl_op!(INT => as_int > as_int)), + ">=" => Some(impl_op!(INT => as_int >= as_int)), + "<" => Some(impl_op!(INT => as_int < as_int)), + "<=" => Some(impl_op!(INT => as_int <= as_int)), + "&" => Some(impl_op!(INT => as_int & as_int)), + "|" => Some(impl_op!(INT => as_int | as_int)), + "^" => Some(impl_op!(INT => as_int ^ as_int)), + ".." => Some(|_, args| { + let x = args[0].as_int().expect(BUILTIN); + let y = args[1].as_int().expect(BUILTIN); + Ok((x..y).into()) + }), + "..=" => Some(|_, args| { + let x = args[0].as_int().expect(BUILTIN); + let y = args[1].as_int().expect(BUILTIN); + Ok((x..=y).into()) + }), + _ => None, + }; + } + + if type1 == TypeId::of::() { + return match op { + "==" => Some(impl_op!(bool => as_bool == as_bool)), + "!=" => Some(impl_op!(bool => as_bool != as_bool)), + ">" => Some(impl_op!(bool => as_bool > as_bool)), + ">=" => Some(impl_op!(bool => as_bool >= as_bool)), + "<" => Some(impl_op!(bool => as_bool < as_bool)), + "<=" => Some(impl_op!(bool => as_bool <= as_bool)), + "&" => Some(impl_op!(bool => as_bool & as_bool)), + "|" => Some(impl_op!(bool => as_bool | as_bool)), + "^" => Some(impl_op!(bool => as_bool ^ as_bool)), + _ => None, + }; + } + + if type1 == TypeId::of::() { + return match op { + "+" => Some(impl_op!(ImmutableString + ImmutableString)), + "-" => Some(impl_op!(ImmutableString - ImmutableString)), + "==" => Some(impl_op!(ImmutableString == ImmutableString)), + "!=" => Some(impl_op!(ImmutableString != ImmutableString)), + ">" => Some(impl_op!(ImmutableString > ImmutableString)), + ">=" => Some(impl_op!(ImmutableString >= ImmutableString)), + "<" => Some(impl_op!(ImmutableString < ImmutableString)), + "<=" => Some(impl_op!(ImmutableString <= ImmutableString)), + OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))), + _ => None, + }; + } + + if type1 == TypeId::of::() { + return match op { + "+" => Some(|_, args| { + let x = args[0].as_char().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); + Ok(format!("{x}{y}").into()) + }), + "==" => Some(impl_op!(char => as_char == as_char)), + "!=" => Some(impl_op!(char => as_char != as_char)), + ">" => Some(impl_op!(char => as_char > as_char)), + ">=" => Some(impl_op!(char => as_char >= as_char)), + "<" => Some(impl_op!(char => as_char < as_char)), + "<=" => Some(impl_op!(char => as_char <= as_char)), + _ => None, + }; + } + + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::Blob; + + return match op { + "+" => Some(|_, args| { + let blob1 = &*args[0].read_lock::().expect(BUILTIN); + let blob2 = &*args[1].read_lock::().expect(BUILTIN); + + Ok(Dynamic::from_blob(if blob2.is_empty() { + blob1.clone() + } else if blob1.is_empty() { + blob2.clone() + } else { + let mut blob = blob1.clone(); + blob.extend(blob2); + blob + })) + }), + "==" => Some(impl_op!(Blob == Blob)), + "!=" => Some(impl_op!(Blob != Blob)), + _ => None, + }; + } + + if type1 == TypeId::of::<()>() { + return match op { + "==" => Some(|_, _| Ok(Dynamic::TRUE)), + "!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + _ => None, + }; + } + } + #[cfg(not(feature = "no_float"))] macro_rules! impl_float { ($x:ty, $xx:ident, $y:ty, $yy:ident) => { @@ -406,141 +543,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option() { - #[cfg(not(feature = "unchecked"))] - use crate::packages::arithmetic::arith_basic::INT::functions::*; - - #[cfg(not(feature = "unchecked"))] - match op { - "+" => return Some(impl_op!(INT => add(as_int, as_int))), - "-" => return Some(impl_op!(INT => subtract(as_int, as_int))), - "*" => return Some(impl_op!(INT => multiply(as_int, as_int))), - "/" => return Some(impl_op!(INT => divide(as_int, as_int))), - "%" => return Some(impl_op!(INT => modulo(as_int, as_int))), - "**" => return Some(impl_op!(INT => power(as_int, as_int))), - ">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))), - "<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))), - _ => (), - } - - #[cfg(feature = "unchecked")] - match op { - "+" => return Some(impl_op!(INT => as_int + as_int)), - "-" => return Some(impl_op!(INT => as_int - as_int)), - "*" => return Some(impl_op!(INT => as_int * as_int)), - "/" => return Some(impl_op!(INT => as_int / as_int)), - "%" => return Some(impl_op!(INT => as_int % as_int)), - "**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - ">>" => return Some(impl_op!(INT => as_int >> as_int)), - "<<" => return Some(impl_op!(INT => as_int << as_int)), - _ => (), - } - - return match op { - "==" => Some(impl_op!(INT => as_int == as_int)), - "!=" => Some(impl_op!(INT => as_int != as_int)), - ">" => Some(impl_op!(INT => as_int > as_int)), - ">=" => Some(impl_op!(INT => as_int >= as_int)), - "<" => Some(impl_op!(INT => as_int < as_int)), - "<=" => Some(impl_op!(INT => as_int <= as_int)), - "&" => Some(impl_op!(INT => as_int & as_int)), - "|" => Some(impl_op!(INT => as_int | as_int)), - "^" => Some(impl_op!(INT => as_int ^ as_int)), - ".." => Some(|_, args| { - let x = args[0].as_int().expect(BUILTIN); - let y = args[1].as_int().expect(BUILTIN); - Ok((x..y).into()) - }), - "..=" => Some(|_, args| { - let x = args[0].as_int().expect(BUILTIN); - let y = args[1].as_int().expect(BUILTIN); - Ok((x..=y).into()) - }), - _ => None, - }; - } - - if type1 == TypeId::of::() { - return match op { - "==" => Some(impl_op!(bool => as_bool == as_bool)), - "!=" => Some(impl_op!(bool => as_bool != as_bool)), - ">" => Some(impl_op!(bool => as_bool > as_bool)), - ">=" => Some(impl_op!(bool => as_bool >= as_bool)), - "<" => Some(impl_op!(bool => as_bool < as_bool)), - "<=" => Some(impl_op!(bool => as_bool <= as_bool)), - "&" => Some(impl_op!(bool => as_bool & as_bool)), - "|" => Some(impl_op!(bool => as_bool | as_bool)), - "^" => Some(impl_op!(bool => as_bool ^ as_bool)), - _ => None, - }; - } - - if type1 == TypeId::of::() { - return match op { - "+" => Some(impl_op!(ImmutableString + ImmutableString)), - "-" => Some(impl_op!(ImmutableString - ImmutableString)), - "==" => Some(impl_op!(ImmutableString == ImmutableString)), - "!=" => Some(impl_op!(ImmutableString != ImmutableString)), - ">" => Some(impl_op!(ImmutableString > ImmutableString)), - ">=" => Some(impl_op!(ImmutableString >= ImmutableString)), - "<" => Some(impl_op!(ImmutableString < ImmutableString)), - "<=" => Some(impl_op!(ImmutableString <= ImmutableString)), - OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))), - _ => None, - }; - } - - if type1 == TypeId::of::() { - return match op { - "+" => Some(|_, args| { - let x = args[0].as_char().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); - Ok(format!("{x}{y}").into()) - }), - "==" => Some(impl_op!(char => as_char == as_char)), - "!=" => Some(impl_op!(char => as_char != as_char)), - ">" => Some(impl_op!(char => as_char > as_char)), - ">=" => Some(impl_op!(char => as_char >= as_char)), - "<" => Some(impl_op!(char => as_char < as_char)), - "<=" => Some(impl_op!(char => as_char <= as_char)), - _ => None, - }; - } - - #[cfg(not(feature = "no_index"))] - if type1 == TypeId::of::() { - use crate::Blob; - - return match op { - "+" => Some(|_, args| { - let blob1 = &*args[0].read_lock::().expect(BUILTIN); - let blob2 = &*args[1].read_lock::().expect(BUILTIN); - - Ok(Dynamic::from_blob(if blob2.is_empty() { - blob1.clone() - } else if blob1.is_empty() { - blob2.clone() - } else { - let mut blob = blob1.clone(); - blob.extend(blob2); - blob - })) - }), - "==" => Some(impl_op!(Blob == Blob)), - "!=" => Some(impl_op!(Blob != Blob)), - _ => None, - }; - } - - if type1 == TypeId::of::<()>() { - return match op { - "==" => Some(|_, _| Ok(Dynamic::TRUE)), - "!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), - _ => None, - }; - } - None } @@ -594,6 +596,98 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio } }; } + // Check for common patterns + if type1 == type2 { + if type1 == TypeId::of::() { + #[cfg(not(feature = "unchecked"))] + use crate::packages::arithmetic::arith_basic::INT::functions::*; + + #[cfg(not(feature = "unchecked"))] + match op { + "+=" => return Some(impl_op!(INT => add(as_int, as_int))), + "-=" => return Some(impl_op!(INT => subtract(as_int, as_int))), + "*=" => return Some(impl_op!(INT => multiply(as_int, as_int))), + "/=" => return Some(impl_op!(INT => divide(as_int, as_int))), + "%=" => return Some(impl_op!(INT => modulo(as_int, as_int))), + "**=" => return Some(impl_op!(INT => power(as_int, as_int))), + ">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))), + "<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))), + _ => (), + } + + #[cfg(feature = "unchecked")] + match op { + "+=" => return Some(impl_op!(INT += as_int)), + "-=" => return Some(impl_op!(INT -= as_int)), + "*=" => return Some(impl_op!(INT *= as_int)), + "/=" => return Some(impl_op!(INT /= as_int)), + "%=" => return Some(impl_op!(INT %= as_int)), + "**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + ">>=" => return Some(impl_op!(INT >>= as_int)), + "<<=" => return Some(impl_op!(INT <<= as_int)), + _ => (), + } + + return match op { + "&=" => Some(impl_op!(INT &= as_int)), + "|=" => Some(impl_op!(INT |= as_int)), + "^=" => Some(impl_op!(INT ^= as_int)), + _ => None, + }; + } + + if type1 == TypeId::of::() { + return match op { + "&=" => Some(impl_op!(bool = x && as_bool)), + "|=" => Some(impl_op!(bool = x || as_bool)), + _ => None, + }; + } + + if type1 == TypeId::of::() { + return match op { + "+=" => Some(|_, args| { + let y = args[1].as_char().expect(BUILTIN); + let x = &mut *args[0].write_lock::().expect(BUILTIN); + Ok((*x = format!("{x}{y}").into()).into()) + }), + _ => None, + }; + } + + if type1 == TypeId::of::() { + return match op { + "+=" => Some(|_, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let x = &mut *first.write_lock::().expect(BUILTIN); + let y = std::mem::take(second[0]).cast::(); + Ok((*x += y).into()) + }), + "-=" => Some(|_, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let x = &mut *first.write_lock::().expect(BUILTIN); + let y = std::mem::take(second[0]).cast::(); + Ok((*x -= y).into()) + }), + _ => None, + }; + } + + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::Blob; + + return match op { + "+=" => Some(|_, args| { + let blob2 = std::mem::take(args[1]).cast::(); + let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); + Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) + }), + _ => None, + }; + } + } + #[cfg(not(feature = "no_float"))] macro_rules! impl_float { ($x:ident, $xx:ident, $y:ty, $yy:ident) => { @@ -752,100 +846,5 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio } } - // No built-in op-assignments for different types. - if type2 != type1 { - return None; - } - - // Beyond here, type1 == type2 - if type1 == TypeId::of::() { - #[cfg(not(feature = "unchecked"))] - use crate::packages::arithmetic::arith_basic::INT::functions::*; - - #[cfg(not(feature = "unchecked"))] - match op { - "+=" => return Some(impl_op!(INT => add(as_int, as_int))), - "-=" => return Some(impl_op!(INT => subtract(as_int, as_int))), - "*=" => return Some(impl_op!(INT => multiply(as_int, as_int))), - "/=" => return Some(impl_op!(INT => divide(as_int, as_int))), - "%=" => return Some(impl_op!(INT => modulo(as_int, as_int))), - "**=" => return Some(impl_op!(INT => power(as_int, as_int))), - ">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))), - "<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))), - _ => (), - } - - #[cfg(feature = "unchecked")] - match op { - "+=" => return Some(impl_op!(INT += as_int)), - "-=" => return Some(impl_op!(INT -= as_int)), - "*=" => return Some(impl_op!(INT *= as_int)), - "/=" => return Some(impl_op!(INT /= as_int)), - "%=" => return Some(impl_op!(INT %= as_int)), - "**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - ">>=" => return Some(impl_op!(INT >>= as_int)), - "<<=" => return Some(impl_op!(INT <<= as_int)), - _ => (), - } - - return match op { - "&=" => Some(impl_op!(INT &= as_int)), - "|=" => Some(impl_op!(INT |= as_int)), - "^=" => Some(impl_op!(INT ^= as_int)), - _ => None, - }; - } - - if type1 == TypeId::of::() { - return match op { - "&=" => Some(impl_op!(bool = x && as_bool)), - "|=" => Some(impl_op!(bool = x || as_bool)), - _ => None, - }; - } - - if type1 == TypeId::of::() { - return match op { - "+=" => Some(|_, args| { - let y = args[1].as_char().expect(BUILTIN); - let x = &mut *args[0].write_lock::().expect(BUILTIN); - Ok((*x = format!("{x}{y}").into()).into()) - }), - _ => None, - }; - } - - if type1 == TypeId::of::() { - return match op { - "+=" => Some(|_, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let x = &mut *first.write_lock::().expect(BUILTIN); - let y = std::mem::take(second[0]).cast::(); - Ok((*x += y).into()) - }), - "-=" => Some(|_, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let x = &mut *first.write_lock::().expect(BUILTIN); - let y = std::mem::take(second[0]).cast::(); - Ok((*x -= y).into()) - }), - _ => None, - }; - } - - #[cfg(not(feature = "no_index"))] - if type1 == TypeId::of::() { - use crate::Blob; - - return match op { - "+=" => Some(|_, args| { - let blob2 = std::mem::take(args[1]).cast::(); - let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) - }), - _ => None, - }; - } - None } diff --git a/src/optimizer.rs b/src/optimizer.rs index d76fc42c..9539b372 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1181,7 +1181,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, &arg_types) => { + _ if x.args.len() == 2 && (cfg!(feature = "fast_ops") || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))]