From eb20aae16f6d1bfb42824dabb59ed8f642a655f0 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sat, 13 Jun 2020 16:19:32 +0200 Subject: [PATCH 01/19] Added the `unit` statement (very basic and experimental). --- kalk/src/ast.rs | 3 +- kalk/src/interpreter.rs | 93 +++++++++++++++++++++------------------- kalk/src/lexer.rs | 4 ++ kalk/src/parser.rs | 86 +++++++++++++++++++++++++++---------- kalk/src/symbol_table.rs | 57 ++++++++++++++++++++---- 5 files changed, 165 insertions(+), 78 deletions(-) diff --git a/kalk/src/ast.rs b/kalk/src/ast.rs index b3bb174..df82c7a 100644 --- a/kalk/src/ast.rs +++ b/kalk/src/ast.rs @@ -7,6 +7,7 @@ use crate::parser::Unit; pub enum Stmt { VarDecl(String, Box), FnDecl(String, Vec, Box), + UnitDecl(String, String, Box), /// For simplicity, expressions can be put into statements. This is the form in which expressions are passed to the interpreter. Expr(Box), } @@ -16,7 +17,7 @@ pub enum Stmt { pub enum Expr { Binary(Box, TokenKind, Box), Unary(TokenKind, Box), - Unit(Box, TokenKind), + Unit(String, Box), Var(String), Group(Box), FnCall(String, Vec), diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 4f60e77..5f04b6f 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -39,18 +39,15 @@ impl<'a> Context<'a> { fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result { match stmt { - Stmt::VarDecl(identifier, _) => eval_var_decl_stmt(context, stmt, identifier), + Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt), Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context), + Stmt::UnitDecl(_, _, _) => eval_unit_decl_stmt(context), Stmt::Expr(expr) => eval_expr_stmt(context, &expr), } } -fn eval_var_decl_stmt( - context: &mut Context, - stmt: &Stmt, - identifier: &str, -) -> Result { - context.symbol_table.insert(&identifier, stmt.clone()); +fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result { + context.symbol_table.insert(stmt.clone()); Ok(Float::with_val(context.precision, 1)) } @@ -58,6 +55,10 @@ fn eval_fn_decl_stmt(context: &mut Context) -> Result { 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_unit_decl_stmt(context: &mut Context) -> Result { + Ok(Float::with_val(context.precision, 1)) +} + fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result { eval_expr(context, &expr) } @@ -66,7 +67,7 @@ fn eval_expr(context: &mut Context, expr: &Expr) -> Result { 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::Unit(expr, kind) => eval_unit_expr(context, expr, kind), + Expr::Unit(_, expr) => eval_unit_expr(context, 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), @@ -78,12 +79,20 @@ fn eval_expr(context: &mut Context, expr: &Expr) -> Result { fn eval_binary_expr( context: &mut Context, - left: &Expr, + left_expr: &Expr, op: &TokenKind, - right: &Expr, + right_expr: &Expr, ) -> Result { - let left = eval_expr(context, &left)?; - let right = eval_expr(context, &right)?; + 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)? + } else { + eval_expr(context, right_expr)? + } + } else { + eval_expr(context, right_expr)? + }; Ok(match op { TokenKind::Plus => left + right, @@ -108,26 +117,26 @@ fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result } } -fn eval_unit_expr( +fn eval_unit_expr(context: &mut Context, expr: &Expr) -> Result { + eval_expr(context, expr) +} + +fn convert_unit( context: &mut Context, expr: &Expr, - kind: &TokenKind, + from_unit: &str, + to_unit: &str, ) -> Result { - let x = eval_expr(context, &expr); - let unit = kind.to_unit()?; + if let Some(Stmt::UnitDecl(_, _, unit_def)) = + context.symbol_table.get_unit(from_unit, to_unit).cloned() + { + context + .symbol_table + .insert(Stmt::VarDecl(String::from("u"), Box::new(expr.clone()))); - // Don't do any angle conversions if the defauly angle unit is the same as the unit kind - match unit { - Unit::Degrees | Unit::Radians => { - if context.angle_unit == unit { - return x; - } - } - } - - match unit { - Unit::Degrees => Ok(prelude::special_funcs::to_radians(x?)), - Unit::Radians => Ok(prelude::special_funcs::to_degrees(x?)), + eval_expr(context, &unit_def) + } else { + Err(CalcError::InvalidUnit) } } @@ -138,7 +147,7 @@ fn eval_var_expr(context: &mut Context, identifier: &str) -> Result eval_expr(context, &expr), _ => Err(CalcError::UndefinedVar(identifier.into())), @@ -202,7 +211,7 @@ fn eval_fn_call_expr( // then calculate the expression and add it to the total sum. context .symbol_table - .set("n", Stmt::VarDecl(String::from("n"), Box::new(n_expr))); + .set(Stmt::VarDecl(String::from("n"), Box::new(n_expr))); sum += eval_expr(context, &expressions[2])?; } @@ -212,10 +221,7 @@ fn eval_fn_call_expr( } // Symbol Table - let stmt_definition = context - .symbol_table - .get(&format!("{}()", identifier)) - .cloned(); + let stmt_definition = context.symbol_table.get_fn(identifier).cloned(); match stmt_definition { Some(Stmt::FnDecl(_, arguments, fn_body)) => { @@ -294,7 +300,7 @@ mod tests { assert!(fact_dec_result > 169.406 && fact_dec_result < 169.407); } - #[test] + /*#[test] fn test_unit() { let rad = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Rad))); let deg = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Deg))); @@ -304,7 +310,7 @@ mod tests { (interpret(deg).unwrap().unwrap() - Float::with_val(PRECISION, 0.017456)).abs() < Float::with_val(PRECISION, 0.0001) ); - } + }*/ #[test] fn test_var() { @@ -312,7 +318,7 @@ mod tests { // Prepare by inserting a variable declaration in the symbol table. let mut symbol_table = SymbolTable::new(); - symbol_table.insert("x", var_decl("x", literal("1"))); + symbol_table.insert(var_decl("x", literal("1"))); let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1); @@ -345,14 +351,11 @@ mod tests { // Prepare by inserting a variable declaration in the symbol table. let mut symbol_table = SymbolTable::new(); - symbol_table.insert( - "f()", - fn_decl( - "f", - vec![String::from("x")], - binary(var("x"), TokenKind::Plus, literal("2")), - ), - ); + symbol_table.insert(fn_decl( + "f", + vec![String::from("x")], + binary(var("x"), TokenKind::Plus, literal("2")), + )); let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3); diff --git a/kalk/src/lexer.rs b/kalk/src/lexer.rs index 4e0f4ad..6bc91c5 100644 --- a/kalk/src/lexer.rs +++ b/kalk/src/lexer.rs @@ -16,6 +16,8 @@ pub enum TokenKind { Equals, Exclamation, + UnitKeyword, + To, Deg, Rad, @@ -170,6 +172,8 @@ impl<'a> Lexer<'a> { let kind = match value.as_ref() { "deg" | "°" => TokenKind::Deg, "rad" => TokenKind::Rad, + "unit" => TokenKind::UnitKeyword, + "to" => TokenKind::To, _ => TokenKind::Identifier, }; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 47de1fe..e735a9f 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -19,6 +19,8 @@ pub struct Context { pos: usize, symbol_table: SymbolTable, angle_unit: Unit, + parsing_unit_decl: bool, + unit_decl_base_unit: Option, } impl Context { @@ -28,6 +30,8 @@ impl Context { pos: 0, symbol_table: SymbolTable::new(), angle_unit: Unit::Radians, + parsing_unit_decl: false, + unit_decl_base_unit: None, } } @@ -101,6 +105,8 @@ fn parse_stmt(context: &mut Context) -> Result { TokenKind::OpenParenthesis => parse_identifier_stmt(context)?, _ => Stmt::Expr(Box::new(parse_expr(context)?)), }); + } else if match_token(context, TokenKind::UnitKeyword) { + return parse_unit_decl_stmt(context); } Ok(Stmt::Expr(Box::new(parse_expr(context)?))) @@ -132,9 +138,7 @@ fn parse_identifier_stmt(context: &mut Context) -> Result { // Insert the function declaration into the symbol table during parsing // so that the parser can find out if particular functions exist. - context - .symbol_table - .insert(&format!("{}()", identifier), fn_decl.clone()); + context.symbol_table.insert(fn_decl.clone()); return Ok(fn_decl); } @@ -156,6 +160,29 @@ fn parse_var_decl_stmt(context: &mut Context) -> Result { Ok(Stmt::VarDecl(identifier.value, Box::new(expr))) } +fn parse_unit_decl_stmt(context: &mut Context) -> Result { + advance(context); // Unit keyword + let identifier = advance(context).clone(); + consume(context, TokenKind::Equals)?; + + // Parse the definition + context.parsing_unit_decl = true; + let def = parse_expr(context)?; + context.parsing_unit_decl = false; + + let base_unit = if let Some(base_unit) = &context.unit_decl_base_unit { + base_unit.clone() + } else { + return Err(CalcError::InvalidUnit); + }; + + let stmt = Stmt::UnitDecl(identifier.value, base_unit, Box::new(def)); + + context.symbol_table.insert(stmt.clone()); + + Ok(stmt) +} + fn parse_expr(context: &mut Context) -> Result { Ok(parse_sum(context)?) } @@ -175,7 +202,7 @@ fn parse_sum(context: &mut Context) -> Result { } fn parse_factor(context: &mut Context) -> Result { - let mut left = parse_unary(context)?; + let mut left = parse_unit(context)?; while match_token(context, TokenKind::Star) || match_token(context, TokenKind::Slash) @@ -188,13 +215,27 @@ fn parse_factor(context: &mut Context) -> Result { _ => advance(context).kind.clone(), }; - let right = parse_unary(context)?; + let right = parse_unit(context)?; left = Expr::Binary(Box::new(left), op, Box::new(right)); } Ok(left) } +fn parse_unit(context: &mut Context) -> Result { + let expr = parse_unary(context)?; + let peek = &peek(&context).value; + + if match_token(context, TokenKind::Identifier) && context.symbol_table.contains_unit(&peek) { + return Ok(Expr::Unit( + advance(context).value.to_string(), + Box::new(expr), + )); + } + + Ok(expr) +} + fn parse_unary(context: &mut Context) -> Result { if match_token(context, TokenKind::Minus) { let op = advance(context).kind.clone(); @@ -236,11 +277,7 @@ fn parse_primary(context: &mut Context) -> Result { _ => Expr::Literal(advance(context).value.clone()), }; - if !is_at_end(context) && peek(context).kind.is_unit() { - Ok(Expr::Unit(Box::new(expr), advance(context).kind.clone())) - } else { - Ok(expr) - } + Ok(expr) } fn parse_group(context: &mut Context) -> Result { @@ -295,7 +332,10 @@ fn parse_identifier(context: &mut Context) -> Result { } // Eg. x - if context.symbol_table.contains_var(&identifier.value) { + if context.parsing_unit_decl { + context.unit_decl_base_unit = Some(identifier.value); + Ok(Expr::Var(String::from("u"))) + } else if context.symbol_table.contains_var(&identifier.value) { Ok(Expr::Var(identifier.value)) } else { let mut chars = identifier.value.chars(); @@ -315,19 +355,19 @@ fn parse_identifier(context: &mut Context) -> Result { } } -fn peek(context: &mut Context) -> &Token { +fn peek(context: &Context) -> &Token { &context.tokens[context.pos] } -fn peek_next(context: &mut Context) -> &Token { +fn peek_next(context: &Context) -> &Token { &context.tokens[context.pos + 1] } -fn previous(context: &mut Context) -> &Token { +fn previous(context: &Context) -> &Token { &context.tokens[context.pos - 1] } -fn match_token(context: &mut Context, kind: TokenKind) -> bool { +fn match_token(context: &Context, kind: TokenKind) -> bool { if is_at_end(context) { return false; } @@ -348,7 +388,7 @@ fn consume(context: &mut Context, kind: TokenKind) -> Result<&Token, CalcError> Err(CalcError::UnexpectedToken(kind)) } -fn is_at_end(context: &mut Context) -> bool { +fn is_at_end(context: &Context) -> bool { context.pos >= context.tokens.len() || peek(context).kind == TokenKind::EOF } @@ -357,7 +397,6 @@ mod tests { use super::*; use crate::lexer::{Token, TokenKind::*}; use crate::test_helpers::*; - use test_case::test_case; fn parse_with_context(context: &mut Context, tokens: Vec) -> Result { context.tokens = tokens; @@ -447,7 +486,7 @@ mod tests { ); } - #[test_case(Deg)] + /*#[test_case(Deg)] #[test_case(Rad)] fn test_unary(angle_unit: TokenKind) { let tokens = vec![ @@ -460,7 +499,7 @@ mod tests { parse(tokens).unwrap(), Stmt::Expr(unary(Minus, Box::new(Expr::Unit(literal("1"), angle_unit)))) ); - } + }*/ #[test] fn test_var_decl() { @@ -517,10 +556,11 @@ mod tests { let mut context = Context::new(); // Add the function to the symbol table first, in order to prevent errors. - context.symbol_table.set( - "f()", - Stmt::FnDecl(String::from("f"), vec![String::from("x")], literal("1")), - ); + context.symbol_table.set(Stmt::FnDecl( + String::from("f"), + vec![String::from("x")], + literal("1"), + )); assert_eq!( parse_with_context(&mut context, tokens).unwrap(), diff --git a/kalk/src/symbol_table.rs b/kalk/src/symbol_table.rs index a8df17e..4ab0e9d 100644 --- a/kalk/src/symbol_table.rs +++ b/kalk/src/symbol_table.rs @@ -1,40 +1,79 @@ use crate::{ast::Stmt, prelude}; use std::collections::HashMap; +#[derive(Debug)] pub struct SymbolTable { hashmap: HashMap, + unit_types: HashMap, } impl SymbolTable { pub fn new() -> Self { SymbolTable { hashmap: HashMap::new(), + unit_types: HashMap::new(), } } - pub fn insert(&mut self, key: &str, value: Stmt) { - self.hashmap.insert(key.into(), value); + pub fn insert(&mut self, value: Stmt) -> Option { + match &value { + Stmt::VarDecl(identifier, _) => { + self.hashmap.insert(format!("var.{}", identifier), value) + } + Stmt::UnitDecl(identifier, to_unit, _) => { + self.unit_types.insert(identifier.to_string(), ()); + self.unit_types.insert(to_unit.to_string(), ()); + self.hashmap + .insert(format!("unit.{}.{}", identifier, to_unit), value) + } + Stmt::FnDecl(identifier, _, _) => { + self.hashmap.insert(format!("fn.{}", identifier), value) + } + _ => panic!("Can only insert VarDecl, UnitDecl and FnDecl into symbol table."), + } } - pub fn get(&self, key: &str) -> Option<&Stmt> { - self.hashmap.get(key) + pub fn get_var(&self, key: &str) -> Option<&Stmt> { + self.hashmap.get(&format!("var.{}", key)) } - pub fn set(&mut self, key: &str, value: Stmt) { - if let Some(stmt) = self.hashmap.get_mut(key) { + pub fn get_unit(&self, key: &str, to_unit: &str) -> Option<&Stmt> { + self.hashmap.get(&format!("unit.{}.{}", key, to_unit)) + } + + pub fn get_fn(&self, key: &str) -> Option<&Stmt> { + self.hashmap.get(&format!("fn.{}", key)) + } + + pub fn set(&mut self, value: Stmt) { + let existing_item = match &value { + Stmt::VarDecl(identifier, _) => self.hashmap.get_mut(&format!("var.{}", identifier)), + Stmt::UnitDecl(identifier, to_unit, _) => self + .hashmap + .get_mut(&format!("unit.{}.{}", identifier, to_unit)), + Stmt::FnDecl(identifier, _, _) => self.hashmap.get_mut(&format!("fn.{}", identifier)), + _ => panic!("Can only set VarDecl, UnitDecl and FnDecl in symbol table."), + }; + + if let Some(stmt) = existing_item { *stmt = value; } else { - self.insert(key, value); + self.insert(value); } } pub fn contains_var(&self, identifier: &str) -> bool { - prelude::CONSTANTS.contains_key(identifier) || self.hashmap.contains_key(identifier) + prelude::CONSTANTS.contains_key(identifier) + || self.hashmap.contains_key(&format!("var.{}", identifier)) + } + + pub fn contains_unit(&self, identifier: &str) -> bool { + self.unit_types.contains_key(identifier) } pub fn contains_fn(&self, identifier: &str) -> bool { prelude::UNARY_FUNCS.contains_key(identifier) || prelude::UNARY_FUNCS.contains_key(identifier) - || self.hashmap.contains_key(&format!("{}()", identifier)) + || self.hashmap.contains_key(&format!("fn.{}", identifier)) } } From bff785bc1ebf79baabb21a4872c89f76ba18bfa3 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sat, 13 Jun 2020 19:01:33 +0200 Subject: [PATCH 02/19] Created the foundation of an expression inverter. This is used to create a second *inverted* unit declaration, in order to cover both of the units. --- kalk/src/ast.rs | 19 ---------------- kalk/src/inverter.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++ kalk/src/lib.rs | 1 + kalk/src/parser.rs | 14 ++++++++++++ 4 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 kalk/src/inverter.rs diff --git a/kalk/src/ast.rs b/kalk/src/ast.rs index df82c7a..5aad8b4 100644 --- a/kalk/src/ast.rs +++ b/kalk/src/ast.rs @@ -1,6 +1,4 @@ use crate::lexer::TokenKind; -use crate::parser::CalcError; -use crate::parser::Unit; /// A tree structure of a statement. #[derive(Debug, Clone, PartialEq)] @@ -23,20 +21,3 @@ pub enum Expr { FnCall(String, Vec), Literal(String), } - -impl TokenKind { - pub fn is_unit(&self) -> bool { - match self { - TokenKind::Deg | TokenKind::Rad => true, - _ => false, - } - } - - pub fn to_unit(&self) -> Result { - match self { - TokenKind::Deg => Ok(Unit::Degrees), - TokenKind::Rad => Ok(Unit::Radians), - _ => Err(CalcError::InvalidUnit), - } - } -} diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs new file mode 100644 index 0000000..d79486c --- /dev/null +++ b/kalk/src/inverter.rs @@ -0,0 +1,52 @@ +use crate::ast::Expr; +use crate::lexer::TokenKind; + +impl Expr { + pub fn invert(&self) -> Self { + match self { + Expr::Binary(left, op, right) => invert_binary(&left, op, &right), + Expr::Unary(op, expr) => invert_unary(op, &expr), + Expr::Unit(identifier, expr) => invert_unit(&identifier, &expr), + Expr::Var(identifier) => invert_value(self), + Expr::Group(expr) => invert_group(&expr), + Expr::FnCall(identifier, expressions) => invert_fn_call(&identifier, expressions), + Expr::Literal(value) => invert_value(self), + } + } +} + +fn invert_binary(left: &Expr, op: &TokenKind, right: &Expr) -> Expr { + let op_inv = match op { + TokenKind::Plus => TokenKind::Minus, + TokenKind::Minus => TokenKind::Plus, + TokenKind::Star => TokenKind::Slash, + TokenKind::Slash => TokenKind::Star, + _ => unreachable!(), + }; + + Expr::Binary(Box::new(right.invert()), op_inv, Box::new(left.invert())) +} + +fn invert_unary(op: &TokenKind, expr: &Expr) -> Expr { + match op { + TokenKind::Minus => expr.clone(), + TokenKind::Exclamation => unimplemented!(), + _ => unreachable!(), + } +} + +fn invert_unit(identifier: &str, expr: &Expr) -> Expr { + unimplemented!() +} + +fn invert_group(expr: &Expr) -> Expr { + invert_value(expr) +} + +fn invert_fn_call(identifier: &str, expressions: &Vec) -> Expr { + unimplemented!() +} + +fn invert_value(expr: &Expr) -> Expr { + Expr::Unary(TokenKind::Minus, Box::new(expr.clone())) +} diff --git a/kalk/src/lib.rs b/kalk/src/lib.rs index 0481264..79e3497 100644 --- a/kalk/src/lib.rs +++ b/kalk/src/lib.rs @@ -1,5 +1,6 @@ pub mod ast; mod interpreter; +mod inverter; mod lexer; pub mod parser; mod prelude; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index e735a9f..bdf37db 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -19,7 +19,12 @@ pub struct Context { pos: usize, symbol_table: SymbolTable, angle_unit: Unit, + /// This is true whenever the parser is currently parsing a unit declaration. + /// It is necessary to keep track of this since you cannot use variables in unit declarations. + /// Unit names are instead treated as variables. parsing_unit_decl: bool, + /// When a unit declaration is being parsed, this value will be set + /// whenever a unit in the expression is found. Eg. unit a = 3b, it will be set to Some("b") unit_decl_base_unit: Option, } @@ -176,9 +181,18 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { return Err(CalcError::InvalidUnit); }; + // Automatically create a second unit decl with the expression inverted. + // This will turn eg. unit a = 3b, into unit b = a/3 + // This is so that you only have to define `a`, and it will figure out the formula for `b` since it is used in the formula for `a`. + let stmt_inv = Stmt::UnitDecl( + base_unit.clone(), + identifier.value.clone(), + Box::new(def.invert()), + ); let stmt = Stmt::UnitDecl(identifier.value, base_unit, Box::new(def)); context.symbol_table.insert(stmt.clone()); + context.symbol_table.insert(stmt_inv); Ok(stmt) } From d042e3577f9ada44ed12e1fd384f847ef1653938 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sat, 13 Jun 2020 20:06:21 +0200 Subject: [PATCH 03/19] Made it possible for the inverter to invert expressions with function calls. --- kalk/src/inverter.rs | 50 ++++++++++++++++++++++++++++++++++---------- kalk/src/parser.rs | 2 +- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index d79486c..11d0b2d 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -1,21 +1,29 @@ -use crate::ast::Expr; +use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; +use crate::symbol_table::SymbolTable; impl Expr { - pub fn invert(&self) -> Self { + pub fn invert(&self, symbol_table: &mut SymbolTable) -> Self { match self { - Expr::Binary(left, op, right) => invert_binary(&left, op, &right), + Expr::Binary(left, op, right) => invert_binary(symbol_table, &left, op, &right), Expr::Unary(op, expr) => invert_unary(op, &expr), Expr::Unit(identifier, expr) => invert_unit(&identifier, &expr), - Expr::Var(identifier) => invert_value(self), + Expr::Var(_) => invert_value(self), Expr::Group(expr) => invert_group(&expr), - Expr::FnCall(identifier, expressions) => invert_fn_call(&identifier, expressions), - Expr::Literal(value) => invert_value(self), + Expr::FnCall(identifier, arguments) => { + invert_fn_call(symbol_table, &identifier, arguments) + } + Expr::Literal(_) => invert_value(self), } } } -fn invert_binary(left: &Expr, op: &TokenKind, right: &Expr) -> Expr { +fn invert_binary( + symbol_table: &mut SymbolTable, + left: &Expr, + op: &TokenKind, + right: &Expr, +) -> Expr { let op_inv = match op { TokenKind::Plus => TokenKind::Minus, TokenKind::Minus => TokenKind::Plus, @@ -24,7 +32,11 @@ fn invert_binary(left: &Expr, op: &TokenKind, right: &Expr) -> Expr { _ => unreachable!(), }; - Expr::Binary(Box::new(right.invert()), op_inv, Box::new(left.invert())) + Expr::Binary( + Box::new(right.invert(symbol_table)), + op_inv, + Box::new(left.invert(symbol_table)), + ) } fn invert_unary(op: &TokenKind, expr: &Expr) -> Expr { @@ -35,7 +47,8 @@ fn invert_unary(op: &TokenKind, expr: &Expr) -> Expr { } } -fn invert_unit(identifier: &str, expr: &Expr) -> Expr { +// Not necessary yet +fn invert_unit(_identifier: &str, _expr: &Expr) -> Expr { unimplemented!() } @@ -43,8 +56,23 @@ fn invert_group(expr: &Expr) -> Expr { invert_value(expr) } -fn invert_fn_call(identifier: &str, expressions: &Vec) -> Expr { - unimplemented!() +fn invert_fn_call(symbol_table: &mut SymbolTable, identifier: &str, arguments: &Vec) -> Expr { + let (parameters, body) = + if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() { + (parameters, body) + } else { + panic!(); // TODO: Error checking + }; + + let mut parameters_iter = parameters.iter(); + for argument in arguments { + symbol_table.insert(Stmt::VarDecl( + parameters_iter.next().unwrap().to_string(), + Box::new(argument.clone()), + )); + } + + body.invert(symbol_table) } fn invert_value(expr: &Expr) -> Expr { diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index bdf37db..a998799 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -187,7 +187,7 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { let stmt_inv = Stmt::UnitDecl( base_unit.clone(), identifier.value.clone(), - Box::new(def.invert()), + Box::new(def.invert(&mut context.symbol_table)), ); let stmt = Stmt::UnitDecl(identifier.value, base_unit, Box::new(def)); From 1edec7d16ef423b2464eb1fb6ee5628cb1914a09 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sat, 13 Jun 2020 22:18:37 +0200 Subject: [PATCH 04/19] Added some error checking to the inverter. --- kalk/src/inverter.rs | 43 ++++++++++++++++++++++++++++--------------- kalk/src/parser.rs | 2 +- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index 11d0b2d..b474c34 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -1,9 +1,10 @@ use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; +use crate::parser::CalcError; use crate::symbol_table::SymbolTable; impl Expr { - pub fn invert(&self, symbol_table: &mut SymbolTable) -> Self { + pub fn invert(&self, symbol_table: &mut SymbolTable) -> Result { match self { Expr::Binary(left, op, right) => invert_binary(symbol_table, &left, op, &right), Expr::Unary(op, expr) => invert_unary(op, &expr), @@ -23,7 +24,7 @@ fn invert_binary( left: &Expr, op: &TokenKind, right: &Expr, -) -> Expr { +) -> Result { let op_inv = match op { TokenKind::Plus => TokenKind::Minus, TokenKind::Minus => TokenKind::Plus, @@ -32,38 +33,50 @@ fn invert_binary( _ => unreachable!(), }; - Expr::Binary( - Box::new(right.invert(symbol_table)), + Ok(Expr::Binary( + Box::new(right.invert(symbol_table)?), op_inv, - Box::new(left.invert(symbol_table)), - ) + Box::new(left.invert(symbol_table)?), + )) } -fn invert_unary(op: &TokenKind, expr: &Expr) -> Expr { - match op { +fn invert_unary(op: &TokenKind, expr: &Expr) -> Result { + Ok(match op { TokenKind::Minus => expr.clone(), TokenKind::Exclamation => unimplemented!(), _ => unreachable!(), - } + }) } // Not necessary yet -fn invert_unit(_identifier: &str, _expr: &Expr) -> Expr { +fn invert_unit(_identifier: &str, _expr: &Expr) -> Result { unimplemented!() } -fn invert_group(expr: &Expr) -> Expr { +fn invert_group(expr: &Expr) -> Result { invert_value(expr) } -fn invert_fn_call(symbol_table: &mut SymbolTable, identifier: &str, arguments: &Vec) -> Expr { +fn invert_fn_call( + symbol_table: &mut SymbolTable, + identifier: &str, + arguments: &Vec, +) -> Result { let (parameters, body) = if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() { (parameters, body) } else { - panic!(); // TODO: Error checking + return Err(CalcError::UndefinedFn(identifier.into())); }; + if parameters.len() != arguments.len() { + return Err(CalcError::IncorrectAmountOfArguments( + parameters.len(), + identifier.into(), + arguments.len(), + )); + } + let mut parameters_iter = parameters.iter(); for argument in arguments { symbol_table.insert(Stmt::VarDecl( @@ -75,6 +88,6 @@ fn invert_fn_call(symbol_table: &mut SymbolTable, identifier: &str, arguments: & body.invert(symbol_table) } -fn invert_value(expr: &Expr) -> Expr { - Expr::Unary(TokenKind::Minus, Box::new(expr.clone())) +fn invert_value(expr: &Expr) -> Result { + Ok(Expr::Unary(TokenKind::Minus, Box::new(expr.clone()))) } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index a998799..be474be 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -187,7 +187,7 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { let stmt_inv = Stmt::UnitDecl( base_unit.clone(), identifier.value.clone(), - Box::new(def.invert(&mut context.symbol_table)), + Box::new(def.invert(&mut context.symbol_table)?), ); let stmt = Stmt::UnitDecl(identifier.value, base_unit, Box::new(def)); From 24771b3a5a65100e10b33e787133878eccff43fe Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 14 Jun 2020 19:23:02 +0200 Subject: [PATCH 05/19] Changed inverter to instead move expressions to the LHS, like done when equations are solved in maths. Also made it multiply into parenthesis. --- kalk/src/interpreter.rs | 5 +- kalk/src/inverter.rs | 155 +++++++++++++++++++++++++++++++--------- kalk/src/parser.rs | 12 ++-- 3 files changed, 131 insertions(+), 41 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 5f04b6f..bf38a38 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -2,6 +2,7 @@ use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; use crate::parser::CalcError; use crate::parser::Unit; +use crate::parser::DECL_UNIT; use crate::prelude; use crate::symbol_table::SymbolTable; use rug::ops::Pow; @@ -128,11 +129,11 @@ fn convert_unit( to_unit: &str, ) -> Result { if let Some(Stmt::UnitDecl(_, _, unit_def)) = - context.symbol_table.get_unit(from_unit, to_unit).cloned() + context.symbol_table.get_unit(to_unit, from_unit).cloned() { context .symbol_table - .insert(Stmt::VarDecl(String::from("u"), Box::new(expr.clone()))); + .insert(Stmt::VarDecl(DECL_UNIT.into(), Box::new(expr.clone()))); eval_expr(context, &unit_def) } else { diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index b474c34..64471da 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -1,67 +1,114 @@ use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; use crate::parser::CalcError; +use crate::parser::DECL_UNIT; use crate::symbol_table::SymbolTable; impl Expr { pub fn invert(&self, symbol_table: &mut SymbolTable) -> Result { - match self { - Expr::Binary(left, op, right) => invert_binary(symbol_table, &left, op, &right), - Expr::Unary(op, expr) => invert_unary(op, &expr), - Expr::Unit(identifier, expr) => invert_unit(&identifier, &expr), - Expr::Var(_) => invert_value(self), - Expr::Group(expr) => invert_group(&expr), - Expr::FnCall(identifier, arguments) => { - invert_fn_call(symbol_table, &identifier, arguments) - } - Expr::Literal(_) => invert_value(self), + let target_expr = Expr::Var(DECL_UNIT.into()); + let result = invert(target_expr, symbol_table, self); + Ok(result?.0) + } +} + +fn invert( + target_expr: Expr, + symbol_table: &mut SymbolTable, + expr: &Expr, +) -> Result<(Expr, Expr), CalcError> { + match expr { + Expr::Binary(left, op, right) => { + invert_binary(target_expr, symbol_table, &left, op, &right) } + Expr::Unary(_, _) => Ok((target_expr, expr.clone())), + Expr::Unit(identifier, expr) => invert_unit(target_expr, &identifier, &expr), + Expr::Var(_) => Ok((target_expr, expr.clone())), + Expr::Group(expr) => Ok((target_expr, *expr.clone())), + Expr::FnCall(identifier, arguments) => { + invert_fn_call(target_expr, symbol_table, &identifier, arguments) + } + Expr::Literal(_) => Ok((target_expr, expr.clone())), } } fn invert_binary( + target_expr: Expr, symbol_table: &mut SymbolTable, left: &Expr, op: &TokenKind, right: &Expr, -) -> Result { +) -> Result<(Expr, Expr), CalcError> { let op_inv = match op { TokenKind::Plus => TokenKind::Minus, TokenKind::Minus => TokenKind::Plus, - TokenKind::Star => TokenKind::Slash, - TokenKind::Slash => TokenKind::Star, + TokenKind::Star => { + if let Expr::Group(inside_group) = left { + return invert( + target_expr, + symbol_table, + &multiply_in(right, inside_group)?, + ); + } + + if let Expr::Group(inside_group) = right { + return invert(target_expr, symbol_table, &multiply_in(left, inside_group)?); + } + + TokenKind::Slash + } + TokenKind::Slash => { + if let Expr::Group(inside_group) = left { + return invert( + target_expr, + symbol_table, + &Expr::Binary(inside_group.clone(), op.clone(), Box::new(right.clone())), + ); + } + + if let Expr::Group(inside_group) = right { + return invert( + target_expr, + symbol_table, + &Expr::Binary(Box::new(left.clone()), op.clone(), inside_group.clone()), + ); + } + + TokenKind::Star + } _ => unreachable!(), }; - Ok(Expr::Binary( - Box::new(right.invert(symbol_table)?), - op_inv, - Box::new(left.invert(symbol_table)?), - )) -} + if contains_the_unit(left) { + return Ok(invert( + Expr::Binary(Box::new(target_expr), op_inv, Box::new(right.clone())), + symbol_table, + left, + )?); + } -fn invert_unary(op: &TokenKind, expr: &Expr) -> Result { - Ok(match op { - TokenKind::Minus => expr.clone(), - TokenKind::Exclamation => unimplemented!(), - _ => unreachable!(), - }) + Ok(invert( + Expr::Binary(Box::new(target_expr), op_inv, Box::new(left.clone())), + symbol_table, + right, + )?) } // Not necessary yet -fn invert_unit(_identifier: &str, _expr: &Expr) -> Result { +fn invert_unit( + _target_expr: Expr, + _identifier: &str, + _expr: &Expr, +) -> Result<(Expr, Expr), CalcError> { unimplemented!() } -fn invert_group(expr: &Expr) -> Result { - invert_value(expr) -} - fn invert_fn_call( + target_expr: Expr, symbol_table: &mut SymbolTable, identifier: &str, arguments: &Vec, -) -> Result { +) -> Result<(Expr, Expr), CalcError> { let (parameters, body) = if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() { (parameters, body) @@ -85,9 +132,49 @@ fn invert_fn_call( )); } - body.invert(symbol_table) + invert(target_expr, symbol_table, &body) } -fn invert_value(expr: &Expr) -> Result { - Ok(Expr::Unary(TokenKind::Minus, Box::new(expr.clone()))) +fn contains_the_unit(expr: &Expr) -> bool { + match expr { + Expr::Binary(left, _, right) => contains_the_unit(left) || contains_the_unit(right), + Expr::Unary(_, expr) => contains_the_unit(expr), + Expr::Unit(_, expr) => contains_the_unit(expr), + Expr::Var(identifier) => identifier == DECL_UNIT, + Expr::Group(expr) => contains_the_unit(expr), + Expr::FnCall(_, args) => { + for arg in args { + if contains_the_unit(arg) { + return true; + } + } + + false + } + Expr::Literal(_) => false, + } +} + +fn multiply_in(expr: &Expr, base_expr: &Expr) -> Result { + match base_expr { + Expr::Binary(left, op, right) => match op { + TokenKind::Plus | TokenKind::Minus => Ok(Expr::Binary( + Box::new(multiply_in(expr, &left)?), + op.clone(), + Box::new(multiply_in(expr, &right)?), + )), + TokenKind::Star | TokenKind::Slash => Ok(Expr::Binary( + Box::new(multiply_in(expr, &left)?), + op.clone(), + right.clone(), + )), + _ => unimplemented!(), + }, + Expr::Literal(_) | Expr::Var(_) => Ok(Expr::Binary( + Box::new(expr.clone()), + TokenKind::Star, + Box::new(base_expr.clone()), + )), + _ => unimplemented!(), + } } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index be474be..6c1b84b 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -6,6 +6,8 @@ use crate::{ }; use rug::Float; +pub const DECL_UNIT: &'static str = ".u"; + /// Struct containing the current state of the parser. It stores user-defined functions and variables. /// # Examples /// ``` @@ -20,7 +22,7 @@ pub struct Context { symbol_table: SymbolTable, angle_unit: Unit, /// This is true whenever the parser is currently parsing a unit declaration. - /// It is necessary to keep track of this since you cannot use variables in unit declarations. + /// It is necessary to keep track of this in order to know when to find (figure out) units that haven't been defined yet. /// Unit names are instead treated as variables. parsing_unit_decl: bool, /// When a unit declaration is being parsed, this value will be set @@ -346,11 +348,11 @@ fn parse_identifier(context: &mut Context) -> Result { } // Eg. x - if context.parsing_unit_decl { - context.unit_decl_base_unit = Some(identifier.value); - Ok(Expr::Var(String::from("u"))) - } else if context.symbol_table.contains_var(&identifier.value) { + if context.symbol_table.contains_var(&identifier.value) { Ok(Expr::Var(identifier.value)) + } else if context.parsing_unit_decl { + context.unit_decl_base_unit = Some(identifier.value); + Ok(Expr::Var(DECL_UNIT.into())) } else { let mut chars = identifier.value.chars(); let mut left = Expr::Var(chars.next().unwrap().to_string()); From a81cef2a86cf9fb694ee8722eacfac1f66d67ada Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 14 Jun 2020 21:35:56 +0200 Subject: [PATCH 06/19] Added support for 'minus' expressions in the inverter and added some comments. --- kalk/src/inverter.rs | 35 ++++++++++++++++++++++++++++++++--- kalk/src/parser.rs | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index 64471da..c6a7c04 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -8,6 +8,7 @@ impl Expr { pub fn invert(&self, symbol_table: &mut SymbolTable) -> Result { let target_expr = Expr::Var(DECL_UNIT.into()); let result = invert(target_expr, symbol_table, self); + Ok(result?.0) } } @@ -21,7 +22,7 @@ fn invert( Expr::Binary(left, op, right) => { invert_binary(target_expr, symbol_table, &left, op, &right) } - Expr::Unary(_, _) => Ok((target_expr, expr.clone())), + Expr::Unary(op, expr) => invert_unary(target_expr, op, &expr), Expr::Unit(identifier, expr) => invert_unit(target_expr, &identifier, &expr), Expr::Var(_) => Ok((target_expr, expr.clone())), Expr::Group(expr) => Ok((target_expr, *expr.clone())), @@ -41,7 +42,19 @@ fn invert_binary( ) -> Result<(Expr, Expr), CalcError> { let op_inv = match op { TokenKind::Plus => TokenKind::Minus, - TokenKind::Minus => TokenKind::Plus, + TokenKind::Minus => { + if let Expr::Group(inside_group) = right { + return invert_binary( + target_expr, + symbol_table, + left, + op, + &multiply_in(&Expr::Literal(String::from("-1")), inside_group)?, + ); + } + + TokenKind::Plus + } TokenKind::Star => { if let Expr::Group(inside_group) = left { return invert( @@ -87,13 +100,29 @@ fn invert_binary( )?); } + let final_target_expr = Expr::Binary(Box::new(target_expr), op_inv, Box::new(left.clone())); + Ok(invert( - Expr::Binary(Box::new(target_expr), op_inv, Box::new(left.clone())), + if op == &TokenKind::Minus { + Expr::Unary(TokenKind::Minus, Box::new(final_target_expr)) + } else { + final_target_expr + }, symbol_table, right, )?) } +fn invert_unary(target_expr: Expr, op: &TokenKind, expr: &Expr) -> Result<(Expr, Expr), CalcError> { + match op { + TokenKind::Minus => Ok(( + Expr::Unary(TokenKind::Minus, Box::new(target_expr)), + expr.clone(), + )), + _ => unimplemented!(), + } +} + // Not necessary yet fn invert_unit( _target_expr: Expr, diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 6c1b84b..9aeb7af 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -195,6 +195,7 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { context.symbol_table.insert(stmt.clone()); context.symbol_table.insert(stmt_inv); + println!("{:#?}", context.symbol_table); Ok(stmt) } From 112d8e78ea1e3be859ddc0e2a3bdcf7b84e40e1a Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Sun, 14 Jun 2020 21:54:39 +0200 Subject: [PATCH 07/19] Added support for 'minus' expressions in the inverter and added some comments. --- kalk/src/inverter.rs | 20 +++++++++++++++++--- kalk/src/parser.rs | 1 - 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index c6a7c04..7dc3966 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -43,6 +43,8 @@ fn invert_binary( let op_inv = match op { TokenKind::Plus => TokenKind::Minus, TokenKind::Minus => { + // Eg. a-(b+c) + // Multiply "-1" into the group, resulting in it becoming a normal expression. Then invert it normally. if let Expr::Group(inside_group) = right { return invert_binary( target_expr, @@ -56,6 +58,8 @@ fn invert_binary( TokenKind::Plus } TokenKind::Star => { + // If the left expression is a group, multiply the right expression into it, dissolving the group. + // It can then be inverted normally. if let Expr::Group(inside_group) = left { return invert( target_expr, @@ -64,6 +68,7 @@ fn invert_binary( ); } + // Same as above but left/right switched. if let Expr::Group(inside_group) = right { return invert(target_expr, symbol_table, &multiply_in(left, inside_group)?); } @@ -71,6 +76,8 @@ fn invert_binary( TokenKind::Slash } TokenKind::Slash => { + // Eg. (a+b)/c + // Just dissolve the group. Nothing more needs to be done mathematically. if let Expr::Group(inside_group) = left { return invert( target_expr, @@ -79,6 +86,8 @@ fn invert_binary( ); } + // Eg. a/(b+c) + // Same as above. if let Expr::Group(inside_group) = right { return invert( target_expr, @@ -92,6 +101,8 @@ fn invert_binary( _ => unreachable!(), }; + // If the left expression contains the unit, invert the right one instead, + // since the unit should not be moved. if contains_the_unit(left) { return Ok(invert( Expr::Binary(Box::new(target_expr), op_inv, Box::new(right.clone())), @@ -100,16 +111,19 @@ fn invert_binary( )?); } + // Otherwise, invert the left side. let final_target_expr = Expr::Binary(Box::new(target_expr), op_inv, Box::new(left.clone())); - Ok(invert( - if op == &TokenKind::Minus { + // Eg. 2-a + // If the operator is minus (and the left expression is being inverted), + // make the target expression negative to keep balance. + if let TokenKind::Minus = op { Expr::Unary(TokenKind::Minus, Box::new(final_target_expr)) } else { final_target_expr }, symbol_table, - right, + right, // Then invert the right expression. )?) } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 9aeb7af..6c1b84b 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -195,7 +195,6 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { context.symbol_table.insert(stmt.clone()); context.symbol_table.insert(stmt_inv); - println!("{:#?}", context.symbol_table); Ok(stmt) } From 7f3fb7c045c8d6cb0924a5f442af2be61923704b Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Mon, 15 Jun 2020 19:10:55 +0200 Subject: [PATCH 08/19] Integrated the angle unit system with then new dynamic unit system. --- kalk/src/interpreter.rs | 80 ++++++++++++++++++++++++++-------------- kalk/src/lexer.rs | 8 +--- kalk/src/parser.rs | 24 ++++++------ kalk/src/prelude.rs | 66 ++++++++++++++++++++------------- kalk/src/test_helpers.rs | 8 ++++ kalk_cli/src/main.rs | 15 ++------ 6 files changed, 118 insertions(+), 83 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index bf38a38..6959929 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -1,7 +1,6 @@ use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; use crate::parser::CalcError; -use crate::parser::Unit; use crate::parser::DECL_UNIT; use crate::prelude; use crate::symbol_table::SymbolTable; @@ -10,14 +9,14 @@ use rug::Float; pub struct Context<'a> { symbol_table: &'a mut SymbolTable, - angle_unit: Unit, + angle_unit: String, precision: u32, } impl<'a> Context<'a> { - pub fn new(symbol_table: &'a mut SymbolTable, angle_unit: &Unit, precision: u32) -> Self { + pub fn new(symbol_table: &'a mut SymbolTable, angle_unit: &str, precision: u32) -> Self { Context { - angle_unit: angle_unit.clone(), + angle_unit: angle_unit.into(), symbol_table, precision, } @@ -68,7 +67,7 @@ fn eval_expr(context: &mut Context, expr: &Expr) -> Result { 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::Unit(_, expr) => eval_unit_expr(context, expr), + 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), @@ -118,11 +117,20 @@ fn eval_unary_expr(context: &mut Context, op: &TokenKind, expr: &Expr) -> Result } } -fn eval_unit_expr(context: &mut Context, expr: &Expr) -> Result { +fn eval_unit_expr( + context: &mut Context, + identifier: &str, + expr: &Expr, +) -> Result { + 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) } -fn convert_unit( +pub fn convert_unit( context: &mut Context, expr: &Expr, from_unit: &str, @@ -175,12 +183,12 @@ fn eval_fn_call_expr( let prelude_func = match expressions.len() { 1 => { let x = eval_expr(context, &expressions[0])?; - prelude::call_unary_func(identifier, x, &context.angle_unit) + 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])?; - prelude::call_binary_func(identifier, x, y, &context.angle_unit) + prelude::call_binary_func(context, identifier, x, y, &context.angle_unit.clone()) } _ => None, }; @@ -259,10 +267,16 @@ mod tests { fn interpret(stmt: Stmt) -> Result, CalcError> { let mut symbol_table = SymbolTable::new(); - let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); + let mut context = Context::new(&mut symbol_table, "rad", PRECISION); + context.interpret(vec![stmt]) } + fn cmp(x: Float, y: f64) -> bool { + println!("{} = {}", x.to_f64(), y); + (x.to_f64() - y).abs() < 0.0001 + } + #[test] fn test_literal() { let stmt = Stmt::Expr(literal("1")); @@ -292,26 +306,38 @@ mod tests { fn test_unary() { let neg = Stmt::Expr(unary(Minus, literal("1"))); let fact = Stmt::Expr(unary(Exclamation, literal("5"))); - let fact_dec = Stmt::Expr(unary(Exclamation, literal("5.2"))); assert_eq!(interpret(neg).unwrap().unwrap(), -1); assert_eq!(interpret(fact).unwrap().unwrap(), 120); - - let fact_dec_result = interpret(fact_dec).unwrap().unwrap(); - assert!(fact_dec_result > 169.406 && fact_dec_result < 169.407); } - /*#[test] - fn test_unit() { - let rad = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Rad))); - let deg = Stmt::Expr(Box::new(Expr::Unit(literal("1"), Deg))); + #[test] + fn test_angle_units() { + let rad_explicit = Stmt::Expr(fn_call("sin", vec![*unit("rad", literal("1"))])); + let deg_explicit = Stmt::Expr(fn_call("sin", vec![*unit("deg", literal("1"))])); + //let implicit = Stmt::Expr(fn_call("sin", vec![*literal("1")])); - assert_eq!(interpret(rad).unwrap().unwrap(), 1); - assert!( - (interpret(deg).unwrap().unwrap() - Float::with_val(PRECISION, 0.017456)).abs() - < Float::with_val(PRECISION, 0.0001) - ); - }*/ + assert!(cmp(interpret(rad_explicit).unwrap().unwrap(), 0.84147098)); + assert!(cmp(interpret(deg_explicit).unwrap().unwrap(), 0.01745240)); + + // TODO: Get this to work. + /*let mut rad_symbol_table = SymbolTable::new(); + let mut deg_symbol_table = SymbolTable::new(); + let mut rad_context = Context::new(&mut rad_symbol_table, "rad", PRECISION); + let mut deg_context = Context::new(&mut deg_symbol_table, "deg", PRECISION); + + assert!(cmp( + rad_context + .interpret(vec![implicit.clone()]) + .unwrap() + .unwrap(), + 0.84147098 + )); + assert!(cmp( + deg_context.interpret(vec![implicit]).unwrap().unwrap(), + 0.01745240 + ));*/ + } #[test] fn test_var() { @@ -321,7 +347,7 @@ mod tests { let mut symbol_table = SymbolTable::new(); symbol_table.insert(var_decl("x", literal("1"))); - let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); + let mut context = Context::new(&mut symbol_table, "rad", PRECISION); assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1); } @@ -339,7 +365,7 @@ mod tests { fn test_var_decl() { let stmt = var_decl("x", literal("1")); let mut symbol_table = SymbolTable::new(); - Context::new(&mut symbol_table, &Unit::Radians, PRECISION) + Context::new(&mut symbol_table, "rad", PRECISION) .interpret(vec![stmt]) .unwrap(); @@ -358,7 +384,7 @@ mod tests { binary(var("x"), TokenKind::Plus, literal("2")), )); - let mut context = Context::new(&mut symbol_table, &Unit::Radians, PRECISION); + let mut context = Context::new(&mut symbol_table, "rad", PRECISION); assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3); } diff --git a/kalk/src/lexer.rs b/kalk/src/lexer.rs index 6bc91c5..5c590c9 100644 --- a/kalk/src/lexer.rs +++ b/kalk/src/lexer.rs @@ -17,9 +17,7 @@ pub enum TokenKind { Exclamation, UnitKeyword, - To, - Deg, - Rad, + ToKeyword, Pipe, OpenCeil, @@ -170,10 +168,8 @@ impl<'a> Lexer<'a> { } let kind = match value.as_ref() { - "deg" | "°" => TokenKind::Deg, - "rad" => TokenKind::Rad, "unit" => TokenKind::UnitKeyword, - "to" => TokenKind::To, + "to" => TokenKind::ToKeyword, _ => TokenKind::Identifier, }; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 6c1b84b..5858316 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -7,6 +7,7 @@ use crate::{ use rug::Float; pub const DECL_UNIT: &'static str = ".u"; +pub const DEFAULT_ANGLE_UNIT: &'static str = "rad"; /// Struct containing the current state of the parser. It stores user-defined functions and variables. /// # Examples @@ -20,7 +21,7 @@ pub struct Context { tokens: Vec, pos: usize, symbol_table: SymbolTable, - angle_unit: Unit, + angle_unit: String, /// This is true whenever the parser is currently parsing a unit declaration. /// It is necessary to keep track of this in order to know when to find (figure out) units that haven't been defined yet. /// Unit names are instead treated as variables. @@ -32,18 +33,22 @@ pub struct Context { impl Context { pub fn new() -> Self { - Context { + let mut context = Self { tokens: Vec::new(), pos: 0, symbol_table: SymbolTable::new(), - angle_unit: Unit::Radians, + angle_unit: DEFAULT_ANGLE_UNIT.into(), parsing_unit_decl: false, unit_decl_base_unit: None, - } + }; + + parse(&mut context, crate::prelude::INIT).unwrap(); + + context } - pub fn set_angle_unit(mut self, unit: Unit) -> Self { - self.angle_unit = unit; + pub fn set_angle_unit(mut self, unit: &str) -> Self { + self.angle_unit = unit.into(); self } @@ -55,13 +60,6 @@ impl Default for Context { } } -/// Mathematical unit used in calculations. -#[derive(Debug, Clone, PartialEq)] -pub enum Unit { - Radians, - Degrees, -} - /// Error that occured during parsing or evaluation. #[derive(Debug, Clone, PartialEq)] pub enum CalcError { diff --git a/kalk/src/prelude.rs b/kalk/src/prelude.rs index 2b6d8a6..c9f73b4 100644 --- a/kalk/src/prelude.rs +++ b/kalk/src/prelude.rs @@ -1,6 +1,10 @@ +use crate::ast::Expr; +use crate::interpreter; use rug::Float; use FuncType::*; +pub const INIT: &'static str = "unit deg = (rad*180)/pi"; + pub const CONSTANTS: phf::Map<&'static str, &'static str> = phf::phf_map! { "pi" => "3.14159265", "π" => "3.14159265", @@ -11,7 +15,6 @@ pub const CONSTANTS: phf::Map<&'static str, &'static str> = phf::phf_map! { "ϕ" => "1.61803398", }; -use crate::parser::Unit; use funcs::*; pub const UNARY_FUNCS: phf::Map<&'static str, UnaryFuncInfo> = phf::phf_map! { "cos" => UnaryFuncInfo(cos, Trig), @@ -75,57 +78,76 @@ pub struct UnaryFuncInfo(fn(Float) -> Float, FuncType); pub struct BinaryFuncInfo(fn(Float, Float) -> Float, FuncType); impl UnaryFuncInfo { - fn call(&self, x: Float, angle_unit: &Unit) -> Float { + fn call(&self, context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float { let func = self.0; match self.1 { - FuncType::Trig => func(from_angle_unit(x, angle_unit)), - FuncType::InverseTrig => to_angle_unit(func(x), angle_unit), + FuncType::Trig => func(from_angle_unit(context, x, angle_unit)), + FuncType::InverseTrig => to_angle_unit(context, func(x), angle_unit), FuncType::Other => func(x), } } } impl BinaryFuncInfo { - fn call(&self, x: Float, y: Float, angle_unit: &Unit) -> Float { + fn call( + &self, + context: &mut interpreter::Context, + x: Float, + y: Float, + angle_unit: &str, + ) -> Float { let func = self.0; match self.1 { FuncType::Trig => func( - from_angle_unit(x, angle_unit), - from_angle_unit(y, angle_unit), + from_angle_unit(context, x, angle_unit), + from_angle_unit(context, y, angle_unit), ), - FuncType::InverseTrig => to_angle_unit(func(x, y), angle_unit), + FuncType::InverseTrig => to_angle_unit(context, func(x, y), angle_unit), FuncType::Other => func(x, y), } } } -pub fn call_unary_func(name: &str, x: Float, angle_unit: &Unit) -> Option { +pub fn call_unary_func( + context: &mut interpreter::Context, + name: &str, + x: Float, + angle_unit: &str, +) -> Option { if let Some(func_info) = UNARY_FUNCS.get(name) { - Some(func_info.call(x, &angle_unit)) + Some(func_info.call(context, x, &angle_unit)) } else { None } } -pub fn call_binary_func(name: &str, x: Float, y: Float, angle_unit: &Unit) -> Option { +pub fn call_binary_func( + context: &mut interpreter::Context, + name: &str, + x: Float, + y: Float, + angle_unit: &str, +) -> Option { if let Some(func_info) = BINARY_FUNCS.get(name) { - Some(func_info.call(x, y, angle_unit)) + Some(func_info.call(context, x, y, angle_unit)) } else { None } } -fn to_angle_unit(x: Float, angle_unit: &Unit) -> Float { +fn to_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float { match angle_unit { - Unit::Radians => x, - Unit::Degrees => special_funcs::to_degrees(x), + "rad" => x, + _ => interpreter::convert_unit(context, &Expr::Literal(x.to_string()), "rad", angle_unit) + .unwrap(), } } -fn from_angle_unit(x: Float, angle_unit: &Unit) -> Float { +fn from_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float { match angle_unit { - Unit::Radians => x, - Unit::Degrees => special_funcs::to_radians(x), + "rad" => x, + _ => interpreter::convert_unit(context, &Expr::Literal(x.to_string()), angle_unit, "rad") + .unwrap(), } } @@ -135,14 +157,6 @@ pub mod special_funcs { pub fn factorial(x: Float) -> Float { ((x + 1) as Float).gamma() } - - pub fn to_degrees(x: Float) -> Float { - Float::with_val(53, x.to_f64().to_degrees()) - } - - pub fn to_radians(x: Float) -> Float { - Float::with_val(53, x.to_f64().to_radians()) - } } mod funcs { diff --git a/kalk/src/test_helpers.rs b/kalk/src/test_helpers.rs index 89d8d9f..5aa202d 100644 --- a/kalk/src/test_helpers.rs +++ b/kalk/src/test_helpers.rs @@ -36,6 +36,10 @@ pub fn group(expr: Box) -> Box { Box::new(Expr::Group(expr)) } +pub fn unit(identifier: &str, expr: Box) -> Box { + Box::new(Expr::Unit(identifier.into(), expr)) +} + pub fn var_decl(identifier: &str, value: Box) -> Stmt { Stmt::VarDecl(identifier.into(), value) } @@ -43,3 +47,7 @@ pub fn var_decl(identifier: &str, value: Box) -> Stmt { pub fn fn_decl(identifier: &str, parameters: Vec, value: Box) -> Stmt { Stmt::FnDecl(identifier.into(), parameters, value) } + +pub fn unit_decl(unit: &str, base_unit: &str, expr: Box) -> Stmt { + Stmt::UnitDecl(unit.into(), base_unit.into(), expr) +} diff --git a/kalk_cli/src/main.rs b/kalk_cli/src/main.rs index 5440718..4f8d379 100644 --- a/kalk_cli/src/main.rs +++ b/kalk_cli/src/main.rs @@ -2,13 +2,12 @@ mod output; mod repl; use kalk::parser; -use kalk::parser::Unit; use std::env; use std::fs::File; use std::io::Read; fn main() { - let mut parser_context = parser::Context::new().set_angle_unit(get_angle_unit()); + let mut parser_context = parser::Context::new().set_angle_unit(&get_angle_unit()); // Command line argument input, execute it and exit. let mut args = env::args().skip(1); @@ -64,16 +63,10 @@ kalk [OPTIONS] [INPUT] } } -fn get_angle_unit() -> Unit { +fn get_angle_unit() -> String { if let Ok(angle_unit_var) = env::var("ANGLE_UNIT") { - match angle_unit_var.as_ref() { - "radians" => Unit::Radians, - "degrees" => Unit::Degrees, - _ => { - panic!("Unexpected angle unit: {}.", angle_unit_var); - } - } + angle_unit_var } else { - Unit::Radians + String::from("rad") } } From 99fbdd68832ff288392dc236b4cf6e2c7a1ee952 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Mon, 15 Jun 2020 21:27:47 +0200 Subject: [PATCH 09/19] Fixed the broken unit tests in the parser and completed the test_angle_units test in the interpreter. --- Cargo.lock | 1 + kalk/Cargo.toml | 1 + kalk/src/interpreter.rs | 43 +++++++++++++++++++++++++++++++++++----- kalk/src/inverter.rs | 32 +++++++++++++++++++++--------- kalk/src/parser.rs | 29 +++++++++++++++++---------- kalk/src/symbol_table.rs | 14 +++++++------ 6 files changed, 89 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76e1cbe..57a744e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,7 @@ dependencies = [ name = "kalk" version = "0.1.10" dependencies = [ + "lazy_static", "phf", "regex", "rug", diff --git a/kalk/Cargo.toml b/kalk/Cargo.toml index 9fcd679..f89239b 100644 --- a/kalk/Cargo.toml +++ b/kalk/Cargo.toml @@ -15,3 +15,4 @@ phf = { version = "0.8", features = ["macros"] } rug = "1.9.0" test-case = "1.0.0" regex = "1" +lazy_static = "1.4.0" diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 6959929..61fe871 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -265,10 +265,38 @@ mod tests { const PRECISION: u32 = 53; + lazy_static::lazy_static! { + static ref DEG_RAD_UNIT: Stmt = unit_decl( + "deg", + "rad", + binary( + binary( + var(crate::parser::DECL_UNIT), + TokenKind::Star, + literal("180"), + ), + TokenKind::Slash, + var("pi"), + ), + ); + static ref RAD_DEG_UNIT: Stmt = unit_decl( + "rad", + "deg", + binary( + binary(var(crate::parser::DECL_UNIT), TokenKind::Star, var("pi")), + TokenKind::Slash, + literal("180"), + ), + ); + } + fn interpret(stmt: Stmt) -> Result, CalcError> { let mut symbol_table = SymbolTable::new(); - let mut context = Context::new(&mut symbol_table, "rad", PRECISION); + symbol_table + .insert(DEG_RAD_UNIT.clone()) + .insert(RAD_DEG_UNIT.clone()); + let mut context = Context::new(&mut symbol_table, "rad", PRECISION); context.interpret(vec![stmt]) } @@ -315,14 +343,19 @@ mod tests { fn test_angle_units() { let rad_explicit = Stmt::Expr(fn_call("sin", vec![*unit("rad", literal("1"))])); let deg_explicit = Stmt::Expr(fn_call("sin", vec![*unit("deg", literal("1"))])); - //let implicit = Stmt::Expr(fn_call("sin", vec![*literal("1")])); + let implicit = Stmt::Expr(fn_call("sin", vec![*literal("1")])); assert!(cmp(interpret(rad_explicit).unwrap().unwrap(), 0.84147098)); assert!(cmp(interpret(deg_explicit).unwrap().unwrap(), 0.01745240)); - // TODO: Get this to work. - /*let mut rad_symbol_table = SymbolTable::new(); + let mut rad_symbol_table = SymbolTable::new(); + rad_symbol_table + .insert(DEG_RAD_UNIT.clone()) + .insert(RAD_DEG_UNIT.clone()); let mut deg_symbol_table = SymbolTable::new(); + deg_symbol_table + .insert(DEG_RAD_UNIT.clone()) + .insert(RAD_DEG_UNIT.clone()); let mut rad_context = Context::new(&mut rad_symbol_table, "rad", PRECISION); let mut deg_context = Context::new(&mut deg_symbol_table, "deg", PRECISION); @@ -336,7 +369,7 @@ mod tests { assert!(cmp( deg_context.interpret(vec![implicit]).unwrap().unwrap(), 0.01745240 - ));*/ + )); } #[test] diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index 7dc3966..353deed 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -51,7 +51,7 @@ fn invert_binary( symbol_table, left, op, - &multiply_in(&Expr::Literal(String::from("-1")), inside_group)?, + &multiply_into(&Expr::Literal(String::from("-1")), inside_group)?, ); } @@ -64,13 +64,17 @@ fn invert_binary( return invert( target_expr, symbol_table, - &multiply_in(right, inside_group)?, + &multiply_into(right, inside_group)?, ); } // Same as above but left/right switched. if let Expr::Group(inside_group) = right { - return invert(target_expr, symbol_table, &multiply_in(left, inside_group)?); + return invert( + target_expr, + symbol_table, + &multiply_into(left, inside_group)?, + ); } TokenKind::Slash @@ -130,14 +134,15 @@ fn invert_binary( fn invert_unary(target_expr: Expr, op: &TokenKind, expr: &Expr) -> Result<(Expr, Expr), CalcError> { match op { TokenKind::Minus => Ok(( + // Make the target expression negative Expr::Unary(TokenKind::Minus, Box::new(target_expr)), - expr.clone(), + expr.clone(), // And then continue inverting the inner-expression. )), _ => unimplemented!(), } } -// Not necessary yet +// TODO: Implement fn invert_unit( _target_expr: Expr, _identifier: &str, @@ -152,6 +157,7 @@ fn invert_fn_call( identifier: &str, arguments: &Vec, ) -> Result<(Expr, Expr), CalcError> { + // Get the function definition from the symbol table. let (parameters, body) = if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() { (parameters, body) @@ -159,6 +165,7 @@ fn invert_fn_call( return Err(CalcError::UndefinedFn(identifier.into())); }; + // Make sure the input-expression is valid. if parameters.len() != arguments.len() { return Err(CalcError::IncorrectAmountOfArguments( parameters.len(), @@ -167,6 +174,7 @@ fn invert_fn_call( )); } + // Make the parameters usable as variables inside the function. let mut parameters_iter = parameters.iter(); for argument in arguments { symbol_table.insert(Stmt::VarDecl( @@ -175,10 +183,12 @@ fn invert_fn_call( )); } + // Invert everything in the function body. invert(target_expr, symbol_table, &body) } fn contains_the_unit(expr: &Expr) -> bool { + // Recursively scan the expression for the unit. match expr { Expr::Binary(left, _, right) => contains_the_unit(left) || contains_the_unit(right), Expr::Unary(_, expr) => contains_the_unit(expr), @@ -198,21 +208,25 @@ fn contains_the_unit(expr: &Expr) -> bool { } } -fn multiply_in(expr: &Expr, base_expr: &Expr) -> Result { +/// Multiply an expression into a group. +fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result { match base_expr { Expr::Binary(left, op, right) => match op { + // If + or -, multiply the expression with each term. TokenKind::Plus | TokenKind::Minus => Ok(Expr::Binary( - Box::new(multiply_in(expr, &left)?), + Box::new(multiply_into(expr, &left)?), op.clone(), - Box::new(multiply_in(expr, &right)?), + Box::new(multiply_into(expr, &right)?), )), + // If * or /, only multiply with the first factor. TokenKind::Star | TokenKind::Slash => Ok(Expr::Binary( - Box::new(multiply_in(expr, &left)?), + Box::new(multiply_into(expr, &left)?), op.clone(), right.clone(), )), _ => unimplemented!(), }, + // If it's a literal, just multiply them together. Expr::Literal(_) | Expr::Var(_) => Ok(Expr::Binary( Box::new(expr.clone()), TokenKind::Star, diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 5858316..e78aa28 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -414,6 +414,7 @@ mod tests { fn parse_with_context(context: &mut Context, tokens: Vec) -> Result { context.tokens = tokens; + context.pos = 0; parse_stmt(context) } @@ -421,6 +422,7 @@ mod tests { fn parse(tokens: Vec) -> Result { let mut context = Context::new(); context.tokens = tokens; + context.pos = 0; parse_stmt(&mut context) } @@ -448,6 +450,7 @@ mod tests { token(Slash, ""), token(Literal, "5"), token(ClosedParenthesis, ""), + token(EOF, ""), ]; assert_eq!( @@ -480,6 +483,7 @@ mod tests { token(Literal, "4"), token(Plus, ""), token(Literal, "5"), + token(EOF, ""), ]; assert_eq!( @@ -500,20 +504,20 @@ mod tests { ); } - /*#[test_case(Deg)] - #[test_case(Rad)] - fn test_unary(angle_unit: TokenKind) { - let tokens = vec![ - token(Minus, ""), - token(Literal, "1"), - token(angle_unit.clone(), ""), - ]; + #[test] + fn test_unit() { + let tokens = vec![token(Literal, "1"), token(Identifier, "a")]; + + let mut context = Context::new(); + context + .symbol_table + .insert(unit_decl("a", "b", var(super::DECL_UNIT))); assert_eq!( - parse(tokens).unwrap(), - Stmt::Expr(unary(Minus, Box::new(Expr::Unit(literal("1"), angle_unit)))) + parse_with_context(&mut context, tokens).unwrap(), + Stmt::Expr(unit("a", literal("1"))) ); - }*/ + } #[test] fn test_var_decl() { @@ -523,6 +527,7 @@ mod tests { token(Literal, "1"), token(Plus, ""), token(Literal, "2"), + token(EOF, ""), ]; assert_eq!( @@ -542,6 +547,7 @@ mod tests { token(Literal, "1"), token(Plus, ""), token(Literal, "2"), + token(EOF, ""), ]; assert_eq!( @@ -565,6 +571,7 @@ mod tests { token(ClosedParenthesis, ""), token(Plus, ""), token(Literal, "3"), + token(EOF, ""), ]; let mut context = Context::new(); diff --git a/kalk/src/symbol_table.rs b/kalk/src/symbol_table.rs index 4ab0e9d..aead965 100644 --- a/kalk/src/symbol_table.rs +++ b/kalk/src/symbol_table.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; #[derive(Debug)] pub struct SymbolTable { - hashmap: HashMap, - unit_types: HashMap, + pub(crate) hashmap: HashMap, + pub(crate) unit_types: HashMap, } impl SymbolTable { @@ -15,22 +15,24 @@ impl SymbolTable { } } - pub fn insert(&mut self, value: Stmt) -> Option { + pub fn insert(&mut self, value: Stmt) -> &mut Self { match &value { Stmt::VarDecl(identifier, _) => { - self.hashmap.insert(format!("var.{}", identifier), value) + self.hashmap.insert(format!("var.{}", identifier), value); } Stmt::UnitDecl(identifier, to_unit, _) => { self.unit_types.insert(identifier.to_string(), ()); self.unit_types.insert(to_unit.to_string(), ()); self.hashmap - .insert(format!("unit.{}.{}", identifier, to_unit), value) + .insert(format!("unit.{}.{}", identifier, to_unit), value); } Stmt::FnDecl(identifier, _, _) => { - self.hashmap.insert(format!("fn.{}", identifier), value) + self.hashmap.insert(format!("fn.{}", identifier), value); } _ => panic!("Can only insert VarDecl, UnitDecl and FnDecl into symbol table."), } + + self } pub fn get_var(&self, key: &str) -> Option<&Stmt> { From 2f7c1870f773b981602784e638768c4d5f92910f Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Mon, 15 Jun 2020 21:31:55 +0200 Subject: [PATCH 10/19] Updated README to ignore .vscode. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d26f037..c7f0aeb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ target */target kalk/Cargo.lock kalk_cli/test +.vscode/ From aa449ee92b6ac6a1142a7f97c5bd672278ca8c8e Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Wed, 17 Jun 2020 17:45:46 +0200 Subject: [PATCH 11/19] Fixed inversion for (function) variables and added unit tests for the inverter. --- kalk/src/inverter.rs | 240 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 229 insertions(+), 11 deletions(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index 353deed..ffdd22e 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -24,7 +24,7 @@ fn invert( } Expr::Unary(op, expr) => invert_unary(target_expr, op, &expr), Expr::Unit(identifier, expr) => invert_unit(target_expr, &identifier, &expr), - Expr::Var(_) => Ok((target_expr, expr.clone())), + Expr::Var(identifier) => invert_var(target_expr, symbol_table, identifier), Expr::Group(expr) => Ok((target_expr, *expr.clone())), Expr::FnCall(identifier, arguments) => { invert_fn_call(target_expr, symbol_table, &identifier, arguments) @@ -50,7 +50,7 @@ fn invert_binary( target_expr, symbol_table, left, - op, + &TokenKind::Plus, &multiply_into(&Expr::Literal(String::from("-1")), inside_group)?, ); } @@ -107,7 +107,7 @@ fn invert_binary( // If the left expression contains the unit, invert the right one instead, // since the unit should not be moved. - if contains_the_unit(left) { + if contains_the_unit(symbol_table, left) { return Ok(invert( Expr::Binary(Box::new(target_expr), op_inv, Box::new(right.clone())), symbol_table, @@ -151,6 +151,18 @@ fn invert_unit( unimplemented!() } +fn invert_var( + target_expr: Expr, + symbol_table: &mut SymbolTable, + identifier: &str, +) -> Result<(Expr, Expr), CalcError> { + if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(identifier).cloned() { + invert(target_expr, symbol_table, &var_expr) + } else { + Ok((target_expr, Expr::Var(identifier.into()))) + } +} + fn invert_fn_call( target_expr: Expr, symbol_table: &mut SymbolTable, @@ -165,7 +177,7 @@ fn invert_fn_call( return Err(CalcError::UndefinedFn(identifier.into())); }; - // Make sure the input-expression is valid. + // Make sure the input is valid. if parameters.len() != arguments.len() { return Err(CalcError::IncorrectAmountOfArguments( parameters.len(), @@ -187,17 +199,26 @@ fn invert_fn_call( invert(target_expr, symbol_table, &body) } -fn contains_the_unit(expr: &Expr) -> bool { +fn contains_the_unit(symbol_table: &SymbolTable, expr: &Expr) -> bool { // Recursively scan the expression for the unit. match expr { - Expr::Binary(left, _, right) => contains_the_unit(left) || contains_the_unit(right), - Expr::Unary(_, expr) => contains_the_unit(expr), - Expr::Unit(_, expr) => contains_the_unit(expr), - Expr::Var(identifier) => identifier == DECL_UNIT, - Expr::Group(expr) => contains_the_unit(expr), + Expr::Binary(left, _, right) => { + contains_the_unit(symbol_table, left) || contains_the_unit(symbol_table, right) + } + Expr::Unary(_, expr) => contains_the_unit(symbol_table, expr), + Expr::Unit(_, expr) => contains_the_unit(symbol_table, expr), + Expr::Var(identifier) => { + identifier == DECL_UNIT + || if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(identifier) { + contains_the_unit(symbol_table, var_expr) + } else { + false + } + } + Expr::Group(expr) => contains_the_unit(symbol_table, expr), Expr::FnCall(_, args) => { for arg in args { - if contains_the_unit(arg) { + if contains_the_unit(symbol_table, arg) { return true; } } @@ -235,3 +256,200 @@ fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result { _ => unimplemented!(), } } + +#[allow(unused_imports, dead_code)] // Getting warnings for some reason +mod tests { + use crate::ast::Expr; + use crate::lexer::TokenKind::*; + use crate::symbol_table::SymbolTable; + use crate::test_helpers::*; + + fn decl_unit() -> Box { + Box::new(Expr::Var(crate::parser::DECL_UNIT.into())) + } + + #[test] + fn test_binary() { + let ladd = binary(decl_unit(), Plus, literal("1")); + let lsub = binary(decl_unit(), Minus, literal("1")); + let lmul = binary(decl_unit(), Star, literal("1")); + let ldiv = binary(decl_unit(), Slash, literal("1")); + + let radd = binary(literal("1"), Plus, decl_unit()); + let rsub = binary(literal("1"), Minus, decl_unit()); + let rmul = binary(literal("1"), Star, decl_unit()); + let rdiv = binary(literal("1"), Slash, decl_unit()); + + let mut symbol_table = SymbolTable::new(); + assert_eq!( + ladd.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Minus, literal("1")) + ); + assert_eq!( + lsub.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Plus, literal("1")) + ); + assert_eq!( + lmul.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Slash, literal("1")) + ); + assert_eq!( + ldiv.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Star, literal("1")) + ); + + assert_eq!( + radd.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Minus, literal("1")) + ); + assert_eq!( + rsub.invert(&mut symbol_table).unwrap(), + *unary(Minus, binary(decl_unit(), Plus, literal("1"))) + ); + assert_eq!( + rmul.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Slash, literal("1")) + ); + assert_eq!( + rdiv.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Star, literal("1")) + ); + } + + #[test] + fn test_unary() { + let neg = unary(Minus, decl_unit()); + + let mut symbol_table = SymbolTable::new(); + assert_eq!(neg.invert(&mut symbol_table).unwrap(), *neg); + } + + #[test] + fn test_fn_call() { + let call_with_literal = binary(fn_call("f", vec![*literal("2")]), Plus, decl_unit()); + let call_with_decl_unit = fn_call("f", vec![*decl_unit()]); + let call_with_decl_unit_and_literal = + fn_call("f", vec![*binary(decl_unit(), Plus, literal("2"))]); + let decl = fn_decl( + "f", + vec![String::from("x")], + binary(var("x"), Plus, literal("1")), + ); + + let mut symbol_table = SymbolTable::new(); + symbol_table.insert(decl); + assert_eq!( + call_with_literal.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Minus, fn_call("f", vec![*literal("2")])), + ); + assert_eq!( + call_with_decl_unit.invert(&mut symbol_table).unwrap(), + *binary(decl_unit(), Minus, literal("1")) + ); + assert_eq!( + call_with_decl_unit_and_literal + .invert(&mut symbol_table) + .unwrap(), + *binary( + binary(decl_unit(), Minus, literal("1")), + Minus, + literal("2") + ) + ); + } + + #[test] + fn test_group() { + let group_x = binary( + group(binary(decl_unit(), Plus, literal("3"))), + Star, + literal("2"), + ); + let group_unary_minus = binary( + literal("2"), + Minus, + group(binary(decl_unit(), Plus, literal("3"))), + ); + let x_group_add = binary( + literal("2"), + Star, + group(binary(decl_unit(), Plus, literal("3"))), + ); + let x_group_sub = binary( + literal("2"), + Star, + group(binary(decl_unit(), Minus, literal("3"))), + ); + let x_group_mul = binary( + literal("2"), + Star, + group(binary(decl_unit(), Star, literal("3"))), + ); + let x_group_div = binary( + literal("2"), + Star, + group(binary(decl_unit(), Slash, literal("3"))), + ); + + let mut symbol_table = SymbolTable::new(); + assert_eq!( + group_x.invert(&mut symbol_table).unwrap(), + *binary( + binary(decl_unit(), Minus, binary(literal("2"), Star, literal("3"))), + Slash, + literal("2") + ) + ); + assert_eq!( + group_unary_minus.invert(&mut symbol_table).unwrap(), + *binary( + binary( + binary(decl_unit(), Minus, literal("2")), + Minus, + binary(literal("-1"), Star, literal("3")) + ), + Slash, + literal("-1") + ) + ); + assert_eq!( + x_group_add.invert(&mut symbol_table).unwrap(), + *binary( + binary(decl_unit(), Minus, binary(literal("2"), Star, literal("3"))), + Slash, + literal("2") + ) + ); + assert_eq!( + x_group_sub.invert(&mut symbol_table).unwrap(), + *binary( + binary(decl_unit(), Plus, binary(literal("2"), Star, literal("3"))), + Slash, + literal("2") + ) + ); + assert_eq!( + x_group_mul.invert(&mut symbol_table).unwrap(), + *binary( + binary(decl_unit(), Slash, literal("3")), + Slash, + literal("2") + ) + ); + assert_eq!( + x_group_div.invert(&mut symbol_table).unwrap(), + *binary(binary(decl_unit(), Star, literal("3")), Slash, literal("2")) + ); + } + + #[test] + fn test_multiple_decl_units() { + 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")) + ); + } +} From 8adabaa992d486976421ff98dcde2bbc1e805bae Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Wed, 17 Jun 2020 21:28:54 +0200 Subject: [PATCH 12/19] Added UnsupportedExpression errors. --- kalk/src/inverter.rs | 15 ++++++++++++++- kalk/src/parser.rs | 1 + kalk_cli/src/output.rs | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index ffdd22e..d9965d4 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -108,6 +108,14 @@ fn invert_binary( // If the left expression contains the unit, invert the right one instead, // since the unit should not be moved. if contains_the_unit(symbol_table, left) { + // But if the right expression *also* contains the unit, + // 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).", + ))); + } + return Ok(invert( Expr::Binary(Box::new(target_expr), op_inv, Box::new(right.clone())), symbol_table, @@ -148,7 +156,9 @@ fn invert_unit( _identifier: &str, _expr: &Expr, ) -> Result<(Expr, Expr), CalcError> { - unimplemented!() + Err(CalcError::UnsupportedExpression(String::from( + "Cannot invert expressions containing other units (yet).", + ))) } fn invert_var( @@ -253,6 +263,9 @@ fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result { TokenKind::Star, Box::new(base_expr.clone()), )), + Expr::Group(_) => Err(CalcError::UnsupportedExpression(String::from( + "Cannot invert parenthesis multiplied with parenthesis (yet).", + ))), _ => unimplemented!(), } } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index e78aa28..0dd94d2 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -70,6 +70,7 @@ pub enum CalcError { UnexpectedToken(TokenKind), UndefinedFn(String), UndefinedVar(String), + UnsupportedExpression(String), Unknown, } diff --git a/kalk_cli/src/output.rs b/kalk_cli/src/output.rs index ba7260a..984b0f0 100644 --- a/kalk_cli/src/output.rs +++ b/kalk_cli/src/output.rs @@ -96,6 +96,7 @@ fn print_calc_err(err: CalcError) { UnexpectedToken(kind) => format!("Unexpected token: '{:?}'.", kind), UndefinedFn(name) => format!("Undefined function: '{}'.", name), UndefinedVar(name) => format!("Undefined variable: '{}'.", name), + UnsupportedExpression(msg) => format!("{}", msg), Unknown => format!("Unknown error."), }); } From 7d845523d813333f1d0787f3540deb5373bfd541 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 02:02:48 +0200 Subject: [PATCH 13/19] Made the unit be displayed in the result. --- kalk/src/interpreter.rs | 172 +++++++++++++++++++++++++--------------- kalk/src/inverter.rs | 7 +- kalk/src/parser.rs | 2 +- kalk/src/prelude.rs | 14 +++- kalk_cli/src/output.rs | 6 +- 5 files changed, 127 insertions(+), 74 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 61fe871..cdd53cd 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -22,7 +22,10 @@ impl<'a> Context<'a> { } } - pub fn interpret(&mut self, statements: Vec) -> Result, CalcError> { + pub fn interpret( + &mut self, + statements: Vec, + ) -> Result, 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 { +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 { } } -fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result { +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 { - 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 { - 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 { - 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 { +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 { - 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 { - 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 { +) -> 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 { +) -> 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 { +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 { +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 { - 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 { + 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, CalcError> { + fn interpret_with_unit(stmt: Stmt) -> Result, 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, 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 )); } diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index d9965d4..d9570e6 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -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")) - ); + );*/ } } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 0dd94d2..446d522 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -81,7 +81,7 @@ pub fn eval( context: &mut Context, input: &str, precision: u32, -) -> Result, CalcError> { +) -> Result, CalcError> { let statements = parse(context, input)?; let mut interpreter = diff --git a/kalk/src/prelude.rs b/kalk/src/prelude.rs index c9f73b4..01ed72a 100644 --- a/kalk/src/prelude.rs +++ b/kalk/src/prelude.rs @@ -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 + } } } diff --git a/kalk_cli/src/output.rs b/kalk_cli/src/output.rs index 984b0f0..684f9be 100644 --- a/kalk_cli/src/output.rs +++ b/kalk_cli/src/output.rs @@ -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); } } } From 07e34c6bbbb2826ebd09753cf8aead31b68d1abc Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 16:20:18 +0200 Subject: [PATCH 14/19] Implemented inversion for some prelude functions. --- kalk/src/inverter.rs | 83 +++++++++++++++++++++++++++++++++++++++--- kalk/src/parser.rs | 2 +- kalk_cli/src/output.rs | 2 +- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index d9570e6..bfced60 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -2,8 +2,37 @@ use crate::ast::{Expr, Stmt}; use crate::lexer::TokenKind; use crate::parser::CalcError; use crate::parser::DECL_UNIT; +use crate::prelude; use crate::symbol_table::SymbolTable; +pub const INVERSE_UNARY_FUNCS: phf::Map<&'static str, &'static str> = phf::phf_map! { + "cos" => "acos", + "cosec" => "acosec", + "cosech" => "cosech", + "cosh" => "acosh", + "cot" => "acot", + "coth" => "acoth", + "sec" => "asec", + "sech" => "asech", + "sin" => "asin", + "sinh" => "asinh", + "tan" => "atan", + "tanh" => "atanh", + + "acos" => "cos", + "acosec" => "cosec", + "acosech" => "cosech", + "acosh" => "cosh", + "acot" => "cot", + "acoth" => "coth", + "asec" => "sec", + "asech" => "sech", + "asin" => "sin", + "asinh" => "sinh", + "atan" => "tan", + "atanh" => "tanh", +}; + impl Expr { pub fn invert(&self, symbol_table: &mut SymbolTable) -> Result { let target_expr = Expr::Var(DECL_UNIT.into()); @@ -111,8 +140,8 @@ fn invert_binary( // But if the right expression *also* contains the unit, // 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). Try simplifying the expression.", + return Err(CalcError::UnableToInvert(String::from( + "Expressions with several instances of an unknown variable (this might be supported in the future). Try simplifying the expression.", ))); } @@ -155,8 +184,8 @@ fn invert_unit( _identifier: &str, _expr: &Expr, ) -> Result<(Expr, Expr), CalcError> { - Err(CalcError::UnsupportedExpression(String::from( - "Cannot invert expressions containing other units (yet).", + Err(CalcError::UnableToInvert(String::from( + "Expressions containing other units (this should be supported in the future).", ))) } @@ -178,6 +207,48 @@ fn invert_fn_call( identifier: &str, arguments: &Vec, ) -> Result<(Expr, Expr), CalcError> { + // If prelude function + match arguments.len() { + 1 => { + if prelude::UNARY_FUNCS.contains_key(identifier) { + if let Some(fn_inv) = INVERSE_UNARY_FUNCS.get(identifier) { + return Ok(( + Expr::FnCall(fn_inv.to_string(), vec![target_expr]), + arguments[0].clone(), + )); + } else { + match identifier { + "sqrt" => { + return Ok(( + Expr::Binary( + Box::new(target_expr), + TokenKind::Power, + Box::new(Expr::Literal(String::from("2"))), + ), + arguments[0].clone(), + )); + } + _ => { + return Err(CalcError::UnableToInvert(format!( + "Function '{}'", + identifier + ))); + } + } + } + } + } + 2 => { + if prelude::BINARY_FUNCS.contains_key(identifier) { + return Err(CalcError::UnableToInvert(format!( + "Function '{}'", + identifier + ))); + } + } + _ => (), + } + // Get the function definition from the symbol table. let (parameters, body) = if let Some(Stmt::FnDecl(_, parameters, body)) = symbol_table.get_fn(identifier).cloned() { @@ -262,8 +333,8 @@ fn multiply_into(expr: &Expr, base_expr: &Expr) -> Result { TokenKind::Star, Box::new(base_expr.clone()), )), - Expr::Group(_) => Err(CalcError::UnsupportedExpression(String::from( - "Cannot invert parenthesis multiplied with parenthesis (yet).", + Expr::Group(_) => Err(CalcError::UnableToInvert(String::from( + "Parenthesis multiplied with parenthesis (this should be possible in the future).", ))), _ => unimplemented!(), } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 446d522..84aef27 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -70,7 +70,7 @@ pub enum CalcError { UnexpectedToken(TokenKind), UndefinedFn(String), UndefinedVar(String), - UnsupportedExpression(String), + UnableToInvert(String), Unknown, } diff --git a/kalk_cli/src/output.rs b/kalk_cli/src/output.rs index 684f9be..fa5d9eb 100644 --- a/kalk_cli/src/output.rs +++ b/kalk_cli/src/output.rs @@ -94,9 +94,9 @@ fn print_calc_err(err: CalcError) { InvalidOperator => format!("Invalid operator."), InvalidUnit => format!("Invalid unit."), UnexpectedToken(kind) => format!("Unexpected token: '{:?}'.", kind), + UnableToInvert(msg) => format!("Unable to invert: {}", msg), UndefinedFn(name) => format!("Undefined function: '{}'.", name), UndefinedVar(name) => format!("Undefined variable: '{}'.", name), - UnsupportedExpression(msg) => format!("{}", msg), Unknown => format!("Unknown error."), }); } From 3259e78597c05341772a1910a22ce94c48e1fac0 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 16:20:52 +0200 Subject: [PATCH 15/19] Changed nth_sqrt to nth_root. --- kalk/src/prelude.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kalk/src/prelude.rs b/kalk/src/prelude.rs index 01ed72a..aea58f0 100644 --- a/kalk/src/prelude.rs +++ b/kalk/src/prelude.rs @@ -63,7 +63,7 @@ pub const BINARY_FUNCS: phf::Map<&'static str, BinaryFuncInfo> = phf::phf_map! { "min" => BinaryFuncInfo(min, Other), "hyp" => BinaryFuncInfo(hyp, Other), "log" => BinaryFuncInfo(logx, Other), - "sqrt" => BinaryFuncInfo(nth_sqrt, Other), + "root" => BinaryFuncInfo(nth_root, Other), }; enum FuncType { @@ -317,7 +317,7 @@ mod funcs { x.sqrt() } - pub fn nth_sqrt(x: Float, n: Float) -> Float { + pub fn nth_root(x: Float, n: Float) -> Float { x.pow(Float::with_val(1, 1) / n) } From ce824511ffbf8c4adfc79b570632b0d6bd2935bf Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 16:58:01 +0200 Subject: [PATCH 16/19] Created unit convertion expression. Syntax: to --- kalk/src/interpreter.rs | 9 +++++++++ kalk/src/inverter.rs | 4 +++- kalk/src/parser.rs | 20 ++++++++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index cdd53cd..51433f1 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -87,6 +87,15 @@ fn eval_binary_expr( right_expr: &Expr, unit: &str, ) -> Result<(Float, String), CalcError> { + 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, "")?; + 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 (right, _) = if left_unit.len() > 0 { let (_, right_unit) = eval_expr(context, right_expr, "")?; // TODO: Avoid evaluating this twice. diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index bfced60..e62491b 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -194,7 +194,9 @@ fn invert_var( symbol_table: &mut SymbolTable, identifier: &str, ) -> Result<(Expr, Expr), CalcError> { - if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(identifier).cloned() { + if identifier == DECL_UNIT { + Ok((target_expr, Expr::Var(identifier.into()))) + } else if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(identifier).cloned() { invert(target_expr, symbol_table, &var_expr) } else { Ok((target_expr, Expr::Var(identifier.into()))) diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 84aef27..7ad16e3 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -95,6 +95,8 @@ pub fn eval( pub fn parse(context: &mut Context, input: &str) -> Result, CalcError> { context.tokens = Lexer::lex(input); context.pos = 0; + context.parsing_unit_decl = false; + context.unit_decl_base_unit = None; let mut statements: Vec = Vec::new(); while !is_at_end(context) { @@ -171,7 +173,8 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { let identifier = advance(context).clone(); consume(context, TokenKind::Equals)?; - // Parse the definition + // Parse the mut definition + context.unit_decl_base_unit = None; context.parsing_unit_decl = true; let def = parse_expr(context)?; context.parsing_unit_decl = false; @@ -199,7 +202,20 @@ fn parse_unit_decl_stmt(context: &mut Context) -> Result { } fn parse_expr(context: &mut Context) -> Result { - Ok(parse_sum(context)?) + Ok(parse_to(context)?) +} + +fn parse_to(context: &mut Context) -> Result { + let left = parse_sum(context)?; + + if match_token(context, TokenKind::ToKeyword) { + let op = advance(context).kind.clone(); + let right = Expr::Var(advance(context).value.clone()); // Parse this as a variable for now. + + return Ok(Expr::Binary(Box::new(left), op, Box::new(right))); + } + + Ok(left) } fn parse_sum(context: &mut Context) -> Result { From c1577a523cdd868f7808342e24e7bc346e16b858 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 17:01:09 +0200 Subject: [PATCH 17/19] Added environment variables to help. --- kalk_cli/src/main.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kalk_cli/src/main.rs b/kalk_cli/src/main.rs index 4f8d379..0721602 100644 --- a/kalk_cli/src/main.rs +++ b/kalk_cli/src/main.rs @@ -25,10 +25,14 @@ fn main() { // The indentation... Will have to do something more scalable in the future. println!( " --= kalk help =-\n +[kalk help] + kalk [OPTIONS] [INPUT] -h, --help : show this -i : load a file with predefined functions/variables + +[Environment variables] +ANGLE_UNIT=(deg/rad) : Sets the default unit used for trigonometric functions. " ); return; From 5de759b56b05f3abd181f8bebb5298987f422b24 Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 17:06:21 +0200 Subject: [PATCH 18/19] Fixed parser documentation and interpreter units tests. --- kalk/src/interpreter.rs | 4 ++-- kalk/src/parser.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 51433f1..817f1a9 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -438,7 +438,7 @@ mod tests { symbol_table.insert(var_decl("x", literal("1"))); let mut context = Context::new(&mut symbol_table, "rad", PRECISION); - assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 1); + assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap().0, 1); } #[test] @@ -475,7 +475,7 @@ mod tests { )); let mut context = Context::new(&mut symbol_table, "rad", PRECISION); - assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap(), 3); + assert_eq!(context.interpret(vec![stmt]).unwrap().unwrap().0, 3); } #[test] diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 7ad16e3..73906ca 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -15,7 +15,8 @@ pub const DEFAULT_ANGLE_UNIT: &'static str = "rad"; /// use kalk::parser; /// let mut parser_context = parser::Context::new(); /// let precision = 53; -/// assert_eq!(parser::eval(&mut parser_context, "5*3", precision).unwrap().unwrap(), 15); +/// let (result, unit) = parser::eval(&mut parser_context, "5*3", precision).unwrap().unwrap(); +/// assert_eq!(result, 15); /// ``` pub struct Context { tokens: Vec, From 3b492f51193c4dc7c638aba949f9417a5169130f Mon Sep 17 00:00:00 2001 From: PaddiM8 Date: Thu, 18 Jun 2020 17:41:57 +0200 Subject: [PATCH 19/19] Added units to prelude functions. --- kalk/src/interpreter.rs | 11 +++- kalk/src/prelude.rs | 108 +++++++++++++++++++++------------------- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 817f1a9..1437e00 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -241,8 +241,15 @@ fn eval_fn_call_expr( _ => None, }; - if let Some(result) = prelude_func { - return Ok((result, unit.into())); + if let Some((result, func_unit)) = prelude_func { + return Ok(( + result, + if unit.len() > 0 { + unit.into() + } else { + func_unit.into() + }, + )); } // Special functions diff --git a/kalk/src/prelude.rs b/kalk/src/prelude.rs index aea58f0..7b159b1 100644 --- a/kalk/src/prelude.rs +++ b/kalk/src/prelude.rs @@ -16,54 +16,54 @@ pub const CONSTANTS: phf::Map<&'static str, &'static str> = phf::phf_map! { }; use funcs::*; -pub const UNARY_FUNCS: phf::Map<&'static str, UnaryFuncInfo> = phf::phf_map! { - "cos" => UnaryFuncInfo(cos, Trig), - "cosec" => UnaryFuncInfo(cosec, Trig), - "cosech" => UnaryFuncInfo(cosech, Trig), - "cosh" => UnaryFuncInfo(cosh, Trig), - "cot" => UnaryFuncInfo(cot, Trig), - "coth" => UnaryFuncInfo(coth, Trig), - "sec" => UnaryFuncInfo(sec, Trig), - "sech" => UnaryFuncInfo(sech, Trig), - "sin" => UnaryFuncInfo(sin, Trig), - "sinh" => UnaryFuncInfo(sinh, Trig), - "tan" => UnaryFuncInfo(tan, Trig), - "tanh" => UnaryFuncInfo(tanh, Trig), +pub const UNARY_FUNCS: phf::Map<&'static str, (UnaryFuncInfo, &'static str)> = phf::phf_map! { + "cos" => (UnaryFuncInfo(cos, Trig), ""), + "cosec" => (UnaryFuncInfo(cosec, Trig), ""), + "cosech" => (UnaryFuncInfo(cosech, Trig), ""), + "cosh" => (UnaryFuncInfo(cosh, Trig), ""), + "cot" => (UnaryFuncInfo(cot, Trig), ""), + "coth" => (UnaryFuncInfo(coth, Trig), ""), + "sec" => (UnaryFuncInfo(sec, Trig), ""), + "sech" => (UnaryFuncInfo(sech, Trig), ""), + "sin" => (UnaryFuncInfo(sin, Trig), ""), + "sinh" => (UnaryFuncInfo(sinh, Trig), ""), + "tan" => (UnaryFuncInfo(tan, Trig), ""), + "tanh" => (UnaryFuncInfo(tanh, Trig), ""), - "acos" => UnaryFuncInfo(acos, InverseTrig), - "acosec" => UnaryFuncInfo(acosec, InverseTrig), - "acosech" => UnaryFuncInfo(acosech, InverseTrig), - "acosh" => UnaryFuncInfo(acosh, InverseTrig), - "acot" => UnaryFuncInfo(acot, InverseTrig), - "acoth" => UnaryFuncInfo(acoth, InverseTrig), - "asec" => UnaryFuncInfo(asec, InverseTrig), - "asech" => UnaryFuncInfo(asech, InverseTrig), - "asin" => UnaryFuncInfo(asin, InverseTrig), - "asinh" => UnaryFuncInfo(asinh, InverseTrig), - "atan" => UnaryFuncInfo(atan, InverseTrig), - "atanh" => UnaryFuncInfo(atanh, InverseTrig), + "acos" => (UnaryFuncInfo(acos, InverseTrig), "rad"), + "acosec" => (UnaryFuncInfo(acosec, InverseTrig), "rad"), + "acosech" => (UnaryFuncInfo(acosech, InverseTrig), "rad"), + "acosh" => (UnaryFuncInfo(acosh, InverseTrig), "rad"), + "acot" => (UnaryFuncInfo(acot, InverseTrig), "rad"), + "acoth" => (UnaryFuncInfo(acoth, InverseTrig), "rad"), + "asec" => (UnaryFuncInfo(asec, InverseTrig), "rad"), + "asech" => (UnaryFuncInfo(asech, InverseTrig), "rad"), + "asin" => (UnaryFuncInfo(asin, InverseTrig), "rad"), + "asinh" => (UnaryFuncInfo(asinh, InverseTrig), "rad"), + "atan" => (UnaryFuncInfo(atan, InverseTrig), "rad"), + "atanh" => (UnaryFuncInfo(atanh, InverseTrig), "rad"), - "abs" => UnaryFuncInfo(abs, Other), - "cbrt" => UnaryFuncInfo(cbrt, Other), - "ceil" => UnaryFuncInfo(ceil, Other), - "exp" => UnaryFuncInfo(exp, Other), - "floor" => UnaryFuncInfo(floor, Other), - "frac" => UnaryFuncInfo(frac, Other), - "gamma" => UnaryFuncInfo(gamma, Other), - "Γ" => UnaryFuncInfo(gamma, Other), - "log" => UnaryFuncInfo(log, Other), - "ln" => UnaryFuncInfo(ln, Other), - "round" => UnaryFuncInfo(round, Other), - "sqrt" => UnaryFuncInfo(sqrt, Other), - "√" => UnaryFuncInfo(sqrt, Other), - "trunc" => UnaryFuncInfo(trunc, Other), + "abs" => (UnaryFuncInfo(abs, Other), ""), + "cbrt" => (UnaryFuncInfo(cbrt, Other), ""), + "ceil" => (UnaryFuncInfo(ceil, Other), ""), + "exp" => (UnaryFuncInfo(exp, Other), ""), + "floor" => (UnaryFuncInfo(floor, Other), ""), + "frac" => (UnaryFuncInfo(frac, Other), ""), + "gamma" => (UnaryFuncInfo(gamma, Other), ""), + "Γ" => (UnaryFuncInfo(gamma, Other), ""), + "log" => (UnaryFuncInfo(log, Other), ""), + "ln" => (UnaryFuncInfo(ln, Other), ""), + "round" => (UnaryFuncInfo(round, Other), ""), + "sqrt" => (UnaryFuncInfo(sqrt, Other), ""), + "√" => (UnaryFuncInfo(sqrt, Other), ""), + "trunc" => (UnaryFuncInfo(trunc, Other), ""), }; -pub const BINARY_FUNCS: phf::Map<&'static str, BinaryFuncInfo> = phf::phf_map! { - "max" => BinaryFuncInfo(max, Other), - "min" => BinaryFuncInfo(min, Other), - "hyp" => BinaryFuncInfo(hyp, Other), - "log" => BinaryFuncInfo(logx, Other), - "root" => BinaryFuncInfo(nth_root, Other), +pub const BINARY_FUNCS: phf::Map<&'static str, (BinaryFuncInfo, &'static str)> = phf::phf_map! { + "max" => (BinaryFuncInfo(max, Other), ""), + "min" => (BinaryFuncInfo(min, Other), ""), + "hyp" => (BinaryFuncInfo(hyp, Other), ""), + "log" => (BinaryFuncInfo(logx, Other), ""), + "root" => (BinaryFuncInfo(nth_root, Other), ""), }; enum FuncType { @@ -113,9 +113,12 @@ pub fn call_unary_func( name: &str, x: Float, angle_unit: &str, -) -> Option { - if let Some(func_info) = UNARY_FUNCS.get(name) { - Some(func_info.call(context, x, &angle_unit)) +) -> Option<(Float, String)> { + if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) { + Some(( + func_info.call(context, x, &angle_unit), + func_unit.to_string(), + )) } else { None } @@ -127,9 +130,12 @@ pub fn call_binary_func( x: Float, y: Float, angle_unit: &str, -) -> Option { - if let Some(func_info) = BINARY_FUNCS.get(name) { - Some(func_info.call(context, x, y, angle_unit)) +) -> Option<(Float, String)> { + if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) { + Some(( + func_info.call(context, x, y, angle_unit), + func_unit.to_string(), + )) } else { None }