Noms et expressions
Les règles applicables aux différentes formes de nom et d'expression, ainsi qu'à leur évaluation, sont données dans cette page.
Les noms
Les noms peuvent désigner des entités déclarées, qu'elles soient déclarées explicitement ou implicitement. Les noms peuvent également désigner des objets désignés par des valeurs d'accès; des sous-composantes et des tranches d'objets et de valeurs; des entrées uniques, des familles d'entrées et des entrées dans des familles d'entrées. Enfin, les noms peuvent désigner des attributs de l'un quelconque des éléments précédents.
nom ::= nom_simple | caractere_litteral | operateur_symbole | composante_indexe | tranche | composante_selectionne | attribut nom_simple ::= identifiant prefixe ::= nom | appel_fonction |
Un nom simple pour une entité est soit l'identifiant associé à l'entité par sa déclaration, soit un autre identifiant associé à l'entité par une déclaration de renommage.
Certaines formes de nom (composantes indexés et sélectionnés, tranches et attributs) incluent un préfixe étant soit un nom, soit un appel de fonction. Si le type d'un préfixe est un type d'accès, alors le préfixe ne doit pas être un nom désignant un paramètre formel de mode out ou un sous-composante de celui-ci.
Si le préfixe d'un nom est un appel de fonction, alors le nom désigne une composante, une tranche, un attribut, une entrée ou une famille d'entrées, soit le résultat de l'appel de fonction, soit (si le résultat est une valeur d'un accès) de l'objet désigné par le résultat.
Un préfixe est dit approprié pour un type dans l'un des cas suivants :
- Le type du préfixe est le type considéré.
- Le type du préfixe est un type d'accès dont le type désigné est le type considéré.
L'évaluation d'un nom détermine l'entité désignée par le nom. Cette évaluation n'a aucun autre effet pour un nom étant un nom simple, un littéral de caractère ou un symbole d'opérateur.
L'évaluation d'un nom ayant un préfixe inclut l'évaluation du préfixe, c'est-à-dire du nom ou de l'appel de fonction correspondant. Si le type du préfixe est un type d'accès, l'évaluation du préfixe inclut la détermination de l'objet désigné par la valeur d'accès correspondante; l'exception CONSTRAINT_ERROR est levée si la valeur du préfixe est une valeur d'accès nulle, sauf dans le cas du préfixe d'un attribut de représentation.
Exemples de noms simples :
- PI -- le nom simple d'un nombre
- LIMIT -- le nom simple d'une constante
- COUNT -- le nom simple d'une variable scalaire
- BOARD -- le nom simple d'une variable de tableau
- MATRIX -- le nom simple d'un type
- RANDOM -- le nom simple d'une fonction
- ERROR -- le nom simple d'une exception
Les composantes indexés
Une composante indexé désigne soit un composante d'un tableau, soit une entrée dans une famille d'entrées.
composante_indexe ::= prefixe(expression |, expression|) |
Dans le cas d'une composante d'un tableau, le préfixe doit être approprié pour un type de tableau. Les expressions spécifient les valeurs d'index du composant ; il doit y avoir une telle expression pour chaque position d'index du type de tableau. Dans le cas d'une entrée dans une famille d'entrées, le préfixe doit être un nom désignant une famille d'entrées d'un objet tâche, et l'expression (il doit y en avoir exactement une) spécifie la valeur d'index pour l'entrée individuelle.
Chaque expression doit être du type de l'index correspondant. Pour l'évaluation d'une composante indexé, le préfixe et les expressions sont évalués dans un ordre n'étant pas défini par le langage. L'exception CONSTRAINT_ERROR est levée si une valeur d'index n'appartient pas à l'intervalle de l'index correspondant du tableau de préfixe ou de la famille d'entrées.
Exemples de composants indexés :
- MY_SCHEDULE(SAT) -- Une composante d'un tableau unidimensionnel
- PAGE(10) -- Une composante d'un tableau unidimensionnel
- BOARD(M, J + 1) -- Une composante d'un tableau bidimensionnel
- PAGE(10)(20) -- Une composante d'une composante
- REQUEST(MEDIUM) -- Une entrée dans une famille d'entrées
- NEXT_FRAME(L)(M, N) -- Une composante d'un appel de fonction
Remarques sur les exemples : Des notations distinctes sont utilisées pour les composantes de tableaux multidimensionnels (tels que BOARD) et les tableaux de tableaux (tels que PAGE). Les composantes d'un tableau de tableaux sont des tableaux et peuvent donc être indexés. Ainsi, PAGE(10)(20) désigne la 20e composante de PAGE(10). Dans le dernier exemple, NEXT_FRAME(L) est un appel de fonction renvoyant une valeur d'accès désignant un tableau à deux dimensions.
Les tranches
Une tranche désigne un tableau unidimensionnel formé par une séquence de composantes consécutifs d'un tableau unidimensionnel. Une tranche d'une variable est une variable; une tranche d'une constante est une constante; une tranche d'une valeur est une valeur.
tranche ::= prefixe(intervalle_discrete) |
Le préfixe d'une tranche doit être approprié pour un type de tableau unidimensionnel. Le type de la tranche est le type de base de ce type de tableau. Les limites de l'intervalle discrète définissent celles de la tranche et doivent être du type de l'index ; la tranche est une tranche nulle désignant un tableau nul si l'intervalle discrète est un intervalle nulle.
Pour l'évaluation d'un nom étant une tranche, le préfixe et l'intervalle discrète sont évalués dans un ordre n'étant pas défini par le langage. L'exception CQNSTRAINT_ERROR est levée par l'évaluation d'une tranche, autre qu'une tranche nulle, si l'une des limites de l'intervalle discrète n'appartient pas à l'intervalle d'index du tableau de préfixation. (Les limites d'une tranche nulle n'ont pas besoin d'appartenir au sous-type de l'index.)
Exemples de tranches :
- STARS(1 .. 15) -- une tranche de 15 caractères
- PAGE(10 .. 10 + SIZE) -- une tranche de composantes 1 + SIZE
- PAGE(L)(A .. B) -- une tranche du tableau PAGE(L)
- STARS(1 .. 0) -- une tranche nulle
- MY_SCHEDULE(WEEKDAY) -- limites données par sous-type
- STARS(5 .. 15)(K) -- même que STARS(K)
- -- à condition que K soit dans 5 .. 15
Remarques : Pour un tableau unidimensionnel A, le nom A(N .. N) est une tranche d'une composante; son type est le type de base 6 de A. D'autre part, A(N) est une composante du tableau A et possède le type de composante correspondant.
Les composantes sélectionnés
Les composantes sélectionnés sont utilisés pour désigner les composantes d'enregistrement, les entrées, les familles d'entrées et les objets désignés par des valeurs d'accès; ils sont également utilisés comme noms développés comme décrit ci-dessous.
composante_selectionne ::= prefixe.selecteur selecteur ::= nom_simple | caractere_litteral | operateur_symbole | all |
Les quatre formes suivantes de composantes sélectionnés sont utilisées pour désigner un discriminant, une composante d'enregistrement, une entrée ou un objet désigné par une valeur d'accès :
- Un discriminant : le sélecteur doit être un nom simple désignant un discriminant d'un objet ou d'une valeur. Le préfixe doit être approprié au type de cet objet ou de cette valeur.
- Une composante d'un enregistrement : le sélecteur doit être un nom simple désignant une composante d'un objet ou d'une valeur d'enregistrement. Le préfixe doit être approprié au type de cet objet ou de cette valeur. Pour un composant d'une variante, une vérification est effectuée pour s'assurer que les valeurs des discriminants sont telles que l'enregistrement possède ce composant. L'exception CONSTRAINT_ERROR est levée si cette vérification échoue.
- Une entrée unique ou une famille d'entrées d'une tâche : le sélecteur doit être un nom simple désignant une entrée unique ou une famille d'entrées d'une tâche. Le préfixe doit être approprié au type de cette tâche.
- Un objet désigné par une valeur d'accès : le sélecteur doit être le mot réservé all. La valeur du préfixe doit appartenir à un type d'accès.
Une composante sélectionné de l'une des deux formes restantes est appelé un nom étendu. Dans chaque cas, le sélecteur doit être soit un nom simple, un caractère littéral ou un symbole d'opérateur. Un appel de fonction n'est pas autorisé comme préfixe d'un nom étendu. Un nom étendu peut désigner :
- Une entité déclarée dans la partie visible d'un paquet : le préfixe doit désigner le paquet. Le sélecteur doit être le nom simple, le caractère littéral ou le symbole d'opérateur de l'entité.
- Une entité dont la déclaration se produit immédiatement dans une construction nommée : le préfixe doit désigner une construction étant soit une unité de programme, une instruction de bloc, une instruction de boucle ou une instruction d'acceptation. Dans le cas d'une instruction d'acceptation, le préfixe doit être soit le nom simple de l'entrée ou de la famille d'entrées, soit un nom étendu se terminant par un tel nom simple (c'est-à-dire qu'aucun index n'est autorisé). Le sélecteur doit être le nom simple, le caractère littéral ou le symbole d'opérateur d'une entité dont la déclaration se produit immédiatement dans la construction. Cette forme de nom étendu n'est autorisée qu'au sein de la construction elle-même (y compris le corps et les sous-unités, dans le cas d'une unité de programme). Un nom déclaré par une déclaration de renommage n'est pas autorisé comme préfixe. Si le préfixe est le nom d'un sous-programme ou d'une instruction accept et s'il existe plusieurs sous-programmes ou instructions accept visibles de ce nom, le nom étendu est ambigu, indépendamment du sélecteur.
Si, selon les règles de visibilité, il existe au moins une interprétation possible du préfixe d'une composante sélectionné comme nom d'un sous-programme englobant ou d'une instruction accept, alors les seules interprétations considérées sont celles de la règle (f), comme noms développés (aucune interprétation du préfixe comme appel de fonction n'est alors considérée).
L'évaluation d'un nom étant une composante sélectionné inclut l'évaluation du préfixe.
Exemples de composantes sélectionnés :
- TOMORROW.MONTH -- une composante d'enregistrement
- NEXT_CAR.OWNER -- une composante d'enregistrement
- NEXT_CAR.OWNER.AGE -- une composante d'enregistrement
- WRITER.UNIT -- une composante d'enregistrement (un discriminant)
- MIN_CELL(H).VALUE -- une composant d'enregistrement du résultat de l'appel de fonction MIN_CELL(H)
- CONTROL.SEIZE -- une entrée de la tâche CONTROL
- POOL(K).WRITE -- une entrée de la tâche POOL(K)
- NEXT_CAR.all -- l'objet désigné par la variable d'accès NEXT_CAR
Exemples de noms développés :
- TABLE_MANAGER.INSERT -- une procédure de la partie visible d'un paquet
- KEY_MANAGER."<" -- un opérateur de la partie visible d'un paquet
-
- DOT_PRODUCT.SUM -- une variable déclarée dans le corps d'une procédure
- BUFFER.POOL -- une variable déclarée dans une unité de tâche
- BUFFER.READ -- une entrée d'une unité de tâche
- SWAP.TEMP -- une variable déclarée dans une instruction de bloc
- STANDARD.BOOLEAN -- le nom d'un type prédéfini
Remarque : pour un enregistrement dont les composantes sont d'autres enregistrements, les règles ci-dessus impliquent que le nom simple doit être indiqué à chaque niveau pour le nom d'une sous-composante. Par exemple, le nom NEXT_CAR.OWNER.BIRTH.MONTH ne peut pas être raccourci (NEXT_CAR.OWNER.MONTH n'est pas autorisé).
Les attributs
Un attribut désigne une opération de base d'une entité donnée par un préfixe :
attribut ::= prefix'attribut_designateur attribut_designateur ::= nom_simple [(expression_statique_universelle)] |
Les désignateurs d'attribut applicables dépendent du préfixe. Un attribut peut être une opération de base délivrant une valeur; il peut également s'agir d'une fonction, d'un type ou d'un intervalle. La signification du préfixe d'un attribut doit pouvoir être déterminée indépendamment du désignateur d'attribut et indépendamment du fait qu'il s'agisse du préfixe d'un attribut.
De plus, une implémentation peut fournir des attributs définis par l'implémentation. Le désignateur d'attribut de tout attribut défini par l'implémentation ne doit pas être le même que celui de tout attribut défini par le langage.
L'évaluation d'un nom étant un attribut consiste en l'évaluation du préfixe.
Remarques : Les désignateurs d'attribut DIGITS, DELTA et RANGE ont le même identifiant qu'un mot réservé. Cependant, aucune confusion n'est possible car un désignateur d'attribut est toujours précédé d'une apostrophe. Les seuls désignateurs d'attribut prédéfinis qui ont une expression universelle sont ceux de certaines opérations de types de tableaux.
Exemples d'attributs :
- COLOR'FIRST -- valeur minimale du type d'énumération COLOR
- RAINBOWBASE'FIRST -- identique à COLOR'FIRST
- REALDIGITS -- précision de type REAL
- BQARD'LAST(2) -- limite supérieure de la deuxième dimension de BOARD
- BOARDRANGE(1) -- intervalle d'index de la première dimension de BOARD
- POOL(K)TERMINATED -- TRUE si la tâche POOL(K) est terminée
- DATESIZE -- nombre de bits pour les enregistrements de type DATE
- MESSAGE'ADDRESS -- adresse de la variable d'enregistrement MESSAGE
Les littéraux
Un littéral est soit un littéral numérique, un littéral d'énumération, le littéral null, soit un littéral de chaîne de caractères. L'évaluation d'un littéral donne la valeur correspondante.
Les littéraux numériques sont les littéraux des types universa_integer et universal_real. Les littéraux d'énumération incluent les littéraux de caractères et donnent des valeurs des types d'énumération correspondants. Le littéral null donne une valeur d'accès null ne désignant aucun objet du tout.
Un littéral de chaîne de caractères est une opération de base combinant une séquence de caractères en une valeur d'un tableau unidimensionnel d'un type de caractère; les limites de ce tableau sont déterminées selon les règles des agrégats de tableaux positionnels. Pour un littéral de chaîne de caractères null, la limite supérieure est le prédécesseur, tel que donné par l'attribut PRED, de la limite inférieure. L'évaluation d'un littéral de chaîne nul génère l'exception CONSTRAINT_ERROR si la borne inférieure n'a pas de prédécesseur.
Le type d'un littéral de chaîne et de même le type du littéral null doivent être déterminables uniquement à partir du contexte dans lequel ce littéral apparaît, en excluant le littéral lui-même, mais en utilisant le fait que le littéral null est une valeur d'un type d'accès, et de même qu'un littéral de chaîne de caractères est une valeur d'un type de tableau unidimensionnel dont le type de composante est un type de caractère.
Les littéraux de caractères correspondant aux caractères graphiques contenus dans un littéral de chaîne de caractères doivent être visibles à la place du littéral de chaîne de caractères (bien que ces caractères eux-mêmes ne soient pas utilisés pour déterminer le type du littéral de chaîne de caractères).
Exemples :
- 3.14159_26536 -- un littéral réel
- 1_345 -- un littéral entier
- CLUBS -- un littéral d'énumération
- 'A' -- un caractère littéral
- "QUELQUES TEXTES" -- une chaîne de caractères littérale
Les agrégats
Un agrégat est une opération de base combinant les valeurs des composantes en une valeur composite de type enregistrement ou tableau :
agregat ::= (association_de_composantes |, association_de_composantes|) association_de_composantes ::= [choix || choix| => ] expression |
Chaque association de composantes associe une expression à des composantes (éventuellement aucun). Une association de composantes est dite nommée si les composantes sont spécifiés explicitement par des choix; sinon, elle est dite positionnelle. Pour une association positionnelle, la composante (unique) est implicitement spécifié par position, dans l'ordre des déclarations de composantes correspondantes pour les composantes d'enregistrement, dans l'ordre d'index pour les composantes de tableau.
Les associations nommées peuvent être données dans n'importe quel ordre (sauf pour le choix autres), mais si les associations positionnelles et nommées sont utilisées dans le même agrégat, alors les associations positionnelles doivent apparaître en premier, à leur position normale. Ainsi, une fois qu'une association nommée est utilisée, le reste de l'agrégat ne doit utiliser que des associations nommées. Les agrégats contenant une seule association de composantes doivent toujours être donnés en notation nommée. Des règles spécifiques concernant les associations de composantes existent pour les agrégats d'enregistrements et les agrégats de tableaux.
Les choix dans les associations de composantes ont la même syntaxe que dans les parties variantes. Un choix étant un nom simple de composante n'est autorisé que dans un agrégat d'enregistrements. Pour une association de composantes, un choix étant une expression simple ou un intervalle discrète n'est autorisé que dans un agrégat de tableau; un choix étant une expression simple spécifie la composante à la valeur d'index correspondante; de même, un intervalle discrète spécifie les composantes aux valeurs d'index dans l'intervalle. Le choix autres n'est autorisé dans une association de composantes que si l'association apparaît en dernier et possède ce seul choix; il spécifie tous les composantes restantes, le cas échéant.
Chaque composante de la valeur définie par un agrégat doit être représenté une fois et une seule fois dans l'agrégat. Par conséquent, chaque agrégat doit être complet et une composante donné ne peut pas être spécifié par plus d'un choix.
Le type d'un agrégat doit être déterminable uniquement à partir du contexte dans lequel l'agrégat apparaît, à l'exclusion de l'agrégat lui-même, mais en utilisant le fait que ce type doit être composite et non limité. Le type d'un agrégat détermine à son tour le type requis pour chacun de ses composantes.
Remarques : La règle ci-dessus implique que la détermination du type d'un agrégat ne peut utiliser aucune information provenant de l'agrégat. En particulier, cette détermination ne peut pas utiliser le type de l'expression d'une association de composantes, ni la forme ou le type d'un choix. Un agrégat peut toujours être distingué d'une expression entre parenthèses : c'est une conséquence du fait que la notation nommée est requise pour un agrégat à une seule composante.
Les agrégats d'enregistrements
Si le type d'un agrégat est un type d'enregistrement, les noms de composantes donnés comme choix doivent désigner les composantes (y compris les discriminants) du type d'enregistrement. Si le choix autres est donné comme choix d'un agrégat d'enregistrements, il doit représenter au moins une composante. Une association de composantes avec le choix autres ou avec plusieurs choix n'est autorisée que si les composantes représentés sont tous du même type. L'expression d'une association de composantes doit avoir le type des composantes d'enregistrement associés.
La valeur spécifiée pour un discriminant régissant une partie variante doit être donnée par une expression statique (notez que cette valeur détermine les composantes dépendants devant apparaître dans la valeur d'enregistrement).
Pour l'évaluation d'un agrégat d'enregistrements, les expressions données dans les associations de composantes sont évaluées dans un ordre n'étant pas défini par le langage. L'expression d'une association nommée est évaluée une fois pour chaque composante associé. Une vérification est effectuée pour s'assurer que la valeur de chaque sous-composante de l'agrégat appartient au sous-type de cette sous-composante. L'exception CONSTRAINT_ERROR est levée si cette vérification échoue.
Exemple d'un agrégat d'enregistrements avec des associations positionnelles :
- (4, JULY, 1776)
Exemples d'agrégats d'enregistrements avec des associations nommées :
- (DAY => 4, MONTH => JULY, YEAR => 1776)
- (MONTH => JULY, DAY => 4, YEAR => 1776)
- (DISK, CLOSED, TRACK => 5, CYLINDER => 12)
- (UNIT => DISK, STATUS => CLOSED, CYLINDER => 9, TRACK => 1)
Exemple d'association de composantes avec plusieurs choix :
- (VALUE => 0, SUCCIPRED => new CELL'(0, null, null))
- -- L'allocateur est évalué deux fois : SUCC et PRED désignent des cellules différentes
Remarque : pour un agrégat avec des associations positionnelles, les valeurs discriminantes apparaissent en premier puisque la partie discriminante est donnée en premier dans la déclaration de type d'enregistrement ; elles doivent être dans le même ordre que dans la partie discriminante.
Agrégats de tableau
Si le type d'un agrégat est un type de tableau unidimensionnel, chaque choix doit alors spécifier des valeurs du type d'index et l'expression de chaque association de composantes doit être du type de composante.
Si le type d'un agrégat est un type de tableau multidimensionnel, un agrégat n-dimensionnel est écrit comme un agrégat unidimensionnel, dans lequel l'expression spécifiée pour chaque association de composantes est elle-même écrite comme un agrégat (n-1)-dimensionnel étant appelé un sous-agrégat, le sous-type d'index de l'agrégat unidimensionnel est donné par la première position d'index du type de tableau. La même règle est utilisée pour écrire un sous-agrégat s'il est à nouveau multidimensionnel, en utilisant des positions d'index successives. Un littéral de chaîne de caractères est autorisé dans un agrégat multidimensionnel à la place d'un tableau unidimensionnel d'un type de caractère. Dans ce qui suit, les règles concernant les agrégats de tableau sont formulées en termes d'agrégats unidimensionnels.
En dehors d'une association de composantes finale avec le choix unique others, le reste (le cas échéant) des associations de composantes d'un agrégat de tableau doivent être soit toutes positionnelles, soit toutes nommées. Une association nommée d'un agrégat de tableau ne peut avoir un choix n'étant pas statique, ou de même un choix étant un intervalle nulle, que si l'agrégat comprend une seule association de composantes et que cette association de composantes n'a qu'un seul choix. Un autre choix est statique si la contrainte d'index applicable est statique.
Les limites d'un agrégat de tableau ayant un autre choix sont déterminées par la contrainte d'index applicable. Un autre choix n'est autorisé que si l'agrégat apparaît dans l'un des contextes suivants (définissant la contrainte d'index applicable) :
- L'agrégat est un paramètre réel, un paramètre réel générique, l'expression de résultat d'une fonction ou l'expression suivant un délimiteur composé d'affectation. De plus, le sous-type du paramètre formel, du paramètre formel générique, du résultat de fonction ou de l'objet correspondant est un sous-type de tableau contraint. Pour un agrégat apparaissant dans un tel contexte et contient une association avec un autre choix, les associations nommées ne sont autorisées pour d'autres associations que dans le cas d'un paramètre réel (non générique) ou d'un résultat de fonction. Si l'agrégat est un tableau multidimensionnel, cette restriction s'applique également à chacun de ses sous-agrégats.
- L'agrégat est l'opérande d'une expression qualifiée dont la marque de type désigne un sous-type de tableau contraint.
- L'agrégat est l'expression de l'association de composantes d'un agrégat englobant (tableau ou enregistrement). De plus, si cet agrégat englobant est un agrégat de tableau multidimensionnel, il se trouve lui-même dans l'un de ces trois contextes.
Les limites d'un agrégat de tableau n'ayant pas d'autres choix sont déterminées comme suit. Pour un agrégat ayant des associations nommées, les limites sont déterminées par les choix les plus petits et les plus grands donnés. Pour un agrégat positionnel, la limite inférieure est déterminée par la contrainte d'index applicable si l'agrégat apparaît dans l'un des contextes (a) à (c) ; sinon, la limite inférieure est donnée par S'FIRST où S est le sous-type d'index ; dans les deux cas, la limite supérieure est déterminée par le nombre de composantes.
L'évaluation d'un agrégat de tableau n'étant pas un sous-agrégat se déroule en deux étapes. Tout d'abord, les choix de cet agrégat et de ses sous-agrégats, le cas échéant, sont évalués dans un ordre n'étant pas défini par le langage. Ensuite, les expressions des associations de composants de l'agrégat de tableau sont évaluées dans un ordre n'étant pas défini par le langage ; l'expression d'une association nommée est évaluée une fois pour chaque composant associé. L'évaluation d'un sous-agrégat consiste en cette deuxième étape (la première étape est omise puisque les choix ont déjà été évalués).
Pour l'évaluation d'un agrégat n'étant pas un tableau nul, on vérifie que les valeurs d'index définies par les choix appartiennent aux sous-types d'index correspondants, et également que la valeur de chaque sous-composante de l'agrégat appartient au sous-type de ce sous-composante. Pour un agrégat multidimensionnel n-dimensionnel, on vérifie que tous les sous-agrégats (n-1)-dimensionnels ont les mêmes bornes. L'exception CONSTRAINT_ERROR est levée si l'une de ces vérifications échoue.
Remarque : les contextes autorisés pour un agrégat de tableau incluant un autre choix sont tels que les limites d'un tel agrégat sont toujours connues à partir du contexte.
Exemples d'agrégats de tableau avec associations positionnelles :
- (7, 9, 5, 1, 3, 2, 4, 8, 6, 0)
- TABLE'(5, 8, 4, 1, others => 0)
Exemples d'agrégats de tableaux avec des associations nommées :
- (1 .. 5 => (1 ..8 => 0.0)) -- bidimensionnel
- (1 .. N => new CELL) -- N nouvelles cellules, en particulier pour N = 0
-
- TABLE'(2 | 4 | 10 => 1, others => 0)
- SCHEDULE'(MON .. FRI => TRUE, others => FALSE)
- SCHEDULE'(WED | SUN => FALSE, others => TRUE )
Exemples d'agrégats de tableaux bidimensionnels :
- -- Trois agrégats pour la même valeur de type MATRIX
- ((1.1, 1.2, 1.3), (2.1, 2.2, 2.3))
- (1 => (1.1, 1.2, 1.3), 2 => (2.1, 2.2, 2.3))
- (1 => (1 => 1.1, 2 => 1.2, 3 => 1.3), 2 => (1 => 2.1, 2 => 2.2, 3 => 2.3))
Exemples d'agrégats comme valeurs initiales :
- A : TABLE := (7, 9, 5, 1, 3, 2, 4, 8, 6, 0); -- A(1)=7, A(10)=0
- B : TABLE := TABLE'(2 | 4 | 10 => 1, others => 0); -- B(1)=0, B(10)=1
- C : constant MATRIX := (1 .. 5 => (1 .. 8 => 0.0)); -- C'FIRST(1 )=1, C'LAST(2)=8
- D : BIT_VECTOR(M .. N) := (M .. N => TRUE);
- E : BIT_VECTOR(M .. N) := (others => TRUE);
- F : STRING(0 .. 1) := (1 => 'F'); -- un agrégat à une seule composante : identique à « F »
Les expressions
Une expression est une formule définissant le calcul d'une valeur :
expression ::= relation {relation and} | relation (relation and then} | relation {relation or} | relation (relation or else} | relation {relation xor} relation ::= expression_simple [operateur_relationnel expression_simple] | expression_simple [not] in intervalle | expression_simple [not] in marque_type expression_simple [operateur_d_addition_unaire] terme [operateur_d_ajout_binaire terme} terme ::= facteur |operateur_multiplicateur facteur} facteur ::= primaire [** primaire] | abs primaire | not primaire primaire ::= litteral_numerique | null | agregat | chaine_litterale | nom | allocateur | appel_fonction | conversion_de_type | expression_qualifiee | (expression) |
Chaque primaire a une valeur et un type. Les seuls noms autorisés comme primaires sont les nombres nommés, les attributs produisant des valeurs et les noms désignant des objets (la valeur d'un tel primaire est la valeur de l'objet) ou désignant des valeurs. Les noms désignant des paramètres formels du mode out ne sont pas autorisés comme primaires; les noms de leurs sous-composants ne sont autorisés que dans le cas de discriminants.
Le type d'une expression dépend uniquement du type de ses constituants et des opérateurs appliqués; pour un constituant ou un opérateur surchargé, la détermination du type de constituant, ou l'identification de l'opérateur approprié, dépend du contexte.
Exemples de primaires :
- 4.0 -- littéral réel
- PI -- numéro nommé
- (1 .. 10 => 0) -- agrégat de tableau
- SUM -- variable
- INTEGER'LAST -- attribut
- SINE(X) -- appel de fonction
- COLOR'(BLUE) -- expression qualifiée
- REAL(M*N) -- conversion
- (LINE_COUNT + 10) -- expression entre parenthèses
Exemples d'expressions :
- VOLUME -- primaire
- not DESTROYED -- facteur
- 2 LINE_COUNT -- terme
- -4.0 -- expression simple
- -4.0 + A -- expression simple
- B**2 - 4.0*A*C -- expression simple
- PASSWORD(1 .. 3) = "BWV" -- relation
- COUNT in SMALL_INT -- relation
- COUNT not in SMALL_INT -- relation
- INDEX = 0 or ITEM_HIT -- expression
- (COLD and SUNNY) or WARM -- expression (les parenthèses sont obligatoires)
- A ** (B ** C) -- expression (les parenthèses sont obligatoires)
Opérateurs et évaluation des expressions
Le langage définit les six classes d'opérateurs suivantes. Les symboles d'opérateur correspondants (à l'exception de /=), et uniquement ceux-là, peuvent être utilisés comme désignateurs dans les déclarations de fonctions pour les opérateurs définis par l'utilisateur. Ils sont donnés dans l'ordre de priorité croissante.
operateur_logique ::= and | or | xor operateur_relationnel ::= = | /= | < | <= | > | >= operateur_d_ajout_binaire ::= + | - | & operateur_d_addition_unaire ::= + | - operateur_multiplicateur ::= * 1 / | mod | rem operateur_de_priorite_la_plus_elevee ::= ** | abs | not |
Les formes de contrôle de court-circuit and then et or else ont la même priorité que les opérateurs logiques. Les tests d'appartenance in et not in ont la même priorité que les opérateurs relationnels.
Pour un terme, une expression simple, une relation ou une expression, les opérateurs de priorité supérieure sont associés à leurs opérandes avant les opérateurs de priorité inférieure. Dans ce cas, pour une séquence d'opérateurs de même niveau de priorité, les opérateurs sont associés dans l'ordre textuel de gauche à droite; des parenthèses peuvent être utilisées pour imposer des associations spécifiques.
Les opérandes d'un facteur, d'un terme, d'une expression simple ou d'une relation, et les opérandes d'une expression ne contenant pas de forme de contrôle de court-circuit, sont évalués dans un ordre n'étant pas défini par le langage (mais avant l'application de l'opérateur correspondant). L'opérande droit d'une forme de contrôle de court-circuit est évalué si et seulement si l'opérande gauche a une certaine valeur.
Pour chaque forme de déclaration de type, certains des opérateurs ci-dessus sont prédéfinis, c'est-à-dire qu'ils sont implicitement déclarés par la déclaration de type. Pour chaque déclaration d'opérateur implicite de ce type, les noms des paramètres sont LEFT et RIGHT pour les opérateurs binaires; le paramètre unique est appelé RIGHT pour les opérateurs d'addition unaires et pour les opérateurs unaires abs et not.
Les opérations prédéfinies sur les types entiers donnent soit le résultat mathématiquement correct, soit génèrent l'exception NUMERIC_ERROR. Une opération prédéfinie fournissant un résultat d'un type entier (autre que universal_integer) ne peut générer l'exception NUMERIC_ERROR que si le résultat mathématique n'est pas une valeur du type. Les opérations prédéfinies sur les types réels génèrent des résultats. Une opération prédéfinie fournissant un résultat d'un type réel (autre que universal_real) ne peut générer l'exception NUMERIC_ERROR que si le résultat n'est pas dans l'intervalle des nombres sûrs du type.
Exemples de préséance :
- not SUNNY or WARM -- identique à (pas SUNNY) ou WARM
- X > 4.0 and Y > 0.0 -- identique à (X > 4,0) et (Y > 0,0)
-
- -4.0 * A**2 -- identique à -(4.0 * (A**2))
- abs(1 + A) + B -- identique à (abs (1 + A)) + B
- Y (-3) -- les parenthèses sont nécessaires
- A / B * C -- identique à (A/B)*C
- A + (B + C) -- évaluer B + C avant de l'ajouter à A
Opérateurs logiques et formes de contrôle de court-circuit
Les opérateurs logiques suivants sont prédéfinis pour tout type booléen et tout type de tableau unidimensionnel dont les composantes sont de type booléen; dans les deux cas, les deux opérandes ont le même type.
Opérateur | Opération | Type d'opérande | Type de résultat |
---|---|---|---|
and | conjonction | tout type booléen tableau de composantes booléennes |
même type booléen même type de tableau |
or | disjonction inclusive | tout type booléen tableau de composants booléens |
même type booléen même type de tableau |
xor | disjonction exclusive | tout type booléen tableau de composants booléens |
même type booléen même type de tableau |
Les opérations sur les tableaux sont effectuées composante par composante sur les composantes correspondants, s'il y en a (comme pour l'égalité). Les bornes du tableau résultant sont celles de l'opérande gauche. On vérifie que pour chaque composant de l'opérande gauche, il existe une composante correspondant de l'opérande droit, et vice versa. L'exception CONSTRAINT_ERROR est levée si cette vérification échoue.
Les formes de contrôle de court-circuit and then et or else sont définies pour deux opérandes de type booléen et fournissent un résultat du même type. L'opérande gauche d'une forme de contrôle de court-circuit est toujours évalué en premier. Si l'opérande gauche d'une expression avec la forme de contrôle and then est évalué à FALSE, l'opérande droit n'est pas évalué et la valeur de l'expression est FALSE. Si l'opérande gauche d'une expression avec la forme de contrôle or else est évalué à TRUE, l'opérande droit n'est pas évalué et la valeur de l'expression est TRUE. Si les deux opérandes sont évalués, et donne alors le même résultat que et, et ou sinon donne le même résultat que or.
Remarque : La signification conventionnelle des opérateurs logiques est donnée par la table de vérité suivante :
A | B | A and B | A or B | A xor B |
---|---|---|---|---|
TRUE | TRUE | TRUE | TRUE | FALSE |
TRUE | FALSE | FALSE | TRUE | TRUE |
FALSE | TRUE | FALSE | TRUE | TRUE |
FALSE | FALSE | FALSE | FALSE | FALSE |
Exemples d'opérateurs logiques :
- SUNNY or WARM
- FILTER(1 .. 10) and FILTER(15 .. 24)
Exemples de formes de contrôle de court-circuit :
- NEXT_CAR.OWNER /= null and then NEXT_CAR.OWNER.AGE > 25
- N = 0 of else A(N) = HIT.VALUE
Les opérateurs relationnels et les tests d'appartenance
Les opérateurs d'égalité et d'inégalité sont prédéfinis pour tout type non limité. Les autres opérateurs relationnels sont les opérateurs de classement < (inférieur à), <= (inférieur ou égal à), > (supérieur à) et >= (supérieur ou égal à). Les opérateurs de classement sont prédéfinis pour tout type scalaire et pour tout type de tableau discret, c'est-à-dire un type de tableau unidimensionnel dont les composants sont de type discret. Les opérandes de chaque opérateur relationnel prédéfini ont le même type. Le type de résultat est le type prédéfini BOOLEAN.
Les opérateurs relationnels ont leur signification conventionnelle : le résultat est égal à TRUE si la relation correspondante est satisfaite; le résultat est FALSE sinon. L'opérateur d'inégalité donne le résultat complémentaire à l'opérateur d'égalité : FALSE si égal, TRUE si différent.
Opérateur | Opération | Type d'opérande | Type de résultat |
---|---|---|---|
= /= | égalité et inégalité | tout type | BOOLEAN |
< <= > >= | test pour ordonner | tout type scalaire type de tableau discret |
BOOLEAN BOOLEAN |
L'égalité pour les types discrets est l'égalité des valeurs. Deux valeurs d'accès sont égales soit si elles désignent le même objet, soit si les deux sont égales à la valeur nulle du type d'accès.
Pour deux valeurs de tableau ou deux valeurs d'enregistrement du même type, l'opérande gauche est égal à l'opérande droit si et seulement si pour chaque composant de l'opérande gauche il existe un composant correspondant de l'opérande droit et vice versa ; et les valeurs des composantes correspondantes sont égales, comme indiqué par l'opérateur d'égalité prédéfini pour le type de composante. En particulier, deux tableaux nuls du même type sont toujours égaux ; deux enregistrements nuls du même type sont toujours égaux.
Pour comparer deux enregistrements du même type, les composantes correspondants sont ceux ayant le même identifiant de composante.
Pour comparer deux tableaux unidimensionnels du même type, les composants correspondants sont ceux (le cas échéant) dont les valeurs d'index correspondent dans le sens suivant : les limites inférieures des intervalles d'index sont définies comme correspondantes et les successeurs des index correspondants sont définis comme correspondants. Pour comparer deux tableaux multidimensionnels, les composants correspondants sont ceux dont les valeurs d'index correspondent dans les positions d'index successives.
Si l'égalité est explicitement définie pour un type limité, elle ne s'étend pas aux types composites ayant des sous-composantes du type limité (la définition explicite de l'égalité est autorisée pour de tels types composites). Les opérateurs de classement <, <=, > et >= étant définis pour les types de tableaux discrets correspondent à l'ordre lexicographique en utilisant la relation d'ordre prédéfinie du type de composant. Un tableau nul est lexicographiquement inférieur à tout tableau ayant au moins une composante. Dans le cas de tableaux non nuls, l'opérande gauche est lexicographiquement inférieur à l'opérande droit si le premier composant de l'opérande gauche est inférieur à celui de droite; sinon, l'opérande gauche est lexicographiquement inférieur à l'opérande droit uniquement si leurs premiers composants sont égaux et la queue de l'opérande gauche est lexicographiquement inférieure à celle de droite (la queue se compose des composantes restants au-delà du premier et peut être nulle).
Les tests d'appartenance dans and not in sont prédéfinis pour tous les types. Le type de résultat est le type prédéfini BOOLEAN. Pour un test d'appartenance avec un intervalle, l'expression simple et les bornes de l'intervalle doivent être du même type scalaire; pour un test d'appartenance avec une marque de type, le type de l'expression simple doit être le type de base de la marque de type. L'évaluation du test d'appartenance dans donne le résultat TRUE si la valeur de l'expression simple est dans l'intervalle donnée, ou si cette valeur appartient au sous-type désigné par la marque de type donnée; sinon cette évaluation donne le résultat FALSE (pour une valeur de type réel). Le test d'appartenance not in donne le résultat complémentaire du test d'appartenance in.
Exemples :
- X /= Y
-
- < "A" and "A" < "AA" -- TRUE
- "AA" < "B" and "A" < "A -- TRUE
-
- MY_CAR = null -- vrai si MY_CAR a été défini sur null
- MY_CAR = YOUR_CAR -- c'est vrai si nous partageons tous les deux la même voiture
- MY_CAR.all = YOUR_CAR.all -- vrai si les deux voitures sont identiques
-
- N not in 1 .. 10 -- test d'appartenance à la gamme
- TODAY in MON .. FRI -- test d'appartenance à la gamme
- TODAY in WEEKDAY -- test d'appartenance au sous-type
- ARCHIVE in DISK_UNIT -- test d'appartenance au sous-type
Remarques : Aucune exception n'est jamais levée par un opérateur relationnel prédéfini ou par un test d'appartenance, mais une exception peut être levée par l'évaluation des opérandes.
Si un type d'enregistrement comporte des composantes dépendant de discriminants, deux valeurs de ce type ont des composants correspondants si et seulement si leurs discriminants sont égaux. Deux tableaux non nuls ont des composantes correspondantes si et seulement si la valeur de l'attribut LENGTH(N) pour chaque position d'index N est la même pour les deux.
Opérateurs d'addition binaires
Les opérateurs d'addition binaires + et - sont prédéfinis pour tout type numérique et ont leur signification conventionnelle. Les opérateurs de chaîne de caractères & sont prédéfinis pour tout type de tableau unidimensionnel n'étant pas limité.
Opérateur | Opération | Type d'opérande gauche | Type d'opérande droit | Type de résultat |
---|---|---|---|---|
+ | addition | tout type numérique | même type numérique | même type numérique |
- | soustraction | tout type numérique | même type numérique | même type numérique |
& | concaténation tout type de tableau tout type de tableau le type de composante le type de composante |
même type de tableau le type de composant tout type de tableau le type de composante |
même type de tableau même type de tableau même type de tableau tout type de tableau |
Pour les types réels, la précision du résultat est déterminée par le type d'opérande.
Si les deux opérandes sont des tableaux unidimensionnels, le résultat de la chaîne de caractères est un tableau unidimensionnel dont la longueur est la somme des longueurs de ses opérandes et dont les composantes comprennent les composantes de l'opérande gauche suivis des composantes de l'opérande droit. La limite inférieure de ce résultat est la limite inférieure de l'opérande gauche, sauf si l'opérande gauche est un tableau nul, auquel cas le résultat de la chaîne est l'opérande droit.
Si l'un des opérandes est du type de composant d'un type de tableau, le résultat de la chaîne est donné par les règles ci-dessus, en utilisant à la place de cet opérande un tableau ayant cet opérande comme seul composant et ayant la limite inférieure du sous-type d'index du type de tableau comme limite inférieure.
L'exception CONSTRAINT_ERROR est levée par la chaîne de caractères si la limite supérieure du résultat dépasse la plage du sous-type d'index, sauf si le résultat est un tableau nul. Cette exception est également levée si un opérande est du type composante mais a une valeur n'appartenant pas au sous-type de composante.
Exemples :
- Z + 0.1 -- Z doit être de type réel
-
- "A" & "BCD" -- concaténation de deux littéraux de chaîne de caractères
- 'A' & "BCD" -- concaténation d'un littéral de caractère et d'un littéral de chaîne de caractères
- 'A' & TV -- concaténation de deux littéraux de caractère
Opérateurs d'addition unaires
Les opérateurs d'addition unaires + et - sont prédéfinis pour tout type numérique et ont leur signification conventionnelle. Pour chacun de ces opérateurs, l'opérande et le résultat ont le même type.
Opérateur | Opération | Type d'opérande | Type de résultat |
---|---|---|---|
+ | identité | tout type numérique | même type numérique |
- | négation | tout type numérique | même type numérique |
Opérateurs de multiplication
Les opérateurs * et / sont prédéfinis pour tout type entier et tout type à virgule flottante et ont leur signification conventionnelle; les opérateurs mod et rem sont prédéfinis pour tout type entier. Pour chacun de ces opérateurs, les opérandes et le résultat ont le même type de base. Pour les types à virgule flottante, la précision du résultat est déterminée par le type d'opérande.
Opérateur | Opération | Type d'opérande | Type de résultat |
---|---|---|---|
* | multiplication | tout type d'entier tout type de virgule flottante |
même type d'entier même type de virgule flottante |
/ | division entière division flottante |
tout type entier tout type à virgule flottante |
même type d'entier même type de virgule flottante |
mod | module | tout type entier | même type entier |
rem | reste | tout type entier | même type entier |
La division entière et le reste sont définis par la relation :
- A = (A/B)*B + (A rem B)
où (A rem B) a le signe de A et une valeur absolue inférieure à la valeur absolue de B. La division entière satisfait l'identité :
- (-A)/B = -(A/B) = A/(-B)
Le résultat de l'opération de module est tel que (A mod B) a le signe de B et une valeur absolue inférieure à la valeur absolue de B; de plus, pour une valeur entière N, ce résultat doit satisfaire la relation :
- A = B + N + (A mod B)
Pour chaque type de virgule fixe, les opérateurs de multiplication et de division suivants, avec un opérande de type prédéfini INTEGER, sont prédéfinis :
Opérateur | Opération | Type d'opérande gauche | Type d'opérande droit | Type de résultat |
---|---|---|---|---|
* | multiplication | tout type de point fixe INTEGER |
INTEGER tout type de point fixe |
identique à gauche identique à droite |
/ | division | tout type de point fixe | INTEGER | même chose que la gauche |
La multiplication d'entiers de valeurs à virgule fixe équivaut à une addition répétée. La division d'une valeur à virgule fixe par un entier n'implique pas de changement de type mais est approximative.
Enfin, les opérateurs de multiplication et de division suivants sont déclarés dans le paquet prédéfini STANDARD. Ces deux opérateurs spéciaux s'appliquent aux opérandes de tous les types à virgule fixe (il est une conséquence d'autres règles qu'ils ne peuvent pas être renommés ou donnés comme paramètres réels génériques).
Opérateur | Opération | Type d'opérande gauche | Type d'opérande droit | Type de résultat |
---|---|---|---|---|
* | multiplication | tout type de virgule fixe | tout type de virgule fixe | universal_fixed |
/ | division | tout type de point fixe | tout type de point fixe | universal_fixed |
La multiplication d'opérandes de même type ou de types de virgule fixe différents est exacte et fournit un résultat de type de virgule fixe prédéfini anonyme universal_fixed dont le delta est arbitrairement petit. Le résultat d'une telle multiplication doit toujours être explicitement converti en un type numérique. Cela garantit un contrôle explicite de la précision du calcul. Les mêmes considérations s'appliquent à la division d'une valeur de virgule fixe par une autre valeur de virgule fixe. Aucun autre opérateur n'est défini pour le type universal_fixed.
L'exception NUMERIC_ERROR est générée par la division entière, rem et mod si l'opérande de droite est zéro.
Exemples :
Expression | Valeur | Type de résultat |
---|---|---|
I**J | 2 | identique à 1 et J, c'est-à-dire INTEGER |
K/J | 1 | même que K et J, c'est-à-dire INTEGER |
K mod J | 1 | identique à K et J, c'est-à-dire. INTEGER |
X/Y | 0.5 | identique à X et Y, c'est-à-dire REAL |
F/2 | 0.05 | même que F, c'est-à-dire FRACTION |
3*F | 0.3 | même que F, c'est-à-dire FRACTION |
F*G | 0.01 | universal_fixed, conversion nécessaire |
FRACTION(F*G) | 0.01 | FRACTION, comme indiqué par la conversion |
REAL(J)*Y | 4.0 | REAL, le type des deux opérandes après conversion de J |
Remarques : Pour des valeurs positives de A et B, A/B est le quotient et A rem B est le reste lorsque A est divisé par B. Les relations suivantes sont satisfaites par l'opérateur rem :
- A rem (-B) = A rem B
- (-A) rem B = -(A rem B)
Pour tout entier K, l'identité suivante est vraie :
Les relations entre la division entière, le reste et le module sont illustrées par le tableau suivant :
A | B | A/B | A rem B | A mod B | A | B | A/B | A rem B | A mod B |
---|---|---|---|---|---|---|---|---|---|
10 | 5 | 2 | 0 | 0 | -10 | 5 | -2 | 0 | 0 |
11 | 5 | 2 | 1 | 1 | -11 | 5 | -2 | -1 | 4 |
12 | 5 | 2 | 2 | 2 | -12 | 5 | -2 | -2 | 3 |
13 | 5 | 2 | 3 | 3 | -13 | 5 | -2 | -3 | 2 |
14 | 5 | 2 | 4 | 4 | -14 | 5 | -2 | -4 | 1 |
10 | -5 | -2 | 0 | 0 | -10 | -5 | 2 | 0 | 0 |
11 | -5 | -2 | 1 | -4 | -11 | -5 | 2 | -1 | -1 |
12 | -5 | -2 | 2 | -3 | -12 | -5 | 2 | -2 | -2 |
13 | -5 | -2 | 3 | -2 | -13 | -5 | 2 | -3 | -3 |
14 | -5 | -2 | 4 | -1 | -14 | -5 | 2 | -4 | -4 |
Opérateurs de priorité la plus élevée
L'opérateur unaire de priorité la plus élevée abs est prédéfini pour tout type numérique. L'opérateur unaire de priorité la plus élevée not est prédéfini pour tout type booléen et tout type de tableau unidimensionnel dont les composantes ont un type booléen.
Opérateur | Opération | Type d'opérande | Type de résultat |
---|---|---|---|
abs | valeur absolue | tout type numérique | même type numérique |
not | négation logique | tout type booléen tableau de composants booléens |
même type booléen même type de tableau |
L'opérateur not s'appliquant à un tableau unidimensionnel de composantes booléens produit un tableau booléen unidimensionnel avec les mêmes limites; chaque composante du résultat est obtenu par négation logique de composante correspondant de l'opérande (c'est-à-dire le composant ayant la même valeur d'index).
L'opérateur d'exponentiation de priorité la plus élevée ** est prédéfini pour chaque type d'entier et pour chaque type de virgule flottante. Dans les deux cas, l'opérande de droite, appelé exposant, est du type prédéfini INTEGER.
Opérateur | Opération | Type d'opérande gauche | Type d'opérande droit | Type de résultat |
---|---|---|---|---|
** | exponentiation | tout type entier tout type à virgule flottante |
INTEGER INTEGER |
même à gauche même à gauche |
L'exponentiation avec un exposant positif équivaut à la multiplication répétée de l'opérande de gauche par lui-même, comme indiqué par l'exposant et de gauche à droite. Pour un opérande de type à virgule flottante, l'exposant peut être négatif, auquel cas la valeur est l'inverse de la valeur avec l'exposant positif. L'exponentiation avec un exposant nul donne la valeur un. L'exponentiation d'une valeur de type à virgule flottante est approximative. L'exponentiation d'un entier lève l'exception CONSTRAINT_ERROR pour un exposant négatif.
Précision des opérations avec des opérandes réels
Un sous-type réel spécifie un ensemble de numéros de modèle. La précision requise pour toute opération de base ou prédéfinie donnant un résultat réel, ainsi que le résultat de toute relation prédéfinie entre des opérandes réels sont définis en termes de ces numéros de modèle.
Un intervalle de modèle d'un sous-type est tout intervalle dont les limites sont des numéros de modèle du sous-type. L'intervalle de modèle associé à une valeur appartenant à un sous-type réel est le plus petit intervalle de modèle (du sous-type) incluant la valeur. (L'intervalle de modèle associé à un numéro de modèle d'un sous-type se compose uniquement de ce numéro.)
Pour toute opération de base ou opérateur prédéfini produisant un résultat d'un sous-type réel, les limites requises sur le résultat sont données par un intervalle de modèle défini comme suit :
- L'intervalle de modèle de résultat est le plus petit intervalle de modèle (du sous-type de résultat) incluant le minimum et le maximum de toutes les valeurs obtenues en appliquant l'opération mathématique (exacte), lorsque chaque opérande reçoit une valeur de l'intervalle de modèle (du sous-type d'opérande) défini pour l'opérande.
- L'intervalle de modèle d'un opérande étant lui-même le résultat d'une opération, autre qu'une conversion implicite, est l'intervalle de modèle résultant de cette opération.
- L'intervalle de modèle d'un opérande dont la valeur est obtenue par conversion implicite d'une expression universelle est l'intervalle de modèle associé à cette valeur dans le sous-type d'opérande.
L'intervalle du modèle de résultat est indéfini si la valeur absolue de l'un des résultats mathématiques ci-dessus dépasse le plus grand nombre sûr du type de résultat. Chaque fois que l'intervalle du modèle de résultat est indéfini, il est hautement souhaitable que l'exception NUMERIC_ERROR soit levée si l'implémentation ne peut pas produire un résultat réel étant dans l'intervalle des nombres sûrs. Cela n'est cependant pas requis par les règles de langage, compte tenu du fait que certaines machines cibles ne permettent pas une détection facile des situations de dépassement. La valeur de l'attribut MACHINE_OVERFLOWS indique si la machine cible lève l'exception NUMERIC_ERROR dans les situations de dépassement.
Les nombres sûrs d'un type réel sont définis comme un sur-ensemble des nombres de modèles, pour lesquels les limites d'erreur suivent les mêmes règles que pour les nombres de modèles. Toute définition donnée dans cette section en termes d'intervalles de modèles peut donc être étendue aux intervalles sûrs de nombres sûrs. Une conséquence de cette extension est qu'une implémentation n'est pas autorisée à déclencher l'exception NUMERIC_ERROR lorsque l'intervalle de résultat est un intervalle sûr.
Pour le résultat de l'exponentiation, l'intervalle de modèle définissant les limites du résultat est obtenu en appliquant les règles ci-dessus à la séquence de multiplications définie par l'exposant, et à la division finale dans le cas d'un exposant négatif.
Pour le résultat d'une relation entre deux opérandes réels, considérons pour chaque opérande l'intervalle de modèle (du sous-type d'opérande) défini pour l'opérande ; le résultat peut être n'importe quelle valeur obtenue en appliquant la comparaison mathématique à des valeurs choisies arbitrairement dans les intervalles de modèle d'opérande correspondants. Si l'un ou les deux intervalles de modèle d'opérande ne sont pas définis (et si aucune des évaluations d'opérande ne génère d'exception), le résultat de la comparaison peut être n'importe quelle valeur possible (c'est-à-dire TRUE ou FALSE).
Le résultat d'un test d'appartenance est défini en termes de comparaisons de la valeur d'opérande avec les limites inférieure et supérieure de l'intervalle ou de la marque de type donnée (les règles habituelles s'appliquent à ces comparaisons).
Remarque : pour un type à virgule flottante, les nombres 15,0, 3,0 et 5,0 sont toujours des numéros de modèle. Par conséquent, X/Y où X est égal à 15,0 et Y est égal à 3,0 donne exactement 5,0 selon les règles ci-dessus. Dans le cas général, la division ne donne pas de numéros de modèle et, par conséquent, on ne peut pas supposer que (1,0/X)*X - 1,0.
Conversions de type
L'évaluation d'une conversion de type explicite évalue l'expression donnée comme opérande et convertit la valeur résultante en un type cible spécifié. Les conversions de type explicites sont autorisées entre des types étroitement liés comme défini ci-dessous :
conversion_de_type ::= marque_type(expression) |
Le type cible d'une conversion de type est le type de base de la marque de type. Le type de l'opérande d'une conversion de type doit pouvoir être déterminé indépendamment du contexte (en particulier, indépendamment du type cible). De plus, l'opérande d'une conversion de type ne doit pas être un littéral null, un allocateur, un agrégat ou un littéral de chaîne ; une expression entre parenthèses n'est autorisée comme opérande d'une conversion de type que si l'expression seule est autorisée.
Une conversion vers un sous-type consiste en une conversion vers le type cible suivie d'une vérification que le résultat de la conversion appartient au sous-type. Une conversion d'un opérande d'un type donné vers le type lui-même est autorisée.
Les autres conversions de type explicite autorisées correspondent aux trois cas suivants :
- Types numériques : l'opérande peut être de n'importe quel type numérique ; la valeur de l'opérande est convertie dans le type cible devant également être un type numérique. Pour les conversions impliquant des types réels, le résultat est dans la précision du sous-type spécifié. La conversion d'une valeur réelle en un type entier s'arrondit à l'entier le plus proche ; si l'opérande est à mi-chemin entre deux entiers (dans la précision du sous-type réel), l'arrondi peut être supérieur ou inférieur.
- Types dérivés : la conversion est autorisée si l'un des types cible et opérande est dérivé de l'autre, directement ou indirectement, ou s'il existe un troisième type dont les deux types sont dérivés, directement ou indirectement.
- Types de tableau : la conversion est autorisée si le type d'opérande et le type cible sont des types de tableau qui satisfont aux conditions suivantes : les deux types doivent avoir la même dimensionnalité ; pour chaque position d'index, les types d'index doivent être soit identiques, soit convertibles l'un en l'autre ; les types des composantes doivent être identiques ; enfin, si le type du composante est un type avec discriminants ou un type d'accès, les sous-types des composantes doivent être tous les deux contraints ou tous les deux non contraints. Si la marque de type désigne un type de tableau non contraint, alors, pour chaque position d'index, les limites du résultat sont obtenues en convertissant les limites de l'opérande au type d'index correspondant du type cible. Si la marque de type désigne un sous-type de tableau contraint, alors les limites du résultat sont celles imposées par la marque de type. Dans les deux cas, la valeur de chaque composant du résultat est celle du composante correspondant de l'opérande.
Dans le cas de conversions de types numériques et de types dérivés, l'exception CONSTRAINT_ERROR est levée par l'évaluation d'une conversion de type si le résultat de la conversion ne satisfait pas une contrainte imposée par la marque de type.
Dans le cas de types de tableau, une vérification est effectuée pour s'assurer que toute contrainte sur le sous-type de composante est la même pour le type de tableau d'opérande que pour le type de tableau cible. Si la marque de type désigne un type de tableau sans contrainte et si l'opérande n'est pas un tableau nul, alors, pour chaque position d'index, une vérification est effectuée pour s'assurer que les limites du résultat appartiennent au sous-type d'index correspondant du type cible. Si la marque de type désigne un sous-type de tableau contraint, une vérification est effectuée pour s'assurer que pour chaque composant de l'opérande, il existe une composante correspondant du sous-type cible, et vice versa. L'exception CQNSTRAINT_ERRQR est levée si l'une de ces vérifications échoue.
Si une conversion d'un type à un autre est autorisée, la conversion inverse est également autorisée. Cette conversion inverse est utilisée lorsqu'un paramètre réel de mode in out ou out a la forme d'une conversion de type d'un nom (de variable).
En dehors des conversions de type explicites, la seule forme autorisée de conversion de type est la conversion implicite d'une valeur de type universal_integer ou universal_real en un autre type numérique. Une conversion implicite d'un opérande de type universal_integer en un autre type entier, ou d'un opérande de type universal_real en un autre type réel, ne peut être appliquée que si l'opérande est soit un littéral numérique, un nombre nommé ou un attribut; un tel opérande est appelé opérande universel convertible dans cette section. Une conversion implicite d'un opérande universel convertible est appliquée si et seulement si le contexte complet le plus profond détermine un type cible (numérique) unique pour la conversion implicite, et il n'y a pas d'interprétation légale de ce contexte sans cette conversion.
Remarques : Les règles de conversion implicite impliquent qu'aucune conversion implicite ne soit jamais appliquée à l'opérande d'une conversion de type explicite. De même, les conversions implicites ne sont pas appliquées si les deux opérandes d'un opérateur relationnel prédéfini sont des opérandes universels convertibles.
Le langage permet des conversions implicites de sous-types dans le cas de types tableau. Une conversion de type explicite peut avoir pour effet un changement de représentation. Les conversions explicites sont également utilisées pour les paramètres réels.
Exemples de conversion de type numérique :
Exemple de conversion entre types dérivés :
- type A_FORM is new B_FORM;
-
- X : A_FORM;
- Y : B_FORM;
-
- X := A_FORM(Y);
- Y := B_FORM(X); -- la conversion inverse
Exemples de conversions entre types de tableaux :
Exemples de conversions implicites :
- X : INTEGER := 2;
-
- X + 1 + 2 -- conversion implicite de chaque littéral entier
- 1 + 2 + X -- conversion implicite de chaque littéral entier
- X + (1 + 2) -- conversion implicite de chaque littéral entier
-
- 2 = (1 + 1) -- pas de conversion implicite : le type est universal_integer
- A'LENGTH = B'LENGTH -- pas de conversion implicite : le type est universal_integer
- C : constant := 3 + 2; -- pas de conversion implicite : le type est universal_integer
-
- X = 3 and 1 = 2 -- conversion implicite de 3, mais pas de 1 et 2
Expressions qualifiées
Une expression qualifiée est utilisée pour indiquer explicitement le type, et éventuellement le sous-type, d'un opérande étant l'expression ou l'agrégat donné.
expression_qualifiee ::= marque_type'(expression) | marque_type'agregat |
L'opérande doit avoir le même type que le type de base de la marque de type. La valeur d'une expression qualifiée est la valeur de l'opérande. L'évaluation d'une expression qualifiée évalue l'opérande et vérifie que sa valeur appartient au sous-type désigné par la marque de type. L'exception CONSTRAINT_ERROR est levée si cette vérification échoue.
Exemples :
- type MASK is (FIX, DEC, EXP, SIGNIF);
- type CODE is (FIX, CLA, DEC, TNZ, SUB);
-
- PRINT (MASK'(DEC)); -- DEC est de type MASK
- PRINT (CODE'(DEC)); -- DEC est de type CODE
-
- for J in CODE'(FIX) .. CODE'(DEC) loop ... -- qualification requise pour FIX ou DEC
- for J in CODE range FIX .. DEC loop ... -- qualification inutile
- for J in CODE'(FIX) .. DEC loop ... -- qualification inutile pour le DEC
-
- DOZEN'(1 | 3 | 5 | 7 => 2, others => 0)
Remarques : Lorsque le type d'un littéral ou d'un agrégat d'énumération n'est pas connu du contexte, une expression qualifiée peut être utilisée pour indiquer explicitement le type. Par exemple, un littéral d'énumération surchargé doit être qualifié dans les cas suivants : lorsqu'il est donné comme paramètre dans un appel de sous-programme à un sous-programme surchargé ne pouvant pas être identifié autrement sur la base des types de paramètres ou de résultats restants, dans une expression relationnelle où les deux opérandes sont des littéraux d'énumération surchargés, ou dans une plage de paramètres de tableau ou de boucle où les deux bornes sont des littéraux d'énumération surchargés. La qualification explicite est également utilisée pour spécifier à laquelle d'un ensemble de fonctions sans paramètre surchargées il est fait référence, ou pour contraindre une valeur à un sous-type donné.
Allocateurs
L'évaluation d'un allocateur crée un objet et génère une valeur d'accès désignant l'objet :
allocateur ::= new sous_type_indication | new expression_qualifiee |
Le type de l'objet créé par un allocateur est le type de base de la marque de type donnée dans l'indication de sous-type ou dans l'expression qualifiée. Pour un allocateur avec une expression qualifiée, cette expression définit la valeur initiale de l'objet créé. Le type de la valeur d'accès renvoyée par un allocateur doit être déterminable uniquement à partir du contexte, mais en utilisant le fait que la valeur renvoyée est d'un type d'accès ayant le type désigné nommé.
Les seules formes de contrainte autorisées dans l'indication de sous-type d'un allocateur sont les contraintes d'index et de discriminant. Si un allocateur inclut une indication de sous-type et si le type de l'objet créé est un type de tableau ou un type avec des discriminants n'ayant pas d'expressions par défaut, alors l'indication de sous-type doit soit désigner un sous-type contraint, soit inclure une contrainte d'index ou de discriminant explicite.
Si le type de l'objet créé est un type de tableau ou un type avec des discriminants, alors l'objet créé est toujours contraint. Si l'allocateur inclut une indication de sous-type, l'objet créé est contraint soit par le sous-type, soit par les valeurs discriminantes par défaut. Si l'allocateur inclut une expression qualifiée, l'objet créé est contraint par les bornes ou les discriminants de la valeur initiale. Pour les autres types, le sous-type de l'objet créé est le sous-type défini par l'indication de sous-type de la définition du type d'accès.
Pour l'évaluation d'un allocateur, l'élaboration de l'indication de sous-type ou l'évaluation de l'expression qualifiée est effectuée en premier. Le nouvel objet est ensuite créé. Les initialisations sont ensuite effectuées comme pour un objet déclaré; l'initialisation est considérée comme explicite dans le cas d'une expression qualifiée; les éventuelles initialisations sont implicites dans le cas d'une indication de sous-type. Enfin, une valeur d'accès désignant l'objet créé est renvoyée.
Une implémentation doit garantir que tout objet créé par l'évaluation d'un allocateur reste alloué aussi longtemps que cet objet ou l'un de ses sous-composantes est accessible directement ou indirectement, c'est-à-dire aussi longtemps qu'il peut être désigné par un nom. De plus, si un objet ou l'un de ses sous-composantes appartient à un type de tâche, il est considéré comme accessible tant que la tâche n'est pas terminée. Une implémentation peut (mais n'est pas obligée) récupérer l'entreposage occupé par un objet créé par un allocateur, une fois que cet objet est devenu inaccessible.
Lorsqu'une application a besoin d'un contrôle plus étroit sur l'allocation de stockage pour les objets désignés par des valeurs d'un type d'accès, ce contrôle peut être réalisé par un ou plusieurs des moyens suivants :
- La quantité totale de stockage disponible pour la collection d'objets d'un type d'accès peut être définie au moyen d'une clause de longueur.
- Le pragma CONTROLLED informe l'implémentation que la récupération automatique de stockage ne doit pas être effectuée pour les objets désignés par les valeurs du
type d'accès, sauf après avoir quitté l'instruction de bloc la plus interne, le corps du sous-programme ou le corps de la tâche entourant la déclaration de type d'accès, ou
après avoir quitté le programme principal.
pragma CONTROLLED (nom_du_type_d_accès_simple); Un pragma CONTROLLED pour un type d'accès donné est autorisé aux mêmes endroits qu'une clause de représentation pour le type. Ce pragma n'est pas autorisé pour un type dérivé.
- La désallocation explicite de l'objet désigné par une valeur d'accès peut être réalisée en appelant une procédure obtenue par instanciation de la procédure de bibliothèque générique prédéfinie UNCHECKED_DEALLOCATION.
L'exception STORAGE_ERROR est levée par un allocateur s'il n'y a pas assez de stockage. Notez également que l'exception CONSTRAINT_ERROR peut être levée par l'évaluation de l'expression qualifiée, par l'élaboration de l'indication de sous-type ou par l'initialisation.
Exemples :
- new CELL'(0, null, null) -- initialisé explicitement
- new CELL'(VALUE => 0, SUCC => null, PRED => null) -- initialisé explicitement
- new CELL -- non initialisé
-
- new MATRIX(1 .. 10, 1 .. 20) -- seules les limites sont données
- new MATRIX'(1 .. 10 => (1 .. 20 => 0.0)) -- initialisé explicitement
-
- new BUFFER(100) -- seul le discriminant est donné
- new BUFFER'(SIZE => 80, POS => 0, VALUE => (1 .. 80 => 'A')) -- initialisé explicitement
Expressions statiques et sous-types statiques
Certaines expressions d'un type scalaire sont dites statiques. De même, certaines plages discrètes sont dites statiques et les marques de type de certains sous-types scalaires sont dites statiques.
Une expression d'un type scalaire est dite statique si et seulement si chaque opérateur primaire est l'un de ceux répertoriés dans (a) à (h) ci-dessous, chaque opérateur désigne un opérateur prédéfini et l'évaluation de l'expression fournit une valeur (c'est-à-dire qu'elle ne génère pas d'exception) :
- Un littéral d'énumération (y compris un littéral de caractère).
- Un littéral numérique.
- Un nombre nommé.
- Une constante explicitement déclarée par une déclaration de constante avec un sous-type statique et initialisée avec une expression statique.
- Un appel de fonction dont le nom de fonction est un symbole d'opérateur désignant un opérateur prédéfini, y compris un nom de fonction étant un nom développé ; chaque paramètre réel doit également être une expression statique.
- Un attribut défini par le langage d'un sous-type statique; pour un attribut étant une fonction, le paramètre réel doit également être une expression statique.
- Une expression qualifiée dont la marque de type désigne un sous-type statique et dont l'opérande est une expression statique.
- Une expression statique entre parenthèses.
Un intervalle statique est un intervalle dont les limites sont des expressions statiques. Une contrainte d'intervalle statique est une contrainte d'intervalle dont l'intervalle est statique. Un sous-type statique est soit un type de base scalaire, autre qu'un type formel générique, soit un sous-type scalaire formé en imposant à un sous-type statique soit une contrainte d'intervalle statique, soit une contrainte à virgule flottante ou fixe dont la contrainte d'intervalle, le cas échéant, est statique. Un intervalle discret statique est soit un sous-type statique, soit un intervalle statique. Une contrainte d'index statique est une contrainte d'index pour laquelle chaque sous-type d'index du type de tableau correspondant est statique, et dans laquelle chaque plage discrète est statique. Une contrainte discriminante statique est une contrainte discriminante pour laquelle le sous-type de chaque discriminant est statique, et dans laquelle chaque expression est statique.
Remarques : Si le résultat n'est pas un numéro de modèle (ou un numéro sûr) du type, la valeur obtenue par cette évaluation au moment de la compilation n'a pas besoin d'être la même que la valeur qui serait obtenue par une évaluation au moment de l'exécution.
Les attributs de tableau ne sont pas statiques : en particulier, l'attribut RANGE n'est pas statique.
Expressions universelles
Une expression universelle est soit une expression fournissant un résultat de type universal_integer, soit une expression fournissant un résultat de type universal_real. Les mêmes opérations sont prédéfinies pour le type universal_integer que pour tout type entier. Les mêmes opérations sont prédéfinies pour le type universal_real que pour tout type à virgule flottante. De plus, ces opérations incluent les opérateurs de multiplication et de division suivants :
Opérateur | Opération | Type d'opérande gauche | Type d'opérande droit | Type de résultat |
---|---|---|---|---|
* | multiplication | universal_real universal_integer |
universal_integer universal_real |
universal_real universal_real |
/ | division | universal_real | universal_integer | universal_real |
La précision de l'évaluation d'une expression universelle de type universal_real est au moins aussi bonne que celle du type à virgule flottante prédéfini le plus précis pris en charge par l'implémentation, à l'exception d'universal_real lui-même. De plus, si une expression universelle est une expression statique, l'évaluation doit être exacte.
Pour l'évaluation d'une opération d'une expression universelle non statique, une implémentation est autorisée à déclencher l'exception NUMERIC_ERROR uniquement si le résultat de l'opération est une valeur réelle dont la valeur absolue dépasse le plus grand nombre sûr du type à virgule flottante prédéfini le plus précis (à l'exclusion de universal_real), ou une valeur entière supérieure à SYSTEM.MAX_INT ou inférieure à SYSTEM.MIN_INT.
Remarque : il résulte des règles ci-dessus que le type d'une expression universelle est universal_integer si chaque élément primaire contenu dans l'expression est de ce type (à l'exclusion des paramètres réels des attributs qui sont des fonctions et à l'exclusion des opérandes de droite des opérateurs d'exponentiation) et que sinon le type est universal_real.
Exemples :