xod 1.0.1

A tiny REPL for bitwise arithmetic and expression evaluation.
Documentation
pub mod ast;
pub mod bitops;
pub mod compare;
pub mod exprs;
pub mod funcs;
pub mod general;
pub mod loops;
pub mod numbers;
pub mod utils;

use self::exprs::NumOrList;
use crate::repl::help::{EW, NE, NS, SE};
use color_print::{cformat, cwriteln};
use nom_locate::LocatedSpan;
use std::collections::BTreeMap;
use std::fmt;
use std::sync::Mutex;

pub static VARIABLES: Mutex<BTreeMap<String, NumOrList>> = Mutex::new(BTreeMap::new());

pub type Span<'a> = LocatedSpan<&'a str>;

pub enum XodErr<I> {
    Parse(I, nom::error::ErrorKind),
    Incomplete(I, nom::Needed),
}

#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ExprError<'a> {
    #[error("{0}")]
    Partial(PartialEvalError<'a>),
    #[error("Exiting...")]
    Quit,
    #[error("Help requested.")]
    Help,
    #[error("Printing history...")]
    History,
    #[error("Clearing screen...")]
    Clear,
}

impl<'a> From<PartialEvalError<'a>> for ExprError<'a> {
    fn from(value: PartialEvalError<'a>) -> Self {
        Self::Partial(value)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("Error parsing {loc:#?}. {msg} {fix}")]
pub struct PartialEvalError<'a> {
    pub loc: Span<'a>,
    pub msg: String,
    pub fix: String,
}

impl<'a> From<(PartialEvalError<'a>, Span<'a>)> for EvalError<'a> {
    fn from(value: (PartialEvalError<'a>, Span<'a>)) -> Self {
        Self {
            msg: value.0.msg,
            loc: value.0.loc,
            body: value.1,
            fix: value.0.fix,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub struct EvalError<'a> {
    pub msg: String,
    pub loc: Span<'a>,
    pub body: Span<'a>,
    pub fix: String,
}

impl fmt::Display for EvalError<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        cwriteln!(f, "<s><r!>error</>: {}</>", self.msg)?;
        let start = self.loc.naive_get_utf8_column();
        let end = self.loc.fragment().len();
        let line = self.loc.location_line() as usize - 1;
        cwriteln!(
            f,
            "   <s><b!>{SE}{EW}></> line {line}, cols {start}-{}</>",
            start + end,
        )?;
        let sep_line = cformat!("<s><b!>{NS}</></>");
        writeln!(f, "   {sep_line}")?;
        let space = vec![' '; start - 1].into_iter().collect::<String>();
        let underline = cformat!("<s><c!>{}</></>", vec!['^'; end].iter().collect::<String>());
        let arrow1 = cformat!("<s><c!>{NS}</></>");
        for (i, b) in self.body.split('\n').enumerate() {
            cwriteln!(f, "<s><b!>{i: >2}</> {sep_line}\t{b}")?;
            if i == line {
                writeln!(f, "   {sep_line}\t{space}{underline}")?;
                writeln!(f, "   {sep_line}\t{space}{arrow1}")?;
                cwriteln!(
                    f,
                    "   {sep_line}\t{space}<s><c!>{NE}{EW}</> Suggested fix:</> <g!>{}</>",
                    self.fix
                )?;
            }
        }
        Ok(())
    }
}

pub trait Expression<'a, 'b, T>
where
    'a: 'b,
    T: 'a,
{
    fn eval(&'b mut self) -> Result<T, ExprError<'a>>;
}

pub trait RefSpan<'a> {
    fn get_span<'b>(&self) -> Span<'b>
    where
        'a: 'b;
}

impl<'a> RefSpan<'a> for Span<'a> {
    fn get_span<'b>(&self) -> Span<'b>
    where
        'a: 'b,
    {
        *self
    }
}