use chumsky::{
IterParser, Parser,
combinator::{IgnoreThen, Not, Rewind},
container::Seq,
error::{RichReason, Simple},
extra::{self, Full},
input::{Input, Stream},
primitive::{Any, Choice, any, choice, empty, end, just, none_of, one_of, todo},
recursive::{Recursive, recursive},
regex::regex,
span::SimpleSpan,
text::{ascii::ident, inline_whitespace, newline, whitespace},
};
use crate::{
ast::{Content, FmlFunction, FmlValue, Section, Token, TokenVec},
llp,
parser::FML_TOKENS,
to_both,
};
pub fn esc<'a>() -> impl Parser<'a, &'a str, char, extra::Err<Simple<'a, char>>> + Clone + Copy {
just('\\').ignore_then(one_of(FML_TOKENS))
}
pub fn esc_np<'a>() -> impl Parser<'a, &'a str, char, extra::Err<Simple<'a, char>>> + Clone + Copy {
just('\\').ignore_then(one_of(FML_TOKENS).rewind())
}
pub fn esc_nl<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Simple<'a, char>>> + Clone + Copy {
just('\\').ignore_then(newline())
}
pub fn esc_nl_np<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Simple<'a, char>>> + Clone + Copy
{
just('\\').ignore_then(newline().rewind())
}
pub fn sol<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Simple<'a, char>>> + Clone {
choice((
regex(r"^").ignored(),
empty().delimited_by(newline(), empty()),
))
}
pub fn nl<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Simple<'a, char>>> + Clone {
choice((newline(), esc_nl()))
}
pub fn parser<'a>() -> impl Parser<'a, &'a str, Content, extra::Err<Simple<'a, char>>> {
let section_sep = choice((
inline_whitespace()
.ignore_then(newline())
.repeated()
.at_least(2),
whitespace().ignore_then(end()),
));
let section = any()
.and_is(section_sep.not())
.repeated()
.at_least(1)
.to_slice();
let section = fml_values()
.try_map(|x, _| {
TokenVec(x)
.try_into()
.map_err(|_| Simple::new(None, SimpleSpan::default()))
})
.nested_in(section)
.map(Section);
section.padded().repeated().collect().map(Content)
}
pub fn delimiteds<'a>() -> impl Parser<'a, &'a str, Token, extra::Err<Simple<'a, char>>> + Clone {
recursive(|delims| {
let inner = choice((
nl().to(Token::Newline),
delims,
headings(),
nestables(),
plain(),
))
.repeated()
.at_least(1)
.collect::<Vec<Token>>();
let inner = || inner.clone();
let spoiler = inner().nested_in(delimited_pat!(just("|")));
let bold = inner().nested_in(delimited_pat!(just("*")));
let underlined = inner().nested_in(delimited_pat!(just("__")));
let italic = inner().nested_in(delimited_pat!(just("_")));
let strikethrough = inner().nested_in(delimited_pat!(just("~")));
choice((
spoiler.map(Token::CwSpoiler),
bold.map(Token::Bold),
underlined.map(Token::Underline),
italic.map(Token::Italic),
strikethrough.map(Token::Strikethrough),
))
})
}
pub fn headings<'a>() -> impl Parser<'a, &'a str, Token, extra::Err<Simple<'a, char>>> + Clone {
let inner = choice((
nl().to(Token::Newline),
delimiteds(),
nestables(),
plain_no_headings(),
))
.repeated()
.at_least(1)
.collect::<Vec<Token>>();
let inner = || inner.clone();
let head_1 = inner().nested_in(llp!(just('#')));
let head_2 = inner().nested_in(llp!(just("##")));
let head_3 = inner().nested_in(llp!(just("###")));
let sup_line = inner().nested_in(llp!(just("#^")));
let sub_line = inner().nested_in(llp!(just("#!"), just("#_")));
choice((
head_1.map(Token::Heading1),
head_2.map(Token::Heading2),
head_3.map(Token::Heading3),
sup_line.map(Token::SupLine),
sub_line.map(Token::SubLine),
))
}
pub fn nestables<'a>() -> impl Parser<'a, &'a str, Token, extra::Err<Simple<'a, char>>> + Clone {
let quo_start = just('>').ignore_then(inline_whitespace().at_least(1));
let quote = sol().ignore_then(quo_start).ignore_then(
choice((
just('\\').ignore_then(any().and_is(newline())),
any().and_is(newline()).then_ignore(quo_start),
any().and_is(newline().not()),
))
.repeated()
.at_least(1)
.collect::<String>()
.map(Token::Quote),
);
recursive(|nestable| {
let list_inner = choice((
nl().to(Token::Newline),
delimiteds(),
headings(),
nestable,
plain(),
))
.repeated()
.at_least(1)
.collect::<Vec<Token>>()
.map(Token::ListItem);
choice((quote, list_inner.nested_in(llp!(just('-')))))
})
}
pub fn plain_no_headings<'a>()
-> impl Parser<'a, &'a str, Token, extra::Err<Simple<'a, char>>> + Clone {
let non_style =
any().and_is(choice((nl().to(Token::Newline), delimiteds(), nestables())).not());
choice((esc(), any()))
.then(non_style.repeated().collect::<String>())
.map(|(ch, mut s): (char, String)| {
s.insert(0, ch); Token::Text(s)
})
}
pub fn plain<'a>() -> impl Parser<'a, &'a str, Token, extra::Err<Simple<'a, char>>> + Clone {
let non_style = any().and_is(
choice((
nl().to(Token::Newline),
delimiteds(),
headings(),
nestables(),
))
.not(),
);
choice((esc(), any()))
.then(non_style.repeated().collect::<String>())
.map(|(ch, mut s): (char, String)| {
s.insert(0, ch); Token::Text(s)
})
}
pub fn fml_value<'a>() -> impl Parser<'a, &'a str, Token, extra::Err<Simple<'a, char>>> + Clone {
choice((
nl().to(Token::Newline),
delimiteds(),
headings(),
nestables(),
plain(),
))
}
pub fn fml_values<'a>() -> impl Parser<'a, &'a str, Vec<Token>, extra::Err<Simple<'a, char>>> + Clone
{
fml_value().repeated().at_least(1).collect()
}