use crate::{source::Source, span::Span};
use std::{
borrow::Borrow,
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
ops::Deref,
};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Location {
source: Source,
position: usize,
}
impl Location {
pub(super) fn new_unchecked(source: Source, position: usize) -> Self {
Self { source, position }
}
pub fn new(source: Source, position: usize) -> Self {
if source.len() < position {
panic!(
"Location position is too big; availabe: {}, given: {}",
source.len(),
position,
);
}
Self::new_unchecked(source, position)
}
pub fn position(&self) -> usize {
self.position
}
pub(super) fn position_mut(&mut self) -> &mut usize {
&mut self.position
}
pub fn source(&self) -> &Source {
&self.source
}
pub fn line_column(&self) -> (usize, usize) {
let line = self.source.line(self.position);
let line_start = self.source.line_start(line);
(line, self.position - line_start)
}
pub fn line(&self) -> usize {
self.source.line(self.position)
}
pub fn column(&self) -> usize {
let (_, column) = self.line_column();
column
}
pub fn as_str(&self) -> &str {
&self.source[self.position]
}
pub fn segment(&self) -> LocatedSegment {
LocatedSegment { location: self.clone() }
}
pub fn line_span(&self) -> Span {
let line = self.line();
let init = self.source().line_start(line);
let end = self
.source()
.try_line_start(line + 1)
.unwrap_or(self.source().len());
Span::new(Self::new(self.source.clone(), init), end - init)
}
}
impl fmt::Debug for Location {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
let (line, column) = self.line_column();
fmtr.debug_struct("Location")
.field("source", &self.source)
.field("position", &self.position)
.field("line", &line)
.field("column", &column)
.finish()
}
}
impl fmt::Display for Location {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
let (line, column) = self.line_column();
write!(fmtr, "in {} ({}, {})", self.source, line + 1, column + 1)
}
}
impl AsRef<Self> for Location {
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<str> for Location {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Clone, Debug)]
pub struct LocatedSegment {
location: Location,
}
impl LocatedSegment {
pub fn location(&self) -> &Location {
&self.location
}
pub fn as_str(&self) -> &str {
self.location.as_str()
}
pub fn is_single_char(&self) -> bool {
self.len() == 1
}
pub fn is_alphabetic(&self) -> bool {
self.chars().next().map_or(false, |ch| ch.is_alphabetic())
}
pub fn is_ascii_alphabetic(&self) -> bool {
self.is_single_char()
&& self.chars().next().map_or(false, |ch| ch.is_ascii_alphabetic())
}
pub fn is_numeric(&self) -> bool {
self.chars().next().map_or(false, |ch| ch.is_numeric())
}
pub fn is_ascii_numeric(&self) -> bool {
self.is_single_char()
&& self.chars().next().map_or(false, |ch| ch.is_ascii_digit())
}
pub fn is_alphanumeric(&self) -> bool {
self.chars().next().map_or(false, |ch| ch.is_alphanumeric())
}
pub fn is_ascii_alphanumeric(&self) -> bool {
self.is_single_char()
&& self
.chars()
.next()
.map_or(false, |ch| ch.is_ascii_alphanumeric())
}
pub fn is_digit(&self, base: u32) -> bool {
self.is_single_char()
&& self.chars().next().map_or(false, |ch| ch.is_digit(base))
}
pub fn to_digit(&self, base: u32) -> Option<u32> {
self.chars()
.next()
.and_then(|ch| ch.to_digit(base))
.filter(|_| self.is_single_char())
}
pub fn is_newline(&self) -> bool {
self == "\n"
}
pub fn is_space(&self) -> bool {
self == " "
}
pub fn is_whitespace(&self) -> bool {
self.chars().all(char::is_whitespace)
}
}
impl Deref for LocatedSegment {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for LocatedSegment {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
write!(fmtr, "{}", &**self)
}
}
impl PartialEq for LocatedSegment {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<str> for LocatedSegment {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl<'seg> PartialEq<&'seg str> for LocatedSegment {
fn eq(&self, other: &&'seg str) -> bool {
self.as_str() == *other
}
}
impl Eq for LocatedSegment {}
impl PartialOrd for LocatedSegment {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd<str> for LocatedSegment {
fn partial_cmp(&self, other: &str) -> Option<Ordering> {
self.as_str().partial_cmp(other)
}
}
impl<'seg> PartialOrd<&'seg str> for LocatedSegment {
fn partial_cmp(&self, other: &&'seg str) -> Option<Ordering> {
self.as_str().partial_cmp(*other)
}
}
impl Ord for LocatedSegment {
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl Hash for LocatedSegment {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
(**self).hash(hasher)
}
}
impl AsRef<Self> for LocatedSegment {
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<str> for LocatedSegment {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for LocatedSegment {
fn borrow(&self) -> &str {
self.as_str()
}
}