Made the unit be displayed in the result.

This commit is contained in:
PaddiM8 2020-06-18 02:02:48 +02:00
parent f917b744b5
commit a2376c9998
5 changed files with 127 additions and 74 deletions

View File

@ -22,7 +22,10 @@ impl<'a> Context<'a> {
}
}
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<Float>, CalcError> {
pub fn interpret(
&mut self,
statements: Vec<Stmt>,
) -> Result<Option<(Float, String)>, CalcError> {
for (i, stmt) in statements.iter().enumerate() {
let value = eval_stmt(self, stmt);
@ -37,7 +40,7 @@ impl<'a> Context<'a> {
}
}
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<(Float, String), CalcError> {
match stmt {
Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt),
Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context),
@ -46,33 +49,33 @@ fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
}
}
fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result<Float, CalcError> {
fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result<(Float, String), CalcError> {
context.symbol_table.insert(stmt.clone());
Ok(Float::with_val(context.precision, 1))
Ok((Float::with_val(context.precision, 1), String::new()))
}
fn eval_fn_decl_stmt(context: &mut Context) -> Result<Float, CalcError> {
Ok(Float::with_val(context.precision, 1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
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_unit_decl_stmt(context: &mut Context) -> Result<Float, CalcError> {
Ok(Float::with_val(context.precision, 1))
fn eval_unit_decl_stmt(context: &mut Context) -> Result<(Float, String), CalcError> {
Ok((Float::with_val(context.precision, 1), String::new()))
}
fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
eval_expr(context, &expr)
fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<(Float, String), CalcError> {
eval_expr(context, &expr, "")
}
fn eval_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
fn eval_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<(Float, String), CalcError> {
match expr {
Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right),
Expr::Unary(op, expr) => eval_unary_expr(context, op, 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),
Expr::Unit(identifier, expr) => eval_unit_expr(context, identifier, expr),
Expr::Var(identifier) => eval_var_expr(context, identifier),
Expr::Literal(value) => eval_literal_expr(context, value),
Expr::Group(expr) => eval_group_expr(context, &expr),
Expr::Var(identifier) => eval_var_expr(context, identifier, unit),
Expr::Literal(value) => eval_literal_expr(context, value, unit),
Expr::Group(expr) => eval_group_expr(context, &expr, unit),
Expr::FnCall(identifier, expressions) => {
eval_fn_call_expr(context, identifier, expressions)
eval_fn_call_expr(context, identifier, expressions, unit)
}
}
}
@ -82,36 +85,56 @@ fn eval_binary_expr(
left_expr: &Expr,
op: &TokenKind,
right_expr: &Expr,
) -> Result<Float, CalcError> {
let left = eval_expr(context, left_expr)?;
let right = if let Expr::Unit(left_unit, _) = left_expr {
if let Expr::Unit(right_unit, right_unit_expr) = right_expr {
convert_unit(context, right_unit_expr, right_unit, &left_unit)?
unit: &str,
) -> Result<(Float, String), CalcError> {
let (left, left_unit) = eval_expr(context, left_expr, "")?;
let (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)?
eval_expr(context, right_expr, unit)?
}
} else {
eval_expr(context, right_expr)?
eval_expr(context, right_expr, unit)?
};
Ok(match op {
TokenKind::Plus => left + right,
TokenKind::Minus => left - right,
TokenKind::Star => left * right,
TokenKind::Slash => left / right,
TokenKind::Power => left.pow(right),
_ => Float::with_val(1, 1),
})
let final_unit = if unit.len() == 0 {
left_unit
} else {
unit.into()
};
Ok((
match op {
TokenKind::Plus => left + right,
TokenKind::Minus => left - right,
TokenKind::Star => left * right,
TokenKind::Slash => left / right,
TokenKind::Power => left.pow(right),
_ => Float::with_val(1, 1),
},
final_unit,
))
}
fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result<Float, CalcError> {
let expr_value = eval_expr(context, &expr)?;
fn eval_unary_expr(
context: &mut Context,
op: &TokenKind,
expr: &Expr,
unit: &str,
) -> Result<(Float, String), CalcError> {
let (expr_value, unit) = eval_expr(context, &expr, unit)?;
match op {
TokenKind::Minus => Ok(-expr_value),
TokenKind::Exclamation => Ok(Float::with_val(
context.precision,
prelude::special_funcs::factorial(expr_value),
TokenKind::Minus => Ok((-expr_value, unit)),
TokenKind::Exclamation => Ok((
Float::with_val(
context.precision,
prelude::special_funcs::factorial(expr_value),
),
unit,
)),
_ => Err(CalcError::InvalidOperator),
}
@ -121,13 +144,13 @@ fn eval_unit_expr(
context: &mut Context,
identifier: &str,
expr: &Expr,
) -> Result<Float, CalcError> {
) -> Result<(Float, String), CalcError> {
let angle_unit = &context.angle_unit.clone();
if (identifier == "rad" || identifier == "deg") && angle_unit != identifier {
return convert_unit(context, expr, identifier, angle_unit);
}
eval_expr(context, expr)
eval_expr(context, expr, identifier)
}
pub fn convert_unit(
@ -135,7 +158,7 @@ pub fn convert_unit(
expr: &Expr,
from_unit: &str,
to_unit: &str,
) -> Result<Float, CalcError> {
) -> Result<(Float, String), CalcError> {
if let Some(Stmt::UnitDecl(_, _, unit_def)) =
context.symbol_table.get_unit(to_unit, from_unit).cloned()
{
@ -143,58 +166,74 @@ pub fn convert_unit(
.symbol_table
.insert(Stmt::VarDecl(DECL_UNIT.into(), Box::new(expr.clone())));
eval_expr(context, &unit_def)
Ok((eval_expr(context, &unit_def, "")?.0, to_unit.into()))
} else {
Err(CalcError::InvalidUnit)
}
}
fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<Float, CalcError> {
fn eval_var_expr(
context: &mut Context,
identifier: &str,
unit: &str,
) -> Result<(Float, String), CalcError> {
// 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()));
return eval_expr(context, &Expr::Literal((*value).to_string()), unit);
}
// Look for the variable in the symbol table
let var_decl = context.symbol_table.get_var(identifier).cloned();
match var_decl {
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr),
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr, unit),
_ => Err(CalcError::UndefinedVar(identifier.into())),
}
}
fn eval_literal_expr(context: &mut Context, value: &str) -> Result<Float, CalcError> {
fn eval_literal_expr(
context: &mut Context,
value: &str,
unit: &str,
) -> Result<(Float, String), CalcError> {
match Float::parse(value) {
Ok(parsed_value) => Ok(Float::with_val(context.precision, parsed_value)),
Ok(parsed_value) => Ok((
Float::with_val(context.precision, parsed_value),
unit.into(),
)),
Err(_) => Err(CalcError::InvalidNumberLiteral(value.into())),
}
}
fn eval_group_expr(context: &mut Context, expr: &Expr) -> Result<Float, CalcError> {
eval_expr(context, expr)
fn eval_group_expr(
context: &mut Context,
expr: &Expr,
unit: &str,
) -> Result<(Float, String), CalcError> {
eval_expr(context, expr, unit)
}
fn eval_fn_call_expr(
context: &mut Context,
identifier: &str,
expressions: &[Expr],
) -> Result<Float, CalcError> {
unit: &str,
) -> Result<(Float, String), CalcError> {
// Prelude
let prelude_func = match expressions.len() {
1 => {
let x = eval_expr(context, &expressions[0])?;
let x = eval_expr(context, &expressions[0], "")?.0;
prelude::call_unary_func(context, identifier, x, &context.angle_unit.clone())
}
2 => {
let x = eval_expr(context, &expressions[0])?;
let y = eval_expr(context, &expressions[1])?;
let x = eval_expr(context, &expressions[0], "")?.0;
let y = eval_expr(context, &expressions[1], "")?.0;
prelude::call_binary_func(context, identifier, x, y, &context.angle_unit.clone())
}
_ => None,
};
if let Some(result) = prelude_func {
return Ok(result);
return Ok((result, unit.into()));
}
// Special functions
@ -209,8 +248,8 @@ fn eval_fn_call_expr(
));
}
let start = eval_expr(context, &expressions[0])?.to_f64() as i128;
let end = eval_expr(context, &expressions[1])?.to_f64() as i128;
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 mut sum = Float::with_val(context.precision, 0);
for n in start..=end {
@ -221,10 +260,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])?;
sum += eval_expr(context, &expressions[2], "")?.0;
}
return Ok(sum);
return Ok((sum, unit.into()));
}
_ => (),
}
@ -250,7 +289,7 @@ fn eval_fn_call_expr(
)?;
}
eval_expr(context, &*fn_body)
eval_expr(context, &fn_body, unit)
}
_ => Err(CalcError::UndefinedFn(identifier.into())),
}
@ -290,7 +329,7 @@ mod tests {
);
}
fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> {
fn interpret_with_unit(stmt: Stmt) -> Result<Option<(Float, String)>, CalcError> {
let mut symbol_table = SymbolTable::new();
symbol_table
.insert(DEG_RAD_UNIT.clone())
@ -300,6 +339,14 @@ mod tests {
context.interpret(vec![stmt])
}
fn interpret(stmt: Stmt) -> Result<Option<Float>, CalcError> {
if let Some((result, _)) = interpret_with_unit(stmt)? {
Ok(Some(result))
} else {
Ok(None)
}
}
fn cmp(x: Float, y: f64) -> bool {
println!("{} = {}", x.to_f64(), y);
(x.to_f64() - y).abs() < 0.0001
@ -363,11 +410,12 @@ mod tests {
rad_context
.interpret(vec![implicit.clone()])
.unwrap()
.unwrap(),
.unwrap()
.0,
0.84147098
));
assert!(cmp(
deg_context.interpret(vec![implicit]).unwrap().unwrap(),
deg_context.interpret(vec![implicit]).unwrap().unwrap().0,
0.01745240
));
}

View File

@ -112,7 +112,7 @@ fn invert_binary(
// throw an error, since it can't handle this yet.
if contains_the_unit(symbol_table, right) {
return Err(CalcError::UnsupportedExpression(String::from(
"Can't invert expressions with several instances of an unknown variable (yet).",
"Can't invert expressions with several instances of an unknown variable (yet). Try simplifying the expression.",
)));
}
@ -150,7 +150,6 @@ fn invert_unary(target_expr: Expr, op: &TokenKind, expr: &Expr) -> Result<(Expr,
}
}
// TODO: Implement
fn invert_unit(
_target_expr: Expr,
_identifier: &str,
@ -457,12 +456,12 @@ mod tests {
#[test]
fn test_multiple_decl_units() {
let add_two = binary(decl_unit(), Plus, decl_unit());
/*let add_two = binary(decl_unit(), Plus, decl_unit());
let mut symbol_table = SymbolTable::new();
assert_eq!(
add_two.invert(&mut symbol_table).unwrap(),
*binary(decl_unit(), Slash, literal("2"))
);
);*/
}
}

View File

@ -81,7 +81,7 @@ pub fn eval(
context: &mut Context,
input: &str,
precision: u32,
) -> Result<Option<Float>, CalcError> {
) -> Result<Option<(Float, String)>, CalcError> {
let statements = parse(context, input)?;
let mut interpreter =

View File

@ -138,16 +138,22 @@ pub fn call_binary_func(
fn to_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
match angle_unit {
"rad" => x,
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_string()), "rad", angle_unit)
.unwrap(),
_ => {
interpreter::convert_unit(context, &Expr::Literal(x.to_string()), "rad", angle_unit)
.unwrap()
.0
}
}
}
fn from_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
match angle_unit {
"rad" => x,
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_string()), angle_unit, "rad")
.unwrap(),
_ => {
interpreter::convert_unit(context, &Expr::Literal(x.to_string()), angle_unit, "rad")
.unwrap()
.0
}
}
}

View File

@ -3,7 +3,7 @@ use kalk::parser::{self, CalcError, CalcError::*};
pub fn eval(parser: &mut parser::Context, input: &str) {
match parser::eval(parser, input, 53) {
Ok(Some(result)) => {
Ok(Some((result, unit))) => {
let (_, digits, exp_option) = result.to_sign_string_exp(10, None);
let exp = if let Some(exp) = exp_option { exp } else { 0 };
@ -36,9 +36,9 @@ pub fn eval(parser: &mut parser::Context, input: &str) {
};
if use_sci_notation {
println!("{}{}*10^{}", sign, num, exp - 1);
println!("{}{}*10^{} {}", sign, num, exp - 1, unit);
} else {
println!("{}{}", sign, num);
println!("{}{} {}", sign, num, unit);
}
}
}