1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::fmt;
use std::error::Error;

use std::num::ParseIntError;

/// Represents a partial computation that can be captured as part of an
/// error message.
#[derive(Debug, PartialEq)]
pub enum PartialComp {
    Unary { op: String, arg: String },
    Binary {
        op: String,
        lhs: String,
        rhs: String,
    },
}

impl PartialComp {
    pub fn unary<T, U>(op: T, arg: U) -> Self
        where T: ToString,
              U: ToString
    {
        PartialComp::Unary {
            op: op.to_string(),
            arg: arg.to_string(),
        }
    }

    pub fn binary<T, U, V>(op: T, lhs: U, rhs: V) -> Self
        where T: ToString,
              U: ToString,
              V: ToString
    {
        PartialComp::Binary {
            op: op.to_string(),
            lhs: lhs.to_string(),
            rhs: rhs.to_string(),
        }
    }
}

impl fmt::Display for PartialComp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            PartialComp::Unary { ref op, ref arg } => {
                write!(f, "{} {}", op, arg)
            }
            PartialComp::Binary {
                ref op,
                ref lhs,
                ref rhs,
            } => write!(f, "{} {} {}", lhs, op, rhs),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum CalcError {
    BadTypes(PartialComp),
    DivideByZero,
    InvalidNumber(String),
    InvalidOperator(char),
    UnrecognizedToken(String),
    UnexpectedToken(String, &'static str),
    UnknownAtom(String),
    UnexpectedEndOfInput,
    UnmatchedParenthesis,
    WouldOverflow(PartialComp),
    WouldTruncate(PartialComp),
}

use CalcError::*;

impl fmt::Display for CalcError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            BadTypes(ref comp) => {
                write!(f, "expression '{}' is not well typed", comp)
            }
            DivideByZero => write!(f, "attempted to divide by zero"),
            InvalidNumber(ref number) => {
                write!(f, "invalid number: {}", number)
            }
            InvalidOperator(ref c) => write!(f, "invalid operator: {}", c),
            UnrecognizedToken(ref token) => {
                write!(f, "unrecognized token: {}", token)
            }
            UnexpectedToken(ref token, ref kind) => {
                write!(f, "expected {} token, got {} instead", kind, token)
            }
            UnknownAtom(ref atom) => {
                write!(f, "unknown variable or function '{}'", atom)
            }
            WouldOverflow(ref comp) => {
                write!(f, "expression '{}' would overflow", comp)
            }
            WouldTruncate(ref comp) => {
                write!(f, "expression '{}' would be truncated", comp)
            }
            UnexpectedEndOfInput => write!(f, "unexpected end of input"),
            UnmatchedParenthesis => write!(f, "unmatched patenthesis"),
        }
    }
}

impl From<ParseIntError> for CalcError {
    fn from(data: ParseIntError) -> CalcError {
        CalcError::InvalidNumber(data.description().into())
    }
}

impl From<CalcError> for String {
    fn from(data: CalcError) -> String {
        format!("{}", data)
    }
}