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
//! Syntactic constructs and related data structures.
use std::fmt;
use std::rc::Rc;

/// A location in source code.
///
/// Stores both the bytewise [position](#structfield.pos) and the logical [line](#structfield.row) and [character](#structfield.col) numbers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Loc {
    /// 0-based byte index.
    pub pos: usize,
    /// 0-based line number.
    pub row: usize,
    /// 0-based character number on the line.
    pub col: usize,
}

impl Loc {
    /// Creates a new `Loc` with the given positions.
    #[inline]
    pub fn new(pos: usize, row: usize, col: usize) -> Self {
        Loc {
            pos,
            row,
            col,
        }
    }

    /// Creates a new `Loc` pointing to the first byte of the source code (`pos`, `row`, and `col` all zero).
    #[inline]
    pub fn zero() -> Self {
        Default::default()
    }
}

/// A region of source code.
///
/// A pair of locations, representing a half-open range, and a file name, identifying the source code in which this region appears.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpanT<F> {
    /// The name of the source code.
    ///
    /// Often a file name, but can be an arbitrary string like `<input>` or even any other type.
    pub file_name: F,
    /// The (inclusive) starting location.
    pub start: Loc,
    /// The (exclusive) ending location.
    pub end: Loc,
}

// TODO link to parser
/// A `SpanT` with a borrowed string file name.
///
/// Used widely by the [lexer](../lex/index.html) and parser because it appears in every syntactic construct and is cheap to copy.
///
/// If the `SpanT` must own its filename, use [`SpanRc`](type.SpanRc.html) instead.
pub type Span<'f> = SpanT<&'f str>;

/// A `SpanT` with a reference-counted file name.
///
/// Useful for creating `SpanT`s which own their file name, but more expensive to clone than a regular [`Span`](type.Span.html).
pub type SpanRc = SpanT<Rc<String>>;

impl<F> SpanT<F> {
    /// Creates a new `SpanT` with the given file name and locations.
    #[inline]
    pub fn new(file_name: F, start: Loc, end: Loc) -> Self {
        SpanT {
            file_name,
            start,
            end,
        }
    }

    /// Creates an empty `SpanT` at the given location, with the given file name.
    #[inline]
    pub fn empty(file_name: F, loc: Loc) -> Self {
        SpanT::new(file_name, loc, loc)
    }

    /// Creates an empty `SpanT` with the given file name, pointing to the first position in the file.
    #[inline]
    pub fn zero(file_name: F) -> Self {
        SpanT::new(file_name, Default::default(), Default::default())
    }
}

impl<'f> Span<'f> {
    /// Converts a `Span` into a [`SpanRc`](type.SpanRc.html) by cloning the borrowed file name.
    pub fn with_rc(&self) -> SpanRc {
        SpanT::new(Rc::new(self.file_name.to_owned()), self.start, self.end)
    }

    /// Converts a `Span` into a [`SpanT`](struct.SpanT.html) which owns its data by cloning the borrowed file name.
    pub fn with_owned(&self) -> SpanT<String> {
        SpanT::new(self.file_name.to_owned(), self.start, self.end)
    }
}

impl<F: fmt::Display> fmt::Display for SpanT<F> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.start.row == self.end.row {
            if self.start.col == self.end.col {
                write!(
                    f,
                    "{}:{},{}",
                    self.file_name,
                    self.start.row + 1,
                    self.start.col + 1,
                )
            } else {
                write!(
                    f,
                    "{}:{},{}-{}",
                    self.file_name,
                    self.start.row + 1,
                    self.start.col + 1,
                    self.end.col + 1,
                )
            }
        } else {
            write!(
                f,
                "{}:{},{}-{},{}",
                self.file_name,
                self.start.row + 1,
                self.start.col + 1,
                self.end.row + 1,
                self.end.col + 1,
            )
        }
    }
}