rune 0.9.0

An embeddable dynamic programming language for Rust.
Documentation
use crate::ir::eval::prelude::*;

impl IrEval for ir::IrBinary {
    type Output = IrValue;

    fn eval(
        &self,
        interp: &mut IrInterpreter<'_>,
        used: Used,
    ) -> Result<Self::Output, IrEvalOutcome> {
        use std::ops::{Add, Mul, Shl, Shr, Sub};

        let span = self.span();
        interp.budget.take(span)?;

        let a = self.lhs.eval(interp, used)?;
        let b = self.rhs.eval(interp, used)?;

        match (a, b) {
            (IrValue::Integer(a), IrValue::Integer(b)) => match self.op {
                ir::IrBinaryOp::Add => {
                    return Ok(IrValue::Integer(a.add(&b)));
                }
                ir::IrBinaryOp::Sub => {
                    return Ok(IrValue::Integer(a.sub(&b)));
                }
                ir::IrBinaryOp::Mul => {
                    return Ok(IrValue::Integer(a.mul(&b)));
                }
                ir::IrBinaryOp::Div => {
                    let number = a
                        .checked_div(&b)
                        .ok_or_else(|| IrError::msg(span, "division by zero"))?;
                    return Ok(IrValue::Integer(number));
                }
                ir::IrBinaryOp::Shl => {
                    let b = u32::try_from(b).map_err(|_| {
                        IrError::msg(&self.rhs, "cannot be converted to shift operand")
                    })?;

                    let n = a.shl(b);
                    return Ok(IrValue::Integer(n));
                }
                ir::IrBinaryOp::Shr => {
                    let b = u32::try_from(b).map_err(|_| {
                        IrError::msg(&self.rhs, "cannot be converted to shift operand")
                    })?;

                    let n = a.shr(b);
                    return Ok(IrValue::Integer(n));
                }
                ir::IrBinaryOp::Lt => return Ok(IrValue::Bool(a < b)),
                ir::IrBinaryOp::Lte => return Ok(IrValue::Bool(a <= b)),
                ir::IrBinaryOp::Eq => return Ok(IrValue::Bool(a == b)),
                ir::IrBinaryOp::Gt => return Ok(IrValue::Bool(a > b)),
                ir::IrBinaryOp::Gte => return Ok(IrValue::Bool(a >= b)),
            },
            (IrValue::Float(a), IrValue::Float(b)) => {
                #[allow(clippy::float_cmp)]
                match self.op {
                    ir::IrBinaryOp::Add => return Ok(IrValue::Float(a + b)),
                    ir::IrBinaryOp::Sub => return Ok(IrValue::Float(a - b)),
                    ir::IrBinaryOp::Mul => return Ok(IrValue::Float(a * b)),
                    ir::IrBinaryOp::Div => return Ok(IrValue::Float(a / b)),
                    ir::IrBinaryOp::Lt => return Ok(IrValue::Bool(a < b)),
                    ir::IrBinaryOp::Lte => return Ok(IrValue::Bool(a <= b)),
                    ir::IrBinaryOp::Eq => return Ok(IrValue::Bool(a == b)),
                    ir::IrBinaryOp::Gt => return Ok(IrValue::Bool(a > b)),
                    ir::IrBinaryOp::Gte => return Ok(IrValue::Bool(a >= b)),
                    _ => (),
                };
            }
            (IrValue::String(a), IrValue::String(b)) => {
                if let ir::IrBinaryOp::Add = self.op {
                    return Ok(IrValue::String(add_strings(span, &a, &b)?));
                }
            }
            _ => (),
        }

        Err(IrEvalOutcome::not_const(span))
    }
}

fn add_strings(
    span: Span,
    a: &Shared<String>,
    b: &Shared<String>,
) -> Result<Shared<String>, IrError> {
    let a = a.borrow_ref().map_err(|e| IrError::new(span, e))?;
    let b = b.borrow_ref().map_err(|e| IrError::new(span, e))?;

    let mut a = String::from(&*a);
    a.push_str(&b);
    Ok(Shared::new(a))
}