Μεταγλωττιστές Εργαστήριο 7 Συντακτικός Αναλυτής Κατασκευή Πίνακα Συμβόλων (ΠΣ) Διδάσκοντες: Δρ. Γεώργιος Δημητρίου Δρ. Άχμεντ Μάχντι 2016-2017
Πίνακας Συμβόλων Συγκεντρώνει πληροφορίες για τα ονόματα (identifiers) που εμφανίζονται στο αρχικό πρόγραμμα Ονόματα είναι Πρόγραμμα μεταβλητές Υποπρογράμματα (διαδικασίες, συναρτήσεις,..) Παράμετροι των υποπρογραμμάτων Ετικέτες εντολών Σταθερές Τύποι δεδομένων
Πίνακας Συμβόλων Η πληροφορία που αποθηκεύεται στον πίνακα συμβόλων χρησιμοποιείται κατά τη σημασιολογική ανάλυση (έλεγχος τύπων) και κατά την παραγωγή κώδικα (πχ. πόσο χώρο στη μνήμη απαιτούν). πχ. int x; float y; Αποθήκευση στον πίνακα συμβόλων ότι ο x είναι ακέραιου τύπου. Ανάκληση από τον πίνακα συμβόλων κάθε φορά που απαιτείται το x είναι ακέραιος (πχ κατά την ανάθεση τιμής στη μεταβλητή x).
Πότε κατασκευάζεται ο Πίνακας Συμβόλων; Ο ΠΣ κατασκευάζεται κατά την φάση της ανάλυσης, δηλαδή κατά την λεκτική, συντακτική ή/και σημασιολογική ανάλυση. Είναι δυνατό κατά τη λεκτική ανάλυση να δημιουργηθεί μια νέα θέση στο πίνακα που να περιέχει αρχικές πληροφορίες για το σύμβολο. Η κατασκευή του πίνακα λαμβάνει χώρα στη φάση της συντακτικής ανάλυση, καθώς υπάρχει εκεί διαθέσιμη πληροφορία σχετική με το συγκεκριμένο όνομα.
Περιεχόμενα Πίνακα Συμβόλων Μεταβλητές Εμβέλεια Ορατότητα Διάρκεια Ζωής Τύποι Θέση (διεύθυνση μνήμης, καταχωρητής, ) Τρόπος περάσματος παραμέτρων υποπρογράμματος Ονόματα διαδικασιών/συναρτήσεων Αριθμός παραμέτρων υποπρογράμματος Τύποι παραμέτρων υποπρογράμματος Τύπος αποτελέσματος συνάρτησης
Εμβέλεια Εμβέλεια: δομική μονάδα προγράμματος η οποία περιέχει δηλώσεις μιας ή περισσοτέρων μεταβλητών. Δομική μονάδα προγράμματος: το ίδιο το πρόγραμμα, συναρτήσεις, διαδικασίες, αρχεία (C). Δυναμική εμβέλεια (dynamic scope): όταν η απόφαση για το σε ποια μεταβλητή αναφέρεται ένα όνομα βασίζεται σε πληροφορία που είναι διαθέσιμη μόνο κατά την εκτέλεση. Στατική εμβέλεια (static scope): όταν η παραπάνω απόφαση μπορεί να γίνει κατά την μεταγλώττιση.
Ορατότητα και Εμβέλειες Περισσότερες γλώσσες επιτρέπουν φωλιασμένες δομικές μονάδες (block structure). Φωλιασμένες εμβέλειες ορίζουν ένα δένδρο. Η ορατότητα μιας μεταβλητής είναι η περιοχή στην οποία το όνομα της μεταβλητής αναφέρεται στη συγκεκριμένη μεταβλητή.
Ορατότητα και Εμβέλειες Η μεταβλητή είναι ορατή σε μια εμβέλεια s που δηλώνεται από το σημείο δήλωσης και μετά, καθώς και σε κάθε φωλιασμένη εμβέλεια s n αν: η s n βρίσκεται μετά τη δήλωση της μεταβλητής στην s το όνομα της μεταβλητής δεν δηλώνεται ξανά στην s n ή σε κάποια φωλιασμένη εμβέλεια s n της s που περιέχει την s n. Πολλές φορές οι όροι εμβέλεια (scope) και ορατότητα (visibility) χρησιμοποιούνται χωρίς διαφορά.
program p; var x,y,z : int; function f; var x : real; begin... end Παράδειγμα p procedure g; var x,w: real; function h; var x,z: real; f g h begin... end begin... end
Μερικά Είδη Μεταβλητών Καθολικές Μεταβλητές: μέγιστη δυνατή εμβέλεια. Μεταβλητές Αρχείου: εμβέλεια στο αρχείο που εμφανίζονται. Μεταβλητές Στοίβας (stack): η εμβέλεια και διάρκεια ζωής είναι ίδια με τη συγκεκριμένη δομική μονάδα που τις περιέχει. Στατικές μεταβλητές (static): διατηρούν την τιμή τους μεταξύ διαδοχικών κλήσεων της δομικής μονάδας στην οποία ανήκουν. Νεότερες γλώσσες: public, private, protected
Άλλα χαρακτηριστικά των Μεταβλητών Γλώσσες μοναδικής ανάθεσης (single assignment): η ανάθεση τιμής σε κάθε μεταβλητή γίνεται μια και ΜΟΝΑΔΙΚΗ φορά. Γλώσσες καταστροφικής ανάθεσης (desctructive assignment): Η ανάθεση τιμών στη μεταβλητή μπορεί να γίνει περισσότερες από μια φορές, και κάθε φορά η προηγούμενη τιμή εξαφανίζεται. Διάρκεια ζωής: χρόνος από την στιγμή που δεσμεύεται μνήμη για μια μεταβλητή μέχρι να ελευθερωθεί.
Οργάνωση πίνακα συμβόλων Βασικές λειτουργίες Προσθήκη ονόματος Αναζήτηση ονόματος Διαγραφή ονόματος ή ομάδας ονομάτων Κόστος προσθήκης ή αναζήτησης ανάλογα με την υλοποίηση Γραμμική λίστα O(n) Δυαδικό δέντρο αναζήτησης O(log n) Πίνακας κατακερματισμού O(n/k) η πιο συμφέρουσα υλοποίηση!!
Πίνακες Κατακερματισμού (Hash Tables) Η αποδοτικότερη των τριών δομών. Βασίζεται στην ύπαρξη δύο πινάκων: του πίνακα αποθήκευσης και του πίνακα κατακερματισμού. Πίνακας κατακερματισμού Αποτελείται από k θέσεις. Κάθε θέση έχει ένα δείκτη σε μια συνδεδεμένη λίστα. Η συνάρτηση κατακερματισμού (hash function) δίνει σε ποια θέση του πίνακα κατακερματισμού αντιστοιχεί ένα όνομα, και οδηγεί στην αντίστοιχη σειριακή (συνδεδεμένη) λίστα.
Επανάληψη: Κεντρική Ιδέα του hashing 1. Χρησιμοποιείται ένας πίνακας Α που αποτελείται από m δείκτες 2. Κάθε ένας από τους δείκτες του Α μπορεί να δείχνει σε μια συνδεδεμένη λίστα. Σε κάθε εγγραφή της λίστας αποθηκεύεται ένα στοιχείο του πίνακα συμβόλων 3. Το στοιχείο s αποθηκεύεται στη λίστα στη οποία δείχνει ο δείκτης Α, ο οποίος βρίσκεται στη θέση h(s). h είναι η συνάρτηση κατακερματισμού (hashing function) η οποία επιστρέφει έναν αριθμό από 0 έως m-1 4. Κάθε φορά που εξετάζεται ένα όνομα s, Πρώτα εφαρμόζεται πάνω του η συνάρτηση h, και στη συνέχεια εξετάζεται η θέση h(s). Αν ΔΕΝ δείχνει σε συνδεδεμένη λίστα, τότε δημιουργεί μια λίστα και το s γίνεται το πρώτο στοιχείο της. Διαφορετικά η λίστα διατρέχεται και εξετάζεται αν το s είναι στοιχείο της. Αν είναι στοιχείο της λίστας, σημαίνει ότι η λέξη έχει αναγνωριστεί στο παρελθόν Αν όχι, τότε προστίθεται στη λίστα ακόμα μια εγγραφή με το στοιχείο s
Συνάρτηση Καταακερματισμού Σημαντικό ρόλο για την επιτυχία της μεθόδου. Επιθυμητή η ομοιόμορφη κατανομή ονομάτων στη λίστα. Εύκολος υπολογισμός όταν το όνομα είναι ακολουθίες χαρακτήρων. Παράδειγμα Σ ASCII (name) = Άθροισμα ASCII χαρακτήρων ονόματος hash(name) = Σ ASCII mod k
Υλοποίηση με Πίνακα Κατακερματισμού 0 1 2... Οι εγγραφές με το ίδιο χρώμα ανήκουν στην ίδια εμβέλεια (scope) k-1
Πολλαπλές Εμβέλειες Ένας πίνακας αποθήκευσης για κάθε εμβέλεια. Οι πίνακες οργανώνονται σε δενδρική δομή που αντιστοιχεί στην δενδρική δομή των εμβελειών. Γενική αρχή αναζήτησης: ένα αναγνωριστικό (identifier) αφορά την αντίστοιχη μεταβλητή που δηλώνεται στη πιο "κοντινά" φωλιασμένη δομική μονάδα (most closely nested scope) Η αναζήτηση ξεκινά από τον "τρέχοντα" ΠΣ και κινείται προς την "ρίζα" του δένδρου. Η πρώτη καταχώριση της μεταβλητής είναι η ζητούμενη.
prog p; var x,y,z:int; Παράδειγμα fun f; var x : begin proc g; real;... end var x,w: real; z w fun h; x var x,z:real; z y x fun f x real prog p z y x int int int w x real real proc g begin...end begin...end z x real real fun h
Πολλαπλές Εμβέλειες με ένα μοναδικό πίνακα αποθήκευσης Ένας μοναδικός πίνακας για την αποθήκευση των στοιχείων και μια συνάρτηση κατακερματισμού. Στοίβα εμβελειών: πληροφορία όσον αφορά τις φωλιασμένες εμβέλειες και το σημείο του πίνακα από όπου ξεκινά δήλωση των αντίστοιχων μεταβλητών. Λειτουργία όπως πίνακες κατακερματισμού, με την διαφορά ότι τα νέα στοιχεία τοποθετούνται στην αρχή της συνδεδεμένης λίστας.
Οργάνωση Πίνακα συμβόλων Πολλαπλών Εμβελειών 0 1 2... k-2 k-1 a6 a5 b3 a4 a3 a2 c3 b2 b1 a1 c2 c1 ε n... ε2 ε1
Οργάνωση του Πίνακα Συμβόλων Βασικές Λειτουργίες Insert Εισαγωγή ενός νέου συμβόλου στον πίνακα lookup Αναζήτηση ενός συμβόλου στον πίνακα hide Απενεργοποίηση (όχι διαγραφή) όλων των συμβόλων ενός επιπέδου εμβέλειας
Εισαγωγή - Αναζήτηση Κάθε φορά που αναγνωρίζεται ένα όνομα δημιουργείται μία νέα εγγραφή για αυτό στον πίνακα συμβόλων, εφόσον δεν υπάρχει ήδη κατά τη δήλωση μιας νέας μεταβλητής ή συνάρτησης Αναζήτηση ενός ονόματος στο τρέχον επίπεδο εμβέλειας ή σε περιέχουσα εμβέλεια με βάση τη σημασιολογία της γλώσσας κατά τον ορισμό ενός συμβόλου κατά τη χρήση ενός συμβόλου Hide Απενεργοποίηση των μεταβλητών κάποιου επιπέδου εμβέλειας (συνήθως του τρέχοντος) κατά την έξοδο από κάποιο block ή συνάρτηση
Λειτουργίες ΠΣ - Παράδειγμα input(x); g = 12.4; print(typeof(x)); function foo(x, y) { print(x + y); local p = y; ::print(p); function h(a) { return a + x + y; } y = h(::x); } lookup(input), lookup(x), ins(x) lookup(g), ins(g) lookup(print), lookup(typeof), lookup(x) lookup(foo), ins(foo), lookup(x), ins(x), lookup(y), ins(y) lookup(print), lookup(x), lookup(y) lookup(p) ins(p) lookup(y) lookup(::print), lookup(p) lookup(h) ins(h) lookup(a) ins(a) lookup(a), lookup(x), error hide(a) lookup(y), lookup(h), lookup(::x) hide(foo::x, foo::y, foo::p, foo::h)
Υλοποίηση ΠΣ με Hash Table Ένας Hashtable Hashtable αποτελείται από buckets buckets. Κάθε σύμβολο κατανέμεται σε ένα από αυτά τα buckets buckets με βάση μια hash συνάρτηση. Σύμβολα α που τυχαίνει να πέσουν στο ίδιο bucket δημιουργούν μια linked list. Επιπλέον είναι χρήσιμο να υπάρχει και ένα scope link που δημιουργεί μία λίστα συνδέοντας όλα τα σύμβολα που ανήκουν στο ίδιο scope. Αυτή η λίστα μπορεί να είναι μία δομή πάνω στην ήδη υπάρχουσα ή και ξεχωριστή (απλά πρέπει να ανανεώνεται παράλληλα με την κύρια δομή)
Υλοποίηση ΠΣ με Hash Table 0 0 1 2 2 1 2 0 1 Λίστες Εμβέλειας... 2 0 Λίστες Συγκρούσεων 0 2 3 k-1 Οι εγγραφές με το ίδιο χρώμα ανήκουν στην ίδια εμβέλεια (scope)
Υλοποίηση ΠΣ με Hash Table Οι συναρτήσεις Insert και Lookup είναι όμοιες με αυτές που έχετε υλοποιήσει σε προηγούμενα μαθήματα για Hashtables Πρέπει όμως να γίνει και ο χειρισμός του scope link Επιπλέον, είναι πιθανό θα χρειαστείτε δύο εκδόσεις για τη lookup Αναζήτηση ενός συμβόλου σε οποιοδήποτε scope Αναζήτηση ενός συμβόλου σε συγκεκριμένο scope Η συνάρτηση Hide θα «ακολουθεί» το scope link για μία συγκεκριμένη εμβέλεια και θα ακυρώνει αυτά τα σύμβολα Ένας τρόπος χειρισμού των χώρων εμβέλειας (scopes) είναι να υπάρχει μία καθολική μεταβλητή που να δείχνει την εμβέλεια στην οποία βρισκόμαστε. Η μεταβλητή αυτή θα πρέπει να αυξάνεται ή να μειώνεται στα actions των κατάλληλων γραμματικών κανόνων Π.χ. block : { {++scope;} stmts } { Hide(scope--); }
Flex- Υλοποίηση ΠΣ - Παράδειγμα symtab.h void insert( char * name, int len, int type, int lineno ) { /* ADDED */ /*int len = strlen(name);*/ int h = hash(name); Node l = hashtable[h]; while ((l!= NULL) && (strcmp(name,l->st_name)!= 0)) l = l->next; if (l == NULL) /* variable not yet in table */ { l = (Node) malloc(sizeof(struct HashRec)); strncpy(l->st_name, name, len); /* ADDED */ l->st_type = type; l->lines = (RefList) malloc(sizeof(struct RefListRec)); l->lines->lineno = lineno; l->lines->next = NULL; l->next = hashtable[h]; hashtable[h] = l; } else /* found in table, so just add line number */ { RefList t = l->lines; while (t->next!= NULL) t = t->next; t->next = (RefList) malloc(sizeof(struct RefListRec)); t->next->lineno = lineno; t->next->next = NULL; } } #include "symtab.h" scanner.l {integer} { yylval.tokname = malloc(sizeof(yytext)); printf("yylval: %s\n",yylval.tokname); strncpy(yylval.tokname,yytext,yyleng); insert(yytext, yyleng, INT_TYPE, lineno); printf("yytext: %s\n",yytext); return INTEGER; } parser.y #include "symtab.h" decltype : INTEGER { $$ = INT_TYPE; } FLOAT { $$ = REAL_TYPE; }
Παράδειγμα Hashing // Hash string s // Hash value = (sn-1 + 16(sn-2 +.. + 16(s1+16s0))) // Return hash value (independent of table size) unsigned hash(char* s) { unsigned hval = 0; while (*s!= \0 ) { hval = (hval << 4) + *s; Hash Function s++; } return hval; } /* return value (address) of symbol if found or -1 if not found */ int lookup ( char * name ) { int h = hash(name); Node l = hashtable[h]; while ((l!= NULL) && (strcmp(name,l->st_name)!= 0)) l = l->next; if (l == NULL) return -1; else return l->st_value; } /* a linked list of references (line nos) for each variable */ typedef struct RefListRec { int lineno; // Χειρισμός της εμβέλειας unsigned int scope; struct RefListRec * next; int type; } * RefList; /* hash entry holds variable name and its reference list */ typedef struct HashRec { char st_name[maxtokenlen]; int st_size; RefList lines; int st_value; int st_type; struct HashRec * next; } * Node;