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