358 lines
9.4 KiB
Rust
358 lines
9.4 KiB
Rust
#![recursion_limit = "128"]
|
|
|
|
extern crate proc_macro;
|
|
|
|
use crate::proc_macro::TokenStream;
|
|
use quote::quote;
|
|
use std::str::FromStr;
|
|
use syn::Data::{Enum, Struct};
|
|
use syn::{
|
|
self, parse_macro_input, AttributeArgs, DeriveInput, Expr, ItemFn, Meta, NestedMeta, Stmt,
|
|
};
|
|
|
|
#[proc_macro_derive(Node)]
|
|
pub fn node_derive(input: TokenStream) -> TokenStream {
|
|
let ast = syn::parse(input).unwrap();
|
|
impl_node(&ast)
|
|
}
|
|
|
|
fn impl_node(ast: &DeriveInput) -> TokenStream {
|
|
let name = &ast.ident;
|
|
|
|
let next = match ast.data {
|
|
Enum(ref data) => {
|
|
let mut items = quote! {};
|
|
for v in &data.variants {
|
|
let ident = &v.ident;
|
|
let item = quote! {
|
|
#name::#ident(x) => { x.into() },
|
|
};
|
|
items = quote! {
|
|
#items
|
|
#item
|
|
};
|
|
}
|
|
|
|
quote! {
|
|
match self {
|
|
#items
|
|
}
|
|
}
|
|
}
|
|
Struct(_) => {
|
|
quote! {
|
|
(&(self.nodes)).into()
|
|
}
|
|
}
|
|
_ => {
|
|
quote! {
|
|
vec![].into()
|
|
}
|
|
}
|
|
};
|
|
|
|
let gen = quote! {
|
|
impl<'a> Node<'a> for #name {
|
|
fn next(&'a self) -> RefNodes<'a> {
|
|
#next
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a #name> for RefNodes<'a> {
|
|
fn from(x: &'a #name) -> Self {
|
|
vec![RefNode::#name(x)].into()
|
|
}
|
|
}
|
|
|
|
impl From<#name> for AnyNode {
|
|
fn from(x: #name) -> Self {
|
|
AnyNode::#name(x)
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a #name {
|
|
type Item = RefNode<'a>;
|
|
type IntoIter = Iter<'a>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
let nodes: RefNodes = self.into();
|
|
Iter { next: nodes }
|
|
}
|
|
}
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
#[proc_macro_derive(AnyNode)]
|
|
pub fn any_node_derive(input: TokenStream) -> TokenStream {
|
|
let ast = syn::parse(input).unwrap();
|
|
impl_any_node(&ast)
|
|
}
|
|
|
|
fn impl_any_node(ast: &DeriveInput) -> TokenStream {
|
|
let ref data = match ast.data {
|
|
Enum(ref data) => data,
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let mut items = quote! {};
|
|
for v in &data.variants {
|
|
let ident = &v.ident;
|
|
let item = quote! {
|
|
impl TryFrom<AnyNode> for #ident {
|
|
type Error = ();
|
|
fn try_from(x: AnyNode) -> Result<Self, Self::Error> {
|
|
match x {
|
|
AnyNode::#ident(x) => Ok(x),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
};
|
|
items = quote! {
|
|
#items
|
|
#item
|
|
};
|
|
}
|
|
|
|
let gen = quote! {
|
|
#items
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
#[proc_macro_derive(RefNode)]
|
|
pub fn ref_node_derive(input: TokenStream) -> TokenStream {
|
|
let ast = syn::parse(input).unwrap();
|
|
impl_ref_node(&ast)
|
|
}
|
|
|
|
fn impl_ref_node(ast: &DeriveInput) -> TokenStream {
|
|
let ref data = match ast.data {
|
|
Enum(ref data) => data,
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let mut items = quote! {};
|
|
for v in &data.variants {
|
|
let ident = &v.ident;
|
|
let item = quote! {
|
|
RefNode::#ident(x) => x.next(),
|
|
};
|
|
items = quote! {
|
|
#items
|
|
#item
|
|
};
|
|
}
|
|
|
|
let name = &ast.ident;
|
|
let gen = quote! {
|
|
impl<'a> #name<'a> {
|
|
fn next(&self) -> RefNodes<'a> {
|
|
match self {
|
|
#items
|
|
}
|
|
}
|
|
}
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
#[proc_macro_attribute]
|
|
pub fn parser(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
let attr = parse_macro_input!(attr as AttributeArgs);
|
|
let item = parse_macro_input!(item as ItemFn);
|
|
impl_parser(&attr, &item)
|
|
}
|
|
|
|
fn impl_parser(attr: &AttributeArgs, item: &ItemFn) -> TokenStream {
|
|
let (maybe_recursive, ambiguous) = impl_parser_attribute(attr);
|
|
|
|
let trace = impl_parser_trace(&item);
|
|
let trace = parse_macro_input!(trace as Stmt);
|
|
|
|
let check_recursive_flag = impl_parser_check_recursive_flag(&item);
|
|
let check_recursive_flag = parse_macro_input!(check_recursive_flag as Stmt);
|
|
|
|
let set_recursive_flag = impl_parser_set_recursive_flag(&item);
|
|
let set_recursive_flag = parse_macro_input!(set_recursive_flag as Stmt);
|
|
|
|
let body = if ambiguous {
|
|
impl_parser_body_ambiguous(&item)
|
|
} else {
|
|
impl_parser_body(&item)
|
|
};
|
|
let body = parse_macro_input!(body as Stmt);
|
|
|
|
let body_unwrap = impl_parser_body_unwrap(&item);
|
|
let body_unwrap = parse_macro_input!(body_unwrap as Stmt);
|
|
|
|
let clear_recursive_flags = impl_parser_clear_recursive_flags(&item);
|
|
let clear_recursive_flags = parse_macro_input!(clear_recursive_flags as Expr);
|
|
let clear_recursive_flags = Stmt::Expr(clear_recursive_flags);
|
|
|
|
let mut item = item.clone();
|
|
|
|
item.block.stmts.clear();
|
|
item.block.stmts.push(trace);
|
|
if maybe_recursive {
|
|
item.block.stmts.push(check_recursive_flag);
|
|
item.block.stmts.push(set_recursive_flag);
|
|
}
|
|
item.block.stmts.push(body);
|
|
item.block.stmts.push(body_unwrap);
|
|
item.block.stmts.push(clear_recursive_flags);
|
|
|
|
let gen = quote! {
|
|
#item
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_attribute(attr: &AttributeArgs) -> (bool, bool) {
|
|
let mut maybe_recursive = false;
|
|
let mut ambiguous = false;
|
|
|
|
for a in attr {
|
|
match a {
|
|
NestedMeta::Meta(Meta::Word(x)) if x == "MaybeRecursive" => maybe_recursive = true,
|
|
NestedMeta::Meta(Meta::Word(x)) if x == "Ambiguous" => ambiguous = true,
|
|
_ => panic!(),
|
|
}
|
|
}
|
|
|
|
(maybe_recursive, ambiguous)
|
|
}
|
|
|
|
fn impl_parser_trace(item: &ItemFn) -> TokenStream {
|
|
let ident = &item.ident;
|
|
|
|
let gen = quote! {
|
|
#[cfg(feature = "trace")]
|
|
let s = trace(s, stringify!(#ident));
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_check_recursive_flag(item: &ItemFn) -> TokenStream {
|
|
let ident = &item.ident;
|
|
|
|
let gen = quote! {
|
|
if thread_context::PARSER_INDEX.with(|p| {
|
|
if let Some(i) = p.borrow_mut().get(stringify!(#ident)) {
|
|
return check_recursive_flag(s, i);
|
|
} else {
|
|
return false
|
|
}
|
|
}) {
|
|
#[cfg(feature = "trace")]
|
|
println!("{:<128} : loop detect", format!("{}{}", " ".repeat(s.extra.depth), stringify!(#ident)));
|
|
return Err(nom::Err::Error(nom::error::make_error(s, nom::error::ErrorKind::Fix)));
|
|
}
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_set_recursive_flag(item: &ItemFn) -> TokenStream {
|
|
let ident = &item.ident;
|
|
|
|
let gen = quote! {
|
|
let s = thread_context::PARSER_INDEX.with(|p| {
|
|
if let Some(i) = p.borrow_mut().get(stringify!(#ident)) {
|
|
set_recursive_flag(s, i, true)
|
|
} else {
|
|
#[cfg(feature = "trace")]
|
|
println!("{:<128} : allocate failed", format!("{}{}", " ".repeat(s.extra.depth), stringify!(#ident)));
|
|
s
|
|
}
|
|
});
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_body(item: &ItemFn) -> TokenStream {
|
|
let mut gen = quote! {};
|
|
for s in &item.block.stmts {
|
|
gen = quote! {
|
|
#gen
|
|
#s
|
|
};
|
|
}
|
|
let gen = quote! {
|
|
let body_ret = { #gen };
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_body_ambiguous(item: &ItemFn) -> TokenStream {
|
|
let mut token = quote! {};
|
|
for s in &item.block.stmts {
|
|
token = quote! {
|
|
#token
|
|
#s
|
|
};
|
|
}
|
|
let mut token = token.to_string();
|
|
|
|
let ambiguous_cnt: Vec<&str> = token.matches("ambiguous").collect();
|
|
let ambiguous_cnt = ambiguous_cnt.len();
|
|
|
|
let mut replace_parsers = Vec::new();
|
|
for i in 0..ambiguous_cnt {
|
|
let pos = token.find("ambiguous").unwrap();
|
|
let (head, rest) = token.split_at(pos);
|
|
if rest.starts_with("ambiguous_opt") {
|
|
let rest = rest.replacen("ambiguous_opt", &format!("amb_temporary{}", i), 1);
|
|
token = format!("{}{}", head, rest);
|
|
replace_parsers.push(("opt", "none"));
|
|
} else if rest.starts_with("ambiguous_alt") {
|
|
let rest = rest.replacen("ambiguous_alt", &format!("amb_temporary{}", i), 1);
|
|
token = format!("{}{}", head, rest);
|
|
replace_parsers.push(("alt_left", "alt_right"));
|
|
}
|
|
}
|
|
|
|
let mut gen = quote! {};
|
|
for i in 0..2_u32.pow(ambiguous_cnt as u32) {
|
|
let mut token = token.clone();
|
|
for j in 0..ambiguous_cnt {
|
|
let (p0, p1) = replace_parsers[j];
|
|
let repl = if ((i >> j) & 1) == 0 { p0 } else { p1 };
|
|
token = token.replace(&format!("amb_temporary{}", j), repl);
|
|
}
|
|
let token = format!("{{ {} }}", token);
|
|
let token = TokenStream::from_str(&token).unwrap();
|
|
let token = parse_macro_input!(token as Stmt);
|
|
gen = quote! {
|
|
#gen
|
|
|s| #token,
|
|
};
|
|
}
|
|
|
|
let gen = quote! {
|
|
alt((
|
|
#gen
|
|
))(s)
|
|
};
|
|
|
|
let gen = quote! {
|
|
let body_ret = { #gen };
|
|
};
|
|
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_body_unwrap(_item: &ItemFn) -> TokenStream {
|
|
let gen = quote! {
|
|
let (s, ret) = body_ret?;
|
|
};
|
|
gen.into()
|
|
}
|
|
|
|
fn impl_parser_clear_recursive_flags(_item: &ItemFn) -> TokenStream {
|
|
let gen = quote! {
|
|
Ok((clear_recursive_flags(s), ret))
|
|
};
|
|
gen.into()
|
|
}
|