Sunteți pe pagina 1din 34

FLEX + BISON

FLEX + BISON
1. Structura unui analizor lexical si sintatctic realizat cu ajutorul utilitarelor flex si bison Instrumentele flex i bison pot fi folosite mpreun pentru a realiza analiza lexical i sintactic a unui text i chiar pentru a obine un interpretor sau un compilator prin mecanismul aciunilor semantice. Schema de utilizare mpreun a celor dou generatoare este dat n figura urmatoare.

Realizarea unui compilator folosind generatoarele automate flex si bison Dac fiierul a1.l descrie unitile lexicale ale limbajului pe care dorim s-l implementm, iar a1.y descrie sintaxa limbajului, atunci comenzile (linux) pentru a obine fiierul a1.exe din a1.l i a1.y sunt urmtoarele: bison -d a l . y Produce (genereaza) fiierul .tab.c care conine funcia de parsare yyparse() Opiunea -d produce fiierul header .tab.h care conine directivele care atribuie unitilor lexicale coduri numerice (va conine declaraiile pentru terminale). Acestea vor fi folosite de analizorul lexical produs de flex. flex -o a l . y y . c a l . l Produce fiierul a1.yy.c care conine definiia funciei yylex() care este invocat de yyparse() pentru obinerea, pe rnd, a unitilor lexicale din textul surs. Fiierul a1.l trebuie s conin directiva #include a1.tab.h. gcc -o a l . e x e a l . y y . c a l . t a b . c - l m - l f l Compilarea si linkeditarea fis-lor sursa .c, generate de catre flex, respectiv bison. EXEMPLE: Exemplul 1: Analizor lexical si sintactic pentru limbajul natural. Aplicatia rezultata analizeaza lexical si sintactic doar prop. simple, insa poate fi dezvoltata (ca in exemplul 3 din referatul anterior, cel cu bison-ul).

FLEX + BISON

Fisier de intrare (yyin )


S t u d e n t u l i n v a t a

Citire din fisierul de intrare


Analizor lexical generat de flex

1/
Valoare atom lexical = yylva (ex. Studentul) A

Cerere de atom lexical

Tip (categorie) atom lexical (ex. SUBST)

Analizor sintactic de bison

generat

Fisier de iesire ( yyout) - poate coincide cu fis. intrare REZOLVARE: 1. Creati fisierul de specificatii pentru bison (analiz_prop.y):
%{ /*analiza propozitie simpla*/ #include <stdio.h> o} /*sectiunea definitii bison */ %union {char *strval; } %token <strval> SUBST %token <strval> VERB %type <strval> prop simpla %type <strval> subiect %type <strval> predicat %% prop simpla: subiect predicat { _ printf("PROP. SIMPLA!!!! subiect %s, predicat %s\n", $1, $2);} ; subiect: SUBST {$$=$1} ; predicat: VERB {$$=$1} ; %% extern FILE * yyin; #include <stdio.h>

FLEX + BISON

main() {yyin=stdin; while (!feof(yyin)){ yyparse(); } } yyerror(char *s) { fprintf(stderr, "%s\n", s); }

Tipurile de atomi sunt definite in cadrul fisierului sursa pentru bison cu directiva %token . Exemplu : %token SUBST Transferul valorii atomului lexical spre analizorul sintactic se realizeaza cu ajutorul variabilei yylval. Aceasta variabila are implicit tipul int . Cu ajutorul directivei %union putem defini o variabila yylval de tip structura de date . Exemplu: %union { char *strval; int ival; } Cu ajutorul acestei directive bisonul genereaza in cadrul fisierului y.tab.h o declaratie de forma: typedef union { char* strval; int ival; } YYSTYPE; extern YYSTYPE yylval; Daca dorim ca in momentul in care analizorul sintactic primeste un identificator(SUBST) sa primeasca si o valoare de tip sir de caractere, putem specifica acest lucru prin : %token <strval> SUBST %token <ival> NUMAR Pentru a specifica ca un neterminal utilizeaza o anumita parte a valorii yylval utilizam o declaratie de forma : %type <ival> expr; 2. Apelati bison pentru generarea fisierului analiz_prop.tab.c (care contine functia yyparse()), cu optiunea -d (pentru generarea fiierului header analiz_prop.tab.h, care conine directivele care atribuie unitilor lexicale coduri numerice (va conine declaraiile pentru terminale); directivele vor fi folosite de analizorul lexical produs de flex). bison d a n a l i z prop.y 3.Creati fis. de specificatii ptr. flex (analiz_prop.l).
%{ /* analiza unor propozitii! */ #include "analiz prop.tab.h" %} %% [ ] ; /*consumare spatiu */ \. {return 0; } Studentul { yylval.strval=yytext; return

FLEX + BISON

invata %%

(SUBST); } { yylval.strval=yytext; return (VERB); }

4.Apel flex: flex -o analiz_prop.yy.c analiz_prop.l (sau flex -o aanaliz_prop.yy.c analiz_prop.l ) 5.Compilarea si linkeditarea fisierelor generate de flex si bison:
gcc o analiz_prop.exe analiz_prop.yy.c analiz_prop.tab.c -lm -lfl

Sa consideram ca avem pe banda de intrare propozitia : "Studentul nvata ." Analizorul sintactic "cere" analizorului lexical un atom lexical. Acesta citeste caractere de la intrare pana ajunge la un sir de caractere care se potriveste cu una din expresiile regulate date n fisierul sursa flex. Dupa citirea sirului de caractere Studentul, analizorul lexical determina faptul ca acesta se potriveste cu o expresie regulata data n fisierul sursa flex. In acest moment analizorul executa actiunea asociata expresiei regulate studentul . In cadrul actiunii asociate expresiei regulate Studentul, analizorul lexical intoarce catre analizorul sintactic o valoare intreaga (corespunzatoare constantei SUBST) care specifica faptul ca atomul lexical detectat este substantiv. De asemenea intoarce si sirul de caractere asociat substantivului prin intermediul variabilei yylval.strval . Valorile intoarse de analizorul lexical spre analizorul sintactic sunt pe rand: SUBST , VERB, 0 . Valoarea 0 specifica analizorului sintactic ca s-a terminat preluarea datelor . Functionarea analizorului sintactic Vom urmri stiva asociata acestuia. Valorile primite de la analizorul lexical sunt puse in varful unei stive. Daca partea de sus a stivei corespunde partii drepte a unei productii din analizorul sintactic, este realizata actiunea corespunzatoare acelei productii, sunt scoase din stiva elementele ce corespund partii drepte a productiei si apoi este pusa in stiva partea stanga a productiei respective. Aceasta actiune poarta numele de reducere. De oricate ori analizorul sintactic nu poate efectua o reducere, acesta cheama un atom lexical de la analizorul lexical. Aceasta operatie se numeste deplasare . Pentru propozitia data obtinem in stiva : SUBST -aplicam reducere cu regula 2 subiect -intrucat nu e posibila o noua reducere apelam la an. lexical ptr un nou atom subiect VERB -aplicam reducere cu regula 3

FLEX + BISON

subiect predicat -aplicam reducere cu regula 1 se afiseaza date conform actiunii asociate regulii 1 . prop_simpla -intrucat nu mai putem realiza nici o reducere apelam analizorul lexical. Analizorul lexical intoarce valoarea 0 corespunzatoare sfarsitului datelor de la intrare. Intrucat in stiva avem simbolul de start -> propozitia este corecta conform gramaticii date in analizorul sintactic . Arborele sintactic corespunzator analizei precedente este urmtorul: In primul pas avem nodul : SUBST Dupa aplicarea reducerii avem : subiect
SUBST

Dupa realizarea deplasarii avem un arbore de forma : subiect


SUBST VERB

Dupa realizarea reducerii cu productia predicat->VERB obtinem : subiect predicat


SUBST VERB

Dupa ultima reducere, arborele e de forma : prop_simpla / \ subiect predicat SUBST VER.B Dupa cum se observa, arborele este construit plecand de la frunze spre radacina deci avem o constructie bottom - up (de la frunze spre radacina). Observaie: Structura unui program asemanator unui utilitar flex poate contine : -Generarea unui AFN pe baza expresiilor regulate introduse in fisierul sursa -Transformarea AFN -> AFD -Pe baza AFD obtinut se genereaza cod C pentru analizorul lexical (acceptor ). Implementarea unei tabele de simboli in cadrul analizorului lexical 0tabela de simboli simpla se poate implementa sub forma unei liste . Exemplu: #define NIMIC 0 struct simbol
{

}; struct simbol * lista_simboli = NULL; int adauga_simbol( int tip, char * simbol ) 1_____________________________________________________________________________ struct simbol * sp; if( cauta_simbol ( simbol ) != NIMIC ) { printf("!!! Atentie : simbol %s deja definit\n",simbol); return 0;
5

char * nume_simbol; int tip_simbol; struct simbol * urmatorul;

FLEX + BISON

} sp = (struct simbol *) malloc ( sizeof( struct simbol)); sp> urmatorul = lista_simboli; sp ->nume_simbol = ( char * ) malloc ( strlen(simbol) + 1 ); strcpy(sp -> nume_simbol, simbol); sp -> tip_simbol = tip; lista_simboli = sp; return 1;
}

int cauta_simbol( char * simbol )


{

struct simbol * sp = lista_simboli; for( ; sp; sp = sp -> urmatorul) { if( strcmp(sp -> nume_simbol, simbol) == 0 ) return sp -> tip_simbol; } return NIMIC;

Exemplul 2: Sa se construiasca cu ajutorul generatoarelor flex si bison un program care sa realizeze translatarea expresiilor aritmetice din forma normala in forma postfixata. Definiie: Notatia postfixata (poloneza). 1.daca E e o variabila sau o constanta atunci notatia postfixata pentru E este tot E 2.daca E este o expresie de forma E1 op E2 , unde op este o operatie binara , atunci notatia postfixata pentru E este E1' E2' op unde E1' si E2' sunt notatiile postfixate pentru E1 si E2 3.Daca E este o expresie de forma ( E1 ) atunci notatia postfixata pentru E este notatia postfixata pentru E1. Exemple : ( 9 - 5 ) + 2 in forma postfixata este : 9 5 - 2 + 9 ( 5 + 2 ) in forma postfixata este : 9 5 2 + REZOLVARE: Fisierul de specificatii ptr bison (polish.y)
#include <stdio.h> #include <stdlib.h> %} %union{ double ct; char id; int ri; }

FLEX + BISON

input:

itoken <ct> CT itoken <id> ID itype <ri> exp itype <id> iden iright '=' ileft '-' '+' left '*' '/' iright 'A' taonassoc '(' ') linie | input linie linie: '\n' | exp '\n' {printf("\n ");} " + " " * " / ' " A

exp:

CT { printf("%g ",$1); } exp '+' exp { printf ( exp '-' exp { printf ( exp '*' exp { printf ( exp '/' exp { printf ( iden: A exp ' ' exp { printf ( '(' exp ')' { iden '= exp

$$=$2; } { printf " ( = ID { printf("%c ",$1); } extern FILE* yyin; m a i n ( ) { y y i n = s t d i n ; w h

FLEX + BISON

i l e ( ! f e o f ( y y i n ) ) { yyparse(); } yyerror (char* s) { printf("%s\n",s ); return 0; }

OBS: apelati bison SI cu opiunea -v (pentru generarea fis-lui polish.output) si analizai continutul fis-lui polish.output! (bison -dv polish.y) Fisierul de specificatii ptr. flex (polish.l):
%{ #include "polish.tab.h" //extern int yywrapp(); %}

%option noyywrap %% [ \t] + [0-9]+ { yylval.ct=a toi(yytext) ; r e t u


oa

FLEX + BISON

r n C T ; } [a-z] { yylval.id=y ytext[0];

FLEX + BISON

r e t u r n I D ; . { } r e t u r n y y t e x t [ 0 ] ; \n } r e t u r n y y t e x t [ 0 ] ; } {

10

FLEX + BISON

Exemplul 3:

11

FLEX + BISON

A p l i c a i e

p e n t r u

e v a l u a r
12

FLEX + BISON

e a

u n o r

e x p r e s i i

R E Z O L V A
13

FLEX + BISON

R E : Fisierul sursa pentru bison (expresie.y)


%{ # i n c l u d e < s t d i o . h > # d e f i n e Y Y E R R O R _ V E R B O S E 1 0 e x t

14

FLEX + BISON

e r n i n t n o w r a p p ( ) ; % } %token NUMAR

/* Sirul %token arata definirea terminalelor */ /* %left arata ca operatiil e de adunare si de scadere sunt asociati ve stanga, si anume: x+y+z=( x+y)+z . Pentru asociati vitate dreapta se folosest e %right. Daca operatiil e nu
15

%left '-' '+'

FLEX + BISON

prezinta asociati vitate , se folosest e %nonas soc=(no n associati ve)*/


% l e f t ' * ' ' / ' O n o n a s s o c U M I N U S

/* UMINU S reprezin ta minusul unar. nonasso c exprima faptul ca

16

FLEX + BISON

operator ul nu este asociati v. Ordinea de declarar e a asociati vitatii este un mecanis m ce permite stabilire a precede ntei operatiil or . Ultima operatie careia ia fost declarat a asociati vitatea este cea mai prioritar a (in cazul nostru UMINU S) ,iar operatiil e din prima declarati e sunt cele mai putin prioritar e . In exemplu nostru ,

17

FLEX + BISON

cea mai prioritar a operatie este UMINU S , urmata de '*' si '/' si apoi de '+' si '-'*/
%% expresie:expresi e '+' expresie {$$=$1+$3; rintf("E fectueaz a %i + %i, rezultat =%i\n", $1, $3, $$);}

/ * r n d u l a n t e r i o r s e r e f e r a l a

18

FLEX + BISON

p r o d u c t i a : e x p r e s i e + e x p r e s i e . D u p a d e t e c t a r e a a c e s t e

19

FLEX + BISON

i p r o d u c t i i s e e x e c u t a a c t i u n e a : $ $ = $ 1 + $ 3 , i a r a p o i s e e x

20

FLEX + BISON

e c u t a p r i n t f . $ $ = v a l o a r e a s t a n g a p r o d u c t i e . $ 1 = v a l o a r

21

FLEX + BISON

e a p r i m e i e x p r e s i i d i n p a r t e a d r e a p t a . $ 3 = v a l o a r e a c e l

22

FLEX + BISON

e i d e a d o u a e x p r e s i i d i n p a r t e a d r e a p t a * /
| expresie '-' expresie {$$=$1$3; p rint f("E fect ueaz a %i -

23

FLEX + BISON

%i, rezu ltat = %i\n ", $1, $3, $ $);} | expr esie '*' expr esie {$ $=$1 *$3; p rint f("E fect ueaz a %i * %i, rezu ltat = %i\n ", $1, $3, $ $);} | expr esie '/' expr esie { if ($3= =0) y yerror("impartir e cu 0"); else { $ $ = $ 1 / $ 3

24

FLEX + BISON

; p r i n t f ( " E f e c t u e a z $ 1 / $ 3 , r e z u l t a t = % i \ n " , $ 1 , $ 3 , $ $ ) ;

25

FLEX + BISON

} } expresie %prec UMINUS {$$=-$2;}

/* %prec UMINUS realizeaza legatura cu UMINUS din sectiunea de definitii*/

%% int main( ) { yyparse( ); } yyerror (char *s) {fprintf(stderr,"%s\n",s); }

|NUMAR {$$=$1;} ;

Fisierul sursa pentru flex (expresie.l)


%{ #include "expresie.tab.h"

extern int yylval;

/*fisierul y.tab.h este generat automat de bison (parametru -d). El contine definitii de terminale . Exemplu definitie terminal NUMAR: #define NUMAR 257*/ /* variabila yylval este definita in fisierul C generat de bison . Ea contine valoarea atomului lexical detectat . De exemplu, pentru un intreg, ea contine valoarea intregului detectat.*/

%r

%option noyywrap %% [0-9]+ \t] ; \n . %% {yylval=atoi(yytext); printf("numar=%d\n", atoi(yytext));return NUMAR;} [ return 0; return yytext[0];

Exemplul 4: Implementarea unui calculator de buzunar. Vom construi fiierele pentru flex i bison care descriu sintaxa expresiilor aritmetice ce se pot forma cu constante ntregi (CINT), variabile (VAR), operaiile aritmetice uzuale i paranteze. Desigur, orice variabil ce poate aprea ntr-o expresii trebuie s fie iniializat cu valoarea unei expresii . Astfel, un program n accepiunea de aici este o succesiune de expresii aritmetice i/sau asignri. Gramatica ce descrie un astfel de limbaj este urmtoarea:
program ^ program statement | X statement ^ expression | VAR '=' expression expression ^ CINT | VAR | expression '+' expression | expression '-' expression | expression '*' expression | expression '/' expression | '(' expression ')' Fiierul

pentru bison, calc.y : aciunile semantice au ca efect obinerea valorii numerice a unei expresii atunci cnd ea este transmis la intrare programului calc.exe ce se obine. Tabloul
9

FLEX + BISON

sym[26] este folosit pentru a pstra valorile variabilelor ce apar n programe; o variabil este aici o liter (va fi descris n fiierul pentru flex). Fisier specificatii ptr bison (calc.y):
%{ /* fisierul calc.y */ #include <stdio.h> void yyerror(char *); int yylex(void); int sym[26]; %} %token CINT VAR %left '+' '-' %left '*' '/' program: program statement | /* NULL */

1 statement: expression \n' {printf("%d\n", $1);} | VAR '=' expression { sym[$1] = expression: CINT | VAR { $$ = sym[$1]; } | expression '+' expression { $$ = $1 $3; } | expression '-' expression { $$ = $1 $3; } | expression '*' expression { $$ = $1 $3; } | expression '/' expression { $$ = $1 $3; } | '(' expression ')' { $$ = $2; }

$3; } + * /

void yyerror(char *s) { fprintf(stderr, "%s\n", s); } int main(void) { yyparse(); }

Fisier specificatii ptr flex (calc.l):

%{ /* fis. calc.l */ #include <stdlib.h> #include "calc.tab.h" void yyerror(char *); %} o o %% [a-z] { yylval = *yytext - 'a'; return VAR; } [0-9]+ { yylval = atoi(yytext); return CINT; } [-+()=/*\n] { return *yytext; } /*Variabila yytext este pointer la unitatea lexicala

tocmai depistata, de aceea pentru operatori (token-uri semn) definiia n fisierul flex*/

27

FLEX + BISON

[ \t] ; /* ignora spatiile */ . yyerror("Caracter necunoscut"); %% int yywrap(void) { return 1; }

Aplicaia rezultata (calc.exe) poate "funciona" astfel: x = 30 (intrare) a = 5*6 (intr.) x+a (intr.) 60 (rezultat afisat!!!!!) Exemplul 5: Sa se realizeze un parser care recunoate toate irurile limbajului: L = {w | w = anbn, n>0} REZOLVARE: Gramatica care genereaza acest limbaj este: A ^ aAb | ab (terminalii - tokenii - a si b). Fisierul de specificatii pentru bison (ab.y):
%{ /* ab.y */ #include <stdio.h> #include "ab.tab.h" %} %token TOKEN_A %token TOKEN_B %% S : S T '\n' {printf("Sir recunoscut: n= %d\n", $2); } | S '\n' { /* linie goala tastata */ } | S '.' {return; } | /* lambda, regula implicita */ T : TOKEN_A T TOKEN_B { $$ = $2 + 1;} | TOKEN_A TOKEN_B { $$ = 1; } ; %% int main() { yyparse(); return 0;} int yyerror(char *s) {printf("%s \n", s);}

Regulile gramaticii - in fisierul al.y. Parser-ul are o procesare pe linii. Am "imbunatatit" putin gramatica. Prima seciune defineste cei doi atomi. A doua seciune descrie gramatica, cu 6 productii (4 pentru S si 2 pentru T). Seciunea de subrutine contine functiile main() si yyparse() (pentru tratarea erorilor de sintaxa). Sa analizam sectiunea care descrie gramatica. Deoarece prntru simbolul de start, S, exista o X-productie, acesta este redus implicit (fara a citi nimic de pe banda de intrare), parser-ul plaseaza S in vf. stivei. Pentru prima productie a lui S, se detecteaza o linie completa care contine un cuvant din limbajul specificat; sa va afisa n. Observatie ca $2 reprezinta valoarea celui de-al doilea neterminal din partea dreapta a regulii (T), adica un cuv. al limbajului. A doua productie a lui S precizeaza actiunea cand se gaseste o linie vida (daca se introduce newline si pe stiva nu era decat S, atunci acest neterminal se pastreaza). A treia productie a lui S va executa

FLEX + BISON return din analizorul sintactic atunci cand utilizatorul introduce punct (se incheie procesarea). Productiile pentru T sunt relativ familiare, insa sa analizam actiunile de realizat. Pentru a doua productie a lui T se detecteaza un n ucleu ab, ir valoarea asociata neterminalului rezultat al reducerii acestui sir este 1 (s-a recunoscut un sir al limbajului cu n=1). Prima productie pentru T recunoaste recursiv siruri de dimensiuni din ce in ce mai mari, iar valoarea neterminalului in care se reduce contex recunoscut se calculeaza prin incrementarea valorii n eterminalului incadrat intre un a si un b care constituie capatul. Fiserul de specificatii pentru flex (ab.l):
%{ /* ab.l limbaj anbn, n>0 */ #include "ab.tab.h" %}

%% a {return TOKEN_A;} b {return TOKEN_B;} \. {return yytext[0]; } . {printf("atomul %s nu este recunoscut\n", yytext); } \n {return yytext[0]; } %%

Prima regula: fiecare aparitie a lui a in textul sursa reprezinta TOKEN_A. Regula a doua: fiecare aparitie a lui b - TOKEN_B. Regula a treia: recunoaste punctul. La intalnirea lui, scanner-ul il recunoaste si il paseaza parser-ului ca atare, fara a-i asocia vreun cod. Regula a patra: se potriveste tuturor celorlalte caractere nerecunoscute la intrare si afiseaza un mesaj de eroare (nu se termina cu return, astfel ca scaner-ul pastreaza mai departe controlul, adica tokenul recunoscut de aceasta regula este ignorat de parser). Regula a cincea: recunoaste car. newline si il transmite ca atare parser-ului. Sectiunea de subrutine - nimic. Exemplul 6: Vom descrie n acest paragraf modul cum se poate proiecta un interpreter folosind instrumentele flex i bison. Dorim s interpretm programe de forma:
a = 3 6; b = 42; while (a != b) if(a > b) a = a - b; else b = b - a; print a;

REZOLVARE: Pentru aceasta vom descrie fiierele de intrare pentru flex respectiv bison. Fiierul pentru flex descrie unitile lexicale care vor fi transmise analizorului sintactic construit de bison. Fisierul flex este urmtorul: /* Fisierul glang.l intrare pentru flex. Se foloseste comanda flex glang.l dupa comanda bison -d glang.y */
%{ #include <stdlib.h> #include "glang.h" #include "glang.tab.h" void yyerror(char *); %} o o. %% [a-z] { yylval.sIndex = *yytext - 'a'; return VAR; } 29

FLEX + BISON

[0-9]+ { yylval.iVal = atoi(yytext); return INT; } [-()<>=+*/;{}.] { return *yytext;} ">=" { return GE; } "<=" { return LE; } "==" { return EQ; } "!=" { return NE; } "while" { return WHILE; } "if" { return IF; } "else" { return ELSE; } "print" { return PRINT; } [ \t\n]+ ; /* Spatiile sunt ignorate */ . yyerror(" Caracter ilegal!\n"); o o. %% int yywrap(void) { return 1; }

Fiierul glang.h descrie structurile de date folosite pentru reprezentarea constantelor, a identificatorilor precum i a tipurilor de noduri din arborele abstract care va fi costruit de anlizorul sintactic. Identificatorii sunt definii aici ca fiind litere i de aceea am folosit extern int sym[26] ca tabel a identificatorilor. O s indicm mai trziu cum se poate aborda cazul general.
/* Fisierul glang.h */ typedef enum { typeCon, typeId, typeOper } nodeEnum; /* constante */ typedef struct { int val; /* valoarea constantei */ } conNodeType; /* identificatori */ typedef struct { int i; /* indice in tabela de simboluri */ } idNodeType; /* operatori */ typedef struct { int oper; /* operator */ int nops; /* nr. operanzi */ struct nodeTypeTag *op[1]; /* operanzi */ } oprNodeType; typedef struct nodeTypeTag { nodeEnum type; /* tipul nodului */ union { conNodeType con; /* constante */ idNodeType id; /* identificatori */ oprNodeType opr; /* operatori */ }; } nodeType; extern int sym[26];

Fiierul glang.tab.h este obinut de ctre yacc (bison) atunci cnd se folosete opiunea - d. Acest fiier definete codurile numerice pentru unitile lexicale: /* Fisierul glang.tab.h creat de bison cu optiunea -d */
#ifndef BISON_GLANG_TAB_H #define BISON_GLANG_TAB_H #ifndef YYSTYPE typedef union { int iVal; /* valoare intreaga */ char sIndex; /* index in tabela de simboluri */ nodeType *nodPtr; /* pointer la un nod in arbore */

FLEX + BISON
} yystyp ; e # define YYSTYPE yystype # define YYSTYPE IS TRIVIAL 1 #endif # define INT 257 # define VAR 25 8 # define WHILE 259 # define IF 260 # define PRINT 261 # define IFX 262 # define ELSE 263 # define GE 264 # define LE 2 65 # define EQ 266 # define NE 2 67 # define UMINUS 2 68 extern YYSTYPE yylval; #endif /* not BISON_GLANG_TAB_H */

Fiierul de intrare pentru bison descrie sintaxa limbajului de programare mpreun cu aciunile semantice ce trebuiesc aplicate n cazul reducerilor. Aici este vorba de apelul funciei de construire a arborelui abstract corespunztor. Iat mai jos fiierul de intrare pentru bison:

/* Fisierul glang.y intrare pentru bison. Se foloseste comanda : bison -d glang.y */ %{ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include "glang.h" /* prototipurile functiilor */ nodeType *nodOper(int oper, int nops, ...); /* nod operator in arbore */ nodeType *nodId(int i); /* nod frunza identificator */ nodeType *nodCon(int value); /* nod frunza constanta */ void freeNode(nodeType *p); /* eliberare memorie */ int interpret(nodeType *p); /* functia de interpretare */ int yylex(void); /* functia creata de flex */ void yyerror(char *s); int sym[26]; /* tabela de simboluri */ %} %union { int iVal; /* valoare intreaga */ char sIndex; /* index in tabela de simboluri */ nodeType *nodPtr; /* pointer la un nod in arbore */ }; %token <iVal> INT %token <sIndex> VAR %token WHILE IF PRINT %nonassoc IFX /* rezolvarea ambiguitatii if-then-else */ %nonassoc ELSE %left GE LE EQ NE '>' '<' %left '+' '-' %left '*' '/' %nonassoc UMINUS %type <nodPtr> stmt expr stmt list o o. %% program: function { exit(0); } ; function: function stmt { interpret($2); freeNode($2); } | /* NULL */ ; stmt: ';' { $$ = nodOper(';', 2, NULL, NULL); } | expr ';' { $$ = $1; } | PRINT expr ';' { $$ = nodOper(PRINT, 1, $2); } | VAR '=' expr ';' 31

FLEX + BISON

{$$ = nodOper('=', 2, nodId($1), $3);} | WHILE '(' expr ')' stmt { $$ = nodOper(WHILE, 2, $3, $5); } | IF '(' expr ')' stmt %prec IFX { $$ = nodOper(IF, 2, $3, $5); } | IF '(' expr ')' stmt ELSE stmt { $$ = nodOper(IF, 3, $3, $5, $7); } | '{' stmt_list '}' { $$ = $2; } stmt list: "stmt { $$ = $1; } | stmt_list stmt { $$ = expr: INT { $$ = nodCon($1); nodId($1); } | '-' expr { $$ =

nodOper(';', 2, $1, $2); } ;

} | VAR { $$ = %prec UMINUS nodOper UMINUS , expr '+ expr ' { $$ = nodOper '+ , ' 2, expr '- expr ' { $$ = nodOper '- , ' 2, expr expr { $$ = nodOper , 2, expr '/ expr ' { $$ = nodOper , 2, expr '< expr ' { $$ = nodOper '< , ' 2, expr '> expr ' { $$ = nodOper '> , ' 2, expr GE expr { $$ = nodOper GE 2, , expr LE expr { $$ = nodOper LE 2, , expr NE expr { $$ = nodOper NE 2, , expr EQ expr { $$ = nodOper EQ 2, , '(' expr ')' $$ = $2 } ;

1, $2); }

$1 , $1 , $1 , $1 , $1 , $1 ,

$3) ; $3) ; $3) ; $3) ; $3) ; $3) ;

$1, $3); } $1, $3); } $1, $3); } $1, $3); }

o o oo

FLEX + BISON
#define SIZEOF_NODETYPE ((char *)&p->con - (char *)p) nodeType *nodCon(int value) { nodeType *p; size t nodeSize; /* alocare memorie pentru noul nod */ nodeSize = SIZEOF_NODETYPE + sizeof(conNodeType); if ((p = malloc(nodeSize)) == NULL) yyerror("out of memory"); /* copiere valoare constanta */ p->type = typeCon; p->con.val = value; return p; } nodeType *nodId(int i) { nodeType *p; size t nodeSize; /* alocare memorie pentru noul nod */ nodeSize = SIZEOF_NODETYPE + sizeof(idNodeType); if ((p = malloc(nodeSize)) == NULL) yyerror("out of memory"); /* copiere valoare indice */ p->type = typeId; p->id.i = i; return p; } nodeType *nodOper(int oper, int nops, ...) { va list ap;nodeType *p; size t nodeSize; int i; /* allocare memorie pentru noul nod */ nodeSize = SIZEOF_NODETYPE + sizeof(oprNodeType) + (nops - 1) * sizeof(nodeType*); if ((p = malloc(nodeSize)) == NULL) yyerror("out of memory"); /* copiere informatii functie de nops */ p->type = typeOper; p->opr.oper = oper; p->opr.nops = nops; va start(ap, nops); for (i = 0; i < nops; i++) p->opr.op[i] = va arg(ap, nodeType*); va end(ap); return p;

void freeNode(nodeType *p) { int i; if (!p) return; if (p->type == typeOper) { for (i = 0; i < p->opr.nops; i++) freeNode(p->opr.op[i]); } free (p); } void yyerror(char *s) { fprintf(stdout, "%s\n", s); } int main(void) { yyparse(); return 0;}

Conform regulilor urmtoare (din fiierul tocmai prezentat):


function: function stmt { interpret($2); freeNode($2); } | /* NULL */ ;

un program n limbajul surs descris este un ir de instruciuni (stmt). Fiecare din aceste instruciuni este interpretat: apelul funciei interpret pentru argumentul $2 care este valoarea 33

FLEX + BISON

atributului lui stm, adic arborele construit pentru stmt, face ca aceast instruciune s fie executat. Prin freeNode($2) se elibereaz memoria alocat la construirea arborelui amintit. Funcia interpret() este prezentat mai jos : /* Functia interpret(nodeType *p) */
#include <stdio.h> #include "glang.h" #include "glang.tab.h" int interpret(nodeType *p) { if (!p) return 0; switch(p->type) { case typeCon: return p->con.val; case typeId: return sym[p->id.i]; case typeOper: switch(p->opr.oper) { case WHILE: while(interpret(p->opr.op[0])) interpret(p->opr.op[1]); return 0; case IF: if (interpret(p>opr.op[0])) interpret(p->opr.op[1]); else if (p->opr.nops > 2) interpret(p->opr.op[2]); return 0; case PRINT: printf("%d\n", interpret(p->opr.op[0])); return 0; case ';': interpret(p->opr.op[0]); return interpret(p->opr.op[1]); case '=': return sym[p->opr.op[0]>id.i] = interpret(p->opr.op[1]); case UMINUS: return -interpret(p->opr.op[0]); case '+': return interpret(p->opr.op[0]) + interpret(p->opr.op[1]); case '-': return interpret(p->opr.op[0]) interpret(p->opr.op[1]); case '*': return interpret(p->opr.op[0]) * interpret(p->opr.op[1]); case '/': return interpret(p>opr.op[0]) / interpret(p->opr.op[1]); case '<': return interpret(p->opr.op[0]) < interpret(p->opr.op[1]); case '>': return interpret(p->opr.op[0]) > interpret(p->opr.op[1]); case GE: return interpret(p->opr.op[0]) >= interpret(p->opr.op[1]); case LE: return interpret(p->opr.op[0]) <= interpret(p->opr.op[1]); case NE: return interpret(p->opr.op[0]) ! = interpret(p->opr.op[1]); case EQ: return interpret(p->opr.op[0]) == interpret(p->opr.op[1]); } } return 0; }

n urma lansrii comenzilor bison -dv glang.y i flex -oglang.yy.c glang.l se obin fiierele glang.tab.c (analizorul sintactic), glang.output, glang.tab.h, respectiv glang.yy.c (analizorul lexical). Fisierele glang.tab.c si glang.yy.c, impreun cu interpretorul din fiierul interpret.c, vor fi compilate cu un compilator de limbaj C i se obine astfel un interpreter pentru limbajul surs descris. (gcc -o glang.exe glang.tab.c glang.yy.c interpret.c -lm -lfl) Dac lansm acest interpreter pentru programul prezentat la nceputul exemplului, se obine rezultatul 6.

S-ar putea să vă placă și