Cohérence du programme
Avant de générer un programme exécutable ou un module objet à partir d'un ou plusieurs fichiers source, Up ! Compiler réalise différentes étapes importantes qui sont présentées dans cette rubrique. Il s'agit de vérifier la correction et la cohérence du futur programme.
Voici la présentation des principales étapes. Celles-ci sont présentées succinctement dans les sujets suivants.
- Up ! 5GL Parser analyse chaque fichier source en vue de détecter des erreurs lexicales ou des erreurs syntaxiques.
Up ! 5GL Parser vérifie chaque instruction prise unitairement : il s'agit d'effectuer une vérification sémantique en appliquant des règles de cohérence de premier niveau.
- Up ! 5GL Optimizer vérifie l'enchaînement des instructions : il s'agit d'effectuer une preuve de programme en appliquant des règles de cohérence de second niveau.
- Up ! 5GL Optimizer optimise les instructions : il s'agit de modifier le programme en redécoupant, en réagençant ou en factorisant les instructions en vue de le rendre plus performant.
- Up ! 5GL Optimizer ajoute des instructions en vue de contrôler dynamiquement la cohérence du programme lors de l'exécution.
- Up ! 5GL Optimizer vérifie la complétude du programme en vue de voir s'il comporte tous les traitements déclarés.
- Up ! Compiler énumère une liste d'objets qui sont déclarés mais non utilisés.
Analyse lexicale, syntaxique et sémantique
Analyse lexicale
Lors de l'analyse lexicale, Up ! 5GL Parser vérifie que les éléments du langage sont corrects. Voici une liste non exhaustive des vérifications effectuées lors de l'analyse lexicale :
- Les chaînes de caractères sont bien fermées.
- Les commentaires sont bien fermés.
- Les constantes entières ou réelles n'excède pas les limites du langage.
- L'absence de caractère parasite non éditable.
Analyse syntaxique
Lors de l'analyse syntaxique, Up ! 5GL Parser vérifie que les éléments du langage sont enchaînés correctement en vue de constituer les instructions du programme. Voici une liste non exhaustive des vérifications effectuées lors de l'analyse syntaxique :
- La présence obligatoire des éléments lexicaux constants tels une parenthèse, une virgule, un mot-clé, etc.
- L'emploi d'une constante entière et non pas l'emploi d'un booléen.
- Le source est complet : il ne se termine pas prématurément.
- Le source ne comporte des éléments lexicaux suite à la fin programme, sauf des commentaires.
L'erreur la plus courante produite à cette étape est l'erreur de syntaxe. En ce cas, Up ! 5GL Parser présente l'élément lexical inattendu et énonce la liste des éléments lexicaux attendus à la place.
Dans le cas d'un fonctionnement interactif, Up ! 5GL Parser propose lors de cette phase d'effectuer des corrections pour les erreurs de syntaxes déterministes :
- L'ajout superflu d'un élément lexical tel une parenthèse en trop.
- L'omission d'un élément lexical tel une virgule en moins.
- L'usage d'une constante entière au lieu d'une constante réelle.
- La dyslexie concernant les identificateurs des objets : permutation de deux lettres suite à une faute de frappe, etc.
Lors qu'une telle erreur est détectée, il est proposée de corriger automatiquement le source et l'analyse continue comme s'il n'y avait pas eu d'erreur.
Analyse sémantique
Lors de l'analyse sémantique, Up ! 5GL Parser vérifie que la sémantique des instructions est respectée conformément à ce qui est défini dans le Manuel de Référence. Voici une liste non exhaustive des vérifications effectuées lors de l'analyse sémantique :
- Deux objets ne portent pas le même nom.
- Le respect de la portée des objets.
- Le respect de l'indexation d'un tableau, de la lecture du suivant pour une liste, etc.
- Le respect des prototypes des fonctions, des procédures et des méthodes.
- Le respect des droits d'accès des objets.
- La disponibilité des méthodes implicites ou explicites des objets en fonction de leur type de rattachement.
- La majorité des erreurs envoyées lors de l'analyse du source provient de cette étape.
Revenir au haut de la page...
Preuve de programme
Principe
Le programme est syntaxiquement et sémantiquement cohérent. Il peut compiler, mais ce n'est pas pour autant qu'il fonctionnera correctement ! Up ! 5GL Optimizer analyse alors finement le déroulement des algorithmes en vue de détecter des situations menant à des erreurs certaines. Pour chaque traitement, il y a trois décisions possibles :
- Le traitement ne mène pas à une erreur (cas déterministe).
- Le traitement mène forcément à une erreur (cas déterministe).
- On ne peut rien conclure : il peut y avoir une erreur ou non en fonction du contexte d'exécution du traitement (cas indéterministe).
Pour effectuer cette preuve de programme, Up ! 5GL Optimizer utilise les règles énoncées ci-dessous. Certaines concernent la définition des opérateurs sur les types. Il est important de respecter leur propriété. Si un opérateur ne peut pas respecter une de ces propriétés attendues, alors il ne faut employer un opérateur mais une méthode basée sur une procédure ou une fonction.
Voici une liste non exhaustive des vérifications effectuées lors de la preuve de programme :
- Les variables sont initialisées avant d'être employées.
- Les fonctions comportent bien une instruction Retourner dans chaque bloc d'instructions s'il n'y en a pas une globalement à la fin du corps du traitement.
- Les conditions des boucles et des tests ne sont pas validées ou invalidées par une condition issue d'une boucle ou d'un test préalable ou englobant.
- Les conditions des boucles ne sont pas constantes.
- L'emploi d'une expressions forcément nulle alors qu'une valeur non nulle est attendue.
- La sélection d'un cas d'un type alors que la valeur énumérée de la propriété sélecteur ne correspond pas à ce cas.
- La sélection d'une propriété d'un objet alors que l'objet peut être Nul.
Règles utilisées par la preuve de programme
Expression | Expression équivalente | Commentaire |
A Et B | B Et A | Commutativité de Et |
(A Et B) Et C | A Et (B Et C) | Associativité de Et |
A Ou B | B Ou A | Commutativité de Ou |
(A Ou B) Ou C | A Ou (B Ou C) | Associativité de Ou |
A OuExclusif B | B OuExclusif A | Commutativité d'OuExclusif |
(A OuExclusif B) OuExclusif C | A OuExclusif (B OuExclusif C) | Associativité d'OuExclusif |
Non(Non A) | A | Réflexivité de Non |
Non(A Et B) | Non A Ou Non B | Transformation booléenne |
Non(A EtSi B) | Non A OuSinon Non B | Transformation booléenne |
Non(A Ou B) | Non A Et Non B | Transformation booléenne |
Non(A OuSinon B) | Non A EtSi Non B | Transformation booléenne |
Non(A OuExclusif B) | Non A OuExclusif Non B | Transformation booléenne |
A & B | B & A | Commutativité de & |
(A & B) & C | A & (B & C) | Associativité de & |
A | B | B | A | Commutativité de | |
(A | B) & C | A | (B | C) | Associativité de | |
A + B | B + A | Commutativité de + |
(A + B) & C | A + (B | C) | Associativité de + |
A - B | A + (- B) | Elimination de - |
- A + B | B + (- A) | Elimination de - |
A - (B + C) | (A - B) - C | Pseudo-associativité de - |
A * B | B * A | Commutativité de * |
(A * B) * C | A * (B * C) | Associativité de * |
A / (B * C) | (A / B) / C | Pseudo-associativité de / |
(A + B) * C | A * C + B * C | Distributivité de + par rapport à * |
(A - B) * C | A * C - B * C | Distributivité de - par rapport à * |
(A + B) / C | A / C + B / C | Distributivité de + par rapport à / |
(A - B) / C | A / C - B / C | Distributivité de - par rapport à / |
A ^ (B * C) | (A ^ B) ^ C | Pseudo-associativité de ^ |
- (- A) | A | Réflexivité de - |
- (A + B) | (- A) + (- B) | Transformation arithmétique |
- (A * B) | (- A) * B | Transformation arithmétique |
- (A - B) | (- A) + B | Transformation arithmétique |
- (A / B) | (- A) / B | Transformation arithmétique |
A == B | B == A | Commutativité de == |
A != B | B != A | Commutativité de != |
A > B | B < A | Inversion de la comparaison |
A >= B | B <= A | Inversion de la comparaison |
Non (A > B) | A <= B | Inversion de la comparaison |
Non (A < B) | A >= B | Inversion de la comparaison |
Non (A >= B) | A < B | Inversion de la comparaison |
Non (A <= B) | A > B | Inversion de la comparaison |
Non (A == B) | A != B | Inversion de la comparaison |
Non (A != B) | A == B | Inversion de la comparaison |
A Entre B Et C | (A >= B) Et (A <= C) | Décomposition de Entre |
A Dans (B, C) | (A == B) Ou (A == C) | Décomposition de Dans |
A == B et B == C | A == C | Transitivité de == |
A < B et B < C | A < C | Transitivité de < |
A <= B et B <= C | A <= C | Transitivité de <= |
A < B et B == C | A < C | Semi-transitivité de < |
A <= B et B == C | A <= C | Semi-transitivité de <= |
A < B et A == C | C < B | Semi-transitivité de < |
A <= B et A == C | C <= B | Semi-transitivité de <= |
A == B et B != C | A != C | Semi-transitivité de == |
Revenir au haut de la page...
Optimisation
Up ! 5GL Optimizer réalise des petites transformations au code intermédiaire issu de la vérification de programme. L'objectif est de rendre le programme plus performant :
- Le code est factorisé afin d'éviter les calculs redondants.
- Le code est réagencé afin de minimiser la taille et le nombre de temporaires intermédiaires déclarés implicitement.
- Le code est redécoupé pour interfacer les traitements avec les bibliothèques d'Up ! Application System.
- Du code implicite est ajouté notamment pour le ramasse-miettes.
Il est possible d'influencer la tâche d'optimisation au moyen de l'instruction Optimiser. Celle-ci permet de signifier que le résultat d'un appel de procédure, de fonction ou de méthode est optimisable i.e. que deux appels successifs avec les mêmes paramètres produiront le même résultat sans effet de bord.
Toutes ces transformations sont sans incidence quant au bon fonctionnement du programme : elles le laissent inchangé. Néanmoins, aucune supposition ne doit être effectuée quand à l'ordre de l'évaluation des valeurs passées en paramètre à une fonction, une procédure, une méthode ou à un opérateur.
Il peut arriver que le premier paramètre soit évalué avant le second. Mais le contraire est possible. De plus, si le second paramètre est composée d'une expression, il est possible qu'une partie de l'expression du second paramètre soit évaluée, puis la valeur du premier paramètre est calculée puis, enfin, la fin de l'expression du second paramètre soit évaluée !
Il est cependant possible de définir des tests orientés. Voici des situations où cela est nécessaire :
- Il faut vérifier si un élément suivant ou précédent d'une liste existe puis si la valeur de cet élément vérifie une condition.
- Il faut vérifier si un élément vérifie une condition ou le cas échéant si un autre élément vérifie une autre condition.
Les tests orientés sont construits avec les instructions EtSi
et OuSinon au lieu des instructions Et
et Ou.
L'ordre d'évaluation des deux sous-conditions est garanti pour les instructions EtSi et
OuSinon alors qu'il n'est pas garanti pour les instructions
Et et Ou.
Revenir au haut de la page...
Cohérence dynamique
Lors de la phase de preuve de programme, il y a trois décisions possibles pour chaque traitement :
- Le traitement ne mène pas à une erreur (cas déterministe).
- Le traitement mène forcément à une erreur (cas déterministe).
- On ne peut rien conclure : il peut y avoir une erreur ou non en fonction du contexte d'exécution du traitement (cas indéterministe.)
Dans le cas indéterministe, Up ! 5GL Optimizer ajoute du code permettant de vérifier à l'exécution la cohérence dynamique du programme. Ainsi les tests suivants sont ajoutés :
- Usage d'une valeur nulle alors qu'une valeur non nulle est demandée.
- Respect des bornes d'un tableau.
- Sélection d'un cas d'un type conforme à la valeur du sélecteur.
- Existence de l'élément suivant ou précédent d'une liste avant son usage.
En cas d'erreur détectée à l'exécution, une exception est envoyée précisant le nom du fichier source, le numéro de ligne et colonne à l'origine de l'erreur.
Cette exception peut être rattrapée au moyen de l'instruction AttraperException.
Ces tests sont particulièrement pertinents lors de la mise au point du programme. Ils permettent d'éciter les arrêts violents des programmes (General Protection Fault (GPF), Segment Violation, etc.).
Revenir au haut de la page...
Complétude
Avant de générer le code objet, Up ! 5GL Optimizer vérifie que le code du futur programme ou du futur module est complet. Il vérifie notamment que :
- Les méthodes des types déclarés dans les composants constituants le module sont bien déclarées.
- Les méthodes des propriétés ou des variables virtuelles sont bien déclarées.
- Les interfaces sont bien implémentées.
- Les ressources employées par le futur programme ou du futur module sont disponibles.
Revenir au haut de la page...