diff options
| author | HampusM <hampus@hampusmat.com> | 2023-03-05 19:46:12 +0100 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2023-03-05 20:05:22 +0100 | 
| commit | 3d9c9d7ac68a439a29c8ad0a745a7e2a52234a44 (patch) | |
| tree | ae37e7242f2ed611ee862f046ae20d235df49e81 | |
| parent | 57ab49b24ed34fcddadb1a293511ee74a066e026 (diff) | |
refactor: improve replacement syntax
| -rw-r--r-- | src/iter.rs | 65 | ||||
| -rw-r--r-- | src/lib.rs | 29 | ||||
| -rw-r--r-- | src/repeat.rs | 9 | ||||
| -rw-r--r-- | src/token_stream.rs | 77 | 
4 files changed, 146 insertions, 34 deletions
diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000..650dceb --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,65 @@ +use std::fmt::Debug; +use std::iter::Peekable; + +pub trait IteratorExt: Sized + Iterator +{ +    /// Returns an iterator over overlapping item tuples. +    fn windows(self) -> Windows<Self>; +} + +impl<Iter: Iterator> IteratorExt for Iter +where +    Iter::Item: Clone, +{ +    fn windows(self) -> Windows<Self> +    { +        Windows { +            iter: self.peekable(), +        } +    } +} + +pub struct Windows<Iter: Iterator> +{ +    iter: Peekable<Iter>, +} + +impl<Iter: Iterator> Iterator for Windows<Iter> +where +    Iter::Item: Clone + Debug, +{ +    type Item = (Iter::Item, Option<Iter::Item>); + +    fn next(&mut self) -> Option<Self::Item> +    { +        let opt_first = self.iter.next(); +        let opt_second = self.iter.peek(); + +        let Some(first) = opt_first else { +            return None; +        }; + +        Some((first, opt_second.cloned())) +    } +} + +#[cfg(test)] +mod tests +{ +    use pretty_assertions::assert_eq; + +    use super::*; + +    #[test] +    fn windows_works() +    { +        let mut windows = (0..6).windows(); + +        assert_eq!(windows.next(), Some((0, Some(1)))); +        assert_eq!(windows.next(), Some((1, Some(2)))); +        assert_eq!(windows.next(), Some((2, Some(3)))); +        assert_eq!(windows.next(), Some((3, Some(4)))); +        assert_eq!(windows.next(), Some((4, Some(5)))); +        assert_eq!(windows.next(), Some((5, None))); +    } +} @@ -9,48 +9,49 @@ use proc_macro::TokenStream;  use crate::repeat::for_each_opengl_command_impl; +mod iter;  mod repeat;  mod str;  mod token_stream; -const OPENGL_CMD_IDENT_REPLACE: &str = "_gl_command_"; -const OPENGL_CMD_RET_TYPE_REPLACE: &str = "_gl_command_ret_type_"; -const OPENGL_CMD_ARGS_REPLACE: &str = "_gl_command_args_"; -const OPENGL_CMD_ARG_NAMES_REPLACE: &str = "_gl_command_arg_names_"; -const OPENGL_CMD_ARG_TYPES_REPLACE: &str = "_gl_command_arg_types_"; +const OPENGL_CMD_IDENT_REPLACE: &str = "gl_command"; +const OPENGL_CMD_RET_TYPE_REPLACE: &str = "gl_command_ret_type"; +const OPENGL_CMD_ARGS_REPLACE: &str = "gl_command_args"; +const OPENGL_CMD_ARG_NAMES_REPLACE: &str = "gl_command_arg_names"; +const OPENGL_CMD_ARG_TYPES_REPLACE: &str = "gl_command_arg_types";  static OPENGL_REGISTRY: Lazy<Registry> = Lazy::new(|| Registry::retrieve().unwrap());  /// Repeats the input for each command in the [OpenGL API and Extension Registry].  ///  /// # Replacements -/// This macro will replace some specific identifiers. These identifiers are to be put -/// between underscores. +/// This macro will replace some specific identifiers **prefixed with a hashtag**.  ///  /// ### The following identifiers will be replaced  ///  /// **`gl_command`**<br> -/// Will be replaced by the command name. +/// Will be replaced by the full command name. For example, `glViewport`.  ///  /// **`gl_command_ret_type`**<br> -/// Will be replaced by the command return type. +/// Will be replaced by the command return type. For example, `GLuint`.  ///  /// **`gl_command_args`**<br> -/// Will be replaced by the command arguments formatted as `name: type`. +/// Will be replaced by the command arguments formatted as `name: type`, seperated by +/// comma.  ///  /// **`gl_command_arg_names`**<br> -/// Will be replaced by the command argument names. +/// Will be replaced by the command argument names, seperated by comma.  ///  /// **`gl_command_arg_types`**<br> -/// Will be replaced by the command argument types. +/// Will be replaced by the command argument types, seperated by comma.  ///  /// # Examples  /// ```  /// # use opengl_registry_macros::for_each_opengl_command;  /// for_each_opengl_command! { -///     fn _gl_command_() +///     fn #gl_command()  ///     { -///         println!("Hello from {}", stringify!(_gl_command_)); +///         println!("Hello from {}", stringify!(#gl_command));  ///     }  /// }  /// ``` diff --git a/src/repeat.rs b/src/repeat.rs index 5d9ece7..fe7523c 100644 --- a/src/repeat.rs +++ b/src/repeat.rs @@ -125,6 +125,7 @@ mod tests  {      use opengl_registry::command::{Command, Parameter, Prototype};      use pretty_assertions::assert_eq; +    use proc_macro2::{Punct, Spacing};      use quote::quote;      use super::*; @@ -147,12 +148,14 @@ mod tests              ),          ]); +        let ht = Punct::new('#', Spacing::Alone); +          assert_eq!(              for_each_opengl_command_impl(                  "e! {                      unsafe { -                        functions::_gl_command_ = FunctionPtr::new_initialized( -                            get_proc_addr(stringify!(_gl_command_)) +                        functions::#ht gl_command = FunctionPtr::new_initialized( +                            get_proc_addr(stringify!(#ht gl_command))                          );                      }                  }, @@ -182,7 +185,7 @@ mod tests          assert_eq!(              for_each_opengl_command_impl(                  "e! { -                    fn _gl_command_(_gl_command_args_) -> _gl_command_ret_type_ {} +                    fn #ht gl_command(#ht gl_command_args) -> #ht gl_command_ret_type {}                  },                  ®istry,              ) diff --git a/src/token_stream.rs b/src/token_stream.rs index 5897529..1bbf431 100644 --- a/src/token_stream.rs +++ b/src/token_stream.rs @@ -1,5 +1,7 @@  use proc_macro2::{Group, Ident, TokenStream, TokenTree}; +use crate::iter::IteratorExt; +  #[allow(clippy::module_name_repetitions)]  pub trait TokenStreamExt  { @@ -16,7 +18,7 @@ impl TokenStreamExt for TokenStream      fn replace_ident(&self, target_ident: &Ident, substitution: &TokenTree)          -> TokenStream      { -        recurse_replace_ident(self.clone(), target_ident, substitution) +        recurse_replace_ident(self.clone(), target_ident, substitution, '#')      }  } @@ -24,17 +26,50 @@ fn recurse_replace_ident(      token_stream: TokenStream,      target_ident: &Ident,      substitution: &TokenTree, +    prefix: char,  ) -> TokenStream  { +    let mut prev_token_was_prefix = false; +      token_stream          .into_iter() -        .map(|token_tree| match token_tree { -            TokenTree::Ident(ident) if &ident == target_ident => substitution.clone(), -            TokenTree::Group(group) => TokenTree::Group(Group::new( -                group.delimiter(), -                recurse_replace_ident(group.stream(), target_ident, substitution), -            )), -            tt => tt, +        .windows() +        .filter_map(|(token_tree, next_token_tree)| match token_tree { +            TokenTree::Punct(punct) if punct.as_char() == prefix => { +                if matches!(next_token_tree, Some(TokenTree::Ident(ident)) if &ident == target_ident) { +                    prev_token_was_prefix = true; + +                    None +                } +                else { +                    prev_token_was_prefix = false; + +                    Some(TokenTree::Punct(punct)) +                } +            } +            TokenTree::Ident(ident) if prev_token_was_prefix && &ident == target_ident => { +                prev_token_was_prefix = false; + +                Some(substitution.clone()) +            } +            TokenTree::Ident(_) if prev_token_was_prefix => { +                prev_token_was_prefix = false; + +                Some(substitution.clone()) +            } +            TokenTree::Group(group) => { +                prev_token_was_prefix = false; + +                Some(TokenTree::Group(Group::new( +                    group.delimiter(), +                    recurse_replace_ident(group.stream(), target_ident, substitution, prefix), +                ))) +            } +            tt => { +                prev_token_was_prefix = false; + +                Some(tt) +            }          })          .collect()  } @@ -46,7 +81,7 @@ mod tests      use std::hint::black_box; -    use proc_macro2::Span; +    use proc_macro2::{Punct, Spacing, Span};      use quote::{format_ident, quote};      use test::Bencher; @@ -55,9 +90,11 @@ mod tests      #[test]      fn replace_ident_works()      { +        let ht = Punct::new('#', Spacing::Alone); +          assert_eq!(              quote! { -                let abc = xyz; +                let abc = #ht xyz;              }              .replace_ident(                  &format_ident!("xyz"), @@ -72,7 +109,7 @@ mod tests          assert_eq!(              quote! { -                let abc = (xyz, "123"); +                let abc = (#ht xyz, "123");              }              .replace_ident(                  &format_ident!("xyz"), @@ -87,7 +124,7 @@ mod tests          assert_eq!(              quote! { -                let abc = (hello, "123").iter_map(|_| xyz); +                let abc = (hello, "123").iter_map(|_| #ht xyz);              }              .replace_ident(                  &format_ident!("xyz"), @@ -102,13 +139,15 @@ mod tests          assert_eq!(              quote! { -                fn xyz(bar: &Bar) { +                fn #ht xyz(bar: &Bar) {                      let foo = "baz";                      foo                  } -                println!("Hello {}", xyz()); +                let xyz = "123"; + +                println!("Hello {}", #ht xyz());              }              .replace_ident(                  &format_ident!("xyz"), @@ -122,6 +161,8 @@ mod tests                      foo                  } +                let xyz = "123"; +                  println!("Hello {}", foo());              }              .to_string() @@ -131,15 +172,17 @@ mod tests      #[bench]      fn bench_replace_ident(bencher: &mut Bencher)      { +        let ht = Punct::new('#', Spacing::Alone); +          let input_strem = quote! { -            let foo = |abc| { +            let #ht foo = |abc| {                  let x = "foo";                  let y = a_foo; -                let foo = "Hello"; +                let #ht foo = "Hello"; -                foo.to_string() +                #ht foo.to_string()              };          };  | 
