diff --git a/kalk/src/integration_testing.rs b/kalk/src/integration_testing.rs index 281bbe9..4cc2fdb 100644 --- a/kalk/src/integration_testing.rs +++ b/kalk/src/integration_testing.rs @@ -51,6 +51,7 @@ mod tests { #[test_case("matrices/operations")] #[test_case("matrices/transpose")] #[test_case("radix")] + #[test_case("recursion")] #[test_case("redefining")] #[test_case("sum")] #[test_case("variables")] diff --git a/kalk/src/kalk_value/rounding.rs b/kalk/src/kalk_value/rounding.rs index 382fee7..46cbdd2 100644 --- a/kalk/src/kalk_value/rounding.rs +++ b/kalk/src/kalk_value/rounding.rs @@ -156,7 +156,7 @@ fn equivalent_fraction(value: f64) -> Option { numer = numer.trunc(); denom = denom.trunc(); - if denom <= 1f64 || denom >= 100f64 || denom == 10f64 { + if denom <= 1f64 || denom >= 100f64 || denom as i64 == 10 { return None; } diff --git a/kalk/src/lib.rs b/kalk/src/lib.rs index 9872e45..12e8897 100644 --- a/kalk/src/lib.rs +++ b/kalk/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::unused_unit)] +#![allow(clippy::float_cmp)] mod analysis; pub mod ast; pub mod calculation_result; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 54414bd..0163265 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -212,16 +212,11 @@ pub fn parse(context: &mut Context, input: &str) -> Result, CalcError> } fn parse_stmt(context: &mut Context) -> Result { - if match_token(context, TokenKind::Identifier) { - return Ok(match peek_next(context).kind { - //TokenKind::Equals => parse_var_decl_stmt(context)?, - _ => Stmt::Expr(Box::new(parse_expr(context)?)), - }); - } else if match_token(context, TokenKind::UnitKeyword) { - return parse_unit_decl_stmt(context); + if match_token(context, TokenKind::UnitKeyword) { + parse_unit_decl_stmt(context) + } else { + Ok(Stmt::Expr(Box::new(parse_expr(context)?))) } - - Ok(Stmt::Expr(Box::new(parse_expr(context)?))) } fn parse_piecewise(context: &mut Context) -> Result { @@ -376,6 +371,57 @@ fn parse_comparison(context: &mut Context) -> Result { { let op = peek(context).kind; advance(context); + + // If it's potentially a function declaration, run it through + // the analysis phase to ensure it gets added to the symbol + // table before parsing the right side. This is necessary for + // recursion to work. + if let (TokenKind::Equals, Expr::Binary(_, TokenKind::Star, _)) = (TokenKind::Equals, &left) + { + let analysed = analysis::analyse_stmt( + context.symbol_table.get_mut(), + Stmt::Expr(Box::new(Expr::Binary( + Box::new(left), + op, + Box::new(Expr::Literal(0f64)), + ))), + )?; + + left = match analysed { + // Reconstruct function declarations into what they were originally parsed as + Stmt::FnDecl(identifier, parameters, _) => { + let mut parameter_vars: Vec = parameters + .into_iter() + .map(|x| { + Expr::Var(Identifier::from_full_name( + // Parameters will come back as eg. f-x, + // therefore the function name needs to be removed + &x[identifier.full_name.len() + 1..], + )) + }) + .collect(); + + Expr::Binary( + Box::new(Expr::Var(identifier)), + TokenKind::Star, + Box::new(if parameter_vars.len() > 1 { + Expr::Vector(parameter_vars) + } else { + Expr::Group(Box::new(parameter_vars.pop().unwrap())) + }), + ) + } + Stmt::Expr(analysed_expr) => { + if let Expr::Binary(analysed_left, TokenKind::Equals, _) = *analysed_expr { + *analysed_left + } else { + unreachable!() + } + } + _ => unreachable!(), + }; + } + let right = if op == TokenKind::Equals && match_token(context, TokenKind::OpenBrace) { parse_piecewise(context)? } else { @@ -385,7 +431,9 @@ fn parse_comparison(context: &mut Context) -> Result { left = match right { Expr::Binary( inner_left, - inner_op @ (TokenKind::Equals + inner_op + @ + (TokenKind::Equals | TokenKind::NotEquals | TokenKind::GreaterThan | TokenKind::LessThan diff --git a/tests/recursion.kalker b/tests/recursion.kalker new file mode 100644 index 0000000..97319b0 --- /dev/null +++ b/tests/recursion.kalker @@ -0,0 +1,11 @@ +f(x) = { + f(x - 1) if x >= 1 + x otherwise +} + +g(x, y) = { + g(x - 1, y + 1) if x >= 1 + y otherwise +} + +f(5) = 0 and g(5, 0) = 5 \ No newline at end of file