Skip to content

Instantly share code, notes, and snippets.

@zbraniecki
Last active May 22, 2019 09:18
Show Gist options
  • Save zbraniecki/cf3894471410fe5975a6f126e2e63bae to your computer and use it in GitHub Desktop.
Save zbraniecki/cf3894471410fe5975a6f126e2e63bae to your computer and use it in GitHub Desktop.
Generating Rust Functions
extern crate syn;
#[macro_use]
extern crate quote;
extern crate proc_macro2;
use quote::ToTokens;
use proc_macro2::{Ident, Span};
/**
* Generate a boilerplate for a plural rule function.
*/
fn create_boilerplate_from_quote() -> syn::File {
let locale = "en";
let plural_type = "cardinal";
// let fn_name = format!("pr_{}_{}", locale, plural_type);
let fnname = "pr_en";
let boilerplate_tokens = quote! {
fn pr_en (po: PluralOperands) -> PluralCategory {}
};
syn::parse2(boilerplate_tokens).expect("Unable to parse boilerplate")
}
// This is a bit too high level since this one creates a full clause for `one` for
// english, cardinal. But for now, it gives us a good starting point to get
// an AST for the en, cardinal `one` rule.
fn create_en_one_clause() -> syn::Expr {
let one_clause_tokens = quote! {
if po.i == 1 { return PluralCategory::ONE }
};
syn::parse2(one_clause_tokens).expect("Unable to parse tokens")
}
// This is just a generic return clause for `other`.
fn create_en_other_clause() -> syn::Expr {
let s = "PluralCategory::OTHER";
let result = syn::parse_str(s).expect("Unable to parse boilerplate");
return result;
}
/**
* This function takes the file AST and adds an expression as a block
* to its first function's body.
*
* This is what we'll use to inject blocks into the body.
*/
fn add_block(f: &mut syn::File, b: syn::Expr) {
let func = &mut f.items[0];
match func {
syn::Item::Fn(ref mut f) => {
let block = &mut f.block;
let stmts = &mut block.stmts;
stmts.push(syn::Stmt::Expr(b));
},
_ => panic!("Unknown boilerplate")
};
}
fn main() {
// 1. Get the boilerplate AST
let mut result = create_boilerplate_from_quote();
// 2. Get the `one` block
let one = create_en_one_clause();
// 3. Get the `other` block
let other = create_en_other_clause();
// 4. Insert them in order
add_block(&mut result, one);
add_block(&mut result, other);
// Print the result function.
println!("{}", result.into_token_stream().to_string());
}
/**
* This is just an insight into how the AST look. We're actually *not*
* going to use this function at all :)
*/
fn _create_boilerplate() -> syn::File {
let mut segments: syn::punctuated::Punctuated<syn::PathSegment, syn::token::Colon2> = syn::punctuated::Punctuated::new();
segments.push(syn::PathSegment {
ident: Ident::new("PluralOperands", Span::call_site()),
arguments: syn::PathArguments::None
});
let mut pc_segments: syn::punctuated::Punctuated<syn::PathSegment, syn::token::Colon2> = syn::punctuated::Punctuated::new();
pc_segments.push(syn::PathSegment {
ident: Ident::new("PluralCategory", Span::call_site()),
arguments: syn::PathArguments::None
});
let mut inputs: syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma> = syn::punctuated::Punctuated::new();
inputs.push_value(
syn::FnArg::Captured(
syn::ArgCaptured {
pat: syn::Pat::Ident(
syn::PatIdent {
by_ref: None,
mutability: None,
ident: Ident::new("po", Span::call_site()),
subpat: None,
}
),
colon_token: syn::token::Colon::new(Span::call_site()),
ty: syn::Type::Path(
syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments
}
}
),
}
));
let syntax2 = syn::File {
shebang: None,
attrs: vec![],
items: vec![
syn::Item::Fn(
syn::ItemFn {
attrs: vec![],
vis: syn::Visibility::Inherited,
constness: None,
unsafety: None,
abi: None,
ident: Ident::new("en_cardinal", Span::call_site()),
decl: Box::new(syn::FnDecl {
fn_token: syn::token::Fn(Span::call_site()),
generics: syn::Generics {
lt_token: None,
params: syn::punctuated::Punctuated::new(),
gt_token: None,
where_clause: None,
},
paren_token: syn::token::Paren(Span::call_site()),
inputs,
variadic: None,
output: syn::ReturnType::Type(
syn::token::RArrow::new(Span::call_site()),
Box::new(syn::Type::Path(syn::TypePath {
qself: None,
path: syn::Path {
leading_colon: None,
segments: pc_segments
}
}))
),
}),
block: Box::new(syn::Block {
brace_token: syn::token::Brace(Span::call_site()),
stmts: vec![]
})
}
)
]
};
return syntax2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment