use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Span {
pub start: u32,
pub end: u32,
pub start_line: usize,
pub start_col: usize,
}
impl Span {
pub fn new(start: u32, end: u32, start_line: usize, start_col: usize) -> Self {
Self {
start,
end,
start_line,
start_col,
}
}
pub fn point(offset: u32, line: usize, col: usize) -> Self {
Self::new(offset, offset + 1, line, col)
}
pub fn dummy() -> Self {
Self::new(0, 0, 0, 0)
}
pub fn is_dummy(&self) -> bool {
self.start == 0 && self.end == 0 && self.start_line == 0
}
pub fn merge(self, other: Span) -> Span {
if self.is_dummy() {
return other;
}
if other.is_dummy() {
return self;
}
let (start, start_line, start_col) = if self.start <= other.start {
(self.start, self.start_line, self.start_col)
} else {
(other.start, other.start_line, other.start_col)
};
Span {
start,
end: self.end.max(other.end),
start_line,
start_col,
}
}
pub fn len(&self) -> u32 {
self.end.saturating_sub(self.start)
}
pub fn is_empty(&self) -> bool {
self.start >= self.end
}
pub fn slice<'a>(&self, source: &'a str) -> &'a str {
&source[self.start as usize..self.end as usize]
}
pub fn to_range(&self) -> std::ops::Range<usize> {
self.start as usize..self.end as usize
}
}
impl Default for Span {
fn default() -> Self {
Self::dummy()
}
}
impl std::fmt::Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.start_line, self.start_col)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Spanned<T> {
pub node: T,
pub span: Span,
}
impl<T> Spanned<T> {
pub fn new(node: T, span: Span) -> Self {
Self { node, span }
}
pub fn dummy(node: T) -> Self {
Self::new(node, Span::dummy())
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Spanned<U> {
Spanned {
node: f(self.node),
span: self.span,
}
}
pub fn as_ref(&self) -> Spanned<&T> {
Spanned {
node: &self.node,
span: self.span,
}
}
}
impl<T: PartialEq> PartialEq for Spanned<T> {
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
impl<T: Eq> Eq for Spanned<T> {}
impl<T: std::hash::Hash> std::hash::Hash for Spanned<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.node.hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_span_merge() {
let a = Span::new(0, 5, 1, 1);
let b = Span::new(3, 10, 1, 4);
let merged = a.merge(b);
assert_eq!(merged.start, 0);
assert_eq!(merged.end, 10);
}
#[test]
fn test_span_merge_with_dummy() {
let a = Span::new(5, 10, 2, 3);
let dummy = Span::dummy();
assert_eq!(a.merge(dummy), a);
assert_eq!(dummy.merge(a), a);
}
#[test]
fn test_span_slice() {
let source = "hello world";
let span = Span::new(0, 5, 1, 1);
assert_eq!(span.slice(source), "hello");
}
#[test]
fn test_spanned_equality_ignores_span() {
let a = Spanned::new(42, Span::new(0, 1, 1, 1));
let b = Spanned::new(42, Span::new(100, 101, 5, 5));
assert_eq!(a, b);
}
}