Use of moved value while trying to fix a syntax tree

I'm new to Rust and I've always wanted to make my own language, so I decided to try and make an interpreter, I'd say 80% of it is my own code with a few snippets here and there. It's uses a recursive decent parser but this raised the problem of stuff like 1-1-1 being read as (1-(1-1)) which is 0 not -1. I tried to implement a function to fix this but I'm struggling with how to not use moved values, since it's handling subtrees cloning would be an extremely bad idea. Here's the code:

executor.rs:

use syntaxtree::*;

pub fn evaluate(expr: Expression) -> Types {
    match expr {
        Expression::Atom(val) => return val,
        Expression::Binary(val) => {
            let fixed = fix(val.lhs, val.rhs, val.op);
            let op = fixed.op;
            let left = evaluate(fixed.lhs);
            let right = evaluate(fixed.rhs);

            match (left, right) {
                (Types::Number(lval), Types::Number(rval)) => {
                    match op {
                        Operators::Plus => Types::Number(lval + rval),
                        Operators::Minus => Types::Number(lval - rval),
                        Operators::Asterisk => Types::Number(lval * rval),
                        Operators::Slash => Types::Number(lval / rval),
                        Operators::Caret => Types::Number(lval.powf(rval)),
                        _ => panic!("Not implemented yet."),
                    }
                }
                _ => panic!("End me")
            }
        },
        Expression::Invalid => panic!()
    }
}

fn fix(lhs: Expression, rhs : Expression, op : Operators) -> BinaryExpression {
    match rhs { 
        Expression::Binary(binary_rhs) => {
            let replacement = BinaryExpression::new(
                lhs,
                binary_rhs.lhs,
                op.clone()
            );

            fix(Expression::Binary(Box::new(replacement)), binary_rhs.rhs, op)
        }
        _ => {
            BinaryExpression::new(
                lhs,
                rhs,
                op
            )
        }
    }
}

And syntaxtree.rs for reference:

#[derive(Debug, PartialEq)]
pub enum Types {
    String(String),
    Ident(String),
    Number(f64),
    Bool(bool),
}

#[derive(PartialEq)]
pub enum Expression {
    Invalid,
    Atom(Types),
    Binary(Box<BinaryExpression>),
}

#[derive(PartialEq, Clone)]
pub enum Operators {
    Assign,
    Plus,
    Minus,
    Bang,
    Asterisk,
    Slash,
    Caret,
    Percent,
    LessThan,
    GreaterThan,
    Equal,
    NotEqual,
    LessEqual,
    GreatEqual,
}

#[derive(PartialEq)]
pub struct BinaryExpression {
    pub op: Operators,
    pub lhs: Expression, pub rhs: Expression
}

impl BinaryExpression {
    pub fn new(left: Expression, right: Expression, oper: Operators) -> BinaryExpression {
        return BinaryExpression {lhs: left, rhs: right, op: oper}
    }
}

And the output:

Any help would be appreciated!

This is a bit unfortunate: Rust usually allows you to move subcomponents of a struct individually, but in this case, since val is not BinaryExpression but Box<BinaryExpression>, and doing box.member moves the whole box.

Since you cannot move out of the box while matching right now (you might be in the future with box syntax), you have to move out of the box afterwards, inserting a let val = *val in the one match arm (after Expression::Binary(val) => {, and similarly let binary_rhs = *binary_rhs in the other one.

2 Likes

Thanks! Works great!