Added fixes and documentation

master
peshwar9 4 years ago
parent 222338ca3b
commit 8894e08c90

@ -1,14 +1,11 @@
/// This program contains list of valid AST nodes that can be constructed and also evaluates an AST to compute a value
// Standard lib // Standard lib
use std::error; use std::error;
use std::fmt;
//Primary external libraries
//utility externa libraries
//internal modules
//structs //structs
// List of allowed AST nodes that can be constructed by Parser
// Tokens can be arithmetic operators or a Number
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Node { pub enum Node {
Add(Box<Node>, Box<Node>), Add(Box<Node>, Box<Node>),
@ -16,14 +13,12 @@ pub enum Node {
Multiply(Box<Node>, Box<Node>), Multiply(Box<Node>, Box<Node>),
Divide(Box<Node>, Box<Node>), Divide(Box<Node>, Box<Node>),
Caret(Box<Node>, Box<Node>), Caret(Box<Node>, Box<Node>),
LeftParen(Box<Node>, Box<Node>),
RightParen(Box<Node>, Box<Node>),
Negative(Box<Node>), Negative(Box<Node>),
Absolute(Box<Node>),
Number(f64), Number(f64),
} }
pub fn eval(expr: Node) -> Result<f64, EvaluationError> { // Given an AST, calculate the numeric value.
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),
@ -33,32 +28,10 @@ pub fn eval(expr: Node) -> Result<f64, EvaluationError> {
Divide(expr1, expr2) => Ok(eval(*expr1)? / eval(*expr2)?), Divide(expr1, expr2) => Ok(eval(*expr1)? / eval(*expr2)?),
Negative(expr1) => Ok(-(eval(*expr1)?)), Negative(expr1) => Ok(-(eval(*expr1)?)),
Caret(expr1, expr2) => Ok(eval(*expr1)?.powf(eval(*expr2)?)), Caret(expr1, expr2) => Ok(eval(*expr1)?.powf(eval(*expr2)?)),
Absolute(expr1) => Ok(eval(*expr1)?.abs()),
_ => Err(EvaluationError::UnableToEvaluate(
"No clue, sorry".to_string(),
)),
}
}
#[derive(Debug)]
pub enum EvaluationError {
UnableToEvaluate(String),
}
impl fmt::Display for EvaluationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
self::EvaluationError::UnableToEvaluate(e) => write!(f, "Error in evaluating {}", e),
}
}
}
impl error::Error for EvaluationError {
fn description(&self) -> &str {
match &self {
self::EvaluationError::UnableToEvaluate(e) => &e,
}
} }
} }
//Unit tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

@ -1,3 +1,4 @@
/// Module Parsemath
pub mod ast; pub mod ast;
pub mod parser; pub mod parser;
pub mod token; pub mod token;

@ -1,34 +1,39 @@
/// This program reads tokens returned by Tokenizer and converts them into AST.
// Standard lib // Standard lib
use std::error; use std::error;
use std::fmt; use std::fmt;
// Primary external libs
// Secondary internal libs
// Internal modules // Internal modules
use super::ast::Node; use super::ast::Node;
use super::token::{OperPrec, Token}; use super::token::{OperPrec, Token};
use super::tokenizer::Tokenizer; use super::tokenizer::Tokenizer;
//Structs and constants //Structs and constants
// Parser struct
pub struct Parser<'a> { pub struct Parser<'a> {
tokenizer: Tokenizer<'a>, tokenizer: Tokenizer<'a>,
current_token: Token, current_token: Token,
} }
// Public methods of Parser
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
// Create a new instance of Parser
pub fn new(expr: &'a str) -> Result<Self, ParseError> { pub fn new(expr: &'a str) -> Result<Self, ParseError> {
let mut lexer = Tokenizer::new(expr); let mut lexer = Tokenizer::new(expr);
let cur_token = lexer.next().unwrap(); let cur_token = match lexer.next() {
Some(token) => token,
None => return Err(ParseError::InvalidOperator("Invalid character".into())),
};
Ok(Parser { Ok(Parser {
tokenizer: lexer, tokenizer: lexer,
current_token: cur_token, current_token: cur_token,
}) })
} }
fn next_token(&mut self) -> Result<(), ParseError> {
self.current_token = self.tokenizer.next().unwrap(); // Take an arithmetic expression as input and return an AST
Ok(())
}
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 {
@ -36,6 +41,22 @@ impl<'a> Parser<'a> {
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
}
// Private methods of Parser
impl<'a> Parser<'a> {
// Retrieve the next token from arithmetic expression and set it to current_token field in Parser struct
fn get_next_token(&mut self) -> Result<(), ParseError> {
let next_token = match self.tokenizer.next() {
Some(token) => token,
None => return Err(ParseError::InvalidOperator("Invalid character".into())),
};
self.current_token = next_token;
Ok(())
}
// Main workhorse method that is called recursively
fn generate_ast(&mut self, oper_prec: OperPrec) -> Result<Node, ParseError> { fn generate_ast(&mut self, oper_prec: OperPrec) -> Result<Node, ParseError> {
let mut left_expr = self.parse_number()?; let mut left_expr = self.parse_number()?;
@ -50,22 +71,24 @@ impl<'a> Parser<'a> {
Ok(left_expr) Ok(left_expr)
} }
// Construct AST node for numbers, taking into account negative prefixes while handling parenthesis
fn parse_number(&mut self) -> Result<Node, ParseError> { fn parse_number(&mut self) -> Result<Node, ParseError> {
let token = self.current_token.clone(); let token = self.current_token.clone();
match token { match token {
Token::Subtract => { Token::Subtract => {
self.next_token()?; self.get_next_token()?;
let expr = self.generate_ast(OperPrec::Negative)?; let expr = self.generate_ast(OperPrec::Negative)?;
Ok(Node::Negative(Box::new(expr))) Ok(Node::Negative(Box::new(expr)))
} }
Token::Num(i) => { Token::Num(i) => {
self.next_token()?; self.get_next_token()?;
Ok(Node::Number(i)) Ok(Node::Number(i))
} }
Token::LeftParen => { Token::LeftParen => {
self.next_token()?; self.get_next_token()?;
let expr = self.generate_ast(OperPrec::DefaultZero)?; let expr = self.generate_ast(OperPrec::DefaultZero)?;
self.expect(Token::RightParen)?; self.check_paren(Token::RightParen)?;
if self.current_token == Token::LeftParen { if self.current_token == Token::LeftParen {
let right = self.generate_ast(OperPrec::MulDiv)?; let right = self.generate_ast(OperPrec::MulDiv)?;
return Ok(Node::Multiply(Box::new(expr), Box::new(right))); return Ok(Node::Multiply(Box::new(expr), Box::new(right)));
@ -77,9 +100,11 @@ impl<'a> Parser<'a> {
} }
} }
fn expect(&mut self, expected: Token) -> Result<(), ParseError> { // Check for balancing parenthesis
fn check_paren(&mut self, expected: Token) -> Result<(), ParseError> {
if expected == self.current_token { if expected == self.current_token {
self.next_token()?; self.get_next_token()?;
Ok(()) Ok(())
} else { } else {
Err(ParseError::InvalidOperator(format!( Err(ParseError::InvalidOperator(format!(
@ -89,34 +114,36 @@ impl<'a> Parser<'a> {
} }
} }
// 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 { match self.current_token {
Token::Add => { Token::Add => {
self.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)))
} }
Token::Subtract => { Token::Subtract => {
self.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::Subtract(Box::new(left_expr), Box::new(right_expr))) Ok(Node::Subtract(Box::new(left_expr), Box::new(right_expr)))
} }
Token::Multiply => { Token::Multiply => {
self.next_token()?; self.get_next_token()?;
//Get right-side expression //Get right-side expression
let right_expr = self.generate_ast(OperPrec::MulDiv)?; let right_expr = self.generate_ast(OperPrec::MulDiv)?;
Ok(Node::Multiply(Box::new(left_expr), Box::new(right_expr))) Ok(Node::Multiply(Box::new(left_expr), Box::new(right_expr)))
} }
Token::Divide => { Token::Divide => {
self.next_token()?; self.get_next_token()?;
//Get right-side expression //Get right-side expression
let right_expr = self.generate_ast(OperPrec::MulDiv)?; let right_expr = self.generate_ast(OperPrec::MulDiv)?;
Ok(Node::Divide(Box::new(left_expr), Box::new(right_expr))) Ok(Node::Divide(Box::new(left_expr), Box::new(right_expr)))
} }
Token::Caret => { Token::Caret => {
self.next_token()?; self.get_next_token()?;
//Get right-side expression //Get right-side expression
let right_expr = self.generate_ast(OperPrec::Power)?; let right_expr = self.generate_ast(OperPrec::Power)?;
Ok(Node::Caret(Box::new(left_expr), Box::new(right_expr))) Ok(Node::Caret(Box::new(left_expr), Box::new(right_expr)))
@ -129,6 +156,7 @@ impl<'a> Parser<'a> {
} }
} }
// Custom error handler for Parser
#[derive(Debug)] #[derive(Debug)]
pub enum ParseError { pub enum ParseError {
UnableToParse(String), UnableToParse(String),
@ -143,6 +171,7 @@ impl fmt::Display for ParseError {
} }
} }
} }
impl error::Error for ParseError { impl error::Error for ParseError {
fn description(&self) -> &str { fn description(&self) -> &str {
match &self { match &self {
@ -152,6 +181,14 @@ impl error::Error for ParseError {
} }
} }
// Handle error thrown from Tokenizer
impl std::convert::From<std::boxed::Box<dyn std::error::Error>> for ParseError {
fn from(_evalerr: std::boxed::Box<dyn std::error::Error>) -> Self {
return ParseError::UnableToParse("Unable to parse".into());
}
}
// Unit tests // Unit tests
#[cfg(test)] #[cfg(test)]

@ -1,3 +1,7 @@
/// This contains enum for list of Tokens, and handles Operator precedence rules.
// List of valid tokens that can be constructed from arithmetic expression by Tokenizer
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Token { pub enum Token {
Add, Add,
@ -11,6 +15,8 @@ pub enum Token {
EOF, EOF,
} }
// Order of operators as per operator precedence rules (low to high)
#[derive(Debug, PartialEq, PartialOrd)] #[derive(Debug, PartialEq, PartialOrd)]
/// Defines all the OperPrec levels, from lowest to highest. /// Defines all the OperPrec levels, from lowest to highest.
pub enum OperPrec { pub enum OperPrec {
@ -21,6 +27,8 @@ pub enum OperPrec {
Negative, Negative,
} }
// This contains methods to retrieve operator precedence for a given arithmetic operator
impl Token { impl Token {
pub fn get_oper_prec(&self) -> OperPrec { pub fn get_oper_prec(&self) -> OperPrec {
use self::OperPrec::*; use self::OperPrec::*;

@ -1,22 +1,20 @@
/// This module reads characters in arithmetic expression and converts them to tokens.
/// The allowed tokens are defined in ast module.
// Standard lib // Standard lib
use std::error;
use std::fmt;
use std::iter::Peekable; use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
// Primary external libraries
// Utility external libraries
//Other internal modules //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
pub struct Tokenizer<'a> { pub struct Tokenizer<'a> {
expr: Peekable<Chars<'a>>, expr: Peekable<Chars<'a>>,
} }
// Constructs a new instance of Tokenizer
impl<'a> Tokenizer<'a> { impl<'a> Tokenizer<'a> {
pub fn new(new_expr: &'a str) -> Self { pub fn new(new_expr: &'a str) -> Self {
Tokenizer { Tokenizer {
@ -25,6 +23,9 @@ impl<'a> Tokenizer<'a> {
} }
} }
// Implement Iterator trait for Tokenizer struct.
// With this, we can use next() method on tokenier 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;
@ -33,14 +34,18 @@ impl<'a> Iterator for Tokenizer<'a> {
match next_char { match next_char {
Some('0'..='9') => { Some('0'..='9') => {
let mut number = next_char.unwrap().to_string(); let mut number = next_char?.to_string();
while let Some(next_char) = self.expr.peek() { while let Some(next_char) = self.expr.peek() {
if next_char.is_numeric() || next_char == &'.' { if next_char.is_numeric() || next_char == &'.' {
number.push(self.expr.next().unwrap()); number.push(self.expr.next()?);
} else if next_char == &'(' {
return None;
} else { } else {
break; break;
} }
} }
Some(Token::Num(number.parse::<f64>().unwrap())) Some(Token::Num(number.parse::<f64>().unwrap()))
} }
Some('+') => Some(Token::Add), Some('+') => Some(Token::Add),
@ -56,32 +61,6 @@ impl<'a> Iterator for Tokenizer<'a> {
} }
} }
#[derive(Debug)]
/// Defines the various errors that can occur during evaluation.
pub enum TokenizerError {
CharacterIsInvalid(String),
}
impl fmt::Display for TokenizerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TokenizerError::*;
match *self {
CharacterIsInvalid(ref e) => write!(f, "Lexing error: {}", e),
}
}
}
impl error::Error for TokenizerError {
fn description(&self) -> &str {
use self::TokenizerError::*;
match *self {
CharacterIsInvalid(ref e) => e,
}
}
}
// Unit tests // Unit tests
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

Loading…
Cancel
Save