use crate::error_consts::*;
use super::Buffer;
#[derive(PartialEq, Debug)]
pub enum Ind <'a> {
Selection,
BufferLen,
Literal(usize),
Tag(char),
Pattern(&'a str),
RevPattern(&'a str),
Add(Box<Ind<'a>>, usize),
Sub(Box<Ind<'a>>, usize),
}
pub enum Sel <'a> {
Pair(Ind<'a>, Ind<'a>),
Lone(Ind<'a>)
}
enum State {
Default(usize),
Tag,
Pattern(usize),
RevPattern(usize),
Offset(usize, bool),
}
pub fn parse_index<'a> (
input: &'a str,
) -> Result<(usize, Option<Ind<'a>>), &'static str> {
let mut end = None;
let mut state = State::Default(0);
let mut current_ind = None;
let mut iter = input.char_indices();
while let Some((i, ch)) = iter.next() {
match state {
State::Default(start) => {
match ch {
'/' | '\'' | '?' | '.' | '$' => {
if start != i { return Err(INDEX_PARSE); }
if current_ind.is_some() { return Err(INDEX_PARSE); }
match ch {
'\'' => {
state = State::Tag;
},
'/' => {
state = State::Pattern(i + 1); },
'?' => {
state = State::RevPattern(i + 1); },
'.' => {
current_ind = Some(Ind::Selection);
state = State::Default(i + 1); },
'$' => {
current_ind = Some(Ind::BufferLen);
state = State::Default(i + 1); },
_ => panic!("Unreachable"),
}
}
'+' | '-' => {
if start != i {
if current_ind.is_some() { return Err(INDEX_PARSE) }
let literal = input[start .. i].parse::<usize>().map_err(|_| INDEX_PARSE)?;
current_ind = Some(Ind::Literal(literal));
}
state = State::Offset(i + 1, ch == '-');
},
_ => {
if ! ch.is_digit(10) {
end = Some(i);
break;
}
},
}
},
State::Tag => {
current_ind = Some(Ind::Tag(ch));
state = State::Default( i + ch.len_utf8() );
},
State::Pattern(start) => {
if ch == '/' {
current_ind = Some(Ind::Pattern(&input[start .. i]));
state = State::Default( i + ch.len_utf8() );
}
},
State::RevPattern(start) => {
if ch == '?' {
current_ind = Some(Ind::RevPattern(&input[start .. i]));
state = State::Default( i + ch.len_utf8() );
}
},
State::Offset(start, negative) => {
match ch {
'+' | '-' => {
let offset = if start != i {
input[start .. i].parse::<usize>().map_err(|_| INDEX_PARSE)?
} else { 1 };
current_ind = Some( if negative {
Ind::Sub(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
} else {
Ind::Add(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
});
state = State::Offset( i + ch.len_utf8(), ch == '-' );
},
x if x.is_ascii_digit() => {}, _ => { end = Some(i);
break;
},
}
},
} }
let end = match end {
Some(i) => i,
None => input.len(),
};
match state {
State::Default(start) => {
if start < end {
if current_ind.is_some() { Err(INDEX_PARSE) }
else {
let literal = input[start .. end].parse::<usize>().map_err(|_| INDEX_PARSE)?;
Ok((end, Some(Ind::Literal(literal))))
}
}
else {
Ok((end, current_ind))
}
},
State::Offset(start, negative) => {
let offset = if start < end {
input[start .. end].parse::<usize>().map_err(|_| INDEX_PARSE)?
} else {
1
};
Ok((end, Some(
if negative {
Ind::Sub(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
} else {
Ind::Add(Box::new(current_ind.unwrap_or(Ind::Selection)), offset)
}
)))
},
_ => {
Err(INDEX_PARSE)
},
}
}
pub fn parse_selection<'a>(
input: &'a str,
) -> Result<(usize, Option<Sel<'a>>), &'static str> {
let (offset, ind) = parse_index(input)?;
match input[offset .. ].chars().next() {
Some(';') => {
let (offset2, ind2) = parse_index(&input[offset + 1 ..])?;
let unwrapped1 = ind.unwrap_or(Ind::Selection);
let unwrapped2 = ind2.unwrap_or(Ind::BufferLen);
Ok((offset2 + 1 + offset, Some(Sel::Pair(unwrapped1, unwrapped2))))
},
Some(',') => {
let (offset2, ind2) = parse_index(&input[offset + 1 ..])?;
let unwrapped1 = ind.unwrap_or(Ind::Literal(0));
let unwrapped2 = ind2.unwrap_or(Ind::BufferLen);
Ok((offset2 + 1 + offset, Some(Sel::Pair(unwrapped1, unwrapped2))))
},
_ => { Ok(( offset, ind.map(|i| Sel::Lone(i)) ))
},
}
}
pub fn interpret_index<'a> (
index: Ind<'a>,
buffer: &impl Buffer,
old_selection: Option<usize>,
) -> Result<usize, &'static str> {
let ind = match index {
Ind::Selection => match old_selection {
Some(sel) => Ok(sel),
None => Err(NO_SELECTION),
},
Ind::BufferLen => Ok(buffer.len().saturating_sub(1)),
Ind::Literal(index) => {
let i = index.saturating_sub(1);
if i > buffer.len() {
Ok(buffer.len())
}
else {
Ok(i)
}
},
Ind::Tag(tag) => buffer.get_tag(tag),
Ind::Pattern(pattern) =>
buffer.get_matching(pattern, old_selection.unwrap_or(0), false),
Ind::RevPattern(pattern) =>
buffer.get_matching(pattern, old_selection.unwrap_or(buffer.len().saturating_sub(1)), true),
Ind::Add(inner, offset) => {
let inner = interpret_index(*inner, buffer, old_selection)?;
let res = if inner + offset >= buffer.len() { buffer.len().saturating_sub(1) }
else { inner + offset };
Ok(res)
},
Ind::Sub(inner, offset) => {
let inner = interpret_index(*inner, buffer, old_selection)?;
Ok(inner.saturating_sub(offset))
},
}?;
super::verify_index(buffer, ind)?;
Ok(ind)
}
pub fn interpret_selection<'a>(
input: Option<Sel<'a>>,
old_selection: Option<(usize, usize)>,
buffer: &impl Buffer,
) -> Result<(usize, usize), &'static str> {
let selection = input.unwrap_or(Sel::Pair( Ind::Selection, Ind::Selection ));
let interpreted = match selection {
Sel::Lone(ind) => {
let i = interpret_index(ind, buffer, old_selection.map(|x| x.0) )?;
(i, i)
},
Sel::Pair(ind1, ind2) => {
let i = interpret_index(ind1, buffer, old_selection.map(|x| x.0) )?;
let i2 = interpret_index(ind2, buffer, old_selection.map(|x| x.1) )?;
(i, i2)
},
};
Ok(interpreted)
}