Expressions et opérateurs
Les expressions combinent des valeurs pour en produire d’autres. Savoir lire leur ordre de calcul est essentiel pour éviter les surprises et écrire des conditions limpides.
Une expression produit une valeur
Un littéral, un nom, un appel de procédure ou une opération peut former une expression. Dans prix * quantite + frais, les multiplications et additions produisent un résultat. Dans score >= minimum, la comparaison produit un booléen. Cette idée aide à construire un programme : les expressions calculent des valeurs, puis les instructions décident quoi en faire.
proc prix_total(prix: i64, quantite: i64, livraison: i64) -> i64 {
let sous_total: i64 = prix * quantite;
return sous_total + livraison;
}
Calculs et priorité
Pour les calculs courants, *, / et % se font avant + et -. Les parenthèses se font d’abord. Ainsi, 2 + 3 * 4 vaut 14, tandis que (2 + 3) * 4 vaut 20. Les décalages, les opérations bit à bit, les comparaisons, les égalités et les opérateurs logiques ont eux aussi un ordre de priorité défini par la grammaire.
vous n’avez pas besoin de mémoriser toute la table pour écrire du code lisible. Dans une expression de plus de deux opérations, ajoutez des parenthèses quand elles expliquent votre intention, même si la priorité du langage suffirait.
examples/tutorials/expressions.vit vérifie que 2 + 3 * 4 vaut 14 et que (2 + 3) * 4 vaut 20.
build/bin/vitte run examples/tutorials/expressions.vit --quiet --no-colorRésultat confirmé : code de sortie 0 si les deux calculs donnent ces valeurs.
| Ordre, du moins prioritaire au plus prioritaire | Opérateurs |
|---|---|
| Affectation | =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= |
| Valeur de repli | ?? |
| OU logique | or, || |
| ET logique | and, && |
| OU bit à bit | | |
| OU exclusif bit à bit | ^ |
| ET bit à bit | & |
| Égalité | ==, != |
| Comparaison | <, <=, >, >= |
| Décalage | <<, >> |
| Addition et soustraction | +, - |
| Multiplication et division | *, /, % |
| Plage | .., ..= |
| Unaire | +, -, !, ~, *, &, not, move, ref, await |
| Postfixe | Appel, indice, champ, ? |
La table résume les niveaux de la grammaire, du moins prioritaire au plus prioritaire. L’affectation et ?? s’associent vers la droite ; les autres familles listées se regroupent de gauche à droite selon la grammaire. ?? exprime une valeur de repli pour une donnée nullable. Comme les opérateurs avancés ne sont pas tous disponibles dans chaque étape du compilateur, vérifiez-les sur les sources et l’outil de votre version.
Comparer puis combiner
Les opérateurs == et != testent l’égalité ; <, <=, > et >= comparent un ordre. Ils donnent un bool. On combine les résultats avec and ou &&, et avec or ou ||. not ou ! inverse une condition.
proc admissible(age: i64, a_un_billet: bool) -> bool {
return age >= 18 and a_un_billet;
}
En lecture, and signifie que les deux conditions sont requises ; or signifie qu’au moins l’une suffit. La grammaire et le compilateur définissent l’évaluation des opérateurs logiques. Quand l’ordre d’un effet de bord compte, évitez de cacher un appel qui modifie l’état dans une condition composée.
Opérateurs binaires et affectations
Vitte prévoit également des opérateurs bit à bit (&, |, ^), des décalages (<<, >>) et des affectations composées dans la grammaire, comme += ou <<=. Ils sont surtout utiles quand on travaille sur des masques de bits, des drapeaux ou des représentations bas niveau. Ce sont des opérateurs distincts des mots logiques and et or.
Un opérateur reconnu par la grammaire peut ne pas être disponible dans chaque étape du compilateur. Pour le code ordinaire, commencez avec l’arithmétique, les comparaisons et les booléens ; explorez les opérations binaires seulement avec les exemples du dépôt et une vérification de l’outil.
Appels, accès et conversions
Après une expression, les parenthèses peuvent appeler une procédure, les crochets indexer un tableau, et le point lire un champ de forme. Les chemins qualifiés emploient ::. La grammaire prévoit aussi le point d’interrogation postfixé pour certaines expressions optionnelles ainsi que as Type pour une conversion explicite.
proc lire_tableau() -> i64 {
let valeurs: [3]i64 = [8, 13, 21];
return valeurs[1] + valeurs[2];
}
Cette expression fait d’abord deux accès par indice, puis une addition. Si vous devez vérifier les bornes, décomposez le calcul en variables nommées et consultez les opérations de la bibliothèque adaptées au type de données.
Une méthode simple pour déboguer un calcul
- Repérez les parenthèses déjà présentes.
- Notez le type de chaque littéral, variable et appel.
- Ajoutez des parenthèses temporaires pour écrire l’ordre de regroupement attendu.
- Remplacez les sous-expressions par des variables nommées si le calcul reste difficile à expliquer.
Le meilleur calcul est celui dont une personne peut vérifier le sens sans faire une devinette sur la priorité des opérateurs.
Dessiner l’ordre du calcul
Une expression se lit comme une structure imbriquée. Dans 2 + 3 * 4, la multiplication forme d’abord une sous-expression, puis son résultat est ajouté à 2 :
+
/ \
2 *
/ \
3 4
La structure correspond à 2 + (3 * 4) et donne 14. Avec (2 + 3) * 4, les parenthèses forment la sous-expression gauche avant la multiplication et le résultat devient 20. Cette représentation en arbre est utile dès que les expressions mêlent calcul, comparaison et booléens.
Suivre le type de chaque nœud
Pour vérifier une expression, calcule son type en même temps que sa valeur. Pour prix * quantite + livraison, les deux opérandes de la multiplication doivent être compatibles ; la multiplication produit un type ; l’addition doit ensuite combiner ce résultat et livraison. Pour age >= 18 and a_un_billet, la comparaison produit un booléen, puis and combine deux booléens.
Si le compilateur signale un opérande invalide, marque les types de chaque feuille de l’expression, puis remonte vers le nœud fautif. La fixture erreur-type.vit montre précisément ce genre de faute : un entier est utilisé avec un opérateur logique qui attend des booléens.
Ne pas confondre opérateurs qui se ressemblent
and et & ne désignent pas la même opération : le premier exprime une combinaison logique ; le second appartient aux opérations bit à bit dans la grammaire. De même, == compare deux valeurs, alors que = apparaît comme opérateur d’affectation dans la syntaxe. Dans les exemples de mise à jour, l’affectation s’écrit avec set.
Les formes d’affectation composée, les opérateurs de plage, les décalages et les casts sont décrits dans les documents, mais toutes leurs variantes ne sont pas confirmées à chaque étape du compilateur. Distinguez donc deux questions : « comment se lit cet opérateur ? » et « ce cas précis est-il compilé par l’outil que j’utilise ? ».
Rendre un calcul vérifiable
Quand une expression devient difficile à expliquer, nomme ses étapes :
let sous_total: i64 = prix * quantite;
let remise: i64 = sous_total * taux / 100;
let prix_final: i64 = sous_total - remise;
Cette décomposition donne trois endroits où vérifier les types, les valeurs et les règles d’arrondi. Elle rend également un diagnostic plus local : vous pouvez voir si le problème vient du sous-total, du calcul de remise ou de la soustraction finale. Dans un calcul financier réel, définissez explicitement l’unité et l’arrondi ; un type entier n’indique pas à lui seul si une valeur représente des euros, des centimes ou un pourcentage.
2 + 3 * 4 se regroupe en 2 + (3 * 4), soit 2 + 12 = 14. (2 + 3) * 4 se regroupe en 5 * 4 = 20. Pour une condition entre 10 et 20 inclus, la forme attendue est valeur >= 10 and valeur <= 20 ; Les valeurs 9, 10, 20 et 21 produisent respectivement faux, vrai, vrai et faux pour cette condition..
Priorité, associativité et parenthèses
Quand une expression contient plusieurs opérateurs, la grammaire précise leur ordre de regroupement. Par exemple, 2 + 3 * 4 est lu comme 2 + (3 * 4), parce que la multiplication a priorité sur l’addition ; le résultat vaut 14. Les parenthèses changent explicitement ce regroupement : (2 + 3) * 4 vaut 20. Elles sont utiles même lorsque la priorité est connue, car elles rendent l’intention visible au lecteur.
L’associativité décide comment regrouper plusieurs opérateurs de même priorité. Une soustraction en chaîne se lit de gauche à droite : 20 - 5 - 3 signifie (20 - 5) - 3, soit 12. Changer le regroupement donnerait 20 - (5 - 3), soit 18. Les opérateurs d’affectation ont leurs propres règles et les formes réellement prises en charge dépendent du frontend ; un tableau de précédence décrit la grammaire, pas l’achèvement de chaque chemin de compilation.
Le type d’une expression se calcule de proche en proche
Une expression possède un type produit par sa structure. Une comparaison comme age >= 18 produit un booléen. L’opérateur logique and combine des valeurs booléennes ; une addition combine des opérandes compatibles et produit un résultat numérique. Dans prix * quantite + livraison, il faut donc pouvoir expliquer le type de la multiplication avant celui de l’addition finale.
Cette lecture est utile face à une erreur de type. L’opérateur le plus à gauche n’est pas nécessairement la cause : une sous-expression peut déjà avoir un type inattendu. Les annotations et les variables intermédiaires rendent les nœuds du calcul visibles et rapprochent le diagnostic de la valeur qui pose problème.
Valeur calculée et effet sur l’état
Une expression comme prix * quantite calcule une valeur ; l’instruction set total = total + 1; met à jour une liaison mutable. Les séparer aide à comprendre l’effet d’une ligne : la partie droite calcule ce qui sera assigné, tandis que set annonce le changement. La différence entre ==, qui compare, et set, qui modifie, est donc fondamentale pour suivre une condition ou un état.
Il faut également séparer les opérateurs logiques des opérateurs bit à bit. and travaille sur des conditions booléennes ; & représente une opération sur les bits dans la grammaire. Leur orthographe proche ne leur donne pas le même domaine. Pour toute expression complexe, les parenthèses, les types des sous-expressions et la distinction entre calcul et mise à jour forment une explication reproductible.
Conversions et opérateurs de cast
Une expression de conversion relie une valeur source à une représentation cible. Le cast valeur as u8 est visible dans la syntaxe et apparaît dans un exemple vérifié, mais cette preuve vaut pour ce contexte précis. Elle ne confirme pas automatiquement les conversions de signe, de flottant vers entier, les dépassements ou chaque position où un cast peut apparaître.
La conversion et la validation répondent à deux questions distinctes. La conversion demande quelle représentation est produite ; la validation demande si la valeur respecte la règle du domaine. Pour une entrée externe comprise entre 0 et 255, la vérification de cette plage doit précéder ou accompagner la conversion vers u8. Sans cela, le programme peut obtenir une valeur typée tout en ayant perdu le sens de l’entrée.
Expressions composées et variables intermédiaires
Une expression longue peut être découpée en valeurs nommées. Dans un panier, sous_total, remise et prix_final donnent des repères entre la multiplication, le pourcentage et la soustraction. Le découpage n’est pas seulement stylistique : chaque liaison reçoit un type, un point de diagnostic et une signification métier. Il devient plus facile de vérifier où une conversion ou un arrondi intervient.
Lorsque la priorité des opérateurs est complexe, des parenthèses définissent le regroupement et les noms intermédiaires exposent le résultat des sous-calculs. Une affectation mutable devrait rester une instruction identifiable avec set, plutôt que d’être confondue avec une comparaison ou une expression arithmétique. Cette séparation aide à relier la valeur calculée à l’état modifié.