unidok-parser 0.2.0

Parser for the Unidok document format
Documentation
use std::convert::TryInto;

use aho_corasick::AhoCorasick;
use unidok_repr::ast::segments::{CodeAst, SegmentAst};

use crate::parsing_mode::ParsingMode;
use crate::utils::{ParseLineBreak, ParseLineEnd, While};
use crate::{Context, Indents, Input, Parse};

use super::segments::{strip_space_end, strip_space_start};
use super::Segments;

pub(crate) struct ParseCode<'a> {
    pub ind: Indents<'a>,
    pub mode: Option<ParsingMode>,
    pub ac: &'a AhoCorasick,
}

impl Parse for ParseCode<'_> {
    type Output = CodeAst;

    fn parse(&mut self, input: &mut Input) -> Option<Self::Output> {
        let mut input = input.start();

        input.parse('`')?;
        let len = (1 + input.parse_i(While('`')).len()).try_into().ok()?;

        let mode = self.mode.unwrap_or_else(ParsingMode::new_nothing);

        let mut segments = if mode.is_nothing() {
            let mut segments = Vec::new();

            loop {
                let i = input.rest().find(find_special)?;
                if i > 0 {
                    segments.push(SegmentAst::Text(input.bump(i)));
                }

                match input.peek_char().unwrap() {
                    '`' => {
                        if input.parse(ParseCodeEndDelimiter { len }).is_some() {
                            break;
                        } else {
                            let backticks = input.parse_i(While('`'));
                            segments.push(SegmentAst::Text(backticks));
                        }
                    }
                    '\n' | '\r' => {
                        input.parse(ParseLineBreak(self.ind))?;
                        segments.push(SegmentAst::Text2(" "));
                        if input.can_parse(ParseLineEnd) {
                            return None;
                        }
                    }
                    c => unreachable!("{:?} was not expected", c),
                }
            }

            segments
        } else {
            let parser = Segments::parser(self.ind, Context::Code(len), mode, self.ac);
            input.parse(parser)?.into_segments_no_underline()?
        };

        if let Some(s) = segments.first_mut() {
            strip_space_start(s, &input);
        }
        if let Some(s) = segments.last_mut() {
            strip_space_end(s, &input);
        }

        input.apply();
        Some(CodeAst { segments })
    }
}

fn find_special(c: char) -> bool {
    matches!(c, '`' | '\n' | '\r')
}

struct ParseCodeEndDelimiter {
    len: u8,
}

impl Parse for ParseCodeEndDelimiter {
    type Output = ();

    fn parse(&mut self, input: &mut Input) -> Option<Self::Output> {
        let mut input = input.start();

        let backticks = input.parse_i(While('`')).len();
        if backticks != self.len as usize {
            return None;
        }

        input.apply();
        Some(())
    }
}