uiua 0.18.1

A stack-based array programming language
Documentation
use smallvec::SmallVec;

use crate::{Array, Boxed, Complex, SubSide, Uiua, Value};

pub struct Fill<'a> {
    env: &'a Uiua,
    value_fill: fn(env: &'a Uiua) -> Option<&'a FillFrame>,
    other_value_fill: fn(env: &'a Uiua) -> Option<&'a FillFrame>,
    other_error: &'static str,
}

#[derive(Debug, Clone, Default)]
pub struct FillFrame {
    pub values: SmallVec<[Value; 1]>,
    pub side: Option<SubSide>,
}

impl From<Value> for FillFrame {
    fn from(value: Value) -> Self {
        (value, None).into()
    }
}

impl From<(Value, Option<SubSide>)> for FillFrame {
    fn from((value, side): (Value, Option<SubSide>)) -> Self {
        FillFrame {
            values: [value].into(),
            side,
        }
    }
}

impl From<FillValue> for FillFrame {
    fn from(fv: FillValue) -> Self {
        (fv.value, fv.side).into()
    }
}

#[derive(Debug, Clone, Copy)]
pub struct FillValue<T = Value> {
    pub value: T,
    pub side: Option<SubSide>,
}

impl<T> FillValue<T> {
    pub fn new(val: impl Into<T>, side: impl Into<Option<SubSide>>) -> Self {
        Self {
            value: val.into(),
            side: side.into(),
        }
    }
    pub fn try_map<U, E>(&self, f: impl FnOnce(&T) -> Result<U, E>) -> Result<FillValue<U>, E> {
        Ok(FillValue {
            value: f(&self.value)?,
            side: self.side,
        })
    }
    pub fn map_ref<'a, U>(&'a self, f: impl FnOnce(&'a T) -> U) -> FillValue<U> {
        FillValue {
            value: f(&self.value),
            side: self.side,
        }
    }
    pub fn is_left(&self) -> bool {
        self.side == Some(SubSide::Left)
    }
    pub fn is_right(&self) -> bool {
        self.side == Some(SubSide::Right)
    }
}

impl<T: Clone> FillValue<&T> {
    pub fn cloned(self) -> FillValue<T> {
        FillValue {
            value: self.value.clone(),
            side: self.side,
        }
    }
}

impl<'a> Fill<'a> {
    pub fn new(env: &'a Uiua) -> Self {
        Self {
            env,
            value_fill: Uiua::fill_frame,
            other_value_fill: Uiua::unfill_frame,
            other_error: ". An unfill is set, but not a normal fill.",
        }
    }
    pub fn new_un(env: &'a Uiua) -> Self {
        Self {
            env,
            value_fill: Uiua::unfill_frame,
            other_value_fill: Uiua::fill_frame,
            other_error: ". A normal fill is set, but not an unfill.",
        }
    }
    pub fn frame(&self) -> Option<&FillFrame> {
        (self.value_fill)(self.env)
    }
    fn value_map<U>(
        &self,
        f: impl Fn(&Value) -> Result<U, &'static str>,
    ) -> Result<FillValue<U>, &'static str> {
        match self.frame() {
            Some(frame) => {
                let mut error = None;
                for val in &frame.values {
                    match f(val) {
                        Ok(val) => return Ok(FillValue::new(val, frame.side)),
                        Err(e) => {
                            error.get_or_insert(e);
                        }
                    }
                }
                Err(error.unwrap_or_else(|| self.error(false)))
            }
            None => Err(self.error(false)),
        }
    }
    pub(crate) fn num_scalar(&self) -> Result<FillValue<f64>, &'static str> {
        self.value_map(|val| match val {
            Value::Num(n) if n.rank() == 0 => Ok(n.data[0]),
            Value::Num(_) => Err(self.error(true)),
            Value::Byte(n) if n.rank() == 0 => Ok(n.data[0] as f64),
            Value::Byte(_) => Err(self.error(true)),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn num_array(&self) -> Result<FillValue<Array<f64>>, &'static str> {
        self.value_map(|val| match val {
            Value::Num(n) => Ok(n.clone()),
            Value::Byte(n) => Ok(n.convert_ref()),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn byte_scalar(&self) -> Result<FillValue<u8>, &'static str> {
        self.value_map(|val| match val {
            Value::Num(n)
                if n.rank() == 0
                    && n.data[0].fract() == 0.0
                    && (0.0..=255.0).contains(&n.data[0]) =>
            {
                Ok(n.data[0] as u8)
            }
            Value::Num(n) if n.rank() == 0 => Err(self.error(false)),
            Value::Num(_) => Err(self.error(true)),
            Value::Byte(n) if n.rank() == 0 => Ok(n.data[0]),
            Value::Byte(_) => Err(self.error(true)),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn byte_array(&self) -> Result<FillValue<Array<u8>>, &'static str> {
        self.value_map(|val| match val {
            Value::Num(n)
                if (n.data.iter()).all(|&n| n.fract() == 0.0 && (0.0..=255.0).contains(&n)) =>
            {
                Ok(n.convert_ref_with(|n| n as u8))
            }
            Value::Byte(n) => Ok(n.clone()),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn char_scalar(&self) -> Result<FillValue<char>, &'static str> {
        self.value_map(|val| match val {
            Value::Char(c) if c.rank() == 0 => Ok(c.data[0]),
            Value::Char(_) => Err(self.error(true)),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn char_array(&self) -> Result<FillValue<Array<char>>, &'static str> {
        self.value_map(|val| match val {
            Value::Char(c) => Ok(c.clone()),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn box_scalar(&self) -> Result<FillValue<Boxed>, &'static str> {
        self.value_map(|val| match val {
            Value::Box(b) if b.rank() == 0 => Ok(b.data[0].clone()),
            Value::Box(_) => Err(self.error(true)),
            val => Ok(Boxed(val.clone())),
        })
    }
    pub(crate) fn box_array(&self) -> Result<FillValue<Array<Boxed>>, &'static str> {
        self.value_map(|val| {
            Ok(match val {
                Value::Box(b) => b.clone(),
                val => Array::new([], [Boxed(val.clone())]),
            })
        })
    }
    pub(crate) fn complex_scalar(&self) -> Result<FillValue<Complex>, &'static str> {
        self.value_map(|val| match val {
            Value::Num(n) if n.rank() == 0 => Ok(Complex::new(n.data[0], 0.0)),
            Value::Num(_) => Err(self.error(true)),
            Value::Byte(n) if n.rank() == 0 => Ok(Complex::new(n.data[0] as f64, 0.0)),
            Value::Byte(_) => Err(self.error(true)),
            Value::Complex(c) if c.rank() == 0 => Ok(c.data[0]),
            Value::Complex(_) => Err(self.error(true)),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn complex_array(&self) -> Result<FillValue<Array<Complex>>, &'static str> {
        self.value_map(|val| match val {
            Value::Num(n) => Ok(n.convert_ref()),
            Value::Byte(n) => Ok(n.convert_ref()),
            Value::Complex(c) => Ok(c.clone()),
            _ => Err(self.error(false)),
        })
    }
    pub(crate) fn value_for(&self, val: &Value) -> Option<FillValue<&Value>> {
        let frame = self.frame()?;
        (frame.values.iter())
            .find(|v| val.type_id() == v.type_id())
            .map(|value| FillValue::new(value, frame.side))
    }
    fn error(&self, scalar: bool) -> &'static str {
        if scalar {
            match self.frame().and_then(|frame| frame.values.first()) {
                Some(Value::Num(_)) => ". A number fill is set, but it is not a scalar.",
                Some(Value::Byte(_)) => ". A number fill is set, but it is not a scalar.",
                Some(Value::Char(_)) => ". A character fill is set, but it is not a scalar.",
                Some(Value::Complex(_)) => ". A complex fill is set, but it is not a scalar.",
                Some(Value::Box(_)) => ". A box fill is set, but it is not a scalar.",
                None => {
                    if (self.other_value_fill)(self.env).is_some() {
                        self.other_error
                    } else {
                        ""
                    }
                }
            }
        } else {
            match self.frame().and_then(|frame| frame.values.first()) {
                Some(Value::Num(_)) => ". A number fill is set, but the array is not numbers.",
                Some(Value::Byte(_)) => ". A number fill is set, but the array is not numbers.",
                Some(Value::Char(_)) => {
                    ". A character fill is set, but the array is not characters."
                }
                Some(Value::Complex(_)) => {
                    ". A complex fill is set, but the array is not complex numbers."
                }
                Some(Value::Box(_)) => ". A box fill is set, but the array is not boxed values.",
                None => {
                    if (self.other_value_fill)(self.env).is_some() {
                        self.other_error
                    } else {
                        ""
                    }
                }
            }
        }
    }
}