Last active
May 22, 2019 09:18
-
-
Save zbraniecki/cf3894471410fe5975a6f126e2e63bae to your computer and use it in GitHub Desktop.
Generating Rust Functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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