use nom::Slice;
use crate::{
alloc::{format, String},
Error,
};
pub type InputSpan<'a> = nom_locate::LocatedSpan<&'a str, ()>;
pub type NomResult<'a, T> = nom::IResult<InputSpan<'a>, T, Error>;
#[derive(Debug, Clone, Copy)]
pub struct LocatedSpan<Span, T = ()> {
offset: usize,
line: u32,
column: usize,
fragment: Span,
pub extra: T,
}
impl<Span: PartialEq, T> PartialEq for LocatedSpan<Span, T> {
fn eq(&self, other: &Self) -> bool {
self.line == other.line && self.offset == other.offset && self.fragment == other.fragment
}
}
impl<Span, T> LocatedSpan<Span, T> {
pub fn location_offset(&self) -> usize {
self.offset
}
pub fn location_line(&self) -> u32 {
self.line
}
pub fn get_column(&self) -> usize {
self.column
}
pub fn fragment(&self) -> &Span {
&self.fragment
}
pub fn map_extra<U>(self, map_fn: impl FnOnce(T) -> U) -> LocatedSpan<Span, U> {
LocatedSpan {
offset: self.offset,
line: self.line,
column: self.column,
fragment: self.fragment,
extra: map_fn(self.extra),
}
}
pub fn map_fragment<U>(self, map_fn: impl FnOnce(Span) -> U) -> LocatedSpan<U, T> {
LocatedSpan {
offset: self.offset,
line: self.line,
column: self.column,
fragment: map_fn(self.fragment),
extra: self.extra,
}
}
}
impl<Span: Copy, T> LocatedSpan<Span, T> {
pub fn as_ref(&self) -> LocatedSpan<Span, &T> {
LocatedSpan {
offset: self.offset,
line: self.line,
column: self.column,
fragment: self.fragment,
extra: &self.extra,
}
}
pub fn copy_with_extra<U>(&self, value: U) -> LocatedSpan<Span, U> {
LocatedSpan {
offset: self.offset,
line: self.line,
column: self.column,
fragment: self.fragment,
extra: value,
}
}
pub fn with_no_extra(&self) -> LocatedSpan<Span> {
self.copy_with_extra(())
}
}
#[allow(clippy::mismatching_type_param_order)] impl<'a, T> From<nom_locate::LocatedSpan<&'a str, T>> for LocatedSpan<&'a str, T> {
fn from(value: nom_locate::LocatedSpan<&'a str, T>) -> Self {
Self {
offset: value.location_offset(),
line: value.location_line(),
column: value.get_column(),
fragment: *value.fragment(),
extra: value.extra,
}
}
}
pub type Spanned<'a, T = ()> = LocatedSpan<&'a str, T>;
impl<'a, T> Spanned<'a, T> {
pub(crate) fn new(span: InputSpan<'a>, extra: T) -> Self {
Self {
offset: span.location_offset(),
line: span.location_line(),
column: span.get_column(),
fragment: *span.fragment(),
extra,
}
}
}
impl<'a> Spanned<'a> {
pub fn from_str<R>(code: &'a str, range: R) -> Self
where
InputSpan<'a>: Slice<R>,
{
let input = InputSpan::new(code);
Self::new(input.slice(range), ())
}
}
pub type Location<T = ()> = LocatedSpan<usize, T>;
impl Location {
pub fn from_str<'a, R>(code: &'a str, range: R) -> Self
where
InputSpan<'a>: Slice<R>,
{
Spanned::from_str(code, range).into()
}
}
impl<T> Location<T> {
pub fn to_string(&self, default_name: &str) -> String {
format!("{default_name} at {}:{}", self.line, self.column)
}
pub fn span<'a>(&self, code: &'a str) -> &'a str {
&code[self.offset..(self.offset + self.fragment)]
}
}
impl<T> From<Spanned<'_, T>> for Location<T> {
fn from(value: Spanned<'_, T>) -> Self {
value.map_fragment(str::len)
}
}
pub fn with_span<'a, O>(
mut parser: impl FnMut(InputSpan<'a>) -> NomResult<'a, O>,
) -> impl FnMut(InputSpan<'a>) -> NomResult<'a, Spanned<'_, O>> {
move |input: InputSpan<'_>| {
parser(input).map(|(rest, output)| {
let len = rest.location_offset() - input.location_offset();
let spanned = Spanned {
offset: input.location_offset(),
line: input.location_line(),
column: input.get_column(),
fragment: &input.fragment()[..len],
extra: output,
};
(rest, spanned)
})
}
}
pub(crate) fn unite_spans<'a, T, U>(
input: InputSpan<'a>,
start: &Spanned<'_, T>,
end: &Spanned<'_, U>,
) -> Spanned<'a> {
debug_assert!(input.location_offset() <= start.location_offset());
debug_assert!(start.location_offset() <= end.location_offset());
debug_assert!(
input.location_offset() + input.fragment().len()
>= end.location_offset() + end.fragment().len()
);
let start_idx = start.location_offset() - input.location_offset();
let end_idx = end.location_offset() + end.fragment().len() - input.location_offset();
Spanned {
offset: start.offset,
line: start.line,
column: start.column,
fragment: &input.fragment()[start_idx..end_idx],
extra: (),
}
}