aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-03-05 19:46:12 +0100
committerHampusM <hampus@hampusmat.com>2023-03-05 20:05:22 +0100
commit3d9c9d7ac68a439a29c8ad0a745a7e2a52234a44 (patch)
treeae37e7242f2ed611ee862f046ae20d235df49e81
parent57ab49b24ed34fcddadb1a293511ee74a066e026 (diff)
refactor: improve replacement syntax
-rw-r--r--src/iter.rs65
-rw-r--r--src/lib.rs29
-rw-r--r--src/repeat.rs9
-rw-r--r--src/token_stream.rs77
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)));
+ }
+}
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<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(
&quote! {
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(
&quote! {
- fn _gl_command_(_gl_command_args_) -> _gl_command_ret_type_ {}
+ fn #ht gl_command(#ht gl_command_args) -> #ht gl_command_ret_type {}
},
&registry,
)
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()
};
};