use quote::{ToTokens, quote};
use syn::{Expr, parse::{Parse, discouraged::Speculative}, Token, token::Paren, parenthesized, ext::IdentExt, braced, Error};
use proc_macro::Span;
use proc_macro2::{Group, TokenStream, Ident, Punct};
use crate::state::get_exts;
use crate::state::{VerbGenerator, BlockGenerator, HandleGenerator};
#[derive(Clone)]
pub struct Arg
{
pub name: Option<Ident>,
pub eq: Option<Token![=]>,
pub value: Expr
}
#[derive(Clone)]
pub struct Verb
{
pub name: Ident,
pub args: Vec<Arg>,
pub simple: bool
}
#[derive(Clone)]
pub struct Block
{
pub name: Ident,
pub args: Vec<Arg>,
pub block: TokenStream,
pub simple: bool
}
#[derive(Clone)]
pub struct Handle
{
pub name: Ident,
pub args: Vec<Arg>,
pub block: TokenStream,
pub simple: bool
}
#[derive(Clone)]
pub enum DroughterItem
{
Verb(Verb),
Block(Block),
Handle(Handle)
}
pub struct DroughterBody
{
pub items: Vec<DroughterItem>
}
impl Parse for Arg
{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self>
{
if input.peek(syn::Ident) && input.peek2(Token![=]) && !input.peek3(Token![>])
{
Ok(
Self{
name: Some(input.call(Ident::parse_any)?),
eq: Some(input.parse()?),
value: input.parse()?
}
)
}
else
{
Ok(
Self{
name: None,
eq: None,
value: input.parse()?,
}
)
}
}
}
impl ToTokens for Arg
{
fn to_tokens(&self, tokens: &mut TokenStream)
{
if let Some(ref name) = self.name
{
name.to_tokens(tokens);
self.eq.unwrap().to_tokens(tokens);
}
self.value.to_tokens(tokens);
}
}
impl ToTokens for Verb
{
fn to_tokens(&self, tokens: &mut TokenStream)
{
self.name.to_tokens(tokens);
let mut argstream = TokenStream::new();
if !self.args.is_empty()
{
self.args[0].to_tokens(&mut argstream);
for arg in &self.args[1..]
{
Punct::new(',', proc_macro2::Spacing::Alone)
.to_tokens(&mut argstream);
arg.to_tokens(&mut argstream);
}
}
if self.simple
{
Group::new(proc_macro2::Delimiter::Parenthesis, argstream)
.to_tokens(tokens);
}
else
{
argstream.to_tokens(tokens)
}
}
}
impl ToTokens for Block
{
fn to_tokens(&self, tokens: &mut TokenStream)
{
self.name.to_tokens(tokens);
let mut argstream = TokenStream::new();
if !self.args.is_empty()
{
self.args[0].to_tokens(&mut argstream);
for arg in &self.args[1..]
{
Punct::new(',', proc_macro2::Spacing::Alone)
.to_tokens(&mut argstream);
arg.to_tokens(&mut argstream);
}
}
if self.simple
{
Group::new(proc_macro2::Delimiter::Parenthesis, argstream)
.to_tokens(tokens);
}
else
{
argstream.to_tokens(tokens)
}
let body = &self.block;
quote!({#body})
.to_tokens(tokens)
}
}
impl ToTokens for Handle
{
fn to_tokens(&self, tokens: &mut TokenStream)
{
self.name.to_tokens(tokens);
let mut argstream = TokenStream::new();
if !self.args.is_empty()
{
self.args[0].to_tokens(&mut argstream);
for arg in &self.args[1..]
{
Punct::new(',', proc_macro2::Spacing::Alone)
.to_tokens(&mut argstream);
arg.to_tokens(&mut argstream);
}
}
if self.simple
{
Group::new(proc_macro2::Delimiter::Parenthesis, argstream)
.to_tokens(tokens);
}
else
{
argstream.to_tokens(tokens)
}
let body = &self.block;
quote!( => {#body})
.to_tokens(tokens)
}
}
impl Parse for DroughterItem
{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self>
{
let name = input.call(Ident::parse_any)?;
let (simple, args);
if input.peek(Paren)
{
simple = true;
let argsbuf;
parenthesized!(argsbuf in input);
let mut parsedargs = vec![];
loop
{
parsedargs.push(argsbuf.parse()?);
if !argsbuf.is_empty() { argsbuf.parse::<Token![,]>()?; }
else { break; }
}
args = parsedargs;
}
else
{
simple = false;
let mut parsedargs = vec![];
loop
{
let fork = input.fork();
if let Ok(arg) = fork.parse()
{
parsedargs.push(arg);
input.advance_to(&fork);
if input.peek(Token![,]) { input.parse::<Token![,]>()?; }
}
else { break; }
}
args = parsedargs;
}
let ret = if input.peek(Token![;])
{
Ok(
DroughterItem::Verb(Verb{
name, args, simple
})
)
}
else if input.peek(Token![=>])
{
input.parse::<Token![=>]>()?;
let block;
braced!(block in input);
Ok(
DroughterItem::Handle(Handle{
name, args, simple,
block: block.cursor().token_stream()
})
)
}
else
{
let block;
braced!(block in input);
Ok(
DroughterItem::Block(Block{
name, args, simple,
block: block.cursor().token_stream()
})
)
};
input.parse::<Token![;]>()?;
ret
}
}
impl Parse for DroughterBody
{
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self>
{
let mut items = vec![];
while !input.is_empty()
{
items.push(input.parse()?);
}
Ok(DroughterBody{items})
}
}
impl ToTokens for DroughterBody
{
fn to_tokens(&self, tokens: &mut TokenStream) {
use crate::droughter::DroughterItem::*;
let (verbs, blocks, handles) = get_exts(Span::call_site().source_file());
{
let (checksvec, checksstream) = {
let mut checks = Vec::new();
for item in &self.items
{
match item
{
Verb(verb) => {
if !verbs.contains_key(&verb.name.to_string())
{
Error::new(verb.name.span(), format!("Unknown verb `{}`!", verb.name))
.to_compile_error()
.to_tokens(tokens);
continue;
}
checks.push(verb.clone());
},
Block(block) => {
if !blocks.contains_key(&block.name.to_string())
{
Error::new(block.name.span(), format!("Unknown block `{}`!", block.name))
.to_compile_error()
.to_tokens(tokens);
continue;
}
checks.push(crate::droughter::Verb{name: block.name.clone(), args: block.args.clone(), simple: block.simple});
},
Handle(handle) => {
if !handles.contains_key(&handle.name.to_string())
{
Error::new(handle.name.span(), format!("Unknown handle `{}`!", handle.name))
.to_compile_error()
.to_tokens(tokens);
continue;
}
checks.push(crate::droughter::Verb{name: handle.name.clone(), args: handle.args.clone(), simple: handle.simple});
}
}
}
let mut stream = TokenStream::new();
for check in &checks{
check.to_tokens(&mut stream);
Punct::new(';', proc_macro2::Spacing::Alone)
.to_tokens(&mut stream);
}
(checks, stream)
};
for val in verbs.values()
{
match val {
VerbGenerator::Internal { check, .. } => check(tokens, &checksvec),
VerbGenerator::Extension { check, .. } => {
quote!(#check!(check, #checksstream))
.to_tokens(tokens);
}
}
}
for val in blocks.values()
{
match val {
BlockGenerator::Internal { check, .. } => check(tokens, &checksvec),
BlockGenerator::Extension { check, .. } => {
quote!(#check!(check, #checksstream))
.to_tokens(tokens);
}
}
}
for val in handles.values()
{
match val {
HandleGenerator::Internal { check, .. } => check(tokens, &checksvec),
HandleGenerator::Extension { check, .. } => {
quote!(#check!(check, #checksstream))
.to_tokens(tokens);
}
}
}
}
let builder = Ident::new("builder", Span::call_site().into());
quote!(
let mut #builder = drought::DroughterBuilder::new();
).to_tokens(tokens);
for item in &self.items
{
match item
{
Verb(verb) => {
match &verbs[&verb.name.to_string()]
{
VerbGenerator::Internal { call, .. } => call(tokens, &verb, &builder),
VerbGenerator::Extension { call, .. } => {
quote!(#call!(call verb, #verb))
.to_tokens(tokens);
}
}
},
Block(block) => {
match &blocks[&block.name.to_string()]
{
BlockGenerator::Internal { call, .. } => call(tokens, &block, &builder),
BlockGenerator::Extension { call, .. } => {
quote!(#call!(call block, #block))
.to_tokens(tokens);
}
}
},
Handle(handle) => {
match &handles[&handle.name.to_string()]
{
HandleGenerator::Internal { call, .. } => call(tokens, &handle, &builder),
HandleGenerator::Extension { call, .. } => {
quote!(#call!(call handle, #handle))
.to_tokens(tokens);
}
}
},
}
}
quote!(#builder.build())
.to_tokens(tokens);
}
}