Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/modprog/forr
meta-for
https://github.com/modprog/forr
Last synced: 2 months ago
JSON representation
meta-for
- Host: GitHub
- URL: https://github.com/modprog/forr
- Owner: ModProg
- License: apache-2.0
- Created: 2023-05-02T12:31:27.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-07-25T22:54:15.000Z (6 months ago)
- Last Synced: 2024-09-17T16:44:57.098Z (4 months ago)
- Language: Rust
- Size: 81.1 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
### forr
[![CI Status](https://github.com/ModProg/forr/actions/workflows/test.yaml/badge.svg)](https://github.com/ModProg/forr/actions/workflows/test.yaml)
[![Crates.io](https://img.shields.io/crates/v/forr)](https://crates.io/crates/forr)
[![Docs.rs](https://img.shields.io/crates/v/template?color=informational&label=docs.rs)](https://docs.rs/forr)
[![Documentation for `main`](https://img.shields.io/badge/docs-main-informational)](https://modprog.github.io/forr/forr/)[Control flow](#iff) and [loops](#forr) in compile time.
### `forr!`
Aims to replace single use [`macro_rules!`](https://doc.rust-lang.org/reference/macros-by-example.html) for the purpose to repeating code.
For example it can reduce an implementation for multiple tuples:
```rust
use forr::forr;
trait Like { fn like (&self, other: &Self) -> bool; }
impl Like for i32 { fn like (&self, other: &Self) -> bool { self == other } }forr! { $gens:inner in [(), (A,), (A, B), (A, B, C),
(A, B, C, D), (A, B, C, D, E)] $*
// $idx is special this would be like (idx, gen) = [...].enumerate()
forr! { $idx:idx, $gen:expr in [$gens] $:
impl<$($gen: Like,)*> Like for ($gens) {
fn like(&self, other: &Self) -> bool {
$(self.$idx.like(&other.$idx)&&)* true
}
}
}
}assert!((1, 3).like(&(1, 3)));
assert!((1,).like(&(1,)));
assert!(().like(&()));
```With macro-rules this would be:
```rust
macro_rules! impl_like_for_tuples {
[$(($gen:ident, $idx:tt), $(($gens:ident, $idxs:tt)),*)?$(,)?] => {
impl$(<$gen: Like, $($gens: Like),*>)? Like for ($($gen, $($gens),*)?) {
fn like(&self, other: &Self) -> bool {
$(self.$idx.like(&other.$idx) &&)?
$($(self.$idxs.like(&other.$idxs) &&)*)?
true
}
}
$(impl_like_for_tuples![$(($gens, $idxs)),*,];)?
}
}
impl_like_for_tuples![(E, 4), (D, 3), (C, 2), (B, 1), (A, 0)];assert!((1, 3).like(&(1, 3)));
assert!((1,).like(&(1,)));
assert!(().like(&()));
```Granted in this example it is not a lot more complicated, and adding more
tuple variants actually requires less code. But it took me quite a few more
trys getting it to work correctly. (If you don't count me implementing this
whole library for the first one.)### Usage
The first part of the invocation is the pattern, similar to a normal `for`
loop in rust. Here you can use either a [single
variable](#single-variable-binding) i.e. `$name:type` or a [tuple
binding](#tuple-binding) `($name:type, $nbme:type, ...)`. There can
optionally be [non consuming patterns](#non-consuming-patterns) specified
before or after, currently that includes only [`:idx`](#idx).This is followed by the keyword `in`, an array literal `[...]` containing
the tokens to iterate and the [body](#body) marked with either `$*` or `$:`.#### Single variable binding
`$` [`name`](#names) `:` [`type`](#types)
```rust
forr! { $val:expr in [1, 2 + 4, 20]
```
`$val` will be `1`, `2 + 4` and `20`.#### Tuple binding
`(` [`$name:type`](#single-variable-binding), ... `)`
```rust
forr! { ($val:expr, $vbl:ty) in [(1, i32), (Ok(2 + 4), Result), (20.0, f32)]
```
`$val` will be `1`, `Ok(2 + 4)` and `20.0`.`$vbl` will be `i32`, `Result` and `f32`.
#### Non consuming patterns
Non consuming patterns can be specified before or after the consuming
patterns or inside if using a tuple binding.Currently, only [`:idx`](#idx) is supported
```rust
forr! { $val:expr, $i:idx in [1, 2]
forr! { $i:idx, $val:expr in [1, 2]
forr! { $i:idx, ($val:expr, $vbl:ty) in [(1, i32), (2, i32)]
forr! { ($val:expr, $vbl:ty), $i:idx in [(1, i32), (2, i32)]
forr! { ($val:expr, $i:idx, $vbl:ty) in [(1, i32), (2, i32)]
```
`$val` will be `1` and `2``$i` will be `0` and `1`
`$vbl` will be `i32`
#### Body
The body can be in two different modes. When it is initialized with `$*` the
whole body is repeated similar to a normal for loop. Is it started with
`$:`, the body will behave more like macro expansion using `$()*` for
repetition. In both cases there is special handling for [optional
values](#optional values) when placed inside `$()?` the innermost such group
is only added if the value is present.##### `$*` outer repetition
In the tokens following the `$*` every occurrence of a `$ident` where the
ident matches one of the declared variables is replaced with the
corresponding value.
```rust
forr! {$val:expr in [(1, "a", true)] $*
assert_eq!($val, $val);
}
```
will expand to
```rust
assert_eq!(1, 1);
assert_eq!("a", "a");
assert_eq!(true, true);
```##### `$:` inner repetition
`$:` allows to have non repeated code surrounding the expansion, mainly
useful for cases where a macro would not be allowed.```rust
forr! {($pat:expr, $res:expr) in [(0, true), (1, false), (2.., true)] $:
match 1u8 {
$($pat => $res,)*
}
}
```
Without the inner repetition this would not be possible, as macros are not
allowed as the body of a `match`.
```rust
match 1 {
forr! {($pat:expr, $res:expr) in [(0, true), (1, false), (2.., true)] $*
$pat => $res
}
}
```### Names
Any valid rust idents including keywords can be used to name variables. Note
that shadowing does not work, i.e. an inner `forr!` needs to use different
names.### Types
#### `:expr`
As this uses [`TokenParser::next_expression()`](https://docs.rs/proc-macro-utils/latest/proc_macro_utils/struct.TokenParser.html#method.next_expression) this will allow anything
that matches the `,` rules for expressions i.e. it cannot contain `;` and no
`,` outside turbofishes for Types/Generics (`HashMap::`).#### `:ty`
As this uses [`TokenParser::next_type()`](https://docs.rs/proc-macro-utils/latest/proc_macro_utils/struct.TokenParser.html#method.next_type) this will allow anything
that matches the `,` rules for types i.e. it cannot contain `;` and no
`,` outside `<>` for Generics (`HashMap`) and unopened closing `>`.#### `:tt`
Currently matches exactly one [`proc_macro::TokenTree`](https://doc.rust-lang.org/beta/proc_macro/enum.TokenTree.html), but the plan is to extend this to what [`macro_rule!`'s `:tt` matches](https://doc.rust-lang.org/reference/macros-by-example.html#metavariables).#### `:inner`
The most versatile type, allowing arbitrary tokens wrapped by any bracket
`[`, `{` or `(` ... `)}]`.#### `:idx`
[Non consuming pattern](#non-consuming-patterns) that will contain the
current index.## `iff!`
Aims to be an alternative version of
[`#[cfg(...)]`](https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute)
able to conditionally enable any rust tokens and being able to, e.g. compare
tokens, but it is not able to be conditional other actual `cfg` or features.```rust
iff! { true && false $:
compile_error!("This is not expanded")
}iff! { true || false $:
compile_error!("This is expanded")
}
```On top of the basic boolean operations (`!`, `&&`, `||`) there are some
functions:- `empty()` tests if `` is empty.
- `equals()()` tests if `` is equal to ``.```rust
iff! { empty() $:
compile_error!("This is expanded")
}
iff! { empty(something) $:
compile_error!("This is not expanded")
}
iff! { equals(something)(another thing) $:
compile_error!("Neither is this")
}
```