Γλώσσες Προγραμματισμού Μεταγλωττιστές Συντακτική Ανάλυση με το Εργαλείο BISON Πανεπιστήμιο Μακεδονίας Τμήμα Εφαρμοσμένης Πληροφορικής Ηλίας Σακελλαρίου
Δομή ▪Γεννήτριες Συντακτικών Αναλυτών ▪Bison/yacc ▪Δομή ενός Προγράμματος Bison ▪Δηλώσεις Bison ▪Σημασιολογικές τιμές και Κανόνες ▪Flex & Bison
Τι είναι το Bison; ▪Το bison αποτελεί μια είναι μια βελτίωση του εργαλείου yacc του Unix. Σε μεγάλο βαθμό συμβατό με το yacc. ▪Είναι μια γεννήτρια συντακτικών αναλυτών: που δέχεται μια γραμματική χωρίς συμφραζόμενα LALR(1) και παράγει έναν συντακτικό αναλυτή σε C/C++ η παραγώμενη συνάρτηση yyparse() αναγνωρίζει τις συμβολοσειρές εισόδου, κατασκευάζει το συντακτικό δέντρο και εκτελεί τις ενέργειες που περιγράφονται στο πρόγραμμα. συνεργάζεται στενά με το εργαλείο FLEX
Δομή Προγράμματος Ένα πρόγραμμα bison χωρίζεται σε τρία μέρη: MΕΡΟΣ Α (Δηλώσεις τελεστών, συνόλου σημασιολογικών τιμών, κώδικας C, κλπ) % MΕΡΟΣ Β (κανόνες γραμματικής) % MΕΡΟΣ Γ (Κώδικας C, Συνάρτηση main(), κλπ).
Παράδειγμα ορισμού ΣΑ σε Bison %token TK_num %left '+' %left '*' % program :expression ; expression : term | expression ‘+’ term ; term : factor | term ‘*’ factor ; factor : ‘(‘ expression ‘)’ | TΚ_num ; E -> T | E + T T -> F | T * F F -> (E) | num
Μέρος Α ▪Σχόλια /*... */ ▪Κώδικας C που περικλείεται στα %{...%} Δηλώσεις μακροεντολών, τύπων δεδομένων και μεταλητών ▪Δηλώσεις λεκτικών μονάδων (tokens) ▪Δηλώσεις Τελεστών (operators) ▪Δήλωση Συνόλου Σημασιολογικών Τιμών ▪Δήλωση Τύπου Σημασιολογικής Τιμής ▪Άλλες δηλώσεις
Δηλώσεις Λεκτικών Μονάδων ▪Γίνονται με το πρόθεμα %token. %token TK_num %token T_lpar "(" %token T_implies "=>" Στη δεύτερη περίπτωση το T_lpar και "(" μπορούν να χρησιμοποιηθούν χωρίς διαφορά. ▪Όταν υπάρχει συνεργασία μεταξύ των flex και bison τότε το bison καλείται με -f ώστε να παραχθεί το αρχείο name.tab.h το οποίο περιέχει τις δηλώσεις των λεκτικών μονάδων (#define T_implies 300).
Δηλώσεις Τελεστών ▪Δηλώσεις τελεστών της γλώσσας και της προτεραιότητας και προσεταιριστικότητας τους. %nonassoc ‘=‘ ‘ ’ %left ‘+’ ‘-’ %left ‘*’ ‘/’ TK_div, TK_mod ▪ Η προτεραιότητα δίνεται από την σειρά με την οποία δηλώνονται οι τελεστές στο πρόγραμμα. Οι τελεστές που βρίσκονται "ψηλότερα" στη λίστα έχουν μικρότερη προτεραιότητα. Οι τελεστές στην ίδια σειρά έχουν την ίδια προτεραιότητα.
Προσεταιριστικότητα Τελεστών ▪Τα %left - %right δηλώνουν τις προσεταρι-στικότητες και προτεραιότητες των τελεστών που τα ακολουθούν left: αριστερά προσεταιριστικός τελεστής right: δεξιά προσεταιριστικός τελεστής ▪Το %nonassoc χρησιμοποιείται για τελεστές που δεν μπορούν να συνδυαστούν μεταξύ τους, π.χ. το ‘=‘ (μη-προσεταιριστικός τελεστής) ▪Ότι ορίζεται ως τελεστής είναι εξ ορισμού και λεκτική μονάδα. Δεν απαιτείται δήλωση %token.
Παράδειγμα Ορισμού Τελεστών ▪Για παράδειγμα έστω οι δηλώσεις: %nonassoc '=' %left '+' '-' %left '*' '/' Τα ‘+’ και ‘-’ έχουν μικρότερη προτεραιότητα από τα ‘*’ και ‘/’ Η έκφραση a+b+c υπολογίζεται ως (a+b)+c και ομοίως η α*β*c Ο τελεστής '=' είναι μη-προσεταιριστικός.
Σημασιολογικές Τιμές ▪Δήλωση του συνόλου των σημασιολογι- κών τιμών και του τύπου κάθε συμβόλου. Τύπος YYSTYPE Συνήθως ένωση (union) %union{ int i; double f; char str[80];} ▪Δήλωση του τύπου του κάθε συμβόλου. %token T_id %token T_int_const %type expression
Μέρος Β - Κανόνες Παραγωγής Γραμματικής ▪Η περιγραφή της γραμματικής της γλώσσας γίνεται με κανόνες παραγωγής διατυπωμένους σε BNF γενική μορφή: αριστερό_μέλος: δεξιό_μέλος_1 | δεξιό_μέλος_2... | δεξιό_μέλος_ν ; Το αριστερό μέλος είναι ένα μη τερματικό σύμβολο Το δεξιό μέλος μπορεί να περιέχει μηδέν ή περισσότερα τερματικά, μη τερματικά σύμβολα ή σημασιολογικές ρουτίνες.
Κανόνες Παραγωγής Γραμματικής ▪Παράδειγματα: arithmetic_expr: NUM |arithmetic_expr‘+’arithmetic_expr |arithmetic_expr‘-’arithmetic_expr |arithmetic_expr‘*’arithmetic_expr |arithmetic_expr‘/’arithmetic_expr |‘(‘arithmetic_expr ‘)’ ; id_list: /*empty*/ | id | id TK_COMMA id_list;
Απλή γλώσσα αριθμητικών εκφράσεων σε Bison %token TK_num %left '+' %left '*' % program :expression ; expression : term | expression ‘+’ term ; term : factor | term ‘*’ factor ; factor : ‘(‘ expression ‘)’ | TΚ_num ; E -> T | E + T T -> F | T * F F -> (E) | num
Επίλυση Συγκρούσεων ▪Σε περίπτωση συγκρούσεων το εργαλείο Bison χρησιμοποιεί τους ακόλουθους κανόνες: Σε σύγκρουση ολίσθησης-ελάττωσης (shift-reduce) επιλέγεται πάντα η ολίσθηση. Σε σύγκρουση ελλάτωσης-ελάττωσης (reduce-reduce), το bison κάνει ελάττωση με τον κανόνα που έχει περιγραφεί πρώτος στο αρχείο της γραμματικής. ▪Αν χρησιμοποιήσουμε την επιλογή bison –v παράγεται ένα αρχείο (.output) με περιγραφές των conflicts.
Μεταβλητές – Συναρτήσεις του Bison (i) ▪int yyparse( ) Η κύρια συνάρτηση του συντακτικού αναλυτή, που παράγεται βάσει της περιγραφής. Επιστρέφει 0 αν η συντακτική ανάλυση τελειώσει χωρίς σφάλματα, 1 αν βρέθηκαν συντακτικά σφάλματα. ▪int yylex( ) Η κύρια συνάρτηση του λεκτικού αναλυτή που πρέπει να δίνεται από το χρήστη ή παράγεται από τον (f)lex. Καλείται από την yyparse όταν πρέπει να διαβαστεί μια νέα λεκτική μονάδα. Επιστρέφει έναν ακέραιο αριθμό, που αντιστοιχεί σε ένα τερματικό σύμβολο ή 0 όταν αναγνωσθεί το EOF.
Μεταβλητές – Συναρτήσεις του Bison (ii) ▪void yyerror(const char * s) Η συνάρτηση χειρισμού σφαλμάτων που πρέπει να δίνεται από το χρήστη. Η συμβολοσειρά s περιέχει το μήνυμα σφάλματος που παρουσιάστηκε. ▪Μεταβλητές του bison YYSTYPE yylval: μέσω της συγκεκριμένης μεταβλητής γίνεται η επικοινωνία με τον ΛΑ. Η μεταβλητή περιέχει την σημασιολογική τιμή της λεκτικής μονάδας. int yydebug: μη-μηδενική τιμή περιέχει πληροφορίες σχετικά με την πρόοδο της ΣΑ.
Παραδείγματα ▪Αρχείο simple.l simple.y
Σημασιολογικές Τιμές και Κανόνες ▪Εκτός από την απλή παραγωγή του δένδρου, ο ΣΑ θα πρέπει να παράγει και κάτι περισσότερο χρήσιμο. ▪Αυτό το επιτυγχάνουν οι σημασιολογικές ρουτίνες που μπορούν να εμφανίζονται σε οποιοδήποτε σημείο του δεξιού μέλους του κανόνα. ▪Οι σημασιολογικοί κανόνες είναι συχνά εκφράσεις σημασιολογικών τιμών.
Σημασιολογικές Τιμές (i) ▪Σε κάθε σύμβολο της γραμματικής μπορεί να αποδοθεί μια σημασιολογική τιμή. Επιτρέπει την υλοποίηση κατηγορικών γραμματικών (attributed grammars) Οι τιμές είναι προσπελάσιμες μέσα στις σημασιολογικές ρουτίνες. ▪Ο τύπος των σημασιολογικών τιμών καθορίζεται από το YYSTYPE, που είναι εξ' ορισμού int. Σε περίπτωση που θέλουμε να ορίσουμε άλλη τιμή: #define YYSTYPE double
Σημασιολογικές Τιμές (ii) ▪Οι σημασιολογικές τιμές μπορούν να είναι διαφορετικού τύπου για διαφορετικά σύμβολα. Οι τύποι αυτοί δε δηλώνονται με ορισμό του YYSTYPE, αλλά στο πρώτο μέρος ως εξής: %union{ double number; char* string; } ▪Ο τύπος των συμβόλων καθορίζεται επίσης στο πρώτο μέρος της περιγραφής με δηλώσεις όπως: %token TK_NUMCONST %token TK_STRCONST %type expression
Σημασιολογικές τιμές και Κανόνες (i) ▪Ο υπολογισμός των σημασιολογικών τιμών των συμβόλων γίνεται με την χρήση ρουτινών. ▪Με $$ συμβολίζεται η σημασιολογική τιμή του αριστερού μέλους ενός κανόνα. ▪Τα σύμβολα $n, όπου n>0, παριστάνουν τη σημασιολογική τιμή του n-οστού συμβόλου του δεξιού μέλους του κανόνα. Το πρώτο σύμβολο αντιστοιχεί στο $1, κ.ο.κ. expr : expr ‘+’ term{ $$ = $1 + $3;}
Σημασιολογικές τιμές και Κανόνες (ii) ▪Οι σημασιολογικές τιμές των τερματικών συμβόλων συνήθως καθορίζονται από το λεκτικό αναλυτή και τοποθετούνται στη μεταβλητή yylval. Στην περιγραφή του λεκτικού αναλυτή πρέπει σε κάθε αναγνωρίσιμο στοιχείο να αποθηκεύσουμε την τιμή του, για παράδειγμα: {id} {yylval.string=strdup(yytext); return(TK_ID);}
Παράδειγμα ▪Έστω ότι θέλω να υπολογίζω την τιμή απλών αριθμητικών εκφράσεων. ▪Έστω η έκφραση , που βάσει της (γνωστής) γραμματικής αντιστοιχεί στο δένδρο expr term + + expr factor expr term factor
Προσθήκη Σημασιολογικών Κανόνων στην Απλή Γραμμτική %{typedef int YYSTYPE; %} program :expression {printf(“Value: %d\n”, $1};) ; expression : term{$$ = $1;} | expression ‘+’ term {$$ = $1 + $3;} ; term: factor {$$ = $1;} | term ‘*’ factor {$$ = $1 * $3;} ; factor: ‘(‘ expression ‘)’{$$ = $2;} | TK_num {$$ = $1;} ; Flex file (part): {D}+ {yylval = atoi(yytext); return T_num; }
Flex και Bison ▪Τα εργαλεία Flex και Bison έχουν κατασκευαστεί με στόχο να συνεργάζονται. ▪Στο αρχείο flex: Αφαιρούμε τις δηλώσεις των λεκτικών μονάδων (tokens) και στη θέση τους προσθέτουμε το αρχείο που παράγεται από τον bison -d #include.tab.h αφαιρούμε την main() ▪Στο αρχείο Bison προστίθεται στο Γ μέρος το: #include.lex.c ▪Η συνάρτηση yylex() καλείται μέσα από την yyparse().
Διαδικασία Παραγωγής Αναλυτή Bison compiler Flex compiler C compiler Flex source program expr.l Bison source program expr.y expr expr.tab.h expr.lex.c expr.tab.c expr expr.tab.c expr.lex.c Input stream Sequence of grammar rules
Εντολές Παραγωγής ΣΑ ▪Α' Σενάριο (χωρίς include στο Γ μέρος του bison αρχείου) bison -dv expr.y flex -s -oexpr.lex.c expr.l gcc -o expr expr.lex.c expr.tab.c -lfl ▪Β΄Σενάριο bison -dv expr.y flex -s -oexpr.lex.c expr.l gcc -o expr expr.tab.c -lfl
Ανάνηψη από Συντακτικά Λάθη ▪Ο bison όταν συναντήσει στοιχεία εισόδου που δεν μπορεί να τα αναγνωρίσει (συντακτικά λάθη) καλεί την συνάρτηση yyerror και σταματάει. Είναι επιθυμητό να συνεχίσουμε ώστε να εντοπίσουμε και τυχόν επόμενα λάθη. ▪Προσφέρεται το σύμβολο error που όταν συναντήσει λάθος συνεχίζει την κατανάλωση στοιχείων της στοίβας και της εισόδου ωσότου αναγνωρίσει τουλάχιστον τρία στοιχεία που ξεκινούν έναν άλλο κανόνα.
Ανάνηψη από Συντακτικά Λάθη (ΙΙ) ▪Μπορούμε να χρησιμοποιήσουμε το error σε κανόνες που πιστέυουμε ότι είναι επιρεπείς σε λάθη και/ή να το συνδιάσουμε με άλλα σύμβολα stmt : expr “;” {$$ = $1;} | error {yyerrok;} ▪Υπάρχουν τα macros yyerrok που αναγκάζει τον συντακτικό αναλυτή να συνεχίσει την ανάλυση και yyclearin που καθαρίζει το lookahead.
Δομή ▪Γεννήτριες Συντακτικών Αναλυτών ▪Bison/yacc ▪Δομή ενός Προγράμματος Bison ▪Δηλώσεις Bison ▪Σημασιολογικές τιμές και Κανόνες ▪Flex & Bison