forked from bonsai/harakit
		
	rpn: initial working prototype
This commit is contained in:
		
							parent
							
								
									e246290bff
								
							
						
					
					
						commit
						ab4d732aaf
					
				
							
								
								
									
										211
									
								
								src/rpn.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/rpn.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,211 @@ | ||||
| /* | ||||
|  * Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media> | ||||
|  * SPDX-License-Identifier: AGPL-3.0-or-later | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify it under | ||||
|  * the terms of the GNU Affero General Public License as published by the Free | ||||
|  * Software Foundation, either version 3 of the License, or (at your option) any | ||||
|  * later version. | ||||
|  * 
 | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||||
|  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||||
|  * details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program. If not, see https://www.gnu.org/licenses/.
 | ||||
|  * | ||||
|  * This file incorporates work covered by the following copyright and permission | ||||
|  * notice: | ||||
|  * | ||||
|  *     MIT License | ||||
|  * 
 | ||||
|  *     Copyright (c) 2022 Lilly Cham | ||||
|  * 
 | ||||
|  *     Permission is hereby granted, free of charge, to any person obtaining a | ||||
|  *     copy of this software and associated documentation files 
 | ||||
|  *     (the "Software"), to deal in the Software without restriction, including | ||||
|  *     without limitation the rights | ||||
|  *     to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
|  *     sell copies of the Software, and to permit persons to whom the Software | ||||
|  *     is furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  *     The above copyright notice and this permission notice shall be included | ||||
|  *     in all copies or substantial portions of the Software. | ||||
|  * 
 | ||||
|  *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||||
|  *     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  *     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | ||||
|  *     NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||||
|  *     DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||||
|  *     OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||||
|  *     USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| use std::{ | ||||
| 	collections::VecDeque, | ||||
| 	env::args, | ||||
| 	fmt, | ||||
| 	io::stdin, | ||||
| 	process::ExitCode, | ||||
| }; | ||||
| 
 | ||||
| use CalcType::{ Add, Divide, Multiply, Power, Subtract, Val }; | ||||
| 
 | ||||
| #[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] | ||||
| /// enum CalcType is a type containing operations used in the calculator.
 | ||||
| enum CalcType { | ||||
| 	Add, | ||||
| 	Subtract, | ||||
| 	Multiply, | ||||
| 	Divide, | ||||
| 	Power, | ||||
| 	Val(f64), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| struct EvaluationError { | ||||
| 	pub message: String, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for EvaluationError { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		write!(f, "{}", self.message) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /// str_to_calc_type converts a string to an optional `CalcType`.
 | ||||
| fn str_to_calc_type(string: &str) -> Option<CalcType> { | ||||
| 	let as_int = string.parse::<f64>(); | ||||
| 	let result = match as_int { | ||||
| 		Ok(x) => Some(Val(x)), | ||||
| 		Err(_) => None, | ||||
| 	}; | ||||
| 
 | ||||
| 	if result.is_some() { | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	match string { | ||||
| 		"+" => Some(Add), | ||||
| 		"-" => Some(Subtract), | ||||
| 		"*" => Some(Multiply), | ||||
| 		"/" => Some(Divide), | ||||
| 		"^" => Some(Power), | ||||
| 		_ => None, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn eval( | ||||
| 	input: &str, | ||||
| 	initial_stack: VecDeque<f64>, | ||||
| ) -> Result<VecDeque<f64>, EvaluationError> { | ||||
| 	let mut stack = initial_stack; | ||||
| 	// Split the input into tokens.
 | ||||
| 	let toks = input.split(' ').collect::<Vec<&str>>(); | ||||
| 	let mut ops: VecDeque<CalcType> = VecDeque::new(); | ||||
| 
 | ||||
| 	for tok in &toks { | ||||
| 		let x: CalcType = match str_to_calc_type(tok) { | ||||
| 			Some(x) => x, | ||||
| 			None => { | ||||
| 				return Err(EvaluationError { | ||||
| 					message: format!("Invalid token: {}", tok), | ||||
| 				}) | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		match x { | ||||
| 			Add | Divide | Multiply | Power | Subtract => ops.push_back(x), | ||||
| 
 | ||||
| 			Val(x_) => stack.push_back(x_), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for op in &ops { | ||||
| 		match op { | ||||
| 			Add | Subtract | Multiply | Divide | Power => { | ||||
| 				let x = &stack.pop_back().ok_or(EvaluationError { | ||||
| 					message: "Stack is empty.".to_string(), | ||||
| 				})?; | ||||
| 
 | ||||
| 				let y = &stack.pop_back().ok_or(EvaluationError { | ||||
| 						message: "Stack is empty.".to_string(), | ||||
| 				})?; | ||||
| 
 | ||||
| 				match op { | ||||
| 					Add => { | ||||
| 						&stack.push_back(y + x) | ||||
| 					}, | ||||
| 					Subtract => { | ||||
| 						&stack.push_back(y - x) | ||||
| 					}, | ||||
| 					Multiply => { | ||||
| 						&stack.push_back(y * x) | ||||
| 					}, | ||||
| 					Divide => { | ||||
| 						&stack.push_back(y / x) | ||||
| 					}, | ||||
| 					Power => { | ||||
| 						let result = x.powf(*y); | ||||
| 						&stack.push_back(result) | ||||
| 					}, | ||||
| 					_ => &{}, | ||||
| 				}; | ||||
| 			}, | ||||
| 			Val(_) => { | ||||
| 				return Err( | ||||
| 					EvaluationError { | ||||
| 						message: "Unexpected value in the operator stack.".to_string() | ||||
| 					} | ||||
| 				) | ||||
| 			}, | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	Ok(stack) | ||||
| } | ||||
| 
 | ||||
| fn round_precise(value: &f64, precision: usize) -> f64 { | ||||
| 	let multiplier = 10_f64.powi(precision as i32); | ||||
| 	(value * multiplier).round() / multiplier | ||||
| } | ||||
| 
 | ||||
| fn main() -> ExitCode { | ||||
| 	let argv = args().collect::<Vec<String>>(); | ||||
| 	let stack = VecDeque::new(); | ||||
| 	let mut buf = String::new(); | ||||
| 	let precision = (-f64::EPSILON.log10() * 0.999).ceil() as usize; | ||||
| 	
 | ||||
| 	if argv.get(1).is_none() { | ||||
| 		while let Ok(_) = stdin().read_line(&mut buf) { | ||||
| 			match eval(&buf.trim(), stack.clone()) { | ||||
| 				Ok(val) => { | ||||
| 					let precise = round_precise(val.iter().last().unwrap(), precision); | ||||
| 					println!("{}", precise.to_string()); | ||||
| 					buf.clear(); | ||||
| 				}, | ||||
| 				Err(err) => { | ||||
| 					eprintln!("{}: {}", argv[0], err.message); | ||||
| 					return ExitCode::from(1); | ||||
| 				}, | ||||
| 			}; | ||||
| 		} | ||||
| 	} else { | ||||
| 		let input = argv | ||||
| 			.iter() | ||||
| 			.skip(1) | ||||
| 			.map(|x| x.to_owned()) | ||||
| 			.collect::<Vec<String>>() | ||||
| 			.join(" "); | ||||
| 
 | ||||
| 		match eval(&input, stack) { | ||||
| 			Ok(val) => println!("{}", val.iter().last().unwrap().to_string()), | ||||
| 			Err(err) => { | ||||
| 				eprintln!("{}: {}", argv[0], err.message); | ||||
| 				return ExitCode::from(1); | ||||
| 			}, | ||||
| 		}; | ||||
| 	} | ||||
| 	ExitCode::from(0) | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user