Chapitre 3 : Analyse sémantique
Une fois le texte source reconnu syntaxiquement correct, il y a
lieu de voir si le texte vérifie des règles non-exprimées par la
syntaxe. C'est le rôle de l'analyse sémantique.
3.1 Table des symboles
Pour le langage PP1, cela consiste essentiellement à vérifier que
les identificateurs utilisés sont bien déclarés. Pour cela, il
est nécessaire de mémoriser les identificateurs déclarés pour
tester la déclaration ou non des identificateurs utilisés. Cette
mémorisation se fait dans une table des symboles.
Dans cette table, on associe à chaque entrée (chaque
identificateur) toutes les informations connues sur lui, pour le
moment :
-
sa forme textuelle (son nom) ;
- sa classe, à savoir s'il désigne un programme, une constante,
ou une variable.
type
TABLEINDEX = 1 .. INDEXMAX ;
CLASSES = (PROGRAMME, CONSTANTE, VARIABLE) ;
CLASSET = set of CLASSES ;
var
TABLESYM : array [TABLEINDEX] of record
NOM : ALFA ;
CLASSE : CLASSES
end ;
DERNIERSYM : TABLEINDEX ; (* indice du dernier symbole entre *)
La table des symboles est manipulée par deux fonctions :
-
ENTRERSYM
-
ajoute le symbole
SYM
dans la table des symboles avec
la classe passée en paramètre ; une version de base peut être
la suivante :
procedure ENTRERSYM (C:CLASSES) ;
begin
if DERNIERSYM = INDEXMAX then ERREUR ;
DERNIERSYM := DERNIERSYM + 1 ;
with TABLESYM [DERNIERSYM] do
begin NOM := SYM ; CLASSE := C end
end ;
- CHERCHERSYM
-
recherche l'identificateur
SYM
dans la table des symboles parmi les
classes permises passées en paramètre et retourne l'index de
l'entrée correspondante dans la table des symboles, ou s'arrête
sur ERREUR
; on donne ici une version de base :
procedure CHERCHERSYM (var INDEX:TABLEINDEX ; PERMIS:CLASSET) ;
var FIN : booleen ;
begin
INDEX := DERNIERSYM ; FIN := false ;
while not FIN do begin
if INDEX = 0 then FIN := true
else if TABLESYM [INDEX].NOM = SYM
then FIN := true
else INDEX := INDEX - 1
end ;
if INDEX = 0 then ERREUR (* SYM absent *)
else if not (TABLESYM[INDEX].CLASSE in PERMIS)
then ERREUR ; (* SYM pas de bonne classe *)
end ;
3.2 Entrée dans la table des symboles
La procédure ENTRERSYM
est utilisée lors de la déclaration de
symboles par l'intermédiaire d'une procédure TESTE_ET_ENTRE
qui vérifie que le prochain token est celui attendu et qui met à
jour la table des symboles :
procedure TESTE_ET_ENTRE (T:TOKENS ; C:CLASSES) ;
begin
if TOKEN = T then
begin
ENTRERSYM (C) ;
NEXT_TOKEN
end
else ERREUR
end ;
Cette fonction TESTE_ET_ENTRE
est appelée lors de la
déclaration de symboles ; c'est-à-dire dans les fonctions
CONSTS
,
VARS
et
PROGRAM
, par exemple :
procedure PROGRAM ;
begin
TESTE (PROGRAM_TOKEN) ;
TESTE_ET_ENTRE (ID_TOKEN, PROGRAMME) ;
TEST (PT_VIRG_TOKEN) ;
BLOCK ;
if TOKEN <> POINT_TOKEN then ERREUR
end ;
3.3 Consultation de la table des symboles
De manière symétrique, une procédure
TESTE_ET_CHERCHE
est une
évolution de la procédure TESTE
qui de plus recherche le
symbole SYM
dans la table des symboles par un appel à
CHERCHESYM
, l'index du symbole recherché dans la table des
symboles est retourné par la variable
PLACESYM
:
var PLACESYM : TABLEINDEX ;
procedure TESTE_ET_CHERCHE (T:TOKENS; PERMIS:CLASSET) ;
begin
if TOKEN = T then begin
CHERCHERSYM (PLACESYM, PERMIS) ;
NEXT_TOKEN ;
else ERREUR ;
end ;
Cette fonction est appelée lors de l'utilisation d'identificateur,
c'est-à-dire par FACT
,
AFFEC
, et
LIRE
. On a par exemple :
procedure AFFEC ;
begin
TESTE_ET_CHERCHE (ID_TOKEN, VARIABLE) ;
TESTE (AFFEC_TOKEN) ;
EXPR ;
end ;
procedure FACT ;
begin
if TOKEN = ID_TOKEN
then TESTE_ET_CHERCHE (ID_TOKEN, [CONSTANTE, VARIABLE])
else if TOKEN = NUM_TOKEN
then NEXT_TOKEN
else begin
TESTE (PAR_OUV_TOKEN) ;
EXPR ;
TESTE (PAR_FER_TOKEN) ;
end
end ;
3.4 Travaux dirigés et travaux pratiques
-
Les versions proposées de
ENTRERSYM
et CHERCHERSYM
ne tiennent pas
compte des doubles déclarations. Comment prendre ces déclarations
fautives en compte ?
Serait-il imaginable d'accepter des déclarations telles que :
var A, B, A ;
et telles que
const A = 3 ;
var A ;
- La recherche de symboles est telle que deux symboles de classes
différentes ne peuvent avoir le même nom. Cette règle est-elle
nécessaire ?
- Écrire l'analyseur sémantique de PP1. On testera que les
identificateurs ne sont pas déclarés plusieurs fois.
-
Il est intéressant de proposer à l'utilisateur la production d'une
table des symboles. Cette option peut être activée par le passage
d'un paramètre au compilateur, par exemple
pp1 -tablesymb
ou par l'utilisation d'une directive de compilation incluse dans un
commentaire :
(*$T+ *)
pour l'activation de la production de la table des symboles, et
(*$T- *)
pour la désactivation. Quelles sont les avantages de l'une et
l'autre des méthodes. Sont-elles incompatibles ? Proposer une
implantation.
- Il est possible d'imaginer que les identificateurs non déclarés
doivent être traités comme des variables (déclaration implicite
; c'est le cas en Fortran) et mis dans la table des symboles au moment
de leur première référence. Est-ce une bonne idée ? Comment
l'implémenter ?
- La table des symboles proposée en exemple est une simple table. Il
est possible d'implanter cette table par des arbres binaires de
recherches ou d'aider la recherche par une table de hashage. Est-ce
une bonne idée ? Quel problème pourrait survenir ?