shelly-shell 0.5.2

A Rust based Unix style shell with a typed and structured language syntax.

use std::fmt::{ self, Display, Formatter, Debug };

use crate::language::{ text::location::Location,
                       tokenizer::{ TokenKind, TokenizerError },
                       ast::AstError };

pub enum ParserErrorKind
{
    TokenizerError(TokenizerError),
    UnexpectedEOF(TokenKind),
    MatchError(Vec<ParserError>),
    ExpectedToken(TokenKind, Option<TokenKind>),
    ExpectedExpression,
    SplatExecutable,
    InvalidAssignmentTarget,
    InvalidRange,
    InvalidMatch(String),
    InvalidImport(String),
    InvalidVisibility(String),
    InvalidExclusion(String),
    InvalidRedirection(String),
    InvalidEnum(String),
    InvalidType(String),
    UnexpectedElse,
    ExpressionNotString,
    DuplicateParameter(String),
    DuplicateLoopBinding(String),
    NoProgress,
}


impl Display for ParserErrorKind
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
    {
        match self
        {
            ParserErrorKind::TokenizerError(error) => write!(f, "Tokenizer error: {}.", error),

            ParserErrorKind::UnexpectedEOF(kind) =>
                {
                    write!(f, "Unexpected end of file, expected token: {}.", kind)
                },

            ParserErrorKind::MatchError(errors) => write!(f, "Match error: {:?}.", errors),
            ParserErrorKind::ExpectedToken(expected, found) =>
                {
                    match found
                    {
                        Some(found) =>
                            write!(f, "Expected token: {}, but found: {}.", expected, found),
                        None => write!(f, "Expected token: {}, but found end of file.", expected)
                    }
                }

            ParserErrorKind::ExpectedExpression =>
                {
                    write!(f, "Expected expression.")
                },

            ParserErrorKind::SplatExecutable =>
                write!(f, "A variable splat cannot be used as an executable."),

            ParserErrorKind::InvalidAssignmentTarget =>
                write!(f, "Assignment requires a variable or an indexed variable."),

            ParserErrorKind::InvalidType(message) | ParserErrorKind::InvalidEnum(message) =>
                write!(f, "{}", message),

            ParserErrorKind::InvalidRedirection(message) => write!(f, "{}", message),
            ParserErrorKind::InvalidMatch(message) => write!(f, "{}", message),
            ParserErrorKind::InvalidExclusion(message) => write!(f, "{}", message),
            ParserErrorKind::InvalidImport(message) => write!(f, "{}", message),
            ParserErrorKind::InvalidVisibility(message) => write!(f, "{}", message),

            ParserErrorKind::InvalidRange =>
                write!(f, "Ranges cannot be chained and inclusive ranges require an end bound."),

            ParserErrorKind::ExpressionNotString =>
                {
                    write!(f, "Expression does not resolve to a string.")
                },

            ParserErrorKind::UnexpectedElse => write!(
                f,
                "Else must follow an if branch and may appear only once at the end of a chain."
            ),

            ParserErrorKind::DuplicateParameter(name) =>
                write!(f, "Duplicate function parameter: {}.", name),

            ParserErrorKind::DuplicateLoopBinding(name) =>
                write!(f, "Duplicate loop binding: {}.", name),

            ParserErrorKind::NoProgress =>
                write!(f, "Repeated parser succeeded without consuming a token.")
        }
    }
}


pub struct ParserError
{
    pub location: Option<Location>,
    pub kind: ParserErrorKind
}


pub type ParseResult<T> = Result<T, ParserError>;


impl From<TokenizerError> for ParserError
{
    fn from(error: TokenizerError) -> Self
    {
        ParserError
            {
                location: None,
                kind: ParserErrorKind::TokenizerError(error),
            }
    }
}


impl From<AstError> for ParserError
{
    fn from(error: AstError) -> Self
    {
        match error
        {
            AstError::ExpressionNotString(location) =>
                ParserError
                    {
                        location: Some(location),
                        kind: ParserErrorKind::ExpressionNotString
                    }
        }
    }
}


impl Display for ParserError
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
    {
        write!(f, "{:?}", self)
    }
}


impl Debug for ParserError
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
    {
        if let Some(location) = &self.location
        {
            write!(f, "Parser error: {}: {}", location, self.kind)
        }
        else
        {
            write!(f, "Parser error: {}", self.kind)
        }
    }
}