From 3d9c9d7ac68a439a29c8ad0a745a7e2a52234a44 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 5 Mar 2023 19:46:12 +0100 Subject: refactor: improve replacement syntax --- src/iter.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 29 ++++++++++---------- src/repeat.rs | 9 ++++--- src/token_stream.rs | 77 +++++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 146 insertions(+), 34 deletions(-) create mode 100644 src/iter.rs 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; +} + +impl IteratorExt for Iter +where + Iter::Item: Clone, +{ + fn windows(self) -> Windows + { + Windows { + iter: self.peekable(), + } + } +} + +pub struct Windows +{ + iter: Peekable, +} + +impl Iterator for Windows +where + Iter::Item: Clone + Debug, +{ + type Item = (Iter::Item, Option); + + fn next(&mut self) -> Option + { + 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))); + } +} diff --git a/src/lib.rs b/src/lib.rs index 0ec71be..555cd58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 = 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`**
-/// Will be replaced by the command name. +/// Will be replaced by the full command name. For example, `glViewport`. /// /// **`gl_command_ret_type`**
-/// Will be replaced by the command return type. +/// Will be replaced by the command return type. For example, `GLuint`. /// /// **`gl_command_args`**
-/// 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`**
-/// Will be replaced by the command argument names. +/// Will be replaced by the command argument names, seperated by comma. /// /// **`gl_command_arg_types`**
-/// 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() }; }; -- cgit v1.2.3-18-g5258