Chapitre 12 : Procédures paramétrées
On introduit donc ici les procédures paramétrées ; l'extension de PP6
ainsi construite se nomme PP8. (PP8 est une extension de PP6, on ne reprend
donc pas les procédures imbriquées.)
12.1 Grammaire et sémantique
12.1.1 Définition de procédures
Les règles suivantes de la grammaire de PP6 sont modifiées
|
PROGRAM |
|
::= |
|
program ID ; BLOCK . |
|
|
BLOCK |
|
::= |
|
CONSTS VARS PROCS INSTS |
|
|
PROCS |
|
::= |
|
{ proc ID PARAMLIST ; CONSTS VARS INSTS ; } |
|
|
PARAMLIST |
|
::= |
|
( PARAMDECL { , PARAMDECL } ) | e |
|
|
PARAMDECL |
|
::= |
|
var ID | ID |
|
La règle PARAMLIST
définit la liste des paramètres formels de
la procédure.
12.1.2 Appel de procédure
L'appel de procédure peut maintenant comporter une liste de paramètres
effectifs :
|
APPEL |
|
::= |
|
ID PARAMEFFLIST |
|
|
PARAMEFFLIST |
|
::= |
|
( PARAMEFF { , PARAMEFF } ) | e |
|
|
PARAMEFF |
|
::= |
|
EXPR |
|
Sémantiquement, le nombre de paramètres effectifs de l'appel de
procédure doit correspondre au nombre de paramètres déclarés
dans la définition de la procédure. Il est possible au compilateur
de vérifier le respect de cette règle sémantique en associant le
nombre de paramètres de la procédure à son entrée dans la table
des symboles.
12.1.3 Passages de paramètre par valeur et par adresse
Contrairement à d'autres langages, deux types de passage de
paramètres existent en Pascal. En Fortran, tous les passages de
paramètres se font par adresse ; en C tous les passages de
paramètres se font par valeur1.
Afin de différencier les deux types de passages de paramètres, les
définitions de paramètres formels PARAMDECL
peuvent être
étiquetées par le mot clé var
pour indiquer un passage par
adresse ; dans le cas contraire , il s'agit d'un passage par valeur.
Le paramètre effectif correspondant à une définition de
paramètre formel déclaré var
est une expression EXPR
limitée à un identificateur ID
(cf.
figure 2.2).
Un paramètre effectif correspondant à un passage par valeur est
une expression EXPR
. Cette expression est évaluée et sa valeur
est passée à la procédure.
12.2 Implémentation du passage de paramètre par valeur
12.2.1 Allocation mémoire des paramètres
Les paramètres d'une procédure ne peuvent être connus avant l'exécution.
De plus, ils sont différents à chaque appel. L'idée de base est qu'une
procédure retrouve ses paramètres sur la pile sous la zone qui lui
est allouée, cf figure 12.1
|----------|
| |
| |<--- zone allouee pour
| | l'appel (AR
| | + liens statiques
BASE ---------> | | + variables locales)
|----------|
| |<--- parametres effectifs
| | de l'appel
|----------|
| |
| |
+----------+
Figure 12.1 : État de la pile juste après l'appel d'une procédure.
Ainsi, les paramètres seront accédés grâce à un
déplacement négatif relatif à BASE
.
12.2.2 Génération de code
Le code à générer pour un appel de procédure doit inclure
-
Le P-Code empilant les valeurs des paramètres effectifs.
- Une instruction P-Code
CAL i
réalisant le branchement
nécessaire à l'appel de la procédure.
On modifie l'instruction P-Code RET
de retour de procédure qui accepte maintenant un paramètre :
RET n
Ce paramètre correspond au nombre de paramètres devant être
dépilés pour mettre à jour la variable SP
du compilateur.
On modifie donc l'interpréteur P-Code
(section 9.2) en conséquence :
with INST do
case MNE of
...
RET : begin
PC := MEM [BASE] ;
SP := SP - SUITE ;
BASE := MEM [BASE + 1]
end ;
end
Prenons en exemple le code PP8 suivant :
program X ;
var A ;
proc Y (N) ;
begin
write (N)
end ;
begin
read (A) ;
Y (A+2)
end .
On générera le P-Code suivant :
|
0 |
|
|
|
INT 1 |
|
|
|
réserve 1 emplacement pour A |
|
|
1 |
|
|
|
BRN 6 |
|
|
|
saut au début du programme principal |
|
|
# |
|
|
|
|
|
|
|
début de la fonction Y |
|
|
2 |
|
|
|
LDL -3 |
|
|
|
empile l'adresse de N |
|
|
3 |
|
|
|
LDV |
|
|
|
accède la valeur de N |
|
|
4 |
|
|
|
PRN |
|
|
5 |
|
|
|
RET 1 |
|
|
# |
|
|
|
|
|
|
|
début du programme |
|
|
6 |
|
|
|
LDA 0 |
|
|
|
adresse de A |
|
|
7 |
|
|
|
INN |
|
|
|
lecture d'une valeur dans A |
|
|
8 |
|
|
|
LDA 0 |
|
|
|
adresse de A |
|
|
9 |
|
|
|
LDV |
|
|
|
valeur de A |
|
|
10 |
|
|
|
LDI 2 |
|
|
|
2 |
|
|
11 |
|
|
|
ADD |
|
|
|
addition : la valeur est sur la pile |
|
|
12 |
|
|
|
CAL 2 |
|
|
|
appel de Y |
|
|
13 |
|
|
|
HLT |
|
|
|
|
|
La pile avant l'exécution de l'instruction LDL -3
de la ligne 2
est schématisée à la figure 12.2.
| |<-------- variables locales
| |
|----------|
| -----|---+
|----------| |
| AR |<-------- BASE
|----------| |
| N | |
|----------| V
| |
Figure 12.2 : État de la pile juste avant l'exécution de LDL -3.
Le paramètre de l'instruction LDL
a pour valeur -3 car l'instruction
LDL i
référence l'emplacement mémoire BASE+2-i
(cf.
exercice 5).
12.3 Implémentation du passage de paramètre par adresse
Le mécanisme mis en place pour accéder les paramètres passés
par adresse est semblable à celui mis en place pour les passages par
valeur.
L'appelant laisse sur la pile non pas la valeur du paramètre
effectif, mais son adresse.
L'accès à un paramètre formel se fait de la manière suivante :
-
Pour accéder à l'adresse du paramètre effectif, on
génère le P-Code
LDL -n
LDV
- Pour accéder à sa valeur, on génère :
LDL -n
LDV
LDV
12.4 Travaux dirigés et travaux pratiques
-
Quels sont les avantages de proposer les deux types de passage
de paramètre par adresse et par valeur au sein d'un même
langage. Comparer avec les approches de Fortran (passage par
adresse uniquement) et C (passage par valeur uniquement).
- Qu'advient-il si, dans un code Pascal, on passe une expression
non réduite à un nom de variable comme paramètre effectif
dont le paramètre correspondant est déclaré
var
? Comment
un compilateur Pascal peut-il détecter une telle erreur ?
- Implanter dans un premier temps le passage de paramètre par
valeur. Étendre cette implantation au passage par adresse.
- Quels problèmes nouveaux surviennent si l'on désire
étendre le passage de paramètre à des variables et valeurs
de type différents (entiers et réels, puis tableaux,
structures et types construits) ?
- 1
- Cette limitation apparente
est contournée par la possibilité de passer par valeur des
adresses ; le passage par adresse est donc à la charge du
programmeur.