use crate::{Token, tokens_of};
use chumsky::{
extra::{Err, ParserExtra},
input::{BorrowInput, ValueInput},
prelude::*,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenTree {
Lone(Token),
Parens(Vec<(TokenTree, SimpleSpan)>),
Brackets(Vec<(TokenTree, SimpleSpan)>),
Braces(Vec<(TokenTree, SimpleSpan)>),
TreeError,
}
pub fn trees_of(
source: &str,
) -> (Vec<(TokenTree, SimpleSpan)>, Vec<Rich<'static, Token>>) {
let tokens: Vec<(Token, SimpleSpan)> = tokens_of(source);
let eoi: SimpleSpan = match tokens.last() {
Some(s) => s.1,
None => return (Vec::new(), Vec::new()),
};
let recovery = via_parser(
any()
.repeated()
.at_least(1)
.to(TokenTree::TreeError)
.map_with(|tree, ex| (tree, ex.span())),
);
let tree_parser = recursive(|tokens| {
let braces = tokens
.clone()
.repeated()
.collect()
.delimited_by(open_brace_p(), close_brace_p())
.map_with(|out, ex| (TokenTree::Braces(out), ex.span()));
let brackets = tokens
.clone()
.repeated()
.collect()
.delimited_by(open_bracket_p(), close_bracket_p())
.map_with(|out, ex| (TokenTree::Brackets(out), ex.span()));
let parens = tokens
.clone()
.repeated()
.collect()
.delimited_by(open_paren_p(), close_paren_p())
.map_with(|out, ex| (TokenTree::Parens(out), ex.span()));
let lone =
non_tree_token_p().map_with(|out, ex| (TokenTree::Lone(out), ex.span()));
assert_output::<(TokenTree, SimpleSpan), _, _, _>(&lone);
let comment = {
let single_comment = single_line_comment_p();
let block_comment = tokens
.clone()
.repeated()
.delimited_by(open_comment_p(), close_comment_p())
.ignored();
single_comment.or(block_comment)
};
let x =
choice((brackets, braces, parens, lone)).padded_by(comment.repeated());
x
})
.recover_with(recovery)
.repeated()
.collect::<Vec<_>>();
let (opt_out, errors) = tree_parser
.parse(Input::map(&tokens[..], eoi, |(tk, span)| (tk, span)))
.into_output_errors();
let out = opt_out.unwrap_or_default();
let errors = errors.into_iter().map(|error| error.into_owned()).collect();
(out, errors)
}
fn assert_output<'src, O, P, I, E>(_: &P)
where
P: Parser<'src, I, O, E>,
I: Input<'src>,
E: ParserExtra<'src, I>,
{
}
fn open_brace_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::OpBrace => (),
}
.labelled("open_brace")
.as_context()
}
fn close_brace_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::ClBrace => (),
}
.labelled("close_brace")
.as_context()
}
fn open_bracket_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::OpBracket => (),
}
.labelled("open_bracket")
.as_context()
}
fn close_bracket_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::ClBracket => (),
}
.labelled("close_bracket")
.as_context()
}
fn open_paren_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::OpParen => (),
}
.labelled("open_paren")
.as_context()
}
fn close_paren_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::ClParen => (),
}
.labelled("close_paren")
.as_context()
}
fn open_comment_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::OpBlockComment => (),
}
.labelled("open_comment")
.as_context()
}
fn close_comment_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::ClBlockComment => (),
}
.labelled("close_comment")
.as_context()
}
fn non_tree_token_p<'src, I>()
-> impl Parser<'src, I, Token, Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
none_of([
Token::OpBracket,
Token::ClBracket,
Token::OpBrace,
Token::ClBrace,
Token::OpParen,
Token::ClParen,
Token::OpBlockComment,
Token::ClBlockComment,
])
.labelled("non_tree_token")
.as_context()
}
fn single_line_comment_p<'src, I>()
-> impl Parser<'src, I, (), Err<Rich<'src, Token, SimpleSpan>>> + Clone
where
I: BorrowInput<'src, Token = Token, Span = SimpleSpan> + ValueInput<'src>,
{
select! {
Token::LineComment => (),
}
.labelled("single_comment")
.as_context()
}