woojin 0.1.3

Woojinlang interpreter written as Rust
Documentation
use nom::{
  IResult,
  branch::{alt},
  multi::{fold_many0},
  bytes::complete::{ tag },
  character::complete::{ char, space0, digit1 },
  sequence::{pair, delimited},
  combinator::{map}
};
use crate::{types::WoojinValue, parser::WoojinResult, error::WoojinError};

#[derive(Debug, PartialEq, Clone)]
pub(crate) enum Calc {
  Value(WoojinValue),
  Add(Box<Calc>, Box<Calc>),
  Sub(Box<Calc>, Box<Calc>),
  Mul(Box<Calc>, Box<Calc>),
  Div(Box<Calc>, Box<Calc>),
}

pub(crate) fn parse_primary(input: &str) -> IResult<&str, Calc> {
  alt((
    map(digit1, |digits: &str| {
      let value = digits.parse().unwrap();
      let woojin_value = WoojinValue::Int(value);
      Calc::Value(woojin_value)
    }),
    delimited(
      char('('),
      delimited(space0, parse_expr, space0),
      char(')'),
    ),
  ))(input.trim())
}

pub(crate) fn parse_expr(input: &str) -> IResult<&str, Calc> {
  let (input, init): (&str, Calc) = parse_term(input)?;
  let (input, pairs) = fold_many0(
    pair(alt((tag("+"), tag("-"))), parse_term),
    move || init.clone(),
    |acc, (op, val)| match op {
      "+" => Calc::Add(Box::new(acc), Box::new(val)),
      "-" => Calc::Sub(Box::new(acc), Box::new(val)),
      _ => unreachable!(),
    },
  )(input.trim())?;
  Ok((input.trim(), pairs))
}

pub(crate) fn parse_term(input: &str) -> IResult<&str, Calc> {
  let (input, init): (&str, Calc) = parse_primary(input)?;
  let (input, pairs) = fold_many0(
    pair(alt((tag("*"), tag("/"))), parse_primary),
    move || init.clone(),
    |acc, (op, val)| match op {
      "*" => Calc::Mul(Box::new(acc), Box::new(val)),
      "/" => Calc::Div(Box::new(acc), Box::new(val)),
      _ => unreachable!(),
    },
  )(input.trim())?;
  Ok((input.trim(), pairs))
}


pub(crate) fn parse_calc(input: &str) -> IResult<&str, Calc> {
  delimited(space0, parse_expr, space0)(input)
}

pub(crate) trait ValueCalc {
  fn add(&self, other: &WoojinValue) -> WoojinResult<WoojinValue>;
  fn sub(&self, other: &WoojinValue) -> WoojinResult<WoojinValue>;
  fn mul(&self, other: &WoojinValue) -> WoojinResult<WoojinValue>;
  fn div(&self, other: &WoojinValue) -> WoojinResult<WoojinValue>;
}

impl ValueCalc for WoojinValue {
  fn add(&self, other: &WoojinValue) -> WoojinResult<WoojinValue> {
    match (self, other.value()) {
      (WoojinValue::Int(a), WoojinValue::Int(b)) => Ok(WoojinValue::Int(a + b)),
      (WoojinValue::Float(a), WoojinValue::Float(b)) => Ok(WoojinValue::Float(a + b)),
      (WoojinValue::String(a), WoojinValue::String(b)) => Ok(WoojinValue::String(format!("{}{}", a, b))),
      _ => Err(WoojinError::new("The type that can't be added", crate::error::WoojinErrorKind::CannotAdd)),
    }  
  }

  fn sub(&self, other: &WoojinValue) -> WoojinResult<WoojinValue> {
    match (self, other.value()) {
      (WoojinValue::Int(a), WoojinValue::Int(b)) => Ok(WoojinValue::Int(a - b)),
      (WoojinValue::Float(a), WoojinValue::Float(b)) => Ok(WoojinValue::Float(a - b)),
      _ => Err(WoojinError::new("The type that can't be subtracted", crate::error::WoojinErrorKind::CannotSubtract)),
    }  
  }

  fn mul(&self, other: &WoojinValue) -> WoojinResult<WoojinValue> {
    match (self, other.value()) {
      (WoojinValue::Int(a), WoojinValue::Int(b)) => Ok(WoojinValue::Int(a * b)),
      (WoojinValue::Float(a), WoojinValue::Float(b)) => Ok(WoojinValue::Float(a * b)),
      _ => Err(WoojinError::new("The type that can't be multiplied!", crate::error::WoojinErrorKind::CannotMultiply)),
    }
  }

  fn div(&self, other: &WoojinValue) -> WoojinResult<WoojinValue> {
    match (self, other.value()) {
      (WoojinValue::Int(a), WoojinValue::Int(b)) => {
        Ok(if b != 0 { WoojinValue::Int(a / b) } else {
          return Err(WoojinError::new("It cannot be divided by 0", crate::error::WoojinErrorKind::DivisionByZero))
        })
      },
      (WoojinValue::Float(a), WoojinValue::Float(b)) => {
        Ok(if b != 0.0 { WoojinValue::Float(a / b) } else {
          return Err(WoojinError::new("It cannot be divided by 0.0", crate::error::WoojinErrorKind::DivisionByZero))
        })
      },
      _ => Err(WoojinError::new("an indivisible type!", crate::error::WoojinErrorKind::CannotDivide)),
    }
  }
}