diff --git a/Cargo.toml b/Cargo.toml index 5cf84bd..0dbef67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mini-calc" -version = "2.1.1" +version = "2.2.2" license = "GPL-3.0-or-later" description = "A minimalistic configurable rust calculator" homepage = "https://calc.nwa2coco.fr" diff --git a/README.md b/README.md index 3cf3bcd..8efd955 100644 --- a/README.md +++ b/README.md @@ -78,14 +78,15 @@ The page walking down the configuration of the project is available [here](docs/ - [X] Config - [X] Config colours - [X] Config prompt -- [ ] Add more operations +- [X] Add more operations - [X] exponent -- [ ] Add support for functions - - [ ] exp - - [ ] ln - - [ ] log base a - - [ ] cos/sin/tan - - [ ] cosh/sinh/tanh +- [X] Add support for functions + - [X] exp + - [X] ln + - [X] log base a + - [X] cos/sin/tan + - [X] cosh/sinh/tanh + - [X] atan/acos/asin - [ ] For later - [ ] Defining your own functions - [ ] Add RPN mode diff --git a/docs/assets/expln.png b/docs/assets/expln.png new file mode 100644 index 0000000..eda28da Binary files /dev/null and b/docs/assets/expln.png differ diff --git a/docs/assets/trigo.png b/docs/assets/trigo.png new file mode 100644 index 0000000..570d86f Binary files /dev/null and b/docs/assets/trigo.png differ diff --git a/docs/function.md b/docs/function.md new file mode 100644 index 0000000..c1e6c23 --- /dev/null +++ b/docs/function.md @@ -0,0 +1,33 @@ +# Functions + +The following functions are available + +Trigonometry +- sin +- cos +- tan + +Hyperbolic trigonometry +- sinh +- cosh +- tanh + +Reverse trigonometry +- asin +- acos +- atan + +Exponentiation +- exp +- ln +- log (alias of ln) + +## Trigonometry + +For trigonometry, the input are assumed to be in radian, if not, you have to put "false" or "true" as second argument, example shown bellow +![img.png](assets/trigo.png) + +## Exp/ln + +If you use the exp function you can pass a second argument for the base you are using, if no second arguments are passed this is assumed to be in natural base +![img.png](assets/expln.png) diff --git a/docs/index.md b/docs/index.md index 32d5343..805256a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,10 +32,14 @@ cargo install mini-calc You can see how the calculator works over at [the usage page](usage.md) -# Config +## Config An overview of the configuration of mini-calc can be found [here](config.md) +## Function + +An overview of the function of mini-calc can be found [here](function.md) + ## TODO List - [X] Lexing of basic operations @@ -73,17 +77,17 @@ An overview of the configuration of mini-calc can be found [here](config.md) - [X] Built-in - [X] pi - [X] e -- [ ] Config - - [ ] Config colours - - [ ] Config prompt -- [ ] Add more operations +- [X] Config + - [X] Config colours + - [X] Config prompt +- [X] Add more operations - [X] exponent -- [ ] Add support for functions - - [ ] exp - - [ ] ln - - [ ] log base a - - [ ] cos/sin/tan - - [ ] cosh/sinh/tanh +- [X] Add support for functions + - [X] exp + - [X] ln + - [X] log base a + - [X] cos/sin/tan + - [X] cosh/sinh/tanh - [ ] For later - [ ] Defining your own functions - [ ] Add RPN mode @@ -101,6 +105,4 @@ An overview of the configuration of mini-calc can be found [here](config.md) ### REPL and functionning interpreter (verbose mode: off by default) -![](docs/assets/test_interpreter.png) - -## Configuration \ No newline at end of file +![](docs/assets/test_interpreter.png) \ No newline at end of file diff --git a/src/configuration/loader.rs b/src/configuration/loader.rs index e90b518..33d0427 100644 --- a/src/configuration/loader.rs +++ b/src/configuration/loader.rs @@ -1,5 +1,3 @@ -use std::str::FromStr; - use ansi_term::{ANSIGenericString, Color}; use serde::{Deserialize, Serialize}; @@ -74,7 +72,7 @@ pub fn load_rgb_color(str: &str) -> (u8, u8, u8) { let r = match rd { Ok(c) => c, - Err(e) => 0xFF, + Err(_) => 0xFF, }; let g = match gd { @@ -121,7 +119,7 @@ pub fn load_color(string: String) -> Color { pub fn replace_variable(str: String) -> String { str.replace("%author%", "Charlotte Thomas") - .replace("%version%", "v2.2.2") + .replace("%version%", "v2.3.0") .to_string() } diff --git a/src/interpreting/function.rs b/src/interpreting/function.rs index 228ba7d..04146fc 100644 --- a/src/interpreting/function.rs +++ b/src/interpreting/function.rs @@ -71,6 +71,12 @@ pub fn add(i: Parameters, i2: Parameters, ram: Option<&HashMap { apply_operator(Parameters::Identifier(s), Parameters::Int(i), ram, add) } + (Parameters::Null, Parameters::Identifier(s)) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, add) + } + (Parameters::Identifier(s), Parameters::Null) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, add) + } (Parameters::Int(i), Parameters::Identifier(s)) => { apply_operator(Parameters::Identifier(s), Parameters::Int(i), ram, add) } @@ -107,6 +113,12 @@ pub fn minus( (Parameters::Identifier(s), Parameters::Int(i)) => { apply_operator(Parameters::Identifier(s), Parameters::Int(i), ram, minus) } + (Parameters::Null, Parameters::Identifier(s)) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, minus) + } + (Parameters::Identifier(s), Parameters::Null) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, minus) + } (Parameters::Int(i), Parameters::Identifier(s)) => { let v = apply_operator(Parameters::Identifier(s), Parameters::Int(i), ram, minus); match v { @@ -154,6 +166,12 @@ pub fn mult( (Parameters::Int(i), Parameters::Identifier(s)) => { apply_operator(Parameters::Identifier(s), Parameters::Int(i), ram, mult) } + (Parameters::Null, Parameters::Identifier(s)) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, mult) + } + (Parameters::Identifier(s), Parameters::Null) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, mult) + } (Parameters::Identifier(s), Parameters::Float(i)) => { apply_operator(Parameters::Identifier(s), Parameters::Float(i), ram, mult) } @@ -194,6 +212,12 @@ pub fn divide( _ => Parameters::Null, } } + (Parameters::Null, Parameters::Identifier(s)) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, divide) + } + (Parameters::Identifier(s), Parameters::Null) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, divide) + } (Parameters::Identifier(s), Parameters::Float(i)) => { apply_operator(Parameters::Identifier(s), Parameters::Float(i), ram, divide) } @@ -237,6 +261,12 @@ pub fn expo( (Parameters::Identifier(s), Parameters::Float(i)) => { apply_operator(Parameters::Identifier(s), Parameters::Float(i), ram, expo) } + (Parameters::Identifier(s), Parameters::Null) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, expo) + } + (Parameters::Null, Parameters::Identifier(s)) => { + apply_operator(Parameters::Identifier(s), Parameters::Null, ram, expo) + } (Parameters::Float(i), Parameters::Identifier(s)) => { apply_operator_reverse(Parameters::Float(i), Parameters::Identifier(s), ram, expo) } diff --git a/src/interpreting/interpreter.rs b/src/interpreting/interpreter.rs index 96f9bc9..5830338 100644 --- a/src/interpreting/interpreter.rs +++ b/src/interpreting/interpreter.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::interpreting::function::{add, assign, divide, expo, minus, mult}; +use crate::interpreting::stdlib::exec; use crate::parsing::ast::{Ast, Parameters}; pub fn interpret(ast: Ast, mut ram: &mut HashMap) -> Parameters { @@ -29,10 +30,15 @@ pub fn interpret(ast: Ast, mut ram: &mut HashMap) -> Paramet Parameters::Float(f) => Parameters::Float(f), Parameters::Int(i) => Parameters::Int(i), Parameters::Identifier(s) => Parameters::Identifier(s), + Parameters::Call(s) => Parameters::Identifier(s), Parameters::Null => Parameters::Null, }; last.clone() } + Ast::Call { name: n, lst: list } => { + let v: Vec = list.iter().map(|x| interpret(x.clone(), ram)).collect(); + exec(n, v, Some(&ram)) + } } } diff --git a/src/interpreting/mod.rs b/src/interpreting/mod.rs index 8be6046..4645d72 100644 --- a/src/interpreting/mod.rs +++ b/src/interpreting/mod.rs @@ -1,2 +1,3 @@ mod function; pub(crate) mod interpreter; +mod stdlib; diff --git a/src/interpreting/stdlib.rs b/src/interpreting/stdlib.rs new file mode 100644 index 0000000..a6c7de6 --- /dev/null +++ b/src/interpreting/stdlib.rs @@ -0,0 +1,468 @@ +use std::collections::HashMap; +use std::f64::consts::PI; + +use crate::parsing::ast::Parameters; + +pub fn exec( + s: String, + lst: Vec, + ram: Option<&HashMap>, +) -> Parameters { + match s.as_str() { + "cos" => cos(&lst, ram), + "sin" => sin(&lst, ram), + "tan" => tan(&lst, ram), + "cosh" => cosh(&lst, ram), + "sinh" => sinh(&lst, ram), + "tanh" => tanh(&lst, ram), + "exp" => exp(&lst, ram), + "acos" => acos(&lst, ram), + "asin" => asin(&lst, ram), + "atan" => atan(&lst, ram), + "ln" => ln(&lst, ram), + "log" => ln(&lst, ram), + _ => cos(&lst, ram), + } +} + +pub fn cos(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = if degrees { + ((*i).clone() as f64) * (PI / 180.0) + } else { + (*i).clone() as f64 + }; + Parameters::Float(fs.cos()) + } + Parameters::Float(f) => { + let fs: f64 = if degrees { (*f) * (PI / 180.0) } else { *f }; + Parameters::Float(fs.cos()) + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => cos(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn sin(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = if degrees { + ((*i).clone() as f64) * (PI / 180.0) + } else { + (*i).clone() as f64 + }; + Parameters::Float(fs.sin()) + } + Parameters::Float(f) => { + let fs: f64 = if degrees { (*f) * (PI / 180.0) } else { *f }; + Parameters::Float(fs.sin()) + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => sin(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn tan(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = if degrees { + ((*i).clone() as f64) * (PI / 180.0) + } else { + (*i).clone() as f64 + }; + Parameters::Float(fs.tan()) + } + Parameters::Float(f) => { + let fs: f64 = if degrees { (*f) * (PI / 180.0) } else { *f }; + Parameters::Float(fs.tan()) + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => tan(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn cosh(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = if degrees { + ((*i).clone() as f64) * (PI / 180.0) + } else { + (*i).clone() as f64 + }; + Parameters::Float(fs.cosh()) + } + Parameters::Float(f) => { + let fs: f64 = if degrees { (*f) * (PI / 180.0) } else { *f }; + Parameters::Float(fs.cosh()) + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => cos(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn sinh(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = if degrees { + ((*i).clone() as f64) * (PI / 180.0) + } else { + (*i).clone() as f64 + }; + Parameters::Float(fs.sinh()) + } + Parameters::Float(f) => { + let fs: f64 = if degrees { (*f) * (PI / 180.0) } else { *f }; + Parameters::Float(fs.sinh()) + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => sinh(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn tanh(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = if degrees { + ((*i).clone() as f64) * (PI / 180.0) + } else { + (*i).clone() as f64 + }; + Parameters::Float(fs.tanh()) + } + Parameters::Float(f) => { + let fs: f64 = if degrees { (*f) * (PI / 180.0) } else { *f }; + Parameters::Float(fs.tanh()) + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => tanh(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn acos(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = (*i) as f64; + Parameters::Float(if degrees { + fs.acos() * (180.0 / PI) + } else { + fs.acos() + }) + } + Parameters::Float(f) => Parameters::Float(if degrees { + f.acos() * (180.0 / PI) + } else { + f.acos() + }), + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => acos(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn asin(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = (*i) as f64; + Parameters::Float(if degrees { + fs.asin() * (180.0 / PI) + } else { + fs.asin() + }) + } + Parameters::Float(f) => Parameters::Float(if degrees { + f.asin() * (180.0 / PI) + } else { + f.asin() + }), + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => asin(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn atan(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut degrees = false; + + if p.len() > 1 { + match p.get(1) { + None => degrees = false, + Some(_) => degrees = true, + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = (*i) as f64; + Parameters::Float(if degrees { + fs.atan() * (180.0 / PI) + } else { + fs.atan() + }) + } + Parameters::Float(f) => Parameters::Float(if degrees { + f.atan() * (180.0 / PI) + } else { + f.atan() + }), + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => atan(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} + +pub fn exp(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut plus = false; + let mut ln: f64 = 0.0; + + if p.len() > 1 { + match p.get(1) { + None => plus = false, + Some(t) => { + plus = true; + match t { + Parameters::Float(f) => ln = (*f), + Parameters::Int(i) => ln = ((*i) as f64), + _ => ln = 0.0, + } + } + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = (*i) as f64; + if plus { + Parameters::Float(ln.powf(fs)) + } else { + Parameters::Float(fs.exp()) + } + } + Parameters::Float(f) => { + if plus { + Parameters::Float(ln.powf(*f)) + } else { + Parameters::Float((*f).exp()) + } + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => exp(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} +pub fn ln(p: &Vec, ram: Option<&HashMap>) -> Parameters { + if p.len() < 1 { + return Parameters::Null; + } + + let mut plus = false; + let mut sln: f64 = 0.0; + + if p.len() > 1 { + match p.get(1) { + None => plus = false, + Some(t) => { + plus = true; + match t { + Parameters::Float(f) => sln = (*f), + Parameters::Int(i) => sln = ((*i) as f64), + _ => sln = 0.0, + } + } + } + } + + match p.get(0).unwrap() { + Parameters::Int(i) => { + let fs: f64 = (*i) as f64; + if plus { + Parameters::Float(fs.log(sln)) + } else { + Parameters::Float(fs.ln()) + } + } + Parameters::Float(f) => { + if plus { + Parameters::Float((*f).log(sln)) + } else { + Parameters::Float((*f).ln()) + } + } + Parameters::Identifier(s) => match ram { + None => Parameters::Identifier("This variable is not initialized yet".to_string()), + Some(t) => match t.get(s.as_str()) { + None => Parameters::Null, + Some(t) => ln(&vec![t.clone()], ram), + }, + }, + _ => Parameters::Null, + } +} diff --git a/src/lexing/lexer.rs b/src/lexing/lexer.rs index 38a4c3c..9f2e3e5 100644 --- a/src/lexing/lexer.rs +++ b/src/lexing/lexer.rs @@ -15,6 +15,7 @@ pub fn is_an_allowed_char(character: char) -> bool { || character == '.' || character == '=' || character == '^' + || character == ',' } fn lex_int( @@ -140,6 +141,10 @@ pub fn lex(input: String) -> Vec { vec.push(Token::OPE(EXPO)); current_pos += 1 } + ',' => { + vec.push(Token::COMMA); + current_pos += 1 + } ch => { if ch.is_numeric() { let (a, b) = lex_int(current_character, &mut chars, current_pos, length); diff --git a/src/lexing/token.rs b/src/lexing/token.rs index 68330f5..d848fe5 100644 --- a/src/lexing/token.rs +++ b/src/lexing/token.rs @@ -18,6 +18,7 @@ pub enum Token { EQUAL, RPAR, LPAR, + COMMA, Null, } @@ -34,6 +35,7 @@ pub enum TokenType { RPAR, LPAR, Null, + COMMA, EXPO, } @@ -47,7 +49,7 @@ pub enum Precedence { EXPONENT = 7, //PREFIX = 8, //POSTFIX = 9, - //CALL = 10, + CALL = 10, } impl Display for Operator { @@ -72,6 +74,7 @@ impl Display for Token { Token::INT(i) => write!(f, "{}", i), Token::IDENTIFIER(s) => write!(f, "{}", s), Token::OPE(s) => write!(f, "{}", s), + Token::COMMA => write!(f, ","), Token::Null => write!(f, "Null"), } } @@ -111,6 +114,7 @@ impl Token { Token::EQUAL => TokenType::EQUAL, Token::RPAR => TokenType::RPAR, Token::LPAR => TokenType::LPAR, + Token::COMMA => TokenType::COMMA, Token::Null => TokenType::Null, } } diff --git a/src/main.rs b/src/main.rs index 2a25380..8fb5d1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,7 +34,7 @@ fn main() { let style = loaded.prompt_style; let text = loaded.prompt; let mut verbose = false; - let version: String = "v2.2.2".to_string(); + let version: String = "v2.3.0".to_string(); interface .set_prompt(&format!( "\x01{prefix}\x02{text}\x01{suffix}\x02", @@ -54,9 +54,9 @@ fn main() { } "exit" => break, "help" => { - let message = loaded.general_color.paint((format!( + let message = loaded.general_color.paint(format!( " Calc {version} Help \n > info : show infos \n > exit : exit the program \n > help : print this help \n > verbose : toggle the verbose \n > version : prints the version \n" - ))); + )); println!("{}", message) } "version" => { diff --git a/src/parsing/ast.rs b/src/parsing/ast.rs index 1290041..fd58120 100644 --- a/src/parsing/ast.rs +++ b/src/parsing/ast.rs @@ -17,6 +17,7 @@ pub enum Parameters { Assign, Null, ExpoOperation, + Call(String), } #[derive(Debug, Clone, PartialEq)] @@ -27,6 +28,10 @@ pub enum Ast { left: Box, right: Box, }, + Call { + name: String, + lst: Vec, + }, } impl Display for Parameters { @@ -42,6 +47,7 @@ impl Display for Parameters { Assign => write!(f, "="), Null => write!(f, ""), ExpoOperation => write!(f, "^"), + Call(s) => write!(f, "{s}"), } } } @@ -49,7 +55,7 @@ impl Display for Parameters { impl Parameters { pub fn pretty_print(&self, ram: Option<&HashMap>) { match self { - Parameters::Identifier(s) => { + Identifier(s) => { if ram == None { println!("{self}") } else { @@ -99,6 +105,7 @@ impl Ast { left: Box::from(node), right: right.clone(), }, + _ => node, } } pub fn insert_right(self, node: Ast) -> Self { @@ -113,6 +120,7 @@ impl Ast { left: left.clone(), right: Box::from(node), }, + _ => node, } } } diff --git a/src/parsing/parselets/infix_parselet.rs b/src/parsing/parselets/infix_parselet.rs index f49c082..00e34f1 100644 --- a/src/parsing/parselets/infix_parselet.rs +++ b/src/parsing/parselets/infix_parselet.rs @@ -1,4 +1,5 @@ -use crate::lexing::token::{Precedence, Token}; +use crate::lexing::token::{Precedence, Token, TokenType}; +use crate::parsing::ast::Ast::Call; use crate::parsing::ast::{Ast, Parameters}; use crate::parsing::parser::CalcParser; @@ -30,6 +31,8 @@ pub struct ExpoParselet { pub is_right: bool, } +pub struct CallParselet {} + pub struct NullParset {} impl InfixParselet for PlusParselet { @@ -142,6 +145,42 @@ impl InfixParselet for AssignParselet { } } +impl InfixParselet for CallParselet { + fn parse(&self, parser: &mut CalcParser, left: &Ast, _token: Token) -> Ast { + let name = match left { + Ast::Nil => "", + Ast::Node { + value: v, + left: _left, + right: _right, + } => match v { + Parameters::Identifier(s) => s.as_str(), + _ => "", + }, + _ => "", + }; + + let mut lst: Vec = Vec::new(); + if !parser.match_token(TokenType::RPAR) { + lst.push(parser.parse_expression_empty()); + while parser.match_token(TokenType::COMMA) { + parser.consume(); + let ast = parser.parse_expression_empty(); + lst.push(ast); + } + parser.consume_expected(TokenType::RPAR); + } + Call { + name: name.to_string(), + lst, + } + } + + fn get_precedence(&self) -> i64 { + Precedence::CALL as i64 + } +} + impl InfixParselet for NullParset { fn parse(&self, _parser: &mut CalcParser, left: &Ast, _token: Token) -> Ast { left.clone() diff --git a/src/parsing/parser.rs b/src/parsing/parser.rs index bd1d602..4bbf5a1 100644 --- a/src/parsing/parser.rs +++ b/src/parsing/parser.rs @@ -4,8 +4,8 @@ use crate::lexing::token::Token::*; use crate::lexing::token::{Token, TokenType}; use crate::parsing::ast::Ast; use crate::parsing::parselets::infix_parselet::{ - AssignParselet, DivideParselet, ExpoParselet, InfixParselet, MinusParselet, MultParselet, - NullParset, PlusParselet, + AssignParselet, CallParselet, DivideParselet, ExpoParselet, InfixParselet, MinusParselet, + MultParselet, NullParset, PlusParselet, }; use crate::parsing::parselets::prefix_parselet::{ FloatParselet, GroupParselet, IdentifierParselet, IntParselet, NullParselet, @@ -70,6 +70,14 @@ impl CalcParser<'_> { self.read.remove(0) } + pub fn match_token(&mut self, expected: TokenType) -> bool { + let token = self.look_ahead(0); + if token.to_token_type() != expected { + return false; + } + return true; + } + pub fn consume_expected(&mut self, expected: TokenType) -> Token { self.look_ahead(0); if self.read.len() == 0 { @@ -105,6 +113,7 @@ impl CalcParser<'_> { TokenType::DIVIDE => Some(Box::from(DivideParselet { is_right: false })), TokenType::EQUAL => Some(Box::from(AssignParselet {})), TokenType::EXPO => Some(Box::from(ExpoParselet { is_right: false })), + TokenType::LPAR => Some(Box::from(CallParselet {})), _ => Some(Box::from(NullParset {})), } }