- 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:
+76
-27
@@ -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();
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user