use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt};
use syn::Ident;
use super::parser::ParseTree;
impl ToTokens for ParseTree {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(match self {
ParseTree::DefinitionList(definitions) => {
quote! {
{
struct PEGParser {}
impl PEGParser {
#(
#definitions
)*
}
PEGParser {}
}
}
}
ParseTree::ParserDefinition(name, return_type, expr) => {
let return_type = match return_type {
Some(return_type) => quote! { #return_type },
None => quote! { &'input str },
};
quote! {
fn #name<'input>(&self, input: &'input str) -> ::nom::IResult<&'input str, #return_type> {
do_parse!(input, __ret: #expr >> (__ret))
}
}
}
ParseTree::Capture(term, _ident) => {
quote! {
#term
}
}
ParseTree::NonTerminal(ident) => {
quote! {
call!(|input| self.#ident(input))
}
}
ParseTree::Call(func) => {
quote! {
call!(|input| #func(input))
}
}
ParseTree::Sequence(seq, block) => {
let capture_map: Vec<(bool, &Option<Ident>)> = seq.iter()
.map(|expr| {
match expr {
ParseTree::Capture(_, ident) => (true, ident),
_ => (false, &None),
}
})
.collect();
let block_prelude = if !capture_map.iter().any(|x| x.0) {
quote! { let mut result = __result; }
} else {
let (indices, idents): (Vec<usize>, Vec<&Ident>) = capture_map.iter()
.enumerate()
.filter_map(|x| {
match x {
(index, (_, Some(ident))) => Some((index, ident)),
(_index, (_, &None)) => None,
}
})
.unzip();
let anon_indices: Vec<usize> = capture_map.iter()
.enumerate()
.filter_map(|x| {
match x {
(index, (true, None)) => Some(index),
(_index, (_, _)) => None,
}
})
.collect();
quote! {
let mut result = ( #( __result.#anon_indices ),* );
#(
let mut #idents = __result.#indices;
)*
}
};
let block = match block {
Some(block) => quote! { #block },
None => quote! { result },
};
quote! {
do_parse!(__result: tuple!(#(#seq),*) >> ( { #block_prelude #block } ))
}
}
ParseTree::Empty => quote! {
take!(0)
},
ParseTree::Terminal(term) => {
quote! {
tag!(#term)
}
}
ParseTree::Choice(choices) => {
quote! {
alt!(#(#choices)|*)
}
}
ParseTree::Many0(term) => {
quote! {
many0!(#term)
}
}
ParseTree::Many1(term) => {
quote! {
many1!(#term)
}
}
ParseTree::Optional(term) => {
quote! {
opt!(#term)
}
}
ParseTree::Peek(term) => {
quote! {
peek!(#term)
}
}
ParseTree::Not(term) => {
quote! {
not!(#term)
}
}
});
}
}