Analyse sémantique
Généralités
L'analyse sémantique a pour rôle de donner un sens aux différentes instructions du programme.
Elle prépare ainsi le travail pour la phase de génération de code (ou d'interprétation).
C'est la phase la plus importante d'un compilateur.
Elle transforme un programme en une forme interne qui reflète la décomposition du programme en opérations très élémentaires très proche du langage machine.
Dans notre compilateur, cette forme interne est un ensemble de quadruplés. Tous les quadruplets possibles sont décrits dans le fichier Aide_quadruplés.htm.
Cette forme interne adresse un ensemble de tables. Aussi La phase d'analyse sémantique consiste à construire ces tables.
Toutes ses tables sont décrites dans le fichier Aide_tables.htm.
La phase d'analyse sémantique est accomplie grâce à des fonctions sémantiques insérées au niveau des règles de grammaires.
Tables de compilation
Le compilateur garde la trace de tous les modules compilés dans une table présente en RAM ( TABPRO : table des modules )
Pour chaque module compilé, les tables suivantes sont créées :
Tabob : table des objets
Tabcomp : table complémentaires
Quadruple : table des quadruplets
Tabtyp : table des types
Validf : table des identificateurs
Tabidf : table des indices vers les objets
Toutes les tables de compilation, à l'exception de Tabpro, sont dynamiques.
Le compilateur maintient aussi deux tables communes à tous les modules renfermant toutes les constantes.
l'une contient toutes les constantes numériques, les valeurs logiques VRAI et FAUX et la constante pointeur NIL (Tabcons)
l'autre contient toutes les constantes chaines de caractères (Tabconscar)
Lors de la compilation il y a la présence permanente des tables de compilation du module principal et les tables d'un module au plus.
Des fichiers temporaires '$$$kk.tmp' sont donc créés à partir du troisième module compilé.
A la compilation du k-ième module ( k > 2) , les tables du (k-1)-ième module sont sauvegardées pour être utilisées par les opérations de " Swapping " lors de l'interprétation.
Dans le cas où k >2, le dernier module compilé doit exister en mémoire et sur disque.
Les variables temporaires (ou auxiliaires)
Lors de la compilation, des variables temporaires sont créées par le compilateur. Il existe deux types:
des auxiliaires de valeur ('X')
des auxiliaires d'adresse ('Y').
Les fonctions suivantes génèrent des auxiliaires d'adresse ('Y') :
Element, Entete, Struct, Valeur, Suivant
Lors d'un appel de fonction, il y a création d'un auxiliaire d'adresse ('Y') pour le résultat de celle-ci. Ceci est fait dans le but de ne pas libérer ce résultat lors des retours de procédures.
Attribution des adresses aux objets
Une phase de l'analyse sémantique consiste à attribuer des adresses relatives aux objets utilisés dans les différents modules.
Pour cela, il est nécessaire d'anticiper et d'imaginer ce qui va se passer à l'exécution de point de vue gestion mémoire.
A tout module compilé est associé une zone de données fictive qui contient les variables locales, les paramètres, les variables temporaires créés par le compilateur et certaines informations primordiales pour la gestion des appels et des retours.
La zone de données fictive devient réelle au moment de l'exécution.
Organisation de la zone de données
Notre zone de données a la forme suivante :
Mot 0 : Adresse zone de données de l'appelant
Mot 1 : Adresse vers un entier contenant le numéro de l'appelant
Mot 2 : Adresse vers un entier contenant le numéro du quadruplé de retour dans l'appelant ( adresse de retour)
Mot 3, Mot 4, .....Mot i : Paramètres
Mot i+1, Mot i+2, ... : Variables locaux et temporaires
Une zone de données ne contient que des adresses ( vers n'importe quel type d'objet )
Les zones de données sont gérées en pile.
Sémantique des modules
Tous les paramètres sont transmis par "nom".
Tous les paramètres doivent être déclarés.
Si une variable utilisée n'est pas définie dans l'action ou la fonction, elle doit être globale ( déclarée dans le module principal).
Compilation d'un module
Vérifier que le module est annoncé dans le module principal.
Vérifier que tous les paramètres sont définis.
Si c'est une fonction, vérifier que son type est le même que celui annoncé dans le module principal.
Générer le quadruplé (PROC, Pt1, Pt2, Pt3)
Pt1 : Nombre de paramètres
Pt2 : Adresse dans Tabcomp où l'on trouve la liste des paramètres formels.
Pt3 : Adresse dans Tabpro
Appel de procédure (ou fonction)
Générer le quadruplé (Appel, Pt1, Pt2, Pt3)
Pt1 : Adresse dans Tabconscar vers le nom du module appelé
Pt2 : Adresse dans Tabcomp où l'on trouve la liste des paramètres réels
Pt3 : Nombre de paramètres
Retour de procédure (ou fonction)
Générer le quadruplé (Ret, , , )
Fonction utilisateur dans une expression
Retrouver le type de la fonction
Générer une variable temporaire du type de la fonction qui contiendra le résultat de la fonction et l'ajouter comme premier paramètre.
De cette manière, les fonctions se comportent comme des modules 'procédure'.
Fragment de l'analyseur Sémantique
Voici un fragment de la procédure de l'analyseur syntaxico- sémantique correspondant aux instructions :
PROCEDURE Fonction;
VAR
Sauvp2, Fonct, Idf : STRING;
Sauvivar, Sauvtemp : BYTE;
Bsauvivar, Bsauvtemp : BOOLEAN;
Sauvpteurtabcomp : BYTE;
Nbrind : BYTE;
LABEL Et1, Et2, Et3, Et4, Et5, Fin ;
{---------------------------------------------------}
{ F O N C T I O N S S E M A N T I Q U E S }
{---------------------------------------------------}
PROCEDURE F1 ;
BEGIN
Fonct := Semprec;
END;
{-----------------------------------------------------------------------}
PROCEDURE F2;
BEGIN
Idf := Semprec
END;
{-----------------------------------------------------------------------}
PROCEDURE F3;
VAR
P1: CHAR;
P2: Type255;
Pp3,P4,P5 : BYTE ;
Bpt : BOOLEAN;
BEGIN
Old_ident (Idf,Ivar, Bpt);
IF Ivar = 0
THEN P3(3)
ELSE
BEGIN
Old_attribut(Ivar,Bpt, P1,P2,Pp3,P4,P5);
IF Fonct = 'NBRNOMBRE'
THEN
IF (P2 <> 'MN')
THEN
P3(10)
ELSE
BEGIN
Genertemp(Temp,'X','E') ;
Btemp := TRUE;
Enterquad(Sauvnumligne,'Nbrnombre',Ivar,0,Temp, Bpt, TRUE, Btemp);
END;
IF Fonct = 'NBRCAR'
THEN
IF (P2 <> 'MC')
THEN
P3(11)
ELSE
BEGIN
Genertemp(Temp,'X','E') ;
Btemp := TRUE;
Enterquad(Sauvnumligne,'Nbrcar',Ivar,0,Temp, Bpt, TRUE, Btemp );
END;
IF Fonct = 'FINFICH'
THEN
IF (P2[1] <> 'F')
THEN
P3(41)
ELSE
BEGIN
Genertemp(Temp,'X','B') ;
Btemp := TRUE;
Enterquad(Sauvnumligne,'Finfich',Ivar,0,Temp, Bpt, TRUE, Btemp );
END;
IF Fonct = 'ALLOC_BLOC'
THEN
IF (P2[1] <> 'F')
THEN
P3(41)
ELSE
BEGIN
Genertemp(Temp,'X','E') ;
Btemp := TRUE;
Enterquad(Sauvnumligne,'Alloc_bloc',Ivar,0,Temp, Bpt, TRUE, Btemp );
END;
END;
END;
...
...
BEGIN
IF Syn = 1 THEN GOTO Et1 ;{ Idf Sauf Premier Appel à Fonction }
IF Syn = 29 THEN GOTO Et2; { Nbrcar, Nbrnombre, finfich, Alloc_bloc }
IF Syn = 33 THEN GOTO Et3; { Element }
IF Syn = 34 THEN GOTO Et4; { Struct, Entete }
IF Syn = 27 THEN GOTO Et5; { Valeur, Suivant }
Et1 : { Idf }
Check(1, 0);
Ff2;
GOTO Fin;
Et2 : { Nbrcar, Nbrnombre, finfich }
Check(29, 0);
F1;
Check(7, 1);
Check(1, 7);
F2;
Check(8, 3);
F3;
GOTO Fin;
Et3 : { Element }
Check(33, 0);
F1;
Check(7, 1);
Fonction ;
Check(37, 17);
Ff4;
Expr;
Ff5(TRUE);
WHILE Syn = 9 DO
BEGIN
Check(9, 0);
Expr;
Ff5(FALSE);
END;
Check (38, 18);
Check(8, 3);
F6;
GOTO Fin;
Et4 : { Struct, Entete, Caract }
Check(34, 0);
F1;
Check(7, 1);
Expr;
Ff3;
Check(9, 6);
Expr;
F7;
Check(8, 3);
GOTO Fin;
Et5 : { Suivant, Valeur , Aleachaine, Aleanombre, Longchaine}
Check (27, 0);
F1;
Check(7, 1);
Expr ;
Check(8, 3);
F4;
GOTO Fin;
Fin :
END;