use std::fmt;
use std::sync::Arc;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Span {
pub start: usize,
pub end: usize,
pub source: Arc<str>,
}
impl Span {
#[must_use]
pub fn new(start: usize, end: usize, source: Arc<str>) -> Self {
Self { start, end, source }
}
#[must_use]
pub fn dummy() -> Self {
Self {
start: 0,
end: 0,
source: Arc::from(""),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.end.saturating_sub(self.start)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn text(&self) -> &str {
&self.source[self.start..self.end]
}
#[must_use]
pub fn merge(&self, other: &Span) -> Span {
Span {
start: self.start.min(other.start),
end: self.end.max(other.end),
source: Arc::clone(&self.source),
}
}
#[must_use]
pub fn line_col(&self) -> (usize, usize) {
let mut line = 1;
let mut col = 1;
for (i, ch) in self.source.char_indices() {
if i >= self.start {
break;
}
if ch == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
(line, col)
}
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (line, col) = self.line_col();
let len = self.end - self.start;
write!(f, "{line}:{col}..{len}")
}
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (line, col) = self.line_col();
write!(f, "{line}:{col}")
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Ident {
pub name: String,
pub span: Span,
}
impl Ident {
#[must_use]
pub fn new(name: impl Into<String>, span: Span) -> Self {
Self {
name: name.into(),
span,
}
}
#[must_use]
pub fn dummy(name: impl Into<String>) -> Self {
Self {
name: name.into(),
span: Span::dummy(),
}
}
}
impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ident({:?})", self.name)
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn span_text() {
let source: Arc<str> = Arc::from("hello world");
let span = Span::new(0, 5, Arc::clone(&source));
assert_eq!(span.text(), "hello");
}
#[test]
fn span_line_col() {
let source: Arc<str> = Arc::from("line1\nline2\nline3");
let span = Span::new(0, 1, Arc::clone(&source));
assert_eq!(span.line_col(), (1, 1));
let span = Span::new(6, 7, Arc::clone(&source));
assert_eq!(span.line_col(), (2, 1));
let span = Span::new(14, 15, Arc::clone(&source));
assert_eq!(span.line_col(), (3, 3));
}
#[test]
fn span_merge() {
let source: Arc<str> = Arc::from("hello world");
let span1 = Span::new(0, 5, Arc::clone(&source));
let span2 = Span::new(6, 11, Arc::clone(&source));
let merged = span1.merge(&span2);
assert_eq!(merged.start, 0);
assert_eq!(merged.end, 11);
}
#[test]
fn ident_display() {
let ident = Ident::dummy("foo");
assert_eq!(format!("{ident}"), "foo");
}
}