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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::fmt;
use error::{ Location, Error };

/// Stack trace record.
#[derive(Clone, Debug)]
pub enum TraceEntry {
    /// Trace source file change that caused the error.
    SourceFile { target: String },
    /// Trace operator call that caused the error.
    Operator { target: String, extension: String },
    /// Trace position in last known source that cause the error.
    Position { from: Location },
}

#[derive(Debug, Clone)]
pub enum CastTarget {
    Int,
    Float,
    Number,
}

#[derive(Clone, Debug)]
pub enum CastError {
    /// Float is infinite, target can not be infinite.
    FloatIsInfinite(f64),
    /// Float is not a number, target has to be a number.
    FloatNotANumber(f64),
    /// Float can not be represented, target does not support the range.
    FloatRange(f64),
    /// Null can not be represented.
    Null,
    /// Target can not be created from Array.
    Array,
    /// Target is not be created from Hash.
    Hash,
    /// Target is not be created from Object.
    Object,
    /// Target is not be created from Function.
    Function,
    /// Empty string can not be represented.
    StringEmpty,
    /// String could not be parsed as number.
    StringNotNumerical(String),
}

impl fmt::Display for CastError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CastError::FloatIsInfinite(v) => write!(f, "Infinite float {:?}", v),
            CastError::FloatNotANumber(v) => write!(f, "Nonnumerical float {:?}", v),
            CastError::FloatRange(v) => write!(f, "Out-of-range float {:?}", v),
            CastError::Null => "Null".fmt(f),
            CastError::Array => "Array".fmt(f),
            CastError::Hash => "Associative array".fmt(f),
            CastError::Object => "Object".fmt(f),
            CastError::Function => "Function".fmt(f),
            CastError::StringEmpty => "Empty string".fmt(f),
            CastError::StringNotNumerical(ref v) => write!(f, "Nonnumerical string {:?}", v),
        }
    }
}

#[derive(Clone, Debug)]
/// Runtime error message.
pub enum RuntimeError {
    /// Callable invoked with argument count that does not match defined count.
    InvalidArgumentCount { defined: usize, given: usize },
    /// Tried to access object property that does not exist.
    ObjectHasNoProperty(String),
    /// Tried to access object method that does not exist.
    ObjectHasNoMethod(String),
    /// Tried to access object property but it was a method.
    ObjectPropertyIsNotMethod(String),
    /// Tried to access object method but it was a property.
    ObjectMethodIsNotProperty(String),
    /// Tried to call object method with wrong argument count.
    ObjectMethodArgumentMismatch { name: String, defined: u16, given: u16 },
    /// Value casting error.
    ImpossibleCast { target: CastTarget, reason: CastError },
}

impl RuntimeError {
    pub fn at(self, stack_trace: Vec<TraceEntry>) -> TracedRuntimeError {
        TracedRuntimeError {
            message: self,
            stack_trace: stack_trace
        }
    }
}

impl fmt::Display for RuntimeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RuntimeError::InvalidArgumentCount { ref defined, ref given } => {
                write!(f, "Target requires {} arguments, called with {}", defined, given)
            },
            RuntimeError::ObjectHasNoProperty(ref name) => {
                write!(f, "Object has no property {:?}", name)
            },
            RuntimeError::ObjectHasNoMethod(ref name) => {
                write!(f, "Object has no method {:?}", name)
            },
            RuntimeError::ObjectPropertyIsNotMethod(ref name) => {
                write!(f, "Property is not a method {:?}", name)
            },
            RuntimeError::ObjectMethodIsNotProperty(ref name) => {
                write!(f, "Method is not a property {:?}", name)
            },
            RuntimeError::ObjectMethodArgumentMismatch { ref name, ref defined, ref given } => {
                write!(f, "Method {:?} requires {} arguments, called with {}", name, defined, given)
            },
            RuntimeError::ImpossibleCast { ref target, ref reason } => {
                write!(f, "{} is not {}", reason, match *target {
                    CastTarget::Float => "a float",
                    CastTarget::Int => "an integer",
                    CastTarget::Number => "a number",
                })
            }
        }
    }
}

/// Runtime error with stack trace.
#[derive(Clone, Debug)]
pub struct TracedRuntimeError {
    pub message: RuntimeError,
    pub stack_trace: Vec<TraceEntry>,
}

impl TracedRuntimeError {
    pub fn new(message: RuntimeError) -> TracedRuntimeError {
        TracedRuntimeError {
            message: message,
            stack_trace: Vec::new(),
        }
    }
}

impl fmt::Display for TracedRuntimeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.message)
    }
}

impl From<TracedRuntimeError> for Error {
    fn from(inner: TracedRuntimeError) -> Error {
        Error::Runtime(inner)
    }
}