compiladores - aula01

Post on 30-Jun-2015

308 Views

Category:

Documents

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Introdução à Compilação

- Aula 01 -

Noiza Waltrick Trindade

[noiza@uems.br]

Compiladores

Pág. 2

O que é um Compilador?

“Um compilador é um programa que lê um programa

escrito em uma linguagem (linguagem fonte) e a

traduz em um programa equivalente em outra

linguagem (linguagem alvo).”

Aho, Sethi, Ullman.

Pág. 3

O que é um Compilador?

Nesse processo de tradução, há duas tarefas

básicas a serem executadas por um compilador,

conhecida como modelo análise-síntese:

– Análise, em que o texto de entrada (na linguagem fonte) é

examinado, verificado e compreendido

• Análise léxica, sintática e semântica

– 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

Pág. 4

É possível representar completamente a sintaxe de

uma LP através de uma gramática sensível ao

contexto.

Mas como não existem algoritmos práticos para

tratar essas gramáticas, a preferência recai em usar

gramáticas livres 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.

Pág. 5

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 LP, 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.

Pág. 6

Compiladores – Separando em partes

Um dos modelos possíveis para a construção de compiladores faz a separação total entre o front-end, encarregado da fase de análise, e o back-end, encarregado da geração de código, de forma que◦ O front-end e back-end se comunicam

apenas através da representação intermediária

◦ O front-end depende exclusivamente da linguagem fonte;

◦ O back-end depende exclusivamente da linguagem objeto.

Verificador de Tipos

Parser

Desugarer

Sintaxe Core

CoreToSTG

Sintaxe STG

Gerador de IL

Código MSIL

(Texto)

ILDASM

Gerador GHC

Código Assembly

/ Código C

Assembler / Compilador C

Código Nativo

CorePrinter

ArquivoCore

PhxSTGCompiler

JIT

Código Nativo

Arquivo Haskell '

Otimizações

Haskell.

NE

T

GH

C N

ativo

Assembly MSIL

Front-end

Back-end

Pág. 7

Compiladores – Separando em partes

Um dos modelos possíveis para a construção de

compiladores faz a separação total entre o front-

end, encarregado da fase de análise, e o back-end,

encarregado da geração de código, de forma que

– O front-end e o back-end se comunicam apenas através

da representação intermediária

Pág. 8

Compiladores – Separando em partes

– O front-end depende exclusivamente da linguagem fonte;

• Consiste das fases que dependem primariamente da linguagem

fonte e são praticamente independentes da máquina alvo.

Normalmente, inclui a análise léxica, análise sintática, análise

semântica, a geração do código intermediário, além do

gerenciamento da tabela de símbolos e o tratador de erros. Às vezes

até o otimizador de código é colocado no front end.

– O back-end depende exclusivamente da linguagem destino.

• Consiste das fases que dependem da máquina alvo e geralmente

não dependem, em nada, do programa-fonte. As fases que contêm

estas propriedades são o otimizador de código e o gerador de

código, além do gerenciador da tabela de símbolos e o tratador de

erros.

Pág. 9

Compiladores – Separando em partes

Simplifica a implementação de novos

compiladores

◦ Front-end específico para cada

linguagem

◦ Back-end específico para a arquitetura

alvo

Pág. 10

Compiladores - Fases

Analisador

léxico

Analisador

sintático

Analisador

semântico

Gerador de

código

intermediário

Otimizador

de código

Gerador de

código

código fonte

código alvo

Tratador de

erros

Gerador tabela

de símbolos

Pág. 11

Fases

Conjunto de alterações feitas no código as quais

são responsáveis por uma atividade específica do

processo de compilação

– Análise Léxica (scanner)

– Análise Sintática (parser)

– Análise Semântica

– Otimização

– Geração de código

Pág. 12

Análise Léxica

Também chamada de scanner

Agrupa caracteres em símbolos (ou tokens)

Entrada: fluxo de caracteres

Saída: fluxo de símbolos

Símbolos são:

– Palavras reservadas, identificadores de variáveis e

procedimentos, operadores, pontuação,...

Uso de expressões regulares no reconhecimento

Lex/Flex são ferramentas que geram scanners.

Pág. 13

Análise Léxica

Dado os caracteres da instrução

montante := saldo + taxa_de_juros * 30;

São identificados os seguintes tokens:

Identificador montant

Símbolo de atribuição :=

Identificador saldo

Símbolo de adição +

Identificador taxa_de_juros

Símbolo de multiplicação *

Número 30

Pág. 14

Análise Sintática

Também chamada de parser

Agrupa símbolos em unidades sintáticas

– Ex.: os 3 símbolos A+B podem ser agrupados em uma estrutura

chamada de expressão.

Expressões depois podem ser agrupados para formar

comandos ou outras unidades.

Saída: representação da árvore de parse do programa

Gramática livre de contexto é usada para definir a estrutura

do programa reconhecida por um parser

Yacc/Bison são ferramentas para gerar parsers

Pág. 15

Análise Sintática

Comando

Expressão

Identificador

Expressão

Expressão

Expressão

Expressão

:=

+

*

Identificador

Identificador

montante

saldo

taxa_de_juros

Número

60

Árvore gerada para: montante := saldo + taxa_de_juros * 60

Pág. 16

Análise Semântica

Verifica se estruturas sintáticas, embora corretas

sintaticamente, têm significado admissível na linguagem.

Por exemplo, não é possível representar em uma gramática

livre de contexto uma regra como “todo identificador deve

ser declarado antes de ser usado“

Um importante componente é checagem de tipos.

Utiliza informações coletadas anteriormente e armazenadas

na tabela de símbolos

Considerando “A + B”, quais os possíveis problemas

semânticos?

Saída: árvore de parse anotada

Pág. 17

Análise Semântica

montante +

*

saldo

taxa_de_juros

*

60

montante +

*

saldo

taxa_de_juros

*

inttoreal

Conversão de inteiro para real inserida pela análise semântica

Pág. 18

Gerador de Código Intermediário

Usa as estruturas produzidas pelo analisador

sintático e verificadas pelo analisador semântico

para criar uma seqüência de instruções simples

(código intermediário)

Está entre a linguagem de alto nível e a linguagem

de baixo nível

Pág. 19

Gerador de Código Intermediário

Considere que temos um único registrador

acumulador.

Considere o comando de atribuição

x := a + b * c

pode ser traduzido em:

t1:=b*c

t2:=a+t1

x:=t2

Pode-se fazer um gerador de código relativamente

simples usando regras como:

Pág. 20

Gerador de Código Intermediário

Toda operação aritmética (binária) gera 3 instruções. Para b*c

1. Carga do primeiro operando no acumuladorload b

2. Executa a operação correspondente com o segundo operando, deixando o resultado no acumuladormult c

3. Armazena o resultado em uma nova variável temporáriastore t1

Um comando de atribuição gera sempre duas instruções. Para x:= t2

1. Carrega o valor da expressão no acumuladorload t2

2. Armazena o resultado na variávelstore x

Pág. 21

Gerador de Código Intermediário

Para o comando de atribuiçãox := a + b * c;

é gerado o código intermediário:1. Load b { t1 := b * c }2. Mult c3. Store t14. Load a { t2 := a + t1 }5. Add t16. Store t27. Load t28. Store x { x := t2 }

Pág. 22

Otimizador de Código

Independente da máquina

Melhora o código intermediário de modo que o

programa objeto seja menor (ocupe menos espaço

de memória) e/ou mais rápido (tenha tempo de

execução menor)

A saída do otimizador de código é um novo código

intermediário

Pág. 23

Otimizador de Código

1. Load b

2. Mult c

3. Add a

4. Store x

Pág. 24

Otimizador de Código

Pág. 25

Gerador de Código

Produz o código objeto final

Cada máquina ou cada plataforma possui um conjunto

diferente de instruções e de meios de acesso ao sistema

operacional. Em geral é necessário um gerador de código

para cada plataforma.

Toma decisões com relação à:

– Alocação de espaço para os dados do programa;

– Seleção da forma de acessá-los;

– Definição de quais registradores serão usados, etc.

Projetar um gerador de código que produza programas

objeto eficientes é uma das tarefas mais difíceis no projeto

de um compilador

Pág. 26

Gerador de Código

Várias considerações têm que ser feitas:

– Há vários tipos de instruções correspondendo a vários

tipos de dados e a vários modos de endereçamento;

– Há instruções de soma específicas, por exemplo para

incrementar/decrementar de 1;

– Algumas somas não foram especificadas explicitamente;

– Cálculo de endereço de posições em vetores;

– Incremento/decremento registrador de topo pilha;

– Local onde armazenar variáveis;

– Alocação de registradores.

Pág. 27

Gerenciamento da Tabela de Símbolos

Uma função essencial do compilador é registrar os

identificadores usados no programa fonte e coletar

as informações sobre os seus diversos atributos de

um identificador, tais como: memória alocada, tipo,

escopo).

Uma tabela de símbolos é uma estrutura de dados

contendo um registro para cada identificador, com

os campos contendo os atributos do identificador.

Pág. 28

Tratamento de Erros

Tipos de erros: léxicos, sintáticos, semânticos e

lógicos.

Os erros sintáticos são os mais freqüentes.

É ativado sempre que for detectado um erro no

programa fonte. Ele deve avisar o programador da

ocorrência do erro emitindo uma mensagem, e

ajustar-se novamente à informação sendo passada

de fase a fase de modo a poder completar o

processo de compilação (mesmo que não seja mais

possível gerar código objeto, a análise léxica e

sintática deve prosseguir até o fim).

top related