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() {
|
||||
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();
|
||||
|
|
|
@ -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)?)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {:?}",
|
||||
|
|
|
@ -41,7 +41,6 @@ impl Token {
|
|||
Add | Subtract => AddSub,
|
||||
Multiply | Divide => MulDiv,
|
||||
Caret => Power,
|
||||
|
||||
_ => 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::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);
|
||||
}
|
||||
|
|
|
@ -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