use nom::error::VerboseError;
use nom::IResult;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Location {
pub line: usize,
pub column: usize,
pub offset: usize,
}
impl Location {
pub fn new(line: usize, column: usize, offset: usize) -> Self {
Self {
line,
column,
offset,
}
}
pub fn from_offset(input: &str, offset: usize) -> Self {
let offset = offset.min(input.len());
let prefix = &input[..offset];
let line = prefix.matches('\n').count() + 1;
let column = prefix
.rfind('\n')
.map(|pos| offset - pos)
.unwrap_or(offset + 1);
Self {
line,
column,
offset,
}
}
pub fn start() -> Self {
Self {
line: 1,
column: 1,
offset: 0,
}
}
}
impl std::fmt::Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.line, self.column)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub struct Span {
pub start: Location,
pub end: Location,
}
impl Span {
#[allow(dead_code)]
pub fn new(start: Location, end: Location) -> Self {
Self { start, end }
}
#[allow(dead_code)]
pub fn single(location: Location) -> Self {
Self {
start: location,
end: location,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArrowType {
Right,
Left,
Bidirectional,
Undirected,
DoubleUndirected,
DoubleRight,
DoubleLeft,
DoubleBidirectional,
Squiggle,
SquiggleRight,
SquiggleLeft,
SquiggleBidirectional,
}
impl ArrowType {
#[allow(dead_code)]
pub fn is_forward(&self) -> bool {
matches!(
self,
ArrowType::Right | ArrowType::DoubleRight | ArrowType::SquiggleRight
)
}
pub fn is_backward(&self) -> bool {
matches!(
self,
ArrowType::Left | ArrowType::DoubleLeft | ArrowType::SquiggleLeft
)
}
#[allow(dead_code)]
pub fn is_bidirectional(&self) -> bool {
matches!(
self,
ArrowType::Bidirectional
| ArrowType::DoubleBidirectional
| ArrowType::SquiggleBidirectional
)
}
#[allow(dead_code)]
pub fn is_undirected(&self) -> bool {
matches!(
self,
ArrowType::Undirected | ArrowType::DoubleUndirected | ArrowType::Squiggle
)
}
}
impl std::fmt::Display for ArrowType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
ArrowType::Right => "-->",
ArrowType::Left => "<--",
ArrowType::Bidirectional => "<-->",
ArrowType::Undirected => "--",
ArrowType::DoubleUndirected => "==",
ArrowType::DoubleRight => "==>",
ArrowType::DoubleLeft => "<==",
ArrowType::DoubleBidirectional => "<==>",
ArrowType::Squiggle => "~~",
ArrowType::SquiggleRight => "~~>",
ArrowType::SquiggleLeft => "<~~",
ArrowType::SquiggleBidirectional => "<~~>",
};
write!(f, "{}", s)
}
}
pub type ParseResult<'a, O> = IResult<&'a str, O, VerboseError<&'a str>>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_location_from_offset() {
let input = "line1\nline2\nline3";
let loc = Location::from_offset(input, 0);
assert_eq!(loc.line, 1);
assert_eq!(loc.column, 1);
let loc = Location::from_offset(input, 6);
assert_eq!(loc.line, 2);
assert_eq!(loc.column, 1);
let loc = Location::from_offset(input, 8);
assert_eq!(loc.line, 2);
assert_eq!(loc.column, 3);
}
#[test]
fn test_arrow_type_predicates() {
assert!(ArrowType::Right.is_forward());
assert!(ArrowType::Left.is_backward());
assert!(ArrowType::Bidirectional.is_bidirectional());
assert!(ArrowType::Squiggle.is_undirected());
assert!(ArrowType::SquiggleRight.is_forward());
}
}