Added Rust program
This commit is contained in:
parent
7f3d00c9a1
commit
ed43edbf66
|
@ -23,8 +23,8 @@ fn evaluate(expr: String) -> Result<f64, ParseError> {
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello! Welcome to Arithmetic expression evaluator.");
|
println!("Hello! Welcome to Arithmetic expression evaluator.");
|
||||||
println!("You can calculate value for expression such as 2*3+(4-5)+2^3/4. ");
|
println!("You can calculate value for expression such as 2*3+(4-5)+2^3/4. ");
|
||||||
println!("Allowed numbers: positive, negative and decimals.");
|
println!("Allowed numbers: positive, negative, and decimals.");
|
||||||
println!("Supported operations: Add, Subtract, Multiply, Divide, PowerOf(^). ");
|
println!("Supported operations: Add, Subtract, Multiply, Divide, PowerOf(^), Bitwise Operations: &, |. ");
|
||||||
println!("Enter your arithmetic expression below:");
|
println!("Enter your arithmetic expression below:");
|
||||||
loop {
|
loop {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub enum Node {
|
||||||
Divide(Box<Node>, Box<Node>),
|
Divide(Box<Node>, Box<Node>),
|
||||||
Caret(Box<Node>, Box<Node>),
|
Caret(Box<Node>, Box<Node>),
|
||||||
Negative(Box<Node>),
|
Negative(Box<Node>),
|
||||||
|
|
||||||
Number(f64),
|
Number(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,10 +27,16 @@ pub fn eval(expr: Node) -> Result<f64, Box<dyn error::Error>> {
|
||||||
use self::Node::*;
|
use self::Node::*;
|
||||||
match expr {
|
match expr {
|
||||||
Number(i) => Ok(i),
|
Number(i) => Ok(i),
|
||||||
Add(expr1, expr2) => Ok(eval(*expr1)? + eval(*expr2)?),
|
|
||||||
|
|
||||||
// TODO: complete the match expression to evaluate the numeric value
|
And(expr1, expr2) => Ok((eval(*expr1)? as i32 & eval(*expr2)? as i32).into()),
|
||||||
_ => panic!()
|
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)?)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,8 @@ impl<'a> Parser<'a> {
|
||||||
pub fn parse(&mut self) -> Result<Node, ParseError> {
|
pub fn parse(&mut self) -> Result<Node, ParseError> {
|
||||||
let ast = self.generate_ast(OperPrec::DefaultZero);
|
let ast = self.generate_ast(OperPrec::DefaultZero);
|
||||||
match ast {
|
match ast {
|
||||||
// TODO: Replace this with proper handling of return value from generate_ast result
|
Ok(ast) => Ok(ast),
|
||||||
_ => panic!()
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,9 +87,15 @@ impl<'a> Parser<'a> {
|
||||||
Token::LeftParen => {
|
Token::LeftParen => {
|
||||||
self.get_next_token()?;
|
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 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)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
_ => Err(ParseError::UnableToParse("Unable to parse".to_string())),
|
_ => Err(ParseError::UnableToParse("Unable to parse".to_string())),
|
||||||
|
@ -110,15 +116,57 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct Operator AST nodes
|
// Construct Operator AST nodes
|
||||||
fn convert_token_to_node(&mut self, left_expr: Node) -> Result<Node, ParseError> {
|
fn convert_token_to_node(&mut self, left_expr: Node) -> Result<Node, ParseError>
|
||||||
match self.current_token {
|
{
|
||||||
Token::Add => {
|
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()?;
|
self.get_next_token()?;
|
||||||
//Get right-side expression
|
//Get right-side expression
|
||||||
let right_expr = self.generate_ast(OperPrec::AddSub)?;
|
let right_expr = self.generate_ast(OperPrec::AddSub)?;
|
||||||
Ok(Node::Add(Box::new(left_expr), Box::new(right_expr)))
|
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!(
|
_ => Err(ParseError::InvalidOperator(format!(
|
||||||
"Please enter valid operator {:?}",
|
"Please enter valid operator {:?}",
|
||||||
|
|
|
@ -41,7 +41,6 @@ impl Token {
|
||||||
Add | Subtract => AddSub,
|
Add | Subtract => AddSub,
|
||||||
Multiply | Divide => MulDiv,
|
Multiply | Divide => MulDiv,
|
||||||
Caret => Power,
|
Caret => Power,
|
||||||
|
|
||||||
_ => DefaultZero,
|
_ => DefaultZero,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::iter::Peekable;
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
|
|
||||||
//Other internal modules
|
|
||||||
use super::token::Token;
|
use super::token::Token;
|
||||||
|
|
||||||
// Other structs
|
// Other structs
|
||||||
|
|
||||||
// Tokenizer struct contains a Peekable iterator on the arithmetic expression
|
// Tokenizer struct contains a Peekable iterator on the arithmetic expression
|
||||||
pub struct Tokenizer<'a> {
|
pub struct Tokenizer<'a>
|
||||||
|
{
|
||||||
expr: Peekable<Chars<'a>>,
|
expr: Peekable<Chars<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructs a new instance of Tokenizer
|
// Constructs a new instance of Tokenizer
|
||||||
impl<'a> Tokenizer<'a> {
|
impl<'a> Tokenizer<'a>
|
||||||
pub fn new(new_expr: &'a str) -> Self {
|
{
|
||||||
Tokenizer {
|
pub fn new(new_expr: &'a str) -> Self
|
||||||
|
{
|
||||||
|
Tokenizer
|
||||||
|
{
|
||||||
expr: new_expr.chars().peekable(),
|
expr: new_expr.chars().peekable(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,21 +26,45 @@ impl<'a> Tokenizer<'a> {
|
||||||
// Implement Iterator trait for Tokenizer struct.
|
// Implement Iterator trait for Tokenizer struct.
|
||||||
// With this, we can use next() method on tokenizer to retrieve the next token from arithmetic expression
|
// 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;
|
type Item = Token;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Token> {
|
fn next(&mut self) -> Option<Token>
|
||||||
|
{
|
||||||
let next_char = self.expr.next();
|
let next_char = self.expr.next();
|
||||||
|
|
||||||
match next_char {
|
match next_char
|
||||||
|
{
|
||||||
Some('0'..='9') => {
|
Some('0'..='9') => {
|
||||||
// TODO: Iterate & peeking through the next characters to create Num token
|
let mut number = next_char?.to_string();
|
||||||
// Make sure to return None if the value is not parsable
|
while let Some(next_char) = self.expr.peek()
|
||||||
None
|
{
|
||||||
|
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),
|
None => Some(Token::EOF),
|
||||||
Some(_) => None,
|
Some(_) => None,
|
||||||
}
|
}
|
||||||
|
@ -49,21 +73,25 @@ impl<'a> Iterator for Tokenizer<'a> {
|
||||||
|
|
||||||
// Unit tests
|
// Unit tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests
|
||||||
|
{
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_positive_integer() {
|
fn test_positive_integer()
|
||||||
|
{
|
||||||
let mut tokenizer = Tokenizer::new("34");
|
let mut tokenizer = Tokenizer::new("34");
|
||||||
assert_eq!(tokenizer.next().unwrap(), Token::Num(34.0))
|
assert_eq!(tokenizer.next().unwrap(), Token::Num(34.0))
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decimal_number() {
|
fn test_decimal_number()
|
||||||
|
{
|
||||||
let mut tokenizer = Tokenizer::new("34.5");
|
let mut tokenizer = Tokenizer::new("34.5");
|
||||||
assert_eq!(tokenizer.next().unwrap(), Token::Num(34.5))
|
assert_eq!(tokenizer.next().unwrap(), Token::Num(34.5))
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_char() {
|
fn test_invalid_char()
|
||||||
|
{
|
||||||
let mut tokenizer = Tokenizer::new("#$%");
|
let mut tokenizer = Tokenizer::new("#$%");
|
||||||
assert_eq!(tokenizer.next(), None);
|
assert_eq!(tokenizer.next(), None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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":{}}
|
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue