1#![allow(clippy::float_cmp)]
4
5use super::call::FnCallArgs;
6use super::native::FnBuiltin;
7#[allow(clippy::enum_glob_use)]
8use crate::tokenizer::{Token, Token::*};
9use crate::types::dynamic::Union;
10use crate::{
11 Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult,
12 SmartString, INT,
13};
14use std::any::TypeId;
15#[cfg(feature = "no_std")]
16use std::prelude::v1::*;
17
18#[cfg(not(feature = "no_float"))]
19use crate::FLOAT;
20
21#[cfg(not(feature = "no_float"))]
22#[cfg(feature = "no_std")]
23use num_traits::Float;
24
25#[cfg(feature = "decimal")]
26use rust_decimal::Decimal;
27
28const CHECKED_BUILD: bool = cfg!(not(feature = "unchecked"));
30
31#[inline(always)]
33#[allow(clippy::unnecessary_wraps)]
34fn const_true_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiResult {
35 Ok(Dynamic::TRUE)
36}
37#[inline(always)]
39#[allow(clippy::unnecessary_wraps)]
40fn const_false_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiResult {
41 Ok(Dynamic::FALSE)
42}
43#[inline(always)]
45fn is_numeric(typ: TypeId) -> bool {
46 if typ == TypeId::of::<INT>() {
47 return true;
48 }
49
50 #[cfg(not(feature = "no_float"))]
51 if typ == TypeId::of::<f32>() || typ == TypeId::of::<f64>() {
52 return true;
53 }
54
55 #[cfg(feature = "decimal")]
56 if typ == TypeId::of::<Decimal>() {
57 return true;
58 }
59
60 #[cfg(not(feature = "only_i32"))]
61 #[cfg(not(feature = "only_i64"))]
62 if typ == TypeId::of::<u8>()
63 || typ == TypeId::of::<u16>()
64 || typ == TypeId::of::<u32>()
65 || typ == TypeId::of::<u64>()
66 || typ == TypeId::of::<i8>()
67 || typ == TypeId::of::<i16>()
68 || typ == TypeId::of::<i32>()
69 || typ == TypeId::of::<i64>()
70 {
71 return true;
72 }
73
74 #[cfg(not(feature = "only_i32"))]
75 #[cfg(not(feature = "only_i64"))]
76 #[cfg(not(target_family = "wasm"))]
77 if typ == TypeId::of::<u128>() || typ == TypeId::of::<i128>() {
78 return true;
79 }
80
81 false
82}
83
84#[must_use]
88pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
89 macro_rules! impl_op {
90 ($xx:ident $op:tt $yy:ident) => { Some((|_, args| {
91 let x = &*args[0].read_lock::<$xx>().unwrap();
92 let y = &*args[1].read_lock::<$yy>().unwrap();
93 Ok((x $op y).into())
94 }, false)) };
95 ($xx:ident . $func:ident ( $yy:ty )) => { Some((|_, args| {
96 let x = &*args[0].read_lock::<$xx>().unwrap();
97 let y = &*args[1].read_lock::<$yy>().unwrap();
98 Ok(x.$func(y).into())
99 }, false)) };
100 ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { Some((|_, args| {
101 let x = &*args[0].read_lock::<$xx>().unwrap();
102 let y = &*args[1].read_lock::<$yy>().unwrap();
103 Ok(x.$func(y.$yyy()).into())
104 }, false)) };
105 ($func:ident ( $op:tt )) => { Some((|_, args| {
106 let (x, y) = $func(args);
107 Ok((x $op y).into())
108 }, false)) };
109 ($base:ty => $xx:ident $op:tt $yy:ident) => { Some((|_, args| {
110 let x = args[0].$xx().unwrap() as $base;
111 let y = args[1].$yy().unwrap() as $base;
112 Ok((x $op y).into())
113 }, false)) };
114 ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { Some((|_, args| {
115 let x = args[0].$xx().unwrap() as $base;
116 let y = args[1].$yy().unwrap() as $base;
117 Ok(x.$func(y as $yyy).into())
118 }, false)) };
119 ($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
120 let x = args[0].$xx().unwrap() as $base;
121 let y = args[1].$yy().unwrap() as $base;
122 Ok($func(x, y).into())
123 }, false)) };
124 ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
125 let x = args[0].$xx().unwrap() as $base;
126 let y = args[1].$yy().unwrap() as $base;
127 $func(x, y).map(Into::into)
128 }, false)) };
129 (from $base:ty => $xx:ident $op:tt $yy:ident) => { Some((|_, args| {
130 let x = <$base>::from(args[0].$xx().unwrap());
131 let y = <$base>::from(args[1].$yy().unwrap());
132 Ok((x $op y).into())
133 }, false)) };
134 (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { Some((|_, args| {
135 let x = <$base>::from(args[0].$xx().unwrap());
136 let y = <$base>::from(args[1].$yy().unwrap());
137 Ok(x.$func(y).into())
138 }, false)) };
139 (from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
140 let x = <$base>::from(args[0].$xx().unwrap());
141 let y = <$base>::from(args[1].$yy().unwrap());
142 Ok($func(x, y).into())
143 }, false)) };
144 (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
145 let x = <$base>::from(args[0].$xx().unwrap());
146 let y = <$base>::from(args[1].$yy().unwrap());
147 $func(x, y).map(Into::into)
148 }, false)) };
149 }
150
151 #[cfg(not(feature = "no_float"))]
152 macro_rules! impl_float {
153 ($xx:ident, $yy:ident) => {
154 return match op {
155 Plus => impl_op!(FLOAT => $xx + $yy),
156 Minus => impl_op!(FLOAT => $xx - $yy),
157 Multiply => impl_op!(FLOAT => $xx * $yy),
158 Divide => impl_op!(FLOAT => $xx / $yy),
159 Modulo => impl_op!(FLOAT => $xx % $yy),
160 PowerOf => impl_op!(FLOAT => $xx.powf($yy as FLOAT)),
161
162 #[cfg(feature = "unchecked")]
163 EqualsTo => impl_op!(FLOAT => $xx == $yy),
164 #[cfg(not(feature = "unchecked"))]
165 EqualsTo => Some((|_, args| {
166 let x = args[0].$xx().unwrap() as FLOAT;
167 let y = args[1].$yy().unwrap() as FLOAT;
168 let max = if x * y == 0.0 { 1.0 } else { x.abs().max(y.abs()) };
169 if max == 0.0 { return Ok(Dynamic::TRUE); }
170 Ok(((x - y).abs()/max <= FLOAT::EPSILON).into())
171 }, false)),
172
173 #[cfg(feature = "unchecked")]
174 NotEqualsTo => impl_op!(FLOAT => $xx != $yy),
175 #[cfg(not(feature = "unchecked"))]
176 NotEqualsTo => Some((|_, args| {
177 let x = args[0].$xx().unwrap() as FLOAT;
178 let y = args[1].$yy().unwrap() as FLOAT;
179 let max = if x * y == 0.0 { 1.0 } else { x.abs().max(y.abs()) };
180 if max == 0.0 { return Ok(Dynamic::FALSE); }
181 Ok(((x - y).abs()/max > FLOAT::EPSILON).into())
182 }, false)),
183
184 #[cfg(feature = "unchecked")]
185 GreaterThan => impl_op!(FLOAT => $xx > $yy),
186 #[cfg(not(feature = "unchecked"))]
187 GreaterThan => Some((|_, args| {
188 let x = args[0].$xx().unwrap() as FLOAT;
189 let y = args[1].$yy().unwrap() as FLOAT;
190 let max = if x * y == 0.0 { 1.0 } else { x.abs().max(y.abs()) };
191 if max == 0.0 { return Ok(Dynamic::FALSE); }
192 Ok(((x - y)/max > FLOAT::EPSILON).into())
193 }, false)),
194
195 #[cfg(feature = "unchecked")]
196 GreaterThanEqualsTo => impl_op!(FLOAT => $xx >= $yy),
197 #[cfg(not(feature = "unchecked"))]
198 GreaterThanEqualsTo => Some((|_, args| {
199 let x = args[0].$xx().unwrap() as FLOAT;
200 let y = args[1].$yy().unwrap() as FLOAT;
201 let max = if x * y == 0.0 { 1.0 } else { x.abs().max(y.abs()) };
202 if max == 0.0 { return Ok(Dynamic::TRUE); }
203 Ok(((x - y)/max > -FLOAT::EPSILON).into())
204 }, false)),
205
206 #[cfg(feature = "unchecked")]
207 LessThan => impl_op!(FLOAT => $xx < $yy),
208 #[cfg(not(feature = "unchecked"))]
209 LessThan => Some((|_, args| {
210 let x = args[0].$xx().unwrap() as FLOAT;
211 let y = args[1].$yy().unwrap() as FLOAT;
212 let max = if x * y == 0.0 { 1.0 } else { x.abs().max(y.abs()) };
213 if max == 0.0 { return Ok(Dynamic::FALSE); }
214 Ok(((y - x)/max > FLOAT::EPSILON).into())
215 }, false)),
216
217 #[cfg(feature = "unchecked")]
218 LessThanEqualsTo => impl_op!(FLOAT => $xx <= $yy),
219 #[cfg(not(feature = "unchecked"))]
220 LessThanEqualsTo => Some((|_, args| {
221 let x = args[0].$xx().unwrap() as FLOAT;
222 let y = args[1].$yy().unwrap() as FLOAT;
223 let max = if x * y == 0.0 { 1.0 } else { x.abs().max(y.abs()) };
224 if max == 0.0 { return Ok(Dynamic::TRUE); }
225 Ok(((y - x)/max > -FLOAT::EPSILON).into())
226 }, false)),
227
228 _ => None,
229 }
230 };
231 }
232
233 #[cfg(feature = "decimal")]
234 macro_rules! impl_decimal {
235 ($xx:ident, $yy:ident) => {
236 {
237 #[cfg(not(feature = "unchecked"))]
238 #[allow(clippy::wildcard_imports)]
239 use crate::packages::arithmetic::decimal_functions::builtin::*;
240
241 #[cfg(not(feature = "unchecked"))]
242 match op {
243 Plus => return impl_op!(from Decimal => add($xx, $yy)),
244 Minus => return impl_op!(from Decimal => subtract($xx, $yy)),
245 Multiply => return impl_op!(from Decimal => multiply($xx, $yy)),
246 Divide => return impl_op!(from Decimal => divide($xx, $yy)),
247 Modulo => return impl_op!(from Decimal => modulo($xx, $yy)),
248 PowerOf => return impl_op!(from Decimal => power($xx, $yy)),
249 _ => ()
250 }
251
252 #[cfg(feature = "unchecked")]
253 use rust_decimal::MathematicalOps;
254
255 #[cfg(feature = "unchecked")]
256 match op {
257 Plus => return impl_op!(from Decimal => $xx + $yy),
258 Minus => return impl_op!(from Decimal => $xx - $yy),
259 Multiply => return impl_op!(from Decimal => $xx * $yy),
260 Divide => return impl_op!(from Decimal => $xx / $yy),
261 Modulo => return impl_op!(from Decimal => $xx % $yy),
262 PowerOf => return impl_op!(from Decimal => $xx.powd($yy)),
263 _ => ()
264 }
265
266 return match op {
267 EqualsTo => impl_op!(from Decimal => $xx == $yy),
268 NotEqualsTo => impl_op!(from Decimal => $xx != $yy),
269 GreaterThan => impl_op!(from Decimal => $xx > $yy),
270 GreaterThanEqualsTo => impl_op!(from Decimal => $xx >= $yy),
271 LessThan => impl_op!(from Decimal => $xx < $yy),
272 LessThanEqualsTo => impl_op!(from Decimal => $xx <= $yy),
273 _ => None
274 };
275 }
276 };
277 }
278
279 match (&x.0, &y.0, op) {
281 (Union::Int(..), Union::Int(..), _) => {
282 #[cfg(not(feature = "unchecked"))]
283 #[allow(clippy::wildcard_imports)]
284 use crate::packages::arithmetic::arith_basic::INT::functions::*;
285
286 #[cfg(not(feature = "unchecked"))]
287 match op {
288 Plus => return impl_op!(INT => add(as_int, as_int)),
289 Minus => return impl_op!(INT => subtract(as_int, as_int)),
290 Multiply => return impl_op!(INT => multiply(as_int, as_int)),
291 Divide => return impl_op!(INT => divide(as_int, as_int)),
292 Modulo => return impl_op!(INT => modulo(as_int, as_int)),
293 PowerOf => return impl_op!(INT => power(as_int, as_int)),
294 RightShift => return impl_op!(INT => Ok(shift_right(as_int, as_int))),
295 LeftShift => return impl_op!(INT => Ok(shift_left(as_int, as_int))),
296 Ampersand => return impl_op!(INT => Ok(binary_and(as_int, as_int))),
297 Pipe => return impl_op!(INT => Ok(binary_or(as_int, as_int))),
298 XOr => return impl_op!(INT => Ok(binary_xor(as_int, as_int))),
299 _ => (),
300 }
301
302 #[cfg(feature = "unchecked")]
303 match op {
304 Plus => return impl_op!(INT => as_int + as_int),
305 Minus => return impl_op!(INT => as_int - as_int),
306 Multiply => return impl_op!(INT => as_int * as_int),
307 Divide => return impl_op!(INT => as_int / as_int),
308 Modulo => return impl_op!(INT => as_int % as_int),
309 PowerOf => return impl_op!(INT => as_int.pow(as_int as u32)),
310 RightShift => {
311 return Some((
312 |_, args| {
313 let x = args[0].as_int().unwrap();
314 let y = args[1].as_int().unwrap();
315 Ok((if y < 0 { x << -y } else { x >> y }).into())
316 },
317 false,
318 ))
319 }
320 LeftShift => {
321 return Some((
322 |_, args| {
323 let x = args[0].as_int().unwrap();
324 let y = args[1].as_int().unwrap();
325 Ok((if y < 0 { x >> -y } else { x << y }).into())
326 },
327 false,
328 ))
329 }
330 Ampersand => return impl_op!(INT => as_int & as_int),
331 Pipe => return impl_op!(INT => as_int | as_int),
332 XOr => return impl_op!(INT => as_int ^ as_int),
333 _ => (),
334 }
335
336 return match op {
337 EqualsTo => impl_op!(INT => as_int == as_int),
338 NotEqualsTo => impl_op!(INT => as_int != as_int),
339 GreaterThan => impl_op!(INT => as_int > as_int),
340 GreaterThanEqualsTo => impl_op!(INT => as_int >= as_int),
341 LessThan => impl_op!(INT => as_int < as_int),
342 LessThanEqualsTo => impl_op!(INT => as_int <= as_int),
343 Ampersand => impl_op!(INT => as_int & as_int),
344 Pipe => impl_op!(INT => as_int | as_int),
345 XOr => impl_op!(INT => as_int ^ as_int),
346 ExclusiveRange => impl_op!(INT => as_int .. as_int),
347 InclusiveRange => impl_op!(INT => as_int ..= as_int),
348 _ => None,
349 };
350 }
351
352 (Union::Bool(..), Union::Bool(..), _) => {
353 return match op {
354 EqualsTo => impl_op!(bool => as_bool == as_bool),
355 NotEqualsTo => impl_op!(bool => as_bool != as_bool),
356 GreaterThan => impl_op!(bool => as_bool > as_bool),
357 GreaterThanEqualsTo => impl_op!(bool => as_bool >= as_bool),
358 LessThan => impl_op!(bool => as_bool < as_bool),
359 LessThanEqualsTo => impl_op!(bool => as_bool <= as_bool),
360 Ampersand => impl_op!(bool => as_bool & as_bool),
361 Pipe => impl_op!(bool => as_bool | as_bool),
362 XOr => impl_op!(bool => as_bool ^ as_bool),
363 _ => None,
364 };
365 }
366
367 (Union::Str(..), Union::Str(..), _) => {
368 return match op {
369 Plus => Some((
370 |_ctx, args| {
371 let s1 = &*args[0].as_immutable_string_ref().unwrap();
372 let s2 = &*args[1].as_immutable_string_ref().unwrap();
373
374 #[cfg(not(feature = "unchecked"))]
375 _ctx.unwrap()
376 .engine()
377 .throw_on_size((0, 0, s1.len() + s2.len()))?;
378
379 Ok((s1 + s2).into())
380 },
381 CHECKED_BUILD,
382 )),
383 Minus => impl_op!(ImmutableString - ImmutableString),
384 EqualsTo => impl_op!(ImmutableString == ImmutableString),
385 NotEqualsTo => impl_op!(ImmutableString != ImmutableString),
386 GreaterThan => impl_op!(ImmutableString > ImmutableString),
387 GreaterThanEqualsTo => impl_op!(ImmutableString >= ImmutableString),
388 LessThan => impl_op!(ImmutableString < ImmutableString),
389 LessThanEqualsTo => impl_op!(ImmutableString <= ImmutableString),
390 _ => None,
391 };
392 }
393
394 (Union::Char(..), Union::Char(..), _) => {
395 return match op {
396 Plus => Some((
397 |_ctx, args| {
398 let x = args[0].as_char().unwrap();
399 let y = args[1].as_char().unwrap();
400
401 let mut result = SmartString::new_const();
402 result.push(x);
403 result.push(y);
404
405 #[cfg(not(feature = "unchecked"))]
406 _ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
407
408 Ok(result.into())
409 },
410 CHECKED_BUILD,
411 )),
412 EqualsTo => impl_op!(char => as_char == as_char),
413 NotEqualsTo => impl_op!(char => as_char != as_char),
414 GreaterThan => impl_op!(char => as_char > as_char),
415 GreaterThanEqualsTo => impl_op!(char => as_char >= as_char),
416 LessThan => impl_op!(char => as_char < as_char),
417 LessThanEqualsTo => impl_op!(char => as_char <= as_char),
418 _ => None,
419 };
420 }
421
422 #[cfg(not(feature = "no_index"))]
423 (Union::Blob(..), Union::Blob(..), _) => {
424 use crate::Blob;
425
426 return match op {
427 Plus => Some((
428 |_ctx, args| {
429 let b2 = &*args[1].as_blob_ref().unwrap();
430 if b2.is_empty() {
431 return Ok(args[0].flatten_clone());
432 }
433 let b1 = &*args[0].as_blob_ref().unwrap();
434 if b1.is_empty() {
435 return Ok(args[1].flatten_clone());
436 }
437
438 #[cfg(not(feature = "unchecked"))]
439 _ctx.unwrap()
440 .engine()
441 .throw_on_size((b1.len() + b2.len(), 0, 0))?;
442
443 let mut blob = b1.clone();
444 blob.extend(b2);
445 Ok(Dynamic::from_blob(blob))
446 },
447 CHECKED_BUILD,
448 )),
449 EqualsTo => impl_op!(Blob == Blob),
450 NotEqualsTo => impl_op!(Blob != Blob),
451 _ => None,
452 };
453 }
454
455 (Union::Unit(..), Union::Unit(..), _) => {
456 return match op {
457 EqualsTo => Some((const_true_fn, false)),
458 NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
459 Some((const_false_fn, false))
460 }
461 _ => None,
462 };
463 }
464
465 #[cfg(not(feature = "no_float"))]
466 (Union::Float(..), Union::Float(..), _) => {
467 impl_float!(as_float, as_float)
468 }
469
470 #[cfg(not(feature = "no_float"))]
471 (Union::Float(..), Union::Int(..), _) => {
472 impl_float!(as_float, as_int)
473 }
474
475 #[cfg(not(feature = "no_float"))]
476 (Union::Int(..), Union::Float(..), _) => {
477 impl_float!(as_int, as_float)
478 }
479
480 #[cfg(feature = "decimal")]
481 (Union::Decimal(..), Union::Decimal(..), _) => {
482 impl_decimal!(as_decimal, as_decimal)
483 }
484 #[cfg(feature = "decimal")]
485 (Union::Decimal(..), Union::Int(..), _) => {
486 impl_decimal!(as_decimal, as_int)
487 }
488 #[cfg(feature = "decimal")]
489 (Union::Int(..), Union::Decimal(..), _) => {
490 impl_decimal!(as_int, as_decimal)
491 }
492
493 (Union::Int(..), Union::Unit(..), ExclusiveRange) => {
495 return Some((
496 |_ctx, args| Ok((args[0].as_int().unwrap()..INT::MAX).into()),
497 false,
498 ))
499 }
500 (Union::Unit(..), Union::Int(..), ExclusiveRange) => {
501 return Some((
502 |_ctx, args| Ok((0..args[1].as_int().unwrap()).into()),
503 false,
504 ))
505 }
506 (Union::Int(..), Union::Unit(..), InclusiveRange) => {
507 return Some((
508 |_ctx, args| Ok((args[0].as_int().unwrap()..=INT::MAX).into()),
509 false,
510 ))
511 }
512 (Union::Unit(..), Union::Int(..), InclusiveRange) => {
513 return Some((
514 |_ctx, args| Ok((0..=args[1].as_int().unwrap()).into()),
515 false,
516 ))
517 }
518
519 (Union::Char(..), Union::Str(..), _) => {
521 fn get_s1s2(args: &FnCallArgs) -> ([Option<char>; 2], [Option<char>; 2]) {
522 let x = args[0].as_char().unwrap();
523 let y = &*args[1].as_immutable_string_ref().unwrap();
524 let s1 = [Some(x), None];
525 let mut y = y.chars();
526 let s2 = [y.next(), y.next()];
527 (s1, s2)
528 }
529
530 return match op {
531 Plus => Some((
532 |_ctx, args| {
533 let x = args[0].as_char().unwrap();
534 let y = &*args[1].as_immutable_string_ref().unwrap();
535
536 let mut result = SmartString::new_const();
537 result.push(x);
538 result.push_str(y);
539
540 #[cfg(not(feature = "unchecked"))]
541 _ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
542
543 Ok(result.into())
544 },
545 CHECKED_BUILD,
546 )),
547 EqualsTo => impl_op!(get_s1s2(==)),
548 NotEqualsTo => impl_op!(get_s1s2(!=)),
549 GreaterThan => impl_op!(get_s1s2(>)),
550 GreaterThanEqualsTo => impl_op!(get_s1s2(>=)),
551 LessThan => impl_op!(get_s1s2(<)),
552 LessThanEqualsTo => impl_op!(get_s1s2(<=)),
553 _ => None,
554 };
555 }
556 (Union::Str(..), Union::Char(..), _) => {
558 fn get_s1s2(args: &FnCallArgs) -> ([Option<char>; 2], [Option<char>; 2]) {
559 let x = &*args[0].as_immutable_string_ref().unwrap();
560 let y = args[1].as_char().unwrap();
561 let mut x = x.chars();
562 let s1 = [x.next(), x.next()];
563 let s2 = [Some(y), None];
564 (s1, s2)
565 }
566
567 return match op {
568 Plus => Some((
569 |_ctx, args| {
570 let x = &*args[0].as_immutable_string_ref().unwrap();
571 let y = args[1].as_char().unwrap();
572 let result = x + y;
573
574 #[cfg(not(feature = "unchecked"))]
575 _ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?;
576
577 Ok(result.into())
578 },
579 CHECKED_BUILD,
580 )),
581 Minus => Some((
582 |_, args| {
583 let x = &*args[0].as_immutable_string_ref().unwrap();
584 let y = args[1].as_char().unwrap();
585 Ok((x - y).into())
586 },
587 false,
588 )),
589 EqualsTo => impl_op!(get_s1s2(==)),
590 NotEqualsTo => impl_op!(get_s1s2(!=)),
591 GreaterThan => impl_op!(get_s1s2(>)),
592 GreaterThanEqualsTo => impl_op!(get_s1s2(>=)),
593 LessThan => impl_op!(get_s1s2(<)),
594 LessThanEqualsTo => impl_op!(get_s1s2(<=)),
595 _ => None,
596 };
597 }
598 (Union::Unit(..), Union::Str(..), _) => {
600 return match op {
601 Plus => Some((|_, args| Ok(args[1].clone()), false)),
602 EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
603 Some((const_false_fn, false))
604 }
605 NotEqualsTo => Some((const_true_fn, false)),
606 _ => None,
607 }
608 }
609 (Union::Str(..), Union::Unit(..), _) => {
611 return match op {
612 Plus => Some((|_, args| Ok(args[0].clone()), false)),
613 EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
614 Some((const_false_fn, false))
615 }
616 NotEqualsTo => Some((const_true_fn, false)),
617 _ => None,
618 };
619 }
620
621 #[cfg(not(feature = "no_index"))]
623 (Union::Blob(..), Union::Char(..), _) => {
624 return match op {
625 Plus => Some((
626 |_ctx, args| {
627 let mut blob = args[0].as_blob_ref().unwrap().clone();
628 let mut buf = [0_u8; 4];
629 let x = args[1].as_char().unwrap().encode_utf8(&mut buf);
630
631 #[cfg(not(feature = "unchecked"))]
632 _ctx.unwrap()
633 .engine()
634 .throw_on_size((blob.len() + x.len(), 0, 0))?;
635
636 blob.extend(x.as_bytes());
637 Ok(Dynamic::from_blob(blob))
638 },
639 CHECKED_BUILD,
640 )),
641 _ => None,
642 }
643 }
644
645 _ => (),
646 }
647
648 let type1 = x.type_id();
651 let type2 = y.type_id();
652
653 if type1 == TypeId::of::<ExclusiveRange>() && type2 == TypeId::of::<ExclusiveRange>() {
654 return match op {
655 EqualsTo => impl_op!(ExclusiveRange == ExclusiveRange),
656 NotEqualsTo => impl_op!(ExclusiveRange != ExclusiveRange),
657 _ => None,
658 };
659 }
660 if type1 == TypeId::of::<InclusiveRange>() && type2 == TypeId::of::<InclusiveRange>() {
661 return match op {
662 EqualsTo => impl_op!(InclusiveRange == InclusiveRange),
663 NotEqualsTo => impl_op!(InclusiveRange != InclusiveRange),
664 _ => None,
665 };
666 }
667
668 if (type1 == TypeId::of::<ExclusiveRange>() && type2 == TypeId::of::<InclusiveRange>())
670 || (type1 == TypeId::of::<InclusiveRange>() && type2 == TypeId::of::<ExclusiveRange>())
671 {
672 return match op {
673 NotEqualsTo => Some((const_true_fn, false)),
674 Equals => Some((const_false_fn, false)),
675 _ => None,
676 };
677 }
678
679 if type1 != type2 {
681 return match op {
682 NotEqualsTo if !is_numeric(type1) || !is_numeric(type2) => Some((const_true_fn, false)),
683 EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo
684 if !is_numeric(type1) || !is_numeric(type2) =>
685 {
686 Some((const_false_fn, false))
687 }
688 _ => None,
689 };
690 }
691
692 None
694}
695
696#[must_use]
700pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
701 macro_rules! impl_op {
702 ($x:ty = x $op:tt $yy:ident) => { Some((|_, args| {
703 let x = args[0].$yy().unwrap();
704 let y = args[1].$yy().unwrap() as $x;
705 Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
706 }, false)) };
707 ($x:ident $op:tt $yy:ident) => { Some((|_, args| {
708 let y = args[1].$yy().unwrap() as $x;
709 Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
710 }, false)) };
711 ($x:ident $op:tt $yy:ident as $yyy:ty) => { Some((|_, args| {
712 let y = args[1].$yy().unwrap() as $yyy;
713 Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
714 }, false)) };
715 ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { Some((|_, args| {
716 let x = args[0].$xx().unwrap();
717 let y = args[1].$yy().unwrap() as $x;
718 Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
719 }, false)) };
720 ($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
721 let x = args[0].$xx().unwrap();
722 let y = args[1].$yy().unwrap() as $x;
723 let v: Dynamic = $func(x, y).into();
724 Ok((*args[0].write_lock().unwrap() = v).into())
725 }, false)) };
726 ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
727 let x = args[0].$xx().unwrap();
728 let y = args[1].$yy().unwrap() as $x;
729 Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
730 }, false)) };
731 (from $x:ident $op:tt $yy:ident) => { Some((|_, args| {
732 let y = <$x>::from(args[1].$yy().unwrap());
733 Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
734 }, false)) };
735 (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { Some((|_, args| {
736 let x = args[0].$xx().unwrap();
737 let y = <$x>::from(args[1].$yy().unwrap());
738 Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
739 }, false)) };
740 (from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| {
741 let x = args[0].$xx().unwrap();
742 let y = <$x>::from(args[1].$yy().unwrap());
743 Ok((*args[0].write_lock().unwrap() = $func(x, y).into()).into())
744 }, false)) };
745 (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| {
746 let x = args[0].$xx().unwrap();
747 let y = <$x>::from(args[1].$yy().unwrap());
748 Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
749 }, false)) };
750 }
751
752 #[cfg(not(feature = "no_float"))]
753 macro_rules! impl_float {
754 ($x:ident, $xx:ident, $yy:ident) => {
755 return match op {
756 PlusAssign => impl_op!($x += $yy),
757 MinusAssign => impl_op!($x -= $yy),
758 MultiplyAssign => impl_op!($x *= $yy),
759 DivideAssign => impl_op!($x /= $yy),
760 ModuloAssign => impl_op!($x %= $yy),
761 PowerOfAssign => impl_op!($x => $xx.powf($yy as $x)),
762 _ => None,
763 }
764 }
765 }
766
767 #[cfg(feature = "decimal")]
768 macro_rules! impl_decimal {
769 ($x:ident, $xx:ident, $yy:ident) => {
770 {
771 #[cfg(not(feature = "unchecked"))]
772 #[allow(clippy::wildcard_imports)]
773 use crate::packages::arithmetic::decimal_functions::builtin::*;
774
775 #[cfg(not(feature = "unchecked"))]
776 return match op {
777 PlusAssign => impl_op!(from $x => add($xx, $yy)),
778 MinusAssign => impl_op!(from $x => subtract($xx, $yy)),
779 MultiplyAssign => impl_op!(from $x => multiply($xx, $yy)),
780 DivideAssign => impl_op!(from $x => divide($xx, $yy)),
781 ModuloAssign => impl_op!(from $x => modulo($xx, $yy)),
782 PowerOfAssign => impl_op!(from $x => power($xx, $yy)),
783 _ => None,
784 };
785
786 #[cfg(feature = "unchecked")]
787 use rust_decimal::MathematicalOps;
788
789 #[cfg(feature = "unchecked")]
790 return match op {
791 PlusAssign => impl_op!(from $x += $yy),
792 MinusAssign => impl_op!(from $x -= $yy),
793 MultiplyAssign => impl_op!(from $x *= $yy),
794 DivideAssign => impl_op!(from $x /= $yy),
795 ModuloAssign => impl_op!(from $x %= $yy),
796 PowerOfAssign => impl_op!(from $x => $xx.powd($yy)),
797 _ => None,
798 };
799 }
800 };
801 }
802
803 match (&x.0, &y.0, op) {
805 (Union::Int(..), Union::Int(..), _) => {
806 #[cfg(not(feature = "unchecked"))]
807 #[allow(clippy::wildcard_imports)]
808 use crate::packages::arithmetic::arith_basic::INT::functions::*;
809
810 #[cfg(not(feature = "unchecked"))]
811 match op {
812 PlusAssign => return impl_op!(INT => add(as_int, as_int)),
813 MinusAssign => return impl_op!(INT => subtract(as_int, as_int)),
814 MultiplyAssign => return impl_op!(INT => multiply(as_int, as_int)),
815 DivideAssign => return impl_op!(INT => divide(as_int, as_int)),
816 ModuloAssign => return impl_op!(INT => modulo(as_int, as_int)),
817 PowerOfAssign => return impl_op!(INT => power(as_int, as_int)),
818 RightShiftAssign => return impl_op!(INT => Ok(shift_right(as_int, as_int))),
819 LeftShiftAssign => return impl_op!(INT => Ok(shift_left(as_int, as_int))),
820 AndAssign => return impl_op!(INT => Ok(binary_and(as_int, as_int))),
821 OrAssign => return impl_op!(INT => Ok(binary_or(as_int, as_int))),
822 XOrAssign => return impl_op!(INT => Ok(binary_xor(as_int, as_int))),
823 _ => (),
824 }
825
826 #[cfg(feature = "unchecked")]
827 match op {
828 PlusAssign => return impl_op!(INT += as_int),
829 MinusAssign => return impl_op!(INT -= as_int),
830 MultiplyAssign => return impl_op!(INT *= as_int),
831 DivideAssign => return impl_op!(INT /= as_int),
832 ModuloAssign => return impl_op!(INT %= as_int),
833 PowerOfAssign => return impl_op!(INT => as_int.pow(as_int as u32)),
834 RightShiftAssign => {
835 return Some((
836 |_, args| {
837 let x = args[0].as_int().unwrap();
838 let y = args[1].as_int().unwrap();
839 let v = if y < 0 { x << -y } else { x >> y };
840 *args[0].write_lock::<Dynamic>().unwrap() = v.into();
841 Ok(Dynamic::UNIT)
842 },
843 false,
844 ))
845 }
846 LeftShiftAssign => {
847 return Some((
848 |_, args| {
849 let x = args[0].as_int().unwrap();
850 let y = args[1].as_int().unwrap();
851 let v = if y < 0 { x >> -y } else { x << y };
852 *args[0].write_lock::<Dynamic>().unwrap() = v.into();
853 Ok(Dynamic::UNIT)
854 },
855 false,
856 ))
857 }
858 AndAssign => return impl_op!(INT &= as_int),
859 OrAssign => return impl_op!(INT |= as_int),
860 XOrAssign => return impl_op!(INT ^= as_int),
861 _ => (),
862 }
863
864 match op {
865 AndAssign => impl_op!(INT &= as_int),
866 OrAssign => impl_op!(INT |= as_int),
867 XOrAssign => impl_op!(INT ^= as_int),
868 _ => None,
869 }
870 }
871
872 (Union::Bool(..), Union::Bool(..), _) => match op {
873 AndAssign => impl_op!(bool = x && as_bool),
874 OrAssign => impl_op!(bool = x || as_bool),
875 XOrAssign => impl_op!(bool = x ^ as_bool),
876 _ => None,
877 },
878
879 (Union::Char(..), Union::Char(..), PlusAssign) => Some((
881 |_, args| {
882 let y = args[1].as_char().unwrap();
883 let x = &mut *args[0].write_lock::<Dynamic>().unwrap();
884
885 let mut buf = SmartString::new_const();
886 buf.push(x.as_char().unwrap());
887 buf.push(y);
888
889 *x = buf.into();
890
891 Ok(Dynamic::UNIT)
892 },
893 false,
894 )),
895
896 (Union::Str(..), Union::Str(..), _) => match op {
897 PlusAssign => Some((
898 |_ctx, args| {
899 let (first, second) = args.split_first_mut().unwrap();
900 let x = &mut *first.as_immutable_string_mut().unwrap();
901 let y = &*second[0].as_immutable_string_ref().unwrap();
902
903 #[cfg(not(feature = "unchecked"))]
904 if !x.is_empty() && !y.is_empty() {
905 let total_len = x.len() + y.len();
906 _ctx.unwrap().engine().throw_on_size((0, 0, total_len))?;
907 }
908
909 *x += y;
910
911 Ok(Dynamic::UNIT)
912 },
913 CHECKED_BUILD,
914 )),
915 MinusAssign => Some((
916 |_, args| {
917 let (first, second) = args.split_first_mut().unwrap();
918 let x = &mut *first.as_immutable_string_mut().unwrap();
919 let y = &*second[0].as_immutable_string_ref().unwrap();
920 *x -= y;
921 Ok(Dynamic::UNIT)
922 },
923 false,
924 )),
925 _ => None,
926 },
927
928 #[cfg(not(feature = "no_index"))]
930 (Union::Array(..), Union::Array(..), PlusAssign) => {
931 #[allow(clippy::wildcard_imports)]
932 use crate::packages::array_basic::array_functions::*;
933
934 Some((
935 |_ctx, args| {
936 let x = args[1].take().into_array().unwrap();
937
938 if x.is_empty() {
939 return Ok(Dynamic::UNIT);
940 }
941
942 #[cfg(not(feature = "unchecked"))]
943 if !args[0].as_array_ref().unwrap().is_empty() {
944 _ctx.unwrap().engine().check_data_size(
945 &*args[0].read_lock().unwrap(),
946 crate::Position::NONE,
947 )?;
948 }
949
950 let array = &mut *args[0].as_array_mut().unwrap();
951
952 append(array, x);
953
954 Ok(Dynamic::UNIT)
955 },
956 CHECKED_BUILD,
957 ))
958 }
959
960 #[cfg(not(feature = "no_index"))]
962 (Union::Blob(..), Union::Blob(..), PlusAssign) => {
963 #[allow(clippy::wildcard_imports)]
964 use crate::packages::blob_basic::blob_functions::*;
965
966 Some((
967 |_ctx, args| {
968 let blob2 = args[1].take().into_blob().unwrap();
969 let blob1 = &mut *args[0].as_blob_mut().unwrap();
970
971 #[cfg(not(feature = "unchecked"))]
972 _ctx.unwrap()
973 .engine()
974 .throw_on_size((blob1.len() + blob2.len(), 0, 0))?;
975
976 append(blob1, blob2);
977
978 Ok(Dynamic::UNIT)
979 },
980 CHECKED_BUILD,
981 ))
982 }
983
984 #[cfg(not(feature = "no_float"))]
985 (Union::Float(..), Union::Float(..), _) => {
986 impl_float!(FLOAT, as_float, as_float)
987 }
988
989 #[cfg(not(feature = "no_float"))]
990 (Union::Float(..), Union::Int(..), _) => {
991 impl_float!(FLOAT, as_float, as_int)
992 }
993
994 #[cfg(feature = "decimal")]
995 (Union::Decimal(..), Union::Decimal(..), _) => {
996 impl_decimal!(Decimal, as_decimal, as_decimal)
997 }
998
999 #[cfg(feature = "decimal")]
1000 (Union::Decimal(..), Union::Int(..), _) => {
1001 impl_decimal!(Decimal, as_decimal, as_int)
1002 }
1003
1004 (Union::Str(..), Union::Char(..), _) => match op {
1006 PlusAssign => Some((
1007 |_ctx, args| {
1008 let mut buf = [0_u8; 4];
1009 let ch = &*args[1].as_char().unwrap().encode_utf8(&mut buf);
1010 let mut x = args[0].as_immutable_string_mut().unwrap();
1011
1012 #[cfg(not(feature = "unchecked"))]
1013 _ctx.unwrap()
1014 .engine()
1015 .throw_on_size((0, 0, x.len() + ch.len()))?;
1016
1017 *x += ch;
1018
1019 Ok(Dynamic::UNIT)
1020 },
1021 CHECKED_BUILD,
1022 )),
1023 MinusAssign => impl_op!(ImmutableString -= as_char as char),
1024 _ => None,
1025 },
1026 (Union::Char(..), Union::Str(..), PlusAssign) => Some((
1028 |_ctx, args| {
1029 let ch = {
1030 let s = &*args[1].as_immutable_string_ref().unwrap();
1031
1032 if s.is_empty() {
1033 return Ok(Dynamic::UNIT);
1034 }
1035
1036 let mut ch = args[0].as_char().unwrap().to_string();
1037
1038 #[cfg(not(feature = "unchecked"))]
1039 _ctx.unwrap()
1040 .engine()
1041 .throw_on_size((0, 0, ch.len() + s.len()))?;
1042
1043 ch += s;
1044 ch
1045 };
1046
1047 *args[0].write_lock::<Dynamic>().unwrap() = ch.into();
1048
1049 Ok(Dynamic::UNIT)
1050 },
1051 CHECKED_BUILD,
1052 )),
1053
1054 #[cfg(not(feature = "no_index"))]
1056 (Union::Array(..), _, PlusAssign) => {
1057 #[allow(clippy::wildcard_imports)]
1058 use crate::packages::array_basic::array_functions::*;
1059
1060 Some((
1061 |_ctx, args| {
1062 {
1063 let x = args[1].take();
1064 let array = &mut *args[0].as_array_mut().unwrap();
1065 push(array, x);
1066 }
1067
1068 #[cfg(not(feature = "unchecked"))]
1069 _ctx.unwrap()
1070 .engine()
1071 .check_data_size(&*args[0].read_lock().unwrap(), crate::Position::NONE)?;
1072
1073 Ok(Dynamic::UNIT)
1074 },
1075 CHECKED_BUILD,
1076 ))
1077 }
1078
1079 #[cfg(not(feature = "no_index"))]
1081 (Union::Blob(..), Union::Int(..), PlusAssign) => {
1082 #[allow(clippy::wildcard_imports)]
1083 use crate::packages::blob_basic::blob_functions::*;
1084
1085 Some((
1086 |_ctx, args| {
1087 let x = args[1].as_int().unwrap();
1088 let blob = &mut *args[0].as_blob_mut().unwrap();
1089
1090 #[cfg(not(feature = "unchecked"))]
1091 _ctx.unwrap()
1092 .engine()
1093 .throw_on_size((blob.len() + 1, 0, 0))?;
1094
1095 push(blob, x);
1096
1097 Ok(Dynamic::UNIT)
1098 },
1099 CHECKED_BUILD,
1100 ))
1101 }
1102
1103 #[cfg(not(feature = "no_index"))]
1105 (Union::Blob(..), Union::Char(..), PlusAssign) => {
1106 #[allow(clippy::wildcard_imports)]
1107 use crate::packages::blob_basic::blob_functions::*;
1108
1109 Some((
1110 |_ctx, args| {
1111 let x = args[1].as_char().unwrap();
1112 let blob = &mut *args[0].as_blob_mut().unwrap();
1113
1114 #[cfg(not(feature = "unchecked"))]
1115 _ctx.unwrap()
1116 .engine()
1117 .throw_on_size((blob.len() + 1, 0, 0))?;
1118
1119 append_char(blob, x);
1120
1121 Ok(Dynamic::UNIT)
1122 },
1123 CHECKED_BUILD,
1124 ))
1125 }
1126
1127 #[cfg(not(feature = "no_index"))]
1129 (Union::Blob(..), Union::Str(..), PlusAssign) => {
1130 #[allow(clippy::wildcard_imports)]
1131 use crate::packages::blob_basic::blob_functions::*;
1132
1133 Some((
1134 |_ctx, args| {
1135 let (first, second) = args.split_first_mut().unwrap();
1136 let blob = &mut *first.as_blob_mut().unwrap();
1137 let s = &*second[0].as_immutable_string_ref().unwrap();
1138
1139 if s.is_empty() {
1140 return Ok(Dynamic::UNIT);
1141 }
1142
1143 #[cfg(not(feature = "unchecked"))]
1144 _ctx.unwrap()
1145 .engine()
1146 .throw_on_size((blob.len() + s.len(), 0, 0))?;
1147
1148 append_str(blob, s);
1149
1150 Ok(Dynamic::UNIT)
1151 },
1152 CHECKED_BUILD,
1153 ))
1154 }
1155
1156 _ => None,
1157 }
1158}