refactor #[args()] attribute #40
This commit is contained in:
parent
d4a5a46338
commit
b4deb673c7
|
@ -9,10 +9,8 @@ proc-macro = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote="0.3"
|
quote="0.3"
|
||||||
log="0.3"
|
log="0.3"
|
||||||
nom = "^3.0"
|
|
||||||
env_logger = "0.4"
|
env_logger = "0.4"
|
||||||
|
|
||||||
[dependencies.syn]
|
[dependencies.syn]
|
||||||
version="0.11"
|
version="0.11"
|
||||||
features=["full"]
|
features=["full"]
|
||||||
#git = "https://github.com/dtolnay/syn.git"
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use nom::*;
|
use syn;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Argument {
|
pub enum Argument {
|
||||||
|
@ -9,106 +9,124 @@ pub enum Argument {
|
||||||
Kwarg(String, String),
|
Kwarg(String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_arguments(value: &[u8]) -> Vec<Argument> {
|
pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
||||||
let mut arguments = Vec::new();
|
let mut arguments = Vec::new();
|
||||||
let mut has_kw = false;
|
let mut has_kw = false;
|
||||||
let mut has_varargs = false;
|
let mut has_varargs = false;
|
||||||
let mut has_kwargs = false;
|
let mut has_kwargs = false;
|
||||||
let args_str = String::from_utf8_lossy(value);
|
|
||||||
|
|
||||||
let mut curr = value;
|
let args_str = quote! {
|
||||||
while curr.len() > 0 {
|
#(#items),*
|
||||||
match get_arg(curr) {
|
}.to_string();
|
||||||
IResult::Done(i, t) => {
|
|
||||||
match argument(t) {
|
for item in items.iter() {
|
||||||
IResult::Done(rest, arg) => {
|
match item {
|
||||||
if rest.len() != 0 {
|
&syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => {
|
||||||
println!("can not parse arguments {:?} : {:?}",
|
// arguments in form #[args(somename)]
|
||||||
args_str, String::from_utf8_lossy(t),
|
if has_kwargs {
|
||||||
);
|
println!("syntax error, keyword arguments is defined: {:?}", args_str);
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
if has_kw {
|
||||||
|
println!("syntax error, argument is not allowed after keyword argument: {:?}",
|
||||||
|
args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
arguments.push(Argument::Arg(ident.as_ref().to_owned(), None))
|
||||||
|
}
|
||||||
|
&syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref ident, ref lit)) => {
|
||||||
|
let name = ident.as_ref().to_owned();
|
||||||
|
match lit {
|
||||||
|
&syn::Lit::Str(ref s, _) => {
|
||||||
|
if s == "*" { // #[args(args="*")]
|
||||||
|
if has_kwargs {
|
||||||
|
println!("* - syntax error, keyword arguments is defined: {:?}",
|
||||||
|
args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
if has_varargs {
|
||||||
|
println!("*(var args) is defined: {:?}", args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
has_varargs = true;
|
||||||
|
arguments.push(Argument::VarArgs(name));
|
||||||
|
}
|
||||||
|
else if s == "**" { // #[args(kwargs="**")]
|
||||||
|
if has_kwargs {
|
||||||
|
println!("arguments already define ** (kw args): {:?}",
|
||||||
|
args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
has_kwargs = true;
|
||||||
|
arguments.push(Argument::KeywordArgs(name));
|
||||||
} else {
|
} else {
|
||||||
match arg {
|
if has_varargs {
|
||||||
Argument::VarArgsSeparator => {
|
arguments.push(Argument::Kwarg(name, s.clone()))
|
||||||
if has_kwargs {
|
} else {
|
||||||
println!("syntax error, keyword arguments is defined: {:?}",
|
if has_kwargs {
|
||||||
args_str);
|
println!("syntax error, keyword arguments is defined: {:?}",
|
||||||
return Vec::new()
|
args_str);
|
||||||
}
|
return Vec::new()
|
||||||
if has_varargs {
|
|
||||||
println!("arguments already define * (var args): {:?}",
|
|
||||||
args_str);
|
|
||||||
return Vec::new()
|
|
||||||
} else {
|
|
||||||
arguments.push(Argument::VarArgsSeparator);
|
|
||||||
has_varargs = true;
|
|
||||||
has_kw = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Argument::VarArgs(s) => {
|
has_kw = true;
|
||||||
if has_kwargs {
|
arguments.push(Argument::Arg(name, Some(s.clone())))
|
||||||
println!("syntax error, keyword arguments is defined: {:?}",
|
|
||||||
args_str);
|
|
||||||
return Vec::new()
|
|
||||||
}
|
|
||||||
if has_varargs {
|
|
||||||
println!("*(var args) is defined: {:?}", args_str);
|
|
||||||
return Vec::new()
|
|
||||||
} else {
|
|
||||||
arguments.push(Argument::VarArgs(s));
|
|
||||||
has_varargs = true;
|
|
||||||
has_kw = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Argument::KeywordArgs(s) => {
|
|
||||||
if has_kwargs {
|
|
||||||
println!("arguments already define ** (kw args): {:?}",
|
|
||||||
args_str);
|
|
||||||
return Vec::new()
|
|
||||||
} else {
|
|
||||||
arguments.push(Argument::KeywordArgs(s));
|
|
||||||
has_kwargs = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Argument::Arg(s, opt) => {
|
|
||||||
if has_kwargs {
|
|
||||||
println!("syntax error, keyword arguments is defined: {:?}",
|
|
||||||
args_str);
|
|
||||||
return Vec::new()
|
|
||||||
}
|
|
||||||
if let Some(opt) = opt {
|
|
||||||
has_kw = true;
|
|
||||||
if has_varargs {
|
|
||||||
arguments.push(Argument::Kwarg(s, opt))
|
|
||||||
} else {
|
|
||||||
arguments.push(Argument::Arg(s, Some(opt)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if has_kw {
|
|
||||||
println!("syntax error, argument is not allowed after keyword argument: {:?}",
|
|
||||||
args_str);
|
|
||||||
return Vec::new()
|
|
||||||
}
|
|
||||||
arguments.push(Argument::Arg(s, None));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Argument::Kwarg(_, _) => unreachable!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
IResult::Error(_) | IResult::Incomplete(_) => {
|
|
||||||
println!("can not parse arguments {:?} : {:?}",
|
|
||||||
String::from_utf8_lossy(value), String::from_utf8_lossy(t),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
&syn::Lit::Int(ref s, _) => {
|
||||||
if i.len() > 0 {
|
if has_varargs {
|
||||||
curr = &i[1..i.len()];
|
arguments.push(Argument::Kwarg(name, format!("{}", s)));
|
||||||
} else {
|
} else {
|
||||||
break
|
if has_kwargs {
|
||||||
|
println!("syntax error, keyword arguments is defined: {:?}",
|
||||||
|
args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
has_kw = true;
|
||||||
|
arguments.push(Argument::Arg(name, Some(format!("{}", s))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Only string literal is supported, got: {:?}", lit);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IResult::Error(_) | IResult::Incomplete(_) => {
|
&syn::NestedMetaItem::Literal(ref lit) => {
|
||||||
println!("can not parse arguments {:?}", String::from_utf8_lossy(value));
|
match lit {
|
||||||
|
&syn::Lit::Str(ref s, _) => {
|
||||||
|
// #[args("*")]
|
||||||
|
if s == "*" {
|
||||||
|
if has_kwargs {
|
||||||
|
println!(
|
||||||
|
"syntax error, keyword arguments is defined: {:?}",
|
||||||
|
args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
if has_varargs {
|
||||||
|
println!(
|
||||||
|
"arguments already define * (var args): {:?}",
|
||||||
|
args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
has_varargs = true;
|
||||||
|
arguments.push(Argument::VarArgsSeparator);
|
||||||
|
} else {
|
||||||
|
println!("Unknown string literal, got: {:?} args: {:?}",
|
||||||
|
s, args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Only string literal is supported, got: {:?} args: {:?}",
|
||||||
|
lit, args_str);
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Unknown argument, got: {:?} args: {:?}", item, args_str);
|
||||||
|
return Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,85 +134,34 @@ pub fn parse_arguments(value: &[u8]) -> Vec<Argument> {
|
||||||
arguments
|
arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(get_arg, ws!(take_while!(is_not_arg_end)));
|
|
||||||
|
|
||||||
fn is_not_arg_end(c: u8) -> bool {
|
|
||||||
c as char != ','
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(argument<&[u8], Argument>, alt_complete!(
|
|
||||||
parse_arg | parse_optional_arg | parse_kwargs | parse_var_arg | parse_var_arg_sep));
|
|
||||||
|
|
||||||
named!(parse_var_arg_sep<&[u8], Argument>,
|
|
||||||
do_parse!(
|
|
||||||
char!('*') >> eof!() >> ( Argument::VarArgsSeparator )
|
|
||||||
)
|
|
||||||
);
|
|
||||||
named!(parse_var_arg<&[u8], Argument>,
|
|
||||||
do_parse!(
|
|
||||||
tag!("*") >>
|
|
||||||
take_while!(is_space) >> verify!(peek!(take!(1)), alphabetic) >>
|
|
||||||
name: take_while!(is_alphanumeric) >> take_while!(is_space) >> eof!() >> (
|
|
||||||
Argument::VarArgs(String::from_utf8_lossy(name).to_string())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
named!(parse_kwargs<&[u8], Argument>,
|
|
||||||
do_parse!(
|
|
||||||
tag!("**") >>
|
|
||||||
take_while!(is_space) >> verify!(peek!(take!(1)), alphabetic) >>
|
|
||||||
name: take_while!(is_alphanumeric) >> take_while!(is_space) >> eof!() >> (
|
|
||||||
Argument::KeywordArgs(String::from_utf8_lossy(name).to_string())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
named!(parse_arg<&[u8], Argument>,
|
|
||||||
do_parse!(
|
|
||||||
take_while!(is_space) >> verify!(peek!(take!(1)), alphabetic) >>
|
|
||||||
name: take_while!(is_alphanumeric) >> take_while!(is_space) >> eof!() >> (
|
|
||||||
Argument::Arg(String::from_utf8_lossy(name).to_string(), None)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
named!(parse_optional_arg<&[u8], Argument>,
|
|
||||||
do_parse!(
|
|
||||||
take_while!(is_space) >> verify!(peek!(take!(1)), alphabetic) >>
|
|
||||||
name: take_while!(is_alphanumeric) >>
|
|
||||||
take_while!(is_space) >> tag!("=") >>
|
|
||||||
value: take_while!(any) >> (
|
|
||||||
Argument::Arg(
|
|
||||||
String::from_utf8_lossy(name).to_string(),
|
|
||||||
Some(String::from_utf8_lossy(value).to_string())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
fn any(_: u8) -> bool { true }
|
|
||||||
|
|
||||||
fn alphabetic(b: &[u8]) -> bool {
|
|
||||||
is_alphabetic(b[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use syn;
|
||||||
use args::{Argument, parse_arguments};
|
use args::{Argument, parse_arguments};
|
||||||
|
|
||||||
|
fn items(s: &'static str) -> Vec<syn::NestedMetaItem> {
|
||||||
|
let i = syn::parse_outer_attr(s).unwrap();
|
||||||
|
|
||||||
|
match i.value {
|
||||||
|
syn::MetaItem::List(_, items) => {
|
||||||
|
items
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_errs() {
|
fn test_errs() {
|
||||||
assert!(parse_arguments("123t2113est".as_ref()).is_empty());
|
assert!(parse_arguments(&items("#[args(test=\"1\", test2)]")).is_empty());
|
||||||
assert!(parse_arguments("test=1, test2".as_ref()).is_empty());
|
assert!(parse_arguments(&items("#[args(test=1, \"*\", args=\"*\")]")).is_empty());
|
||||||
assert!(parse_arguments("test=1, *, *args".as_ref()).is_empty());
|
assert!(parse_arguments(&items("#[args(test=1, kwargs=\"**\", args=\"*\")]")).is_empty());
|
||||||
assert!(parse_arguments("test=1, **kwargs, *args".as_ref()).is_empty());
|
assert!(parse_arguments(&items("#[args(test=1, kwargs=\"**\", args)]")).is_empty());
|
||||||
assert!(parse_arguments("test=1, **kwargs, args".as_ref()).is_empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_args() {
|
fn test_simple_args() {
|
||||||
let args = parse_arguments("test1, test2, test3=None".as_ref());
|
let args = parse_arguments(&items("#[args(test1, test2, test3=\"None\")]"));
|
||||||
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
|
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
|
||||||
Argument::Arg("test2".to_owned(), None),
|
Argument::Arg("test2".to_owned(), None),
|
||||||
Argument::Arg("test3".to_owned(), Some("None".to_owned()))]);
|
Argument::Arg("test3".to_owned(), Some("None".to_owned()))]);
|
||||||
|
@ -202,7 +169,8 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_varargs() {
|
fn test_varargs() {
|
||||||
let args = parse_arguments("test1, test2=None, *, test3=None".as_ref());
|
let args = parse_arguments(
|
||||||
|
&items("#[args(test1, test2=\"None\", \"*\", test3=\"None\")]"));
|
||||||
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
|
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
|
||||||
Argument::Arg("test2".to_owned(), Some("None".to_owned())),
|
Argument::Arg("test2".to_owned(), Some("None".to_owned())),
|
||||||
Argument::VarArgsSeparator,
|
Argument::VarArgsSeparator,
|
||||||
|
@ -212,12 +180,11 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_all() {
|
fn test_all() {
|
||||||
let args = parse_arguments(
|
let args = parse_arguments(
|
||||||
"test1, test2=None, *args, test3=None, **kwargs".as_ref());
|
&items("#[args(test1, test2=\"None\", args=\"*\", test3=\"None\", kwargs=\"**\")]"));
|
||||||
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
|
assert!(args == vec![Argument::Arg("test1".to_owned(), None),
|
||||||
Argument::Arg("test2".to_owned(), Some("None".to_owned())),
|
Argument::Arg("test2".to_owned(), Some("None".to_owned())),
|
||||||
Argument::VarArgs("args".to_owned()),
|
Argument::VarArgs("args".to_owned()),
|
||||||
Argument::Kwarg("test3".to_owned(), "None".to_owned()),
|
Argument::Kwarg("test3".to_owned(), "None".to_owned()),
|
||||||
Argument::KeywordArgs("kwargs".to_owned())]);
|
Argument::KeywordArgs("kwargs".to_owned())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ extern crate proc_macro;
|
||||||
extern crate syn;
|
extern crate syn;
|
||||||
#[macro_use] extern crate quote;
|
#[macro_use] extern crate quote;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
#[macro_use] extern crate nom;
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
|
@ -35,6 +35,7 @@ pub struct FnSpec<'a> {
|
||||||
|
|
||||||
impl<'a> FnSpec<'a> {
|
impl<'a> FnSpec<'a> {
|
||||||
|
|
||||||
|
/// Parser function signature and function attributes
|
||||||
pub fn parse(name: &'a syn::Ident,
|
pub fn parse(name: &'a syn::Ident,
|
||||||
sig: &'a syn::MethodSig,
|
sig: &'a syn::MethodSig,
|
||||||
meth_attrs: &'a mut Vec<syn::Attribute>) -> FnSpec<'a> {
|
meth_attrs: &'a mut Vec<syn::Attribute>) -> FnSpec<'a> {
|
||||||
|
@ -314,7 +315,7 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"args" => {
|
"args" => {
|
||||||
spec.extend(parse_args(meta))
|
spec.extend(parse_arguments(meta.as_slice()))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
new_attrs.push(attr.clone())
|
new_attrs.push(attr.clone())
|
||||||
|
@ -334,33 +335,3 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
||||||
None => (FnType::Fn, spec),
|
None => (FnType::Fn, spec),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse: #[args(args="args", kw="kwargs")]
|
|
||||||
fn parse_args(items: &Vec<syn::NestedMetaItem>) -> Vec<Argument> {
|
|
||||||
let mut spec = Vec::new();
|
|
||||||
|
|
||||||
for item in items.iter() {
|
|
||||||
match item {
|
|
||||||
&syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref ident, ref name)) => {
|
|
||||||
match *name {
|
|
||||||
syn::Lit::Str(ref name, _) => match ident.as_ref() {
|
|
||||||
"args" =>
|
|
||||||
spec.push(Argument::VarArgs(name.clone())),
|
|
||||||
"kw" =>
|
|
||||||
spec.push(Argument::KeywordArgs(name.clone())),
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
&syn::NestedMetaItem::Literal(syn::Lit::Str(ref args, _)) => {
|
|
||||||
for item in parse_arguments(args.as_ref()) {
|
|
||||||
spec.push(item);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spec
|
|
||||||
}
|
|
||||||
|
|
|
@ -184,12 +184,7 @@ fn wrap_fn(item: &mut syn::Item) -> Option<Box<syn::Block>> {
|
||||||
modname = None
|
modname = None
|
||||||
}
|
}
|
||||||
if meta.len() >= 3 {
|
if meta.len() >= 3 {
|
||||||
match meta[2] {
|
fn_attrs = args::parse_arguments(&meta[2..meta.len()]);
|
||||||
syn::NestedMetaItem::Literal(syn::Lit::Str(ref s, _)) => {
|
|
||||||
fn_attrs = args::parse_arguments(s.as_ref());
|
|
||||||
},
|
|
||||||
_ => modname = None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,9 +85,10 @@
|
||||||
//!
|
//!
|
||||||
//! // add bindings to the generated python module
|
//! // add bindings to the generated python module
|
||||||
//! // N.B: names: "libhello" must be the name of the `.so` or `.pyd` file
|
//! // N.B: names: "libhello" must be the name of the `.so` or `.pyd` file
|
||||||
|
//!
|
||||||
|
//! /// Module documentation string
|
||||||
//! #[py::modinit(hello)]
|
//! #[py::modinit(hello)]
|
||||||
//! fn init_module(py: Python, m: &PyModule) -> PyResult<()> {
|
//! fn init_module(py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
//! m.add(py, "__doc__", "Module documentation string")?;
|
|
||||||
//!
|
//!
|
||||||
//! // pyo3 aware function. All of our python interface could be declared
|
//! // pyo3 aware function. All of our python interface could be declared
|
||||||
//! // in a separate module.
|
//! // in a separate module.
|
||||||
|
|
Loading…
Reference in New Issue