Compilador

Propaganda
Compilador

Software que traduz o texto (linguagem
fonte) que representa um programa para
código máquina(linguagem alvo) capaz de
ser executado pelo computador
Compilador

Nesse processo de tradução, há duas
tarefas básicas a serem executadas por
um compilador:


análise, em que o texto de entrada (na
linguagem fonte) é examinado, verificado e
compreendido
síntese, ou geração de código, em que o texto
de saída (na linguagem objeto) é gerado, de
forma a corresponder ao texto de entrada.
Compilador

A fase de análise normalmente se subdivide em:





análise léxica,
análise sintática e
análise semântica.
É possível representar completamente a sintaxe
de uma Linguagem de Programação através de
uma gramática livre de contexto.
Deixa-se para a análise semântica a verificação
de todos os aspectos da linguagens que não se
consegue exprimir de forma simples usando
gramáticas livres de contexto.
Pré-processador
Analisador Léxico
Analisador Sintático
front-end
Analisador Semântico
Gerador de Código
(intermediário)
Otimizador
back-end
Gerador de Código
final = (nota1 + nota2) / 2;
Analisador Léxico
Id1 = (Id2 + Id3) / 2
Analisador Sintático
=
Id1
Tabela de Símbolos
/
2
+
Id2
Id3
Id1
final
double
...
Id2
nota1
double
...
Id3
nota2
double
...
...
Analisador Semântico
=
Id1
/
intToDouble(2)
+
Id2
Id3
Gerador de Código
(intermediário)
Tabela de Símbolos
Id1
final
double
...
Id2
nota1
double
...
temp2 = Id3 * temp1
Id3
nota2
double
...
temp3 = Id2 / temp2
...
temp1 = intToDouble(2)
Id1 = temp3
Otimizador de Código
Temp1 = id3 *2.0
Id1 = id2 / temp1
Gerador de Código
MOVF ID3, R2
MULF #2.0, R2
MOVF ID2, R1
DIVF R2, R1
MOVF R1, ID1
Tabela de Símbolos
Id1
final
double
...
Id2
nota1
double
...
Id3
nota2
double
...
...
Fases de um Compilador
Gerenciamento da tabela de símbolos:
uma estrutura de dados contendo um
registro para cada identificador, com os
campos contendo os atributos do
identificador.
 Quando o analisador léxico detecta um
identificador, instala-o na tabela de
símbolos.
 A estrutura de dados permite encontrar
rapidamente cada registro e armazenar ou
recuperar dados do mesmo.

Fases de um Compilador




Um compilador não deve parar quando encontrar
algum erro e sim continuar para detectar todos.
A análise léxica substituir a estrutura por tokens e
acrescenta na tabela de símbolos
A análise sintática transforma um texto na entrada
em uma estrutura de dados, em geral uma árvore,
o que é conveniente para processamento posterior
e captura a hierarquia implícita desta entrada
A análise semântica verifica os erros semânticos,
(por exemplo, uma multiplicação entre tipos de
dados diferentes) no código fonte e coleta as
informações necessárias para a próxima fase da
compilação que é a geração de código objeto
Compilador simples de uma passagem



Uma linguagem de programação pode ser
definida pela descrição da aparência de seus
programas (a sintaxe da linguagem) e do que os
mesmos significam (a semântica da linguagem)
Para especificar a sintaxe de uma linguagem,
apresentamos uma notação amplamente aceita,
chamada gramática livre de contexto ou BFN
(Forma Backus-Naur)
Para especificar a semântica de uma linguagem
usaremos descrições informais e exemplos
sugestivos.
Gramáticas




Uma linguagem consiste essencialmente de uma
seqüência de strings ou símbolos com regras
para definir quais seqüências de símbolos são
válidas na linguagem, ou seja, qual a sintaxe da
linguagem.
A interpretação do significado de uma seqüência
válida de símbolos corresponde à semântica da
linguagem.
Existem meios formais para definir a sintaxe de
uma linguagem - a definição semântica é um
problema bem mais complexo.
A sintaxe de linguagens é expressa na forma de
uma gramática, que será introduzida na
seqüência.
Estrutura da vanguarda de um
compilador
Fluxo de
caracteres
de entrada
Analisador
léxico
Fluxo
de
tokens
Tradutor
dirigido
pela
sintaxe
Representação
intermediária
Definição da Sintaxe
Uma gramática descreve a estrutura
hierárquica de muitas construções das
linguagens de programação.
 O comando if tem a estrutura em C



If (expressão) comando else comando
O comando é if, um parêntese à esquerda,
uma expressão, um parêntese à direita,
um comando, a palavra else e outro
comando

cmd -> if (expr) cmd else cmd
Gramáticas

Um conjunto de regras de produção, é um
símbolo de partida. Uma regra de
produção tem o formato   , onde 
representa o nome da construção sintática
e  representa uma forma possível dessa
construção:
<expressão>  <expressão> +
<expressão>
Definição da Sintaxe
A regra é chamada de produção
 If e parênteses são tokens
 As variáveis expr e cmd são sequências de
tokens e não terminais

Elementos de uma gramática livre de
contexto
Conjunto de tokens (símbolos terminais)
 Conjunto não-terminais
 Conjunto de produções, onde a produção
consiste em um não-terminal, chamado de
lado esquerdo da produção, uma seta e
uma sequência de tokens e/ou nãoterminais, chamado de lado direito da
produção
 Uma designação a um dos não terminais
como símbolo de partida

Gramáticas
<expr>  <expr> + <expr>
| <expr> – <expr>
| (<expr>)
| <const>
<const> 
<const><const>
|0|1|2|3|4|5|6|7|9
Derivação
A verificar se uma frase faz parte da
linguagem gerada pela gramática,
envolve sucessivas substituições da
cadeia de símbolos que ocorre do lado
esquerdo da produção pela sua
construção sintática correspondente,
partindo do símbolo inicial.
 Essa substituição é chamada derivação
sendo normalmente denotada pelo
símbolo .

Derivação
<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>
...
 (10 - 2) + 3
Árvore Gramatical
(10 – 2) + 3
<expr>
<expr>
<expr>
(<expr>)
+
<const>
<expr> - <expr>
<const>
<const>
10
2
3
Gramática Ambíguas
10 – 2 + 3
<expr>
<expr>
<expr>
-
<expr>
<expr>
<expr> + <expr>
10
2
3
+
<expr>
<expr> - <expr>
10
2
3
Precedência de Operadores
Como saber quem precede entre * e +.
 Para tal criamos mais dois não terminais

Precedência de Operadores
<expr>  <expr> + <termo>
| <expr> - <termo>
| <termo>
<termo>  (<expr>)
| <const>
<expr>
<expr>
+
<expr> - <termo>
<expr>
 <expr> + <termo>
 <expr> - <termo> + <termo> 10
2
 <termo> - <termo> + <termo>
 10 – 2 + 3
<termo>
3
Precedência de Operadores
<expr> 
<termo> 
<fator> 
<expr> + <termo>
| <expr> - <termo>
| <termo>
<termo> * <fator>
| <termo> / <fator>
| <fator>
(<expr>)
| <const>
1+2*3
<expr>
<expr>
+
<termo>
<termo> * <fator>
3
2
3
Gramática
<expr> 
<termo> 
<fator> 
<expr> + <termo>
| <expr> - <termo>
| <termo>
<termo> * <fator>
| <termo> / <fator>
| <fator>
(<expr>)
| <const>
1+2*3
<expr>
<termo>
<termo> * <fator>
Tradução Dirigida pela Sintaxe
Programa Fonte
Analisador Léxico
token
Solicita token
Analisador Sintático
Analisador Semântico
Código Intermediário
Tabela de Símbolos
...
Notação Posfixa
(9-5)+2 => 95-2+
 9-(5+2) => 952+ Os parênteses são desnecessários na
notação posfixa porque a posição e a
aridade (número de argumentos) dos
operadores permitem somente um
decodificação de uma expressão posfixa

Definição dirigida pela Sintaxe
A definição dirigida pela sintaxe usa
gramática livre de contexto para
especificar a estrutura sintática de
entrada.
 Cada símbolo da gramática associa um
conjunto de atributos e cada produção
associa um conjunto de regras semânticas
para computar os valores dos atributos
associados aos símbolos que figuram
naquela produção.

Atributos Sintetizados
O valor em um nó da árvore gramatical é
determinado a partir dos valores dos
atributos dos filhos daquele nó.
 Os atributos sintetizados possuem a
desejável propriedade de que podem ser
avaliados durante um único
caminhamento bottom-up (final para
início) da árvore gramatical

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 (“Projeto Mono”),
símbolos de pontuação (; , {, }),
labels.
Token

Tokens, ou lexemas, é uma sequência de
caracteres que podem ser tratados como
uma unidade na gramática de uma
linguagem de programação
Análise Léxica
Entrada: arquivo texto
 Saída: sequência de tokens
 Conta número de linhas
 Remove espaços em branco e comentários
 Apresenta símbolos ilegais
 Produz a tabela de símbolos

Por que análise léxica?
Simplifica a análise sintática
 Simplifica a definição da linguagem
 Modularidade
 Reusabilidade
 Eficiência

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.
Autômatos Finitos

Autômatos finitos, ou máquina de estados
finitos


autômatos finitos determinísticos
autômatos finitos não-determinísticos
Compiladores



A implementação de reconhecedores de
linguagens regulares (autômatos finitos) é mais
simples e mais eficiente do que a implementação
de reconhecedores de linguagens livres de
contexto (autômatos de pilha).
Nesse caso, é possível usar expressões regulares
para descrever a estrutura de componentes
básicos das Linguagens de Programação, tais
como identificadores, palavras reservadas,
literais numéricos, operadores e delimitadores,
etc.
Essa parte da tarefa de análise (análise léxica) é
implementada separadamente, pela simulação de
autômatos finitos.
Autômato Finito Determinístico

Definição. Um autômato finito
determinístico é uma quíntupla






M = (K, Σ, *, s, F), onde
K é um conjunto finito de estados
Σ é um alfabeto
s € K é o estado inicial
F está contido K é o conjunto de estados finais
é a função de transição, K x Σ para K
Exemplo de um AFD






Exemplo. Seja M o
autômato finito
determinístico (K, Σ,
*, s, F), onde
K = {q0, q1}
Σ = {a,b}
s = q0
F = K {q0}
q

 (q, )
q0
q0
q1
q1
a
b
a
a
q0
q1
q1
q0
Valido no AFD
(q0, abba)
(q0, bba)
(q1, ba)
(q0, a)
q
(q0 ,€)
q0
q0
Portanto, (q0, aabba)
q1
é aceita por M
q1
T T T
T
(q0, aabba)
T


a
b
a
a
 (q, )
q0
q1
q1
q0
Autômato Finito Não-Determinístico

Definição. Um autômato finito nãodeterminístico é uma quíntupla






M = (K, Σ, ), s, F), onde
K é um conjunto finito de estados
Σ é um alfabeto
s € K é o estado inicial
F está contido K é o conjunto de estados finais
é a função de transição, K x (Σ c {,}) para K
Exemplo de um AFND

Exemplo. Seja M o
autômato finito nãodeterminístico (K, Σ,
), s, F), onde
K = {q0, q1, q2, q3, q4}
Σ = {a,b}
s = q0
F = K {q4}
q

 (q, )
q0
a
q0
q0
b
q0
q0
b
q1
q1
b
q2
q1
a
q3
q2
€
q4
q3
b
q4
q4
a
q4
q4
b
q4
Entrada para um AFND
T
(q1, ababab)
(q3 babab)
(q4, abab)
(q4, bab)
(q4 ab)
(q4, b)
(q4, €)
T
T
T
T
T
(q0, bababab)
T

Portanto, (q0, bababab)
é aceita por M
Análise Sintática
Verifica se as frases obedecem as regras
sintáticas da linguagem:
Por exemplo, uma expressão pode ser
definida como:
expressão + expressão
expressão – expressão
(expressão)
constante
Gramáticas
Um conjunto de regras de produção, é um
símbolo de partida. Uma regra de
produção tem o formato   , onde 
representa o nome da construção sintática
e  representa uma forma possível dessa
construção:
<expressão>  <expressão> +
<expressão>
Gramáticas
<expr>  <expr> + <expr>
| <expr> – <expr>
| (<expr>)
| <const>
<const> 
<const><const>
|0|1|2|3|4|5|6|7|9
Derivação
A verificar se uma frase faz parte da
linguagem gerada pela gramática,
envolve sucessivas substituições da
cadeia de símbolos que ocorre do lado
esquerdo da produção pela sua
construção sintática correspondente,
partindo do símbolo inicial.
 Essa substituição é chamada derivação
sendo normalmente denotada pelo
símbolo .

Derivação
<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>
...
 (10 - 2) + 3
Árvore Sintática
(10 – 2) + 3
<expr>
<expr>
<expr>
(<expr>)
+
<const>
<expr> - <expr>
<const>
<const>
10
2
3
Linguagens Regulares




Gerada a partir de uma gramática regular.
Pode ser representada através de uma expressão
regular.
Pode ser reconhecida por um Autômato Finito.
Considerando linguagens compostas por símbolos
0 e 1 podemos afirmar:
a linguagem L01 ={0n1n| n  1} não é regular;
a linguagem L01 ={0n1m | n  1, m  1} é regular;
Download