tlisp 0.0.2

Lisp interpreter in Rust
Documentation
use crate::object::Object;

pub fn sum<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut sum = params.next().unwrap()?;

  for param in params {
    sum = match sum {
      Object::Integer(n) => match param? {
        Object::Integer(m) => Object::Integer(m + n),
        Object::Float(m) => Object::Float(m + n as f64),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      Object::Float(n) => match param? {
        Object::Integer(m) => Object::Float(m as f64 + n),
        Object::Float(m) => Object::Float(m + n),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      Object::String(s) => match param? {
        Object::String(t) => Object::String(format!("{}{}", s, t)),
        param => return Err(format!("Expected string, found {}", param)),
      },
      _ => return Err(format!("{} could not be added", sum)),
    }
  }

  Ok(sum)
}

pub fn sub<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut diff: Object = params.next().unwrap()?;

  let next = params.next();

  if next.is_none() {
    diff = match diff {
      Object::Integer(n) => Object::Integer(-n),
      Object::Float(n) => Object::Float(-n),
      _ => return Err(format!("Expected int or float, found {}", diff)),
    };

    return Ok(diff);
  }

  let rest_params = std::iter::once(next.unwrap()).chain(params);

  for param in rest_params {
    diff = match diff {
      Object::Integer(n) => match param? {
        Object::Integer(m) => Object::Integer(n - m),
        Object::Float(m) => Object::Float(n as f64 - m),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      Object::Float(n) => match param? {
        Object::Integer(m) => Object::Float(n - m as f64),
        Object::Float(m) => Object::Float(n - m),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      _ => return Err(format!("{} could not be subtracted", diff)),
    }
  }

  Ok(diff)
}

pub fn mult<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut product: Object = params.next().unwrap()?;

  for param in params {
    product = match product {
      Object::Integer(n) => match param? {
        Object::Integer(m) => Object::Integer(m * n),
        Object::Float(m) => Object::Float(m * n as f64),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      Object::Float(n) => match param? {
        Object::Integer(m) => Object::Float(m as f64 * n),
        Object::Float(m) => Object::Float(m * n),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      _ => return Err(format!("{} could not be multiplied", product)),
    }
  }

  Ok(product)
}

pub fn div<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut quotient: Object = params.next().unwrap()?;

  for param in params {
    let param = param?;

    if param == Object::Integer(0) || param == Object::Float(0.0) {
      return Err(format!("Cannot divide by zero"));
    }

    quotient = match quotient {
      Object::Integer(n) => match param {
        Object::Integer(m) => Object::Integer(n / m),
        Object::Float(m) => Object::Float(n as f64 / m),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      Object::Float(n) => match param {
        Object::Integer(m) => Object::Float(n / m as f64),
        Object::Float(m) => Object::Float(n / m),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      _ => return Err(format!("{} could not be divided", quotient)),
    }
  }

  Ok(quotient)
}

pub fn mod_<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut remainder: Object = params.next().unwrap()?;

  for param in params {
    let param = param?;

    if param == Object::Integer(0) || param == Object::Float(0.0) {
      return Err(format!("Cannot get remainder of zero"));
    }

    remainder = match remainder {
      Object::Integer(n) => match param {
        Object::Integer(m) => Object::Integer(n % m),
        Object::Float(m) => Object::Float(n as f64 % m),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      Object::Float(n) => match param {
        Object::Integer(m) => Object::Float(n % m as f64),
        Object::Float(m) => Object::Float(n % m),
        param => return Err(format!("Expected int or float, found {}", param)),
      },
      _ => return Err(format!("{} could not be divided", remainder)),
    }
  }

  Ok(remainder)
}

pub fn lt<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut result = true;

  let mut prev = params.next().unwrap()?;

  for param in params {
    let next = param?;

    result = match prev {
      Object::Integer(n) => match next {
        Object::Integer(m) => {
          prev = next;
          n < m
        }
        Object::Float(m) => {
          prev = next;
          n < m as i64
        }
        v => return Err(format!("Expected int or float, found {}", v)),
      },
      Object::Float(n) => match next {
        Object::Integer(m) => {
          prev = next;
          n < m as f64
        }
        Object::Float(m) => {
          prev = next;
          n < m
        }
        v => return Err(format!("Expected int or float, found {}", v)),
      },
      _ => return Err(format!("{} could not be compared", prev)),
    };

    if !result {
      break;
    }
  }

  Ok(Object::Bool(result))
}

pub fn gt<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut result = true;

  let mut prev = params.next().unwrap()?;

  for param in params {
    let next = param?;

    result = match prev {
      Object::Integer(n) => match next {
        Object::Integer(m) => {
          prev = next;
          n > m
        }
        Object::Float(m) => {
          n > {
            prev = next;
            m as i64
          }
        }
        v => return Err(format!("Expected int or float, found {}", v)),
      },
      Object::Float(n) => match next {
        Object::Integer(m) => {
          prev = next;
          n > m as f64
        }
        Object::Float(m) => {
          prev = next;
          n > m
        }
        v => return Err(format!("Expected int or float, found {}", v)),
      },
      _ => return Err(format!("{} could not be compared", prev)),
    };

    if !result {
      break;
    }
  }

  Ok(Object::Bool(result))
}

pub fn eq<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut result = true;

  let mut prev = params.next().unwrap()?;

  for param in params {
    let next = param?;

    let updated_prev = match (&prev, &next) {
      (Object::Integer(n), Object::Integer(m)) => n == m,
      (Object::Integer(n), Object::Float(m)) => *n == *m as i64,
      (Object::Integer(_), Object::Bool(_)) => true,
      (Object::Integer(_), _) => false,
      (Object::Float(n), Object::Integer(m)) => *n == *m as f64,
      (Object::Float(n), Object::Float(m)) => n == m,
      (Object::Float(_), Object::Bool(_)) => true,
      (Object::Float(_), _) => false,
      (Object::Bool(n), Object::Bool(m)) => n == m,
      (Object::Bool(n), Object::Void) => !n,
      (Object::Bool(b), Object::String(_)) => *b,
      (Object::Bool(_), _) => false,
      (Object::String(n), Object::String(m)) => n == m,
      (Object::String(_), Object::Bool(b)) => *b,
      (Object::String(_), _) => false,
      (Object::Void, Object::Void) => true,
      (Object::Void, Object::Bool(b)) => !b,
      (Object::Void, _) => false,
      (_, _) => return Err(format!("{} could not be compared", prev)),
    };

    if !updated_prev {
      result = false;
      break;
    }

    prev = next;
  }

  Ok(Object::Bool(result))
}

pub fn strict_eq<I: Iterator<Item = Result<Object, String>>>(
  params: &mut I,
) -> Result<Object, String> {
  let mut result = true;

  let mut prev = params.next().unwrap()?;

  for param in params {
    let next = param?;

    let updated_prev = match (&prev, &next) {
      (Object::Integer(n), Object::Integer(m)) => n == m,
      (Object::Integer(_), _) => false,
      (Object::Float(n), Object::Float(m)) => *n == *m,
      (Object::Float(_), _) => false,
      (Object::Bool(n), Object::Bool(m)) => n == m,
      (Object::Bool(_), _) => false,
      (Object::String(n), Object::String(m)) => n == m,
      (Object::String(_), _) => false,
      (Object::Void, Object::Void) => true,
      (Object::Void, _) => false,
      _ => return Err(format!("{} could not be compared", prev)),
    };

    if !updated_prev {
      result = false;
      break;
    }

    prev = next;
  }

  Ok(Object::Bool(result))
}

pub fn and<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut result = Object::Void;

  for param in params {
    let next = param?;

    result = match next {
      Object::Bool(b) => {
        if !b {
          return Ok(Object::Bool(false));
        }

        Object::Bool(true)
      }
      Object::Void => {
        return Ok(Object::Void);
      }
      v => v,
    }
  }

  Ok(result)
}

pub fn or<I: Iterator<Item = Result<Object, String>>>(params: &mut I) -> Result<Object, String> {
  let mut result = Object::Void;

  for param in params {
    let next = param?;

    result = match next {
      Object::Bool(b) => {
        if b {
          return Ok(Object::Bool(true));
        }

        Object::Bool(false)
      }
      Object::Void => Object::Void,
      v => return Ok(v),
    }
  }

  Ok(result)
}