Você está na página 1de 29

Compiladores:

Introdução - Continuação

Introdução

Œ O uso de linguagens de programação de


alto nível possibilita enorme grau de
abstração e independência de plataforma
permitindo o desenvolvimento eficiente de
software.
Œ Como conseqüência tornam-se necessárias
ferramentas de tradução da linguagem de
alto nível para a linguagem de execução.

12/3/2008 Introdução 2 de 57

1
Introdução

Œ O estudo da técnicas construtivas dos


compiladores não se limita apenas as
questões diretamente envolvidas com
linguagens de programação.
Œ Aplicação de tais técnicas se expande para
arquitetura de computadores, teoria das
linguagens, algoritmos e engenharia de
software.

12/3/2008 Introdução 3 de 57

Introdução

Œ O uso de algumas poucas técnicas de


projeto de compiladores pode ser utilizada
para construção de:
z tradutores,
z formatadores e processadores de texto,
z interpretadores (de programas ou queries),
z máquinas de estado,
z além de compiladores.

12/3/2008 Introdução 4 de 57

2
Definição

ΠCompilador (sentido geral): aquele de


compila, isto é, copia ou transcreve,
podendo ou não traduzir aquilo que é
transcrito.
Œ Compilador (software): programa que lê um
texto escrito numa linguagem (fonte)
traduzindo-o num texto equivalente escrito
em outra linguagem (alvo).

12/3/2008 Introdução 5 de 57

Definição

Programa Programa
Fonte Alvo
Compilador
(linguagem (linguagem
(compiler)
fonte) alvo)

Mensagens
de erro

12/3/2008 Introdução 6 de 57

3
Definição

ΠAs linguagens fonte (source languages) podem


tanto ser linguagens de programação (C,
Pascal, Java, Fortran etc) como linguagens
especializadas (TEX, EQN etc).
Œ As linguagens alvo (target languagens) são
também variadas tal como outra linguagem
de programação, uma linguagem de
máquina (assembly) ou uma outra
representação.
12/3/2008 Introdução 7 de 57

Tipos de Compiladores
Œ Single-Pass: efetuam a compilação numa
única leitura do programa fonte.
Œ Multi-Pass: efetuam a compilação através
de várias leituras do programa fonte.
Œ Load-And-Go: efetuam a compilação e a
execução do programa fonte.
Œ Debugging: efetuam a compilação
permitindo a depuração do programa fonte.
Œ Optimizing: efetuam a compilação e a
otimização do programa alvo.
12/3/2008 Introdução 8 de 57

4
Modelo Análise-Síntese

Compilação constitui-se de duas partes:


Œ Análise
Œ Síntese
Compilador Programa
Alvo

Análise Síntese
Programa
Fonte

Significado

12/3/2008 Introdução 9 de 57

Modelo Análise-Síntese

Œ Análise:
z Inclui possíveis mecanismos de pré-
processamento.
z Divide o programa fonte em suas partes
constituintes.
z Cria uma representação intermediária do
programa fonte.
z É uma tarefa relativamente simples.

12/3/2008 Introdução 10 de 57

5
Modelo Análise-Síntese

Œ Síntese:
z Constrói o programa alvo a partir da
representação intermediária produzida pela
análise.
z É uma tarefa relativamente complexa.
z Utiliza a maior parte das técnicas
especializadas.

12/3/2008 Introdução 11 de 57

Modelo Análise-Síntese
Œ Grande parte das técnicas se aplicam a
maioria das linguagens fonte e alvo.
Œ Existem várias ferramentas que facilitam o
desenvolvimento de novos compiladores
ou softwares que usem as mesmas técnicas.
Œ Dá origem aos conceitos de:
z front-end, relacionado com a linguagem a ser
processada;
z back-end: relacionado com a linguagem a ser
produzida.
12/3/2008 Introdução 12 de 57

6
Sistema de Processamento
de Linguagem
Pré-processador
Módulos (preprocessor)
fonte
Compilador
Programa (compiler)
Fonte
Montador
Assembly (assembler)
Alvo
Loader/Link
Código Editor
Relocável

Código
Absoluto

12/3/2008 Introdução 13 de 57

Fases da Compilação

Œ Compiladores operam em “fases”, cada


qual realizando uma transformação no
programa fonte.
ΠAlgumas fases podem ser agrupadas.
Œ Tipicamente a Análise é realizada em três
fases: análise léxica, sintática e semântica.
Œ Igualmente a Síntese é realizada de uma a
três fases conforme o tipo de compilador.
12/3/2008 Introdução 14 de 57

7
Fases da Compilação
Análise
Programa Léxica
Fonte
Análise
Sintática

Análise
Semântica
Tabela de Detecção
Símbolos de Erros
Geração de
Pseudo-Código

Otimização
de Código
Programa
Alvo
Geração
de Código
12/3/2008 Introdução 15 de 57

Análise Léxica ou Linear

Œ Efetua a análise da cadeia de caracteres do


programa fonte (da esquerda para direita)
agrupando as sequências que possuam
significado coletivo (tokens ou lexeme).
Œ Tokens são separados uns dos outros por
caracteres denominados separadores.
Œ Separadores são usualmente os caracteres
“brancos” (espaços, tabulações e quebras
de linha) e os operadores.
12/3/2008 Introdução 16 de 57

8
Análise Léxica ou Linear

ΠDado o programa abaixo:

void main( ) {
int total = 0;
println(“Total = %d.”, total + 10);
}

ΠQuantos tokens possui?


12/3/2008 Introdução 17 de 57

Análise Léxica ou Linear


ΠResposta: 20 (vinte)
z tipo void z identificador println

z identificador main z pontuação (


z string “Total = %d.”
z pontuação (
z pontuação ,
z pontuação )
z identificador total
z pontuação {
z operador +
z tipo int
z número 10
z identificador total z pontuação )
z atribuição = z pontuação ;
z número 0 z pontuação }
z pontuação ;
12/3/2008 Introdução 18 de 57

9
Análise Léxica ou Linear

Œ Uma tarefa adicional da análise léxica é


determinar os tipos dos tokens obtidos
programa fonte:
Œ A cada token é geralmente associado um
tipo ou classe.
Œ Classes ou tipos comuns são:
palavra-chave, identificador, constante (número
ou string), pontuação e operador.

12/3/2008 Introdução 19 de 57

Análise Sintática ou
Hierárquica (parsing)
ΠEnvolve o agrupamento dos tokens obtidos
do programa fonte em frases ou unidades
lógicas.
ΠAs frases/unidade devem ser
gramaticalmente aceitas pela linguagem
fonte para possibilitar sua síntese.
Œ Usualmente as frases/unidades são
representadas por árvores sintáticas (parsing
trees).
12/3/2008 Introdução 20 de 57

10
Análise Sintática ou
Hierárquica
ΠUm outro exemplo:
subtotal = 10 + valorUnitario * quantidade;
atribuição

=
identificador expressão

subtotal +
expressão expressão

número expressão * expressão

10 identificador identificador
valorUnitario quantidade
12/3/2008 Introdução 21 de 57

Análise Sintática ou
Hierárquica
ΠNote que:
z identificador = expressão é uma unidade lógica
z assim como valorUnitario*quantidade
z mas 10+valorUnitario não são agrupados
como unidade lógica porque as multiplicações
devem ser executadas antes das adições
z ou seja, a precedência das operações deve ser
respeitada apesar da ordem em que os tokens
originalmente aparecem no programa fonte.
12/3/2008 Introdução 22 de 57

11
Análise Sintática ou
Hierárquica
Œ A estrutura hierárquica de uma linguagem é
usualmente expressa por regras:
(1) Um identificador é uma expressão
(2) Um número é uma expressão
(3) Se expr1 e expr2 são expressões então
também são expressões:
„ expr1 + expr2
„ expr1 * expr2
„ ( expr1 )

12/3/2008 Introdução 23 de 57

Análise Sintática ou
Hierárquica
ΠUma outra forma de expressar-se as
mesmas regras:
(1) expr → id
(2) expr → number
(3) expr → expr + expr
| expr * expr
| ( expr )

12/3/2008 Introdução 24 de 57

12
Análise Sintática ou
Hierárquica
Œ As regras (1) e (2) são regras básicas não
recursivas.
Œ A regra (3) define expressões em termos de
operadores aplicados a outras expressões,
ou seja, é uma regra recursiva.
Œ É frequente o uso de regras recursivas na
definição (da gramática) de linguagens.

12/3/2008 Introdução 25 de 57

Análise Sintática ou
Hierárquica
Œ Diretivas de linguagens de programação são
exemplos típicos de regras recursivas:
(1) Se id1 é um identificador e expr1 é uma
expressão então é também uma diretiva:
„ id1 = expr1
(2) Se expr1 é uma expressão e stmt1 e stmt2 são
diretivas então são também diretivas:
„ while (expr1) stmt1
„ if (expr1) stmt1 else stmt2

12/3/2008 Introdução 26 de 57

13
Análise Léxica versus
Análise Sintática
Œ A divisão entre análise léxica e sintática é
arbitrária.
Œ Geralmente tal divisão é pensada para
simplificar a etapa de análise como um
todo.
Œ O fator determinante é a recursão, isto é, se
a linguagem fonte é inerentemente
recursiva ou não.

12/3/2008 Introdução 27 de 57

Análise Léxica versus


Análise Sintática
Œ Construções léxicas são geralmente não
recursivas enquanto construções sintáticas
geralmente o são.
Œ O reconhecimento de identificadores não
envolve recursão enquanto que o
processamento de expressões sim.
Œ Gramáticas livres de contexto (context-free
grammars) são uma formalização de regras
recursivas que podem ser usadas para
conduzir a análise sintática.
12/3/2008 Introdução 28 de 57

14
Análise Semântica ou de
Contexto
Œ Nesta fase verifica-se se cada unidade lógica
pode efetivamente ser aplicada dentro do
contexto onde foi encontrada.
Œ Relaciona portanto se as operações
indicadas são aplicáveis (tem significado
semântico) aos operandos utilizados.
Œ Só pode ser realizada após a análise léxica e
sintática.
12/3/2008 Introdução 29 de 57

Análise Semântica ou de
Contexto
ΠUm exemplo:
A=0
Œ Da análise léxica e sintática temos:
zA é um identificador
z= é o operador de atribuição
z0 é um valor numérico (uma expressão)
ΠEquivale assim a:
identificador = expressão

12/3/2008 Introdução 30 de 57

15
Análise Semântica ou de
Contexto
ΠEmbora sintaticamente correto pode ser
semanticamente inválido se:
„ identificador A não foi declarado;
„ identificador A corresponder a uma função,
método ou classe;
„ o tipo do identificador de variável A for
incompatível com o valor numérico 0 (zero);
„ operação de atribuição for inadequada dentro do
contexto em que ocorre (p.e. expressão condicional
de um laço).

12/3/2008 Introdução 31 de 57

Análise Semântica ou de
Contexto
Œ Realiza a verificação de tipos e, quando
permitido pela linguagem, efetua a coerção
de operandos:
int a = 1; float b = 2.5, c;
c = a + b;
ΠSintaticamente correto. Se permitido a
operação de inteiros com reais ocorre a
coerção:
c = inttoreal(a) + b;
12/3/2008 Introdução 32 de 57

16
Tabela de Símbolos
Œ Função essencial de qualquer compilador
que consiste no registro de todos os
identificadores encontrados no programa
fonte e também seus atributos.
ΠComo atributos temos:
„ tipo,
„ escopo,
„ local de armazenamento,
„ número e tipo dos argumentos (se função),
„ método de passagem dos argumentos (se função) e
„ valor de retorno (se função).
12/3/2008 Introdução 33 de 57

Tabela de Símbolos

Œ As informações sobre os identificadores e


seus atributos é mantida numa tabela
denominada tabela de símbolos na forma
de um registro para cada identificador.
ΠEmbora os identificadores sejam
determinados durante a análise léxica, a
maior parte dos seus atributos só pode ser
determinada durante a análise semântica.

12/3/2008 Introdução 34 de 57

17
Tabela de Símbolos

Œ Durante a etapa de síntese, as informações


contidas na tabela de símbolos será bastante
utilizada para:
z determinação do espaço necessário para
armazenamento,
z determinação dos endereços de
armazenamento e
z seleção das instruções para realização das
operações indicadas pelo programa fonte.
12/3/2008 Introdução 35 de 57

Síntese
Œ Implementa procedimentos de avaliação
para o programa-fonte em termos de
primitivas de um computador-alvo.
Œ Para prover maior independência e
flexibilidade do projeto do compilador
divide-se geralmente em:
z geração de pseudo-código (target independent)
z otimização do pseudo-código
z geração do código-alvo
z otimização do código-alvo
12/3/2008 Introdução 36 de 57

18
Geração de Código
Intermediário (pseudocode)
ΠMuitos compiladores geram uma
representação intermediária antes do
código-alvo.
Œ Esta representação intermediária pode ser
entendida como um programa para uma
máquina abstrata (virtual).
Œ Tal representação deve ser facilmente
gerada e traduzida no programa-alvo.
12/3/2008 Introdução 37 de 57

Geração de Código
Intermediário (pseudocode)
Œ Uma forma típica é a three-address code onde
o trecho de programa abaixo:
montante = depInicial + taxaJuros*60
Œ Poderia originar o seguinte pseudo-código:
temp1 = intoreal(60)
temp2 = id3 * temp1
temp3 = id2 + temp2
id1 = temp3

12/3/2008 Introdução 38 de 57

19
Geração de Código
Intermediário (pseudocode)
Œ O three-address code tem como características:
z cada posição de memória é tratada como um
registrador;
z sequências de instruções possuem no máximo
três operandos;
z possui assim um único operador além da
atribuição
z precedência é resolvida através da ordem de
execução;
z posições temporárias são geradas e usadas para
armazenar valores intermediários.
12/3/2008 Introdução 39 de 57

Geração de Código
Intermediário (pseudocode)
Œ Embora gere sequências de código mais
longas que aparentemente necessário,
facilita futuras otimizações e traduções
devido sua simplicidade.
ΠModificando-se apenas as etapas seguintes
pode-se obter compiladores para diferentes
linguagens-alvo, i.e., diferentes arquiteturas.

12/3/2008 Introdução 40 de 57

20
Otimização de Código
Œ Procura melhorar o código intermediário
gerado em termos de:
z velocidade de execução
z quantidade de armazenamento utilizado
Œ É uma tarefa relativamente complexa que
usualmente toma tempo significativo da
compilação, entretanto algumas otimizações
simples podem melhorar muito o tempo de
execução sem retardar demais a
compilação.
12/3/2008 Introdução 41 de 57

Geração de Código

Œ Fase final da compilação que produz código


relocável ou assembly.
Œ Conforme o tipo de código gerado torna-se
necessário utilizar-se ferramentas adicionais
para que o programa-alvo seja executado.
Œ Instruções do código intermediário são
traduzidas em sequências específicas de
instruções de máquina.
12/3/2008 Introdução 42 de 57

21
Geração de Código

Œ Posições de memória são selecionadas para


cada uma das variáveis usadas pelo
programa.
Œ Um importante aspecto é a atribuição de
variáveis para registradores do processador.
ΠAlguns compiladores procuram otimizar
novamente o código final gerado.

12/3/2008 Introdução 43 de 57

Detecção de Erros

ΠPodem ocorrer erros em qualquer fase da


compilação, os quais devem ser relatados de
forma acurada.
Œ Mesmo com a ocorrência de erros, cada
fase deve procurar prosseguir sempre que
possível (compilador deve ser robusto).
ΠMaior parte dos erros ocorre durante
análise sintática e semântica.
12/3/2008 Introdução 44 de 57

22
Transformações do Código

Œ A medida que as etapas de análise e síntese


se realizam, várias transformações ocorrem,
cada uma delas modificando a
representação de suas entradas pela adição
de novas informações.

12/3/2008 Introdução 45 de 57

Transformações do Código
a=b+c*3
Tabela de Símbolos
# id info
Análise Léxica 1 a
2 b
3 c
Id1 = id2 + id3 * number

Análise Sintática =

id1 +

Id2 *

id3 Number

12/3/2008 Introdução 46 de 57

23
Transformações do Código
=

id1 +
Análise Semântica
Id2 *

id3 Number

id1 +

Id2 *

id3 Intoreal(3)

12/3/2008 Introdução 47 de 57

Transformações do Código
=
Otimização Código
id1 + Intermediário

Id2 *
temp1 = id3 * 3.0
id3 Intoreal(3) id1 = id2 + temp1

Geração de Código
Geração Código Alvo
Intermediário

MOV id3, R2
MUL #3.0, R2
temp1 = intoreal(3)
MOV id2, R1
temp2 = id3 * temp1
ADD R2, R1
temp3 = id2 + temp2
MOV R1, id1
id1 = temp3

12/3/2008 Introdução 48 de 57

24
Softwares Auxiliares

Œ Pré-processadores
z produzem a entrada para os compiladores
z efetuam o processamento de macros
„ #define MAX 30
z realizam a inclusão de arquivos
„ #include “global.h”
z permitem adicionar extensões a linguagem
z permitem realizar compilação condicional

12/3/2008 Introdução 49 de 57

Softwares Auxiliares

ΠAssemblers
z Convertem assembly em linguagem de máquina
z Assembler de 2 passos é o mais simples
ΠCarregadores e Link-Editors
z efetuam o carregamento do programa na
memória efetuando a substituição dos
endereços relocáveis.
z permitem a união de arquivos compilados
separadamente
12/3/2008 Introdução 50 de 57

25
Agrupamento de Fases

Œ Atividades de uma ou mais fases são


geralmente agrupadas para otimizar a tarefa
de compilação.
ΠO agrupamento de fases pode ser realizado
da forma considerada mais adequada pelo
projetista do compilador.
Œ Usualmente as fases são agrupadas em
torno do front-end e do back-end do
compilador.
12/3/2008 Introdução 51 de 57

Agrupamento de Fases

ΠFront-End (Interface de Vanguarda)


z Fases que dependem primariamente da
linguagem-fonte e que são independentes da
linguagem-alvo.
z No front-end estão geralmente incluídas a análise
léxica, a análise sintática, a análise semântica, a
criação da tabela de símbolos e a geração de
código intermediário bem como o tratamento
dos erros ocorridos nas fases associadas.

12/3/2008 Introdução 52 de 57

26
Agrupamento de Fases

ΠBack-End (Interface de Retaguarda)


z Fases que dependem explicitamente da
linguagem-alvo (i.e. da arquitetura de destino
do programa) e que são independentes da
linguagem-alvo (embora dependente também
da linguagem intermediária).
z No back-end estão geralmente incluídas a
geração de código alvo, as demais fases de
otimização e também o tratamento dos erros
ocorridos nas fases associadas.
12/3/2008 Introdução 53 de 57

Agrupamento de Fases

ΠO agrupamento de fases permite o projeto


mais flexível de compiladores pois:
z Se o front-end for bem projetado, refazendo-se
seu back-end temos um compilador para uma
plataforma diferente. (fato comum)
z Se o back-end for bem projetado, pode-se
utilizado para diferentes front-end e com isto
obter-se compiladores de diversas linguagens
para uma mesma máquina. (fato raro)
12/3/2008 Introdução 54 de 57

27
Fases versus Passagens

Œ Uma fase é uma atividade específica do


compilador. Devido ao agrupamento de
fases é muitas vezes difícil identifica-las e
isola-las.
ΠUma passagem consiste na leitura completa
do arquivo de entrada produzindo uma
saída, podendo incluir várias fases.
Œ Passagens são facilmente identificáveis.

12/3/2008 Introdução 55 de 57

Fases versus Passagens


Œ Quanto menor o número de passagens,
mais rápido se torna o compilador pois são
efetuadas menos leituras da entrada e
produzidos um menor número de saídas
intermediárias.
ΠPor outro lado, uma maior parte do
compilador deve permanecer na memória a
medida que se reduzem suas passagens
assim como tornam-se maiores e mais
complexas as representações intermediárias.
12/3/2008 Introdução 56 de 57

28
Fases versus Passagens
ΠCaso comum:
z Numa primeira passagem o analisador sintático
comanda tanto o analisador léxico para
obtenção de tokens como o analisador
semântico para geração de código
intermediário.
z Numa segunda passagem são determinados
gerados os endereços finais de variáveis e
procedimentos cujo código foi previamente
gerado.

12/3/2008 Introdução 57 de 57

29

Você também pode gostar