From ad1c59c29497f38c63391914ac038f897c8941ee Mon Sep 17 00:00:00 2001 From: bakk Date: Sun, 23 Jan 2022 00:01:39 +0100 Subject: [PATCH] Fixed precedence problems for multiplication following function calls --- kalk/src/analysis.rs | 168 ++++++++++++++-------------------------- kalk/src/interpreter.rs | 37 ++++++--- kalk/src/parser.rs | 47 +++++++++-- tests/basics.kalker | 2 +- tests/precedence.kalker | 4 + tests/unicode.kalker | 5 ++ 6 files changed, 133 insertions(+), 130 deletions(-) create mode 100644 tests/precedence.kalker create mode 100644 tests/unicode.kalker diff --git a/kalk/src/analysis.rs b/kalk/src/analysis.rs index ec94d4c..db88851 100644 --- a/kalk/src/analysis.rs +++ b/kalk/src/analysis.rs @@ -197,14 +197,7 @@ fn analyse_expr(context: &mut Context, expr: Expr) -> Result { Expr::Unit(name, value) => Expr::Unit(name, Box::new(analyse_expr(context, *value)?)), Expr::Var(identifier) => analyse_var(context, identifier, None, None)?, Expr::Group(value) => Expr::Group(Box::new(analyse_expr(context, *value)?)), - Expr::FnCall(identifier, arguments) => { - let mut analysed_arguments = Vec::new(); - for argument in arguments { - analysed_arguments.push(analyse_expr(context, argument)?); - } - - Expr::FnCall(identifier, analysed_arguments) - } + Expr::FnCall(identifier, arguments) => analyse_fn(context, identifier, arguments)?, Expr::Literal(_) => expr, Expr::Piecewise(pieces) => { let mut analysed_pieces = Vec::new(); @@ -472,29 +465,6 @@ fn analyse_var( adjacent_factor: Option, adjacent_exponent: Option, ) -> Result { - let mut log_base = None; - if identifier.full_name.starts_with("log") { - if let Some(lowered) = identifier.get_lowered_part() { - if let Ok(lowered_float) = lowered.parse::() { - log_base = Some(Expr::Literal(lowered_float)); - } - } - } - - // Eg. f(1, 2, 3), f(3) or f3 - let exists_as_fn = context.symbol_table.contains_fn(&identifier.pure_name) - || context.current_function_name.as_ref() == Some(&identifier.pure_name) - || log_base.is_some(); - if exists_as_fn { - if let Some(adjacent_expr) = adjacent_factor { - return with_adjacent( - build_fn_call(context, identifier, adjacent_expr, log_base)?, - None, - adjacent_exponent, - ); - } - } - let adjacent_factor = if let Some(adjacent_factor) = adjacent_factor { Some(analyse_expr(context, adjacent_factor)?) } else { @@ -591,86 +561,6 @@ fn with_adjacent( } } -fn build_fn_call( - context: &mut Context, - identifier: Identifier, - adjacent_expr: Expr, - log_base: Option, -) -> Result { - let is_integral = identifier.pure_name == "integrate"; - let prev_in_integral = context.in_integral; - if is_integral { - context.in_integral = true; - } - - let prev_in_sum_prod = context.in_sum_prod; - let is_sum_prod = identifier.pure_name == "sum" || identifier.pure_name == "prod"; - if is_sum_prod { - context.in_sum_prod = true; - if context.sum_variable_names.is_none() { - context.sum_variable_names = Some(Vec::new()); - } - } - - // Don't perform equation solving on special functions - if is_integral || is_sum_prod { - context.in_equation = false; - } - - let arguments = match adjacent_expr { - Expr::Vector(arguments) => { - let mut new_arguments = Vec::new(); - for (i, argument) in arguments.iter().enumerate() { - if i == 0 && context.in_sum_prod { - context.in_conditional = true; - let vars = context.sum_variable_names.as_mut().unwrap(); - if let Expr::Binary(left, TokenKind::Equals, _) = argument { - if let Expr::Var(var_identifier) = &**left { - vars.push(var_identifier.pure_name.clone()); - } else { - vars.push(String::from("n")); - } - } else { - vars.push(String::from("n")); - } - } - - new_arguments.push(analyse_expr(context, argument.to_owned())?); - context.in_conditional = false; - } - - new_arguments - } - _ => { - let argument = if let Expr::Group(argument) = adjacent_expr { - *argument - } else { - adjacent_expr - }; - if let Some(log_base) = log_base { - return Ok(Expr::FnCall( - Identifier::from_full_name("log"), - vec![analyse_expr(context, argument)?, log_base], - )); - } else { - vec![analyse_expr(context, argument)?] - } - } - }; - - if is_integral { - context.in_integral = prev_in_integral; - } - - if is_sum_prod { - context.in_sum_prod = prev_in_sum_prod; - let vars = context.sum_variable_names.as_mut().unwrap(); - vars.pop(); - } - - Ok(Expr::FnCall(identifier, arguments)) -} - fn build_indexed_var(context: &mut Context, identifier: Identifier) -> Result { let underscore_pos = identifier.pure_name.find('_').unwrap(); let var_name = &identifier.pure_name[0..underscore_pos]; @@ -803,3 +693,59 @@ fn build_var(context: &mut Context, name: &str) -> Expr { Expr::Var(Identifier::from_full_name(name)) } + +fn analyse_fn( + context: &mut Context, + identifier: Identifier, + arguments: Vec, +) -> Result { + let is_integral = identifier.pure_name == "integrate"; + let prev_in_integral = context.in_integral; + if is_integral { + context.in_integral = true; + } + + let prev_in_sum_prod = context.in_sum_prod; + let is_sum_prod = identifier.pure_name == "sum" || identifier.pure_name == "prod"; + if is_sum_prod { + context.in_sum_prod = true; + if context.sum_variable_names.is_none() { + context.sum_variable_names = Some(Vec::new()); + } + } + + // Don't perform equation solving on special functions + if is_integral || is_sum_prod { + context.in_equation = false; + } + + let mut analysed_arguments = Vec::new(); + for (i, argument) in arguments.iter().enumerate() { + if i == 0 && context.in_sum_prod { + context.in_conditional = true; + let vars = context.sum_variable_names.as_mut().unwrap(); + if let Expr::Binary(left, TokenKind::Equals, _) = argument { + if let Expr::Var(var_identifier) = &**left { + vars.push(var_identifier.pure_name.clone()); + } else { + vars.push(String::from("n")); + } + } else { + vars.push(String::from("n")); + } + } + + analysed_arguments.push(analyse_expr(context, argument.to_owned())?); + context.in_conditional = false; + } + + context.in_integral = prev_in_integral; + + if is_sum_prod { + context.in_sum_prod = prev_in_sum_prod; + let vars = context.sum_variable_names.as_mut().unwrap(); + vars.pop(); + } + + Ok(Expr::FnCall(identifier, analysed_arguments)) +} diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index ce76ae5..672d950 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -252,7 +252,9 @@ fn eval_var_expr( } if let Some(sum_variables) = &context.sum_variables { - let sum_variable = sum_variables.iter().find(|x| x.name == identifier.full_name); + let sum_variable = sum_variables + .iter() + .find(|x| x.name == identifier.full_name); if let Some(sum_variable) = sum_variable { return Ok(KalkValue::from(sum_variable.value)); } @@ -428,15 +430,16 @@ pub(crate) fn eval_fn_call_expr( )); } - let (var_name, start_expr) = if let Expr::Binary(left, TokenKind::Equals, right) = &expressions[0] { - if let Expr::Var(var_identifier) = &**left { - (var_identifier.pure_name.as_ref(), &**right) + let (var_name, start_expr) = + if let Expr::Binary(left, TokenKind::Equals, right) = &expressions[0] { + if let Expr::Var(var_identifier) = &**left { + (var_identifier.pure_name.as_ref(), &**right) + } else { + ("n", &**right) + } } else { - ("n", &**right) - } - } else { - ("n", &expressions[0]) - }; + ("n", &expressions[0]) + }; if context.sum_variables.is_none() { context.sum_variables = Some(Vec::new()); @@ -444,7 +447,10 @@ pub(crate) fn eval_fn_call_expr( { let sum_variables = context.sum_variables.as_mut().unwrap(); - sum_variables.push(SumVar { name: var_name.into(), value: 0 }); + sum_variables.push(SumVar { + name: var_name.into(), + value: 0, + }); } let start = eval_expr(context, start_expr, "")?.to_f64() as i128; @@ -636,6 +642,15 @@ fn eval_indexer( } } KalkValue::Matrix(rows) => { + if indexes.len() == 1 { + let row_index = eval_expr(context, &indexes[0], unit)?.to_f64() as usize; + return if let Some(row) = rows.get(row_index - 1) { + Ok(KalkValue::Vector(row.clone())) + } else { + Err(CalcError::ItemOfIndexDoesNotExist(vec![row_index])) + }; + } + if indexes.len() != 2 { return Err(CalcError::IncorrectAmountOfIndexes(indexes.len(), 2)); } @@ -660,7 +675,7 @@ fn eval_indexer( column_index, ])) } - _ => Err(CalcError::CanOnlyIndexVectors), + _ => Err(CalcError::CanOnlyIndexX), } } diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 17ba4b1..c97518b 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -89,7 +89,7 @@ impl Default for Context { /// Error that occured during parsing or evaluation. #[derive(Debug, Clone, PartialEq)] pub enum CalcError { - CanOnlyIndexVectors, + CanOnlyIndexX, Expected(String), ExpectedDx, ExpectedIf, @@ -118,7 +118,7 @@ pub enum CalcError { impl ToString for CalcError { fn to_string(&self) -> String { match self { - CalcError::CanOnlyIndexVectors => String::from("Indexing (getting an item with a specific index) is only possible on vectors."), + CalcError::CanOnlyIndexX => String::from("Indexing (getting an item with a specific index) is only possible on vectors and matrices."), CalcError::Expected(description) => format!("Expected: {}", description), CalcError::ExpectedDx => String::from("Expected eg. dx, to specify for which variable the operation is being done to. Example with integration: ∫(0, 1, x dx) or ∫(0, 1, x, dx). You may need to put parenthesis around the expression before dx/dy/du/etc."), CalcError::ExpectedIf => String::from("Expected 'if', with a condition after it."), @@ -411,7 +411,7 @@ fn parse_comparison(context: &mut Context) -> Result { } fn parse_to(context: &mut Context) -> Result { - let left = parse_sum(context)?; + let left = parse_term(context)?; if match_token(context, TokenKind::ToKeyword) { advance(context); @@ -427,13 +427,13 @@ fn parse_to(context: &mut Context) -> Result { Ok(left) } -fn parse_sum(context: &mut Context) -> Result { +fn parse_term(context: &mut Context) -> Result { let mut left = parse_factor(context)?; while match_token(context, TokenKind::Plus) || match_token(context, TokenKind::Minus) { let op = peek(context).kind; advance(context); - let right = parse_sum(context)?; + let right = parse_factor(context)?; left = Expr::Binary(Box::new(left), op, Box::new(right)); } @@ -445,7 +445,7 @@ fn parse_factor(context: &mut Context) -> Result { let mut left = parse_unit(context)?; if let Expr::Unary(TokenKind::Percent, percent_left) = left.clone() { - let try_parse = parse_factor(context); + let try_parse = parse_unit(context); if try_parse.is_ok() { left = Expr::Binary(percent_left, TokenKind::Percent, Box::new(try_parse?)); } @@ -473,7 +473,8 @@ fn parse_factor(context: &mut Context) -> Result { _ => advance(context).kind, }; - let right = parse_factor(context)?; + let right = parse_unit(context)?; + left = Expr::Binary(Box::new(left), op, Box::new(right)); } @@ -650,6 +651,16 @@ fn parse_vector(context: &mut Context) -> Result { fn parse_identifier(context: &mut Context) -> Result { let identifier = Identifier::from_full_name(&advance(context).value); + + let mut log_base = None; + if identifier.full_name.starts_with("log") { + if let Some(lowered) = identifier.get_lowered_part() { + if let Ok(lowered_float) = lowered.parse::() { + log_base = Some(Expr::Literal(lowered_float)); + } + } + } + if context.parsing_unit_decl && !context .symbol_table @@ -658,6 +669,28 @@ fn parse_identifier(context: &mut Context) -> Result { { context.unit_decl_base_unit = Some(identifier.full_name); Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT))) + } else if log_base.is_some() + || context + .symbol_table + .get_mut() + .contains_fn(&identifier.pure_name) + { + // Function call + let mut arguments = match parse_vector(context)? { + Expr::Vector(arguments) => arguments, + Expr::Group(argument) => vec![*argument], + argument => vec![argument], + }; + + if let Some(log_base) = log_base { + let log_arg = arguments.remove(0); + Ok(Expr::FnCall( + Identifier::from_full_name("log"), + vec![log_arg, log_base], + )) + } else { + Ok(Expr::FnCall(identifier, arguments)) + } } else { Ok(Expr::Var(identifier)) } diff --git a/tests/basics.kalker b/tests/basics.kalker index 8921502..a2d5452 100644 --- a/tests/basics.kalker +++ b/tests/basics.kalker @@ -2,4 +2,4 @@ x = 2 y = 3 f(x) = 2x(x - 3)(y + 2) -2f(f(x) + y) = 6800 \ No newline at end of file +2f(f(x) + y) * 2 = 13600 \ No newline at end of file diff --git a/tests/precedence.kalker b/tests/precedence.kalker new file mode 100644 index 0000000..bad4ab9 --- /dev/null +++ b/tests/precedence.kalker @@ -0,0 +1,4 @@ +x = 3 +2sqrt(64)3x + 2 = 146 and +2/sqrt(64)3x + 2 = 4.25 and +2sqrt(64)/3x + 2 = 18 diff --git a/tests/unicode.kalker b/tests/unicode.kalker new file mode 100644 index 0000000..83a7046 --- /dev/null +++ b/tests/unicode.kalker @@ -0,0 +1,5 @@ +x₂₃ = 3 + +π + ϕ + τ + √(64) = 19.0428119495 and +log₁₀(100) = 2 and +1 + x₂₃ = 4 \ No newline at end of file