Fixed precedence problems for multiplication following function calls

This commit is contained in:
bakk 2022-01-23 00:01:39 +01:00 committed by PaddiM8
parent b4e2667f16
commit 39cbbb607d
6 changed files with 133 additions and 130 deletions

View File

@ -197,14 +197,7 @@ fn analyse_expr(context: &mut Context, expr: Expr) -> Result<Expr, CalcError> {
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<Expr>,
adjacent_exponent: Option<Expr>,
) -> Result<Expr, CalcError> {
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::<f64>() {
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<Expr>,
) -> Result<Expr, CalcError> {
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<Expr, CalcError> {
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<Expr>,
) -> Result<Expr, CalcError> {
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))
}

View File

@ -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),
}
}

View File

@ -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<Expr, CalcError> {
}
fn parse_to(context: &mut Context) -> Result<Expr, CalcError> {
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<Expr, CalcError> {
Ok(left)
}
fn parse_sum(context: &mut Context) -> Result<Expr, CalcError> {
fn parse_term(context: &mut Context) -> Result<Expr, CalcError> {
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<Expr, CalcError> {
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<Expr, CalcError> {
_ => 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<Expr, CalcError> {
fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
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::<f64>() {
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<Expr, CalcError> {
{
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))
}

View File

@ -2,4 +2,4 @@ x = 2
y = 3
f(x) = 2x(x - 3)(y + 2)
2f(f(x) + y) = 6800
2f(f(x) + y) * 2 = 13600

4
tests/precedence.kalker Normal file
View File

@ -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

5
tests/unicode.kalker Normal file
View File

@ -0,0 +1,5 @@
x₂₃ = 3
π + ϕ + τ + √(64) = 19.0428119495 and
log₁₀(100) = 2 and
1 + x₂₃ = 4