Create the Span struct, along with its constituent types

This commit is contained in:
Jesse Braham 2024-11-28 18:50:14 +01:00
parent 221fd01470
commit a345c35f80
2 changed files with 143 additions and 0 deletions

View File

@ -1,3 +1,5 @@
//! Onihime programming language. //! Onihime programming language.
#![deny(missing_debug_implementations, missing_docs, rust_2018_idioms)] #![deny(missing_debug_implementations, missing_docs, rust_2018_idioms)]
mod span;

141
onihime/src/span.rs Normal file
View File

@ -0,0 +1,141 @@
use std::{cmp::Ordering, iter, ops::Range, sync::Arc};
/// A location within some source text.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Location {
line: usize,
column: usize,
}
impl Location {
/// Construct a new instance of `Location`.
#[must_use]
pub const fn new(line: usize, column: usize) -> Self {
Self { line, column }
}
}
impl PartialOrd for Location {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.line.partial_cmp(&other.line) {
Some(Ordering::Equal) => self.column.partial_cmp(&other.column),
ord => ord,
}
}
}
/// Some (optionally named) source text.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Source {
name: Option<String>,
contents: String,
lines: Vec<usize>,
}
impl Source {
/// Construct a new instance of `Source`.
#[must_use]
pub fn new(name: Option<String>, contents: String) -> Self {
let lines = contents
.match_indices('\n')
.map(|(i, _)| i)
.chain(iter::once(contents.len()))
.collect();
Self {
name,
contents,
lines,
}
}
/// Get the name of the source.
#[must_use]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
/// Set the name of the source.
pub fn set_name(&mut self, name: String) {
self.name = Some(name);
}
/// Get the [Location] of the specified byte in the source.
#[must_use]
pub fn location(&self, byte: usize) -> Location {
let line = self.lines.partition_point(|&x| x < byte);
let start = line.checked_sub(1).map_or(0, |n| self.lines[n] + 1);
let column = self.contents[start..byte].chars().count();
Location::new(line, column)
}
/// Get the full contents of the source.
#[must_use]
pub fn contents(&self) -> &str {
&self.contents
}
/// Get the specified line from the source.
#[must_use]
pub fn get_line(&self, line: usize) -> &str {
let end = self.lines[line];
let start = line.checked_sub(1).map_or(0, |n| self.lines[n] + 1);
&self.contents[start..end]
}
}
/// A contiguous sequence of bytes within some source.
#[derive(Debug, Default, Clone)]
pub struct Span {
bytes: Range<usize>,
source: Arc<Source>,
}
impl Span {
/// Construct a new instance of `Span`.
#[must_use]
pub fn new(bytes: Range<usize>, source: Arc<Source>) -> Self {
Self { bytes, source }
}
/// Join two spans, creating a new span.
#[must_use]
pub fn join(self, other: &Self) -> Self {
debug_assert!(self.same_source(other));
Self::new(self.bytes.start..other.bytes.end, self.source)
}
/// Extend one span to include another.
pub fn extend(&mut self, other: &Self) {
debug_assert!(self.same_source(other));
self.bytes.end = other.bytes.end;
}
/// The start location of a span within some source.
#[must_use]
pub fn location(&self) -> Location {
self.source.location(self.bytes.start)
}
/// The end location of a span within some source.
#[must_use]
pub fn end_location(&self) -> Location {
self.source.location(self.bytes.end)
}
/// Do two spans share the same source?
#[must_use]
pub fn same_source(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.source, &other.source)
}
}
impl PartialEq for Span {
fn eq(&self, other: &Self) -> bool {
self.same_source(other) && self.bytes == other.bytes
}
}