use std::fmt::{self, Display, Formatter}; #[derive(Debug, Clone, Copy, Eq)] #[cfg_attr(not(test), derive(PartialEq))] pub struct Pos { pub source: usize, pub line: usize, pub col: usize, pub byte: usize, pub len: usize, } impl Display for Pos { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt, "line {} at {}", self.line + 1, self.col + 1) } } impl Default for Pos { fn default() -> Self { Pos { source: 0, line: 0, col: 0, byte: 0, len: 1, } } } #[cfg(test)] impl PartialEq for Pos { fn eq(&self, _other: &Pos) -> bool { true } } impl Pos { pub fn from_char(c: char, source: usize, line: usize, col: usize, byte: usize) -> Self { Pos { source, line, col, byte, len: c.len_utf8(), } } pub fn next_char(&self, c: char) -> Self { Pos { source: self.source + 1, line: self.line, col: self.col + 1, byte: self.byte + self.len, len: c.len_utf8(), } } pub fn adv_char(&mut self, c: char) { *self = self.next_char(c); } pub fn adv_str(&mut self, s: &str) { for c in s.chars() { self.adv_char(c); } } pub fn min(self, other: Self) -> Self { if self.byte < other.byte { self } else { other } } pub fn max(self, other: Self) -> Self { if self.byte > other.byte { self } else { other } } } #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub struct Span { pub start: Pos, pub end: Pos, } impl Span { pub fn union(self, other: Self) -> Self { let start = self.start.min(other.start); let end = self.end.max(other.end); Span { start, end } } } impl Display for Span { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { if self.start == self.end { Display::fmt(&self.start, fmt) } else if self.start.line == self.end.line { write!( fmt, "line {} at {}-{}", self.start.line + 1, self.start.col + 1, self.end.col + 1 ) } else { write!( fmt, "lines {} to {}", self.start.line + 1, self.end.line + 1 ) } } } pub trait Spanned { fn span(&self) -> Span; fn text_at<'t>(&self, text: &'t str) -> &'t str { let Span { start, end } = self.span(); &text[start.byte..end.byte] } } impl Spanned for Span { fn span(&self) -> Span { *self } } /* pub struct Sourced<'t, T: Spanned> { text: &'t str, inner: T, } impl<'t, T: Spanned> Sourced<'t, T> { fn text(&self) -> &'t str { self.text_at(self.text) } } impl Spanned for Sourced<'_, T> { fn span(&self) -> Span { self.inner.span() } } */ #[cfg(test)] mod test { use super::*; #[test] fn test_pos_min() { let small = Pos::default(); let large = Pos { source: 1, byte: 1, ..Default::default() }; assert_eq!(small.min(large), small); assert_eq!(large.min(small), small); } #[test] fn test_pos_max() { let small = Pos::default(); let large = Pos { source: 1, byte: 1, ..Default::default() }; assert_eq!(small.max(large), large); assert_eq!(large.max(small), large); } #[test] fn test_span_union() { let first = Span { start: Pos::default(), end: Pos { source: 15, col: 15, byte: 15, ..Default::default() }, }; let second = Span { start: Pos { source: 25, col: 25, byte: 25, ..Default::default() }, end: Pos { source: 27, col: 27, byte: 27, ..Default::default() }, }; let expected = Span { start: first.start, end: second.end, }; assert_eq!(first.union(second), expected); assert_eq!(second.union(first), expected); } }