From b4deb673c7d039b27008a6456ffdc9ba45452fb3 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 19 Jun 2017 14:05:14 -0700 Subject: [PATCH] refactor #[args()] attribute #40 --- pyo3cls/Cargo.toml | 2 - pyo3cls/src/args.rs | 287 +++++++++++++++++++----------------------- pyo3cls/src/lib.rs | 1 - pyo3cls/src/method.rs | 33 +---- pyo3cls/src/module.rs | 7 +- src/lib.rs | 3 +- 6 files changed, 132 insertions(+), 201 deletions(-) diff --git a/pyo3cls/Cargo.toml b/pyo3cls/Cargo.toml index e0833648..00b9168c 100644 --- a/pyo3cls/Cargo.toml +++ b/pyo3cls/Cargo.toml @@ -9,10 +9,8 @@ proc-macro = true [dependencies] quote="0.3" log="0.3" -nom = "^3.0" env_logger = "0.4" [dependencies.syn] version="0.11" features=["full"] -#git = "https://github.com/dtolnay/syn.git" diff --git a/pyo3cls/src/args.rs b/pyo3cls/src/args.rs index 46214e19..ba811419 100644 --- a/pyo3cls/src/args.rs +++ b/pyo3cls/src/args.rs @@ -1,4 +1,4 @@ -use nom::*; +use syn; #[derive(Debug, PartialEq)] pub enum Argument { @@ -9,106 +9,124 @@ pub enum Argument { Kwarg(String, String), } -pub fn parse_arguments(value: &[u8]) -> Vec { +pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec { let mut arguments = Vec::new(); let mut has_kw = false; let mut has_varargs = false; let mut has_kwargs = false; - let args_str = String::from_utf8_lossy(value); - let mut curr = value; - while curr.len() > 0 { - match get_arg(curr) { - IResult::Done(i, t) => { - match argument(t) { - IResult::Done(rest, arg) => { - if rest.len() != 0 { - println!("can not parse arguments {:?} : {:?}", - args_str, String::from_utf8_lossy(t), - ); + let args_str = quote! { + #(#items),* + }.to_string(); + + for item in items.iter() { + match item { + &syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => { + // arguments in form #[args(somename)] + 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 { - match arg { - Argument::VarArgsSeparator => { - 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() - } else { - arguments.push(Argument::VarArgsSeparator); - has_varargs = true; - has_kw = true; - } + if has_varargs { + arguments.push(Argument::Kwarg(name, s.clone())) + } else { + if has_kwargs { + println!("syntax error, keyword arguments is defined: {:?}", + args_str); + return Vec::new() } - Argument::VarArgs(s) => { - 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() - } 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!() + has_kw = true; + arguments.push(Argument::Arg(name, Some(s.clone()))) } } - }, - IResult::Error(_) | IResult::Incomplete(_) => { - println!("can not parse arguments {:?} : {:?}", - String::from_utf8_lossy(value), String::from_utf8_lossy(t), - ); } - }; - if i.len() > 0 { - curr = &i[1..i.len()]; - } else { - break + &syn::Lit::Int(ref s, _) => { + if has_varargs { + arguments.push(Argument::Kwarg(name, format!("{}", s))); + } else { + 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(_) => { - println!("can not parse arguments {:?}", String::from_utf8_lossy(value)); + &syn::NestedMetaItem::Literal(ref lit) => { + 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 { 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)] mod test { + use syn; use args::{Argument, parse_arguments}; + fn items(s: &'static str) -> Vec { + let i = syn::parse_outer_attr(s).unwrap(); + + match i.value { + syn::MetaItem::List(_, items) => { + items + } + _ => unreachable!() + } + } + #[test] fn test_errs() { - assert!(parse_arguments("123t2113est".as_ref()).is_empty()); - assert!(parse_arguments("test=1, test2".as_ref()).is_empty()); - assert!(parse_arguments("test=1, *, *args".as_ref()).is_empty()); - assert!(parse_arguments("test=1, **kwargs, *args".as_ref()).is_empty()); - assert!(parse_arguments("test=1, **kwargs, args".as_ref()).is_empty()); + assert!(parse_arguments(&items("#[args(test=\"1\", test2)]")).is_empty()); + assert!(parse_arguments(&items("#[args(test=1, \"*\", args=\"*\")]")).is_empty()); + assert!(parse_arguments(&items("#[args(test=1, kwargs=\"**\", args=\"*\")]")).is_empty()); + assert!(parse_arguments(&items("#[args(test=1, kwargs=\"**\", args)]")).is_empty()); } #[test] 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), Argument::Arg("test2".to_owned(), None), Argument::Arg("test3".to_owned(), Some("None".to_owned()))]); @@ -202,7 +169,8 @@ mod test { #[test] 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), Argument::Arg("test2".to_owned(), Some("None".to_owned())), Argument::VarArgsSeparator, @@ -212,12 +180,11 @@ mod test { #[test] fn test_all() { 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), Argument::Arg("test2".to_owned(), Some("None".to_owned())), Argument::VarArgs("args".to_owned()), Argument::Kwarg("test3".to_owned(), "None".to_owned()), Argument::KeywordArgs("kwargs".to_owned())]); } - } diff --git a/pyo3cls/src/lib.rs b/pyo3cls/src/lib.rs index 1f96c72a..980a3d16 100644 --- a/pyo3cls/src/lib.rs +++ b/pyo3cls/src/lib.rs @@ -7,7 +7,6 @@ extern crate proc_macro; extern crate syn; #[macro_use] extern crate quote; #[macro_use] extern crate log; -#[macro_use] extern crate nom; use std::str::FromStr; use proc_macro::TokenStream; diff --git a/pyo3cls/src/method.rs b/pyo3cls/src/method.rs index 03f4771a..809ec39e 100644 --- a/pyo3cls/src/method.rs +++ b/pyo3cls/src/method.rs @@ -35,6 +35,7 @@ pub struct FnSpec<'a> { impl<'a> FnSpec<'a> { + /// Parser function signature and function attributes pub fn parse(name: &'a syn::Ident, sig: &'a syn::MethodSig, meth_attrs: &'a mut Vec) -> FnSpec<'a> { @@ -314,7 +315,7 @@ fn parse_attributes(attrs: &mut Vec) -> (FnType, Vec) } }, "args" => { - spec.extend(parse_args(meta)) + spec.extend(parse_arguments(meta.as_slice())) } _ => { new_attrs.push(attr.clone()) @@ -334,33 +335,3 @@ fn parse_attributes(attrs: &mut Vec) -> (FnType, Vec) None => (FnType::Fn, spec), } } - -/// parse: #[args(args="args", kw="kwargs")] -fn parse_args(items: &Vec) -> Vec { - 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 -} diff --git a/pyo3cls/src/module.rs b/pyo3cls/src/module.rs index 808c98be..86ba481b 100644 --- a/pyo3cls/src/module.rs +++ b/pyo3cls/src/module.rs @@ -184,12 +184,7 @@ fn wrap_fn(item: &mut syn::Item) -> Option> { modname = None } if meta.len() >= 3 { - match meta[2] { - syn::NestedMetaItem::Literal(syn::Lit::Str(ref s, _)) => { - fn_attrs = args::parse_arguments(s.as_ref()); - }, - _ => modname = None - } + fn_attrs = args::parse_arguments(&meta[2..meta.len()]); } continue; } diff --git a/src/lib.rs b/src/lib.rs index fa0d6976..6fc12b8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,9 +85,10 @@ //! //! // add bindings to the generated python module //! // N.B: names: "libhello" must be the name of the `.so` or `.pyd` file +//! +//! /// Module documentation string //! #[py::modinit(hello)] //! 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 //! // in a separate module.