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
//! Provides facilities for expressing and storing tracebacks.
//!
//! When a compilation or execution operation returns an error, it will store
//! a traceback within thread-local storage. To access this value, the
//! functions `get_traceback` (which clones the value) and `take_traceback`
//! (which removes the value) can be used.

use std::cell::RefCell;
use std::fmt::{self, Write};
use std::mem::replace;

use name::{Name, NameDisplay, NameStore};
use pretty::pretty_print;
use value::Value;

/// Represents a series of items, beginning with the outermost context
/// and culminating with the context in which an error was generated.
#[derive(Clone)]
pub struct Trace {
    items: Vec<TraceItem>,
    expr: Option<Value>,
}

impl Trace {
    /// Creates a new `Trace` from a series of items.
    pub fn new(items: Vec<TraceItem>, expr: Option<Value>) -> Trace {
        Trace{
            items: items,
            expr: expr,
        }
    }

    /// Creates a new `Trace` from a single item.
    pub fn single(item: TraceItem, expr: Option<Value>) -> Trace {
        Trace::new(vec![item], expr)
    }

    /// Returns the series of traced items.
    pub fn items(&self) -> &[TraceItem] {
        &self.items
    }

    /// Returns a borrowed reference to the optional contained expression.
    pub fn expr(&self) -> Option<&Value> {
        self.expr.as_ref()
    }

    /// Takes the optional contained expression and returns it.
    pub fn take_expr(&mut self) -> Option<Value> {
        self.expr.take()
    }
}

thread_local!(static TRACEBACK: RefCell<Option<Trace>> = RefCell::new(None));

/// Removes the traceback value for the current thread.
pub fn clear_traceback() {
    TRACEBACK.with(|tb| *tb.borrow_mut() = None);
}

/// Clones and returns the traceback value for the current thread.
///
/// The value remains stored for future calls to `get_traceback`.
pub fn get_traceback() -> Option<Trace> {
    TRACEBACK.with(|tb| tb.borrow().clone())
}

/// Assigns a traceback value for the current thread.
pub fn set_traceback(trace: Trace) {
    TRACEBACK.with(|tb| *tb.borrow_mut() = Some(trace));
}

/// Removes and returns the traceback value for the current thread.
pub fn take_traceback() -> Option<Trace> {
    TRACEBACK.with(|tb| replace(&mut *tb.borrow_mut(), None))
}

/// Represents a single traceable event in either compilation or
/// execution of code.
#[derive(Copy, Clone, Debug)]
pub enum TraceItem {
    /// Call to a code object; `(scope name, code name)`
    CallCode(Name, Name),
    /// Call to a code object generated by an expression
    CallExpr(Name),
    /// Call to an anonymous function
    CallLambda(Name),
    /// Call to a macro; `(scope name, macro name)`
    CallMacro(Name, Name),
    /// Expansion of an operator; `(scope name, operator name)`
    CallOperator(Name, Name),
    /// Call to a system function
    CallSys(Name),
    /// Definition of a named value; `(scope name, definition name)`
    Define(Name, Name),
    /// Definition of a constant value; `(scope name, const name)`
    DefineConst(Name, Name),
    /// Definition of an anonymous lambda
    DefineLambda(Name),
    /// Definition of a macro; `(scope name, macro name)`
    DefineMacro(Name, Name),
    /// Definition of a structure; `(scope name, struct name)`
    DefineStruct(Name, Name),
    /// Module import declaration; `(scope name, module name)`
    UseModule(Name, Name),
}

impl NameDisplay for Trace {
    fn fmt(&self, names: &NameStore, f: &mut fmt::Formatter) -> fmt::Result {
        use self::TraceItem::*;

        for item in &self.items {
            match *item {
                CallCode(m, n) => try!(writeln!(f,
                    "  In {}, function {}", names.get(m), names.get(n))),
                CallExpr(m) => try!(writeln!(f,
                    "  In {}, call expression", names.get(m))),
                CallLambda(m) => try!(writeln!(f,
                    "  In {}, lambda", names.get(m))),
                CallMacro(m, n) => try!(writeln!(f,
                    "  In {}, macro expansion {}", names.get(m), names.get(n))),
                CallOperator(m, n) => try!(writeln!(f,
                    "  In {}, operator {}", names.get(m), names.get(n))),
                CallSys(n) => try!(writeln!(f,
                    "  In system function {}", names.get(n))),
                Define(m, n) => try!(writeln!(f,
                    "  In {}, define {}", names.get(m), names.get(n))),
                DefineConst(m, n) => try!(writeln!(f,
                    "  In {}, const {}", names.get(m), names.get(n))),
                DefineLambda(m) => try!(writeln!(f,
                    "  In {}, lambda", names.get(m))),
                DefineMacro(m, n) => try!(writeln!(f,
                    "  In {}, macro {}", names.get(m), names.get(n))),
                DefineStruct(m, n) => try!(writeln!(f,
                    "  In {}, struct {}", names.get(m), names.get(n))),
                UseModule(m, n) => try!(writeln!(f,
                    "  In {}, use {}", names.get(m), names.get(n))),
            }
        }

        if let Some(ref expr) = self.expr {
            try!(f.write_str("    "));
            try!(pretty_print(f, names, expr, 4));
            try!(f.write_char('\n'));
        }

        Ok(())
    }
}