mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-01-19 03:38:13 +01:00
Fixed "dx" in integrals, and created calculus.rs
Previously, eg. "dx" would not be parsed as "dx" after a function, since the parser did not keep track of whether or not it was currently inside an integral or not, properly. This commit fixes that, and also makes it possible to use any variable after the "d", eg. "dy". The integration function was also put in its own file: calculus.rs.
This commit is contained in:
parent
651b289f4b
commit
0dc2e0572f
78
kalk/src/calculus.rs
Normal file
78
kalk/src/calculus.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use crate::ast::Expr;
|
||||||
|
use crate::ast::Stmt;
|
||||||
|
use crate::interpreter;
|
||||||
|
use crate::kalk_num::KalkNum;
|
||||||
|
use crate::lexer::TokenKind;
|
||||||
|
use crate::parser::CalcError;
|
||||||
|
|
||||||
|
pub fn integrate(
|
||||||
|
context: &mut interpreter::Context,
|
||||||
|
expressions: &[Expr],
|
||||||
|
) -> Result<KalkNum, CalcError> {
|
||||||
|
let mut result = KalkNum::default();
|
||||||
|
let mut integration_variable: Option<&str> = None;
|
||||||
|
|
||||||
|
// integral(a, b, expr dx)
|
||||||
|
if let Expr::Binary(_, TokenKind::Star, right) = &expressions[2] {
|
||||||
|
if let Expr::Var(right_name) = &**right {
|
||||||
|
if right_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..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if integration_variable.is_none() {
|
||||||
|
return Err(CalcError::ExpectedDx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta_x/2[f(a) + 2f(x_1) + 2f(x_2) + ...2f(x_n) + f(b)]
|
||||||
|
// where delta_x = (b - a) / n
|
||||||
|
// and x_n = a + i * delta_x
|
||||||
|
|
||||||
|
// f(a)
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
integration_variable.unwrap().into(),
|
||||||
|
Box::new(expressions[0].clone()),
|
||||||
|
));
|
||||||
|
|
||||||
|
// "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()),
|
||||||
|
Box::new(Expr::Literal(1f64)),
|
||||||
|
));
|
||||||
|
|
||||||
|
result.value += interpreter::eval_expr(context, &expressions[2], "")?.value;
|
||||||
|
|
||||||
|
// 2f(x_n)
|
||||||
|
// where x_n = a + i * delta_x
|
||||||
|
const N: i32 = 100;
|
||||||
|
let a = interpreter::eval_expr(context, &expressions[0], "")?
|
||||||
|
.value
|
||||||
|
.to_f64();
|
||||||
|
let b = interpreter::eval_expr(context, &expressions[1], "")?
|
||||||
|
.value
|
||||||
|
.to_f64();
|
||||||
|
let delta_x = (b - a) / N as f64;
|
||||||
|
for i in 1..N {
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
integration_variable.unwrap().into(),
|
||||||
|
Box::new(Expr::Literal(a + i as f64 * delta_x)),
|
||||||
|
));
|
||||||
|
|
||||||
|
// 2f(x_n)
|
||||||
|
result.value += 2 * interpreter::eval_expr(context, &expressions[2], "")?.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// f(b)
|
||||||
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
|
integration_variable.unwrap().into(),
|
||||||
|
Box::new(expressions[1].clone()),
|
||||||
|
));
|
||||||
|
result.value += interpreter::eval_expr(context, &expressions[2], "")?.value;
|
||||||
|
|
||||||
|
// Finally, delta_x/2 for all of it
|
||||||
|
result.value *= delta_x / 2f64;
|
||||||
|
|
||||||
|
return Ok(result);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
use crate::ast::{Expr, Stmt};
|
use crate::ast::{Expr, Stmt};
|
||||||
|
use crate::calculus;
|
||||||
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;
|
||||||
@ -7,7 +8,7 @@ use crate::prelude;
|
|||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
|
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a> {
|
||||||
symbol_table: &'a mut SymbolTable,
|
pub symbol_table: &'a mut SymbolTable,
|
||||||
angle_unit: String,
|
angle_unit: String,
|
||||||
#[cfg(feature = "rug")]
|
#[cfg(feature = "rug")]
|
||||||
precision: u32,
|
precision: u32,
|
||||||
@ -92,7 +93,11 @@ fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<KalkNum, CalcErr
|
|||||||
eval_expr(context, &expr, "")
|
eval_expr(context, &expr, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<KalkNum, CalcError> {
|
pub(crate) fn eval_expr(
|
||||||
|
context: &mut Context,
|
||||||
|
expr: &Expr,
|
||||||
|
unit: &str,
|
||||||
|
) -> Result<KalkNum, CalcError> {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
if let (Ok(elapsed), Some(timeout)) = (context.start_time.elapsed(), context.timeout) {
|
if let (Ok(elapsed), Some(timeout)) = (context.start_time.elapsed(), context.timeout) {
|
||||||
if elapsed.as_millis() >= timeout {
|
if elapsed.as_millis() >= timeout {
|
||||||
@ -312,68 +317,7 @@ fn eval_fn_call_expr(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = KalkNum::default();
|
return calculus::integrate(context, expressions);
|
||||||
let mut integration_variable: Option<&str> = None;
|
|
||||||
|
|
||||||
// integral(a, b, expr dx)
|
|
||||||
if let Expr::Binary(_, TokenKind::Star, right) = &expressions[2] {
|
|
||||||
if let Expr::Var(right_name) = &**right {
|
|
||||||
if right_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..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if integration_variable.is_none() {
|
|
||||||
unimplemented!(); // TODO: Error message
|
|
||||||
}
|
|
||||||
|
|
||||||
// delta_x/2[f(a) + 2f(x_1) + 2f(x_2) + ...2f(x_n) + f(b)]
|
|
||||||
// where delta_x = (b - a) / n
|
|
||||||
// and x_n = a + i * delta_x
|
|
||||||
|
|
||||||
// f(a)
|
|
||||||
context.symbol_table.set(Stmt::VarDecl(
|
|
||||||
integration_variable.unwrap().into(),
|
|
||||||
Box::new(expressions[0].clone()),
|
|
||||||
));
|
|
||||||
|
|
||||||
// "dx" is still in the expression. Set dx = 1, so that it doesn't affect the expression value.
|
|
||||||
context.symbol_table.set(Stmt::VarDecl(
|
|
||||||
String::from("dx"),
|
|
||||||
Box::new(Expr::Literal(1f64)),
|
|
||||||
));
|
|
||||||
|
|
||||||
result.value += eval_expr(context, &expressions[2], "")?.value;
|
|
||||||
|
|
||||||
// 2f(x_n)
|
|
||||||
// where x_n = a + i * delta_x
|
|
||||||
const N: i32 = 100;
|
|
||||||
let a = eval_expr(context, &expressions[0], "")?.value.to_f64();
|
|
||||||
let b = eval_expr(context, &expressions[1], "")?.value.to_f64();
|
|
||||||
let delta_x = (b - a) / N as f64;
|
|
||||||
for i in 1..N {
|
|
||||||
context.symbol_table.set(Stmt::VarDecl(
|
|
||||||
integration_variable.unwrap().into(),
|
|
||||||
Box::new(Expr::Literal(a + i as f64 * delta_x)),
|
|
||||||
));
|
|
||||||
|
|
||||||
// 2f(x_n)
|
|
||||||
result.value += 2 * eval_expr(context, &expressions[2], "")?.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// f(b)
|
|
||||||
context.symbol_table.set(Stmt::VarDecl(
|
|
||||||
integration_variable.unwrap().into(),
|
|
||||||
Box::new(expressions[1].clone()),
|
|
||||||
));
|
|
||||||
result.value += eval_expr(context, &expressions[2], "")?.value;
|
|
||||||
|
|
||||||
// Finally, delta_x/2 for all of it
|
|
||||||
result.value *= delta_x / 2f64;
|
|
||||||
|
|
||||||
return Ok(result);
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
mod calculus;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
mod inverter;
|
mod inverter;
|
||||||
pub mod kalk_num;
|
pub mod kalk_num;
|
||||||
|
@ -92,6 +92,7 @@ impl Default for Context {
|
|||||||
/// Error that occured during parsing or evaluation.
|
/// Error that occured during parsing or evaluation.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum CalcError {
|
pub enum CalcError {
|
||||||
|
ExpectedDx,
|
||||||
IncorrectAmountOfArguments(usize, String, usize),
|
IncorrectAmountOfArguments(usize, String, usize),
|
||||||
InvalidNumberLiteral(String),
|
InvalidNumberLiteral(String),
|
||||||
InvalidOperator,
|
InvalidOperator,
|
||||||
@ -110,6 +111,7 @@ pub enum CalcError {
|
|||||||
impl ToString for CalcError {
|
impl ToString for CalcError {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
CalcError::ExpectedDx => format!("Expected eg. dx, to specify for which variable the operation is being done to. Example with integration: ∫(0, 1, x dx)."),
|
||||||
CalcError::IncorrectAmountOfArguments(expected, func, got) => format!(
|
CalcError::IncorrectAmountOfArguments(expected, func, got) => format!(
|
||||||
"Expected {} arguments for function {}, but got {}.",
|
"Expected {} arguments for function {}, but got {}.",
|
||||||
expected, func, got
|
expected, func, got
|
||||||
@ -493,7 +495,8 @@ 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);
|
||||||
|
|
||||||
if identifier.value == "integrate" || identifier.value == "∫" {
|
let is_integral = identifier.value == "integrate" || identifier.value == "∫";
|
||||||
|
if is_integral {
|
||||||
context.is_in_integral = true;
|
context.is_in_integral = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +509,10 @@ fn parse_identifier(context: &mut Context) -> Result<Expr, CalcError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
consume(context, TokenKind::ClosedParenthesis)?;
|
consume(context, TokenKind::ClosedParenthesis)?;
|
||||||
context.is_in_integral = false;
|
|
||||||
|
if is_integral {
|
||||||
|
context.is_in_integral = false;
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(Expr::FnCall(identifier.value, parameters));
|
return Ok(Expr::FnCall(identifier.value, parameters));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user