Something went wrong. Try again.
An experiment in static, refcounted, procedural language with ownership
Something went wrong. Try again.
5.9 kB · 241 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242use std::{fs, io, ops};
use camino::{Utf8Path, Utf8PathBuf};use codespan_reporting::files::{self, line_starts};use compiler_data_structures::{ index_vec::{IndexVec, IndexVecExt, Key as _}, new_key_type,};
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]pub struct Span { source_id: SourceId, start: u32, end: u32,}
impl Span { pub fn unknown() -> Self { Self { source_id: SourceId::null(), start: 0, end: 0 } }
pub fn new(source_id: SourceId, start: u32, end: u32) -> Self { Self { source_id, start, end } }
pub fn initial(source_id: SourceId) -> Self { Self { source_id, start: 0, end: 0 } }
pub fn uniform(source_id: SourceId, n: u32) -> Self { Self { source_id, start: n, end: n } }
pub fn source_id(&self) -> SourceId { self.source_id }
pub fn start(&self) -> u32 { self.start }
pub fn end(&self) -> u32 { self.end }
pub fn tail(&self) -> Span { let mut new = *self; new.start = self.end; new }
#[allow(unused)] pub fn contains(&self, other: Self) -> bool { self.start <= other.start && self.end >= other.end }
pub fn merge(&self, other: Self) -> Self { assert!(self.source_id == other.source_id);
Self { source_id: self.source_id, start: self.start.min(other.start), end: self.end.max(other.end), } }}
impl core::fmt::Debug for Span { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Span({}, {}..{})", self.source_id, self.start, self.end) }}
pub trait Spannedwhere Self: Sized,{ fn span(&self) -> Span;}
#[derive(Debug)]pub struct Sources(IndexVec<SourceId, Source>);
new_key_type! { pub struct SourceId;}
impl Sources { pub fn new() -> Self { Self(IndexVec::new()) }
pub fn load_file(&mut self, path: Utf8PathBuf) -> io::Result<SourceId> { let mut source = Source::try_from(path)?;
Ok(self.0.push_with_key(|id| { source.id = id; source })) }
pub fn get(&self, id: SourceId) -> Option<&Source> { self.0.get(id) }
pub fn find_by_path(&self, path: &Utf8Path) -> Option<&Source> { self.0.iter().find(|s| s.path == path) }}
impl ops::Index<SourceId> for Sources { type Output = Source;
fn index(&self, index: SourceId) -> &Self::Output { self.get(index).unwrap() }}
impl Default for Sources { fn default() -> Self { Self::new() }}
#[derive(Debug, Clone)]pub struct Source { id: SourceId, path: Utf8PathBuf, contents: String, line_starts: Vec<usize>,}
impl Source { #[inline] pub fn id(&self) -> SourceId { self.id }
#[inline] pub fn path(&self) -> &Utf8Path { &self.path }
pub fn file_name(&self) -> String { self.path.file_stem().expect("a source to be a file").to_string() }
#[inline] pub fn contents(&self) -> &str { self.contents.as_ref() }
fn line_start(&self, line_index: usize) -> Result<usize, files::Error> { use std::cmp::Ordering;
match line_index.cmp(&self.line_starts.len()) { Ordering::Less => { Ok(self.line_starts.get(line_index).copied().expect("line index to be found")) } Ordering::Equal => Ok(self.contents.len()), Ordering::Greater => Err(files::Error::LineTooLarge { given: line_index, max: self.line_starts.len() - 1, }), } }}
impl TryFrom<Utf8PathBuf> for Source { type Error = io::Error;
fn try_from(value: Utf8PathBuf) -> Result<Self, Self::Error> { let contents = fs::read_to_string(&value)?; let line_starts = line_starts(&contents).collect();
Ok(Self { id: SourceId::null(), path: value, contents, line_starts }) }}
impl<'a> files::Files<'a> for Source { type FileId = SourceId;
type Name = &'a str;
type Source = &'a str;
fn name(&'a self, _id: Self::FileId) -> Result<Self::Name, files::Error> { Ok(self.path().as_str()) }
fn source(&'a self, _id: Self::FileId) -> Result<Self::Source, files::Error> { Ok(self.contents()) }
fn line_index(&'a self, _id: Self::FileId, byte_index: usize) -> Result<usize, files::Error> { Ok(self.line_starts.binary_search(&byte_index).unwrap_or_else(|next_line| next_line - 1)) }
fn line_range( &'a self, _id: Self::FileId, line_index: usize, ) -> Result<ops::Range<usize>, files::Error> { let line_start = self.line_start(line_index)?; let next_line_start = self.line_start(line_index + 1)?;
Ok(line_start..next_line_start) }}
impl<'a> files::Files<'a> for Sources { type FileId = SourceId;
type Name = &'a str;
type Source = &'a str;
fn name(&'a self, id: Self::FileId) -> Result<Self::Name, files::Error> { self.get(id).ok_or(files::Error::FileMissing).and_then(|source| source.name(id)) }
fn source(&'a self, id: Self::FileId) -> Result<Self::Source, files::Error> { self.get(id).ok_or(files::Error::FileMissing).and_then(|source| source.source(id)) }
fn line_index(&'a self, id: Self::FileId, byte_index: usize) -> Result<usize, files::Error> { self.get(id) .ok_or(files::Error::FileMissing) .and_then(|source| source.line_index(id, byte_index)) }
fn line_range( &'a self, id: Self::FileId, line_index: usize, ) -> Result<ops::Range<usize>, files::Error> { self.get(id) .ok_or(files::Error::FileMissing) .and_then(|source| source.line_range(id, line_index)) }}