- added support for a single variable to the calculator

- added support for pre-compiling calculator expressions and substituting in values later
This commit is contained in:
FantasyPvP
2024-03-23 01:24:52 +00:00
parent c4067fabc8
commit 15a8a6ac5d
5 changed files with 124 additions and 56 deletions
+76 -27
View File
@@ -1,8 +1,8 @@
use core::fmt; use core::fmt;
use alloc::{boxed::Box, string::String, vec::Vec}; use alloc::{boxed::Box, format, string::String, vec::Vec};
use alloc::string::ToString; use alloc::string::ToString;
use alloc::borrow::ToOwned; use alloc::borrow::ToOwned;
use crate::{println, print, mknode, std}; use crate::{println, print, mknode, std, serial_println};
use async_trait::async_trait; use async_trait::async_trait;
use crate::std::application::{ use crate::std::application::{
@@ -35,6 +35,7 @@ impl Interpreter {
Node::Number(_) => return self.visit_number(node), Node::Number(_) => return self.visit_number(node),
Node::Operator(_) => return self.visit_operator(node), Node::Operator(_) => return self.visit_operator(node),
Node::Function(_) => return self.visit_function(node), Node::Function(_) => return self.visit_function(node),
Node::Variable => panic!("substitution not used!"),
} }
} }
@@ -195,16 +196,6 @@ impl Interpreter {
} }
impl Parser { impl Parser {
fn new(tokens: Vec<Token>) -> Result<Self, Error> { fn new(tokens: Vec<Token>) -> Result<Self, Error> {
let mut parser = Self { tokens, idx: -1, current: Token::Null }; let mut parser = Self { tokens, idx: -1, current: Token::Null };
@@ -217,9 +208,7 @@ impl Parser {
let result = self.expr(); let result = self.expr();
result result
} }
fn advance(&mut self) -> Result<Option<Token>, Error> { fn advance(&mut self) -> Result<Option<Token>, Error> {
self.idx += 1; self.idx += 1;
if self.idx < self.tokens.len() as i32 { if self.idx < self.tokens.len() as i32 {
@@ -240,7 +229,10 @@ impl Parser {
self.advance()?; self.advance()?;
return Ok(Node::Number(x)) return Ok(Node::Number(x))
}, },
Token::Variable => {
self.advance()?;
return Ok(Node::Variable)
},
Token::Bracket('(') => { Token::Bracket('(') => {
self.advance()?; self.advance()?;
let expr = self.expr()?; let expr = self.expr()?;
@@ -381,7 +373,7 @@ impl Application for Calculator {
match self.calculate_and_format(inp) { match self.calculate_and_format(inp) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
println!("your input must be a valid mathematical expression contaning only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]"); println!("your input must be a valid mathematical expression containing only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]");
println!("{:?}", e); println!("{:?}", e);
return Err(ShellError::CommandFailed(String::from("failed"))) return Err(ShellError::CommandFailed(String::from("failed")))
}, },
@@ -391,7 +383,7 @@ impl Application for Calculator {
match self.calculate_and_format(args.into_iter().collect()) { match self.calculate_and_format(args.into_iter().collect()) {
Ok(x) => x, Ok(x) => x,
Err(e) => { Err(e) => {
println!("your input must be a valid mathematical expression contaning only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]"); println!("your input must be a valid mathematical expression containing only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]");
println!("{:?}", e); println!("{:?}", e);
return Err(ShellError::CommandFailed(String::from("failed"))) return Err(ShellError::CommandFailed(String::from("failed")))
}, },
@@ -417,7 +409,51 @@ impl Calculator {
{}", equation, res); {}", equation, res);
Ok(res) Ok(res)
} }
pub fn get_expr(&self, mut equation: String) -> Result<Node, String> {
equation.push('\n');
let mut neweq = equation.clone();
neweq.pop();
let tokens = tokenise(&equation).map_err(|e| format!(
"failed to tokenise: {:?}",
e
))?;
serial_println!("{:?}", tokens);
let mut parser = Parser::new(tokens).unwrap();
parser.parse().map_err(|e| format!("{:?}", e))
}
pub fn substitute(&self, equation: &mut Node, value: f64) {
match equation {
Node::BinaryOperation(x) => {
self.substitute(&mut x.left, value);
self.substitute(&mut x.right, value);
},
Node::UnaryOperation(x) => {
self.substitute(&mut x.other, value);
},
Node::Function(x) => {
self.substitute(&mut x.arg, value);
},
Node::Variable => {
*equation = Node::Number(value);
}
_ => ()
}
}
pub fn evaluate(&self, equation: &Node) -> Result<f64, String> {
let mut interpreter = Interpreter::new().unwrap();
let result = interpreter.visit(equation.clone()).unwrap();
let return_res = if let Value::Number(x) = result {
x
} else { panic!("the value returned was not a float! THIS IS A BUG") };
Ok(return_res)
}
fn calculate_inner(&self, mut equation: String) -> Result<f64, Error> { fn calculate_inner(&self, mut equation: String) -> Result<f64, Error> {
equation.push('\n'); equation.push('\n');
let mut neweq = equation.clone(); let mut neweq = equation.clone();
@@ -436,6 +472,7 @@ impl Calculator {
} }
} }
fn tokenise(equation: &str) -> Result<Vec<Token>, Error> { fn tokenise(equation: &str) -> Result<Vec<Token>, Error> {
let mut tokens = Vec::new(); let mut tokens = Vec::new();
let mut current_num = "".to_string(); let mut current_num = "".to_string();
@@ -446,6 +483,16 @@ fn tokenise(equation: &str) -> Result<Vec<Token>, Error> {
'mainloop: for (x, character) in equation.chars().enumerate() { 'mainloop: for (x, character) in equation.chars().enumerate() {
match character { match character {
'x' => {
if is_var {
tokens.push(Token::Func(current_string.clone()));
}
is_var = false;
current_string = "".to_string();
tokens.push(Token::Variable);
continue;
}
'a'..='z' => { 'a'..='z' => {
is_var = true; is_var = true;
current_string.push(character); current_string.push(character);
@@ -468,7 +515,6 @@ fn tokenise(equation: &str) -> Result<Vec<Token>, Error> {
tokens.push(Token::Number(current_num.parse::<f64>().unwrap())); tokens.push(Token::Number(current_num.parse::<f64>().unwrap()));
current_num = "".to_string(); current_num = "".to_string();
} else if current_string.len() != 0 { } else if current_string.len() != 0 {
} match character { } match character {
'+' => tokens.push(Token::Operator(Operator::Add)), '+' => tokens.push(Token::Operator(Operator::Add)),
'-' => tokens.push(Token::Operator(Operator::Sub)), '-' => tokens.push(Token::Operator(Operator::Sub)),
@@ -495,7 +541,7 @@ fn tokenise(equation: &str) -> Result<Vec<Token>, Error> {
'\n' => break 'mainloop, '\n' => break 'mainloop,
' ' => (), ' ' => (),
_ => { _ => {
return Err(Error::InvalidCharacter(x)) return Err(Error::InvalidCharacter(character))
}, },
} }
} }
@@ -522,11 +568,12 @@ enum Token {
Bracket(char), Bracket(char),
Null, Null,
Func(String), Func(String),
Variable,
} }
#[derive(Copy, Debug, Clone, PartialEq)] #[derive(Copy, Debug, Clone, PartialEq)]
enum Operator { pub enum Operator {
Add, Add,
Sub, Sub,
Mul, Mul,
@@ -539,7 +586,7 @@ enum Operator {
#[derive(Debug)] #[derive(Debug)]
enum Error { enum Error {
InvalidSyntax(usize), InvalidSyntax(usize),
InvalidCharacter(usize), InvalidCharacter(char),
LogicalError(String), LogicalError(String),
Eof, Eof,
Other(String), Other(String),
@@ -548,8 +595,9 @@ enum Error {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum Node { pub enum Node {
Number(f64), Number(f64),
Variable,
Operator(Operator), Operator(Operator),
Function(Box<FunctionCall>), Function(Box<FunctionCall>),
BinaryOperation(Box<BinaryOperation>), BinaryOperation(Box<BinaryOperation>),
@@ -558,20 +606,20 @@ enum Node {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct BinaryOperation { pub struct BinaryOperation {
left: Node, left: Node,
operator: Node, operator: Node,
right: Node, right: Node,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct UnaryOperation { pub struct UnaryOperation {
operator: Node, operator: Node,
other: Node, other: Node,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct FunctionCall { pub struct FunctionCall {
name: String, name: String,
arg: Node, arg: Node,
} }
@@ -612,6 +660,7 @@ impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Node::Number(x) => write!(f, "{}", x), Node::Number(x) => write!(f, "{}", x),
Node::Variable => write!(f, "x"),
Node::Operator(x) => write!(f, "{}", x), Node::Operator(x) => write!(f, "{}", x),
Node::BinaryOperation(x) => { Node::BinaryOperation(x) => {
let inner = *x.clone(); let inner = *x.clone();
+2 -1
View File
@@ -1,4 +1,5 @@
pub mod calc; //pub mod calc;
mod functions; mod functions;
pub mod calc;
pub use calc::*; pub use calc::*;
+43 -25
View File
@@ -22,6 +22,7 @@ const OFFSET_Y: i64 = 10;
use core::f64::consts::E; use core::f64::consts::E;
use core::f64::consts::PI; use core::f64::consts::PI;
use crate::serial_println;
#[derive(Clone)] #[derive(Clone)]
pub struct Grapher { pub struct Grapher {
@@ -89,14 +90,14 @@ impl Application for Grapher {
let mut offset_x: i64 = 0; let mut offset_x: i64 = 0;
let mut offset_y: i64 = 0; let mut offset_y: i64 = 0;
let mut rerender = true; let mut redraw_graph = true;
while let c = Stdin::keystroke().await { while let c = Stdin::keystroke().await {
let entry_widget = container.elements.get("entry_box").unwrap(); let entry_widget = container.elements.get("entry_box").unwrap();
let mut entry = entry_widget.fetch::<CgLineEdit>().unwrap(); let mut entry = entry_widget.fetch::<CgLineEdit>().unwrap();
rerender = true; redraw_graph = true;
match c { match c {
KeyStroke::Char('\n') => { KeyStroke::Char('\n') => {
commandresult = entry.text.iter().collect(); commandresult = entry.text.iter().collect();
@@ -105,24 +106,27 @@ impl Application for Grapher {
offset_y = 0; offset_y = 0;
}, },
KeyStroke::Char(Stdin::BACKSPACE) => { KeyStroke::Char(Stdin::BACKSPACE) => {
rerender = false; redraw_graph = false;
entry.backspace() entry.backspace()
}, },
KeyStroke::Char('`') => { KeyStroke::Char('`') => {
break; break;
} }
KeyStroke::Char(c) => entry.write_char(c), KeyStroke::Char(c) => {
redraw_graph = false;
entry.write_char(c)
},
KeyStroke::Left => offset_x -= 1, KeyStroke::Left => offset_x -= 1,
KeyStroke::Right => offset_x += 1, KeyStroke::Right => offset_x += 1,
KeyStroke::Up => offset_y -= 1, KeyStroke::Up => offset_y -= 1,
KeyStroke::Down => offset_y += 1, KeyStroke::Down => offset_y += 1,
KeyStroke::Alt => break, KeyStroke::Alt => break,
_ => { _ => {
rerender = false; redraw_graph = false;
} }
} }
if commandresult.len() > 0 && rerender { if commandresult.len() > 0 && redraw_graph {
self.reset_frame(); self.reset_frame();
self.graph_equation(commandresult.clone(), (offset_x, offset_y)); self.graph_equation(commandresult.clone(), (offset_x, offset_y));
let self_widget = container.elements.get("grapher").unwrap(); let self_widget = container.elements.get("grapher").unwrap();
@@ -153,26 +157,40 @@ impl Grapher {
fn graph_equation(&mut self, equation: String, offsets: (i64, i64)) { fn graph_equation(&mut self, equation: String, offsets: (i64, i64)) {
let cal = calc::Calculator::new(); let cal = calc::Calculator::new();
for x in -4000..4000 { let ast = cal.get_expr(equation.chars().map(|c| {
let x = x as f64 / 100.0; match c {
'e' => format!("({})", E),
let new_eq = equation.chars().map(|c| { 'π' => format!("({})", PI),
match c { _ => c.to_string(),
'x' => format!("({})", x + offsets.0 as f64),
'e' => format!("({})", E),
'π' => format!("({})", PI),
_ => c.to_string(),
}
}).collect::<String>();
let fx = cal.calculate(new_eq);
if let Ok(y) = fx {
self.render_point(PointF64 {
x,
y: y + offsets.1 as f64,
})
} }
}; }).collect::<String>());
if let Ok(ast) = ast {
for x in -4000..4000 {
let x = x as f64 / 100.0;
let mut cmd = ast.clone();
cal.substitute(&mut cmd, x + offsets.0 as f64);
//
// let new_eq = equation.chars().map(|c| {
// match c {
// 'x' => format!("({})", x + offsets.0 as f64),
// 'e' => format!("({})", E),
// 'π' => format!("({})", PI),
// _ => c.to_string(),
// }
// }).collect::<String>();
let fx = cal.evaluate(&cmd);
if let Ok(y) = fx {
self.render_point(PointF64 {
x,
y: y + offsets.1 as f64,
})
}
};
}
} }
+2 -2
View File
@@ -131,10 +131,10 @@ impl Ball {
(self.pos.y as i32 + self.vy) as usize (self.pos.y as i32 + self.vy) as usize
); );
for i in 0..5 { for i in 0..5 {
if player1.pos.y + i == pos_next.y && player1.pos.x == pos_next.x { if player1.pos.y + i - 2 == pos_next.y && player1.pos.x == pos_next.x {
self.vx = -self.vx; self.vx = -self.vx;
break; break;
} else if player2.pos.y + i == pos_next.y && player2.pos.x == pos_next.x { } else if player2.pos.y + i - 2 == pos_next.y && player2.pos.x == pos_next.x {
self.vx = -self.vx; self.vx = -self.vx;
break; break;
} }
+1 -1
View File
@@ -173,7 +173,7 @@ impl Game {
false => ColorCode::new(Color::LightGreen, Color::Black), false => ColorCode::new(Color::LightGreen, Color::Black),
}; };
for point in s.tail.iter() { for point in s.tail.iter() {
frame[24 - point.y as usize][point.x as usize] = ColouredChar::coloured('@', curr_colour); frame[24 - point.y as usize][point.x as usize] = ColouredChar::coloured('', curr_colour);
} }
} }