lfac9

50
Curs 9 - plan Sarcinile unui parser Gramatici CF remember Metode de parsare Generatoare de parsere Parsare cu Yacc / Bison Specificații Yacc Utilizare Lex / Flex cu Yacc / Bison Parsare top - down

Upload: madalina-georgiana

Post on 30-Jul-2015

95 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Lfac9

Curs 9 - plan

• Sarcinile unui parser

• Gramatici CF – remember

• Metode de parsare

• Generatoare de parsere

• Parsare cu Yacc / Bison

– Specificații Yacc

– Utilizare Lex / Flex cu Yacc / Bison

• Parsare top - down

Page 2: Lfac9

Parserul

2

Lexical

Analyzer

Parser

and rest of

front-end

Source

Program

Token,

tokenval

Symbol Table

Get next

token

Lexical error Syntax error

Semantic error

Intermediate

representation

Page 3: Lfac9

Parserul

• Un parser implementează o gramatică CF

• Rolul unui parser:

– Verifică sintaxa(= string recognizer)

• raportare cu precizie a erorilor

– Invocă acțiunile semantice:

• semantica statică: verificarea tipului expresiilor, funcțiilor etc.

• traducere orientată sintactic: cod sursă pentru reprezentarea intermediară

3

Page 4: Lfac9

Traducere ”Syntax-Directed”

• Un rol important al parserului: producerea unei reprezentări intermediare a programului sursă folosind metode de traducere ”dirijate sintactic”

• Reprezentări intermediare posibile:

– Arbore abstract(ASTs)

– Cod cu trei adrese (quadruple)

– Forma poloneză

4

Page 5: Lfac9

Tratarea erorilor • Identificarea și localizarea erorilor

– Erori lexicale : importante, compilatorul le

poate recupera ușor și poate continua

– Erori sintactice: cele mai importante pentru compilator, pot fi aproape totdeauna recuperate

5

Page 6: Lfac9

Tratarea erorilor • Identificarea și localizarea erorilor

– Erori de semantică statică : importante, uneori pot fi recuperate

– Erori de semantică dinamică: greu sau imposibile de detectat la momentul compilării, e nevoie de verificat la execuție

– Erori logice: greu sau imposibil de detectat

6

Page 7: Lfac9

Proprietatea ”Viable-Prefix”

• Proprietatea viable - prefix a parserelor LL/LR permite detectarea timpurie a erorilor de sintaxă

– Scop: detectarea unei erori cât mai devreme posibil fără a se consuma intrări nenecesare

– Cum: detectarea unei erori atunci când prefixul cuvântului de la intrare nu se potrivește cu nici un cuvânt din limbaj

7

for (;)

DO 10 I = 1;0

Error is

detected here

Error is

detected here

Prefix Prefix

Page 8: Lfac9

Strategii de recuperare a erorilor • Modul ”panică”

– Renunțarea la simboluri de intrare până când se găsește un token dintr-o mulțime specificată

• Recuperare la nivel de frază – Efectuarea de corecții locale pentru a repara eroarea

• Producții ”error” – Se mărește gramatica cu producții pentru construcții

eronate

– Se alege o secvență minimă de modificări pentru a obține o corecție de cost minim

8

Page 9: Lfac9

Gramatici (Recap)

G = (N, T, S, P)

– T simboluri terminale

–N simboluri neterminale

–P o mulțime finită de producții de forma (NT)* N (NT)* , (NT)*

– S N simbol de start

9

Page 10: Lfac9

Convenții • Terminali:

a, b, c, … T 0, 1, id, +

• Neterminali: A, B, C ,… N expr, term, stmt

10

• Simboluri generice: X, Y, Z (NT)

• Șiruri de terminali: u, v, w, x, y, z T*

• Șiruri oarecare: , , (NT)*

Page 11: Lfac9

Derivări (Recap) • Derivare one-step :

A unde A P

• Derivări extrem stângi (drepte): –lm dacă nu conține nici un neterminal –rm dacă nu conține nici un neterminal

• Închidere reflexivă și tranzitivă * (zero sau mai mulți pași)

• Închidere tranzitivă + (unul sau mai mulți pași)

• Limbajul generat de G: L(G) = {w T* | S + w}

11

Page 12: Lfac9

Derivări

12

G = ({E}, {+,*,(,),-,id}, P, E)

P = E E + E

E E * E

E ( E )

E - E

E id

E - E - id

E * E

E + id * id + id

E rm E + E rm E + id rm id + id

Derivări:

E * id + id

Page 13: Lfac9

Metode de parsare • Universal (orice gramatică CF)

– Cocke-Younger-Kasami (CYK) – Earley

• Top-down (gramatici CF cu restricții) – Recursiv descendent (predictive parsing) – Metode LL (Left-to-right, Leftmost derivation)

• Bottom-up (gramatici CF cu restricții) – Parsare bazată pe relații de precedență – Metode LR (Left-to-right, Rightmost derivation)

• SLR, LR, LALR

13

Page 14: Lfac9

Generatoare de parsere

• ANTLR (http://www.antlr.org/) – Generează parsere LL(k)

• YACC (Yet Another Compiler Compiler) – 1975 S. C. Johnson la Bell Laboratory

– Generează parsere LALR(1)

• Bison – Versiunea îmbunătățită a lui Yacc

– Robert Corbett şi Richard Stallmann (2006 – versiunea 2.3)

14

Page 15: Lfac9

Generatoare de parsere

• http://dinosaur.compilertools.net/

• http://epaperpress.com/lexandyacc/index.html

• http://www.gnu.org/software/bison/

• http://catalog.compilertools.net/lexparse.html

• http://members.cox.net/slkpg/

• http://wiki.python.org/moin/LanguageParsing

• http://scottmcpeak.com/elkhound/

15

Page 16: Lfac9

Crearea unui parser cu Yacc/Bison

16

Yacc or Bison

compiler

yacc

specification yacc.y

y.tab.c

input

stream

C

compiler

a.out output

stream

y.tab.c

a.out

Page 17: Lfac9

Specificații Yacc • O specificație yacc constă din 3 părți:

declarații yacc, eventual declarații C în %{ %} %% reguli de traducere %% proceduri auxiliare definite de utilizator

17

Page 18: Lfac9

Specificații Yacc • regulile de traducere sunt de forma:

production1 { semantic action1 } production2 { semantic action2 } … productionn { semantic actionn }

18

Page 19: Lfac9

Scrierea unei gramatici în Yacc • Producțiile sunt de forma:

Nonterminal : tokens/nonterminals | tokens/nonterminals … ;

• Token-urile definite ca un singur caracter pot fi incluse direct, de exemplu ‘+’

• Token-urile ce au un nume trebuiesc declarate în partea de declarații sub forma: %token NumeToken

19

Page 20: Lfac9

Atribute sintetizate • Acțiunile semantice pot referi valori ale

atributelor sintetizate ale terminalilor și neterminalilor din producția respectivă :

X : Y1 Y2 Y3 … Yn { acțiune }

• acțiune: expresie ce conține $$ și $i – $$ referă valoarea atributului lui X – $i referă valoarea atributului lui Yi

• De exemplu factor : ‘(’ expr ‘)’ { $$=$2; }

20

Page 21: Lfac9

Exemplul 1

21

%{ #include <ctype.h> %}

%token DIGIT

%%

line : expr ‘\n’ { printf(“%d\n”, $1); }

;

expr : expr ‘+’ term { $$ = $1 + $3; }

| term { $$ = $1; }

;

term : term ‘*’ factor { $$ = $1 * $3; }

| factor { $$ = $1; }

;

factor : ‘(’ expr ‘)’ { $$ = $2; }

| DIGIT { $$ = $1; }

;

%%

int yylex()

{ int c = getchar();

if (isdigit(c))

{ yylval = c-’0’;

return DIGIT;

}

return c;

}

Also results in definition of #define DIGIT xxx

Attribute of token (stored in yylval)

Attribute of term (parent)

Attribute of factor (child)

Example of a very crude lexical

analyzer invoked by the parser

Page 22: Lfac9

Procesare ambiguități

• Se pot specifica gramatici ambigui dacă se precizează precedența operatorilor și asociativitatea stângă sau dreaptă :

E E+E | E-E | E*E | E/E | (E) | -E | num

• În partea de declarații se specifică:

%left ‘+’ ‘-’

%left ‘*’ ‘/’

%right UMINUS

22

Page 23: Lfac9

Exemplul 2

23

%{

#include <ctype.h>

#include <stdio.h>

#define YYSTYPE double

%}

%token NUMBER

%left ‘+’ ‘-’

%left ‘*’ ‘/’

%right UMINUS

%%

lines : lines expr ‘\n’ { printf(“%g\n”, $2); }

| lines ‘\n’

| /* empty */

;

expr : expr ‘+’ expr { $$ = $1 + $3; }

| expr ‘-’ expr { $$ = $1 - $3; }

| expr ‘*’ expr { $$ = $1 * $3; }

| expr ‘/’ expr { $$ = $1 / $3; }

| ‘(’ expr ‘)’ { $$ = $2; }

| ‘-’ expr %prec UMINUS { $$ = -$2; }

| NUMBER

;

%%

Double type for attributes and yylval

Page 24: Lfac9

Exemplul 2 (cont)

24

%%

int yylex()

{ int c;

while ((c = getchar()) == ‘ ‘)

;

if ((c == ‘.’) || isdigit(c))

{ ungetc(c, stdin);

scanf(“%lf”, &yylval);

return NUMBER;

}

return c;

}

int main()

{ if (yyparse() != 0)

fprintf(stderr, “Abnormal exit\n”);

return 0;

}

int yyerror(char *s)

{ fprintf(stderr, “Error: %s\n”, s);

}

Run the parser

Crude lexical analyzer for

fp doubles and arithmetic

operators

Invoked by parser

to report parse errors

Page 25: Lfac9

Utilizare Lex/Flex cu Yacc/Bison

25

Yacc or Bison

compiler

yacc

specification yacc.y

lex.yy.c

y.tab.c

input

stream

C

compiler

a.out output

stream

y.tab.c

y.tab.h

a.out

Lex or Flex

compiler

Lex specification lex.l

and token definitions y.tab.h

lex.yy.c

Page 26: Lfac9

Specificații Lex pentru Exemplul 2

26

%option noyywrap

%{

#include “y.tab.h”

extern double yylval;

%}

number [0-9]+\.?|[0-9]*\.[0-9]+

%%

[ ] { /* skip blanks */ }

{number} { sscanf(yytext, “%lf”, &yylval);

return NUMBER;

}

\n|. { return yytext[0]; }

Generated by Yacc, contains #define NUMBER xxx

yacc -d example2.y

lex example2.l

gcc y.tab.c lex.yy.c

./a.out

bison -d -y example2.y

flex example2.l

gcc y.tab.c lex.yy.c

./a.out

Defined in y.tab.c

Page 27: Lfac9

Recuperare erori în Yacc

27

%{

%}

%%

lines : lines expr ‘\n’ { printf(“%g\n”, $2; }

| lines ‘\n’

| /* empty */

| error ‘\n’ { yyerror(“reenter last line: ”);

yyerrok;

}

;

Reset parser to normal mode Error production:

set error mode and

skip input until newline

Page 28: Lfac9

Parsare Top-Down

• Metode LL (Left-to-right, Leftmost derivation) și parsare recursiv-descendentă

28

Gramatica:

E T + T

T ( E )

T - E

T id

Derivare extrem stângă:

E lm T + T

lm id + T

lm id + id

+

E E

T

+

T

id id

E

T T

E

T

+

T

id

Page 29: Lfac9

Left Recursion (Recap)

• Producțiile de forma A A | | sunt stâng recursive

• În cazul existenței recursiei stângi un parser predictiv ciclează la nesfârșit pentru anumite intrări

29

Page 30: Lfac9

Eliminarea recursiei stângi

30

Se stabilește o ordine a neterminalilor: A1, A2, …, An

for i = 1, …, n do

for j = 1, …, i-1 do

replace each

Ai Aj

with

Ai 1 | 2 | … | k

where

Aj 1 | 2 | … | k

enddo

eliminate the immediate left recursion in Ai

enddo

Page 31: Lfac9

Eliminarea recursiei stângi imediate

31

Producțiile (recursie stânga):

A A

|

|

| A

se înlocuiesc cu (recursie dreapta):

A AR

| AR

AR AR

| AR

|

Page 32: Lfac9

Exemplu

32

A B C | a

B C A | A b

C A B | C C | a

Ordinea: A, B, C

i = 1: -

i = 2, j = 1: B C A | A b

B C A | B C b | a b

(imm) B C A BR | a b BR

BR C b BR |

i = 3, j = 1: C A B | C C | a

C B C B | a B | C C | a

i = 3, j = 2: C B C B | a B | C C | a

C C A BR C B | a b BR C B | a B | C C | a

(imm) C a b BR C B CR | a B CR | a CR

CR A BR C B CR | C CR |

Page 33: Lfac9

Factorizare stângă

• Dacă un neterminal are două sau mai multe producții care în partea dreaptă încep cu aceleași simboluri, gramatica nu este LL(1) și nu poate fi parsată predictiv

• Se înlocuiesc producțiile A 1 | 2 | … | n | cu: A AR | AR 1 | 2 | … | n

33

Page 34: Lfac9

Parsare predictivă • Se elimină recursia stângă (dacă este cazul)

• Se factorizează stânga (dacă este cazul)

• Se determină FIRST și FOLLOW

• Se alege una din variante: – Recursivă (recursive calls)

– Non-recursivă (table - driven)

34

Page 35: Lfac9

FIRST

• FIRST() = { mulțimea terminalilor ce încep cuvintele derivate din (incluzând )} FIRST(a) = {a} if a T FIRST() = {} FIRST(A) = A FIRST() for A P FIRST(X1X2…Xk) = if for all j = 1, …, i-1 : FIRST(Xj) then add non- in FIRST(Xi) to FIRST(X1X2…Xk) if for all j = 1, …, k : FIRST(Xj) then add to FIRST(X1X2…Xk)

35

Page 36: Lfac9

FOLLOW • FOLLOW(A) = { mulțimea terminalilor ce pot urma

imediat după neterminalul A în formele propoziționale } FOLLOW(A) = for all (B A ) P do add FIRST()\{} to FOLLOW(A)

if A is the start symbol S then add $ to FOLLOW(A) for all (B A ) P and FIRST() do add FOLLOW(B) to FOLLOW(A) for all (B A) P do add FOLLOW(B) to FOLLOW(A)

36

Page 37: Lfac9

Gramatici LL(1) • O gramatică G este LL(1) dacă nu este stâng

recursivă și pentru orice colecție de producții: A 1 | 2 | … | n pentru neterminalul A au loc: 1. FIRST(i) FIRST(j) = for all i j 2. if i

* then 2.a. j

* for all i j 2.b. FIRST(j) FOLLOW(A) = for all i j

echivalent cu: FIRST(i FOLLOW(A)) FIRST(j FOLLOW(A)) = for all i j

37

Page 38: Lfac9

Exemple S E | B E

B a | begin SC end C | ; SC

S ABC A aA |

B bB |

C cC |

S a R a R S |

38

Page 39: Lfac9

Exemple Non-LL(1)

39

Gramatica Nu este LL(1) deoarece:

S S a | a Este stâng recursiva

S a S | a FIRST(a S) FIRST(a)

S a R |

R S | R: S * and *

S a R a

R S |

R:

FIRST(S) FOLLOW(R)

Page 40: Lfac9

Parsare recursiv descendentă • Gramatica trebuie să fie LL(1)

• Pentru fiecare neterminal se construiește o procedură (eventual recursivă) care realizează parsarea categoriei sintactice corespunzătoare acelui neterminal

• Când un neterminal are producții multiple, fiecare producție este implementată într-o ramură a instrucțiunii de selectare, corespunzătoare informațiilor din intrare (look-ahead information)

40

Page 41: Lfac9

Utilizare FIRST și FOLLOW

41

expr term rest

rest + term rest

| - term rest

|

term id

procedure rest();

begin

if lookahead in FIRST(+ term rest) then

match(‘+’); term(); rest()

else if lookahead in FIRST(- term rest) then

match(‘-’); term(); rest()

else if lookahead in FOLLOW(rest) then

return

else error()

end;

FIRST(+ term rest) = { + }

FIRST(- term rest) = { - }

FOLLOW(rest) = { $ }

Page 42: Lfac9

Parsare predictivă ne-recursivă (bazată pe tabela de parsare)

• Dată o gramatică LL(1) G = (N, T, P, S) se construiește o tabelă M[A,a] pentru fiecare A N, a T și un ”driver program” cu o stivă:

42

Predictive parsing

program (driver)

Parsing table

M

a + b $

X

Y

Z

$

stack

input

output

Page 43: Lfac9

Construirea Tabelei de parsare LL(1)

43

for each production A do

for each a FIRST() do // a

add A to M[A, a]

enddo

if FIRST() then

for each b FOLLOW(A) do

add A to M[A, b]

enddo

endif

enddo

Mark each undefined entry in M error

Page 44: Lfac9

Exemplu

44

E T ER

ER + T ER |

T F TR

TR * F TR |

F ( E ) | id

id + * ( ) $

E E T ER E T ER

ER ER + T ER ER ER

T T F TR T F TR

TR TR TR * F TR TR TR

F F id F ( E )

A FIRST() FOLLOW(A)

E T ER ( id $ )

ER + T ER + $ )

ER $ )

T F TR ( id + $ )

TR * F TR * + $ )

TR + $ )

F ( E ) ( * + $ )

F id id * + $ )

Page 45: Lfac9

LL(1) vs. ambiguitate

45

Gramatică ambiguă

S i E t S SR | a

SR e S |

E b

a b e i t $

S S a S i E t S SR

SR SR

SR e S SR

E E b

A FIRST() FOLLOW(A)

S i E t S SR i e $

S a a e $

SR e S e e $

SR e $

E b b t Error: duplicate table entry

Page 46: Lfac9

Parsare predictivă: Programul (Driver)

46

push($)

push(S)

a := lookahead

repeat

X := pop()

if X is a terminal or X = $ then

match(X) // moves to next token and a := lookahead

else if M[X, a] = X Y1Y2…Yk then

push(Yk, Yk-1, …, Y2, Y1) // such that Y1 is on top

… invoke actions and/or produce output …

else error()

endif

until X = $

Page 47: Lfac9

Exemplu

47

Stack

$E

$ERT

$ERTRF

$ERTRid

$ERTR

$ER

$ERT+

$ERT

$ERTRF

$ERTRid

$ERTR

$ERTRF*

$ERTRF

$ERTRid

$ERTR

$ER

$

Input

id+id*id$

id+id*id$

id+id*id$

id+id*id$

+id*id$

+id*id$

+id*id$

id*id$

id*id$

id*id$

*id$

*id$

id$

id$

$

$

$

Production applied

E T ER

T F TR

F id

TR

ER + T ER

T F TR

F id

TR * F TR

F id

TR

ER

Page 48: Lfac9

Panic Mode Recovery

48

id + * ( ) $

E E T ER E T ER synch synch

ER ER + T ER ER ER

T T F TR synch T F TR synch synch

TR TR TR * F TR TR TR

F F id synch synch F ( E ) synch synch

FOLLOW(E) = { ) $ }

FOLLOW(ER) = { ) $ }

FOLLOW(T) = { + ) $ }

FOLLOW(TR) = { + ) $ }

FOLLOW(F) = { + * ) $ }

Add synchronizing actions to

undefined entries based on FOLLOW

synch: the driver pops current nonterminal A and skips input till

synch token or skips input until one of FIRST(A) is found

Pro: Can be automated

Cons: Error messages are needed

Page 49: Lfac9

Phrase-Level Recovery

49

id + * ( ) $

E E T ER E T ER synch synch

ER ER + T ER ER ER

T T F TR synch T F TR synch synch

TR insert * TR TR * F TR TR TR

F F id synch synch F ( E ) synch synch

Change input stream by inserting missing tokens

For example: id id is changed into id * id

insert *: driver inserts missing * and retries the production

Can then continue here

Pro: Can be automated

Cons: Recovery not always intuitive

Page 50: Lfac9

Error Productions

50

id + * ( ) $

E E T ER E T ER synch synch

ER ER + T ER ER ER

T T F TR synch T F TR synch synch

TR TR F TR TR TR * F TR TR TR

F F id synch synch F ( E ) synch synch

E T ER

ER + T ER |

T F TR

TR * F TR |

F ( E ) | id

Add “error production”:

TR F TR

to ignore missing *, e.g.: id id

Pro: Powerful recovery method

Cons: Cannot be automated