fml 0.1.0

Friendly Markup Language
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,
};

// Trying my darndest to improve compile times by throwing in type declarations anywhere sensible

/// Progressing escape, consuming both the backslash and the token after it.
pub fn esc<'a>() -> impl Parser<'a, &'a str, char, extra::Err<Simple<'a, char>>> + Clone + Copy {
	just('\\').ignore_then(one_of(FML_TOKENS))
}

/// Nonprogressing escape, used for consuming the backslash and keeping the cursor at the char after it.
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())
}

/// Progressing escape of one newline token.
pub fn esc_nl<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Simple<'a, char>>> + Clone + Copy {
	just('\\').ignore_then(newline())
}

/// Nonprogressing escape of one newline token,
/// consumes the backslash and keeps the cursor at the newline token.
pub fn esc_nl_np<'a>() -> impl Parser<'a, &'a str, (), extra::Err<Simple<'a, char>>> + Clone + Copy
{
	just('\\').ignore_then(newline().rewind())
}

/// Detects Start-of-line caused via either the beginning of the file or newline tokens.
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 lexer<'a>() -> impl Parser<'a, &'a str, Content, extra::Err<Simple<'a, char>>> {}

// pub fn parse<'a>(s: &'a str) -> Result<Content, ()> {

// 	todo!()
// }

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()))
		})
		// .map(|x| TokenVec(x).try_into())
		.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));
	// Quotes must be postprocessed
	let quote = sol().ignore_then(quo_start).ignore_then(
		// Now we're actually in the quote and gotta ensure it repeats on
		// "\n>\s" and r"\\n" and breaks if its just "\n"
		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); // append first parsed char to str
			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); // append first parsed char to str
			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()
}

// pub fn fml_values<'a>()
// -> Recursive<dyn Parser<'a, &'a str, Vec<FmlValue>, Full<Simple<'a, char>, (), ()>> + 'a> {
// 	recursive(|fml| {
// 		// for readability
// 		let fml = || fml.clone();
// 		let nl = choice((newline(), esc_nl()));

// 		let head1_pat = llp!(just('#'));
// 		let head2_pat = llp!(just("##"));
// 		let head3_pat = llp!(just("###"));
// 		let sup_line_pat = llp!(just("#^"));
// 		let sub_line_pat = llp!(just("#!"), just("#_"));
// 		let list_pat = llp!(just('-'));

// 		let head1 = fml().nested_in(head1_pat.clone());
// 		let head2 = fml().nested_in(head2_pat.clone());
// 		let head3 = fml().nested_in(head3_pat.clone());
// 		let sup_line = fml().nested_in(sup_line_pat.clone());
// 		let sub_line = fml().nested_in(sub_line_pat.clone());
// 		let list = fml().nested_in(list_pat.clone());

// 		let quote_pat = llp!(just('>'));
// 		let quote = fml().nested_in((quote_pat).clone());

// 		let func_name_pat = just('#').ignore_then(ident()).to_slice();
// 		let func_name = just('#').ignore_then(ident()).map(ToString::to_string);

// 		let func_params_pat = delimited_pat!(just("<"), just(">"));
// 		let func_params = func_params_pat.map(ToString::to_string);

// 		let func_body_pat = recursive(|fbp| {
// 			choice((
// 				none_of(r"\{}").repeated().at_least(1).to_slice(),
// 				just('\\').ignore_then(one_of("{}")).to_slice(),
// 				fbp,
// 			))
// 			.repeated()
// 			.at_least(1)
// 			.to_slice()
// 			.delimited_by(just("{").padded(), whitespace().then(just("}")))
// 		});

// 		let func_body = fml().padded().nested_in(func_body_pat.clone());

// 		let func_pat = func_name_pat
// 			.then(func_params_pat.or_not())
// 			.then(func_body_pat)
// 			.to_slice();
// 		let func = func_name
// 			.then(func_params.or_not())
// 			.then(func_body)
// 			.nested_in(func_pat.clone());

// 		let func_short_body_pat = recursive(|fbp| {
// 			choice((
// 				none_of(r"\{}").repeated().at_least(1).to_slice(),
// 				just('\\').ignore_then(one_of("{}")).to_slice(),
// 				fbp,
// 			))
// 			.repeated()
// 			.at_least(1)
// 			.to_slice()
// 			.delimited_by(just("{"), just("}"))
// 		});

// 		let super_fn_pat = just('^').ignore_then(func_short_body_pat.clone());
// 		let super_fn = fml().nested_in(super_fn_pat.clone());

// 		let sub_fn_pat = just('_').ignore_then(func_short_body_pat.clone());
// 		let sub_fn = fml().nested_in(sub_fn_pat.clone());

// 		let spoiler_pat = delimited_pat!(just("|"));
// 		let spoiler = fml().nested_in(spoiler_pat);

// 		let bold_pat = delimited_pat!(just("*"));
// 		let bold = fml().nested_in(bold_pat);

// 		let underlined_pat = delimited_pat!(just("__"));
// 		let underlined = fml().nested_in(underlined_pat);

// 		let italic_pat = delimited_pat!(just("_"));
// 		let italic = fml().nested_in(italic_pat);

// 		let strikethrough_pat = delimited_pat!(just("~"));
// 		let strikethrough = fml().nested_in(strikethrough_pat);

// 		let styles = choice((
// 			// code_block_pat.ignored(),
// 			// inl_code_pat.ignored(),
// 			head1_pat.ignored(),
// 			head2_pat.ignored(),
// 			head3_pat.ignored(),
// 			sup_line_pat.ignored(),
// 			sub_line_pat.ignored(),
// 			list_pat.ignored(),
// 			quote_pat.ignored(),
// 			func_pat.ignored(),
// 			super_fn_pat.ignored(),
// 			sub_fn_pat.ignored(),
// 			spoiler_pat.ignored(),
// 			bold_pat.ignored(),
// 			underlined_pat.ignored(),
// 			italic_pat.ignored(),
// 			strikethrough_pat.ignored(),
// 			nl,
// 		));
// 		let non_style = choice((esc_np().ignored(), styles.not().rewind())).ignore_then(any());
// 		let plain = choice((esc(), any()))
// 			.then(non_style.repeated().collect::<String>())
// 			.map(|(ch, mut s): (char, String)| {
// 				s.insert(0, ch);
// 				s
// 			});

// 		choice((
// 			// code_block.map(|(lang, body)| FmlValue::CodeBlock { lang, body }),
// 			// inl_code.map(FmlValue::InlineCode),
// 			head1.map(FmlValue::Heading1),
// 			head2.map(FmlValue::Heading2),
// 			head3.map(FmlValue::Heading3),
// 			sup_line.map(FmlValue::Superline),
// 			sub_line.map(FmlValue::Subline),
// 			list.map(FmlValue::ListItem),
// 			quote.map(FmlValue::Quote),
// 			func.map(
// 				|((name, params), body): ((String, Option<String>), Vec<FmlValue>)| {
// 					FmlFunction { name, params, body }.interpret()
// 				},
// 			),
// 			super_fn.map(FmlValue::Superscript),
// 			sub_fn.map(FmlValue::Subscript),
// 			spoiler.map(|body| FmlValue::ContentWarning {
// 				reason: "Spoilers".to_string(),
// 				body,
// 			}),
// 			bold.map(FmlValue::Bold),
// 			underlined.map(FmlValue::Underline),
// 			italic.map(FmlValue::Italic),
// 			strikethrough.map(FmlValue::Strikethrough),
// 			nl.to(FmlValue::Newline),
// 			plain.map(FmlValue::Text),
// 		))
// 		.repeated()
// 		.at_least(1)
// 		.collect::<Vec<FmlValue>>()
// 	})
// }