reorganised some stuff and started coding another game
This commit is contained in:
Executable
+690
@@ -0,0 +1,690 @@
|
||||
use core::fmt;
|
||||
use alloc::{boxed::Box, format, string::String, vec::Vec};
|
||||
use alloc::string::ToString;
|
||||
use alloc::borrow::ToOwned;
|
||||
use crate::{println, print, mknode, std, serial_println};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use crate::std::application::{
|
||||
Application,
|
||||
Error as ShellError
|
||||
};
|
||||
|
||||
|
||||
struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
idx: i32,
|
||||
current: Token
|
||||
}
|
||||
|
||||
|
||||
struct Interpreter {}
|
||||
|
||||
|
||||
|
||||
impl Interpreter {
|
||||
|
||||
fn new() -> Result<Self, Error>{
|
||||
return Ok(Self {})
|
||||
}
|
||||
|
||||
fn visit(&mut self, node: Node) -> Result<Value, Error> {
|
||||
match node {
|
||||
Node::BinaryOperation(_) => return self.visit_binary_operation(node),
|
||||
Node::UnaryOperation(_) => return self.visit_unary_operation(node),
|
||||
Node::Number(_) => return self.visit_number(node),
|
||||
Node::Operator(_) => return self.visit_operator(node),
|
||||
Node::Function(_) => return self.visit_function(node),
|
||||
Node::Variable => panic!("substitution not used!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, node: Node) -> Result<Value, Error> { // function calls such as sqrt(5) within the expression are evaluated here
|
||||
let func = if let Node::Function(x) = node.clone() {
|
||||
x
|
||||
} else {
|
||||
return Err(Error::Other(String::from("value is not a function")))
|
||||
};
|
||||
let function_name = func.name;
|
||||
|
||||
let inner = self.visit(self.get_node(node.clone(), "argument")?.expect("returned none").to_owned())?;
|
||||
if let Value::Number(x) = inner {
|
||||
return Ok(Value::Number(super::functions::run_func(function_name, x).map_err(|x| Error::Other(x.to_string()))?));
|
||||
} else {
|
||||
return Err(Error::Other(String::from("function argument is not a number")))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_number(&mut self, node: Node) -> Result<Value, Error> {
|
||||
|
||||
if let Node::Number(x) = node {
|
||||
Ok(Value::Number(x))
|
||||
} else {
|
||||
Err(Error::Other(String::from("value accessed was not an number")))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn visit_operator(&mut self, node: Node) -> Result<Value, Error> {
|
||||
|
||||
if let Node::Operator(x) = node {
|
||||
Ok(Value::Operator(x))
|
||||
} else {
|
||||
Err(Error::Other(String::from("value is not an operator")))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn visit_binary_operation(&mut self, node: Node) -> Result<Value, Error> {
|
||||
|
||||
let left = match self.visit(self.get_node(node.clone(), "left")?.expect("returned none").to_owned())? {
|
||||
Value::Number(x) => x,
|
||||
_ => return Err(Error::Other(String::from("value is not a number"))),
|
||||
};
|
||||
|
||||
let right = match self.visit(self.get_node(node.clone(), "right")?.expect("returned none").to_owned())? {
|
||||
Value::Number(x) => x,
|
||||
_ => return Err(Error::Other(String::from("value is not a number"))),
|
||||
};
|
||||
|
||||
let operator = match self.visit(self.get_node(node.clone(), "operator")?.expect("returned none").to_owned())? {
|
||||
Value::Operator(x) => x,
|
||||
_ => return Err(Error::Other(String::from("value is not a binary operator"))),
|
||||
};
|
||||
|
||||
match operator {
|
||||
Operator::Add => {
|
||||
return Ok(Value::Number(left + right))
|
||||
},
|
||||
Operator::Sub => {
|
||||
return Ok(Value::Number(left - right))
|
||||
},
|
||||
Operator::Div => {
|
||||
if right != 0.0 {
|
||||
return Ok(Value::Number(left / right))
|
||||
} else {
|
||||
return Err(Error::LogicalError(String::from("division by 0")))
|
||||
}
|
||||
},
|
||||
Operator::Mod => {
|
||||
return Ok(Value::Number(left % right))
|
||||
},
|
||||
Operator::Qot => {
|
||||
if right != 0.0 {
|
||||
return Ok(Value::Number(
|
||||
((left / right) as i64) as f64
|
||||
))
|
||||
} else {
|
||||
return Err(Error::LogicalError(String::from("division by 0")))
|
||||
}
|
||||
},
|
||||
Operator::Mul => {
|
||||
return Ok(Value::Number(left * right))
|
||||
},
|
||||
Operator::Exp => {
|
||||
return Ok(Value::Number({
|
||||
super::functions::exp(left, right)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn visit_unary_operation(&mut self, node: Node) -> Result<Value, Error> {
|
||||
|
||||
let other: f64 = match self.visit(self.get_node(node.clone(), "other")?.expect("returned none").to_owned())? {
|
||||
Value::Number(x) => x,
|
||||
_ => return Err(Error::LogicalError("value is not a number".to_string()))
|
||||
};
|
||||
|
||||
if let Node::UnaryOperation(x) = node {
|
||||
match x.operator {
|
||||
Node::Operator(Operator::Sub) => {
|
||||
return Ok(Value::Number(other * -1f64));
|
||||
},
|
||||
_ => return Err(Error::LogicalError("value is not an operator".to_string()))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::LogicalError("node is not a binary operator".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_node(&self, node: Node, position: &str) -> Result<Option<Node>, Error> {
|
||||
let result = match position {
|
||||
"left" => {
|
||||
if let Node::BinaryOperation(x) = node {
|
||||
Some(x.left)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
"right" => {
|
||||
if let Node::BinaryOperation(x) = node {
|
||||
Some(x.right)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
"other" => {
|
||||
if let Node::UnaryOperation(x) = node {
|
||||
Some(x.other)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
"operator" => {
|
||||
if let Node::BinaryOperation(x) = node {
|
||||
Some(x.operator)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
"argument" => {
|
||||
if let Node::Function(x) = node {
|
||||
Some(x.arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::Other(String::from("invalid param for get_Node")))
|
||||
};
|
||||
Ok(result)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
fn new(tokens: Vec<Token>) -> Result<Self, Error> {
|
||||
let mut parser = Self { tokens, idx: -1, current: Token::Null };
|
||||
parser.advance()?;
|
||||
Ok(parser)
|
||||
|
||||
}
|
||||
|
||||
fn parse(&mut self) -> Result<Node, Error> {
|
||||
let result = self.expr();
|
||||
result
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Result<Option<Token>, Error> {
|
||||
self.idx += 1;
|
||||
if self.idx < self.tokens.len() as i32 {
|
||||
self.current = self.tokens[self.idx as usize].clone();
|
||||
Ok(Some(self.current.clone()))
|
||||
} else if self.idx == self.tokens.len() as i32 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(Error::Eof)
|
||||
}
|
||||
}
|
||||
|
||||
fn atom(&mut self) -> Result<Node, Error> {
|
||||
let token = self.current.clone();
|
||||
|
||||
match token {
|
||||
Token::Number(x) => {
|
||||
self.advance()?;
|
||||
return Ok(Node::Number(x))
|
||||
},
|
||||
Token::Variable => {
|
||||
self.advance()?;
|
||||
return Ok(Node::Variable)
|
||||
},
|
||||
Token::Bracket('(') => {
|
||||
self.advance()?;
|
||||
let expr = self.expr()?;
|
||||
|
||||
if let Token::Bracket(')') = self.current {
|
||||
self.advance()?;
|
||||
return Ok(expr)
|
||||
} else {
|
||||
return Err(Error::InvalidSyntax(0))
|
||||
}
|
||||
},
|
||||
_ => return Err(Error::InvalidSyntax(0))
|
||||
}
|
||||
}
|
||||
|
||||
fn power(&mut self) -> Result<Node, Error> {
|
||||
let mut left = self.atom()?;
|
||||
|
||||
while let Token::Operator(Operator::Exp) = self.current {
|
||||
let current = self.current.clone();
|
||||
self.advance()?;
|
||||
let operator = mknode!(current).expect("mknode function returned None");
|
||||
let right = self.factor()?;
|
||||
|
||||
left = Node::BinaryOperation(Box::new(BinaryOperation { left: left.clone(), operator, right }));
|
||||
}
|
||||
Ok(left)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn factor(&mut self) -> Result<Node, Error> {
|
||||
|
||||
let token = self.current.clone();
|
||||
|
||||
match token {
|
||||
Token::Func(x) => {
|
||||
self.advance()?;
|
||||
if let Token::Bracket('(') = self.current {
|
||||
self.advance()?;
|
||||
} else {
|
||||
return Err(Error::InvalidSyntax(self.idx as usize))
|
||||
};
|
||||
let arg = self.expr()?;
|
||||
if let Token::Bracket(')') = self.current {
|
||||
self.advance()?;
|
||||
} else {
|
||||
return Err(Error::InvalidSyntax(self.idx as usize))
|
||||
};
|
||||
|
||||
return Ok(Node::Function(Box::new(FunctionCall { name: x, arg })))
|
||||
},
|
||||
|
||||
|
||||
Token::Operator(Operator::Add) | Token::Operator(Operator::Sub) => {
|
||||
self.advance()?;
|
||||
let operator = mknode!(token).expect("mknode returned none");
|
||||
let other = self.factor().expect("holup");
|
||||
return Ok(Node::UnaryOperation(Box::new(UnaryOperation { operator, other})))
|
||||
},
|
||||
|
||||
_ => {
|
||||
return Ok(self.power()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn term(&mut self) -> Result<Node, Error> {
|
||||
|
||||
let mut left = self.factor()?;
|
||||
|
||||
while let Token::Operator(Operator::Div)
|
||||
| Token::Operator(Operator::Mul)
|
||||
| Token::Operator(Operator::Mod)
|
||||
| Token::Operator(Operator::Qot) = self.current {
|
||||
|
||||
let current = self.current.clone();
|
||||
self.advance()?;
|
||||
let operator = mknode!(current).expect("mknode function returned None");
|
||||
let right = self.factor()?;
|
||||
|
||||
left = Node::BinaryOperation(Box::new(BinaryOperation { left: left.clone(), operator, right }));
|
||||
}
|
||||
Ok(left)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn expr(&mut self) -> Result<Node, Error> {
|
||||
let mut left = self.term()?;
|
||||
|
||||
while let Token::Operator(Operator::Sub) | Token::Operator(Operator::Add) = self.current {
|
||||
let current = self.current.clone();
|
||||
self.advance()?;
|
||||
let operator = mknode!(current).expect("mknode returned None");
|
||||
let right = self.term()?;
|
||||
|
||||
left = Node::BinaryOperation(Box::new(BinaryOperation { left: left.clone(), operator, right }));
|
||||
}
|
||||
Ok(left)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! mknode {
|
||||
($token:expr) => {
|
||||
match $token {
|
||||
Token::Operator(x) => Some(Node::Operator(x)),
|
||||
Token::Number(x) => Some(Node::Number(x)),
|
||||
_ => None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub struct Calculator {}
|
||||
|
||||
#[async_trait]
|
||||
impl Application for Calculator {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), ShellError> {
|
||||
if args.len() == 0 {
|
||||
loop {
|
||||
print!("enter equation > ");
|
||||
let inp = std::io::Stdin::readline().await;
|
||||
println!("{}", inp);
|
||||
if inp == String::from("exit\n") {
|
||||
return Ok(());
|
||||
}
|
||||
match self.calculate_and_format(inp) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
println!("your input must be a valid mathematical expression containing only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]");
|
||||
println!("{:?}", e);
|
||||
return Err(ShellError::CommandFailed(String::from("failed")))
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
match self.calculate_and_format(args.into_iter().collect()) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
println!("your input must be a valid mathematical expression containing only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]");
|
||||
println!("{:?}", e);
|
||||
return Err(ShellError::CommandFailed(String::from("failed")))
|
||||
},
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl Calculator {
|
||||
pub fn calculate(&self, equation: String) -> Result<f64, String> {
|
||||
self.calculate_inner(equation).map_err(|_| String::from("failed to calculate"))
|
||||
}
|
||||
|
||||
pub fn calculate_and_format(&self, equation: String) -> Result<f64, String> {
|
||||
let res = self.calculate_inner(equation.clone()).map_err(|_| String::from("failed to calculate"))?;
|
||||
println!("
|
||||
Calculating...
|
||||
{}
|
||||
Result:
|
||||
{}", equation, 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> {
|
||||
equation.push('\n');
|
||||
let mut neweq = equation.clone();
|
||||
neweq.pop();
|
||||
|
||||
let tokens = tokenise(&equation)?;
|
||||
let mut parser = Parser::new(tokens)?;
|
||||
let ast = parser.parse()?;
|
||||
let mut interpreter = Interpreter::new()?;
|
||||
let result = interpreter.visit(ast)?;
|
||||
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 tokenise(equation: &str) -> Result<Vec<Token>, Error> {
|
||||
let mut tokens = Vec::new();
|
||||
let mut current_num = "".to_string();
|
||||
let mut current_string: String = "".to_string();
|
||||
|
||||
let mut is_var = false;
|
||||
|
||||
'mainloop: for (x, character) in equation.chars().enumerate() {
|
||||
|
||||
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' => {
|
||||
is_var = true;
|
||||
current_string.push(character);
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
if is_var {
|
||||
tokens.push(Token::Func(current_string.clone()));
|
||||
}
|
||||
is_var = false;
|
||||
current_string = "".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
match character {
|
||||
'0'..='9' => current_num.push(character),
|
||||
'.' => current_num.push(character),
|
||||
_ => {
|
||||
if current_num.len() != 0 {
|
||||
tokens.push(Token::Number(current_num.parse::<f64>().unwrap()));
|
||||
current_num = "".to_string();
|
||||
} else if current_string.len() != 0 {
|
||||
} match character {
|
||||
'+' => tokens.push(Token::Operator(Operator::Add)),
|
||||
'-' => tokens.push(Token::Operator(Operator::Sub)),
|
||||
'%' => tokens.push(Token::Operator(Operator::Mod)),
|
||||
'*' => {
|
||||
if &equation.chars().nth(x-1).unwrap() == &'*' {
|
||||
tokens.push(Token::Operator(Operator::Exp));
|
||||
} else if &equation.chars().nth(x+1).unwrap() == &'*' {
|
||||
()
|
||||
} else {
|
||||
tokens.push(Token::Operator(Operator::Mul));
|
||||
}
|
||||
},
|
||||
'/' => {
|
||||
if &equation.chars().nth(x-1).unwrap() == &'/' {
|
||||
tokens.push(Token::Operator(Operator::Qot));
|
||||
} else if &equation.chars().nth(x+1).unwrap() == &'/' {
|
||||
()
|
||||
} else {
|
||||
tokens.push(Token::Operator(Operator::Div));
|
||||
}
|
||||
},
|
||||
'(' | ')' | '[' | ']' | '{' | '}' | '<' | '>' => tokens.push(Token::Bracket(character)),
|
||||
'\n' => break 'mainloop,
|
||||
' ' => (),
|
||||
_ => {
|
||||
return Err(Error::InvalidCharacter(character))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Value {
|
||||
Number(f64),
|
||||
Operator(Operator)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Token {
|
||||
Number(f64),
|
||||
Operator(Operator),
|
||||
Bracket(char),
|
||||
Null,
|
||||
Func(String),
|
||||
Variable,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Debug, Clone, PartialEq)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Qot,
|
||||
Mod,
|
||||
Exp,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
InvalidSyntax(usize),
|
||||
InvalidCharacter(char),
|
||||
LogicalError(String),
|
||||
Eof,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Node {
|
||||
Number(f64),
|
||||
Variable,
|
||||
Operator(Operator),
|
||||
Function(Box<FunctionCall>),
|
||||
BinaryOperation(Box<BinaryOperation>),
|
||||
UnaryOperation(Box<UnaryOperation>)
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BinaryOperation {
|
||||
left: Node,
|
||||
operator: Node,
|
||||
right: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct UnaryOperation {
|
||||
operator: Node,
|
||||
other: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FunctionCall {
|
||||
name: String,
|
||||
arg: Node,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
impl fmt::Display for BinaryOperation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f, "(\n{} \n{} \n{}\n)
|
||||
|
||||
", self.left, self.operator, self.right
|
||||
)
|
||||
}
|
||||
}
|
||||
impl fmt::Display for UnaryOperation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f, "(\n{} \n{} \n)
|
||||
|
||||
", self.operator, self.other,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FunctionCall {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write! (
|
||||
f, "{}({})", self.name, self.arg
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Node {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Node::Number(x) => write!(f, "{}", x),
|
||||
Node::Variable => write!(f, "x"),
|
||||
Node::Operator(x) => write!(f, "{}", x),
|
||||
Node::BinaryOperation(x) => {
|
||||
let inner = *x.clone();
|
||||
write!(f, "{}", inner)
|
||||
}
|
||||
Node::UnaryOperation(x) => {
|
||||
let inner = *x.clone();
|
||||
write!(f, "{}", inner)
|
||||
}
|
||||
Node::Function(x) => write!(f, "{}", x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Operator {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Operator::Add => write!(f, "+"),
|
||||
Operator::Sub => write!(f, "-"),
|
||||
Operator::Mul => write!(f, "*"),
|
||||
Operator::Div => write!(f, "/"),
|
||||
Operator::Mod => write!(f, "%"),
|
||||
Operator::Qot => write!(f, "//"),
|
||||
Operator::Exp => write!(f, "**"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
|
||||
const PI: f64 = 3.14159265358979323846264338327950288419716939937510;
|
||||
|
||||
pub fn run_func(func: String, x: f64) -> Result<f64, String> {
|
||||
match func.as_str() {
|
||||
"sqrt" => sqrt(x),
|
||||
"ln" => ln(x),
|
||||
"fact" => factorial(x),
|
||||
"sin" => sin(x),
|
||||
"cos" => cos(x),
|
||||
"tan" => tan(x),
|
||||
_ => Err(String::from(format!("unrecognised function name: {}", func))),
|
||||
}
|
||||
}
|
||||
|
||||
fn sqrt(x: f64) -> Result<f64, String> {
|
||||
if x < 0.0 {
|
||||
return Err(String::from("Cannot take the square root of a negative number"));
|
||||
}
|
||||
Ok(libm::sqrt(x))
|
||||
}
|
||||
|
||||
fn ln(x: f64) -> Result<f64, String> {
|
||||
if x < 0.0 {
|
||||
return Err(String::from("Cannot take the natural log of a negative number"));
|
||||
}
|
||||
Ok(libm::log(x))
|
||||
}
|
||||
|
||||
fn factorial(x: f64) -> Result<f64, String> {
|
||||
if x < 0.0 {
|
||||
return Err(String::from("Cannot take the factorial of a negative number"));
|
||||
}
|
||||
let x = x as u64;
|
||||
Ok((1..=x).fold(1, |a, b| a * b) as f64)
|
||||
}
|
||||
|
||||
fn cos(mut x: f64) -> Result<f64, String> {
|
||||
while x > PI {
|
||||
x -= 2.0*PI;
|
||||
}
|
||||
while x < -PI {
|
||||
x += 2.0*PI;
|
||||
}
|
||||
|
||||
let res = 1.0 - trig_term(x, 2) + trig_term(x, 4) - trig_term(x, 6) + trig_term(x, 8) - trig_term(x, 10);
|
||||
if res >= -1.0 && res <= 1.0 {
|
||||
Ok(res)
|
||||
} else {
|
||||
Ok(res)
|
||||
// panic!("something is very wrong with the cos function : {}", res);
|
||||
}
|
||||
}
|
||||
|
||||
fn sin(mut x: f64) -> Result<f64, String> {
|
||||
while x > PI {
|
||||
x -= 2.0*PI;
|
||||
}
|
||||
while x < -PI {
|
||||
x += 2.0*PI;
|
||||
}
|
||||
|
||||
|
||||
let res = x - trig_term(x, 3) + trig_term(x, 5) - trig_term(x, 7) + trig_term(x, 9) - trig_term(x, 11);
|
||||
if res >= -1.0 && res <= 1.0 {
|
||||
Ok(res)
|
||||
} else {
|
||||
Ok(res)
|
||||
// panic!("something is very wrong with the sin function: {}", res);
|
||||
}
|
||||
}
|
||||
|
||||
fn tan(x: f64) -> Result<f64, String> {
|
||||
Ok(libm::tan(x))
|
||||
}
|
||||
|
||||
pub fn exp(x: f64, y: f64) -> f64 {
|
||||
let mut res = 1.0;
|
||||
for _ in 0..(y as usize) {
|
||||
res *= x
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn trig_term(x: f64, y: usize) -> f64 {
|
||||
let mut ex = 1.0;
|
||||
for _ in 0..y {
|
||||
ex *= x;
|
||||
}
|
||||
let fact = (1..=y).fold(1, |a, b| a*b);
|
||||
|
||||
ex as f64 / fact as f64
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
mod functions;
|
||||
mod calc;
|
||||
|
||||
pub use calc::Calculator;
|
||||
@@ -0,0 +1,330 @@
|
||||
use crate::{serial_println, std};
|
||||
use crate::std::application::{self, Application};
|
||||
use crate::std::io::{Color, ColorCode, Display, KeyStroke, Screen};
|
||||
use crate::std::render::{ColouredChar, Frame, Position, RenderError};
|
||||
use crate::user::lib::libgui::cg_core::CgComponent;
|
||||
|
||||
|
||||
|
||||
|
||||
use alloc::format;
|
||||
use alloc::{string::{String, ToString}, vec::Vec, boxed::Box};
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
|
||||
pub struct Editor {
|
||||
buffer: Vec<Vec<char>>, // outer vec is the line, inner is col;
|
||||
cursor_pos: Position<i32>,
|
||||
offset_pos: Position<i32>,
|
||||
command: String,
|
||||
mode: Mode,
|
||||
unsaved: bool,
|
||||
display: Display,
|
||||
lineno_width: i32,
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Normal,
|
||||
Insert,
|
||||
Command,
|
||||
Diff // TODO
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Mode {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Mode::Normal => write!(f, "Normal"),
|
||||
Mode::Insert => write!(f, "Insert"),
|
||||
Mode::Command => write!(f, "Commnd"),
|
||||
Mode::Diff => write!(f, "Diff "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Editor {
|
||||
fn move_cursor(&mut self, dx: i32, dy: i32) {
|
||||
if dy != 0
|
||||
&& self.cursor_pos.y + dy >= 0
|
||||
&& self.cursor_pos.y + dy <= self.buffer.len() as i32
|
||||
{
|
||||
self.cursor_pos.y += dy;
|
||||
let line_width = self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32;
|
||||
if self.cursor_pos.x > line_width {
|
||||
self.cursor_pos.x = line_width;
|
||||
}
|
||||
} else if self.cursor_pos.x + dx < 0 {
|
||||
if self.cursor_pos.y - 1 >= 0 {
|
||||
self.cursor_pos.y -= 1;
|
||||
self.cursor_pos.x = self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32;
|
||||
}
|
||||
} else if self.cursor_pos.x + dx > self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32 {
|
||||
if self.cursor_pos.y + 1 <= self.buffer.len() as i32 {
|
||||
self.cursor_pos.x = 0;
|
||||
self.cursor_pos.y += 1;
|
||||
}
|
||||
} else if dx != 0 {
|
||||
self.cursor_pos.x += dx;
|
||||
}
|
||||
|
||||
serial_println!("cursor: {} {} offset: {} {} ", self.cursor_pos.x, self.cursor_pos.y, self.offset_pos.x, self.offset_pos.y);
|
||||
|
||||
while self.cursor_pos.x + 3 + (self.lineno_width + 2) > 80 + self.offset_pos.x {
|
||||
self.offset_pos.x += 1;
|
||||
}
|
||||
|
||||
while self.cursor_pos.x - 3 < self.offset_pos.x && self.offset_pos.x - 3 >= 0 {
|
||||
self.offset_pos.x -= 1;
|
||||
}
|
||||
|
||||
while self.cursor_pos.y + 3 > self.offset_pos.y + 25 {
|
||||
self.offset_pos.y += 1;
|
||||
}
|
||||
|
||||
while self.cursor_pos.y - 3 < self.offset_pos.y && self.offset_pos.y - 3 >= 0 {
|
||||
self.offset_pos.y -= 1;
|
||||
}
|
||||
|
||||
serial_println!(
|
||||
"moving cursor to {}, {}",
|
||||
(self.cursor_pos.x - self.offset_pos.x + self.lineno_width + 2) as u8,
|
||||
(self.cursor_pos.y - self.offset_pos.y) as u8
|
||||
);
|
||||
|
||||
// print all the values below
|
||||
serial_println!("offset: {}, {}", self.offset_pos.x, self.offset_pos.y);
|
||||
serial_println!("cursor: {}, {}", self.cursor_pos.x, self.cursor_pos.y);
|
||||
serial_println!("line width: {}", self.lineno_width + 2);
|
||||
|
||||
self.display.mv_cursor(
|
||||
(self.cursor_pos.x - self.offset_pos.x + self.lineno_width + 2) as u8,
|
||||
(self.cursor_pos.y - self.offset_pos.y) as u8
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
fn delete_char(&mut self) {
|
||||
self.unsaved = true;
|
||||
// if the cursor is at the end of the line
|
||||
if self.cursor_pos.x == self.buffer.get(self.cursor_pos.y as usize).unwrap_or(&Vec::<char>::new()).len() as i32 {
|
||||
|
||||
if self.cursor_pos.y + 1 == self.buffer.len() as i32 {
|
||||
return;
|
||||
}
|
||||
|
||||
let old_line = self.buffer[self.cursor_pos.y as usize + 1].clone();
|
||||
self.buffer[self.cursor_pos.y as usize].extend(&old_line);
|
||||
self.buffer.remove(self.cursor_pos.y as usize + 1);
|
||||
} else {
|
||||
self.buffer[self.cursor_pos.y as usize].remove(self.cursor_pos.x as usize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn splitline(&mut self) {
|
||||
self.unsaved = true;
|
||||
|
||||
if let Some(_) = self.buffer.get(self.cursor_pos.y as usize) {
|
||||
let first_half = self.buffer[self.cursor_pos.y as usize][..self.cursor_pos.x as usize].to_vec();
|
||||
let second_half = self.buffer[self.cursor_pos.y as usize][self.cursor_pos.x as usize..].to_vec();
|
||||
|
||||
self.buffer[self.cursor_pos.y as usize] = first_half;
|
||||
self.buffer.insert(self.cursor_pos.y as usize + 1, second_half);
|
||||
} else {
|
||||
self.buffer.push(Vec::new());
|
||||
}
|
||||
|
||||
self.move_cursor(1, 0);
|
||||
}
|
||||
|
||||
fn insert_char(&mut self, c: char) {
|
||||
self.unsaved = true;
|
||||
|
||||
if let Some(line) = self.buffer.get_mut(self.cursor_pos.y as usize) {
|
||||
line.insert(self.cursor_pos.x as usize, c);
|
||||
} else {
|
||||
self.buffer.push(Vec::new());
|
||||
self.buffer.get_mut(self.cursor_pos.y as usize).unwrap().push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Editor {
|
||||
fn to_string(&self) -> String {
|
||||
self.buffer.iter().map(|line| line.iter().collect::<String>()).collect::<Vec<String>>().join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Application for Editor {
|
||||
fn new() -> Editor {
|
||||
Editor {
|
||||
buffer: Vec::new(),
|
||||
cursor_pos: Position::zero(),
|
||||
offset_pos: Position::zero(),
|
||||
command: String::new(),
|
||||
mode: Mode::Normal,
|
||||
unsaved: false,
|
||||
display: Display::borrow(),
|
||||
lineno_width: 0
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), application::Error> {
|
||||
|
||||
// if let Some(s) = args.get(0) {
|
||||
// self.buffer = s.lines().map(|l| l.chars().collect()).collect::<Vec<Vec<char>>>()
|
||||
// }
|
||||
|
||||
self.buffer = String::from("
|
||||
/$$ /$$$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$$ /$$ /$$ /$$
|
||||
/$$/|_____ $$ | $$ / $$ /$$__ $$| $$____/ /$$/| $$| $$
|
||||
/$$/ /$$/ | $$/ $$/| $$ \\ $$| $$ /$$/ \\ $$\\ $$
|
||||
/$$/ /$$/ \\ $$$$/ | $$ | $$| $$$$$$$ /$$/ \\ $$\\ $$
|
||||
| $$ /$$/ >$$ $$ | $$ | $$|_____ $$ /$$/ /$$/ /$$/
|
||||
\\ $$ /$$/ /$$/\\ $$| $$/$$ $$ /$$ \\ $$ /$$/ /$$/ /$$/
|
||||
\\ $$ /$$$$$$$$| $$ \\ $$| $$$$$$/| $$$$$$//$$/ /$$/ /$$/
|
||||
\\__/|________/|__/ |__/ \\____ $$$ \\______/|__/ |__/ |__/
|
||||
\\__/
|
||||
").lines().map(|l| l.chars().collect()).collect::<Vec<Vec<char>>>();
|
||||
|
||||
|
||||
|
||||
loop {
|
||||
// start by rendering the screen
|
||||
self.lineno_width = self.buffer.len().to_string().len() as i32;
|
||||
self.render().unwrap().write_to_screen().unwrap();
|
||||
|
||||
// wait for a keyboard input
|
||||
let keystroke = std::io::Stdin::keystroke().await;
|
||||
|
||||
match self.mode {
|
||||
Mode::Normal => {
|
||||
match keystroke {
|
||||
KeyStroke::Char('i') => self.mode = Mode::Insert,
|
||||
KeyStroke::Char(':') => self.mode = Mode::Command,
|
||||
KeyStroke::Char('d') => self.mode = Mode::Diff,
|
||||
KeyStroke::Char('`') => {
|
||||
// TODO: End terminal session
|
||||
// ncurses::endwin();
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
Mode::Insert => {
|
||||
match keystroke {
|
||||
KeyStroke::Enter => {
|
||||
// TODO: newline function
|
||||
},
|
||||
KeyStroke::Char(c) => {
|
||||
match c {
|
||||
// escape
|
||||
'\x1B' => self.mode = Mode::Normal,
|
||||
// delete
|
||||
'\x7F' => self.delete_char(),
|
||||
// backspace
|
||||
'\x08' => {
|
||||
self.move_cursor(-1, 0);
|
||||
self.delete_char();
|
||||
},
|
||||
// enter
|
||||
'\n' => self.splitline(),
|
||||
_ => {
|
||||
self.insert_char(c);
|
||||
self.move_cursor(1, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
KeyStroke::Left => {
|
||||
self.move_cursor(-1, 0);
|
||||
},
|
||||
KeyStroke::Right => {
|
||||
self.move_cursor(1, 0);
|
||||
},
|
||||
KeyStroke::Up => {
|
||||
self.move_cursor(0, -1);
|
||||
},
|
||||
KeyStroke::Down => {
|
||||
self.move_cursor(0, 1);
|
||||
},
|
||||
KeyStroke::None => {
|
||||
serial_println!("none");
|
||||
},
|
||||
_ => {
|
||||
serial_println!("other");
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::Command => {
|
||||
match keystroke {
|
||||
KeyStroke::Enter => {
|
||||
// TODO: execute command
|
||||
},
|
||||
KeyStroke::Char(c) => {
|
||||
if c == '\x1B' {
|
||||
self.mode = Mode::Normal;
|
||||
self.command.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
self.command.push(c);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Mode::Diff => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for Editor {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut frame = Frame::new(Position::zero(), Position::new(80, 25))?;
|
||||
let width = self.lineno_width as usize;
|
||||
let linecolour = ColorCode::new(Color::Cyan, Color::Black);
|
||||
|
||||
for (i, line) in (self.offset_pos.y..self.offset_pos.y + 24).enumerate() {
|
||||
if line >= self.buffer.len() as i32 {
|
||||
break;
|
||||
}
|
||||
|
||||
// render the line numbers on the left hand side of the screen
|
||||
let line_num = format!("{:width$} │", line + 1);
|
||||
for (j, c) in line_num.chars().enumerate() {
|
||||
frame.write(Position::new(j, i), ColouredChar::coloured(c, linecolour))?;
|
||||
}
|
||||
|
||||
let line = self.buffer[line as usize].iter().collect::<String>();
|
||||
|
||||
for (j, c) in line.chars().skip(self.offset_pos.x as usize).take(80 - (width + 2)).enumerate() {
|
||||
frame.write(Position::new(j + width + 2, i), ColouredChar::new(c))?;
|
||||
}
|
||||
}
|
||||
|
||||
// render the toolbar
|
||||
|
||||
// render mode (8 chars)
|
||||
let mode = format!("[{}]", self.mode);
|
||||
|
||||
// render unsaved (10 chars)
|
||||
let unsaved = String::from(if self.unsaved { "[Unsaved!]" } else { "" });
|
||||
|
||||
// line and col (variable width)
|
||||
let line_and_col = format!("[{}:{}] ", self.cursor_pos.y + 1, self.cursor_pos.x + 1);
|
||||
|
||||
// write to screen
|
||||
let toolbar = line_and_col + " " + &mode + " " + &unsaved;
|
||||
|
||||
for (i, c) in toolbar.chars().enumerate() {
|
||||
frame.write(Position::new(i, 24), ColouredChar::new(c))?;
|
||||
}
|
||||
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn core::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::{format, vec};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::boxed::Box;
|
||||
use core::any::Any;
|
||||
use async_trait::async_trait;
|
||||
use crate::std::application::{Application, Error};
|
||||
use crate::std::render::{Frame, Position, Dimensions, ColouredChar, RenderError};
|
||||
use crate::std::io::{Display, KeyStroke, Screen, Stdin};
|
||||
|
||||
use crate::user::lib::libgui::{
|
||||
cg_core::{CgComponent},
|
||||
cg_widgets::CgContainer,
|
||||
cg_inputs::CgLineEdit,
|
||||
};
|
||||
use crate::user::lib::libgui::cg_core::{CgTextEdit, Widget};
|
||||
|
||||
use super::calc;
|
||||
|
||||
const OFFSET_X: i64 = 39;
|
||||
const OFFSET_Y: i64 = 10;
|
||||
|
||||
use core::f64::consts::E;
|
||||
use core::f64::consts::PI;
|
||||
use crate::serial_println;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Grapher {
|
||||
points: Vec<PointF64>,
|
||||
frame: Frame,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct PointF64 {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
struct PointI64 {
|
||||
x: i64,
|
||||
y: i64,
|
||||
}
|
||||
|
||||
|
||||
#[async_trait]
|
||||
impl Application for Grapher {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
points: Vec::new(),
|
||||
frame: Frame::new(Position::new(1, 1), Dimensions::new(78, 22)).unwrap()
|
||||
}
|
||||
}
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||
let d = Display::borrow();
|
||||
|
||||
self.frame.frame = vec![vec![ColouredChar::new(' '); self.frame.dimensions.x]; self.frame.dimensions.y];
|
||||
|
||||
if args.len() > 0 {
|
||||
let equation: String = args.into_iter().collect();
|
||||
self.graph_equation(equation, (0, 0));
|
||||
|
||||
if let Ok(frame) = self.render() {
|
||||
frame.write_to_screen().map_err(|_| Error::ApplicationError(String::from("failed to write to screen")))?;
|
||||
}
|
||||
|
||||
loop {
|
||||
match Stdin::keystroke().await {
|
||||
KeyStroke::Char('x') => break,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
else {
|
||||
let mut container = CgContainer::new(
|
||||
Position::new(0, 0),
|
||||
Dimensions::new(80, 25),
|
||||
true,
|
||||
);
|
||||
|
||||
container.insert("entry_box", Widget::insert(CgLineEdit::new(
|
||||
Position::new(1, 23),
|
||||
78,
|
||||
String::from("function >")
|
||||
)));
|
||||
container.insert("grapher", Widget::insert(self.clone()));
|
||||
|
||||
let mut commandresult = String::new();
|
||||
|
||||
let mut offset_x: i64 = 0;
|
||||
let mut offset_y: i64 = 0;
|
||||
|
||||
let mut redraw_graph = true;
|
||||
|
||||
while let c = Stdin::keystroke().await {
|
||||
|
||||
let entry_widget = container.elements.get("entry_box").unwrap();
|
||||
let mut entry = entry_widget.fetch::<CgLineEdit>().unwrap();
|
||||
|
||||
redraw_graph = true;
|
||||
match c {
|
||||
KeyStroke::Char('\n') => {
|
||||
commandresult = entry.text.iter().collect();
|
||||
entry.clear();
|
||||
offset_x = 0;
|
||||
offset_y = 0;
|
||||
},
|
||||
KeyStroke::Char(Stdin::BACKSPACE) => {
|
||||
redraw_graph = false;
|
||||
entry.backspace()
|
||||
},
|
||||
KeyStroke::Char('`') => {
|
||||
break;
|
||||
}
|
||||
KeyStroke::Char(c) => {
|
||||
redraw_graph = false;
|
||||
entry.write_char(c)
|
||||
},
|
||||
KeyStroke::Left => offset_x -= 1,
|
||||
KeyStroke::Right => offset_x += 1,
|
||||
KeyStroke::Up => offset_y -= 1,
|
||||
KeyStroke::Down => offset_y += 1,
|
||||
KeyStroke::Alt => break,
|
||||
_ => {
|
||||
redraw_graph = false;
|
||||
}
|
||||
}
|
||||
|
||||
if commandresult.len() > 0 && redraw_graph {
|
||||
self.reset_frame();
|
||||
self.graph_equation(commandresult.clone(), (offset_x, offset_y));
|
||||
let self_widget = container.elements.get("grapher").unwrap();
|
||||
self_widget.update(self.clone());
|
||||
}
|
||||
|
||||
entry_widget.update(entry);
|
||||
|
||||
if let Ok(frame) = container.render() {
|
||||
|
||||
let self_widget = container.elements.get("grapher").unwrap();
|
||||
let self_clone = self_widget.fetch::<Grapher>().unwrap();
|
||||
|
||||
let entry = container.elements.get("entry_box").unwrap();
|
||||
let entry_clone = entry.fetch::<CgLineEdit>().unwrap();
|
||||
|
||||
frame.write_to_screen().map_err(|_| Error::ApplicationError(String::from("failed to write to screen")))?;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Grapher {
|
||||
|
||||
fn graph_equation(&mut self, equation: String, offsets: (i64, i64)) {
|
||||
|
||||
let cal = calc::Calculator::new();
|
||||
let ast = cal.get_expr(equation.chars().map(|c| {
|
||||
match c {
|
||||
'e' => format!("({})", E),
|
||||
'π' => format!("({})", PI),
|
||||
_ => c.to_string(),
|
||||
}
|
||||
}).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,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn render_point(&mut self, point: PointF64) {
|
||||
let point = PointI64 {
|
||||
x: point.x as i64,
|
||||
y: point.y as i64,
|
||||
};
|
||||
|
||||
if point.x < -39 || point.x >= 39 || point.y < -10 || point.y >= 12 {
|
||||
return;
|
||||
}
|
||||
|
||||
let offset_x = point.x + OFFSET_X;
|
||||
let offset_y = point.y + OFFSET_Y;
|
||||
self.frame.write(Position::new(offset_x as usize, 21-offset_y as usize), ColouredChar::new('*')).expect("Failed to write to frame - this function is broken.");
|
||||
}
|
||||
|
||||
fn reset_frame(&mut self) {
|
||||
self.frame.frame = vec![vec![ColouredChar::new(' '); self.frame.dimensions.x]; self.frame.dimensions.y];
|
||||
}
|
||||
}
|
||||
|
||||
impl CgComponent for Grapher {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
Ok(self.frame.clone())
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod calc;
|
||||
pub mod editor;
|
||||
pub mod grapher;
|
||||
pub mod tasks;
|
||||
@@ -0,0 +1,167 @@
|
||||
use alloc::{string::String, vec::Vec, boxed::Box};
|
||||
use crate::std::application::{
|
||||
Application,
|
||||
Error
|
||||
};
|
||||
use crate::println;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
use async_trait::async_trait;
|
||||
use alloc::{
|
||||
borrow::ToOwned,
|
||||
};
|
||||
|
||||
use crate::std::random;
|
||||
|
||||
|
||||
lazy_static! {
|
||||
static ref TASKS: Mutex<TaskList> = Mutex::new(TaskList::new());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct Tasks;
|
||||
|
||||
#[async_trait]
|
||||
impl Application for Tasks {
|
||||
fn new() -> Self { Self {} }
|
||||
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||
|
||||
if args[0].clone() == String::from("add") {
|
||||
|
||||
let content = args[1..].to_owned().into_iter().map(|mut s| {s.push_str(" "); s} ).collect::<String>();
|
||||
self.add_task(content);
|
||||
|
||||
}
|
||||
|
||||
if args[0].clone() == String::from("remove") {
|
||||
let idx = match args[1].to_owned().parse::<usize>() {
|
||||
Ok(x) => x,
|
||||
Err(_) => { return Err(Error::CommandFailed(String::from("number must be an integer"))) },
|
||||
};
|
||||
self.remove_task(idx);
|
||||
}
|
||||
|
||||
if args[0].clone() == String::from("select") {
|
||||
let arg2 = args[1].clone();
|
||||
if arg2 == String::from("random") {
|
||||
let len = TASKS.lock().tasks.len();
|
||||
self.select_task(random::Random::int(0, len -1) as i32);
|
||||
} else if arg2.parse::<u64>().is_ok() {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
if args[0].clone() == String::from("priority") {
|
||||
let idx = TASKS.lock().current;
|
||||
if idx < 0 {
|
||||
println!(
|
||||
"-------------------------------------
|
||||
no task currently set as priority
|
||||
-------------------------------------\n"
|
||||
);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let task = TASKS.lock().tasks[idx as usize].clone();
|
||||
let content = task.content.clone();
|
||||
|
||||
println!(
|
||||
"-------------------------------------
|
||||
PRIORITY TASK: {} : {}
|
||||
-------------------------------------\n",
|
||||
idx, content
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if args[0].as_str() == "list" {
|
||||
|
||||
println!(
|
||||
"-------------------------------------
|
||||
Your TODO List:
|
||||
-------------------------------------\n");
|
||||
|
||||
for task in TASKS.lock().tasks.iter() {
|
||||
|
||||
let idx = task.taskid;
|
||||
let content = task.content.clone();
|
||||
println!(" | Task -> {} \n | {}\n", idx, content);
|
||||
}
|
||||
println!("\n-------------------------------------");
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Tasks {
|
||||
fn add_task(&mut self, content: String) {
|
||||
TASKS.lock().add(content).unwrap();
|
||||
}
|
||||
fn remove_task(&self, idx: usize) {
|
||||
TASKS.lock().remove(idx).unwrap();
|
||||
}
|
||||
fn select_task(&self, idx: i32) {
|
||||
TASKS.lock().select(idx).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TaskList {
|
||||
current: i32,
|
||||
tasks: Vec<Task>,
|
||||
next_idx: usize,
|
||||
}
|
||||
|
||||
impl TaskList {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current: -1,
|
||||
tasks: Vec::new(),
|
||||
next_idx: 1
|
||||
}
|
||||
}
|
||||
pub fn next(&mut self) -> usize {
|
||||
self.next_idx += 1;
|
||||
self.next_idx -1
|
||||
}
|
||||
pub fn add(&mut self, content: String) -> Result<(), Error> {
|
||||
let task = Task::new(self.next(), content);
|
||||
let _id = task.taskid.clone();
|
||||
self.tasks.push(task);
|
||||
Ok(())
|
||||
}
|
||||
pub fn remove(&mut self, _id: usize) -> Result<(), Error> {
|
||||
for (i, task) in self.tasks.clone().iter().enumerate() {
|
||||
match task.taskid {
|
||||
_id => { self.tasks.remove(i); },
|
||||
_ => { return Err(Error::CommandFailed(String::from("this task does not exist"))); },
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn select(&mut self, idx: i32) -> Result<(), Error> {
|
||||
self.current = idx;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Task {
|
||||
taskid: usize,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
fn new(id: usize, content: String) -> Self {
|
||||
Self {
|
||||
taskid: id,
|
||||
content,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user