use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Range;
use std::rc::Rc;
use super::input::Input;
use super::span;
pub struct Position<I: Input> {
input: Rc<I>,
pos: usize
}
pub unsafe fn new<I: Input>(input: Rc<I>, pos: usize) -> Position<I> {
Position {
input,
pos
}
}
pub fn into_input<I: Input>(pos: &Position<I>) -> Rc<I> {
pos.input.clone()
}
impl<I: Input> Position<I> {
#[inline]
pub fn from_start(input: Rc<I>) -> Position<I> {
unsafe { new(input, 0) }
}
#[inline]
pub fn pos(&self) -> usize {
self.pos
}
#[inline]
pub fn span(self, other: Position<I>) -> span::Span<I> {
if Rc::ptr_eq(&self.input, &other.input) {
span::new(self.input, self.pos, other.pos)
} else {
panic!("span created from positions from different inputs")
}
}
#[inline]
pub fn line_col(&self) -> (usize, usize) {
unsafe { self.input.line_col(self.pos) }
}
#[inline]
pub fn line_of(&self) -> &str {
unsafe { self.input.line_of(self.pos) }
}
#[inline]
pub fn at_start(self) -> Result<Position<I>, Position<I>> {
if self.pos == 0 {
Ok(self)
} else {
Err(self)
}
}
#[inline]
pub fn at_end(self) -> Result<Position<I>, Position<I>> {
if self.pos == self.input.len() {
Ok(self)
} else {
Err(self)
}
}
#[inline]
pub fn skip(mut self, n: usize) -> Result<Position<I>, Position<I>> {
let skipped = unsafe { self.input.skip(n, self.pos) };
match skipped {
Some(len) => {
self.pos += len;
Ok(self)
}
None => Err(self)
}
}
#[inline]
pub fn match_string(mut self, string: &str) -> Result<Position<I>, Position<I>> {
if unsafe { self.input.match_string(string, self.pos) } {
self.pos += string.len();
Ok(self)
} else {
Err(self)
}
}
#[inline]
pub fn match_insensitive(mut self, string: &str) -> Result<Position<I>, Position<I>> {
if unsafe { self.input.match_insensitive(string, self.pos) } {
self.pos += string.len();
Ok(self)
} else {
Err(self)
}
}
#[inline]
pub fn match_range(mut self, range: Range<char>) -> Result<Position<I>, Position<I>> {
let len = unsafe { self.input.match_range(range, self.pos) };
match len {
Some(len) => {
self.pos += len;
Ok(self)
}
None => Err(self)
}
}
#[inline]
pub fn sequence<F>(self, f: F) -> Result<Position<I>, Position<I>>
where
F: FnOnce(Position<I>) -> Result<Position<I>, Position<I>>
{
let initial_pos = self.pos;
let result = f(self);
match result {
Ok(pos) => Ok(pos),
Err(mut pos) => {
pos.pos = initial_pos;
Err(pos)
}
}
}
#[inline]
pub fn lookahead<F>(self, is_positive: bool, f: F) -> Result<Position<I>, Position<I>>
where
F: FnOnce(Position<I>) -> Result<Position<I>, Position<I>>
{
let initial_pos = self.pos;
let result = f(self);
let result = match result {
Ok(mut pos) => {
pos.pos = initial_pos;
Ok(pos)
}
Err(mut pos) => {
pos.pos = initial_pos;
Err(pos)
}
};
if is_positive {
result
} else {
match result {
Ok(pos) => Err(pos),
Err(pos) => Ok(pos)
}
}
}
#[inline]
pub fn optional<F>(self, f: F) -> Result<Position<I>, Position<I>>
where
F: FnOnce(Position<I>) -> Result<Position<I>, Position<I>>
{
let result = f(self);
match result {
Ok(pos) | Err(pos) => Ok(pos)
}
}
#[inline]
pub fn repeat<F>(self, mut f: F) -> Result<Position<I>, Position<I>>
where
F: FnMut(Position<I>) -> Result<Position<I>, Position<I>>
{
let mut result = f(self);
loop {
match result {
Ok(pos) => result = f(pos),
Err(pos) => return Ok(pos)
};
}
}
}
impl<I: Input> fmt::Debug for Position<I> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Position {{ pos: {} }}", self.pos)
}
}
impl<I: Input> Clone for Position<I> {
fn clone(&self) -> Position<I> {
unsafe { new(self.input.clone(), self.pos) }
}
}
impl<I: Input> PartialEq for Position<I> {
fn eq(&self, other: &Position<I>) -> bool {
Rc::ptr_eq(&self.input, &other.input) && self.pos == other.pos
}
}
impl<I: Input> Eq for Position<I> {}
impl<I: Input> PartialOrd for Position<I> {
fn partial_cmp(&self, other: &Position<I>) -> Option<Ordering> {
if Rc::ptr_eq(&self.input, &other.input) {
self.pos.partial_cmp(&other.pos)
} else {
None
}
}
}
impl<I: Input> Ord for Position<I> {
fn cmp(&self, other: &Position<I>) -> Ordering {
self.partial_cmp(other).expect("cannot compare positions from different inputs")
}
}
impl<I: Input> Hash for Position<I> {
fn hash<H: Hasher>(&self, state: &mut H) {
(&*self.input as *const I).hash(state);
self.pos.hash(state);
}
}