Você está na página 1de 19

CP 5017.

9 – Compiladores I
Prof. Msc. Carlos de Salles

1 - EMENTA
O Processo de Compilação. Deteção e Recuperação de Erros.
Introdução à geração de Código Intermediário. Geração de
Código de Máquina. Otimização. Uma visão sobre alguns
compiladores. A construção de um compilador.

2 - O PROCESSO DE COMPILAÇÃO
2.1 - Introdução
2.2 - Aspectos do Processo de Compilação

3 - DEFINIÇÃO DE LINGUAGEM
3.1 - Introdução
3.2 - Sintaxe e Semântica
3.3 - Gramáticas - Definição Formal de Linguagem de
Programação
3.4 - O Problema da Análise

4 - ANÁLISE LÉXICA
4.1 - Introdução
4.2 - Construção Manual de Analisadores
4.3 - Construção Sistemática de Analisadores
4.4 - Saídas do Analisador Léxico
4.5 - Implementação
4.6 - Erros

5 - GRAMÁTICAS LIVRES DO CONTEXTO


5.1 - Sintaxe e Semântica
5.2 - Gramáticas - Definição Formal de Linguagens de
Programação
5.3 - Gramáticas Livres do Contexto

Slide 1 Compiladores I
CP 5017.9 – Compiladores I
Prof. Msc. Carlos de Salles

Ementa (continuação)
6 - ANÁLISE SINTÁTICA DESCENDENTES
6.1 - Formalização
6.2 - Análise com Recuperação
6.3 - Análise sem Recuperação
6.3.1 - Analisadores Recursivos
6.3.2 - Analisadores Preditivos
7 - ANÁLISE SNTÁTICA ASCENDENTE
7.1 - Formalização
7.2 - Analisadores de Precedência
8 - TRADUÇÃO DIRIGIDA POR SINTAXE
8.1 - Esquemas da Tradução Dirigida por Sintaxe
8.2 - Implementação de Tradutores Dirigidos por Sintaxe
9 - TABELA DE SÍMBOLOS
9.1 - Os Conteúdos da Tabela de Símbolo
9.2 – Organizações de Tabelas de Símbolos
10 - ORGANIZAÇÃO DE MEMÓRIA
11 - CONSTRUÇÃO AUTOMÁTICA DE
ANALISADORES EFICIENTES
Bibliografia
• GRUNE, DICK et al. Projeto Moderno de Compiladores.
Editora Campus.
• AHO, Alfred; SETHI, Ravi; ULLMAN, Jeffrey D. Compiladores
– Princípios, Técnicas e Ferramentas. Editora Guanabara.
Editora LTC.

Slide 2 Compiladores I
Paradigma análise/síntese

código fonte interface de código intermediário gerador código alvo


vanguarda de código

Estrutura básica de um compilador

Slide 3 Compiladores I
Interface de vanguarda do compilador

• Engloba as fases de análise


• Gera uma representação semântica intermediária

• Análise léxica
– Converte uma seqüência de caracteres no(s)
arquivo(s) de entrada com o código fonte em uma
seqüência de tokens equivalente;
– Cada token representa um elemento atômico da
linguagem;

• Análise sintática
– Transforma a seqüência de tokens em uma árvore
sintática que representa o código fonte;
– É dividido em dois grupos de métodos: os top-down
e os bottom-up;

• Tratamento de contexto
– Avalia erros de tipagem e nos identificadores;
– Baseia-se em informações coletadas numa
estrutura chamada tabela de símbolos.

Slide 4 Compiladores I
Geração de código X Interpretação

• Geração de código
– Transforma a representação semântica intermediária em
código executável em uma linguagem alvo;
– Características:
• Processamento considerável;
• A forma intermediária resultante, que é de código binário
específico da máquina, é de baixo-nível;
• O mecanismo de interpretação é o próprio hardware da
CPU;
• A execução do programa é relativamente rápida;
• Interpretação
– Ao invés de traduzir para uma linguagem alvo, realiza as
ações semânticas diretamente;
– Vantagens:
• Geralmente um interpretador é escrito em uma linguagem de
mais alto nível, portanto irá rodar em várias máquinas
diferentes. Um gerador de código é voltado para uma
máquina específica;
• Escrever um interpretador exige esforço bem menor;
• Executar as ações diretamente da representação semântica
permite tanto uma melhor verificação quanto informação de
erros;
• Melhor segurança (explorado por Java)
– Características:
• O processamento do programa é de mínimo a moderado;
• A forma intermediária resultante, alguma estrutura de dados
específica de sistema, de médio a alto nível;
• O mecanismo de interpretação é um programa;
• A execução do programa é relativamente lenta.

Slide 5 Compiladores I
Por que estudar Compiladores?

• Estruturação eficaz do problema


– Compiladores analisam sua entrada, constroem uma
representação semântica intermediária e sintetizam sua
saída a partir disso;
– Esse paradigma análise-síntese é muito poderoso e
amplamente aplicável;
– Usando a mesma representação semântica intermediária,
um compilador pode ser escrito para L linguagens e M
máquinas, bastando escrever as L interfaces de
vanguarda diferentes e os M sintetizadores diferentes;
• Uso eficiente de formalismos
– Expressões regulares – análise léxica
– Gramáticas livres de contexto – análise sintática
– Gramáticas de atributo – tratamento de contexto e
extensão para geração de código e interpretação
– Casamento de padrões e técnicas de programação
dinâmica – geração de código
• Uso de ferramentas de geração de programas
– A entrada para um gerador de programas é de um nível
de abstração mais alto que de uma linguagem de
programação;
– Flexibilidade e mutabilidade ampliadas;
– Código pré-construído pode ser incluído em um programa
gerado, aumentando seu poder a quase nenhum custo;
– Uma descrição formal pode ser usada para gerar mais de
um tipo de programa;
– Exemplo: Flex/Bison; Lex/Yacc.

Slide 6 Compiladores I
Por que estudar Compiladores?

• A construção de compiladores tem uma ampla


aplicabilidade;
– O uso de técnicas de construção de compiladores
são aplicadas em outros escopos
– Exemplos:
• Conversão de formatos diferentes de arquivos;
• Leituras de arquivos HTML ou PostScript.

• Compiladores são baseados em algoritmos gerais úteis


– As estruturas de dados e algoritmos
implementados são didaticamente importantes
– Exemplos:
• Árvores sintáticas;
• Tabelas hash;
• Tabelas pré-processadas ;
• Coletor de lixo;
• Programação dinâmica, etc.

• Enfim, estudar compiladores é solidificar o


conhecimento de programação!

Slide 7 Compiladores I
Propriedades de um bom compilador

• Gerar código correto;


– De que adianta um compilador que erra uma vez a
cada um milhão;
• Estar de acordo com a especificação da
linguagem;
– Nada de super ou sub-conjuntos da linguagem;
• Ser escalável, ou seja, ser capaz de tratar
códigos fonte de tamanhos arbitrários;
– Atualmente isso é muito mais simples já que a
quantidade de memória disponível é cada vez
maior;
• Velocidade de compilação não é mais importante;
– Nada mais de cartões – compilar agora é rápido;
• Algoritmos desejavelmente lineares – O(n);
– Não é interessante a existência de algoritmos não-
lineares no processo de compilação;
– Problema: a otimização de código não é linear;
• Outras características importantes:
– Portabilidade do compilador;
– Portabilidade do código alvo;

Slide 8 Compiladores I
Histórico de compiladores

• 1945 – 1960: Geração de Código


– Linguagens sendo desenvolvidas lentamente;
– O uso de compiladores era chamado de
“programação automática”;
– Linguagens “de alto nível” não eram bem vistas;
• Se o compilador gerava código inferior àquele gerado
manualmente com assemblers, para que uma
linguagem de alto nível?
• 1960 – 1975: Parsing
– Proliferação de novas linguagens;
– Nasce a idéia que é melhor ter ferramentas para
gerar um compilador rapidamente que gerar código
mais eficiente;
– Mudança no paradigma: maior enfoque na análise
que na síntese;
– Aparecimento de técnicas formais notadamente
voltadas para a geração de parsers;
• 1975 – dias atuais: Geração e Otimização de
Código; Novos Paradigmas
– Número de novas linguagens e de novas máquinas
caem;
– Demanda por interfaces com o usuário mais
amigáveis (nascimento dos IDEs);
– Novos paradigmas de programação, como
linguagens funcionais e lógicas e programação
distribuída;
– A ênfase agora é “o que compilar” e não mais
“como compilar”.

Slide 9 Compiladores I
Análise Léxica

• O objetivo da análise léxica é ler o(s)


arquivo(s) fonte de entrada, caractere a
caractere, e transformá-lo(s) numa
seqüência de símbolos léxicos chamados
de tokens;
• Tokens são elementos indivisíveis da
linguagem como palavras reservadas,
constantes, cadeias de caracteres,
identificadores, operadores e outros
delimitadores
• Internamente, um token pode ser expresso
por três informações:
– Classe do Token: define seu tipo;
• Exemplos: operador, palavra reservada,
identificador etc.
– Valor do Token: dependendo da classe do
token, assume um valor que o representa;
• Exemplos: “i” para um identificador; “123”
para uma constante numérica etc;
– Posição do Token: define o arquivo e a
posição (linha e coluna) dentro daquele arquivo
do respectivo token.

Slide 10 Compiladores I
Implementação de um Analisador Léxico

• Como ler o arquivo texto de entrada?


– Memória não é mais um problema;
– A entrada por dois buffers (atual e anterior);
– Leitura do arquivo de entrada colocando-o em
memória em uma única chamada ao sistema
operacional;
• O problema da nova linha
– No Windows/DOS, uma nova linha é marcada
pela seqüência de caracteres ASCII 13 e 10;
– No Linux, uma nova linha é marcada pelo
caractere ASCII 10;
– No OS/2, cada linha é um vetor de caracteres;
• O que é um token exatamente?
– Simplificação: se você pode colocar espaços
na esquerda e direita sem alterar o significado,
trata-se de um token;
• Comentários não são tokens. A análise léxica os
remove da seqüência de tokens gerada como
saída.

Slide 11 Compiladores I
Interface de Programação (API) Léxica
Arquivos fonte Tokens
Análise Léxica

• Funções públicas
– void startLex(string fileName);
• Abre o arquivo fonte de entrada fileName
e o copia por inteiro para a memória
• Caso o compilador manipule múltiplos
arquivos, utiliza uma pilha para armazenar
a posição em que estava manipulando o
arquivo atual e abre o novo arquivo
– Token nextToken();
• Avalia o arquivo fonte atual, caractere por
caractere, e retorna o próximo token
• Implementação de autômatos
• Algumas funções privadas úteis
– char nextChar();
• Retorna o próximo caractere do arquivo
atual, contando as linhas e colunas;
– void skipLayoutAndComment();
• Percorre o fonte ignorando comentários
até encontrar um caractere que não seja
espaço, tabulação ou pulo de linha

Slide 12 Compiladores I
Expressões Regulares (ER)

Padrões Básicos: Significado:


x O caractere x
. Qualquer caractere, exceto nova linha
[xyz...] Quaisquer caracteres x, y, z, ...

Operadores de repetição:
R? Um R ou nada (opcionalmente R)
R* Zero ou mais ocorrências de R
R+ Uma ou mais ocorrências de R

Operadores de composição:
R1R2 Um R1 seguido de um R2
R1|R2 Ou R1, ou R2

Agrupamento:
(R) R por si só

Exemplos:
inteiro := [0..9]+
real := [0-9]*{.}[0-9]+
identificador := [a-zA-Z][a-zA-Z0-9_]*

Slide 13 Compiladores I
Autômatos expressando ER's
0-9
• Número inteiro – [0-9]+
0-9
1 2

• Número real – [0-9]*{.}[0-9]+


0-9 0-9

0-9 0-9
1 2 . 3 4

.
• Identificador – [a-zA-Z][a-zA-Z0-9_]*
a-zA-Z0-9_

a-zA-Z
1 2

• Exemplo mais complexo [a][b|c]*[d]+{e}


bc
d
a d e
1 2 3 4

Slide 14 Compiladores I
Análise Sintática
Tokens Árvore Sintática
Análise Sintática

• Responsável pela construção da árvore sintática


abstrata (AST) com base nos tokens de entrada;
• Há dois tipos de métodos de parsing (verificação
sintática):
– Top-down: a árvore sintática é construída da
raiz para as folhas
– Bottom-up: a partir das folhas da árvore são
construídas hipóteses que montam
recorrentemente seus pais, de forma que a raiz
da árvore é o último nó a ser construído
• Uma linguagem de programação é formalmente
definida por uma gramática, tipicamente livre de
ambigüidade, de forma que uma única árvore
sintática pode representar o fonte;
• Linguages de programação geralmente são
expressas por meio de uma notação chamada
BNF (Backus Naur Form) ou sua extensão E-BNF
(Extended-BNF) que acrescenta o uso de
expressões regulares

Slide 15 Compiladores I
Árvore sintática – expressão b*b-4*a*c

Árvore sintática abstrata

Árvore Abstrata
Anotada

Slide 16 Compiladores I
Parser Recursivo Descendente

• Método top-down baseado no mecanismo de


recursão das linguagens de programação;
• O processo inicia com a construção da raiz da
árvore sintática, representando um não-terminal N;
• A decisão da alternativa correta a ser considerada
de N é feita com base em um processo recursivo
que tenta as alternativas de N;
• A alternativa escolhida é a primeira possível, de
forma a evitar backtracking, o que simplifica o
processo mas cria alguns problemas;
• Exemplo de gramática:
entrada → expressao EOF
expressao → termo resto_expressao
termo → IDENT | parenteses_exp
parenteses_exp → '(' expressao ')'
resto_expressao → operador expressao | ε
operador → '+' | '-' | '*' | '/'

• Tokens dessa linguagem:


IDENT := [a-zA-Z][a-zA-Z0-9_]*
EOF, '(', ')', '+', '*', '-', '/'

Slide 17 Compiladores I
Parser Recursivo Descendente
bool entrada() {
return expressao() && tk(EOF);
}
bool expressao() {
return termo() && resto_expressao();
}
bool termo() {
return tk(IDENT) || parenteses_exp();
}
bool parenteses_exp() {
return tk('(') && expressao() &&
tk(')');
}
bool resto_expressao() {
return (operador() && expressao() )
|| true;
}
bool operador() {
return tk('+') || tk('-') || tk('*')
|| tk('/');
}
bool tk(Token tt) {
if(tokenAtual!=tt) return false;
getNextToken(); return true;
}Slide 18 Compiladores I
Conjunto FIRST de uma gramática
Data definitions:
1. Token sets called FIRST sets for all terminals, non-terminals and
alternatives of non-terminals in G.
2. A token set called FIRST for each alternative tail in G; an
alternative tail is a sequence of zero or more grammar symbols α if
A α is an alternative or alternative tail in G.
Initializations:
1. For all terminals T, set FIRST(T) to {T}.
2. For all non-terminals N, set FIRST(N) to the empty set.
3. For all non-empty alternatives and alternative tails α, set
FIRST(α) to the empty set.
4. Set the FIRST set of all empty alternatives and alternative tails
to {ε}.
Inference rules:
1. For each rule N→α in G, FIRST(N) must contain all tokens in
FIRST(α), including ε if FIRST(α) contains it.
2. For each alternative or alternative tail α of the form Aβ,
FIRST(α) must contain all tokens in FIRST(A), excluding ε, should
FIRST(A) contain it.
3. For each alternative or alternative tail α of the form Aβ and
FIRST(A) contains ε, FIRST(α) must contain all tokens in
FIRST(β), including ε if FIRST(β) contains it.
• entrada { IDENT ’(’ }
• expressao { IDENT ’(’ }
• termo { IDENT ’(’ }
• parenteses_exp { ’(’ }
• resto_expressao { ’+’ ε }
Slide 19 Compiladores I

Você também pode gostar