diff --git a/kalk/src/calculus.rs b/kalk/src/calculus.rs index 95347d0..da58a13 100644 --- a/kalk/src/calculus.rs +++ b/kalk/src/calculus.rs @@ -1,22 +1,51 @@ use crate::ast::Expr; +use crate::ast::Identifier; use crate::ast::Stmt; use crate::interpreter; use crate::kalk_num::KalkNum; use crate::lexer::TokenKind; use crate::parser::CalcError; +pub fn derive_func( + context: &mut interpreter::Context, + name: &Identifier, + argument: KalkNum, +) -> Result { + const H: f64 = 0.000001; + let unit = &argument.unit.to_string(); + let argument_with_h = Expr::Literal(argument.clone().add(context, H.into()).to_f64()); + let argument_without_h = Expr::Literal(argument.to_f64()); + + let f_x_h = interpreter::eval_fn_call_expr( + context, + &Identifier::from_full_name(&name.pure_name), + &[argument_with_h], + unit, + )?; + let f_x = interpreter::eval_fn_call_expr( + context, + &Identifier::from_full_name(&name.pure_name), + &[argument_without_h], + unit, + )?; + + Ok(f_x_h.sub(context, f_x).div(context, H.into())) +} + pub fn integrate( context: &mut interpreter::Context, - expressions: &[Expr], + a: &Expr, + b: &Expr, + expr: &Expr, ) -> Result { let mut integration_variable: Option<&str> = None; // integral(a, b, expr dx) - if let Expr::Binary(_, TokenKind::Star, right) = &expressions[2] { + if let Expr::Binary(_, TokenKind::Star, right) = expr { if let Expr::Var(right_name) = &**right { - if right_name.starts_with("d") { + if right_name.full_name.starts_with("d") { // Take the value, but remove the d, so that only eg. x is left from dx - integration_variable = Some(&right_name[1..]); + integration_variable = Some(&right_name.full_name[1..]); } } } @@ -27,17 +56,11 @@ pub fn integrate( // "dx" is still in the expression. Set dx = 1, so that it doesn't affect the expression value. context.symbol_table.set(Stmt::VarDecl( - format!("d{}", integration_variable.unwrap()), + Identifier::from_full_name(&format!("d{}", integration_variable.unwrap())), Box::new(Expr::Literal(1f64)), )); - simpsons_rule( - context, - &expressions[0], - &expressions[1], - &expressions[2], - integration_variable.unwrap(), - ) + simpsons_rule(context, a, b, expr, integration_variable.unwrap()) } /// Composite Simpson's 3/8 rule @@ -56,16 +79,14 @@ fn simpsons_rule( let h = (b - a) / N as f64; for i in 0..=N { context.symbol_table.set(Stmt::VarDecl( - integration_variable.into(), + Identifier::from_full_name(integration_variable), Box::new(Expr::Literal(a + i as f64 * h)), )); - let factor = if i == 0 || i == N { - 1 - } else if i % 3 == 0 { - 2 - } else { - 3 + let factor = match i { + 0 | N => 1, + _ if i % 3 == 0 => 2, + _ => 3, }; // factor * f(x_n) diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index e8236f9..b918b70 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -244,7 +244,7 @@ fn eval_var_expr( .cloned(); match var_decl { Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr, unit), - _ => Err(CalcError::UndefinedVar(identifier.full_name)), + _ => Err(CalcError::UndefinedVar(identifier.full_name.clone())), } } @@ -272,13 +272,28 @@ pub(crate) fn eval_fn_call_expr( // Prelude let prelude_func = match expressions.len() { 1 => { - let x = eval_expr(context, &expressions[0], "")?.value; - prelude::call_unary_func(context, identifier, x, &context.angle_unit.clone()) + let x = eval_expr(context, &expressions[0], "")?; + if identifier.prime_count > 0 { + return calculus::derive_func(context, &identifier, x); + } else { + prelude::call_unary_func( + context, + &identifier.full_name, + x.value, + &context.angle_unit.clone(), + ) + } } 2 => { let x = eval_expr(context, &expressions[0], "")?.value; let y = eval_expr(context, &expressions[1], "")?.value; - prelude::call_binary_func(context, identifier, x, y, &context.angle_unit.clone()) + prelude::call_binary_func( + context, + &identifier.full_name, + x, + y, + &context.angle_unit.clone(), + ) } _ => None, }; @@ -339,7 +354,7 @@ pub(crate) fn eval_fn_call_expr( if arguments.len() != expressions.len() { return Err(CalcError::IncorrectAmountOfArguments( arguments.len(), - identifier.full_name, + identifier.full_name.clone(), expressions.len(), )); } @@ -357,7 +372,7 @@ pub(crate) fn eval_fn_call_expr( eval_expr(context, &fn_body, unit) } - _ => Err(CalcError::UndefinedFn(identifier.full_name)), + _ => Err(CalcError::UndefinedFn(identifier.full_name.clone())), } } diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index 68b37d4..e9f7db5 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -259,13 +259,13 @@ fn invert_var( unknown_var: &str, ) -> Result<(Expr, Expr), CalcError> { if identifier.full_name == unknown_var { - Ok((target_expr, Expr::Var(*identifier))) + Ok((target_expr, Expr::Var(identifier.clone()))) } else if let Some(Stmt::VarDecl(_, var_expr)) = symbol_table.get_var(&identifier.full_name).cloned() { invert(target_expr, symbol_table, &var_expr, unknown_var) } else { - Ok((target_expr, Expr::Var(*identifier))) + Ok((target_expr, Expr::Var(identifier.clone()))) } } @@ -329,14 +329,14 @@ fn invert_fn_call( { (parameters, body) } else { - return Err(CalcError::UndefinedFn(identifier.full_name)); + return Err(CalcError::UndefinedFn(identifier.full_name.clone())); }; // Make sure the input is valid. if parameters.len() != arguments.len() { return Err(CalcError::IncorrectAmountOfArguments( parameters.len(), - identifier.full_name, + identifier.full_name.clone(), arguments.len(), )); } diff --git a/kalk/src/lexer.rs b/kalk/src/lexer.rs index c0ae117..cc4cca2 100644 --- a/kalk/src/lexer.rs +++ b/kalk/src/lexer.rs @@ -16,6 +16,7 @@ pub enum TokenKind { Equals, Exclamation, Percent, + Tick, UnitKeyword, ToKeyword, @@ -114,6 +115,7 @@ impl<'a> Lexer<'a> { ',' => build(TokenKind::Comma, "", span), ';' => build(TokenKind::Semicolon, "", span), '%' => build(TokenKind::Percent, "", span), + '\'' => build(TokenKind::Tick, "", span), _ => build(TokenKind::Unknown, "", span), }; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 5195b57..8eb4d4f 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -478,23 +478,20 @@ fn parse_group_fn(context: &mut Context) -> Result { } fn parse_identifier(context: &mut Context) -> Result { - let identifier = advance(context).clone(); + let identifier = Identifier::from_full_name(&advance(context).value); // Eg. sqrt64 if match_token(context, TokenKind::Literal) { // If there is a function with this name, parse it as a function, with the next token as the argument. - if context.symbol_table.contains_fn(&identifier.value) { + if context.symbol_table.contains_fn(&identifier.pure_name) { let parameter = Expr::Literal(string_to_num(&advance(context).value)); - return Ok(Expr::FnCall( - Identifier::from_full_name(&identifier.value), - vec![parameter], - )); + return Ok(Expr::FnCall(identifier, vec![parameter])); } } let parse_as_var_instead = match_token(context, TokenKind::OpenParenthesis) && !context.parsing_identifier_stmt - && !context.symbol_table.contains_fn(&identifier.value); + && !context.symbol_table.contains_fn(&identifier.pure_name); // Eg. sqrt(64) // If the function doesn't exist, parse it as a variable and multiplication instead. @@ -503,7 +500,7 @@ fn parse_identifier(context: &mut Context) -> Result { if !parse_as_var_instead && match_token(context, TokenKind::OpenParenthesis) { advance(context); - let is_integral = identifier.value == "integrate" || identifier.value == "∫"; + let is_integral = identifier.full_name == "integrate" || identifier.full_name == "∫"; if is_integral { context.is_in_integral = true; } @@ -522,34 +519,31 @@ fn parse_identifier(context: &mut Context) -> Result { context.is_in_integral = false; } - return Ok(Expr::FnCall( - Identifier::from_full_name(&identifier.value), - parameters, - )); + return Ok(Expr::FnCall(identifier, parameters)); } // Eg. dx inside an integral, should be parsed as *one* identifier - if context.is_in_integral && identifier.value.starts_with("d") { - return Ok(Expr::Var(Identifier::from_full_name(&identifier.value))); + if context.is_in_integral && identifier.full_name.starts_with("d") { + return Ok(Expr::Var(identifier)); } // Eg. x - if parse_as_var_instead || context.symbol_table.contains_var(&identifier.value) { - Ok(Expr::Var(Identifier::from_full_name(&identifier.value))) + if parse_as_var_instead || context.symbol_table.contains_var(&identifier.pure_name) { + Ok(Expr::Var(identifier)) } else if context.parsing_unit_decl { - context.unit_decl_base_unit = Some(identifier.value); + context.unit_decl_base_unit = Some(identifier.full_name); Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT))) } else { if let Some(equation_var) = &context.equation_variable { - if &identifier.value == equation_var { - return Ok(Expr::Var(Identifier::from_full_name(&identifier.value))); + if &identifier.full_name == equation_var { + return Ok(Expr::Var(identifier)); } } else if context.contains_equal_sign { - context.equation_variable = Some(identifier.value.clone()); - return Ok(Expr::Var(Identifier::from_full_name(&identifier.value))); + context.equation_variable = Some(identifier.full_name.clone()); + return Ok(Expr::Var(identifier)); } - let mut chars = identifier.value.chars(); + let mut chars = identifier.pure_name.chars(); let mut left = Expr::Var(Identifier::from_full_name( &chars.next().unwrap().to_string(), )); @@ -575,6 +569,7 @@ fn parse_identifier(context: &mut Context) -> Result { left = Expr::Binary(Box::new(left), TokenKind::Star, Box::new(right)); } + // TODO: When implementing derivation for variables, make sure to add the derivation here. Ok(left) } }