mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-13 18:10:42 +01:00
Basics of derivation
Derivation implemented for function calls (only). Eg. f'(2). It is not yet possible to do something like f''(2), but this should be implemented in the future. It should also be possible to derive normal expressions, but this is not yet possible.
This commit is contained in:
parent
126e2e86f3
commit
7a56322e0f
@ -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<KalkNum, CalcError> {
|
||||
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<KalkNum, CalcError> {
|
||||
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)
|
||||
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
));
|
||||
}
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
|
@ -478,23 +478,20 @@ fn parse_group_fn(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
}
|
||||
|
||||
fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
||||
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<Expr, CalcError> {
|
||||
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<Expr, CalcError> {
|
||||
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<Expr, CalcError> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user