Utilisez ce guide pour consulter rapidement toutes les fonctionnalités du langage de programmation Verse et sa syntaxe. Suivez les liens pour en savoir plus.
Expressions
Une expression est la plus petite unité de code qui produit une valeur lorsqu'elle est évaluée. Par exemple, dans Verse, if... else est évaluée sur une valeur qui dépend du contenu des blocs d'expression.
Le code suivant est évalué sur une valeur de chaîne qui contient soit "Big!" soit "Small!", selon que MyNumber est supérieur à 5 :
if (MyNumber > 5):
"Big!"
else:
"Small!"
Commentaires de code
Un commentaire de code fournit des explications sur le code ou indique la raison pour laquelle le programmeur a procédé à telle ou telle programmation. Lorsque le programme s'exécute, les commentaires de code sont ignorés.
Verse | Commentaire sur une seule ligne : tout le texte qui se trouve entre le signe |
Verse | Commentaire de bloc intégré : tout le texte qui se trouve entre les signes |
Verse | Commentaire de bloc sur plusieurs lignes : tout le texte qui se trouve entre les signes |
Verse | Commentaire de bloc imbriqué : tout le texte qui se trouve entre les signes |
Verse | Commentaire en retrait : tout le texte qui se trouve sur de nouvelles lignes après le signe |
Constantes et variables
Les constantes et les variables peuvent stocker des informations, c'est-à-dire des valeurs que votre programme utilise et les associer à un nom. Ce nom correspond à l'identificateur.
Pour créer votre propre variable ou constante, vous devez en informer Verse. Cette procédure est appelée déclaration. La spécification d'une valeur initiale, appelée initialisation, est facultative (bien que recommandée) pour les variables, mais obligatoire pour les constantes.
Vous pouvez à tout moment modifier la valeur d'une variable. Cette opération est appelée assignation, car vous assignez une valeur à la variable, mais on parle parfois aussi de définition d'une variable.
Verse | Création d'une constante : il est impossible de modifier la valeur d'une constante pendant l'exécution du programme. Vous créez une constante en spécifiant son nom, son type et sa valeur. | Cliquez sur l'image pour l'agrandir. |
Verse | Création d'une variable : il est possible de modifier la valeur d'une variable pendant l'exécution du programme. Vous créez une variable en ajoutant le mot clé | Cliquez sur l'image pour l'agrandir. |
Verse | Modification de la valeur d'une variable : vous pouvez modifier la valeur d'une variable pendant l'exécution du programme en utilisant le mot clé | Cliquez sur l'image pour l'agrandir. |
Types
Verse est un langage de programmation typé statiquement, ce qui signifie qu'un type est attribué à chaque identificateur.
Dans certains cas, le type n'est pas explicitement requis, notamment lors de la création d'une constante dans une fonction. Dans l'exemple MyConstant := 0, le type de MyConstant est déduit, car la valeur 0 lui est affectée.
Types courants
Verse possède des types intégrés qui prennent en charge les opérations fondamentales que la plupart des programmes doivent effectuer. Vous pouvez créer vos propres types en les combinant dans des structures plus importantes, mais il est important de comprendre ces types courants, qui constituent la base de l'utilisation des variables et des constantes dans Verse.
Verse | logic : dans Verse, une |
Verse | int : dans Verse, le type
|
Verse | float : un
|
Verse | string : dans Verse, un type Vous pouvez injecter le résultat d'une expression dans une chaîne en utilisant {} entre "". Cette opération est appelée interpolation de chaîne. Pour en savoir plus sur le type |
Verse | message : dans Verse, le type Vous pouvez convertir le texte en une chaîne affichable au moment de l'exécution en utilisant la fonction Actuellement, le seul texte qui peut être renvoyé par la fonction |
Verse | locale : ce type indique dans quel contexte une valeur de Dans la mesure où le type |
Verse | rational : le type
|
Verse | void : le type
Pour en savoir plus sur le type |
Verse | any : le type |
Verse | comparable : le type |
Pour en savoir plus sur les types communs dans Verse, consultez la rubrique Types communs.
Types de conteneurs
Vous pouvez stocker plusieurs valeurs ensemble en utilisant un type de conteneur. Verse dispose d'un certain nombre de types de conteneurs pour stocker des valeurs. Pour en savoir plus sur les types de conteneurs dans Verse, consultez la rubrique Types de conteneurs.
Option
Le type option peut contenir une valeur ou peut être vide.
Dans l'exemple suivant, MaybeANumber est un nombre entier facultatif ?int qui ne contient aucune valeur. Une nouvelle valeur pour MaybeANumber est alors définie sur 42.
var MaybeANumber : ?int = false # unset optional value
set MaybeANumber := option{42} # assigned the value 42
Cliquez sur l'image pour l'agrandir.
Verse | Création d'une option : vous pouvez initialiser une option avec l'une des valeurs suivantes :
Précisez le type en ajoutant |
Verse | Accéder à un élément dans une option : utiliser l'opérateur de requête |
Voici un exemple d'utilisation du type `option` pour enregistrer une référence à un joueur qui apparaît et, dès que le joueur apparaît, pour activer l'appareil Déclencheur :
my_device := class<concrete>(creative_device):
var SavedPlayer : ?player = false # unset optional value
@editable
PlayerSpawn : player_spawner_device = player_spawner_device{}
@editable
Trigger : trigger_device = trigger_device{}
OnBegin<override>() : void =
Plage
L'expression range contient tous les nombres dans une plage spécifiée, et ne peut être utilisée que dans des expressions spécifiques, notamment une expression for. Les valeurs de plage ne peuvent être que des nombres entiers.
Cliquez sur l'image pour l'agrandir.
Verse | Création d'une plage : le début de la plage est la première valeur de l'expression, et la fin de la plage est la valeur qui suit |
Verse | Itération sur une plage : vous pouvez utiliser l'expression |
Pour en savoir plus, consultez la rubrique Range.
Tableau
Une matrice est un conteneur dans lequel vous pouvez stocker des éléments de même type et y accéder par leur position, appelée index, dans la matrice. Le premier index d'une matrice est 0, et le dernier index est inférieur d'une unité au nombre d'éléments de la matrice.
Players : []player = array{Player1, Player2}
Cliquez sur l'image pour l'agrandir.
Verse | Création d'une matrice : utilisez le mot clé |
Verse | Accès à un élément d'une matrice : utilisez |
Verse | Modification d'un élément dans une matrice : vous pouvez modifier la valeur stockée dans une variable de matrice à un index en utilisant le mot clé |
Verse | Itération sur une matrice : vous pouvez accéder à chaque élément d'une matrice, dans l'ordre (du premier au dernier) en utilisant l'expression for. Avec l'expression |
Verse | Obtention du nombre d'éléments d'une matrice : utilisez |
Verse | Concaténation de matrices : vous pouvez fusionner des matrices en utilisant l'opérateur |
Pour en savoir plus, consultez la rubrique Matrice.
Tuple
Un tuple est un regroupement de deux ou plusieurs expressions qui sont traitées comme une seule expression. L'ordre des éléments d’un tuple dans l'expression est important. La même expression peut se trouver à plusieurs emplacements dans un tuple. Les expressions de tuple peuvent être de n'importe quel type et peuvent contenir des types mixtes (contrairement aux matrices, qui ne peuvent avoir que des éléments d'un seul type).
Les tuples sont particulièrement utiles :
Lors du renvoi de plusieurs valeurs à partir d'une fonction.
Lors de la transmission de plusieurs valeurs à une fonction.
Lorsque le regroupement en place est plus concis que la création d'une structure de données réutilisable (telle qu'une structure ou une classe.
VerseExampleTuple : tuple(int, float, string) = (1, 2.0, "three")
Cliquez sur l'image pour l'agrandir.
Verse | Création d'un tuple : utilisez |
Accès à un élément dans un tuple : utilisez | |
Expansion de tuple : lorsque vous utilisez un tuple comme argument d'une fonction au lieu de spécifier chaque argument individuel, la fonction est appelée avec chaque élément du tuple utilisé comme argument dans l'ordre où il est spécifié dans le tuple. | |
Forçage des matrices de tuple : les tuples peuvent être passés partout où une matrice est attendue, à condition que le type des éléments du tuple soit le même que celui de la matrice. Les matrices ne peuvent pas être passées là où un tuple est attendu. |
Pour en savoir plus, consultez la rubrique Tuple.
Mappage
Un mappage est un conteneur dans lequel vous pouvez stocker des valeurs associées à une autre valeur, appelées paires clé-valeur, et accéder aux éléments par leurs clés uniques.
WordCount : [string]int = map {"apple" => 11, "pear" => 7}
Cliquez sur l'image pour l'agrandir.
Création d'un mappage : utilisez le mot clé | |
Accès à un élément dans un mappage : utilisez | |
Itération sur un mappage : vous pouvez accéder à chaque élément d'un mappage, dans l'ordre, du premier élément inséré au dernier, en utilisant l'expression for. Avec l'expression | |
Obtention du nombre de paires clé-valeur d'un mappage : utilisez |
Pour en savoir plus, consultez la rubrique Mappage.
Types composites
Un type composite est un type qui peut être composé de champs ou d'éléments (généralement nommés) de primitives ou d'autres types. Les types de composites possèdent généralement un nombre fixe de champs ou d'éléments pour leur durée de vie. Pour en savoir plus, consultez la rubrique Types composites.
Enum
Le terme énumération, abrégé enum, signifie nommer ou énumérer une série d'éléments, appelés énumérateurs. Dans Verse, `enum` est un type qui peut être utilisé pour des éléments tels que les jours de la semaine ou les directions d'une boussole.
Cliquez sur l'image pour l'agrandir.
Création d'une énumération : utilisez le mot clé | |
Accès à un énumérateur : utilisez |
Structure
Struct est l'abréviation de structure, qui est un moyen de regrouper plusieurs variables apparentées. Les variables peuvent être de n'importe quel type.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Création d'une structure : utilisez le mot clé | |
Instanciation d'une structure : vous pouvez créer une instance d'une structure à partir d'un archétype. Un archétype définit les valeurs des champs d'une structure. | |
Accès aux champs sur une structure : vous pouvez accéder aux champs d'une structure pour obtenir leurs valeurs en ajoutant |
Classe
Une classe est un modèle permettant de créer des objets ayant des comportements et des propriétés (variables et méthodes) similaires. La classe doit être instanciée pour créer un objet ayant des valeurs réelles. Les classes sont hiérarchiques, ce qui signifie qu'une classe peut hériter des informations de son parent (superclasse) et partager ses informations avec ses enfants (sous-classes). Une classe peut être un type personnalisé défini par l'utilisateur.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Création d'une classe : les définitions qui sont imbriquées à l'intérieur d'une classe définissent les champs de la classe. Les fonctions définies comme champs d'une classe sont également appelées méthodes. | |
Instanciation d'une classe : vous pouvez créer une instance d'une classe en utilisant le nom de cette classe suivi de | |
Accès aux champs sur une classe : vous pouvez accéder aux champs d'une classe pour obtenir leurs valeurs en ajoutant | |
Accès aux méthodes sur une classe : vous pouvez accéder aux méthodes d'une classe pour les appeler en ajoutant | |
Self : vous pouvez utiliser |
Il existe également des spécificateurs propres aux classes qui modifient leur comportement. Consultez la rubrique Spécificateur de classe pour en savoir plus.
Pour en savoir plus sur les classes, consultez la rubrique Classe.
Sous-classe
Une sous-classe est une classe qui étend la définition d'une autre classe en ajoutant ou en modifiant les champs et les méthodes de cette dernière (appelée la super-classe).
Cliquez sur l'image pour l'agrandir.
Création d'une sous-classe : spécifiez une classe comme sous-classe d'une autre classe en ajoutant l'autre classe entre les | |
Substitution des champs de la super-classe : vous pouvez modifier les valeurs d'un champ défini dans la super-classe pour la sous-classe uniquement en ajoutant le spécificateur | |
Substitution des méthodes sur la super-classe : [INCLUDE:#overriding_methods_on_superclass_description] | |
Super : à la manière de |
Pour en savoir plus, consultez la rubrique Sous-classe.
Constructeur
Un constructeur est une fonction spéciale qui crée une instance de la classe à laquelle il est associé. Vous pouvez l'utiliser pour définir les valeurs initiales du nouvel objet.
Définition d'un constructeur pour une classe : vous pouvez ajouter un constructeur pour une classe en ajoutant le spécificateur | |
Ajout de variables et exécution du code dans le constructeur : vous pouvez exécuter des expressions dans un constructeur avec l'expression block. Vous pouvez aussi introduire de nouvelles variables avec le mot clé | |
Appel d'autres constructeurs dans un constructeur : vous pouvez appeler d'autres constructeurs à partir d'un constructeur. Vous pouvez également appeler les constructeurs de la superclasse de la classe à partir d'un constructeur de la classe, à condition que tous les champs soient initialisés. Lorsqu'un constructeur appelle un autre constructeur et que les deux constructeurs initialisent des champs, seules les valeurs fournies au premier constructeur sont utilisées pour les champs. L'ordre d'évaluation des expressions entre les deux constructeurs correspond à l'ordre d'écriture des expressions (en ce qui concerne les effets secondaires), mais seules les valeurs fournies au premier constructeur sont utilisées. |
Interface
Le type « interface » propose un contrat sur la manière d'interagir avec toute classe qui implémente l'interface. Une interface ne peut pas être instanciée, mais une classe peut hériter de l'interface et implémenter ses méthodes. Une interface est similaire à une classe abstraite, hormis le fait qu'elle ne permet pas une implémentation partielle ou n'autorise pas les champs dans le cadre de la définition.
Cliquez sur l'image pour l'agrandir.
Cliquez sur l'image pour l'agrandir.
Création d'une interface : vous définissez une interface de la même manière qu'une classe, mais avec le mot clé | |
Extension d'une interface : une interface peut étendre la définition d'une autre interface en spécifiant l'interface à étendre entre les | |
Implémentation d'une interface : vous pouvez implémenter une interface avec une classe en spécifiant l'interface entre les | |
Implémentation de plusieurs interfaces : une classe peut implémenter plusieurs interfaces. Les interfaces sont séparées par | |
Hériter d'une interface et d'une autre classe : une classe peut implémenter une interface tout en étant une sous-classe d'une autre classe. L'interface et la super-classe sont séparées par |
Pour en savoir plus, consultez la rubrique Interface.
Utiliser les types
Verse dispose de plusieurs méthodes pour faciliter l'utilisation des types.
Alias de type : vous pouvez donner un nom différent à un type dans votre code et référencer ce type par ce nouveau nom. La syntaxe est similaire à l'initialisation de constante. Vous pouvez également attribuer un alias de type à un type de fonction. Pour en savoir plus, consultez la rubrique Alias de type. | |
Type paramétrique comme argument de type explicite : Verse prend en charge les types paramétriques (types attendus comme arguments). Ceux-ci ne fonctionnent qu'avec les classes et les fonctions. Pour en savoir plus, consultez la rubrique Types paramétriques. | |
Type paramétrique en tant qu'arguments de type implicite pour les fonctions : l'utilisation de types paramétriques implicites avec les fonctions permet d'écrire une seule fois du code invariant d'un type particulier, plutôt que d'écrire du code pour chaque type avec lequel la fonction est utilisée. Pour en savoir plus, consultez la rubrique Types paramétriques. | |
Macro de type : Verse possède une construction spéciale permettant d'obtenir le type d'une expression arbitraire. Cette construction peut être utilisée partout où un type peut être utilisé. Pour en savoir plus, consultez la rubrique Macro de type. | |
Sous-type : vous pouvez utiliser un |
Pour en savoir plus, consultez la rubrique Utiliser les types Verse.
Opérateurs
Les opérateurs sont des fonctions spéciales définies en langage de programmation Verse pour effectuer diverses actions, telles que les opérations mathématiques, sur leurs opérandes.
Lorsque plusieurs opérateurs sont utilisés dans la même expression, ils sont évalués en fonction de leur précédence, de la plus élevée à la plus faible. S'il existe des opérateurs avec la même précédence dans la même expression, ils sont évalués de gauche à droite.
Consultez le tableau ci-dessous pour connaître tous les opérateurs intégrés dans Verse, classés en fonction de leur précédence (de la plus élevée à la plus faible).
| Opérateur | Description | Format de l'opérateur | Précédence des opérateurs | Exemple |
|---|---|---|---|---|
Requête | L'opérateur | postfix (suffixe) | 9 |
|
Non | L'opérateur | Préfixe | 8 |
|
Positif | Vous pouvez utiliser l'opérateur | Préfixe | 8 |
|
Négatif | Vous pouvez utiliser l'opérateur | Préfixe | 8 |
|
Multiplication | L'opérateur | infix (infixe) | 7 |
|
Division | L'opérateur | infix (infixe) | 7 |
|
Addition | L'opérateur + additionne deux valeurs numériques. Lorsqu'il est utilisé avec des chaînes de caractères et des matrices, les deux valeurs sont concaténées. Consultez la rubrique Math pour en savoir plus. | infix (infixe) | 6 |
|
Soustraction | L'opérateur | infix (infixe) | 6 |
|
Affectation d'addition | Cet opérateur vous permet de combiner l'addition et l'affectation dans la même opération pour mettre à jour la valeur d'une variable. Consultez la rubrique Math pour en savoir plus. | infix (infixe) | 5 |
|
Affectation de soustraction | Cet opérateur vous permet de combiner la soustraction et l'affectation dans la même opération pour mettre à jour la valeur d'une variable. Consultez la rubrique Math pour en savoir plus. | infix (infixe) | 5 |
|
Affectation de multiplication | Cet opérateur vous permet de combiner la multiplication et l'affectation dans la même opération pour mettre à jour la valeur d'une variable. Consultez la rubrique Math pour en savoir plus. | infix (infixe) | 5 |
|
Affectation de division | Cet opérateur vous permet de combiner la division et l'affectation dans la même opération pour mettre à jour la valeur d'une variable, à moins que la variable soit un nombre entier. Consultez la rubrique Math pour en savoir plus. | infix (infixe) | 5 |
|
Égal à | L'opérateur | infix (infixe) | 4 |
|
Pas égal à | L'opérateur | infix (infixe) | 4 |
|
Inférieur à | L'opérateur | infix (infixe) | 4 |
|
Inférieur ou égal à | L'opérateur | infix (infixe) | 4 |
|
Supérieur à | L'opérateur | infix (infixe) | 4 |
|
Supérieur ou égal à | L'opérateur | infix (infixe) | 4 |
|
Et | L'opérateur | infix (infixe) | 3 |
|
Ou | L'opérateur | infix (infixe) | 2 |
|
Initialisation des variables et des constantes | Cet opérateur permet de stocker des valeurs dans une constante ou une variable. Consultez la rubrique Constantes et variables pour en savoir plus. | infix (infixe) | 1 |
|
Affectation de variable | Cet opérateur permet de mettre à jour les valeurs stockées dans une variable. Consultez la rubrique Constantes et variables pour en savoir plus. | infix (infixe) | 1 |
|
Pour en savoir plus, consultez la rubrique Opérateurs.
Regroupement
Vous pouvez changer l'ordre d'évaluation des opérateurs en regroupant les expressions avec (). Par exemple, (1+2)*3 et 1+(2*3) ne sont pas évaluées sur la même valeur, car les expressions regroupées dans () sont évaluées en premier.
Dans l'exemple suivant, nous vous expliquons comment utiliser le regroupement pour calculer une explosion dans le jeu dont les dégâts sont proportionnels à la distance du joueur, mais où l'armure du joueur peut réduire les dégâts généraux :
BaseDamage : float = 100.0
Armor : float = 15.0
DistanceScaling : float = Max(1.0, Pow(PlayerDistance, 2.0))
ExplosionDamage : float = Max(0.0, (BaseDamage / DistanceScaling) - Armor)
Consultez la rubrique Regroupement pour en savoir plus.
Blocs de code
Un bloc de code est un groupe de plusieurs expressions ou un groupe sans expressions qui introduit un nouveau corps à étendue. Un bloc de code doit suivre un identificateur. Il peut s'agir de l'identificateur d'une fonction ou de l'identificateur d'un flux de contrôle, par exemple if et for.
Espacé : commence par | |
Plusieurs lignes entre accolades : | |
Une seule ligne entre accolades : |
Il est également possible d'utiliser ; pour mettre plusieurs expressions sur une ligne. Dans un format où chaque expression se trouve sur une nouvelle ligne, il n'est pas nécessaire que les caractères {} se trouvent sur leurs propres lignes.
* La dernière expression d'un bloc de code est le résultat du bloc de code. Dans l'exemple suivant, le bloc de code de l'expression if renvoie le résultat false si IsLightOn? réussit, ou true si IsLightOn? échoue. Le résultat logic est alors stocké dans NewLightState.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
Pour en savoir plus, consultez la rubrique Blocs de code.
Portée
L'étendue désigne le code au sein d'un programme Verse où l'association d'un identificateur (nom) à une valeur est valide, et où ce nom peut être utilisé pour faire référence à la valeur.
Par exemple, les constantes ou les variables que vous créez dans un bloc de code n'existent que dans le contexte de ce bloc de code. Cela signifie que la durée de vie des objets est limitée à l'étendue dans laquele ils ont été créés, et qu'ils ne peuvent pas être utilisés en dehors de ce bloc de code.
Dans l'exemple suivant, nous vous expliquons comment calculer le nombre maximum de flèches que le joueur peut acheter avec le nombre de pièces dont il dispose. La constante MaxArrowsYouCanBuy étant créée dans le bloc if, son étendue est donc limitée au bloc if. Lorsque la constante MaxArrowsYouCanBuy est utilisée dans la chaîne d'impression, une erreur se produit, car le nom MaxArrowsYouCanBuy n'existe pas dans l'étendue en dehors de cette expression if.
CoinsPerQuiver : int = 100
ArrowsPerQuiver : int = 15
var Coins : int = 225
if (MaxQuiversYouCanBuy : int = Floor(Coins / CoinsPerQuiver)):
MaxArrowsYouCanBuy : int = MaxQuiversYouCanBuy * ArrowsPerQuiver
Print("You can buy at most {MaxArrowsYouCanBuy} arrows with your coins.") # Error: Unknown identifier MaxArrowsYouCanBuy
Cliquez sur l'image pour l'agrandir.
Verse ne prend pas en charge la réutilisation d'un identificateur même s'il est déclaré dans une étendue différente, à moins que vous ne qualifiiez l'identificateur en ajoutant (qualifying_scope:) devant cet identificateur, où qualifying_scope correspond au nom du module, de la classe ou de l'interface d'un identificateur. Chaque fois que vous définissez et utilisez l'identificateur, vous devez également ajouter un qualificatif à l'identificateur.
Fonctions
Une fonction est une séquence d'expressions nommée que vous pouvez réutiliser. Une fonction fournit des instructions pour exécuter une action ou créer une sortie en fonction d'une entrée.
Définitions de fonction
Pour définir votre propre fonction, vous devez fournir trois éléments clés : son nom unique (identificateur), le type d'information que vous pouvez attendre comme résultat et l'action déclenchée après l'appel de la fonction. Voici la syntaxe de base d'une fonction :
name() : type =
codeblock
name() et type séparés par deux points : il s'agit de la signature de la fonction, qui indique comment vous devez appeler et utiliser cette fonction, mais qui indique aussi que la valeur qui doit être renvoyée par la fonction est du type que vous fournissez. Ce format est similaire à la façon dont vous créez des constantes, à l'exception de () après le nom, qui émule le mode d'appel de la fonction dans votre code.
Bloc de code de la fonction : vous définissez l'action qu'exécute la fonction lorsqu'elle est appelée en fournissant
=codeblock,codeblockétant une séquence quelconque d'une ou de plusieurs expressions. Chaque fois que vous appelez la fonction, les expressions du bloc de code sont exécutées.
Cliquez sur l'image pour l'agrandir.
Résultats de la fonction
Lorsqu'un type de retour a été spécifié pour votre fonction, le corps de la fonction doit produire un résultat de ce type, auquel cas le code n'est pas compilé.
Dernière expression renvoyée avec une valeur : par défaut, la dernière expression du bloc de code de la fonction correspond au résultat de la fonction, et sa valeur doit correspondre au type de retour de la fonction. | |
Retour explicite avec une valeur : vous pouvez également définir explicitement ce que la fonction va renvoyer en utilisant |
Lorsque vous créez une fonction ne devant produire aucun résultat, vous pouvez définir le type de retour de la fonction sur void, ce qui signifie que la fonction n'est pas censée produire un résultat utile et que la dernière expression du bloc de code de la fonction peut être de n'importe quel type.
Vous pouvez quitter une fonction dont le type de retour est void en utilisant l'expression return seule. Cette expression quitte la fonction immédiatement, même si elle est suivie d'autres expressions dans le bloc de code.
Paramètres de fonction
L'entrée d'une fonction est définie à l'aide de paramètres. Un paramètre est une constante qui est déclarée dans la signature de la fonction entre parenthèses, et que vous pouvez ensuite utiliser dans le corps de la fonction.
La syntaxe d'une fonction avec deux paramètres est la suivante :
name(parameter1name : type, parameter2name : type) : type =
codeblock
Cliquez sur l'image pour l'agrandir.
Dans l'exemple suivant, la fonction IncreaseScore() possède un paramètre entier nommé Points, que la fonction utilise pour augmenter la valeur de MyScore :
var MyScore : int = 100
IncreaseScore(Points : int) : void =
# Increase MyScore by Points.
set MyScore = MyScore + Points
Appels de fonction
Lorsque vous souhaitez utiliser la séquence d'expressions nommée (la fonction) dans votre code, vous devez appeler la fonction par son nom, par exemple GetRandomInt(1, 10), qui renvoie un nombre entier aléatoire compris entre 1 et 10, inclus.
Il existe deux façons d'appeler une fonction, selon que l'appel de la fonction est faillible ou non :
Appel de fonction non faillible : le format d'un appel de fonction qui ne peut pas échouer est le suivant : | |
Appel de fonction faillible : le format d'un appel de fonction faillible est le suivant : |
Arguments de fonction
Lorsque vous appelez une fonction qui attend des paramètres, vous devez attribuer des valeurs aux paramètres, tout comme vous devez attribuer des valeurs aux constantes pour pouvoir les utiliser. Les valeurs affectées sont appelées arguments de la fonction.
La syntaxe d'un appel de fonction avec deux paramètres est la suivante :
name(parameter1name := value, parameter2name := value)
Dans l'exemple suivant, la fonction IncreaseScore() est appelée trois fois, avec des arguments différents à chaque appel (10, 5 et 20), pour augmenter la valeur de MyScore :
# After this call, MyScore is 110
IncreaseScore(Points := 10)
# After this call, MyScore is 115
IncreaseScore(Points := 5)
# After this call, MyScore is 135
IncreaseScore(Points := 20)
Méthodes d'extension
Les méthodes d'extension sont un type de fonction qui se comporte comme les membres d'une classe ou d'un type existant, mais qui n'exige pas la création d'un nouveau type ou d'une nouvelle sous-classe.
L'exemple suivant montre comment créer une méthode d'extension pour les matrices de type int. Cette méthode additionne tous les nombres dans la matrice et renvoie le total.
# Sum extension method for type []int
(Arr : []int).Sum<public>() : int =
var Total : int = 0
for (Number : Arr):
set Total = Total + Number
return TotalIl est donc possible d'appeler la méthode sur toutes les matrices de type int.
SumTotal := array{4, 3, 7}.Sum()
Print("The SumTotal is { SumTotal }")
# "The SumTotal is 14"
Échec
Contrairement aux autres langages de programmation qui utilisent les valeurs booléennes true et false pour modifier le déroulement d'un programme, Verse utilise des expressions qui peuvent soit réussir soit échouer. Ces expressions sont appelées expressions faillibles et ne peuvent être exécutées que dans un contexte d'échec.
Expressions faillibles
Une expression faillible est une expression qui peut réussir et produire une valeur ou échouer et ne produire aucune valeur.
La liste suivante comprend toutes les expressions faillibles dans Verse :
Appels de fonction : uniquement lorsque l'appel de fonction est au format | |
Comparaison : une expression de comparaison compare deux éléments en utilisant l'un des opérateurs de comparaison. Pour en savoir plus, consultez la rubrique Opérateurs. | |
Division de nombres entiers : pour les nombres entiers, l'opérateur de division | |
Décision : une expression de décision utilise les opérateurs | |
Requête : une expression de requête utilise l'opérateur | |
Accès à un élément dans une matrice : l'accès à la valeur stockée dans une matrice est une expression faillible, car il est possible qu'aucun élément ne se trouve à cet index et qu'il doive donc être utilisé dans un contexte d'échec. Pour en savoir plus, consultez la rubrique Matrice. |
Contextes d'échec
Un contexte d'échec est un contexte où il est possible d'exécuter des expressions faillibles. Le contexte définit ce qui se passe si l'expression échoue. Tout échec au sein d'un contexte d'échec entraînera l'échec de l'ensemble du contexte.
Un contexte d'échec permet aux expressions imbriquées d'être des expressions d'échec, par exemple des arguments de fonction ou des expressions dans une expression block.
La liste suivante énumère tous les contextes d'échec dans Verse :
La condition dans les expressions | |
Les expressions d'itération et les expressions de filtre dans les expressions | |
Le corps d'une fonction disposant du spécificateur d'effet | |
L'opérande pour l'opérateur | |
L'opérande de gauche pour | |
Initialisation d'une variable disposant du type |
speculative execution (exécution spéculative)
Un aspect utile des contextes d'échec dans Verse est qu'ils constituent une forme d'exécution spéculative, ce qui signifie que vous pouvez tester des actions sans les valider. Lorsqu'une expression réussit, les effets de l'expression sont validés, par exemple le changement de valeur d'une variable. Si l'expression échoue, les effets de l'expression sont annulés, comme si l'expression n'avait jamais eu lieu.
De cette façon, vous pouvez exécuter une série d'actions qui accumulent les changements, mais ces actions sont annulées si elles échouent dans le contexte d'échec.
Pour que cela fonctionne, toutes les fonctions appelées dans le contexte d'échec doivent avoir le spécificateur d'effet transacts.
Spécificateurs
Dans Verse, les spécificateurs décrivent le comportement lié à la sémantique et peuvent être ajoutés aux identificateurs et à certains mots-clés. La syntaxe des spécificateurs utilise < et >, et le mot clé est placé entre les deux, par exemple IsPuzzleSolved()<decides><transacts> : void.
Dans les rubriques suivantes, nous vous décrivons tous les spécificateurs dans Verse et vous expliquons quand vous pouvez les utiliser.
Spécificateurs d'effet
Dans Verse, les effets indiquent les catégories de comportement qu'une fonction est autorisée à présenter. Vous pouvez ajouter des spécificateurs d'effet :
Les () après le nom dans une définition de fonction :
name()<specifier> : type = codeblock.Le mot clé
class:name := class<specifier>():.
Les spécificateurs d'effet sont divisés en deux catégories :
Exclusif : vous ne pouvez disposer que d'un seul ou d'aucun des spécificateurs d'effet exclusifs ajoutés à une fonction ou au mot clé
class. Si aucun spécificateur d'effet exclusif n'est ajouté, l'effet par défaut estno_rollback.Additif : vous pouvez ajouter tous les spécificateurs d'effet additif, certains d'entre eux ou aucun à une fonction ou au mot clé
class.
| Exemple | Effet |
|---|---|
no_rollback : il s'agit de l'effet par défaut lorsqu'aucun effet exclusif n'est spécifié. L'effet | |
Effets exclusifs | |
transacts : cet effet indique que toute action effectuée par la fonction peut être annulée. L'effet transacts est requis chaque fois qu'une variable mutable ( | |
varies : cet effet indique que les mêmes entrées de la fonction ne produisent pas toujours la même sortie. L'effet | |
computes : cet effet exige que la fonction n'ait pas d'effets secondaires. Son achèvement n'est pas garanti. Il existe une exigence non vérifiée selon laquelle la fonction, lorsqu'elle est fournie avec les mêmes arguments, produit le même résultat. Toute fonction qui n'a pas le spécificateur native et qui aurait autrement l'effet | |
converges : cet effet garantit non seulement qu'il n'y a pas d'effet secondaire lié à l'exécution de la fonction connexe, mais aussi que la fonction se termine définitivement (sans récursivité infinie). Cet effet peut apparaître uniquement dans les fonctions dotées du spécificateur native, bien que le compilateur n'effectue pas cette vérification. Le code utilisé pour fournir les valeurs par défaut de la classe ou les valeurs des variables globales doivent avoir cet effet. Les fonctions | |
Effets additifs | |
decides : cet effet indique que la fonction peut échouer et que l'appel de cette fonction est une expression faillible. Les définitions de fonction avec l'effet | |
suspends : cet effet indique que la fonction est asynchrone. Crée un contexte asynchrone pour le corps de la fonction. |
Dans tous les cas, l'appel d'une fonction qui a un effet spécifique exige que l'appelant dispose également de cet effet.
Spécificateurs d'accès
Les spécificateurs d'accès définissent les éléments qui peuvent interagir avec un membre et leur mode d'interaction. Il est possible d'appliquer les spécificateurs d'accès aux éléments suivants :
L'identificateur d'un membre :
name<specifier> : type = valueLe mot clé
vard'un membre :var<specifier> name : type = value
Spécificateurs de classe
Les spécificateurs de classe définissent certaines caractéristiques des classes ou de leurs membres ; ils indiquent par exemple si vous pouvez créer une sous-classe d'une classe.
abstract : lorsqu'une classe ou une méthode de classe possède le spécificateur abstract, vous ne pouvez pas créer une instance de la classe. Les classes abstraites sont destinées à être utilisées comme une superclasse avec une mise en œuvre partielle, ou comme une interface commune. Elles sont utiles lorsque vous n'avez pas besoin des instances d'une super-classe, mais que vous ne souhaitez pas dupliquer les propriétés et les comportements de classes semblables. | |
concrete : lorsqu'une classe possède le spécificateur concrete, il doit être possible de la construire avec un archétype vide, ce qui signifie que chaque champ de la classe doit avoir une valeur par défaut. Toute sous-classe d'une classe concrète est implicitement concrète. Une classe `concrete` ne peut hériter directement d'une classe `abstract` que si les deux classes sont définies dans le même module. | |
unique : le spécificateur unique peut s'appliquer à une classe pour en faire une classe unique. Pour construire une instance d'une classe unique, Verse alloue une identité unique à l'instance qui en résulte. Cela permet aux instances de classes uniques d'être comparées en matière d'égalité selon leurs identités. Les classes sans spécificateur unique n'ont pas d'identité de ce type ; il est donc uniquement possible de comparer leur égalité sur la base des valeurs de leurs champs. Cela signifie que les classes `unique` peuvent être comparées avec les opérateurs = et <>, et sont des sous-types du type `comparable`. |
Spécificateurs d'implémentation
Il n'est pas possible d'utiliser les spécificateurs d'implémentation lors de l'écriture du code ; vous verrez ultérieurement comment les utiliser dans les API de l'UEFN.
native_callable : indique qu'une méthode d'instance est à la fois native (implémentée en C++) et peut être appelée par d'autres codes C++. Ce spécificateur est utilisé sur une méthode d'instance. Ce spécificateur ne se propage pas aux sous-classes, vous n'avez donc pas besoin de l'ajouter à une définition lorsque vous remplacez une méthode disposant de ce spécificateur. |
Spécificateur de localisation
Vous devez utiliser le spécificateur localizes lorsque vous définissez un nouveau message. En particulier, lorsque la variable comporte le type message et que vous initialisez la variable avec une valeur string.
# The winning player's name:
PlayerName<localizes> : message = "Player One"# Build a message announcing the winner.
Announcement<localizes>(WinningPlayerName : string) : message = "...And the winner is: {WinningPlayerName}"
Billboard.SetText(Announcement("Player One"))Il n'est pas nécessaire d'utiliser le spécificateur localizes lorsque vous initialisez une valeur de membre avec un message déjà créé, car le spécificateur localizes s'applique uniquement pour la définition de nouveaux messages.
PlayerOne<localizes> : message = "Player One"
# The winning player's name:
PlayerName : message = PlayerOneAttributs
Les attributs dans Verse décrivent un comportement utilisé en dehors du langage Verse (contrairement aux spécificateurs, qui décrivent la sémantique de Verse). Il est possible d'ajouter des attributs sur la ligne de code précédant les définitions.
La syntaxe des attributs utilise @, suivi du mot clé.
Flux de contrôle
Le flux de contrôle est l'ordre dans lequel un ordinateur exécute les instructions. Verse dispose de différentes expressions vous permettant de contrôler le flux de votre programme.
Bloc
Dans la mesure où Verse exige un identificateur avant un bloc de code, les expressions block vous permettent d'imbriquer les blocs de code. Un bloc de code imbriqué se comporte de la même façon qu'un bloc de code. Comme pour les blocs de code, une expression block introduit un nouveau corps d'étendue imbriqué.
| block |
|---|
Résultat : dernière expression dans le bloc de code |
Pour en savoir plus, consultez la rubrique Block.
If
Avec l'expression if, vous pouvez prendre des décisions qui modifient le flux du programme. Comme avec d'autres langages de programmation, l'expression if de Verse prend en charge l'exécution conditionnelle, mais dans Verse, les conditions utilisent la réussite et l'échec pour motiver la décision.
| Si | if ... Ensuite |
|---|---|
Résultat : dernière expression dans le bloc de code | Résultat : dernière expression dans le bloc de code |
| if ... else | if ... else if ... else |
|---|
Dans l'exemple suivant, le bloc de code de l'expression if renvoie le résultat false si IsLightOn? réussit, ou true si IsLightOn? échoue. Le résultat logic est alors stocké dans NewLightState.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
Dans Verse, l'expression if est utile notamment pour tester les expressions faillibles ; si elles échouent, les actions sont annulées comme si elles n'avaient jamais eu lieu. Pour en savoir plus sur cette fonctionnalité, consultez la rubrique Exécution spéculative.
Pour en savoir plus, consultez la rubrique If.
Case
Avec les expressions case, vous pouvez contrôler le flux d'un programme à partir d'une liste de choix. L'instruction `case` dans Verse est un moyen de tester une valeur par rapport à plusieurs valeurs possibles (comme si vous utilisiez =), et d'exécuter du code en fonction de celle qui correspond.
| étui |
|---|
Pour en savoir plus, consultez la rubrique Case.
Loop
Avec l'expression loop, les expressions du bloc de boucle sont répétées à chaque itération de la boucle.
| loop (boucle) |
|---|
Résultat : le résultat d'une expression |
Dans l'exemple suivant, une plateforme apparaît et disparaît toutes les ToggleDelay secondes pendant toute la durée du jeu.
loop:
Sleep(ToggleDelay) # Sleep(ToggleDelay) waits for ToggleDelay seconds before proceeding to the next instruction.
Platform.Hide()
Sleep(ToggleDelay)
Platform.Show() # The loop restarts immediately, calling Sleep(ToggleDelay) again.
Pour en savoir plus, consultez la rubrique Loop.
Arrêter les boucles
Étant donné qu'un bloc de boucle se répète indéfiniment, pour arrêter la boucle, vous pouvez la quitter soit avec break, soit avec l'expression de retour d'une fonction.
| Loop et Break | boucle et retour |
|---|---|
Résultat : le résultat d'une expression | Résultat : le résultat d'une expression |
| boucle imbriquée et pause |
|---|
Résultat : le résultat d'une expression |
Pour en savoir plus, consultez la rubrique Boucle et pause.
For
Une expression for, parfois appelée boucle for, est identique à une expression loop, sauf que les expressions for utilisent un générateur pour produire une séquence de valeurs (une par une) et donner un nom à chaque valeur.
Par exemple, l'expression for(Value : 1..3) produit la séquence 1, 2, 3, et chaque numéro de la séquence reçoit le nom Value à chaque itération, de sorte que la boucle for s'exécute trois fois.
L'expression for contient deux parties :
Spécification d'itération : les expressions entre parenthèses. La première expression doit être un générateur, mais les autres expressions peuvent être une initialisation de constante ou un filtre.
Corps : les expressions dans le bloc de code après les parenthèses.
| pour | pour avec un filtre |
|---|---|
Résultat : le résultat d'une expression | Résultat : le résultat d'une expression |
| for avec plusieurs générateurs | imbriquée pour les blocs |
|---|---|
Résultat : le résultat d'une expression | Résultat : le résultat d'une expression |
Pour en savoir plus, consultez la rubrique For.
Defer
Une expression defer s'exécute juste avant de transférer le contrôle du programme hors du champ d'application dans lequel l'expression defer apparaît, y compris toute expression de résultat, comme dans une expression de retour. Le mode de transfert du contrôle du programme n'a pas d'importance.
| defer | defer avant une sortie |
|---|---|
Résultat : l'expression | Résultat : l'expression |
Une expression defer n'est pas exécutée si une sortie anticipée se produit avant l'occurrence de l'expression defer.
| defer avec retour anticipé | defer avec tâche concurrente annulée |
|---|---|
Résultat : l'expression | Résultat : l'expression |
Plusieurs expressions defer qui apparaissent dans la même étendue s'accumulent. L'ordre dans lequel elles sont exécutées est l'ordre inverse de celui de leur occurrence, c'est-à-dire l'ordre FILO (première entrée, dernière sortie). Dans la mesure où la dernière expression defer rencontrée dans un champ d'application donné est exécutée en premier, les expressions à l'intérieur de cette dernière expression defer peuvent faire référence à un contexte qui sera nettoyé par d'autres expressions defer rencontrées plus tôt et exécutées plus tard.
| Plusieurs expressions defer dans un bloc de code | Plusieurs expressions defer dans différents blocs de code |
|---|---|
Résultat : l'expression | Résultat : l'expression |
Flux temporel et concurrence
Le contrôle du flux temporel est au cœur du langage de programmation Verse, et mis en œuvre grâce à des expressions concurrentes. Pour en savoir plus la concurrence, consultez la rubrique Aperçu de la concurrence.
structured concurrency (concurrence structurée)
Les expressions de concurrence structurée sont utilisées pour spécifier le flux temporel asynchrone et pour modifier la nature bloquante des expressions asynchrones dont la durée de vie est limitée à une étendue de contexte asynchrone spécifique (notamment le corps d'une fonction asynchrone).
Cela s'apparente à un flux de contrôle structuré tel que block, if, for et loop qui reste limité aux étendues associées.
| sync | branch |
|---|---|
Exécute simultanément toutes les expressions dans son bloc de code et attend que toutes soient terminées avant d'exécuter l'expression qui suit l'expression | Le corps de l'expression |
Résultat : le résultat d'une expression | Résultat : une expression |
| race | rush |
|---|---|
Similaire à | Similaire à |
Résultat : le résultat d'une expression | Résultat : le résultat d'une expression |
unstructured concurrency (concurrence non structurée)
Les expressions de concurrence non structurées (dont `spawn` est la seule existante) ont une durée de vie qui n'est pas limitée à une étendue de contexte asynchrone spécifique et qui peut s'étendre au-delà de celle où elle a été exécutée.
| Apparaître |
|---|
Le corps d'une balise |
Résultat : une expression |
Tâche
Une tâche est un objet qui représente l'état d'une fonction asynchrone en cours d'exécution. Les objets tâches sont utilisés pour identifier l'endroit où une fonction asynchrone est suspendue ainsi que les valeurs des variables locales à cet endroit de suspension.
# Get task to query / give commands to
# starts and continues independently
Task2 := spawn{Player.MoveTo(Target1)}
Task2.Await() # wait until MoveTo() completed
Modules et chemins
Un module Verse est une unité atomique de code qui peut être redistribuée et dont on peut dépendre, et qui peut évoluer au fil du temps sans rompre les dépendances. Vous pouvez importer un module dans votre fichier Verse pour utiliser les définitions de code d'autres fichiers Verse.
using : pour pouvoir utiliser le contenu d'un module Verse, vous devez importer le module par son chemin d'accès. | |
module : en dehors des modules introduits par les dossiers dans un projet, il est possible d'introduire les modules dans un fichier .verse en utilisant le mot clé |
Pour en savoir plus, consultez la rubrique Modules et chemins.