use crate::mplg::parse_mplg;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use syn::{parse2, Attribute, DeriveInput, Expr, ExprLit, Lit, Meta};
pub use self::cascade::{
analyze_char_classes, analyze_first_byte_dispatch, is_left_recursive, is_tail_loop,
DispatchCascade,
};
pub use self::cut::{classify_rules, compute_first_sets};
pub use self::fast::generate_fast;
pub use self::parser::generate_parser;
pub use self::rules::generate_rules;
pub use self::variable::generate_variable;
mod cascade;
mod cut;
mod fast;
mod parser;
mod rules;
mod variable;
enum GrammarData {
Mplg(Vec<u8>),
None,
}
pub fn derive_parser(input: TokenStream) -> TokenStream {
let input: DeriveInput = match parse2(input) {
Ok(input) => input,
Err(e) => return e.to_compile_error(),
};
let parser_ident = input.ident;
let ident = parser_ident.to_string().replace("Parser", "");
let rules_ident = format_ident!("{}Rules", ident);
let variable_ident = format_ident!("{}Variable", ident);
let grammar_data = match input.attrs.as_slice() {
[] => Ok(GrammarData::None),
[attr] => get_grammar_data(attr),
_ => Err(syn::Error::new_spanned(
&parser_ident,
"expected at most one `#[mplg = \"...\"]` attribute",
)
.to_compile_error()),
};
match grammar_data {
Ok(GrammarData::Mplg(data)) => {
let lines = parse_mplg(&data)
.unwrap()
.into_original()
.expect("Lines")
.into_lines();
let variable = generate_variable(&variable_ident, &lines);
let rules = generate_rules(&rules_ident, &variable_ident, &lines);
let parser = generate_parser(&parser_ident, &rules_ident, &variable_ident);
quote! {
#variable
#rules
#parser
}
}
Ok(GrammarData::None) => TokenStream::new(),
Err(e) => e,
}
}
pub fn derive_fast_parser(input: TokenStream) -> TokenStream {
let input: DeriveInput = match parse2(input) {
Ok(input) => input,
Err(e) => return e.to_compile_error(),
};
let parser_ident = input.ident;
let grammar_data = match input.attrs.as_slice() {
[] => Ok(GrammarData::None),
[attr] => get_grammar_data(attr),
_ => Err(syn::Error::new_spanned(
&parser_ident,
"expected at most one `#[mplg = \"...\"]` attribute",
)
.to_compile_error()),
};
match grammar_data {
Ok(GrammarData::Mplg(data)) => {
let lines = parse_mplg(&data)
.unwrap()
.into_original()
.expect("Lines")
.into_lines();
generate_fast(&parser_ident, &lines)
}
Ok(GrammarData::None) => TokenStream::new(),
Err(e) => e,
}
}
fn get_grammar_data(attr: &Attribute) -> Result<GrammarData, TokenStream> {
let err = || syn::Error::new_spanned(attr, "expected `#[mplg = \"...\"]`").to_compile_error();
let Meta::NameValue(name_value) = &attr.meta else {
return Err(err());
};
if !name_value.path.is_ident("mplg") {
return Err(err());
}
let Expr::Lit(ExprLit {
lit: Lit::Str(lit_str),
..
}) = &name_value.value
else {
return Err(err());
};
let path = lit_str.value();
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
let full_path = Path::new(&root).join(&path);
match read_file(&full_path) {
Ok(data) => Ok(GrammarData::Mplg(data)),
Err(e) => panic!("{} ({:#?})", e, full_path),
}
}
fn read_file<P: AsRef<Path>>(path: P) -> std::io::Result<Vec<u8>> {
let mut file = File::open(path)?;
let mut v = Vec::new();
file.read_to_end(&mut v)?;
Ok(v)
}