Você está na página 1de 39

LINGUAGENS E

COMPILADORES

Conceitos de Análise Sintática e


Análise Sintática Descendente
LINGUAGENS E COMPILADORES
Agenda do Módulo 2:
1. Conceitos de Análise Sintática
2. Análise Sintática Descendente
3. Gerador de Analisadores Sintáticos
Descendentes
Obs.:
a. Reveja a apresentação sobre Análise Léxica.
b. Veja a apresentação sobre Conceitos de
Linguagens de Programação do módulo
anterior.
Prof. Dr. Ricardo Luis de Azevedo da Rocha
1. ANÁLISE SINTÁTICA
• Tem a função de combinar a lista de tokens
• Criação de uma estrutura chamada Árvore Sintática
atribuição

identificador := expressão
expressão + expressão

identificador número
SOMA SOMA 35

• A análise sintática também deve rejeitar tokens


inválidos
• Reportar erros sintáticos
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• A análise sintática é mais complexa em natureza
do que a análise léxica
• Precisamos de uma linguagem mais avançada
• Hierarquia de Chomsky

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ANÁLISE SINTÁTICA
• Linguagens Livre de Contexto
• “Constituem um conjunto de linguagens que podem
ser geradas por gramáticas livres de contextos
(GLC), reconhecidas por autômatos de pilha”

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ANÁLISE SINTÁTICA
• A maioria dos construtores das LP’s são
expressos em GLC
• Linguagens são projetadas a partir de GLC
• É comum dividir os construtores em
categorias sintáticas que englobam
algum conceito particular
• Expressões: usada no cálculo de valores
• Statements: ações que ocorrem em um fluxo
• Declarações: propriedades dos nomes usados
em outras partes do programa
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Definição baseada em derivação para uma
linguagem gerada por uma GLC
• Dado uma GLC G com símbolo inicial S, símbolos
terminais T e produções P, a linguagem
L(G) que G gera é definida para ser o
conjunto de todas as strings de símbolos
terminais que podem ser obtidas por
derivação a partir de S usando as
produções P, ou seja, o conjunto
{w  T* | S  w}
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Diferentes derivações para a mesma sentença

Qual a diferença?
Derivação mais à esquerda  Derivação mais à
direita
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Árvore Sintática
• Pode ser representada como uma árvore
• A raiz é o símbolo inicial
• Resultados da produção dos símbolos não terminais são filhos
• As folhas devem conter apenas símbolos terminais
• Lendo as folhas da esquerda para a direita
temos a palavra derivada
• Produções que levam ao vazio também devem
ser representadas, apesar de serem ignoradas
na formação da palavra

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ANÁLISE SINTÁTICA
• Dada uma gramática G, a escolha da produção a ser
derivada influencia na forma da árvore sintática
• T→R
• T → aTc
• R→
• R → RbR

- Árvores sintáticas para a palavra aabbbcc


Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Quando uma gramática permite
diferentes árvores sintáticas ela é dita
ambígua

• Quando usamos gramáticas para


impor estrutura sobre um conjunto
de tokens, tal estrutura tem que ser
sempre a mesma
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Exemplo de problema
E → E + E → Numero + E → 3 + E → 3 + E * E → 3 + Numero * E
→ 3 + 4 * E → 3 + 4 * Numero → 3 + 4 * 5
E → E * E → E + E * E → Numero + E * E → 3 + E * E → 3 + Numero * E
→ 3 + 4 * E → 3 + 4 * Numero → 3 + 4 * 5

23 35

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ANÁLISE SINTÁTICA
• Reescrevendo expressões gramaticais ambíguas
• Considere a seguinte gramática ambígua: E → E  E
• Como torná-la não ambígua? E → num
• Se  é associativo à esquerda, devemos
forçar a gramática a ser recursiva à
esquerda: E → E  E’

E → E’
Única árvore E’ → num
que pode ser
gerada
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Outras fontes de ambiguidade
• Exemplo clássico do “else” em comandos
de decisão
If p then if q then s1 else s2

• A convenção é casar o “else” com o “if”


mais perto que ainda não tenha sido
casado

• Como representar isso na gramática?


Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ANÁLISE SINTÁTICA
• Gramática não ambígua para comandos
Stat → Stat2 ; Stat
Stat → Stat2
Stat2 → Matched
Stat2 → Unmatched
Matched → if Exp then Matched else Matched
Matched → id := Exp
Unmatched → if Exp then Matched else Unmatched
Unmatched → if Exp then Stat2

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


MÉTODOS DE ANÁLISE SINTÁTICA
• Podem seguir duas abordagens:
• Métodos bottom-up
• Utilizam derivação inversa
• Tenta casar partes da entrada com o lado direto das produções
• Métodos top-down
• Sempre partem da raiz (top-down)
• São baseados em derivações sucessivas
• Devem usar busca direcionada
• Tais abordagens podem ser com ou sem Backtracking
• Com = maior número de gramáticas, porém mais
complexo e lento
• Sem = menor número de gramáticas, porém simples
e eficiente
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
2. ANÁLISE SINTÁTICA
DESCENDENTE (TOP-DOWN)
• Podemos escolher uma das seguintes opções:
• Análise preditiva recursiva
• Construção de um conjunto de procedimentos, normalmente
recursivos, um para cada símbolo não-terminal da gramática em
questão
• Requer uma linguagem com recursividade na implementação
• Análise preditiva LL(1)
• Implementa o método descendente utilizando
explicitamente uma pilha
• Limitações do método:
• Entra em ciclo (loop) para gramáticas
recursivas à esquerda
• Não lida com regras não-determinísticas
(A → αβ e A → αγ)
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
ADAPTANDO A GRAMÁTICA
• Retirar recursão à esquerda
• Um não-terminal A, em uma GLC G=(N,T,P,S) é recursivo
se A → A, para  e   (NT)*.
• Se  =, temos recursão à esquerda
• Se  =, temos recursão à direita

• Uma GLC G=(N,T,P,S) possui recursão à


esquerda direta se P contém pelo menos uma
produção da forma A → A.
• Uma GLC G=(N,T,P,S) possui recursão à
esquerda indireta se existe em G uma
derivação da forma A →n A, para algum n2

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ADAPTANDO A GRAMÁTICA
• Retirar recursão à esquerda
• Considere A ::= Aα | β
• A recursividade pode ser eliminada substituindo-se as
produções de G(A) por:
A ::= βA'
A'::= αA' | ε

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ADAPTANDO A GRAMÁTICA
• Retirar recursão à esquerda
• Considere A ::= Aα | β
• A recursividade pode ser eliminada substituindo-se as
produções de G(A) por:
A ::= βA'
A'::= αA' | ε

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ADAPTANDO A GRAMÁTICA

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ADAPTANDO A GRAMÁTICA
• Retirar não determinismo
• Caracterizado por:
A → 
A → 

• Retiramos o determinismo através da seguinte


transformação:
• A → C
• C→|

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


MÉTODO TOP-DOWN
• Análise preditiva recursiva: <cmd> → begin <lista_cmds> end |
while <condição> do <cmd> |
• Seja a gramática if <condição> then <cmd>
<expr> → <termo> + <expr> | <termo>
<termo> → <fator> * <termo> | <fator>
<fator> → <primário> ** <fator> | <primário>
<primário> → IDENT | NÚMERO | ( <expr> )
• Podemos reescrevê-la como:
<expr> → <termo> { + <expr> }
<termo> → <fator> { * <termo> }
<fator> → <primário> { ** <fator> }
<primário> → IDENT | NÚMERO | ( <expr> )
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
MÉTODO TOP-DOWN
• Análise preditiva recursiva:
Procedimento analisador_sintático {
obtenha_símbolo(); /* chama o analisador léxico */
EXPR();
}

Procedimento EXPR() { <expr> ::= <termo> { + <expr> }


TERMO();
se símbolo_lido = '+' então {
obtenha_símbolo();
EXPR();
}
}
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
MÉTODO TOP-DOWN
• Análise preditiva recursiva:
Procedimento TERMO() { <termo> ::= <fator> { * <termo> }
FATOR();
se símbolo_lido = '*' então {
obtenha_símbolo();
TERMO();
}
}
Procedimento FATOR() { <fator> ::= <primário> { ** <fator> }
PRIMÁRIO();
se símbolo_lido = '**' então {
obtenha_símbolo();
FATOR();
}
}
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
MÉTODO TOP-DOWN
• Análise preditiva recursiva:
Procedimento PRIMÁRIO() { <primário> ::= IDENT | NÚMERO | ( <expr> )
se símbolo_lido = IDENT então {
/* trate adequadamente um identificador */
obtenha_símbolo();
}
senão se símbolo_lido = NÚMERO então {
/* trate adequadamente um número */
obtenha_símbolo();
}
senão se símbolo_lido = '(' então {
obtenha_símbolo();
EXPR();
se símbolo ≠ ')' então
ERRO( "falta )" );
senão
obtenha_símbolo();
}
}

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


ANALISADOR LL(K)
• No Analisador LL(k) ("Left-to-right Left-most
derivation") basta olharmos no máximo k
símbolos à frente na sentença para decidir que
regra de produção aplicar

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


MÉTODO TOP-DOWN
• Analisador de Gramáticas LL(1)

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


MÉTODO TOP-DOWN
• LL(1) – Funcionamento
• Sendo X o símbolo do topo da pilha, e PS o atual
símbolo de entrada, as ações podem ser:

• Se X é um terminal = PS = $, o analisador encerra sua


atividade e comunica fim da análise sintática com
sucesso

• Se X é um terminal = PS ≠ $, o analisador elimina X do


topo da pilha e avança para o próximo símbolo de
entrada

• Se X é um terminal ≠ PS, o analisador acusa um erro


de sintaxe (ativa rotina de tratamento de erros)

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


MÉTODO TOP-DOWN
• LL(1) – Funcionamento (continuação)
• Se X é um não-terminal, o analisador consulta
M[X, PS].
• Se a resposta for uma regra de produção X ::=
MVU, o analisador desempilha X do topo da pilha
e empilha UVM (com M no topo da pilha). Para a
saída é enviada a regra de produção usada

• Se M[X, PS] = ERRO, o analisador acusa um erro


de sintaxe (ativa rotina de tratamento de erros)

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


MÉTODO TOP-DOWN
• LL(1) – Exemplo
(1) S ::= aAS
(2) S ::= b
(3) A ::= a
(4) A ::= bSA

• w = abbab$

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


CONSTRUÇÃO DA TABELA
Considerando um não terminal A, três tipos de
informação são importantes para a construção da tabela:
• Se A gera ou não cadeia vazia 

• Quais são os símbolos terminais iniciadores de A


• Se A →* , que terminais podem aparecer como
primeiro símbolo de 

• Quais são os símbolos terminais seguidores de A


• Se S →* A, que terminais podem aparecer como
primeiro símbolo de 

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


CONJUNTO PRIMEIRO (FIRST)
É o conjunto de símbolos terminais que iniciam uma sequência de símbolos, de
acordo com as seguintes regras:
• Se =, Primeiro() = Primeiro() = {}
• Se  é um terminal a, Primeiro() = Primeiro(a) = {a}
• Se  é uma cadeia a, cujo primeiro símbolo é um terminal a, Primeiro() = Primeiro(a) =
{a}
• Se  é um não-terminal A, Primeiro(A) é calculado via
algoritmo
• Se  é uma cadeia A, e o primeiro símbolo é um não não-terminal A,
que deriva , Primeiro() = Primeiro(A) = Primeiro(A)  Primeiro()
• Se  é uma cadeia A, e o primeiro símbolo é um não não-terminal A,
que não deriva , Primeiro() = Primeiro(A) = Primeiro(A)

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


CONJUNTO PRIMEIRO (FIRST)
Algoritmo
1. Inicialmente para todo não-terminal A da gramática, todos Primeiro (A)
estão vazios
2. Se um não terminal A gera , acrescentar  a Primeiro(A)
3. Para cada regra A → B1...Bma, tal que para todo
i=1,...,m; Bi →* , acrescentar “a” a Primeiro(A)
4. Para cada regra A → B1...Bm, tal que para todo
i=1,...,m; Bi →* , acrescentar Primeiro() a Primeiro(A)
(Repetir 4 enquanto houver alteração no valor de algum
Primeiro)
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
CONJUNTO SEGUIDOR
(FOLLOW)
• Seguidor(A) é o conjunto de símbolos terminais que
seguem o não-terminal A

• Se o não-terminal aparece no fim da cadeia


não há um seguidor, neste caso usamos o
$ para indicar o final da cadeia (eof)

• $ sempre será seguidor de S (símbolo inicial


da gramática)

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


CONJUNTO SEGUIDOR (FOLLOW)
Algoritmo
1. Todos os não terminais A da gramática,
inicialmente, terão Seguidor(A) = { }, exceto S que
terá Seguidor(S) = {$}
2. Se há uma regra A → Ba, e =B1...Bm →* ,
acrescentar “a” a Seguidor(B)
3. Se há uma regra A → BC, e =B1...Bm →* ,
acrescentar Primeiro(C) a Seguidor(B)
4. Se há uma regra A → B, e =B1...Bm →* ,
acrescentar Seguidor(A) a Seguidor(B)
5. Repetir passo 4 sempre que houver
modificações nos conjuntos

Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”


CONSTRUÇÃO DA TABELA
Algoritmo: considere uma matriz M[X,y], onde as linhas são os
não-terminais e as colunas terminais
1.Para cada produção A→  de G, executar os passos 2 e 3 (criação da
linha A da tabela)
2.Para cada terminal a de Primeiro(), adicionar a
produção A →  a M[A,a]
3.Se Primeiro() inclui , então adicione A→  a M[A,b]
para cada b em Seguidor(A)
Se houver mais de uma produção para M[X,y] a
gramática é ambígua
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
3.GERADOR DE ANALISADORES
SINTÁTICOS DESCENDENTES
• Analisadores léxicos e sintáticos são
construídos a partir de especificações formais
• Gramáticas Regulares / Autômatos finitos
• Gramáticas Livre de contexto

• A formalidade das descrições permite


que tais analisadores sejam
automaticamente construídos
• Programas que geram programas
Slide extraído de Clauirton Siebra – UFPB “Construção de Compiladores”
PARSERS GENERATORS
• Existem vários geradores de parser
• JavaCC É um dos mais populares na
• JLex & Cup comunidade Java, sendo suportado hoje
• AntLR pela comunidade java.net
• SableCC
•… Tem um dos melhores suportes a
gramáticas em EBNF (Extended Backus
Naur Form) e gera estrutura OO da AST
(Abstract Syntax Tree) além de classes
utilitárias usando o padrão Visitor.
Slide extraído de Augusto Sampaio – UFPE “JavaCC”

Você também pode gostar