pub mod symbol;
use logid::capturing::MappedLogId;
use symbol::Symbol;
use crate::{
config::Config,
document::Document,
elements::{
atomic::{Heading, Paragraph},
enclosed::Verbatim,
Blocks,
},
metadata::{Metadata, MetadataKind},
security,
};
use self::symbol::{IntoSymbols, SymbolKind};
pub type ParserFn = for<'i> fn(&'i [Symbol<'i>]) -> Option<(Blocks, &'i [Symbol<'i>])>;
pub(crate) struct TokenizeOutput<'a, T>
where
T: 'a,
{
pub(crate) tokens: Vec<T>,
pub(crate) rest_of_input: &'a [Symbol<'a>],
}
pub(crate) trait ElementParser {
type Token<'a>;
fn tokenize<'i>(input: &'i [Symbol<'i>]) -> Option<TokenizeOutput<'i, Self::Token<'i>>>;
fn parse(input: Vec<Self::Token<'_>>) -> Option<Blocks>;
}
mod private {
pub trait Sealed {}
impl<'a, T> Sealed for T where T: super::ElementParser + 'a + 'static {}
}
pub trait ParserGenerator: private::Sealed {
fn generate_parser() -> ParserFn;
}
impl<'a, T> ParserGenerator for T
where
T: ElementParser + 'a + 'static,
{
fn generate_parser() -> ParserFn {
|input| {
let tokenize_output = T::tokenize(input)?;
let blocks = T::parse(tokenize_output.tokens)?;
Some((blocks, tokenize_output.rest_of_input))
}
}
}
#[derive(Clone)]
pub struct MainParser {
parsers: Vec<ParserFn>,
default_parser: ParserFn,
}
impl Default for MainParser {
fn default() -> Self {
tracing::info!("Initializing MainParser");
let default = Paragraph::generate_parser();
let mut parser = Self {
parsers: Vec::with_capacity(2),
default_parser: default,
};
parser.register_parser(Heading::generate_parser());
parser.register_parser(Verbatim::generate_parser());
tracing::info!("MainParser initialized");
parser
}
}
impl MainParser {
fn register_parser(&mut self, parser: ParserFn) {
self.parsers.push(parser);
}
pub fn parse<'s>(&self, input: impl IntoSymbols<'s, &'s [Symbol<'s>]>) -> Blocks {
let mut input = input.into_symbols();
let mut blocks = Vec::default();
#[cfg(debug_assertions)]
let mut input_len = input.len();
'outer: while let Some(sym) = input.first() {
match sym.kind {
SymbolKind::Blankline => input = &input[1..],
SymbolKind::EOI => break,
_ if sym.is_not_keyword() => {
let (mut res_blocks, rest_of_input) = (self.default_parser)(input)
.expect("Default parser could not parse content!");
blocks.append(&mut res_blocks);
input = rest_of_input;
}
_ => {
for parser_fn in &self.parsers {
if let Some((mut res_blocks, rest_of_input)) = parser_fn(input) {
blocks.append(&mut res_blocks);
input = rest_of_input;
continue 'outer; }
}
let (mut res_blocks, rest_of_input) = (self.default_parser)(input)
.expect("Default parser could not parse content!");
blocks.append(&mut res_blocks);
input = rest_of_input;
}
}
#[cfg(debug_assertions)]
{
assert_ne!(input.len(), input_len);
input_len = input.len();
}
}
blocks
}
}
pub fn parse_unimarkup(um_content: &str, config: &mut Config) -> Result<Document, MappedLogId> {
let parser = MainParser::default();
let symbols = um_content.into_symbols();
let blocks = parser.parse(&symbols);
let mut unimarkup = Document {
config: config.clone(),
blocks,
..Default::default()
};
let metadata = Metadata {
file: config.um_file.clone(),
contenthash: security::get_contenthash(um_content),
preamble: String::new(),
kind: MetadataKind::Root,
namespace: ".".to_string(),
};
unimarkup.metadata.push(metadata);
Ok(unimarkup)
}