mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-14 18:40:50 +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::Expr;
|
||||||
|
use crate::ast::Identifier;
|
||||||
use crate::ast::Stmt;
|
use crate::ast::Stmt;
|
||||||
use crate::interpreter;
|
use crate::interpreter;
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_num::KalkNum;
|
||||||
use crate::lexer::TokenKind;
|
use crate::lexer::TokenKind;
|
||||||
use crate::parser::CalcError;
|
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(
|
pub fn integrate(
|
||||||
context: &mut interpreter::Context,
|
context: &mut interpreter::Context,
|
||||||
expressions: &[Expr],
|
a: &Expr,
|
||||||
|
b: &Expr,
|
||||||
|
expr: &Expr,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkNum, CalcError> {
|
||||||
let mut integration_variable: Option<&str> = None;
|
let mut integration_variable: Option<&str> = None;
|
||||||
|
|
||||||
// integral(a, b, expr dx)
|
// 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 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
|
// 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.
|
// "dx" is still in the expression. Set dx = 1, so that it doesn't affect the expression value.
|
||||||
context.symbol_table.set(Stmt::VarDecl(
|
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)),
|
Box::new(Expr::Literal(1f64)),
|
||||||
));
|
));
|
||||||
|
|
||||||
simpsons_rule(
|
simpsons_rule(context, a, b, expr, integration_variable.unwrap())
|
||||||
context,
|
|
||||||
&expressions[0],
|
|
||||||
&expressions[1],
|
|
||||||
&expressions[2],
|
|
||||||
integration_variable.unwrap(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Composite Simpson's 3/8 rule
|
/// Composite Simpson's 3/8 rule
|
||||||
@ -56,16 +79,14 @@ fn simpsons_rule(
|
|||||||
let h = (b - a) / N as f64;
|
let h = (b - a) / N as f64;
|
||||||
for i in 0..=N {
|
for i in 0..=N {
|
||||||
context.symbol_table.set(Stmt::VarDecl(
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
integration_variable.into(),
|
Identifier::from_full_name(integration_variable),
|
||||||
Box::new(Expr::Literal(a + i as f64 * h)),
|
Box::new(Expr::Literal(a + i as f64 * h)),
|
||||||
));
|
));
|
||||||
|
|
||||||
let factor = if i == 0 || i == N {
|
let factor = match i {
|
||||||
1
|
0 | N => 1,
|
||||||
} else if i % 3 == 0 {
|
_ if i % 3 == 0 => 2,
|
||||||
2
|
_ => 3,
|
||||||
} else {
|
|
||||||
3
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// factor * f(x_n)
|
// factor * f(x_n)
|
||||||
|
@ -244,7 +244,7 @@ fn eval_var_expr(
|
|||||||
.cloned();
|
.cloned();
|
||||||
match var_decl {
|
match var_decl {
|
||||||
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr, unit),
|
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
|
// Prelude
|
||||||
let prelude_func = match expressions.len() {
|
let prelude_func = match expressions.len() {
|
||||||
1 => {
|
1 => {
|
||||||
let x = eval_expr(context, &expressions[0], "")?.value;
|
let x = eval_expr(context, &expressions[0], "")?;
|
||||||
prelude::call_unary_func(context, identifier, x, &context.angle_unit.clone())
|
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 => {
|
2 => {
|
||||||
let x = eval_expr(context, &expressions[0], "")?.value;
|
let x = eval_expr(context, &expressions[0], "")?.value;
|
||||||
let y = eval_expr(context, &expressions[1], "")?.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,
|
_ => None,
|
||||||
};
|
};
|
||||||
@ -339,7 +354,7 @@ pub(crate) fn eval_fn_call_expr(
|
|||||||
if arguments.len() != expressions.len() {
|
if arguments.len() != expressions.len() {
|
||||||
return Err(CalcError::IncorrectAmountOfArguments(
|
return Err(CalcError::IncorrectAmountOfArguments(
|
||||||
arguments.len(),
|
arguments.len(),
|
||||||
identifier.full_name,
|
identifier.full_name.clone(),
|
||||||
expressions.len(),
|
expressions.len(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -357,7 +372,7 @@ pub(crate) fn eval_fn_call_expr(
|
|||||||
|
|
||||||
eval_expr(context, &fn_body, unit)
|
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,
|
unknown_var: &str,
|
||||||
) -> Result<(Expr, Expr), CalcError> {
|
) -> Result<(Expr, Expr), CalcError> {
|
||||||
if identifier.full_name == unknown_var {
|
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)) =
|
} else if let Some(Stmt::VarDecl(_, var_expr)) =
|
||||||
symbol_table.get_var(&identifier.full_name).cloned()
|
symbol_table.get_var(&identifier.full_name).cloned()
|
||||||
{
|
{
|
||||||
invert(target_expr, symbol_table, &var_expr, unknown_var)
|
invert(target_expr, symbol_table, &var_expr, unknown_var)
|
||||||
} else {
|
} else {
|
||||||
Ok((target_expr, Expr::Var(*identifier)))
|
Ok((target_expr, Expr::Var(identifier.clone())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,14 +329,14 @@ fn invert_fn_call(
|
|||||||
{
|
{
|
||||||
(parameters, body)
|
(parameters, body)
|
||||||
} else {
|
} else {
|
||||||
return Err(CalcError::UndefinedFn(identifier.full_name));
|
return Err(CalcError::UndefinedFn(identifier.full_name.clone()));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure the input is valid.
|
// Make sure the input is valid.
|
||||||
if parameters.len() != arguments.len() {
|
if parameters.len() != arguments.len() {
|
||||||
return Err(CalcError::IncorrectAmountOfArguments(
|
return Err(CalcError::IncorrectAmountOfArguments(
|
||||||
parameters.len(),
|
parameters.len(),
|
||||||
identifier.full_name,
|
identifier.full_name.clone(),
|
||||||
arguments.len(),
|
arguments.len(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ pub enum TokenKind {
|
|||||||
Equals,
|
Equals,
|
||||||
Exclamation,
|
Exclamation,
|
||||||
Percent,
|
Percent,
|
||||||
|
Tick,
|
||||||
|
|
||||||
UnitKeyword,
|
UnitKeyword,
|
||||||
ToKeyword,
|
ToKeyword,
|
||||||
@ -114,6 +115,7 @@ impl<'a> Lexer<'a> {
|
|||||||
',' => build(TokenKind::Comma, "", span),
|
',' => build(TokenKind::Comma, "", span),
|
||||||
';' => build(TokenKind::Semicolon, "", span),
|
';' => build(TokenKind::Semicolon, "", span),
|
||||||
'%' => build(TokenKind::Percent, "", span),
|
'%' => build(TokenKind::Percent, "", span),
|
||||||
|
'\'' => build(TokenKind::Tick, "", span),
|
||||||
_ => build(TokenKind::Unknown, "", 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> {
|
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
|
// Eg. sqrt64
|
||||||
if match_token(context, TokenKind::Literal) {
|
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 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));
|
let parameter = Expr::Literal(string_to_num(&advance(context).value));
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(identifier, vec![parameter]));
|
||||||
Identifier::from_full_name(&identifier.value),
|
|
||||||
vec![parameter],
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let parse_as_var_instead = match_token(context, TokenKind::OpenParenthesis)
|
let parse_as_var_instead = match_token(context, TokenKind::OpenParenthesis)
|
||||||
&& !context.parsing_identifier_stmt
|
&& !context.parsing_identifier_stmt
|
||||||
&& !context.symbol_table.contains_fn(&identifier.value);
|
&& !context.symbol_table.contains_fn(&identifier.pure_name);
|
||||||
|
|
||||||
// Eg. sqrt(64)
|
// Eg. sqrt(64)
|
||||||
// If the function doesn't exist, parse it as a variable and multiplication instead.
|
// 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) {
|
if !parse_as_var_instead && match_token(context, TokenKind::OpenParenthesis) {
|
||||||
advance(context);
|
advance(context);
|
||||||
|
|
||||||
let is_integral = identifier.value == "integrate" || identifier.value == "∫";
|
let is_integral = identifier.full_name == "integrate" || identifier.full_name == "∫";
|
||||||
if is_integral {
|
if is_integral {
|
||||||
context.is_in_integral = true;
|
context.is_in_integral = true;
|
||||||
}
|
}
|
||||||
@ -522,34 +519,31 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
context.is_in_integral = false;
|
context.is_in_integral = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(identifier, parameters));
|
||||||
Identifier::from_full_name(&identifier.value),
|
|
||||||
parameters,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eg. dx inside an integral, should be parsed as *one* identifier
|
// Eg. dx inside an integral, should be parsed as *one* identifier
|
||||||
if context.is_in_integral && identifier.value.starts_with("d") {
|
if context.is_in_integral && identifier.full_name.starts_with("d") {
|
||||||
return Ok(Expr::Var(Identifier::from_full_name(&identifier.value)));
|
return Ok(Expr::Var(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eg. x
|
// Eg. x
|
||||||
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.value) {
|
if parse_as_var_instead || context.symbol_table.contains_var(&identifier.pure_name) {
|
||||||
Ok(Expr::Var(Identifier::from_full_name(&identifier.value)))
|
Ok(Expr::Var(identifier))
|
||||||
} else if context.parsing_unit_decl {
|
} 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)))
|
Ok(Expr::Var(Identifier::from_full_name(DECL_UNIT)))
|
||||||
} else {
|
} else {
|
||||||
if let Some(equation_var) = &context.equation_variable {
|
if let Some(equation_var) = &context.equation_variable {
|
||||||
if &identifier.value == equation_var {
|
if &identifier.full_name == equation_var {
|
||||||
return Ok(Expr::Var(Identifier::from_full_name(&identifier.value)));
|
return Ok(Expr::Var(identifier));
|
||||||
}
|
}
|
||||||
} else if context.contains_equal_sign {
|
} else if context.contains_equal_sign {
|
||||||
context.equation_variable = Some(identifier.value.clone());
|
context.equation_variable = Some(identifier.full_name.clone());
|
||||||
return Ok(Expr::Var(Identifier::from_full_name(&identifier.value)));
|
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(
|
let mut left = Expr::Var(Identifier::from_full_name(
|
||||||
&chars.next().unwrap().to_string(),
|
&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));
|
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)
|
Ok(left)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user