Bitterless Rust

Practice: Multiplication and Division

We have addition and subtraction. Let's add multiplication and division the same way.

Add Star and Slash to Token

#[derive(Debug, Clone, PartialEq)]
enum Token {
    Number(f64),
    Plus,
    Minus,
    Star,       // <- added
    Slash,      // <- added
}

Lexer

'*' => {
    tokens.push(Token::Star);
    self.pos += 1;
}
'/' => {
    tokens.push(Token::Slash);
    self.pos += 1;
}

You should be used to this by now.

Parser

Add Star and Slash to the loop in parse:

fn parse(&mut self) -> Expr {
    let mut left = self.parse_primary();

    loop {
        match self.peek() {
            Some(Token::Plus)
            | Some(Token::Minus)
            | Some(Token::Star)       // <- added
            | Some(Token::Slash) => { // <- added
                let op = self.next_token().unwrap();
                let right = self.parse_primary();
                left = Expr::BinOp {
                    op,
                    left: Box::new(left),
                    right: Box::new(right),
                };
            }
            _ => break,
        }
    }

    left
}

eval

match op {
    Token::Plus => l + r,
    Token::Minus => l - r,
    Token::Star => l * r,    // <- added
    Token::Slash => l / r,   // <- added
    _ => panic!("unknown operator: {:?}", op),
}

Try It Out

fn main() {
    let tests = vec![
        ("2 * 3", 6.0),
        ("10 / 3", 10.0 / 3.0),
        ("2 + 3 * 4", 20.0),     // ???
    ];

    for (input, expected) in tests {
        let mut lexer = Lexer::new(input.to_string());
        let tokens = lexer.tokenize();
        let mut parser = Parser::new(tokens);
        let ast = parser.parse();
        let result = eval(ast);
        println!("{} = {} (expected {})", input, result, expected);
    }
}
2 * 3 = 6 (expected 6)
10 / 3 = 3.3333333333333335 (expected 3.3333333333333335)
2 + 3 * 4 = 20 (expected 20)

Wait. 2 + 3 * 4 gives 20?

In arithmetic, 3 * 4 should be computed first, giving 2 + 12 = 14. But our Parser treats all operators with the same precedence from left to right, so it computes (2 + 3) * 4 = 20.

Let's look at the AST:

let mut lexer = Lexer::new("2 + 3 * 4".to_string());
let tokens = lexer.tokenize();
let mut parser = Parser::new(tokens);
let ast = parser.parse();
println!("{:?}", ast);
BinOp { op: Star, left: BinOp { op: Plus, left: Number(2.0), right: Number(3.0) }, right: Number(4.0) }

Plus gets grouped first. It should be Star that gets grouped first.

We'll fix this in the next chapter.

Complete Code for This Chapter

#[derive(Debug, Clone, PartialEq)]
enum Token {
    Number(f64),
    Plus,
    Minus,
    Star,
    Slash,
}

struct Lexer {
    input: Vec<char>,
    pos: usize,
}

impl Lexer {
    fn new(input: String) -> Lexer {
        Lexer {
            input: input.chars().collect(),
            pos: 0,
        }
    }

    fn tokenize(&mut self) -> Vec<Token> {
        let mut tokens = Vec::new();

        while self.pos < self.input.len() {
            let ch = self.input[self.pos];

            match ch {
                ' ' | '\t' => {
                    self.pos += 1;
                }
                '+' => {
                    tokens.push(Token::Plus);
                    self.pos += 1;
                }
                '-' => {
                    tokens.push(Token::Minus);
                    self.pos += 1;
                }
                '*' => {
                    tokens.push(Token::Star);
                    self.pos += 1;
                }
                '/' => {
                    tokens.push(Token::Slash);
                    self.pos += 1;
                }
                '0'..='9' => {
                    let token = self.read_number();
                    tokens.push(token);
                }
                _ => {
                    self.pos += 1;
                }
            }
        }

        tokens
    }

    fn read_number(&mut self) -> Token {
        let start = self.pos;

        while self.pos < self.input.len()
            && (self.input[self.pos].is_ascii_digit() || self.input[self.pos] == '.')
        {
            self.pos += 1;
        }

        let num_str: String = self.input[start..self.pos].iter().collect();
        let num: f64 = num_str.parse().unwrap();

        Token::Number(num)
    }
}

#[derive(Debug, Clone)]
enum Expr {
    Number(f64),
    BinOp {
        op: Token,
        left: Box<Expr>,
        right: Box<Expr>,
    },
}

struct Parser {
    tokens: Vec<Token>,
    pos: usize,
}

impl Parser {
    fn new(tokens: Vec<Token>) -> Parser {
        Parser { tokens, pos: 0 }
    }

    fn peek(&self) -> Option<Token> {
        if self.pos < self.tokens.len() {
            Some(self.tokens[self.pos].clone())
        } else {
            None
        }
    }

    fn next_token(&mut self) -> Option<Token> {
        if self.pos < self.tokens.len() {
            let token = self.tokens[self.pos].clone();
            self.pos += 1;
            Some(token)
        } else {
            None
        }
    }

    fn parse(&mut self) -> Expr {
        let mut left = self.parse_primary();

        loop {
            match self.peek() {
                Some(Token::Plus)
                | Some(Token::Minus)
                | Some(Token::Star)
                | Some(Token::Slash) => {
                    let op = self.next_token().unwrap();
                    let right = self.parse_primary();
                    left = Expr::BinOp {
                        op,
                        left: Box::new(left),
                        right: Box::new(right),
                    };
                }
                _ => break,
            }
        }

        left
    }

    fn parse_primary(&mut self) -> Expr {
        match self.next_token() {
            Some(Token::Number(n)) => Expr::Number(n),
            other => panic!("expected number, got {:?}", other),
        }
    }
}

fn eval(expr: Expr) -> f64 {
    match expr {
        Expr::Number(n) => n,
        Expr::BinOp { op, left, right } => {
            let l = eval(*left);
            let r = eval(*right);
            match op {
                Token::Plus => l + r,
                Token::Minus => l - r,
                Token::Star => l * r,
                Token::Slash => l / r,
                _ => panic!("unknown operator: {:?}", op),
            }
        }
    }
}

fn main() {
    // All four operations work! ...but precedence is wrong
    let input = String::from("2 + 3 * 4");

    let mut lexer = Lexer::new(input);
    let tokens = lexer.tokenize();
    let mut parser = Parser::new(tokens);
    let ast = parser.parse();
    let result = eval(ast);

    println!("2 + 3 * 4 = {} (should be 14)", result); // gives 20
}

All four operations are in, but operator precedence is broken. Next chapter, we fix this and finish the calculator.

Related

Back to book