aboutsummaryrefslogtreecommitdiff
path: root/src/lex/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lex/mod.rs')
-rw-r--r--src/lex/mod.rs283
1 files changed, 0 insertions, 283 deletions
diff --git a/src/lex/mod.rs b/src/lex/mod.rs
deleted file mode 100644
index 3222c12..0000000
--- a/src/lex/mod.rs
+++ /dev/null
@@ -1,283 +0,0 @@
1/* Copyright (C) 2019 Akshay Oppiliappan <[email protected]>
2 * Refer to LICENCE for more information.
3 * */
4
5use lazy_static::lazy_static;
6use std::collections::HashMap;
7
8use crate::CONFIGURATION;
9
10use crate::error::{CalcError, Math};
11
12#[derive(Debug, Copy, Clone, PartialEq)]
13pub struct Operator {
14 token: char,
15 pub operation: fn(f64, f64) -> f64,
16 pub precedence: u8,
17 pub is_left_associative: bool,
18}
19
20impl Operator {
21 fn token_from_op(
22 token: char,
23 operation: fn(f64, f64) -> f64,
24 precedence: u8,
25 is_left_associative: bool,
26 ) -> Token {
27 Token::Operator(Operator {
28 token,
29 operation,
30 precedence,
31 is_left_associative,
32 })
33 }
34 pub fn operate(self, x: f64, y: f64) -> Result<f64, CalcError> {
35 if self.token == '/' && y == 0. {
36 Err(CalcError::Math(Math::DivideByZero))
37 } else {
38 Ok((self.operation)(x, y))
39 }
40 }
41}
42
43#[derive(Debug, Clone, PartialEq)]
44pub struct Function {
45 token: String,
46 relation: fn(f64) -> f64,
47}
48
49impl Function {
50 fn token_from_fn(token: String, relation: fn(f64) -> f64) -> Token {
51 Token::Function(Function { token, relation })
52 }
53 pub fn apply(self, arg: f64) -> Result<f64, CalcError> {
54 let result = (self.relation)(arg);
55 if !result.is_finite() {
56 Err(CalcError::Math(Math::OutOfBounds))
57 } else {
58 Ok(result)
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub enum Token {
65 Operator(Operator),
66 Num(f64),
67 Function(Function),
68 LParen,
69 RParen,
70}
71
72lazy_static! {
73 static ref CONSTANTS: HashMap<&'static str, Token> = {
74 let mut m = HashMap::new();
75 m.insert("e", Token::Num(std::f64::consts::E));
76 m.insert("pi", Token::Num(std::f64::consts::PI));
77 m
78 };
79
80 static ref FUNCTIONS: HashMap<&'static str, Token> = {
81 let mut m = HashMap::new();
82 m.insert("sin", Function::token_from_fn("sin".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).sin()));
83 m.insert("cos", Function::token_from_fn("cos".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).cos()));
84 m.insert("tan", Function::token_from_fn("tan".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).tan()));
85 m.insert("csc", Function::token_from_fn("csc".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).sin().recip()));
86 m.insert("sec", Function::token_from_fn("sec".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).cos().recip()));
87 m.insert("cot", Function::token_from_fn("cot".into(), |x| is_radian_mode(x, CONFIGURATION.radian_mode).tan().recip()));
88 m.insert("sinh", Function::token_from_fn("sinh".into(), |x| x.sinh()));
89 m.insert("cosh", Function::token_from_fn("cosh".into(), |x| x.cosh()));
90 m.insert("tanh", Function::token_from_fn("tanh".into(), |x| x.tanh()));
91 m.insert("ln", Function::token_from_fn("ln".into(), |x| x.ln()));
92 m.insert("log", Function::token_from_fn("log".into(), |x| x.log10()));
93 m.insert("sqrt", Function::token_from_fn("sqrt".into(), |x| x.sqrt()));
94 m.insert("ceil", Function::token_from_fn("ceil".into(), |x| x.ceil()));
95 m.insert("floor", Function::token_from_fn("floor".into(), |x| x.floor()));
96 m.insert("rad", Function::token_from_fn("rad".into(), |x| x.to_radians()));
97 m.insert("deg", Function::token_from_fn("deg".into(), |x| x.to_degrees()));
98 m.insert("abs", Function::token_from_fn("abs".into(), |x| x.abs()));
99 m.insert("asin", Function::token_from_fn("asin".into(), |x| x.asin()));
100 m.insert("acos", Function::token_from_fn("acos".into(), |x| x.acos()));
101 m.insert("atan", Function::token_from_fn("atan".into(), |x| x.atan()));
102 m.insert("acsc", Function::token_from_fn("acsc".into(), |x| (1./x).asin()));
103 m.insert("asec", Function::token_from_fn("asec".into(), |x| (1./x).acos()));
104 m.insert("acot", Function::token_from_fn("acot".into(), |x| (1./x).atan()));
105 // single arg function s can be added here
106 m
107 };
108
109 static ref OPERATORS: HashMap<char, Token> = {
110 let mut m = HashMap::new();
111 m.insert('+', Operator::token_from_op('+', |x, y| x + y, 2, true));
112 m.insert('-', Operator::token_from_op('-', |x, y| x - y, 2, true));
113 m.insert('*', Operator::token_from_op('*', |x, y| x * y, 3, true));
114 m.insert('/', Operator::token_from_op('/', |x, y| x / y, 3, true));
115 m.insert('%', Operator::token_from_op('%', |x, y| x % y, 3, true));
116 m.insert('^', Operator::token_from_op('^', |x, y| x.powf(y) , 4, false));
117 m.insert('!', Operator::token_from_op('!', |x, _| factorial(x) , 4, true));
118 m
119 };
120}
121
122fn factorial(n: f64) -> f64 {
123 n.signum() * (1..=n.abs() as u64).fold(1, |p, n| p * n) as f64
124}
125
126pub fn lexer(input: &str, prev_ans: Option<f64>) -> Result<Vec<Token>, CalcError> {
127 let mut num_vec: String = String::new();
128 let mut char_vec: String = String::new();
129 let mut result: Vec<Token> = vec![];
130 let mut last_char_is_op = true;
131
132 let mut chars = input.chars().peekable();
133 while let Some(mut letter) = chars.next() {
134 match letter {
135 '0'..='9' | '.' => {
136 if !char_vec.is_empty() {
137 if FUNCTIONS.get(&char_vec[..]).is_some() {
138 return Err(CalcError::Syntax(format!(
139 "Function '{}' expected parentheses",
140 char_vec
141 )));
142 } else {
143 return Err(CalcError::Syntax(format!(
144 "Unexpected character '{}'",
145 char_vec
146 )));
147 }
148 }
149 num_vec.push(letter);
150 last_char_is_op = false;
151 }
152 '_' => {
153 if prev_ans.is_none() {
154 return Err(CalcError::Syntax("No previous answer!".into()));
155 }
156 if !char_vec.is_empty() {
157 if FUNCTIONS.get(&char_vec[..]).is_some() {
158 return Err(CalcError::Syntax(format!(
159 "Function '{}' expected parentheses",
160 char_vec
161 )));
162 } else {
163 return Err(CalcError::Syntax(format!(
164 "Unexpected character '{}'",
165 char_vec
166 )));
167 }
168 }
169 let parse_num = num_vec.parse::<f64>().ok();
170 if let Some(x) = parse_num {
171 result.push(Token::Num(x));
172 result.push(OPERATORS.get(&'*').unwrap().clone());
173 num_vec.clear();
174 }
175 last_char_is_op = false;
176 result.push(Token::Num(prev_ans.unwrap()));
177 }
178 'a'..='z' | 'A'..='Z' => {
179 let parse_num = num_vec.parse::<f64>().ok();
180 if let Some(x) = parse_num {
181 result.push(Token::Num(x));
182 result.push(OPERATORS.get(&'*').unwrap().clone());
183 num_vec.clear();
184 }
185 char_vec.push(letter);
186 last_char_is_op = false;
187 }
188 '+' | '-' => {
189 let op_token = OPERATORS.get(&letter).unwrap().clone();
190 let parse_num = num_vec.parse::<f64>().ok();
191 if !last_char_is_op {
192 if let Some(x) = parse_num {
193 result.push(Token::Num(x));
194 num_vec.clear();
195 last_char_is_op = true;
196 } else if let Some(token) = CONSTANTS.get(&char_vec[..]) {
197 result.push(token.clone());
198 char_vec.clear();
199 last_char_is_op = true;
200 }
201 result.push(op_token);
202 } else if last_char_is_op {
203 result.push(Token::LParen);
204 result.push(Token::Num(
205 (letter.to_string() + "1").parse::<f64>().unwrap(),
206 ));
207 result.push(Token::RParen);
208 result.push(Operator::token_from_op('*', |x, y| x * y, 10, true));
209 }
210 }
211 '/' | '*' | '%' | '^' | '!' => {
212 drain_stack(&mut num_vec, &mut char_vec, &mut result);
213 if letter == '*' && chars.peek() == Some(&'*') {
214 // Accept `**` operator as meaning `^` (exponentation).
215 let _ = chars.next();
216 letter = '^';
217 }
218 let operator_token: Token = OPERATORS.get(&letter).unwrap().clone();
219 result.push(operator_token);
220 last_char_is_op = true;
221 if letter == '!' {
222 result.push(Token::Num(1.));
223 last_char_is_op = false;
224 }
225 }
226 '(' => {
227 if !char_vec.is_empty() {
228 if let Some(res) = FUNCTIONS.get(&char_vec[..]) {
229 result.push(res.clone());
230 } else {
231 return Err(CalcError::Syntax(format!(
232 "Unknown function '{}'",
233 char_vec
234 )));
235 }
236 char_vec.clear();
237 } else {
238 let parse_num = num_vec.parse::<f64>().ok();
239 if let Some(x) = parse_num {
240 result.push(Token::Num(x));
241 result.push(OPERATORS.get(&'*').unwrap().clone());
242 num_vec.clear();
243 }
244 }
245
246 if let Some(Token::RParen) = result.last() {
247 result.push(OPERATORS.get(&'*').unwrap().clone());
248 }
249 result.push(Token::LParen);
250 last_char_is_op = true;
251 }
252 ')' => {
253 drain_stack(&mut num_vec, &mut char_vec, &mut result);
254 result.push(Token::RParen);
255 last_char_is_op = false;
256 }
257 ' ' => {}
258 _ => return Err(CalcError::Syntax(format!("Unexpected token: '{}'", letter))),
259 }
260 }
261 // println!("{:?}", result);
262 drain_stack(&mut num_vec, &mut char_vec, &mut result);
263 Ok(result)
264}
265
266fn drain_stack(num_vec: &mut String, char_vec: &mut String, result: &mut Vec<Token>) {
267 let parse_num = num_vec.parse::<f64>().ok();
268 if let Some(x) = parse_num {
269 result.push(Token::Num(x));
270 num_vec.clear();
271 } else if let Some(token) = CONSTANTS.get(&char_vec[..]) {
272 result.push(token.clone());
273 char_vec.clear();
274 }
275}
276
277fn is_radian_mode(x: f64, is_radian: bool) -> f64 {
278 if is_radian {
279 x
280 } else {
281 x.to_radians()
282 }
283}