Enchaîner les calculs
Le langage Elixir permet d'enchaîner les calculs grâce à son opérateur |>
qui insère son argument de gauche comme premier élément de l'appel de fonction de son argument de droite :
iex> "Elixir" |> String.graphemes() |> Enum.frequencies()
%{"E" => 1, "i" => 2, "l" => 1, "r" => 1, "x" => 1}
Nous voudrions pouvoir faire la même chose en Rust. Comme nous souhaitons utiliser un visiteur, nous allons prendre en entrée un arbre valide en Rust. Nous allons donc sacrifier le caractère |
(pipe, qui représente le « ou bit-à-bit » à cet effet).
#[pipe]
fn pipe_example() {
let f = "Rust" | str::chars | Vec::from_iter;
assert_eq!(f, vec!['R', 'u', 's', 't']);
}
Ce code est équivalent à :
fn pipe_example() {
let f = Vec::from_iter(str::chars("Rust"));
assert_eq!(f, vec!['R', 'u', 's', 't']);
}
Notre macro #[pipe]
et son opérateur |
doivent également supporter les appels de fonctions à plusieurs arguments. L'expression a | f(b, c)
est équivalente à f(a, b, c)
.
Implémentation
- À l'aide d'un visiteur
syn::visit_mut::VisitMut
, interceptez l'analyse des expressions et remplacez une expression binaire utilisant l'opérateur|
et suivie d'un appel de fonction ou d'un chemin par un appel de fonction qui utilise l'argument de gauche comme premier argument de l'appel. N'oubliez pas de récurser ensuite pour visiter le nouveau nœud (ou les sous-nœuds si aucune transformation n'a été faite).
Demandez-vous quel type de nœud vous souhaitez visiter dans le but de le modifier. Si par exemple vous modifiez un nœud de type ExprBinary
qui représente une expression binaire, vous ne pourrez pas le remplacer par un nœud qui ne soit pas une expression binaire. Si vous modifiez un nœud de type Expr
qui est moins spécifique cela vous permet de remplacer une expression par une autre.
Également, n'oubliez pas que vous pouvez utiliser syn::parse_quote!()
pour générer des nœuds d'un type quelconque à partir d'un template.
-
Créez une macro procédurale de type attribut
pipe
qui utilise ce visiteur pour transfomer n'importe quel item en utilisant le visiteur. -
Ajoutez des tests vérifiant le comportement correct de cette macro, qui doit pouvoir s'appliquer aussi bien à une fonction qu'à un module ou à une implémentation.