···11# Changelog
2233## Next
44+- Rewrite units to be exponent-based, making units much more powerful
55+- Add generic unit multiplication/division, such as `1kg/1m` or `1m*1kg`.
66+- Add generic unit simplification. For example, `1kg*1sqm/1s/1s/1s/1a` now becomes `1 volt`
47- Fix `^` allowing numbers to be raised to the power of units
5869## 3.1.0 - 2025 Jun 20
+88-58
src/evaluator.rs
···11-use crate::lookup::{lookup_factorial, lookup_named_number};
22-use crate::parser::AstNode;
33-use crate::units::{add, convert, divide, modulo, multiply, pow, subtract, Unit, UnitType};
44-use crate::Constant::{Pi, E};
11+use crate::Constant::{E, Pi};
52use crate::FunctionIdentifier::*;
63use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus};
74use crate::TextOperator::{Of, To};
85use crate::UnaryOperator::{Factorial, Percent};
66+use crate::lookup::{lookup_factorial, lookup_named_number};
77+use crate::parser::AstNode;
88+use crate::units::{add, convert, divide, modulo, multiply, pow, subtract};
99use crate::{Number, Token};
1010-use fastnum::{dec128 as d, decimal::RoundingMode, D128};
1010+use fastnum::{D128, dec128 as d, decimal::RoundingMode};
11111212/// Evaluate an [`AstNode`] into a [`Number`]
1313pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
···6161 let token = &ast_node.token;
6262 let children = &ast_node.children;
6363 match token {
6464- Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)),
6464+ Token::Number(number) => Ok(Number::new_unitless(*number)),
6565 Token::Constant(constant) => match constant {
6666- Pi => Ok(Number::new(
6767- D128::PI,
6868- Unit::NoUnit,
6969- )),
7070- E => Ok(Number::new(
7171- D128::E,
7272- Unit::NoUnit,
7373- )),
6666+ Pi => Ok(Number::new_unitless(D128::PI)),
6767+ E => Ok(Number::new_unitless(D128::E)),
7468 },
7569 Token::FunctionIdentifier(function) => {
7670 let child_node = children.get(0).ok_or("Paren has no child[0]")?;
7771 let child_answer = evaluate_node(child_node)?;
7872 match function {
7973 Cbrt => {
8080- if child_answer.unit.category() == UnitType::NoType {
7474+ if child_answer.is_unitless() {
8175 let result = cbrt(child_answer.value);
8282- Ok(Number::new(result, child_answer.unit))
7676+ Ok(Number::with_unit(result, child_answer.unit))
8377 } else {
8484- Err("log() only accepts UnitType::NoType".to_string())
7878+ Err("cbrt() only accepts unitless numbers".to_string())
8579 }
8680 }
8781 Sqrt => {
8888- if child_answer.unit.category() == UnitType::NoType {
8282+ if child_answer.is_unitless() {
8983 let result = sqrt(child_answer.value);
9090- Ok(Number::new(result, child_answer.unit))
8484+ Ok(Number::with_unit(result, child_answer.unit))
9185 } else {
9292- Err("log() only accepts UnitType::NoType".to_string())
8686+ Err("sqrt() only accepts unitless numbers".to_string())
9387 }
9488 }
9589 Log => {
9696- if child_answer.unit.category() == UnitType::NoType {
9090+ if child_answer.is_unitless() {
9791 let result = child_answer.value.log10();
9898- Ok(Number::new(result, child_answer.unit))
9292+ Ok(Number::with_unit(result, child_answer.unit))
9993 } else {
100100- Err("log() only accepts UnitType::NoType".to_string())
9494+ Err("log() only accepts unitless numbers".to_string())
10195 }
10296 }
10397 Ln => {
104104- if child_answer.unit.category() == UnitType::NoType {
9898+ if child_answer.is_unitless() {
10599 let result = child_answer.value.ln();
106106- Ok(Number::new(result, child_answer.unit))
100100+ Ok(Number::with_unit(result, child_answer.unit))
107101 } else {
108108- Err("ln() only accepts UnitType::NoType".to_string())
102102+ Err("ln() only accepts unitless numbers".to_string())
109103 }
110104 }
111105 Exp => {
112112- if child_answer.unit.category() == UnitType::NoType {
106106+ if child_answer.is_unitless() {
113107 let result = child_answer.value.exp();
114114- Ok(Number::new(result, child_answer.unit))
108108+ Ok(Number::with_unit(result, child_answer.unit))
115109 } else {
116116- Err("exp() only accepts UnitType::NoType".to_string())
110110+ Err("exp() only accepts unitless numbers".to_string())
117111 }
118112 }
119113 Round => {
···122116 .value
123117 .with_rounding_mode(RoundingMode::HalfUp)
124118 .round(0);
125125- Ok(Number::new(result, child_answer.unit))
119119+ Ok(Number::with_unit(result, child_answer.unit))
126120 }
127121 Ceil => {
128122 let result = child_answer.value.ceil();
129129- Ok(Number::new(result, child_answer.unit))
123123+ Ok(Number::with_unit(result, child_answer.unit))
130124 }
131125 Floor => {
132126 let result = child_answer.value.floor();
133133- Ok(Number::new(result, child_answer.unit))
127127+ Ok(Number::with_unit(result, child_answer.unit))
134128 }
135129 Abs => {
136130 let result = child_answer.value.abs();
137137- Ok(Number::new(result, child_answer.unit))
131131+ Ok(Number::with_unit(result, child_answer.unit))
138132 }
139133 Sin => {
140134 let result = sin(child_answer.value);
141141- Ok(Number::new(result, child_answer.unit))
135135+ Ok(Number::with_unit(result, child_answer.unit))
142136 }
143137 Cos => {
144138 let result = cos(child_answer.value);
145145- Ok(Number::new(result, child_answer.unit))
139139+ Ok(Number::with_unit(result, child_answer.unit))
146140 }
147141 Tan => {
148142 let result = tan(child_answer.value);
149149- Ok(Number::new(result, child_answer.unit))
143143+ Ok(Number::with_unit(result, child_answer.unit))
150144 }
151145 }
152146 }
153147 Token::Unit(unit) => {
154148 let child_node = children.get(0).ok_or("Unit has no child[0]")?;
155149 let child_answer = evaluate_node(child_node)?;
156156- Ok(Number::new(child_answer.value, *unit))
150150+ Ok(Number::with_basic_unit(child_answer.value, *unit))
157151 }
158152 Token::Negative => {
159153 let child_node = children.get(0).ok_or("Negative has no child[0]")?;
160154 let child_answer = evaluate_node(child_node)?;
161161- Ok(Number::new(-child_answer.value, child_answer.unit))
155155+ Ok(Number::with_unit(-child_answer.value, child_answer.unit))
162156 }
163157 Token::Paren => {
164158 let child_node = children.get(0).ok_or("Paren has no child[0]")?;
···170164 .ok_or(format!("Token {:?} has no child[0]", token))?;
171165 let child_answer = evaluate_node(child_node)?;
172166 match operator {
173173- Percent => Ok(Number::new(
167167+ Percent => Ok(Number::with_unit(
174168 child_answer.value / d!(100),
175169 child_answer.unit,
176170 )),
···181175 "Can only perform factorial on integers from 0 to 1000".to_string()
182176 );
183177 }
184184- Ok(Number::new(result, child_answer.unit))
178178+ Ok(Number::with_unit(result, child_answer.unit))
185179 }
186180 }
187181 }
···198192 }
199193 let child_answer = evaluate_node(child_node)?;
200194 let result = child_answer.value * named_number_value;
201201- Ok(Number::new(result, child_answer.unit))
195195+ Ok(Number::with_unit(result, child_answer.unit))
202196 }
203197 Token::TextOperator(operator) => {
204198 let left_child = children
···212206 To => {
213207 if let Token::Unit(right_unit) = right_child.token {
214208 let left = evaluate_node(left_child)?;
215215- let result = convert(left, right_unit)?;
209209+ let result = convert(left, vec![(right_unit, 1)])?;
216210 Ok(result)
217211 } else {
218212 Err("Right side of To operator needs to be a unit".to_string())
···221215 Of => {
222216 let left = evaluate_node(left_child)?;
223217 let right = evaluate_node(right_child)?;
224224- if left.unit == Unit::NoUnit {
225225- Ok(Number::new(left.value * right.value, right.unit))
218218+ if left.is_unitless() {
219219+ Ok(Number::with_unit(left.value * right.value, right.unit))
226220 } else {
227221 Err("Left side of the Of operator must be NoUnit".to_string())
228222 }
···254248255249#[cfg(test)]
256250mod tests {
257257- use crate::eval;
258251 use super::*;
252252+ use crate::{eval, units::Unit::*};
259253260254 fn eval_default<'a>(input: &'a str) -> Number {
261255 let result = eval(input, true, false).unwrap();
···263257 }
264258 fn eval_num<'a>(input: &'a str) -> String {
265259 let result = eval(input, true, false).unwrap();
266266- assert_eq!(result.unit, Unit::NoUnit);
260260+ assert!(result.is_unitless());
267261268262 result.to_string()
269263 }
···273267 assert_eq!(eval_num("-2(-3)"), "6");
274268 assert_eq!(eval_num("-2(3)"), "-6");
275269 assert_eq!(eval_num("(3)-2"), "1");
276276- assert_eq!(eval_default("-1km to m"), Number::new(d!(-1000), Unit::Meter));
270270+ assert_eq!(
271271+ eval_default("-1km to m"),
272272+ Number::with_basic_unit(d!(-1000), Meter)
273273+ );
277274 assert_eq!(eval_num("2*-3*0.5"), "-3");
278275 assert_eq!(eval_num("-3^2"), "-9");
279276 assert_eq!(eval_num("e^2"), "7.3890560989306502272304274605750078132");
280280- assert_eq!(eval_num("e^2.5"), "12.1824939607034734380701759511679661832");
277277+ assert_eq!(
278278+ eval_num("e^2.5"),
279279+ "12.1824939607034734380701759511679661832"
280280+ );
281281 assert_eq!(eval_num("-1+2"), "1");
282282 }
283283284284 #[test]
285285 fn test_functions() {
286286 assert_eq!(eval_num("cbrt(125)"), "5");
287287- assert_eq!(eval_num("cbrt(2)"), "1.25992104989487316476721060727822835057");
287287+ assert_eq!(
288288+ eval_num("cbrt(2)"),
289289+ "1.25992104989487316476721060727822835057"
290290+ );
288291289292 assert_eq!(eval_num("sqrt(25)"), "5");
290290- assert_eq!(eval_num("sqrt(2)"), "1.41421356237309504880168872420969807857");
293293+ assert_eq!(
294294+ eval_num("sqrt(2)"),
295295+ "1.41421356237309504880168872420969807857"
296296+ );
291297292298 assert_eq!(eval_num("log(100)"), "2");
293293- assert_eq!(eval_num("log(2)"), "0.301029995663981195213738894724493026768");
299299+ assert_eq!(
300300+ eval_num("log(2)"),
301301+ "0.301029995663981195213738894724493026768"
302302+ );
294303295304 assert_eq!(eval_num("ln(1)"), "0");
296296- assert_eq!(eval_num("ln(2)"), "0.69314718055994530941723212145817656808");
305305+ assert_eq!(
306306+ eval_num("ln(2)"),
307307+ "0.69314718055994530941723212145817656808"
308308+ );
297309 assert_eq!(eval_num("ln(e)"), "1");
298310 assert_eq!(eval_num("ln(e^2)"), "2");
299311300300- assert_eq!(eval_num("exp(1)"), "2.71828182845904523536028747135266249776");
312312+ assert_eq!(
313313+ eval_num("exp(1)"),
314314+ "2.71828182845904523536028747135266249776"
315315+ );
301316302317 assert_eq!(eval_num("round(1.4)"), "1");
303318 assert_eq!(eval_num("round(1.6)"), "2");
···312327313328 assert_eq!(eval_num("abs(-3)"), "3");
314329315315- assert_eq!(eval_num("sin(2)"), "0.9092974268256816953960198659117448427");
316316- assert_eq!(eval_num("sin(-2)"), "-0.9092974268256816953960198659117448427");
330330+ assert_eq!(
331331+ eval_num("sin(2)"),
332332+ "0.9092974268256816953960198659117448427"
333333+ );
334334+ assert_eq!(
335335+ eval_num("sin(-2)"),
336336+ "-0.9092974268256816953960198659117448427"
337337+ );
317338318318- assert_eq!(eval_num("cos(2)"), "-0.41614683654714238699756822950076218977");
319319- assert_eq!(eval_num("cos(-2)"), "-0.41614683654714238699756822950076218977");
320320- assert_eq!(eval_num("tan(2)"), "-2.18503986326151899164330610231368254343");
339339+ assert_eq!(
340340+ eval_num("cos(2)"),
341341+ "-0.41614683654714238699756822950076218977"
342342+ );
343343+ assert_eq!(
344344+ eval_num("cos(-2)"),
345345+ "-0.41614683654714238699756822950076218977"
346346+ );
347347+ assert_eq!(
348348+ eval_num("tan(2)"),
349349+ "-2.18503986326151899164330610231368254343"
350350+ );
321351 }
322352}