Create the Span
struct, along with its constituent types
This commit is contained in:
parent
221fd01470
commit
a345c35f80
@ -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
141
onihime/src/span.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user