Section courante

A propos

Section administrative du site

 Langage  Elément  Tutoriel  Programmation  IDE  Annexe  Aide 
ABAP/4
Ada
Assembleur
Assembly & bytecode
ASP (Active Server Pages)
Basic
C
C++
C# (C Sharp)
Cobol
ColdFusion
Fortran
HTML
Java
JavaScript
LISP
Logo
LotusScript
Oberon
Pascal
Perl
PHP
PL/1
Prolog
Python
Rebol
REXX
Ruby
Rust
SAS
NoSQL
SQL
Swift
X++ (Axapta)
GNAT
SMALLAda
VHDL
Assembleur 370
Assembleur 1802
Assembleur 4004
Assembleur 6502
Assembleur 6800
Assembleur 68000
Assembleur 8080 et 8085
Assembleur 8089
Assembleur 80x86
Assembleur AGC4
Assembleur ARM
Assembleur DPS 8000
Assembleur i860
Assembleur Itanium
Assembleur MIPS
Assembleur PDP-11
Assembleur PowerPC
Assembleur RISC-V
Assembleur SPARC
Assembleur SuperH
Assembleur UNIVAC I
Assembleur VAX
Assembleur Z80
Assembleur Z8000
Assembleur z/Architecture
ASSEMBLER/MONITOR 64
Micol Assembler
GFA Assembler
A86
MASM (Macro Assembler)
TASM (Turbo Assembler)
CIL
Jasmin
LLVM
MSIL
Parrot
P-Code (PCode)
SWEET16
G-Pascal
ASP 1.0
ASP 2.0
ASP 3.0
ASP.NET
ASP.NET Core
ABasiC (Amiga)
Adam SmartBASIC
Altair BASIC
AmigaBASIC (Amiga)
AMOS Basic (Amiga)
Atari Basic (Atari 400, 600 XL, 800, 800XL)
Basic Apple II (Integer BASIC/APPLESOFT)
Basic Commodore 64 (CBM-BASIC)
Basic Commodore 128 (BASIC 7.0)
Basic Commodore VIC-20 (CBM-BASIC 2.0)
Basic Coco 1 (Color Basic)
Basic Coco 2 (Extended Color Basic)
Basic Coco 3 (Extended Color Basic 2.0)
BASICA (PC DOS)
Basic Pro
BBC BASIC
Blitz BASIC (Amiga)
DarkBASIC
Dartmouth BASIC
GFA-Basic (Atari ST/Amiga)
GWBASIC (MS-DOS)
Liberty BASIC
Locomotive BASIC (Amstrad CPC)
MSX-Basic
Omikron Basic (Atari ST)
Oric Extended Basic
Power Basic
Quick Basic/QBasic (MS-DOS)
Sinclair BASIC (ZX80, ZX81, ZX Spectrum)
ST BASIC (Atari ST)
Turbo Basic
Vintage BASIC
VBScript
Visual Basic (VB)
Visual Basic .NET (VB .NET)
Visual Basic pour DOS
Yabasic
BeckerBASIC
SIMONS' BASIC
Basic09 d'OS-9
Disk Extended Color Basic
Basic09 d'OS-9
Disk Extended Color Basic
Access
Excel
Visual Basic pour Windows
Visual Basic .NET pour Windows
C Shell Unix (csh)
C pour Amiga
C pour Atari ST
C pour DOS
C pour Falcon030
C pour GEMDOS (Atari ST)
C pour Linux
C pour PowerTV OS
C pour OS/2
C pour Unix
C pour Windows
Aztec C
CoCo-C
GNU C
HiSoft C
IBM C/2
Introl-C
Lattice C
Microsoft C
MinGW C
MSX-C
Open Watcom C
OS-9 C Compiler
Pure C
Quick C
Turbo C
HiSoft C for Atari ST
HiSoft C for CP/M (Amstrad CPC)
C++ pour OS/2
C++ pour Windows
Borland C++
C++Builder
IBM VisualAge C++
Intel C++
MinGW C++
Open Watcom C++
Symantec C++
Turbo C++
Visual C++
Visual C++ .NET
Watcom C++
Zortech C++
C# (C Sharp) pour Windows
Apple III Cobol
Microsoft Cobol
BlueDragon
Lucee
OpenBD
Railo
Smith Project
Microsoft Fortran
WATFOR-77
CSS
FBML
Open Graph
SVG
XML
XSL/XSLT
LESS
SASS
GCJ (GNU)
JSP
Jython
Visual J++
Node.js
TypeScript
AutoLISP
ACSLogo
LotusScript pour Windows
Amiga Oberon
Oberon .NET
Apple Pascal
Delphi/Kylix/Lazarus
Free Pascal
GNU Pascal
HighSpeed Pascal
IBM Personal Computer Pascal
Lisa Pascal
Maxon Pascal
MPW Pascal
OS-9 Pascal
OSS Personal Pascal
Pascal-86
Pascal du Cray Research
Pascal/VS
Pascal-XT
PURE Pascal
QuickPascal
RemObjets Chrome
Sun Pascal
THINK Pascal
Tiny Pascal (TRS-80)
Turbo Pascal
UCSD Pascal
VAX Pascal
Virtual Pascal
Turbo Pascal for CP/M-80
Turbo Pascal for DOS
Turbo Pascal for Macintosh
Turbo Pascal for Windows
CodeIgniter (Cadre d'application)
Drupal (Projet)
Joomla! (Projet)
Phalanger (PHP .NET)
phpBB (Projet)
Smarty (balise)
Twig (balise)
Symfony (Cadre d'application)
WordPress (Projet)
Zend (Cadre d'application)
PL360
PL/M-80
PL/M-86
Turbo Prolog
CPython
IronPython
Jython
PyPy
AREXX
Regina REXX
JMP
Btrieve
Cassandra
Clipper
CouchDB
dBASE
Hbase
Hypertable
MongoDB
Redis
Access
BigQuery
DB2
H2
Interbase
MySQL
Oracle
PostgreSQL
SAP HANA
SQL Server
Sybase
U-SQL
Introduction
Les remarques
Les opérateurs
Les instruction conditionnelles
Les instructions de boucles
Type de données
Définition de fonction
Référence de mots réservés (mots clefs)
Référence de procédures et fonctions
Les modules (Packages)
Les premiers pas
Les éléments lexicaux
Les déclarations et types
Les noms et les expressions
Les instructions
Les sous-programmes
Les paquets
Les règles de visibilité
Les tâches
Structure du programme et problèmes de compilation
Les exceptions
Les unités génériques
Clauses de représentation et fonctionnalités dépendantes de la mise en oeuvre
Les entrées-sorties
Bonjour
Biochimie
Géographie
Géométrie
Histoire
Mathématique
Médicale
Météorologie
Océanographie
Sport
Temps
Trigonométrie
Validation
Calcul du calcium corrigé
Distance en Km entre deux longitudes et latitudes
Aire d'un cercle
Aire d'une surface de prisme rectangulaire
Aire d'un triangle
Distance entre deux points
Chiffre romain
Ackermann
Exp
Factoriel
Fibonacci
Log
Nombre premier
Odd
Random
Sqrt
Triangle Pascal
Hauteur utérine
Unité de mesure
Fréquence des vagues
Hockey
Année bissextile
Date de la Pâque
Atn/ATan/ArcTan
Courriel
AdaGIDE
GNAT Programming Studio
SET's Editor
Attributs de langages prédéfinis
Les pragma de langage prédéfinis
Environnement de langage prédéfini
Référence de termes et du vocabulaire
Téléchargement
Préface
Notes légal
Dictionnaire
Recherche

Déclarations et types

Cette page décrit les types du langage et les règles de déclaration des constantes, des variables et des nombres nommés.

Déclarations

Le langage définit plusieurs types d'entités étant déclarées, explicitement ou implicitement, par des déclarations. Une telle entité peut être un littéral numérique, un objet, un discriminant, une composante d'enregistrement, un paramètre de boucle, une exception, un type, un sous-type, un sous-programme, un paquet, une unité de tâche, une unité générique, une entrée unique, une famille d'entrées, un paramètre formel (d'un sous-programme, d'une entrée ou d'un sous-programme générique), un paramètre formel générique, un bloc ou une boucle nommés, une instruction étiquetée ou une opération (en particulier, un attribut ou un littéral d'énumération).

Il existe plusieurs formes de déclaration. Une déclaration de base est une forme de déclaration définie comme suit :

declaration_de_base ::=
  declaration_d_objet           | numero_declaration
type_declaration              | declaration_de_sous_type
declaration_de_sous-programme | declaration_de_paquet
declaration_de_tache          | declaration_generique
declaration_d_exception       | instantiation_generique
renommage_declaration         | declaration_constante_differee

Certaines formes de déclaration apparaissent toujours (explicitement) dans le cadre d'une déclaration de base; ces formes sont les spécifications discriminantes, les déclarations de composants, les déclarations d'entrées, les spécifications de paramètres, les déclarations de paramètres génériques et les spécifications de littéraux d'énumération. Une spécification de paramètre de boucle est une forme de déclaration qui n'apparaît que dans certaines formes d'instructions de boucle.

Les autres formes de déclaration sont implicites : le nom d'un bloc, le nom d'une boucle et un libellé d'instruction sont déclarés implicitement. Certaines opérations sont déclarées implicitement. Pour chaque forme de déclaration, les règles de langage définissent une certaine région de texte appelée la portée de la déclaration. Plusieurs formes de déclaration associent un identifiant à une entité déclarée. Dans sa portée, et seulement là, il existe des endroits où il est possible d'utiliser l'identifiant pour faire référence à l'entité déclarée associée; ces endroits sont définis par les règles de visibilité. À ces endroits, l'identifiant est dit être un nom de l'entité (son nom simple) ; le nom est dit désigner l'entité associée.

Certaines formes de spécification de littéraux d'énumération associent un littéral de caractère à l'entité déclarée correspondante. Certaines formes de déclaration associent un symbole d'opérateur ou une autre notation à une opération déclarée explicitement ou implicitement.

Le processus par lequel une déclaration atteint son effet est appelé l'élaboration de la déclaration ; ce processus se produit pendant l'exécution du programme.

Après son élaboration, une déclaration est dite élaborée. Avant la fin de son élaboration (y compris avant l'élaboration), la déclaration n'est pas encore élaborée. L'élaboration de toute déclaration a toujours au moins pour effet d'accomplir ce changement d'état (de non encore élaboré à élaboré). L'expression «l'élaboration n'a pas d'autre effet» est utilisée dans cette section chaque fois que ce changement d'état est le seul effet de l'élaboration pour une forme de déclaration. Un processus d'élaboration est également défini pour les parties déclaratives, les éléments déclaratifs et les unités de compilation.

Les déclarations d'objet, de nombre, de type et de sous-type sont décrites ici. Les déclarations de base restantes sont décrites dans les pages suivantes.

Remarque : les règles de syntaxe utilisent le terme identifiant pour la première occurrence d'un identifiant dans une forme de déclaration; le terme nom simple est utilisé pour toute occurrence d'un identifiant qui désigne déjà une entité déclarée.

Objets et nombres nommés

Un objet est une entité contenant (possédant) une valeur d'un type donné. Un objet est l'un des éléments suivants :

Une déclaration de nombre est une forme spéciale de déclaration d'objet associant un identifiant à une valeur de type universal_integer ou universal_real :

declaration_d_objet ::=
      liste_d_identifiants : [constantsous_type_indication [:= expression];
    | liste_d_identifiants : [constantdefinition_de_tableau_contraint [:= expression];

numero_declaration ::=
liste_d_identifiants : constant := expression_statique_universelle;
liste_d_identifiants ::= identifiant [, identifiant]

Une déclaration d'objet est dite déclaration d'objet unique si sa liste d'identifiants possède un identifiant unique; elle est dite déclaration d'objet multiple si la liste d'identifiants possède deux identifiants ou plus. Une déclaration d'objet multiple est équivalente à une séquence du nombre correspondant de déclarations d'objet unique. Pour chaque identifiant de la liste, la séquence équivalente possède une déclaration d'objet unique formée par cet identifiant, suivi de deux points et de ce qui apparaît à droite des deux points dans la déclaration d'objet multiple; la séquence équivalente est dans le même ordre que la liste d'identifiants. Une équivalence similaire s'applique également aux listes d'identifiants des déclarations de nombre, des déclarations de composants, des spécifications discriminantes, des spécifications de paramètres, des déclarations de paramètres génériques, des déclarations d'exception et des déclarations de constantes différées.

Dans le reste de cette section de référence, des explications sont données pour les déclarations avec un identifiant unique ; les explications correspondantes pour les déclarations avec plusieurs identifiants découlent de l'équivalence indiquée ci-dessus.

Exemple :

  1. -- la déclaration d'objet multiple
  2. JEAN, PAUL : PERSON_NAME := new PERSON(SEX => M);
  3. -- est équivalent aux deux déclarations d'objet unique dans l'ordre donné
  4. JEAN : PERSON_NAME := new PERSON(SEX => M);
  5. PAUL : PERSON_NAME := new PERSON(SEX => M);

Déclarations d'objet

Une déclaration d'objet déclare un objet dont le type est donné soit par une indication de sous-type, soit par une définition de tableau contraint. Si la déclaration d'objet inclut le délimiteur composé d'affectation suivi d'une expression, l'expression spécifie une valeur initiale pour l'objet déclaré ; le type de l'expression doit être celui de l'objet.

L'objet déclaré est une constante si le mot réservé constant apparaît dans la déclaration d'objet ; la déclaration doit alors inclure une initialisation explicite. La valeur d'une constante ne peut pas être modifiée après l'initialisation. Les paramètres formels de mode in des sous-programmes et des entrées, et les paramètres formels génériques de mode in, sont également des constantes ; un paramètre de boucle est une constante dans la boucle correspondante ; un sous-composant ou une tranche d'une constante est une constante.

Un objet n'étant pas une constante est appelé une variable (en particulier, l'objet déclaré par une déclaration d'objet n'incluant pas le mot réservé constant est une variable). Les seules façons de modifier la valeur d'une variable sont soit directement par une affectation, soit indirectement lorsque la variable est mise à jour par une instruction d'appel de procédure ou d'entrée (cette action peut être effectuée soit sur la variable elle-même, soit sur une sous-composante de la variable, soit sur une autre variable ayant la variable donnée comme sous-composante).

L'élaboration d'une déclaration d'objet se déroule comme suit :

  1. L'indication de sous-type ou la définition du tableau contraint est d'abord élaborée. Cela établit le sous-type de l'objet.
  2. Si la déclaration d'objet comprend une initialisation explicite, la valeur initiale est obtenue en évaluant l'expression correspondante. Sinon, toutes les valeurs initiales implicites de l'objet ou de ses sous-composantes sont évaluées.
  3. L'objet est créé.
  4. Toute valeur initiale (explicite ou implicite) est attribuée à l'objet ou au sous-composant correspondant.

Les valeurs initiales implicites sont définies pour les objets déclarés par des déclarations d'objet, et pour les composants de tels objets, dans les cas suivants :

Dans le cas d'une composante étant lui-même un objet composite et dont la valeur n'est définie ni par une initialisation explicite ni par une expression par défaut, les valeurs initiales implicites des composantes de l'objet composite sont définies par les mêmes règles que pour un objet déclaré.

Les étapes (a) à (d) sont effectuées dans l'ordre indiqué. Pour l'étape (b), si l'expression par défaut d'un discriminant est évaluée, alors cette évaluation est effectuée avant celle des expressions par défaut des sous-composantes dépendant des discriminants, et également avant celle des expressions par défaut incluant le nom du discriminant. Outre la règle précédente, l'évaluation des expressions par défaut est effectuée dans un ordre n'étant pas défini par le langage.

L'initialisation d'un objet (l'objet déclaré ou l'un de ses sous-composantes) vérifie que la valeur initiale appartient au sous-type de l'objet; pour un objet tableau déclaré par une déclaration d'objet, une conversion implicite de sous-type est d'abord appliquée comme pour une instruction d'affectation, sauf si l'objet est une constante dont le sous-type est un type tableau non contraint. L'exception CONSTRAINT_ERROR est levée si cette vérification échoue.

La valeur d'une variable scalaire est indéfinie après l'élaboration de la déclaration d'objet correspondante à moins qu'une valeur initiale ne soit affectée à la variable par une initialisation (explicitement ou implicitement).

Si l'opérande d'une conversion de type ou d'une expression qualifiée est une variable ayant des sous-composantes scalaires avec des valeurs indéfinies, alors les valeurs des sous-composants correspondants du résultat sont indéfinies. L'exécution d'un programme est erronée si elle tente d'évaluer une variable scalaire avec une valeur indéfinie. De même, l'exécution d'un programme est erronée si elle tente d'appliquer un opérateur prédéfini à une variable ayant une sous-composante scalaire avec une valeur indéfinie.

Exemples de déclarations de variables :

  1. COUNT, SUM   : INTEGER;
  2. SIZE         : INTEGER range 0 .. 10_000 := 0;
  3. SORTED       : BOOLEAN := FALSE;
  4. COLOR_TABLE  : array(1 .. N) of COLOR;
  5. OPTION       : BIT_VECTOR(1 .. 10) := (others => TRUE);

Exemples de déclarations constantes :

  1. LIMIT     : constant INTEGER := 10_000;
  2. LOW_LIMIT : constant INTEGER := LIMIT/10;
  3. TOLERANCE : constant REAL := DISPERSION(1.15)

Remarque : L'expression initialisant un objet constant n'a pas besoin d'être une expression statique. Dans les exemples ci-dessus, LIMIT et LOW_LIMIT sont initialisées avec des expressions statiques, mais TOLERANCE ne l'est pas si DISPERSION est une fonction définie par l'utilisateur.

Déclarations de nombres

Une déclaration de nombre est une forme spéciale de déclaration de constante. Le type de l'expression statique donnée pour l'initialisation d'une déclaration de nombre doit être soit du type universal_integer, soit du type universal_real. La constante déclarée par une déclaration de nombre est appelée un nombre nommé et a le type de l'expression statique.

Remarque : Il résulte de ces règles de type universel que si chaque élément primaire contenu dans l'expression est du type universal_integer, alors le nombre nommé est également de ce type. De même, si chaque élément primaire est du type universal_real, alors le nombre nommé est également de ce type.

Exemples de déclarations de nombres :

  1. PI            : constant := 3.141 59_26536;   -- un nombre réel
  2. TWO_PI        : constant := 2.0*PI;           -- un nombre réel
  3. MAX           : constant := 500;              -- un nombre entier
  4. POWER_16      : constant := 2**16;            -- l'entier 65_536
  5. ONE, UN, EINS : constant := 1;                -- trois noms différents pour 1

Types et sous-types

Un type est caractérisé par un ensemble de valeurs et un ensemble d'opérations.

Il existe plusieurs classes de types. Les types scalaires sont des types entiers, des types réels et des types définis par l'énumération de leurs valeurs; les valeurs de ces types n'ont pas de composantes. Les types tableau et enregistrement sont composites; une valeur d'un type composite est constituée de valeurs composantes. Un type d'accès est un type dont les valeurs donnent accès aux objets. Les types privés sont des types pour lesquels l'ensemble des valeurs possibles est bien défini, mais n'est pas directement disponible pour les utilisateurs de ces types. Enfin, il existe des types de tâches.

Certains types d'enregistrement et privés ont des composants spéciaux appelés discriminants dont les valeurs distinguent les formes alternatives de valeurs de l'un de ces types. Si un type privé a des discriminants, ils sont connus des utilisateurs du type. Par conséquent, un type privé n'est connu que par son nom, ses discriminants s'il y en a, et par l'ensemble d'opérations correspondant.

L'ensemble des valeurs possibles pour un objet d'un type donné peut être soumis à une condition appelée contrainte (le cas où la contrainte n'impose aucune restriction est également inclus); une valeur est dite satisfaire à une contrainte si elle satisfait à la condition correspondante. Un sous-type est un type avec une contrainte ; une valeur est dite appartenir à un sous-type d'un type donné si elle appartient au type et satisfait à la contrainte; le type donné est appelé le type de base du sous-type. Un type est un sous-type de lui-même ; un tel sous-type est dit «sans contrainte», il correspond à une condition qui n'impose aucune restriction. Le type de base d'un type est le type lui-même.

L'ensemble des opérations définies pour un sous-type d'un type donné comprend les opérations définies pour le type; cependant, l'opération d'affectation à une variable ayant un sous-type donné n'affecte que les valeurs appartenant au sous-type. Des opérations supplémentaires, telles que la qualification (dans une expression qualifiée), sont implicitement définies par une déclaration de sous-type.

Certains types ont des valeurs initiales par défaut définies pour les objets du type; certains autres types ont des expressions par défaut définies pour certains ou tous leurs composantes. Certaines opérations de types et de sous-types sont appelées «attributs».

Le terme sous-composante est utilisé dans cette page à la place du terme composante pour indiquer soit une composante, soit une composante d'une autre composante ou sous-composante. Lorsque d'autres sous-composants sont exclus, le terme composante est utilisé à la place.

Un type donné ne doit pas avoir de sous-composante dont le type est le type donné lui-même.

Le nom d'une classe de types est utilisé dans cette page comme qualificatif pour les objets et les valeurs ayant un type de la classe considérée. Par exemple, le terme «objet tableau» est utilisé pour un objet dont le type est un type tableau; de même, le terme «valeur d'accès» est utilisé pour une valeur d'un type d'accès.

Remarque : L'ensemble des valeurs d'un sous-type est un sous-ensemble des valeurs du type de base. Ce sous-ensemble n'a pas besoin d'être un sous-ensemble propre; il peut s'agir d'un sous-ensemble vide.

Déclarations de type

Une déclaration de type déclare un type :

declaration_type ::= declaration_de_type_complet
  | declaration_de_type_incomplete | declaration_de_type_prive

declaration_de_type_complet ::=
     type identifiant [partie_discriminante] is type_definition;

type_definition ::=
  definition_du_type_d_enumeration    | definition_de_type_entier
definition_de_type_reel             | definition_du_type_de_tableau
definition_du_type_d_enregistrement | definition_du_type_d_acces
definition_de_type_derive

L'élaboration d'une déclaration de type complète consiste en l'élaboration de la partie discriminante, s'il y en a une (sauf dans le cas de la déclaration de type complète pour une déclaration de type incomplète ou privée), et en l'élaboration de la définition de type.

Les types créés par l'élaboration de définitions de type distinctes sont des types distincts. De plus, l'élaboration de la définition de type pour un type numérique ou dérivé crée à la fois un type de base et un sous-type du type de base ; il en va de même pour une définition de tableau contraint (l'une des deux formes de définition de type de tableau).

Le nom simple déclaré par une déclaration de type complète désigne le type déclaré, à moins que la déclaration de type ne déclare à la fois un type de base et un sous-type du type de base, auquel cas le nom simple désigne le sous-type, et le type de base est anonyme. Un type est dit anonyme s'il n'a pas de nom simple. À des fins explicatives, ce page de référence fait parfois référence à un type anonyme par un pseudo-nom, écrit en italique, et utilise de tels pseudo-noms aux endroits où la syntaxe requiert normalement un identifiant.

Exemples de définitions de type :

  1. (WHITE, RED, YELLOW, GREEN, BLUE, BROWN, BLACK)
  2. range 1 .. 72
  3. array(1 .. 10) of INTEGER

Exemples de déclarations de type :

  1. type COLOR is (WHITE, RED, YELLOW, GREEN, BLUE, BROWN, BLACK);
  2. type COLUMN is range 1 .. 72;
  3. type TABLE is array(1 .. 10) of INTEGER;

Remarques : Deux définitions de type définissent toujours deux types distincts, même si elles sont textuellement identiques. Ainsi, les définitions de type de tableau données dans les déclarations de A et B ci-dessous définissent des types distincts.

  1. A : array(1 .. 10) of BOOLEAN;
  2. B : array(1 .. 10) of BOOLEAN;

Si A et B sont déclarés par une déclaration d'objet multiple comme ci-dessous, leurs types sont néanmoins différents, puisque la déclaration d'objet multiple est équivalente aux deux déclarations d'objet unique ci-dessus :

  1. A, B : array(1 .. 10) of BOOLEAN;

Les déclarations de types incomplètes sont utilisées pour la définition de types récursifs et mutuellement dépendants. Les déclarations de types privés sont utilisées dans les spécifications de paquets et dans les déclarations de paramètres génériques.

Déclarations de sous-type

Une déclaration de sous-type déclare un sous-type :

declaration_de_sous_type ::=
  subtype identifiant is sous_type_indication;

sous_type_indication ::= marque_type [contrainte]

marque_type ::= type_nom | sous_type_nom

contrainte ::=
    contrainte_de_portee | contrainte_en_virgule_flottante | contrainte_a_point_fixe
  | contrainte_index | contrainte_discriminante

Une marque de type désigne un type ou un sous-type. Si une marque de type est le nom d'un type, la marque de type désigne ce type ainsi que le sous-type non contraint correspondant. Le type de base d'une marque de type est, par définition, le type de base du type ou du sous-type désigné par la marque de type.

Une indication de sous-type définit un sous-type du type de base de la marque de type.

Si une contrainte d'index apparaît après une marque de type dans une indication de sous-type, la marque de type ne doit pas déjà imposer une contrainte d'index. De même pour une contrainte discriminante, la marque de type ne doit pas déjà imposer une contrainte discriminante.

L'élaboration d'une déclaration de sous-type consiste en l'élaboration de l'indication de sous-type. L'élaboration d'une indication de sous-type crée un sous-type. Si l'indication de sous-type n'inclut pas de contrainte, le sous-type est le même que celui désigné par la marque de type. L'élaboration d'une indication de sous-type incluant une contrainte se déroule comme suit :

La condition imposée par une contrainte est la condition obtenue après élaboration de la contrainte. (Les règles d'élaboration des contraintes sont telles que les expressions et les plages de contraintes sont évaluées par l'élaboration de ces contraintes.) Les règles définissant la compatibilité sont données pour chaque forme de contrainte dans la section appropriée. Ces règles sont telles que si une contrainte est compatible avec un sous-type, alors la condition imposée par la contrainte ne peut pas contredire une condition déjà imposée par le sous-type sur ses valeurs. L'exception CONSTRAINT_ERROR est levée si une vérification de compatibilité échoue.

Exemples de déclarations de sous-types :

  1. subtype RAINBOW   is COLOR range RED .. BLUE; 
  2. subtype RED_BLUE  is RAINBOW;
  3. subtype INT       is INTEGER;
  4. subtype SMALL_INT is INTEGER range -10 .. 10;
  5. subtype UP_TO_K   is COLUMN range 1 .. K; 
  6. subtype SQUARE    is MATRIX( 1 .. 10, 1 .. 10); 
  7. subtype MALE      is PERSON(SEX => M); 

Remarque : une déclaration de sous-type ne définit pas un nouveau type.

Classification des opérations

L'ensemble des opérations d'un type inclut les sous-programmes explicitement déclarés ayant un paramètre ou un résultat du type; ces sous-programmes sont nécessairement déclarés après la déclaration de type.

Les opérations restantes sont chacune implicitement déclarées pour une déclaration de type donnée, immédiatement après la définition de type. Ces opérations implicitement déclarées comprennent les opérations de base, les opérateurs prédéfinis et les littéraux d'énumération. Dans le cas d'une déclaration de type dérivé, les opérations implicitement déclarées incluent tous les sous-programmes dérivés. Les opérations implicitement déclarées pour une déclaration de type donnée interviennent après la déclaration de type et avant la déclaration explicite suivante, le cas échéant. Les déclarations implicites des sous-programmes dérivés interviennent en dernier.

Une opération de base est une opération inhérente à l'un des éléments suivants :

Pour chaque type ou sous-type T, l'attribut suivant est défini :

Attribut Description
T'BASE Le type de base de T. Cet attribut n'est autorisé que comme préfixe du nom d'un autre attribut : par exemple, T'BASE 'FIRST.

Chaque littéral est une opération dont l'évaluation produit la valeur correspondante. De même, un agrégat est une opération dont l'évaluation produit une valeur d'un type composite. Certaines opérations d'un type opèrent sur des valeurs du type, par exemple des opérateurs prédéfinis et certains sous-programmes et attributs. L'évaluation de certaines opérations d'un type renvoie une valeur du type, par exemple des littéraux et certaines fonctions, attributs et opérateurs prédéfinis. L'affectation est une opération opérant sur un objet et une valeur. L'évaluation de l'opération correspondant à un composant sélectionné, un composant indexé ou une tranche produit l'objet ou la valeur désignée par cette forme de nom.

Types dérivés

Une définition de type dérivé définit un nouveau type (de base) dont les caractéristiques sont dérivées de celles d'un type parent; le nouveau type est appelé type dérivé. Une définition de type dérivé définit en outre un sous-type dérivé, étant un sous-type du type dérivé :

definition_de_type_derive new sous_type_indication

L'indication de sous-type apparaissant après le mot réservé new définit le sous-type parent. Le type parent est le type de base du sous-type parent. Si une contrainte existe pour le sous-type parent, une contrainte similaire existe pour le sous-type dérivé ; la seule différence est que pour une contrainte d'intervalle, et de même pour une contrainte à virgule flottante ou fixe qui inclut une contrainte de plage, la valeur de chaque limite est remplacée par la valeur correspondante du type dérivé. Les caractéristiques du type dérivé sont définies comme suit :

Chaque opération du type dérivé est implicitement déclarée à la place de la déclaration du type dérivé. Les déclarations implicites des éventuels sous-programmes dérivés interviennent en dernier.

La spécification d'un sous-programme dérivé est obtenue implicitement par remplacement systématique du type parent par le type dérivé dans la spécification du sous-programme dérivable. Tout sous-type du type parent est également remplacé par un sous-type du type dérivé avec une contrainte similaire (comme pour la transformation d'une contrainte du sous-type parent en contrainte correspondante du sous-type dérivé). Enfin, toute expression du type parent est transformée en opérande d'une conversion de type donnant un résultat du type dérivé.

L'appel d'un sous-programme dérivé équivaut à appeler le sous-programme correspondant du type parent, dans lequel chaque paramètre réel étant du type dérivé est remplacé par une conversion de type de ce paramètre réel vers le type parent (cela signifie qu'une conversion vers le type parent se produit avant l'appel des modes in et in out; une conversion inverse vers le type dérivé se produit après l'appel des modes in out et out). De plus, si le résultat d'une fonction appelée est du type parent, ce résultat est converti vers le type dérivé.

Si un type dérivé ou privé est déclaré immédiatement dans la partie visible d'un paquet, alors, dans cette partie visible, ce type ne doit pas être utilisé comme type parent d'une définition de type dérivé.

Pour l'élaboration d'une définition de type dérivé, l'indication de sous-type est d'abord élaborée, le type dérivé est ensuite créé, et enfin, le sous-type dérivé est créé.

Exemples :

  1. type LOCALCOORDINATE is new COORDINATE;    -- deux types différents
  2. type MIDWEEK is new DAY range TUE .. THU;
  3. type COUNTER is new POSITIVE;              -- même gamme que POSITIF
  4.  
  5. type SPECIAL_KEY is new KEY.MANAGER.KEY; 
  6. -- les sous-programmes dérivés ont les spécifications suivantes :
  7.  
  8. -- procedure GET_KEY(K : out SPECIAL_KEY);
  9. -- function "<"(X,Y : SPECIALKEY) return BOOLEAN;

Les règles de dérivation des opérations de base et des littéraux d'énumération impliquent que la notation de tout littéral ou agrégat du type dérivé est la même que pour le type parent ; de tels littéraux et agrégats sont dits surchargés. De même, il s'ensuit que la notation pour désigner un composant, un discriminant, une entrée, une tranche ou un attribut est la même pour le type dérivé que pour le type parent.

Le masquage d'un sous-programme dérivé est autorisé même au sein de la même région déclarative Un sous-programme dérivé cache un opérateur prédéfini ayant le même profil de paramètre et de type de résultat.

Une déclaration de sous-programme générique n'est pas dérivable car elle déclare une unité générique plutôt qu'un sous-programme. D'autre part, une instanciation d'un sous-programme générique est un sous-programme (non générique), étant dérivable s'il satisfait aux exigences de dérivabilité des sous-programmes. Si le type parent est un type booléen, les opérateurs relationnels prédéfinis du type dérivé fournissent un résultat du type prédéfini BOOLEAN.

Si une clause de représentation est donnée pour le type parent mais apparaît après la déclaration du type dérivé, aucune clause de représentation correspondante ne s'applique au type dérivé; par conséquent, une clause de représentation explicite pour un tel type dérivé est autorisée.

Pour un sous-programme dérivé, si un paramètre appartient au type dérivé, le sous-type de ce paramètre n'a pas besoin d'avoir de valeur en commun avec le sous-type dérivé.

Types scalaires

Les types scalaires comprennent les types d'énumération, les types entiers et les types réels. Les types d'énumération et les types entiers sont appelés types discrets; chaque valeur d'un type discret possède un numéro de position étant une valeur entière. Les types entiers et les types réels sont appelés types numériques. Tous les types scalaires sont ordonnés, c'est-à-dire que tous les opérateurs relationnels sont prédéfinis pour leurs valeurs :

contrainte_de_portee ::= range intervalle

intervalle := attribut_d_intervalle
   | simple_expression .. simple_expression

Un intervalle spécifie un sous-ensemble de valeurs d'un type scalaire. L'intervalle L..R spécifie les valeurs de L à R incluses si la relation L <= R est vraie. Les valeurs L et R sont appelées respectivement limite inférieure et limite supérieure de l'intervalle. On dit qu'une valeur V satisfait une contrainte d'intervalle si elle appartient à l'intervalle; la valeur V est dite appartenir à l'intervalle si les relations L <= V et V <= R sont toutes deux VRAIES. Un intervalle nulle est une intervalle pour laquelle la relation R < L est VRAIE ; aucune valeur n'appartient à un intervalle nulle. Les opérateurs <= et < dans les définitions ci-dessus sont les opérateurs prédéfinis du type scalaire.

Si une contrainte d'intervalle est utilisée dans une indication de sous-type, soit directement, soit dans le cadre d'une contrainte à virgule flottante ou fixe, le type des expressions simples (de même que celui des limites d'un attribut d'intervalle) doit être le même que le type de base de la marque de type de l'indication de sous-type. Une contrainte d'intervalle est compatible avec un sous-type si chaque borne de la plage appartient au sous-type, ou si la contrainte d'intervalle définit une intervalle nulle ; sinon, la contrainte d'intervalle n'est pas compatible avec le sous-type.

L'élaboration d'une contrainte d'intervalle consiste en l'évaluation de l'intervalle. L'évaluation d'une plage de 5 définit sa borne inférieure et sa borne supérieure. Si des expressions simples sont données pour spécifier les bornes, l'évaluation de l'intervalle évalue ces expressions simples dans un ordre n'étant pas défini par le langage.

Les attributs

Pour tout type scalaire T ou pour tout sous-type T d'un type scalaire, les attributs suivants sont définis :

Attribut Description
T'FIRST Donne la limite inférieure de T. La valeur de cet attribut a le même type que T.
T'LAST Donne la limite supérieure de T. La valeur de cet attribut a le même type que T.

Remarque : les règles d'indexation et d'itération utilisent des valeurs de types discrets.

Types d'énumération

Une définition de type d'énumération définit un type d'énumération :

definition_du_type_d_enumeration ::=
  (specification_litterale_d_enumeration {, specification_litterale_d_enumeration})

specification_litterale_d_enumeration ::= enumeration_litterale

enumeration_litterale identifiant | caractere_litteral

Les identifiants et les littéraux de caractères listés par une définition de type d'énumération doivent être distincts. Chaque spécification de littéral d'énumération est la déclaration du littéral d'énumération correspondant : cette déclaration est équivalente à la déclaration d'une fonction sans paramètre, le désignateur étant le littéral d'énumération et le type de résultat étant le type d'énumération. L'élaboration d'une définition de type d'énumération crée un type d'énumération; cette élaboration inclut celle de chaque spécification de littéral d'énumération.

Chaque littéral d'énumération produit une valeur d'énumération différente. Les relations d'ordre prédéfinies entre les valeurs d'énumération suivent l'ordre des numéros de position correspondants. Le numéro de position de la valeur du premier littéral d'énumération répertorié est zéro; le numéro de position de chaque autre littéral d'énumération est un de plus que celui de son prédécesseur dans la liste.

Si le même identifiant ou le même littéral de caractère est spécifié dans plusieurs définitions de type d'énumération, les littéraux correspondants sont dits surchargés. À tout endroit où un littéral d'énumération surchargé apparaît dans le texte d'un programme, le type du littéral d'énumération doit pouvoir être déterminé à partir du contexte.

Exemples :

  1. type DAY        is (MON, TUE, WED, THU, FRI, SAT, SUN);
  2. type SUIT       is (CLUBS, DIAMONDS, HEARTS, SPADES);
  3. type GENDER     is (M, F);
  4. type LEVEL      is (LOW, MEDIUM, URGENT);
  5. type COLOR      is (WHITE, RED, YELLOW, GREEN, BLUE, BROWN, BLACK);
  6. type LIGHT      is (RED, AMBER, GREEN); -- Le ROUGE et le VERT sont surchargés
  7.  
  8. type HEXA       is ('A', 'B', 'C', 'D', 'E', 'F');
  9. type MIXED      is ('A', 'B', '*', B, NONE, '?', '%');
  10.  
  11. subtype WEEKDAY is DAY    range MON .. FRI;
  12. subtype MAJOR   is SUIT   range HEARTS .. SPADES;
  13. subtype RAINBOW is COLOR  range RED .. BLUE; -- la couleur RED, pas la lumière

Remarque : si un littéral d'énumération apparaît dans un contexte ne suffisant pas à déterminer le type du littéral, la qualification par le nom du type d'énumération est un moyen de résoudre l'ambiguïté.

Types de caractères

Un type d'énumération est dit type caractère si au moins un de ses littéraux d'énumération est un littéral caractère. Le type prédéfini CHARACTER est un type caractère dont les valeurs sont les 128 caractères de l'ensemble de caractères ASCII. Chacun des 95 caractères graphiques de cette ensemble de caractères est désigné par le littéral caractère correspondant.

Exemple :

  1. type ROMAN_DIGIT is ('I', 'V', 'X', 'L', 'C', 'D', 'M');

Remarques : Le paquet prédéfini ASCII inclut la déclaration de constantes désignant les caractères de contrôle et de constantes désignant les caractères graphiques n'étant pas dans l'ensemble de caractères de base.

Un ensemble de caractères conventionnel tel que EBCDIC peut être déclaré comme type de caractère; les codes internes des caractères peuvent être spécifiés par une clause de représentation d'énumération.

Types booléens

Il existe un type d'énumération prédéfini appelé BOOLEAN. Il contient les deux littéraux FALSE et TRUE ordonnés selon la relation FALSE < TRUE. Un type booléen est soit le type BOOLEAN, soit un type dérivé, directement ou indirectement, d'un type booléen.

Types d'entiers

Une définition de type entier définit un type entier dont l'ensemble de valeurs comprend au moins celles de l'intervalle spécifiée.

definition_de_type_entier := contrainte_de_portee

Si une contrainte d'intervalle est utilisée comme définition de type entier, chaque limite de l'intervalle doit être définie par une expression statique d'un type entier, mais les deux limites n'ont pas besoin d'avoir le même type entier. (Les limites négatives sont autorisées.)

Une déclaration de type de la forme :

  1. type T is range L .. R;

est, par définition, équivalent aux déclarations suivantes :

type integer_type is new predefined_integer_type;
subtype T is integer_type range integer_type(L) .. integer_type(R);

integer_type est un type anonyme, et où le type entier prédéfini est implicitement sélectionné par l'implémentation, de manière à contenir les valeurs L à R incluses. La déclaration de type entier est illégale si aucun des types entiers prédéfinis ne satisfait cette exigence, à l'exception de universal_integer. L'élaboration de la déclaration d'un type entier consiste en l'élaboration des déclarations de type et de sous-type équivalentes.

Les types entiers prédéfinis incluent le type INTEGER. Une implémentation peut également avoir des types prédéfinis tels que SHORT_INTEGER et LONG_INTEGER, ayant respectivement des intervalles (substantiellement) plus courtes et plus longues que INTEGER. La plage de chacun de ces types doit être symétrique par rapport à zéro, à l'exception d'une valeur négative supplémentaire pouvant exister dans certaines implémentations. Le type de base de chacun de ces types est le type lui-même.

Les littéraux entiers sont les littéraux d'un type entier prédéfini anonyme appelé universal_integer dans cette page de référence. Les autres types entiers n'ont pas de littéraux. Cependant, pour chaque type entier, il existe une conversion implicite qui convertit une valeur universal_integer en la valeur correspondante (le cas échéant) du type entier.

Le numéro de position d'une valeur entière est la valeur correspondante du type universal_integer.

Les mêmes opérateurs arithmétiques sont prédéfinis pour tous les types entiers. L'exception NUMERIC_ERROR est levée par l'exécution d'une opération (en particulier une conversion implicite) ne pouvant pas fournir le résultat correct (c'est-à-dire si la valeur correspondant au résultat mathématique n'est pas une valeur de type entier). Cependant, une implémentation n'est pas obligée de déclencher l'exception NUMERIC_ERROR si l'opération fait partie d'une expression plus grande dont le résultat peut être calculé correctement.

Exemples :

  1. type PAGE_NUM       is range 1 .. 2_000;
  2. type LINE_SIZE      is range 1 .. MAX_LINE_SIZE;
  3.  
  4. subtype SMALL_INT   is INTEGER   range -10 .. 10;
  5. subtype CQLUMN_PTR  is LINE_SIZE range 1 .. 10;
  6. subtype BUFFER_SIZE is INTEGER   range 0 .. MAX;     

Remarques :

Le nom déclaré par une déclaration de type entier est un nom de sous-type. D'autre part, les opérateurs prédéfinis d'un type entier fournissent des résultats dont l'intervalle est définie par le type prédéfini parent; un tel résultat n'a pas besoin d'appartenir au sous-type déclaré, auquel cas une tentative d'affectation du résultat à une variable du sous-type entier génère l'exception CONSTRAINT_ERROR.

La plus petite valeur (la plus négative) prise en charge par les types entiers prédéfinis d'une implémentation est le nombre nommé SYSTEM.MIN_INT et la plus grande valeur (la plus positive) est SYSTEM.MAX_INT.

Opérations de types discrets

Les opérations de base d'un type discret incluent les opérations d'affectation, les tests d'appartenance et la qualification; pour un type booléen, elles incluent les formes de contrôle de court-circuit ; pour un type entier, elles incluent la conversion explicite des valeurs d'autres types numériques en type entier et la conversion implicite des valeurs du type universel_integer en type.

Enfin, pour chaque type discret ou sous-type T, les opérations de base incluent les attributs listés ci-dessous. Dans cette présentation, T est considéré comme un sous-type (le sous-type T) pour toute propriété qui dépend des contraintes imposées par T ; les autres propriétés sont énoncées en termes de type de base de T.

Le premier groupe d'attributs donne les caractéristiques du sous-type T. Ce groupe comprend l'attribut BASE, les attributs FIRST et LAST, l'attribut de représentation SIZE et l'attribut WIDTH défini comme suit :

Attribut Description
T'WIDTH Renvoie la longueur d'image maximale sur toutes les valeurs du sous-type T (l'image est la séquence de caractères renvoyée par l'attribut IMAGE). Renvoie zéro pour un intervalle nulle. La valeur de cet attribut est de type universal_integer.

Tous les attributs du deuxième groupe sont des fonctions avec un seul paramètre. Le paramètre réel correspondant est indiqué ci-dessous par X :

Attribut Description
T'POS Cet attribut est une fonction. Le paramètre X doit être une valeur de type de base T. Le type de résultat est le type universal_integer. Le résultat est le numéro de position de la valeur du paramètre.
T'VAL Cet attribut est une fonction spéciale avec un seul paramètre pouvant être de n'importe quel type entier. Le type de résultat est le type de base de T. Le résultat est la valeur dont le numéro de position est la valeur universal_integer correspondant à X. L'exception CONSTRAINT_ERROR est levée si la valeur universal_integer correspondant à X n'est pas dans l'intervalle T'POS(T'BASE'FIRST).. T'POS(T'BASE'LAST).
T'SUCC Cet attribut est une fonction. Le paramètre X doit être une valeur du type de base T. Le type de résultat est le type de base T. Le résultat est la valeur dont le numéro de position est supérieur de un à celui de X. L'exception CONSTRAINT_ERROR est levée si X est égal à T'BASE'LAST.
T'PRED Cet attribut est une fonction. Le paramètre X doit être une valeur du type de base T. Le type du résultat est le type de base de T. Le résultat est la valeur dont le numéro de position est inférieur de un à celui de X. L'exception CONSTRAINT_ERROR est levée si X est égal à T'BASE'FIRST.
T'IMAGE Cet attribut est une fonction. Le paramètre X doit être une valeur du type de base T. Le type de résultat est le type prédéfini STRING. Le résultat est l'image de la valeur de X, c'est-à-dire une séquence de caractères représentant la valeur sous forme d'affichage. L'image d'une valeur entière est le littéral décimal correspondant; sans soulignement, zéros non significatifs, exposant ou espaces de fin; mais avec un seul caractère de début étant soit un signe moins, soit un espace. La limite inférieure de l'image est un. L'image d'une valeur d'énumération est soit l'identifiant correspondant en majuscules, soit le littéral de caractère correspondant (y compris les deux apostrophes); ni les espaces de début ni de fin ne sont inclus. L'image d'un caractère C, autre qu'un caractère graphique, est définie par l'implémentation; la seule exigence est que l'image soit telle que C soit égal à CHARACTER'VALUE (CHARACTER'IMAGE (C)).
T'VALUE Cet attribut est une fonction. Le paramètre X doit être une valeur du type prédéfini STRING. Le type de résultat est le type de base de T. Les espaces de début et de fin de la séquence de caractères correspondant au paramètre sont ignorés. Pour un type d'énumération, si la séquence de caractères a la syntaxe d'un littéral d'énumération et si ce littéral existe pour le type de base de T, le résultat est la valeur d'énumération correspondante. Pour un type entier, si la séquence de caractères a la syntaxe d'un littéral entier, avec un seul caractère de début facultatif étant un signe plus ou moins, et s'il existe une valeur correspondante dans le type de base de T, le résultat est cette valeur. Dans tous les autres cas, l'exception CONSTRAINT_ERROR est générée.

De plus, les attributs A'SIZE et A'ADDRESS sont définis pour un objet A de type discret.

Outre les opérations de base, les opérations d'un type discret incluent les opérateurs relationnels prédéfinis. Pour les types d'énumération, les opérations incluent les littéraux d'énumération. Pour les types booléens, les opérations incluent l'opérateur de négation logique unaire prédéfini NOT et les opérateurs logiques prédéfinis. Pour les types entiers, les opérations incluent les opérateurs arithmétiques prédéfinis : ce sont les opérateurs d'addition binaires et unaires - et +, tous les opérateurs de multiplication, l'opérateur unaire abs et l'opérateur d'exponentiation.

Les opérations d'un sous-type sont les opérations correspondantes de son type de base à l'exception des suivantes : affectation, tests d'appartenance, qualification, conversions de type explicites et les attributs du premier groupe ; l'effet de chacune de ces opérations dépend du sous-type (les affectations, les tests d'appartenance, les qualifications et les conversions impliquent une vérification de sous-type; les attributs du premier groupe donnent une caractéristique du sous-type).

Remarques : Pour un sous-type d'un type discret, les résultats fournis par les attributs SUCC, PRED, VAL et VALUE n'ont pas besoin d'appartenir au sous-type; de même, les paramètres réels des attributs POS, SUCC, PRED et IMAGE n'ont pas besoin d'appartenir au sous-type. Les relations suivantes sont satisfaites (en l'absence d'exception) par ces attributs :

  1. T'POS(T'SUCC(X))  = T'POS(X) + 1
  2. T'POS(T'PRED(X))  = T'POS(X) - 1
  3.  
  4. T'VAL(T'POS(X))   = X
  5. T'POS(T'VAL(N))   = N

Exemples :

  1. -- Pour les types et sous-types déclarés dans la section précédente, nous avons :
  2.  
  3. -- COLOR'FIRST      = WHITE, COLOR'LAST   = BLACK
  4. -- RAINBOW'FIRST    = RED,   RAINBOW'LAST = BLUE
  5.  
  6. -- COLOR'SUCC(BLUE) = RAINBOW'SUCC(BLUE)  = BROWN
  7. -- COLOR'POS (BLUE) = RAINBOW'POS(BLUE)   = 4
  8. -- COLOR'VAL(0)     = RAINBOW'VAL(0)      = WHITE

Types réels

Les types réels fournissent des approximations des nombres réels, avec des limites relatives aux erreurs pour les types à virgule flottante et avec des limites absolues pour les types à virgule fixe :

definition_de_type_reel ::=
  contrainte_en_virgule_flottante | contrainte_a_point_fixe

Un ensemble de nombres appelés numéros de modèle est associé à chaque type réel. Les limites d'erreur sur les opérations prédéfinies sont données en termes de numéros de modèle. Une implémentation du type doit inclure au moins ces numéros de modèle et les représenter exactement.

Un ensemble de nombres dépendant de l'implémentation, appelé numéros de sécurité, est également associé à chaque type réel. L'ensemble des numéros de sécurité d'un type réel doit inclure au moins l'ensemble des numéros de modèle du type. L'intervalle de numéros de sécurité peut être supérieure à l'intervalle de numéros de modèle, mais les limites d'erreur sur les opérations prédéfinies pour les numéros de sécurité sont données par les mêmes règles que pour les numéros de modèle. Les numéros de sécurité fournissent donc des limites d'erreur garanties pour les opérations sur une plage de nombres dépendant de l'implémentation; en revanche, l'intervalle de numéros de modèle dépend uniquement de la définition du type réel et est donc indépendante de l'implémentation.

Les littéraux réels sont les littéraux d'un type réel prédéfini anonyme appelé universal_real dans cette page de référence. Les autres types réels n'ont pas de littéraux. Cependant, pour chaque type réel, il existe une conversion implicite convertissant une valeur universal_real en une valeur du type réel. Si la valeur universal_real est un nombre sûr, la conversion implicite fournit la valeur correspondante; si elle appartient à l'intervalle des nombres sûrs mais n'est pas un nombre sûr, alors la valeur convertie peut être n'importe quelle valeur dans l'intervalle définie par les nombres sûrs immédiatement au-dessus et en dessous de la valeur universal_real.

L'exécution d'une opération qui produit une valeur d'un type réel peut déclencher l'exception NUMERIC_ERROR, si elle ne peut pas fournir un résultat correct (c'est-à-dire si la valeur correspondant à l'un des résultats mathématiques possibles n'appartient pas à la plage des nombres sûrs) ; en particulier, cette exception peut être déclenchée par une conversion implicite. Cependant, une implémentation n'est pas obligée de déclencher l'exception NUMERIC_ERROR si l'opération fait partie d'une expression plus grande dont le résultat peut être calculé correctement.

L'élaboration d'une définition de type réel inclut l'élaboration de la contrainte de virgule flottante ou fixe et crée un type réel.

Remarque : un algorithme écrit pour s'appuyer uniquement sur les propriétés numériques minimales garanties par la définition de type pour les numéros de modèle sera portable sans précautions supplémentaires.

Pour les types à virgule flottante, la limite d'erreur est spécifiée comme une précision relative en donnant le nombre minimum requis de chiffres décimaux significatifs :

contrainte_en_virgule_flottante ::=
   definition_de_précision_flottante [contrainte_de_portee]

definition_de_precision_flottante ::= digits expression_simple_statique

Le nombre minimum de chiffres décimaux significatifs est spécifié par la valeur de l'expression statique simple de la définition de précision flottante. Cette valeur doit appartenir à un type entier et doit être positive (différente de zéro); elle est désignée par D dans le reste de cette section. Si la contrainte à virgule flottante est utilisée comme définition de type réel et inclut une contrainte de plage, chaque limite de l'intervalle doit être définie par une expression statique d'un type réel, mais les deux limites n'ont pas besoin d'avoir le même type réel.

Pour une base donnée, la forme canonique suivante est définie pour tout nombre de modèle à virgule flottante autre que zéro :

signe * mantisse * (base ** exposant)

Sous cette forme : le signe est soit +1, soit -1 ; la mantisse est exprimée dans une base numérique donnée par la base; et l'exposant est un nombre entier (éventuellement négatif) tel que la partie entière de la mantisse soit zéro et que le premier chiffre de sa partie fractionnaire ne soit pas un zéro.

Le nombre spécifié D est le nombre minimum de chiffres décimaux requis après le point dans la mantisse décimale (c'est-à-dire si la base est dix). La valeur de D détermine à son tour un nombre correspondant B étant le nombre minimum de chiffres binaires requis après le point dans la mantisse binaire (c'est-à-dire si la base est deux). Le nombre B associé à D est la plus petite valeur telle que la précision relative de la forme binaire ne soit pas inférieure à celle spécifiée pour la forme décimale. (Le nombre B est l'entier immédiatement supérieur à (D*log(10)/log(2)) + 1).

Les numéros de modèle définis par une définition de précision flottante comprennent zéro et tous les nombres dont la forme canonique binaire comporte exactement B chiffres après le point de la mantisse et un exposant dans l'intervalle -4*B .. +4*B. La précision minimale garantie des opérations d'un type à virgule flottante est définie en termes de numéros de modèle de la contrainte à virgule flottante qui forme la définition de type réel correspondante.

Les types à virgule flottante prédéfinis incluent le type FLOAT. Une implémentation peut également avoir des types prédéfinis tels que SHORT_FLOAT et LONG_FLOAT, ayant respectivement (substantiellement) moins et plus de précision que FLOAT. Le type de base de chaque type à virgule flottante prédéfini est le type lui-même. Les numéros de modèle de chaque type à virgule flottante prédéfini sont définis en termes du nombre D de chiffres décimaux renvoyés par l'attribut DIGITS.

Pour chaque type de virgule flottante prédéfini (et donc également pour chaque type en dérivant), un ensemble de nombres sûrs est défini comme suit. Les nombres sûrs ont le même nombre B de chiffres de mantisse que les nombres de modèle du type et ont un exposant dans l'intervalle -E .. +E où E est défini par l'implémentation et au moins égal à 4*B des nombres de modèle. (Par conséquent, les nombres sûrs incluent les nombres de modèle.) Les nombres sûrs d'un sous-type sont ceux de son type de base.

Une déclaration de type à virgule flottante de l'une des deux formes (c'est-à-dire avec ou sans la contrainte d'intervalle facultative indiquée par les crochets) :

type T is digits D [range L .. R];

est, par définition, équivalent aux déclarations suivantes :

type type_a_virgule_flottante is new type_a_virgule_flottante_predefini;
subtype T is type_a_virgule_flottante digits D
   [range type_a_virgule_flottante(L) .. type_a_virgule_flottante{R)];

type_a_virgule_flottante est un type anonyme, et où le type de virgule flottante prédéfini est implicitement sélectionné par l'implémentation de sorte que ses numéros de modèle incluent les numéros de modèle définis par D ; de plus, si une plage L R est fournie, alors L et R doivent tous deux appartenir à la plage de nombres sûrs. La déclaration de virgule flottante est illégale si aucun des types de virgule flottante prédéfinis ne satisfait ces exigences, à l'exception d'universal_real. Le nombre maximal de chiffres pouvant être spécifiés dans une définition de précision flottante est donné par le nombre nommé dépendant du système SYSTEM.MAX_DIGITS.

L'élaboration d'une déclaration de type à virgule flottante consiste à élaborer les déclarations de type et de sous-type équivalentes.

Si une contrainte à virgule flottante suit une marque de type dans une indication de sous-type, la marque de type doit désigner un type ou un sous-type à virgule flottante. La contrainte à virgule flottante n'est compatible avec la marque de type que si le nombre D spécifié dans la définition de précision flottante n'est pas supérieur au nombre D correspondant pour le type ou le sous-type désigné par la marque de type. De plus, si la contrainte à virgule flottante comprend une contrainte d'intervalle, la contrainte à virgule flottante n'est compatible avec la marque de type que si la contrainte de plage est elle-même compatible avec la marque de type.

L'élaboration d'une telle indication de sous-type comprend l'élaboration de la contrainte de l'intervalle, s'il y en a une ; elle crée un sous-type à virgule flottante dont les numéros de modèle sont définis par la définition de précision flottante correspondante. Une valeur d'un type à virgule flottante appartient à un sous-type à virgule flottante si et seulement si elle appartient à l'intervalle définie par le sous-type.

Les mêmes opérateurs arithmétiques sont prédéfinis pour tous les types à virgule flottante.

Remarques : une contrainte d'intervalle est autorisée dans une indication de sous-type à virgule flottante, soit directement après la marque de type, soit dans le cadre d'une contrainte à virgule flottante. Dans les deux cas, les limites de la plage doivent appartenir au type de base de la marque de type. L'imposition d'une contrainte à virgule flottante sur une marque de type dans une indication de sous-type ne peut pas réduire l'intervalle de valeurs autorisée à moins qu'elle n'inclue une contrainte d'intervalle (l'intervalle de numéros de modèle correspondant au nombre de chiffres spécifié peut être inférieure à la plage de numéros de la marque de type). Une valeur appartenant à un sous-type à virgule flottante n'a pas besoin d'être un numéro de modèle du sous-type.

Exemples :

  1. type COEFFICIENT is digits 10 range -1.0 .. 1.0;
  2.  
  3. type REAL is digits 8;
  4. type MASS is digits 7 range 0.0 .. 1.0E35;
  5.  
  6. subtype SHORT_COEFF is COEFFICIENT digits 5;  -- un sous-type avec moins de précision
  7. subtype PROBABILITY is REAL range 0.0 .. 1.0; -- un sous-type avec une portée plus petite

Remarques sur les exemples : La précision implémentée pour COEFFICIENT est celle d'un type prédéfini ayant au moins 10 chiffres de précision. Par conséquent, la spécification de 5 chiffres de précision pour le sous-type SHORT_COEFF est autorisée. Le plus grand numéro de modèle pour le type MASS est d'environ 1,27E30 et donc inférieur à la limite supérieure spécifiée (1,0E35). Par conséquent, la déclaration de ce type n'est légale que si cette limite supérieure se situe dans l'intervalle des nombres sûrs d'un type à virgule flottante prédéfini ayant au moins 7 chiffres de précision.

Opérations des types à virgule flottante

Les opérations de base d'un type à virgule flottante incluent les opérations impliquées dans l'affectation, les tests d'appartenance, la qualification, la conversion explicite de valeurs d'autres types numériques en type à virgule flottante et la conversion implicite de valeurs du type universal_real en type. De plus, pour chaque type ou sous-type à virgule flottante T, les opérations de base incluent les attributs répertoriés ci-dessous. Dans cette présentation, T est considéré comme un sous-type (le sous-type T) pour toute propriété qui dépend des contraintes imposées par T ; les autres propriétés sont énoncées en termes de type de base de T.

Le premier groupe d'attributs donne les caractéristiques du sous-type T. Les attributs de ce groupe sont l'attribut BASE, les attributs FIRST et LAST, l'attribut de représentation SIZE et les attributs suivants :

Attribut Description
T'DIGITS Renvoie le nombre de chiffres décimaux dans la mantisse décimale des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer.
T'MANTISSA Renvoie le nombre de chiffres binaires dans la mantisse binaire des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer.
T'EPSILON Donne la valeur absolue de la différence entre le numéro de modèle 1.0 et le numéro de modèle supérieur, pour le sous-type T. La valeur de cet attribut est de type universal_real.
T'EMAX Donne la plus grande valeur d'exposant dans la forme canonique binaire des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer.
T'SMALL Donne le plus petit numéro de modèle positif (différent de zéro) du sous-type T. La valeur de cet attribut est de type universal_real.
T'LARGE Donne le plus grand numéro de modèle positif du sous-type T. La valeur de cet attribut est de type universal_real.

Les attributs du deuxième groupe incluent les attributs suivants donnant les caractéristiques des numéros sûrs :

Attribut Description
T'SAFE_EMAX Donne la plus grande valeur d'exposant dans la forme canonique binaire des nombres sûrs du type de base T. La valeur de cet attribut est de type universal_integer.
T'SAFE_SMALL Génère le plus petit nombre sûr positif (non nul) du type de base de T. La valeur de cet attribut est de type universal_real.
T'SAFE_LARGE Donne le plus grand nombre positif sûr du type de base de T. La valeur de cet attribut est de type universal_real.

De plus, les attributs A'SIZE et A'ADDRESS sont définis pour un objet A de type à virgule flottante. Enfin, pour chaque type à virgule flottante, il existe des attributs dépendants de la machine n'étant pas liés aux numéros de modèle et aux numéros de sécurité. Ils correspondent aux désignateurs d'attributs MACHINE_RADIX, MACHINE_MANTISSA, MACHINE_EMAX, MACHINE_EMIN, MACHINE_ROUNDS et MACHINE_0VERFLOWS.

Outre les opérations de base, les opérations d'un type à virgule flottante incluent les opérateurs relationnels et les opérateurs arithmétiques prédéfinis suivants : les opérateurs d'addition binaires et unaires - et +, les opérateurs de multiplication * et /, l'opérateur unaire abs et l'opérateur d'exponentiation.

Les opérations d'un sous-type sont les opérations correspondantes du type à l'exception des suivantes : affectation, tests d'appartenance, qualification, conversion explicite et les attributs du premier groupe; les effets de ces opérations sont redéfinis en fonction du sous-type.

Remarques : les attributs EMAX, SMALL, LARGE et EPSILON sont fournis pour plus de commodité. Ils sont tous liés à MANTISSA par les formules suivantes :

  1. T'EMAX = 4*T'MANTISSA
  2. T'EPSILON = 2.0**(1 - T'MANTISSA)
  3. T'SMALL = 2.0**(-T'EMAX - 1)
  4. T'LARGE - 2.0**T'EMAX * (1.0 - 2.0**(-T'MANTISSA))

L'attribut MANTISSA, donnant le nombre de chiffres binaires de la mantisse, est lui-même lié à DIGITS. Les relations suivantes existent entre les caractéristiques des numéros de modèle et celles des numéros sûrs :

  1. T'BASE'EMAX <= T'SAFE_EMAX
  2. T'BASE'SMALL >= T'SAFE_SMALL
  3. T'BASE'LARGE <= T'SAFE_LARGE

Les attributs T'FIRST et T'LAST ne doivent pas nécessairement donner de numéros de modèle ou de sécurité. Si un certain nombre de chiffres est spécifié dans la déclaration d'un type ou sous-type T, l'attribut T'DIGITS donne ce nombre.

Types de points fixes

Pour les types à virgule fixe, la limite d'erreur est spécifiée comme une valeur absolue, appelée delta du type à virgule fixe :

contrainte_a_point_fixe ::=
   definition_de_precision_fixe [contrainte_de_portee]

definition_de_precision_fixe delta expression_simple_statique

Le delta est spécifié par la valeur de l'expression statique simple de la définition de précision fixe. Cette valeur doit appartenir à un type réel et doit être positive (non nulle). Si la contrainte de point fixe est utilisée comme définition de type réel, elle doit alors inclure une contrainte d'intervalle; chaque limite de l'intervalle spécifié doit être définie par une expression statique de type réel, mais les deux limites n'ont pas besoin d'avoir le même type réel. Si la contrainte de point fixe est utilisée dans une indication de sous-type, la contrainte d'intervalle est facultative.

Une forme canonique est définie pour tout numéro de modèle à virgule fixe autre que zéro. Dans cette forme : le signe est soit +1, soit -1 ; la mantisse est un entier positif (non nul) ; et tout numéro de modèle est un multiple d'un certain nombre réel positif appelé petit, comme suit :

signe * mantisse * petit

Pour les numéros de modèle définis par une contrainte de point fixe, le nombre petit est choisi comme la plus grande puissance de deux n'étant pas supérieure au delta de la définition de précision fixe. Alternativement, il est possible de spécifier la valeur de petit par une clause de longueur, auquel cas les numéros de modèle sont des multiples de la valeur spécifiée. La précision minimale garantie des opérations d'un type à point fixe est définie en termes de numéros de modèle de la contrainte de point fixe formant la définition de type réel correspondante.

Pour une contrainte de point fixe incluant une contrainte d'intervalle, les numéros de modèle comprennent zéro et tous les multiples de petit dont la mantisse peut être exprimée en utilisant exactement B chiffres binaires, où la valeur de B est choisie comme le plus petit nombre entier pour lequel chaque limite de l'intervalle spécifiée est soit un numéro de modèle, soit se situe à la plus petite distance d'un numéro de modèle. Pour une contrainte de point fixe n'incluant pas de contrainte d'intervalle (cela n'est autorisé qu'après une marque de type, dans une indication de sous-type), les numéros de modèle sont définis par le delta de la définition de précision fixe et par l'intervalle du sous-type indiqué par la marque de type.

Une implémentation doit avoir au moins un type de point fixe prédéfini anonyme. Le type de base de chaque type de point fixe est le type lui-même. Les numéros de modèle de chaque type de point fixe prédéfini comprennent zéro et tous les nombres pour lesquels la mantisse (sous la forme canonique) a le nombre de chiffres binaires renvoyés par l'attribut MANTISSA, et pour lesquels le nombre petit a la valeur renvoyée par l'attribut SMALL.

Une déclaration de type de point fixe de la forme :

type T is delta D range L .. R;

est, par définition, équivalent aux déclarations suivantes :

type type_a_virgule_fixe is new type_de_point_fixe_predefini;
subtype T is type_a_virgule_fixe
   range type_a_virgule_fixe{L) .. type_a_virgule_fixe(R);

Dans ces déclarations, type_a_virgule_fixe est un type anonyme et le type de point fixe prédéfini est implicitement sélectionné par l'implémentation de sorte que ses numéros de modèle incluent les numéros de modèle définis par la contrainte de point fixe (c'est-à-dire par D, L et R, et éventuellement par une clause de longueur spécifiant petit).

La déclaration de point fixe est illégale si aucun type prédéfini ne satisfait à ces exigences. Les numéros sûrs d'un type de point fixe sont les numéros de modèle de son type de base. L'élaboration d'une déclaration de type de point fixe consiste en l'élaboration des déclarations de type et de sous-type équivalentes.

Si la contrainte de point fixe suit une marque de type dans une indication de sous-type, la marque de type doit désigner un type ou un sous-type de point fixe. La contrainte de point fixe est compatible avec la marque de type uniquement si le delta spécifié par la définition de précision fixe n'est pas inférieur au delta du type ou du sous-type désigné par la marque de type. De plus, si la contrainte de point fixe inclut une contrainte d'intervalle, la contrainte de point fixe n'est compatible avec la marque de type que si la contrainte d'intervalle est elle-même compatible avec la marque de type.

L'élaboration d'une telle indication de sous-type inclut l'élaboration de la contrainte d'intervalle, s'il y en a une ; elle crée un sous-type de point fixe dont les numéros de modèle sont définis par la contrainte de point fixe correspondante et également par la clause de longueur spécifiant petit, s'il y en a une. Une valeur d'un type de point fixe appartient à un sous-type de point fixe si et seulement si elle appartient à l'intervalle définie par le sous-type.

Les mêmes opérateurs arithmétiques sont prédéfinis pour tous les types de point fixe. La multiplication et la division de valeurs de point fixe donnent des résultats d'un type de point fixe prédéfini anonyme qui est appelé universal_fixed dans cette page de référence; la précision de ce type est arbitrairement fine. Les valeurs de ce type doivent être converties explicitement en un type numérique.

Remarques : Si S est un sous-type d'un type à virgule fixe ou d'un sous-type T, alors l'ensemble des numéros de modèle de S est un sous-ensemble de ceux de T. Si une clause de longueur a été donnée pour T, alors S et T ont la même valeur pour petit. Sinon, comme petit est une puissance de deux, le petit de S est égal au petit de T multiplié par une puissance non négative de deux.

Une contrainte d'intervalle est autorisée dans une indication de sous-type à virgule fixe, soit directement après la marque de type, soit dans le cadre d'une contrainte d'intervalle. Dans les deux cas, les limites de l'intervalle doivent appartenir au type de base de la marque de type.

Exemples :

  1. type VOLT is delta 0.125 range 0.0 .. 255.0;
  2. subtype ROUGH_VOLTAGE is VOLT delta 1.0; -- même gamme que VOLT
  3.  
  4. -- Une fraction pure nécessitant tout l'espace disponible dans un mot
  5. -- sur une machine à complément à deux peut être déclaré comme le type FRACTION :
  6.  
  7. DEL : constant := 1,0/2**(WORD_LENGTH - 1);
  8. type FRACTION is delta DEL range -1.0 .. 1.0 - DEL;

Les opérations de base d'un type à virgule fixe incluent les opérations impliquées dans l'affectation, les tests d'appartenance, la qualification, la conversion explicite de valeurs d'autres types numériques vers le type à virgule fixe et la conversion implicite de valeurs du type universal_real vers le type.

De plus, pour chaque type ou sous-type à virgule fixe T, les opérations de base incluent les attributs répertoriés ci-dessous. Dans cette présentation, T est considéré comme un sous-type (le sous-type T) pour toute propriété qui dépend des contraintes imposées par T ; les autres propriétés sont énoncées en termes de type de base de T.

Le premier groupe d'attributs donne les caractéristiques du sous-type T. Les attributs de ce groupe sont les attributs BASE, les attributs FIRST et LAST, l'attribut de représentation SIZE et les attributs suivants :

Attribut Description
T'DELTA Renvoie la valeur du delta spécifié dans la définition de précision fixe pour le sous-type T. La valeur de cet attribut est de type universal_real.
T'MANTISSA Renvoie le nombre de chiffres binaires dans la mantisse des numéros de modèle du sous-type T. La valeur de cet attribut est de type universal_integer.
T'SMALL Donne le plus petit numéro de modèle positif (différent de zéro) du sous-type T. La valeur de cet attribut est de type universal_real.
T'LARGE Donne le plus grand numéro de modèle positif du sous-type T. La valeur de cet attribut est de type universal_real.
T'FORE Renvoie le nombre minimal de caractères nécessaires à la partie entière de la représentation décimale de toute valeur du sous-type T, en supposant que la représentation n'inclut pas d'exposant, mais inclut un préfixe d'un caractère qui est soit un signe moins, soit un espace. (Ce nombre minimal n'inclut pas les zéros ou les soulignements superflus, et est d'au moins deux.) La valeur de cet attribut est de type universal_integer.
T'AFT Renvoie le nombre de chiffres décimaux nécessaires après le point pour prendre en compte la précision du sous-type T, sauf si le delta du sous-type T est supérieur à 0,1, auquel cas l'attribut renvoie la valeur un. (T'AFT est le plus petit entier positif N pour lequel (10**N)*T'DELTA est supérieur ou égal à un.) La valeur de cet attribut est de type universal_integer.

Les attributs du deuxième groupe incluent les attributs suivants donnant les caractéristiques des numéros sûrs :

Attribut Description
T'SAFE_SMALL Génère le plus petit nombre sûr positif (non nul) du type de base de T. La valeur de cet attribut est de type universal_real.
T'SAFE_LARGE Donne le plus grand nombre positif sûr du type de base de T. La valeur de cet attribut est de type universal_real.

De plus, les attributs A'SIZE et A'ADDRESS sont définis pour un objet A de type à virgule fixe. Enfin, pour chaque type ou sous-type à virgule fixe T, il existe les attributs dépendants de la machine T'MACHINE_ROUNDS et T'MACHINE_OVERFLOWS.

Outre les opérations de base, les opérations d'un type à virgule fixe incluent les opérateurs relationnels et les opérateurs arithmétiques prédéfinis suivants : les opérateurs d'addition binaire et unaire - et +, les opérateurs de multiplication * et /, et l'opérateur abs.

Les opérations d'un sous-type sont les opérations correspondantes du type à l'exception des suivantes : affectation, tests d'appartenance, qualification, conversion explicite et les attributs du premier groupe : les effets de ces opérations sont redéfinis en fonction du sous-type.

Remarques : La valeur de l'attribut T'FORE ne dépend que de la portée du sous-type T. La valeur de l'attribut T'AFT ne dépend que de la valeur de T'DELTA. Les relations suivantes existent entre les attributs d'un type à virgule fixe :

  1. T'LARGE      = (2**T'MANTISSA - 1) * T'SMALL
  2. T'SAFE_LARGE = T'BASE'LARGE
  3. T'SAFE_SMALL = T'BASE'SMALL

Types de tableau

Un objet tableau est un objet composite constitué de composantes ayant le même sous-type. Le nom d'une composante d'un tableau utilise une ou plusieurs valeurs d'index appartenant à des types discrets spécifiés. La valeur d'un objet tableau est une valeur composite constituée des valeurs de ses composantes :

definition_du_type_de_tableau ::=
   definition_de_tableau_sans_contrainte | definition_de_tableau_contraint

definition_de_tableau_sans_contrainte ::=
   array(definition_du_sous_type_d_index |, efinition_du_sous_type_d_index|) of
      indication_sous_type_de_composante

definition_de_tableau_contraint ::=
   array contrainte_index of indication_sous_type_de_composante

definition_du_sous_type_d_index ::= marque_type range <>

contrainte_index ::= (intervalle_discret |, intervalle_discret|)

intervalle_discret ::= indication_de_sous_type_discrete | range

Un objet tableau est caractérisé par le nombre d'index (la dimensionnalité du tableau), le type et la position de chaque index, les limites inférieures et supérieures de chaque index, ainsi que le type et la contrainte possible des composantes. L'ordre des index est important.

Un tableau unidimensionnel possède un composant distinct pour chaque valeur d'index possible. Un tableau multidimensionnel possède une composante distincte pour chaque séquence possible de valeurs d'index pouvant être formée en sélectionnant une valeur pour chaque position d'index (dans l'ordre donné). Les valeurs possibles pour un index donné sont toutes les valeurs comprises entre les limites inférieure et supérieure, incluses ; cette intervalle de valeurs est appelée intervalle d'index.

Une définition de tableau sans contrainte définit un type de tableau. Pour chaque objet ayant le type de tableau, le nombre d'index, le type et la position de chaque index, ainsi que le sous-type des composants sont comme dans la définition de type ; les valeurs des limites inférieure et supérieure pour chaque index appartiennent au sous-type d'index correspondant, à l'exception des tableaux nuls. Le sous-type d'index pour une position d'index donnée est, par définition, le sous-type désigné par la marque de type de la définition de sous-type d'index correspondante. Le délimiteur composé <> (appelé boîte) d'une définition de sous-type d'index représente un intervalle indéfinie (différents objets du type n'ont pas besoin d'avoir les mêmes limites). L'élaboration d'une définition de tableau sans contrainte crée un type de tableau; cette élaboration inclut celle de l'indication de sous-type de composante.

Une définition de tableau avec contrainte définit à la fois un type de tableau et un sous-type de ce type :

Si une définition de tableau restreinte est donnée pour une déclaration de type, le nom simple déclaré par cette déclaration désigne le sous-type de tableau.

L'élaboration d'une définition de tableau restreinte crée le type de tableau et le sous-type de tableau correspondants. Pour cette élaboration, la contrainte d'index et l'indication de sous-type de composant sont élaborées. L'évaluation de chaque intervalle discrète de la contrainte d'index et l'élaboration de l'indication de sous-type de composante sont effectuées dans un ordre n'étant pas défini par le langage de programmation.

Exemples de déclarations de type avec des définitions de tableau non restreintes :

  1. type VECTOR     is array(INTEGER range <>)  of REAL;
  2. type MATRIX     is array(INTEGER range <>, INTEGER range <>) of REAL;
  3. type BIT_VECTOR is array(INTEGER range <>)  of BOOLEAN;
  4. type ROMAN      is array(POSITIVE range <>) of ROMAN_DIGIT;     

Exemples de déclarations de type avec des définitions de tableau contraintes :

  1. type TABLE    is array(1 .. 10) of INTEGER;
  2. type SCHEDULE is array(DAY) of BOOLEAN;
  3. type LINE     is array(1 .. MAX_LINE_SIZE) of CHARACTER;

Exemples de déclarations d'objet avec des définitions de tableaux contraints :

  1. GRID : array(1 .. 80, 1 .. 100)        of BOOLEAN;
  2. MIX  : array(COLOR range RED .. GREEN) of BOOLEAN;
  3. PAGE : array(1 .. 50)                  of LINE;    -- un tableau de tableaux

Remarque : pour un tableau unidimensionnel, la règle donnée signifie qu'une déclaration de type avec une définition de tableau contrainte telle que :

  1. type T is array(POSITIVE range MIN .. MAX) of COMPONENT;

est équivalent (en l'absence d'une dépendance d'ordre incorrecte) à la succession de déclarations :

  1. subtype index_subtype is POSITIVE range MIN .. MAX;
  2. type    array_type    is array(index_subtype range <>) of COMPONENT;
  3. subtype T             is array_type(index_subtype);

index_subtype et array_type sont tous deux anonymes. Par conséquent, T est le nom d'un sous-type et tous les objets déclarés avec cette marque de type sont des tableaux ayant les mêmes limites. Des transformations similaires s'appliquent aux tableaux multidimensionnels.

Une transformation similaire s'applique à un objet dont la déclaration inclut une définition de tableau contraint. Une conséquence de cela est qu'aucun de ces objets n'a le même type.

Contraintes d'index et d'intervalles discrètes

Une contrainte d'index détermine l'intervalle de valeurs possibles pour chaque index d'un type de tableau, et donc les limites de tableau correspondantes.

Pour un intervalle discrète utilisée dans une définition de tableau contraint et définie par un intervalle, une conversion implicite vers le type prédéfini INTEGER est supposée si chaque limite est soit un littéral numérique, un nombre nommé ou un attribut, et le type des deux limites (avant la conversion implicite) est le type universal_integer. Sinon, les deux limites doivent être du même type discret, autre que universal_integer, ce type doit être déterminable indépendamment du contexte, mais en utilisant le fait que le type doit être discret et que les deux limites doivent avoir le même type. Ces règles s'appliquent également à un intervalle discrète utilisée dans une règle d'itération ou dans la déclaration d'une famille d'entrées.

Si une contrainte d'index suit une marque de type dans une indication de sous-type, le type ou le sous-type désigné par la marque de type ne doit pas déjà imposer une contrainte d'index. La marque de type doit désigner soit un type de tableau sans contrainte, soit un type d'accès dont le type désigné est un tel type de tableau. Dans les deux cas, la contrainte d'index doit fournir un intervalle discrète pour chaque index du type de tableau et le type de chaque intervalle discrète doit être le même que celui de l'index correspondant.

Une contrainte d'index est compatible avec le type désigné par la marque de type si et seulement si la contrainte définie par chaque intervalle discrète est compatible avec le sous-type d'index correspondant. Si l'une des intervalles discrètes définit un intervalle nulle, tout tableau ainsi contraint est un tableau nul, n'ayant aucun composante. Une valeur de tableau satisfait une contrainte d'index si à chaque position d'index, la valeur de tableau et la contrainte d'index ont les mêmes limites d'index. (Notez cependant que l'affectation et certaines autres opérations sur les tableaux impliquent une conversion de sous-type implicite.)

Les limites de chaque objet tableau sont déterminées comme suit :

Pour l'élaboration d'une contrainte d'index, les intervalles discrètes sont évaluées dans un ordre n'étant pas défini par le langage de programmation.

Exemples de déclarations de tableaux incluant une contrainte d'index :

  1. BOARD     : MATRIX(1 .. 8, 1 .. 8);
  2. RECTANGLE : MATRIX(1 .. 20, 1 .. 30);
  3. INVERSE   : MATRIX(1 .. N, 1 .. N); -- N n'a pas besoin d'être statique
  4.  
  5. FILTER    : BIT_VECTOR(0 .. 31);

Exemple de déclaration de tableau avec un sous-type de tableau contraint :

  1. MY_SCHEDULE : SCHEDULE; -- tous les tableaux de type SCHEDULE ont les mêmes limites

Exemple de type d'enregistrement avec une composante étant un tableau :

  1. type VAR_LINE(LENGTH : INTEGER) is 
  2.  record
  3.   IMAGE : STRING(1 .. LENGTH);
  4.  end record;
  5.  
  6. NULL_LINE : VAR_LINE(0); -- NULL_LINE.IMAGE est un tableau nul

Remarques : L'élaboration d'une indication de sous-type constituée d'une marque de type suivie d'une contrainte d'index vérifie la compatibilité de la contrainte d'index avec la marque de type. Tous les composantes d'un tableau ont le même sous-type. En particulier, pour un tableau de composantes étant des tableaux unidimensionnels, cela signifie que tous les composants ont les mêmes limites et donc la même longueur.

Opérations des types de tableau

Les opérations de base d'un type de tableau incluent les opérations impliquées dans l'affectation et les agrégats (sauf si le type de tableau est limité), les tests d'appartenance, les composantes indexés, la qualification et la conversion explicite ; pour les tableaux unidimensionnels, les opérations de base incluent également les opérations impliquées dans les tranches, ainsi que les littéraux de chaîne de caractères si le type de composante est un type de caractère. Si A est un objet de tableau, une valeur de tableau ou un sous-type de tableau contraint, les opérations de base incluent également les attributs répertoriés ci-dessous. Ces attributs ne sont pas autorisés pour un type de tableau sans contrainte. Le paramètre N utilisé dans les désignateurs d'attribut pour la N-ième dimension d'un tableau doit être une expression statique de type universal_integer. La valeur de N doit être positive (différente de zéro) et ne pas être supérieure à la dimension du tableau :

Attribut Description
A'FIRST Renvoie la limite inférieure du première intervalle d'index. La valeur de cet attribut a le même type que cette limite inférieure.
A'FIRST(N) Donne la limite inférieure de l'intervalle d'index N. La valeur de cet attribut a le même type que cette limite inférieure.
A'LAST Renvoie la limite supérieure du premier intervalle d'index. La valeur de cet attribut a le même type que cette limite supérieure.
A'LAST(N) Donne la limite supérieure de l'intervalle d'index N. La valeur de cet attribut a le même type que cette limite supérieure.
A'RANGE Donne le premier intervalle d'index, c'est-à-dire l'intervalle A'FIRST .. A'LAST.
A'RANGE(N) Donne la N-ième intervalle d'index, c'est-à-dire l'intervalle A'FIRST(N).. A'LAST(N).
A'LENGTH Renvoie le nombre de valeurs de la première intervalle d'index (zéro pour une plage nulle). La valeur de cet attribut est de type universal_integer.
A'LENGTH(N) Renvoie le nombre de valeurs de l'intervalle d'index N-ième (zéro pour une plage nulle). La valeur de cet attribut est de type universal_integer.

De plus, l'attribut T'BASE est défini pour un type ou sous-type de tableau T; l'attribut T'SIZE est défini pour un type ou sous-type de tableau T, et les attributs A'SIZE et A'ADDRESS sont définis pour un objet tableau A.

Outre les opérations de base, les opérations d'un type tableau incluent la comparaison prédéfinie pour l'égalité et l'inégalité, à moins que le type tableau ne soit limité. Pour les tableaux unidimensionnels, les opérations incluent la chaîne de caractères, à moins que le type tableau ne soit limité; si le type composante est un type discret, les opérations incluent également tous les opérateurs relationnels prédéfinis; si le type composant est un type booléen, alors les opérations incluent également l'opérateur de négation logique unaire not et les opérateurs logiques.

Exemples :

  1. -- FILTER'FIRST      = 0      FILTER'LAST       = 31        FILTER'LENGTH = 32
  2. -- RECTANGLE'LAST(1) = 20     RECTANGLE'LAST(2) = 30

Remarques : les attributs A'FIRST et A'FIRST(1) génèrent la même valeur. Une relation similaire existe pour les attributs A'LAST, A'RANGE et A'LENGTH. Les relations suivantes sont satisfaites (à l'exception d'un tableau nul) par les attributs ci-dessus si le type d'index est un type entier :

  1. A'LENGTH    = A'LAST    - A'FIRST    + 1
  2. A'LENGTH(N) = A'LAST(N) - A'FIRST(N) + 1

Un type de tableau est limité si son type de composante est limité.

Le type STRING

Les valeurs du type prédéfini STRING sont des tableaux unidimensionnels du type prédéfini CHARACTER, indexés par des valeurs du sous-type prédéfini POSITIVE :

  1. subtype POSITIVE is INTEGER range 1 .. INTEGER'LAST;
  2. type STRING is array(POSITIVE range <>) of CHARACTER;

Exemples :

  1. STARS      : STRING(1 .. 120) := (1 .. 120 => '*' );
  2. QUESTION   : constant STRING := "COMBIEN DE CARACTÈRES ?";
  3. -- QUESTION'FIRST - 1, QUESTION'LAST = 20 (le nombre de caractères)
  4.  
  5. ASK.TWICE  : constant STRING := QUESTION & QUESTION;
  6. NINETY_SIX : constant ROMAN  := "XCVI";

Remarques : Les littéraux de chaîne de caractères sont des opérations de base applicables au type STRING et à tout autre type de tableau unidimensionnel dont le type de composante est un type de caractère. L'opérateur de chaîne de caractères est un opérateur prédéfini pour le type STRING et pour les types de tableau unidimensionnel ; il est représenté par &. Les opérateurs relationnels <, <=, > et >= sont définis pour les valeurs de ces types et correspondent à l'ordre lexicographique.

Types d'enregistrement

Un objet enregistrement est un objet composite constitué de composantes nommés. La valeur d'un objet enregistrement est une valeur composite constituée des valeurs de ses composantes.

definition_du_type_d_enregistrement ::=
  record
    liste_de_composantes ::=
  end record

liste_de_composantes ::=
  declaration_de_composante {declaration_de_composante}
| [declaration_de_composante] variante_partie
|  null;
declaration_de_composante ::=
  liste_identifiant : definition_du_sous_type_de_composante [:= expression];

definition_du_sous_type_de_composante ::= sous_type_indication

Chaque déclaration de composante déclare une composante du type d'enregistrement. Outre les composantes déclarés par les déclarations de composante, les composantes d'un type d'enregistrement incluent tous les composantes déclarés par les spécifications discriminantes de la déclaration de type d'enregistrement. Les identifiants de tous les composantes d'un type d'enregistrement doivent être distincts. L'utilisation d'un nom désignant une composante d'enregistrement autre qu'un discriminant n'est pas autorisée dans la définition de type d'enregistrement déclarant la composante. Une déclaration de composante avec plusieurs identifiants équivaut à une séquence de déclarations de composantes uniques. Chaque déclaration de composante unique déclare un composant d'enregistrement dont le sous-type est spécifié par la définition de sous-type de composante.

Si une déclaration de composante inclut le délimiteur composé d'affectation suivi d'une expression, l'expression est l'expression par défaut de la composante d'enregistrement; l'expression par défaut doit être du type de composante. Les expressions par défaut ne sont pas autorisées pour les composantes ayant d'un type limité.

Si un type d'enregistrement n'a pas de partie discriminante, les mêmes composantes sont présents dans toutes les valeurs du type. Si la liste des composantes d'un type d'enregistrement est définie par le mot réservé null et qu'il n'y a pas de partie discriminante, alors le type d'enregistrement n'a pas de composantes et tous les enregistrements de ce type sont des enregistrements nuls.

L'élaboration d'une définition de type d'enregistrement crée un type d'enregistrement; elle consiste en l'élaboration de toutes les déclarations de composantes correspondantes (uniques), dans l'ordre dans lequel elles apparaissent, y compris toute déclaration de composant dans une partie variante. L'élaboration d'une déclaration de composant consiste en l'élaboration de la définition du sous-type de composante.

Pour l'élaboration d'une définition de sous-type de composante, si la contrainte ne dépend pas d'un discriminant, alors l'indication de sous-type est élaborée. Si, en revanche, la contrainte dépend d'un discriminant, alors l'élaboration consiste en l'évaluation de toute expression incluse n'étant pas un discriminant.

Exemples de déclarations de type d'enregistrement :

  1. type DATE is
  2.   record
  3.     DAY   : INTEGER range 1 .. 31;
  4.     MONTH : MONTH.NAME;
  5.     YEAR  : INTEGER range 0 .. 4000;
  6.   end record;
  7.   
  8. type COMPLEX is
  9.   record
  10.     RE : REAL := 0.0;
  11.     IM : REAL := 0.0;
  12.   end record;

Exemples de variables d'enregistrement :

  1. TOMORROW, YESTERDAY : DATE;
  2. A, B, C : COMPLEX;
  3.  
  4. -- les deux composantes de A, B et C sont implicitement initialisés à zéro

Remarques : L'expression par défaut d'une composante d'enregistrement est implicitement évaluée par l'élaboration de la déclaration d'un objet d'enregistrement, en l'absence d'une initialisation explicite. Si une déclaration de composant possède plusieurs identifiants, l'expression est évaluée une fois pour chaque composante de l'objet (puisque la déclaration est équivalente à une séquence de déclarations de composants uniques). Contrairement aux composants d'un tableau, les composantes d'un enregistrement n'ont pas besoin d'être du même type.

Discriminants

Une partie discriminante spécifie les discriminants d'un type. Un discriminant d'un enregistrement est une composante de l'enregistrement. Le type d'un discriminant doit être discret :

partie_discriminante ::=
  (specification_discriminante {; specification_discriminante})

specification_discriminante ::=
  liste_identifiant : marque_type [:= expression]

Une partie discriminante n'est autorisée que dans la déclaration de type pour un type d'enregistrement, dans une déclaration de type privé ou une déclaration de type incomplète (la déclaration complète correspondante doit alors déclarer un type d'enregistrement) et dans la déclaration de paramètre générique pour un type privé formel.

Une spécification discriminante avec plusieurs identifiants est équivalente à une séquence de spécifications discriminantes simples. Chaque spécification discriminante simple déclare un discriminant. Si une spécification discriminante inclut le délimiteur composé d'affectation suivi d'une expression, l'expression est l'expression par défaut du discriminant ; l'expression par défaut doit être du type du discriminant. Les expressions par défaut doivent être fournies pour tous ou pour aucun des discriminants d'une partie discriminante.

L'utilisation du nom d'un discriminant n'est pas autorisée dans les expressions par défaut d'une partie discriminante si la spécification du discriminant est elle-même donnée dans la partie discriminante.

Dans une définition de type d'enregistrement, les seules utilisations autorisées du nom d'un discriminant du type d'enregistrement sont : dans les expressions par défaut des composantes d'enregistrement; dans une partie variante comme nom de discriminant; et dans une définition de sous-type de composante, soit comme limite dans une contrainte d'index, soit pour spécifier une valeur discriminante dans une contrainte discriminante. Un nom de discriminant utilisé dans ces définitions de sous-type de composante doit apparaître seul, et non dans le cadre d'une expression plus large. De telles définitions de sous-type de composant et de telles contraintes sont dites dépendantes d'un discriminant.

On dit qu'un composant dépend d'un discriminant s'il s'agit d'un composant d'enregistrement déclaré dans une partie variante, ou d'une composante d'enregistrement dont la définition de sous-type de composante dépend d'un discriminant, ou enfin, de l'un des sous-composants d'une composante dépendant lui-même d'un discriminant.

Chaque valeur d'enregistrement comprend une valeur pour chaque discriminant spécifié pour le type d'enregistrement; elle comprend également une valeur pour chaque composante d'enregistrement ne dépendant pas d'un discriminant. Les valeurs des discriminants déterminent quelles autres valeurs de composant se trouvent dans la valeur d'enregistrement.

L'affectation directe à un discriminant d'un objet n'est pas autorisée; de plus, un discriminant n'est pas autorisé en tant que paramètre réel de mode in out ou out, ou en tant que paramètre réel générique de mode in out. La seule manière autorisée de modifier la valeur d'un discriminant d'une variable est d'attribuer une valeur (complète) à la variable elle-même. De même, une affectation à la variable elle-même est la seule manière autorisée de modifier la contrainte d'un de ses composantes, si la définition du sous-type du composant dépend d'un discriminant de la variable.

L'élaboration d'une partie discriminante n'a pas d'autre effet.

Exemples :

  1. type BUFFER(SIZE : BUFFER_SIZE := 100) is 
  2.   record
  3.     POS   : BUFFER_SIZE := 0;
  4.     VALUE : STRING(1 .. SIZE);
  5.   end record;
  6.   
  7. type SQUARE(SIDE : INTEGER) is
  8.    record
  9.     MAT : MATRIX(1 .. SIDE, 1 .. SIDE); 
  10.  end record;
  11.  
  12. type DOUBLE_SQUARE(N UMBER : INTEGER) is
  13.   record
  14.     LEFT  : SQUARE (NUMBER);
  15.     RIGHT : SQUARE (NUMBER);
  16.   end record;
  17.  
  18. type ITEM(NUMBER : POSITIVE) is
  19.   record
  20.     CONTENT : INTEGER;
  21.     -- aucune composante ne dépend du discriminant
  22.   end record;

Contraintes discriminantes

Une contrainte discriminante n'est autorisée que dans une indication de sous-type, après une marque de type. Cette marque de type doit désigner soit un type avec des discriminants, soit un type d'accès dont le type désigné est un type avec des discriminants. Une contrainte discriminante spécifie les valeurs de ces discriminants :

contrainte_discriminante ::=
  (association_discriminante |, association_discriminante|)

association_discriminante ::=
  [ discriminant_simple_nom {| discriminant_simple_nom I =>] expression

Chaque association discriminante associe une expression à un ou plusieurs discriminants. Une association discriminante est dite nommée si les discriminants sont spécifiés explicitement par leurs noms ; sinon, elle est dite positionnelle. Pour une association positionnelle, le (seul) discriminant est implicitement spécifié par la position, dans l'ordre textuel. Les associations nommées peuvent être données dans n'importe quel ordre, mais si des associations positionnelles et nommées sont utilisées dans la même contrainte discriminante, 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 la contrainte discriminante ne doit utiliser que des associations nommées.

Pour une association discriminante nommée, les noms des discriminants doivent désigner les discriminants du type pour lequel la contrainte discriminante est donnée. Une association discriminante avec plus d'un nom de discriminant n'est autorisée que si les discriminants nommés sont tous du même type. De plus, pour chaque association discriminante (qu'elle soit nommée ou positionnelle), l'expression et les discriminants associés doivent avoir le même type. Une contrainte discriminante doit fournir exactement une valeur pour chaque discriminant du type.

Une contrainte discriminante est compatible avec le type désigné par une marque de type, si et seulement si chaque valeur discriminante appartient au sous-type du discriminant correspondant. De plus, pour chaque sous-composant dont la spécification de sous-type de composant dépend d'un discriminant, la valeur discriminante est substituée au discriminant dans cette spécification de sous-type de composant et la compatibilité de l'indication de sous-type résultante est vérifiée.

Une valeur composite satisfait une contrainte discriminante si et seulement si chaque discriminant de la valeur composite a la valeur imposée par la contrainte discriminante.

Les valeurs initiales des discriminants d'un objet d'un type à discriminants sont déterminées comme suit :

Pour l'élaboration d'une contrainte discriminante, les expressions données dans les associations discriminantes 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 discriminant nommé.

Exemples (en utilisant les types déclarés dans la section précédente) :

  1. LARGE   : BUFFER(200); -- contraint, toujours 200 caractères (valeur discriminante explicite)
  2. MESSAGE : BUFFER;      -- sans contrainte, initialement 100 caractères (valeur discriminante par défaut)
  3. BASIS   : SQUARE(5);   -- contraint, toujours 5 par 5
  4. ILLEGAL : SQUARE;      -- illégal, un CARRÉ doit être contraint

Remarque : Les règles ci-dessus et les règles définissant l'élaboration d'une déclaration d'objet garantissent que les discriminants ont toujours une valeur. En particulier, si une contrainte discriminante est imposée à une déclaration d'objet, chaque discriminant est initialisé avec la valeur spécifiée par la contrainte. De même, si le sous-type d'un composant a une contrainte discriminante, les discriminants de la composante sont initialisés en conséquence.

Parties variantes

Un type d'enregistrement avec une partie variante spécifie des listes alternatives de composantes. Chaque variante définit les composants pour la ou les valeurs correspondantes du discriminant :

variante_partie ::=
  case discriminant_simple_nom is
      variante
    | variante)
  end case;

variante ::=
  when choix (| choix) =>
    liste_de_composantes

choix ::= expression_simple
  | intervalle_discrete | others | composante_simple_nom

Chaque variante commence par une liste de choix qui doivent être du même type que le discriminant de la partie variante. Le type du discriminant d'une partie variante ne doit pas être un type formel générique. Si le sous-type du discriminant est statique, alors chaque valeur de ce sous-type doit être représentée une fois et une seule fois dans l'ensemble des choix de la partie variante, et aucune autre valeur n'est autorisée. Sinon, chaque valeur du type (de base) du discriminant doit être représentée une fois et une seule fois dans l'ensemble des choix.

Les expressions simples et les intervalles discrets données comme choix dans une partie variante doivent être statiques. Un choix défini par un intervalle discrète représente toutes les valeurs de la plage correspondante (aucune si une plage nulle). Le choix autres n'est autorisé que pour la dernière variante et comme son seul choix ; il représente toutes les valeurs (éventuellement aucune) non données dans les choix des variantes précédentes. Un nom simple de composant n'est pas autorisé comme choix d'une variante (bien qu'il fasse partie de la syntaxe du choix).

Une valeur d'enregistrement contient les valeurs des composantes d'une variante donnée si et seulement si la valeur discriminante est égale à l'une des valeurs spécifiées par les choix de la variante. Cette règle s'applique à son tour à toute autre variante étant elle-même incluse dans la liste des composantes de la variante donnée. Si la liste des composants d'une variante est spécifiée par null, la variante n'a aucune composante.

Exemple de type d'enregistrement avec une partie variante :

  1. type DEVICE is (PRINTER, DISK, DRUM);
  2. type STATE  is (OPEN, CLOSED);
  3.  
  4. type PERIPHERAUUNIT : DEVICE := DISK) is
  5.   record
  6.     STATUS : STATE;
  7.     case UNIT is
  8.        when PRINTER =>
  9.          LINE_COUNT : INTEGER range 1 .. PAGE_SIZE;
  10.        when others =>
  11.          CYLINDER : CYLINDERINDEX;
  12.          TRACK    : TRACK_NUMBER;
  13.        end case;
  14.     end record;

Exemples de sous-types d'enregistrement :

  1. subtype DRUM_UNIT is PERIPHERAL(DRUM);
  2. subtype DISK_UNIT is PERIPHERAL(DISK);

Exemples de variables d'enregistrement contraintes :

  1. WRITER : PERIPHERAL(UNIT => PRINTER);
  2. ARCHIVE : DISK_UNIT;

Remarque : Les choix avec des valeurs discrètes sont également utilisés dans les instructions case et dans les agrégats de tableaux. Les choix avec des noms simples de composants sont utilisés dans les agrégats d'enregistrements.

Opérations des types d'enregistrement

Les opérations de base d'un type d'enregistrement incluent les opérations impliquées dans l'affectation et les agrégats (sauf si le type est limité), les tests d'appartenance, la sélection des composantes d'enregistrement, la qualification et la conversion de type (pour les types dérivés).

Pour tout objet A d'un type avec des discriminants, les opérations de base incluent également l'attribut suivant :

Attribut Description
A'CONSTRAINED Renvoie la valeur TRUE si une contrainte discriminante s'applique à l'objet A, ou si l'objet est une constante (y compris un paramètre formel ou un paramètre formel générique de mode in); renvoie la valeur FALSE dans le cas contraire. Si A est un paramètre formel générique de mode in out, ou si A est un paramètre formel de mode in out ou out et que la marque de type donnée dans la spécification de paramètre correspondante désigne un type sans contrainte avec des discriminants, alors la valeur de cet attribut est obtenue à partir de celle du paramètre actuel correspondant. La valeur de cet attribut est du type prédéfini BOOLEAN.

De plus, les attributs T'BASE et T'SIZE sont définis pour un type ou sous-type d'enregistrement T; les attributs A'SIZE et A'ADDRESS sont définis pour un objet d'enregistrement A. Outre les opérations de base, les opérations d'un type d'enregistrement incluent la comparaison prédéfinie pour l'égalité et l'inégalité, sauf si le type est limité.

Remarque : un type d'enregistrement est limité si le type de l'un de ses composants est limité.

Types access

Un objet déclaré par une déclaration d'objet est créé par l'élaboration de la déclaration d'objet et est désigné par un nom simple ou par une autre forme de nom. En revanche, il existe des objets créés par l'évaluation d'allocateurs et n'ayant pas de nom simple. L'accès à un tel objet est obtenu par une valeur access renvoyée par un allocateur; la valeur d'accès est dite désigner l'objet.

definition_du_type_d_acces ::= access sous_type_indication

Pour chaque type d'accès, il existe un littéral null ayant une valeur d'accès null ne désignant aucun objet. La valeur null d'un type d'accès est la valeur initiale par défaut du type. Les autres valeurs d'un type d'accès sont obtenues par l'évaluation d'une opération spéciale du type, appelée allocateur. Chacune de ces valeurs d'accès désigne un objet du sous-type défini par l'indication de sous-type de la définition du type d'accès ; ce sous-type est appelé le sous-type désigné : le type de base de ce sous-type est appelé le type désigné. Les objets désignés par les valeurs d'un type d'accès forment une collection implicitement associée au type.

L'élaboration d'une définition de type d'accès consiste en l'élaboration de l'indication de sous-type et crée un type d'accès.

Si un objet d'accès est constant, la valeur d'accès contenue ne peut pas être modifiée et désigne toujours le même objet. En revanche, la valeur de l'objet désigné ne doit pas nécessairement rester la même (l'affectation à l'objet désigné est autorisée à moins que le type désigné ne soit limité).

Les seules formes de contrainte autorisées après le nom d'un type access dans une indication de sous-type sont les contraintes d'index et les contraintes discriminantes. Une valeur d'accès appartient à un sous-type correspondant d'un type access soit si la valeur d'accès est la valeur nulle, soit si la valeur de l'objet désigné satisfait la contrainte.

Exemples :

  1. type FRAME       is access MATRIX;
  2. type BUFFER_NAME is access BUFFER;

Remarques : Une valeur access délivrée par un allocateur peut être affectée à plusieurs objets d'accès. Il est donc possible qu'un objet créé par un allocateur soit désigné par plusieurs variables ou constantes du type access. Une valeur d'accès ne peut désigner qu'un objet créé par un allocateur ; en particulier, elle ne peut pas désigner un objet déclaré par une déclaration d'objet.

Si le type des objets désignés par les valeurs access est un type tableau ou un type avec discriminants, ces objets sont contraints soit par les limites du tableau, soit par les valeurs discriminantes fournies implicitement ou explicitement pour les allocateurs correspondants.

Les valeurs d'accès sont appelées pointeurs ou références dans certains autres langages.

Déclarations de type incomplètes

Il n'existe aucune limitation particulière concernant le type désigné d'un type d'accès. En particulier, le type d'une composante du type désigné peut être un autre type d'accès, voire le même type access. Cela permet d'obtenir des types d'accès mutuellement dépendants et récursifs. Leurs déclarations nécessitent une déclaration de type incomplète (ou privée) préalable pour un ou plusieurs types :

declaration_de_type_incomplete ::= type identifiant [partie_discriminante];

Pour chaque déclaration de type incomplète, il doit y avoir une déclaration correspondante d'un type avec le même identifiant. La déclaration correspondante doit être soit une déclaration de type complète, soit la déclaration d'un type de tâche. Dans le reste de cette section, les explications sont données en termes de déclarations de type complètes ; les mêmes règles s'appliquent également aux déclarations de types de tâches. Si la déclaration de type incomplète apparaît immédiatement dans une partie déclarative ou dans la partie visible d'une spécification de paquetage, alors la déclaration de type complète doit apparaître plus tard et immédiatement dans cette partie déclarative ou dans cette partie visible. Si la déclaration de type incomplète apparaît immédiatement dans la partie privée d'un paquetage, alors la déclaration de type complète doit apparaître plus tard et immédiatement dans la partie privée elle-même, ou dans la partie déclarative du corps du paquetage correspondant.

Une partie discriminante doit être donnée dans la déclaration de type complète si et seulement si elle est donnée dans la déclaration de type incomplète; si des parties discriminantes sont données, alors elles doivent être conformes. Avant la fin de la déclaration de type complète, la seule utilisation autorisée d'un nom désignant un type déclaré par une déclaration de type incomplète est comme marque de type dans l'indication de sous-type d'une définition de type d'accès; la seule forme de contrainte autorisée dans cette indication de sous-type est une contrainte discriminante.

L'élaboration d'une déclaration de type incomplète crée un type. Si la déclaration de type incomplète comporte une partie discriminante, cette élaboration inclut celle de la partie discriminante : dans un tel cas, la partie discriminante de la déclaration de type complète n'est pas élaborée.

Exemple de type récursif :

  1. type CELL;                 -- déclaration de type incomplète
  2. type LINK is access CELL;
  3.  
  4. type CELL is
  5.   record
  6.     VALUE : INTEGER;
  7.     SUCC  : LINK;
  8.     PRED  : LINK;
  9.   end record;
  10.   
  11. HEAD : LINK := new CELL'(0, null, null);
  12. NEXT : LINK := HEAD.SUCC;

Exemples de types access mutuellement dépendants :

  1. type PERSON(SEX : GENDER);         -- déclaration de type incomplète
  2. type CAR;                          -- déclaration de type incomplète
  3.  
  4. type PERSON_NAME is access PERSON;
  5. type CAR_NAME    is access CAR;
  6.  
  7. type CAR is
  8.   record
  9.     NUMBER : INTEGER;
  10.     OWNER : PERSON_NAME;
  11.   end record;
  12.   
  13. type PERSON(SEX : GENDER) is
  14.   record
  15.     NAME    : STRING(1 .. 20);
  16.     BIRTH   : DATE;
  17.     AGE     : INTEGER range 0 .. 130;
  18.     VEHICLE : CAR_NAME;
  19.     case SEX is
  20.       when M => WIFE    : PERSON_NAME(SEX => F);
  21.       when F => HUSBAND : PERSON_NAME(SEX => M);
  22.     end case;
  23.   end record;
  24.   
  25. MY_CAR, Y0UR_CAR, NEXT_CAR : CAR_NAME; -- implicitement initialisé avec une valeur nulle

Opérations des types access

Les opérations de base d'un type access incluent les opérations d'affectation, les allocateurs pour le type d'accès, les tests d'appartenance, la qualification, la conversion explicite et la valeur null littérale. Si le type désigné est un type avec des discriminants, les opérations de base incluent la sélection des discriminants correspondants; si le type désigné est un type d'enregistrement, elles incluent la sélection des composants correspondants ; si le type désigné est un type de tableau, elles incluent la formation de composants indexés et de tranches ; si le type désigné est un type de tâche, elles incluent la sélection d'entrées et de familles d'entrées. De plus, les opérations de base incluent la formation d'un composant sélectionné avec le mot réservé all.

Si le type désigné est un type de tableau, les opérations de base incluent les attributs qui ont les désignateurs d'attribut FIRST, LAST, RANGE et LENGTH (de même, les désignateurs d'attribut de la N-ième dimension). Le préfixe de chacun de ces attributs doit être une valeur du type access. Ces attributs donnent les caractéristiques correspondantes de l'objet désigné. Si le type désigné est un type de tâche, les opérations de base incluent les attributs ayant les désignateurs d'attribut TERMINATED et CALLABLE. Le préfixe de chacun de ces attributs doit être une valeur du type access. Ces attributs donnent les caractéristiques correspondantes des objets de tâche désignés.

De plus, l'attribut T'BASE et les attributs de représentation T'SIZE et T'STORAGE_SIZE sont définis pour un type ou sous-type d'accès T ; les attributs A'SIZE et A ADDRESS sont définis pour un objet access A.

Outre les opérations de base, les opérations d'un type d'accès incluent la comparaison prédéfinie pour l'égalité et l'inégalité.

Parties déclaratives

Une partie déclarative contient des éléments déclaratifs (éventuellement aucun) :

partie_declarative ::=
|element_declaratif_de_base| |element_declaratif_ulterieur|

element_declaratif_de_base ::= declaration_de_base
| clause_de_representation | clause_d_utilisation

element_declaratif_ulterieur ::= corps
  | declaration_sous_programme | declaration_de_paquet
  | declaration_de_tache       | declaration_generique
  | clause_d_utilisation       | instantiation_generique

corps ::= corps_propre | corps_stub

corps_propre ::= sous_programme_corps | corps_du_paquet | tache_corps

L'élaboration d'une partie déclarative consiste à élaborer les éventuels éléments déclaratifs dans l'ordre dans lequel ils sont donnés dans la partie déclarative. Après son élaboration, un élément déclaratif est dit élaboré. Avant la fin de son élaboration (y compris avant l'élaboration), l'élément déclaratif n'est pas encore élaboré.

Pour plusieurs formes d'élément déclaratif, les règles de langage (en particulier les règles de portée et de visibilité) sont telles qu'il est soit impossible, soit illégal d'utiliser une entité avant l'élaboration de l'élément déclaratif qui déclare cette entité. Par exemple, il n'est pas possible d'utiliser le nom d'un type pour une déclaration d'objet si la déclaration de type correspondante n'est pas encore élaborée. Dans le cas des corps, les vérifications suivantes sont effectuées :

L'exception PROGRAM_ERROR est levée si l'une de ces vérifications échoue.

Si une déclaration de sous-programme, une déclaration de paquet, une déclaration de tâche ou une déclaration générique est un élément déclaratif d'une partie déclarative donnée, alors le corps (s'il y en a un) de l'unité de programme déclarée par l'élément déclaratif doit lui-même être un élément déclaratif de cette partie déclarative (et doit apparaître plus tard). Si le corps est un stub de corps, alors une sous-unité compilée séparément contenant le corps approprié correspondant est requise pour l'unité de programme.



PARTAGER CETTE PAGE SUR
Dernière mise à jour : Samedi, le 28 décembre 2024