diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/error/mod.rs | 20 | ||||
-rw-r--r-- | src/format/mod.rs | 23 | ||||
-rw-r--r-- | src/lex/mod.rs | 127 | ||||
-rw-r--r-- | src/main.rs | 112 | ||||
-rw-r--r-- | src/parse/mod.rs | 44 | ||||
-rw-r--r-- | src/readline/mod.rs | 66 |
6 files changed, 187 insertions, 205 deletions
diff --git a/src/error/mod.rs b/src/error/mod.rs index 9ac6816..ec2b555 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs | |||
@@ -13,23 +13,17 @@ pub enum CalcError { | |||
13 | pub enum Math { | 13 | pub enum Math { |
14 | DivideByZero, | 14 | DivideByZero, |
15 | OutOfBounds, | 15 | OutOfBounds, |
16 | UnknownBase | 16 | UnknownBase, |
17 | } | 17 | } |
18 | 18 | ||
19 | pub fn handler(e: CalcError) -> String { | 19 | pub fn handler(e: CalcError) -> String { |
20 | match e { | 20 | match e { |
21 | CalcError::Math(math_err) => { | 21 | CalcError::Math(math_err) => match math_err { |
22 | match math_err { | 22 | Math::DivideByZero => "Math Error: Divide by zero error!".to_string(), |
23 | Math::DivideByZero => format!("Math Error: Divide by zero error!"), | 23 | Math::OutOfBounds => "Domain Error: Out of bounds!".to_string(), |
24 | Math::OutOfBounds => format!("Domain Error: Out of bounds!"), | 24 | Math::UnknownBase => "Base too large! Accepted ranges: 0 - 36".to_string(), |
25 | Math::UnknownBase => format!("Base too large! Accepted ranges: 0 - 36") | ||
26 | } | ||
27 | }, | 25 | }, |
28 | CalcError::Syntax(details) => { | 26 | CalcError::Syntax(details) => format!("Syntax Error: {}", details), |
29 | format!("Syntax Error: {}", details) | 27 | CalcError::Parser(details) => format!("Parser Error: {}", details), |
30 | }, | ||
31 | CalcError::Parser(details) => { | ||
32 | format!("Parser Error: {}", details) | ||
33 | } | ||
34 | } | 28 | } |
35 | } | 29 | } |
diff --git a/src/format/mod.rs b/src/format/mod.rs index 3e3ade2..45673d7 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs | |||
@@ -1,12 +1,8 @@ | |||
1 | extern crate num; | 1 | extern crate num; |
2 | use num::{BigInt, FromPrimitive, ToPrimitive}; | 2 | use num::{BigInt, FromPrimitive, ToPrimitive}; |
3 | 3 | ||
4 | use crate::error::{CalcError, Math}; | ||
4 | use crate::CONFIGURATION; | 5 | use crate::CONFIGURATION; |
5 | use crate::error::{ | ||
6 | CalcError, | ||
7 | Math | ||
8 | }; | ||
9 | |||
10 | 6 | ||
11 | pub fn autobalance_parens(input: &str) -> Result<String, CalcError> { | 7 | pub fn autobalance_parens(input: &str) -> Result<String, CalcError> { |
12 | let mut balanced = String::from(input); | 8 | let mut balanced = String::from(input); |
@@ -25,7 +21,7 @@ pub fn autobalance_parens(input: &str) -> Result<String, CalcError> { | |||
25 | balanced.push_str(&extras[..]); | 21 | balanced.push_str(&extras[..]); |
26 | Ok(balanced) | 22 | Ok(balanced) |
27 | } else if left_parens < right_parens { | 23 | } else if left_parens < right_parens { |
28 | return Err(CalcError::Syntax("Mismatched parentheses!".into())) | 24 | Err(CalcError::Syntax("Mismatched parentheses!".into())) |
29 | } else { | 25 | } else { |
30 | Ok(balanced) | 26 | Ok(balanced) |
31 | } | 27 | } |
@@ -39,10 +35,10 @@ fn radix_fmt(number: f64, obase: usize) -> Result<String, CalcError> { | |||
39 | match (number.is_infinite(), number.is_sign_positive()) { | 35 | match (number.is_infinite(), number.is_sign_positive()) { |
40 | (true, true) => return Ok("inf".to_string()), | 36 | (true, true) => return Ok("inf".to_string()), |
41 | (true, false) => return Ok("-inf".to_string()), | 37 | (true, false) => return Ok("-inf".to_string()), |
42 | _ => () | 38 | _ => (), |
43 | } | 39 | } |
44 | 40 | ||
45 | if number.is_nan(){ | 41 | if number.is_nan() { |
46 | return Ok("nan".to_string()); | 42 | return Ok("nan".to_string()); |
47 | } | 43 | } |
48 | 44 | ||
@@ -53,7 +49,7 @@ fn radix_fmt(number: f64, obase: usize) -> Result<String, CalcError> { | |||
53 | let mut obase_int = String::new(); | 49 | let mut obase_int = String::new(); |
54 | let obaseb = BigInt::from_usize(obase).unwrap(); | 50 | let obaseb = BigInt::from_usize(obase).unwrap(); |
55 | 51 | ||
56 | while &integral >= &obaseb { | 52 | while integral >= obaseb { |
57 | obase_int.push(table[(&integral % &obaseb).to_usize().unwrap()]); | 53 | obase_int.push(table[(&integral % &obaseb).to_usize().unwrap()]); |
58 | integral /= &obaseb; | 54 | integral /= &obaseb; |
59 | } | 55 | } |
@@ -81,7 +77,7 @@ fn radix_fmt(number: f64, obase: usize) -> Result<String, CalcError> { | |||
81 | 77 | ||
82 | fn thousand_sep(inp: &str) -> String { | 78 | fn thousand_sep(inp: &str) -> String { |
83 | let mut result_string = String::new(); | 79 | let mut result_string = String::new(); |
84 | for (i,c) in inp.to_string().chars().rev().enumerate() { | 80 | for (i, c) in inp.to_string().chars().rev().enumerate() { |
85 | if i % 3 == 0 && i != 0 && c.to_string() != "-" { | 81 | if i % 3 == 0 && i != 0 && c.to_string() != "-" { |
86 | result_string.push(','); | 82 | result_string.push(','); |
87 | } | 83 | } |
@@ -92,11 +88,10 @@ fn thousand_sep(inp: &str) -> String { | |||
92 | 88 | ||
93 | pub fn pprint(ans: f64) { | 89 | pub fn pprint(ans: f64) { |
94 | let ans_string = radix_fmt(ans, CONFIGURATION.base).unwrap(); | 90 | let ans_string = radix_fmt(ans, CONFIGURATION.base).unwrap(); |
95 | let ans_vector: Vec<&str> = ans_string.split(".").collect(); | 91 | let ans_vector: Vec<&str> = ans_string.split('.').collect(); |
96 | match ans_vector.len() { | 92 | match ans_vector.len() { |
97 | 1 => println!("{:>10}", thousand_sep(ans_vector[0])), | 93 | 1 => println!("{:>10}", thousand_sep(ans_vector[0])), |
98 | 2 => println!("{:>10}.{}", thousand_sep(ans_vector[0]),ans_vector[1]), | 94 | 2 => println!("{:>10}.{}", thousand_sep(ans_vector[0]), ans_vector[1]), |
99 | _ => unreachable!("N-nani?!") | 95 | _ => unreachable!("N-nani?!"), |
100 | } | 96 | } |
101 | } | 97 | } |
102 | |||
diff --git a/src/lex/mod.rs b/src/lex/mod.rs index c82c17e..48e07be 100644 --- a/src/lex/mod.rs +++ b/src/lex/mod.rs | |||
@@ -7,10 +7,7 @@ use std::collections::HashMap; | |||
7 | 7 | ||
8 | use crate::CONFIGURATION; | 8 | use crate::CONFIGURATION; |
9 | 9 | ||
10 | use crate::error::{ | 10 | use crate::error::{CalcError, Math}; |
11 | CalcError, | ||
12 | Math | ||
13 | }; | ||
14 | 11 | ||
15 | #[derive(Debug, Copy, Clone, PartialEq)] | 12 | #[derive(Debug, Copy, Clone, PartialEq)] |
16 | pub struct Operator { | 13 | pub struct Operator { |
@@ -21,22 +18,22 @@ pub struct Operator { | |||
21 | } | 18 | } |
22 | 19 | ||
23 | impl Operator { | 20 | impl Operator { |
24 | fn token_from_op(token: char, | 21 | fn token_from_op( |
25 | operation: fn(f64, f64) -> f64, | 22 | token: char, |
26 | precedence: u8, | 23 | operation: fn(f64, f64) -> f64, |
27 | is_left_associative: bool) -> Token { | 24 | precedence: u8, |
28 | Token::Operator( | 25 | is_left_associative: bool, |
29 | Operator { | 26 | ) -> Token { |
30 | token, | 27 | Token::Operator(Operator { |
31 | operation, | 28 | token, |
32 | precedence, | 29 | operation, |
33 | is_left_associative | 30 | precedence, |
34 | } | 31 | is_left_associative, |
35 | ) | 32 | }) |
36 | } | 33 | } |
37 | pub fn operate(self, x: f64, y: f64) -> Result<f64, CalcError> { | 34 | pub fn operate(self, x: f64, y: f64) -> Result<f64, CalcError> { |
38 | if self.token == '/' && y == 0. { | 35 | if self.token == '/' && y == 0. { |
39 | return Err(CalcError::Math(Math::DivideByZero)) | 36 | Err(CalcError::Math(Math::DivideByZero)) |
40 | } else { | 37 | } else { |
41 | Ok((self.operation)(x, y)) | 38 | Ok((self.operation)(x, y)) |
42 | } | 39 | } |
@@ -51,31 +48,25 @@ pub struct Function { | |||
51 | 48 | ||
52 | impl Function { | 49 | impl Function { |
53 | fn token_from_fn(token: String, relation: fn(f64) -> f64) -> Token { | 50 | fn token_from_fn(token: String, relation: fn(f64) -> f64) -> Token { |
54 | Token::Function( | 51 | Token::Function(Function { token, relation }) |
55 | Function { | ||
56 | token, | ||
57 | relation | ||
58 | } | ||
59 | ) | ||
60 | } | 52 | } |
61 | pub fn apply(self, arg: f64) -> Result<f64, CalcError> { | 53 | pub fn apply(self, arg: f64) -> Result<f64, CalcError> { |
62 | let result = (self.relation)(arg); | 54 | let result = (self.relation)(arg); |
63 | if !result.is_finite() { | 55 | if !result.is_finite() { |
64 | return Err(CalcError::Math(Math::OutOfBounds)); | 56 | Err(CalcError::Math(Math::OutOfBounds)) |
65 | } else { | 57 | } else { |
66 | Ok(result) | 58 | Ok(result) |
67 | } | 59 | } |
68 | } | 60 | } |
69 | } | 61 | } |
70 | 62 | ||
71 | |||
72 | #[derive(Debug, Clone, PartialEq)] | 63 | #[derive(Debug, Clone, PartialEq)] |
73 | pub enum Token { | 64 | pub enum Token { |
74 | Operator(Operator), | 65 | Operator(Operator), |
75 | Num(f64), | 66 | Num(f64), |
76 | Function(Function), | 67 | Function(Function), |
77 | LParen, | 68 | LParen, |
78 | RParen | 69 | RParen, |
79 | } | 70 | } |
80 | 71 | ||
81 | lazy_static! { | 72 | lazy_static! { |
@@ -128,8 +119,8 @@ lazy_static! { | |||
128 | }; | 119 | }; |
129 | } | 120 | } |
130 | 121 | ||
131 | fn factorial (n: f64) -> f64 { | 122 | fn factorial(n: f64) -> f64 { |
132 | n.signum() * (1.. n.abs() as u64 +1).fold(1, |p, n| p*n) as f64 | 123 | n.signum() * (1..=n.abs() as u64).fold(1, |p, n| p * n) as f64 |
133 | } | 124 | } |
134 | 125 | ||
135 | pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { | 126 | pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { |
@@ -141,22 +132,34 @@ pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { | |||
141 | for letter in input.chars() { | 132 | for letter in input.chars() { |
142 | match letter { | 133 | match letter { |
143 | '0'...'9' | '.' => { | 134 | '0'...'9' | '.' => { |
144 | if char_vec.len() > 0 { | 135 | if !char_vec.is_empty() { |
145 | if let Some(_) = FUNCTIONS.get(&char_vec[..]) { | 136 | if FUNCTIONS.get(&char_vec[..]).is_some() { |
146 | return Err(CalcError::Syntax(format!("Function '{}' expected parentheses", char_vec))) | 137 | return Err(CalcError::Syntax(format!( |
138 | "Function '{}' expected parentheses", | ||
139 | char_vec | ||
140 | ))); | ||
147 | } else { | 141 | } else { |
148 | return Err(CalcError::Syntax(format!("Unexpected character '{}'", char_vec))) | 142 | return Err(CalcError::Syntax(format!( |
143 | "Unexpected character '{}'", | ||
144 | char_vec | ||
145 | ))); | ||
149 | } | 146 | } |
150 | } | 147 | } |
151 | num_vec.push(letter); | 148 | num_vec.push(letter); |
152 | last_char_is_op = false; | 149 | last_char_is_op = false; |
153 | }, | 150 | } |
154 | '_' => { | 151 | '_' => { |
155 | if char_vec.len() > 0 { | 152 | if !char_vec.is_empty() { |
156 | if let Some(_) = FUNCTIONS.get(&char_vec[..]) { | 153 | if FUNCTIONS.get(&char_vec[..]).is_some() { |
157 | return Err(CalcError::Syntax(format!("Function '{}' expected parentheses", char_vec))) | 154 | return Err(CalcError::Syntax(format!( |
155 | "Function '{}' expected parentheses", | ||
156 | char_vec | ||
157 | ))); | ||
158 | } else { | 158 | } else { |
159 | return Err(CalcError::Syntax(format!("Unexpected character '{}'", char_vec))) | 159 | return Err(CalcError::Syntax(format!( |
160 | "Unexpected character '{}'", | ||
161 | char_vec | ||
162 | ))); | ||
160 | } | 163 | } |
161 | } | 164 | } |
162 | let parse_num = num_vec.parse::<f64>().ok(); | 165 | let parse_num = num_vec.parse::<f64>().ok(); |
@@ -165,7 +168,7 @@ pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { | |||
165 | result.push(OPERATORS.get(&'*').unwrap().clone()); | 168 | result.push(OPERATORS.get(&'*').unwrap().clone()); |
166 | num_vec.clear(); | 169 | num_vec.clear(); |
167 | } | 170 | } |
168 | last_char_is_op = false; | 171 | last_char_is_op = false; |
169 | result.push(Token::Num(prev_ans)); | 172 | result.push(Token::Num(prev_ans)); |
170 | } | 173 | } |
171 | 'a'...'z' | 'A'...'Z' => { | 174 | 'a'...'z' | 'A'...'Z' => { |
@@ -177,7 +180,7 @@ pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { | |||
177 | } | 180 | } |
178 | char_vec.push(letter); | 181 | char_vec.push(letter); |
179 | last_char_is_op = false; | 182 | last_char_is_op = false; |
180 | }, | 183 | } |
181 | '+' | '-' => { | 184 | '+' | '-' => { |
182 | let op_token = OPERATORS.get(&letter).unwrap().clone(); | 185 | let op_token = OPERATORS.get(&letter).unwrap().clone(); |
183 | let parse_num = num_vec.parse::<f64>().ok(); | 186 | let parse_num = num_vec.parse::<f64>().ok(); |
@@ -194,27 +197,32 @@ pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { | |||
194 | result.push(op_token); | 197 | result.push(op_token); |
195 | } else if last_char_is_op { | 198 | } else if last_char_is_op { |
196 | result.push(Token::LParen); | 199 | result.push(Token::LParen); |
197 | result.push(Token::Num((letter.to_string() + "1").parse::<f64>().unwrap())); | 200 | result.push(Token::Num( |
201 | (letter.to_string() + "1").parse::<f64>().unwrap(), | ||
202 | )); | ||
198 | result.push(Token::RParen); | 203 | result.push(Token::RParen); |
199 | result.push(Operator::token_from_op('*', |x, y| x * y, 10, true)); | 204 | result.push(Operator::token_from_op('*', |x, y| x * y, 10, true)); |
200 | } | 205 | } |
201 | }, | 206 | } |
202 | '/' | '*' | '%' | '^' | '!' => { | 207 | '/' | '*' | '%' | '^' | '!' => { |
203 | drain_stack(&mut num_vec, &mut char_vec, &mut result); | 208 | drain_stack(&mut num_vec, &mut char_vec, &mut result); |
204 | let operator_token: Token = OPERATORS.get(&letter).unwrap().clone(); | 209 | let operator_token: Token = OPERATORS.get(&letter).unwrap().clone(); |
205 | result.push(operator_token); | 210 | result.push(operator_token); |
206 | last_char_is_op = true; | 211 | last_char_is_op = true; |
207 | if letter == '!' { | 212 | if letter == '!' { |
208 | result.push(Token::Num(1.)); | 213 | result.push(Token::Num(1.)); |
209 | last_char_is_op = false; | 214 | last_char_is_op = false; |
210 | } | 215 | } |
211 | }, | 216 | } |
212 | '(' => { | 217 | '(' => { |
213 | if char_vec.len() > 0 { | 218 | if !char_vec.is_empty() { |
214 | if let Some(res) = FUNCTIONS.get(&char_vec[..]) { | 219 | if let Some(res) = FUNCTIONS.get(&char_vec[..]) { |
215 | result.push(res.clone()); | 220 | result.push(res.clone()); |
216 | } else { | 221 | } else { |
217 | return Err(CalcError::Syntax(format!("Unknown function '{}'", char_vec))) | 222 | return Err(CalcError::Syntax(format!( |
223 | "Unknown function '{}'", | ||
224 | char_vec | ||
225 | ))); | ||
218 | } | 226 | } |
219 | char_vec.clear(); | 227 | char_vec.clear(); |
220 | } else { | 228 | } else { |
@@ -226,26 +234,19 @@ pub fn lexer(input: &str, prev_ans: f64) -> Result<Vec<Token>, CalcError> { | |||
226 | } | 234 | } |
227 | } | 235 | } |
228 | 236 | ||
229 | if let Some(x) = result.last() { | 237 | if let Some(Token::RParen) = result.last() { |
230 | match x { | 238 | result.push(OPERATORS.get(&'*').unwrap().clone()); |
231 | Token::RParen => { | ||
232 | result.push(OPERATORS.get(&'*').unwrap().clone()); | ||
233 | }, | ||
234 | _ => {} | ||
235 | }; | ||
236 | } | 239 | } |
237 | result.push(Token::LParen); | 240 | result.push(Token::LParen); |
238 | last_char_is_op = true; | 241 | last_char_is_op = true; |
239 | }, | 242 | } |
240 | ')' => { | 243 | ')' => { |
241 | drain_stack(&mut num_vec, &mut char_vec, &mut result); | 244 | drain_stack(&mut num_vec, &mut char_vec, &mut result); |
242 | result.push(Token::RParen); | 245 | result.push(Token::RParen); |
243 | last_char_is_op = false; | 246 | last_char_is_op = false; |
244 | }, | ||
245 | ' ' => {}, | ||
246 | _ => { | ||
247 | return Err(CalcError::Syntax(format!("Unexpected token: '{}'", letter))) | ||
248 | } | 247 | } |
248 | ' ' => {} | ||
249 | _ => return Err(CalcError::Syntax(format!("Unexpected token: '{}'", letter))), | ||
249 | } | 250 | } |
250 | } | 251 | } |
251 | // println!("{:?}", result); | 252 | // println!("{:?}", result); |
@@ -266,7 +267,7 @@ fn drain_stack(num_vec: &mut String, char_vec: &mut String, result: &mut Vec<Tok | |||
266 | 267 | ||
267 | fn is_radian_mode(x: f64, is_radian: bool) -> f64 { | 268 | fn is_radian_mode(x: f64, is_radian: bool) -> f64 { |
268 | if is_radian { | 269 | if is_radian { |
269 | return x | 270 | x |
270 | } else { | 271 | } else { |
271 | x.to_radians() | 272 | x.to_radians() |
272 | } | 273 | } |
diff --git a/src/main.rs b/src/main.rs index c3f3342..140150c 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -6,26 +6,26 @@ | |||
6 | /* imports */ | 6 | /* imports */ |
7 | // std | 7 | // std |
8 | use std::f64; | 8 | use std::f64; |
9 | use std::path::PathBuf; | ||
10 | use std::fs::create_dir_all; | 9 | use std::fs::create_dir_all; |
10 | use std::path::PathBuf; | ||
11 | 11 | ||
12 | // modules | 12 | // modules |
13 | mod lex; | ||
14 | mod parse; | ||
15 | mod error; | 13 | mod error; |
16 | mod format; | 14 | mod format; |
15 | mod lex; | ||
16 | mod parse; | ||
17 | mod readline; | 17 | mod readline; |
18 | use crate::error::{handler, CalcError}; | ||
19 | use crate::format::*; | ||
18 | use crate::lex::*; | 20 | use crate::lex::*; |
19 | use crate::parse::*; | 21 | use crate::parse::*; |
20 | use crate::error::{ CalcError, handler }; | ||
21 | use crate::format::*; | ||
22 | use crate::readline::*; | 22 | use crate::readline::*; |
23 | 23 | ||
24 | // extern crates | 24 | // extern crates |
25 | use rustyline::error::ReadlineError; | 25 | use clap::{App, Arg}; |
26 | use clap::{Arg, App}; | 26 | use directories::{ProjectDirs, UserDirs}; |
27 | use lazy_static::lazy_static; | 27 | use lazy_static::lazy_static; |
28 | use directories::{ ProjectDirs, UserDirs }; | 28 | use rustyline::error::ReadlineError; |
29 | 29 | ||
30 | /* end of imports */ | 30 | /* end of imports */ |
31 | 31 | ||
@@ -33,7 +33,7 @@ struct Configuration { | |||
33 | radian_mode: bool, | 33 | radian_mode: bool, |
34 | fix: usize, | 34 | fix: usize, |
35 | base: usize, | 35 | base: usize, |
36 | input: String | 36 | input: String, |
37 | } | 37 | } |
38 | 38 | ||
39 | lazy_static! { | 39 | lazy_static! { |
@@ -41,7 +41,7 @@ lazy_static! { | |||
41 | } | 41 | } |
42 | 42 | ||
43 | fn main() { | 43 | fn main() { |
44 | if CONFIGURATION.input.len() > 0 { | 44 | if !CONFIGURATION.input.is_empty() { |
45 | // command mode // | 45 | // command mode // |
46 | let evaled = eval_math_expression(&CONFIGURATION.input[..], 0f64); | 46 | let evaled = eval_math_expression(&CONFIGURATION.input[..], 0f64); |
47 | match evaled { | 47 | match evaled { |
@@ -49,11 +49,11 @@ fn main() { | |||
49 | Err(e) => { | 49 | Err(e) => { |
50 | eprintln!("{}", handler(e)); | 50 | eprintln!("{}", handler(e)); |
51 | std::process::exit(1); | 51 | std::process::exit(1); |
52 | }, | 52 | } |
53 | }; | 53 | }; |
54 | } else { | 54 | } else { |
55 | // REPL mode // | 55 | // REPL mode // |
56 | // create fancy readline | 56 | // create fancy readline |
57 | let mut rl = create_readline(); | 57 | let mut rl = create_readline(); |
58 | 58 | ||
59 | // previous answer | 59 | // previous answer |
@@ -65,7 +65,7 @@ fn main() { | |||
65 | let mut history_path = PathBuf::from(eva_data_dir); | 65 | let mut history_path = PathBuf::from(eva_data_dir); |
66 | match create_dir_all(eva_data_dir) { | 66 | match create_dir_all(eva_data_dir) { |
67 | Ok(_) => history_path.push("history.txt"), | 67 | Ok(_) => history_path.push("history.txt"), |
68 | Err(_) => history_path = PathBuf::from(UserDirs::new().unwrap().home_dir()), | 68 | Err(_) => history_path = PathBuf::from(UserDirs::new().unwrap().home_dir()), |
69 | }; | 69 | }; |
70 | 70 | ||
71 | if rl.load_history(history_path.as_path()).is_err() { | 71 | if rl.load_history(history_path.as_path()).is_err() { |
@@ -86,17 +86,15 @@ fn main() { | |||
86 | } | 86 | } |
87 | Err(e) => println!("{}", handler(e)), | 87 | Err(e) => println!("{}", handler(e)), |
88 | }; | 88 | }; |
89 | }, | 89 | } |
90 | Err(ReadlineError::Interrupted) => { | 90 | Err(ReadlineError::Interrupted) => { |
91 | println!("CTRL-C"); | 91 | println!("CTRL-C"); |
92 | break | 92 | break; |
93 | }, | 93 | } |
94 | Err(ReadlineError::Eof) => { | 94 | Err(ReadlineError::Eof) => break, |
95 | break | ||
96 | }, | ||
97 | Err(err) => { | 95 | Err(err) => { |
98 | println!("Error: {:?}", err); | 96 | println!("Error: {:?}", err); |
99 | break | 97 | break; |
100 | } | 98 | } |
101 | } | 99 | } |
102 | } | 100 | } |
@@ -109,25 +107,33 @@ fn parse_arguments() -> Configuration { | |||
109 | .version(env!("CARGO_PKG_VERSION")) | 107 | .version(env!("CARGO_PKG_VERSION")) |
110 | .author(env!("CARGO_PKG_AUTHORS")) | 108 | .author(env!("CARGO_PKG_AUTHORS")) |
111 | .about(env!("CARGO_PKG_DESCRIPTION")) | 109 | .about(env!("CARGO_PKG_DESCRIPTION")) |
112 | .arg(Arg::with_name("fix") | 110 | .arg( |
113 | .short("f") | 111 | Arg::with_name("fix") |
114 | .long("fix") | 112 | .short("f") |
115 | .takes_value(true) | 113 | .long("fix") |
116 | .value_name("FIX") | 114 | .takes_value(true) |
117 | .help("set number of decimal places in the output")) | 115 | .value_name("FIX") |
118 | .arg(Arg::with_name("base") | 116 | .help("set number of decimal places in the output"), |
119 | .short("b") | 117 | ) |
120 | .long("base") | 118 | .arg( |
121 | .takes_value(true) | 119 | Arg::with_name("base") |
122 | .value_name("RADIX") | 120 | .short("b") |
123 | .help("set the radix of calculation output (1 - 36)")) | 121 | .long("base") |
124 | .arg(Arg::with_name("INPUT") | 122 | .takes_value(true) |
125 | .help("optional expression string to run eva in command mode") | 123 | .value_name("RADIX") |
126 | .index(1)) | 124 | .help("set the radix of calculation output (1 - 36)"), |
127 | .arg(Arg::with_name("radian") | 125 | ) |
128 | .short("r") | 126 | .arg( |
129 | .long("radian") | 127 | Arg::with_name("INPUT") |
130 | .help("set eva to radian mode")) | 128 | .help("optional expression string to run eva in command mode") |
129 | .index(1), | ||
130 | ) | ||
131 | .arg( | ||
132 | Arg::with_name("radian") | ||
133 | .short("r") | ||
134 | .long("radian") | ||
135 | .help("set eva to radian mode"), | ||
136 | ) | ||
131 | .get_matches(); | 137 | .get_matches(); |
132 | 138 | ||
133 | let mut input = String::new(); | 139 | let mut input = String::new(); |
@@ -136,29 +142,25 @@ fn parse_arguments() -> Configuration { | |||
136 | }; | 142 | }; |
137 | Configuration { | 143 | Configuration { |
138 | radian_mode: config.is_present("radian"), | 144 | radian_mode: config.is_present("radian"), |
139 | fix: config.value_of("fix") | 145 | fix: config.value_of("fix").unwrap_or("10").parse().unwrap(), |
140 | .unwrap_or("10") | 146 | base: config.value_of("base").unwrap_or("10").parse().unwrap(), |
141 | .parse() | 147 | input, |
142 | .unwrap(), | ||
143 | base: config.value_of("base") | ||
144 | .unwrap_or("10") | ||
145 | .parse() | ||
146 | .unwrap(), | ||
147 | input, | ||
148 | } | 148 | } |
149 | } | 149 | } |
150 | 150 | ||
151 | pub fn eval_math_expression(input: &str, prev_ans: f64) -> Result<f64, CalcError> { | 151 | pub fn eval_math_expression(input: &str, prev_ans: f64) -> Result<f64, CalcError> { |
152 | let input = input.trim(); | 152 | let input = input.trim(); |
153 | let input = input.replace(" ", ""); | 153 | let input = input.replace(" ", ""); |
154 | if input.len() == 0 { | 154 | if input.is_empty() { |
155 | return Ok(0.) | 155 | return Ok(0.); |
156 | } | 156 | } |
157 | let input = format::autobalance_parens(&input[..])?; | 157 | let input = format::autobalance_parens(&input[..])?; |
158 | let lexed = lexer(&input[..], prev_ans)?; | 158 | let lexed = lexer(&input[..], prev_ans)?; |
159 | let postfixed = to_postfix(lexed)?; | 159 | let postfixed = to_postfix(lexed)?; |
160 | let evaled = eval_postfix(postfixed)?; | 160 | let evaled = eval_postfix(postfixed)?; |
161 | let evaled_fixed = format!("{:.*}", CONFIGURATION.fix, evaled).parse::<f64>().unwrap(); | 161 | let evaled_fixed = format!("{:.*}", CONFIGURATION.fix, evaled) |
162 | .parse::<f64>() | ||
163 | .unwrap(); | ||
162 | Ok(evaled_fixed) | 164 | Ok(evaled_fixed) |
163 | } | 165 | } |
164 | 166 | ||
diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 5c85172..aa8bd4b 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs | |||
@@ -2,8 +2,8 @@ | |||
2 | * Refer to LICENCE for more information. | 2 | * Refer to LICENCE for more information. |
3 | * */ | 3 | * */ |
4 | 4 | ||
5 | use crate::lex::Token; | ||
6 | use crate::error::CalcError; | 5 | use crate::error::CalcError; |
6 | use crate::lex::Token; | ||
7 | 7 | ||
8 | pub fn to_postfix(tokens: Vec<Token>) -> Result<Vec<Token>, CalcError> { | 8 | pub fn to_postfix(tokens: Vec<Token>) -> Result<Vec<Token>, CalcError> { |
9 | let mut postfixed: Vec<Token> = vec![]; | 9 | let mut postfixed: Vec<Token> = vec![]; |
@@ -12,7 +12,7 @@ pub fn to_postfix(tokens: Vec<Token>) -> Result<Vec<Token>, CalcError> { | |||
12 | match token { | 12 | match token { |
13 | Token::Num(_) => { | 13 | Token::Num(_) => { |
14 | postfixed.push(token); | 14 | postfixed.push(token); |
15 | }, | 15 | } |
16 | Token::Function(_) => { | 16 | Token::Function(_) => { |
17 | op_stack.push(token); | 17 | op_stack.push(token); |
18 | } | 18 | } |
@@ -34,14 +34,16 @@ pub fn to_postfix(tokens: Vec<Token>) -> Result<Vec<Token>, CalcError> { | |||
34 | Token::Function(_) => { | 34 | Token::Function(_) => { |
35 | postfixed.push(op_stack.pop().unwrap()); | 35 | postfixed.push(op_stack.pop().unwrap()); |
36 | } | 36 | } |
37 | _ => { unreachable!(); } | 37 | _ => { |
38 | unreachable!(); | ||
39 | } | ||
38 | } | 40 | } |
39 | } | 41 | } |
40 | op_stack.push(token); | 42 | op_stack.push(token); |
41 | }, | 43 | } |
42 | Token::LParen => { | 44 | Token::LParen => { |
43 | op_stack.push(token); | 45 | op_stack.push(token); |
44 | }, | 46 | } |
45 | Token::RParen => { | 47 | Token::RParen => { |
46 | let mut push_until_paren: bool = false; | 48 | let mut push_until_paren: bool = false; |
47 | while let Some(token) = op_stack.pop() { | 49 | while let Some(token) = op_stack.pop() { |
@@ -69,43 +71,35 @@ pub fn eval_postfix(postfixed: Vec<Token>) -> Result<f64, CalcError> { | |||
69 | match token { | 71 | match token { |
70 | Token::Num(n) => { | 72 | Token::Num(n) => { |
71 | num_stack.push(n); | 73 | num_stack.push(n); |
72 | }, | 74 | } |
73 | Token::Operator(op) => { | 75 | Token::Operator(op) => { |
74 | if let Some(n2) = num_stack.pop() { | 76 | if let Some(n2) = num_stack.pop() { |
75 | if let Some(n1) = num_stack.pop() { | 77 | if let Some(n1) = num_stack.pop() { |
76 | num_stack.push(op.operate(n1, n2)?); | 78 | num_stack.push(op.operate(n1, n2)?); |
77 | } else { | 79 | } else { |
78 | return Err( | 80 | return Err(CalcError::Parser( |
79 | CalcError::Parser( | 81 | "Too many operators, Too little operands".to_string(), |
80 | format!("Too many operators, Too little operands") | 82 | )); |
81 | ) | ||
82 | ) | ||
83 | } | 83 | } |
84 | } else { | 84 | } else { |
85 | return Err( | 85 | return Err(CalcError::Parser( |
86 | CalcError::Parser( | 86 | "Too many operators, Too little operands".to_string(), |
87 | format!("Too many operators, Too little operands") | 87 | )); |
88 | ) | ||
89 | ) | ||
90 | } | 88 | } |
91 | }, | 89 | } |
92 | Token::Function(funct) => { | 90 | Token::Function(funct) => { |
93 | if let Some(arg) = num_stack.pop() { | 91 | if let Some(arg) = num_stack.pop() { |
94 | num_stack.push(funct.apply(arg)?) | 92 | num_stack.push(funct.apply(arg)?) |
95 | } | 93 | } |
96 | } | 94 | } |
97 | _ => { | 95 | _ => unreachable!("wut"), |
98 | unreachable!("wut") | ||
99 | } | ||
100 | } | 96 | } |
101 | } | 97 | } |
102 | if num_stack.len() == 1 { | 98 | if num_stack.len() == 1 { |
103 | Ok(num_stack.pop().unwrap()) | 99 | Ok(num_stack.pop().unwrap()) |
104 | } else { | 100 | } else { |
105 | return Err( | 101 | Err(CalcError::Parser( |
106 | CalcError::Parser( | 102 | "Too many operators, Too little operands".to_string(), |
107 | format!("Too many operators, Too little operands") | 103 | )) |
108 | ) | ||
109 | ) | ||
110 | } | 104 | } |
111 | } | 105 | } |
diff --git a/src/readline/mod.rs b/src/readline/mod.rs index e9d4b33..41cd742 100644 --- a/src/readline/mod.rs +++ b/src/readline/mod.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use std::borrow::Cow::{self,Owned}; | 1 | use std::borrow::Cow::{self, Owned}; |
2 | 2 | ||
3 | use rustyline::completion::{Completer, FilenameCompleter, Pair}; | ||
4 | use rustyline::config::{Builder, ColorMode, CompletionType, EditMode}; | ||
3 | use rustyline::error::ReadlineError; | 5 | use rustyline::error::ReadlineError; |
4 | use rustyline::{ Editor, Context, Helper }; | ||
5 | use rustyline::config::{ Builder, ColorMode, EditMode, CompletionType }; | ||
6 | use rustyline::hint::{ Hinter, HistoryHinter }; | ||
7 | use rustyline::completion::{ FilenameCompleter, Completer, Pair }; | ||
8 | use rustyline::highlight::Highlighter; | 6 | use rustyline::highlight::Highlighter; |
7 | use rustyline::hint::{Hinter, HistoryHinter}; | ||
8 | use rustyline::{Context, Editor, Helper}; | ||
9 | 9 | ||
10 | use regex::Regex; | 10 | use regex::Regex; |
11 | 11 | ||
@@ -17,7 +17,7 @@ pub struct RLHelper { | |||
17 | hinter: HistoryHinter, | 17 | hinter: HistoryHinter, |
18 | } | 18 | } |
19 | 19 | ||
20 | struct LineHighlighter { } | 20 | struct LineHighlighter {} |
21 | impl Highlighter for LineHighlighter { | 21 | impl Highlighter for LineHighlighter { |
22 | fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { | 22 | fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { |
23 | Owned(format!("\x1b[90m{}\x1b[0m", hint)) | 23 | Owned(format!("\x1b[90m{}\x1b[0m", hint)) |
@@ -28,14 +28,9 @@ impl Highlighter for LineHighlighter { | |||
28 | Ok(_) => { | 28 | Ok(_) => { |
29 | let constants = ["e", "pi"]; | 29 | let constants = ["e", "pi"]; |
30 | let functions = [ | 30 | let functions = [ |
31 | "sin" , "cos" , "tan" , | 31 | "sin", "cos", "tan", "csc", "sec", "cot", "sinh", "cosh", "tanh", "ln", "log", |
32 | "csc" , "sec" , "cot" , | 32 | "sqrt", "ceil", "floor", "rad", "deg", "abs", "asin", "acos", "atan", "acsc", |
33 | "sinh" , "cosh" , "tanh" , | 33 | "asec", "acot", |
34 | "ln" , "log" , "sqrt" , | ||
35 | "ceil" , "floor" , "rad" , | ||
36 | "deg" , "abs" , "asin" , | ||
37 | "acos" , "atan" , "acsc" , | ||
38 | "asec" , "acot" | ||
39 | ]; | 34 | ]; |
40 | let ops = Regex::new(r"(?P<o>[\+-/\*%\^!])").unwrap(); | 35 | let ops = Regex::new(r"(?P<o>[\+-/\*%\^!])").unwrap(); |
41 | let mut coloured: String = ops.replace_all(line, "\x1b[33m$o\x1b[0m").into(); | 36 | let mut coloured: String = ops.replace_all(line, "\x1b[33m$o\x1b[0m").into(); |
@@ -46,14 +41,14 @@ impl Highlighter for LineHighlighter { | |||
46 | for f in &functions { | 41 | for f in &functions { |
47 | coloured = coloured.replace(f, &format!("\x1b[34m{}\x1b[0m", f)); | 42 | coloured = coloured.replace(f, &format!("\x1b[34m{}\x1b[0m", f)); |
48 | } | 43 | } |
49 | Owned(coloured.into()) | 44 | Owned(coloured) |
50 | }, | 45 | } |
51 | Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line)) | 46 | Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line)), |
52 | } | 47 | } |
53 | } | 48 | } |
54 | } | 49 | } |
55 | 50 | ||
56 | impl Highlighter for RLHelper { | 51 | impl Highlighter for RLHelper { |
57 | fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { | 52 | fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { |
58 | self.highlighter.highlight_hint(hint) | 53 | self.highlighter.highlight_hint(hint) |
59 | } | 54 | } |
@@ -69,11 +64,11 @@ impl Completer for RLHelper { | |||
69 | line: &str, | 64 | line: &str, |
70 | pos: usize, | 65 | pos: usize, |
71 | ctx: &Context<'_>, | 66 | ctx: &Context<'_>, |
72 | ) -> Result<(usize, Vec<Pair>), ReadlineError> { | 67 | ) -> Result<(usize, Vec<Pair>), ReadlineError> { |
73 | self.completer.complete(line, pos, ctx) | 68 | self.completer.complete(line, pos, ctx) |
74 | } | 69 | } |
75 | } | 70 | } |
76 | 71 | ||
77 | impl Hinter for RLHelper { | 72 | impl Hinter for RLHelper { |
78 | fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> { | 73 | fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> { |
79 | self.hinter.hint(line, a, b) | 74 | self.hinter.hint(line, a, b) |
@@ -83,19 +78,20 @@ impl Hinter for RLHelper { | |||
83 | impl Helper for RLHelper {} | 78 | impl Helper for RLHelper {} |
84 | 79 | ||
85 | pub fn create_readline() -> Editor<RLHelper> { | 80 | pub fn create_readline() -> Editor<RLHelper> { |
86 | let config_builder = Builder::new(); | 81 | let config_builder = Builder::new(); |
87 | let config = config_builder.color_mode(ColorMode::Enabled) | 82 | let config = config_builder |
88 | .edit_mode(EditMode::Emacs) | 83 | .color_mode(ColorMode::Enabled) |
89 | .history_ignore_space(true) | 84 | .edit_mode(EditMode::Emacs) |
90 | .completion_type(CompletionType::Circular) | 85 | .history_ignore_space(true) |
91 | .max_history_size(1000) | 86 | .completion_type(CompletionType::Circular) |
92 | .build(); | 87 | .max_history_size(1000) |
93 | let mut rl = Editor::with_config(config); | 88 | .build(); |
94 | let h = RLHelper { | 89 | let mut rl = Editor::with_config(config); |
95 | completer: FilenameCompleter::new(), | 90 | let h = RLHelper { |
96 | highlighter: LineHighlighter {}, | 91 | completer: FilenameCompleter::new(), |
97 | hinter: HistoryHinter {} | 92 | highlighter: LineHighlighter {}, |
98 | }; | 93 | hinter: HistoryHinter {}, |
99 | rl.set_helper(Some(h)); | 94 | }; |
100 | return rl; | 95 | rl.set_helper(Some(h)); |
96 | rl | ||
101 | } | 97 | } |