1use super::as_f64;
2use crate::error::{DogeError, DogeResult};
3use crate::value::Value;
4
5fn type_err_binop(sym: &str, a: &Value, b: &Value) -> DogeError {
6 DogeError::type_error(format!(
7 "cannot {sym} {} and {}",
8 a.describe(),
9 b.describe()
10 ))
11}
12
13fn overflow(sym: &str, x: i64, y: i64) -> DogeError {
14 DogeError::overflow(format!("{x} {sym} {y} overflowed the Int range"))
15}
16
17fn div_by_zero(sym: &str) -> DogeError {
18 DogeError::division_by_zero(format!("cannot {sym} by zero"))
19}
20
21fn float_fallback(sym: &str, a: &Value, b: &Value, op: impl Fn(f64, f64) -> f64) -> DogeResult {
24 match (as_f64(a), as_f64(b)) {
25 (Some(x), Some(y)) => Ok(Value::Float(op(x, y))),
26 _ => Err(type_err_binop(sym, a, b)),
27 }
28}
29
30pub fn add(a: Value, b: Value) -> DogeResult {
32 match (&a, &b) {
33 (Value::Int(x), Value::Int(y)) => x
34 .checked_add(*y)
35 .map(Value::Int)
36 .ok_or_else(|| overflow("+", *x, *y)),
37 (Value::Str(x), Value::Str(y)) => Ok(Value::str(format!("{x}{y}"))),
38 (Value::Str(x), Value::Error(e)) => Ok(Value::str(format!("{x}{}", e.message))),
42 (Value::Error(e), Value::Str(y)) => Ok(Value::str(format!("{}{y}", e.message))),
43 (Value::List(x), Value::List(y)) => {
44 let mut joined = x.borrow().clone();
45 joined.extend(y.borrow().iter().cloned());
46 Ok(Value::list(joined))
47 }
48 _ => match (as_f64(&a), as_f64(&b)) {
49 (Some(x), Some(y)) => Ok(Value::Float(x + y)),
50 _ => Err(type_err_binop("+", &a, &b)),
51 },
52 }
53}
54
55pub fn sub(a: Value, b: Value) -> DogeResult {
57 match (&a, &b) {
58 (Value::Int(x), Value::Int(y)) => x
59 .checked_sub(*y)
60 .map(Value::Int)
61 .ok_or_else(|| overflow("-", *x, *y)),
62 _ => float_fallback("-", &a, &b, |x, y| x - y),
63 }
64}
65
66pub fn mul(a: Value, b: Value) -> DogeResult {
68 match (&a, &b) {
69 (Value::Int(x), Value::Int(y)) => x
70 .checked_mul(*y)
71 .map(Value::Int)
72 .ok_or_else(|| overflow("*", *x, *y)),
73 _ => float_fallback("*", &a, &b, |x, y| x * y),
74 }
75}
76
77pub fn div(a: Value, b: Value) -> DogeResult {
79 match (as_f64(&a), as_f64(&b)) {
80 (Some(_), Some(0.0)) => Err(div_by_zero("/")),
81 (Some(x), Some(y)) => Ok(Value::Float(x / y)),
82 _ => Err(type_err_binop("/", &a, &b)),
83 }
84}
85
86pub fn floordiv(a: Value, b: Value) -> DogeResult {
89 match (&a, &b) {
90 (Value::Int(x), Value::Int(y)) => {
91 if *y == 0 {
92 return Err(div_by_zero("//"));
93 }
94 let q = x.checked_div(*y).ok_or_else(|| overflow("//", *x, *y))?;
95 let r = x % y;
96 let floored = if r != 0 && ((r < 0) != (*y < 0)) {
99 q - 1
100 } else {
101 q
102 };
103 Ok(Value::Int(floored))
104 }
105 _ => match (as_f64(&a), as_f64(&b)) {
106 (Some(_), Some(0.0)) => Err(div_by_zero("//")),
107 (Some(x), Some(y)) => Ok(Value::Float((x / y).floor())),
108 _ => Err(type_err_binop("//", &a, &b)),
109 },
110 }
111}
112
113pub fn rem(a: Value, b: Value) -> DogeResult {
116 match (&a, &b) {
117 (Value::Int(x), Value::Int(y)) => {
118 if *y == 0 {
119 return Err(div_by_zero("%"));
120 }
121 let r = x.checked_rem(*y).ok_or_else(|| overflow("%", *x, *y))?;
122 let m = if r != 0 && ((r < 0) != (*y < 0)) {
123 r + y
124 } else {
125 r
126 };
127 Ok(Value::Int(m))
128 }
129 _ => match (as_f64(&a), as_f64(&b)) {
130 (Some(_), Some(0.0)) => Err(div_by_zero("%")),
131 (Some(x), Some(y)) => {
132 let r = x % y;
133 let m = if r != 0.0 && ((r < 0.0) != (y < 0.0)) {
134 r + y
135 } else {
136 r
137 };
138 Ok(Value::Float(m))
139 }
140 _ => Err(type_err_binop("%", &a, &b)),
141 },
142 }
143}
144
145pub fn pow(a: Value, b: Value) -> DogeResult {
149 match (&a, &b) {
150 (Value::Int(base), Value::Int(exp)) if *exp >= 0 => {
151 let e = u32::try_from(*exp).map_err(|_| overflow("**", *base, *exp))?;
152 base.checked_pow(e)
153 .map(Value::Int)
154 .ok_or_else(|| overflow("**", *base, *exp))
155 }
156 (Value::Int(0), Value::Int(_)) => Err(div_by_zero("**")),
157 _ => match (as_f64(&a), as_f64(&b)) {
158 (Some(0.0), Some(y)) if y < 0.0 => Err(div_by_zero("**")),
159 (Some(x), Some(y)) => Ok(Value::Float(x.powf(y))),
160 _ => Err(type_err_binop("**", &a, &b)),
161 },
162 }
163}
164
165pub fn neg(a: Value) -> DogeResult {
167 match &a {
168 Value::Int(n) => n
169 .checked_neg()
170 .map(Value::Int)
171 .ok_or_else(|| DogeError::overflow(format!("-{n} overflowed the Int range"))),
172 Value::Float(f) => Ok(Value::Float(-f)),
173 _ => Err(DogeError::type_error(format!(
174 "cannot negate {}",
175 a.describe()
176 ))),
177 }
178}
179
180pub fn not_(a: Value) -> DogeResult {
182 Ok(Value::Bool(!a.truthy()))
183}