cypress 0.1.0

Build simple yet expressive parsers
Documentation
// This file is not in use and is a work in progress
use crate::prelude::PInput;
use crate::{parser::utils::IntoToken, text::Char};
use std::fmt::Display;

use crate::parser::core::Span;

pub struct Underline<T>(pub T, pub Span);

impl<T> Display for Underline<T>
where
    T: Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (start, stop) = self.1;
        let underline = Color::Red("-".repeat(stop - start));
        let carrot = Color::Red(">");

        write!(
            f,
            "{}\n{}{}{}",
            self.0,
            " ".repeat(start),
            carrot,
            underline
        )
    }
}

pub enum Style<K>
where
    K: Display,
{
    Bold(K),
    Plain(K),
}

impl<K> Display for Style<K>
where
    K: Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Style::Bold(i) => write!(f, "\x1b[1m{i}\x1b[0m"),
            Style::Plain(i) => write!(f, "{i}"),
        }
    }
}

pub enum Color<K>
where
    K: Display,
{
    Red(K),
    Yellow(K),
    Plain(K),
}

impl<K> Display for Color<K>
where
    K: Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Color::Red(i) => write!(f, "\x1b[31m{i}\x1b[0m"),
            Color::Yellow(i) => write!(f, "\x1b[33m{i}\x1b[0m"),
            Color::Plain(i) => write!(f, "{i}"),
        }
    }
}

pub enum ErrorKind<D>
where
    D: Display,
{
    Custom(String),
    EOF,
    Unexpected { expected: Vec<D>, found: D },
}

impl<D> Display for ErrorKind<D>
where
    D: Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ErrorKind::Custom(str) => write!(f, "{}", Color::Red(str)),
            ErrorKind::Unexpected { expected, found } => {
                let expected = expected
                    .iter()
                    .map(|exp| format!("{exp}"))
                    .collect::<String>();
                write!(
                    f,
                    "{} {}\n{} {}",
                    Style::Bold("Expected:"),
                    Style::Bold("Found:"),
                    Color::Yellow(expected),
                    Color::Red(found)
                )
            }
            ErrorKind::EOF => write!(f, "{}", Color::Red("Reached end of file")),
        }
    }
}

pub struct Error<'a, K, D>
where
    K: PartialEq + Clone + 'a,
    D: Display,
{
    pub kind: ErrorKind<D>,
    pub span: Span,
    pub state: PInput<'a, K>,
}

impl<'a, K, D> Error<'a, K, D>
where
    K: PartialEq + Clone + Char + 'a,
    D: Display,
{
    pub fn new(kind: ErrorKind<D>, span: Span, state: PInput<'a, K>) -> Self {
        Error { kind, span, state }
    }

    pub fn fmt_state(&self) -> String {
        let ((line_start, line_stop), _) = self.state.span_snapshot(self.span);

        format!(
            "{} {}-{}\n\nSource:\n{}",
            Style::Bold(Color::Red("Error at:")),
            Style::Bold(line_start),
            Style::Bold(line_stop),
            self.state.fmt_tokens()
        )
    }
}

impl<'a, T, K> Display for Error<'a, K, T>
where
    K: PartialEq + Clone + Display + Char + 'a,
    T: IntoToken<K> + Display + Clone,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}\n\n{}", self.fmt_state(), self.kind)
    }
}

impl<'a, T> PInput<'a, T>
where
    T: PartialEq + Clone + Char + 'a,
{
    fn span_snapshot(&self, span: Span) -> (Span, usize) {
        let (expected_start, expected_stop) = span;

        let mut start = 0;
        let mut stop = 0;
        let mut tick_offset = 0;
        let mut offset = 0;
        let mut line_num = 0;

        for (i, tok) in self.tokens.iter().enumerate() {
            if tok.is_newline() {
                line_num += 1;
                tick_offset = i;
            }

            if i == expected_start {
                start = line_num;
                offset = tick_offset;
            }

            if i == expected_stop {
                stop = line_num;
                break;
            }
        }

        ((start, stop), offset)
    }

    fn fmt_tokens(&self) -> String {
        String::from_utf8(
            self.tokens
                .iter()
                .map(|tok| tok.to_ascii().unwrap())
                .collect(),
        )
        .unwrap()
    }
}