use super::*;
use alloc::borrow::Cow;
mod directive;
mod grammar;
pub enum TakeOpt {
One,
Range(usize, usize),
More(usize),
}
pub struct Parser<'a> {
doc: &'a [u8],
indent: Vec<usize>,
consumed: u64,
pub(crate) version_checked: bool,
pub(crate) tag: BTreeMap<String, String>,
pub pos: usize,
pub eaten: usize,
}
impl Default for Parser<'_> {
fn default() -> Self {
let mut tag = BTreeMap::new();
tag.insert("!".to_string(), String::new());
tag.insert("!!".to_string(), tag_prefix!().to_string());
Self {
doc: b"",
indent: vec![0],
consumed: 0,
version_checked: false,
tag,
pos: 0,
eaten: 0,
}
}
}
impl<'a> Parser<'a> {
pub fn new(doc: &'a [u8]) -> Self {
Self::default().with_doc(doc)
}
pub fn with_doc(self, doc: &'a [u8]) -> Self {
Self { doc, ..self }
}
pub fn food(&self) -> &'a [u8] {
&self.doc[self.pos..]
}
pub fn food_str(&self) -> Cow<'_, str> {
String::from_utf8_lossy(&self.doc[self.pos..])
}
pub fn text(&mut self) -> String {
if self.eaten < self.pos {
String::from_utf8_lossy(&self.doc[self.eaten..self.pos]).into()
} else {
String::new()
}
}
}
impl Parser<'_> {
pub fn pos(self, pos: usize) -> Self {
Self { pos, eaten: pos, ..self }
}
pub fn indicator(&self) -> u64 {
self.consumed + self.pos as u64
}
pub fn err<R>(&self, name: &'static str) -> PResult<R> {
Err(PError::Terminate {
name,
msg: indicated_msg(self.doc, self.indicator()),
})
}
pub fn consume(&mut self) {
self.forward();
self.consumed += self.eaten as u64;
self.eaten = 0;
self.backward();
}
pub fn forward(&mut self) {
self.eaten = self.pos;
}
pub fn backward(&mut self) {
self.pos = self.eaten;
}
pub fn back(&mut self, n: usize) {
self.pos -= n;
}
pub fn sym(&mut self, s: u8) -> PResult<()> {
self.sym_set(&[s])
}
pub fn sym_set(&mut self, s: &[u8]) -> PResult<()> {
self.take_while(Self::is_in(s), TakeOpt::One)
}
pub fn sym_seq(&mut self, s: &[u8]) -> PResult<()> {
for s in s {
self.sym(*s)?;
}
Ok(())
}
pub fn take_while<F>(&mut self, f: F, opt: TakeOpt) -> PResult<()>
where
F: Fn(&u8) -> bool,
{
let pos = self.pos;
let mut counter = 0;
for c in self.food() {
if !f(c) {
break;
}
self.pos += 1;
counter += 1;
if let TakeOpt::One = opt {
break;
}
if let TakeOpt::Range(_, c) = opt {
if counter == c {
break;
}
}
}
if pos == self.pos {
if let TakeOpt::More(c) | TakeOpt::Range(c, _) = opt {
if c == 0 {
return Ok(());
}
}
self.backward();
Err(PError::Mismatch)
} else {
if let TakeOpt::More(c) | TakeOpt::Range(c, _) = opt {
if counter < c {
self.backward();
return Err(PError::Mismatch);
}
}
Ok(())
}
}
pub fn count<F, R>(&mut self, f: F) -> PResult<usize>
where
F: FnOnce(&mut Self) -> PResult<R>,
{
let pos = self.pos;
let _ = f(self)?;
Ok(self.pos - pos)
}
pub fn context<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
let eaten = self.eaten;
self.forward();
let r = f(self);
self.eaten = eaten;
r
}
pub fn is_in(s: &[u8]) -> impl Fn(&u8) -> bool + '_ {
move |c| !Self::not_in(s)(c)
}
pub fn not_in(s: &[u8]) -> impl Fn(&u8) -> bool + '_ {
move |c| {
for s in s {
if c == s {
return false;
}
}
true
}
}
pub fn ind(&mut self, level: usize) -> PResult<()> {
if level >= self.indent.len() {
for _ in 0..level - self.indent.len() + 1 {
self.indent.push(2);
}
} else {
self.indent.drain(level + 1..);
}
for _ in 0..self.indent[..=level].iter().sum() {
self.sym(b' ')?;
}
Ok(())
}
}