I write a crate (library) for lexical code analyzer. In general, my program works like this: it receives input text and gives the corresponding tokens. I want a user of the library to create own custom enum/struct of token, not me as the library author.
For example:
enum Token {
Star, // *
Plus, // +
Space,
}
To make it work into analyzer, the enum/struct needs implement the ReadToken
trait.
This is a trait that allows you to convert text to the corresponding token.
pub trait ReadToken<'t> {
type Token;
fn read_token(&self, text: &'t str) -> Self::Token;
}
This trait is also impl by functions. For example, so that you can specify the function of converting text to token and use it to tokenize.
Since the implementation of the trait for the token is trivial I decided to make it possible to derive automatically like:
#[derive(Clone, ReadToken)] // it also requires clone
enum Token {
Star, // *
Plus, // +
Space,
}
I wrote the following implementation for deriving in extern crate read-token-derive
:
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
#[proc_macro_derive(ReadToken)]
pub fn read_token(input: TokenStream) -> TokenStream {
let s = input.to_string();
let ast = syn::parse_derive_input(&s).unwrap();
let gen = if ast.generics.lifetimes.is_empty() {
impl_read_token(&ast)
} else {
impl_read_token_lifetime(&ast)
};
gen.parse().unwrap()
}
fn impl_read_token_lifetime(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
quote! {
impl ReadToken<'static> for #name<'static> {
type Token = #name<'static>;
fn read_token(&self, _: &str) -> Self::Token {
self.clone()
}
}
}
}
fn impl_read_token(ast: &syn::DeriveInput) -> quote::Tokens {
let name = &ast.ident;
quote! {
impl ReadToken<'static> for #name {
type Token = #name;
fn read_token(&self, _: &str) -> Self::Token {
self.clone()
}
}
}
}
I tested this in the library and it works. But I don't know how to export this from the library to use deriving from the outside.