Added Rust program

This commit is contained in:
Win 2025-01-28 23:03:11 +07:00
parent 7f3d00c9a1
commit ed43edbf66
99 changed files with 117 additions and 47 deletions

View File

@ -23,8 +23,8 @@ fn evaluate(expr: String) -> Result<f64, ParseError> {
fn main() {
println!("Hello! Welcome to Arithmetic expression evaluator.");
println!("You can calculate value for expression such as 2*3+(4-5)+2^3/4. ");
println!("Allowed numbers: positive, negative and decimals.");
println!("Supported operations: Add, Subtract, Multiply, Divide, PowerOf(^). ");
println!("Allowed numbers: positive, negative, and decimals.");
println!("Supported operations: Add, Subtract, Multiply, Divide, PowerOf(^), Bitwise Operations: &, |. ");
println!("Enter your arithmetic expression below:");
loop {
let mut input = String::new();

View File

@ -18,6 +18,7 @@ pub enum Node {
Divide(Box<Node>, Box<Node>),
Caret(Box<Node>, Box<Node>),
Negative(Box<Node>),
Number(f64),
}
@ -26,10 +27,16 @@ pub fn eval(expr: Node) -> Result<f64, Box<dyn error::Error>> {
use self::Node::*;
match expr {
Number(i) => Ok(i),
Add(expr1, expr2) => Ok(eval(*expr1)? + eval(*expr2)?),
// TODO: complete the match expression to evaluate the numeric value
_ => panic!()
And(expr1, expr2) => Ok((eval(*expr1)? as i32 & eval(*expr2)? as i32).into()),
Or(expr1, expr2) => Ok((eval(*expr1)? as i32 | eval(*expr2)? as i32).into()),
Add(expr1, expr2) => Ok(eval(*expr1)? + eval(*expr2)?),
Subtract(expr1, expr2) => Ok(eval(*expr1)? - eval(*expr2)?),
Multiply(expr1, expr2) => Ok(eval(*expr1)? * eval(*expr2)?),
Divide(expr1, expr2) => Ok(eval(*expr1)? / eval(*expr2)?),
Negative(expr1) => Ok(-(eval(*expr1)?)),
Caret(expr1, expr2) => Ok(eval(*expr1)?.powf(eval(*expr2)?)),
}
}

View File

@ -36,8 +36,8 @@ impl<'a> Parser<'a> {
pub fn parse(&mut self) -> Result<Node, ParseError> {
let ast = self.generate_ast(OperPrec::DefaultZero);
match ast {
// TODO: Replace this with proper handling of return value from generate_ast result
_ => panic!()
Ok(ast) => Ok(ast),
Err(e) => Err(e),
}
}
}
@ -87,9 +87,15 @@ impl<'a> Parser<'a> {
Token::LeftParen => {
self.get_next_token()?;
// TODO: Replace the following code to check for matching parenthesis;
// also convert (x)(y) to x times y
let expr = self.generate_ast(OperPrec::DefaultZero)?;
let _ = self.check_paren(Token::RightParen);
if self.current_token == Token::LeftParen
{
let right = self.generate_ast(OperPrec::MulDiv)?;
return Ok(Node::Multiply(Box::new(expr), Box::new(right)));
}
Ok(expr)
}
_ => Err(ParseError::UnableToParse("Unable to parse".to_string())),
@ -110,15 +116,57 @@ impl<'a> Parser<'a> {
}
// Construct Operator AST nodes
fn convert_token_to_node(&mut self, left_expr: Node) -> Result<Node, ParseError> {
match self.current_token {
Token::Add => {
fn convert_token_to_node(&mut self, left_expr: Node) -> Result<Node, ParseError>
{
match self.current_token
{
Token::And =>
{
self.get_next_token()?;
let right_expr = self.generate_ast(OperPrec::AndOr)?;
Ok(Node::And(Box::new(left_expr), Box::new(right_expr)))
},
Token::Or =>
{
self.get_next_token()?;
let right_expr = self.generate_ast(OperPrec::AndOr)?;
Ok(Node::Or(Box::new(left_expr), Box::new(right_expr)))
},
Token::Add =>
{
self.get_next_token()?;
//Get right-side expression
let right_expr = self.generate_ast(OperPrec::AddSub)?;
Ok(Node::Add(Box::new(left_expr), Box::new(right_expr)))
}
// TODO: Complete the node construction for other tokens
Token::Subtract =>
{
self.get_next_token()?;
let right_expr = self.generate_ast(OperPrec::AddSub)?;
Ok(Node::Subtract(Box::new(left_expr), Box::new(right_expr)))
}
Token::Multiply =>
{
self.get_next_token()?;
let right_expr = self.generate_ast(OperPrec::MulDiv)?;
Ok(Node::Multiply(Box::new(left_expr), Box::new(right_expr)))
}
Token::Divide =>
{
self.get_next_token()?;
let right_expr = self.generate_ast(OperPrec::MulDiv)?;
Ok(Node::Divide(Box::new(left_expr), Box::new(right_expr)))
}
Token::Caret =>
{
self.get_next_token()?;
let right_expr = self.generate_ast(OperPrec::Power)?;
Ok(Node::Caret(Box::new(left_expr), Box::new(right_expr)))
}
_ => Err(ParseError::InvalidOperator(format!(
"Please enter valid operator {:?}",

View File

@ -41,7 +41,6 @@ impl Token {
Add | Subtract => AddSub,
Multiply | Divide => MulDiv,
Caret => Power,
_ => DefaultZero,
}
}

View File

@ -1,23 +1,23 @@
/// This module reads characters in arithmetic expression and converts them to tokens.
/// The allowed tokens are defined in ast module.
// Standard lib
use std::iter::Peekable;
use std::str::Chars;
//Other internal modules
use super::token::Token;
// Other structs
// Tokenizer struct contains a Peekable iterator on the arithmetic expression
pub struct Tokenizer<'a> {
pub struct Tokenizer<'a>
{
expr: Peekable<Chars<'a>>,
}
// Constructs a new instance of Tokenizer
impl<'a> Tokenizer<'a> {
pub fn new(new_expr: &'a str) -> Self {
Tokenizer {
impl<'a> Tokenizer<'a>
{
pub fn new(new_expr: &'a str) -> Self
{
Tokenizer
{
expr: new_expr.chars().peekable(),
}
}
@ -26,21 +26,45 @@ impl<'a> Tokenizer<'a> {
// Implement Iterator trait for Tokenizer struct.
// With this, we can use next() method on tokenizer to retrieve the next token from arithmetic expression
impl<'a> Iterator for Tokenizer<'a> {
impl<'a> Iterator for Tokenizer<'a>
{
type Item = Token;
fn next(&mut self) -> Option<Token> {
fn next(&mut self) -> Option<Token>
{
let next_char = self.expr.next();
match next_char {
match next_char
{
Some('0'..='9') => {
// TODO: Iterate & peeking through the next characters to create Num token
// Make sure to return None if the value is not parsable
None
let mut number = next_char?.to_string();
while let Some(next_char) = self.expr.peek()
{
if next_char.is_numeric() || next_char == &'.'
{
number.push(self.expr.next()?);
}
else if next_char == &'('
{
return None;
}
else
{
break;
}
}
Some(Token::Num(number.parse::<f64>().unwrap()))
},
// TODO: return the appropriate tokens for available symbols
Some('&') => Some(Token::And),
Some('|') => Some(Token::Or),
Some('+') => Some(Token::Add),
Some('-') => Some(Token::Subtract),
Some('*') => Some(Token::Multiply),
Some('/') => Some(Token::Divide),
Some('^') => Some(Token::Caret),
Some('(') => Some(Token::LeftParen),
Some(')') => Some(Token::RightParen),
None => Some(Token::EOF),
Some(_) => None,
}
@ -49,21 +73,25 @@ impl<'a> Iterator for Tokenizer<'a> {
// Unit tests
#[cfg(test)]
mod tests {
mod tests
{
use super::*;
#[test]
fn test_positive_integer() {
fn test_positive_integer()
{
let mut tokenizer = Tokenizer::new("34");
assert_eq!(tokenizer.next().unwrap(), Token::Num(34.0))
}
#[test]
fn test_decimal_number() {
fn test_decimal_number()
{
let mut tokenizer = Tokenizer::new("34.5");
assert_eq!(tokenizer.next().unwrap(), Token::Num(34.5))
}
#[test]
fn test_invalid_char() {
fn test_invalid_char()
{
let mut tokenizer = Tokenizer::new("#$%");
assert_eq!(tokenizer.next(), None);
}

View File

@ -1 +1 @@
{"rustc_fingerprint":15625702514836887422,"outputs":{"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/usr/local/rustup/toolchains/1.83.0-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.83.0 (90b35a623 2024-11-26)\nbinary: rustc\ncommit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf\ncommit-date: 2024-11-26\nhost: x86_64-unknown-linux-gnu\nrelease: 1.83.0\nLLVM version: 19.1.1\n","stderr":""}},"successes":{}}
{"rustc_fingerprint":14404728096486143510,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.83.0 (90b35a623 2024-11-26)\nbinary: rustc\ncommit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf\ncommit-date: 2024-11-26\nhost: x86_64-unknown-linux-gnu\nrelease: 1.83.0\nLLVM version: 19.1.1\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/usr/local/rustup/toolchains/1.83.0-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}