use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; use std::ops::Range; use serde::{Deserialize, Serialize}; use crate::source::SourceId; /// A value with the span it corresponds to in the source code. #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Spanned { /// The spanned value. pub v: T, /// The location in source code of the value. pub span: Span, } impl Spanned { /// Create a new instance from a value and its span. pub fn new(v: T, span: impl Into) -> Self { Self { v, span: span.into() } } /// Convert from `&Spanned` to `Spanned<&T>` pub fn as_ref(&self) -> Spanned<&T> { Spanned { v: &self.v, span: self.span } } /// Map the value using a function keeping the span. pub fn map(self, f: F) -> Spanned where F: FnOnce(T) -> U, { Spanned { v: f(self.v), span: self.span } } } impl Debug for Spanned { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.v.fmt(f)?; if f.alternate() { f.write_str(" <")?; self.span.fmt(f)?; f.write_str(">")?; } Ok(()) } } /// Bounds of a slice of source code. #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Span { /// The id of the source file. pub source: SourceId, /// The inclusive start position. pub start: usize, /// The inclusive end position. pub end: usize, } impl Span { /// Create a new span from start and end positions. pub fn new(source: SourceId, start: usize, end: usize) -> Self { Self { source, start, end } } /// Create a span including just a single position. pub fn at(source: SourceId, pos: usize) -> Self { Self::new(source, pos, pos) } /// Create a span without real location information, usually for testing. pub fn detached() -> Self { Self { source: SourceId::from_raw(0), start: 0, end: 0, } } /// Create a span with a different start position. pub fn with_start(self, start: usize) -> Self { Self { start, ..self } } /// Create a span with a different end position. pub fn with_end(self, end: usize) -> Self { Self { end, ..self } } /// Whether the span is a single point. pub fn is_empty(self) -> bool { self.start == self.end } /// The byte length of the spanned region. pub fn len(self) -> usize { self.end - self.start } /// A new span at the position of this span's start. pub fn at_start(&self) -> Span { Self::at(self.source, self.start) } /// A new span at the position of this span's end. pub fn at_end(&self) -> Span { Self::at(self.source, self.end) } /// Create a new span with the earlier start and later end position. /// /// This panics if the spans come from different files. pub fn join(self, other: Self) -> Self { debug_assert_eq!(self.source, other.source); Self { source: self.source, start: self.start.min(other.start), end: self.end.max(other.end), } } /// Expand a span by merging it with another span. pub fn expand(&mut self, other: Self) { *self = self.join(other) } /// Test whether a position is within the span. pub fn contains(&self, pos: usize) -> bool { self.start <= pos && self.end >= pos } /// Test whether one span complete contains the other span. pub fn surrounds(self, other: Self) -> bool { self.source == other.source && self.start <= other.start && self.end >= other.end } /// Convert to a `Range` for indexing. pub fn to_range(self) -> Range { self.start .. self.end } } impl Debug for Span { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{:?}-{:?}", self.start, self.end) } } impl PartialOrd for Span { fn partial_cmp(&self, other: &Self) -> Option { if self.source == other.source { Some(self.start.cmp(&other.start).then(self.end.cmp(&other.end))) } else { None } } }