maid-lang 1.0.5

Maid Programming Language
Documentation
use crate::{
    errors::standard_error::StandardError,
    interpreting::context::Context,
    lexing::position::Position,
    values::{number::Number, value::Value},
};
use std::{cell::RefCell, iter::zip, rc::Rc};

#[derive(Debug, Clone)]
pub struct List {
    pub elements: Vec<Value>,
    pub context: Option<Rc<RefCell<Context>>>,
    pub pos_start: Option<Position>,
    pub pos_end: Option<Position>,
}

impl List {
    pub fn new(elements: Vec<Value>) -> Self {
        Self {
            elements,
            context: None,
            pos_start: None,
            pos_end: None,
        }
    }

    pub fn from(elements: Vec<Value>) -> Value {
        Value::ListValue(List::new(elements))
    }

    pub fn perform_operation(self, operator: &str, other: Value) -> Result<Value, StandardError> {
        if operator == "*" { return Ok(self.push(other.clone())) }

        match other {
            Value::ListValue(ref right) => match operator {
                "+" => {
                    Ok(self.append(&mut right.elements.clone()))
                }
                "==" => {
                    let mut is_eq = Number::null_value();

                    for (a, b) in zip(&self.elements, &right.elements) {
                        let result = a.to_owned().perform_operation("==", b.to_owned());

                        if result.is_err() {
                            return Err(result.err().unwrap());
                        }

                        is_eq = result.ok().unwrap();
                    }

                    Ok(is_eq.set_context(self.context.clone()))
                }
                "!=" => {
                    let mut is_neq = Number::null_value();

                    for (a, b) in zip(&self.elements, &right.elements) {
                        let result = a.to_owned().perform_operation("!=", b.to_owned());

                        if result.is_err() {
                            return Err(result.err().unwrap());
                        }

                        is_neq = result.ok().unwrap();
                    }

                    Ok(is_neq.set_context(self.context.clone()))
                }
                "and" => {
                    Ok(Value::NumberValue(Number::new(
                        (!self.elements.is_empty() && !right.elements.is_empty()) as u8 as f64,
                    ))
                    .set_context(self.context.clone()))
                }
                "or" => {
                    Ok(Value::NumberValue(Number::new(
                        (!self.elements.is_empty() || !right.elements.is_empty()) as u8 as f64,
                    ))
                    .set_context(self.context.clone()))
                }
                _ => Err(self.illegal_operation(Some(other))),
            },
            Value::NumberValue(ref right) => match operator {
                "^" => {
                    if right.value < -1.0 {
                        return Err(StandardError::new(
                            "cannot access a negative index",
                            right.pos_start.clone().unwrap(),
                            right.pos_end.clone().unwrap(),
                            Some(
                                "use an index greater than or equal to 0 or use -1 to reverse the list",
                            ),
                        ));
                    }

                    if right.value == -1.0 {
                        return Ok(self.reverse());
                    }

                    if (right.value as usize) >= self.elements.len() {
                        return Err(StandardError::new(
                            "index is out of bounds",
                            right.pos_start.clone().unwrap(),
                            right.pos_end.clone().unwrap(),
                            None,
                        ));
                    }

                    Ok(self.retrieve(right.value as usize))
                }
                "-" => {
                    if right.value < 0.0 {
                        return Err(StandardError::new(
                            "cannot access a negative index",
                            right.pos_start.clone().unwrap(),
                            right.pos_end.clone().unwrap(),
                            Some("use an index greater than or equal to 0"),
                        ));
                    }

                    if (right.value as usize) >= self.elements.len() {
                        return Err(StandardError::new(
                            "index is out of bounds",
                            right.pos_start.clone().unwrap(),
                            right.pos_end.clone().unwrap(),
                            None,
                        ));
                    }

                    Ok(self.remove(right.value as usize))
                }
                _ => Err(self.illegal_operation(Some(other))),
            },
            _ => {
                Err(self.illegal_operation(Some(other)))
            }
        }
    }

    pub fn illegal_operation(&self, other: Option<Value>) -> StandardError {
        StandardError::new(
            "operation not supported by type",
            self.pos_start.as_ref().unwrap().clone(),
            if other.is_some() {
                other.unwrap().position_end().unwrap()
            } else {
                self.pos_end.as_ref().unwrap().clone()
            },
            None,
        )
    }

    pub fn push(mut self, item: Value) -> Value {
        self.elements.push(item);

        Value::ListValue(self)
    }

    pub fn append(mut self, other: &mut Vec<Value>) -> Value {
        self.elements.append(other);

        Value::ListValue(self)
    }

    pub fn remove(mut self, index: usize) -> Value {
        self.elements.remove(index);

        Value::ListValue(self)
    }

    pub fn retrieve(&self, index: usize) -> Value {
        self.elements[index].clone()
    }

    pub fn reverse(mut self) -> Value {
        self.elements.reverse();

        Value::ListValue(self)
    }

    pub fn as_string(&self) -> String {
        let output = self
            .elements
            .iter()
            .map(|item| item.as_string())
            .collect::<Vec<_>>()
            .join(", ");

        format!("[{output}]").to_string()
    }
}