ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΕΛΟΠΟΝΝΗΣΟΥ ΤΜΗΜΑ ΕΠΙΣΤΗΜΗΣ ΚΑΙ ΤΕΧΝΟΛΟΓΙΑΣ ΥΠΟΛΟΓΙΣΤΩΝ Εµµανουήλ Ζαφείριος Μπόζης Α.Μ 2009018 pcst0918@uop.gr «Μεταγλωττιστής από minibasic σε Jackal 3.0» Εργασία στο µάθηµα «Προηγµένα Θέµατα Θεωρητικής Πληροφορικής» του Μεταπτυχιακού Προγράµµατος Σπουδών 1.Εισαγωγή Ο Jackal είναι ένας εκπαιδευτικός γενικού σκοπού επεξεργαστής τύπου RISC, ικανός να εξυπηρετήσει µια ευρεία γκάµα υπολογιστικών εφαρµογών. Ο επεξεργαστής αυτός παρέχει ένα πολύ λεπτό πυρήνα 16 δυνατών εντολών. Το µέγεθος του καθιστά δυνατό σε µια οµάδα φοιτητών να σχεδιάσουν και να υλοποιήσουν τον πυρήνα του επεξεργαστή σε µια πλατφόρµα FPGA κατά την διάρκεια ενός εξαµήνου. Για την αρχιτεκτονική αυτή υπάρχει πλήρες περιβάλλον µεταγλώττισης, συµβολοµετάφρασης και προσοµοίωσης, αναρτηµένο στο διαδίκτυο [jack]. Αντικείµενο αυτής της εργασίας είναι η ανάπτυξη και ο σχεδιασµός ενός µεταγλωττιστή από την γλώσσα minibasic σε κώδικα για την αρχιτεκτονική επεξεργαστή Jackal 3.0. Ο µεταγλωττιστής µπορεί να χρησιµοποιηθεί σε εκτελέσιµη µορφή από τη γραµµή εντολών του κατάλληλου τερµατικού σε Linux ή σε περιβάλλον Cygwin/Windows. Η ανάπτυξη του κώδικα έγινε στην γλώσσα προγραµµατισµού ANSI C. 2. ιαδικασία ανάπτυξης 2.1 Επέκταση της γραµµατικής Η υλοποίηση του λεκτικού και συντακτικού αναλυτή της minibasic έγινε µε τα ελεύθερα διαθέσιµα εργαλεία (GNU) flex και bison αντίστοιχα. Η χρήση τους συνδυάσθηκε µε τα αρχεία εισόδου lexer.l και parser.y ώστε να παραχθεί ο επιθυµητός λεκτικός και συντακτικός αναλυτής της γλώσσας. 1
Η γραµµατική της minibasic επεκτάθηκε ώστε να περιλαµβάνει ακέραια διαίρεση / και υπολογισµό ακέραιου υπολοίπου %, τους τελεστές σύγκρισης == και /=. Επίσης προστέθηκε η νέα εντολή plot, η οποία έχει σύνταξη plot ra, rb, rc όπου οι τιµές των καταχωρητών ra και rb καθορίζουν ένα σηµείο pixel στο επίπεδο της µορφής (x,y) µε την αρίθµηση να αρχίζει από το (0,0) το οποίο βρίσκεται στο άνω αριστερό άκρο του επιπέδου. Το pixel αυτό χρωµατίζεται από τον κωδικό χρώµατος που υπάρχει στο καταχωρητή rc και έχει τιµή 0 έως 255 (π.χ. 0 είναι το µαύρο NOIR και 255 το λευκό WHITE). Υποστηρίζει µνήµη απεικόνισης γραφικών [255 x 255] στην οποία αποθηκεύονται οι τιµές χρώµατος για τα εικονοστοιχεία. Η διεύθυνση ενός εικονοστοιχείου δίνεται από την σχέση : ra 512+rB. Η εκτέλεση της plot εκτός από την µετάφρασή της σε assembly για τον Jackal, γράφει στο αρχείο drawing.dat του τρέχοντος καταλόγου την συντεταγµένη του σηµείου και τον κωδικό χρώµατός του. Η plot µεταφράζεται στις παρακάτω εντολές του Jackal : sla rd ra 8 add rd rd rb paint rc rd Οι πρώτες δύο εντολές εκτελούν την πράξη ra 512+rB που είναι η τελική συντεταγµένη του σηµείου στην ra γραµµή και rb στήλη. Στην περιγραφή του ISA του Jackal υπάρχουν κάποιες κενές θέσεις (opcodes) για νέες εντολές του χρήστη που ονοµάζονται UDI (user-defined instructions). Στην θέση 1100 (12 στο δεκαδικό) που είναι ελεύθερη υλοποιείται η εντολή paint, η οποία τοποθετεί στην οθόνη ένα pixel χρώµατος rc στην συντεταγµένη rd. Η εκτεταµένη γραµµατική δίνεται σε µορφή BNF (Backus Naur Form) στο παρακάτω σχήµα : program : stmt_list prg = $1 stmt_list : /* nothing */ $$ = NULL stmt stmt_list $$ = ast_node(t_begin, $1, $2) stmt : "print" expr $$ = ast_node(t_print, $2, NULL) "let" T_var '=' expr $$ = ast_let($2, $4) "for" expr "do" stmt $$ = ast_node(t_for, $2, $4) "begin" stmt_list "end" $$ = $2 "if" expr "then" stmt $$ = ast_node(t_if, $2, $4) "plot" expr ',' expr ',' T_const $$ = ast_plot(t_plot, $6, $2, $4) expr : T_const $$ = ast_num($1) T_var $$ = ast_var($1) '(' expr ')' $$ = $2 expr '+' expr $$ = ast_node('+', $1, $3) expr '-' expr $$ = ast_node('-', $1, $3) expr '*' expr $$ = ast_node('*', $1, $3) expr '/' expr $$ = ast_node('/', $1, $3) expr '%' expr $$ = ast_node('%', $1, $3) expr "==" expr $$ = ast_node(t_equal, $1, $3) expr "/=" expr $$ = ast_node(t_nequal, $1, $3) %% Στο αρχείο lexer.l ορίστηκαν τα νέα tokens που προστέθηκαν plot / % == /= και το σηµείο,, για να υλοποιηθούν οι νέοι τελεστές και η εντολή plot. 2.2 Ενδιάµεση αναπαράσταση του µεταγλωττιστή Ως ενδιάµεση αναπαράσταση του µεταγλωττιστή είναι ένα αφηρηµένο συντακτικό δέντρο (AST) µε τις κατάλληλες βοηθητικές ρουτίνες για την διαχείρισή του. Ως ενδεικτική υλοποίηση του AST χρησιµοποιήθηκε ο σηµασιολογικός αναλυτής της minibasic στην ιστοσελίδα των µεταγλωττιστών του NTUA [minibasic]. Σε αυτόν προστέθηκε και µια βοηθητική ρουτίνα για 2
την υλοποίηση της εντολής plot. Ο κώδικας assembly παράγεται απευθείας από την διάσχιση της δοµής AST και γράφεται στο αρχείο εξόδου output.asm. Κατά την διάσχιση του δέντρου AST σε κάθε κόµβο που υπάρχει τελεστής αποθηκεύεται το αποτέλεσµα της πράξης στη θέση ast num και επιστρέφεται ο τρέχον αριθµός του καταχωρητή (register counter) όπου αποθηκεύεται το αποτέλεσµα (*rc) ο οποίος χρησιµοποιείται από τον ανώτερο κλάδο της δοµής που κάλεσε τον τρέχων κόµβο. Με τον τρόπο αυτό γίνεται η διαχείριση σύνθετων πράξεων όπως η εκτέλεση της εντολής let a = (8*3) +(10/5). Τα µερικά αποτελέσµατα αποθηκεύονται στους καταχωρητές που χρησιµοποιούνται για τον υπολογισµό του τελικού αποτελέσµατος. 2.3 Παραδείγµατα δοκιµής της λειτουργίας του µεταγλωττιστή 2.3.1 Ορισµός µεταβλητών και εκτέλεση πράξεων µε αυτές. Αν µεταγλωττίσουµε το παρακάτω πηγαίο πρόγραµµα σε minibasic : let a = 300 let b= (a-200)+ 10 το µεταγλωττισµένο αρχείο output.asm θα περιέχει τις εντολές : LIL R0 44 LIH R0 1 ST R0 a # actual value 300 has been placed in memory LIL R1 10 LIH R1 0 LIL R2 200 LIH R2 0 LD R3 a SUB R4 R3 R2 ADD R5 R4 R1 ST R5 b # actual value of b will be computed after execution of code Με τις δύο πρώτες εντολές φορτώνεται τµηµατικά στον καταχωρητή R0 (άνω και κάτω byte) ο αριθµός 300. Στον άνω byte φορτώνεται το 1 (είναι το 256 στο δεκαδικό) και στο κάτω byte το 44, έτσι ώστε ο συνολικός αριθµός να είναι το 256+44=300, ο οποίος µε την εντολή ST της τρίτης γραµµής αποθηκεύεται στην θέση µνήµης a. Στην 3 η έως 6 η γραµµή µε τον ίδιο τρόπο αποθηκεύονται οι σταθερές 10 και 200 στους καταχωρητές R1 και R2 αντίστοιχα. Στην 7 η γραµµή ο a φορτώνεται στο καταχωρητή R3. Στις επόµενες δύο γραµµές εκτελούνται οι πράξεις και το τελικό αποτέλεσµα που βρίσκεται στον R5 αποθηκεύεται στην θέση µνήµης που αντιστοιχεί στην µεταβλητή b. 2.3.2 Βρόγχοι (loops) και φωλιασµένοι βρόγχοι (nested loops) Η µεταγλώττιση του παρακάτω πηγαίου προγράµµατος : for 3 do begin for 2 do begin let a = a +1 end end for 5 do begin let b = b+1 end παράγει τον κώδικα σε assembly : LIL R0 3 LIH R0 0 LIL R1 1 LIH R1 0 LIL R2 0 LIH R2 0 L1: LIL R3 2 LIH R3 0 3
LIL R4 1 LIH R4 0 LIL R5 0 LIH R5 0 L2: LIL R6 1 LIH R6 0 LD R7 a ADD R8 R7 R6 ST R8 a # actual value of a will be computed after execution of code SUB R3 R3 R4 CMP R3 R5 BRP L2 SUB R0 R0 R1 CMP R0 R2 BRP L1 LIL R9 5 LIH R9 0 LIL R10 1 LIH R10 0 LIL R11 0 LIH R11 0 L3: LIL R12 1 LIH R12 0 LD R13 b ADD R14 R13 R12 ST R14 b # actual value of b will be computed after execution of code SUB R9 R9 R10 CMP R9 R11 BRP L3 Στην έναρξη κάθε βρόγχου αποθηκεύονται σε καταχωρητές ο αριθµός των επαναλήψεων και η σταθεροί αριθµοί 0 και 1, οι οποίοι χρησιµοποιούνται εν συνεχεία για την αφαίρεση κατά 1 κάθε φορά που εκτελείται ο βρόγχος και για την σύγκριση µε το µηδέν προκειµένου να τερµατιστεί το loop. Ο µεταγλωττιστής δίνει διαδοχικά αριθµηµένες ετικέτες στον κώδικα για τα σηµεία όπου ξεκινάει κάθε loop και φροντίζει στο τέλος του loop να επιστρέφει στο κατάλληλο σηµείο του προγράµµατος. Η εντολή let a = a +1 εκτελείται συνολικά 6 φορές. Οι παρακάτω εντολές του προγράµµατος υλοποιούν την απόφαση για διακλάδωση στο L1: SUB R0 R0 R1 CMP R0 R2 BRP L1 Η εντολή SUB αφαιρεί 1 κάθε φορά που εκτελείται ο βρόγχος από τον αριθµό των επαναλήψεων. Η CMP συγκρίνει το αποτέλεσµα µε το 0 και η επόµενη εντολή BRP αν είναι το αποτέλεσµα θετικό διακλαδίζει το πρόγραµµα στη θέση L1 αλλιώς εξέρχεται του βρόγχου. 2.3.3 Υλοποίηση της plot Αν µεταγλωττίσουµε το παρακάτω πηγαίο πρόγραµµα σε minibasic : begin plot 10,19,5 end παράγεται ο κώδικας σε assembly : LIL R0 10 LIH R0 0 SLA R1 R0 8 LIL R2 19 LIH R2 0 ADD R3 R2 R1 PAINT 5 R3 Στις πρώτες τρεις εντολές φορτώνεται το 10 στον R0 και εκτελείται ο πολλαπλασιασµός του µε 256 (µε την εντολή SLA). Στην συνέχεια προστίθεται το 19 και το αποτέλεσµα που βρίσκεται στον R3 χρησιµοποιείται για να εκτελεστεί η τελευταία εντολή PAINT η οποία αναλαµβάνει να τοποθετήσει το συγκεκριµένο pixel στην οθόνη. Ταυτόχρονα µε την µεταγλώττιση της plot, γράφεται και στο αρχείο drawing.dat η συντεταγµένη και ο κωδικός χρώµατος που υπολογίζει η plot. Τα αρχείο αυτό µπορεί να χρησιµοποιηθεί ως είσοδος σε ένα πρόγραµµα το οποίο θα σχεδιάζει τα pixel στην οθόνη. 4
2.3.4 Εκτέλεση των πράξεων * / και % Η µεταγλώττιση της παρακάτω εντολής : print (3*2)+(8%5)+(10/5) έχει ως αποτέλεσµα την παραγωγή του παρακάτω κώδικα assembly : LIL R0 2 LIH R0 0 LIL R1 3 LIH R1 0 LIL R2 6 LIH R2 0 ADD R3 R2 R1 ADD R4 R3 R0 ST R4 SM # SM is a pointer to screen memory Εδώ παρατηρούµε ότι η εφαρµογή των τελεστών * / και % δεν µεταφράζονται σε εντολές της Jackal, αλλά υπολογίζεται απευθείας το αποτέλεσµα των πράξεων από τον compiler το οποίο και αποθηκεύεται στους καταχωρητές R0 έως R2, για να εκτελεστούν εν συνεχεία οι δύο προσθέσεις µε τις εντολές ADD. Η εντολή PRINT µεταφράζεται στον Jackal σε µια εντολή αποθήκευσης ST σε µια θέση που χρησιµοποιείται από το σύστηµα ως µνήµη απεικόνισης χαρακτήρων στην οθόνη. 2.3.4 Η εντολή if και οι τελεστές /= και == Η µεταγλώττιση των εντολών : let a=1 if (a/=0) then print 1 if (a==1) then print 2 έχει ως αποτέλεσµα το παρακάτω κώδικα : LIL R0 1 LIH R0 0 ST R0 a # actual value 1 has been placed in memory LIL R1 0 LIH R1 0 LD R2 a CMP R2 R1 BRZ L0 LIL R4 1 LIH R4 0 ST R4 SM # SM is a pointer to screen memory L0: LIL R5 1 LIH R5 0 LD R6 a CMP R6 R5 L1: BRP L1 BRN L1 LIL R8 2 LIH R8 0 ST R8 SM # SM is a pointer to screen memory Στις πρώτες 3 εντολές αποθηκεύεται στην µεταβλητή a η τιµή 1. Στις επόµενες 3 εντολές φορτώνεται το 0 στον R1 και ο a στον R2. Η εντολή BRZ, αν η διαφορά τους είναι µηδέν (δηλαδή οι δύο αριθµοί είναι ίδιοι) παραλείπει την εκτέλεση των επόµενων 3 εντολών εκτρέποντας τη ροή του προγράµµατος στην ετικέτα L0. Αντίστοιχα υλοποιείται και η επόµενη if, µόνο που τώρα η διακλάδωση συµβαίνει όταν η διαφορά των αριθµών είναι θετική ή αρνητική (οι αριθµοί δεν είναι ίσοι) και υλοποιείται µε δύο διαδοχικές εντολές BRP και BRN. 5
3. Προδιαγραφές συστήµατος τελικού χρήστη για την δηµιουργία του µεταγλωττιστή και κτίσιµο make από τον πηγαίο κώδικα. Για την εκτέλεση του προγράµµατος απαιτείται ένα σύστηµα Linux ή περιβάλλον Cygwin/Windows στο οποίο είναι διαθέσιµα τα πακέτα του flex,bison και gcc. Η δηµιουργία του εκτελέσιµου αρχείου του µεταγλωττιστή γίνεται δίνοντας στην γραµµή εντολών την εντολή make, στον κατάλογο που βρίσκεται και το αρχείο MAKEFILE που περιέχει τις κατάλληλες οδηγίες. 4. Στόχοι µελλοντικής ανάπτυξης του µεταγλωττιστή. Στους στόχους της µελλοντικής ανάπτυξης του µεταγλωττιστή περιλαµβάνεται η υλοποίηση κατάλληλης τεχνικής για τον καταµερισµό των καταχωρητών όπως είναι ο αλγόριθµος γραµµικής σάρωσης (linear scan register allocation) των Poletto και Sarkar. Στον κώδικα είναι πιθανό να υπάρχουν σφάλµατα (bugs) καθώς η ανάπτυξη ενός µεταγλωττιστή είναι ιδιαίτερα σύνθετη εργασία και η εξασφάλιση της απρόσκοπτης λειτουργίας του εξασφαλίζεται µε ένα µεγάλο αριθµό δοκιµών που δεν ήταν δυνατόν να πραγµατοποιηθεί στα πλαίσια της παρούσας εργασίας, µπορεί όµως να γίνει σε επόµενο στάδιο εξέλιξής του. 5. Βιβλιογραφικές αναφορές. [flex] Flex (The Fast Lexical Analyzer) homepage. http://flex.sourceforge.net [bison] Bison homepage. http://www.gnu.org/software/bison/bison.html [GCC] The GNU Compiler Collection homepage. http://gcc.gnu.org [jack] Jackal 3.0 Development tools http://sourceforge.net/projects/jackcc/ [minibasic] minibasic homepage. http://courses.softlab.ntua.gr/compilers/2009a/examples/minibasic/ [Ker90] Brian Kernighan and Dennis Ritchie, The C Programming Language, 2nd edition, Prentice Hall, 1990. [Lev09] John Levine, Flex & Bison, O'Reilly Publishing, 2009. [Nie10] Tom Niemann, A Compact Guide to Lex & Yacc. http://www.epaperpress.com/lexandyacc/ 6
Παράρτηµα Α Υλοποίηση της δοµής AST σε ANSI C /* manolis bozis */ /* mini basic compiler for Jackal */ /* 22-5-2010 */ #include <stdio.h> #include <stdlib.h> #include "ast.h" #include "parser.h" AST ast_node(int code, AST left, AST right) AST n = (AST) malloc(sizeof(struct tree_tag)) if (n == NULL) fprintf(stderr, "Out of memory\n") exit(1) n->code = code n->left = left n->right = right return n AST ast_plot(int code, number num, AST left, AST right) AST n = (AST) malloc(sizeof(struct tree_tag)) if (n == NULL) fprintf(stderr, "Out of memory\n") exit(1) n->code = code n->num = num n->left = left n->right = right return n AST ast_let(variable var, AST expr) AST n = (AST) malloc(sizeof(struct tree_tag)) if (n == NULL) fprintf(stderr, "Out of memory\n") exit(1) n->code = T_let n->var = var n->left = expr return n AST ast_num(number num) AST n = (AST) malloc(sizeof(struct tree_tag)) if (n == NULL) fprintf(stderr, "Out of memory\n") exit(1) n->code = T_const n->num = num return n AST ast_var(variable var) AST n = (AST) malloc(sizeof(struct tree_tag)) if (n == NULL) fprintf(stderr, "Out of memory\n") exit(1) n->code = T_var n->var = var return n 7
#define NO_RESULT 0 number memory[26] /* number of possible variables */ number compile (AST ast, FILE *f1, FILE *f2, int *rp) int i, n extern int loop_counter extern int counter if (ast!= NULL) switch (ast->code) case T_begin: compile(ast->left,f1,f2,rp) compile(ast->right,f1,f2,rp) return NO_RESULT case T_print: fprintf(f2,"\t%-8sr%i\t%s\t\t%s\n","st",compile(ast->left,f1,f2,rp),"sm","# SM is a pointer to screen memory") return NO_RESULT case T_plot: /* ---------- code selection ------------*/ fprintf(f2,"\t%-8sr%i\tr%i\t%i\n","sla",(*rp),compile(ast->left,f1,f2,rp),8) fprintf(f2,"\t%-8sr%i\tr%i\tr%i\n","add",*rp,compile(ast->right,f1,f2,rp),(*rp)++) fprintf(f2,"\t%-8s%i\tr%i\n","paint",ast->num,*rp) /* ---------- create drawing file ------------*/ fprintf(f1,"%i\t%i\n",(ast->left->num)*256+(ast->right->num),(ast->num)) return NO_RESULT case T_let: fprintf(f2,"\t%-8sr%i\t%c","st",compile(ast->left,f1,f2,rp),(ast->var)+'a') memory[ast->var] = ast->left->num if (ast->left->code == T_const) fprintf(f2,"\t\t%s%i%s\n","# actual value ",ast->left->num," has been placed in memory") else fprintf(f2,"\t\t%s%c%s\n","# actual value of ",(ast->var)+'a'," will be computed after execution of code") return NO_RESULT case T_for: counter = loop_counter + counter +1 n = compile(ast->left,f1,f2,rp) fprintf(f2,"\t%-8sr%i\t%i\n","lil",(*rp),1) fprintf(f2,"\t%-8sr%i\t%i\n","lih",(*rp)++,0) fprintf(f2,"\t%-8sr%i\t%i\n","lil",(*rp),0) fprintf(f2,"\t%-8sr%i\t%i\n","lih",(*rp)++,0) fprintf(f2,"l%i:",counter) i = compile(ast->right,f1,f2,rp) fprintf(f2,"\t%-8sr%i\tr%i\tr%i\n","sub",n,n,n+1) fprintf(f2,"\t%-8sr%i\tr%i\n","cmp",n,n+2) fprintf(f2,"\t%-8sl%i\n","brp",counter) counter = counter - 1 loop_counter = loop_counter +1 return n case T_if: n=compile(ast->left,f1,f2,rp) if (ast->left->code==t_equal) fprintf(f2,"\t%-8sl%i\n","brp",counter) fprintf(f2,"\t%-8sl%i\n","brn",counter) else fprintf(f2,"\t%-8sl%i\n","brz",counter) n=compile(ast->right,f1,f2,rp) fprintf(f2,"l%i:",counter) 8
counter++ return NO_RESULT case T_const: fprintf(f2,"\t%-8sr%i\t%i\n","lil",*rp,(ast->num)-256*(ast->num/256)) fprintf(f2,"\t%-8sr%i\t%i\n","lih",*rp,(ast->num/256)) case T_var: fprintf(f2,"\t%-8sr%i\t%c\n","ld",*rp,(ast->var)+'a') ast->num = memory[ast->var] case '+': fprintf(f2,"\t%-8sr%i\tr%i\tr%i\n","add",*rp,compile(ast->left,f1,f2,rp),compile(ast- >right,f1,f2,rp)) ast->num = (ast->left->num) + (ast->right->num) case '-': fprintf(f2,"\t%-8sr%i\tr%i\tr%i\n","sub",*rp,compile(ast->left,f1,f2,rp),compile(ast- >right,f1,f2,rp)) ast->num = (ast->left->num) - (ast->right->num) case '*': if (ast->left->code!= T_const) compile(ast->left,f1,f2,rp) if (ast->right->code!= T_const) compile(ast->right,f1,f2,rp) ast->num = (ast->left->num) * (ast->right->num) ast->code = T_const fprintf(f2,"\t%-8sr%i\t%i\n","lil",*rp,(ast->num)-256*(ast->num/256)) fprintf(f2,"\t%-8sr%i\t%i\n","lih",*rp,(ast->num/256)) case '/': if (ast->left->code!= T_const) compile(ast->left,f1,f2,rp) if (ast->right->code!= T_const) compile(ast->right,f1,f2,rp) ast->num = (ast->left->num) / (ast->right->num) ast->code = T_const fprintf(f2,"\t%-8sr%i\t%i\n","lil",*rp,(ast->num)-256*(ast->num/256)) fprintf(f2,"\t%-8sr%i\t%i\n","lih",*rp,(ast->num/256)) case '%': if (ast->left->code!= T_const) compile(ast->left,f1,f2,rp) if (ast->right->code!= T_const) compile(ast->right,f1,f2,rp) ast->num = (ast->left->num) % (ast->right->num) ast->code = T_const fprintf(f2,"\t%-8sr%i\t%i\n","lil",*rp,(ast->num)-256*(ast->num/256)) fprintf(f2,"\t%-8sr%i\t%i\n","lih",*rp,(ast->num/256)) case T_equal: fprintf(f2,"\t%-8sr%i\tr%i\n","cmp",compile(ast->left,f1,f2,rp),compile(ast- >right,f1,f2,rp)) ast->num = (ast->left->num) - (ast->right->num) case T_nequal: fprintf(f2,"\t%-8sr%i\tr%i\n","cmp",compile(ast->left,f1,f2,rp),compile(ast- >right,f1,f2,rp)) ast->num = (ast->left->num) - (ast->right->num) return NO_RESULT 9
Παράρτηµα Β Το αρχείο lexer.l % #include <stdio.h> #include <stdlib.h> #include "ast.h" /* "ast.h" must be BEFORE "parser.h" */ #include "parser.h" #define T_eof 0 void yyerror (const char * msg) % %option yylineno D [0-9] L [a-z] W [ \t\r\n] %% "print" return T_print "let" return T_let "for" return T_for "do" return T_do "begin" return T_begin "end" return T_end "if" return T_if "then" return T_then "plot" return T_plot "==" return T_equal "/=" return T_nequal D+ yylval.num = atoi(yytext) return T_const L yylval.var = yytext[0] - 'a' return T_var [=\(\)\+\-\*\/\%\,] return yytext[0] W+ /* nothing */ \'.*\n /* comment insertion */ <<EOF>> return T_eof. yyerror("lexical error") exit(1) %% Παράρτηµα Γ Το αρχείο parser.y % #include <stdio.h> #include <stdlib.h> #include "ast.h" FILE *f1 /* pointer to file drawing.dat */ FILE *f2 /* pointer to file output.asm */ AST prg int yylineno /* number of current line */ int *rc /* register counter */ void yyerror (const char * msg) number compile(ast ast, FILE *,FILE *, int *rp) % %union AST ast number num variable var %token T_print "print" %token T_let "let" %token T_for "for" %token T_do "do" %token T_begin "begin" %token T_end "end" 10
%token T_if "if" %token T_then "then" %token T_plot "plot" %token T_equal "==" %token T_nequal "/=" %token<num> T_const %token<var> T_var %left '+' '-' %left '*' '/' '%' %nonassoc "==" "/=" %type<ast> stmt_list %type<ast> stmt %type<ast> expr %% program : stmt_list prg = $1 stmt_list : /* nothing */ $$ = NULL stmt stmt_list $$ = ast_node(t_begin, $1, $2) stmt : "print" expr $$ = ast_node(t_print, $2, NULL) "let" T_var '=' expr $$ = ast_let($2, $4) "for" expr "do" stmt $$ = ast_node(t_for, $2, $4) "begin" stmt_list "end" $$ = $2 "if" expr "then" stmt $$ = ast_node(t_if, $2, $4) "plot" expr ',' expr ',' T_const $$ = ast_plot(t_plot, $6, $2, $4) expr : T_const $$ = ast_num($1) T_var $$ = ast_var($1) '(' expr ')' $$ = $2 expr '+' expr $$ = ast_node('+', $1, $3) expr '-' expr $$ = ast_node('-', $1, $3) expr '*' expr $$ = ast_node('*', $1, $3) expr '/' expr $$ = ast_node('/', $1, $3) expr '%' expr $$ = ast_node('%', $1, $3) expr "==" expr $$ = ast_node(t_equal, $1, $3) expr "/=" expr $$ = ast_node(t_nequal, $1, $3) %% void yyerror (const char * msg) fprintf(stderr, "Minibasic: %s in line %d\n", msg,yylineno) exit(1) int loop_counter int counter int main () int rc int *rp extern int loop_counter extern int counter loop_counter = 0 counter = 0 f1 = fopen("drawing.dat","w") f2 = fopen("output.asm","w") int result = yyparse() rp =malloc(sizeof(rc)) if (rp!= NULL) rc = 0 /* register counter initialization */ if (result == 0) compile(prg,f1,f2,rp) fclose(f1) fclose(f2) return result 11
Περιεχόµενα 1.Εισαγωγή.. 2. ιαδικασία ανάπτυξης.. 2.1 Επέκταση της γραµµατικής.. 2.2 Ενδιάµεση αναπαράσταση του µεταγλωττιστή... 2.3 Παραδείγµατα δοκιµής της λειτουργίας του µεταγλωττιστή. 2.3.1 Ορισµός µεταβλητών και εκτέλεση πράξεων µε αυτές. 2.3.2 Βρόγχοι (loops) και φωλιασµένοι βρόγχοι (nested loops). 2.3.3 Υλοποίηση της plot 2.3.4 Η εντολή if και οι τελεστές /= και == 3. Προδιαγραφές συστήµατος τελικού χρήστη. 4. Στόχοι µελλοντικής ανάπτυξης του µεταγλωττιστή.. 5. Βιβλιογραφικές αναφορές. Παράρτηµα Α Υλοποίηση της δοµής AST σε ANSI C. Παράρτηµα Β Το αρχείο lexer.l... Παράρτηµα Γ Το αρχείο parser.y.. Σελ. 1 1 1 2 3 3 3 4 4 5 6 6 7 10 10 12