Made it possible for functions to call themselves

This commit is contained in:
bakk 2021-05-31 23:16:47 +02:00
parent 5b8ad2829c
commit 4da372aefe
2 changed files with 40 additions and 10 deletions

View File

@ -382,17 +382,45 @@ pub(crate) fn eval_fn_call_expr(
}
// Initialise the arguments as their own variables.
let mut new_argument_values = Vec::new();
for (i, argument) in arguments.iter().enumerate() {
eval_stmt(
context,
&Stmt::VarDecl(
Identifier::from_full_name(argument),
Box::new(expressions[i].clone()),
),
)?;
let identifier_parts: Vec<&str> = argument.split('-').collect();
let var_decl = Stmt::VarDecl(
Identifier::parameter_from_name(identifier_parts[1], identifier_parts[0]),
Box::new(Expr::Literal(
eval_expr(context, &expressions[i], "")?.to_f64(),
)),
);
// Don't set these values just yet, since
// to avoid affecting the value of arguments
// during recursion.
new_argument_values.push((argument, var_decl));
}
eval_expr(context, &fn_body, unit)
let mut old_argument_values = Vec::new();
for (name, value) in new_argument_values {
// Save the original argument values,
// so that they can be reverted to after
// the function call is evaluated.
// This is necessary since recursive
// function calls have the same argument names.
old_argument_values.push(context.symbol_table.get_and_remove_var(name));
// Now set the new variable value
eval_stmt(context, &value)?;
}
let fn_value = eval_expr(context, &fn_body, unit);
// Revert to original argument values
for old_argument_value in old_argument_values {
if let Some(old_argument_value) = old_argument_value {
context.symbol_table.insert(old_argument_value);
}
}
fn_value
}
_ => Err(CalcError::UndefinedFn(identifier.full_name.clone())),
}

View File

@ -574,6 +574,8 @@ fn parse_group_fn(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 exists_as_fn = context.symbol_table.contains_fn(&identifier.pure_name)
|| context.current_function.as_ref() == Some(&identifier.pure_name);
// Eg. sqrt64
if match_token(context, TokenKind::Literal)
@ -582,7 +584,7 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|| peek(context).value == "ϕ"
{
// 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.pure_name) {
if exists_as_fn {
let parameter = if identifier.full_name == "" {
parse_exponent(context)?
} else {
@ -594,7 +596,7 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
let parse_as_var_instead = match_token(context, TokenKind::OpenParenthesis)
&& !context.parsing_identifier_stmt
&& !context.symbol_table.contains_fn(&identifier.pure_name);
&& !exists_as_fn;
// Eg. sqrt(64)
// If the function doesn't exist, parse it as a variable and multiplication instead.