use std::vec;
use proc_macro2::TokenStream;
use proc_macro2_diagnostics::Diagnostic;
use syn::{parse::ParseStream, spanned::Spanned, Result};
pub mod recoverable;
use self::recoverable::{ParseRecoverable, ParsingResult, RecoverableContext};
use crate::{node::*, ParserConfig};
pub struct Parser {
config: ParserConfig,
}
impl Parser {
pub fn new(config: ParserConfig) -> Parser {
Parser { config }
}
pub fn parse_simple(&self, v: impl Into<TokenStream>) -> Result<Vec<Node>> {
self.parse_recoverable(v).into_result()
}
pub fn parse_recoverable(&self, v: impl Into<TokenStream>) -> ParsingResult<Vec<Node>> {
use syn::parse::Parser as _;
let parser = move |input: ParseStream| Ok(self.parse_syn_stream(input));
let res = parser.parse2(v.into());
res.expect("No errors from parser")
}
pub fn parse_syn_stream(&self, input: ParseStream) -> ParsingResult<Vec<Node>> {
let mut nodes = vec![];
let mut top_level_nodes = 0;
let mut parser = RecoverableContext::new(self.config.clone().into());
while !input.cursor().eof() {
let Some(parsed_node) = Node::parse_recoverable(&mut parser, input) else {
parser.push_diagnostic(input.error("Node parse failed".to_string()));
break;
};
if let Some(type_of_top_level_nodes) = &self.config.type_of_top_level_nodes {
if &parsed_node.r#type() != type_of_top_level_nodes {
parser.push_diagnostic(input.error(format!(
"top level nodes need to be of type {}",
type_of_top_level_nodes
)));
break;
}
}
top_level_nodes += 1;
nodes.push(parsed_node)
}
if !input.is_empty() {
let tts = input
.parse::<TokenStream>()
.expect("No error in parsing token stream");
parser.push_diagnostic(Diagnostic::spanned(
tts.span(),
proc_macro2_diagnostics::Level::Error,
"Tokens was skipped after incorrect parsing",
));
}
if let Some(number_of_top_level_nodes) = &self.config.number_of_top_level_nodes {
if &top_level_nodes != number_of_top_level_nodes {
parser.push_diagnostic(input.error(format!(
"saw {} top level nodes but exactly {} are required",
top_level_nodes, number_of_top_level_nodes
)))
}
}
let nodes = if self.config.flat_tree {
nodes.into_iter().flat_map(Node::flatten).collect()
} else {
nodes
};
let errors = parser.diagnostics;
let nodes = if nodes.is_empty() { None } else { Some(nodes) };
ParsingResult::from_parts(nodes, errors)
}
}