tópicos em compiladores - udesc · programa fonte caractere por caractere agrupando-os em...

Post on 15-May-2018

225 Views

Category:

Documents

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Compiladores

Cristiano Damiani Vasconcellos

cristiano.vasconcellos@udesc.br

Bibliografia Recomendada

Aho, Alfred V.; Lam, Monica S.; Sethi, Ravi; Ullman, Jeffrey D.;

Compiladores: Princípios, Técnicas e Ferramentas. Pearson.

Cooper, Keith D.; Torczon, Linda.; Construindo compiladores. Elsevier.

Bryant, Randal E.; O'Hallaron, David R.; Computer Systems: A

Programmer's Perspective. Prentice Hall.

Introdução

(Construção de um Executável)

Pré-processador (cpp)

prog.c

Compilador (cc1)

Montador (as)

prog.i

prog.as

prog.o Editor de Ligação (ld)

outro.o

biblioteca.lib

prog

(executável)

Introdução

(Fases de um Compilador)

Analisador Léxico

Analisador Sintático

Analisador Semântico

Gerador de Código(intermediário)

Otimizador

Gerador de Código

front-end

back-end

Introdução

final = (nota1 + nota2) / 2;

Analisador Léxico

Id1 = (Id2 + Id3) / 2

Analisador Sintático

=

Id1 /

+

Id2 Id3

2

Id1 final double ...

Id2 nota1 double ...

Id3 nota2 double ...

...

Tabela de Símbolos

Árvore Sintática

Abstrata.

Introdução

=

Id1 /

+

Id2 Id3

intToDouble(2)

Id1 final double ...

Id2 nota1 double ...

Id3 nota2 double ...

...

Tabela de Símbolos

Analisador Semântico

temp1 = Id2 + Id3

temp2 = temp1 / 2.0

Id1 = temp2

Gerador de Código

(intermediário)

Análise Léxica

O Analisador Léxico (scanner) examina o

programa fonte caractere por caractere

agrupando-os em conjuntos com um significado

coletivo (tokens):

• palavras chave (if, else, while, int, etc),

• operadores (+, -, *, /, ^, &&, etc),

• constantes (1, 1.0, ‘a’, 1.0f, etc),

• literais (“Alo Mundo”, etc),

• símbolos de pontuação (; , {, }, etc),

• labels.

Análise Léxica

constanteInt digito digito*

constanteDouble digito digito*. digito*

digito {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

X* Representa uma seqüência de zero ou mais X.

Análise Sintática

Verifica se as frases obedecem as regrassintáticas da linguagem:

Por exemplo, uma expressão pode ser definidacomo:

expressão + expressão

expressão – expressão

(expressão)

constante

Gramáticas Livres de

Contexto

Definidas por uma quádrupla (VN, VT, S, P), onde:

VN é um conjunto de símbolos não terminais(representam as construções sintáticas dalinguagem).

VT é um conjunto de símbolos terminais (tokens dalinguagem).

S VN é o símbolo inicial da gramática.

P é um conjunto de regras de produção, paresordenados representados na forma , onde VN e (VN VT)*.

Gramáticas Livres de

Contexto

<expr> <expr> + <expr>

| <expr> – <expr>

| (<expr>)

| <const>

<const> <const><const>

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9

Derivação

Verificar se uma frase faz parte da linguagemgerada pela gramática, envolve sucessivassubstituições dos símbolos que ocorrem dolado esquerdo da produção pela suaconstrução sintática correspondente.

Essa substituição é chamada derivação sendonormalmente denotada pelo símbolo . Edeve iniciar a partir do símbolo inicial dagramática.

Derivação

(10 - 2) + 3

<expressão>

<expr> + <expr> (<expr>) + <expr>

(<expr> - <expr>) + <expr> (<const> - <expr>) + <expr>

(<const><const> - <expr>) + <expr> (1<const> - <expr>) + <expr>

(10 - <expr>) + <expr>

(10 - <const>) + <expr>

...

Árvore de Análise Sintática

(Parser Tree)

<expr>

<expr> + <expr>

(<expr>) <const>

<expr> - <expr>

<const> <const>

10 2 3

(10 – 2) + 3

Gramáticas Ambíguas

10 – 2 + 3

<expr> - <expr>

<expr>

<expr> + <expr>

10 2 3

<expr>

<expr> + <expr>

<expr> - <expr>

10 2 3

Gramáticas

<expr> <expr> + <termo>

| <expr> - <termo>

| <termo>

<termo> (<expr>)

| <const>

<expr>

<expr> + <termo>

<expr> - <termo> + <termo>

<termo> - <termo> + <termo>

10 – 2 + 3

<expr>

<expr> + <termo>

<expr> - <termo>

10 2 3

Gramáticas

<expr> <expr> + <termo>

| <expr> - <termo>

| <termo>

<termo> <termo> * <fator>

| <termo> / <fator>

| <fator>

<fator> (<expr>)

| <const>

<expr> + <termo>

<termo> * <fator>

3 2 3

<expr>

1 + 2 * 3

Gramáticas

<expr> <expr> + <termo>

| <expr> - <termo>

| <termo>

<termo> <termo> * <fator>

| <termo> / <fator>

| <fator>

<fator> (<expr>)

| <const>

<termo>

<termo> * <fator>

<expr>

1 + 2 * 3

Tradução Dirigida pela

Sintaxe

Analisador Sintático

Analisador Léxico

Analisador Semântico

Tabela de Símbolos

...

Programa Fonte

Código Intermediário

Solicita tokentoken

Gramáticas - Exercícios

1. Considerando a gramática apresentada anteriormente derive as expressões e apresente a árvore sintática correspondente:(1 + 2) * 3 (1 – 2) + 3 * 4

2. Altere a gramática para incluir o operador unário -, esse operador deve ter precedência maior que todos os outros operadores.

3. Altere a gramática para que os operadores de adição, subtração, multiplicação e divisão tenham associatividade da direita para a esquerda.

4. Defina uma gramática para expressões aritméticas (operadores +, -, *, /) pós fixadas.

Gramáticas

Dados 2 conjuntos independentes de símbolos:

• VT – Símbolos terminais.

• VN – Símbolos não terminais.

Uma gramática é definida como a quádrupla:

(VN, VT, S, P)

Onde,

S VN é o símbolo inicial da gramática.

P é um conjunto de regras de reescrita na forma:

, sendo: (VN VT)* VN (VN VT)*

(VN VT)*

Classificação de

Gramáticas

• Irrestritas – nenhuma restrição é imposta

• Sensíveis ao Contexto - || ||

• Livres de Contexto - VN

(VN VT)+

• Regulares - VN

tem a forma a ou aB, onde

a VT e B VN

Gramáticas Regulares

Uma gramática regular gera uma linguagem regular.

C 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9

| 0C | 1C | 2C | 3C | 4C | 5C | 7C | 8C | 9C

C CC | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9

Linguagens Regulares

• Geradas a partir de uma gramática regular;

• Podem ser representadas por meio de uma

expressão regular;

• Podem ser reconhecidas por Autômatos Finitos.

Considerando linguagens compostas por

símbolos 0 e 1 podemos afirmar:

a linguagem L1 ={0n1n | n 1} não é regular;

a linguagem L2 ={0n1m | n 1, m 1} é regular;

Expressões Regulares

Maneira compacta de representar linguagens

regulares. É composta de 3 operações. Sendo e1

e e2 expressões que geram respectivamente duas

linguagens regulares L1 e L2:

• Concatenação: e1e2 = { xy | x L1 e y L2}

• Alternação: e1|e2 = { x | x L1 ou x L2}

• Fechamento: e1* = zero ou mais ocorrências de e1.

É definida a precedência desses operadores como sendo: fechamento,

concatenação, alternação (da maior precedência para a menor).

Expressões Regulares

Exemplos:

identificador (letra | _) (letra | digito | _)*

letra a | b | ... | z | A | B | ... | Z

digito 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

constInt digito digito*

constDouble digito digito*.digito* | . digito digito*

Autômato Finito

A linguagem gerada por uma gramática regular

pode ser reconhecida por um autômato finito.

Um autômato finito consiste em:

1. Um conjunto finito de estados.

2. Um conjunto finito de símbolos de entrada (alfabeto).

3. Uma função de transição que tem como argumentos um

estado e um símbolo de entrada e retorna a um estado.

4. Um estado inicial.

5. Um conjunto de estados finais também chamados

estados de aceitação.

Autômato Finito

letra | digito | _

letra | _

letra | digito | _

letra | _

.

.

digitodigito

digito digito

Autômato Finito

letra | digito | _

letra | _

f

o r

letra | digito | _

letra | _

f

o r

ldld

ld

Onde ld representa letra | digito | _

(com exceção das letras que fazem

transições para outros estados).

AFD – Autômato Finito Determinista

AFN – Autômato Finito Não Determinista

Autômato Finito

Implementação

letra | digito | _

letra | _

digito

digito

0 1

2

Geradores de

Analisadores Léxicos

delim [ \t]

ws {delim}+

letra [A-Za-z]

digito [0-9]

id {letra}({letra}|{digito})*

int {digito}+

real {digito}+\.{digito}*(E[+-]?{digito}+)?

char '{letra}'

string '({letra}|{digito}|[ \t\\:])*'

%%

{char} {yylval.ptr=insereTab(&TabSimb[0], yytext);return TCCHARACTER;}

{string} {yylval.ptr=insereTab(&TabSimb[0], yytext);return TCSTRING;}

\n {num_linhas++;}

FUNCTION {return TFUNCTION;}

INTEGER {return TINTEGER;}

ARRAY {return TARRAY;}

IF {return TIF;}

{id} {yylval.ptr=instalar(yytext); return TID;}

"<" {return TMENOR;}

Análise Léxica -

Exercícios

1. Escreva uma gramática, expressão regular e AFD que defina os números binários terminados em zero.

2. Mostre uma expressão regular e o AFD correspondente a gramática abaixo:S aS B bC C aC

| aB | a

3. Escreva uma expressão regular para as constantes double da linguagem C.

Especificação

• Análise Léxica – expressões regulares.

• Análise Sintática – gramáticas livres de contexto.

• Análise Semântica – sistema de tipos (regras de

inferência), semântica denotacional, semântica

operacional, semântica de ações.

• Geração/Otimização de Código – linguagens para

descrição de arquiteturas.

Analisador Sintático

Obtém uma sequência de tokens fornecida pelo

analisador léxico e verifica se a mesma pode ser

gerada pela gramática.

Os métodos de análise sintática comumente

usados em compiladores são classificados como:

• Métodos top-down.

• Métodos bottom-up.

Os métodos eficientes, tanto top-down quanto

bottom-up, trabalham com subclasses das

gramáticas livres de contexto.

Métodos top-down

Podem ser vistos como a tentativa de encontrar a

derivação mais a esquerda para uma cadeia de

entrada. Partindo do símbolo inicial da gramática

são aplicadas sucessivas derivações tentado

produzir a cadeia que se deseja reconhecer.

Exemplos:

• Método descendente recursivo.

• Método LL(1).

Método Descendente

Recursivo

<expr> + <expr> <expr>

| - <expr> <expr>

<expr> <const>

<const> 0 | 1| 2 | 3| 4 | 5 | 6 | 7 | 8| 9

Método Descendente

Recursivo

void cons()

{

if (isdigit(lookahead))

nextToken();

else

erro("Erro sintático");

}

void expr ()

{

if (lookahead == '+' || lookahead == '-')

{

nextToken(); expr(); expr();

}

else

cons();

}

Analisadores Sintáticos

Preditivos

Escrevendo a gramática de forma cuidadosa, podemos

obter uma gramática processável por um analisador

sintático que não necessite de retrocesso. Dado um

símbolo de entrada a e um não terminal A, a ser

expandido, a gramática deve possuir uma única

produção que leve ao reconhecimento da cadeia iniciada

com a.

Analisadores sintáticos não preditivos (ou não

deterministas) necessitam de retrocesso (backtraking) e

em geral são ineficientes.

Fatoração à Esquerda

As vezes é necessário fazer alterações na gramática

que possibilitem a implementação de um

reconhecedor preditivo:

<cmd> if <expr> then <cmd> else <cmd>

| if <expr> then <cmd>

<cmd> if <expr> then <cmd><cmd’>

<cmd’> else <cmd>

|

Fatoração à Esquerda

A 1 | 2 | ... | n | 1 | 2 | ... | m

A A’ | 1 | 2| ... | m

A’ 1 | 2 | ... | n

Eliminação da

Recursividade à Esquerda

E E + T

| E - T

| T

T c

| (E)

E

E – T

E + T – T

T + T – T

* c + c - c

Eliminação da

Recursividade à Esquerda

E E + T

| E - T

| T

T c

| (E)

A A1 | A2 | ... | An | 1 | 2 | ... | m

A 1A’ | 2A’ | ... | mA’

A’ 1A’ | 2A’ | ... |nA’ |

E TE’

E’ +TE’

| -TE’

|

T c

| (E)

Análise Sintática Preditiva

não Recursiva LL(1)E TE’

E’ +TE’

|

T FT’

T’ * FT’

|

F c

| (E)

Não

Terminal c + * ( ) #

E TE’ TE’

E’ +TE’

T FT’ FT’

T’ * FT’

F c (E)

E

TE’

FT’E’

cT’E’

cE’

c+TE’

c+FT’E’

c+cT’E’

c+c*FT’E’

c+c*cT’E’

c+c*cE’

c+c*c

Analisador Sintático LL(1)

Considerando w a cadeia de entrada.

Empilhar #, Empilhar o símbolo inicial da gramática.

Faça p apontar para o primeiro símbolo de w#

Repetir

Seja X o símbolo no topo da pilha e a o símbolo apontado por p;

Se X for um terminal ou # então

Se X = a então

Remover X da pilha e avançar p;

Senão erro.

Senão /* X não é um terminal */

Se M[X, a] = X Y1Y2...Yk então

Remover X da Pilha

Empilhar Yk...Y2Y1

Senão

erro

Até que X = #

Analisador Sintático LL(1)

Uma gramática cuja tabela não possui entradas

multiplamente definidas é dita LL(1). O primeiro L

indica a varredura da cadeia de entrada, que e

feita da esquerda para a direita (left to right) o

segundo L indica que são aplicadas derivações

mais a esquerda (left linear). O número 1 indica

que é necessário apenas um símbolo para decidir

qual produção aplicar (1 lookahead).

Construção da Tabela LL(1)

A construção de um analisador sintático preditivo e auxiliada por

duas funções associadas a gramática: PRIMEIROS e SEGUINTES

(FIRST e FOLLOW)

Seja uma cadeia qualquer de símbolos gramaticais,

PRIMEIROS() representa o conjunto de símbolos terminais que

começam as cadeias derivadas a partir de . Se * então

também é um elemento de PRIMEIROS().

E E + T

E T

T T * F

T F

F (E)

F c

PRIMEIROS(E)

E T

F

(E)

E T

F

c

= { (, c}

Construção da Tabela LL(1)

SEGUINTES(A), para um não terminal A, é o conjunto de

terminais a tais que existe uma derivação S * Aa, para

alguma cadeia e alguma cadeia , onde S é o símbolo inicial

da gramática. Ou seja o conjunto de símbolos que podem

ocorrer após o não terminal A em alguma forma sentencial da

gramática.

E E + T

E T

T T * F

T F

F (E)

F c

E E + T

T + T

F + T

E E + T

E + T

E + F #

SEGUINTES(F)

E T

T * F

F * F

E T

F

(E)

(E + T)

(E + F)

= { +, #, *, ) }

Construção da Tabela LL(1)

Entrada: Gramática

Saída: Tabela M

Para cada produção A da gramática faça:

• Para cada terminal a em PRIMEIROS(), adicione A em M[A, a].

• Se estiver em PRIMEIROS(), adicione A em M[A, b], para cada

terminal b em SEGUINTES(A).

Cada entrada indefinida em M indica uma situação de erro.

Construção da Tabela LL(1)

E TE’

E’ +TE’

|

T FT’

T’ * FT’

|

F c

| (E)

PRIMEIROS (TE’) = {c, ( }

PRIMEIROS (+TE’) = {+ }

SEGUINTES (E’) = { ), # }

PRIMEIROS (FT’) = {c, ( }

PRIMEIROS (*FT’) = { * }

SEGUINTES (T’) = { +, ), # }

PRIMEIROS (c) = {c}

PRIMEIROS(E) = { ( }

Não

Terminal c + * ( ) #

E TE’ TE’

E’ +TE’

T FT’ FT’

T’ * FT’

F c (E)

Métodos bottom-up

Podem ser vistos como a tentativa de se reduzir a cadeia de

entrada ao símbolo inicial da gramática.

Exemplos:

• Precedência de Operadores;

• SLR(1), LR(1), LALR(1).

Métodos LR(k)

Os métodos de análise sintática LR executam uma

derivação mais a direita ao contrário. O L significa que a

varredura da entrada e feita da esquerda para a direita

(left to right), o R que a derivação correspondente é a

derivação mais a direita (rightmost derivation) e o k indica

o número de símbolos de entrada que tem que ser

examinados para se tomar uma decisão na análise

sintática.

A diferença entre os métodos SLR e LALR é apenas a

técnica usada para a construção das tabelas sintáticas.

Métodos LR

c + * ( ) # E T F

0 5 4 1 2 3

1 6 AC

2 R2 7 R2 R2

3 R4 R4 R4 R4

4 5 4 8 2 3

5 R6 R6 R6 R6

6 5 4 9 3

7 5 4 10

8 6 11

9 R1 7 R1 R1

10 R3 R3 R3 R3

11 R5 R5 R5 R5

Estado AÇÃO DESVIO

(1) E E + T

(2) E T

(3) T T * F

(4) T F

(5) F (E)

(6) F c

Algoritmo LR(1)

Considerando w a cadeia de entrada.

Empilhar 0. /* Estado inicial */

Faça p apontar para o primeiro símbolo de w#

Repetir para sempre

Seja s o estado no topo da Pilha e a o símbolo apontado por p

Se AÇÃO[s, a] = empilhar s’ então

Empilhar a; Empilhar s’;

Avançar p;

Senão

Se AÇÂO[s, a] = reduzir A então

Desempilhar 2 * || símbolos;

Seja s’ o estado no topo da pilha

Empilhar A; Empilhar DESVIO[s’, A];

Senão

Se AÇÃO[s, a] = aceitar então Retornar;

Senão erro

fim

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 c 5

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 F 3

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 T 2

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 E 1

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 E 1 + 6

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 E 1 + 6 c 5

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 E 1 + 6 F 3

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 E 1 + 6 T 9

Entrada: c + c #

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Pilha : 0 E 1

Entrada: c + c #

Tabelas SLR(1)

Um item LR(0), para uma gramática G, é uma produção

de G com um ponto em alguma das posições do seu lado

direito.

Exemplo: A produção E E + T produz 4 itens:

[E .E + T]

[E E .+ T]

[E E +.T]

[E E + T.]

Intuitivamente o . indica até que parte da produção foi

analisada em um determinado estado do analisador

sintático.

Construção da Tabela

Sintática SLR(1)

A construção da tabela sintática é auxiliada por duas operações:

Fechamento:

Sendo I um conjunto de itens da gramática, o fechamento de I é definido por

duas regras:

1. Inicialmente cada item de I é adicionado em FECHAMENTO(I).

2. Se [A .B] estiver em FECHAMENTO(I) e B for uma produção,

adicionar o item [B .] a FECHAMENTO(I). Essa regra é aplicada até

que nenhum novo item possa ser adicionado.

Desvio:

A operação DESVIO(I, X) é definida como o fechamento do conjunto de todos

os itens [A X.] tais que [A .X] esteja em I.

Construção da Tabela

Sintática SLR(1)

Construção de um conjunto de itens LR(0):

C FECHAMENTO(S’ .S#) /* Onde S é o símbolo inicial da linguagem */

Repetir

Para cada conjunto de itens I em C e cada símbolo gramatical X

tal que DESVIO(I,X) não seja vazio e não esteja em C

Incluir Desvio(I,X) em C

Até que não haja mais conjunto de itens a serem incluídos em C

Construção da Tabela

Sintática SLR(1)

Construção da tabela sintática SLR(1):

1. Construir o conjunto de itens C = {I0, I1,..., In}

2. Cada estado i é construído a partir de Ii. As ações sintáticas são

determinadas como:

• Se [A .a] estiver em Ii e DESVIO(Ii, a) = Ij então ação[i, a] = Empilhar j

(se a for um terminal) ou desvio[i, a] = Empilhar j (se a for um não terminal)

• Se [A .] estiver em Ii então ação(i, a) = reduzir através de A , para

todo a em SEGUINTES(A).

• Se [S’ S.#] estiver em Ii então ação(i, #) = aceitar

Construção da Tabela

Sintática SLR(1)

I0S .E#

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I1 Desvio(0,E)

S E.#

E E. + T

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I4 Desvio(0,()

F (.E)

E .E + T

E .T

T .T * F

T .F

F .(E)

F .c

I5 Desvio(0,c)

F c.

I6 Desvio(1,+)

E E +.T

T .T * F

T .F

F .(E)

F .c

I7 Desvio(2,*)

T T * .F

F .(E)

F .c

I8 Desvio(4,E)

F (E.)

E E.+ T

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

E

T

F

(c

+

*

E

T

F

)

FI3

I4

I5

c

(

(

c

c

(

I6+

*

Construção da Tabela

Sintática SLR(1)

I5 Desvio(0,c)

F c.

I2 Desvio(0, T)

E T.

T T.* F

I3 Desvio(0,F)

T F.

I9 Desvio(6,T)

E E +T.

T T. * F

I10 Desvio(7,F)

T T * F.

I11 Desvio(8, ))

F (E).

SEGUINTES(F) = { +, *, ), #

}

SEGUINTES(E) = { +, ), # }

SEGUINTES(T) = { +, *, ), #

}

SEGUINTES(E) = { +, ), # }

SEGUINTES(F) = { +, *, ), #

}

SEGUINTES(F) = { +, *, ), #

}

Se [A .] estiver em Ii então

ação(i, a) = redução através de

A , para todo a em

SEGUINTES(A).

Construção da Tabela

Sintática SLR(1)

No método SLR(1), como a decisão de reduzir aplicando

uma produção A , é tomada usando o conjunto

SEGUINTES(A) e não o contexto onde ocorreu, algumas

gramáticas LR(1) podem apresentar conflitos

empilhar/reduzir se tentamos construir as tabelas usando

esse método. Por o exemplo:

S L = R

S R

L *R

L id

R L(Nesse exemplo os não terminais L e R representam l-value e r-value

respectivamente)

Construção da Tabela

Sintática SLR(1)I0

S’ .S#

S .L = R

S .R

L .*R

L .id

R .L

I1 Desvio (0,S)

S’ S.#

I2 Desvio (0,L)

S L. = R

R L.

I3 Desvio (0,R)

S R.

I4 Desvio (0,*)

L *.R

R .L

L .*R

L .id

I5 Desvio (0,id)

L id.

I6 Desvio (2,=)

S L=.R

R .L

L .*R

L .id

I7 Desvio (4,R)

L *R.

I8 Desvio (4,L)

R L.

I9 Desvio (6,L)

S L=R.

S

L

R

*

id

=

R

L

L

*id

*

L

id

Construção da Tabela

Sintática SLR(1)

SEGUINTES(R) = {=, # }

I2 Desvio (0,L)

S L. = R

R L.

No estado 2, a ação reduzir R L deve ser executada para todos os

seguintes de R, o que nesse caso ocasiona um conflito

empilhar/reduzir. Entretanto não existe forma sentencial da gramática

que inicie com R =. Para tratar essa gramática é necessário um método

que carregue mais informação sobre o contexto para o estado.

Construção da Tabela

Sintática LR(1)

Fechamento(I):

Repetir

Para cada item [A .B, a] em I, cada produção B na Gramática

e cada termina b em PRIMEIROS(a) tal que [B ., b] não esta em I

Faça Incluir [B ., b] em I

até que não seja mais possível adicionar itens a I.

Desvio(I, X): é fechamento do conjunto de itens [A X., a] tais que

[A .X, a] esta em I.

Itens(G’):

C = {FECHAMENTO({[S’.S#, ]})} (Onde S é o símbolo inicial da gramática)

Repetir

Para cada conjunto de itens I em C e cada símbolo gramatical X tal que

Desvio(I, X) e Desvio(I, X) C

Faça Incluir Desvio(I, X) em C

até que nenhum novo item possa ser adicionado a C

Construção da Tabela

Sintática LR(1)I0

[S’ .S#, ]

[S .L = R,#]

[S .R,#]

[L .*R,=,#]

[L .id,=,#]

[R .L,#]

I1 Desvio (0,S)

[S’ S.#, ]

I2 Desvio (0,L)

[S L. = R,#]

[R L.,#]

I3 Desvio (0,R)

[S R.,#]

I4 Desvio (0,*)

[L *.R,=,#]

[R .L,=,#]

[L .*R,=,#]

[L .id,=,#]

I5 Desvio (0,id)

[L id.,=,#]

I6 Desvio (2,=)

[S L=.R,#]

[R .L,#]

[L .*R,#]

[L .id,#]

I7 Desvio (4,R)

[L *R.,=,#]

I8 Desvio (4,L)

[R L.,=,#]

I9 Desvio (6,L)

[S L=R.,#]

S

L

R

*

id

=

R

L

R

*id

*

L

id

I12 Desvio (6,id)

[L id.,#]

I11 Desvio (6,*)

[L *.R,#]

[R .L,#]

[L .*R,#]

[L .id,#]

I10 Desvio (6,L)

[R L.,#]

*

I13 Desvio (11,R)

[L *R.,#]

R

LALR – lookahead LR

A idéia geral é construir o conjunto de itens LR(1) e, se nenhum conflito

aparecer, combinar os itens com núcleo comum.

Algoritmos eficientes para a geração de tabelas LALR constroem o

conjunto de itens LR(0) e numa segunda etapa determinam os lookaheads

correspondentes de cada item.

O método LALR:

• Trata a maioria dos casos presentes em linguagens de programação;

• Na grande maioria dos casos o número de estados é muito inferior ao

número de estados gerados pelo método LR(1).

• Comparando com o método LR(1), em algumas situações, a detecção de

erro é postergada, reduções desnecessárias são aplicadas antes que o

erro seja detectado.

Construção da Tabela

Sintática LALR(1)I0

[S’ .S#, ]

[S .L = R,#]

[S .R,#]

[L .*R,=,#]

[L .id,=,#]

[R .L,#]

I1 Desvio (0,S)

[S’ S.#, ]

I2 Desvio (0,L)

[S L. = R,#]

[R L.,#]

I3 Desvio (0,R)

[S R.,#]

I4 Desvio (0,*)

[L *.R,=,#]

[R .L,=,#]

[L .*R,=,#]

[L .id,=,#]

I5 Desvio (0,id)

[L id.,=,#]

I6 Desvio (2,=)

[S L=.R,#]

[R .L,#]

[L .*R,#]

[L .id,#]

I7 Desvio (4,R)

[L *R.,=,#]

I8 Desvio (4,L)

[R L.,=,#]

I9 Desvio (6,L)

[S L=R.,#]

S

L

R

*

id

=

R

L

R

*id

*

id

L

Hierarquia de Gramáticas

Livres de Contexto

Gramáticas

Ambíguas

Gramáticas não ambíguas

LR(k)

LR(1)

LALR(1)

SLR(1)

LL(k)

LL(1)

Uso de Gramáticas Ambíguas

<cmd_If> if (<expr>) <cmd> else <cmd>

| if (<expr>) <cmd>

<cmd> ... | <cmd> | ...

<cmd_If>

if (<expr>) <cmd>

if (<expr>) <cmd> else <cmd>

if (a > 0) if (a > b) m = a else m = b if (a < 0) if (a > b) m = a else m = b

<cmd_If>

if (<expr>) <cmd> else <cmd>

if (<expr>) <cmd>

Essa gramática é ambígua, como consequêcia temos um conflito empilhar/reduzir

no estado em que ocorre a transição para o token “else”. Caso esse conflito seja

resolvido escolhendo a opção empilhar as reduções executadas serão as

correspondentes a primeira árvore.

Lex

delim [ \t]

ws {delim}+

digito [0-9]

num {digito}+(\.{digito}*(E[+-]?{digito}+)?)?

%%

{ws} {}

"+" {return TADD;}

"-" {return TSUB;}

"*" {return TMUL;}

"/" {return TDIV;}

"(" {return TAPAR;}

")" {return TFPAR;}

\n {return TFIM;}

{num} {yylval=atof(yytext); return TNUM;}

yacc

%{

#include <stdio.h>

#include <stdlib.h>

#define YYSTYPE double

%}

%token TADD TMUL TSUB TDIV TAPAR TFPAR TNUM TFIM

%%

yacc

Linha :Expr TFIM {printf("Resultado:%lf\n", $1);exit(0);}

;

Expr: Expr TADD Termo {$$ = $1 + $3;}

| Expr TSUB Termo {$$ = $1 - $3;}

| Termo

;

Termo: Termo TMUL Fator {$$ = $1 * $3;}

| Termo TDIV Fator {$$ = $1 / $3;}

| Fator

;

Fator: TNUM

| TAPAR Expr TFPAR {$$ = $2;}

;

%%

Yacc/Bison

int yyerror (char *str)

{

printf("%s - antes %s\n", str, yytext);

}

int yywrap()

{

return 1;

}

Programa

#include <stdio.h>

extern FILE *yyin;

int main()

{

yyin = stdin;

printf("Digite uma expressão:");

yyparse();

return 0;

}

Definição Dirigida pela Sintaxe

É uma gramática livre de contexto na qual a cada símbolo é associado um

conjunto de atributos. A cada produção pode estar associado um conjunto

de regras semânticas. Essa regras podem alterar valores de atributos,

emitir código, atualizar a tabela de símbolos, emitir mensagens de erro ou

realizar quaisquer outras atividades. Em geradores de analisadores

sintáticos essas regras são geralmente descritas em uma linguagem de

programação.

Os atributos são classificados como:

• Atributos Sintetizados: O valor do atributo de um nó é computado a

partir dos atributos de seus filhos.

• Atributos Herdados: O valor do atributo de um nó é computado a

partir dos atributos dos nós irmãos e/ou pais.

Definição Dirigida pela Sintaxe

Produção Regra Semântica

S E

E E1 + T

E T

T T1 * F

T F

F (E)

F const

{Imprimir (E.val)}

{E.val = E1.val + T.val}

{E.val = T.val}

{T.val = T1.val * F.val}

{T.val = F.val}

{F.val = E.val}

{F.val = const.lexval}

E.val = 1 + T.val = 6

T.val = 1 T.val = 2 * F.val = 3

F.val = 1 F.val = 2 const.lexval

const.lexval const.lexval

1 + 2 * 3

S

E.val = 7

Árvores Sintáticas Abstrata

E E1 + T {E.ptr = criarNo (‘+’, E1.ptr, T.ptr)}

E T {E.ptr = T.ptr}

T T1 * F {T.ptr = criarNo (‘*’, T1.ptr, F.ptr)}

T F {T.ptr = F.ptr}

F (E) {F.ptr = E.ptr}

F const {F.ptr = criarFolha(const.lexval)}

1 2 3

*

+

1 + 2 * 3

DAG Grafo Direcionado Acíclico

Identifica as subexpressões comuns. Exemplo:

DAG que representa a expressão: a * b + c + a *b.

a b c

*

+

+

Código de 3 endereços

Uma sequência de enunciados na forma:

x = y op z

Sendo x, y e z nomes, constantes ou dados

temporários (criados pelo compilador) e op o

código que representa uma operação qualquer.

Uma versão linearizada da árvore sintática, é

assim chamado por cada instrução poder conter

até três endereços, dois para os operandos e um

para o resultado. Bastante semelhante a uma

linguagem de montagem.

Código de 3 endereços

Exemplo: a = 2 * b + c

t1 = 2 * b

t2 = t1 + c

a = t2

Código de 3 endereços

S id = E {S.cod = E.cod ++ gerar(id.lexval = E.local)}

E E1 + T {E.local = novoTemporario();

E.cod = E1.cod ++T.cod ++ gerar(E.local = E1.local + T.local)}

E T {E.local = T.local; E.cod = T.cod}

T T1 * F {T.local = novoTemporario();

T.cod = T1.cod ++ F.cod ++ gerar(T.local = T1.local * F.local)}

T F {T.local = F.local; T.cod = F.cod}

F (E) {F.local = E.local; F.cod = E.cod}

F id {F.local = id.lexval; F.cod =“”}

F const {F.local = const.lexval; F.cod =“”}

Código de 3 endereços

Alguns enunciados comumente usados:

• x = y op z, onde op é uma operação binária.

• x = op y, onde op é uma operação unária.

• x = y, enunciado de cópia.

• goto L, desvio incondicional.

• if x relop y goto L, onde relop é um operador relacional.

• param x, passagem de parâmetro para funções/procedimentos.

• call p, chamada de uma função/procedimento.

• x = a[i] ou a[i] = x, atribuições indexadas.

• *x = y ou x = *y, indireções.

Código de 3 endereços

n = 1;

f = 1;

while (n < 10)

{

f = f * n;

n = n + 1;

}

n = 1

f = 1

L1: if n < 10 goto L2

goto L3

L2: f = f * n

n = n + 1

goto L1:

L3:

Código de 3 endereços

n = 1;

f = 1;

while (n < 10)

{

f = f * n;

n = n + 1;

}

n = 1

f = 1

L1: if n >= 10 goto L3

f = f * n

n = n + 1

goto L1:

L3:

Definições S-atribuídas

Definições dirigidas pela sintaxe que possuem apenas

atributos sintetizados.

Produção Regra Semântica

S E

E E1 + T

E T

T T1 * F

T F

F (E)

F const

{Imprimir (E.val)}

{E.val = E1.val + T.val}

{E.val = T.val}

{T.val = T1.val * F.val}

{T.val = F.val}

{F.val = E.val}

{F.val = const.lexval}

E.val = 1 + T.val = 6

T.val = 1 T.val = 2 * F.val = 3

F.val = 1 F.val = 2 const.lexval

const.lexval const.lexval

1 + 2 * 3

S

E.val = 7

Definições L-atribuídas

Uma definição dirigida pela sintaxe é L-atribuída

se cada atributo herdado de Xj, 1 j n, do lado

direito de uma produção, A X1X2...Xn depende

somente:

1.Dos atributos dos símbolos X1X2...Xj-1 (símbolos

a esquerda de Xj).

2.Dos atributos herdados de A.

Tradução Top-Down

S E {imprimir (E.val)}

E T {E’.h = T.val} E’ {E.val = E’.s}

E’ +T {E’1.h = E’.h + T.val} E1 {E’.s = E’1.s}

E’ {E’.s = E’.h}

T F {T’.h = F.val} T’ {T.val = T’.s}

T’ * F{T’1.h = T’.h * F.val} T’ {T’.s = T1’.s}

T’ {T’.s = T’.h}

F const {F.val = const.lexval}

F ({push(T’.h); push(E’.h)} E {E’.h = pop(); T’.h = pop()})

Análise Semântica(Verificação de Contexto)

D var S

S id L {atribuirTipo(id.lexval, L.tipo)}

S S id L {atribuirTipo(id.lexval, L.tipo)}

L , id L1 {atribuirTipo(id.lexval, L.tipo); L.tipo = L1.tipo}

L :T {L.tipo = T.tipo}

T integer {T.tipo = integer}

T string {T.tipo = string}

Análise Semântica(Verificação de Contexto)

E E1 + T {if (E1.tipo = T.tipo) then E.tipo = E1.tipo else error()}

E T {E.tipo = T.tipo}

T T1 * F {if (T1.tipo = F.tipo) then T.tipo = T1.tipo else error()}

T F {T.tipo = F.tipo}

F id {F.tipo = consultaTipo(id.lexval);}

F constInt {F.tipo = Inteiro}

F constReal {F.tipo = Real}

Obs: Em uma situação real as regras semânticas devem implementar a

coerção dos tipos.

Expressões Lógicas e Relacionais

B B1 or M C {corrigir(B1.listaf, M.label);

B.listav = merge(B1.listav, C.listav);

B.listaf = C.listaf;}

B B1 and M C {corrigir(B1.listav, M.label);

B.listaf = merge(B1.listaf, C.listaf);

B.listav = C.listav;}

B C {B.listav = C.listav; B.listaf = C.listaf;}

C not C1 {C.listav = C1.listaf; C.listaf = C1.listav;}

C (B) {C.listav = B.listav; C.listaf = B.listaf;}

C E1 rel E2 {C.listav = criaLista(proxInst);

C.listf = criaLista(proxInst+1);}

gerar(if E1.local rel E2.local goto _);

gerar (goto _);

M {M.label = novolabel()}

Comandos de Seleção e Repetição

S if (B) then M S {corrigir (B.listav, M.label);

corrigir(B.listaf, novolabel();}

S if (B) then M1 S N else M2 S

{corrigir(B.listav, M1.label);

corrigir(B.listf, M2.label);

corrigir(N.listav, novoLabel();}

S while M1 (B) M2 S

{corrigir(B.listav, M2.label);

gerar(goto M1.label);

corrigir(B.listaf, novolabel();}

N {N.listav = criarLista(ProxInstr); gerar(goto _);}

Organização da Memória

Dados estáticos

Heap

Pilha

Dados

Código

Dados dinâmicos

Endereço 0 Não usado

Dados Estáticos

A área de memória é reservada no início da

execução do programa e liberada apenas no fim

de sua execução (e.g. variáveis globais e variáveis

locais declaradas com o modificador static em

linguagem C).

Dados Dinâmicos

Pilha – Área de armazenamento temporário ondeé armazenado o registro de ativação das funções.

Heap – Área reservada para alocação dinâmica,permite ao programador alocar e liberar espaçosde memória quando necessário (e.g. áreas dememória reservadas pelas funções malloc eliberadas pela função free em linguagem C).

Registro de Ativação

As informações necessárias para execução de uma

função/procedimento são gerenciadas utilizando um

bloco de memória chamado registro de ativação,

fazem parte do registro de ativação: parâmetros,

endereço de retorno e variáveis locais.

Chamadas de

Funções/Procedimentos

int fat (int n)

{

if (n <= 1)

return 1;

return n * fat(n-1);

}

fat:

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %ebx

cmpl $1, %ebx

jl L1:

movl $1, %eax

movl %ebp, %esp

popl %ebp

ret

L1: subl $1, %ebx

pushl %ebx

call fat

subl $4, %esp

movl 8(%ebp), %ebx

imull %ebx, %eax

movl %ebp, %esp

popl %ebp

ret

fat:

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %ebx

cmpl $1, %ebx

jl L1:

movl $1, %eax

jmp L3

L1: subl $1, %ebx

pushl %ebx

call fat

movl 8(%ebp), %ebx

imull %ebx, %eax

L3: movl %ebp, %esp

popl %ebp

ret

Chamadas de

Funções/Procedimentos

int fat (int n)

{

if (n >= 1)

return 1;

return n * fat(n-1);

}

Int main()

{

int x;

x = fat(3);

}

%ebp-ant

-

3

ret

1000

2

ret

984

1

ret

972

1000

996

992

988

984

980

976

972

968

964

960

%ebp-4 x

%ebp+8 n

%ebp = 984

%ebp = 972

%ebp = 960

%ebp+8 n

%ebp+8 n

%ebp = 1000

top related