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