From 4ddb2bf33f44e50da3e2e51947d9c674f811a1b5 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 13 Dec 2020 15:31:11 +0100 Subject: [PATCH] Moved some unit logic into kalk_num --- kalk/src/interpreter.rs | 152 +++++++++++++++++----------------------- kalk/src/kalk_num.rs | 101 ++++++++++++++++++++++++-- kalk/src/prelude.rs | 4 +- 3 files changed, 162 insertions(+), 95 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 306d338..6a54adb 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -5,7 +5,6 @@ use crate::parser::CalcError; use crate::parser::DECL_UNIT; use crate::prelude; use crate::symbol_table::SymbolTable; -use rug::ops::Pow; use rug::Float; pub struct Context<'a> { @@ -25,27 +24,27 @@ impl<'a> Context<'a> { pub fn interpret(&mut self, statements: Vec) -> Result, CalcError> { for (i, stmt) in statements.iter().enumerate() { - let (value, unit) = eval_stmt(self, stmt)?; + let num = eval_stmt(self, stmt)?; // Insert the last value into the `ans` variable. - self.symbol_table.set(if (&unit).len() > 0 { + self.symbol_table.set(if (&num.unit).len() > 0 { Stmt::VarDecl( String::from("ans"), Box::new(Expr::Unit( - unit.clone(), - Box::new(Expr::Literal(value.clone().to_string())), + num.unit.clone(), + Box::new(Expr::Literal(num.value.clone().to_string())), )), ) } else { Stmt::VarDecl( String::from("ans"), - Box::new(Expr::Literal(value.clone().to_string())), + Box::new(Expr::Literal(num.value.clone().to_string())), ) }); if i == statements.len() - 1 { if let Stmt::Expr(_) = stmt { - return Ok(Some(KalkNum::new(value, &unit))); + return Ok(Some(num)); } } } @@ -54,33 +53,33 @@ impl<'a> Context<'a> { } } -fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<(Float, String), CalcError> { +fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result { match stmt { Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt), - Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context), - Stmt::UnitDecl(_, _, _) => eval_unit_decl_stmt(context), + Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(), + Stmt::UnitDecl(_, _, _) => eval_unit_decl_stmt(), Stmt::Expr(expr) => eval_expr_stmt(context, &expr), } } -fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result<(Float, String), CalcError> { +fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result { context.symbol_table.insert(stmt.clone()); - Ok((Float::with_val(context.precision, 1), String::new())) + Ok(KalkNum::from(1)) } -fn eval_fn_decl_stmt(context: &mut Context) -> Result<(Float, String), CalcError> { - Ok((Float::with_val(context.precision, 1), String::new())) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table. +fn eval_fn_decl_stmt() -> Result { + Ok(KalkNum::from(1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table. } -fn eval_unit_decl_stmt(context: &mut Context) -> Result<(Float, String), CalcError> { - Ok((Float::with_val(context.precision, 1), String::new())) +fn eval_unit_decl_stmt() -> Result { + Ok(KalkNum::from(1)) } -fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<(Float, String), CalcError> { +fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result { eval_expr(context, &expr, "") } -fn eval_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<(Float, String), CalcError> { +fn eval_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result { match expr { Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right, unit), Expr::Unary(op, expr) => eval_unary_expr(context, op, expr, unit), @@ -100,55 +99,41 @@ fn eval_binary_expr( op: &TokenKind, right_expr: &Expr, unit: &str, -) -> Result<(Float, String), CalcError> { +) -> Result { if let TokenKind::ToKeyword = op { // TODO: When the unit conversion function takes a Float instead of Expr, // move this to the match statement further down. if let Expr::Var(right_unit) = right_expr { - let (_, left_unit) = eval_expr(context, left_expr, "")?; + let left_unit = eval_expr(context, left_expr, "")?.unit; return convert_unit(context, left_expr, &left_unit, &right_unit); // TODO: Avoid evaluating this twice. } } - let (left, left_unit) = eval_expr(context, left_expr, "")?; - let (mut right, _) = if left_unit.len() > 0 { - let (_, right_unit) = eval_expr(context, right_expr, "")?; // TODO: Avoid evaluating this twice. - - if right_unit.len() > 0 { - convert_unit(context, right_expr, &right_unit, &left_unit)? - } else { - eval_expr(context, right_expr, unit)? - } - } else { - eval_expr(context, right_expr, unit)? - }; - - let final_unit = if unit.len() == 0 { - left_unit - } else { - unit.into() - }; - + let left = eval_expr(context, left_expr, "")?; + let mut right = eval_expr(context, right_expr, "")?; if let Expr::Unary(TokenKind::Percent, _) = right_expr { if let TokenKind::Star = op { - right *= 0.01; + right = right.mul(context, KalkNum::from(0.01)); } else { - right *= left.clone(); + right = right.mul(context, left.clone()); } } - Ok(( - match op { - TokenKind::Plus => left + right, - TokenKind::Minus => left - right, - TokenKind::Star => left * right, - TokenKind::Slash => left / right, - TokenKind::Percent => left % right, - TokenKind::Power => left.pow(right), - _ => Float::with_val(1, 1), - }, - final_unit, - )) + let mut result = match op { + TokenKind::Plus => left.add(context, right), + TokenKind::Minus => left.sub(context, right), + TokenKind::Star => left.mul(context, right), + TokenKind::Slash => left.div(context, right), + TokenKind::Percent => left.rem(context, right), + TokenKind::Power => left.pow(context, right), + _ => KalkNum::from(1), + }; + + if unit.len() > 0 { + result.unit = unit.to_string(); + }; + + Ok(result) } fn eval_unary_expr( @@ -156,16 +141,16 @@ fn eval_unary_expr( op: &TokenKind, expr: &Expr, unit: &str, -) -> Result<(Float, String), CalcError> { - let (expr_value, unit) = eval_expr(context, &expr, unit)?; +) -> Result { + let num = eval_expr(context, &expr, unit)?; match op { - TokenKind::Minus => Ok((-expr_value, unit)), - TokenKind::Percent => Ok((expr_value * 0.01, unit)), - TokenKind::Exclamation => Ok(( + TokenKind::Minus => Ok(KalkNum::new(-num.value, &num.unit)), + TokenKind::Percent => Ok(KalkNum::new(num.value * 0.01, unit)), + TokenKind::Exclamation => Ok(KalkNum::new( Float::with_val( context.precision, - prelude::special_funcs::factorial(expr_value), + prelude::special_funcs::factorial(num.value), ), unit, )), @@ -177,7 +162,7 @@ fn eval_unit_expr( context: &mut Context, identifier: &str, expr: &Expr, -) -> Result<(Float, String), CalcError> { +) -> Result { let angle_unit = &context.angle_unit.clone(); if (identifier == "rad" || identifier == "deg") && angle_unit != identifier { return convert_unit(context, expr, identifier, angle_unit); @@ -191,7 +176,7 @@ pub fn convert_unit( expr: &Expr, from_unit: &str, to_unit: &str, -) -> Result<(Float, String), CalcError> { +) -> Result { if let Some(Stmt::UnitDecl(_, _, unit_def)) = context.symbol_table.get_unit(to_unit, from_unit).cloned() { @@ -199,7 +184,10 @@ pub fn convert_unit( .symbol_table .insert(Stmt::VarDecl(DECL_UNIT.into(), Box::new(expr.clone()))); - Ok((eval_expr(context, &unit_def, "")?.0, to_unit.into())) + Ok(KalkNum::new( + eval_expr(context, &unit_def, "")?.value, + to_unit.into(), + )) } else { Err(CalcError::InvalidUnit) } @@ -209,7 +197,7 @@ fn eval_var_expr( context: &mut Context, identifier: &str, unit: &str, -) -> Result<(Float, String), CalcError> { +) -> Result { // If there is a constant with this name, return a literal expression with its value if let Some(value) = prelude::CONSTANTS.get(identifier) { return eval_expr(context, &Expr::Literal((*value).to_string()), unit); @@ -223,13 +211,9 @@ fn eval_var_expr( } } -fn eval_literal_expr( - context: &mut Context, - value: &str, - unit: &str, -) -> Result<(Float, String), CalcError> { +fn eval_literal_expr(context: &mut Context, value: &str, unit: &str) -> Result { match Float::parse(value) { - Ok(parsed_value) => Ok(( + Ok(parsed_value) => Ok(KalkNum::new( Float::with_val(context.precision, parsed_value), unit.into(), )), @@ -237,11 +221,7 @@ fn eval_literal_expr( } } -fn eval_group_expr( - context: &mut Context, - expr: &Expr, - unit: &str, -) -> Result<(Float, String), CalcError> { +fn eval_group_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result { eval_expr(context, expr, unit) } @@ -250,29 +230,25 @@ fn eval_fn_call_expr( identifier: &str, expressions: &[Expr], unit: &str, -) -> Result<(Float, String), CalcError> { +) -> Result { // Prelude let prelude_func = match expressions.len() { 1 => { - let x = eval_expr(context, &expressions[0], "")?.0; + let x = eval_expr(context, &expressions[0], "")?.value; prelude::call_unary_func(context, identifier, x, &context.angle_unit.clone()) } 2 => { - let x = eval_expr(context, &expressions[0], "")?.0; - let y = eval_expr(context, &expressions[1], "")?.0; + let x = eval_expr(context, &expressions[0], "")?.value; + let y = eval_expr(context, &expressions[1], "")?.value; prelude::call_binary_func(context, identifier, x, y, &context.angle_unit.clone()) } _ => None, }; if let Some((result, func_unit)) = prelude_func { - return Ok(( + return Ok(KalkNum::new( result, - if unit.len() > 0 { - unit.into() - } else { - func_unit.into() - }, + if unit.len() > 0 { unit } else { &func_unit }, )); } @@ -288,8 +264,8 @@ fn eval_fn_call_expr( )); } - let start = eval_expr(context, &expressions[0], "")?.0.to_f64() as i128; - let end = eval_expr(context, &expressions[1], "")?.0.to_f64() as i128; + let start = eval_expr(context, &expressions[0], "")?.to_f64() as i128; + let end = eval_expr(context, &expressions[1], "")?.to_f64() as i128; let mut sum = Float::with_val(context.precision, 0); for n in start..=end { @@ -300,10 +276,10 @@ fn eval_fn_call_expr( context .symbol_table .set(Stmt::VarDecl(String::from("n"), Box::new(n_expr))); - sum += eval_expr(context, &expressions[2], "")?.0; + sum += eval_expr(context, &expressions[2], "")?.value; } - return Ok((sum, unit.into())); + return Ok(KalkNum::new(sum, unit.into())); } _ => (), } diff --git a/kalk/src/kalk_num.rs b/kalk/src/kalk_num.rs index bcba603..3243440 100644 --- a/kalk/src/kalk_num.rs +++ b/kalk/src/kalk_num.rs @@ -1,9 +1,11 @@ +use crate::ast::Expr; +use rug::ops::Pow; use rug::Float; -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug, Clone)] pub struct KalkNum { - value: Float, - unit: String, + pub(crate) value: Float, + pub(crate) unit: String, } pub struct ScientificNotation { @@ -62,8 +64,12 @@ impl KalkNum { format!("{} {}", self.to_string(), self.unit) } - pub fn get_unit(self) -> String { - self.unit + pub fn get_unit(&self) -> &str { + &self.unit + } + + pub fn has_unit(&self) -> bool { + self.unit.len() > 0 } pub fn to_scientific_notation(&self) -> ScientificNotation { @@ -81,6 +87,67 @@ impl KalkNum { exponent: if let Some(exp) = exp_option { exp } else { 0 }, } } + + pub fn convert_to_unit( + &self, + context: &mut crate::interpreter::Context, + to_unit: &str, + ) -> Option { + let result = crate::interpreter::convert_unit( + context, + &Expr::Literal(self.value.to_string()), // TODO: Store as f64, jeez... + &self.unit, + to_unit, + ); + + if let Ok(num) = result { + Some(num) + } else { + None + } + } + + 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) + } + + 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) + } + + 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) + } + + 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) + } + + 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) + } + + 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) + } +} + +fn calculate_unit( + context: &mut crate::interpreter::Context, + left: &KalkNum, + right: KalkNum, +) -> Option { + if left.has_unit() && right.has_unit() { + right.convert_to_unit(context, &left.unit) + } else { + Some(KalkNum::new(right.value, &left.unit)) + } } impl Into for ScientificNotation { @@ -100,3 +167,27 @@ impl Into for KalkNum { self.to_string() } } + +impl From for KalkNum { + fn from(x: f64) -> Self { + KalkNum::new(Float::with_val(63, x), "") + } +} + +impl From for KalkNum { + fn from(x: f32) -> Self { + KalkNum::new(Float::with_val(63, x), "") + } +} + +impl From for KalkNum { + fn from(x: i64) -> Self { + KalkNum::new(Float::with_val(63, x), "") + } +} + +impl From for KalkNum { + fn from(x: i32) -> Self { + KalkNum::new(Float::with_val(63, x), "") + } +} diff --git a/kalk/src/prelude.rs b/kalk/src/prelude.rs index 277f9ca..01644fb 100644 --- a/kalk/src/prelude.rs +++ b/kalk/src/prelude.rs @@ -156,7 +156,7 @@ fn to_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) _ => { interpreter::convert_unit(context, &Expr::Literal(x.to_string()), "rad", angle_unit) .unwrap() - .0 + .value } } } @@ -167,7 +167,7 @@ fn from_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &st _ => { interpreter::convert_unit(context, &Expr::Literal(x.to_string()), angle_unit, "rad") .unwrap() - .0 + .value } } }