Table des symboles Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI) www.zegour.uuuq.com email: d_zegour@esi.dz
Table des symboles Introduction Symboles Portée des symboles Types Univers
Rôles de la table des symboles 1. Elle range tous les noms déclarés avec leurs attributs type valeur (pour les constantes) adresse (pour les variables locales et les paramètres de procédures) paramètre (pour les procédures) ... 2. Elle est utilisée pour retrouver les attributs d’un nom Fonction : nom (type, value, adresse, ...) Contenu de la table des symboles Les nœuds de symbole : information sur les noms déclarés Les nœuds de Structure : information sur les types déclarés C’est plus commode de l’implémenter par une structure de données dynamique : - liste linéaire chaînée - arbre binaire - Hash-code
Table des symboles comme une liste Soient les déclarations suivantes const int n = 10; class T { ... } int a, b, c; void M () { ... } Nous obtenons la liste linéaire chaînée suivante Pour chaque nom déclaré il existe un nœud de symbole "n" Const "T" Type "a" Field "b" Field "c" Field "M" Meth + simple + l’ordre des déclarations est retenu (important si les adresses sont attribuées plus tard) - lent s’il existe beaucoup de déclarations Interface de base public class Tab { public static Symbol Insert (Symbol.Kinds kind, string name, ...); public static Symbol Find (string name); }
Table des symboles comme un arbre binaire Déclarations const int n = 10; class T { ... } int a, b, c; void M () { ... } Arbre binaire résultant "M" Meth + rapide - l’arbre peut dégénérer sauf s’il est équilibré - Consommation de la mémoire - l’ordre des déclarations est perdu Utile seulement s’il y a beaucoup de déclarations "b" Field "T" Type "a" Field "c" Field "n" Const
Table des symboles comme une table hash-code Déclarations const int n = 10; class T { ... } int a, b, c; void M () { ... } Table hash-code résultante "M" Meth + rapide - plus compliqué qu’une liste - l’ordre des déclarations est perdu "b" Field "T" Type 1 "n" Const 2 Pour notre propos une liste est suffisante Chaque étendue (portée) est une liste de ses propres déclarations Une étendue a à peine plus de 10 noms "c" Field "a" Field 3
Table des symboles Introduction Symboles Portée des symboles Types Univers
Les nœuds de symbole Sortes courants de symboles Chaque nœud déclaré est rangé dans un nœud de symbole Sortes courants de symboles public enum Kinds { Const, Global, Field, Arg, Local, Type, Meth, Prog } constantes variables globales champs paramètres variables locales types méthodes programme Quelles sont les informations désirées sur les objets? pour tous les symboles nom, type structuré, sorte de symbole, .. pour les constantes valeur pour les paramètres adresse (= ordre de déclaration) pour les variables locales adresse (= ordre de déclaration) pour les méthodes nombre d’arguments et variables locales, symboles locaux (paramètres + variables locales) pour le programme symboles globaux(= locaux au programme) pour les variables globales, champs, types, ….
La classe des symboles Exemple Défini plus loin class Symbol { public enum Kinds { Const, Global, Field, Arg, Local, Type, Meth, Prog } Kinds kind; string name; Struct type; Symbol next; int val; // Const: value int adr; // Arg, Local: address int nArgs; // Meth: number of arguments int nLocs; // Meth: number of local variables Symbol locals; // Meth: parameters & local variables; Prog: symbol table of program } Défini plus loin Const "n" 10 - const int n = 10; class T { ... } int a, b; void M (int x, int y) char ch; { ... } kind name next val adr nArgs nLocs locals Type "T" Global "a" "b" Meth "M" 2 1 Arg "x" Exemple Local "ch" "y"
Introduction des noms dans la table des symboles La méthode suivante est appelée quand un nom est déclaré Symbol sym = Tab.Insert(kind, name, type); Crée un nouveau nœud d’objet avec kind, name, type Vérifie si name est déjà déclaré (si c’est le cas => message d’erreur) Attribuer des adresses successives aux variables et champs Entrer le niveau de déclaration pour les variables (Global, Local,…) Ajouter le nouveau nœud en fin de la liste linéaire des symboles Retourner le nouveau nœud à l’appelant Exemple d’appel à la méthode Insert() VarDecl<Symbol.Kinds kind> = Type<type> ident (. Tab.insert(kind, name, type); .) { ";" ident (. Tab.insert(kind, name, type); .) }.
Les symboles (noms) prédéfinis Exemples de noms prédéfinis ? Types standards : int, char Constants standards : null Fonctions Standards : ord(ch), chr(i), len(arr) On peut aussi ranger les noms prédéfinis dans la table des symboles ("Univers") kind name val adr nArgs nLocs locals Type "int" - "char" Const "null" Meth "ord" 1 "chr" "len" Arg "ch" "i" "arr"
Noms spéciaux comme des mot-clés int et char peuvent aussi être implémentées comme des mot-clés. Exige un traitement spécial dans la grammaire Type<Struct type> = ident (. Symbol sym = Tab.Find(token.str); type = sym.type; .) | "int" (. type = Tab.intType; .) | "char" (. type = Tab.charType; .) . C’est plus simple de les avoir pré déclarés dans la table des symboles Type<Struct type> = ident (. Symbol sym = Tab.Find(token.str); type = sym.type; .) + traitement uniforme pour les noms pré déclarés et les noms utilisateur - on peut re déclarer "int" comme un type utilisateur
Table des symboles Introduction Symboles Portée des symboles Types Univers
Portée = Domaine où l’objet est valide(connu) Il y a des portées séparées (listes d’objets) pour L’ "univers" contient les noms pré définis ( on ajoute le symbole ‘P’ pour programme) Le programme contient noms globaux (= constantes, variables globales, classes, méthodes) Chaque méthode contient les noms locaux (= arguments et variables locales) Chaque classe contient des champs Exemple class P int a, b; { void M (int x) int b, c; ... } Portée M (tous les noms déclarés dans M) Portée P (tous les noms déclarés dans P) univers (noms pré déclarés) La recherche d’un nom commence toujours dans topScope Si non trouvé, la recherche continue dans la prochaine portée englobante (outer) Exemple: rechercher b, a et int "x" locals "b" "c" "a" "M" outer "int" "char" "P" topScope
Les nœuds d’une portée Méthode pour l’ouverture d’une portée class Scope { Scope outer; // vers la portée englobante Symbol locals; // vers les symboles de cette portée int nArgs; // nombre of arguments de cette portée (pour l’attribution des adresses) int nLocs; // nombre de variables locales dans cette portée (pour l’attribution des adresses) } Méthode pour l’ouverture d’une portée static void OpenScope () { // in class Tab Scope s = new Scope(); s.nArgs = 0; s.nLocs = 0; s.outer = topScope; topScope = s; } Appelée au début d’une méthode ou une classe Relie la nouvelle portée avec celles existantes La nouvelle portée devient topScope Tab.Insert() crée toujours des symboles dans topScope Méthode pour la fermeture d’une portée static void CloseScope () { // in class Tab topScope = topScope.outer; } Appelée à la fin d’une méthode ou une classe La prochaine portée englobante(outer) devient topScope
Introduction des noms dans une portée Les noms sont toujours introduit dans topScope class Tab { static Scope topScope; // pointer to current scope ... static Symbol Insert (Symbol.Kinds kind, string name, Struct type) { //--- create symbol node Symbol sym = new Symbol(name, kind, type); if (kind == Symbol.Kinds.Arg) sym.adr = topScope.nArgs++; else if (kind == Symbol.Kinds.Local) sym.adr = topScope.nLocs++; //--- insert symbol node Symbol cur = topScope.locals, last = null; while (cur != null) { if (cur.name == name) Error(name + " declared twice"); last = cur; cur = cur.next; } if (last == null) topScope.locals = sym; else last.next = sym; return sym; Struct : Défini plus loin
Ouverture et fermeture d’une portée MethodDecl (. Struct type; .) = Type<type> ident (. curMethod = Tab.insert(Symbol.Kinds.Meth, token.str, type); Tab.OpenScope(); .) ... "{" "}" (. curMethod.nArgs = topScope.nArgs; curMethod.nLocs = topScope.nLocs; curMethod.locals = Tab.topScope.locals; Tab.CloseScope(); . variable globale Remarques Le nom de la méthode est introduit dans la portée englobant la méthode Avant de fermer une portée, les objets locaux sont attribués au champ locals de la méthode en cours Même principe pour les classes(les portées sont aussi ouvertes et fermés pour les classes)
Exemple class P "int" "char" ... "P" Tab.OpenScope(); topScope
Exemple "int" "char" ... class P int a, b; { "P" Tab.Insert(..., "a", ...); Tab.Insert(..., "b", ...); "a" "b" topScope
Exemple "int" "char" ... class P int a, b; { void M () "P" Tab.Insert(..., "M", ...); Tab.OpenScope(); "a" "b" "M" topScope
Exemple "int" "char" ... class P int a, b; { void M () int x, y; "P" Tab.Insert(..., "x", ...); Tab.Insert(..., "y", ...); "x" "y" topScope
Exemple "int" "char" ... class P int a, b; { void M () int x, y; ... } meth.locals = Tab.topScope.locals; Tab.CloseScope(); topScope "x" "y"
Exemple "int" "char" ... class P int a, b; { void M () int x, y; ... } prog.locals = Tab.topScope.locals; Tab.CloseScope(); topScope "a" "b" "M" "x" "y"
Recherche des noms dans la table des symboles La méthode suivante est appelée quand un nom est utilisé Symbol sym = Tab.Find(name); static Symbol Find (string name) { for (Scope s = topScope; s != null; s = s.outer) for (Symbol sym = s.locals; sym != null; sym = sym.next) if (sym.name == name) return sym; Parser.Error(name + " is undeclared"); return noSym; } La recherche commence dans topScope Si non trouvé, la recherche continue dans la portée englobante (pointeur outer ) x b c locals a m outer int char topScope Si un nom est non trouvé la méthode retourne un symbole spécial : noSym Symbole pré déclaré Mieux que null, car il évite les effets de bord (exceptions) kind name type val adr nArgs nLocs locals Const "noSymbol" noSym noType
Table des symboles Introduction Symboles Portée des symboles Types Univers
Les types Chaque objet a un type avec les propriétés suivantes taille structure (champs pour les classes, type des éléments pour les tableaux, ...) Sortes de types ? types primitives (int, char) tableaux classes Les types sont représentés par des nœuds de structure class Struct { public enum Kinds { None, Int, Char, Arr, Class } Kinds kind; Struct elemType; // Arr: element type Symbol fields; // Class: list of fields }
Nœuds de structure pour les types primitifs int a, b; char c; kind name type next val adr nArgs nVars locals Local "a" - Local "b" - 1 Local "c" - 2 Nœud d’objet Nœud de structure kind elemType fields Int - Char - Il existe un seul nœud de structure pour le type int dans toute la table des symboles. Tous les symboles de type int référencent celui-ci. Même chose pour le nœud de structure correspondant au type char.
Nœuds de structure pour les tableaux int[] a; int b; kind name type next val adr nArgs nVars locals Local "a" - Local "b" - 1 kind elemType fields Arr - Int - La longueur d’un tableau peut ne pas être connue au moment de la compilation. Elle sera déterminée à l’exécution.
Nœuds de structure pour les classes class C { int x; int y; int z; } C v; kind name type next val adr nArgs nVars locals Type "C" - Global "v" - Int - kind elemType fields Class - kind name type next val adr nArgs nVars locals Field "x" - Field "y" - Field "z" -
Table des symboles Introduction Symboles Portée des symboles Types Univers
Structure de l "univers" chrSym ordSym lenSym noSym intType charType kind name type val adr nArgs nLocs locals Type "int" - Type "char" - Const "null" - Meth "chr" - 1 Meth "ord" - 1 Meth "len" - 1 Const "noSymbol" - Arg "i" - Arg "ch" - Arg "arr" - kind elemType fields Int - Char - Class - Arr - None - intType charType nullType noType
Interface de la table des symboles class Tab { static Scope topScope; // current top scope static Struct intType; // predefined types static Struct charType; static Struct nullType; static Struct noType; static Symbol chrSym; // predefined symbols static Symbol ordSym; static Symbol lenSym; static Symbol noSym; static Symbol Insert (Symbol.Kinds kind, string name, Struct type) {...} static Symbol Find (string name) {...} static void OpenScope () {...} static void CloseScope () {...} static void Init () {...} // builds the universe and initializes Tab }