calculat0r 0.1.0

Cli calculator app
Documentation
use core::fmt::Display;

use crate::lexer::token::{Token, TokenKind};
use crate::Result;

pub struct Parser<'a> {
    tokens: &'a [Token],
    current: usize,
    src: &'a str,
}

impl<'a> Parser<'a> {
    pub fn new(src: &'a str, tokens: &'a [Token]) -> Self {
        Self {
            tokens,
            current: 0,
            src,
        }
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = Result<Expression>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current >= self.tokens.len() {
            None
        } else {
            Some(self.expr())
        }
    }
}

#[derive(Debug)]
pub enum ArithmeticOp {
    Add, Sub, Mul, Div, Mod
}

impl From<TokenKind> for ArithmeticOp {
    fn from(value: TokenKind) -> Self {
        match value {
            TokenKind::Slash => ArithmeticOp::Div,
            TokenKind::Mul => ArithmeticOp::Mul,
            TokenKind::Mod => ArithmeticOp::Mod,
            TokenKind::Minus => ArithmeticOp::Sub,
            TokenKind::Plus => ArithmeticOp::Add,
            _ => unreachable!("Can't with {value:?}")
        }
    }
}

#[derive(Debug)]
pub enum Expression {
    Arithmetic {
        left: Box<Expression>,
        op: ArithmeticOp,
        right: Box<Expression>,
    },
    Num(f64),
}

impl Expression {
    pub fn calc(&self) -> f64 {
        match self {
            Expression::Arithmetic { left, op, right } => {
                match op {
                    ArithmeticOp::Add => left.calc() + right.calc(),
                    ArithmeticOp::Sub => left.calc() - right.calc(),
                    ArithmeticOp::Mul => left.calc() * right.calc(),
                    ArithmeticOp::Div => left.calc() / right.calc(),
                    ArithmeticOp::Mod => left.calc() % right.calc(),
                }
            }
            Expression::Num(n) => *n,
        }
    }
}

impl Display for Expression {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.calc())
    }
}

impl<'a> Parser<'a> {
    pub fn expr(&mut self) -> Result<Expression> {
        self.sum_sub()
    }

    fn sum_sub(&mut self) -> Result<Expression> {
        let mut left = self.factor()?;
        while self.matches(&[TokenKind::Plus, TokenKind::Minus]) {
            let op = self.previous().unwrap().kind;
            let right = Box::new(self.sum_sub()?);
            left = Expression::Arithmetic {
                left: Box::new(left),
                op: ArithmeticOp::from(op),
                right
            };
        }

        Ok(left)
    }

    fn factor(&mut self) -> Result<Expression> {
        let mut left = self.literal()?;
        while self.matches(&[TokenKind::Mul, TokenKind::Slash, TokenKind::Mod]) {
            let op = self.previous().unwrap().kind;
            let right = Box::new(self.factor()?);
            left = Expression::Arithmetic {
                left: Box::new(left),
                op: ArithmeticOp::from(op),
                right
            };
        }

        Ok(left)
    }

    fn literal(&mut self) -> Result<Expression> {
        let t = self.next()?;

        match t.kind {
            TokenKind::Number => {
                let span = t.span.slice(self.src);
                let num = span.parse::<f64>().unwrap();
                Ok(Expression::Num(num))

            },
            TokenKind::LParen => {
                let e = self.expr()?;
                assert!( self.next()?.kind == TokenKind::RParen );
                Ok(e)
            },
            _ => Err(format!("Unexpected token {:?}", t.kind).into())
        }

    }

    fn next(&mut self) -> Result<Token> {
        if self.current >= self.tokens.len() {
            Err("Expected token".into())
        } else {
            self.current += 1;
            Ok(self.tokens[self.current - 1])
        }
    }

    fn previous(&mut self) -> Result<Token> {
        if self.current > 0 {
            Ok(self.tokens[self.current - 1])
        } else {
            Err("No previous token".into())
        }
    }

    fn peek(&mut self) -> Result<Token> {
        if self.current >= self.tokens.len() {
            Err("Expected token".into())
        } else {
            Ok(self.tokens[self.current])
        }
    }

    fn advance(&mut self) {
        self.current += 1;
    }

    fn matches(&mut self, kinds: &[TokenKind]) -> bool {
        if self.peek().is_ok_and(|t| {
            kinds.contains(&t.kind)
        }) {
            self.advance();
            true
        } else {
            false
        }
    }
}