diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 8ff39b8..39879e2 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -184,9 +184,11 @@ pub fn convert_unit( .symbol_table .insert(Stmt::VarDecl(DECL_UNIT.into(), Box::new(expr.clone()))); - Ok(KalkNum::new( - eval_expr(context, &unit_def, "")?.value, + let num = eval_expr(context, &unit_def, "")?; + Ok(KalkNum::new_with_rational( + num.clone().value, to_unit.into(), + num.get_internal_rational(), )) } else { Err(CalcError::InvalidUnit) @@ -243,7 +245,7 @@ fn eval_fn_call_expr( }; if let Some((result, func_unit)) = prelude_func { - return Ok(KalkNum::new( + return Ok(KalkNum::new_without_rational( result, if unit.len() > 0 { unit } else { &func_unit }, )); diff --git a/kalk/src/kalk_num.rs b/kalk/src/kalk_num.rs index d4725cc..003d476 100644 --- a/kalk/src/kalk_num.rs +++ b/kalk/src/kalk_num.rs @@ -1,11 +1,13 @@ use crate::ast::Expr; use rug::ops::Pow; use rug::Float; +use rug::Rational; #[derive(PartialEq, Debug, Clone)] pub struct KalkNum { pub(crate) value: Float, pub(crate) unit: String, + rational: Option, } pub struct ScientificNotation { @@ -34,8 +36,25 @@ impl ScientificNotation { impl KalkNum { pub fn new(value: Float, unit: &str) -> Self { Self { - value, + value: value.clone(), unit: unit.to_string(), + rational: value.to_rational(), + } + } + + pub(crate) fn new_with_rational(value: Float, unit: &str, rational: Option) -> Self { + Self { + value: value.clone(), + unit: unit.to_string(), + rational: rational, + } + } + + pub fn new_without_rational(value: Float, unit: &str) -> Self { + Self { + value: value.clone(), + unit: unit.to_string(), + rational: None, } } @@ -68,6 +87,16 @@ impl KalkNum { &self.unit } + pub fn get_rational(self) -> Option<(i64, i64)> { + let (numer, denom) = self.rational?.into_numer_denom(); + + Some((numer.to_i64()?, denom.to_i64()?)) + } + + pub fn get_internal_rational(self) -> Option { + self.rational + } + pub fn has_unit(&self) -> bool { self.unit.len() > 0 } @@ -109,32 +138,52 @@ impl KalkNum { pub fn add(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); - KalkNum::new(self.value + right.value, &right.unit) + let mut num = KalkNum::new_without_rational(self.value + right.value, &right.unit); + if let (Some(left_rational), Some(right_rational)) = (self.rational, right.rational) { + num.rational = Some(left_rational + right_rational); + }; + + num } pub fn sub(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); - KalkNum::new(self.value - right.value, &right.unit) + let mut num = KalkNum::new_without_rational(self.value - right.value, &right.unit); + if let (Some(left_rational), Some(right_rational)) = (self.rational, right.rational) { + num.rational = Some(left_rational - right_rational); + }; + + num } pub fn mul(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); - KalkNum::new(self.value * right.value, &right.unit) + let mut num = KalkNum::new_without_rational(self.value * right.value, &right.unit); + if let (Some(left_rational), Some(right_rational)) = (self.rational, right.rational) { + num.rational = Some(left_rational * right_rational); + }; + + num } pub fn div(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); - KalkNum::new(self.value / right.value, &right.unit) + let mut num = KalkNum::new_without_rational(self.value / right.value, &right.unit); + if let (Some(left_rational), Some(right_rational)) = (self.rational, right.rational) { + num.rational = Some(left_rational / right_rational); + }; + + num } pub fn rem(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); - KalkNum::new(self.value % right.value, &right.unit) + KalkNum::new_without_rational(self.value % right.value, &right.unit) } pub fn pow(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); - KalkNum::new(self.value.pow(right.value), &right.unit) + KalkNum::new_without_rational(self.value.pow(right.value), &right.unit) } } @@ -146,7 +195,11 @@ fn calculate_unit( if left.has_unit() && right.has_unit() { right.convert_to_unit(context, &left.unit) } else { - Some(KalkNum::new(right.value, &left.unit)) + Some(KalkNum::new_with_rational( + right.value, + &left.unit, + right.rational, + )) } } diff --git a/kalk_cli/src/output.rs b/kalk_cli/src/output.rs index 52dda88..106dd5f 100644 --- a/kalk_cli/src/output.rs +++ b/kalk_cli/src/output.rs @@ -12,6 +12,12 @@ pub fn eval(parser: &mut parser::Context, input: &str) { }; println!("{} {}", result_str, result.get_unit()); + + if result_str.contains(".") { + if let Some((numer, denom)) = result.get_rational() { + println!("{}/{}", numer, denom); + } + } } Ok(None) => print!(""), Err(err) => print_calc_err(err),